- OAuth 2.1 (interactive clients — Claude Code, Cursor, VS Code, ChatGPT, Claude Desktop): a user approves the connection in a browser once, and every request thereafter carries a short-lived JWT minted by the Particle Pro Authorization Server, bound to the MCP resource via its
audclaim. Granular, per-project, user-revocable — prefer it whenever a human is present to consent. - Platform API keys (headless agents — cron jobs, server-side harnesses, OpenAI’s Responses API): the same
pp_*keys the REST API accepts, sent as a bearer. No browser, no consent screen — see API keys for headless agents.
At a glance
| Protected resource | https://mcp.particle.pro |
| Resource metadata | https://mcp.particle.pro/.well-known/oauth-protected-resource (and /mcp sub-path) |
| Authorization Server | https://api.particle.pro |
| AS metadata | https://api.particle.pro/.well-known/oauth-authorization-server |
| JWKS | https://api.particle.pro/.well-known/jwks.json |
| Grant types | authorization_code, refresh_token |
| PKCE | Required, code_challenge_method=S256 only |
| Auth methods | none (public clients), client_secret_basic, client_secret_post |
| Scopes | mcp:read, mcp:write |
| Access token TTL | 15 minutes |
| Refresh token TTL | 30 days, rotated on every use |
| Agent onboarding | https://api.particle.pro/auth.md (also on the MCP host) + agent_auth block in the AS metadata |
Discovery
Start from the resource. Fetch the protected-resource metadata document:authorization_servers:
agent_auth block for autonomous agents — see Agent discovery below.
If a request to the MCP endpoint comes in unauthenticated, the server returns 401 with a WWW-Authenticate header pointing at the resource-metadata document, so a client that doesn’t pre-fetch metadata still discovers the AS:
bearer token required, grant revoked or unknown) is in the response body, not the header.
Agent discovery
Particle Pro publishes the auth.md agent-registration protocol, so autonomous agents (and agent-readiness scanners) can discover how to obtain a credential without reading human documentation:/auth.md— a human- and machine-readable onboarding recipe athttps://api.particle.pro/auth.mdandhttps://mcp.particle.pro/auth.md: the discovery walkthrough, a credential decision tree, registration steps, errors, and revocation. A docs-flavored mirror lives at Agent authentication.agent_auth— a machine-readable block inside the AS metadata document above:
user_consent_authorization_code — dynamic client registration (or a Client ID Metadata Document) followed by the PKCE authorization-code flow, with a human approving on platform.particle.pro. Flows Particle Pro deliberately does not offer (anonymous registration, ID-JAG identity assertion, device authorization) are declared in unsupported_flows, so an agent fails fast instead of probing.
The REST API publishes its own RFC 9728 document at https://api.particle.pro/.well-known/oauth-protected-resource. Mind the asymmetry it implies: the REST surface authenticates with pp_* API keys only — AS-issued access tokens are audience-bound to the MCP resource and are not accepted by the REST API.
Client registration
You have two ways to register a client.Dynamic Client Registration (RFC 7591)
The simplest path. POST to the registration endpoint with at leastclient_name and redirect_uris:
token_endpoint_auth_method:none(i.e. public client, PKCE-only)grant_types:["authorization_code", "refresh_token"]response_types:["code"]
token_endpoint_auth_method other than none) the response includes client_secret exactly once — store it securely, the AS does not return it again.
Client ID Metadata Document (CIMD)
Agents that already publish a metadata document at an HTTPS URL can skip DCR entirely and pass the URL itself asclient_id to /oauth/authorize. The AS fetches the document, validates it, and caches it. The CIMD spec is draft-ietf-oauth-client-id-metadata-document.
Use this when your agent runs in many places (you don’t want to register N clients) but has a stable identity (your domain).
Authorization code with PKCE
Generate a high-entropycode_verifier, then derive its S256 challenge:
response_type=code(only flow supported)client_id— from registration or CIMD URLredirect_uri— must match a value the client registeredcode_challenge+code_challenge_method=S256— PKCE is mandatoryresource=https://mcp.particle.pro— RFC 8707 audience binding; the AS rejects any other value
scope is optional. If omitted the AS issues mcp:read mcp:write by default. Naming an unknown scope is rejected (the AS will redirect with error=invalid_scope rather than silently up-scoping).
The user signs in (if not already), picks a project, and approves the requested scopes. The AS redirects back to your redirect_uri:
client_secret_basic— dropclient_idfrom the form body and sendclient_id:client_secretURL-form-encoded in an HTTP BasicAuthorizationheader.client_secret_post— keepclient_idin the form body and add aclient_secretfield alongside it.
token_endpoint_auth_method=none) keep client_id in the form body and send no secret.
Response:
Calling the MCP endpoint
Send the access token as a bearer in theAuthorization header. The MCP server only accepts header-borne tokens (bearer_methods_supported: ["header"]) — no query-string tokens, no form-body tokens.
- Verify the JWT signature against the rotating JWKS at
https://api.particle.pro/.well-known/jwks.json. - Confirm
iss=https://api.particle.pro,aud=https://mcp.particle.pro, andexpis in the future. - Look up the
grant_idclaim — the grant must still be active (not revoked, not user-disabled). - Confirm the token’s
sub,client_id, andproject_idclaims match the grant row (defense in depth against a buggy or compromised signer).
401 with a WWW-Authenticate challenge.
API keys for headless agents
Autonomous agents have no browser to complete an OAuth consent in. For them, the MCP endpoint accepts the same project-scopedpp_* API keys as the REST API — as an Authorization: Bearer header or an X-API-Key header:
- Keys are project-scoped — give each agent its own key in a project provisioned for it, so a leaked key exposes one project’s access, is attributable in request logs, and can be revoked without breaking anything else.
- Never embed keys in code or prompts. Provide them through a secrets vault or environment variable, as in the
$PARTICLE_API_KEYexample above. - Prefer OAuth when a human is present. Consent-scoped, short-lived, per-client tokens beat a long-lived shared secret anywhere an interactive approval is possible.
401 whose body says invalid or expired API key — distinct from the JWT validation errors above, so a misconfigured agent can tell which credential path failed.
Refresh and rotation
When the access token nears expiry, exchange the refresh token:Revocation
Either side can revoke a grant:Manage connections
Every OAuth approval creates a connection (a grant) binding one MCP client to one of your projects. You can review and revoke them at any time — no client involvement needed:- Sign in at platform.particle.pro and open Connected Applications.
- Each entry shows the client’s registered name, the project it acts on, and when it was approved. Find the client you want to disconnect.
- Choose Revoke. The grant — and every access and refresh token issued from it — stops working immediately.
403 after a previously working connection).
Revoke a connection whenever a device is lost, an agent misbehaves, or you no longer use the client — reconnecting later is a one-click approval.
Audience binding (RFC 8707)
Every authorization request and every token request must includeresource=https://mcp.particle.pro. The AS rejects mismatches with error=invalid_target. The minted access token carries aud=https://mcp.particle.pro, and the MCP server rejects any token with a different aud — even a valid token issued for some hypothetical other Particle resource would not work here. This is the standard “confused deputy” safeguard.
Scopes
Two scopes today; finer-grained scopes can be added as the surface grows:| Scope | Granted access |
|---|---|
mcp:read | Read your Particle Pro data via MCP tools. |
mcp:write | Take actions on your behalf in Particle Pro via MCP tools. (All current tools are read-only — mcp:write is reserved for future write tools.) |
readOnlyHint: true; mcp:write is requested by default but does not unlock additional surface yet.
Key rotation
The AS uses RS256 with a rotating JWKS. The signer publishes the “next” key well before promoting it to “active” and keeps “retired” keys live long enough for outstanding tokens to expire (15-minute access-token TTL). Validators cache the JWKS for up to 5 minutes — a freshkid will be picked up within that window.
See also
- Quickstart — install snippets for stock MCP clients.
- Errors — how tool errors surface (different from REST
application/problem+json). auth_required— REST-side auth error code.