new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

MCP Server Development in Practice · all subjects

OAuth and auth pitfalls

5 notes, read out of this brain and free to use. Each one was extracted from a source and is re-checked against its exam.

How do clients know my server needs OAuth?

They learn it from your 401. When an unauthenticated request hits the MCP endpoint, respond with status 401 and a WWW-Authenticate header — clients key off that header to start the authorization flow. Since spec 2025-06-18, servers should also point to their protected resource metadata: either in the WWW-Authenticate header's resource_metadata parameter or at /.well-known/oauth-protected-resource, which names the authorization server. The client then discovers that server's endpoints via /.well-known/oauth-authorization-server (or OpenID configuration). The pitfall: returning a bare 401 with no header, or a JSON error body with status 200 — the client then reports a generic connection failure and the user never sees an auth prompt. Test by curling your endpoint without a token and inspecting headers.

Dynamic client registration — why does the OAuth flow stall before it starts?

MCP authorization expects OAuth 2.1 with dynamic client registration (RFC 7591), because a generic client cannot pre-register with every MCP server on earth. The pitfall: most real authorization servers — Auth0, some Cognito setups, many corporate IdPs — either disable DCR or gate it behind admin approval, so the client's POST to the registration endpoint 404s or 401s and the flow dies silently before any login screen. Mitigations, in order of practicality: put a thin DCR proxy in front of your IdP that mints a client per request; document a manual client_id for big clients that allow one; or skip OAuth entirely and use bearer tokens the user pastes into their client config — the pattern many production MCP servers still ship in early 2026.

Bearer token vs full OAuth for a small MCP server — which is defensible?

A long-lived bearer token pasted into client config (e.g. claude mcp add --transport http name URL --header "Authorization: Bearer tok_...") is defensible and widely used when: one user owns the token, it scopes to read-mostly operations, and you can revoke it server-side. It is how many production servers bootstrapped before OAuth tooling matured, and it sidesteps the entire DCR/consent-UX problem. Where it stops being defensible: multi-tenant servers where one token could reach another tenant's data, write-heavy tools, or anything you would put in a directory listing for strangers. Token hygiene that matters either way: prefix tokens so scanners recognize them, store only hashes, log usage per token, and rotate on suspicion without invalidating sessions.

Who stores and refreshes OAuth tokens — my server or the client?

Access and refresh tokens live in the CLIENT, not your server — Claude Code, Cursor and friends persist them in their own credential stores and run the refresh flow against your token endpoint. Your server only validates access tokens per request. Two pitfalls follow. First, refresh token rotation: if your IdP rotates refresh tokens (OAuth 2.1 best practice), a client that mishandles rotation gets permanently logged out on token expiry — test a full expiry-and-refresh cycle per client you claim to support, not just first login. Second, revocation UX: users revoke access in your dashboard and are surprised the client keeps working for up to the access token's lifetime — keep access token TTLs short (minutes, not days) so revocation actually bites.

Audience validation — why did my server accept a token meant for another API?

Confused-deputy territory: if your MCP server trusts tokens from a shared IdP without checking the audience, a token a user minted for some other service can call your tools. Since spec 2025-06-18, MCP servers are Resource Servers (RFC 9728) and clients are expected to request tokens with resource indicators (RFC 8707) naming your server — so validate that the token's aud actually equals your server's canonical URL, and reject otherwise. Also validate issuer, expiry, and signature against the IdP's JWKS on every request, caching the keys. If you issue your own opaque tokens instead, the equivalent discipline is: hash at rest, bind to a user, scope to a plan, and never log the raw value.

Give your agent this brain