README.md

# Agent Client Protocol (ACP) for Elixir

Elixir implementation of the [Agent Client Protocol](https://agentclientprotocol.com/) — a standardized protocol for communication between code editors (clients) and AI coding agents over JSON-RPC 2.0.

This library is a port of the Rust [`agent-client-protocol`](https://crates.io/crates/agent-client-protocol) and [`agent-client-protocol-schema`](https://crates.io/crates/agent-client-protocol-schema) crates.

## Installation

Add `agent_client_protocol` to your list of dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:agent_client_protocol, "~> 0.1.0"}
  ]
end
```

## Overview

The library has two layers:

### Schema Types

All ACP message types with JSON serialization (`to_json/1` and `from_json/1`):

- **Content** — `ContentBlock`, `TextContent`, `ImageContent`, `AudioContent`, `ResourceLink`, `EmbeddedResource`
- **Tool Calls** — `ToolCall`, `ToolCallUpdate`, `ToolKind`, `ToolCallStatus`, `ToolCallContent`
- **Plans** — `Plan`, `PlanEntry`
- **Agent Types** — `InitializeRequest/Response`, `AuthenticateRequest/Response`, `NewSessionRequest/Response`, `LoadSessionRequest/Response`, `SetSessionModeRequest/Response`, `PromptRequest/Response`, `CancelNotification`
- **Client Types** — `SessionNotification`, `SessionUpdate`, `RequestPermissionRequest/Response`, `WriteTextFileRequest/Response`, `ReadTextFileRequest/Response`, terminal operations
- **Capabilities** — `AgentCapabilities`, `ClientCapabilities`, `SessionCapabilities`, `PromptCapabilities`, `McpCapabilities`
- **MCP Servers** — `McpServer` (tagged union: Stdio, Http, Sse)
- **Extensions** — `ExtRequest`, `ExtResponse`, `ExtNotification`
- **Unstable** — `SessionModelState`, `SessionConfigOption`, `ForkSessionRequest/Response`, `ResumeSessionRequest/Response`, `ListSessionsRequest/Response`, `ConfigOptionUpdate`, `SessionInfoUpdate`, `MaybeUndefined`

### SDK Layer

- **`ACP.Agent`** — Behaviour for implementing an ACP agent with callbacks for `initialize`, `authenticate`, `new_session`, `prompt`, `cancel`, etc.
- **`ACP.Client`** — Behaviour for implementing an ACP client with callbacks for `request_permission`, `session_notification`, file/terminal operations, etc.
- **`ACP.Connection`** — GenServer for bidirectional JSON-RPC 2.0 over IO streams with request/response correlation
- **`ACP.ClientSideConnection`** / **`ACP.AgentSideConnection`** — High-level connection wrappers
- **`ACP.Side`** — Behaviour with `ClientSide`/`AgentSide` implementations for message routing and decoding
- **`ACP.StreamBroadcast`** — PubSub GenServer for observing protocol messages

## Usage

### Implementing an Agent

```elixir
defmodule MyAgent do
  use ACP.Agent

  @impl true
  def initialize(request) do
    {:ok, ACP.InitializeResponse.new(ACP.ProtocolVersion.v1())}
  end

  @impl true
  def authenticate(_request) do
    {:ok, ACP.AuthenticateResponse.new()}
  end

  @impl true
  def new_session(request) do
    {:ok, ACP.NewSessionResponse.new("session-#{System.unique_integer()}")}
  end

  @impl true
  def prompt(request) do
    # Process the prompt and send session updates...
    {:ok, ACP.PromptResponse.new(:end_turn)}
  end

  @impl true
  def cancel(_notification), do: :ok
end
```

### Implementing a Client

```elixir
defmodule MyClient do
  use ACP.Client

  @impl true
  def request_permission(request) do
    # Present permission options to the user...
    option = hd(request.options)
    {:ok, ACP.RequestPermissionResponse.new({:selected, ACP.SelectedPermissionOutcome.new(option.option_id)})}
  end

  @impl true
  def session_notification(notification) do
    IO.inspect(notification.update, label: "Session update")
    :ok
  end
end
```

### Working with Types

```elixir
# Create and serialize a content block
text = ACP.TextContent.new("Hello, world!")
json = ACP.ContentBlock.to_json({:text, text})
# => %{"type" => "text", "text" => "Hello, world!"}

# Deserialize
{:ok, {:text, decoded}} = ACP.ContentBlock.from_json(json)

# Session updates
update = {:agent_message_chunk, ACP.ContentChunk.new({:text, ACP.TextContent.new("Hi")})}
json = ACP.SessionUpdate.to_json(update)

# JSON-RPC messages
request = %ACP.RPC.Request{id: 1, method: "initialize", params: %{"protocolVersion" => 1}}
encoded = ACP.RPC.JsonRpcMessage.encode!(request)
```

## Protocol Compatibility

This library targets ACP schema version 0.10.6. All stable types are fully implemented. Unstable features (session fork/resume/list, model selection, config options) are included but may change with future protocol versions.

## License

MIT