Building Chat and Triage Integrations with Cursor SDK Patterns

Use the durable pattern with Agent.create for multi-turn chat bots, and the resume pattern with Agent.resume for scheduled triage jobs that persist state across runs.

The Cursor TypeScript SDK (@cursor/sdk) provides a programmatic interface for running Cursor agents in automation workflows. According to the cursor/plugins repository, all integrations boil down to three invocation patterns—one-shot, durable, and resume—backed by reference files that encode best practices for runtime choice, authentication, and error handling.

Understanding the Three Cursor SDK Invocation Patterns

The SDK supports three distinct patterns for invoking agents, each documented in cursor-sdk/skills/cursor-sdk/SKILL.md.

One-shot pattern uses Agent.prompt() for fire-and-forget tasks. The SDK creates a temporary agent, sends a single prompt, returns the result, and automatically disposes resources. This is ideal for quick prototypes or single-turn commands.

Durable pattern uses Agent.create() to initialize a long-lived agent instance. You call agent.send() to dispatch prompts and receive a run object, then stream events via run.stream() and finalize with run.wait(). This pattern requires explicit disposal using await agent[Symbol.asyncDispose]() or the await using syntax.

Resume pattern uses Agent.resume() to reconnect to a persisted agent using its agentId. This allows scheduled jobs or interrupted workflows to continue with full conversational context, making it the standard approach for triage automation.

Building Chat Bots with the Durable Pattern

Chat-style bots require continuity across multiple turns, which maps to the durable pattern. The implementation in cursor-sdk/skills/cursor-sdk/references/patterns.md shows how to stream partial outputs while waiting for completion.

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

await using agent = Agent.create({
  apiKey: process.env.CURSOR_API_KEY!,
  model: { id: "composer-2" },
  cloud: { repos: [{ url: "https://github.com/your-org/repo", startingRef: "main" }] },
});

async function chat(userMsg: string) {
  const run = await agent.send(userMsg);
  
  // Stream partial assistant messages to stdout
  for await (const ev of run.stream()) {
    if (ev.type === "assistant") {
      process.stdout.write(ev.message.content?.[0]?.text ?? "");
    }
  }
  
  const result = await run.wait();
  if (result.status !== "finished") {
    console.error(`Run failed with status: ${result.status}`);
  }
}

The await using declaration ensures the agent disposes correctly even if streaming throws an exception. According to the streaming reference file (cursor-sdk/skills/cursor-sdk/references/streaming.md), valid event types include assistant, tool_call, and status.

Implementing Triage Workflows with the Resume Pattern

Triage jobs that run on a schedule typically use the resume pattern to maintain continuity. The agent's agentId is persisted to a file or database between runs, allowing Agent.resume() to restore state.

import { Agent, CursorAgentError } from "@cursor/sdk";
import { readFile, writeFile } from "node:fs/promises";

const STATE = "/var/lib/triage/state.json";

async function loadState(): Promise<{ agentId?: string }> {
  try { 
    return JSON.parse(await readFile(STATE, "utf-8")); 
  } catch { 
    return {}; 
  }
}

async function saveState(id: string) {
  await writeFile(STATE, JSON.stringify({ agentId: id }));
}

const { agentId } = await loadState();

await using agent = agentId
  ? Agent.resume(agentId, {
      apiKey: process.env.CURSOR_API_KEY!,
      model: { id: "composer-2" },
      cloud: { repos: [{ url: process.env.REPO_URL!, startingRef: "main" }] },
    })
  : Agent.create({
      apiKey: process.env.CURSOR_API_KEY!,
      model: { id: "composer-2" },
      cloud: { repos: [{ url: process.env.REPO_URL!, startingRef: "main" }] },
    });

console.log(`[triage] agent=${agent.agentId}`);

