Nếu bạn đã trả tiền gói Pro cho Cursor mà workflow vẫn quanh quẩn trong cửa sổ editor — thì giống như thuê PT nhưng chỉ chat chữ “khỏe chưa” chứ chưa bao giờ nhờ họ tập thay một buổi cụ thể. Cursor API (qua package @cursor/sdk) là cách nhờ “PT đó” chạy một buổi tập có mệnh lệnh rõ: từ script, CI, hoặc service nhỏ của bạn.
Cursor API và SDK là một “điều khiển từ xa”
SDK TypeScript gói hai chế độ chạy chung một interface: local (đọc đúng thư mục cwd trên máy bạn) và cloud (Cursor clone repo lên VM rồi chạy — hợp khi máy gọi API không nằm cạnh repo). Mình thích nghĩ nó như điều khiển TV: nút bấm giống nhau, nhưng TV ở phòng khách hay TV nhà bạn bè là hai thiết bị khác nhau — bạn phải chọn đúng “phòng” (local hay cloud), không thì tối quen dùng nhầm kênh.
Theo tài liệu chính thức (public beta tại thời điểm mình viết), package là @cursor/sdk; API có thể đổi trước khi GA — nên khóa version trong package.json nếu bạn mang vào CI.
Gói Pro và tiền “usage” chạy API
Điểm nhiều người hỏi mình nhất: gọi SDK có tính tiền riêng một loại “hóa đơn API” không? Theo mô tả billing trên docs Cursor, các run SDK tuân theo cùng pricing, pool request và Privacy Mode như khi bạn chạy agent trong IDE / Cloud Agents — và phần spend hiển thị ở usage dashboard với tag SDK. Thực tế nghĩa là: gói Pro của bạn vẫn là “vé vào cửa + quota theo tài khoản”, còn từng job API là một lần “đổ xăng” vào cùng đồng hồ công tơ đó.
Nếu bạn không mở dashboard usage sau tuần đầu chơi SDK, khả năng cao là tháng sau bạn sẽ hỏi “sao tốn nhanh vậy?” — trong khi đáp án nằm ở chỗ script cron gọi model nặng mỗi phút.
Chuẩn bị: Node, package và API key
- Cài Node LTS, tạo thư mục project,
npm init -y. - Thêm SDK:
npm install @cursor/sdk(khóa version nếu CI). - Lấy User API key tại Cursor Dashboard → Integrations (docs cũng nhắc service account cho team).
- Xuất biến môi trường:
export CURSOR_API_KEY="cursor_..."— hoặc truyền thẳngapiKeyvào code, nhưng đừng commit lên git.
mkdir cursor-sdk-demo && cd cursor-sdk-demo
npm init -y
npm install @cursor/sdk typescript ts-node @types/node --save-dev
npx tsc --init
Với TypeScript thuần, nhớ bật "module" / moduleResolution phù hợp Node hiện đại (hoặc dùng tsx để chạy file .ts cho lẹ).
Ví dụ 1: Một lệnh — Agent.prompt
Khi bạn chỉ cần một câu hỏi, một kết quả, thoát (ví dụ bước CI kiểm tra nhanh), dùng Agent.prompt. Hàm này tạo agent, gửi prompt, chờ xong, và tự dọn dẹp — ít rủi ro quên dispose hơn kiểu tự quản lý vòng đời.
// file: one-shot.ts
import { Agent } from "@cursor/sdk";
async function main() {
const result = await Agent.prompt(
"Đọc file README.md và trả lời một câu: repo này làm gì?",
{
apiKey: process.env.CURSOR_API_KEY!,
model: { id: "composer-2" },
local: { cwd: process.cwd() },
}
);
console.log("status:", result.status);
console.log("result:", result.result);
}
main().catch((e) => {
console.error(e);
process.exit(1);
});
Chạy: CURSOR_API_KEY=... npx tsx one-shot.ts (hoặc biên dịch rồi node dist/one-shot.js). Nếu status là error, đọc transcript/tool output trong dashboard — đó là lỗi agent đã chạy nhưng thất bại, khác với lỗi không khởi động được (mục dưới).
Ví dụ 2: Nhiều lượt — stream rồi hỏi tiếp
Khi bạn muốn in log từng chunk hoặc hỏi tiếp trong cùng ngữ cảnh, dùng await Agent.create(...) rồi agent.send. Luôn await run.wait() sau khi stream (docs nhấn mạnh: bỏ wait() là không biết run kết thúc hay treo). Và luôn await agent[Symbol.asyncDispose]() trong finally — hoặc dùng cú pháp await using nếu môi trường bạn hỗ trợ.
// file: multi-turn.ts
import { Agent } from "@cursor/sdk";
async function main() {
const agent = await Agent.create({
apiKey: process.env.CURSOR_API_KEY!,
model: { id: "composer-2" },
local: { cwd: process.cwd() },
});
try {
const run1 = await agent.send("Liệt kê 3 file TypeScript quan trọng nhất trong src/");
for await (const event of run1.stream()) {
if (event.type === "assistant") {
for (const block of event.message.content) {
if (block.type === "text") process.stdout.write(block.text);
}
}
}
const result1 = await run1.wait();
if (result1.status === "error") {
console.error("run1 failed", result1.id);
process.exitCode = 2;
return;
}
const run2 = await agent.send("Chỉ chọn một file và gợi ý một test case nhỏ cho nó.");
await run2.wait();
} finally {
await agent[Symbol.asyncDispose]();
}
}
main().catch((e) => {
console.error(e);
process.exit(1);
});
Ví dụ 3: Cloud — khi máy gọi không có repo
Pattern tối thiểu: truyền cloud.repos với URL Git và nhánh. Cẩn thận: nếu bạn quên object cloud nhưng lại tưởng mình đang chạy cloud, SDK có thể im lặng chọn local — đọc kỹ options mỗi lần merge PR.
// Gợn ý cấu hình — chỉnh URL + ref thật của bạn
const agent = await Agent.create({
apiKey: process.env.CURSOR_API_KEY!,
model: { id: "composer-2" },
cloud: {
repos: [{ url: "https://github.com/org/repo", startingRef: "main" }],
autoCreatePR: false,
},
});
Trên CI cho cloud agent, docs gợi ý skipReviewerRequest: true nếu bạn không muốn spam bước xin reviewer.
Hai loại lỗi — đừng trộn một lối xử lý
CursorAgentError (ném ra khi gọi) thường là auth, cấu hình, mạng — run chưa kịp chạy. Còn result.status === "error" là agent đã bắt đầu rồi fail giữa chừng — khi đó cần transcript, git state, tool output. Exit code CI nên tách: 1 cho startup, 2 cho run failed, 0 khi finished.
import { Agent, CursorAgentError } from "@cursor/sdk";
try {
const agent = await Agent.create({ /* ... */ });
const run = await agent.send("...");
const result = await run.wait();
if (result.status === "error") process.exit(2);
} catch (err) {
if (err instanceof CursorAgentError) process.exit(1);
throw err;
}
Giới hạn và khi không nên dùng
- Public beta: ký hiệu API / hành vi có thể đổi — đừng embed sâu vào luồng billing khách hàng nếu chưa có lớp tách abstraction.
- Team Admin API keys (theo docs hiện tại) chưa được SDK hỗ trợ — kiểm tra lại bảng compatibility trước khi triển khai enterprise.
- Không thay senior review: SDK mạnh cho automation lặp, không phải chứng chỉ “an toàn production” nếu bạn không có policy test và quyền ghi repo rõ ràng.
Tóm lại — checklist trước khi bật cron
- Key lấy đúng chỗ Integrations; biến môi trường không dính khoảng trắng thừa.
- Chọn đúng
local.cwdhoặccloud.repos— đừng để mặc định làm trái ý bạn. - One-shot ưu tiên
Agent.prompt; multi-turn nhớwait()+dispose. - Tách exit code: startup vs run error để log CI sạch.
- Xem usage dashboard định kỳ — đặc biệt sau khi bật stream nặng.
Nếu bạn mới bắt đầu: hãy clone một repo toy, chạy ví dụ 1 trước, chỉnh prompt để chỉ đọc file không ghi, rồi mới mở quyền sửa code trong sandbox. Phần còn lại (MCP, model params, artifact cloud) đọc tiếp trên docs — mình cố tình giữ bài này mỏng để bạn chạy được trong mười lăm phút.
Chúc anh em code vui! 🚀
Tags: #Cursor #TypeScript #SDK #API #DevOps #khoaphambk

