A Go implementation of the Agent Client Protocol (ACP) — 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.
- 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:
AgentSideConnectionandClientSideConnectionwrappers with methods matching the ACP interface. - Subprocess transport:
transport.Spawnlaunches 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, andSessionAccumulatorfor common real-world patterns. - Examples: Runnable echo agent, interactive client, and agent+client duet.
The examples/cli directory ships a ready-to-use interactive ACP client powered by the SDK:
# 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=xxxInside the session:
- Type any message and press Enter to prompt the agent
:cancel— cancel the current prompt:exitor:quit— end the session
go get github.com/keepmind9/acp-sdk-gopackage 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{}})
}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)
}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
The schema/ package is auto-generated from the ACP specification. Do not edit generated files directly.
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 GitHubAfter make gen, run make fmt to ensure the generated code is formatted. make all runs both.
make test # all tests with race detector
make test-short # fast tests without race detector
make e2e # end-to-end smoke testContributions are welcome. Please ensure:
make allpasses 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:
feat:,fix:,docs:,refactor:,opt:,chore:
MIT License. See LICENSE.