MCP SSE Proxy
/mcp/:model_name/sseProxies JSON-RPC messages to an upstream MCP server and streams the response back as Server-Sent Events (SSE). This is the primary endpoint for invoking MCP tools — your client sends a JSON-RPC request, SandBase injects the required credentials, forwards it to the upstream server, and streams the result back to you.
Authentication
| Header | Required | Description |
|---|---|---|
Authorization: Bearer <API_KEY> | Yes | Your SandBase API key |
All requests to this endpoint require a valid API key. The key is used to identify your organization for usage tracking and rate limiting. Upstream server credentials are managed by SandBase and injected automatically — you never need to provide them.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model_name | string | Yes | The unique server identifier (e.g. exa/exa). Obtain this from the List MCP Servers endpoint. |
Request Body
The request body must be a valid JSON-RPC 2.0 message. The most common method is tools/call to invoke a tool on the server.
Tool Call Request
| Field | Type | Description |
|---|---|---|
jsonrpc | string | Must be "2.0" |
id | integer | string | Request identifier — echoed back in the response |
method | string | The MCP method to invoke (e.g. tools/call, prompts/get, resources/read) |
params | object | Method-specific parameters |
For tools/call, the params object contains:
| Field | Type | Description |
|---|---|---|
name | string | Tool name (from the List Tools endpoint) |
arguments | object | Tool input matching the tool's inputSchema |
How SSE Streaming Works
Connection Lifecycle
- Client sends POST — Your client sends a JSON-RPC request to
/mcp/:model_name/sse - SandBase authenticates — Validates your API key and resolves the MCP server
- Credential injection — SandBase injects the upstream server's API key (you don't need it)
- Upstream forwarding — The request is forwarded to the MCP server's deployment URL
- SSE streaming — The upstream response is streamed back chunk-by-chunk as SSE events
- Connection closes — The stream ends when the upstream server completes its response
Response Format
The response uses Content-Type: text/event-stream. Events are streamed as they arrive from the upstream server, following the standard SSE format:
event: message
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"..."}]}}Each SSE event contains:
event:— The event type (typicallymessage)data:— A JSON-RPC response object
Timeout
The proxy has a 30-second timeout. If the upstream server does not respond within 30 seconds, the connection is closed with a gateway timeout error.
Example Request
Invoke a Tool (curl)
curl -X POST "https://mcp.sandbase.ai/exa/exa/sse" \
-H "Authorization: Bearer sb-your-api-key" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "web_search",
"arguments": {
"query": "latest developments in AI agents",
"num_results": 3
}
}
}'Python Example
import requests
import json
response = requests.post(
"https://mcp.sandbase.ai/exa/exa/sse",
headers={
"Authorization": "Bearer sb-your-api-key",
"Content-Type": "application/json",
},
json={
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "web_search",
"arguments": {"query": "MCP protocol specification"}
}
},
stream=True,
)
# Process SSE events as they arrive
for line in response.iter_lines():
if line:
decoded = line.decode("utf-8")
if decoded.startswith("data: "):
payload = json.loads(decoded[6:])
print(payload)JavaScript Example
const response = await fetch("https://mcp.sandbase.ai/exa/exa/sse", {
method: "POST",
headers: {
"Authorization": "Bearer sb-your-api-key",
"Content-Type": "application/json",
},
body: JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "tools/call",
params: {
name: "web_search",
arguments: { query: "MCP protocol specification" },
},
}),
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split("\n");
for (const line of lines) {
if (line.startsWith("data: ")) {
const payload = JSON.parse(line.slice(6));
console.log(payload);
}
}
}Example Response
A successful tool call streams back a JSON-RPC result:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "Here are the search results for 'latest developments in AI agents':\n\n1. ..."
}
]
}
}Error Responses
Errors can occur at two levels: the SandBase proxy layer (returned as HTTP errors) and the upstream MCP server (returned as JSON-RPC errors within the stream).
Proxy-Level Errors
| Status | Error Code | Description |
|---|---|---|
| 400 | invalid_request | Missing model_name or unreadable request body |
| 401 | unauthorized | Missing or invalid API key |
| 404 | not_found | Server does not exist or is not an MCP-type model |
| 502 | upstream_error | Upstream server returned an error or is unreachable |
| 502 | configuration_error | Server credentials or deployment URL misconfigured |
| 504 | upstream_timeout | Upstream server did not respond within 30 seconds |
Proxy Error Example
{
"error": "not_found",
"message": "MCP server not found: invalid/server"
}{
"error": "upstream_timeout",
"message": "Upstream server did not respond within 30 seconds"
}Upstream JSON-RPC Errors
If the upstream server returns an error within the MCP protocol, it appears as a JSON-RPC error in the streamed response:
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32602,
"message": "Invalid params: 'query' is required"
}
}Common JSON-RPC error codes:
| Code | Meaning |
|---|---|
| -32700 | Parse error — invalid JSON |
| -32600 | Invalid request — not a valid JSON-RPC object |
| -32601 | Method not found — the server doesn't support this method |
| -32602 | Invalid params — missing or invalid parameters |
| -32603 | Internal error — server-side failure |
Error Handling Best Practices
- Check HTTP status first — A non-2xx status means the proxy itself failed (auth, routing, timeout)
- Parse SSE events — On a 200 response, read the streamed events and check for JSON-RPC
errorfields - Handle timeouts — If your tool call may take longer than 30 seconds, consider breaking it into smaller operations
- Retry on 502 — Upstream errors may be transient; retry with exponential backoff
import time
import requests
def call_mcp_tool(server, tool_name, arguments, max_retries=3):
for attempt in range(max_retries):
resp = requests.post(
f"https://mcp.sandbase.ai/{server}/sse",
headers={
"Authorization": "Bearer sb-your-api-key",
"Content-Type": "application/json",
},
json={
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {"name": tool_name, "arguments": arguments},
},
stream=True,
)
if resp.status_code == 200:
return resp
elif resp.status_code in (502, 504) and attempt < max_retries - 1:
time.sleep(2 ** attempt)
continue
else:
resp.raise_for_status()Usage Tracking
Each successful tool call is recorded as a usage event tied to your organization and API key. You can view MCP usage in the SandBase Dashboard.
Notes
- The SSE proxy URL is also available at
https://mcp.sandbase.ai/:model_name/sse— this is the canonical URL used in client configurations. - The proxy handles credential injection transparently. You authenticate with your SandBase API key; the upstream server's credentials are never exposed to your client.
- Request bodies are forwarded as-is after credential injection. The proxy does not validate JSON-RPC structure — invalid requests will be passed through and the upstream server will return a JSON-RPC error.
- The
Content-Typeof the response depends on the upstream server but is typicallytext/event-streamfor SSE connections.
Related
- List MCP Servers — Discover available servers and get
model_nameidentifiers - List Tools — Get available tools for a server before calling them
- Client Configuration — Generate IDE config snippets that point to this endpoint

