Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Guide users building apps, scripts, CI pipelines, or automations on top of the Cursor SDK — TypeScript (@cursor/sdk) or Python (cursor-sdk / cursor_sdk). Covers Agent.create, Agent.prompt, Agent.resume, streaming, local vs cloud runtime, MCP servers, error handling, and production best practices. Use when the user mentions integrating or writing code against the Cursor SDK.
.claude/skills/kunanonj-cursor-sdk/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | -9% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 100% | 0% |
| case-03 | ✗→✓ | ▲ Improved | -9% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 5% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 47% | 0% |
The Cursor SDK runs Cursor agents programmatically. Two language variants share the same concepts:
@cursor/sdk, npm) - docs at cursor.com/docs/sdk/typescriptcursor-sdk, pip) - docs at cursor.com/docs/sdk/pythonBoth are in public beta and follow the same Agent → Run model across local (runs on the caller's machine against cwd) and cloud (runs on a Cursor-hosted VM against a cloned repo) runtimes.
pyproject.toml / .py files → Python. package.json / .ts files → TypeScript.Agent.prompt(...) - one-shotFire-and-forget scripts, GitHub Actions steps, "send prompt, get result, exit" flows. No streaming, no follow-ups, no cleanup to remember.
TypeScript:
typescriptimport { Agent } from "@cursor/sdk"; const result = await Agent.prompt("Refactor src/utils.ts for readability", { apiKey: process.env.CURSOR_API_KEY!, model: { id: "composer-2.5" }, local: { cwd: process.cwd() }, }); console.log(result.status, result.result);
Python:
pythonfrom cursor_sdk import Agent, AgentOptions, LocalAgentOptions result = Agent.prompt( "Refactor src/utils.py for readability", AgentOptions(api_key=os.environ["CURSOR_API_KEY"], model="composer-2.5", local=LocalAgentOptions(cwd=os.getcwd())), ) print(result.status, result.result)
Agent.create(...) + agent.send(...) - durable with follow-upsStreaming, multi-turn conversation, lifecycle operations (cancel, status listener).
TypeScript:
typescriptimport { Agent } from "@cursor/sdk"; await using agent = await Agent.create({ apiKey: process.env.CURSOR_API_KEY!, model: { id: "composer-2.5" }, local: { cwd: process.cwd() }, }); const run = await agent.send("Find the bug in src/auth.ts"); 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); } await run.wait(); const run2 = await agent.send("Now write a regression test for it"); await run2.wait();
Python:
pythonfrom cursor_sdk import Agent, LocalAgentOptions with Agent.create(model="composer-2.5", api_key=os.environ["CURSOR_API_KEY"], local=LocalAgentOptions(cwd=os.getcwd())) as agent: run = agent.send("Find the bug in src/auth.py") for message in run.messages(): if message.type == "assistant": for block in message.message.content: if block.type == "text": print(block.text, end="") run.wait() run2 = agent.send("Now write a regression test for it") run2.wait()
Agent.resume(...) - pick up an existing agent laterCross-process boundaries: cron continuations, webhooks, interactive CLIs. Runtime is auto-detected from the ID prefix — bc- is cloud, anything else is local.
TypeScript:
typescriptawait using agent = await Agent.resume(previousAgentId, { apiKey }); const run = await agent.send("Also update the changelog"); await run.wait();
Python:
pythonwith Agent.resume(previous_agent_id, AgentOptions(api_key=os.environ["CURSOR_API_KEY"])) as agent: run = agent.send("Also update the changelog") run.wait()
Inline MCP servers are not persisted across resume — pass them again.
local or cloud explicitly — the SDK defaults to local silently.CursorAgentError = run never executed (auth, config). result.status == "error" = run executed and failed. Different fixes.await using (TS) or with ... as agent: (Python). Skipping disposal leaks child processes and memory.wait() is required: wait() returns the terminal result. Always call it.run.supports("cancel") before calling.cwd. Good for dev loops and CI with a repo checkout.bashexport CURSOR_API_KEY="cursor_..."
Both SDKs read CURSOR_API_KEY when no key is passed explicitly.
composer-2.5 is the current default. model="auto" lets the server pick. Cursor.models.list() returns valid IDs.
Both SDKs support HTTP (with headers or OAuth auth) or stdio (command / args / env). Pass servers inline on Agent.create or agent.send.
await using (TS) or with ... as agent: (Python).CursorAgentError, exit 2 for result.status == "error".run.id and agent.agentId immediately after send().error.isRetryable. Blind retries can cause duplicate cloud runs.apiKey explicitly in shared-infrastructure code.Agent.prompt(...) for true one-shots — it disposes for you.| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-06 | pass→pass | 15,270 | 9,904 | -35% | 1 | 1 | 0% | 2,931 | 3,442 | +17% | 0 | 0 | — |
case-01 | fail→pass | 15,079 | 5,432 | -64% | 1 | 1 | 0% | 2,974 | 2,721 | -9% | 0 | 0 | — |
case-02 | fail→pass | 8,340 | 8,835 | +6% | 1 | 1 | 0% | 1,745 | 3,482 | +100% | 0 | 0 | — |
case-03 | fail→pass | 22,786 | 12,921 | -43% | 1 | 1 | 0% | 4,686 | 4,257 | -9% | 0 | 0 | — |
case-04 | pass→pass | 13,373 | 11,132 | -17% | 1 | 1 | 0% | 2,447 | 3,578 | +46% | 0 | 0 | — |
case-05 | pass→fail | 11,373 | 6,427 | -43% | 1 | 1 | 0% | 2,113 | 2,738 | +30% | 0 | 0 | — |
case-07 | fail→pass | 16,008 | 9,373 | -41% | 1 | 1 | 0% | 3,338 | 3,515 | +5% | 0 | 0 | — |
case-08 | fail→pass | 10,770 | 7,090 | -34% | 1 | 1 | 0% | 2,042 | 2,996 | +47% | 0 | 0 | — |
case-09 | fail→fail | 12,681 | 5,662 | -55% | 1 | 1 | 0% | 2,162 | 2,683 | +24% | 0 | 0 | — |
case-10 | fail→pass | 14,411 | 6,365 | -56% | 1 | 1 | 0% | 2,973 | 2,855 | -4% | 0 | 0 | — |
case-11 | fail→pass | 7,712 | 4,817 | -38% | 1 | 1 | 0% | 1,468 | 2,543 | +73% | 0 | 0 | — |
case-12 | fail→pass | 8,826 | 6,647 | -25% | 1 | 1 | 0% | 1,920 | 2,939 | +53% | 0 | 0 | — |
case-13 | fail→pass | 7,380 | 5,107 | -31% | 1 | 1 | 0% | 1,487 | 2,633 | +77% | 0 | 0 | — |
case-14 | fail→pass | 7,735 | 5,956 | -23% | 1 | 1 | 0% | 1,494 | 2,662 | +78% | 0 | 0 | — |
case-15 | pass→pass | 7,905 | 4,610 | -42% | 1 | 1 | 0% | 1,545 | 2,374 | +54% | 0 | 0 | — |
case-16 | fail→pass | 7,224 | 7,058 | -2% | 1 | 1 | 0% | 1,574 | 3,223 | +105% | 0 | 0 | — |
case-17 | fail→pass | 8,914 | 5,747 | -36% | 1 | 1 | 0% | 1,751 | 2,734 | +56% | 0 | 0 | — |
case-18 | fail→pass | 10,417 | 8,592 | -18% | 1 | 1 | 0% | 2,283 | 3,466 | +52% | 0 | 0 | — |
case-19 | fail→pass | 13,838 | 7,990 | -42% | 1 | 1 | 0% | 2,966 | 3,104 | +5% | 0 | 0 | — |
case-20 | pass→pass | 14,265 | 3,426 | -76% | 1 | 1 | 0% | 2,345 | 2,176 | -7% | 0 | 0 | — |
case-21 | fail→pass | 12,305 | 8,746 | -29% | 1 | 1 | 0% | 2,417 | 3,389 | +40% | 0 | 0 | — |
case-22 | fail→pass | 18,626 | 2,348 | -87% | 1 | 1 | 0% | 3,151 | 1,992 | -37% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +68 percentage points is the difference between those two pass rates over the 22 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.