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
A foreign Origin is rejected
served a request from Origin https://passmcp-origin-probe.invalid on loopback: any web page the user opens can drive this server through DNS rebinding — reject requests whose Origin is not one you expect with 403; the Streamable HTTP transport requires it, and a server on this machine or network is exactly what DNS rebinding reaches
The server answered a request whose Origin header named a site it has no reason to trust. Through DNS rebinding, any web page the user opens can point a hostname at this server's address and send it requests from their browser, with whatever access that network position gives.
How to fix it
- Check Origin on every request, and refuse with 403Compare it against an allowlist of the origins your clients actually use; a request with no Origin is a non-browser client and is unaffected. The Streamable HTTP transport requires it.
- Bind a local server to 127.0.0.1, not 0.0.0.0That keeps the rest of the network out. It does not keep a browser on the same machine out, which is why the Origin check is needed as well.
A public endpoint only warns: the rule still applies, but DNS rebinding is an attack on what a browser can reach that the attacker cannot, which a public server is not.
-
3
Parameters are described well enough to use
3 parameters across 3 tools; 1 parameter required and undescribed: list_orders.customer_id — describe every required parameter: the model has to supply it and a bare type tells it nothing, so it guesses
A parameter with a type and no description tells the model nothing about what to put in it. The call is still well-formed, so the protocol is satisfied and nothing fails a conformance check — the model simply guesses, and the failure surfaces later as a wrong answer rather than as an error. A required parameter is the acute case: the model has to supply it.
How to fix it
- Describe every required parameter firstOne sentence saying what the value is and where the caller gets it. `id` is not a description; "the account id from list_accounts" is, and it tells the model which other tool to call first.
- Constrain what you canAn `enum` removes a whole class of guesses. A `pattern`, a `format`, a numeric bound or a `default` each narrow the space the model is choosing from, and all of them are cheaper than the prose that would otherwise be needed.
- Add an example where the shape is not obvious`examples` on a property costs a few tokens and removes the most common category of malformed call: the right type in the wrong shape.
-
4
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.
-
5
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.
What passmcp checked
Reachable, plain HTTP on this machine
Open server, no credentials required
Credentials accepted
3 tools callable with no credentials, all of them read-only
an open catalogue still discloses what the system does — tool names and descriptions map your internal capabilities for anyone who asks
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, 3 things to improve
error -32601 rather than -32700
use -32700 for a body that does not parse
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
served a request from Origin https://passmcp-origin-probe.invalid on loopback: any web page the user opens can drive this server through DNS rebinding
reject requests whose Origin is not one you expect with 403; the Streamable HTTP transport requires it, and a server on this machine or network is exactly what DNS rebinding reaches
served a request without Mcp-Session-Id, although the server issued one at initialize
answer a request that lacks the session id with 400 Bad Request, or stop issuing one if the server does not need sessions
3 tools · 1 issue, 1 thing to improve
under 20 characters: list_orders
describe what the tool does, when to use it, and what it returns
3 parameters across 3 tools; 1 parameter required and undescribed: list_orders.customer_id
describe every required parameter: the model has to supply it and a bare type tells it nothing, so it guesses
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.3ms | 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 |