cursor-docs/latest/content · Jun 26, 20:20 UTC
pages/cli/acp.txt
TXT10.4 KB267 lines
route: /docs/cli/acp
title: ACP
description: Use Agent Client Protocol (ACP) with Cursor CLI to run `agent acp` as a protocol server for custom clients.
ACP
Overview
Cursor CLI supports ACP (Agent Client Protocol) for advanced integrations. You can run agent acp and connect a custom client over stdio using JSON-RPC.
Learn more in the official Agent Client Protocol docs.
ACP is intended for building custom clients and integrations. For normal terminal
workflows, use the interactive CLI with agent.
Start ACP server
Start Cursor CLI in ACP mode:
agent acp
Transport and message format
Transport: stdio
Protocol envelope: JSON-RPC 2.0
Framing: newline-delimited JSON (one message per line)
Direction:
Client writes requests/notifications to stdin
Cursor CLI writes responses/notifications to stdout
Logs may be written to stderr
Request flow
Typical ACP session flow:
initialize
authenticate with methodId: "cursor_login"
session/new (or session/load)
session/prompt
Handle session/update notifications while the model streams output
Handle session/request_permission by returning a decision
Optionally send session/cancel
Authentication
Cursor CLI advertises cursor_login as the ACP auth method. In practice, you can pre-authenticate before startup using existing CLI auth paths:
agent login
--api-key (or CURSOR_API_KEY)
--auth-token (or CURSOR_AUTH_TOKEN)
You can also pass endpoint and TLS options from the root CLI command:
agent --api-key "$CURSOR_API_KEY" acp
agent -e https://api2.cursor.sh acp
agent -k acp
Sessions, modes, and permissions
Sessions
Create a session with session/new
Resume an existing conversation with session/load
Modes
ACP sessions support the same core modes as CLI:
agent (full tool access)
plan (planning, read-only behavior)
ask (Q&A/read-only behavior)
Permissions
When tools need approval, Cursor sends session/request_permission. Clients should return one of:
allow-once
allow-always
reject-once
If your client does not answer permission requests, tool execution can block.
MCP servers
ACP supports MCP servers defined in a project-level or user-level .cursor/mcp.json. Launch agent from your project directory and approve the servers you want to use.
Team-level MCP servers configured through the Cursor dashboard are not supported in ACP mode.
Cursor extension methods
Cursor sends ACP extension methods for richer client UX. There are two types:
Blocking methods (cursor/ask_question, cursor/create_plan): The agent waits for a response before continuing. Your client must reply with a JSON-RPC response.
Notification methods (cursor/update_todos, cursor/task, cursor/generate_image): The agent sends these as fire-and-forget notifications. Your client can display them but doesn't need to respond.
Method
Type
Use
cursor/ask_question
Blocking
Ask users multiple-choice questions
cursor/create_plan
Blocking
Request explicit plan approval
cursor/update_todos
Notification
Notify client about todo state updates
cursor/task
Notification
Notify client about subagent task completion
cursor/generate_image
Notification
Notify client about generated image output
cursor/ask_question
Present multiple-choice questions to the user. The agent blocks until the client responds.
Request:
interface CursorAskQuestionRequest {
toolCallId: string;
title?: string;
questions: Array<{
id: string;
prompt: string;
options: Array<{ id: string; label: string }>;
allowMultiple?: boolean;
}>;
}
Response:
interface CursorAskQuestionResponse {
outcome:
| {
outcome: "answered";
answers: Array<{
questionId: string;
selectedOptionIds: string[];
}>;
}
| { outcome: "skipped"; reason?: string }
| { outcome: "cancelled" };
}
Example request:
{
"toolCallId": "call_123",
"title": "Need input",
"questions": [
{
"id": "q1",
"prompt": "Which mode should I use?",
"options": [
{ "id": "agent", "label": "Agent" },
{ "id": "plan", "label": "Plan" }
],
"allowMultiple": false
}
]
}
cursor/create_plan
Request plan approval from the user. The agent blocks until the client accepts or rejects the plan.
Request:
interface CursorCreatePlanRequest {
toolCallId: string;
name?: string;
overview?: string;
plan: string;
todos: Array<{
id: string;
content: string;
status: "pending" | "in_progress" | "completed" | "cancelled";
}>;
isProject?: boolean;
phases?: Array<{
name: string;
todos: Array<{
id: string;
content: string;
status: "pending" | "in_progress" | "completed" | "cancelled";
}>;
}
plan: A markdown string describing the full plan.
phases: Optional grouping of todos into named phases for larger plans.
Response:
interface CursorCreatePlanResponse {
outcome:
| { outcome: "accepted"; planUri?: string }
| { outcome: "rejected"; reason?: string }
| { outcome: "cancelled" };
}
Example request:
{
"toolCallId": "call_124",
"name": "Refactor tabs layout",
"overview": "Tighten layout behavior and preserve existing UX.",
"plan": "1. Inspect current tab sizing logic.\n2. Update layout calculations.\n3. Verify editor behavior.",
"todos": [
{ "id": "todo-1", "content": "Inspect current tab sizing logic", "status": "completed" },
{ "id": "todo-2", "content": "Update layout calculations", "status": "in_progress" },
{ "id": "todo-3", "content": "Verify editor behavior", "status": "pending" }
],
"isProject": false
}
cursor/update_todos
Update the client's todo list. Sent as a notification; no response required.
Request:
interface CursorUpdateTodosRequest {
toolCallId: string;
todos: Array<{
id: string;
content: string;
status: "pending" | "in_progress" | "completed" | "cancelled";
}>;
merge: boolean;
}
merge: When true, merge these todos into the existing list. When false, replace the entire list.
Response:
interface CursorUpdateTodosResponse {
outcome:
| {
outcome: "accepted";
todos: Array<{
id: string;
content: string;
status: "pending" | "in_progress" | "completed" | "cancelled";
}>;
}
| { outcome: "rejected"; reason?: string }
| { outcome: "cancelled" };
}
Example request:
{
"toolCallId": "call_125",
"todos": [
{ "id": "1", "content": "Set up project structure", "status": "completed" },
{ "id": "2", "content": "Add authentication", "status": "in_progress" },
{ "id": "3", "content": "Write unit tests", "status": "pending" }
],
"merge": true
}
cursor/task
Notify the client about a subagent task. Sent as a notification; no response required.
Request:
interface CursorTaskRequest {
toolCallId: string;
description: string;
prompt: string;
subagentType:
| "unspecified"
| "computer_use"
| "explore"
| "video_review"
| "browser_use"
| "shell"
| "vm_setup_helper"
| { custom: string };
model?: string;
agentId?: string;
durationMs?: number;
}
subagentType: The type of subagent to run. Use { custom: "your_type" } for custom subagent types.
agentId: Set this to resume a previously created subagent.
durationMs: How long the task ran, included in the response.
Response:
interface CursorTaskResponse {
outcome:
| { outcome: "completed"; agentId?: string; durationMs?: number }
| { outcome: "rejected"; reason?: string }
| { outcome: "cancelled" };
}
Example request:
{
"toolCallId": "call_126",
"description": "Explore codebase",
"prompt": "Find where authentication is handled and report the file paths.",
"subagentType": "explore"
}
cursor/generate_image
Notify the client about a generated image. Sent as a notification; no response required.
Request:
interface CursorGenerateImageRequest {
toolCallId: string;
description: string;
filePath?: string;
referenceImagePaths?: string[];
}
filePath: Suggested file path for the generated image.
referenceImagePaths: Paths to reference images used as input.
Response:
interface CursorGenerateImageResponse {
outcome:
| { outcome: "generated"; filePath: string; imageData?: string }
| { outcome: "rejected"; reason?: string }
| { outcome: "cancelled" };
}
Example request:
{
"toolCallId": "call_127",
"description": "Minimal flat app icon for a note-taking app",
"filePath": "/tmp/icon.png",
"referenceImagePaths": ["/tmp/reference.png"]
}
Minimal Node.js client
This example shows the minimum control flow for a cust
…