sdk.v1 defines five services. Three are served by the bridge; two are
callback services served by your adapter that the bridge calls into.
served by the bridge served by the adapter
──────────────────── ─────────────────────
SdkAgentService SdkCustomToolCallbackService
SdkCursorService SdkStoreCallbackService
SdkBridgeControlService
The core surface: agent lifecycle, message sending, run streaming, and
artifacts. Agents come in two runtimes, selected by AgentOptions:
- Local agents (
AgentOptions.local) run on the machine hosting the bridge, against one or more working directories (local.cwd). They require an explicitAgentOptions.model(discover IDs viaSdkCursorService.ListModels). Their durable state lives in the bridge's local agent store (SQLite by default; see the store callback service for fully adapter-owned storage). - Cloud agents (
AgentOptions.cloud) run in Cursor's cloud (or a self-hosted worker pool / machine) against git repositories.
Highlights:
| RPC | Notes |
|---|---|
CreateAgent / ResumeAgent |
Create a new agent or re-attach to an existing one with updated options. CreateAgent accepts an optional idempotency_key for safe retries on cloud agents. |
Send |
Send a UserMessage and stream RunStreamMessage events until the run completes. This is the main streaming RPC — see streaming.md. |
ObserveRun |
Subscribe to a run's durable events, optionally resuming after a prior stream offset. |
WaitLiveRun / GetRun / ListRuns / GetRunConversation |
Blocking wait, point-in-time snapshots, listing, and the raw conversation JSON. |
CancelRun |
Request cancellation of an in-flight run. |
GetAgent / ListAgents / ArchiveAgent / UnarchiveAgent / DeleteAgent / CloseAgent |
Agent management. CloseAgent releases local resources only; DeleteAgent removes durable data. |
ListAgentMessages |
Messages recorded for an agent. |
ListArtifacts / DownloadArtifact |
Cloud agent artifacts; downloads stream DownloadArtifactChunk bytes. |
GetUsage |
Billed token usage and cost. Cloud agents only. |
Client-level operations against Cursor's API that need no agent runtime:
Me— the authenticated account identity for the API key.ListModels— models available to the account, including parameter and variant metadata.ListRepositories— repositories usable with cloud agents.
Each request carries CursorRequestOptions.api_key, and for these catalog
RPCs it is required: current bridges fail closed with UNAUTHENTICATED
("API key is required for cloud catalog calls.") rather than falling back
to the bridge's environment.
Agent operations also accept an explicit key (AgentOptions.api_key), and
adapters should always set it rather than relying on the bridge's
CURSOR_API_KEY env var: not every operation falls back to the env var on
every bridge build — on some, runs on an agent created without an explicit
api_key fail with Invalid User API Key. Setting the option works
everywhere.
Manages the bridge process itself:
Ping— liveness; the natural first RPC after the handshake.GetVersion—bridge_version,protocol_version("sdk.v1"), and a list of capability strings (for exampleagent.create,run.observe,artifacts.chunked) for feature negotiation. Treat unknown capability strings as forward-compatible additions.Shutdown— graceful shutdown with agrace_secondsdrain window.SetToolCallback— register (or clear, with an empty URL) the adapter's custom-tool callback endpoint after startup. Same-host/loopback only; equivalent to launching with--tool-callback-url/--tool-callback-auth-token.
Both callback services invert the connection direction: your adapter runs a small Connect server on loopback, tells the bridge its URL plus a bearer token you choose, and the bridge authenticates to you with that token on every callback. Validate it exactly like the bridge validates yours.
Implementation note for hand-rolled servers: the bridge's callback requests
are ordinary Connect unary POSTs but may arrive with
Transfer-Encoding: chunked and no Content-Length. Minimal HTTP server
libraries often do not decode chunked request bodies for you — handle both
framings or callbacks will appear empty.
Custom tools let agent code call functions defined in your adapter's language. The split is:
- Metadata travels with agent options. Declare tools in
LocalAgentOptions.custom_tools(name →CustomToolDefinitionwith a description and a JSON Schemainput_schema) onCreateAgent/ResumeAgent. - Execution round-trips to the adapter. When the agent invokes a tool, the
bridge calls
CallCustomToolon your server with thetool_name, the arguments as a JSON object (google.protobuf.Struct), an optionaltool_call_idfor correlating with stream events, and the owningagent_id. Your response'sresultis aStructand therefore must be a JSON object — wrap scalar results (for example{"value": "..."}) or use a content envelope the SDK recognizes; a bare string cannot be encoded.
Register the endpoint at launch (--tool-callback-url +
--tool-callback-auth-token) or at runtime via
SdkBridgeControlService.SetToolCallback. Custom tools are a local agent
feature.
By default the bridge persists local agent state itself ("sqlite", or
"jsonl" with a root_dir). Setting LocalAgentStoreConfig.type to
"custom" hands the entire store to your adapter: the bridge forwards every
store operation over a single generic RPC.
CallStore requests carry:
substore—"agents","runs","runEvents", or"checkpoints", mirroring the local agent store topology;method—"get","create","update","delete","list", or"append"(runEventsonly);input— the operation input as a JSON object. Checkpoint blob bytes are base64-encoded strings.
Return the operation output in output, or leave it unset for a null result
(a get miss or a delete). The store callback endpoint can only be
configured at launch (--store-callback-url +
--store-callback-auth-token), since agents may load state before any RPC
arrives.
The input/output objects mirror the SDK's store interface, and the
structural rules matter more than exact fields (which follow the SDK's
document types and may gain fields over time):
create/updateinputs wrap the record under a singular key (for example{"agent": {...}});getinputs carry bare id fields (for example{"agentId": ...}).- Outputs must be the bare record object — echoing the wrapped input envelope back causes opaque internal errors in the bridge.
runEvents.appendinput is{"runId", "eventType", "payload"}.checkpointsblobs are base64 strings:create/updateinput is{"agentId", "blobId", "data"};getreturns{"found": bool, "data": <base64>}.
When building a store, log the live traffic from a real agent turn first —
one CreateAgent + Send exercises most substores and methods.
sdk_messages.proto holds all shared types and is imported by the agent and
cursor services; sdk_errors.proto stands alone (error details arrive inside
Connect/gRPC error metadata, not as response fields). The only external
imports are Google well-known types (struct, timestamp, duration), so
codegen needs nothing beyond proto/sdk/v1 and the standard protobuf runtime.