Skip to main content
Glama

mcp-oauth-doctor

The step where your MCP server's sign-in breaks.

npx --allow-git=root github:agentwares/mcp-oauth-doctor https://your-server.example/mcp

It walks a remote MCP server's OAuth chain from outside, the way a client does, with no credential and no client registration, and prints:

  • the first broken step;

  • whose it is: the server, its identity provider, or a known client bug, with the issue;

  • the fix, citing the section of the 2026-07-28 MCP authorization spec or the RFC that requires it;

  • a verdict per client — claude.ai, Claude Code, ChatGPT, VS Code and Cursor — emulated from each client's published requirements and the issues filed against it. No client is run.

The same check is a web page, /auth?url=, and an MCP tool, mcp_liveness_check_auth on https://agentwares-mcp-liveness.vercel.app/mcp.

What it walks

  1. The 401. One unauthenticated request, the way a dual-era client opens (server/discover at 2026-07-28, then initialize). It must answer 401 — clients start sign-in only on 401 — with a WWW-Authenticate: Bearer resource_metadata="…" challenge, or serve the well-known document.

  2. RFC 9728 protected-resource metadata: the URL in the challenge (a client that has one uses it and stops there), then /.well-known/oauth-protected-resource/<path>, then the root. Its resource must be the server's URL; it must name an authorization server.

  3. RFC 8414 or OpenID Connect discovery, at the locations the spec lists, in its order. A document found only where the spec does not tell clients to look is reported as such. The issuer must be identical to the URL it was looked up for (RFC 8414 §3.3, which clients must enforce since 2026-07-28), and code_challenge_methods_supported must list S256 (clients must refuse to proceed without it).

  4. How a client gets a client ID: a client ID metadata document (preferred since 2026-07-28), RFC 7591 dynamic registration (deprecated, kept for compatibility), or a pasted one.

  5. Each client's published rules: which registration it uses, the redirect URIs it sends, and known bugs that match this server — every rule with its source and date; a rule seen only in an issue is labelled observed in, never presented as documented.

A metadata document behind a bot challenge (Cloudflare, Vercel, DataDome, …) is named: clients fetch it from servers and CLIs, which cannot solve one.

Related MCP server: Remote MCP Server on Cloudflare

Options

npx --allow-git=root github:agentwares/mcp-oauth-doctor <url> [--json] [--client <name>]

  --json            the full report as JSON (schema mcp-oauth-doctor/1), plus `next` and `also`
  --client <name>   judge for one client only: claude-ai, claude-code, chatgpt, vscode, cursor

Exit code 1 when sign-in is broken or nothing answered, 0 otherwise, 2 for a usage error. Usage errors carry code, cause, fix and retryable (as JSON with --json).

In CI

- uses: agentwares/mcp-oauth-doctor@main
  with:
    url: https://your-server.example/mcp
    # client: claude-ai          # one client only
    # fail-on: at_risk           # broken (default) | at_risk | never

