HTTP Stream Transport
Modern HTTP-based transport with streaming, session management, and authentication
HTTP Stream Transport
The HTTP Stream Transport is the recommended transport mechanism for web-based MCP applications, implementing the Streamable HTTP transport protocol from the MCP specification version 2025-03-26.
Overview
The HTTP Stream Transport provides a modern, flexible transport layer that supports both batch responses and streaming via Server-Sent Events (SSE). It offers advanced features like session management, resumable streams, and comprehensive authentication options.
Key Features
- Single Endpoint: Uses a single HTTP endpoint for all MCP communication
- Multiple Response Modes: Support for both batch (JSON) and streaming (SSE) responses
- Session Management: Built-in session tracking and management
- Resumability: Support for resuming broken SSE connections
- Authentication: Comprehensive authentication support
- CORS: Flexible CORS configuration for web applications
Configuration
The HTTP Stream Transport supports extensive configuration options:
import { MCPServer } from "mcp-framework";
const server = new MCPServer({
transport: {
type: "http-stream",
options: {
port: 8080, // Port to listen on (default: 8080)
host: "127.0.0.1", // Host to bind to (default: "127.0.0.1")
endpoint: "/mcp", // HTTP endpoint path (default: "/mcp")
responseMode: "batch", // Response mode: "batch" or "stream" (default: "batch")
maxMessageSize: "4mb", // Maximum message size (default: "4mb")
batchTimeout: 30000, // Timeout for batch responses in ms (default: 30000)
headers: { // Custom headers for responses
"X-Custom-Header": "value"
},
cors: { // CORS configuration
allowOrigin: "*",
allowMethods: "GET, POST, DELETE, OPTIONS",
allowHeaders: "Content-Type, Accept, Authorization, x-api-key, Mcp-Session-Id, Last-Event-ID",
exposeHeaders: "Content-Type, Authorization, x-api-key, Mcp-Session-Id",
maxAge: "86400"
},
auth: { // Authentication configuration
provider: authProvider
},
session: { // Session configuration
enabled: true, // Enable session management (default: true)
headerName: "Mcp-Session-Id", // Session header name (default: "Mcp-Session-Id")
allowClientTermination: true // Allow clients to terminate sessions (default: true)
},
resumability: { // Stream resumability configuration
enabled: false, // Enable stream resumability (default: false)
historyDuration: 300000 // How long to keep message history in ms (default: 300000 - 5 minutes)
}
}
}
});
await server.start();Quick Start Configuration
For a simple setup with recommended defaults, you can use:
import { MCPServer } from "mcp-framework";
const server = new MCPServer({
transport: {
type: "http-stream",
options: {
port: 8080,
cors: {
allowOrigin: "*"
}
}
}
});
await server.start();Using CLI to Create a Project with HTTP Transport
You can use the MCP Framework CLI to create a new project with HTTP transport enabled:
mcp create my-mcp-server --http --port 1337 --corsThis will create a new project with HTTP transport configured on port 1337 with CORS enabled.
Configuration Options
Port, Host, and Endpoint
port: The HTTP port to listen on (default: 8080)host: The host address to bind to (default:"127.0.0.1")endpoint: The endpoint path for all MCP communication (default: "/mcp")
The server binds to 127.0.0.1 by default for security. Set host: '0.0.0.0' to accept connections from other machines (required for Docker, cloud platforms).
transport: {
type: "http-stream",
options: {
port: 8080,
host: "0.0.0.0", // Accept connections from any network interface
}
}Response Mode
The responseMode option controls how the server responds to client requests:
batch: Collects all responses for a request batch and sends them as a single JSON response (default)stream: Opens an SSE stream for each request, allowing streaming responses
transport: {
type: "http-stream",
options: {
responseMode: "batch" // or "stream"
}
}Batch mode is more efficient for simple operations, while stream mode is better for long-running operations that may benefit from progressive responses.
Batch Timeout
When using batch mode, the batchTimeout option controls how long the server will wait for all responses to be collected before sending the batch:
batchTimeout: 30000 // 30 seconds (default)Message Size Limit
The maxMessageSize option controls the maximum allowed size for incoming messages:
maxMessageSize: "4mb" // defaultCORS Configuration
The HTTP Stream Transport provides comprehensive CORS support:
cors: {
allowOrigin: "*", // Access-Control-Allow-Origin
allowMethods: "GET, POST, DELETE, OPTIONS", // Access-Control-Allow-Methods
allowHeaders: "Content-Type, Accept, Authorization, x-api-key, Mcp-Session-Id, Last-Event-ID", // Access-Control-Allow-Headers
exposeHeaders: "Content-Type, Authorization, x-api-key, Mcp-Session-Id", // Access-Control-Expose-Headers
maxAge: "86400" // Access-Control-Max-Age
}Origin Validation (DNS Rebinding Protection)
When allowedOrigins is configured within the cors block, the server validates the Origin header on every incoming request to protect against DNS rebinding attacks:
cors: {
allowedOrigins: ["http://localhost:3000", "https://myapp.example.com"],
allowOrigin: "*", // CORS Access-Control-Allow-Origin (separate from origin validation)
allowMethods: "GET, POST, DELETE, OPTIONS",
allowHeaders: "Content-Type, Accept, Authorization, x-api-key, Mcp-Session-Id, Last-Event-ID",
exposeHeaders: "Content-Type, Authorization, x-api-key, Mcp-Session-Id",
maxAge: "86400"
}How origin validation works:
- Requests with an
Originheader that does not match any entry inallowedOriginsreceive an HTTP 403 Forbidden response. - Requests without an
Originheader (non-browser clients likecurl, SDKs, and other server-side callers) are allowed through, since they are not subject to DNS rebinding. Origin: nullis always rejected whenallowedOriginsis set. This is a security best practice, asnullorigins can be crafted by malicious pages.- When
allowedOriginsis not configured, all origins are allowed. This preserves backwards compatibility with existing deployments.
For production deployments, always configure allowedOrigins to prevent DNS rebinding attacks.
Session Management
The HTTP Stream Transport provides built-in session management capabilities:
session: {
enabled: true, // Enable session management (default: true)
headerName: "Mcp-Session-Id", // Session header name (default: "Mcp-Session-Id")
allowClientTermination: true, // Allow clients to terminate sessions (default: true)
reinitializationMode: 'smart', // How to handle repeated init requests (default: 'smart')
debounceTime: 100 // Debounce time for rapid init requests in ms (default: 100)
}When sessions are enabled:
- A unique session ID is generated during initialization
- The session ID is included in the
Mcp-Session-Idheader of the server's response - Clients must include this session ID in subsequent requests
- Sessions can be explicitly terminated by clients via a DELETE request (if allowed)
Handling Rapid Initialization Requests
The reinitializationMode option controls how the transport handles repeated initialization requests:
'recreate': Always recreate the transport (legacy behavior)'reuse': Reuse the existing session without recreation'smart': Intelligently handle rapid initialization requests with debouncing (default)
The default 'smart' mode prevents issues caused by clients that send multiple initialization requests in rapid succession. This mode:
- Debounces rapid requests (if multiple init requests come within 100ms, they're grouped)
- Only recreates the transport once after the debounce period
- Prevents race conditions and 500 errors that can occur with rapid session recreation
// Example: Handling rapid initialization gracefully
const server = new MCPServer({
transport: {
type: "http-stream",
options: {
port: 8080,
cors: "*",
// Smart session management is enabled by default
// No need to configure unless you want to change the behavior
}
}
});If you need to customize the session handling:
session: {
reinitializationMode: 'reuse', // Never recreate, always reuse existing session
// or
reinitializationMode: 'recreate', // Always recreate (legacy behavior)
// or
reinitializationMode: 'smart', // Intelligent debouncing (default)
debounceTime: 200 // Increase debounce window to 200ms
}Stream Resumability
The HTTP Stream Transport can maintain message history to support resuming broken SSE connections:
resumability: {
enabled: false, // Enable stream resumability (default: false)
historyDuration: 300000 // How long to keep message history in ms (default: 300000 - 5 minutes)
}When enabled:
- Each SSE event is assigned a unique ID
- Clients can reconnect and provide the last received event ID using the
Last-Event-IDheader - The server will replay missed messages since that event ID
HTTP Methods
The HTTP Stream Transport uses the following HTTP methods:
- POST: For sending client requests, notifications, and responses
- GET: For establishing SSE streams for receiving server messages
- DELETE: For terminating sessions (when
session.allowClientTerminationis enabled) - OPTIONS: For CORS preflight requests
Client Implementation
Here's an example of how to implement a client for the HTTP Stream Transport:
/**
* Basic client for the HTTP Stream Transport
*/
class HttpStreamClient {
private baseUrl: string;
private sessionId: string | null = null;
private eventSource: EventSource | null = null;
constructor(baseUrl: string) {
this.baseUrl = baseUrl;
}
async initialize() {
// Create initialization request
const initRequest = {
jsonrpc: "2.0",
id: "init-" + Date.now(),
method: "initialize",
params: { /* initialization parameters */ }
};
// Send initialize request
const response = await fetch(this.baseUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json, text/event-stream'
},
body: JSON.stringify(initRequest)
});
// Get session ID from response headers
this.sessionId = response.headers.get('Mcp-Session-Id');
console.log(`Session established: ${this.sessionId}`);
// Process the response
if (response.headers.get('Content-Type')?.includes('text/event-stream')) {
// Handle streaming response
this.processStream(response);
} else {
// Handle JSON response
const result = await response.json();
console.log('Initialization result:', result);
}
// Open SSE stream for server-to-client messages
this.openEventStream();
}
private openEventStream() {
const url = new URL(this.baseUrl);
if (this.sessionId) {
url.searchParams.append('session', this.sessionId);
}
this.eventSource = new EventSource(url.toString());
this.eventSource.onmessage = (event) => {
try {
const message = JSON.parse(event.data);
console.log('Received SSE message:', message);
// Process message...
} catch (e) {
console.error('Error parsing SSE message:', e);
}
};
this.eventSource.onerror = (error) => {
console.error('SSE connection error:', error);
this.reconnectEventStream();
};
console.log('SSE stream opened');
}
private reconnectEventStream() {
if (this.eventSource) {
this.eventSource.close();
this.eventSource = null;
}
setTimeout(() => this.openEventStream(), 1000);
}
private async processStream(response: Response) {
const reader = response.body?.getReader();
if (!reader) return;
const decoder = new TextDecoder();
let buffer = "";
try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
// Process SSE events in buffer
const events = buffer.split("\n\n");
buffer = events.pop() || "";
for (const event of events) {
const lines = event.split("\n");
const data = lines.find(line => line.startsWith("data:"))?.slice(5);
if (data) {
try {
const message = JSON.parse(data);
console.log('Received stream message:', message);
// Process message...
} catch (e) {
console.error('Error parsing stream message:', e);
}
}
}
}
} catch (e) {
console.error('Error reading stream:', e);
}
}
async sendRequest(method: string, params: any = {}) {
if (!this.sessionId) {
throw new Error('Session not initialized');
}
const request = {
jsonrpc: "2.0",
id: method + "-" + Date.now(),
method,
params
};
const response = await fetch(this.baseUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json, text/event-stream',
'Mcp-Session-Id': this.sessionId
},
body: JSON.stringify(request)
});
if (response.headers.get('Content-Type')?.includes('text/event-stream')) {
// Handle streaming response
this.processStream(response);
return null; // Response will be processed asynchronously
} else {
// Handle JSON response
return await response.json();
}
}
async terminate() {
if (!this.sessionId) return;
if (this.eventSource) {
this.eventSource.close();
this.eventSource = null;
}
try {
await fetch(this.baseUrl, {
method: 'DELETE',
headers: {
'Mcp-Session-Id': this.sessionId
}
});
console.log('Session terminated');
} catch (e) {
console.error('Error terminating session:', e);
}
this.sessionId = null;
}
}Security Considerations
- HTTPS: Always use HTTPS in production environments
- Authentication: Enable authentication for all endpoints
- CORS: Configure appropriate CORS settings for your environment
- Origin Validation: Configure
allowedOriginsto prevent DNS rebinding attacks - Host Binding: Keep the default
127.0.0.1binding unless external access is required - Message Size: Set appropriate message size limits
- Session Timeout: Implement session timeout logic for production use
- Rate Limiting: Implement rate limiting for production use
Backward Compatibility
The HTTP Stream Transport is designed to replace the deprecated SSE Transport while maintaining compatibility with the MCP protocol. If you're migrating from the SSE Transport:
- Update your server configuration to use
type: "http-stream"instead oftype: "sse" - Update your client to use the single endpoint pattern instead of separate endpoints for SSE and messages
- Implement session management using the
Mcp-Session-Idheader
Error Handling
The transport includes comprehensive error handling, with appropriate HTTP status codes and JSON-RPC error responses:
- 400 Bad Request: Invalid JSON, invalid message format
- 401 Unauthorized: Authentication failure
- 404 Not Found: Invalid session ID
- 405 Method Not Allowed: Unsupported HTTP method
- 406 Not Acceptable: Missing required Accept header
- 413 Payload Too Large: Message size exceeds limit
- 429 Too Many Requests: Rate limit exceeded
- 500 Internal Server Error: Server-side errors
JSON-RPC error responses follow the standard format with detailed information:
{
"jsonrpc": "2.0",
"id": "request-id",
"error": {
"code": -32000,
"message": "Error message",
"data": {
// Additional error information
}
}
}HTTP QUICKSTART [EXPERIMENTAL]
Ready to build your first HTTP-based MCP server? Follow our HTTP Quickstart Guide to create and run a project using the HTTP Stream Transport in just a few minutes.