Choosing an Approach
createMcpHandler()is the fastest way to get a stateless MCP server running. Use it when your tools do not need per-session state.McpAgentgives you a Durable Object per session with built-in state management, elicitation support, and both SSE and Streamable HTTP transports.- Raw transport gives you full control if you want to use the
@modelcontextprotocol/sdkdirectly without the Agents SDK helpers.
Stateless MCP Server with createMcpHandler()
The simplest way to create an MCP server. No Durable Objects or bindings required:
createMcpHandler Options
Accessing Authenticated User Context
When your MCP server is wrapped withOAuthProvider from @cloudflare/workers-oauth-provider, authenticated user information is available inside tools via getMcpAuthContext():
OAuthProvider sets ctx.props on the execution context, which createMcpHandler automatically picks up and makes available via getMcpAuthContext().
Stateful MCP Server with McpAgent
McpAgent gives each client session its own Durable Object with persistent state. Use this when your tools need to track per-session data.
Writing TinyMCP
Prototyping is very easy! If you want to quickly deploy an MCP, it only takes ~20 lines of code:wrangler.jsonc would look something like:
What is going on here?
McpAgent requires us to define 2 bits, server and init().
init() is the initialization logic that runs every time our MCP server is started (each client session goes to a different Agent instance).In there you will normally setup all your tools/resources and anything else you might need. In this case, we are only setting the tool
square.
That was just the McpAgent, but we still need a Worker to route requests to our MCP server. McpAgent exports a static method that deals with that for you. That is what TinyMcp.serve(...) is for.It returns an object with a
fetch handler that can act as our Worker entrypoint and deal with the Streamable HTTP transport for us, so we can deploy our MCP directly!
Putting it to the test
It is a very simple MCP indeed, but you can get a feel of how fast you can get a server up and running. You can deploy this worker and test your MCP with any client.Password-protected StorageMCP with OAuth
To get a feel of what a more realistic MCP might look like, let’s deploy an MCP that lets anyone that knows our secret password access a shared R2 bucket.1
Define the McpAgent
Create your
McpAgent with tools that interact with R2:2
Create the OAuth flow
Build a simple password-based authentication flow:
3
Wire it all together
Combine the MCP server with OAuth:Add these to your
wrangler.jsonc:Data Jurisdiction for Compliance
McpAgent supports specifying a data jurisdiction for your MCP server, which is particularly useful for satisfying GDPR and other data residency regulations.
Using the EU Jurisdiction for GDPR
To comply with GDPR requirements, you can specify the"eu" jurisdiction to ensure that all data processed by your MCP server remains within the European Union:
jurisdiction: "eu", Cloudflare will create the Durable Object instances in EU data centers, ensuring that:
- All MCP session data stays within the EU
- User data processed by your tools remains in the EU
- State stored in the Durable Object’s storage API stays in the EU
Available Jurisdictions
Thejurisdiction option accepts any value supported by Cloudflare’s Durable Objects jurisdiction API, including:
"eu"- European Union"fedramp"- FedRAMP compliant locations
Elicitation (Human-in-the-Loop)
MCP servers can request additional input from the user during a tool call using elicitation. This is useful for confirmation dialogs, requesting amounts, or any interactive tool flow. Elicitation is supported viaMcpAgent (which manages the request/response lifecycle through Durable Object storage) or via WorkerTransport (for stateful non-McpAgent setups).
See the
examples/mcp-elicitation example for a full working demo.WorkerTransport
WorkerTransport is a server-side transport for running MCP servers in stateless Workers while optionally persisting session state. It is used internally by createMcpHandler() but can also be used directly for advanced scenarios like stateful sessions without McpAgent.
Key Options
Next Steps
Connecting Clients
Connect your agent to external MCP servers
Transports
Learn about different MCP transport options
Securing Servers
Implement OAuth and security best practices
Examples
View example implementations on GitHub