Skip to content
Production systems

Claude Remote MCP OAuth: Build Discovery, PKCE, Scopes, and Token Refresh

A production implementation contract for remote MCP OAuth in Claude, including discovery, client registration, audience validation, refresh rotation, and failure tests.

A remote MCP server is not ready for Claude when the login screen appears. It is ready when discovery is deterministic, every token is bound to the right resource and user, scopes match the requested tools, refresh survives rotation, and failure tests prove that the server rejects the wrong token.

This guide turns the current Claude connector behavior and MCP authorization requirements into an implementation contract. The original contribution is an acceptance model: eight interfaces, three client-registration choices, and a test matrix that a platform team can run before a connector reaches users.

Evidence status before implementation

Keep three evidence classes separate.

  • Measured first-party search evidence: unavailable. ClaudeExperts has no configured Search Console or GA4 property, so this topic is a low-data experiment rather than a claim of measured demand.
  • Current external evidence: Anthropic documents Claude's supported connector authentication modes, PKCE behavior, metadata discovery, scope selection, callback differences, and token refresh behavior. The MCP specification and OAuth RFCs define protected-resource metadata, resource indicators, token audience binding, and public-client protections.
  • Implementation inference: the architecture, decision table, operational ownership, and acceptance tests below are a proposed production pattern derived from those sources. They are not requirements published by Anthropic.

Specialist review: Dhruv Khatri is the named technical reviewer. Review is requested and pending. Treat the proposed defaults below as review-ready controls, not as a record of completed expert approval.

The eight-interface authorization contract

Most failed OAuth integrations are not one bug. They are a mismatch between interfaces owned by different teams.

InterfaceProducerConsumerAcceptance condition
Initial protected requestClaude clientMCP serverMissing or invalid bearer token returns 401, not a tool-shaped success response
Authentication challengeMCP serverClaude clientWWW-Authenticate points to the protected-resource metadata document
Protected-resource metadataMCP serverClaude clientresource exactly matches the configured MCP URL and lists the intended authorization server
Authorization-server discoveryIdentity platformClaude clientDiscovery exposes supported endpoints, PKCE S256, scopes, and a usable registration path
Client registrationDCR, CIMD, or pre-registered credentialsClaude client and identity platformRedirect URIs and public or confidential client behavior match the Claude surface
Authorization requestClaude clientIdentity platformstate, PKCE challenge, resource, and least-privilege scopes survive the round trip
Token validationMCP serverPresented access tokenSignature, issuer, audience/resource, expiry, and scope are verified before any tool executes
Refresh and revocationClaude client and identity platformToken endpoint and MCP serverRotation, expiry, revocation, and reconnect behavior work without reusing an invalid token

Assign an owner to every row. If the MCP team owns only the last HTTP handler while identity, networking, and directory onboarding are unowned, the connector is not production-ready.

Choose the registration model deliberately

Claude supports more than one way to establish an OAuth client. Use the operating model, not implementation convenience, to choose.

ModelUse whenMain tradeoffProduction check
Dynamic Client Registration (DCR)The authorization server can create public clients safely and connection volume is modestSimple onboarding, but fresh connections can create many registered clientsRate-limit and lifecycle-test registration records
Client ID Metadata Document (CIMD)The authorization server can resolve a self-hosted client metadata URL and you want stable public-client metadataAvoids a registration call, but discovery support must be exactVerify client_id_metadata_document_supported and public-client token authentication
Anthropic-held credentialsA directory connector needs one stable OAuth application and the integration follows Anthropic's review pathOperational coordination is required when credentials or authorization-server ownership changeTest the hosted Claude surfaces and document credential rotation ownership
Customer-supplied credentialsAn enterprise custom connector needs an OAuth application scoped to that organizationMore customer setup, but cleaner tenant controlValidate redirect registration and secret handling per tenant

Do not use a query-string API key as a shortcut. Anthropic's connector documentation says user-pasted bearer tokens and credentials in connector URLs are not supported. A server-to-server client_credentials grant is also not a substitute for a consent-gated user connection.

Implement the discovery path first

Start with the unauthenticated request because it controls everything that follows.

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource/mcp"

The protected-resource document should identify the exact MCP resource and its authorization server:

{
  "resource": "https://mcp.example.com/mcp",
  "authorization_servers": ["https://identity.example.com"],
  "scopes_supported": ["records.read", "records.write"]
}

Exactness matters. If a user enters https://mcp.example.com/mcp, do not advertise only the origin as the resource. Anthropic documents path-aware discovery and recommends an explicit resource_metadata pointer in the 401 challenge. Relying on client probing adds round trips and breaks when a hosting platform cannot serve the expected well-known path.

The authorization server must then publish RFC 8414 or OpenID Connect discovery metadata. For Claude interoperability, verify the registration mechanism and advertise S256 PKCE support. Return discovery documents over HTTPS and test them from outside the corporate network so a WAF or conditional-access policy does not create a false local success.

Bind every token to this MCP resource

An access token that proves a user signed in is not sufficient. The MCP server must also prove that the token was issued for this resource.

