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 ## 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. 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 |