Skip to main content
The OAuth conformance module runs real OAuth handshakes against your MCP server — the same flows Claude Desktop, ChatGPT, and Claude Code perform — and tells you exactly where they break.

Why you need this

  • Each MCP client exercises OAuth slightly differently. Passing conformance means your server works for all of them.
  • OAuth bugs are silent. A valid-looking token can be rejected by MCP authentication downstream. Users see “failed to connect” with no context. --verify-tools catches this.
  • 3 registration strategies x 3 protocol versions x 3 auth modes = 27 possible flow combinations. The suite runner covers the matrix in one invocation.
  • CI-ready output. --reporter junit-xml for dashboards. Exit 0/1/2 for scripts.

Quick start

The default --auth-mode is interactive — a browser opens for consent. Pass --auth-mode headless for CI against auto-consenting auth servers, or --auth-mode client_credentials for M2M. What success looks like:

What it tests

Each conformance run walks through:
  1. Initial request — sends an unauthenticated request to trigger a 401
  2. Discovery — fetches resource metadata and authorization server metadata
  3. Registration — registers the client via CIMD, DCR, or pre-registered credentials
  4. Authorization — obtains an authorization code (interactive, headless, or client_credentials)
  5. Token exchange — exchanges the code for access/refresh tokens
  6. Authenticated request — retries the MCP request with the token
  7. Post-auth verification (optional) — connects via MCP and lists tools / calls a tool
When --conformance-checks is enabled, six additional negative checks run after the flow:
  • DCR redirect URI policy — attempts dynamic client registration with a non-loopback http:// redirect URI and expects rejection under the MCP authorization profile
  • Invalid client — confirms the auth server rejects an unknown client ID
  • Invalid redirect at the authorization endpoint — sends an authorization request with a mismatched redirect_uri and looks for rejection before the server redirects back to it
  • Invalid token — confirms the MCP server rejects an obviously invalid bearer token with HTTP 401
  • Invalid redirect — attempts a token request with a mismatched redirect URI to look for exact-match enforcement; this check may be skipped if the request is rejected for a different reason first
  • Token format — validates the token response has the expected fields

Scenarios

I just added OAuth to my MCP server

CI conformance testing without a browser

Always pass --auth-mode explicitly in CI. The default is interactive and will attempt to open a browser.

Test multiple protocol versions at once

Create a config file:

Users report tokens work but tool calls fail

This is exactly what --verify-call-tool catches. OAuth can succeed (valid token issued) while the MCP layer rejects it (wrong audience, missing scope, session binding).
--verify-call-tool implies --verify-tools.

Test specific scopes

--scopes sets what you request from the auth server. The runner does not currently verify that granted scopes match requested scopes. When --scopes is omitted, the runner prefers the scope= value from the initial WWW-Authenticate challenge and falls back to scopes_supported.
--print-url writes the consent URL to stderr (OAUTH_CONSENT_URL: https://...) instead of launching a browser. The local callback listener still runs — your Playwright script opens the URL, drives consent, and the CLI catches the redirect.

Run the OAuth negative checks

This adds 6 checks after the main flow: DCR redirect URI policy, invalid client rejection, invalid redirect enforcement at the authorization endpoint, invalid bearer token rejection at the MCP server, invalid redirect enforcement at the token endpoint, and token format validation.

Registration strategies

When to choose what:
  • CIMD (2025-11-25): preferred for production when the auth server supports it — avoids mutable DCR state.
  • DCR: the default for quick testing — works everywhere, no pre-configuration needed.
  • Preregistered: use when the auth server doesn’t support DCR, or for client_credentials M2M flows.
When cimd is used without --client-metadata-url, the runner falls back to mcpjam’s public metadata document at https://www.mcpjam.com/.well-known/oauth/client-metadata.json.

Auth modes

Compatibility

cimd + client_credentials is rejected with a clear error: “CIMD is a browser-based registration flow and only works with —auth-mode headless or —auth-mode interactive.”

Post-auth verification

OAuth conformance proves a token was issued. Verification proves the token is actually usable for MCP operations. Three silent-failure modes this catches:
  1. Token audience mismatch — OAuth flow succeeds but tools/list returns 401 because aud doesn’t match the MCP resource URL.
  2. Scope mismatch — token issued but the MCP tool handler requires different scopes.
  3. Session binding — some servers require a separate session init after auth.

Reuse credentials safely

Use --credentials-out <path> when a conformance run should hand credentials to later connected commands. The file is written with 0600 permissions and can be passed to commands such as tools list, apps conformance, resources list, and server doctor with --credentials-file <path>.
Raw JSON output redacts OAuth secrets by default. --credentials-out is the supported way to persist live tokens.

Troubleshooting

CI/CD integration

GitHub Actions

GitLab CI (JUnit output)

All flags

oauth conformance

For interactive conformance runs, a custom --redirect-url must still be an http://localhost or http://127.0.0.1 loopback URL. Custom callback paths are supported.

oauth conformance-suite

--verify-tools and --verify-call-tool on the suite command are forced onto every flow. Per-flow verification entries in the config file cannot disable them once the CLI flag is set.