A documented MCP server schema specifies four parameters. The agent confidently passed a fifth (`max_results: 10`) on every call. The server returned a 500, the agent retried with the same payload, and the loop produced 200+ failed calls before the parent timeout fired.
What happened
The MCP server notes-search@1.0.4 exposed a search_notes tool with this schema:
{
"name": "search_notes",
"input_schema": {
"type": "object",
"properties": {
"query": { "type": "string" },
"since": { "type": "string", "format": "date" },
"until": { "type": "string", "format": "date" },
"tag": { "type": "string" }
},
"required": ["query"]
}
}
The agent's call:
{ "query": "Q4 planning", "max_results": 10 }
max_results doesn't exist in the schema. The MCP server's strict validator rejected the call:
Error: unrecognized argument: max_results
The agent's reasoning step interpreted this as a transient error and retried the same payload. 200 retries in 4 minutes.
Diagnosis
Two compounding issues:
1. **The model's training data has many APIs that do accept max_results. Pagination is a common pattern. When the model needs to limit output, "add max_results" is a high-prior generation. It's not really "hallucinating" — it's a pattern-match that happened to be wrong here.
2. Strict MCP validators throw on unknown keys. This is correct behavior (catches typos, prevents drift) but the error message didn't say "this argument is not in the schema; valid arguments are: query, since, until, tag." It just said "unrecognized argument."
The fix
Server side: Improve the error message to enumerate the valid schema:
throw new Error(
Unknown argument: ${unknownKey}. +
Valid arguments: ${Object.keys(schema.properties).join(", ")}.
);
After this change, the model self-corrected on the second attempt by re-reading the schema.
Client side:** Wrap the MCP client to drop unknown keys before sending, with a warning log:
function pruneToSchema(args, schema) {
const allowed = new Set(Object.keys(schema.properties));
const dropped = [];
const cleaned = {};
for (const [k, v] of Object.entries(args)) {
if (allowed.has(k)) cleaned[k] = v;
else dropped.push(k);
}
if (dropped.length) console.warn("Dropped unknown args:", dropped);
return cleaned;
}
This is defense-in-depth — server validation should still happen, but the client shouldn't be the one wedging in a retry loop.
Takeaway
Schema-strict tool servers must surface the schema in their error messages — otherwise models can't self-correct. And clients calling untrusted-shape tools should sanitize input against the declared schema before sending.