Errors

Tool errors

Tool errors return isError: true with a JSON body in the text content:

{
  "error": {
    "code": "NOT_FOUND",
    "message": "No report found for run abc-123. The run may still be in progress.",
    "details": { "run_id": "abc-123" }
  }
}

details is present only when the handler supplies it.

Code Meaning Retry?
NOT_FOUND No matching record Not without changing the input
VALIDATION_FAILED Handler-level validation failed No — fix the input
RATE_LIMITED Bucket empty Yes, after details.retry_after_seconds
UPSTREAM_ERROR Downstream or configuration failure Sometimes — check the message
INTERNAL_ERROR Uncaught exception Yes, with backoff

Schema-level violations are rejected by the MCP SDK before the handler runs, so they surface as protocol errors rather than as VALIDATION_FAILED.

Successful responses

{
  "content": [{ "type": "text", "text": "<pretty-printed JSON>" }],
  "structuredContent": { }
}

Prefer structuredContent — the text block is the same data formatted for display.

Transport and auth errors

Status Code Meaning
401 -32001 Missing or invalid bearer key
503 -32002 Authentication is not configured on the server
500 -32603 Internal server error

NOT_FOUND is frequently a timing problem

get_report returns NOT_FOUND for a run that exists but has not finished. The message says so. Poll get_audit_status until status is complete rather than retrying get_report blindly.

Audit logging

Every call writes one audit row: timestamp, tool, tier, truncated caller-key hash, redacted arguments, status, duration, and any error. Argument redaction truncates strings at 200 characters, caps arrays at 20 items, and replaces any key matching token|key|secret|password|authorization|credential with [redacted].