Events API
The Events API provides a persistent SSE connection for workspace-level events. Use it to react to bundle lifecycle changes, data mutations, and connection health in real time.
GET /v1/events
Section titled “GET /v1/events”Open a persistent SSE connection. The server sends events as they occur and a heartbeat every 30 seconds to keep the connection alive.
Response headers:
Content-Type: text/event-streamCache-Control: no-cacheConnection: keep-aliveExample:
curl http://localhost:27247/v1/events \ -H "Authorization: Bearer your-secret-key" \ --no-bufferevent: heartbeatdata: {"timestamp":"2025-10-15T10:30:00.000Z"}
event: bundle.installeddata: {"name":"weather","bundleName":"@nimblebraininc/weather","status":"running","ui":null}
event: data.changeddata: {"server":"weather","tool":"get_forecast","timestamp":"2025-10-15T10:31:15.000Z"}
event: heartbeatdata: {"timestamp":"2025-10-15T10:30:30.000Z"}Event Types
Section titled “Event Types”bundle.installed
Section titled “bundle.installed”A new bundle was installed and started.
event: bundle.installeddata: {"name":"weather","bundleName":"@nimblebraininc/weather","status":"running","ui":null}| Field | Type | Description |
|---|---|---|
name | string | Short server name |
bundleName | string | Scoped manifest name |
status | string | Lifecycle state after installation |
ui | object | null | UI metadata, or null |
bundle.uninstalled
Section titled “bundle.uninstalled”A bundle was removed.
event: bundle.uninstalleddata: {"name":"weather"}| Field | Type | Description |
|---|---|---|
name | string | Short server name of the removed bundle |
bundle.crashed
Section titled “bundle.crashed”A bundle’s MCP server process crashed. The runtime will attempt to restart it.
event: bundle.crasheddata: {"name":"weather","restartAttempt":1}| Field | Type | Description |
|---|---|---|
name | string | Short server name |
restartAttempt | number | Which restart attempt this is (1-based) |
bundle.recovered
Section titled “bundle.recovered”A previously crashed bundle was successfully restarted.
event: bundle.recovereddata: {"name":"weather"}| Field | Type | Description |
|---|---|---|
name | string | Short server name |
bundle.dead
Section titled “bundle.dead”A bundle exhausted all restart attempts and is permanently stopped. Manual intervention is needed to restart it.
event: bundle.deaddata: {"name":"weather","message":"Exceeded maximum restart attempts (3)"}| Field | Type | Description |
|---|---|---|
name | string | Short server name |
message | string | Reason the bundle was marked dead |
data.changed
Section titled “data.changed”An agent-initiated tool call completed successfully, which may have mutated data in the tool’s backing service. Use this event to refresh UI components that display data from the affected server.
event: data.changeddata: {"server":"tasks","tool":"create_task","timestamp":"2025-10-15T10:31:15.000Z"}| Field | Type | Description |
|---|---|---|
server | string | MCP server that owns the tool |
tool | string | Tool name that was called |
timestamp | string | ISO 8601 timestamp |
config.changed
Section titled “config.changed”A runtime configuration change occurred (e.g., model update, preferences change).
event: config.changeddata: {"key":"defaultModel","timestamp":"2026-04-13T10:30:00.000Z"}| Field | Type | Description |
|---|---|---|
key | string | Configuration key that changed |
timestamp | string | ISO 8601 timestamp |
skill.created / skill.updated / skill.deleted
Section titled “skill.created / skill.updated / skill.deleted”A user skill was created, modified, or removed.
event: skill.createddata: {"name":"my-skill","timestamp":"2026-04-13T10:30:00.000Z"}| Field | Type | Description |
|---|---|---|
name | string | Skill name |
timestamp | string | ISO 8601 timestamp |
file.created / file.deleted
Section titled “file.created / file.deleted”A file was uploaded or removed from the workspace.
event: file.createddata: {"fileId":"file_abc123","name":"report.pdf","timestamp":"2026-04-13T10:30:00.000Z"}| Field | Type | Description |
|---|---|---|
fileId | string | File identifier |
name | string | Original filename |
timestamp | string | ISO 8601 timestamp |
bridge.tool.call / bridge.tool.done
Section titled “bridge.tool.call / bridge.tool.done”An MCP App Bridge tool call started or completed. Used by the web client to show tool call activity from app iframes.
event: bridge.tool.calldata: {"server":"tasks","tool":"create_task","timestamp":"2026-04-13T10:30:00.000Z"}| Field | Type | Description |
|---|---|---|
server | string | MCP server name |
tool | string | Tool name |
timestamp | string | ISO 8601 timestamp |
heartbeat
Section titled “heartbeat”Sent every 30 seconds to keep the connection alive and allow clients to detect stale connections.
event: heartbeatdata: {"timestamp":"2025-10-15T10:30:30.000Z"}| Field | Type | Description |
|---|---|---|
timestamp | string | ISO 8601 timestamp |
GET /v1/conversations/:id/events
Section titled “GET /v1/conversations/:id/events”Open a persistent SSE connection for a specific conversation. Only authorized participants receive events. Use this to build multi-participant chat — when one user sends a message, other participants watching the same conversation receive the assistant’s response in real time.
Authentication: Required. The server validates workspace membership and conversation access (canAccess()) on every connection. Unauthorized requests receive 404 (to avoid leaking conversation existence).
Response headers:
Content-Type: text/event-streamCache-Control: no-cacheConnection: keep-aliveExample:
curl http://localhost:27247/v1/conversations/conv_a1b2c3d4e5f67890/events \ -H "Authorization: Bearer your-secret-key" \ -H "X-Workspace-Id: ws_engineering" \ --no-bufferevent: user.messagedata: {"userId":"user_xyz","displayName":"Sarah","content":"What's the status?","timestamp":"2026-04-13T14:30:00.000Z"}
event: text.deltadata: {"runId":"abc123","text":"Based on the latest"}
event: tool.startdata: {"runId":"abc123","name":"tasks__list","id":"tc_001"}
event: tool.donedata: {"runId":"abc123","name":"tasks__list","id":"tc_001","ok":true,"ms":120}
event: text.deltadata: {"runId":"abc123","text":" data, here's the current status..."}
event: donedata: {"response":"Based on the latest data, here's the current status...","conversationId":"conv_a1b2c3d4e5f67890","stopReason":"complete"}
event: heartbeatdata: {"timestamp":"2026-04-13T14:30:30.000Z"}Conversation event types
Section titled “Conversation event types”| Event | Description |
|---|---|
user.message | Another participant sent a message. Contains userId, displayName, content, timestamp. |
text.delta | Streaming text chunk from the assistant response. |
tool.start | A tool call began. |
tool.done | A tool call completed. |
llm.done | LLM response finished (contains token counts). |
done | Full assistant response complete. Contains response, conversationId, stopReason, usage. |
heartbeat | Keep-alive, sent every 30 seconds. |
Participant eviction
Section titled “Participant eviction”When a participant is removed from a conversation (via manage_conversation tool) or a conversation is unshared, their SSE connection is closed immediately server-side. On reconnect, they receive 404 and should stop retrying.
Connection Management
Section titled “Connection Management”- The server supports multiple concurrent SSE clients. Each client receives all events.
- If a client disconnects, the server cleans up the connection automatically on the next broadcast.
- There is no reconnection protocol built into the stream. If the connection drops, open a new
GET /v1/eventsrequest. - The server sets
idleTimeoutto 255 seconds (the maximum Bun allows) to prevent premature connection closures.
Event Buffer
Section titled “Event Buffer”The server maintains an in-memory buffer of the 500 most recent events. This buffer is used internally by components like the Home dashboard. It is not exposed through the HTTP API.