What to fix first
-
1
Unknown tool is reported
calling a non-existent tool returned success — return -32602 or an isError result
Calling a tool that does not exist returned success. A client has no way to tell a typo from a working call, and a model that hallucinates a tool name is told it was right.
How to fix it
- Return -32601 for an unknown method or tool`Method not found`. A model that receives an error learns the tool does not exist; one that receives success learns the opposite.
-
2
Malformed JSON is rejected
HTTP 200 for a truncated body — return 400 or -32700
A truncated or malformed request body was answered with a success status. A client cannot distinguish that from a real answer, and a proxy or a retry that corrupts a body will look like it worked.
How to fix it
- Reject a body that does not parseReturn HTTP 400, or the JSON-RPC `Parse error` code -32700. Either tells the caller what happened; 200 does not.
- Check the framing before the handlerThis usually belongs in the transport layer rather than in any single tool, which is why it is easy to miss.
-
3
tools/call without a name is rejected
a tools/call with no name succeeded — validate params and return -32602
A request missing a required parameter was answered as though it were valid. The parameter list is part of the contract; not enforcing it means the contract is advisory.
How to fix it
- Return -32602 for bad params`Invalid params` is the specified answer. Include which parameter and why in the error message — that message reaches the model.
-
4
Tools reject missing required arguments
3 tool(s) accepted a call with a required argument omitted — validate arguments against inputSchema before executing
The tool accepted a call with a required argument missing. The schema said the argument was required and the server did not enforce it, which means the schema is describing an intention rather than a contract. A model that gets a plausible answer to an incomplete call has no way to learn it made a mistake.
How to fix it
- Validate arguments against your own schemaBefore the tool body runs, check the arguments against the `inputSchema` you published. Most SDKs will do this for you if you let them.
- Fail with -32602Return the JSON-RPC `Invalid params` error rather than a success with a guess in it. An error the model can read is a correction; a plausible wrong answer is not.
-
5
Protocol generation
speaks a handshake revision: it uses initialize and Mcp-Session-Id, which 2026-07-28 removed — the current revision is 2026-07-28: requests carry their own protocol version, client identity and capabilities in _meta, sessions are gone, and Mcp-Method/Mcp-Name let a gateway route without parsing the body. Agents on current clients will keep working through the compatibility rules, but plan the move
MCP has two generations in the field. The older one opens with an `initialize` request, and the server answers with an `Mcp-Session-Id` that both sides then carry for the life of the connection. The 2026-07-28 revision removes that entirely: there is no handshake and no session. Every request instead carries its own context, so a server can answer any request without remembering the one before it.
How to fix it
- Stop depending on initializeRemove the code that expects an initialization step, and anything that stores or looks up per-session state keyed by `Mcp-Session-Id`. On the current revision neither arrives.
- Read the context out of _metaEach request carries the protocol version, the client's identity and its capabilities in a `_meta` object on the request. That is where the values you used to take from the initialize result now live.
- Route on the headers, not the body`Mcp-Method` and `Mcp-Name` mirror the method and the target name outside the JSON. A gateway can route on them without parsing the payload — but only if your server validates that they agree with the body, which `protocol.routing_headers` checks.
If you build on an official SDK, updating the package usually carries the whole shift for you.
What scout checked
Reachable, plain HTTP on this machine
Open server, no credentials required
Not needed
acme-crm 2.4.0, protocol 2025-11-25 · 1 thing to improve
speaks a handshake revision: it uses initialize and Mcp-Session-Id, which 2026-07-28 removed
the current revision is 2026-07-28: requests carry their own protocol version, client identity and capabilities in _meta, sessions are gone, and Mcp-Method/Mcp-Name let a gateway route without parsing the body. Agents on current clients will keep working through the compatibility rules, but plan the move
Mostly conformant · 3 issues, 1 thing to improve
HTTP 200 for a truncated body
return 400 or -32700
a tools/call with no name succeeded
validate params and return -32602
calling a non-existent tool returned success
return -32602 or an isError result
server answered a request carrying a session id it never issued
reject unknown session ids with 404
3 tools · 1 thing to improve
under 20 characters: list_orders
describe what the tool does, when to use it, and what it returns
3 of 3 tools ran cleanly · 1 issue
3 tool(s) accepted a call with a required argument omitted
validate arguments against inputSchema before executing
Fast, slowest tool answers in 0.2ms
Recovers from a lost session
What an agent sees
| Tool | Description | Behaviour | Contract |
|---|---|---|---|
| find_customer | Find a customer by email address or account number. Returns the account record. | read only | outputSchema |
| list_orders | Lists orders. | read only | none |
| summarise_account | Produce a short written summary of an account, suitable for an agent to relay to a customer. | read only | none |
How fast it answers
| Tool | Cold | p50 | p95 | Max | Errors |
|---|---|---|---|---|---|
| find_customer | 0.2ms | 0.1ms | 0.2ms | 0.2ms | 0 |
| list_orders | 0.2ms | 0.2ms | 0.2ms | 0.2ms | 0 |
| summarise_account | 0.2ms | 0.2ms | 0.2ms | 0.2ms | 0 |