I kept seeing tutorials on building Model Context Protocol servers that wrapped a couple of tool definitions in an Express or Hono handler, tacked on a static bearer token check, and called it production-ready. That mental model worked fine when testing with local inspector scripts. But the moment I deployed the server to a real domain and tried plugging it into Claude.ai and ChatGPT, everything broke. Neither client asked for an API key. Instead, they hit the endpoint unauthenticated, received an uninformative response, and gave up. Here is what I learned about how remote MCP authentication actually works, why standard API keys are not what AI clients reach for first, and how the OAuth discovery dance fits together in Hono.
What It Is
Remote MCP authentication is an automated, standards-based discovery and authorization handshake between an AI agent and your API, governed primarily by RFC 8414 and RFC 9728.
When you connect a modern AI client like Claude Desktop or ChatGPT to a remote MCP endpoint over Streamable HTTP, the client does not expect the human operator to manually paste secret keys into a form. Instead, the agent initiates an unauthenticated probe. Your server is expected to reject this probe with a standard 401 Unauthorized challenge that contains a resource_metadata URI. The client follows this link, discovers your authorization server endpoints, dynamically registers itself as an OAuth application (RFC 7591), and directs the user through an authorization code flow with PKCE before ever exchanging a single MCP protocol frame.
The Mental Model: The Valet and the Hotel Concierge
Think of a static API key like giving a delivery driver your master building key: it works, but it gives unconditional access, cannot be easily scoped per user session, and requires manual out-of-band setup.
The MCP OAuth discovery pattern works more like a hotel concierge checking in a valet. When the valet (the AI client) arrives at the garage gate (/api/mcp), the guard does not say "go away"; the guard hands them a card pointing to the concierge desk (/.well-known/oauth-protected-resource). The valet walks over to the desk, registers their company name dynamically, gets temporary visitor credentials, and asks you (the owner) to sign a slip approving access. Only after you sign does the valet return to the gate with a validated badge. The gatekeeper checks the badge, confirms which room it belongs to, and unlocks only that room's parking bay.
When To Use It
You should implement the full OAuth 2.0 discovery contract whenever your MCP server runs on a public domain and serves external agents such as Claude.ai, ChatGPT Custom Actions, or multi-tenant web clients.
In multi-tenant SaaS environments where tools manipulate private user data—like an image hosting service where uploads must be tagged to the authenticated user's gallery—OAuth is mandatory. It eliminates the need for users to manually generate and copy database secret keys into third-party chat interfaces. Because the flow terminates in an authorization code grant tied to your primary authentication system (such as Better Auth), every downstream tool invocation automatically inherits the authenticated user's identity.
If you are building on Next.js or looking for a full implementation of dynamic client registration, Upstash Redis token storage, and custom HTML consent screens for Claude Web and CLI, read our companion guide OAuth for MCP Server: Complete Guide to Protecting Claude. While that walkthrough handles Redis session persistence, this guide focuses on the protocol handshake nuances—RFC 9728 resource metadata, Streamable HTTP challenges, and Hono CORS exposure—that remote endpoints require when serving both Claude and ChatGPT.
Do not implement this flow if your MCP server is strictly local and runs over stdio inside a desktop environment like Cursor or Claude Desktop on your personal laptop.
In local process-spawned integrations, the client and the server share the same process boundary or operating system user. Adding OAuth discovery here introduces unnecessary network round-trips, database tables, and browser redirects for zero security gain. Similarly, if your MCP server is intended solely for internal server-to-server cron jobs or automated CI/CD runners, a permanent personal API key or a static master token passed directly in the Authorization header is significantly simpler to maintain and debug.
Gotchas & Common Mistakes
1. The Missing resource_metadata Challenge Header
The most common failure happens on the very first HTTP request. When an unauthorized client hits your MCP route, returning a simple JSON { "error": "Unauthorized" } with a 401 status code causes Claude or ChatGPT to fail immediately with a generic connection error.
AI clients do not assume OAuth is supported unless your 401 Unauthorized response explicitly broadcasts where to find the Protected Resource Metadata per RFC 9728. In Hono, you must supply the WWW-Authenticate header containing the exact metadata URL.
If that header or the resource_metadata parameter is missing, the client has no way to locate your authorization server and terminates the handshake.
2. Failing to Expose CORS Headers
Even if your server responds with the proper WWW-Authenticate header, web-based clients running in the browser will drop it if your CORS configuration does not explicitly expose it.
Standard browser security prevents client-side scripts from reading response headers unless they are safelisted in Access-Control-Expose-Headers. When setting up your Hono middleware, you must expose both the authentication challenge and MCP protocol headers.
Omitting WWW-Authenticate from exposeHeaders leaves web clients blind to the discovery URL.
3. Missing the RFC 9207 iss Parameter on Redirects
Once the user approves the authorization prompt in their browser, your server redirects back to the client's redirect_uri with an authorization code. Modern MCP clients enforce RFC 9207 to prevent authorization code injection attacks.
RFC 9207 requires that the authorization server append an iss (issuer) query parameter to the redirect URL matching the issuer URL declared in your metadata. If your authentication library redirects without this parameter, clients like Claude will reject the code exchange during the final step.
Capturing the redirect response and appending the verified issuer parameter ensures strict compliance with client security verifiers.
Conclusion
Building a production-ready remote MCP server requires treating authentication as an automated protocol conversation rather than a static header check. When an AI agent connects to your backend, it relies on RFC 9728 protected resource discovery, RFC 8414 metadata, dynamic registration, and RFC 9207 redirect binding to establish trust autonomously.
Reach for full OAuth discovery when your MCP tools operate on user-scoped data in cloud environments, and stick to simple personal tokens or stdio when running local desktop tools.
If you have questions or ran into a different gotcha, drop a comment below. And if you found this useful, subscribe for more.