Introducing Gram by Speakeasy. Gram is everything you
need to power integrations for Agents and LLMs. Easily create, curate and host
MCP servers for internal and external use. Build custom tools and remix
toolsets across 1p and 3p APIs. Get on the
waitlist today!
Deploying remote MCP servers
It is still early days for the Model Context Protocol (MCP), and even though it is already widely implemented, the MCP Specification is changing rapidly . For example, in the March 2025 update , a new transport, “streamable HTTP” was added to the specification, along with an optional authorization framework , based on OAuth 2.1.
Keeping up with these changes feels a bit like treading water, but the water is alphabet soup, and someone keeps dumping more letters in.
In this guide, we’ll focus on one specific topic: hosting remote MCP servers safely. As part of this, we’ll cover the new streamable HTTP transport and the authorization framework.
Remote MCP transports
Most of the early MCP servers began as small programs running on a developer’s laptop, connected to MCP clients through local stdio. Indeed, as of writing this, the very first example on the MCP website still shows all servers running on “your computer”. The specification, however, always included server-sent events (SSE) as a transport mechanism, allowing MCP clients to connect to remote servers via HTTP, and allowing servers to stream responses back to clients.
Besides the risk of prompt injection, the HTTP+SSE transport part of the specification seems the most contentious in the recent online discourse around MCP. This intensified after the release of the March 2025 update, with many commenters concluding that “streamable HTTP” is essentially HTTP+SSE with more steps and speculating that it will one day converge on something akin to WebSocket.
We’re not throwing a hat in the ring with this debate today, but we’ll say this: The specification in its current state is useful without immediate changes to the transport mechanism.
Relying on SSE and the normal HTTP pipeline avoids the complexities of upgrading connections from HTTP to WebSocket. With the addition of session IDs passed via headers in HTTP+SSE, maintaining stickiness in serverless or proxied servers is trivial.
Waiting for all the details to be ironed out feels like debating tabs vs spaces at this point.
With that out of the way, let’s use the protocol we have, even if not everyone agrees about the current direction.
Reasons to host MCP servers remotely
Remote MCP servers allow teams to connect to a common server that is always available, which avoids server-version disparity among team members and prevents secrets (such as organization-level API keys) from being shared with users.
If your users are less technical, you can also provide a simple web interface to the server, allowing them to run servers and use tools without needing to know how to use the command line or Docker.
Let’s look at the options available for hosting remote MCP servers.
Choosing your hosting model
Hosting model
Cloudflare Workers
Best for
Need zero-ops edge scale, instant HTTPS, built-in OAuth
Pain points
Limited CPU / 50 ms wall-clock per event; can't spawn native binaries
Cloud VM
Best for
Want full OS control, can hand-roll networking, cheap burst workloads
Pain points
You patch and monitor; long-lived SSE needs extra Nginx tuning
The protocol is identical across models: An HTTPS endpoint accepts JSON-RPC 2.0 over an HTTP POST (optionally upgrading to SSE) and streams the response. Only the infrastructure changes.
Let’s summarize the steps here by modifying the Sentry MCP server and deploying it to Cloudflare Workers.
Requirements
You’ll need a Cloudflare account (free is fine), and Node.js with pnpm installed locally. Our example uses the Sentry MCP server, so you’ll also need a Sentry account.
Starting with a template
If you’re building your own server, you can start with one of the many Cloudflare demos . But to get something useful up and running in this guide, we decided to use the Sentry MCP server (itself based on the mcp-github-oauth demo) as an example.
Clone the Sentry MCP server:
Installing dependencies
Install the dependencies using pnpm:
Authenticating Wrangler with Cloudflare
Wrangler is Cloudflare’s CLI for deploying and managing Workers. We’ll use npx to run it without installing globally.
Check that you can run npx wrangler:
This should print the version of Wrangler. If you get an error, you may need to install Node.js or add it to your PATH.
Next, you’ll need to authenticate Wrangler with your Cloudflare account. This is a one-time step:
This opens a browser window and asks you to log in to your Cloudflare account. Once you’ve logged in, you’ll be redirected to a page that says, You have granted authorization to Wrangler! You can close this page.
Creating a Cloudflare KV namespace
The Sentry MCP server uses a Cloudflare KV namespace to store OAuth tokens. You can create a new KV namespace using the Wrangler CLI:
This creates a new KV namespace and prints the ID. Copy this ID, as you’ll need it in the next step.
Configuring the project
Now that you have your KV namespace set up, you can configure the project. The packages/mcp-cloudflare/wrangler.jsonc file contains all the configuration for your Cloudflare Worker.
Update the kv_namespaces section with the ID of the KV namespace you created earlier:
Creating a Sentry API application
Next, you’ll need to create a Sentry API application. This is a one-time step that allows the MCP server to authenticate with Sentry.
Log in to your Sentry account.
Click on your profile picture in the top-left corner to open the menu.
Click on User settings.
Under API, click on Applications.
Click on Create New Application.
Take note of the Client ID and Client Secret. You’ll need these in the next step.
Configuring the environment variables
The Sentry MCP server uses environment variables to configure the OAuth flow. You can set these using Cloudflare Wrangler:
Change the working directory to packages/mcp-cloudflare, where the wrangler.jsonc file is located:
Now, set the environment variables in Cloudflare using the following commands:
When prompted, enter the values you copied from the Sentry API application and the random string you generated. This stores the secrets in your Cloudflare account.
Building the project
Now that you have everything set up, you can build the project. Run the following command in the packages/mcp-cloudflare directory:
Deploying the server
Next, you can deploy the server to Cloudflare Workers. Run the following command in the packages/mcp-cloudflare directory:
Cloudflare will build the project and deploy it to your account. This may take a few minutes.
Once the deployment is complete, you should see a message like this:
Testing the server
The Sentry MCP repository includes a documentation page that explains how to use the server. Click the link Cloudflare Workers provides to open the docs page in your browser.
Now let’s test the MCP server. Open a terminal and run the following command:
This starts the MCP Inspector, which allows you to interact with the MCP server in your browser.
Setting the redirect URL in Sentry
Before you can connect the MCP Inspector to the Sentry MCP server, you need to set the redirect URL in Sentry. This is the URL that Sentry redirects to after the OAuth flow is complete.
In a new browser tab, go back to the Sentry API application you created earlier.
Click on the application name to open the details.
Under Redirect URIs, add the OAuth callback URL:
There is no save button. Unfocus the field, and Sentry should display a toast message indicating that the redirect URL was updated successfully. You can close this tab.
Connecting the MCP Inspector to the Sentry MCP server
Now that you have the redirect URL set up, you can connect the MCP Inspector to the Sentry MCP server.
In the inspector window:
Select the SSE transport.
Enter the URL of your MCP server (for example, https://sentry-mcp.<your-cloudflare-hostname>.workers.dev/sse).
Click Connect.
This establishes a connection to the MCP server, which kicks off the OAuth flow. You should see a message in the inspector window indicating that the MCP Inspector is requesting authorization:
If you look at the URL in the address bar, you’ll notice that the redirect URL is set to your local MCP Inspector URL. This is expected, as the MCP Inspector is running locally and will handle the OAuth client flow. Also of note is that the OAuth page you’re looking at is hosted on the Sentry MCP server, not on Sentry itself.
This is because the Sentry MCP server is acting as a proxy for the OAuth flow. The MCP Inspector is requesting authorization to access your Sentry account, and the Sentry MCP server is handling the redirect. The MCP Specification recommends this approach, as it allows the MCP server to act as a trusted intermediary between the client and the OAuth provider.
There are, in effect, two OAuth flows happening here:
The MCP Inspector is requesting authorization to access your Sentry MCP server.
The Sentry MCP server is requesting authorization to access your Sentry account on behalf of the MCP Inspector.
Click Approve. The MCP Inspector will then redirect to the Sentry OAuth page for your app.
In the screenshot above, “Useful Goshawk” is the name of the Sentry API application we created earlier. The permissions listed are the scopes that the MCP Inspector is requesting access to. You can review these permissions and decide whether to approve or deny the request.
Click Approve to grant the MCP Inspector access to your Sentry account. This will redirect you back to the MCP server, which will redirect you back to the MCP Inspector.
Click on Tools in the top navigation bar, then on List Tools. This will show you a list of available tools on the MCP server. You can click on any tool to see its details and run it.
We clicked on list_organizations to see a list of all organizations that the user has access to in Sentry. This is a good way to verify that the OAuth flow is working correctly and that the MCP server can access your Sentry account.
Now click the Run Tool button to execute the tool. This will send a request to the MCP server, which will then forward it to Sentry and return the response.
The response shows a list of organizations that you have access to in Sentry. This confirms that the MCP server can access your Sentry account and that the OAuth flow is working correctly.
Now everyone on your team can use the Sentry MCP server with their IDEs, LLMs, or any other MCP client. Of course, if you don’t need to change the server code, you could just as well use the hosted Sentry MCP server .
On-premises deployment with Cloudflare tunnels
While Cloudflare Workers provides an easy way to deploy remote MCP servers, you may have reasons to run servers on your own infrastructure instead. When your organization has strict data handling requirements or MCP servers need access to services on your network, you can host your servers on-premises or in a private cloud.
The Polar.sh MCP server is a great example of a server that you might want to run on-premises. It allows AI assistants to interact with your Polar data, including subscriptions, posts, and other content. You may want to run this server on your own infrastructure for security, compliance, or performance reasons.
In this example, we’ll deploy the Polar MCP server on-premises and make it securely accessible from anywhere using a Cloudflare Tunnel combined with Zero Trust access controls.
Reasons to use Cloudflare Tunnel with Zero Trust
When hosting an MCP server on-premises, you have several options for making it remotely accessible:
Open a port on your firewall (not recommended for security reasons).
Set up a VPN (adds client complexity and management overhead).
Use a reverse proxy with authentication (requires careful configuration).
Use Cloudflare Tunnel with Zero Trust (our recommended approach).
Cloudflare Tunnel creates an encrypted tunnel between your local server and Cloudflare’s edge, with no inbound ports required. Combined with Cloudflare Zero Trust, you can restrict access to specific authenticated users without exposing your server to the public internet.
Requirements
You’ll need:
A machine to host the Polar MCP server (Linux recommended)
Zero Trust enabled (free tier works for basic setups)
Generating a Polar access token
To allow the on-premises MCP server to authenticate with the Polar API, you need to generate an organization access token from the Polar dashboard. The steps below describe this process, referencing the user interface elements shown in the screenshot that follows.
Log in to your Polar organization on sandbox.polar.sh for testing. For production environments, you would typically use polar.sh .
In the left-hand navigation sidebar, click Settings.
On the Settings page, locate the Developers section. Click New Token to open the Create Organization Access Token dialog.
In the Name field, enter a descriptive name for your token, such as polar-mcp.
Select an Expiration period for the token from the dropdown menu, for example, 7 days.
Under Scopes, ensure all permissions are selected by checking each corresponding box. This grants the MCP server the necessary access to interact with your Polar data.
Scroll down and click Create.
Polar displays the generated access token only once upon creation. Copy this token and store it. You will need this value for the POLAR_ACCESS_TOKEN environment variable when you configure your Docker container in a later step.
Setting up the Polar MCP server
First, let’s get the Polar MCP server running locally.
Create a new directory for your Polar MCP deployment:
Create a docker-compose.yml file with the following content:
Replace your_polar_api_key with the actual Polar API key you generated in the previous step.
Start the Polar MCP server:
Verify that the server is running correctly:
If everything is working, you should see a response with a session ID and /message endpoint.
Setting up Cloudflare Zero Trust
Now let’s configure Cloudflare Zero Trust to secure access to your MCP server.
Log in to your Cloudflare account.
Navigate to the Zero Trust dashboard.
If this is your first time using Cloudflare Zero Trust, you’ll need to create a Zero Trust organization.
In the Zero Trust dashboard, go to Access > Applications.
Click Add an application.
Select Self-hosted as the application type.
Configure the application.
Application name: Enter the name Polar MCP.
Session duration: Choose how long users stay authenticated (for example, 24 hours).
Application domain: Choose + Add public hostname and add a subdomain where your MCP server will be available (for example, polar-mcp.yourdomain.com).
Leave other settings at default values for now.
Create an access policy:
Click Add a policy.
Policy name: Enter the name Polar MCP Access.
Action: Allow.
Configure rules: Select who should have access to your MCP server.
For testing, you can use Emails and add your email address.
For a team, you might use Emails ending in with your company domain.
Click Save
Click Next on the application. You can leave the default selected values for the optional settings.
Finish by clicking Save to create the application.
Make sure that the policy you created is linked to the application’s policies on the application’s Policies tab.
Installing and configuring Cloudflare Tunnel
Next, we’ll set up a tunnel to securely connect your local MCP server to Cloudflare.
In the Cloudflare Zero Trust dashboard, navigate to Networks > Tunnels.
Click Create a tunnel.
Select Cloudflared as the tunnel type.
Give your tunnel a name (for example, polar-mcp-tunnel).
Click Save tunnel.
Select Docker as the installation method. Instead of running the command in the terminal, copy only the tunnel token from the command.
Now update your docker-compose.yml file to include the Cloudflare Tunnel configuration:
Replace your_tunnel_token with the token you copied earlier.
Now, your docker-compose.yml file should look like this:
Restart the Docker containers:
In Cloudflare, your tunnel should now show up as a connector.
Click Next.
Configuring the tunnel
On the Route Traffic page, configure the tunnel using the details below.
Hostname: Enter the subdomain you chose earlier (for example, polar-mcp.yourdomain.com).
Service: Enter the URL of your MCP server (for example, http://polar-mcp:3000).
Click Save tunnel.
Testing your secure MCP setup
Now it’s time to test whether everything is working correctly. Open a browser and navigate to:
You should be prompted to authenticate through Cloudflare Access.
After logging in, you should see a response similar to the one you got when testing the local server.
Using Cloudflare Warp to connect to the MCP server
In Cloudflare Zero Trust:
Navigate to Gateway.
Click on Add a device.
Follow the instructions to install Cloudflare Warp on your machine.
Open the Cloudflare Warp app and navigate to the Preferences > Account tab.
Click the Login to Cloudflare Zero Trust button. This will show a modal with a text field, where you enter your team name.
Your team name is the customizable portion of your team domain. You can view your team name in Zero Trust under Settings > Custom Pages.
Enter your team name and click Continue. This will open a browser window to authenticate with Cloudflare Zero Trust.
Once authenticated, you should see a message indicating that your device is connected to Cloudflare Zero Trust.
Note
If you encounter connection issues, you may need to disable Warp’s DNS
features. To do so, navigate to your Cloudflare Zero Trust dashboard and go to
Settings > Warp Client > Profile settings. Select Configure
from the three-dot menu option for your profile, and under Service mode,
select Secure Web Gateway without DNS Filtering.
Once Warp is connected, restart your polar-mcp and cloudflared Docker containers so that the cloudflared routes tunnel correctly.
Adding Warp access to the MCP server
In the Cloudflare Zero Trust dashboard, navigate to Settings > Warp Client.
Under Device enrollment permissions, click Manage.
Click Login methods.
Activate WARP authentication identity.
Activate Apply to all Access applications.
Click Save.
Testing the MCP connection
To test the MCP connection, you can use the MCP Inspector we used earlier:
In the MCP Inspector:
Select the SSE transport.
Enter your MCP server URL, https://polar-mcp.yourdomain.com/sse.
Click Connect.
You’ll be redirected to the Cloudflare Access login page. After authentication, the MCP Inspector should connect to your Polar MCP server securely.
With this in place, you can enroll your team members in Cloudflare Zero Trust and provide them with access to the MCP server. They can use any MCP client to connect to the server, and all traffic will be encrypted and authenticated through Cloudflare.
Comparing deployment models
Feature
Setup complexity
Cloudflare Workers
Low
On-prem with CF Tunnel
Medium
Maintenance overhead
Cloudflare Workers
Very low (serverless)
On-prem with CF Tunnel
Medium (server management)
Access to local data
Cloudflare Workers
Limited
On-prem with CF Tunnel
Full
Customization
Cloudflare Workers
Limited by Workers runtime
On-prem with CF Tunnel
Full OS-level control
Cost model
Cloudflare Workers
Pay-per-request
On-prem with CF Tunnel
Fixed infrastructure costs
Cold start latency
Cloudflare Workers
Yes, but minimal
On-prem with CF Tunnel
None (always running)
Resource constraints
Cloudflare Workers
CPU/memory limits
On-prem with CF Tunnel
Based on your hardware
Authentication
Cloudflare Workers
OAuth providers
On-prem with CF Tunnel
Cloudflare Access (flexible)
Feature
Cloudflare Workers
On-prem with CF Tunnel
Setup complexity
Low
Medium
Maintenance overhead
Very low (serverless)
Medium (server management)
Access to local data
Limited
Full
Customization
Limited by Workers runtime
Full OS-level control
Cost model
Pay-per-request
Fixed infrastructure costs
Cold start latency
Yes, but minimal
None (always running)
Resource constraints
CPU/memory limits
Based on your hardware
Authentication
OAuth providers
Cloudflare Access (flexible)
Both deployment models follow the MCP Specification, allowing you to choose the approach that best fits your needs without changing client-side integrations.
Other deployment models
We experimented with several other deployment models, including:
AWS Lambda
AWS Lambda is similar to Cloudflare Workers, but managing the AWS environment and IAM roles is more complex. We used the Run Model Context Protocol (MCP) servers with AWS Lambda example as a starting point, but ran into a dead end when it came to the SSE transport.
AWS open-sourced a server adapter for the MCP servers, which simplifies running stdio MCP servers as Lambda functions. However, the adapter depends on a custom transport that no clients currently support.
AWS notes that with upcoming changes to the MCP Specification, this may change. For now, we recommend using Cloudflare Workers or an on-premises deployment.
Container-based deployment with Docker and Kubernetes
This was our initial approach, but we found it more complex than necessary for most use cases. While Docker and Kubernetes provide great flexibility and scalability, they also introduce additional overhead in terms of setup and maintenance.
We recommend using Docker for local development and testing, but Kubernetes is often overkill for production deployments unless you have a specific need for it.
A Kubernetes deployment may suit your needs if you provide MCP servers as a service to multiple teams or customers. In that case, you can use Kubernetes to manage scaling, resource allocation, and isolation between different MCP servers.
In summary
When deploying remote MCP servers, start with the server’s transport mechanism and hosting requirements. If it needs to be publicly accessible, Cloudflare Workers is a great option. If you need to access internal data or have specific hosting requirements, consider using Cloudflare Tunnel with Zero Trust (or a VPN) to expose your server securely.
Then make sure you understand the security implications of your deployment model. For example, if you’re using Cloudflare Workers, ensure that your server is properly configured to handle authentication and authorization. If you’re using Cloudflare Tunnel, you have to ensure that your server is properly secured and that only authorized users can access it.
Finally, consider the trade-offs between different deployment models. Cloudflare Workers is a great option for quick deployments with minimal maintenance overhead, while on-premises deployments give you more control over the environment but require more management.
As MCP continues to evolve, these deployment patterns will adapt, but the core principles of secure, accessible MCP servers will remain relevant regardless of the transport mechanisms chosen by the specification.
If you have any questions or feedback about this guide, or want to share your own experiences with deploying remote MCP servers, please join us on Slack .
To find out how we can help you generate your own MCP servers, get in touch on our website.