Skip to content

MCP SSE Proxy

POST/mcp/:model_name/sse

Proxies 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

HeaderRequiredDescription
Authorization: Bearer <API_KEY>YesYour 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

ParameterTypeRequiredDescription
model_namestringYesThe 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

FieldTypeDescription
jsonrpcstringMust be "2.0"
idinteger | stringRequest identifier — echoed back in the response
methodstringThe MCP method to invoke (e.g. tools/call, prompts/get, resources/read)
paramsobjectMethod-specific parameters

For tools/call, the params object contains:

FieldTypeDescription
namestringTool name (from the List Tools endpoint)
argumentsobjectTool input matching the tool's inputSchema

How SSE Streaming Works

Connection Lifecycle

  1. Client sends POST — Your client sends a JSON-RPC request to /mcp/:model_name/sse
  2. SandBase authenticates — Validates your API key and resolves the MCP server
  3. Credential injection — SandBase injects the upstream server's API key (you don't need it)
  4. Upstream forwarding — The request is forwarded to the MCP server's deployment URL
  5. SSE streaming — The upstream response is streamed back chunk-by-chunk as SSE events
  6. 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 (typically message)
  • 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)

bash
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

python
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

javascript
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:

json
{
  "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

StatusError CodeDescription
400invalid_requestMissing model_name or unreadable request body
401unauthorizedMissing or invalid API key
404not_foundServer does not exist or is not an MCP-type model
502upstream_errorUpstream server returned an error or is unreachable
502configuration_errorServer credentials or deployment URL misconfigured
504upstream_timeoutUpstream server did not respond within 30 seconds

Proxy Error Example

json
{
  "error": "not_found",
  "message": "MCP server not found: invalid/server"
}
json
{
  "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:

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params: 'query' is required"
  }
}

Common JSON-RPC error codes:

CodeMeaning
-32700Parse error — invalid JSON
-32600Invalid request — not a valid JSON-RPC object
-32601Method not found — the server doesn't support this method
-32602Invalid params — missing or invalid parameters
-32603Internal error — server-side failure

Error Handling Best Practices

  1. Check HTTP status first — A non-2xx status means the proxy itself failed (auth, routing, timeout)
  2. Parse SSE events — On a 200 response, read the streamed events and check for JSON-RPC error fields
  3. Handle timeouts — If your tool call may take longer than 30 seconds, consider breaking it into smaller operations
  4. Retry on 502 — Upstream errors may be transient; retry with exponential backoff
python
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-Type of the response depends on the upstream server but is typically text/event-stream for SSE connections.