@cursor/sdk 包让你可以从自己的代码中调用 Cursor 的智能体。现在,原本运行在 Cursor IDE、CLI 和网页端 app 中的同一智能体,也可以通过 TypeScript 脚本调用。在 Cursor 中运行 /sdk 技能即可快速入门。
Cookbook¶
Cursor
Cookbook 提供端到端示例:包括 SDK
快速开始、app-builder 原型工具、面向云端
代理的 看板,以及 编程智能体
CLI。这些示例可作为构建 CI 自动修复机器人、缺陷分诊 worker、代码评审流程、嵌入产品的智能体和编排器的良好起点。
概览¶
SDK 通过统一接口封装本地和云端运行时。无论智能体在何处运行,编写的代码都相同。
| 运行时 | 功能 | 适用场景 |
|---|---|---|
| 本地 | 在 Node 进程中以内联方式运行智能体循环。文件直接从磁盘读取。 | 针对工作树运行的开发脚本和 CI 检查。 |
| 云端 (由 Cursor 托管) | 在已克隆仓库的隔离 VM 中运行。VM 由 Cursor 运行。 | 调用方没有仓库、需要并行运行多个智能体,或需要在调用方断开连接后继续运行时。 |
本地指本地智能体循环,而非本地模型¶
“本地”描述的是智能体循环和文件系统访问的运行位置,而不是
模型的运行位置。两种模式下,所有推理均通过 Cursor 托管的模型进行。
本地模式会将文件保留在你的机器上;云端模式则在
Cursor 环境中运行。无论哪种情况,模型本身均由托管服务提供。
运行时由传递给 Agent.create() 的键 (local 或 cloud) 决定。两种运行时都使用相同的 CURSOR_API_KEY。
如需了解 REST API,请参阅 Cloud Agents API。如需了解其他语言,请参阅 SDK Bridge。
身份验证¶
创建智能体前,请设置 CURSOR_API_KEY (或传入 apiKey) 。对于尚未预配密钥的交互式主机,Cursor.auth.login() 会通过浏览器登录签发并存储密钥。
SDK 支持将用户 API 密钥和服务账户 API 密钥用于本地和云端运行。暂不支持团队 Admin API 密钥。
- Cursor Dashboard → API Keys 中的用户 API 密钥
- 团队设置中的服务账户 API 密钥。请参阅服务账户
export CURSOR_API_KEY="your-key"
用量与计费¶
SDK 运行与 IDE 和 Cloud Agents 运行适用相同的定价、请求池和隐私模式规则。费用会显示在团队的用量仪表盘中,并带有 SDK 标签。
服务账户 API 密钥的费用将计入拥有该服务账户的团队。用户 API 密钥的费用将计入对应用户的方案。
如需在代码中读取每次运行的 token 数量,请参阅 Token 用量。如需获取某个智能体运行的已计费用量和美元费用,请参阅 Agent.getUsage()。
核心概念¶
| 概念 | 描述 |
|---|---|
| 智能体 | 持久化容器,包含对话状态、工作区配置和设置,可跨多个提示词持续存在。 |
| 运行 | 一次提示词提交,拥有独立的流、状态、结果和取消操作。 |
| SDKMessage | 运行期间发出的标准化流事件,在所有运行时中具有相同结构。 |
安装¶
npm install @cursor/sdk
包名以 @ 开头。npm 上不存在不带 @ 的 cursor/sdk。
运行时支持¶
SDK 要求 Node.js 22.13 或更高版本。它提供按平台划分的 @cursor/sdk-<os>-<arch> 二进制文件,用于沙盒隔离和 ripgrep,因此这是一个以 Node 为主的包。
导入 @cursor/sdk 时不会立即加载本地智能体栈。本地执行器会在首次本地 acquire 时加载,因此仅使用云端功能或类型的用户无需承担本地导入开销。进程中的第一个本地智能体会产生一次性导入开销,之后模块会保持缓存。
@cursor/sdk 发布自包含的 .d.ts 文件,因此无需拉取未发布的工作区包即可解析类型。升级后,请重新运行类型检查。TurnEndedUpdate 等流类型会解析为实际类型,而不是 any。
单文件捆绑包和编译型可执行文件¶
@cursor/sdk/bundled 是 SDK 的自包含单文件构建,提供与 @cursor/sdk 相同的公共 API。当你的应用以单个文件形式发布时,请使用它:通过 bun build --compile 生成的独立二进制可执行文件,或由 esbuild 生成的单文件捆绑包。
默认构建会在运行时按需加载部分模块。单文件捆绑器无法跟踪这些加载,因此编译后的应用在首次调用 Agent.create() 时会失败,并报出类似 Cannot find module './986.js' 的错误。打包入口将所有内容放入一个文件中,因此捆绑器会预先嵌入整个 SDK。
| 入口 | 内容 |
|---|---|
@cursor/sdk/bundled |
@cursor/sdk 导出的全部内容。 |
@cursor/sdk/bundled/sqlite |
SqliteLocalAgentStore,与 @cursor/sdk/sqlite 相同。 |
import { Agent } from "@cursor/sdk/bundled";
const agent = await Agent.create({
apiKey: process.env.CURSOR_API_KEY!,
model: { id: "composer-2.5" },
local: { cwd: process.cwd() },
});
像平常一样使用 Bun 编译:
bun build --compile main.ts --outfile my-agent
以下几点需要注意:
- 打包入口在 Bun 上运行,包括通过
bun build --compile生成的可执行文件。它们也可在 Node 上加载,但 SQLite 存储在该环境中不可用,因此默认的本地智能体存储会回退到 JSONL。不以单文件形式发布的 Node 应用应继续导入@cursor/sdk。 - **
zod、@bufbuild/protobuf和@connectrpc/*包会从你自行安装的依赖中解析。**它们随@cursor/sdk提供,打包工具会嵌入一份共享副本,因此传递给自定义工具的 Zod schema 能继续正常工作。 - **原生二进制文件不能包含在 JavaScript bundle 中。**沙盒隔离和内置 ripgrep 由各平台对应的
@cursor/sdk-<os>-<arch>包提供。请将node_modules/@cursor/sdk-<os>-<arch>/放在编译后的可执行文件旁,SDK 会在那里找到它。否则,搜索会回退到PATH中的rg,启用sandboxOptions则会抛出ConfigurationError。
打包入口的类型解析方式与 @cursor/sdk 相同。无需更改 TypeScript 配置。
快速开始¶
最快的入门方式:让本地智能体针对当前工作树运行,并实时接收事件流。有关云端设置,请参阅下方的创建智能体。
import { Agent } from "@cursor/sdk";
const agent = await Agent.create({
apiKey: process.env.CURSOR_API_KEY!,
model: { id: "composer-2.5" },
local: { cwd: process.cwd() },
});
const run = await agent.send("Summarize what this repository does");
for await (const event of run.stream()) {
console.log(event);
}
每个事件都是可区分的 SDKMessage。流式传输介绍了如何提取助手文本、处理工具调用,以及通过 await using 清理资源。有关一次性提示词 (创建、运行、释放资源) ,请参阅 Agent.prompt()。
快速开始会自动批准工具调用¶
默认本地智能体会执行工具调用 (shell、编辑、写入等) ,无需
请求批准;无界面模式下不会提示人工介入。若要控制工具调用,请配置
钩子 (如 beforeShellExecution 或
preToolUse) ,或通过 local.sandboxOptions.enabled:
true 运行。
创建智能体¶
function Agent.create(options: AgentOptions): Promise<SDKAgent>;
Agent.create() 会验证选项,并立即返回句柄。传入 local 或 cloud 以选择运行时。
// 本地智能体
const agent = await Agent.create({
apiKey: process.env.CURSOR_API_KEY!,
model: { id: "composer-2.5" },
local: { cwd: "/path/to/repo" },
});
// 云端代理
const agent = await Agent.create({
apiKey: process.env.CURSOR_API_KEY!,
model: { id: "composer-2.5" },
cloud: {
repos: [{ url: "https://github.com/your-org/your-repo", startingRef: "main" }],
autoCreatePR: true,
},
});
agent.agentId 会立即赋值。本地智能体的 ID 为 agent-<uuid>;云端代理的 ID 为 bc-<uuid>。
SDK 启动的云端代理不会显示在默认智能体列表中。要在 Cursor Web 或 Cursor 窗口中
查看它们,请点击 筛选 > 来源 > SDK。
无代码仓库的云端代理¶
云端代理可在不含代码仓库的空 VM 上运行。传入 cloud 并将 repos 列表留空,或者完全省略 repos。省略 cloud 则会选择本地运行时。
const agent = await Agent.create({
apiKey: process.env.CURSOR_API_KEY!,
cloud: { repos: [] },
});
const run = await agent.send(
"Research the top 3 TypeScript testing frameworks and summarize."
);
console.log((await run.wait()).result);
必须为您的账户或团队启用无仓库智能体。代码仓库范围的 API 密钥无法创建此类智能体;请改用不受限制的服务账户密钥或用户 API 密钥。
会话环境变量¶
对于云端代理,当运行需要短期凭证或其他仅供该智能体使用的值时,请传入 cloud.envVars。
const agent = await Agent.create({
apiKey: process.env.CURSOR_API_KEY!,
cloud: {
repos: [{ url: "https://github.com/your-org/your-repo" }],
envVars: {
STAGING_API_TOKEN: process.env.STAGING_API_TOKEN!,
},
},
});
这些值会在静态存储时加密,注入云端代理的 shell,并在删除智能体时一并删除。envVars 不能与调用方提供的 agentId 同时使用;请省略 agentId,并从 agent.agentId 读取服务器签发的 ID。变量名不能以 CURSOR_ 开头。
对于仅需在单次运行期间存在的值,请改用 agent.send() 传递。请参阅单次运行的环境变量。
智能体元数据¶
使用 cloud.metadata 为云端代理添加自定义 string 标签。这些标签会随智能体持久保存,并可通过 Agent.get() 和 Agent.list() 的 SDKAgentInfo.metadata 返回。这些标签不同于 VM 内的
智能体元数据 API;该 API 会在 VM 内提供当前
运行的 ID、所有者、轮次和工作区。
const agent = await Agent.create({
apiKey: process.env.CURSOR_API_KEY!,
cloud: {
repos: [{ url: "https://github.com/your-org/your-repo" }],
metadata: {
end_user_id: "user-123",
ticket_id: "ENG-456",
},
},
});
模型参数¶
使用 model.params 传递模型专属选项,例如 reasoning effort。参数 ID 和取值因模型而异。使用 Cursor.models.list() 查看您的账户支持哪些参数和预设变体。
对于旧版按请求计费方案,如果所选模型需要,Cursor 会自动启用 Max Mode。
Composer 2 会重新路由至 Composer 2.5¶
Composer 2 已停止服务。仍传递 composer-2 或
composer-2-fast 的 SDK 请求会在认证时重新路由至 Composer 2.5,因此现有
脚本可继续运行。如果您依赖 composer-2-fast 变体,请确认
快速模式的行为仍符合您的预期。
const agent = await Agent.create({
apiKey: process.env.CURSOR_API_KEY!,
model: {
id: "composer-2.5",
params: [{ id: "fast", value: "true" }],
},
local: { cwd: process.cwd() },
});
Cursor Router¶
Cursor Router 会为每个 Auto 请求选择模型。在 SDK 中,Router 是带有 optimize_for 参数的 auto-smart 模型。它适用于 Teams 和企业版。企业版管理员必须先为团队启用 Router,auto-smart 才会出现在目录中。
Cursor SDK 是智能体 SDK,而非独立的模型推理或聊天补全 API。Router 会为能理解工作区、调用工具、运行命令和编辑文件的 Cursor 智能体运行选择模型。Cursor 目前未提供可用于任意模型调用的原始 Router 端点文档。
选择 Cost、Balance 或 Intelligence¶
传入 auto-smart,并显式设置 optimize_for:
| 产品标签 | SDK 值 |
|---|---|
| Cost | cost |
| Balance | balanced |
| Intelligence | intelligence |
产品文案中使用 Balance。balanced 仅用作 SDK 传输值。
import { Agent } from "@cursor/sdk";
await using agent = await Agent.create({
apiKey: process.env.CURSOR_API_KEY!,
model: {
id: "auto-smart",
params: [{ id: "optimize_for", value: "balanced" }],
},
local: { cwd: process.cwd() },
});
const run = await agent.send("Find and fix the failing authentication test");
const result = await run.wait();
console.log(result.status);
console.log(result.requestId);
始终传递 optimize_for。请勿省略该字段,也不要传递旧版 default 值;通过目录发现是受支持的合约。
在模型目录中查找 Router¶
Cursor.models.list() 会返回当前 API 密钥的账户和团队可用的模型、参数定义及预设变体。Router 可用时,Cursor Router 会显示为 auto-smart。团队管理员可以禁用 Router,或限制成员可选择的优化模式。
在硬编码选择之前,应以目录为准:
import { Cursor, type ModelSelection } from "@cursor/sdk";
const models = await Cursor.models.list();
const router = models.find((model) => model.id === "auto-smart");
const optimizeFor = router?.parameters?.find(
(parameter) => parameter.id === "optimize_for",
);
if (!router || !optimizeFor) {
throw new Error(
"Cursor Router is not available for this API key. Verify that Router is enabled for the key's team.",
);
}
const requestedMode = "balanced";
const allowedValues = new Set(
optimizeFor.values.map(({ value }) => value),
);
if (!allowedValues.has(requestedMode)) {
throw new Error(
`Router mode "${requestedMode}" is not enabled for this team.`,
);
}
const model: ModelSelection = {
id: router.id,
params: [{ id: optimizeFor.id, value: requestedMode }],
};
按运行切换模式¶
在 agent.send() 中覆盖模型,以更改某次运行的 Router 模式:
const run = await agent.send("Handle this complex migration", {
model: {
id: "auto-smart",
params: [{ id: "optimize_for", value: "intelligence" }],
},
});
单次运行的模型覆盖会持续生效。后续发送时若未指定覆盖,仍会使用新的模型选择。参阅单次运行模型覆盖。
模型 ID:auto-smart、auto 和 default¶
| 选择 | 含义 |
|---|---|
搭配 optimize_for 使用 auto-smart |
Cursor Router。需要 Cost、Balance 或 Intelligence 时使用。 |
{ id: "auto" } |
当目录中没有指定模型时,由服务器选择的 Auto 回退方案。如需显式指定 Router 模式,优先使用 auto-smart。 |
省略 optimize_for 或传入 default |
不受支持的 Router 合约。请始终查询允许的值,并传入 cost、balanced 或 intelligence。 |
计费与路由用量池¶
- 所有 Auto 模式均按每个请求所路由模型的标价计费。
- 底层模型可能因请求而异。需要进行可复现的比较时,请优先使用固定的模型 ID。
- 企业版模型允许列表决定路由用量池。屏蔽必需模型可能会禁用 Router。
有关当前费率和路由用量池,请参阅 Cursor Router 和 Auto 模式。
Router 缺失的疑难排查¶
如果找不到 auto-smart,或优化模式被拒绝:
- 调用
Cursor.models.list()。 - 确认结果中包含
auto-smart。 - 确认
optimize_for包含所需的值 (cost、balanced或intelligence) 。 - 确认与 API 密钥关联的团队已启用 Router。
- 如果您属于多个团队,请确认该密钥在预期的团队上下文中使用。
- 如果 Router 不可用或无法选择有效的底层模型,请检查团队的模型访问策略。
SDKAgent¶
Agent.create() 和 Agent.resume() 返回的句柄。
interface SDKAgent {
readonly agentId: string;
readonly model: ModelSelection | undefined;
send(message: string | SDKUserMessage, options?: SendOptions): Promise<Run>;
close(): void;
reload(): Promise<void>;
[Symbol.asyncDispose](): Promise<void>;
listArtifacts(): Promise<SDKArtifact[]>;
downloadArtifact(path: string): Promise<Buffer>;
getUsage(options?: GetUsageOptions): Promise<AgentUsage>;
}
| 成员 | 描述 |
|---|---|
agentId |
稳定的智能体标识符。本地为 agent-<uuid>,云端为 bc-<uuid>。 |
model |
当前模型选择。每次成功调用 send({ model }) 后都会更新。在被设置前为 undefined (包括调用方未传入 model 的已恢复智能体) 。 |
send |
使用给定提示词启动新的运行。返回 Run 句柄。 |
close |
开始释放资源,不等待完成。即发即弃。 |
reload |
重新读取文件系统配置 (钩子、项目 MCP、子智能体) ,无需释放资源。 |
[Symbol.asyncDispose] |
异步释放资源。与 await using 搭配使用以自动清理。 |
listArtifacts |
列出智能体生成的文件 (仅限云端;本地返回空) 。 |
downloadArtifact |
按路径下载文件 (仅限云端;本地会抛出异常) 。 |
getUsage |
获取智能体的计费 token 用量和美元费用。 |
Agent.prompt()¶
function Agent.prompt(message: string, options?: AgentOptions): Promise<RunResult>;
一次性便捷方法:创建智能体、发送一条提示词、等待运行完成后释放资源。
const result = await Agent.prompt("What does the auth middleware do?", {
apiKey: process.env.CURSOR_API_KEY!,
model: { id: "composer-2.5" },
local: { cwd: process.cwd() },
});
发送消息¶
每次调用 agent.send() 都会返回一个 Run。智能体会在多次运行之间保留对话上下文;一次运行对应处理一个提示词的工作单元。
运行¶
type RunStatus = "running" | "finished" | "error" | "cancelled";
type RunOperation = "stream" | "wait" | "cancel" | "conversation";
interface Run {
readonly id: string;
readonly requestId?: string;
readonly agentId: string;
readonly status: RunStatus;
readonly result?: string;
readonly error?: RunError;
readonly model?: ModelSelection;
readonly durationMs?: number;
readonly usage?: TokenUsage;
readonly git?: RunGitInfo;
readonly createdAt?: number;
stream(): AsyncGenerator<SDKMessage, void>;
wait(): Promise<RunResult>;
cancel(): Promise<void>;
conversation(): Promise<ConversationTurn[]>;
supports(operation: RunOperation): boolean;
unsupportedReason(operation: RunOperation): string | undefined;
onDidChangeStatus(listener: (status: RunStatus) => void): () => void;
}
interface RunGitInfo {
branches: Array<{ repoUrl: string; branch?: string; prUrl?: string }>;
}
interface RunError {
message: string;
code?: string;
}
interface TokenUsage {
inputTokens: number;
outputTokens: number;
cacheReadTokens: number;
cacheWriteTokens: number;
totalTokens: number;
reasoningTokens?: number;
}
interface RunResult {
id: string;
requestId?: string;
status: "finished" | "error" | "cancelled";
result?: string;
error?: RunError;
model?: ModelSelection;
durationMs?: number;
usage?: TokenUsage;
git?: RunGitInfo;
}
流式传输¶
const run = await agent.send("Find the bug in src/auth.ts");
for await (const event of run.stream()) {
switch (event.type) {
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(`[tool] ${event.name}: ${event.status}`);
break;
case "status":
console.log(`[status] ${event.status}`);
break;
}
}
// 在同一智能体中继续对话。上一次运行的对话状态
// 会自动加载。
const run2 = await agent.send("Fix it and add a regression test");
await run2.wait();
要随文本发送图像:
const run = await agent.send({
text: "What's in this screenshot?",
images: [{ data: base64Png, mimeType: "image/png" }],
});
不使用流式传输时等待¶
const result = await run.wait();
console.log(result.status); // "finished" | "error" | "cancelled"
console.log(result.result); // 最终助手文本(如有)
console.log(result.error); // 运行失败时为 { message, code? }
console.log(result.model); // 本次运行使用的已解析 ModelSelection
console.log(result.durationMs);
console.log(result.usage); // 累计 TokenUsage;不可用时为 undefined
console.log(result.git); // 在 Cloud 上为 { branches: [{ repoUrl, branch?, prUrl? }] }
最终的助手文本以 string 形式存储在 result.result 中。无需在 text、message、messages 或 content 字段中查找。如果需要按步骤获取会话记录,请调用 run.conversation(),以获得结构化的 ConversationTurn[] 视图:
const result = await run.wait();
const finalText = result.result ?? "";
const turns = await run.conversation();
const lastAssistant = turns
.flatMap((t) => (t.type === "agentConversationTurn" ? t.turn.steps : []))
.filter((s) => s.type === "assistantMessage")
.at(-1);
console.log(lastAssistant?.message.text);
取消一次运行¶
await run.cancel();
取消运行。状态将变为 "cancelled",实时流将中止,正在进行的工具调用将停止,run.wait() 将以 status: "cancelled" 完成。部分输出 (截至目前生成的助手文本) 会保留在 Run 对象中。
运行中的本地和云端运行均支持取消;如果运行已结束,则此操作无效。
读取运行状态¶
console.log(run.status); // "running" | "finished" | "error" | "cancelled"
const stop = run.onDidChangeStatus((status) => {
console.log(`status changed to ${status}`);
});
// 调用 `stop()` 移除监听器。
// 此次运行中累积对话的按轮次结构化视图
const turns = await run.conversation();
run.conversation() 返回该运行的 ConversationTurn[] (包含步骤的智能体轮次,或包含命令和输出的 shell 轮次) 。无需订阅实时流,即可用它渲染或持久化该运行的结构化历史记录。
使用量¶
运行时提供使用量时,运行会报告该用量。运行期间可从 run.usage 读取累计总量,调用 run.wait() 后可从 result.usage 读取。两者都包含所有报告用量的轮次汇总而成的 TokenUsage;如果没有任何轮次报告用量,则两者均为 undefined (例如,已取消且从未完成任何轮次的运行,或不提供用量信息的运行时) 。
interface TokenUsage {
inputTokens: number;
outputTokens: number;
cacheReadTokens: number;
cacheWriteTokens: number;
totalTokens: number;
reasoningTokens?: number;
}
| 字段 | 描述 |
|---|---|
inputTokens |
发送给模型的提示词 token。 |
outputTokens |
模型生成的 token。 |
cacheReadTokens |
从提示词缓存读取的 token。 |
cacheWriteTokens |
写入提示词缓存的 token。 |
totalTokens |
inputTokens + outputTokens + cacheReadTokens + cacheWriteTokens。不包括 reasoningTokens。 |
reasoningTokens |
推理 token,是 outputTokens 的一部分。模型或运行时未报告时省略。 |
const result = await run.wait();
if (result.usage) {
console.log(`total: ${result.usage.totalTokens}`);
console.log(`in: ${result.usage.inputTokens}, out: ${result.usage.outputTokens}`);
console.log(
`cache read/write: ${result.usage.cacheReadTokens}/${result.usage.cacheWriteTokens}`
);
} else {
console.log("no usage reported for this run");
}
reasoningTokens 已计入 outputTokens,因此 totalTokens 不再重复计入,以避免重复计算。
如需获取流式传输过程中每轮的用量数据,请处理 usage 流事件 (SDKUsageMessage)。它会在每个上报用量的轮次结束时触发一次,并携带该轮的 TokenUsage。run.usage 和 result.usage 在整个运行过程中始终为累计值。
for await (const event of run.stream()) {
if (event.type === "usage") {
console.log(`turn used ${event.usage.totalTokens} tokens`);
}
}
令牌计数由运行时报告,不代表费用。要获取智能体运行的已计费用量和美元费用,请调用 agent.getUsage()。
使用 requestId 关联 Run¶
每次调用 agent.send() 时,平台都会生成一个 UUID,并通过 requestId 字段在 Run 和 RunResult 中提供。使用它将脚本或 CI Run 与后端日志、使用分析和支持线程关联,而不要仅凭 agentId 猜测。
const run = await agent.send("Audit the auth middleware");
console.log(run.requestId); // 例如:"6e0d261c-86a2-4383-89f0-9162c1c10662"
const result = await run.wait();
logger.info({ requestId: result.requestId }, "run finished");
requestId 会随运行一并持久化,因此可在内存、SQLite 和 JSONL 本地存储之间往返传递;后端返回该值时,也会将其设置到云端运行中。请将其与错误中的 error.requestId 一并记录,以便同一标识符贯穿成功和失败路径。
单次运行模型覆盖¶
传递给 agent.send() 的 model 会覆盖智能体在该次运行中的模型选择,之后会持续生效:后续发送时若未指定覆盖,将继续使用新模型。若要切换回原来的模型,请传递另一个 model 覆盖,或通过 agent.model 读取当前选择。
const run = await agent.send("Plan the refactor", {
model: { id: "composer-2.5", params: [{ id: "fast", value: "true" }] },
});
console.log(agent.model); // 发送成功后更新为该覆盖设置
run.model 和 result.model 反映该次运行实际使用的模型选择,且运行开始后不可更改。
单次运行的环境变量¶
云端代理还可接收仅用于单次运行的环境变量。在 agent.send() 中传入 cloud.envVars,这些值会注入智能体在该次运行中使用的 shell。运行结束后,它们会从 VM 中移除,下一次运行无法访问。对于在轮次之间轮换的凭据,这种方式很合适,例如在让智能体使用前刚刚签发的短期部署令牌。
const run = await agent.send("Deploy the preview environment", {
cloud: {
envVars: {
DEPLOY_TOKEN: await mintShortLivedToken(),
},
},
});
如果单次运行的变量与 Agent.create() 的 cloud.envVars 中某个智能体范围变量同名,该次运行会优先使用单次运行的值;下一次运行时则会恢复使用智能体范围的值。
单次运行变量同样适用于首次发送。SDK 会在创建智能体时一并传递这些变量,并将其作用范围限定为初始运行,因此不会持久保存在智能体中。与智能体范围变量一样,它们在静态存储时会加密,且名称不能以 CURSOR_ 开头。
单次运行的环境变量仅适用于云端代理,且不适用于针对公开仓库运行的智能体。对于本地智能体,智能体进程会继承你自己的环境,因此请在调用 send() 前为进程设置变量。
对话模式¶
传入 mode: "plan" 或 mode: "agent",控制运行是先探索并制定方案,还是直接实施更改。有关 Plan 模式在产品中的作用,请参阅 Plan 模式。
在 Agent.create() 中设置 mode,以确定首次运行的模式。在后续的 agent.send() 调用中,省略 mode 可保留对话当前的模式;传入 mode 则仅切换该次运行的模式。
const agent = await Agent.create({
apiKey: process.env.CURSOR_API_KEY!,
model: { id: "composer-2.5" },
mode: "plan",
cloud: {
repos: [{ url: "https://github.com/your-org/your-repo" }],
},
});
await (await agent.send("Design the auth refactor")).wait();
await (await agent.send("Looks good, start building", { mode: "agent" })).wait();
流式获取原始增量¶
run.stream() 会产生规范化的 SDKMessage 事件。若需获取更底层的更新 (逐 token 文本、流式传入的工具调用 args、思考增量、嵌套任务更新和步骤边界) ,请向 send() 传入 onDelta 和 onStep 回调:
const run = await agent.send("Refactor the utils module", {
onDelta: ({ update }) => {
if (update.type === "text-delta") process.stdout.write(update.text);
if (update.type === "thinking-delta") process.stdout.write(update.text);
},
onStep: ({ step }) => {
console.log(`[step] ${step.type}`);
},
});
在处理下一次更新前,会先等待回调完成,因此你可以施加背压。InteractionUpdate涵盖text-delta、thinking-delta、thinking-completed、tool-call-started、tool-call-completed、tool-call-delta、partial-tool-call、token-delta、step-started、step-completed、turn-ended,以及少量摘要和 shell 输出增量。
单次发送选项¶
| 属性 | 类型 | 描述 |
|---|---|---|
model |
ModelSelection |
单次发送的模型覆盖设置。若省略,则使用 agent.model。粘性:发送成功后会更新 agent.model。 |
mode |
"agent" \| "plan" |
单次发送的对话模式覆盖设置。若在后续消息中省略,则保留对话当前的模式。 |
mcpServers |
Record<string, McpServerConfig> |
内联 MCP 服务器定义。会完全替换此次运行创建时配置的服务器。 |
onStep |
(args: { step }) => void \| Promise<void> |
每个对话步骤完成后 (文本、思考或工具批次) 调用的回调。 |
onDelta |
(args: { update }) => void \| Promise<void> |
每个原始 InteractionUpdate 的回调。 |
idempotencyKey |
string |
可选的客户端生成幂等键,用于此次发送。 |
cloud.envVars |
Record<string, string> |
仅限云端代理。为此次运行注入单次运行的环境变量,运行结束后会移除。仅在此次运行中按名称覆盖智能体范围内的 cloud.envVars。 |
local.force |
boolean |
仅限智能体。默认值为 false。开始发送此消息前,使卡住的活跃运行过期。云端会在服务器端返回 409 agent_busy,因此无需对应选项。 |
local.customTools |
Record<string, SDKCustomTool> |
仅限智能体。此次运行的自定义工具。会替换该智能体创建时配置的 local.customTools,仅对此次运行生效。 |
接下来的三个部分详细介绍 SDKMessage、InteractionUpdate 和 ConversationTurn。首次阅读时可快速浏览或跳过;恢复智能体将继续正文。
流事件¶
由 run.stream() 产生的事件。可通过 type 区分。所有事件均包含 agent_id 和 run_id。
type SDKMessage =
| SDKSystemMessage
| SDKUserMessageEvent
| SDKAssistantMessage
| SDKThinkingMessage
| SDKToolUseMessage
| SDKStatusMessage
| SDKTaskMessage
| {
type: "request";
agent_id: string;
run_id: string;
request_id: string;
}
| SDKUsageMessage;
type |
描述 | 关键字段 |
|---|---|---|
"system" |
初始化元数据。在运行开始时发出一次。 | subtype? ("init"), model?, tools? |
"user" |
此次运行的用户提示词回显。 | message.content: TextBlock[] |
"assistant" |
模型文本输出。 | message.content: (TextBlock \| ToolUseBlock)[] |
"thinking" |
思考内容。 | text, thinking_duration_ms? |
"tool_call" |
工具调用生命周期。开始时携带 args 发出,完成时再次携带 result 发出。 |
call_id, name, status, args?, result?, truncated? |
"status" |
云端运行生命周期状态变化。 | status, message? |
"task" |
任务级里程碑和摘要。 | status?, text? |
"request" |
等待用户输入或批准。 | request_id |
"usage" |
每轮 token 使用量;运行时报告后,会在该轮结束时发出一次。 | usage (TokenUsage) |
流完成后,结果数据 (最终文本、模型、时长、累计 token 使用量、Git 元数据) 会保存在 Run 对象中。使用 run.wait() 读取。
工具调用 schema 不稳定。
tool_call事件中的args和result负载反映各工具的内部结构,可能会随工具演进而变化。工具名称也可能被重命名或替换。请将args和result视为unknown,并进行防御性解析。事件信封 (type、call_id、name、status) 是稳定的。
消息类型¶
interface SDKSystemMessage {
type: "system";
subtype?: "init";
agent_id: string;
run_id: string;
model?: ModelSelection;
tools?: string[];
}
interface SDKUserMessageEvent {
type: "user";
agent_id: string;
run_id: string;
message: { role: "user"; content: TextBlock[] };
}
interface SDKAssistantMessage {
type: "assistant";
agent_id: string;
run_id: string;
message: {
role: "assistant";
content: Array<TextBlock | ToolUseBlock>;
};
}
interface SDKThinkingMessage {
type: "thinking";
agent_id: string;
run_id: string;
text: string;
thinking_duration_ms?: number;
}
interface SDKToolUseMessage {
type: "tool_call";
agent_id: string;
run_id: string;
call_id: string;
name: string;
status: "running" | "completed" | "error";
args?: unknown;
result?: unknown;
truncated?: { args?: boolean; result?: boolean };
}
interface SDKStatusMessage {
type: "status";
agent_id: string;
run_id: string;
status: "CREATING" | "RUNNING" | "FINISHED" | "ERROR" | "CANCELLED" | "EXPIRED";
message?: string;
}
interface SDKTaskMessage {
type: "task";
agent_id: string;
run_id: string;
status?: string;
text?: string;
}
interface SDKUsageMessage {
type: "usage";
agent_id: string;
run_id: string;
usage: TokenUsage;
}
interface TextBlock {
type: "text";
text: string;
}
interface ToolUseBlock {
type: "tool_use";
id: string;
name: string;
input: unknown;
}
大多数工具调用会发出两次 SDKToolUseMessage:首次 status: "running",并填充 args;完成时再次发出,status: "completed" (或 "error") ,并填充 result。truncated 标志表示 SDK 是否因负载过大而截断了 args 或 result。
SDKStatusMessage 涵盖云端的生命周期状态转换。CREATING 表示 VM 配置和仓库克隆;RUNNING 表示智能体正在工作;其余均为终态。
每个报告使用量的轮次结束时,都会发出一次 SDKUsageMessage,其中包含该轮的 TokenUsage。跨轮次的累计总用量保存在 run.usage 和 result.usage 中。参阅 使用量。
交互更新¶
InteractionUpdate 是传递给 agent.send() 的 onDelta 回调的原始增量类型。与 SDKMessage 事件相比,更新粒度更细:文本按 token 流式传输,工具调用会随着 args 的累积报告部分状态,思考内容会实时传入。
type InteractionUpdate =
| TextDeltaUpdate
| ThinkingDeltaUpdate
| ThinkingCompletedUpdate
| ToolCallStartedUpdate
| ToolCallCompletedUpdate
| ToolCallDeltaUpdate
| PartialToolCallUpdate
| TokenDeltaUpdate
| StepStartedUpdate
| StepCompletedUpdate
| TurnEndedUpdate
| UserMessageAppendedUpdate
| SummaryUpdate
| SummaryStartedUpdate
| SummaryCompletedUpdate
| ShellOutputDeltaUpdate;
更新类型¶
interface TextDeltaUpdate {
type: "text-delta";
text: string;
}
interface ThinkingDeltaUpdate {
type: "thinking-delta";
text: string;
}
interface ThinkingCompletedUpdate {
type: "thinking-completed";
thinkingDurationMs: number;
}
interface ToolCallStartedUpdate {
type: "tool-call-started";
callId: string;
toolCall: ToolCall;
modelCallId: string;
}
interface PartialToolCallUpdate {
type: "partial-tool-call";
callId: string;
toolCall: ToolCall;
modelCallId: string;
}
interface ToolCallCompletedUpdate {
type: "tool-call-completed";
callId: string;
toolCall: ToolCall;
modelCallId: string;
}
interface ToolCallDeltaUpdate {
type: "tool-call-delta";
callId: string;
modelCallId: string;
taskUpdate: NestedTaskUpdate;
}
type NestedTaskUpdate =
| TextDeltaUpdate
| ToolCallStartedUpdate
| ToolCallCompletedUpdate
| ThinkingDeltaUpdate
| ThinkingCompletedUpdate
| PartialToolCallUpdate
| StepStartedUpdate
| StepCompletedUpdate;
interface TokenDeltaUpdate {
type: "token-delta";
tokens: number;
}
interface StepStartedUpdate {
type: "step-started";
stepId: number;
}
interface StepCompletedUpdate {
type: "step-completed";
stepId: number;
stepDurationMs: number;
}
interface TurnEndedUpdate {
type: "turn-ended";
usage?: {
inputTokens: number;
outputTokens: number;
cacheReadTokens: number;
cacheWriteTokens: number;
reasoningTokens?: number;
};
}
interface UserMessageAppendedUpdate {
type: "user-message-appended";
userMessage: UserMessage;
}
interface SummaryUpdate {
type: "summary";
summary: string;
}
interface SummaryStartedUpdate {
type: "summary-started";
}
interface SummaryCompletedUpdate {
type: "summary-completed";
}
interface ShellOutputDeltaUpdate {
type: "shell-output-delta";
event: Record<string, unknown>;
}
ToolCallDeltaUpdate 包含任务或子智能体工具调用中一层嵌套的交互更新。模型在提交工具调用前流式传入参数时,会发出 PartialToolCallUpdate。适用于 SDKToolUseMessage.args 的稳定性免责声明同样适用于此处。
对话类型¶
运行中每轮对话的结构化视图,由 run.conversation() 返回,并作为 onStep 回调的参数传入。
type ConversationTurn =
| { type: "agentConversationTurn"; turn: AgentConversationTurn }
| { type: "shellConversationTurn"; turn: ShellConversationTurn };
interface AgentConversationTurn {
userMessage?: UserMessage;
steps: ConversationStep[];
}
interface ShellConversationTurn {
shellCommand?: ShellCommand;
shellOutput?: ShellOutput;
}
type ConversationStep =
| { type: "assistantMessage"; message: AssistantMessage }
| { type: "toolCall"; message: ToolCall }
| { type: "thinkingMessage"; message: ThinkingMessage };
interface AssistantMessage {
text: string;
}
interface ThinkingMessage {
text: string;
thinkingDurationMs?: number;
}
interface UserMessage {
text: string;
}
interface ShellCommand {
command: string;
workingDirectory?: string;
}
interface ShellOutput {
stdout: string;
stderr: string;
exitCode: number;
}
ToolCall 是一个涵盖所有内置工具 (shell、edit、read、write、glob、grep、ls、semSearch、mcp、task 等) 的可辨别联合类型。其结构仅供内部使用;请参阅“Stream events”下的稳定性说明。
恢复智能体会话¶
function Agent.resume(agentId: string, options?: Partial<AgentOptions>): Promise<SDKAgent>;
使用 Agent.resume() 通过 ID 重新连接到现有智能体。常见场景包括:重新连接之前启动的长时间运行云端代理,或在本地进程重新启动后继续对话。系统会根据 ID 前缀自动检测运行环境 (bc- 表示云端,其他表示本地) 。
await using agent = await Agent.resume("bc-abc123", {
apiKey: process.env.CURSOR_API_KEY!,
});
const run = await agent.send("Also update the changelog");
await run.wait();
恢复时,除非再次传入 model,否则 agent.model 为 undefined。内联 mcpServers 不会在恢复后持久保留——它们通常包含机密信息,且仅存于内存中。恢复时请再次传入,或者对于需要持续保留的服务器,使用基于文件的 MCP 配置 (.cursor/mcp.json + local.settingSources) 。
查看智能体和运行记录¶
列出、获取和重新加载历史智能体。列表端点返回 { items, nextCursor? },支持基于游标的分页。
Agent.list()¶
function Agent.list(options?: ListAgentsOptions): Promise<ListResult<SDKAgentInfo>>;
type ListAgentsOptions = {
limit?: number;
cursor?: string;
} & (
| { runtime?: undefined }
| { runtime: "local"; cwd?: string; store?: LocalAgentStore }
| {
runtime: "cloud";
prUrl?: string;
includeArchived?: boolean;
apiKey?: string;
}
);
const { items, nextCursor } = await Agent.list({
runtime: "local",
cwd: process.cwd(),
});
Agent.get()¶
function Agent.get(agentId: string, options?: GetAgentOptions): Promise<SDKAgentInfo>;
interface GetAgentOptions {
cwd?: string; // 本地路由
apiKey?: string; // 云端路由
store?: LocalAgentStore;
}
运行时会根据智能体 ID 前缀自动判断 (bc- → 云端,否则为本地) 。
Agent.listRuns()¶
function Agent.listRuns(agentId: string, options?: ListRunsOptions): Promise<ListResult<Run>>;
type ListRunsOptions = {
limit?: number;
cursor?: string;
} & (
| { runtime?: "local"; cwd?: string; store?: LocalAgentStore }
| { runtime: "cloud"; apiKey?: string }
);
Agent.getRun()¶
function Agent.getRun(runId: string, options?: GetRunOptions): Promise<Run>;
type GetRunOptions =
| { runtime?: "local"; cwd?: string; store?: LocalAgentStore }
| { runtime: "cloud"; agentId: string; apiKey?: string };
Cloud getRun 需要父级智能体的 agentId。
Agent.cancelRun()¶
function Agent.cancelRun(runId: string, options?: GetRunOptions): Promise<void>;
当你有运行 ID 但没有 Run 句柄时,取消该运行。
Agent.messages.list()¶
Agent.messages.list(
agentId: string,
options?: GetAgentMessagesOptions
): Promise<AgentMessage[]>;
interface GetAgentMessagesOptions {
limit?: number;
offset?: number;
runtime?: "local";
cwd?: string;
store?: LocalAgentStore;
}
interface AgentMessage {
type: "user" | "assistant";
uuid: string;
agent_id: string;
message: unknown;
}
返回本地智能体中存储的用户和助手消息。
Agent.getUsage()¶
获取智能体运行的计费使用量和美元费用。可通过 handle 调用;如果没有 handle,也可通过 ID 静态调用。云端代理返回按运行细分的数据;智能体返回按轮次细分的数据。传入 runId 可将结果限定为单个条目:云端代理使用 run-<uuid> 格式的运行 ID;智能体使用之前 getUsage().runs[].runId 中的 ID。
agent.getUsage(options?: GetUsageOptions): Promise<AgentUsage>;
function Agent.getUsage(
agentId: string,
options?: GetUsageOptions & { apiKey?: string }
): Promise<AgentUsage>;
interface GetUsageOptions {
runId?: string;
}
interface AgentUsage {
usage: TokenUsage; // 在各个 `runs` 中汇总
cost?: UsageCost; // 在各个 `runs` 中汇总
runs: RunUsage[];
}
interface RunUsage {
runId: string;
usage: TokenUsage;
cost?: UsageCost;
}
interface UsageCost {
rawCostCents: number; // 未享受折扣的模型 token 成本;按请求计费的用量为 0
chargedCents: number; // 实际收取的金额,已包含折扣和 Cursor Token 费用
}
const { usage, cost, runs } = await agent.getUsage();
console.log(`tokens: ${usage.totalTokens}`);
if (cost) {
console.log(`charged: $${(cost.chargedCents / 100).toFixed(2)}`);
}
for (const run of runs) {
console.log(run.runId, run.usage.totalTokens, run.cost?.chargedCents);
}
费用已包含折扣。一次运行结束后,费用可能需要片刻才能结算;在此之前不会显示 cost。对于方案内用量、BYOK (自带密钥) 用量和信用额度授予的用量,chargedCents 均为 0。
这与 使用量 是不同的视图:run.usage 显示单次运行的实时 token 数量,而 getUsage() 则是智能体运行的计费记录。
云端代理生命周期¶
云端代理会一直保留在你的团队工作区中,直到你将其归档或删除。Agent.list({ runtime: "cloud" }) 默认不显示已归档的智能体;传入 includeArchived: true 可查看它们。按 prUrl 筛选,可找到创建特定 PR 的智能体。
function Agent.archive(agentId: string, options?: AgentOperationOptions): Promise<void>;
function Agent.unarchive(agentId: string, options?: AgentOperationOptions): Promise<void>;
function Agent.delete(agentId: string, options?: AgentOperationOptions): Promise<void>;
interface AgentOperationOptions {
cwd?: string;
apiKey?: string;
store?: LocalAgentStore;
}
await Agent.archive(agentId); // 软删除;仍可读取会话记录
await Agent.unarchive(agentId); // 恢复已归档的智能体
await Agent.delete(agentId); // 永久删除;后续读取将返回 404
SDKAgentInfo¶
Agent.list() 和 Agent.get() 返回的元数据类型。
type SDKAgentInfo = {
agentId: string;
name: string;
summary: string;
lastModified: number;
status?: "running" | "finished" | "error";
createdAt?: number;
archived?: boolean;
} & (
| { runtime?: undefined }
| { runtime: "local"; cwd?: string }
| {
runtime: "cloud";
env?: { type: "cloud" | "pool" | "machine"; name?: string };
repos?: string[];
metadata?: Record<string, string>;
}
);
Cursor 命名空间¶
提供账户级读取、目录读取和进程级 SDK 配置。读取方法可接受 { apiKey };如未提供,则依次使用 CURSOR_API_KEY 和已保存的浏览器登录。
Cursor.auth¶
为未预先配备 API 密钥的主机提供交互式登录。Cursor.auth.login() 会在浏览器中打开 Cursor 网站的登录页面,等待登录完成后签发用户 API 密钥 (默认有效期为 90 天) ,并将其存储在 ~/.cursor/sdk/auth.json 中。登录后,Agent.create()、Cursor.me() 和其他读取操作无需 apiKey 或 CURSOR_API_KEY 即可使用。
import { Cursor } from "@cursor/sdk";
await Cursor.auth.login();
const status = await Cursor.auth.status();
// { status: "已登录", backendUrl, email?, apiKeyExpiresAtMs? }
// | { status: "已登出" }
await Cursor.auth.logout();
| 选项 | 描述 |
|---|---|
backendUrl |
API 基础 URL。默认值依次为 CURSOR_BACKEND_URL 和生产环境。 |
websiteUrl |
浏览器登录基础 URL。默认值依次为 CURSOR_WEBSITE_URL 和生产环境。 |
openBrowser |
true (默认值) 会在适当情况下打开系统浏览器;false 则绝不打开;也可传入函数作为自定义打开方式。在 SSH 会话中或设置了 NO_OPEN_BROWSER 时将跳过此操作。 |
onLoginUrl |
等待前会使用登录 URL 调用此函数,以便主机显示该 URL。若省略此项且未打开浏览器,URL 会写入 stderr。 |
signal |
用于取消等待的 AbortSignal;随后 login 会抛出 AuthenticationError。 |
store |
持久化保存凭据的位置。默认值为 ~/.cursor/sdk/auth.json;传入 null 则仅在结果中返回密钥。 |
apiKeyName |
仪表盘 API 密钥列表中所签发密钥的显示名称。 |
apiKeyTtlMs |
所签发密钥的有效期,单位为毫秒。默认值为 90 天。 |
Cursor.auth.login() 返回
{ apiKey, email?, apiKeyExpiresAtMs }。使用 FileCredentialStore 或
InMemoryCredentialStore 为 login()、status()
或 logout() 提供自定义 store。
SDK 中所有位置的凭据解析顺序:显式指定的 apiKey、CURSOR_API_KEY、已存储的登录信息。本地存储的登录信息不会从已安装的 Cursor app 中读取凭据;其中仅保存由 Cursor.auth.login() 签发的密钥。
Cursor.configure()¶
function Cursor.configure(options: CursorConfigureOptions): void;
interface CursorConfigureOptions {
local?: {
store?: LocalAgentStore | null;
useHttp1ForAgent?: boolean | null;
workspaceScanCacheTtlMs?: number | null;
};
}
为后续 Agent.* 调用设置本地智能体的默认值。单次调用中指定的字段会覆盖这些值;传入 null 可清除先前设置的默认值。
| 选项 | 描述 |
|---|---|
local.store |
调用未指定 local.store 时使用的默认本地智能体存储。SQLite 后端可用时,SDK 使用基于磁盘的 SQLite;否则会回退到 JsonlLocalAgentStore。 |
local.useHttp1ForAgent |
强制本地智能体后端流使用带 SSE 的 HTTP/1.1,而非 HTTP/2。在代理之后运行,或使用不支持 HTTP/2 的 fetch 栈时很有用。由于上游 HTTP/2 兼容性问题,Bun 默认使用 HTTP/1.1。 |
local.workspaceScanCacheTtlMs |
SDK 重用工作区扫描结果 (规则、技能、AGENTS.md、忽略文件) 的时长,单位为毫秒。默认值为 20 秒。对于仅在部署时变更的 checkout,可在长期运行的主机中提高此值;代价是时效性降低,因为进程启动后添加的规则可能在这段时间内不会被发现。CURSOR_RIPWALK_CACHE_TTL_MS 环境变量可设置相同的值。 |
import { Cursor, JsonlLocalAgentStore } from "@cursor/sdk";
Cursor.configure({
local: {
store: new JsonlLocalAgentStore("/var/lib/cursor-agents"),
useHttp1ForAgent: true,
},
});
Cursor.me()¶
function Cursor.me(options?: CursorRequestOptions): Promise<SDKUser>;
interface CursorRequestOptions {
apiKey?: string;
}
interface SDKUser {
apiKeyName: string;
userId?: number;
userEmail?: string;
userFirstName?: string;
userLastName?: string;
createdAt: string;
}
Cursor.models.list()¶
function Cursor.models.list(options?: CursorRequestOptions): Promise<SDKModel[]>;
type SDKModel = ModelListItem;
interface ModelListItem {
id: string;
displayName: string;
description?: string;
aliases?: string[];
parameters?: ModelParameterDefinition[];
variants?: ModelVariant[];
}
interface ModelParameterDefinition {
id: string;
displayName?: string;
values: Array<{ value: string; displayName?: string }>;
}
interface ModelVariant {
params: ModelParameterValue[];
displayName: string;
description?: string;
isDefault?: boolean;
}
在调用 Agent.create() 或 agent.send() 前,使用 Cursor.models.list() 查询有效的 model ID 及各模型支持的 params。参数因模型而异。常见参数包括推理 effort,以及 auto-smart 的 Cursor Router optimize_for。
可用目录因账户和团队而异。只有 API 密钥所属团队可使用 Router 时,Cursor Router 才会以 auto-smart 形式显示。请参阅 Cursor Router。
const models = await Cursor.models.list();
const composer = models.find((model) => model.id === "composer-2.5");
console.log(composer?.parameters);
// [
// {
// id: "fast",
// displayName: "快速",
// values: [
// { value: "false" },
// { value: "true", displayName: "快速" },
// ],
// },
// ]
通过 model.params 传入所选参数值。预设 variants 已包含有效的 params,因此可以将其复制到模型选择中。
const agent = await Agent.create({
apiKey: process.env.CURSOR_API_KEY!,
model: {
id: "composer-2.5",
params: [{ id: "fast", value: "true" }],
},
local: { cwd: process.cwd() },
});
最佳实践¶
- 动态获取,不要硬编码。 在启动时 (或每个进程启动一次) 调用
Cursor.models.list(),并缓存结果。随着新模型上线,模型 ID 和参数结构可能发生变化。 - 模型需要参数时,务必显式传入。
parameters数组非空的模型属于参数化模型。请传入所需参数;否则,运行时会使用每个参数允许的第一个值,这可能不符合你的预期。对于 Cursor Router,始终显式传入optimize_for。 - 按能力而非 ID 解析。 如果你需要的是“当前快速模式下的默认模型”,而不是某个特定模型,请按如下方式查找:
const models = await Cursor.models.list();
const composer = models.find((m) => m.id === "composer-2.5");
const fast = composer?.parameters?.find((p) => p.id === "fast");
const fastValue = fast?.values.find((v) => v.value === "true")?.value;
const model = composer
? {
id: composer.id,
params: fastValue ? [{ id: "fast", value: fastValue }] : undefined,
}
: {
id: "auto-smart",
params: [{ id: "optimize_for", value: "balanced" }],
};
当目标模型不可用时,优先显式选择 Router (auto-smart + optimize_for) 。只有在希望由服务器选择 Auto,且不指定 Cost、Balance 或 Intelligence 时,才回退到 { id: "auto" }。
Cursor.repositories.list()¶
function Cursor.repositories.list(options?: CursorRequestOptions): Promise<SDKRepository[]>;
interface SDKRepository {
url: string;
}
返回调用用户所属团队已连接的 GitHub 仓库。仅限 Cloud。
配置来源概览¶
MCP 服务器、子智能体和钩子都会从内联选项和磁盘上的配置中解析。三者的优先级结构相同:每次发送时的内联配置 > 创建时的内联配置 > 项目文件 > 用户文件 > 团队 / 仪表盘配置。
| 功能 | 内联选项 | 本地文件 (项目) | 本地文件 (用户) | 云端 / 仪表盘 | 优先级 |
|---|---|---|---|---|---|
| MCP 服务器 | Agent.create() 和 agent.send() 中的 mcpServers |
.cursor/mcp.json (仅当 local.settingSources 包含 \"project\" 时加载) |
~/.cursor/mcp.json (仅当包含 \"user\" 时加载) |
在 cursor.com/agents 配置的服务器 (仅限云端) | 发送 > 创建 > 插件 > 项目 > 用户 (本地) ;发送 > 创建 > 仪表盘 (云端) |
| 子智能体 | Agent.create() 中的 agents |
.cursor/agents/*.md (frontmatter:name、description、model?) |
不适用 | 智能体针对克隆的仓库运行时,云端会加载相同的项目文件 | 内联配置会覆盖同名的文件配置 |
| 钩子 | 无 — 仅支持文件配置 | .cursor/hooks.json (+ 脚本) |
~/.cursor/hooks.json |
云端运行项目钩子。企业版方案还会运行团队和企业管理的钩子。 | 基于文件;项目配置会与用户 / 团队 / 企业配置分层,详见 Hooks |
| 配置来源 | local.settingSources 用于选择要加载的磁盘配置层 |
.cursor/ |
~/.cursor/ |
不适用 | 云端始终加载 project / team / plugins,并忽略 local.settingSources。 |
内联值适合绝不应写入磁盘的机密信息 (如单次运行的 API 密钥、租户范围的 token) 。文件配置适合用于策略:钩子尤其如此,它们属于项目边界,而非单次运行的控制项。
MCP 服务器¶
智能体可从多种来源获取 MCP 服务器。最常见的方式是在 Agent.create() 或 agent.send() 中内联定义。也支持基于文件的配置和通过仪表盘管理的配置。
加载的内容¶
本地智能体最多可从五个来源加载服务器;如果名称冲突,以最先匹配的来源为准:
agent.send()中的mcpServers。会完全替换该次运行创建时的服务器 (不合并) 。Agent.create()中的mcpServers。未提供单次发送覆盖配置时使用。- 插件服务器,前提是
local.settingSources包含"plugins"。 - 来自
.cursor/mcp.json的项目服务器,前提是local.settingSources包含"project"。 - 来自
~/.cursor/mcp.json的用户服务器,前提是local.settingSources包含"user"。
未设置 local.settingSources 时,只会加载内联服务器。如果本地 MCP 服务器需要通过 OAuth 登录,SDK 无法提示你登录。只有当你已在 Cursor app 中登录过该服务器时才能使用;此时 SDK 会复用已保存的登录凭据。
云端代理从以下来源加载服务器:
agent.send()中的mcpServers。会完全替换该次运行创建时的服务器 (不合并) 。Agent.create()中的mcpServers。未提供单次发送覆盖配置时使用。- 来自 cursor.com/agents 的用户和团队 MCP 服务器。
如果内联服务器不包含 auth 或 headers,且你之前已在 cursor.com/agents 授权该服务器 URL,使用个人 API token 认证的运行会自动复用这些 OAuth token。服务账户 API 密钥无法回退使用用户认证,因为它们不与用户关联。
local.settingSources 不适用于云端代理。
本地¶
const agent = await Agent.create({
apiKey: process.env.CURSOR_API_KEY!,
model: { id: "auto" },
local: { cwd: process.cwd() },
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(),
},
},
});
云端¶
云端代理也可以直接接收已认证的 MCP 配置。当 Cursor 需要通过后端代理远程 MCP 时,请使用 HTTP 认证。当服务器在云端 VM 中运行,并从环境变量读取凭据时,请使用 stdio env。
const agent = await Agent.create({
apiKey: process.env.CURSOR_API_KEY!,
model: { id: "composer-2.5" },
cloud: {
repos: [{ url: "https://github.com/your-org/your-repo", startingRef: "main" }],
},
mcpServers: {
linear: {
type: "http",
url: "https://mcp.linear.app/sse",
headers: {
Authorization: `Bearer ${process.env.LINEAR_API_KEY!}`,
},
},
figma: {
type: "http",
url: "https://api.figma.com/mcp",
auth: {
CLIENT_ID: process.env.FIGMA_CLIENT_ID!,
CLIENT_SECRET: process.env.FIGMA_CLIENT_SECRET!,
scopes: ["file_content:read"],
},
},
github: {
type: "stdio",
command: "npx",
args: ["-y", "@modelcontextprotocol/server-github"],
env: {
GITHUB_TOKEN: process.env.GITHUB_TOKEN!,
},
},
},
});
静态 API 密钥或 Bearer token 请使用 headers,Cursor 会在每次请求中原样传递。受 OAuth 保护的服务器请使用 auth。在云端,Cursor 会在服务端执行一次 OAuth 流程,并在多次运行间复用 token。在本地,SDK 无法打开浏览器让您登录;它只能复用您已通过 Cursor app 登录获取的 token。
- HTTP
headers和auth由 Cursor 后端处理。敏感字段会被脱敏,不会进入 VM。 - Stdio
env值会传入 VM,因为服务器在其中运行。请将其视为其他运行时机密信息一样妥善处理。 - 在 cursor.com/agents 上配置的 MCP 服务器,其 OAuth 始终按用户区分,即使是团队级服务器也是如此。
完整配置格式请参阅 MCP,云端特定行为请参阅 Cloud Agent capabilities。
子智能体¶
定义由主智能体通过 Agent 工具启动的具名子智能体,并以内联方式传入:
const agent = await Agent.create({
model: { id: "composer-2.5" },
apiKey: process.env.CURSOR_API_KEY!,
local: { cwd: process.cwd() },
agents: {
"code-reviewer": {
description: "Expert code reviewer for quality and security.",
prompt: "Review code for bugs, security issues, and proven approaches.",
model: "inherit",
},
"test-writer": {
description: "Writes tests for code changes.",
prompt: "Write comprehensive tests for the given code.",
},
},
});
同样会识别已提交到仓库 .cursor/agents/*.md 中的子智能体 (包含 name、description 和可选的 model frontmatter) 。内联定义会覆盖同名的基于文件的定义。
嵌套子智能体¶
子智能体可在嵌套层级限制内启动自己的子智能体。当子智能体使用 Agent 工具时,SDK 会为其提供与父智能体相同的子智能体执行器,因此父智能体可以将任务委派给还能继续委派的子智能体。每一层都可访问同一组具名子智能体和自定义工具。顶层智能体及其直接子智能体可以启动子智能体,但由子智能体启动的子智能体无法再启动下一级子智能体。
限制工具集¶
tools 将模型可用的内置工具限定为允许列表中的工具;disallowedTools 会移除指定工具,保留其余工具,包括在您的 SDK 版本发布后添加到平台的工具。目前两者仅支持本地智能体,且都不会持久保存在智能体中:如需在后续运行中保持此限制,请在 Agent.resume() 中再次传入。
// 只读智能体:仅可使用这些工具。
const reader = await Agent.create({
apiKey: process.env.CURSOR_API_KEY!,
model: { id: "composer-2.5" },
tools: ["read", "grep", "glob", "ls"],
local: { cwd: process.cwd() },
});
// 除 shell 外的所有工具。
const noShell = await Agent.create({
apiKey: process.env.CURSOR_API_KEY!,
model: { id: "composer-2.5" },
disallowedTools: ["shell"],
local: { cwd: process.cwd() },
});
tools: undefined(默认值) 为所选模型提供标准工具集;tools: []不提供任何内置工具,因此模型只能返回文本响应。- 两个字段都接受
ToolName联合类型:公开名称 ("read"、"edit"、"task"、"webSearch"、……) 、能力群组"shell"和"mcp",以及原始 proto 工具名称。未知名称会在调用Agent.create()/Agent.resume()时抛出ConfigurationError。 - 禁止项优先:要提供某个工具,该工具必须包含在
tools中 (如已设置) ,且不在disallowedTools中。 - 禁用
"mcp"也会移除自定义工具。禁用"task"会阻止子智能体;否则子智能体会保留各自精选的工具集。
自定义工具¶
自定义工具让你无需单独搭建 MCP 服务器,即可向智能体公开自己的函数。通过 local.customTools 传入这些工具后,SDK 会将其注册为名为 custom-user-tools 的 MCP 服务器。智能体会通过与其他服务器相同的 MCP 路径发现并调用它们。拒绝规则和 沙箱限额仍然适用,但自定义工具无需交互式批准,因此沙箱化和Auto-review 模式运行会直接调用它们,不会提示。自定义工具也可供子智能体 (包括嵌套子智能体) 使用。
自定义工具仅适用于本地智能体。向云端代理传入 local.customTools 会抛出 ConfigurationError。
const agent = await Agent.create({
apiKey: process.env.CURSOR_API_KEY!,
model: { id: "composer-2.5" },
local: {
cwd: process.cwd(),
customTools: {
get_deployment_status: {
description: "Look up the current deployment status for a service.",
inputSchema: {
type: "object",
properties: {
service: { type: "string", description: "Service name" },
},
required: ["service"],
},
async execute({ service }) {
const res = await fetch(`https://deploys.internal/api/${service}`);
const body = await res.json();
return `Service ${service} is ${body.status} (build ${body.build}).`;
},
},
},
},
});
await agent.send("Is the checkout service deployed yet?").then((r) => r.wait());
在 Agent.create() 中设置一次自定义工具,即可应用于每次运行;或者在单次 agent.send() 中传入 local.customTools,以替换该次运行的自定义工具。
await agent.send("Roll forward if the canary is healthy", {
local: {
customTools: {
promote_canary: {
description: "Promote the current canary build to production.",
async execute() {
await promoteCanary();
return { content: [{ type: "text", text: "Promoted." }] };
},
},
},
},
});
工具定义¶
interface SDKCustomTool {
description?: string;
inputSchema?: Record<string, SDKJsonValue>;
execute: (
args: Record<string, SDKJsonValue>,
context: SDKCustomToolContext
) => SDKCustomToolResult | Promise<SDKCustomToolResult>;
}
interface SDKCustomToolContext {
toolCallId?: string;
}
| 字段 | 描述 |
|---|---|
description |
展示给模型,用于判断何时调用该工具。默认值为空 string。 |
inputSchema |
arguments 的 JSON Schema。默认值为允许任意属性的开放 object。 |
execute |
您的 callback。接收解析后的 args 以及包含 toolCallId 的 context。在您的 process 中运行,因此可访问您的代码能访问的任何内容。 |
工具结果¶
execute 可以返回普通 string、任意 JSON 值或结构化 envelope。map 的键是模型调用的工具名称。
type SDKCustomToolResult =
| string
| SDKJsonValue
| {
content: SDKCustomToolContent[];
isError?: boolean;
structuredContent?: Record<string, SDKJsonValue>;
};
type SDKCustomToolContent =
| { type: "text"; text: string }
| { type: "image"; data: string; mimeType?: string };
- 纯文本输出请返回 string。
- 返回任意 JSON 值即可将其作为文本发回;对象还会填充
structuredContent。 - 如需完全控制,请返回 envelope:可混合文本和 base64 图像
content,设置isError: true以报告失败,或附加structuredContent供模型解析。execute抛出异常也会作为工具错误报告给智能体。
钩子¶
钩子仅支持基于文件的配置方式,不提供可编程的钩子回调。钩子是项目策略的边界,而不是每次运行时可调节的选项。
- 本地: 将
.cursor/hooks.json添加到传递给local.cwd的仓库中,或添加~/.cursor/hooks.json以配置用户级钩子。 - 云端: 将
.cursor/hooks.json及其脚本提交到cloud.repos中传入的仓库。由 SDK 创建的云端代理会自动加载项目钩子。在企业版方案中,它们还会运行团队钩子和企业级管理的钩子。
有关配置格式,请参阅钩子;有关云端行为,请参阅云端代理钩子支持。
沙箱选项¶
默认情况下,本地智能体以 local.sandboxOptions.enabled: false 运行。智能体可读取和写入工作目录、执行 shell 命令,并可不受限制地访问网络。无界面 SDK 运行没有人工批准流程,因此默认启用沙箱要么会静默阻止合法的工具调用,要么需要不适用于脚本的回调。
启用沙箱后,SDK 会限制每次 shell 工具调用以及由 shell 启动的进程:
- 文件系统 — 仅允许写入工作目录 (
local.cwd) 和一小部分允许的路径。禁止读取工作区以外的内容。 - Shell — 命令在平台沙箱中运行 (Linux 上使用
bubblewrap,macOS 上使用seatbelt,或使用随附的@cursor/sdk-<os>-<arch>辅助程序) 。不允许执行特权操作。 - 网络 — 默认禁止出站网络访问。若要允许特定主机,请在工作区中添加
.cursor/sandbox.json,列出允许访问的主机。如果存在,SDK 也会读取位于~/.cursor/sandbox.json的同一用户级策略。
const agent = await Agent.create({
apiKey: process.env.CURSOR_API_KEY!,
model: { id: "composer-2.5" },
local: {
cwd: process.cwd(),
sandboxOptions: { enabled: true },
},
});
如果主机不支持沙箱 (例如较早版本的 Linux 未安装 bubblewrap,或缺少辅助二进制文件) ,SDK 会抛出 ConfigurationError,并在消息中指出缺少的依赖项。禁用 sandboxOptions.enabled 或改用云端模式运行即可恢复。
云端运行始终在隔离的 VM 中执行,因此 sandboxOptions 不适用。
Auto-review 模式¶
默认情况下,本地智能体会不受限制地执行每个工具调用,因为无头运行时没有人工进行批准。设置 local.autoReview: true 后,本地工具调用将改由 Auto-review 模式 模式处理。该模式使用与 IDE 相同的分类器,根据安全性以及各调用与运行意图的匹配程度,允许或阻止 Shell、MCP 和 Fetch 调用。
const agent = await Agent.create({
apiKey: process.env.CURSOR_API_KEY!,
model: { id: "composer-2.5" },
local: {
cwd: process.cwd(),
autoReview: true,
},
});
Auto-review 模式 需要在已连接的后端启用分类器;如果分类器不可用,运行将回退到默认行为。由于无界面运行不支持交互式批准,分类器拦截的调用会被拒绝而非升级处理;智能体会收到拦截原因,并可尝试其他方法。可通过工作区中的 permissions.json autoRun 块引导分类器,方式与 IDE 相同。格式请参阅 permissions.json。
Auto-review 模式 仅适用于本地智能体。云端运行已在隔离的 VM 中执行。分类器是一项尽力而为的便捷功能,并非安全边界;如需严格控制,请结合使用 sandboxOptions 或允许列表。
产物¶
列出并下载智能体工作区中的文件。
interface SDKArtifact {
path: string;
sizeBytes: number;
updatedAt: string;
}
const artifacts: SDKArtifact[] = await agent.listArtifacts();
for (const artifact of artifacts) {
console.log(artifact.path, artifact.sizeBytes);
}
const buffer = await agent.downloadArtifact(artifacts[0].path);
Artifact 支持因运行时而异。Local SDK agents 目前不返回任何 artifact,调用 downloadArtifact 时会抛出错误。
资源管理¶
使用完智能体后,务必将其释放。最简洁的方式是使用 await using:
await using agent = await Agent.create({ /* ... */ });
// 退出代码块时会自动释放
要显式释放资源:
await agent[Symbol.asyncDispose]();
agent.close() 是无需等待即可开始释放资源的推荐方法。Symbol.asyncDispose 也可用 (await using 基于它实现) ,但对于不使用 await using 语法的代码,应使用 close()。agent.reload() 可在不释放资源的情况下加载文件系统配置的更改 (钩子、项目 MCP、子智能体) 。
智能体生命周期¶
预热本地工作区¶
解析本地工作区 (规则、技能、MCP 服务器、忽略文件) 是本地智能体首次执行时最耗时的环节,在大型仓库中甚至可能占据绝大部分时间。默认情况下,这项开销会计入首次 send() 调用。主机如果知道智能体将在何处运行,可以通过 prewarmLocalWorkspace() 提前完成这项工作:
import { createAgentPlatform } from "@cursor/sdk";
const platform = await createAgentPlatform();
const release = await platform.prewarmLocalWorkspace({
apiKey: process.env.CURSOR_API_KEY!,
local: { cwd: "/srv/checkout", settingSources: ["project"] },
});
// 此工作区的首次 send() 会立即执行。
await release(); // 主机关闭时
传入智能体将使用的同一组 AgentOptions;预热仅对工作区选项相匹配的发送有效。主机关闭时,调用返回的 release 函数。
重新连接到现有智能体¶
Agent.resume(agentId) 会返回一个用于操作现有智能体的新句柄。系统会根据 ID 前缀自动识别运行时 (bc- 表示云端,其他前缀表示本地) ,并从云端 (云端) 或本地检查点存储 (本地) 加载对话状态。这可用于在进程重新启动后继续工作,或让其他 worker 接手由另一个进程启动的智能体。
const agent = await Agent.resume("bc-abc123", {
apiKey: process.env.CURSOR_API_KEY!,
});
const run = await agent.send("Apply the suggested fix");
const result = await run.wait();
如果重新连接时该运行已在进行中,Agent.getRun(runId, { runtime: "cloud", agentId }) (或本地等效方法) 会返回一个 Run,可对其调用 stream()、wait() 或 cancel()。
对话上下文¶
本地智能体会将对话状态持久化到检查点存储中。默认使用主目录下基于磁盘的 SQLite;也可通过 local.store 替换为 JSONL 或自定义后端。每次调用 agent.send() 都会加载该智能体的最新检查点并传递给模型,因此后续交互能获得上一次运行结束时的相同上下文。该存储在进程重新启动后仍会保留,因此在全新的进程中调用 Agent.resume(agentId) 可以从上次结束的位置继续。
云端代理会在服务器端持久化状态。从任何位置重新连接,都会返回同一对话。
以下几种情况看似丢失了上下文,但其实并非如此:
- 每次调用
Agent.create()都会使用新的agentId创建一个全新的智能体。若要继续现有对话,请在首次调用时获取agent.agentId,之后使用Agent.resume(agentId)。 Agent.prompt()会一次性创建、运行并释放智能体。不会有第二轮交互;这是其合约。- 内联
mcpServers不会在Agent.resume()后保留,因为其中通常包含机密信息。恢复时请再次传入,或使用基于文件的 MCP 配置。
调度器模式¶
调度器管理一个智能体池,并在任务到达时将其分配给相应的智能体。其模式很简单:维护一个从 agentId 到长期存活的 SDKAgent 的映射,根据某个键 (用户、仓库、工单) 路由传入的提示词;如果进程重启导致内存中的映射丢失,则从磁盘恢复并调用 Agent.resume()。
import { Agent, type SDKAgent } from "@cursor/sdk";
const agents = new Map<string, SDKAgent>();
async function getAgent(key: string, savedId?: string): Promise<SDKAgent> {
const existing = agents.get(key);
if (existing) return existing;
const agent = savedId
? await Agent.resume(savedId, {
apiKey: process.env.CURSOR_API_KEY!,
})
: await Agent.create({
apiKey: process.env.CURSOR_API_KEY!,
model: { id: "composer-2.5" },
local: { cwd: process.cwd() },
});
agents.set(key, agent);
return agent;
}
async function handleMessage(key: string, prompt: string, savedId?: string) {
const agent = await getAgent(key, savedId);
const run = await agent.send(prompt);
return run.wait();
}
云端 SSE 流会在运行开始后的一段时间内保留积压事件,因此向多个订阅者提供流的调度器可让每个订阅者调用 run.stream(),而不会丢失先前的事件。对于运行时间特别长的云端运行,调度器通常会将任务分发给 run.wait(),并让需要结构化会话记录的订阅者轮询 run.conversation()。
本地智能体存储¶
本地智能体会将智能体元数据、对话检查点、运行记录和运行事件持久化到磁盘,以便后续操作和 Agent.resume() 在进程重启后仍可继续执行。默认情况下,SQLite 后端可用时,SDK 会使用基于磁盘的 SQLite;否则回退到 JsonlLocalAgentStore。您可以通过 local.store 替换为其他后端。
SDK 提供两种后端,也支持您自定义后端:
| 存储 | 导入 | 适用场景 |
|---|---|---|
SqliteLocalAgentStore |
@cursor/sdk/sqlite |
工作区状态根目录下的基于磁盘的 SQLite。 |
JsonlLocalAgentStore |
@cursor/sdk |
存储在您指定目录中的可移植换行分隔 JSON (NDJSON) 文件。便于查看、复制和 diff。 |
自定义 LocalAgentStore |
您的代码 | 可持久化到任意位置:内存、Redis、Postgres 或托管数据库。实现该接口或组合子存储。 |
云端代理在服务器端持久化,因此 local.store 仅适用于本地智能体。
JSONL 存储¶
JsonlLocalAgentStore 会在你指定的目录中写入四个 NDJSON 文件 (agents.ndjson、runs.ndjson、run_events.ndjson、checkpoints.ndjson) 。创建一个实例,并将其传给 local.store。
import { Agent, JsonlLocalAgentStore } from "@cursor/sdk";
const store = new JsonlLocalAgentStore("/var/lib/cursor-agents");
const agent = await Agent.create({
apiKey: process.env.CURSOR_API_KEY!,
model: { id: "composer-2.5" },
local: { cwd: process.cwd(), store },
});
在 Agent.resume() 以及本地列表和获取 API (Agent.list、Agent.get、Agent.listRuns、Agent.getRun) 中使用同一存储实例,确保它们读取相同的数据。
设置进程级默认值¶
为避免在每次调用时都传递存储,可通过 Cursor.configure() 一次性设置默认值。传入 local.store 时,仍以该调用中指定的值为准。
import { Cursor, JsonlLocalAgentStore } from "@cursor/sdk";
Cursor.configure({ local: { store: new JsonlLocalAgentStore("/var/lib/cursor-agents") } });
// 后续调用会使用已配置的存储,除非自行传入存储。
const agent = await Agent.create({
apiKey: process.env.CURSOR_API_KEY!,
model: { id: "composer-2.5" },
local: { cwd: process.cwd() },
});
向 Cursor.configure({ local: { store: null } }) 传入 store: null,即可清除先前设置的默认值,并恢复为使用 SDK’s 默认本地存储。
自定义存储¶
如需将数据持久化到其他位置 (如共享的 Postgres、Redis,或供测试使用的内存映射) ,请实现 LocalAgentStore。它由四个子存储组成,每个都是供 SDK 调用的小型 CRUD 接口:
interface LocalAgentStore {
readonly agents: LocalAgentStoreAgents; // 智能体元数据记录
readonly checkpoints: LocalAgentStoreCheckpoints; // 按内容寻址的对话 Blob
readonly runs: LocalAgentStoreRuns; // 运行记录
readonly runEvents: LocalAgentStoreRunEvents; // 仅追加的运行事件日志
}
直接实现该接口,或分别构建各个子存储,再使用 composeLocalAgentStore 将其组合起来:
import { composeLocalAgentStore } from "@cursor/sdk";
const store = composeLocalAgentStore({
agents: myAgentsTable,
checkpoints: myCheckpointBlobs,
runs: myRunsTable,
runEvents: myRunEventLog,
});
这些子存储与默认的 SQLite 表相对应:agents 中每个智能体占一行 (包含一个精简的 latestCheckpoint.rootBlobId 指针) ,checkpoints 存储这些指针所引用的按内容寻址的对话 blob,runs 中每次运行占一行,runEvents 则是仅追加的流式日志。目录子存储通过不透明的 cursor / nextCursor 实现分页;运行事件日志则通过排他性的 afterOffset / nextOffset 恢复。具体的数据结构请参阅导出的 LocalAgentStore、LocalAgentDocument、LocalAgentRunDocument 及相关类型。
配置参考¶
AgentOptions¶
| 属性 | 类型 | 默认值 | 描述 |
|---|---|---|---|
model |
ModelSelection |
本地必填;云端会回退到服务器解析的默认值 | 要使用的模型。参见 ModelSelection。 |
apiKey |
string |
CURSOR_API_KEY 环境变量 |
用户 API 密钥或服务账户密钥。暂不支持团队 Admin 密钥。 |
name |
string |
自动生成 | 供人阅读的智能体名称,在 Agent.list() / Agent.get() 中以 name 字段返回。 |
local |
LocalAgentOptions |
本地智能体配置。参见 LocalAgentOptions。 |
|
cloud |
CloudAgentOptions |
云端代理配置。 | |
mcpServers |
Record<string, McpServerConfig> |
内联 MCP 服务器定义。 | |
agents |
Record<string, AgentDefinition> |
子智能体定义。 | |
tools |
ToolName[] |
默认工具集 | 限制工具集:仅向模型提供列出的内置工具。[] 表示不提供内置工具。仅适用于本地智能体。 |
disallowedTools |
ToolName[] |
从工具集中移除工具;其他工具仍可用。与 tools 一起使用时,以拒绝为准。仅适用于本地智能体。 |
|
agentId |
string |
自动生成 | 持久化智能体 ID。传入此值可在多次调用间保持 ID 稳定。 |
idempotencyKey |
string |
云端自动生成 | 可选的客户端生成幂等性密钥。 |
mode |
"agent" \| "plan" |
"agent" |
智能体首次运行时的初始对话模式。参见对话模式。 |
LocalAgentOptions¶
本地智能体的配置,通过 Agent.create() 的 local 参数传入。也可作为独立类型导出,供 Partial<LocalAgentOptions> 使用。
| 属性 | 类型 | 默认值 | 描述 |
|---|---|---|---|
cwd |
string |
默认 shell 的主工作目录,并用于确定智能体存储的作用域。 | |
dirs |
string[] |
用于多根工作区的其他工作区文件夹。与 cwd 合并 (去除重复项) ,以便从每个路径加载规则、技能和工作区上下文。 |
|
settingSources |
SettingSource[] |
要加载的全局设置层:"project"、"user"、"team"、"mdm"、"plugins" 或 "all"。 |
|
sandboxOptions |
{ enabled: boolean } |
{ enabled: false } |
沙箱配置。 |
autoReview |
boolean |
false |
通过 Auto-review 模式处理本地工具调用。 |
customTools |
Record<string, SDKCustomTool> |
以 custom-user-tools MCP 服务器形式公开的自定义工具。 |
|
store |
LocalAgentStore |
SDK 默认存储 | 用于持久化的本地智能体存储。 |
enableAgentRetries |
boolean |
true |
为本地智能体运行启用传输错误和停滞时的自动重试。设为 false 可在首次失败时直接显示传输错误。 |
CloudAgentOptions¶
| 属性 | 类型 | 默认值 | 描述 |
|---|---|---|---|
env |
{ type: "cloud"; name?: string } \| { type: "pool"; name?: string } \| { type: "machine"; name?: string } |
{ type: "cloud" } |
执行环境目标。cloud 使用 Cursor 托管的 VM;设置 name 可使用已保存的 Cursor 托管环境。pool 和 machine 会路由到您运行的自托管 worker。若要创建工作区为空且不含仓库的智能体,请省略 repos 并保留 env 的默认值。已命名的 Cursor 托管环境与显式指定的 repos 互斥。 |
repos |
Array<{ url: string; startingRef?: string; prUrl?: string }> |
要克隆到 VM 的仓库。单仓库智能体传入一个条目,多仓库智能体最多可传入 20 个条目。对于无仓库智能体,请省略此项或传入 []。与 Cursor 托管环境中指定的 env.name 互斥。传入 prUrl 可将智能体关联到现有 PR。 |
|
workOnCurrentBranch |
boolean |
false |
将提交推送到现有分支,而不是新分支。 |
autoCreatePR |
boolean |
false |
运行完成时创建 PR。 |
openAsCursorGithubApp |
boolean |
服务账户密钥为 true,用户密钥为 false |
以 Cursor GitHub App 的身份而非 API 密钥所有者的身份创建 PR。解析后的值会在创建、获取和列出时返回。 |
skipReviewerRequest |
boolean |
false |
不请求将调用用户添加为 PR 审阅人。 |
envVars |
Record<string, string> |
云端代理的会话范围环境变量。 | |
metadata |
Record<string, string> |
由调用方拥有、持久保存在云端代理上的 string 标签。请参阅智能体元数据。 |
AgentDefinition¶
| 属性 | 类型 | 默认值 | 描述 |
|---|---|---|---|
description |
string |
必填 | 此子智能体的使用时机。会显示给父智能体,以便其了解何时生成该子智能体。 |
prompt |
string |
必填 | 子智能体的系统提示词。 |
model |
ModelSelection \| "inherit" |
"inherit" |
模型覆盖。传入 "inherit" 以使用父智能体的模型选择。 |
mcpServers |
Array<string \| Record<string, McpServerConfig>> |
此子智能体可用的 MCP 服务器。名称引用父智能体 mcpServers 中的服务器。 |
ModelSelection¶
interface ModelSelection {
id: string;
params?: ModelParameterValue[];
}
interface ModelParameterValue {
id: string;
value: string;
}
id 是模型标识符 (例如 "composer-2.5" 或 "auto-smart") 。params 用于指定模型专属参数,例如 reasoning effort 或 Router 的 optimize_for。使用 Cursor.models.list() 查找您的账户可用的有效 id、参数定义和预设变体。有关 Router 的选择合约,请参阅 Cursor Router。
McpServerConfig¶
type McpServerConfig =
// stdio
| {
type?: "stdio";
command: string;
args?: string[];
env?: Record<string, string>;
cwd?: string; // 仅限本地;云端不支持此字段
}
// HTTP / SSE
| {
type?: "http" | "sse";
url: string;
headers?: Record<string, string>; // 原样传递;可在此处使用 Authorization
auth?: {
CLIENT_ID: string;
CLIENT_SECRET?: string;
scopes?: string[];
};
};
对于在云端运行的 HTTP 服务器,headers 和 auth 由 Cursor 后端处理。敏感字段会在 VM 接收到之前被脱敏。对于在云端运行的 stdio 服务器,env 值会传入 VM (请将其视为运行时机密信息) 。
SDK用户消息¶
interface SDKUserMessage {
text: string;
images?: SDKImage[];
}
agent.send() 消息参数的结构化格式。可用于在发送文本时附带图像。
SDKImage¶
type SDKImage =
| { url: string; dimension?: SDKImageDimension }
| { data: string; mimeType: string; dimension?: SDKImageDimension };
interface SDKImageDimension {
width: number;
height: number;
}
传入远程 url,或包含 mimeType 的 base64 编码 data。
SettingSource¶
type SettingSource =
| "project"
| "user"
| "team"
| "mdm"
| "plugins"
| "all";
控制本地智能体加载哪些存储在磁盘上的设置层。云端代理始终加载 project / team / plugins,并忽略此字段。
| 值 | 来源 |
|---|---|
"project" |
工作区中的 .cursor/ |
"user" |
~/.cursor/ |
"team" |
从仪表盘同步的团队设置 |
"mdm" |
由 MDM 管理的企业版设置 |
"plugins" |
插件提供的设置 |
"all" |
上述所有设置的简写 |
ListResult¶
interface ListResult<T> {
items: T[];
nextCursor?: string;
}
由 Agent.list() 和 Agent.listRuns() 返回。没有更多页时,不会返回 nextCursor。
错误¶
所有 SDK 错误均继承自 CursorSdkError (为保持向后兼容性,以 CursorAgentError 重新导出) 。可使用 isRetryable 控制重试逻辑,并通过 code / status / requestId 进行诊断。
class CursorSdkError extends Error {
readonly isRetryable: boolean;
readonly code?: string; // 稳定的 SDK / 后端错误代码
readonly status?: number; // HTTP 状态码(如有)
readonly cause?: unknown; // 封装的底层错误
readonly endpoint?: string;
readonly requestId?: string;
readonly operation?: string; // 引发该错误的 SDK 操作
}
| 错误类 | 典型消息 | 可能原因 | 推荐解决方法 |
|---|---|---|---|
AuthenticationError |
“无效的 API 密钥” | 缺少或错误的 CURSOR_API_KEY、令牌已过期,或管理员已禁用该密钥。 |
在 API Keys (用户) 或 团队设置 (服务账户) 中生成新密钥。确认该密钥拥有执行此操作的权限。 |
RateLimitError |
“超出速率限制”或 “超出用量限制” | 突发请求限制或每月用量上限。 | 使用指数退避重试 (对于临时情况,SDK 会报告 isRetryable: true) 。若达到每月上限,请提高套餐的用量限制。 |
ConfigurationError |
“模型名称错误”、”不支持 API 密钥”、”不支持该文件” | model.id 无效、缺少必需的 params、工具调用中使用了不受支持的文件,或管理员策略阻止了请求。 |
调用 Cursor.models.list() 确认 ID 和参数。检查仓库和文件路径是否存在。 |
AgentBusyError |
“智能体正忙” | 同一云端代理已有处于 CREATING 或 RUNNING 状态的运行时,又发送了后续请求。 |
等待活跃运行完成、取消该运行,或在再次发送前轮询 Agent.listRuns()。 |
IntegrationNotConnectedError |
“[提供商] 集成未连接” | 为 SCM 提供商尚未连接到 Cursor 团队的仓库创建云端代理。 | 打开 error.helpUrl 重新连接提供商,然后重试。 |
NetworkError |
“服务不可用”、”超时” | 临时后端问题、网络分区或超过截止时间。 | 使用退避策略重试。如需提交支持工单,请查看 error.requestId。 |
UnsupportedRunOperationError |
“此运行时不支持操作 “stream”“ | 调用了当前运行时无法支持的 Run 方法 (例如,对已完成的重新获取的本地运行进行流式处理) 。 |
先通过 run.supports(operation) / run.unsupportedReason(operation) 检查。 |
AgentNotFoundError |
“未找到智能体” | 请求的智能体不存在,或在解析后的本地工作区中不可见。 | 检查智能体 ID、cwd 和 local.store。 |
UnknownAgentError |
由服务器定义的消息 | 未分类的后端或运行时错误。 | 查看 error.code 和 error.cause 以了解底层详情。 |
检查 error.helpUrl¶
某些错误会附带一键解决链接。最常见的是
IntegrationNotConnectedError,但未来更多错误类型也可能添加 helpUrl。
捕获错误时,如果存在 error.helpUrl,请记录并展示给用户。
IntegrationNotConnectedError¶
class IntegrationNotConnectedError extends ConfigurationError {
readonly provider: string; // 例如:"github"、"gitlab"、"azuredevops"
readonly helpUrl: string; // 用于重新连接的仪表盘链接
}
默认错误消息不包含 helpUrl,因此请显式记录:
import { Agent, IntegrationNotConnectedError } from "@cursor/sdk";
try {
await Agent.create({
apiKey: process.env.CURSOR_API_KEY!,
cloud: {
repos: [{ url: "https://github.com/your-org/private-repo" }],
},
});
} catch (err) {
if (err instanceof IntegrationNotConnectedError) {
console.error(err.provider, err.helpUrl);
}
}
AgentBusyError¶
class AgentBusyError extends CursorAgentError {}
对于 agent_busy,isRetryable 为 false。立即重试会一直失败,直到正在运行的任务结束或您将其取消。其他 409 响应 (例如 agent_archived) 则会抛出 ConfigurationError。
等待正在运行的任务完成,使用 run.cancel() 将其取消,或在再次发送前轮询 Agent.listRuns():
import { Agent, AgentBusyError } from "@cursor/sdk";
const agent = await Agent.resume("bc-00000000-0000-0000-0000-000000000001");
try {
await agent.send({ text: "Also add tests for the auth middleware." });
} catch (err) {
if (err instanceof AgentBusyError) {
const runs = await Agent.listRuns(agent.agentId, { runtime: "cloud", limit: 1 });
const active = runs.items[0];
if (active?.status === "running") {
await active.cancel();
}
await agent.send({ text: "Also add tests for the auth middleware." });
return;
}
throw err;
}
本地智能体不会返回 agent_busy。启动新的本地运行前,使用 send({ local: { force: true } }) 终止卡住的本地运行。
UnsupportedRunOperationError¶
class UnsupportedRunOperationError extends ConfigurationError {
readonly operation: RunOperation;
}
当前运行时无法执行某个 Run 操作时抛出。请在调用前使用 run.supports(operation) 和 run.unsupportedReason(operation) 进行检查。
已知限制¶
- 内联
mcpServers不会在Agent.resume()后保留。如有需要,请在恢复时再次传入。 - 自定义工具 (
local.customTools)、Auto-review 模式 (local.autoReview)、自定义存储 (local.store) 和工具集限制 (tools、disallowedTools) 仅适用于本地智能体。云端代理会拒绝local.customTools,并在服务器端持久化。 tools和disallowedTools不会保留在智能体中。请在Agent.resume()时再次传入,以维持这些限制。- 本地智能体尚不支持下载 Artifact (
agent.listArtifacts()返回空列表,agent.downloadArtifact()会抛出异常) 。 local.settingSources(以及它所控制的基于文件的 MCP / 子智能体路径) 不适用于云端代理。云端始终加载project/team/plugins。- 钩子仅支持基于文件的方式 (
.cursor/hooks.json) ,不支持编程式回调。 - SDK 不会自动从本地安装的 Cursor app 中发现凭据。请显式设置
CURSOR_API_KEY(或传入apiKey) ,或通过Cursor.auth.login()创建密钥。 - 本地模式需要 Node.js 22.13 或更高版本,以及平台对 sandbox-helper 的支持。SQLite 后端不可用时,默认存储会回退到
JsonlLocalAgentStore。