Remote MCP Server: How Streamable HTTP and OAuth Work
Updated October 31, 2026
A remote MCP server is an MCP server that runs on the internet at an HTTPS address instead of on your computer, so any AI app with the URL can use it. It talks over the Streamable HTTP transport and usually signs you in with OAuth 2.1, which is why adding one is a URL and a browser sign-in rather than a config file and an API key.
For you, a remote server is one URL and one sign-in. For whoever runs it, it's an HTTP endpoint and an OAuth setup.
If you only want to add a remote server to Claude, read the next section and then go to the custom connector guide. The transport and OAuth sections after it are for people building or debugging a server, and they follow the current MCP specification, revision 2026-07-28, flagging where older revisions differ.
Remote vs local, briefly
A local server is a program your AI app starts on your machine and talks to over stdio. A remote server is already running somewhere else, and many clients connect to it at once. The MCP docs give Sentry's official server as the textbook remote example: it runs on Sentry's platform and speaks Streamable HTTP.
So remote servers work wherever your AI app does, including web and mobile apps that can't launch programs. The Claude MCP servers page compares the two kinds in detail.
How Streamable HTTP works
Streamable HTTP arrived in protocol version 2025-03-26, replacing the older HTTP+SSE transport from 2024-11-05. The rules in the current revision are short:
- The server exposes a single endpoint that accepts POST, like
https://example.com/mcp. - The client sends every message as its own HTTP POST to that endpoint. The body is one JSON-RPC request.
- The server answers each request with either a plain JSON response or a Server-Sent Events (SSE) stream scoped to that request, which can carry progress updates before the final answer.
- Closing that stream is how the client cancels the request.
Every POST also carries a few headers so load balancers and gateways can route requests without reading the body: MCP-Protocol-Version, Mcp-Method, and for tool calls Mcp-Name. A tool call looks like this on the wire:
POST /mcp HTTP/1.1
Content-Type: application/json
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: get_weather
Authorization: Bearer eyJhbGciOi...
The server checks those headers match the body, and rejects the request with a 400 if they don't.
What changed in 2026-07-28
The biggest change is that MCP is now stateless. Every request carries its protocol version and client capabilities, so the server doesn't have to remember anything between requests. Two things went away as a result:
- Sessions. Revisions 2025-03-26 through 2025-11-25 let a server hand out an
Mcp-Session-Idheader. A current server ignores it. - The GET stream. Older clients could open a standing SSE stream with GET. Now a server answers GET with 405, and clients that want change notifications send a
subscriptions/listenrequest instead.
If you run a server behind ordinary web infrastructure, this is good news. No sticky sessions, so any instance can answer any request.
What about SSE?
The old HTTP+SSE transport (two endpoints, one for a stream and one for posts) has been deprecated since 2025-03-26 and is eligible for removal. SSE itself lives on, as the optional streaming response inside Streamable HTTP. Claude Code, for example, still accepts --transport sse but labels it deprecated and tries HTTP first.
How OAuth works on a remote MCP server
Authorization is optional in MCP. But when a remote server needs to know who you are, the spec says to use OAuth 2.1, and it's specific about how. Here's the flow you trigger when you click Connect:
- Your AI app calls the server without a token. The server answers 401 with a
WWW-Authenticateheader pointing to its Protected Resource Metadata. - The app reads that metadata (RFC 9728) to find which authorization server issues tokens for this MCP server.
- It fetches the authorization server's metadata to learn its endpoints.
- It gets a client ID. The spec's order: pre-registered credentials first, if the app already has some for this authorization server; then a Client ID Metadata Document (the app's ID is an HTTPS URL describing itself), if the authorization server supports it; then dynamic client registration, which is deprecated and kept for older servers.
- Your browser opens the sign-in page. The app uses PKCE and names the exact server it wants a token for with a
resourceparameter (RFC 8707). - You sign in and click Allow. The app trades the code for an access token.
- Every request from then on carries
Authorization: Bearer <token>.
That's what turns "paste an API key" into "sign in and click Allow". The app never sees your password, only a token.
The token rules that keep you safe
A server must accept only tokens issued for it specifically, and must never pass your token on to another service, so if it calls another API it uses its own credentials. Tokens travel in the Authorization header on every request, never in the URL, and an invalid or expired one gets a 401 so the app knows to sign you in again.
Local servers skip all of this. The spec says stdio servers should read credentials from environment variables instead.
A worked example: Kitepod's remote server
Kitepod, a free remote MCP server plus a WordPress plugin, shows the whole flow. Its endpoint is https://kitepod.com/mcp, it speaks Streamable HTTP, and it signs you in with OAuth 2.1.
You install the plugin on your WordPress site, click Connect in wp-admin and choose which WordPress user the AI acts as. Then you add the URL to your AI app, sign in to Kitepod and click Allow.
That last click is the OAuth flow above. Your app ends up holding a token that's only good for Kitepod. Kitepod then signs every request it sends to your site, and the AI never gets more power than the WordPress user you picked. No application password gets pasted anywhere.
Then you ask in plain English:
Find every page on my site with no meta description and draft one for each, but don't publish until I've seen them.
The same URL goes into every app. In Claude it's a custom connector. In Claude Code it's claude mcp add --transport http --scope user kitepod https://kitepod.com/mcp. In Cursor it's "kitepod": { "url": "https://kitepod.com/mcp" } under mcpServers. More on the WordPress side on the WordPress MCP page.
Using a remote server from your own code
The Claude apps and Claude Code handle the OAuth flow for you, and their steps are on the Claude MCP overview. Two cases are different.
From the Claude API, you list the server in mcp_servers and enable it with an mcp_toolset entry, using the MCP connector (beta). Only tool calls are supported, and the API doesn't run the sign-in for you: you get the OAuth token yourself and pass it as authorization_token.
In any other MCP client, look for "add remote server", "HTTP server" or a url field in its MCP settings. If it supports OAuth, it'll open your browser on first use.
What goes wrong
| Symptom | Likely cause | Fix |
|---|---|---|
| An older client gets 405 errors | It's using the GET stream or session DELETE that 2026-07-28 removed | Update the client, or keep supporting the older revision on the server |
| Progress updates arrive all at once | A reverse proxy like nginx is buffering the SSE stream | Send X-Accel-Buffering: no on streaming responses, as the spec recommends |
| Sign-in loops | The Protected Resource Metadata names the wrong authorization server, or the token's audience doesn't match the server's canonical URL | Fix the metadata; write the URL the same way everywhere, without a trailing slash |
| A web page can reach your local HTTP server | No Origin check, or the server listens on every interface | Validate Origin (reject bad ones with 403) and bind to 127.0.0.1 |
| Claude's custom connector can't reach a server that works for you | The server isn't reachable from the public internet | See the custom connector guide |
Finer points
Do remote MCP servers need OAuth?
No, authorization is optional in the spec. Public servers can skip it, and some use a fixed API key in a header. But when a remote server needs to know who you are, OAuth 2.1 is what the spec describes, and it's what Claude's custom connectors are built around.
Is SSE deprecated in MCP?
The old two-endpoint HTTP+SSE transport is, since 2025-03-26. SSE as a streaming response inside Streamable HTTP isn't. New servers should use Streamable HTTP.
What does "stateless" mean for MCP?
As of 2026-07-28, each request carries everything the server needs (protocol version, client capabilities), so there's no session to keep. Any server instance can answer any request.
Can a remote MCP server run on localhost?
It can, and clients on the same machine, like Claude Code, can reach it. Claude's custom connectors can't, and the custom connector guide explains why.
How do I build a remote MCP server?
Start from an official SDK, which handles the transport and most of the OAuth plumbing. The build an MCP server guide covers the SDKs and when you shouldn't build one at all.
Next time an app asks you to paste an API key, check its docs for an /mcp endpoint first.