I had a remote MCP server that worked with every client I pointed at it. Claude connected and ran tools, and so did ChatGPT. The OAuth flow issued tokens, the tokens validated, and the whole thing looked finished. Then I connected Grok, its OAuth initialization asked my server a discovery question no other client had asked, and the integration stopped cold before a single tool call. My authentication, my MCP implementation, and my OAuth server were all fine. What failed was an assumption I didn't know I was making, and this article is about that class of assumption.
The first MCP servers most of us built were local adapters. You launched a process, talked to it over stdio, exposed a few tools, and suddenly Claude could drive your application. That proved the idea, and then the moment you put an MCP server on a public URL, all the boring questions that Internet software eventually has to answer showed up at once. How does a client discover it, and which OAuth resource is a token actually for? How do you host multiple MCP endpoints behind one domain and run them on Lambda, containers, or an ordinary reverse proxy? Can a server have an identity, can a tool have an icon, and can a tool return a user interface instead of describing one in text?
As of August 2026, the current MCP specification is the 2026-07-28 revision,1 and it goes straight at those questions. The protocol core is stateless now, with the old initialization handshake and session ID gone and discovery made explicit. Routing information can travel in HTTP headers, caching is becoming first-class, OAuth has been hardened, and extensions finally have a formal home. MCP is growing up, and it's growing up by turning into ordinary Internet infrastructure.
HTTP Can Finally See What MCP Is Doing
The next change sounds small until you have operated one of these things in production. The specification introduces HTTP headers such as `Mcp-Method` and `Mcp-Name`, which let infrastructure understand what kind of MCP request is passing through without parsing the JSON-RPC body. A proxy can now tell `tools/call` from `resources/read`, and potentially which specific tool is being invoked.
The MCP server is never the whole system. It sits behind a reverse proxy, an authorization layer, and a rate limiter, with a telemetry pipeline and probably a pile of dashboards hanging off it, and until now every one of those saw the same opaque `POST /mcp`. With the method and tool name visible at the HTTP layer, I can rate-limit `deploy_production` to ten requests a minute while `search_documents` gets a hundred, collect per-tool metrics without teaching half my infrastructure to parse MCP JSON, and apply different security policies to different operations.
None of this looks exciting in a demo, and all of it matters when you're the one responsible for the thing at two in the morning. Boring production is a feature.2
OAuth Is Where Remote MCP Gets Serious
Authentication has taken more of my recent attention than any other part of this, because once your MCP server is public, authentication stops being a bearer token you made up and shared out of band. The MCP authorization model is built on the existing OAuth ecosystem instead of a new MCP-specific authentication protocol, which was the right call, since we already paid for that machinery.
Six standards do most of the work. RFC 6750 covers bearer token usage, and RFC 8414 describes the authorization server, so a client can find its authorization endpoint, token endpoint, and the rest of its OAuth services. RFC 8707, Resource Indicators, lets a client tell an authorization server which protected resource it wants a token for. RFC 9728, Protected Resource Metadata, lets the MCP resource describe itself and name the authorization servers that can issue credentials for it.3 RFC 9207 lets the authorization response identify its issuer, which defends against authorization-server mix-up attacks, and the July MCP revision specifically hardened authorization around that behavior. Dynamic Client Registration, RFC 7591, is still in the picture for compatibility, though MCP is moving away from it and toward Client ID Metadata Documents.
That reads like RFC soup until you map who is responsible for what. The resource describes itself and says who vouches for it, and the authorization server publishes where its endpoints are. The client identifies itself, and the authorization request names the resource the token is for.
The RFC 9728 Detail That Broke My Server
Suppose you expose one MCP server at `https://mcp.example.com/mcp`. Its Protected Resource Metadata lives at `https://mcp.example.com/.well-known/oauth-protected-resource`, and that is the form most people meet first. Now suppose you are building a multi-tenant service where each endpoint has its own resource identity, such as `/mcp/acme`, `/mcp/contoso`, or `/mcp/12345`. RFC 9728 defines how resource identifiers containing paths are handled, and for `https://mcp.example.com/mcp/acme` the metadata location becomes:
https://mcp.example.com/.well-known/oauth-protected-resource/mcp/acmeThe well-known segment is inserted between the host and the path, and the `resource` property in the returned document must correspond to the identifier used to derive it. A minimal response looks like this:
{
"resource": "https://mcp.example.com/mcp/acme",
"authorization_servers": ["https://auth.example.com"],
"scopes_supported": ["mcp:read", "mcp:write"],
"resource_name": "Acme MCP"
}This is what makes dynamically provisioned MCP endpoints practical. You don't need a subdomain per customer just to establish distinct OAuth resources, because the URL itself carries the resource identity.
It's also the spot where my server fell over when Grok connected. My endpoint's identity lived in the path, and Grok's OAuth initialization required the path-derived metadata form far more strictly than the clients I had been testing against. Authentication worked and the tools worked, but my discovery topology was incomplete, even though nothing in my code was wrong.
The specification is ahead of complete client convergence. ChatGPT now documents RFC 9728 discovery explicitly and sends the resulting identifier as the OAuth `resource` parameter. It also recommends binding that identifier to the token audience, and it supports CIMD alongside DCR. Claude supports remote MCP servers with OAuth through its connector infrastructure, and Grok performs OAuth automatically for servers that require it. Each client exercises a slightly different slice of the spec, and the client you haven't tested yet is the one that finds your assumption.
So implement the path-derived form correctly, keep serving root-level metadata where it helps compatibility, and don't build your OAuth architecture around whichever client happens to accept the least metadata today.
Build to the protocol.
There's a mundane routing consequence hiding in here too. If your MCP endpoint is `POST /mcp/{id}`, you already have a route handing `{id}` to your application. Then a client requests `GET /.well-known/oauth-protected-resource/mcp/{id}`, which is a different route entirely, and your application may know how to answer it and never receive it, because a proxy, router, or hosting layer rejected or rewrote the path first.
Tokens Should Know Where They Work
Protected Resource Metadata tells the client what resource exists. RFC 8707 lets the client request authorization for that specific resource, carrying `resource=https://mcp.example.com/mcp/acme` through the authorization flow so the server can issue a token intended for that resource and nothing else. An access token that only proves Bryan logged in successfully is carrying too little. It should say that this client is authorized to reach this resource with these scopes, and if your tokens are JWTs, that resource typically becomes part of the audience you validate. Authentication answers who you are, and resource binding answers where that credential is allowed to work.
Client registration is getting the same treatment. Dynamic Client Registration made sense for MCP because clients and servers frequently meet for the first time at runtime; the authorization server has never seen this particular client before, and RFC 7591 lets the client register itself on the spot. It works, but it creates persistent registration state just so two Internet services can introduce themselves. With Client ID Metadata Documents, the client ID itself is an HTTPS URL pointing at a document that describes the client, including its name and redirect URIs, and the authorization server can fetch and validate that document without maintaining a registration lifecycle.
MCP Servers Have Faces Now
Icons sound cosmetic until you count how many servers a user ends up connecting. SEP-973 added richer metadata across MCP implementations, tools, resources, and prompts, including icons that come with MIME types, size hints, and light and dark theme variants. A tool can now carry a human-readable title, a description, and an icon on top of its snake_case name, and a server can expose an identity and a website.
Nobody cares about any of this while a user has one MCP server. Once they have fifteen, the gap between a list of lowercase identifiers and a list of recognizable applications with names and icons is the gap between a config file and a product. Tool discovery has been treated as a machine problem since the day MCP shipped, and the person scrolling through those fifteen servers has been part of it the whole time.
MCP Apps Turn Tools into Applications
For a long time MCP had a simple mental model, in which the model calls a tool, the server does something and returns text or structured data, and the model explains the result. MCP Apps break that ceiling. A tool can reference a `ui://` resource containing HTML, and the host fetches that resource, renders it inside a sandboxed iframe, and connects it back through the MCP Apps bridge, so the interface can receive tool results and initiate further tool calls through the host.
Imagine, hypothetically, a deployment MCP. Before Apps, I would ask for the status of release 4832 and the model would retrieve fifteen fields and narrate them into prose. With Apps, the tool can return a deployment view that puts the commit and the health checks right next to a rollback control. The model stays useful because I can talk to the system, and the UI stays useful because I don't need a language model reading a dashboard to me one field at a time.
The sandbox is what keeps it from being terrifying. The application runs inside a sandboxed iframe, MCP Apps defines controls around the UI resource and its content security policy, and interaction with the host goes through defined messaging, so third-party code never gets access to the surrounding Claude or ChatGPT page. Without that boundary, the feature would amount to running arbitrary third-party JavaScript inside your AI client, and nobody wants that architecture.
This is the point where I stopped thinking of MCP as a protocol for exposing tools and started treating it as an application integration layer. MCP Apps grew out of work including MCP-UI and OpenAI's Apps SDK, and the MCP project shipped it as the protocol's first official extension in January 2026, with support across hosts including Claude, ChatGPT, VS Code, and Goose.
The Quiet Changes Are the Operational Ones
Three smaller changes in the July revision round out the picture, and they're the ones an operator feels first.
Discovery is explicit now. A server implements `server/discover` and advertises its supported protocol versions and capabilities, along with instructions and identifying metadata, so a client can ask who you are and what you can do without creating a session just to hear the answer.
Caching is becoming part of the contract. List and read operations carry cache metadata, including TTL and scope hints, and the specification tightened deterministic ordering specifically because stable responses matter for caching and prompt reuse. None of that will excite anyone shipping their first three-tool server, but if every interaction re-fetches the same hundred tool descriptions and schemas, you pay for it in latency, in tokens, and in prompt caches that keep getting invalidated. Once MCP is infrastructure, cacheability belongs in the protocol contract, and leaving it as an optimization somebody might add someday gets expensive.
Stateless doesn't mean non-interactive, either. The protocol now supports multi-round-trip behavior where a server responds with `input_required`, describing the additional input it needs, and the client gathers that information and retries the operation. A stateless transport carrying a stateful human workflow sounds like a contradiction until you see that the state has become explicit, carried in the retried request where both sides can see it.
What I Would Build Today
If I were starting a production remote MCP service today, my baseline would look like this.
1. Use Streamable HTTP for the public remote transport, and stdio only where local integration makes sense.
2. Design the HTTP layer to be stateless, with application state represented explicitly through IDs and handles rather than connection affinity.
3. Support `server/discover` and the current protocol metadata rather than treating the old initialization handshake as permanent architecture.
4. Preserve `Mcp-Method` and `Mcp-Name` through proxies and routing layers, so observability, rate limiting, and security infrastructure can use them.
5. Implement OAuth discovery properly: RFC 9728 Protected Resource Metadata, RFC 8414 authorization server metadata, RFC 8707 resource indicators, bearer-token handling, PKCE where appropriate, and RFC 9207 issuer validation.
6. If MCP resources have path-based identities such as `/mcp/{id}`, serve the RFC 9728 path-derived metadata endpoint rather than assuming the root form is sufficient.
7. Prefer Client ID Metadata Documents for new OAuth client integration, and keep DCR compatibility where the clients you support still need it.
8. Add implementation and tool metadata, including titles, descriptions, icons, and website identity, because it matters more with every integration a user accumulates.
9. Use MCP Apps where an interaction is easier as a form, visualization, dashboard, chooser, or approval screen, and don't build a UI merely because the protocol allows one.
10. Design for caching, observability, authorization boundaries, retries, version compatibility, and failure from the beginning.
That last item matters most, because the MCP server is bigger than the function behind `tools/call`, and the failures that hurt tend to show up in the proxy, the token, or the cache.
MCP Is Becoming Boring, and That Is Exciting
The most interesting thing about MCP right now is how much of it we already know how to operate. Nearly every piece the protocol has picked up lately, from well-known URLs to cache semantics to sandboxed iframes, is something the web worked out long before anyone pointed a model at it, and that's a relief. We spent decades learning painful lessons about how Internet services identify themselves, authenticate clients, and recover from failure, and MCP gets to compose those lessons instead of reinventing them.
Look again at why my server stopped working with Grok. Nothing AI-shaped went wrong. It failed at one of the oldest jobs on the Internet, telling a stranger who you are and who vouches for you, and now that the first generation of MCP has proved an AI can call my software, that old job is where most of the engineering left to do lives.
The RFCs Worth Keeping Open
RFC 9728: OAuth 2.0 Protected Resource Metadata. The important one for discovering the OAuth configuration associated with an MCP resource, including resources identified by URL paths.
RFC 8414: OAuth 2.0 Authorization Server Metadata. Describes the authorization server itself, including its authorization and token endpoints and supported capabilities.
RFC 8707: Resource Indicators for OAuth 2.0. Defines the `resource` parameter that identifies which protected resource an access token is intended for.
RFC 6750: OAuth 2.0 Bearer Token Usage. Defines how bearer access tokens are used with HTTP protected resources.
RFC 9207: OAuth 2.0 Authorization Server Issuer Identification. Adds explicit issuer identification and protects against mix-up attacks.
RFC 7591: OAuth 2.0 Dynamic Client Registration. Still relevant for compatibility while MCP moves toward Client ID Metadata Documents.
MCP Specification 2026-07-28. The revision that moves the core protocol toward stateless operation, adds explicit discovery and routable HTTP metadata, strengthens authorization, formalizes extensions, and begins retiring several older protocol mechanisms.
MCP Apps, SEP-1865. Defines the interactive UI model that lets tools supply sandboxed application interfaces inside MCP hosts.
SEP-973. Added richer metadata, including icons, for implementations, tools, resources, templates, and prompts.
Frequently asked questions
What transport should a remote MCP server use?
- Streamable HTTP. The 2026-07-28 MCP specification makes it the remote transport going forward and gives the older HTTP plus SSE transport a one-year compatibility off-ramp. Stdio remains the right choice for local MCP servers that run next to the client.
Is MCP stateless now?
- At the protocol level, yes. The 2026-07-28 revision removes the initialize/initialized handshake and the Mcp-Session-Id header, so every request is self-contained and any instance behind a load balancer can serve it. Application state still exists, carried explicitly in the IDs and handles the application returns instead of in the connection.
Which OAuth standards does a remote MCP server need to implement?
- RFC 9728 Protected Resource Metadata for describing the resource and its authorization servers, RFC 8414 Authorization Server Metadata, RFC 8707 Resource Indicators for binding tokens to a specific resource, RFC 6750 bearer token usage, and RFC 9207 issuer identification. RFC 7591 Dynamic Client Registration still matters for compatibility, but MCP is moving toward Client ID Metadata Documents as the preferred client registration mechanism.
Where does RFC 9728 metadata live for an MCP endpoint with a path-based identity?
- The well-known segment is inserted between the host and the path. For the resource https://mcp.example.com/mcp/acme, the metadata document lives at https://mcp.example.com/.well-known/oauth-protected-resource/mcp/acme, and its resource property must match the identifier used to derive it. Some clients require this path-derived form strictly, so serving only the root-level well-known endpoint can break OAuth discovery.
What are MCP Apps?
- MCP Apps are the protocol's first official extension, shipped in January 2026. They let a tool reference a ui:// resource containing HTML, which the host renders inside a sandboxed iframe connected back through a defined messaging bridge, so the interface can receive tool results and initiate further tool calls without getting access to the surrounding client page. Supported hosts include Claude, ChatGPT, VS Code, and Goose.
Why does an MCP server work with one AI client and fail with another?
- Because the specification is ahead of complete client convergence. Clients exercise different slices of the spec, particularly around OAuth discovery: one client may accept root-level protected resource metadata while another strictly requires the RFC 9728 path-derived form. The fix is to build to the protocol rather than to whichever client accepts the least metadata.
Footnotes
- Model Context Protocol Specification, 2026-07-28 revision The authoritative protocol requirements for the current revision. ↩
- Boring Software ↩
- RFC 9728: OAuth 2.0 Protected Resource Metadata Section 3.1 defines the path-derived well-known metadata location for resource identifiers that contain path components. ↩
