If remote MCP Connect finishes the browser login and then every tool call still returns 401 (often invalid_token), the usual cause is the token itself: it was issued for a different audience or resource than the one your MCP server checks. MCP authorization guidance has clients name the target server with the OAuth resource parameter (RFC 8707), and servers must only accept tokens meant for them. If the strings don’t match exactly, login succeeds and every call fails. This page covers that post-login check — not discovery. Trailing slash, host form, and environment drift are enough to break an otherwise successful login.
Symptom card
- Browser login completes and Connect reports success.
- `tools/list` or the first tool call returns 401, sometimes with `WWW-Authenticate: Bearer error="invalid_token"`.
- Reconnecting repeats the same loop.
- Inspector shows a token was issued, so the authorization server (AS) isn't the problem. The server is rejecting a token that isn't addressed to it.
Discovery (how the client finds the resource identifier) lives in Protected Resource Metadata in plain English. This page stays on the post-login audience check.
How the resource gets bound to the token
- Your server's PRM advertises its resource identifier.
- The client sends that identifier as `resource` on the authorize and token requests.
- The AS issues a token bound to it, typically through the `aud` claim on a JWT or through introspection.
- Your server validates the audience against its own canonical identifier before it serves anything.
A mismatch at any of these steps causes the 401.
Canonical URL pitfalls
- Trailing slash: `https://api.example.com/mcp` and `https://api.example.com/mcp/` are different strings.
- Host form: apex vs `www`, mixed case, or an internal hostname behind a proxy.
- Port and path: a `:443` written out explicitly, or `/mcp` vs `/v1/mcp`.
- Environment drift: a staging resource in the AS config and a prod URL in PRM, or the other way round.
- AS defaults: some authorization servers ignore `resource` unless it's enabled and fall back to a generic audience. Check your AS docs instead of assuming.
Pick one canonical identifier, publish it in PRM, configure the AS to accept it, and compare against that same string on the server.
Checking it without leaking anything
Run the flow in MCP Inspector. If the access token is a JWT, decode it locally and read `aud`. Otherwise use your AS's introspection endpoint. Compare the value with what your server expects. Never paste live tokens into chats, tickets, or AI assistants.
Where the kit fits
The MCP Starter Kit ($27) ships a remote MCP layout with challenge, PRM, and audience validation wired to a single canonical identifier, so Cursor and Claude Connect don't fall into this loop. Soft path: start at /free; Cheat Sheet for the skim checklist mindset; kit when you want the auth-ready layout.
Soft next step
Lock one canonical resource identifier, then compare `aud` against it with Inspector. Soft ladder: /free, then the MCP Starter Kit ($27). Related: OAuth pillar, auth checklist, PRM in plain English.