Setting up the Cursor TypeScript SDK: Agent.prompt, Agent.create, and Agent.resume Patterns

The Cursor TypeScript SDK (@cursor/sdk) provides three canonical patterns—Agent.prompt for one-shot scripts, Agent.create for multi-turn interactions, and Agent.resume for reconnecting to persisted agents—enabling developers to embed Cursor agents into everything from CI pipelines to background jobs.

The @cursor/sdk package in the cursor/plugins repository offers programmatic control over Cursor agents through these distinct execution models. Each pattern targets specific operational requirements, from ephemeral automation tasks to durable conversations that survive process restarts.

The Three Execution Patterns

Agent.prompt for One-Shot Tasks

Agent.prompt(...) creates a temporary agent, executes a single prompt, and automatically disposes of resources. This pattern is ideal for CI steps, GitHub Actions, or simple CLI commands where you need a result and immediate exit without managing lifecycle cleanup.

According to cursor-sdk/skills/cursor-sdk/SKILL.md (lines 55-66), this method accepts a prompt string and options, returning a RunResult directly:

import { Agent } from "@cursor/sdk";

(async () => {
  const result = await Agent.prompt(
    "Refactor src/utils.ts for readability",
    {
      apiKey: process.env.CURSOR_API_KEY!,
      model: { id: "composer-2" },
      local: { cwd: process.cwd() },
    },
  );

  console.log("Status:", result.status);
  console.log("Output:", result.result);
})();

Agent.create for Durable Conversations

Agent.create(...) returns a persistent Agent object that maintains conversation state across multiple turns. Unlike Agent.prompt, this pattern requires manual resource cleanup via await agent[Symbol.asyncDispose]() to prevent leaks.

The implementation in orchestrate/skills/orchestrate/scripts/cli/task.ts (around line 170) demonstrates this pattern in production use. The agent supports real-time streaming via run.stream() and final resolution via run.wait():

import { Agent } from "@cursor/sdk";

(async () => {
  const agent = Agent.create({
    apiKey: process.env.CURSOR_API_KEY!,
    model: { id: "composer-2" },
    local: { cwd: process.cwd() },
  });

  try {
    // First turn with streaming
    const run1 = await agent.send("Find the bug in src/auth.ts");
    for await (const ev of run1.stream()) {
      if (ev.type === "assistant") {
        for (const blk of ev.message.content) {
          if (blk.type === "text") process.stdout.write(blk.text);
        }
      }
    }
    const result1 = await run1.wait();
    console.log("First turn status:", result1.status);

    // Follow-up turn preserves conversation context
    const run2 = await agent.send("Now write a regression test for the bug");
    await run2.wait();
  } finally {
    // Prevent resource leaks
    await agent[Symbol.asyncDispose]();
  }
})();

Agent.resume for Process Resumption

Agent.resume(...) reconnects to a previously persisted agent using its ID. This pattern supports cron jobs, webhooks, or CLI restarts where the original process no longer exists. Note that MCP servers are not persisted and must be passed again in the options.

As documented in cursor-sdk/skills/cursor-sdk/SKILL.md (lines 102-112):

import { Agent } from "@cursor/sdk";

(async () => {
  // previousAgentId is persisted somewhere (DB, file, env, etc.)
  const previousAgentId = "agent-1234abcd";

  const agent = Agent.resume(previousAgentId, {
    apiKey: process.env.CURSOR_API_KEY!,
    model: { id: "composer-2" },
    local: { cwd: process.cwd() },
  });

  const run = await agent.send("Also update the changelog for the latest release");
  const final = await run.wait();
  console.log("Resume run status:", final.status);
})();

The file orchestrate/skills/orchestrate/scripts/core/agent-manager.ts (lines 89-95) demonstrates the lazy import pattern used when loading the SDK for these operations.

Runtime Configuration and Architecture

