Skip to content

Commit 3570545

Browse files
feat: Add HTTP-Streaming support for MCP with backward compatibility
- Implemented complete HTTP-Streaming transport for Python and TypeScript - Added smart auto-detection: URLs ending with /sse use SSE, others use HTTP-streaming - Added explicit transport selection via transport parameter - Maintained 100% backward compatibility - all existing code works unchanged - Added comprehensive examples and documentation - Created backward compatibility test suite Co-authored-by: Mervin Praison <MervinPraison@users.noreply.github.com>
1 parent d7fcaf8 commit 3570545

File tree

10 files changed

+1886
-6
lines changed

10 files changed

+1886
-6
lines changed

docs/MCP_HTTP_STREAMING.md

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
# MCP HTTP-Streaming Support
2+
3+
This document describes the HTTP-Streaming transport implementation for MCP (Model Context Protocol) in PraisonAI.
4+
5+
## Overview
6+
7+
HTTP-Streaming provides bidirectional streaming communication over HTTP using chunked transfer encoding. This transport method offers advantages over SSE (Server-Sent Events) including:
8+
9+
- **Bidirectional streaming** - Both client and server can stream data
10+
- **Binary support** - Can transmit binary data, not just text
11+
- **Lower overhead** - More efficient than SSE's text-based protocol
12+
- **Better performance** - Ideal for high-throughput scenarios
13+
14+
## Usage
15+
16+
### Auto-Detection (Default)
17+
18+
The MCP client automatically detects the appropriate transport based on URL patterns:
19+
20+
```python
21+
# SSE transport (URLs ending with /sse)
22+
agent = Agent(
23+
tools=MCP("http://localhost:8080/sse") # Uses SSE
24+
)
25+
26+
# HTTP-Streaming transport (other HTTP URLs)
27+
agent = Agent(
28+
tools=MCP("http://localhost:8080/api") # Uses HTTP-Streaming
29+
)
30+
```
31+
32+
### Explicit Transport Selection
33+
34+
You can explicitly specify the transport type:
35+
36+
```python
37+
# Force SSE transport
38+
agent = Agent(
39+
tools=MCP("http://localhost:8080/api", transport="sse")
40+
)
41+
42+
# Force HTTP-Streaming transport
43+
agent = Agent(
44+
tools=MCP("http://localhost:8080/sse", transport="http-streaming")
45+
)
46+
```
47+
48+
### TypeScript Usage
49+
50+
The TypeScript implementation follows the same pattern:
51+
52+
```typescript
53+
import { MCP } from '@praisonai/agents/tools';
54+
55+
// Auto-detection
56+
const mcpAuto = new MCP("http://localhost:8080/api");
57+
await mcpAuto.initialize();
58+
59+
// Explicit transport
60+
const mcpExplicit = new MCP("http://localhost:8080/api", {
61+
transport: "http-streaming",
62+
debug: true,
63+
headers: {
64+
"Authorization": "Bearer token"
65+
}
66+
});
67+
await mcpExplicit.initialize();
68+
```
69+
70+
## Transport Detection Rules
71+
72+
The following URL patterns automatically use SSE transport:
73+
- `/sse` (exact ending)
74+
- `/sse/` (with trailing slash)
75+
- `/events` (exact ending)
76+
- `/stream` (exact ending)
77+
- `/server-sent-events`
78+
- URLs containing `transport=sse` query parameter
79+
80+
All other HTTP/HTTPS URLs default to HTTP-Streaming transport.
81+
82+
## Implementation Details
83+
84+
### Message Format
85+
86+
HTTP-Streaming uses NDJSON (Newline Delimited JSON) format:
87+
- Each message is a complete JSON object
88+
- Messages are separated by newline characters (`\n`)
89+
- Supports efficient streaming parsing
90+
91+
### Python Architecture
92+
93+
```
94+
MCP (main class)
95+
├── _detect_transport() - Auto-detection logic
96+
├── HTTPStreamingMCPClient - HTTP-Streaming implementation
97+
│ ├── HTTPStreamingTransport - Low-level transport
98+
│ ├── HTTPReadStream - Read adapter
99+
│ └── HTTPWriteStream - Write adapter
100+
└── SSEMCPClient - SSE implementation (existing)
101+
```
102+
103+
### TypeScript Architecture
104+
105+
```
106+
MCP (unified class)
107+
├── detectTransport() - Auto-detection logic
108+
├── MCPHttpStreaming - HTTP-Streaming implementation
109+
│ └── HTTPStreamingTransport - Transport layer
110+
│ ├── HTTPStreamingTransport - Modern browsers
111+
│ └── HTTPStreamingTransportFallback - Legacy browsers
112+
└── MCPSse - SSE implementation (existing)
113+
```
114+
115+
## Server Implementation
116+
117+
### Python Server Example
118+
119+
```python
120+
from fastapi import FastAPI, Request
121+
from fastapi.responses import StreamingResponse
122+
import json
123+
import asyncio
124+
125+
app = FastAPI()
126+
127+
@app.post("/mcp/v1/stream")
128+
async def mcp_stream(request: Request):
129+
async def generate():
130+
async for chunk in request.stream():
131+
# Process incoming messages
132+
message = json.loads(chunk)
133+
134+
# Generate response
135+
response = process_mcp_message(message)
136+
yield json.dumps(response).encode() + b'\n'
137+
138+
return StreamingResponse(
139+
generate(),
140+
media_type="application/x-ndjson"
141+
)
142+
```
143+
144+
### Node.js Server Example
145+
146+
```javascript
147+
const express = require('express');
148+
const app = express();
149+
150+
app.post('/mcp/v1/stream', (req, res) => {
151+
res.writeHead(200, {
152+
'Content-Type': 'application/x-ndjson',
153+
'Transfer-Encoding': 'chunked'
154+
});
155+
156+
req.on('data', (chunk) => {
157+
const message = JSON.parse(chunk);
158+
const response = processMCPMessage(message);
159+
res.write(JSON.stringify(response) + '\n');
160+
});
161+
162+
req.on('end', () => {
163+
res.end();
164+
});
165+
});
166+
```
167+
168+
## Configuration Options
169+
170+
### Python Options
171+
172+
```python
173+
MCP(
174+
url,
175+
transport="http-streaming", # Explicit transport
176+
timeout=60, # Request timeout in seconds
177+
debug=True, # Enable debug logging
178+
headers={ # Custom headers
179+
"Authorization": "Bearer token"
180+
}
181+
)
182+
```
183+
184+
### TypeScript Options
185+
186+
```typescript
187+
new MCP(url, {
188+
transport: "http-streaming", // Explicit transport
189+
timeout: 60000, // Timeout in milliseconds
190+
debug: true, // Enable debug logging
191+
headers: { // Custom headers
192+
"Authorization": "Bearer token"
193+
},
194+
fallbackMode: false // Force fallback for testing
195+
})
196+
```
197+
198+
## Backward Compatibility
199+
200+
The implementation maintains 100% backward compatibility:
201+
202+
1. **Existing SSE URLs** continue to use SSE transport
203+
2. **Stdio commands** work unchanged
204+
3. **NPX commands** work unchanged
205+
4. **All existing code** continues to function without modification
206+
207+
## Migration Guide
208+
209+
No migration is required! Existing code continues to work. To use HTTP-Streaming:
210+
211+
1. **Option 1**: Use URLs that don't match SSE patterns (recommended)
212+
2. **Option 2**: Add `transport="http-streaming"` parameter
213+
214+
## Troubleshooting
215+
216+
### Debug Mode
217+
218+
Enable debug logging to see transport selection:
219+
220+
```python
221+
MCP(url, debug=True)
222+
```
223+
224+
### Common Issues
225+
226+
1. **Connection Refused**: Ensure the server supports HTTP-Streaming at the endpoint
227+
2. **Transport Errors**: Check if the server implements the correct protocol
228+
3. **Browser Compatibility**: TypeScript fallback mode handles older browsers
229+
230+
## Performance Considerations
231+
232+
HTTP-Streaming is recommended for:
233+
- High-frequency message exchange
234+
- Large message payloads
235+
- Binary data transmission
236+
- Bidirectional communication needs
237+
238+
SSE remains suitable for:
239+
- Simple server-to-client streaming
240+
- Text-only data
241+
- Browser compatibility requirements
242+
- Existing SSE infrastructure
243+
244+
## Future Enhancements
245+
246+
Potential future improvements:
247+
- WebSocket transport option
248+
- gRPC streaming support
249+
- Connection pooling
250+
- Automatic reconnection for HTTP-Streaming
251+
- Compression support
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
"""
2+
Example demonstrating MCP with HTTP-Streaming transport.
3+
4+
This example shows:
5+
1. Auto-detection of transport based on URL
6+
2. Explicit transport selection
7+
3. Backward compatibility with existing code
8+
"""
9+
10+
from praisonaiagents import Agent
11+
from praisonaiagents.mcp import MCP
12+
13+
# Example 1: Auto-detection - SSE endpoint (backward compatible)
14+
print("Example 1: Auto-detection with SSE endpoint")
15+
try:
16+
agent_sse_auto = Agent(
17+
instructions="You are a helpful assistant that can use MCP tools.",
18+
llm="gpt-4o-mini",
19+
tools=MCP("http://localhost:8080/sse") # Auto-detects SSE transport
20+
)
21+
print("✓ SSE transport detected automatically")
22+
except Exception as e:
23+
print(f"Note: {e}")
24+
25+
# Example 2: Auto-detection - HTTP endpoint
26+
print("\nExample 2: Auto-detection with HTTP endpoint")
27+
try:
28+
agent_http_auto = Agent(
29+
instructions="You are a helpful assistant that can use MCP tools.",
30+
llm="gpt-4o-mini",
31+
tools=MCP("http://localhost:8080/api") # Auto-detects HTTP-streaming transport
32+
)
33+
print("✓ HTTP-streaming transport detected automatically")
34+
except Exception as e:
35+
print(f"Note: {e}")
36+
37+
# Example 3: Explicit SSE transport
38+
print("\nExample 3: Explicit SSE transport selection")
39+
try:
40+
agent_sse_explicit = Agent(
41+
instructions="You are a helpful assistant that can use MCP tools.",
42+
llm="gpt-4o-mini",
43+
tools=MCP("http://localhost:8080/api", transport="sse") # Force SSE
44+
)
45+
print("✓ SSE transport explicitly selected")
46+
except Exception as e:
47+
print(f"Note: {e}")
48+
49+
# Example 4: Explicit HTTP-streaming transport
50+
print("\nExample 4: Explicit HTTP-streaming transport selection")
51+
try:
52+
agent_http_explicit = Agent(
53+
instructions="You are a helpful assistant that can use MCP tools.",
54+
llm="gpt-4o-mini",
55+
tools=MCP("http://localhost:8080/sse", transport="http-streaming") # Force HTTP-streaming
56+
)
57+
print("✓ HTTP-streaming transport explicitly selected")
58+
except Exception as e:
59+
print(f"Note: {e}")
60+
61+
# Example 5: HTTP-streaming with custom headers
62+
print("\nExample 5: HTTP-streaming with custom headers")
63+
try:
64+
agent_http_headers = Agent(
65+
instructions="You are a helpful assistant that can use MCP tools.",
66+
llm="gpt-4o-mini",
67+
tools=MCP(
68+
"http://localhost:8080/api",
69+
transport="http-streaming",
70+
headers={"Authorization": "Bearer your-token-here"}
71+
)
72+
)
73+
print("✓ HTTP-streaming with custom headers configured")
74+
except Exception as e:
75+
print(f"Note: {e}")
76+
77+
# Example 6: Existing stdio usage - completely unchanged
78+
print("\nExample 6: Existing stdio usage (backward compatible)")
79+
try:
80+
agent_stdio = Agent(
81+
instructions="You are a helpful assistant that can use MCP tools.",
82+
llm="gpt-4o-mini",
83+
tools=MCP(
84+
command="/path/to/python",
85+
args=["/path/to/mcp_server.py"]
86+
)
87+
)
88+
print("✓ Stdio transport works as before")
89+
except Exception as e:
90+
print(f"Note: {e}")
91+
92+
# Example 7: NPX usage - completely unchanged
93+
print("\nExample 7: NPX usage (backward compatible)")
94+
try:
95+
agent_npx = Agent(
96+
instructions="You are a helpful assistant that can use MCP tools.",
97+
llm="gpt-4o-mini",
98+
tools=MCP("npx @modelcontextprotocol/server-brave-search")
99+
)
100+
print("✓ NPX transport works as before")
101+
except Exception as e:
102+
print(f"Note: {e}")
103+
104+
print("\n" + "="*50)
105+
print("Summary: HTTP-Streaming support added with full backward compatibility!")
106+
print("- Auto-detection: URLs ending with /sse use SSE, others use HTTP-streaming")
107+
print("- Explicit control: Use transport='sse' or transport='http-streaming'")
108+
print("- All existing code continues to work without modification")
109+
print("="*50)

0 commit comments

Comments
 (0)