At the start of every authenticated request, validate:

  1. the token signature against the trusted issuer's current keys;
  2. the issuer against an explicit allowlist;
  3. the audience or resource identifier against the exact MCP server URL;
  4. expiry and not-before claims with a small documented clock-skew allowance;
  5. required scopes for the requested tool;
  6. the subject and tenant expected for this connection;
  7. revocation or session state where the identity architecture requires it.

Then build an authorization context that tools receive explicitly. Do not let a tool recover identity from a global variable or a connection-wide cache.

type McpAuthContext = {
  subject: string;
  tenantId: string;
  scopes: Set<string>;
  tokenId?: string;
};

function requireScope(ctx: McpAuthContext, scope: string) {
  if (!ctx.scopes.has(scope)) throw new Error("insufficient_scope");
}

The token from Claude is for the MCP resource. If the MCP server calls Salesforce, Google Drive, or another upstream API, use a separate upstream credential or delegated token issued for that API. Token passthrough creates an audience-confusion problem and is explicitly rejected by the MCP security model.

Make scopes match tools, not teams

Broad role names such as employee or admin are weak tool boundaries. Prefer scopes tied to stable actions:

  • records.read for search and retrieval;
  • records.write for reversible edits;
  • records.export for bulk data movement;
  • billing.refund for a narrow irreversible action.

Return only tools that the current authorization context may see, and recheck scope inside the handler. The first control reduces accidental selection; the second is the enforcement boundary.

Use the scope parameter in the WWW-Authenticate challenge when a request needs step-up access. Do not request every possible scope during initial connection. A useful policy is read scopes at connection time, write scopes when a write-capable tool is first requested, and a fresh consent event for high-impact scopes.

Design refresh as a state transition

Anthropic documents both proactive refresh shortly before expiry and reactive refresh after a 401. Your token endpoint therefore needs predictable behavior under overlap, retries, and rotation.

For public clients, rotate refresh tokens or sender-constrain them. When rotating, return the replacement refresh token in the same successful response that invalidates the old token. Store only a protected representation, link the token family to the user and client, and detect reuse of an invalidated family member.

Model refresh with these states:

StateServer responseOperator meaning
Access token still validResource request succeedsNo refresh needed
Access token expired, refresh validToken endpoint returns new access token and rotated refresh tokenNormal renewal
Refresh token already used or revokedinvalid_grantStop retrying and require reconnection
Client metadata no longer trustedregistration or token request rejectedInvestigate client lifecycle or identity configuration
User lost required permissionrefreshed token omits scope or resource returns insufficient scopeAccess change propagated correctly

Accept form-encoded bodies at the token endpoint. A JSON-only parser that returns 415 can make the entire flow look like a Claude client problem.

Run this acceptance test matrix

Do not stop at one successful login. Run positive, negative, expiry, and tenant-isolation cases.

TestInput or faultExpected result
Discovery happy pathNo token on exact MCP URL401, valid challenge, metadata and authorization-server discovery resolve
Wrong resourceValidly signed token for another APIMCP request rejected before tool listing or execution
Wrong issuerToken from an untrusted tenant or issuerRejected even if signature format is valid
Missing scopeRead token calls a write toolTool hidden or call rejected with insufficient scope
PKCE downgradeAuthorization request without S256Authorization server rejects the request
Redirect mismatchUnregistered callbackAuthorization server rejects the request without redirection
Refresh rotationOld refresh token reused after successful rotationinvalid_grant; token family alert recorded if policy requires it
Revoked userUser disabled between access-token expiry and refreshRefresh fails or returns a token without access
Cross-tenant replayTenant A token sent to tenant B resource contextRejected; no data or tool result returned
Upstream boundaryMCP token presented to downstream API pathDesign prevents passthrough; downstream credential is separate
WAF reachabilityDiscovery from Anthropic network pathMetadata and token endpoints remain reachable under intended policy
Audit integritySuccessful and denied callsLog captures subject, tenant, tool, decision, and correlation ID without storing bearer tokens

Worked example: a CRM connector

Assume the MCP endpoint is https://mcp.example.com/crm. A sales operator connects with crm.contacts.read. Claude can list and search contacts, but the update_contact tool is absent. When the operator asks to correct a title, the server challenges for crm.contacts.write. After consent, the refreshed tool list includes the update operation.

The handler validates the token audience as the exact /crm resource, checks tenant membership, applies the write, and logs the subject and record identifier. It does not forward the MCP token to the CRM. A separate service credential, constrained to that tenant, performs the upstream API call.

This example is intentionally narrow. The design makes a mistaken write observable and bounded, and it lets removing crm.contacts.write take effect at the next refresh rather than waiting for a human to delete an integration.

Ship the contract, not only the code

Before review, attach four artifacts to the implementation:

  1. the exact protected-resource and authorization-server metadata documents;
  2. a registration-model decision with an owner for credential and redirect changes;
  3. the scope-to-tool matrix and upstream credential boundary;
  4. automated results for the acceptance matrix, including denied cases.

This turns OAuth from a login feature into a testable authorization system. If your team needs a review of the MCP resource boundary, identity model, token validation, or interoperability tests, the MCP and Tool Integration engagement is designed around that implementation work. Start with MCP versus ad-hoc Claude tools for the protocol decision and Claude tool schema design for the tool contract.

Sources

Related notes