All three patterns share a common options shape but differ in runtime behavior. The SDK examines the supplied configuration to determine whether to execute locally (on the caller's machine) or in the cloud (Cursor-hosted VM):

  • local: { cwd: ... }: Pins execution to the current working directory
  • cloud: { repos: [...] }: Forces cloud execution with specific repository access
  • Omitting both: Defaults to local execution silently

See cursor-sdk/skills/cursor-sdk/references/runtime-choice.md for the complete decision matrix.

Run Lifecycle and Streaming Control

A Run represents a single prompt execution within an agent session. Each run provides:

  • run.stream(): Yields events (assistant, tool_call, etc.) for live output processing
  • run.wait(): Resolves to the final RunResult containing status, output, and errors
  • run.supports(op): Guards optional operations like run.cancel() or run.conversation(), which may be unavailable on certain runtimes (e.g., paused cloud runs)

Always call wait() after streaming to release internal watchers and determine final completion status.

Error Handling and Resource Management

The SDK distinguishes between two error domains:

  1. Startup failures (CursorAgentError): Thrown before any run starts (e.g., missing API key, invalid configuration). These are catchable at the Agent.create or Agent.prompt level.
  2. Runtime failures: Indicated by result.status === "error" after run.wait() completes—the agent started but encountered issues like tool failures or model errors.

As shown in cursor-sdk/skills/cursor-sdk/SKILL.md (lines 124-142), handle both domains separately:

import { Agent, CursorAgentError } from "@cursor/sdk";

(async () => {
  let agent;
  try {
    agent = Agent.create({ apiKey: process.env.CURSOR_API_KEY! });
    const run = await agent.send("Summarize the repo");
    const res = await run.wait();
    
    if (res.status === "error") {
      console.error("Run failed:", res.error?.message);
      // Inspect res.transcript, tool outputs, etc.
    } else {
      console.log("Summary:", res.result);
    }
  } catch (err) {
    if (err instanceof CursorAgentError) {
      console.error("Startup error:", err.message, "Retryable:", err.isRetryable);
    } else {
      throw err;
    }
  } finally {
    if (agent) await agent[Symbol.asyncDispose]();
  }
})();

Common Pitfalls and Best Practices

Based on the "Top Five Traps" documented in cursor-sdk/skills/cursor-sdk/SKILL.md and references/error-handling.md:

  • Always specify runtime config: Explicitly pass local or cloud to avoid silent local fallbacks
  • Never forget disposal: Wrap Agent.create and Agent.resume in try ... finally blocks, or use await using syntax if your TypeScript target supports it
  • Distinguish error types: Catch CursorAgentError for startup issues; inspect result.status for runtime problems
  • Stream with wait: Always call run.wait() after streaming to release internal resources
  • Check capability support: Use run.supports(op) before calling run.cancel() or similar optional methods that may be unavailable on paused cloud runs

Summary

  • Agent.prompt handles one-shot tasks with automatic cleanup—perfect for CI/CD pipelines and simple scripts
  • Agent.create enables stateful, multi-turn conversations but requires manual disposal via Symbol.asyncDispose
  • Agent.resume reconnects to persisted agents by ID, supporting durable workflows across process restarts
  • Runtime choice between local and cloud execution depends on the local or cloud options passed to any pattern
  • Error handling requires catching CursorAgentError for startup failures and checking result.status for runtime issues
  • Resource management demands explicit disposal for created/resumed agents to prevent file handle and socket leaks

Frequently Asked Questions

What is the difference between Agent.prompt and Agent.create?

Agent.prompt creates a temporary agent instance that automatically disposes after returning the result, making it ideal for one-shot tasks. Agent.create returns a persistent agent that maintains conversation context across multiple send() calls but requires manual cleanup via await agent[Symbol.asyncDispose](). Use Agent.prompt for scripts and Agent.create for interactive or multi-step workflows.

How do I prevent resource leaks when using the Cursor SDK?

Always wrap Agent.create or Agent.resume calls in a try ... finally block to ensure await agent[Symbol.asyncDispose]() runs. Alternatively, use the await using syntax if your TypeScript configuration supports it. Forgetting disposal leaks file handles, child processes, and network sockets, as noted in cursor-sdk/skills/cursor-sdk/SKILL.md.

Can I resume an agent after my process restarts?

Yes. Persist the agentId returned when creating an agent, then use Agent.resume(agentId, options) to reconnect. Note that MCP servers and runtime configuration must be passed again during resume, as they are not persisted with the agent state. This pattern supports cron jobs, webhooks, and CLI tools that need to continue conversations across restarts.

How do I handle errors correctly in the Cursor TypeScript SDK?

Distinguish between startup errors (CursorAgentError) thrown during agent initialization, and runtime errors indicated by result.status === "error" after run.wait() returns. Catch CursorAgentError for configuration issues like missing API keys. For runtime errors, inspect the result.error and result.transcript properties to diagnose tool failures or model issues.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →