shareAI-lab/learn-claude-code · error · ValueError
MCP tool name collision after normalization: {prefixed!r} ma
Error message
MCP tool name collision after normalization: {prefixed!r} maps both {origins[prefixed]} and {origin} What it means
Normalization (disallowed chars → '_') plus the mcp__server__tool prefix can make two distinct tools collide: e.g. 'my-tool' and 'my_tool' on the same server both become mcp__srv__my_tool, or a server/tool pair that prefixes to a built-in tool name. The catalog build detects the collision against the origins map (which pre-seeds built-in tools) and raises with both conflicting origins.
Source
Thrown at s14_mcp_plugin/code.py:334
tools = list(BUILTIN_TOOLS)
handlers = dict(BUILTIN_HANDLERS)
policies: dict[str, str] = {}
origins = {
tool["name"]: f"built-in tool {tool['name']!r}"
for tool in tools
}
for server_name, server in mcp_clients.items():
safe_server = normalize_mcp_name(server_name)
for tool_def in server.tools:
raw_name = tool_def["name"]
safe_tool = normalize_mcp_name(raw_name)
prefixed = f"mcp__{safe_server}__{safe_tool}"
if len(prefixed) > 64:
raise ValueError(f"MCP tool name is longer than 64 characters: {prefixed}")
origin = f"MCP tool {server_name!r}/{raw_name!r}"
if prefixed in origins:
raise ValueError(
"MCP tool name collision after normalization: "
f"{prefixed!r} maps both {origins[prefixed]} and {origin}"
)
schema = tool_def.get("inputSchema", {})
if not isinstance(schema, dict) or schema.get("type", "object") != "object":
raise ValueError(f"Invalid input schema for {origin}")
origins[prefixed] = origin
tools.append({
"name": prefixed,
"description": tool_def.get("description", ""),
"input_schema": schema,
})
handlers[prefixed] = (
lambda *, client=server, tool=raw_name, **kwargs:
client.call_tool(tool, kwargs)
)
policies[prefixed] = MCP_HOST_POLICY.get(
(server_name, raw_name), "confirm"View on GitHub (pinned to 985456f4ad)
Solutions
- Rename one of the colliding servers or tools so their normalized forms differ (change letters, not just punctuation).
- Check the error message: it names both origins — rename the less important one.
- If mirroring built-ins, drop or prefix the MCP variant (e.g. tool name 'ext_search').
Example fix
// before
servers = {'my.server': srv_a, 'my_server': srv_b} // both -> mcp__my_server__*
// after
servers = {'myserver_docs': srv_a, 'myserver_ops': srv_b} Defensive patterns
Strategy: validation
Validate before calling
def catalog_names_unique(existing: set[str], server: str, tools: list[str]) -> list[str]:
import re
norm = lambda s: re.sub(r'[^A-Za-z0-9_-]', '_', s)
clashes = []
for t in tools:
p = f'mcp__{norm(server)}__{norm(t)}'
if p in existing:
clashes.append(p)
existing.add(p)
return clashes # empty means safe to register Try / catch
try:
build_catalog(tools, mcp_clients)
except ValueError as exc:
if 'collision after normalization' in str(exc):
# error names both origins; rename the less critical one and rebuild
logging.error('MCP name collision: %s', exc)
raise SystemExit(2) from exc
raise Prevention
- Make server names differ by letters, not punctuation (normalization collapses punctuation to '_').
- Avoid tool names that differ only in '-' vs '_' within a server.
- In multi-server setups, namespace tool names per server to avoid built-in collisions.
When it happens
Trigger: Server 'my.server' with tool 'search' vs server 'my_server' with tool 'search' → both 'mcp__my_server__search'; tools 'a-b' and 'a_b' on one server; an MCP tool that normalizes to a name already used by a built-in tool.
Common situations: Registering multiple MCP servers whose names differ only in punctuation; tools whose raw names differ only by case/punctuation; mirroring a tool that collides with built-ins.
Related errors
- Every MCP tool needs a non-empty name
- Duplicate MCP tool name on server {self.name!r}
- Missing MCP handlers: {', '.join(missing)}
- MCP tool name is longer than 64 characters: {prefixed}
- Invalid input schema for {origin}
AI-assisted analysis of shareAI-lab/learn-claude-code@985456f4ad (2026-08-14).
Data as JSON: /api/errors/a0523f511bb217c2.
Report an issue: GitHub.