ACP¶
Anthropic ACP (Agent Client Protocol) implementation -- zero dependencies, stdlib only, Python 3.10+.
Replaces:
acp-python
Overview¶
The ACP module provides a pure-stdlib implementation of Anthropic's Agent Client Protocol for communication between code editors (Clients) and AI coding agents (Agents). It uses JSON-RPC 2.0 over stdio, similar to how LSP standardized language-server integration. It can serve as a drop-in alternative to agent-client-protocol.
| File | Description | Dependencies |
|---|---|---|
acp.py |
Full ACP protocol stack: transport, data types, client, agent framework | None (stdlib only) |
Four layers are provided:
- JsonRpcTransport -- async read/write of newline-delimited JSON-RPC 2.0 messages over
asynciostreams - Protocol Data Types -- dataclass representations of every ACP message and structure (protocol version 1)
- ACPClient -- high-level async client that spawns an agent subprocess, performs handshake, and streams updates
- ACPAgent -- abstract base class for implementing ACP-compatible agents
How to Use in Your Project¶
Just copy the single .py file into your project:
Then import directly:
API Reference¶
Transport¶
| Class | Description |
|---|---|
JsonRpcTransport |
Async JSON-RPC 2.0 over newline-delimited stdio streams |
from acp import JsonRpcTransport
transport = JsonRpcTransport(reader, writer)
await transport.send_request("initialize", params={...})
message = await transport.read_message()
Protocol Data Types¶
Content blocks:
| Class | Description |
|---|---|
TextContent |
Plain text content |
ImageContent |
Base64-encoded image |
AudioContent |
Base64-encoded audio |
ResourceContent |
File content with URI |
ResourceLinkContent |
URI reference to a resource |
Session and initialization:
| Class | Description |
|---|---|
InitializeParams |
Client capabilities and info for handshake |
InitializeResult |
Agent capabilities and info from handshake |
NewSessionParams |
Parameters for creating a new session |
NewSessionResult |
Session ID and metadata |
PromptParams |
Prompt with content blocks |
PromptResult |
Prompt completion result |
Update events (streamed via session/update):
| Class | Description |
|---|---|
AgentMessageChunkUpdate |
Streaming text from agent |
UserMessageChunkUpdate |
Echoed user content |
ThoughtMessageChunkUpdate |
Internal reasoning / chain-of-thought |
ToolCallUpdate |
Tool invocation progress |
ToolCallStatusUpdate |
Tool call status changes |
PlanUpdate |
Plan entries update |
AvailableCommandsUpdate |
Available slash commands |
CurrentModeUpdate |
Session mode change |
ConfigOptionUpdate |
Configuration option change |
SessionInfoUpdate |
Session metadata update |
All data types support to_dict() / from_raw() for JSON serialization:
from acp import TextContent, to_dict, from_raw
content = TextContent(text="Hello!")
wire = to_dict(content) # dict with camelCase keys
restored = from_raw(wire) # reconstruct from dict
ACPClient¶
High-level async client for driving an ACP agent subprocess:
import asyncio
from acp import ACPClient
async def main():
client = ACPClient(["python", "-m", "my_agent"])
await client.start()
# Initialize handshake
init = await client.initialize()
print(f"Agent: {init.agent_info.name}")
# Create a session
session = await client.new_session("/home/user/project")
# Send a prompt and stream updates
async for update in client.prompt(session.session_id, "Hello!"):
print(update)
await client.stop()
asyncio.run(main())
ACPAgent¶
Abstract base class for implementing an ACP agent:
from acp import ACPAgent, TextContent
class MyAgent(ACPAgent):
async def on_initialize(self, params):
return InitializeResult(
protocol_version=1,
agent_capabilities=AgentCapabilities(),
agent_info=ImplementationInfo(name="my-agent", version="1.0"),
)
async def on_new_session(self, params):
return NewSessionResult(session_id="sess-1")
async def on_prompt(self, params):
# Emit streaming updates
await self.emit_update(params.session_id,
AgentMessageChunkUpdate(message_id="msg-1",
delta=TextContent(text="Hello back!")))
return PromptResult()
if __name__ == "__main__":
MyAgent().run()
Enums¶
| Enum | Values |
|---|---|
StopReason |
END_TURN, MAX_TOKENS, STOP_SEQUENCE, TOOL_USE |
ToolKind |
READ, EDIT, EXECUTE, BROWSER, MCP, COMPUTER_USE |
ToolCallStatus |
PENDING, IN_PROGRESS, COMPLETED, FAILED, CANCELLED |
SessionMode |
PLAN, AGENT, EDIT (and more) |
Error Handling¶
| Class | Description |
|---|---|
JsonRpcError |
JSON-RPC error with code and data |
JsonRpcErrorData |
Structured error data |
Usage Examples¶
Client: Send a Prompt¶
import asyncio
from acp import ACPClient
async def main():
async with ACPClient(["python", "my_agent.py"]) as client:
init = await client.initialize()
session = await client.new_session("/path/to/project")
async for update in client.prompt(session.session_id,
"Explain the main function"):
if hasattr(update, 'delta') and hasattr(update.delta, 'text'):
print(update.delta.text, end="")
asyncio.run(main())
Agent: Handle Prompts¶
from acp import (
ACPAgent, AgentCapabilities, ImplementationInfo,
InitializeResult, NewSessionResult, PromptResult,
AgentMessageChunkUpdate, TextContent,
)
class EchoAgent(ACPAgent):
async def on_initialize(self, params):
return InitializeResult(
protocol_version=1,
agent_capabilities=AgentCapabilities(),
agent_info=ImplementationInfo(name="echo", version="0.1"),
)
async def on_new_session(self, params):
return NewSessionResult(session_id="s1")
async def on_prompt(self, params):
text = params.prompt[0].text if params.prompt else ""
await self.emit_update(params.session_id,
AgentMessageChunkUpdate(
message_id="m1",
delta=TextContent(text=f"Echo: {text}"),
))
return PromptResult()
if __name__ == "__main__":
EchoAgent().run()
Notes and Caveats¶
Protocol Version
This implementation targets ACP protocol version 1, as specified in the ACP specification.
- Python version: Requires Python 3.10+ (uses
X | Yunion type hint syntax). - Transport: Uses newline-delimited JSON over stdio (stdin/stdout), like LSP.
- Async only: Both
ACPClientandACPAgentare fully async (asyncio). - Wire format: All field names are automatically converted between
snake_case(Python) andcamelCase(wire format).
Benchmark¶
Benchmarked against agent-client-protocol (Pydantic-based) across three input sizes (small, medium, large) for serialization, deserialization, and JSON round-trip operations.
See ACP Benchmark for detailed results.