An agent asks a tool to retrieve an invoice. The invoice does not exist. Should the server reject the protocol request, return an empty result, or send a message the agent can act on?
The distinction matters because “the request was handled” and “the task succeeded” are different outcomes. In MCP, a tool execution failure can arrive inside an ordinary JSON-RPC result. Your client needs to inspect that result, not just celebrate that the call returned.
The core idea
The MCP tools specification distinguishes two error channels. Protocol errors use the JSON-RPC error field for problems such as an unknown tool or malformed request. Tool execution errors use a tool result with isError: true for failures such as an API rejection, invalid business input, or a business-rule violation.
A useful design question is: who can fix this? A broken request envelope needs client or integration work. An invoice ID that does not identify a visible invoice may need corrected input from the user. Do not reduce both cases to “Something went wrong.”
How it works
Here is an illustrative tool failure response using the July 2026 protocol shape. It describes a hypothetical read-only invoice lookup, not a response captured from a live service:
{
"jsonrpc": "2.0",
"id": 17,
"result": {
"resultType": "complete",
"content": [
{
"type": "text",
"text": "No invoice is available for the supplied ID in this account. Confirm the invoice ID with the user before trying again."
}
],
"isError": true
}
}
The protocol exchange produced a result, but the lookup failed. The message should help the agent choose a next step without encouraging it to guess identifiers or search across accounts.
Compare that with an illustrative protocol error:
{
"jsonrpc": "2.0",
"id": 18,
"error": {
"code": -32602,
"message": "Unknown tool: lookup_invoice_typo"
}
}
The specification says clients should provide tool execution errors to models for self-correction. Clients may also expose protocol errors to models, so do not assume those messages are always hidden. Both channels deserve deliberate wording and sensitive-data controls.
Why it matters for AI agents
Design the client around separate outcomes: protocol rejection, tool failure, and successful tool result. Add a fourth application-level state when a consequential operation has an uncertain outcome. This is a suggested workflow model, not a new MCP error type.
For a read-only lookup, useful failure feedback might justify asking the user for a corrected identifier. For a write, require evidence before deciding whether to repeat it. A failed response is not sufficient evidence that no email was sent or no ticket was created. Our retry-safe write tool guide develops that separate design problem.
Keep failure recovery bounded. Set a retry budget, require a meaningful input change for validation failures, and stop when authorization or user judgment is needed. An agent that repeats the same rejected call has not recovered; it has made the incident noisier.
Tool errors in practice
Write failure messages as a small operational handoff. Include what failed, what is known about side effects, and an allowed next step. For example, prefer an application-authored message such as:
The destination address failed validation. No message was submitted. Ask the user to correct the address.
Use that wording only if your implementation can establish that nothing was submitted. For an uncertain submission, say that the outcome is unknown and direct the workflow to an authorized status check rather than a fresh send.
Do not expose credentials, raw upstream response bodies, private records, or stack traces to make an error “helpful.” Keep detailed diagnostics in access-controlled logs and return a safe reference identifier when support needs to investigate. Treat any upstream text as untrusted data, as discussed in tool results are data, not instructions.
Also inspect your SDK’s behavior rather than guessing what a thrown exception means. The official TypeScript SDK error guide documents conversion of ordinary tool-handler exceptions into isError results. It also documents exceptions to that behavior and different handling for resource, prompt, and completion callbacks. Check the release you actually deploy; an exception in application code is not, by itself, a portable declaration of the wire response.
Before connecting real accounts, test these cases with your local MCP debugging workflow:
- Invoke an unknown tool and inspect the protocol response.
- Supply validly shaped arguments that violate a business rule.
- Simulate an upstream failure and check the user-visible message.
- Confirm the client does not treat
isError: trueas task success. - Interrupt a write response and verify that recovery checks its outcome before resubmitting.
When evaluating a server from the AgentNDX directory, ask for failure examples alongside installation instructions. A documented happy path tells you less about unattended operation than a clear account of what happens when the backing service refuses a request.
FAQ
Does isError: true mean the MCP connection is broken?
No. It marks a tool execution failure inside a tool result. Diagnose the failure separately from transport health.
Should every error be retried?
No. Prefer corrected input for validation failures, an explicit stop for permission barriers, and outcome reconciliation for uncertain writes. Define retry policy in application code instead of leaving it entirely to the model.
Can I return an empty result when something fails?
Avoid disguising failure as an empty successful search. Reserve an empty result for a search that completed and found nothing; make actual failures explicit.
Is a good error message a security control?
No. Use it to guide recovery, not to enforce permissions. Keep authorization, approval, and account boundaries in the application and backing service.