MCP Server Slug Disambiguation#
Claude Code can register the same logical MCP server under three distinct mcp_server slug namespaces, and a real archive routinely contains two or three of them as separate rows in tools/mcp-usage :
| Namespace | Shape | Example |
|---|---|---|
| Bare / local | plain id from .mcp.json | dosu, linear, context7 |
| Claude.ai connector | claude_ai_<Name> | claude_ai_Dosu, claude_ai_Linear |
| Plugin-bundled | plugin_<plugin>_<server> | plugin_posthog_posthog, plugin_context7_context7 |
The mcp_server field is populated at ingest by classifyTool(), which splits the MCP naming convention mcp__<server>__<base> on the first __ separator and stores whatever raw string appears as <server>. No prefix normalization happens at the storage layer β the raw slug is the canonical identity .
The Collision Problem#
Prefix-stripping formatters are lossy by construction . A formatter that naively strips ^claude_ai_ and ^plugin_[^_]+_ will collapse dosu, claude_ai_Dosu, and any plugin variant onto a single label "Dosu". This reads to users as a duplicate-row bug rather than multiple real registrations. The problem is worst where call volumes are comparable (e.g., posthog 786 calls vs plugin_posthog_posthog 444 calls), because it looks like one server was arbitrarily split.
Two additional lossy edges in the same formatter shape :
- Slugs are hyphen-separated as often as underscore-separated (
claude-in-chrome,linear-server), so splitting on_alone leaves "Claude-in-chrome". - A bare UUID
mcp_servervalue should be preserved as-is; title-casing its segments produces ungreppable hex noise.
Set-Aware Formatting: mcpServerLabels()#
The solution is a set-aware formatter rather than a per-value one. mcpServerLabels() in src/mcp-names.ts takes an iterable of raw slugs and returns a Map<string, string> that:
- Computes the short display name for every slug via
formatMcpServer(). - Counts how many distinct slugs produce each short name.
- For unambiguous names (used by only one slug), returns the short name unchanged.
- For colliding names, appends the origin from
mcpServerOrigin()β e.g."Dosu (local)"vs"Dosu (connector)"vs"Posthog (posthog plugin)".
A per-value formatMcpServer() function also exists but is explicitly documented as unsafe when sibling servers are on screen . It remains useful only for isolated contexts where no collision is possible.
Origin labels#
mcpServerOrigin() returns:
"connector"β slug starts withclaude_ai_"local"β no recognized prefix (bare.mcp.jsonregistration)"plugin"β plugin-bundled where the plugin name matches the display name (avoids stutter like "Posthog (posthog plugin)")"<plugin> plugin"β plugin-bundled where plugin name differs from display name
UUID handling#
The regex OPAQUE_ID detects UUID-shaped slugs and returns them unchanged, since title-casing hex segments produces noise rather than a meaningful name.
Layering Constraint#
Display formatters live under src/ui/ and should stay there. The file src/ui/mcp-server.ts is a thin re-export wrapper that exposes formatMcpServer, mcpServerLabel, mcpServerLabels, and mcpServerOrigin to UI consumers without exposing the implementation module directly.
The server-side recommendations path (recommendations.ts) embeds the raw slug into signal titles (e.g., Heavy reliance on the "codex_apps" MCP server) and cannot use a formatter from src/ui/ without inverting the core-to-UI dependency direction. If server-side formatting is ever needed, the formatter belongs in src/tools.ts, which already owns classifyTool() and is where the mcp__<server>__<base> split happens . Note that src/ui/mcp-server.ts is documented as "shared with recommendations.ts" , meaning the naming module is already reachable from src/mcp-names.ts directly without going through the UI layer.
UI Integration#
In src/ui/main.tsx, mcpServerLabels() is called once per render across the union of all MCP servers visible in both the MCP and Tools tables . This ensures consistent naming across all display contexts β table cells, filter dropdowns, drilldown row labels, and the tool detail modal. Raw slugs are always preserved in title attributes, filter values, hrefs, and API payloads; only rendered text uses the formatted label .
Key Files#
| File | Role |
|---|---|
src/mcp-names.ts | Core formatting and disambiguation logic |
src/ui/mcp-server.ts | UI re-export wrapper |
src/tools.ts | classifyTool() β extracts mcp_server from tool name at ingest |
src/ui/main.tsx | Calls mcpServerLabels() and passes labels to all display contexts |