Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:24:23 +08:00
commit 5f996ee003
23 changed files with 6697 additions and 0 deletions

View File

@@ -0,0 +1,439 @@
# MCP Transport Comparison: SSE vs Streamable HTTP
**Detailed comparison of MCP transport methods for Cloudflare Workers**
---
## Overview
MCP supports two transport methods:
1. **SSE (Server-Sent Events)** - Legacy standard (2024)
2. **Streamable HTTP** - New standard (2025+)
Both work on Cloudflare Workers. You can (and should) support both for maximum compatibility.
---
## SSE (Server-Sent Events)
### What It Is
SSE is a W3C standard for server-to-client streaming over HTTP. The server holds the connection open and pushes events as they occur.
**Technical Details:**
- Protocol: HTTP/1.1 or HTTP/2
- Content-Type: `text/event-stream`
- Connection: Long-lived, unidirectional (server → client)
- Format: Plain text with `data:`, `event:`, `id:` fields
### Code Example
**Server:**
```typescript
MyMCP.serveSSE("/sse").fetch(request, env, ctx)
```
**Client config:**
```json
{
"mcpServers": {
"my-mcp": {
"url": "https://worker.dev/sse"
}
}
}
```
### Pros
**Wide compatibility** - Supported by all MCP clients (2024+)
**Well-documented** - Lots of examples and tooling
**Easy debugging** - Plain text format, human-readable
**Works with proxies** - Most HTTP proxies support SSE
**Battle-tested** - Used in production for years
### Cons
**Less efficient** - Overhead from text encoding
**Being deprecated** - MCP is moving to Streamable HTTP
**Unidirectional** - Server can push, but client uses separate requests
**Text-only** - Binary data must be base64-encoded
**Connection limits** - Browsers limit SSE connections per domain
### When to Use
- **2024-2025 transition period** - Maximum compatibility
- **Debugging** - Easier to inspect traffic
- **Legacy clients** - Older MCP implementations
- **Development** - Simpler to test with curl/MCP Inspector
---
## Streamable HTTP
### What It Is
Streamable HTTP is a modern standard for bidirectional streaming using HTTP/2+ streams. More efficient than SSE.
**Technical Details:**
- Protocol: HTTP/2 or HTTP/3
- Content-Type: `application/json` (streaming)
- Connection: Bidirectional (client ↔ server)
- Format: NDJSON (newline-delimited JSON)
### Code Example
**Server:**
```typescript
MyMCP.serve("/mcp").fetch(request, env, ctx)
```
**Client config:**
```json
{
"mcpServers": {
"my-mcp": {
"url": "https://worker.dev/mcp"
}
}
}
```
### Pros
**More efficient** - Binary-safe, less overhead
**2025 standard** - MCP's future default
**Bidirectional** - Full-duplex communication
**Better streaming** - Natively supports streaming responses
**HTTP/2 multiplexing** - Multiple streams over one connection
### Cons
**Newer clients only** - Not all 2024 clients support it
**Less tooling** - Fewer debugging tools than SSE
**HTTP/2 required** - Cloudflare Workers support this automatically
**More complex** - Harder to debug than plain text SSE
### When to Use
- **2025+** - Future-proof your implementation
- **Performance-critical** - High-throughput or low-latency needs
- **Modern clients** - Latest Claude Desktop, MCP Inspector
- **Production** - When paired with SSE fallback
---
## Side-by-Side Comparison
| Feature | SSE | Streamable HTTP |
|---------|-----|-----------------|
| **Protocol** | HTTP/1.1+ | HTTP/2+ |
| **Direction** | Unidirectional | Bidirectional |
| **Format** | Text (`text/event-stream`) | NDJSON |
| **Efficiency** | Lower | Higher |
| **Compatibility** | All MCP clients | 2025+ clients |
| **Debugging** | Easy (plain text) | Moderate (JSON) |
| **Binary data** | base64-encoded | Native support |
| **Cloudflare cost** | Standard | Standard (no difference) |
| **MCP Standard** | Legacy (2024) | Current (2025+) |
---
## Supporting Both Transports
**Best practice:** Serve both for maximum compatibility during 2024-2025 transition.
### Implementation
```typescript
export default {
fetch(request: Request, env: Env, ctx: ExecutionContext) {
const { pathname } = new URL(request.url);
// SSE transport (legacy)
if (pathname.startsWith("/sse")) {
return MyMCP.serveSSE("/sse").fetch(request, env, ctx);
}
// HTTP transport (2025 standard)
if (pathname.startsWith("/mcp")) {
return MyMCP.serve("/mcp").fetch(request, env, ctx);
}
// Health check showing available transports
if (pathname === "/" || pathname === "/health") {
return new Response(JSON.stringify({
name: "My MCP Server",
version: "1.0.0",
transports: {
sse: "/sse", // For legacy clients
http: "/mcp" // For modern clients
}
}));
}
return new Response("Not Found", { status: 404 });
}
};
```
### Client Configuration
Clients can choose which transport to use:
**Legacy client (SSE):**
```json
{
"mcpServers": {
"my-mcp": {
"url": "https://worker.dev/sse"
}
}
}
```
**Modern client (HTTP):**
```json
{
"mcpServers": {
"my-mcp": {
"url": "https://worker.dev/mcp"
}
}
}
```
---
## Performance Comparison
### Latency
**SSE:**
- Initial connection: ~100-200ms
- Tool call: ~50-100ms
- Streaming response: Good (text-based)
**Streamable HTTP:**
- Initial connection: ~100-200ms (similar)
- Tool call: ~40-80ms (slightly faster)
- Streaming response: Excellent (binary-safe)
**Verdict:** Streamable HTTP is marginally faster, but difference is negligible for most use cases.
---
### Bandwidth
**Example: 1KB text response**
**SSE:**
```
data: {"content":[{"type":"text","text":"Hello"}]}\n\n
```
- Overhead: `data: ` prefix, double newlines
- Total: ~1.05KB
**Streamable HTTP:**
```json
{"content":[{"type":"text","text":"Hello"}]}
```
- Overhead: None (pure JSON)
- Total: ~1.00KB
**Verdict:** Streamable HTTP is 5-10% more efficient (text) and much better for binary data.
---
### Connection Limits
**SSE:**
- Browsers: 6 connections per domain (HTTP/1.1)
- Not an issue for CLI/Desktop clients
**Streamable HTTP:**
- HTTP/2 multiplexing: Effectively unlimited
- Multiple streams over single connection
**Verdict:** Streamable HTTP scales better for multi-connection scenarios.
---
## Migration Path
### 2024 (Now)
**Recommendation:** Support both transports
- SSE for wide compatibility
- HTTP for future-proofing
**Code:**
```typescript
// Support both
if (pathname.startsWith("/sse")) {
return MyMCP.serveSSE("/sse").fetch(...);
}
if (pathname.startsWith("/mcp")) {
return MyMCP.serve("/mcp").fetch(...);
}
```
---
### 2025 (Future)
**Recommendation:** Deprecate SSE, keep as fallback
- HTTP as primary
- SSE for legacy clients only
**Code:**
```typescript
// Prefer HTTP
if (pathname.startsWith("/mcp")) {
return MyMCP.serve("/mcp").fetch(...);
}
// Legacy SSE fallback
if (pathname.startsWith("/sse")) {
return MyMCP.serveSSE("/sse").fetch(...);
}
```
---
### 2026+ (Long-term)
**Recommendation:** HTTP only
- Remove SSE support
- All clients updated to HTTP
**Code:**
```typescript
// HTTP only
if (pathname.startsWith("/mcp")) {
return MyMCP.serve("/mcp").fetch(...);
}
```
---
## Cloudflare Workers Considerations
### Cost
**Both transports cost the same** on Cloudflare Workers:
- Charged per request
- Charged per CPU time
- No difference in pricing
### Performance
**Cloudflare Workers natively support both:**
- SSE: Works perfectly (HTTP/1.1 and HTTP/2)
- HTTP/2: Automatic (no configuration needed)
- Streaming: Both transports can stream responses
### Limits
**No transport-specific limits:**
- Request size: 100MB (both)
- CPU time: 50ms-30s depending on plan (both)
- Concurrent requests: Unlimited (both)
---
## Debugging
### SSE Debugging
**curl:**
```bash
curl -N https://worker.dev/sse
```
**Output:**
```
data: {"jsonrpc":"2.0","method":"initialize",...}
data: {"jsonrpc":"2.0","method":"tools/list",...}
```
**Human-readable:** ✅ Easy to inspect
---
### HTTP Debugging
**curl:**
```bash
curl https://worker.dev/mcp -H "Content-Type: application/json" -d '{...}'
```
**Output:**
```json
{"jsonrpc":"2.0","method":"initialize",...}
{"jsonrpc":"2.0","method":"tools/list",...}
```
**Human-readable:** ✅ Still readable (NDJSON)
---
## When to Choose One Over the Other
### Choose SSE When:
- Supporting 2024 MCP clients
- Debugging connection issues (easier to inspect)
- Working with legacy systems
- Need maximum compatibility
- Developing/testing with basic tools
### Choose Streamable HTTP When:
- Building for 2025+
- Performance matters (high-throughput)
- Using modern clients (latest Claude Desktop)
- Want future-proof implementation
- Need bidirectional streaming
### Support Both When:
- In production (2024-2025 transition)
- Serving diverse clients
- Want maximum compatibility
- No cost difference (same Worker code)
---
## Summary
**Current Recommendation (2024-2025):**
```typescript
// Support BOTH transports
if (pathname.startsWith("/sse")) {
return MyMCP.serveSSE("/sse").fetch(request, env, ctx);
}
if (pathname.startsWith("/mcp")) {
return MyMCP.serve("/mcp").fetch(request, env, ctx);
}
```
**Why:**
- SSE: Maximum compatibility
- HTTP: Future-proof
- No cost difference
- Clients choose what they support
**Future (2026+):**
- HTTP will be the only standard
- SSE will be deprecated
- But for now, support both!
---
## Additional Resources
- **MCP Specification**: https://modelcontextprotocol.io/
- **SSE Spec (W3C)**: https://html.spec.whatwg.org/multipage/server-sent-events.html
- **HTTP/2 Spec (RFC 7540)**: https://httpwg.org/specs/rfc7540.html
- **Cloudflare Workers**: https://developers.cloudflare.com/workers/