@cursor/february
v1.0.7
Published
Cursor SDK (private alpha).
Keywords
Readme
@cursor/february
TypeScript SDK for Cursor agents (private alpha — codenamed).
The primary API is the static Agent namespace. It creates durable agents, starts runs, streams normalized run events, waits for completion, resumes existing agents, and inspects persisted agent state.
Quick Start
import { Agent } from "@cursor/february/agent";
const agent = Agent.create({
apiKey: process.env.CURSOR_API_KEY!,
model: { id: "composer-2" },
local: { cwd: process.cwd() },
});
try {
const run = await agent.send("Refactor the utils module", {
onDelta: ({ update }) => {
console.log("raw update", update.type);
},
onStep: ({ step }) => {
console.log("completed step", step.type);
},
});
for await (const event of run.stream()) {
if (event.type === "assistant") {
for (const block of event.message.content) {
if (block.type === "text") {
process.stdout.write(block.text);
}
}
}
}
const result = await run.wait();
console.log(result.status);
} finally {
await agent[Symbol.asyncDispose]();
}Creating Agents
Agent.create(options) returns an SDKAgent immediately. Runtime initialization is deferred until the first send. agent.agentId is populated immediately for both local and cloud agents: local uses an agent-<uuid> ID, cloud uses a bc-<uuid> ID the SDK generates and reserves with the server on first send.
const localAgent = Agent.create({
apiKey: process.env.CURSOR_API_KEY!,
model: { id: "composer-2" },
local: { cwd: "/path/to/repo" },
});
const cloudAgent = Agent.create({
apiKey: process.env.CURSOR_API_KEY!,
model: { id: "composer-2" },
cloud: {
repos: [
{
url: "https://github.com/owner/repo",
startingRef: "main",
},
],
},
});Agent Options
model: Model selection for runs. Required for local agents. Optional for cloud — omit to use the server-resolved default. Structure is{ id, params? }; seeCursor.models.list()for valid selections.apiKey: Cursor API key. Required for cloud agents and real local execution.local.cwd: Working directory for local agents. May be a string or string array.local.settingSources: Ambient Cursor settings layers to load from the local filesystem. Local agents only.cloud.repos: GitHub repository configuration for cloud agents.agentId: Optional durable agent ID. If omitted, the SDK generates one.mcpServers: Inline MCP server definitions available to local runs.platform: Advanced local persistence/workspace configuration.
Sending Messages
Use agent.send(message, options) to create a follow-up run.
const run = await agent.send(
{
text: "Explain this repository",
images: [
{
data: base64Png,
mimeType: "image/png",
},
],
},
{
model: { id: "composer-2" },
local: { force: false },
}
);Send Options
model: Per-run model override for local agents. Cloud follow-up model overrides are unsupported in v1.local.force: Local agents only. Expire an active persisted run before starting this message. Use this as an explicit recovery path after a crashed local process. Cloud enforces a busy-run check server-side, so no equivalent is needed.onDelta: Callback for rawInteractionUpdatedeltas as the executor emits them.onStep: Callback when a conversation step finishes after batching text, thinking, or tool updates.
onDelta and onStep are awaited before the update continues through the run pipeline, so callback backpressure is preserved.
Runs
A Run represents one durable agent turn.
const run = await agent.send("Update the README");
console.log(run.id);
console.log(run.agentId);
console.log(run.status);
const result = await run.wait();Run Operations
run.stream(): Async generator of normalizedSDKMessageevents.run.wait(): Resolves to a terminalRunResult.run.cancel(): Cancels a supported running run.run.conversation(): Returns accumulatedConversationTurn[]for this run.run.supports(operation): Checks whetherstream,wait,cancel, orconversationis supported.run.unsupportedReason(operation): Explains why an operation is unavailable.run.onDidChangeStatus(listener): Subscribes to run status changes.
run.cancel() is supported for both local and cloud runs. For cloud runs it calls the server's cancel endpoint and reconciles local status from the authoritative server response.
Streaming Events
run.stream() emits SDKMessage events. This is the stable public stream surface shared by local and cloud runs.
for await (const event of run.stream()) {
switch (event.type) {
case "user":
console.log(event.message.content.map(block => block.text).join(""));
break;
case "assistant":
for (const block of event.message.content) {
if (block.type === "text") {
process.stdout.write(block.text);
}
}
break;
case "thinking":
process.stdout.write(event.text);
break;
case "tool_call":
console.log(event.call_id, event.name, event.status);
if (event.args) {
console.log("args", event.args);
}
if (event.result) {
console.log("result", event.result);
}
break;
case "status":
console.log(event.status);
break;
case "task":
console.log(event.text);
break;
}
}SDKMessage Types
SDKUserMessageEvent: User prompt content for the run.SDKAssistantMessage: Assistant text and tool-use content blocks.SDKThinkingMessage: Thinking text.SDKToolUseMessage: Tool call metadata withcall_id,name,status, and optionalargs,result, andtruncatedpayload fields.SDKStatusMessage: Run lifecycle status such asRUNNING,FINISHED, orERROR.SDKTaskMessage: Task-level status or summary text.SDKSystemMessage: System/init metadata when available.
Tool payload fields are emitted when the runtime provides them. Cloud V1 stream payloads use the public stream shape from the API; local runs forward the normalized local tool call arguments and results from InteractionUpdate.toolCall.
Conversation State
run.conversation() returns old-style accumulated ConversationTurn[] for the run.
const run = await agent.send("Inspect package scripts");
await run.wait();
const turns = await run.conversation();Live local runs can accumulate tool steps from raw executor deltas. Persisted replay reconstructs conversation from SDKMessage events, so replayed conversations can include tool-call steps when the persisted stream contains tool payload data.
Raw Delta Callbacks
onDelta exposes the lower-level InteractionUpdate stream.
const run = await agent.send("Run tests", {
onDelta: ({ update }) => {
switch (update.type) {
case "text-delta":
process.stdout.write(update.text);
break;
case "tool-call-started":
console.log("tool started", update.toolCall.type);
break;
case "tool-call-completed":
console.log("tool completed", update.toolCall);
break;
}
},
});The callback surface is useful for local UI integrations that need detailed tool payloads. The normalized run.stream() surface is the cross-runtime public stream.
Resuming and Inspecting Agents
Durable agents can be resumed later by ID.
const resumed = Agent.resume(agent.agentId, {
apiKey: process.env.CURSOR_API_KEY!,
model: { id: "composer-2" },
local: { cwd: process.cwd() },
});
const run = await resumed.send("What changed last time?");
await run.wait();You can also inspect persisted agent and run state:
const agents = await Agent.list({ runtime: "local", cwd: process.cwd() });
const runs = await Agent.listRuns(agent.agentId, {
runtime: "local",
cwd: process.cwd(),
});
const run = await Agent.getRun(runs.items[0]!.id, {
runtime: "local",
cwd: process.cwd(),
});
const messages = await Agent.messages.list(agent.agentId, {
runtime: "local",
cwd: process.cwd(),
});Agent.messages.list(...) reads persisted conversation/checkpoint messages. It is complementary to run.conversation(), which is scoped to one run.
Managing Cloud Agents
Cloud agents support lifecycle operations beyond create/resume/list:
const info = await Agent.get("bc-abc123", { apiKey });
await Agent.archive("bc-abc123", { apiKey });
await Agent.unarchive("bc-abc123", { apiKey });
await Agent.delete("bc-abc123", { apiKey });Agent.get returns the same SDKAgentInfo shape as Agent.list. archive, unarchive, and delete return nothing; a missing agent throws.
apiKey falls back to process.env.CURSOR_API_KEY if omitted.
Account and Platform
The Cursor namespace exposes account-level queries that aren't tied to a specific agent.
import { Cursor } from "@cursor/february/agent";
const me = await Cursor.me({ apiKey });
const models = await Cursor.models.list({ apiKey });
const repos = await Cursor.repositories.list({ apiKey });Cursor.me()returns the authenticated user'sapiKeyName,userEmail, andcreatedAt.Cursor.models.list()returns the model IDs available to the caller.Cursor.repositories.list()returns GitHub repositories the caller has connected to Cursor, for use ascloud.reposentries onAgent.create.
All three are cloud-only and require an API key (param or CURSOR_API_KEY env var).
Local Settings and MCP
Store-backed local agents do not load ambient Cursor settings unless you opt in. Use local.settingSources to choose which settings layers can affect MCP servers, hooks, skills, rules, subagents, team policy, MDM hooks, and plugins. This field is only available on the local shape — on cloud agents, project / team / plugins are always on and user / mdm / local have no VM equivalent.
const agent = Agent.create({
apiKey: process.env.CURSOR_API_KEY!,
model: { id: "composer-2" },
local: {
cwd: process.cwd(),
settingSources: ["project", "plugins"],
},
mcpServers: {
docs: {
type: "http",
url: "https://example.com/mcp",
auth: {
CLIENT_ID: "client-id",
scopes: ["read", "write"],
},
},
filesystem: {
type: "stdio",
command: "npx",
args: ["-y", "@modelcontextprotocol/server-filesystem", process.cwd()],
cwd: process.cwd(),
},
},
});local.settingSources: ["all"] loads every ambient source. Inline mcpServers are explicit API input, so they are available even when local.settingSources is omitted or []. Inline MCP server definitions are not persisted for Agent.resume(...); pass them again when resuming if the run needs those servers.
Call agent.reload() after local hook, MCP, or settings files change and you want the underlying local executor to pick them up.
Artifacts
The SDKAgent interface includes artifact helper methods:
const artifacts = await agent.listArtifacts();Artifact support is still runtime-dependent. Local SDK agents currently return no artifacts and throw for downloadArtifact.
Resource Management
Dispose agents when you are done with them.
await agent[Symbol.asyncDispose]();agent.close() starts best-effort disposal without awaiting it. Prefer [Symbol.asyncDispose]() in scripts and tests.
Static API Reference
Agent.create(options): SDKAgentAgent.resume(agentId, options?): SDKAgentAgent.prompt(message, options?): Promise<RunResult>Agent.list(options?): Promise<ListResult<SDKAgentInfo>>Agent.listRuns(agentId, options?): Promise<ListResult<Run>>Agent.getRun(runId, options?): Promise<Run>Agent.messages.list(agentId, options?): Promise<AgentMessage[]>
Known Limitations
- Persisted conversation replay can only reconstruct information present in the public
SDKMessagestream. - Inline MCP server definitions are not persisted across
Agent.resume(...). - Artifact download support is not implemented for local SDK agents yet.
