Teams shipping a remote MCP server that real users outside the team will connect to.
Whether an MCP server is ready to be reachable from a client you do not control.
The control gap
Local MCP servers over stdio inherit the trust of the machine they run on. A remote server has none of that. The specification supplies the pieces, but each one fails independently, and the common failure is a server that authenticates correctly in the happy path while accepting a token issued for something else, or one that never revokes.
What good looks like
Each item below either passes with a reproducible test or is written down as a known gap with an owner, rather than being assumed.
- Discovery: an unauthenticated request returns 401 with a resource_metadata pointer, and that document resolves.
- Registration: unknown clients can register as public PKCE clients, and confidential auth methods are refused.
- Binding: tokens carry an audience matching this endpoint, and a token for a sibling endpoint is rejected.
- Revocation: withdrawing consent stops the next tool call, including on an already open session.
A production workflow
- Run the unauthenticated request and capture the challenge header verbatim.
- Complete registration and the authorization code flow from a clean client with no cached state.
- Present a token from a different resource and confirm the rejection is 401 with invalid_token.
- Revoke the consent and confirm the next call fails without requiring a reconnect.
Copy this
Each line is a request and the response that counts as a pass. Anything that only passes by hand-editing a config file is not ready for an external client.
1 curl -i https://host/mcp/<slug>
PASS: 401 with WWW-Authenticate carrying resource_metadata=
2 curl https://host/.well-known/oauth-protected-resource/mcp/<slug>
PASS: JSON with resource, authorization_servers, scopes_supported
3 curl https://host/.well-known/oauth-authorization-server
PASS: registration_endpoint present, code_challenge_methods_supported = ["S256"]
4 Register with token_endpoint_auth_method: "client_secret_post"
PASS: rejected
5 Authorize without code_challenge
PASS: rejected
6 Token request with a resource different from the authorization request
PASS: rejected
7 Present a token minted for /mcp/<other-slug>
PASS: 401 invalid_token
8 Present a tools:read token to a tools:call operation
PASS: 403 with WWW-Authenticate insufficient_scope, scope="tools:call"
9 Revoke consent, then call a tool on the existing session
PASS: fails immediately, no reconnect required
10 Delete the session, then replay the session header
PASS: 404, client re-initializesItems 6, 7 and 9 are the ones that most often fail on a server that otherwise looks correct. They are also the three whose failure is invisible until someone tests for it deliberately.
Evidence to require
- The captured response for each numbered check, with the date it was run.
- The scope set the server actually enforces, compared with the scope set it advertises.
- Revocation latency measured on a live session rather than described in a design document.
- A list of clients currently holding valid consents, with expiry dates.
Buyer checklist
- Which of these ten checks currently fail, and who owns each gap?
- Is the check suite run on every deploy or only before launch?
- Does the staging environment issue tokens that would be rejected in production, and vice versa?
- Who can revoke a consent, and how long does revocation take to bite?
Practical answers
Common implementation questions
Do local stdio servers need any of this?
Not the OAuth parts, but the trust assumption should be explicit. A stdio server runs with the user's own authority, so the control question moves to which tools it exposes and what those tools can reach.
Can we start with a static API key?
You can, and many teams do while a server is internal. Write down that clients cannot discover it, it does not identify a user, and it does not revoke per client, so the migration is planned rather than forced by an incident.
What does Endram provide here?
Endram implements the discovery, registration, PKCE, resource binding, and scope enforcement described above in front of an existing MCP server, so the server itself keeps its original transport and tools.