ACP¶
Pegg implements the Agent Client Protocol (ACP): a JSON-RPC 2.0 protocol for driving a coding agent from an editor or other client. The server exposes the same orchestrator used by the CLI and TUI.
Starting the server¶
Newline-delimited JSON-RPC on stdin/stdout. This is the transport used by editor integrations that spawn the agent as a subprocess.
Host and port come from --host / --port or the server config block. The ACP
handler does not apply the HTTP API's auth or CORS middleware.
Protocol basics¶
- Every message is a JSON-RPC 2.0 envelope.
"jsonrpc"must be exactly"2.0". - Batch requests are not supported.
- A message with
methodand noidis a notification: handlers run, no response is written. - A message with
idand nomethodis a response to a server-initiated request.
Error codes¶
| Code | Meaning |
|---|---|
-32700 |
Parse error |
-32600 |
Invalid request |
-32601 |
Method not found |
-32602 |
Invalid params |
-32603 |
Internal error |
-32800 |
Cancelled |
Methods¶
| Method | Params | Result |
|---|---|---|
initialize |
protocolVersion, clientCapabilities?, clientInfo? |
Capabilities and agent info |
session/new |
cwd (required), mcpServers?, additionalDirectories? |
{ "sessionId": "..." } |
session/load |
sessionId, cwd, mcpServers?, additionalDirectories? |
null (replays history as notifications) |
session/resume |
same as session/load |
{} (no replay) |
session/prompt |
sessionId, prompt |
{ "stopReason": "..." } |
session/delete |
sessionId |
null |
session/close |
sessionId |
{} |
session/list |
— | { "sessions": [...], "nextCursor": "" } |
session/cancel |
sessionId |
no response |
$/cancelRequest |
— | no response (no-op) |
Unknown methods return -32601.
initialize¶
protocolVersion is required and must be at least 1. The result advertises what
the agent supports:
{
"protocolVersion": 1,
"agentCapabilities": {
"loadSession": true,
"promptCapabilities": { "image": true, "audio": false, "embeddedContext": true },
"mcpCapabilities": { "http": true, "sse": false },
"sessionCapabilities": {
"delete": {},
"resume": {},
"close": {},
"additionalDirectories": {}
}
},
"agentInfo": { "name": "pegg", "title": "Pegg AI Agent", "version": "0.1.0" },
"authMethods": []
}
session/new¶
Creates a new ACP session. cwd is required. Pegg also creates a persisted
session (titled ACP Session) in its session store and links the two.
Warning
The sessionId returned by session/new is the ACP session id, not the
persisted Pegg session id. session/load and session/resume, however,
expect the Pegg session id.
mcpServers and additionalDirectories are accepted but currently ignored.
session/prompt¶
Only the first text content block is used as the agent input. While the agent
runs, the server pushes session/update notifications (see below). The result's
stopReason is one of:
| Stop reason | Meaning |
|---|---|
end_turn |
The agent finished normally |
cancelled |
The prompt was cancelled via session/cancel |
error |
The run failed |
{ "jsonrpc": "2.0", "id": 2, "method": "session/prompt",
"params": { "sessionId": "9d1f...", "prompt": [{ "type": "text", "text": "Fix the failing test" }] } }
session/list¶
Lists persisted sessions (page 1, size 50, newest first). nextCursor is currently
always empty.
{
"sessions": [
{ "id": "3f2b...", "title": "Fix parser bug",
"createdAt": "2026-09-10T09:15:00Z", "updatedAt": "2026-09-10T09:42:11Z" }
],
"nextCursor": ""
}
Server notifications¶
The server pushes session/update notifications:
{
"jsonrpc": "2.0",
"method": "session/update",
"params": {
"sessionId": "9d1f...",
"update": { "sessionUpdate": "agent_message_chunk", "messageId": "…",
"content": { "type": "text", "text": "Working on it" } }
}
}
sessionUpdate |
Emitted when |
|---|---|
user_message_chunk |
Replaying a stored user message (session/load) |
agent_message_chunk |
Streaming assistant text |
tool_call |
A tool call starts (toolCallId, title, kind: "other", status: "pending") |
tool_call_update |
A tool call completes (toolCallId, status: "completed") |
usage_update |
After a run with token usage (used, size, cost) |
When the context is compacted mid-run, an agent_message_chunk notification is
emitted with the text [Context compacted (<strategy>) — <messages> messages,
<tokens> tokens].
The usage_update payload uses a fixed size of 200000 and a USD cost object.
Transports¶
Write one JSON-RPC message per line to stdin; read newline-delimited JSON from stdout. Empty lines are ignored. Closing stdin ends the server cleanly.
The flow is:
POSTaninitializerequest without a connection header. The response is200with anAcp-Connection-Idheader and the initialize result.GETthe same URL withAccept: text/event-streamand theAcp-Connection-Idheader to open a long-lived event stream.POSTfurther JSON-RPC requests withAcp-Connection-Id. The server returns202 Acceptedimmediately; the JSON-RPC response arrives on the SSE stream.DELETEwithAcp-Connection-Idto close the connection.
| Header | Purpose |
|---|---|
Acp-Connection-Id |
Connection identifier (returned by initialize) |
Acp-Session-Id |
Routes responses/notifications to a per-session SSE stream |
Status codes: 415 for a missing JSON content type, 400 for an invalid or
missing connection, 404 for an unknown connection, 406 when the stream
request lacks Accept: text/event-stream, 405 for unsupported methods.
# 1. initialize
curl -i http://127.0.0.1:8080/acp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":1,"clientInfo":{"name":"demo","version":"1.0"}}}'
# 2. open the event stream (long-lived)
curl -N http://127.0.0.1:8080/acp \
-H 'Accept: text/event-stream' \
-H 'Acp-Connection-Id: <uuid>'
A GET with Upgrade: websocket establishes a WebSocket connection; JSON-RPC
responses are sent as text frames on the same socket.
Note
In pure WebSocket mode, session/update notifications are only delivered if
a matching SSE stream is also open. Use HTTP+SSE for full notification
support.
Complete example (stdio)¶
→ {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":1}}
← {"jsonrpc":"2.0","id":0,"result":{"protocolVersion":1,"agentCapabilities":{...},"agentInfo":{...}}}
→ {"jsonrpc":"2.0","id":1,"method":"session/new","params":{"cwd":"/home/user/project"}}
← {"jsonrpc":"2.0","id":1,"result":{"sessionId":"9d1f..."}}
→ {"jsonrpc":"2.0","id":2,"method":"session/prompt","params":{"sessionId":"9d1f...","prompt":[{"type":"text","text":"List the Go files"}]}}
← {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"9d1f...","update":{"sessionUpdate":"agent_message_chunk","content":{"type":"text","text":"Here are"}}}}
← {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"9d1f...","update":{"sessionUpdate":"tool_call","toolCallId":"…","title":"glob","kind":"other","status":"pending"}}}
← {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"9d1f...","update":{"sessionUpdate":"tool_call_update","toolCallId":"…","status":"completed"}}}
← {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"9d1f...","update":{"sessionUpdate":"usage_update","used":1540,"size":200000,"cost":{"amount":0.004,"currency":"USD"}}}}
← {"jsonrpc":"2.0","id":2,"result":{"stopReason":"end_turn"}}
Cancellation¶
Send session/cancel with the session id to abort a running prompt. The running
agent's context is cancelled and the in-flight session/prompt resolves with
stopReason: "cancelled". No response is written for the cancel message itself.
$/cancelRequest is accepted but does nothing.
Outbound requests¶
The server can also call methods on the client (fs/read_text_file,
fs/write_text_file, session/request_permission). The plumbing is implemented, but
current tools do not invoke it, so clients do not need to handle these methods yet.