TermAl

Feature Reference: Cursor CLI Integration

This document tracks Cursor as a first-class TermAl agent via its ACP (Agent Client Protocol) mode.

Reference: agent-integration-comparison.md

Status

Implemented in TermAl via the shared ACP adapter.

Cursor sessions now support live model discovery, session-scoped /model and /mode controls, Prompt-tab settings, and standard approval handling through the same ACP runtime plumbing that also serves Gemini.

Problem

Cursor CLI is now wired through session creation, runtime spawning, message dispatch, and frontend rendering. The open work is protocol coverage and UX polish, not basic agent support.

Why Cursor fits well

Cursor CLI’s agent acp subcommand exposes a JSON-RPC 2.0 over stdio server that is structurally very close to the Codex app-server adapter TermAl already implements. This means the existing Codex adapter can serve as a near-direct template for the Cursor adapter.

Protocol overview — ACP

Source: https://cursor.com/docs/cli/acp

Transport

Property Value
Transport stdio (stdin/stdout)
Envelope JSON-RPC 2.0
Framing Newline-delimited JSON (one message per line)
Logs stderr (ignored by protocol)

Launch command

cursor agent acp

Authentication

Authenticate before first use with one of:

cursor agent login                    # interactive browser login
cursor agent acp --api-key <key>      # API key
cursor agent acp --auth-token <token> # auth token
# or environment variables:
CURSOR_API_KEY=...
CURSOR_AUTH_TOKEN=...

Lifecycle

Client                                  cursor agent acp
  │                                           │
  │──── initialize ──────────────────────────>│
  │<─── initialize result ───────────────────│
  │                                           │
  │──── authenticate ────────────────────────>│
  │<─── authenticate result ─────────────────│
  │                                           │
  │──── session/new ─────────────────────────>│
  │<─── { sessionId } ──────────────────────│
  │                                           │
  │──── session/prompt ──────────────────────>│
  │<─── session/update (notification) ───────│  (streaming, repeats)
  │<─── session/request_permission ──────────│  (if tool needs approval)
  │──── permission response ─────────────────>│
  │<─── session/update (notification) ───────│  (streaming continues)
  │<─── session/prompt result ───────────────│  { stopReason }
  │                                           │
  │──── session/prompt (next turn) ──────────>│
  │     ...                                   │
  │                                           │
  │──── session/cancel (optional) ───────────>│

Core methods

initialize

Establishes protocol version and capabilities.

// Client → Server
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
    "protocolVersion": "2025-01-01",
    "clientInfo": { "name": "termal", "version": "0.1.0" },
    "clientCapabilities": {
      "fs": { "readTextFile": true, "writeTextFile": true },
      "terminal": true
    }
  }
}

authenticate

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "authenticate",
  "params": { "methodId": "cursor_login" }
}

session/new

Creates a new conversation session.

{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "session/new",
  "params": {
    "cwd": "/projects/my-app",
    "mcpServers": []
  }
}
// Response: { "sessionId": "..." }

session/load

Resumes an existing session by ID.

{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "session/load",
  "params": { "sessionId": "<previous-session-id>" }
}

session/prompt

Sends a user message. Returns when the turn completes.

{
  "jsonrpc": "2.0",
  "id": 4,
  "method": "session/prompt",
  "params": {
    "sessionId": "<session-id>",
    "prompt": [{ "type": "text", "text": "Fix the auth middleware" }]
  }
}
// Response: { "stopReason": "end_turn" }

session/cancel

Interrupts the current turn. This is a JSON-RPC notification, so it has no request id and Cursor does not send a response.

{
  "jsonrpc": "2.0",
  "method": "session/cancel",
  "params": { "sessionId": "<session-id>" }
}

Streaming — session/update notifications

While a prompt is being processed, the server emits JSON-RPC notifications (no id field):

{
  "jsonrpc": "2.0",
  "method": "session/update",
  "params": {
    "sessionUpdate": "agent_message_chunk",
    "content": { "text": "I'll investigate the auth..." }
  }
}

Permission requests — session/request_permission

When a tool requires approval, the server sends a JSON-RPC request:

// Server → Client
{
  "jsonrpc": "2.0",
  "id": 100,
  "method": "session/request_permission",
  "params": {
    "toolName": "edit_file",
    "description": "Edit src/auth.ts",
    "options": ["allow-once", "allow-always", "reject-once"]
  }
}

// Client → Server
{
  "jsonrpc": "2.0",
  "id": 100,
  "result": {
    "outcome": { "outcome": "selected", "optionId": "allow-once" }
  }
}

Extended notification methods

Cursor advertises additional notification methods for richer UX:

Method Purpose
cursor/ask_question Multiple-choice prompts to the user
cursor/create_plan Plan approval flow
cursor/update_todos Progress/task notifications
cursor/task Sub-agent completion events
cursor/generate_image Image output notifications

Agent modes

Cursor supports three modes that can be selected at session creation:

Mode Description
agent Full tool access — reads, writes, commands
plan Read-only planning — proposes changes without executing
ask Q&A only — explores code, answers questions

Mapping to TermAl concepts

Cursor concept TermAl equivalent Notes
session/update agent_message_chunk TextDeltaEvent Streaming text into chat bubble
session/request_permission ApprovalMessage Maps to approve/reject/approve-for-session
session/cancel Turn interrupt Same as Claude’s control_request interrupt
session/load Session resume Like --resume for Claude, thread/resume for Codex
cursor/update_todos Could map to a new message type Optional enhancement
Modes (agent/plan/ask) New Cursor-specific session setting Surface in session creation UI

Backend tasks

Enum extensions

Add a Cursor variant to each of these enums in src/main.rs:

New types

New functions

Existing function changes

Frontend tasks

Testing

Open questions

  1. Exact session/update payload shapes — need to test against a live cursor agent acp process to catalogue all sessionUpdate variants beyond agent_message_chunk (file diffs, command executions, thinking, etc.).
  2. Image attachment support — does session/prompt accept image content blocks?
  3. MCP server passthrough — should TermAl forward its own MCP config to Cursor via the mcpServers param, or let Cursor use its own .cursor/mcp.json?
  4. Cloud handoff — Cursor supports pushing a session to the cloud via & prefix. Should TermAl surface this capability?
  5. Subscription model — Cursor uses its own subscription (not a raw API key). How does this affect multi-user or team scenarios?