All MCP setup guides
MCPTroubleshootingOAuth

MCP Server Sign-In Works, Then Every Call Fails with 401: How to Fix It

The OAuth consent screen approves fine, but every tool call after that comes back Unauthorized. Here is why that happens — on the client side and the server side — and how to fix each cause.

Last verified September 28, 2026

Why does my MCP server work at sign-in and then fail every call with 401?

Because sign-in and every call after it are two different trust checks. The OAuth consent screen only proves that the authorization code exchange succeeded once, at that moment. Every subsequent MCP request depends on three things independently staying true: the client still holds a valid access token, that token names the exact server as its intended audience, and the server's own validation accepts it. A 401 after a clean sign-in means one of those three broke — and which one depends on whether the fault is in the client, the token itself, or the server.

This page walks through both sides: what a user can fix by reconnecting, and what a server owner has to fix in their own auth stack.

First, tell a 401 apart from a 403

Per the MCP authorization specification, these are different failures with different fixes:

CodeMeaningWhat it tells you
401 UnauthorizedNo token, or an invalid/expired oneIdentity problem — the token isn't being accepted at all. Everything on this page applies.
403 ForbiddenToken is valid but missing a required scopePermissions problem — check the scope value in the WWW-Authenticate header or error body for the scope name the operation needs, then re-authorize with that scope included.

A spec-compliant 403 carries WWW-Authenticate: Bearer error="insufficient_scope", scope="..." — if you see that, you don't have a 401 problem, you have a scope problem, and reconnecting won't help until the server is asked for the right scope.

Where to see the actual error, per client

  • Claude (web and Desktop) — a failed connection or sign-in shows an error, and the page URL carries a reference ID starting with ofid_; include it when you report the problem. A failed tool call on a connected connector shows "Unexpected error while invoking tool" instead, with no reference ID.
  • Claude Code — run /mcp to see each server's status and re-authenticate a remote server that needs sign-in.
  • ChatGPT — a connector holding a stale or revoked credential often shows a generic "the connector's server isn't responding" rather than naming the 401 explicitly; if a connector that was working suddenly can't list or call tools, treat it as a credential problem first, not a downtime problem.
  • Cursor — OAuth failures surface as an "Unauthorized" or "Invalid OAuth Error Response" state in the MCP settings panel next to the server entry.
  • VS Code — a server stuck on a stale token keeps failing with 401 silently in the background; check the MCP output channel/log for the server, or use MCP: List Servers to see its status.

User-side causes and fixes

These are the things a user can fix themselves, without touching server configuration.

1. The access token expired and was never refreshed

Access tokens are short-lived by design (commonly around an hour), and the client is supposed to use its refresh token to get a new one automatically. Several clients don't reliably do this: VS Code has documented behavior where, when a cached token becomes invalid, it keeps sending the stale token, receives a 401, and does not automatically refresh it or re-run discovery (microsoft/vscode#263990; recovery requires Sign Out, "forget cached tools," or Authentication: Remove Dynamic Authentication Providers, then restarting the server). ChatGPT users have reported the same shape: a connector shows as connected but calls fail with 401 because the credential expired, and the only reliable fix is to disconnect and go through OAuth again.

Fix: remove and re-add the connector (see the reset below). If your client has a narrower option — VS Code's Sign Out / Remove Dynamic Authentication Providers, Claude Code's /mcp re-authentication — try that first since it's less disruptive.

2. The token was issued for a different resource — audience or path mismatch

Per RFC 8707 (Resource Indicators), a client is supposed to send a resource parameter naming the exact canonical URI of the server it intends to use the token with, and the authorization server should audience-restrict the token to that URI. The MCP spec makes this mandatory for clients and requires servers to validate the audience before accepting a token. If the client requested a token for one URL and then calls a different URL — most commonly a trailing-slash mismatch (/mcp vs /mcp/) or a different path than what was registered — the server will reject it as not intended for it, even though the token is otherwise valid and unexpired. This is a token problem, not a config-key problem, and it's a different failure from the config-key mismatches covered in MCP Server Configured but Not Showing Up — that page is about the entry never being read at all; this one is about the entry being read correctly and the token still being refused.

Fix: confirm the server URL in your client's config matches, character for character including trailing slash, the URL the server actually publishes (copy it fresh from the server owner's dashboard rather than retyping it). If it matches and calls still fail, this becomes a server-side check (see below).

