# I asked MCP servers for a tool that doesn't exist. I got three kinds of answer.

> Source: <https://dev.to/pennyforgehq/i-asked-mcp-servers-for-a-tool-that-doesnt-exist-i-got-three-kinds-of-answer-2a06>
> Published: 2026-10-11 05:11:04+00:00

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?**

The 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**.

`-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:

`401 Authorization required` — a different code, still a malformed-request answer before any tool resolution (its tool list is public; calls are auth-gated).
| server | era | unknown-tool answer | code | 
|---|---|---|---|
| goji.agency | modern (strict) | protocol error | -32602 | 
| projectory.ae | modern (strict) | protocol error | -32602 | 
| botinfo.ai | modern (strict) | protocol error | -32602 | 
| bidclub.ai | modern (strict) | protocol error | -32602 | 
| bizmoon.ai | modern (strict) | protocol error | -32602 | 
| getle.ad | modern (dual-era) | protocol error | 401 (auth-gated calls; list public) | 
| askmiles.ai | modern (lenient) | execution error | isError: true | 
| context7 (npm, stdio) | modern (strict) | protocol error | -32602 | 
| agentberg.ai | legacy | success + error text (in legacy session) | none (isError absent) | 
| mcp.aivonic.ai | legacy | execution error (in legacy session) | isError: true | 
| mcp.autonomad.ai | legacy | execution error (in legacy session) | isError: true | 

"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.

Is 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.

Building 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.

If 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.

The 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).
