Convert your MCP Server from STDIO to HTTP — enabling remote access and cloud deployment.
MCP supports two transport models: STDIO (standard input/output) and HTTP (over the network). STDIO is local, simple, and fast — ideal for development and personal use. HTTP is remote, accessible over the network, and perfect for cloud deployment. The key question is: when to migrate? When others need to access your server — whether it's your team, your clients, or your production infrastructure.
STDIO works like a Unix pipe: the client launches the server as a child process and communicates via stdin/stdout. It’s zero-config and instant, but limited to the local machine. HTTP exposes the server as a web service: any client on the network can connect via URL. This enables scenarios such as multiple simultaneous clients, deployment in containers, and access from anywhere in the world. The tradeoff is that HTTP requires network, security, and infrastructure configuration — but the gain in flexibility is enormous.
The MCP specification officially defines two transports: STDIO e HTTP with SSE. In practice, most MCP Servers in production use HTTP/SSE to allow integration with multiple clients and cloud deployment. The HTTP protocol typically adds 1–5 ms latency per request on local networks, but this difference is imperceptible compared to the tools’ processing time. The latest version of the protocol also supports Streamable HTTP, an evolution of SSE with better reconnection support.
FastMCP abstracts away all the complexity of configuring an HTTP server. With a single line of code, you can switch the transport from STDIO to HTTP or SSE. The framework handles routes, JSON-RPC serialization, connection management, and streaming — you focus on your tools’ logic.
Configuring HTTP in FastMCP means changing a single parameter:
# Antes — transporte STDIO (padrão) mcp.run() # Depois — transporte HTTP mcp.run(transport="http", host="0.0.0.0", port=8000) # Ou com SSE (Server-Sent Events) mcp.run(transport="sse", host="0.0.0.0", port=8000)
The parameter host="0.0.0.0" accepts connections from any IP (required for Docker/cloud). The port sets the server port. FastMCP internally uses the uvicorn as an ASGI server, ensuring performance and compatibility with the Python ecosystem.
Use an environment variable to switch between transports without modifying the code: transport = os.getenv("MCP_TRANSPORT", "stdio"). For local development, use STDIO. In production, define MCP_TRANSPORT=http. This way, the same code works in both scenarios without any changes to the source.
Add uvicorn e starlette to the dependencies in the pyproject.toml.
Change mcp.run() to mcp.run(transport="http", host="0.0.0.0", port=8000).
Test locally by accessing http://localhost:8000 and verify that the tools respond over HTTP.
SSE (Server-Sent Events) is a technology that lets the server send continuous updates to the client over a persistent HTTP connection. Unlike WebSockets (bidirectional), SSE is unidirectional: the server sends, the client receives. This is ideal for long-running operations where the client needs to track progress in real time.
With MCP’s SSE transport, the client makes an HTTP request and keeps the open connection. The server sends JSON-RPC messages as SSE events while processing requests. This allows for streaming partial results, progress notifications, and incremental responses. MCP uses SSE as the response channel while the client sends requests via regular POST. This architecture is robust and works well with proxies, load balancers, and CDNs—unlike WebSockets, which require special infrastructure support.
SSE is natively supported by all modern browsers and most HTTP frameworks. Compared with WebSockets, SSE uses Standard HTTP, which means full compatibility with reverse proxies (Nginx, Cloudflare) without special configuration. Automatic reconnection is built into the SSE spec — if the connection drops, the browser reconnects automatically using the last Last-Event-ID. The evolution of SSE in MCP is Streamable HTTP, which combines POST for sending and SSE for receiving through a single endpoint.
Exposing an MCP Server over HTTP means exposing it to the network—and potentially to the internet. Security is not optional: HTTPS required in production, authentication via headers, CORS configuration, and rate limiting are the minimum. An unprotected MCP server is an open door to arbitrary code execution.
Security checklist for an HTTP MCP Server:
Authorization: Bearer <token>).NEVER use HTTP without TLS in production! Without HTTPS, authentication tokens and request and response data travel in plain text—anyone on the same network can intercept them. An MCP Server exposes tools that can execute code, access databases, and manipulate files. Using HTTP without TLS in production is like leaving the vault door open with the password taped to the wall.
allow_origins=["*"] in productionWith the server running over HTTP, the next step is to put it in the cloud. There are several options—from simple platforms like Railway e Fly.io all the way to complete infrastructure with AWS Lambda or Docker on a VPS. The choice depends on your budget, scale, and control requirements.
A basic Dockerfile for deploying the MCP Server:
FROM python:3.12-slim WORKDIR /app COPY . . RUN pip install --no-cache-dir . ENV MCP_TRANSPORT=http ENV PORT=8000 EXPOSE 8000 CMD ["meu-server"]
The Dockerfile installs the Python package, sets environment variables, and exposes the port. Platforms like Railway and Fly.io automatically detect the Dockerfile and build the app. For AWS Lambda, use an adapter like mangum to convert ASGI to a Lambda handler.
Create the Dockerfile in the project root with the HTTP server configuration.
Configure environment variables on the platform: API tokens, MCP_TRANSPORT, port, and secrets.
Push the code to the Git repository connected to the platform—the deploy happens automatically.
Check the deployment logs, test the public URL, and configure a custom domain if needed.
For personal projects and MVPs, Railway e Fly.io are the fastest options—deploy directly from GitHub in minutes, with automatic HTTPS and a free tier. For production with more control, use Docker + VPS (Hetzner, DigitalOcean) with Nginx as a reverse proxy and Let's Encrypt for TLS. Always use .dockerignore to delete .env, .git e __pycache__.
With the server deployed, it’s time to verify that everything works from outside — from another machine, on another network, as a real client would. Remote testing reveals problems that local tests hide: firewalls, DNS, network latency, and CORS settings.
Testing with curl and configuring a remote client:
# Testar se o server responde $ curl https://meu-server.railway.app/sse # Configurar no Claude Desktop (claude_desktop_config.json) { "mcpServers": { "meu-server": { "url": "https://meu-server.railway.app/sse" } } }
For HTTP/SSE clients, just point to the deployed server’s URL. The client connects via HTTPS, keeps the SSE connection open, and sends requests as usual. Check latency with curl -w "%{time_total}" to ensure the response time is acceptable.
Test starting from multiple environments: your local machine, a VM in another region, and your phone (via a 4G hotspot). Each environment reveals different problems — DNS can fail on one network, CORS can block in a browser, and latency can be unacceptable from another region. Use mcp-cli or npx @modelcontextprotocol/inspector to inspect the JSON-RPC messages exchanged.
Check that the public URL responds with curl — HTTP 200 on the health check.
Connect a remote MCP client and list the available tools with tools/list.
Run a tool and measure latency — verify that the response time is less than 5 seconds for simple operations.
Next Module: 5.4 — MCP with TypeScript