The Action runs the committed dist/ from its own checkout (no npm install), writes the report to the job summary, and sets verdict, first-broken and report (the JSON's path) as outputs.

A recorded run

Recorded on 9 October 2026 with pnpm record (scripts/record.mjs): our own servers, live, and two synthetic broken servers that the script serves on 127.0.0.1 for the length of the run (src/vendor/mcp-oauth-walk/synthetic.ts). The synthetic ones are not anyone's real server. Our own servers ask for no credential, so the doctor says there is no sign-in to check and offers the free hourly watch. The synthetic servers have a sign-in, but nothing outside this machine can reach 127.0.0.1, so their hand-off is the watch form left empty, for once a server is deployed, then the nightly checks.

## Our own server, live (2026-10-09)

$ npx --allow-git=root github:agentwares/mcp-oauth-doctor https://agentwares-mcp-liveness.vercel.app/mcp
mcp-oauth-doctor 0.1.0
https://agentwares-mcp-liveness.vercel.app/mcp · 2026-10-09 18:19 UTC · 0.8 s
Discovery only: no credential sent, no client registered.

The server answered without asking for a credential, so there is no sign-in to break.

Steps
  info  Unauthenticated request  HTTP 200: the server accepts requests without a token, so there is no sign-in to check.

Re-check it every hour and email me when it stops answering: agentcheck's free watch, no account. https://agentwares-agentcheck.vercel.app/watch?target=https%3A%2F%2Fagentwares-mcp-liveness.vercel.app%2Fmcp&ref=mcp-oauth-doctor
(exit 0)

## Our own server, live (2026-10-09)

$ npx --allow-git=root github:agentwares/mcp-oauth-doctor https://agentwares-agentcheck.vercel.app/api/mcp
mcp-oauth-doctor 0.1.0
https://agentwares-agentcheck.vercel.app/api/mcp · 2026-10-09 18:19 UTC · 0.5 s
Discovery only: no credential sent, no client registered.

The server answered without asking for a credential, so there is no sign-in to break.

Steps
  info  Unauthenticated request  HTTP 200: the server accepts requests without a token, so there is no sign-in to check.

Re-check it every hour and email me when it stops answering: agentcheck's free watch, no account. https://agentwares-agentcheck.vercel.app/watch?target=https%3A%2F%2Fagentwares-agentcheck.vercel.app%2Fapi%2Fmcp&ref=mcp-oauth-doctor
(exit 0)

## SYNTHETIC broken server ("issuer-mismatch"), served on 127.0.0.1 for this recording — not anyone's real server

$ npx --allow-git=root github:agentwares/mcp-oauth-doctor http://127.0.0.1:56268/mcp
mcp-oauth-doctor 0.1.0
http://127.0.0.1:56268/mcp · 2026-10-09 18:19 UTC · 0.0 s
Discovery only: no credential sent, no client registered.

Sign-in breaks at "Metadata `issuer` is identical to the advertised authorization server", and
the fix is the identity provider's.

Steps
  ok    Unauthenticated request returns 401                   HTTP 401
  ok    WWW-Authenticate points at the resource metadata      resource_metadata=http://127.0.0.1:56268/.well-known/oauth-protected-resource/mcp
  info  Challenge advertises the scope to request             scope="mcp:tools"
  ok    Protected resource metadata is discoverable           http://127.0.0.1:56268/.well-known/oauth-protected-resource/mcp (header)
  ok    PRM `resource` is this server's canonical URI         resource=http://127.0.0.1:56268/mcp
  ok    PRM lists an authorization server                     http://127.0.0.1:56269/tenant1
  info  Scopes advertised                                     mcp:tools
  ok    Authorization server metadata is discoverable         http://127.0.0.1:56269/.well-known/oauth-authorization-server/tenant1; issuer=http://127.0.0.1:56269; token_e…
  FAIL  Metadata `issuer` is identical to the advertised au…  issuer=http://127.0.0.1:56269 vs http://127.0.0.1:56269/tenant1
  ok    PKCE S256 advertised                                  S256
  ok    Client registration                                   client ID metadata documents and dynamic registration (registration_endpoint=http://127.0.0.1:56269/tenant1/r…
  ok    Refresh tokens supported                              authorization_code, refresh_token
  ok    Public clients can use the token endpoint             none, client_secret_basic

First broken step: Metadata `issuer` is identical to the advertised authorization server
  whose: the identity provider
  what:  issuer=http://127.0.0.1:56269 vs http://127.0.0.1:56269/tenant1
  fix:   Make `issuer` in the metadata exactly the URL listed in the protected-resource
         metadata's authorization_servers (http://127.0.0.1:56269/tenant1), character for
         character, or list exactly the issuer the metadata states.
  spec:  RFC 8414 §3.3; MCP 2026-07-28 "Authorization Server Metadata Discovery"
  about "Issuer mismatch": https://agentwares-mcp-liveness.vercel.app/auth/issuer-mismatch?ref=mcp-oauth-doctor

Clients (emulated from each client's published requirements and known issues; no client was run)
  claude.ai    BLOCKED      Metadata `issuer` is identical to the advertised authorization server: issuer=http://127.0.0.1:56269 vs http://127.0.0.…
  Claude Code  BLOCKED      Metadata `issuer` is identical to the advertised authorization server: issuer=http://127.0.0.1:56269 vs http://127.0.0.…
  ChatGPT      BLOCKED      Metadata `issuer` is identical to the advertised authorization server: issuer=http://127.0.0.1:56269 vs http://127.0.0.…
  VS Code      BLOCKED      Metadata `issuer` is identical to the advertised authorization server: issuer=http://127.0.0.1:56269 vs http://127.0.0.…
  Cursor       BLOCKED      Metadata `issuer` is identical to the advertised authorization server: issuer=http://127.0.0.1:56269 vs http://127.0.0.…

Redirect URIs to allow if your identity provider restricts them
  claude.ai    https://claude.ai/api/mcp/auth_callback
  Claude Code  http://localhost/callback  http://127.0.0.1/callback
  ChatGPT      https://chatgpt.com/connector_platform_oauth_redirect
  VS Code      http://127.0.0.1:33418  https://vscode.dev/redirect
  Cursor       http://localhost:8787/callback  https://www.cursor.com/agents/mcp/oauth/callback  cursor://anysphere.cursor-mcp/oauth/callback

Once it is deployed, re-check it every hour and email me when a step breaks: agentcheck's free watch, no account — it follows the sign-in a new client follows, with these same rules. https://agentwares-agentcheck.vercel.app/watch?ref=mcp-oauth-doctor
Nightly, with history: mcpcheck Server Pro re-runs these sign-in checks against the server every night, with 90 days of history, and emails when one fails ($49 a server a month; the first run is free). https://agentwares-agentcheck.vercel.app/mcpcheck?ref=mcp-oauth-doctor
(exit 1)

## SYNTHETIC broken server ("second-issuer"), served on 127.0.0.1 for this recording — not anyone's real server

$ npx --allow-git=root github:agentwares/mcp-oauth-doctor http://127.0.0.1:56268/mcp --client claude-ai
mcp-oauth-doctor 0.1.0
http://127.0.0.1:56268/mcp · 2026-10-09 18:19 UTC · 0.0 s
Discovery only: no credential sent, no client registered.

Sign-in breaks at "claude.ai cannot sign in", and the fix is the server's (only claude.ai is
affected).

Steps
  ok    Unauthenticated request returns 401               HTTP 401
  ok    WWW-Authenticate points at the resource metadata  resource_metadata=http://127.0.0.1:56268/.well-known/oauth-protected-resource/mcp
  info  Challenge advertises the scope to request         scope="mcp:tools"
  ok    Protected resource metadata is discoverable       http://127.0.0.1:56268/.well-known/oauth-protected-resource/mcp (header)
  ok    PRM `resource` is this server's canonical URI     resource=http://127.0.0.1:56268/mcp
  ok    PRM lists an authorization server                 http://127.0.0.1:56269/gone, http://127.0.0.1:56269/tenant1
  info  Scopes advertised                                 mcp:tools
  ok    Authorization server metadata is discoverable     http://127.0.0.1:56269/.well-known/oauth-authorization-server/tenant1; issuer=http://127.0.0.1:56269/tenant1;…
  ok    PKCE S256 advertised                              S256
  ok    Client registration                               client ID metadata documents and dynamic registration (registration_endpoint=http://127.0.0.1:56269/tenant1/r…
  ok    Refresh tokens supported                          authorization_code, refresh_token
  ok    Public clients can use the token endpoint         none, client_secret_basic

First broken step: claude.ai cannot sign in
  whose: the server — claude.com/docs connectors: authentication (2026-10-08)
  what:  claude.ai uses only the first authorization_servers entry (http://127.0.0.1:56269/gone),
         and it published no metadata; the one that answered is http://127.0.0.1:56269/tenant1
  fix:   Put the authorization server that serves metadata first in authorization_servers, or
         serve metadata for the first one.
  spec:  MCP 2026-07-28 "Authorization Server Location"
  docs:  https://claude.com/docs/connectors/building/authentication

Clients (emulated from each client's published requirements and known issues; no client was run)
  claude.ai  BLOCKED      claude.ai uses only the first authorization_servers entry (http://127.0.0.1:56269/gone), and it published no metadata; …

Redirect URIs to allow if your identity provider restricts them
  claude.ai  https://claude.ai/api/mcp/auth_callback

Once it is deployed, re-check it every hour and email me when a step breaks: agentcheck's free watch, no account — it follows the sign-in a new client follows, with these same rules. https://agentwares-agentcheck.vercel.app/watch?ref=mcp-oauth-doctor
Nightly, with history: mcpcheck Server Pro re-runs these sign-in checks against the server every night, with 90 days of history, and emails when one fails ($49 a server a month; the first run is free). https://agentwares-agentcheck.vercel.app/mcpcheck?ref=mcp-oauth-doctor
(exit 1)

What it will not do

  • It never sends a credential, never registers a client and never requests a token.

  • It never retries. Each request has a 10-second timeout and the whole walk a 30-second budget.

  • It makes one POST (two for a server on the older protocol revision) and GETs of public discovery documents. It sends no telemetry and needs no account.

Error messages people paste

One page each, saying what the message means, its three common causes and how to tell them apart, with the public issues and their dates:

Sources, read 8 October 2026

The per-client rules and known issues are in src/vendor/mcp-oauth-walk/clients.ts, each with its source and date.

Keep checking

A check is one look. The doctor's last lines say how to keep looking:

  • Every hour, free: agentcheck's watch takes an email address and the server's URL, with no account. For a server with a sign-in it follows the sign-in a new client follows, with these same rules, and emails the step when one breaks, then again when it is fixed. For a server without one, it emails when the server stops answering or its tools change. A server on localhost cannot be watched from outside, so the doctor offers the form empty, for once it is deployed.

  • Every night, with history: mcpcheck Server Pro re-runs these sign-in checks with 90 days of history and emails when one fails ($49 a server a month; the first run is free). The doctor offers it to a server with a sign-in, after the watch.

With --json, the watch is next and Server Pro is also (null for a server with no sign-in).

Library

npm install github:agentwares/mcp-oauth-doctor
import { diagnose } from "mcp-oauth-doctor";
const report = await diagnose("https://your-server.example/mcp", { clients: ["claude-ai"] });

Web-standard (fetch, URL, AbortSignal), no runtime dependencies.

MIT, by agentwares.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    Diagnose MCP servers — health checks, tool testing, token cost audits, conflict detection, and security scanning with 50+ prompt injection patterns. Works as CLI or MCP server inside Claude Desktop.
    1
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables deploying and connecting to MCP servers on Cloudflare Workers with OAuth login, allowing remote access to tools via MCP clients like Claude Desktop.
    204 npm
    MIT
  • F
    license
    Not graded
    quality
    C
    maintenance
    Enables running an MCP server remotely on Cloudflare Workers with OAuth authentication, allowing MCP clients like Claude Desktop to connect and use tools such as a math calculator.
    1
    -