Skip to main content

Server-Sent Events

Tool execution in ORS utilises Server-Sent Events (SSE) to deliver results. SSE keeps connections alive during long-running tool calls and chunks large responses for reliable delivery.

Why SSE for Tool Calls?

Traditional request-response doesn’t work well for tool execution: Problems with standard HTTP:
  • Bash commands can run for minutes
  • LLM calls take 10-30 seconds
  • File operations vary in duration
  • Connections may time out before the tool finishes
Benefits of SSE:
  • Keeps connections alive with periodic pings while tools execute
  • Chunks large responses (>4KB) into smaller pieces for reliable delivery
  • Enables client reconnection via task IDs if the connection drops
  • Built into browsers and HTTP libraries
  • Simpler than WebSockets (one-way, server → client)

SSE Basics

What is SSE?

Server-Sent Events is a standard for server-to-client streaming over HTTP:
Format:
  • Each event has an event type and data payload
  • Events separated by blank lines
  • Connection stays open for multiple events
  • Client closes when done

Tool Call SSE Flow

Request

Headers:
  • Accept: text/event-stream - Recommended (server returns SSE regardless)
  • X-Session-ID - Session identifier
Body:
  • name: Tool to call
  • input: Tool parameters
  • task_id: Optional ID for reconnection/tracing

Response Stream

The server sends events in this sequence:

1. Task ID Event

First event identifies the task:
Purpose: Client can use this ID to reconnect if disconnected.

2. Chunk Events (if result is large)

For results > 4KB, sent in chunks:
Purpose: Deliver large results in 4KB chunks.

3. End Event

Final event with complete result:
For small results (under 4KB), this is the only data event.

4. Error Event (if error occurs)

If tool execution fails:
Note: This is an HTTP-level error (tool execution itself failed), not a tool logic error.

Complete Example

Successful tool call:
Response:
Failed tool call:

Event Types

task_id

Purpose: Provide task ID for reconnection. Data: String task ID (UUID format). When: First event in every stream. Usage:

chunk

Purpose: Deliver large results in 4KB chunks. Data: Partial JSON string (may not be valid JSON until fully assembled). When: For results > 4KB, sent progressively. Usage:

end

Purpose: Final result (either complete small result or last chunk of large result). Data: Complete JSON for RunToolOutput:
When: Last event in successful stream. Usage:

error

Purpose: Indicate tool execution failure. Data: String error message. When: Tool execution failed at HTTP/server level. Note: This is different from tool logic errors, which return {"ok": false, "error": "..."} in an end event.

Handling SSE in Clients

Python (httpx)

Python (ORS SDK)

The Python SDK handles SSE automatically:

JavaScript (fetch)

curl

Reconnection

If the SSE connection drops, clients can reconnect using the task_id:

Save Task ID

Reconnect with Task ID

What happens:
  • Server checks if task is already running
  • If still running, stream events from current state
  • If completed, immediately send cached result
  • If unknown, returns an error event with unknown task_id
Caching: Completed task results are cached for 60 seconds.

Server-Side Considerations

Implementing SSE in Your Server

If implementing an ORS server from scratch:

Keep-Alive Pings

The server sends periodic pings (every 10 seconds) to keep the connection alive:
These are SSE comments (lines starting with :) and are ignored by clients.

Error Handling

HTTP-Level Errors

Tool execution fails at server level:
Causes:
  • Session doesn’t exist
  • Session timed out
  • Tool name not recognized
  • Server internal error
Client handling:

Tool Logic Errors

Tool executes but returns error:
Causes:
  • Tool input validation failed
  • Tool logic error (e.g., file not found)
  • Expected failure (e.g., incorrect answer)
Client handling:

Performance Considerations

Chunking

Results > 4KB are automatically chunked:
  • Chunk size: 4KB (4096 bytes)
  • Purpose: Prevent memory issues with large outputs
  • Client: Reassemble chunks before parsing JSON

Timeouts

SSE streams can run indefinitely:
  • Connection: Set reasonable timeout on client (e.g., 60s for quick tools, 600s for bash)
  • Keep-alive: Server sends pings every 10s to prevent idle timeout
  • Session: 15-minute session timeout still applies

Connection Limits

Be mindful of concurrent SSE connections:
  • Each connection holds server resources
  • Limit concurrent tool calls per agent
  • Use connection pooling in clients

Debugging SSE

View Raw SSE Stream

Common Issues

Issue: “Connection closed immediately”
  • Cause: Missing Accept: text/event-stream header
  • Fix: Add header to request
Issue: “Response not streaming”
  • Cause: Client buffering responses
  • Fix: Use streaming API (e.g., httpx.stream(), not httpx.post())
Issue: “Incomplete JSON in chunk”
  • Cause: Not accumulating chunks before parsing
  • Fix: Buffer all chunks until event: end

Next Steps

Implementing a Client

Build a client that handles SSE responses

Implementing a Server

Implement SSE responses in your server

HTTP API Reference

See complete endpoint documentation

Key Takeaway: SSE keeps connections alive during tool execution and chunks large results for reliable delivery. The protocol is simple: task_id → chunks (optional) → end/error. Clients reassemble chunks and parse the final JSON result.