# ACP SDK for Go

A Go implementation of the [Agent Client Protocol (ACP)](https://agentclientprotocol.com/) — a JSON-RPC 2.0 protocol over stdio for communication between AI agents and their clients (editors, IDEs).

> Build ACP-compliant agents and clients in Go with strongly-typed schema models, stdio transport, helper builders, and runnable examples.

## Features

- **Schema parity:** Type-safe Go structs for every ACP schema type, including discriminated unions for `ContentBlock`, `SessionUpdate`, `ToolCall`, and more.
- **Bidirectional JSON-RPC 2.0:** Full request/response over newline-delimited stdio, with notification support for streaming updates.
- **Typed connections:** `AgentSideConnection` and `ClientSideConnection` wrappers with methods matching the ACP interface.
- **Subprocess transport:** `transport.Spawn` launches agent/client subprocesses with clean environment, configurable timeouts, and graceful shutdown.
- **Helper builders:** `helpers.AgentMessageUpdate`, `helpers.ToolCallStartUpdate`, `helpers.ToolCallProgressUpdate`, and more for ergonomic content construction.
- **Contrib utilities:** `ToolCallTracker`, `PermissionBroker`, and `SessionAccumulator` for common real-world patterns.
- **Examples:** Runnable echo agent, interactive client, and agent+client duet.

## Interactive CLI

The `examples/cli` directory ships a ready-to-use interactive ACP client powered by the SDK:

```bash
# Build
go build -o acp-cli ./examples/cli

# Run a session with a local agent
./acp-cli run -- ./examples/agent

# Run with any ACP-compatible agent binary
./acp-cli run -- claude-agent-acp

# Pass environment variables and set working directory
./acp-cli run -- claude-agent-acp --work-dir /project --env API_KEY=xxx
```

Inside the session:

- Type any message and press Enter to prompt the agent
- `:cancel` — cancel the current prompt
- `:exit` or `:quit` — end the session

## Install

```bash
go get github.com/keepmind9/acp-sdk-go
```

## Quick Start

### Run an Agent

```go
package main

import (
    "context"
    "log"

    "github.com/keepmind9/acp-sdk-go/agent"
    "github.com/keepmind9/acp-sdk-go/core"
    "github.com/keepmind9/acp-sdk-go/schema"
)

type MyAgent struct {
    *agent.Base
}

func (a *MyAgent) Initialize(_ context.Context, req *schema.InitializeRequest) (*schema.InitializeResponse, error) {
    pv := schema.ProtocolVersion(1)
    return &schema.InitializeResponse{
        ProtocolVersion:   &pv,
        AgentCapabilities: &schema.AgentCapabilities{},
        AgentInfo:         &schema.Implementation{Name: "my-agent", Version: "0.1.0"},
    }, nil
}

func (a *MyAgent) NewSession(_ context.Context, req *schema.NewSessionRequest) (*schema.NewSessionResponse, error) {
    return &schema.NewSessionResponse{}, nil
}

func (a *MyAgent) Prompt(_ context.Context, req *schema.PromptRequest) (*schema.PromptResponse, error) {
    return &schema.PromptResponse{}, nil
}

func main() {
    core.RunAgent(&MyAgent{Base: &agent.Base{}})
}
```

### Connect a Client

```go
package main

import (
    "context"
    "log"

    "github.com/keepmind9/acp-sdk-go/client"
    "github.com/keepmind9/acp-sdk-go/core"
    "github.com/keepmind9/acp-sdk-go/schema"
    "github.com/keepmind9/acp-sdk-go/transport"
)

type MyClient struct {
    *client.Base
}

func (c *MyClient) SessionUpdate(_ context.Context, notif *schema.SessionNotification) error {
    if notif.Update.AgentMessageChunk != nil {
        log.Printf("session update: %s", notif.Update.AgentMessageChunk.Content.Text.Text)
    }
    return nil
}

func main() {
    subprocess, err := transport.Spawn("my-agent-binary")
    if err != nil {
        log.Fatal(err)
    }
    defer subprocess.Close()

    conn := core.ConnectToAgent(&MyClient{Base: &client.Base{}}, subprocess)

    pv := schema.ProtocolVersion(1)
    resp, err := conn.Initialize(&schema.InitializeRequest{
        ProtocolVersion:    &pv,
        ClientCapabilities: &schema.ClientCapabilities{},
    })
    if err != nil {
        log.Fatal(err)
    }
    log.Printf("connected: agent %s", resp.AgentInfo.Name)
}
```

## Project Layout

```
acp-sdk-go/
├── agent/          # Agent-side SDK: Agent interface, AgentSideConnection, router
├── client/         # Client-side SDK: Client interface, ClientSideConnection, router
├── contrib/        # High-level utilities: ToolCallTracker, PermissionBroker, SessionAccumulator
├── core/           # Convenience helpers: RunAgent, ConnectToAgent
├── helpers/        # Factory functions for ContentBlock, SessionUpdate, ToolCall
├── rpc/            # JSON-RPC 2.0 engine: Connection, Handler, Router, DecodeResponse
├── schema/         # All ACP data types, constants, discriminated unions
├── transport/      # Stdio transport and subprocess management
└── examples/       # Runnable examples: agent, client, duet, echo_agent
```

## Schema Code Generation

The `schema/` package is auto-generated from the ACP specification. Do not edit generated files directly.

```bash
make gen          # download latest schema + regenerate all schema/*.go
make gen-schema   # regenerate from local schema.json (no download)
make gen-download # download schema.json and meta.json from GitHub
```

After `make gen`, run `make fmt` to ensure the generated code is formatted. `make all` runs both.

## Testing

```bash
make test        # all tests with race detector
make test-short  # fast tests without race detector
make e2e         # end-to-end smoke test
```

## Contributing

Contributions are welcome. Please ensure:

- `make all` passes before opening a PR (build, fmt, vet, test)
- New code includes table-driven tests with `testify`
- Public APIs have doc comments
- Commits follow [Conventional Commits](https://www.conventionalcommits.org/): `feat:`, `fix:`, `docs:`, `refactor:`, `opt:`, `chore:`

## License

MIT License. See [LICENSE](LICENSE).
