MP-201a · Module 1
Input Validation & Error Handling
4 min read
JSON Schema validation is your first line of defense, but it is not your last. The MCP SDK validates incoming arguments against your inputSchema before your handler runs — if a required field is missing or a type is wrong, the call is rejected with a standard error. But schema validation only catches structural problems. Business logic validation — "this customer ID does not exist," "the date range exceeds 90 days," "you do not have permission to access this resource" — must happen inside your handler.
When a tool fails, the error message goes back to the LLM. This means your error messages are prompts — the LLM reads them to decide what to do next. A generic "Error: something went wrong" gives the model nothing to work with. A specific "Error: customer_id 'CUS-999' not found. Valid customer IDs start with 'CUS-' followed by 3 digits (e.g., CUS-001). Use the list_customers tool to find valid IDs." tells the model exactly how to recover. Write error messages as instructions for retry.
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === "get_customer") {
const { customer_id } = request.params.arguments ?? {};
// Business logic validation (beyond what JSON Schema catches)
if (!customer_id.match(/^CUS-\d{3}$/)) {
return {
content: [{
type: "text",
text: `Invalid customer_id format: "${customer_id}". ` +
`Expected format: CUS-XXX (e.g., CUS-001). ` +
`Use list_customers to find valid IDs.`,
}],
isError: true,
};
}
const customer = await db.findCustomer(customer_id);
if (!customer) {
return {
content: [{
type: "text",
text: `Customer "${customer_id}" not found. ` +
`It may have been archived. ` +
`Use list_customers with include_archived=true to search all records.`,
}],
isError: true,
};
}
return {
content: [{ type: "text", text: JSON.stringify(customer, null, 2) }],
};
}
});
Do This
- Return specific error messages that explain what went wrong and how to fix it
- Set isError: true on all error responses so the LLM knows the call failed
- Validate business logic inside the handler — schema only catches structure
- Suggest alternative tools or parameters in error messages to guide recovery
Avoid This
- Throw unhandled exceptions — they crash the server instead of returning a clean error
- Return generic messages like "invalid input" with no guidance on valid formats
- Rely solely on JSON Schema for validation — it cannot check database state or permissions
- Return error text without isError: true — the LLM will treat it as a successful result