Skip to content

fix(mcp): give clashing MCP tool names a server prefix; clear build warnings - #220

Merged
fuseraft merged 2 commits into
mainfrom
fix/mcp-tool-name-clashes
Nov 12, 2026
Merged

fuseraft merged 2 commits into
mainfrom
fix/mcp-tool-name-clashes

Conversation

@fuseraft

@fuseraft fuseraft commented Nov 12, 2026 •

Copy link
Copy Markdown
Owner

Why

When two MCP servers expose a tool with the same name, every request fails with a 400: tools: Tool names must be unique. The same happens when a server's tool shares a name with a built-in one, such as read_file. #219 makes this likelier: a server the launcher provides and one you saved can be the same server under different names. I hit it while testing the VS Code MCP view.

What

  • McpToolNames.Disambiguate: an MCP tool whose name another tool also has is offered as <server>_<tool>, such as Sql_query_database.
    • Built-in tools always keep their names, so @mentions and other code that looks tools up by name still finds them.
    • Characters a tool name can't hold are replaced with _. Names stay within 64 characters, and a name that's already taken gets _2.
  • The rename is a DelegatingAIFunction that changes only the name. Calls, schema and GetService go to the server's own tool, so REPL approval gates and read-only hints still apply.
  • Applied where tool lists reach a model:
    • the REPL's GetActiveTools, which also feeds ChatOptions;
    • each orchestration agent's tools (ConvertPluginTools);
    • a subagent's custom SubagentPlugins.
  • Docs: a note in docs/mcp.md.

Also: the build's three warnings

The build now has none.

  • CS4014 in ReplTurn: the auto-title task (ReplAutoTitle.MaybeStart) runs alongside the next prompt on purpose and handles its own errors. It's discarded explicitly (_ =) instead of awaited, so behavior doesn't change.
  • CA2022 in SseEventIdleTimeoutStreamTests: the byte-idle test expects the read to throw, so it uses ReadExactlyAsync instead of ignoring ReadAsync's count. It still goes through the stream's ReadAsync(Memory<byte>).
  • CS8619 in McpSessionManagerStderrTests: the env helper now takes Dictionary<string, string?>, matching McpServerConfig.Env.

Testing

  • McpToolNamesTests:
    • no clash returns the same instances;
    • a server-vs-server clash and a server-vs-built-in clash;
    • a renamed tool invokes its own tool and still answers GetService;
    • unsafe server names, taken names and long names.
  • AgentToolResolverTests covers an agent's tools and a subagent's tools. ReplCommandsMcpServerLifecycleTests covers GetActiveTools and ChatOptions. Each fails if its wiring is removed.
  • Live test, a bridge chat with the same sql-mcp connected twice (saved and launcher-provided):
    • The model saw Local_list_databases and Sql_list_databases.
    • It called Sql_list_databases and got the server's answer, where before every turn failed.

/mcp still lists each server's tools under their own names.

- Two MCP servers with a tool of the same name, or one shadowing a built-in,
  made the provider reject every request ("Tool names must be unique")
- Clashing MCP tools are offered as <server>_<tool>; built-ins keep their
  names so @mentions and tool-specific code still find them
- The rename only changes the name, so calls, approval gates and read-only
  hints still reach the server's own tool
- CS4014: the auto-title task is meant to run alongside the next prompt, so
  it's discarded explicitly instead of awaited; it handles its own errors
- CA2022: the idle-timeout test expects the read to throw, so it uses
  ReadExactlyAsync rather than ignoring ReadAsync's count
- CS8619: the stderr test's env helper takes the nullable values Env holds
@fuseraft fuseraft changed the title fix(mcp): give clashing MCP tool names a server prefix fix(mcp): give clashing MCP tool names a server prefix; clear build warnings Nov 12, 2026
@fuseraft
fuseraft merged commit cd11665 into main Nov 12, 2026
9 checks passed
@fuseraft
fuseraft deleted the fix/mcp-tool-name-clashes branch November 12, 2026 05:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant