Skip to content

plat 519

github-actions[bot] edited this page Oct 6, 2026 · 1 revision

PLAT-519: an MCP tool named like a platform tool stops the chat from starting

Field Value
State fixed on main
Priority P1
Product integrations
Area mcp
Summary . Fixed on main: every MCP tool is offered as <alias>__<tool> (mcpagent 69ba72f, pinned in the builder), not deployed, not verified live on a server.

State: fixed on main (2026-10-06): every MCP tool is offered as <alias>__<tool> (mcpagent 69ba72f, builder pin bumped in the same commit as this ticket update). Not deployed, not verified live on a server. The earlier stopgap (mcpagent b619356) stays as a safety net. P1.

Found: 2026-10-05, Excellence, Code project linkedscrapper: "Failed to finalize agent definition: finalize immutable agent definition: register direct tool "delete_function": tool name "delete_function" is already registered by MCP server "ue03d5aec…__neon_46fb2cad6010"". A Neon MCP connection on that project exposes a tool named delete_function; the platform has its own delete_function (Crew/Code functions, crew_functions.go). mcpagent's registerDirectTool (agent/agent.go) rejected any direct tool whose name an MCP server already used, and that failed the whole agent, so the chat could not start at all. Any connected server with a tool named like one of ours does the same.

Stopgap (owner decision, option 3): the platform tool is used and the MCP tool of that name is hidden, with a [TOOL_SHADOW] warning in the log (canonicalToolRegistry.removeMCP). The chat starts; that one MCP tool is not callable. One test pins it (agent/tool_registry_uniqueness_test.go).

Real fix (owner decision 2026-10-06: prefix EVERY MCP tool, not only on a clash) — built: in the platform's own agent loop every MCP tool is registered and offered as <alias>__<tool>; platform (direct/virtual) tools keep their names. Claude Code's own mcp__server__tool names for its plugins are not ours and are untouched.

  • Alias rule (agent/tool_name.go in mcpagent): the connection's server name with the personal-store prefix u<32 hex>__ and a trailing _<8+ hex> connection id removed, lowercased, sanitized to [a-z0-9_-] (no __ inside), at most 24 characters, mcp if empty. When two connections in one agent would share an alias, each gets _<first 4 chars of its connection id> (or of a hash of the server name when it has no id), 8 hash characters if even that collides. So ue03d5aec…__neon_46fb2cad6010 → neon alone, or neon_46fb beside a second Neon. A tool name that sanitizing changed gets a 6-char hash suffix; a name over 64 characters is cut and ends in an 8-char hash of the real server + tool. All names match ^[a-zA-Z0-9_-]{1,64}$.
  • Routing back to the real server + tool: NewAgentConnectionWithSession builds the visible names and a visible → real map (Agent.mcpToolRealNames); toolToServer is keyed by the visible name. The sequential loop (conversation.go), the parallel runner (parallel_tool_execution.go) and the broken-pipe retry (error_handler.go) call the server with the real name (realMCPToolName, which replaced actualMCPToolName). The canonical registry records the real name per MCP tool (MCPToolName). Two connections of the same server used to lose the second one's tools as "duplicates"; now both are callable.
  • Unchanged: saved server:tool selections and selected_tools filtering (matched on the real name); the shared code-execution registry (codeexec/registry.go, fed real names); the CLI bridge executor/handlers.go ($MCP_MCP/<server>/<tool> with real names, no change needed); the mcpbridge native tool list (it never carries MCP tools).
  • Code execution / CLI agents (judgment call): the tool index in the system prompt still lists each server's real tool names under its $MCP_MCP/<server>/{tool} route, so bridge paths do not change. search_tools returns the prefixed name. get_api_spec accepts the prefixed name, or a real name when only one server has it and no platform tool takes it, or a real name plus server_name; the bare name of a clashing platform tool stays the platform tool. Specs always show the real route. The spec cache key now includes server_name. One line in guidance/templates/system/mcp-bridge.md says search returns <connection>__<tool> and the returned route is called as given.
  • Stopgap: [TOOL_SHADOW] / removeMCP stays as a safety net; it can now only fire for a platform tool named exactly like a prefixed MCP tool.
  • Guidance scan: no guidance template or mcpagent prompt hard-codes MCP tool names; nothing else to rename.

Verified: by test, not live on a server. mcpagent agent/mcp_tool_prefix_e2e_test.go starts two real stdio MCP servers (the test binary itself, named like two Neon connections of one person, both with delete_function) plus a platform delete_function, and drives one turn through the real agent loop and the real OpenAI adapter against a local OpenAI-compatible endpoint, sequential and parallel: all three calls run, each reaches its own server with the real tool name, every offered name is valid; get_api_spec by prefixed name, by real name + server_name, and by bare name (platform tool) gives the right route. agent/tool_name_test.go pins the alias and 64-character rules. The stopgap test still passes. The builder builds with the new pin. Builder cmd/server, pkg/orchestrator and pkg/agentwrapper with the 69ba72f pin: 16 failures, all also failing with the 78db549 pin (pre-existing); one more (TestVaultChatCreatesMissingUserWorkspaceBeforeCLILaunch) failed once in the full run and passes alone 3/3 (flaky, not from this change).

Left: deploy (owner's go), then confirm live: linkedscrapper on Excellence starts and its Neon tools answer as neon__…, and the person with two Neon connections sees both (neon_xxxx__…).

Impact scan (2026-10-06, read-only, all servers): nothing saved depends on bare MCP tool names.

  • Local: only legacy gmail / google_sheets (dormant workflows) and the built-in workspace_* categories; one plan step text gmail.send_email (dormant instagram workflow).
  • Confida: Linear, Notion, Resend; filters only server:*; no plan, skill or note names a tool. The bridge URL $MCP_MCP/Linear/list_issues appears only in CLI session transcripts.
  • Excellence: personal connections Neon (x3, two for one user: identical tool names), Context7, DeepWiki; no saved text names their tools.
  • RTS: Notion, Jam, an official Notion connection; one narrow filter Notion:notion-fetch / Notion:notion-search on rtsprreviweer, stored as server:tool, unaffected.

Tests at the pin bump: the mcpagent registry tests pass; 8 builder tests in cmd/server and pkg/orchestrator fail with both the old and the new pin (pre-existing, e.g. PLAT-537); none fails only with the new pin.

Register notes

PLAT-519, P1. Fixed on main: every MCP tool is offered as <alias>__<tool> (mcpagent 69ba72f, pinned in the builder), not deployed, not verified live on a server.

Clone this wiki locally