Skip to main content
The MCP server surfaces three distinct kinds of failures, each with a different shape. This page explains how to recognize and react to each one.

1. Authentication / authorization failures (HTTP)

If the bearer token is missing, malformed, expired, or revoked, the MCP endpoint returns an HTTP 401 Unauthorized before the request reaches the tool layer. The response carries an RFC 9728 WWW-Authenticate challenge:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="mcp", error="invalid_token", resource_metadata="https://mcp.particle.pro/.well-known/oauth-protected-resource"
Content-Type: text/plain

grant revoked or unknown
The human-readable explanation lives in the response body. The header carries only the structured fields (realm, error, optional scope, resource_metadata). Reactions:
  • Re-run the OAuth flow. The resource_metadata URL is the authoritative pointer to the AS — do not hard-code the token endpoint.
  • For revoked grants, the user must reconnect — see Manage connections.
  • If the request authenticated with a platform API key instead of OAuth, a bad key gets the same 401 shape with the body invalid or expired API key — rotate or re-issue the key from the project settings; the OAuth challenge in the header does not apply to you.
See Authentication for the full validation pipeline.

2. Subscription / rate-limit failures (HTTP)

Requests that pass authentication but hit a subscription block or per-org rate limit return RFC 9457 application/problem+json bodies — the same shape as the REST API. The response is HTTP, not an MCP tool result, because the request never reached a tool.
HTTP/1.1 402 Payment Required
Content-Type: application/problem+json

{
  "type": "https://docs.particle.pro/errors/spend_limit_exceeded",
  "title": "Payment Required",
  "status": 402,
  "detail": "Spend limit reached for the current billing period.",
  "error_code": "spend_limit_exceeded",
  "resolve": {
    "message": "Increase the spend limit or upgrade the plan to continue.",
    "url": "https://platform.particle.pro/organizations/<orgId>/billing",
    "action": "update_spend_limits",
    "method": "POST",
    "endpoint": "/v1/organizations/<orgId>/billing/spend-limits"
  }
}
The full set of codes is documented in REST → Errors. The ones an MCP client is most likely to see at this layer: Branch on error_code; surface resolve.message and resolve.url to the user. Agents that can take action programmatically should use resolve.action, resolve.method, and resolve.endpoint.

3. Tool-level failures (MCP isError: true)

When a request reaches a tool but the tool itself fails (missing required parameter, slug not found, invalid date, downstream handler error), the MCP server returns a 200 OK HTTP response carrying an MCP CallToolResult with isError: true and a single text content block:
{
  "jsonrpc": "2.0",
  "id": 17,
  "result": {
    "isError": true,
    "content": [
      {
        "type": "text",
        "text": "**Error code:** missing_parameter\n\ntool particle_entity_resolve requires parameter `query` (e.g. query=\"sam altman\")"
      }
    ]
  }
}
Every tool error leads with a **Error code:** <slug> line — a stable, machine-readable code to branch on without substring-matching the prose that follows. Two kinds of codes appear:
  • MCP-only codes for failures that never reach the REST layer: missing_parameter, invalid_parameter, invalid_input (malformed arguments), and unknown_tool (a particle_call dispatch to a name that doesn’t exist on the public surface).
  • REST catalog codes reused as-is when the underlying handler fails — e.g. not_found, bad_request, premium_required. See REST → Errors for the full catalog.
After the code line comes the handler’s message, and — where the server knows a better next move than a blind retry — a course-correcting suggestion. Structured resolve guidance from the REST layer (message + URL) is folded into that suggestion; common statuses get a default:
  • 404 suggests re-resolving the identifier first (particle_podcast_resolve, particle_person_resolve, particle_company_resolve, or particle_entity_resolve) and retrying with the returned slug.
  • 400/422 suggests checking the parameter constraints via particle_catalog with the tool’s category, which returns the full input schema.
Reactions:
  • Branch on the **Error code:** slug, then follow the suggestion in the text — it names the exact tool to call next.
  • For missing_parameter / invalid_parameter, the text includes a usage hint (e.g. query="sam altman") — fix the call and retry.
  • For internal_error, surface the message to the user and avoid retry loops — the resource server logs the underlying error with full structure for support follow-up.

Why three layers?

  • HTTP 401 is the only path that’s defined by RFC 6750 / RFC 9728 — MCP clients that follow the OAuth spec already handle it. Putting auth errors anywhere else would break the discovery loop.
  • HTTP 402/403/429 with RFC 9457 bodies stays consistent with the REST API: the same billing or rate-limit middleware runs in front of both surfaces, so the same problem-details shape comes out.
  • isError: true MCP results are how the spec recommends per-tool failures be returned — a 200 OK JSON-RPC response with the structured error in the result envelope, so the agent can keep the session open and fix the call.

See also

  • REST → Errors — the canonical catalog of error_code values and resolution actions.
  • Authentication — how 401s arise and what WWW-Authenticate carries.
  • Tool reference — every tool documents its required parameters and the failure modes it can raise.