An agent points at a remote MCP server, the connection fails, and the log says 401 Unauthorized — or 403 Forbidden. The reflex is to treat it like any other 401: something is broken, a token is wrong, fix the credential. Sometimes that is exactly right. Just as often, the 401 is the server working correctly — it is supposed to challenge for a credential, and the real problem is somewhere upstream of the status code. The two cases look identical in a one-line log, and they need opposite responses. Telling them apart is most of the debugging.
This matters more for MCP than for a typical REST API, because MCP's authorization story is young and the tooling around it is younger. The spec's HTTP transport defines an OAuth 2.1 flow — the server challenges with a 401 and a WWW-Authenticate header pointing at its protected-resource metadata, the client discovers the authorization server, obtains a token, and retries. A lot of servers half-implement it, a lot of clients don't implement the discovery half at all, and a lot of endpoints sit behind a gateway that returns its own 401 before the MCP layer is ever reached. Every one of those produces the same first symptom.
A 401 Is Not a Failure — It's a State
The most useful reframing: for a remote MCP server, 401/403 is a posture, not a fault. A server that answers 401 on an unauthenticated request is reachable, it accepted the connection, it parsed the request enough to decide you need a credential, and it told you so. That is a working server behind a door. Contrast it with the genuinely broken cases — a connection refused, a 5xx, a hung socket, an initialize response that doesn't parse. Those are down. An authenticated challenge is up, gated.
That distinction is why a good MCP monitor does not collapse 401 into "down." When Merlonix's MCP health checker probes an endpoint and sees it is reachable but answered 401 or 403 before the handshake completed, it classifies the result as degraded, not down, with a finding that says so in words: "MCP endpoint is up but requires authentication (HTTP 401). It's reachable; add an access token to this monitor for a full behind-the-auth health check." The endpoint is doing its job. What it can't do — yet — is prove the handshake and tool inventory behind the door are healthy, because nobody handed it a key.
If you page yourself every time a private MCP server returns 401 to an anonymous prober, you will page yourself forever, on a server that is perfectly fine. The alert you actually want fires when a server that used to complete an authenticated handshake starts returning 401 to a request carrying the same credential — that is a real regression. The unauthenticated 401 is the steady state.
What Actually Causes the 401
When the 401 is the bug — the credential is present and still rejected — it is almost always one of a short list of causes. In rough order of how often they bite:
-
The client never did the OAuth discovery step. The MCP HTTP-auth flow expects the client to read the
WWW-Authenticateheader on the401, fetch the server's protected-resource metadata, find the authorization server, and get a token. Clients that predate the auth spec, or that only know how to send a static bearer token, skip all of that and send nothing — so the retry is as unauthenticated as the first attempt. The 401 loops. -
The token is for the wrong audience. OAuth 2.1 resource indicators mean a token minted for one resource is not valid for another. A token that works against the authorization server's own userinfo endpoint can still be rejected by the MCP resource server if the
resource/audience it was issued for doesn't match the endpoint you're calling. The token is real; it is just not for this door. -
The token expired and nothing refreshed it. Access tokens are short-lived by design. A long-running agent that grabbed a token at startup and never refreshed it will sail along until the token ages out, then get a
401on every subsequent call — intermittently, from the operator's point of view, which makes it read like flakiness rather than an auth bug. -
A gateway is answering, not the MCP server. Cloudflare Access, an API gateway, a reverse proxy, or an auth middleware can return its own
401/403before the request ever reaches the MCP handler. The status code is identical, but the fix is in the gateway config, not the MCP server — and a giveaway is that theWWW-Authenticateheader (or the HTML body) names the gateway's identity provider, not the server's. -
The header is malformed or missing. A server that challenges with
401but no usableWWW-Authenticateheader gives a spec-compliant client nothing to discover, so the client can't start the flow. This is a server bug that presents as a client failure. -
HTTP method or content-type rejection dressed up as auth. Some servers return
403for a wrongAcceptheader or an unexpected method on the MCP endpoint. That is not really an authorization problem, but it lands in the same bucket when you're reading status codes.
Notice that only two of those are fixed on the server. The rest live in the client, the token, or the infrastructure in front — which is why "the server returns 401" is a starting point for debugging, not a diagnosis.
How to Tell Auth-Gated From Down Without a Credential
You can learn a surprising amount about an MCP server you have no token for, precisely because the interesting information is in how it refuses you.
- Reachable + 401/403 → up, gated. The TCP/TLS connection succeeded and the server answered. This is the healthy-behind-auth state. Roughly a quarter of the public remote MCP servers we've probed require authentication, so this is common and correct — see the measured breakdown in we health-checked 86 public MCP servers.
-
Reachable + 200 but the
initializehandshake doesn't parse → up, but not a working MCP server, or mid-migration. That is a different failure than auth, and it's the subject of why MCP initialize fails. - Connection refused / timeout / 5xx → down. No door, not a gated door.
-
WWW-Authenticatepresent → the server is inviting the OAuth flow; read it for the authorization-server URL and theresourceyou need a token for. -
WWW-Authenticateabsent on a 401 → the server is gating but not telling you how to get in. Either it expects a static token out-of-band, or its auth implementation is incomplete.
You can run all of this by hand. A single curl at the endpoint with no credential tells you reachable-vs-down and shows you the challenge headers; the MCP health-check walkthrough has the exact request to send. What a one-shot manual probe can't tell you is whether today's 401 is the same steady-state 401 you had yesterday or a new regression — that needs a check with memory.
What This Means If You Operate the Server
If you run a remote MCP server, the goal is for your 401 to be the good kind — a correct, discoverable challenge — and never the bad kind.
-
Always send a
WWW-Authenticateheader on the401. It is the thing that lets a compliant client start the OAuth flow instead of giving up. A401without it is a locked door with no keyhole. -
Serve auth over HTTPS, always. A bearer token sent to a plaintext
http://endpoint is exposed on the wire. Merlonix grades transport as its own dimension for exactly this reason; a plaintext MCP endpoint that also handles credentials is the worst of both. -
Decide, deliberately, whether you need auth at all. An unauthenticated server that exposes callable tools is the marquee 2026 MCP exposure — anyone who can reach the URL can call every tool. If your tools do anything sensitive, a
401is a feature. The full list of what to check before shipping is in the remote MCP server security checklist. - Keep token lifetimes and refresh behavior documented, so a client author knows the token they minted will age out and needs refreshing — the single most common cause of "it worked this morning."
What This Means If You Consume One
-
Read the
401, don't just retry it. TheWWW-Authenticateheader and the response body usually tell you whether you're talking to the MCP server or a gateway in front of it, and where the authorization server is. -
Check the token's audience, not just its validity. A token that authenticates fine elsewhere can be the wrong
resourcefor this endpoint. -
Monitor an authenticated server with its credential. An anonymous check of a private server will forever report
401/degraded — true, but not useful. Give the monitor a scoped access token and it can complete the handshake, readtools/list, and watch the real health signals behind the door; without one, every check just re-confirms the server has a lock.
That last point is worth making concrete. A remote MCP server that requires auth is only half-monitored by an unauthenticated probe: you learn it's reachable and gated, and nothing else. Merlonix's free MCP health checker will tell you the auth posture of any endpoint in one shot — reachable, gated, transport-secure — and if you add a scoped access token to a monitored check, it runs the full handshake, tool-inventory, and drift checks behind the auth instead of stopping at the door. The MCP directory shows how a range of live servers present their auth posture from the outside, and if you build or operate MCP servers for a living, MCP server developers collects the rest of the toolchain in one place.
A 401 from a remote MCP server is the beginning of a question — up and gated, or actually broken? — and answering it correctly is the difference between a page you should ignore and a regression you should chase.
This post first appeared on the Merlonix blog.
Top comments (1)
Treating 401 as a state rather than a generic outage is exactly right. I would make the monitor's state machine more explicit:
reachable_unauthenticated,challenge_valid,metadata_discoverable,token_obtained,authenticated_handshake_ok, andauthorized_tool_probe_ok.Fingerprint the challenge too: status,
WWW-Authenticatescheme/parameters, protected-resource metadata URL, authorization-server issuer, and TLS identity. Alert on semantic changes rather than the steady-state 401 itself. A gateway-generated HTML 401 and an MCP OAuth challenge may share a status code but imply completely different recovery paths.For the authenticated canary, use a dedicated least-privilege principal and a harmless read-only tool—not a production user token. Test wrong audience, expired token, missing scope, rotated signing key, and a valid token denied for one tool. That separates authentication health from authorization policy health and catches the dangerous case where “login works” but scope enforcement drifted.