cursor-acp
v0.1.0
Published
An adapter that bridges the Cursor CLI agent (`cursor-agent`) to the Agent Client Protocol (ACP). It exposes the Cursor agent over ACP’s ndjson stream so any ACP client can drive it. So far, it can be used in these clients: - Zed - JetBrains (coming soon)
Downloads
172
Readme
Cursor → ACP Adapter
An adapter that bridges the Cursor CLI agent (cursor-agent) to the Agent Client Protocol (ACP). It exposes the Cursor agent over ACP’s ndjson stream so any ACP client can drive it. So far, it can be used in these clients:
- Zed
- JetBrains (coming soon)
- AionUi
- Emacs via agent-shell.el
- marimo notebook
- neovim
- through the CodeCompanion plugin
- through the yetone/avante.nvim plugin
Features
- Prompt streaming: forwards assistant/user chunks from Cursor to ACP in real time
- Tool call mapping:
- Search:
grepToolCall/globToolCall→ ACP tool_call with summaries and filelocations - Execute:
bashToolCall/shellToolCall→ ACP tool_call with output + exit code - Read/Write: map to content blocks and diffs
- Search:
- Modes:
defaultandplanmode, includingcurrent_mode_update - Cancellation: keeps streaming updates, responds with
cancelled, and flushes final updates before resolve - Auth hint: if the Cursor CLI requires auth, emits a helpful login message
Install
- Node.js 18+
- Cursor CLI (
cursor-agent) installed and on PATH
Then:
npm install
npm run buildUsage
Expose the ACP server:
node ./dist/index.jsBy default, the adapter calls cursor-agent on your PATH. To use a specific binary:
export CURSOR_AGENT_EXECUTABLE=/full/path/to/cursor-agentAuthenticate Cursor if needed:
cursor-agent loginIntegration (ACP Client)
Point your ACP client to the adapter’s stdio process. For SDK-based clients, use ndJsonStream with the adapter’s stdin/stdout.
Minimal Client Snippet (Node)
import { spawn } from "node:child_process";
import { Readable, Writable } from "node:stream";
import { ndJsonStream, ClientSideConnection, PROTOCOL_VERSION } from "@agentclientprotocol/sdk";
const env = { ...process.env };
// Optional: target a specific Cursor binary
// env.CURSOR_AGENT_EXECUTABLE = "/usr/local/bin/cursor-agent";
// Start the adapter
const proc = spawn("node", ["./dist/index.js"], { stdio: ["pipe", "pipe", "inherit"], env });
// Wire ACP stream
const input = Writable.toWeb(proc.stdin);
const output = Readable.toWeb(proc.stdout);
const stream = ndJsonStream(input, output);
// Minimal client
class Client { async requestPermission({ options }) { return { outcome: { outcome: "selected", optionId: options?.[0]?.optionId ?? "allow-once" } }; }
async sessionUpdate(u) { if (u.update.sessionUpdate === "agent_message_chunk" && u.update.content.type === "text") console.log(u.update.content.text); } }
const conn = new ClientSideConnection(() => new Client(), stream);
// Drive a simple prompt
const init = await conn.initialize({ protocolVersion: PROTOCOL_VERSION, clientCapabilities: { fs: { readTextFile: true, writeTextFile: true } } });
const { sessionId } = await conn.newSession({ cwd: process.cwd(), mcpServers: [] });
const res = await conn.prompt({ sessionId, prompt: [{ type: "text", text: "Say hello" }] });
console.log("stopReason=", res.stopReason);
proc.kill();Zed
Use this adapter as an External Agent in Zed.
- Build the adapter
cd /path/to/cursor-acp/cursor-acp
npm install
npm run build- Configure Zed (settings.json)
Add an entry under agent_servers pointing to the built adapter:
"agent_servers": {
// ... your other agents
"Cursor": {
"command": "node",
"args": ["/absolute/path/to/cursor-acp/cursor-acp/dist/index.js"],
"env": {
// Optional: only if cursor-agent isn’t on PATH
// "CURSOR_AGENT_EXECUTABLE": "/usr/local/bin/cursor-agent"
}
}
}- Use it in Zed
- Open the Agent panel in Zed.
- Click "+" → New Thread → choose "Cursor".
- If prompted to authenticate, run
cursor-agent loginin a terminal and retry.
Environment
CURSOR_AGENT_EXECUTABLE– optional path to the Cursor binary (defaults tocursor-agent)
Notes
- The adapter forwards tool call updates even after cancellation, then responds with
cancelledas required by ACP. - For shell tools, empty stdout is rendered as “(no output)” and exit code is included when provided.
Development
- Build:
npm run build - Watch:
npm run dev
License
MIT
