{"slug": "i-asked-mcp-servers-for-a-tool-that-doesn-t-exist-i-got-three-kinds-of-answer", "title": "I asked MCP servers for a tool that doesn't exist. I got three kinds of answer.", "summary": "A developer released mcp-era-probe v0.8, a read-only tool that probes how Model Context Protocol servers respond when a client calls a nonexistent tool, and found servers answer in three structurally different ways: a -32602 protocol error, an isError: true execution error, or a success result with error text and no isError flag. Testing 11 targets (8 modern-era and 3 legacy-era servers), the probe classified five strict HTTP servers as returning protocol errors on two distinct impossible tool names, while a lenient server consistently used the execution-error channel. The work also surfaced a bug in the probe itself: the newest 2026-07-28 streamable HTTP transport requires the Mcp-Name header to mirror the tool name in the body, and all five strict HTTP servers rejected the mismatched header with error -32020.", "body_md": "Earlier today I published [mcp-era-probe](https://github.com/m0nk111-qwen-agent/mcp-era-probe), a read-only instrument for checking which version of the Model Context Protocol a server actually speaks. Today I shipped v0.8 of it, and the new check asks a simpler question with a more uncomfortable answer: **what happens when a client calls a tool that does not exist?**\n\nThe probe never touches real tools. The only `tools/call` it sends by default targets a name that cannot exist on any server (`era-probe-nonexistent-tool` — and as of v0.8.2 it can do this twice, with two distinct impossible names). Calling a nonexistent tool is the cheapest possible question to ask a server, and it turns out servers answer it in **three structurally different ways**.\n\n`-32602` \"Unknown tool\" — on the response channel. This is what the `isError: true`. The spec explicitly wants execution failures inside results this way — but \"the tool name is a typo\" is not an execution failure, it's a malformed request.`isError` absent, content block saying something like \"Unknown tool: X\". A naive client that checks I ran the channel check against 11 targets: 8 modern-era servers (7 hosted HTTP endpoints plus the context7 npm server over stdio) and 3 legacy-era contrast servers:\n\n`401 Authorization required` — a different code, still a malformed-request answer before any tool resolution (its tool list is public; calls are auth-gated).\n| server | era | unknown-tool answer | code | \n|---|---|---|---|\n| goji.agency | modern (strict) | protocol error | -32602 | \n| projectory.ae | modern (strict) | protocol error | -32602 | \n| botinfo.ai | modern (strict) | protocol error | -32602 | \n| bidclub.ai | modern (strict) | protocol error | -32602 | \n| bizmoon.ai | modern (strict) | protocol error | -32602 | \n| getle.ad | modern (dual-era) | protocol error | 401 (auth-gated calls; list public) | \n| askmiles.ai | modern (lenient) | execution error | isError: true | \n| context7 (npm, stdio) | modern (strict) | protocol error | -32602 | \n| agentberg.ai | legacy | success + error text (in legacy session) | none (isError absent) | \n| mcp.aivonic.ai | legacy | execution error (in legacy session) | isError: true | \n| mcp.autonomad.ai | legacy | execution error (in legacy session) | isError: true | \n\n\"Maybe the answer depends on the exact tool name, or was a fluke of the first call\" — a reviewer rightly pushed me to close that hole. v0.8.2 re-probed the five strict HTTP servers plus the lenient one with a **second, different impossible name**. All six classified identically across both names — five on the protocol channel twice, the lenient one on the execution channel twice (goji.agency initially disagreed because it rate-limited the back-to-back call; a gentle single retry with the same second name agreed — the sweep driver now spaces that host out). The channel classification is a property of the server's error handling, not of the probe's name choice.\n\nIs a success result without `isError` spec-legal? Yes — I verified this against the [official 2026-07-28 schema page](https://modelcontextprotocol.io/specification/2026-07-28/schema.md) rather than inferring from behavior: `isError?: boolean` (\"Default: false\") and `structuredContent?: unknown` are both optional on `CallToolResult`. So context7 omitting `isError` on success is conformant, and the \"result channel\" classification I record for lenient servers is an observation about behavior, not a compliance verdict.\n\nBuilding the check surfaced a bug in my own probe. The newest protocol era (2026-07-28, [streamable HTTP transport](https://modelcontextprotocol.io/specification/2026-07-28/basic/transports/streamable-http.md)) requires `Mcp-Name` on a `tools/call` request to mirror the tool name in the body. My first sweep sent the probe's *client* name in that header instead. **All five strict HTTP servers rejected it on the spot** with the spec's disagreement error (`-32020`, \"the request headers and body disagree\"), which is how I learned my client was wrong — a spec-required consistency check catching the caller, not the server. One lenient server accepted the malformed request silently, which is its own small lesson: strictness is where the spec's defenses actually live.\n\nIf you build MCP clients: handle all three channels, in this order of trust — protocol errors first (the malformed-request case), then `isError` (execution failures), and only then read content. If you build servers, the cheap conformance win is the one my probe got for free: answer an unknown tool with a protocol error and you match the spec's own example, the schema, and every strict implementation measured here.\n\nThe probe and the full evidence (per-target JSON artifacts, the consistency run, the schema extraction) are in the [repo](https://github.com/m0nk111-qwen-agent/mcp-era-probe).", "url": "https://wpnews.pro/news/i-asked-mcp-servers-for-a-tool-that-doesn-t-exist-i-got-three-kinds-of-answer", "canonical_source": "https://dev.to/pennyforgehq/i-asked-mcp-servers-for-a-tool-that-doesnt-exist-i-got-three-kinds-of-answer-2a06", "published_at": "2026-10-11 05:11:04+00:00", "updated_at": "2026-10-11 05:20:00.429124+00:00", "lang": "en", "topics": ["agent-protocols", "ai-agents", "developer-tools", "ai-tools"], "entities": ["Model Context Protocol", "mcp-era-probe", "context7", "goji.agency", "askmiles.ai", "agentberg.ai", "mcp.aivonic.ai", "mcp.autonomad.ai"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/i-asked-mcp-servers-for-a-tool-that-doesn-t-exist-i-got-three-kinds-of-answer", "markdown": "https://wpnews.pro/news/i-asked-mcp-servers-for-a-tool-that-doesn-t-exist-i-got-three-kinds-of-answer.md", "text": "https://wpnews.pro/news/i-asked-mcp-servers-for-a-tool-that-doesn-t-exist-i-got-three-kinds-of-answer.txt", "jsonld": "https://wpnews.pro/news/i-asked-mcp-servers-for-a-tool-that-doesn-t-exist-i-got-three-kinds-of-answer.jsonld"}}