3. Stale cached client registration (DCR) after the server rotated its auth setup

Most MCP clients register themselves with the authorization server the first time they connect (Dynamic Client Registration) and cache the resulting client_id locally. If the server owner rotates or resets their authorization server, that cached client ID becomes invalid, but the client keeps using it. Cursor has a documented version of this: it retains OAuth client IDs after a server is removed, stored in its local application data, and reusing a server URL can replay the old client ID instead of registering fresh — the practical fix reported is deleting the cached OAuth entries (or, in the worst case, reinstalling the client) to force new registration. VS Code has the same failure mode named directly: it does not evict a stale dynamically-registered client or expired token on a 401, so it keeps retrying with credentials the server no longer recognizes (microsoft/vscode#321834).

Fix: a full remove-and-re-add of the connector is the reliable fix here, because a partial "reconnect" in some clients reuses the cached client registration rather than discarding it. If your client has an explicit "clear auth" or "remove cached credentials" action, use that instead of just re-authorizing, since re-authorizing alone may not clear the stale registration.

4. Client clock skew

Some token validators check the token's issued-at (iat) time against the local clock with zero tolerance. If the client machine's clock is behind the authorization server's, a freshly issued token can look like it's "not yet valid" and get rejected — a real, documented failure mode, reported for example against an MCP server whose client library applied no leeway on iat validation (dbt-labs/dbt-mcp#903). This shows up as an invalid_token error immediately after a sign-in that otherwise looked successful, and it's easy to mistake for a broken auth flow when it's actually a wrong system clock.

Fix: check that your machine's clock is set to sync automatically (most OSes do this by default) and is not drifted by more than a few seconds. This is worth checking early because it produces the exact same symptom — works at sign-in, fails right after — as the more common causes above, but none of the reconnect steps fix it.

The user-level reset: remove and re-add the connector

For nearly every cause above, this is the fastest path back to working:

  1. Remove the connector entirely from your client — not just "disconnect," but delete the server entry so cached tokens and client registration go with it. In Claude this is Customize → Connectors → Remove (authentication settings can't be edited after adding, so remove and re-add is the documented path); in Claude Code, remove the server with claude mcp remove <name> and add it again; in ChatGPT, disconnect the app under Connectors before reconnecting; in Cursor and VS Code, remove the server from MCP settings rather than just retrying the call.
  2. Re-add it using the exact server URL from the server owner's current dashboard or docs — don't reuse a URL you had saved from before, in case it changed.
  3. Complete the OAuth consent screen again. This forces a new authorization code exchange, a new token issued for the current resource URI, and in most clients a fresh client registration.
  4. Make one call to confirm it's actually using the new token, not just showing "connected."

If a clean remove-and-re-add still 401s on the very next call, the cause is very likely on the server side, not the client — move to the next section.

Server-owner-side causes and fixes

These require access to the MCP server's own configuration or authorization server. If you're a user hitting a 401 that a full reconnect doesn't fix, this is what to ask the server owner to check.

1. WWW-Authenticate is missing the resource_metadata parameter

Per RFC 9728 and the MCP spec, a compliant 401 response must look like:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource"

The resource_metadata URL is how a client discovers which authorization server to use and what scopes are expected. If the server returns a bare 401 without this header — or without the .well-known/oauth-protected-resource document actually existing at that URL and returning valid JSON — compliant clients have nothing to discover, and less strict clients may fall back to a token they already have that isn't actually valid for this server, producing a loop of "sign in, then 401" instead of a clean re-auth prompt.

Fix: serve WWW-Authenticate with resource_metadata on every 401, and confirm the protected resource metadata document is reachable and returns the correct authorization server(s) and scopes_supported.

2. Audience validation is too strict, too loose, or checking the wrong value

The MCP spec requires servers to validate that a token's audience was issued specifically for them, per RFC 8707. Two failure directions are both common:

  • Too strict / wrong comparison: the server compares the token's audience against a URL that doesn't exactly match what it told clients to request (for example, checking against a URL with a trailing slash when it told clients to request one without, or checking against a bare hostname when the canonical URI includes a path). Every legitimately issued token then fails audience validation.
  • Too loose: the server accepts any token from its authorization server regardless of audience, which isn't a 401 symptom itself, but means a genuine audience-mismatch bug elsewhere goes undetected until it's caught as a security issue instead of a support ticket.

Fix: log the audience claim on every rejected token during a 401 spike and diff it against the exact canonical URI your protected resource metadata advertises. Per the spec, the canonical form should have no trailing slash and no fragment; accept both cases from clients for robustness but be consistent about what you issue.

3. Scopes requested don't match scopes the server expects for the operation

If the initial WWW-Authenticate 401 doesn't include a scope parameter, per the spec a client falls back to requesting everything in scopes_supported from the protected resource metadata — but if that field is missing or incomplete, the client may request too little, get a token, and then have some calls succeed and others fail. This tends to look like "some tools work, others 401" rather than a clean full-server failure, but it's worth ruling out when the failure isn't universal across every tool.

Fix: make sure scopes_supported in your protected resource metadata is complete, and that 401 responses include a scope parameter naming what's needed for that specific request, per RFC 6750 Section 3.

4. A reverse proxy or CDN in front of the server strips the Authorization header

If the MCP server sits behind a load balancer, CDN, or API gateway, that layer can be configured (often unintentionally) to strip the Authorization header before forwarding the request — common with default caching or WAF rule configurations that treat auth headers as something to normalize away. The client sends a perfectly valid, unexpired, correctly-audienced token, and the server never sees it, so it responds 401 exactly as if no token were sent at all.

Fix: confirm the header reaches your application layer by logging the raw incoming headers at the edge versus at the application, not just after your framework has parsed them. If you also redirect between hostnames (for example a bare domain to a subdomain, or http to https), check whether the redirect drops the Authorization header — some HTTP clients, including some MCP clients, intentionally do not forward Authorization across a cross-host redirect for security reasons, which will look identical to a proxy stripping it.

5. Refresh tokens aren't being issued, or aren't confidential

Per the MCP spec's guidance on refresh tokens, a client that wants long-lived access should include refresh_token in its registered grant_types and may request the offline_access scope if the authorization server advertises it — but the authorization server retains full discretion over whether to actually issue one. If your authorization server never issues refresh tokens, every client is forced to re-run the full user-facing OAuth flow every time the short-lived access token expires, which produces exactly the symptom in this page's title on a predictable schedule (typically hourly) even though nothing is actually broken.

Fix: if you control the authorization server, issue refresh tokens for clients that request offline_access and support the grant_types metadata correctly. If you don't control it (e.g., you're relying on a third-party IdP), document the access token lifetime clearly so users don't mistake scheduled re-auth for a bug.

Quick diagnostic order

  1. Check the code: 401 or 403? A 403 needs a scope fix, not anything on this page.
  2. Check the clock: rule out client clock skew — free, and it's often overlooked because it looks identical to a broken flow.
  3. Do the user-level reset: remove and re-add the connector, completing a fresh OAuth consent. This clears the most common client-side causes (expired token, stale DCR cache) in one step.
  4. Still 401 immediately after a clean reconnect? It's very likely server-side. Check, in order: is resource_metadata present on the 401, does the audience the server validates against exactly match the canonical URI it advertises, and is anything between the client and the server (proxy, CDN, redirect) capable of dropping the Authorization header.

Sources

The dated claims on this page were checked against these vendor documents. Client UIs move constantly, so if one of these has changed since the date shown, trust the vendor over this page — and tell us.

Frequently asked questions

Why does an MCP server authorize fine but then every call returns 401?
The consent screen only proves the authorization code exchange worked once. Every call afterward depends on the client attaching a still-valid access token, that token's audience matching the exact server URL, and the server accepting it — any of those can fail silently after a successful sign-in. The most common causes are an access token that expired and never got refreshed, a token issued for a different resource URL (audience or path mismatch), or a proxy in front of the server stripping the Authorization header before it arrives.
Is a 401 the same problem as a 403 on an MCP server?
No. Per the MCP authorization spec, 401 means no token or an invalid or expired one, and 403 means the token is valid but does not carry a scope the operation requires. A 403 is a permissions problem — check what scope the WWW-Authenticate header or error message names as missing. A 401 is an identity problem — the token itself is not being accepted, and the fixes below apply.
Does removing and re-adding the connector actually fix a 401 after sign-in?
Often yes, and it is the right first move for a user who cannot edit server-side config: it forces a fresh authorization code exchange, discards a stale or corrupted cached token, and in most clients triggers new dynamic client registration if the old registration was the problem. It will not fix causes that live on the server (a missing resource_metadata header, a token audience the server never accepts) — those need the server owner. If reconnecting once does not clear it, the cause is more likely server-side than client-side.