docs: document best practice for handling argument errors in MCP tools (#891)

Resolves #356

Clarify the two-tier error model:
- Recoverable tool errors: use CallToolResult with isError(true)
- Protocol-level errors: throw McpError / let exceptions propagate as JSON-RPC errors

Signed-off-by: Daniel Garnier-Moiroux <git@garnier.wf>
This commit is contained in:
Radesh Govind 2026-04-02 10:07:04 +01:00 • committed by GitHub
commit cd2c21c1c8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -795,3 +795,42 @@ Supported logging levels (in order of increasing severity): DEBUG (0), INFO (1),
## Error Handling
The SDK provides comprehensive error handling through the McpError class, covering protocol compatibility, transport communication, JSON-RPC messaging, tool execution, resource management, prompt handling, timeouts, and connection issues. This unified error handling approach ensures consistent and reliable error management across both synchronous and asynchronous operations.
### Error Handling in Tool Implementations
#### Two Tiers of Errors
MCP distinguishes between two categories of errors in tool execution:
**1. Tool-Level Errors (Recoverable by the LLM)**
Use `CallToolResult` with `isError(true)` for validation failures, missing arguments, or domain errors the LLM can act on and retry.
```java
// Example: Domain validation failure (e.g., invalid email format)
if (!emailAddress.matches("^[A-Za-z0-9+_.-]+@(.+)$")) {
return CallToolResult.builder()
.content(List.of(new McpSchema.TextContent("Invalid argument: 'email' must be a valid email address.")))
.isError(true)
.build();
}
```
The LLM receives this as part of the normal tool response and can self-correct in a subsequent interaction.
**2. Protocol-Level Errors (Unrecoverable)**
Uncaught exceptions from a tool handler are mapped to a JSON-RPC error response. Use this only for truly unexpected failures (e.g., infrastructure errors such as DB timeout), not for input validation.
```java
// This propagates as a JSON-RPC error — use sparingly
throw new McpError(McpSchema.ErrorCodes.INTERNAL_ERROR, "Unexpected failure");
```
#### Decision Guide
| Situation | Approach |
|------------------------------------|---------------------------------------|
| Domain validation failure | `CallToolResult` with `isError=true` |
| Infrastructure / unexpected error | Throw `McpError` or let it propagate |
| Partial success with a warning | `CallToolResult` with warning in text |