The Agent Client Protocol (ACP): A Complete Technical Guide
ACP — the Agent Client Protocol — is an emerging open standard that allows any editor to communicate with any AI coding agent through a unified, JSON‑RPC–based interface. If LSP standardized language servers, ACP is doing the same for AI assistants.
This guide explains ACP from first principles, including architecture, message types, streaming, tool calling, and how to integrate ACP with your own model backends (like a WebSocket → Ollama gateway).
What ACP Is
ACP is a transport‑agnostic protocol that defines how editors and AI agents communicate. It uses JSON‑RPC 2.0 as its message envelope and supports:
- code generation
- code edits
- explanations
- multi-turn chat
- streaming output
- tool calling
- diff application
- file context exchange
ACP is designed to be editor‑agnostic and agent‑agnostic.
Why ACP Exists
Before ACP, every editor had to integrate every AI agent separately:
- VS Code → custom extension
- JetBrains → custom plugin
- Neovim → custom Lua plugin
- Agents → custom APIs for each editor
This created a combinatorial explosion of integrations.
ACP collapses this into a single standard, just like LSP did for language servers.
ACP Architecture
ACP defines a clean separation of concerns:
Editor ⇄ ACP Agent ⇄ Model Backend
- Editor: VS Code, JetBrains, Neovim, etc.
- ACP Agent: A process that speaks JSON‑RPC and orchestrates reasoning, tool calls, and model requests.
- Model Backend: OpenAI, Ollama, vLLM, llama.cpp, or your own gateway.
ACP does not care what model you use — only how messages are exchanged.
JSON‑RPC 2.0 Primer
ACP uses JSON‑RPC 2.0 for all communication.
Request
{
"jsonrpc": "2.0",
"id": 1,
"method": "requestExplanation",
"params": { "code": "func main() {}" }
}
Response
{
"jsonrpc": "2.0",
"id": 1,
"result": "This function does nothing."
}
Notification (no response expected)
{
"jsonrpc": "2.0",
"method": "progress",
"params": { "id": 1, "content": "Thinking..." }
}
ACP uses notifications heavily for streaming.
Core ACP Message Types
ACP defines a set of high-level operations:
Initialization
initializecapabilities
File Operations
openFilecloseFileupdateFile
User Requests
requestCompletionsrequestEditsrequestExplanationrequestActions
Agent Responses
completionResponseeditResponseexplanationResponseactionResponse
Streaming
progress(token-by-token)
Tool Calling
toolCalltoolCallResult
Lifecycle
shutdownexit
Streaming in ACP
ACP supports streaming via progress notifications.
Example:
{
"jsonrpc": "2.0",
"method": "progress",
"params": {
"id": 42,
"content": "First, the program initializes..."
}
}
Editors render these tokens incrementally.
Tool Calling
ACP includes a built‑in tool calling mechanism similar to OpenAI’s function calling.
Agent → Editor
{
"jsonrpc": "2.0",
"method": "toolCall",
"params": {
"id": 99,
"tool": "search",
"arguments": { "query": "raft consensus" }
}
}
Editor → Agent
{
"jsonrpc": "2.0",
"method": "toolCallResult",
"params": {
"id": 99,
"result": "RAFT is a consensus algorithm..."
}
}
This enables:
- file search
- running tests
- executing commands
- reading files
- writing files
Sessions and State
ACP sessions track:
- conversation history
- open files
- cursor position
- project structure
- tool call state
- agent memory
Sessions allow multi-turn reasoning and context retention.
ACP vs LSP
| Feature | LSP | ACP |
|---|---|---|
| Purpose | Language servers | AI coding agents |
| Transport | stdio/TCP | stdio/TCP/WebSocket |
| Streaming | No | Yes |
| Tool calling | No | Yes |
| Multi-turn chat | No | Yes |
| Diff application | Yes | Yes |
| Contextual reasoning | No | Yes |
ACP is essentially LSP + AI + streaming + tool calling.
Building Your Own ACP Agent
An ACP agent is simply:
- A process that reads JSON‑RPC messages from stdin or a socket
- Processes them
- Sends JSON‑RPC responses back
A minimal agent loop looks like:
for {
msg := readJSONRPC()
switch msg.Method {
case "initialize":
sendCapabilities()
case "requestExplanation":
handleExplanation(msg)
case "toolCallResult":
resumeToolCall(msg)
}
}
The agent is responsible for:
- prompt construction
- calling the model backend
- streaming tokens
- handling tool calls
- applying diffs
- managing sessions
ACP + WebSocket Gateway Architecture
If you have a WebSocket → Ollama gateway, your ACP agent becomes the orchestrator:
Editor → ACP Agent → WebSocket Gateway → Ollama
ACP Agent Responsibilities
- Translate ACP requests into prompts
- Send prompts to your WS gateway
- Receive streamed tokens
- Emit
progressnotifications
- Handle tool calls
- Return final ACP responses
Gateway Responsibilities
- Normalize requests
- Call Ollama’s
/api/chatwithstream: true
- Stream tokens back
- Forward tool calls (if your model supports them)
This separation keeps your ACP agent clean and backend‑agnostic.
End-to-End Message Flow
1. Editor → ACP Agent
{
"jsonrpc": "2.0",
"id": 7,
"method": "requestExplanation",
"params": { "code": "func main() {}" }
}
2. ACP Agent → Gateway
{"model":"llama3.1:8b","prompt":"Explain this code: func main() {}"}
3. Gateway → ACP Agent (streaming)
"First, the program..."
"Then it..."
"[DONE]"
4. ACP Agent → Editor (streaming)
{
"jsonrpc": "2.0",
"method": "progress",
"params": { "id": 7, "content": "First, the program..." }
}
5. ACP Agent → Editor (final)
{
"jsonrpc": "2.0",
"id": 7,
"result": {
"explanation": "This Go program defines an empty main function..."
}
}
Conclusion
ACP is the missing glue layer between editors and AI agents. It standardizes:
- communication
- streaming
- tool calling
- diffs
- context exchange
By pairing ACP with a backend‑agnostic gateway (like a WebSocket → Ollama bridge), you can build a universal AI coding agent that works in any editor without custom plugins.
ACP is to AI agents what LSP was to language servers — a unifying protocol that simplifies everything.