MCP: an unknown tool name gets "did you mean <nearest tool>?"
Fixes an internal report: MCP servers answer an unknown tool with no nearest name.
What
An agent that mistypes a tool name (team_sned, workspace_lst) got a bare unknown tool: NAME from every MCP server we ship, and had to list the tools again to find the right one. Now each server answers unknown tool: NAME; did you mean TOOL? when one of its own tools is close, and keeps the bare text when nothing is.
- New
acpmux::mcp_hint(unknown_tool,nearest,names): one shared nearest-name helper. A name is close when, ignoring case, it is within an edit distance (a swap of two neighbouring letters is one edit) of 1 for up to 3 letters ormax(2, len / 3)for longer, or when it is a prefix (3+ letters) of a tool name. The closest wins; a tie goes to the first in list order. A name is never its own hint. - acpmux's MCP servers (
acpmux_mcp,forum_mcp,offer_mcp,secret_mcp,subsession_mcp, and the bg tools' fallback, which the server's own routing makes unreachable) pass the names from their owntools()/TOOLS, so the hint cannot drift from what they list. cmux mcp(cli/mcp.rs) passes the names it lists to this chat (allowed tools only; pluscmux_docs/cmux_execin code mode).list()is split into a refresh andlisted(), so the hint uses the actions and proxied tools as last read and asks the app nothing again.list()itself behaves as before.
No other behaviour changes: the error codes, the isError results and every known-tool path are unchanged.
Overlap: #26625 adds a similar hint inside cmux_exec scripts only (code_mode::nearest_names). If both land, a script call to a mistyped tool would carry two hints; whichever lands second should drop one.
Test
Red commit 58cd1ab66c3 adds the cases; fix commit follows.
cd cmux-tui && cargo test -p acpmux --lib acpmux_mcp mcp_hint:an_unknown_session_or_a_missing_argument_is_an_error_the_agent_readsnow expectsunknown tool: team_sned; did you mean team_send?(and stillunknown tool: nope), plus the helper's unit tests.cd cmux-tui && cargo test -p cmux-tui --lib json_rpc_lifecycle_and_errors:workspace_lstgetsunknown tool: workspace_lst; did you mean workspace_list?;no_such_toolkeeps the bare text.
Red (the red commit's cmux-tui/ tree, on the fleet Linux runner):
cargo test -p acpmux --lib -- acpmux_mcp: 7 passed, 1 failed:an_unknown_session_or_a_missing_argument_is_an_error_the_agent_reads, left"unknown tool: team_sned", right"unknown tool: team_sned; did you mean team_send?".cargo test -p cmux-tui --lib -- cli::mcp::tests::json_rpc_lifecycle_and_errors: failed at tests.rs:817, left"unknown tool: workspace_lst", right"unknown tool: workspace_lst; did you mean workspace_list?".
Green (head 80d2b70b97a5):
cargo test -p acpmux --lib -- acpmux_mcp mcp_hint: 14 passed (8 server tests, 6 helper tests).cargo test -p cmux-tui --lib -- cli::mcp::tests::json_rpc_lifecycle_and_errors cli::mcp::tests::code_mode: 4 passed.cargo check -p cmux-tui --all-targets: clean.
Observed / Not run
- Observed: the red and green runs above, on the fleet at the PR head;
prepush-gate.pyreports no new violations; rustfmt clean on the changed files. - Not run: the full acpmux and cmux-tui suites, and clippy. The forum, offer, secret and subsession servers have no daemon-backed unknown-tool test; their change is the same one-line call to the helper with their own
tools(), read but not run end to end.
Changelog
- Changed: MCP servers answer a mistyped tool name with the nearest tool name ("did you mean ...?").
risk: low (error text only).
Agent session: agent session
🤖 Generated with Claude Code