Remote MCP: bringing the Model Context Protocol online
Most discussion around the Model Context Protocol (MCP) has centered on servers that developers install and run locally. That is starting to change. Remote MCP servers—accessible over the Internet, with proper sign-in and authorization flows—are now supported on Cloudflare, and the pieces needed to build them are already available.
Four additions to the Cloudflare stack handle the hard parts:
workers-oauth-provider— an OAuth provider library that adds authorization to a Worker’s API endpoints, including MCP server endpoints.McpAgent— a class in the Cloudflare Agents SDK that manages remote transport, handling serialization and persistent connections for you.mcp-remote— an adapter that lets MCP clients which only support local connections work with remote servers.- AI playground as a remote MCP client — a chat interface that connects to remote MCP servers, with the authentication check included.
A working example server can be deployed to production in under two minutes either through the developer docs or with a one-click deployment button:
The significance here is reach. Local MCP servers are valuable for developers, but they never made MCP available to people using web-based interfaces or mobile apps. There was no practical way for users to log in and grant an MCP client permissions. Remote connections change that. Just as desktop software gave way to web-based software, MCP servers that live online can support use cases like logging in on one device and continuing a task on another—expectations that local setups simply cannot meet.
Authentication and authorization as a managed layer
Moving MCP from stdio to streamable HTTP is only part of the picture. A remote MCP server that accesses data from a user’s account needs authentication—proving who the user is—and authorization—letting the user control what the AI agent can do.
MCP accomplishes this with OAuth, where the MCP server itself acts as the OAuth provider. Implementing OAuth with MCP from scratch is involved, so Cloudflare provides it as part of the Worker runtime.
workers-oauth-provider: OAuth 2.1 for Workers
workers-oauth-provider is a TypeScript library that wraps a Worker and adds authorization to its endpoints. When this is in place, the MCP server receives already-authenticated user details as a parameter. You do not perform your own checks or manage tokens directly. The flow follows the MCP OAuth specification:

