{"slug": "how-remote-mcp-servers-trigger-oauth-sign-in", "title": "How Remote MCP Servers Trigger OAuth Sign-In", "summary": "Remote MCP servers must return a 401 with a `WWW-Authenticate` header pointing at RFC 9728 protected-resource metadata to trigger OAuth sign-in in Claude or ChatGPT, but the same 401 causes directories and health checkers to mark servers as down, Jake Marsh reports. CallForMe logged about 700 checker 401s in two days, and one directory listed it as \"Unhealthy, 0.0% uptime over 32 days\" while it was running fine. Marsh's Taskhold defaults to anonymous access and challenges a list of known OAuth clients, while CallForMe defaults to the 401 and opens up for known checkers, a trade-off he resolves by which mistake costs more.", "body_md": "# How Remote MCP Servers Trigger OAuth Sign-In\n\n[Jake Marsh](https://deallocatedobjects.com/about)\n\nWhen someone pastes your MCP server’s URL into Claude or ChatGPT, a sign-in window should open. What makes that happen is one HTTP response from your server. I run a few remote MCP servers with OAuth, and the response those clients need is the same one that gets a server marked as down in directories.\n\n## The Response That Starts It[#](#the-response-that-starts-it)\n\nThe flow in the [MCP authorization spec](https://modelcontextprotocol.io/specification/draft/basic/authorization) begins when the client’s request gets a 401 with a `WWW-Authenticate` header pointing at your protected resource metadata:\n\nThe expose header is there for clients running in a browser. Without it, their JavaScript can’t read the challenge.\n\nThat URL returns a small JSON document ([RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728)) naming your authorization server:\n\nFrom there the client reads your authorization server’s metadata, registers itself and opens the browser. I serve the metadata at both `/.well-known/oauth-protected-resource/mcp` and the bare `/.well-known/oauth-protected-resource`, because some clients try the root first.\n\n## Two Callers Want Opposite Things[#](#two-callers-want-opposite-things)\n\nIf an anonymous `initialize` returns 200, an OAuth-capable client can decide your server needs no auth. It connects, lists the tools and only fails later on `tools/call`, with no sign-in window. So those clients need the 401 on their very first request.\n\nDirectories and health checkers want the opposite. They call `initialize` and `tools/list` with no credentials, and many of them read a 401 as an outage. [CallForMe](https://callforme.tel/?ref=deallocated-post) logged about 700 of those 401s from checkers in two days, and one directory listed it as “Unhealthy, 0.0% uptime over 32 days” while it was running fine.\n\n## Answer By Who’s Asking[#](#answer-by-whos-asking)\n\nSo the endpoint looks at the caller. This is the version in [Taskhold](https://taskhold.com/?ref=deallocated-post), trimmed down. Clients that sign in get challenged, and everything else can read the public parts:\n\nThe User-Agent catches most clients. Some send something generic like `node`, and those name themselves in `clientInfo` inside the `initialize` body, which is why there’s a second regex.\n\nA spoofed User-Agent gets a caller nothing here, since the list only decides who sees a challenge. Every tool that touches data still needs a token, and anonymous requests run against a backend that throws on any call.\n\n## Which Default To Pick[#](#which-default-to-pick)\n\nTaskhold defaults to anonymous and challenges a list of known OAuth clients. CallForMe does the inverse: it defaults to the 401 and opens up for a list of known checkers. Your choice decides who gets the wrong answer when a caller you’ve never seen shows up:\n\n- With a list of OAuth clients, a new client gets a 200 and may not offer sign-in until someone adds it.\n- With a list of checkers, a new checker gets a 401 and may list you as down.\n\nI’d pick by which mistake costs you more. CallForMe also matches checkers by how they describe themselves (`mcp-?index`, `health-?check`, `crawler`), so new ones don’t sit on a 401 until I notice them.\n\nThere are exceptions on both sides. The CI for [awesome-remote-mcp-servers](https://github.com/punkpeye/awesome-remote-mcp-servers) labels a server by its `initialize` reply: 200 means no auth, and a 401 with `resource_metadata` means OAuth. An OAuth server that wants the right label has to challenge that checker too, so it’s in Taskhold’s full list.\n\n## Log The 401s[#](#log-the-401s)\n\nSome clients see the challenge and never finish. `codex mcp add --url` without `codex mcp login` hit a 401 on every session: in early November my logs showed 7 IPs stuck in one day and no completed Codex sign-ins in 3 days. CallForMe now lets unauthenticated Codex sessions into a public mode that needs no sign-in, and signed-in ones keep their token. Another runtime registered with the OAuth server, never called `/authorize` and kept retrying hourly.\n\nI only found these by logging each 401 with its User-Agent, limited to once per IP and client every 6 hours. Comparing that against completed sign-ins shows which clients get stuck.\n\nYou can check your own server’s answer with curl by changing the User-Agent:\n\nRun it once with that header and once with `User-Agent: directory-check`. The first should return the 401 and the second a 200, unless your server is one that challenges everyone by default.", "url": "https://wpnews.pro/news/how-remote-mcp-servers-trigger-oauth-sign-in", "canonical_source": "https://deallocatedobjects.com/posts/how-remote-mcp-servers-trigger-oauth-sign-in", "published_at": "2026-11-09 14:00:00+00:00", "updated_at": "2026-11-09 14:19:34.843871+00:00", "lang": "en", "topics": ["agent-protocols", "ai-agents", "ai-tools", "developer-tools"], "entities": ["Jake Marsh", "Model Context Protocol", "Claude", "ChatGPT", "CallForMe", "Taskhold", "RFC 9728", "Codex"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/how-remote-mcp-servers-trigger-oauth-sign-in", "markdown": "https://wpnews.pro/news/how-remote-mcp-servers-trigger-oauth-sign-in.md", "text": "https://wpnews.pro/news/how-remote-mcp-servers-trigger-oauth-sign-in.txt", "jsonld": "https://wpnews.pro/news/how-remote-mcp-servers-trigger-oauth-sign-in.jsonld"}}