try {
  const run = await agent.send(
    "Triage new Linear tickets opened in the last 24h. Label, assign, comment with next steps."
  );
  const result = await run.wait();
  
  if (result.status === "error") {
    console.error(`[triage] run ${result.id} errored`);
  }
  
  await saveState(agent.agentId);
} catch (err) {
  if (err instanceof CursorAgentError && err.isRetryable) {
    console.error(`[triage] transient error: ${err.message}`);
    // Skip this tick; the next cron run will retry
  } else {
    throw err;
  }
}

This pattern appears in lines 92-138 of cursor-sdk/skills/cursor-sdk/references/patterns.md. The code distinguishes between startup failures (CursorAgentError) and runtime failures (result.status === "error"), checking err.isRetryable to decide whether to retry or escalate.

Adding External Tooling via MCP Servers

Both chat and triage integrations often need to query external services like Linear, GitHub, or Datadog. The SDK supports Model Context Protocol (MCP) servers declared inline in the agent options.

const mcpServers = {
  linear: {
    type: "http",
    url: "https://mcp.linear.app/sse",
    headers: { Authorization: `Bearer ${process.env.LINEAR_API_KEY!}` },
  },
};

await using agent = Agent.create({
  apiKey: process.env.CURSOR_API_KEY!,
  model: { id: "composer-2" },
  cloud: { repos: [{ url: process.env.REPO_URL!, startingRef: "main" }] },
  mcpServers,
});

As documented in cursor-sdk/skills/cursor-sdk/references/mcp.md, these servers provide tool-call support that agents can invoke during triage or conversational workflows.

Cross-Cutting Concerns and Reference Files

The cursor/plugins repository maintains several reference files that encode cross-cutting logic applicable to both chat and triage integrations:

  • runtime-choice.md: Decides between local: { cwd } for development and cloud: { repos } for production workloads.
  • auth.md: Details CURSOR_API_KEY handling, including service-account keys and common authentication errors.
  • error-handling.md: Distinguishes CursorAgentError (thrown during agent startup) from result.status === "error" (thrown during run execution).
  • streaming.md: Catalogues event types and documents best practices for run.stream() consumption.

These files are located in cursor-sdk/skills/cursor-sdk/references/ and are referenced throughout the SDK skill documentation.

Summary

  • One-shot pattern (Agent.prompt) works for quick, single-turn tasks but lacks continuity.
  • Durable pattern (Agent.create) powers multi-turn chat bots; always dispose the agent explicitly.
  • Resume pattern (Agent.resume) enables triage jobs to persist conversation state across cron runs by storing the agentId.
  • Error handling must distinguish CursorAgentError (startup) from run-time errors and respect isRetryable flags.
  • MCP servers extend agents with external API capabilities required for ticket triage and data retrieval.
  • Reference files in cursor-sdk/skills/cursor-sdk/references/ provide the canonical logic for runtime selection, auth, streaming, and error handling.

Frequently Asked Questions

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

Agent.create returns a durable agent instance that maintains state across multiple send() calls and requires explicit disposal, while Agent.prompt is a static method that creates a temporary agent, sends one message, returns the result, and auto-disposes. Use Agent.create for chat bots and Agent.prompt for one-off scripts.

How do I persist state for a triage bot between cron runs?

Persist the agentId string to a durable store (JSON file, database, or KV store) after each run. On the next invocation, read the stored ID and pass it to Agent.resume(agentId, options) instead of creating a new agent. This restores the previous conversation context and tool-call history.

How should I handle retries when the SDK throws errors?

Catch CursorAgentError instances and check the isRetryable property. If true, log the transient failure and skip the current tick (the next cron run will retry). If false, escalate the error. For runtime errors, inspect result.status === "error" after run.wait() completes.

When should I add MCP servers to my integration?

Add MCP servers when your chat or triage logic requires tool calls to external systems—such as creating Linear tickets, querying Datadog metrics, or fetching GitHub code owners. Define the mcpServers object in the options passed to Agent.create or Agent.resume, as shown in cursor-sdk/skills/cursor-sdk/references/mcp.md.

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 →