The MCP server plays a dual role here: it is an OAuth client to your upstream service (Google, GitHub, or your own provider) and an OAuth server to MCP clients. workers-oauth-provider guarantees that the server is spec-compliant and works with the range of client apps and websites expecting standard OAuth behavior, including support for Dynamic Client Registration (RFC 7591) and Authorization Server Metadata (RFC 8414).
The interface is pluggable. An MCP server built with Workers provides the OAuth provider paths—authorization, token, and client registration endpoints—along with handlers for both the MCP server and the auth flow:
import OAuthProvider from "@cloudflare/workers-oauth-provider";
import MyMCPServer from "./my-mcp-server";
import MyAuthHandler from "./auth-handler";
export default new OAuthProvider({
apiRoute: "/sse", // MCP clients connect to your server at this route
apiHandler: MyMCPServer.mount('/sse'), // Your MCP Server implmentation
defaultHandler: MyAuthHandler, // Your authentication implementation
authorizeEndpoint: "/authorize",
tokenEndpoint: "/token",
clientRegistrationEndpoint: "/register",
});
With this abstraction, you can bring your own authentication. A GitHub-hosted example shows an MCP server using GitHub as the identity provider, where the /callback and /authorize routes are implemented in under 100 lines of code. The user gets to choose the login UI and the upstream provider, or you can integrate with your own systems.
Why MCP servers issue their own tokens
The authorization diagram shows the MCP server giving the MCP client its own token, not passing through the token received from the upstream provider. Instead, the Worker stores an encrypted access token in Workers KV and issues a separate token to the client. Library code handles this so that your application never directly touches the stored token:
// When you call completeAuthorization, the accessToken you pass to it
// is encrypted and stored, and never exposed to the MCP client
// A new, separate token is generated and provided to the client at the /token endpoint
const { redirectTo } = await c.env.OAUTH_PROVIDER.completeAuthorization({
request: oauthReqInfo,
userId: login,
metadata: { label: name },
scope: oauthReqInfo.scope,
props: {
accessToken, // Stored encrypted, never sent to MCP client
},
})
return Response.redirect(redirectTo)
This indirection provides a meaningful security boundary. By issuing its own token, the MCP server can restrict access at a more granular level than the upstream provider allows. If a token issued to an MCP client is compromised, the attacker only gets the permissions explicitly granted through the MCP tools—not the full scope of the original token.
Consider an MCP server that asks a user for Gmail’s gmail.readonly scope, but only exposes a tool that reads travel booking notifications from a limited set of senders to answer a question like “What’s the check-out time for my hotel room tomorrow?” If the client token leaks, it cannot be used against Google’s API directly; it only works against your MCP server’s constrained tool surface. OWASP lists “Excessive Agency” as a top AI application risk, and issuing narrow tokens to clients is one way to keep tool access limited to what the client actually needs.
The same pattern can enforce per-user restrictions. An allowlist check can determine which users may even see or invoke a given tool, as in an example where only listed users can call a generateImage tool backed by Workers AI:
import { McpAgent } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
const USER_ALLOWLIST = ["geelen"];
export class MyMCP extends McpAgent<Props, Env> {
server = new McpServer({
name: "Github OAuth Proxy Demo",
version: "1.0.0",
});
async init() {
// Dynamically add tools based on the user's identity
if (USER_ALLOWLIST.has(this.props.login)) {
this.server.tool(
'generateImage',
'Generate an image using the flux-1-schnell model.',
{
prompt: z.string().describe('A text description of the image you want to generate.')
},
async ({ prompt }) => {
const response = await this.env.AI.run('@cf/black-forest-labs/flux-1-schnell', {
prompt,
steps: 8
})
return {
content: [{ type: 'image', data: response.image!, mimeType: 'image/jpeg' }],
}
}
)
}
}
}
McpAgent and the shift to streamable HTTP
Remote MCP servers must implement a transport that works over the Internet, not just local stdio. The McpAgent class from the Agents SDK handles that transport using Durable Objects behind the scenes to hold persistent connections open for server-sent events (SSE). A minimal server can be expressed in about 15 lines:
import { McpAgent } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
export class MyMCP extends McpAgent {
server = new McpServer({
name: "Demo",
version: "1.0.0",
});
async init() {
this.server.tool("add", { a: z.number(), b: z.number() }, async ({ a, b }) => ({
content: [{ type: "text", text: String(a + b) }],
}));
}
}
The remote transport portion of the MCP specification is in flux. After community discussion, Streamable HTTP is replacing HTTP+SSE, enabling stateless pure HTTP connections with an optional upgrade to SSE, and removing the need for a separate message endpoint. McpAgent is designed to evolve with this revision, so servers built today will not need to be rewritten when the transport updates.
Future MCP iterations will likely push interactive use cases further. Today most servers only expose tools—remote procedure calls that suit stateless transport. More complex human-in-the-loop and agent-to-agent interactions will require prompts and sampling, which implies chatty, bidirectional exchanges. Those real-time interactions are expected to need a genuine bidirectional transport layer. Cloudflare’s Agents SDK and Durable Objects natively support WebSockets for exactly that kind of full-duplex communication, so the path forward remains open.
Stateful sessions with Durable Objects
MCP servers on Cloudflare get per-session state through the Agents SDK. Each client session is backed by a Durable Object, which means every session can persist its own data — complete with its own SQL database. That turns an MCP server from a thin stateless shim in front of an API into something closer to an application itself.
The practical effect is that you can build stateful MCP servers with genuine conversational memory. A shopping cart flow, an interactive game, a persistent knowledge graph — all become natural fits. Rather than treating each tool call as a standalone request, your server can track context and build on prior interactions within the session.
Here is a minimal counter that demonstrates session memory:
import { McpAgent } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
type State = { counter: number }
export class MyMCP extends McpAgent<Env, State, {}> {
server = new McpServer({
name: "Demo",
version: "1.0.0",
});
initialState: State = {
counter: 1,
}
async init() {
this.server.resource(`counter`, `mcp://resource/counter`, (uri) => {
return {
contents: [{ uri: uri.href, text: String(this.state.counter) }],
}
})
this.server.tool('add', 'Add to the counter, stored in the MCP', { a: z.number() }, async ({ a }) => {
this.setState({ ...this.state, counter: this.state.counter + a })
return {
content: [{ type: 'text', text: String(`Added ${a}, total is now ${this.state.counter}`) }],
}
})
}
onStateUpdate(state: State) {
console.log({ stateUpdate: state })
}
}
For any given session, the counter's value persists across tool calls. Your server also has access to the full Cloudflare platform while it runs. It can spin up a headless browser, trigger a Workflow, or invoke AI models directly from a tool handler — letting you compose richer agent interactions than simple API relays.
Bridging the gap for local-only MCP clients
Cloudflare is betting on remote MCP being the eventual standard — where an MCP server lives on the network and multiple clients can reach it with proper authentication. Most popular MCP client applications don't support that model yet, but two new options let you exercise your remote server today without waiting for those clients to catch up.
The Workers AI Playground has been upgraded to function as a full remote MCP client. It's a hosted chat interface with built-in authentication support, so there's nothing to install. You simply paste your remote MCP server's URL (for example, https://remote-server.example.com/sse) and click Connect:

After the connection, any authentication flow you've set up runs, and you can immediately chat with the server and invoke its tools from the browser.
If you'd rather stay in a tool like Claude Desktop or Cursor, the mcp-remote adapter fills the same role locally. It proxies a local MCP connection to your remote server, letting clients that only know local MCP talk to remote servers anyway. That gives you — and anyone you share the setup with — a preview of the remote experience without waiting for native client support.
Cloudflare has published a setup guide for mcp-remote on Claude Desktop, Cursor, Windsurf, and similar clients. On Claude Desktop, for instance, the configuration looks like this:
{
"mcpServers": {
"remote-example": {
"command": "npx",
"args": [
"mcp-remote",
"https://remote-server.example.com/sse"
]
}
}
}
What early remote MCP support gets you
The bet is simple: once mainstream client apps speak remote authenticated MCP, the user base for MCP-powered services stops being engineers and becomes the general public. Building your service as a remote MCP server is the on-ramp to being reachable from the AI assistants that ordinary people are using.
That future isn't here yet — but you can start building for it now. Cloudflare has published a guide to getting a remote MCP server running today, and their team is fielding direct questions at [email protected]. The ecosystem is still moving quickly, and the expectation is that the next wave of MCP-native applications won't just connect to existing APIs — they'll be their own products.



