This guide covers converting STDIO MCP servers to Streamable HTTP, the current standard for remote MCP deployments (protocol version 2025-03-26). All code examples follow correct initialization patterns to avoid common errors.
Why Convert to Remote?
Cloud Deployment Host your server on any cloud platform and make it globally accessible
Multi-Client Support Handle multiple concurrent client connections simultaneously
Better Integration Easier integration with web apps, mobile apps, and distributed systems
Horizontal Scaling Deploy behind load balancers and scale as needed
Understanding MCP Transports
STDIO Transport
Best for: Local development, single client
Client spawns server as subprocess → stdin/stdout communication
Pros: Zero network overhead, simple setup
Cons: Same machine only, no multi-client support
Streamable HTTP (Recommended)
Best for: Production, cloud hosting, multiple clients
Server runs independently → Clients connect via HTTP
Pros: Single endpoint, bidirectional, optional sessions
Cons: Requires web server configuration
Streamable HTTP is the current standard (protocol version 2025-03-26). Use this for all new projects!
SSE Transport (Legacy)
Status: Superseded by Streamable HTTP
SSE is no longer the standard. Only use for backward compatibility with older clients.
Prerequisites
# Check Python version (need 3.10+)
python --version
# Install dependencies
pip install mcp fastapi uvicorn
# Optional: FastMCP for rapid development
pip install fastmcp
1️⃣ Your Original STDIO Server
Let’s start with a typical STDIO server that runs locally:
# stdio_server.py
import asyncio
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
server = Server( "weather-server" , version = "1.0.0" )
@server.list_tools ()
async def list_tools () -> list[Tool]:
return [
Tool(
name = "get_weather" ,
description = "Get weather for a location" ,
inputSchema = {
"type" : "object" ,
"properties" : {
"location" : { "type" : "string" }
},
"required" : [ "location" ]
}
)
]
@server.call_tool ()
async def call_tool ( name : str , arguments : dict ) -> list[TextContent]:
if name == "get_weather" :
location = arguments.get( "location" , "Unknown" )
return [TextContent(
type = "text" ,
text = f "Weather in { location } : Sunny, 72°F"
)]
raise ValueError ( f "Unknown tool: { name } " )
async def main ():
# STDIO transport - runs as subprocess
async with stdio_server() as (read_stream, write_stream):
await server.run(
read_stream,
write_stream,
server.create_initialization_options()
)
if __name__ == "__main__" :
asyncio.run(main())
2️⃣ Convert to Streamable HTTP
# http_server.py
from fastmcp import FastMCP
# Create MCP server at startup
mcp = FastMCP( "weather-server" ) // [ ! code highlight]
# Define your tool (same logic as before!)
@mcp.tool ()
def get_weather ( location : str ) -> str :
"""Get weather for a location."""
return f "Weather in { location } : Sunny, 72°F"
if __name__ == "__main__" :
# FastMCP handles transport initialization
mcp.run( // [ ! code highlight]
transport = "http" , // [ ! code highlight]
host = "0.0.0.0" , // [ ! code highlight]
port = 8000 , // [ ! code highlight]
path = "/mcp" // [ ! code highlight]
) // [ ! code highlight]
See all 20 lines
FastMCP vs FastAPI: FastMCP provides a simpler API for quick setups. Use FastAPI when integrating MCP into existing FastAPI applications or when you need more control over the web server configuration.
3️⃣ Add auth
Most STDIO servers use environment variables for authentication. Convert these to HTTP-based auth patterns for remote servers.
Example: OAuth Credentials Pattern
STDIO Version (environment variables):
{
"mcpServers" : {
"google-calendar" : {
"command" : "npx" ,
"args" : [ "@cocal/google-calendar-mcp" ],
"env" : {
"GOOGLE_OAUTH_CREDENTIALS" : "/path/to/gcp-oauth.keys.json"
}
}
}
}
Remote Version (request headers):
from fastapi import Header, HTTPException, Depends
import base64
import json
def get_credentials ( authorization : str = Header( None )) -> dict :
"""Extract credentials from Authorization header."""
if not authorization or not authorization.startswith( "Bearer " ):
raise HTTPException( status_code = 401 , detail = "Invalid auth" )
token = authorization.replace( "Bearer " , "" )
try :
return json.loads(base64.b64decode(token))
except Exception :
raise HTTPException( status_code = 401 , detail = "Invalid token" )
@app.post ( "/mcp" )
async def handle_mcp (
request : Request,
credentials : dict = Depends(get_credentials)
):
# Use credentials from request
pass
Simpler Pattern: API Keys
For basic authentication, use API keys:
from fastapi import Header, HTTPException, Depends
import os
async def verify_api_key ( authorization : str = Header( None )):
"""Verify API key from header."""
if not authorization:
raise HTTPException( status_code = 401 , detail = "Missing API key" )
api_key = authorization.replace( "Bearer " , "" )
if api_key != os.getenv( "API_KEY" ):
raise HTTPException( status_code = 401 , detail = "Invalid API key" )
return api_key
@app.post ( "/mcp" )
async def handle_mcp (
request : Request,
api_key : str = Depends(verify_api_key)
):
# Request is authenticated
pass
4️⃣ Run Your MCP Server
Start your converted server:
python http_server.py
# Server runs at http://localhost:8000/mcp
5️⃣ Testing with Hoot 🦉
Hoot - MCP Testing Tool Like Postman, but specifically designed for testing MCP servers. Perfect for development!
Quick Start
# Run directly (no installation needed!)
npx -y @portkey-ai/hoot
# Or install globally
npm install -g @portkey-ai/hoot
hoot
Hoot opens at http://localhost:8009
Using Hoot
Start your server
python http_server.py
# Server runs at http://localhost:8000/mcp
Open Hoot
Navigate to http://localhost:8009
Connect to your server
Paste URL: http://localhost:8000/mcp
Hoot auto-detects the transport type!
Test your tools
View all available tools
Select get_weather
Add parameters: {"location": "San Francisco"}
Click “Execute”
See the response!
Hoot Features
Auto-Detection Automatically detects HTTP vs SSE
Tool Explorer View and test all server tools
OAuth Support Handles OAuth 2.1 authentication
Beautiful Themes 8 themes with light & dark modes
Optional: Session Management
Session management is optional in the MCP spec. FastMCP handles it automatically if you need stateful interactions.
# FastMCP handles sessions automatically
# Stateful mode (maintains session state)
mcp = FastMCP( "weather-server" , stateless_http = False )
# Stateless mode (no session state)
mcp = FastMCP( "weather-server" , stateless_http = True )
Optional: CORS Configuration
Only add CORS if you need to support browser-based clients. For server-to-server communication, CORS isn’t necessary.
# CORS with FastMCP
mcp.run(
transport = "http" ,
host = "0.0.0.0" ,
port = 8000 ,
cors_allow_origins = [ "https://yourdomain.com" ]
)
Deployment
Docker
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
EXPOSE 8000
CMD [ "python" , "http_server.py" ]
Quick Deploy
# Install Fly CLI
curl -L https://fly.io/install.sh | sh
# Deploy
fly launch
fly deploy
Troubleshooting
Check:
Server is running on the correct port
Firewall allows connections
URL is correct (including /mcp path)
Test with curl: curl -X POST http://localhost:8000/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}'
Solution: Client must store and send session ID correctly# Extract from initialization response
session_id = response.headers.get( "Mcp-Session-Id" )
# Include in all subsequent requests
headers = { "Mcp-Session-Id" : session_id}
Summary
You’ve successfully converted your STDIO server to a remote Streamable HTTP server!
Key Principles
Use HTTP Transport Replace STDIO with Streamable HTTP for remote access
Header-Based Auth Convert environment variables to HTTP headers
Initialize at Startup Server and transport created once at startup
Test Thoroughly Use Hoot to verify all tools work correctly
What We Covered
✅ Original STDIO server structure
✅ Converting to Streamable HTTP
✅ Auth conversion from env vars to headers
✅ Running your converted server
✅ Testing with Hoot
Resources
Building something cool? Share it with the MCP community and let us know how this guide helped!