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