GlyphGuard

Find hidden Unicode (zero-width, bidi, tag payloads), homoglyphs and lookalike names in text.

Should I use this

Quality & Safety

A
Description quality
100%
Schema completeness
100%
Naming quality
80%
Poisoning risk
100%
Permission match
100%
Protocol compliance
100%

Based on automated analysis of tool definitions and protocol compliance.

Context Cost

~1,269Tokens (tool definitions)
~3.3 KBTypical response size
Moderate attention impact (0.99% of 128k context)

This is the approximate number of tokens consumed each time the server's tools are loaded into a model's context. Higher counts reduce the attention available for other tasks.

Install

One-Click Install

Add this to your `claude_desktop_config.json` file:

{
  "mcpServers": {
    "glyphguard": {
      "url": "https://glyphguard.mike-tusa.workers.dev/mcp"
    }
  }
}

Remote endpoints

https://glyphguard.mike-tusa.workers.dev/mcpstreamable-http

What it can do

Tool inventory

Tools (3)

🟒 Read-only🟑 WriteπŸ”΄ Deleteβšͺ Unknown
🟒scan_text(text)

Find invisible characters, bidi controls (Trojan Source), hidden Unicode tag payloads (decoded), variation-selector runs, control characters, unusual spaces, mixed-script words and lookalike letters. ZWJ/ZWNJ/VS16 in legitimate context (emoji sequences, Persian/Arabic joining, Indic virama) are reported as info joiner_in_context and do not raise risk; elsewhere they stay medium. Lone surrogates are flagged. Returns a risk level (none/low/medium/high), counts, and findings with UTF-16 offsets and code points. Read-only. Same engine as GET /api/scan.

Input Schema

{
  "type": "object",
  "properties": {
    "text": {
      "type": "string",
      "minLength": 1,
      "maxLength": 2000,
      "description": "Text to inspect (up to 2000 UTF-16 code units)."
    }
  },
  "required": [
    "text"
  ],
  "additionalProperties": false
}

Output Schema

{
  "type": "object",
  "properties": {
    "risk": {
      "type": "string",
      "enum": [
        "none",
        "low",
        "medium",
        "high"
      ]
    },
    "length": {
      "type": "integer"
    },
    "codePoints": {
      "type": "integer"
    },
    "total": {
      "type": "integer"
    },
    "counts": {
      "type": "object"
    },
    "tagPayload": {
      "type": "string"
    },
    "findings": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string"
          },
          "severity": {
            "type": "string"
          },
          "offset": {
            "type": "integer"
          },
          "codePoint": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "char": {
            "type": "string"
          },
          "looksLike": {
            "type": "string"
          },
          "script": {
            "type": "string"
          },
          "word": {
            "type": "string"
          },
          "scripts": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      }
    },
    "truncated": {
      "type": "boolean"
    },
    "notes": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "normalizedNfkcDiffers": {
      "type": "boolean"
    },
    "version": {
      "type": "string"
    }
  },
  "required": [
    "risk",
    "total",
    "counts",
    "findings",
    "version"
  ]
}
🟒clean_text(text, lookalikes, nfkc, spaces, keep_joiners, ...)

Return the text with invisible characters, bidi controls, tag characters and control characters removed and unusual spaces turned into normal spaces. By default ZWJ/ZWNJ/VS16 are kept inside emoji sequences, between letters of joining scripts (Arabic, Persian) and after an Indic virama (keep_joiners=true); strict=true removes them everywhere. Optional: map lookalike letters to Latin (lookalikes) and apply NFKC normalisation (nfkc). Read-only. Same engine as GET /api/clean.

Input Schema

{
  "type": "object",
  "properties": {
    "text": {
      "type": "string",
      "minLength": 1,
      "maxLength": 2000,
      "description": "Text to inspect (up to 2000 UTF-16 code units)."
    },
    "lookalikes": {
      "type": "boolean",
      "default": false,
      "description": "Map lookalike letters (e.g. Cyrillic Π°) to Latin. Changes legitimate non-Latin text too."
    },
    "nfkc": {
      "type": "boolean",
      "default": false,
      "description": "Apply Unicode NFKC first (fullwidth and math letters β†’ plain)."
    },
    "spaces": {
      "type": "boolean",
      "default": true,
      "description": "Turn unusual spaces into a normal space."
    },
    "keep_joiners": {
      "type": "boolean",
      "default": true,
      "description": "Keep ZWJ/ZWNJ/VS16 where they are legitimate: emoji ZWJ sequences and VS16 emoji presentation, between letters of joining scripts (Arabic, Persian), and after an Indic virama. Joiners elsewhere are always removed."
    },
    "strict": {
      "type": "boolean",
      "default": false,
      "description": "Strict mode: remove every joiner and variation selector, even in emoji and Persian/Indic text (overrides keep_joiners)."
    }
  },
  "required": [
    "text"
  ],
  "additionalProperties": false
}

Output Schema

{
  "type": "object",
  "properties": {
    "text": {
      "type": "string"
    },
    "changed": {
      "type": "boolean"
    },
    "removed": {
      "type": "object"
    },
    "joinersKept": {
      "type": "integer"
    },
    "lookalikesMapped": {
      "type": "integer"
    },
    "options": {
      "type": "object"
    },
    "length": {
      "type": "integer"
    },
    "notes": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "version": {
      "type": "string"
    }
  },
  "required": [
    "text",
    "changed",
    "removed",
    "version"
  ]
}
🟒compare_names(name, targets)

Check whether a name, username, domain label or brand looks the same as any of up to 50 protected names (for example "pΠ°ypal" with a Cyrillic Π° vs "paypal"), using a UTS #39-style confusable skeleton (curated subset, case and accents ignored). Read-only. Same engine as GET /api/compare.

Input Schema

{
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 128,
      "description": "Name, username, domain label or brand to test."
    },
    "targets": {
      "type": "array",
      "minItems": 1,
      "maxItems": 50,
      "items": {
        "type": "string",
        "minLength": 1,
        "maxLength": 128
      },
      "description": "1 to 50 protected names."
    }
  },
  "required": [
    "name",
    "targets"
  ],
  "additionalProperties": false
}

Output Schema

{
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "skeleton": {
      "type": "string"
    },
    "confusableWith": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "results": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "target": {
            "type": "string"
          },
          "skeleton": {
            "type": "string"
          },
          "identical": {
            "type": "boolean"
          },
          "confusable": {
            "type": "boolean"
          },
          "caseOnly": {
            "type": "boolean"
          }
        }
      }
    },
    "nameRisk": {
      "type": "string"
    },
    "nameScripts": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "notes": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "version": {
      "type": "string"
    }
  },
  "required": [
    "skeleton",
    "confusableWith",
    "results",
    "version"
  ]
}

Community

Rate this Server

Evidence

Recent observations

verifiedversion not recorded3 tools