{
  "serverInfo": {
    "name": "glyphguard",
    "title": "GlyphGuard",
    "version": "0.1.0"
  },
  "description": "Hidden and deceptive Unicode detection, text cleaning and lookalike name comparison. No API key: about 60 checks per minute per IP.",
  "homepage": "https://glyphguard.mike-tusa.workers.dev/",
  "contact": {
    "email": "digitalpromohub.support@gmail.com"
  },
  "transport": {
    "type": "streamable-http",
    "url": "https://glyphguard.mike-tusa.workers.dev/mcp"
  },
  "protocolVersions": [
    "2026-07-28",
    "2025-11-25",
    "2025-06-18",
    "2025-03-26",
    "2024-11-05"
  ],
  "capabilities": {
    "tools": {
      "listChanged": false
    }
  },
  "authentication": {
    "required": false
  },
  "instructions": "GlyphGuard finds hidden and deceptive Unicode in text: invisible characters (zero-width, soft hyphen, fillers), bidi controls used for Trojan Source attacks, Unicode tag characters (decoded, a prompt-injection smuggling channel), variation-selector runs, control characters, odd spaces, mixed-script words and lookalike letters (Cyrillic \"а\" posing as Latin \"a\"). Joiners in legitimate context (emoji ZWJ sequences, VS16, Persian/Arabic ZWNJ, Indic virama ZWJ/ZWNJ) are info only and kept by clean_text unless strict=true. Lone surrogates are flagged. Use scan_text before trusting pasted text, tool descriptions, usernames or links; clean_text to strip the hidden characters (optionally mapping lookalikes to Latin and applying NFKC); compare_names to check whether a name, username or brand is visually confusable with protected names (UTS #39-style skeleton, curated subset). Text up to 2000 characters; names up to 128; up to 50 targets. Pure computation, nothing stored, no network calls. No API key: about 60 checks per minute per IP.",
  "tools": [
    {
      "name": "scan_text",
      "title": "Scan text for hidden or deceptive Unicode",
      "description": "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.",
      "inputSchema": {
        "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
      },
      "outputSchema": {
        "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"
        ]
      },
      "annotations": {
        "title": "Scan text for hidden Unicode",
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "clean_text",
      "title": "Remove hidden Unicode from text",
      "description": "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.",
      "inputSchema": {
        "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
      },
      "outputSchema": {
        "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"
        ]
      },
      "annotations": {
        "title": "Clean hidden Unicode",
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "compare_names",
      "title": "Check a name for lookalike impersonation",
      "description": "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.",
      "inputSchema": {
        "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
      },
      "outputSchema": {
        "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"
        ]
      },
      "annotations": {
        "title": "Compare names for lookalikes",
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    }
  ]
}