cd /news/agent-protocols/how-remote-mcp-servers-trigger-oauth… · home › topics › agent-protocols › article
[ARTICLE · art-166243] src=deallocatedobjects.com ↗ pub= topic=agent-protocols verified=true sentiment=· neutral

How Remote MCP Servers Trigger OAuth Sign-In

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.

by read3 min views2 publishedNov 9, 2026
How Remote MCP Servers Trigger OAuth Sign-In
Image: Deallocatedobjects (auto-discovered)

Jake Marsh When 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.

The Response That Starts It# #

The flow in the MCP authorization spec begins when the client’s request gets a 401 with a WWW-Authenticate header pointing at your protected resource metadata:

The expose header is there for clients running in a browser. Without it, their JavaScript can’t read the challenge.

That URL returns a small JSON document ([RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728)) naming your authorization server:

From 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.

## Two Callers Want Opposite Things[#](#two-callers-want-opposite-things)

If 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.

Directories 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 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.

Answer By Who’s Asking# #

So the endpoint looks at the caller. This is the version in Taskhold, trimmed down. Clients that sign in get challenged, and everything else can read the public parts:

The 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.

A 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.

Which Default To Pick# #

Taskhold 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:

  • With a list of OAuth clients, a new client gets a 200 and may not offer sign-in until someone adds it.
  • With a list of checkers, a new checker gets a 401 and may list you as down.

I’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.

There are exceptions on both sides. The CI for 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.

Log The 401s# #

Some 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.

I 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.

You can check your own server’s answer with curl by changing the User-Agent:

Run 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.

── more in #agent-protocols 4 stories · sorted by recency
── more on @jake marsh 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/how-remote-mcp-serve…] indexed:0 read:3min 2026-11-09 · —