agent-client-protocol 5.0.0

Core protocol types and traits for the Agent Client Protocol
Documentation

agent-client-protocol

Core protocol types and traits for the Agent Client Protocol (ACP).

ACP is a protocol for communication between AI agents and their clients (IDEs, CLIs, etc.), enabling features like tool use, permission requests, and streaming responses.

What can you build with this crate?

  • Clients that talk to ACP agents (like building your own Claude Code interface)
  • Proxies that add capabilities to existing agents (like adding custom tools via MCP)
  • Agents that respond to prompts with AI-powered responses

Quick Start: Connecting to an Agent

The most common use case is connecting to an existing ACP agent as a client. This quick start uses stable protocol v1 and the opt-in process feature. Add it to Cargo.toml:

agent-client-protocol = { version = "5", features = ["process"] }
use agent_client_protocol::{AcpAgent, Client, Result};
use agent_client_protocol::{schema::ProtocolVersion, v1::schema::InitializeRequest};

# async fn connect() -> Result<()> {
let agent = AcpAgent::from_args(["my-agent"])?;
Client.v1()
    .name("my-client")
    .connect_with(agent, async |cx| {
        // Initialize the connection
        cx.send_request(InitializeRequest::new(ProtocolVersion::V1))
            .block_task()
            .await?;

        Ok(())
    })
    .await
# }

Every connection names its protocol version. Client.v1(), Agent.v1(), and Proxy.v1() start stable v1 connections; their types (ConnectionTo, Builder, Responder, SessionBuilder, ActiveSession, ...) are in agent_client_protocol::v1. Draft protocol v2 is opt-in through unstable_protocol_v2, and agent_client_protocol::v2 uses the same names for its connection, builder and request handles. The session APIs differ: v1::ActiveSession reads updates from its own stream, while Client.v2(), Agent.v2(), and Proxy.v2() callbacks receive a version-typed v2::ConnectionTo with high-level, command-only session helpers because prompt acceptance and inbound traffic are independent. Session updates and interactive requests use typed connection handlers. See Protocol V2. Use Proxy.protocol_router() to expose separate strict v1 and v2 proxy implementations as one component; custom raw routing infrastructure can use Builder::new(Proxy), which applies no protocol-version guard.

Runnable draft-v2 pair

The simple_agent_v2 example implements the complete baseline session lifecycle, and v2_one_shot_client demonstrates the split prompt lifecycle: the prompt response acknowledges acceptance, while output and completion arrive through session/update notifications.

cargo build -p agent-client-protocol \
  --features process,stdio,unstable_protocol_v2 \
  --examples

./target/debug/examples/v2_one_shot_client \
  --command ./target/debug/examples/simple_agent_v2 \
  "Hello from ACP v2"

See the Runnable Protocol V2 Quickstart for the lifecycle invariants to preserve when adapting these examples.

Choosing request publication

send_request(request) sends immediately, with unchanged behavior. prepare_request(request) waits until a consuming method is called:

connection.prepare_request(request).on_receiving_result(async move |result| {
    // Enqueue or apply bounded work before later inbound messages.
    handle_result(result)?;
    Ok(())
})?;

Preparation installs callback ordering before publication, avoiding a race with fast responses. block_task() and detach() instead select unordered consumption and send during the method call; dropping an unconsumed prepared request sends nothing. Raw, v1 and v2 connections also offer prepare_request_to(peer, request).

See Sending Requests for ordering limits, cancellation, errors, and migration guidance.

Cargo Features

No features are enabled by default. The protocol engine, serialization, clients, agents, proxies, custom MCP servers, and generic Channel, Lines, and ByteStreams adapters do not require native I/O or JSON Schema generation.

Feature Provides
process Native subprocess support through AcpAgent and AcpAgentConfig
stdio The native Stdio adapter
schemars JsonSchema implementations and typed MCP tool helpers
wasm_js UUID randomness for JavaScript-hosted WebAssembly

process and stdio are independent. Native LineDirection is available with either. The McpTool trait, McpToolRegistry and its metadata types, and the mcp_server::tool_fn / mcp_server::tool_fn_mut functions require schemars. Unstable protocol features remain independent opt-ins.

agent-client-protocol-rmcp explicitly enables schemars for its tool builders. See Cargo Features for configuration and migration examples. No features have been enabled by default since 3.0. When upgrading from 2.x, enable the features your application uses explicitly; a lean dependency no longer needs default-features = false.

Stability

  • The stable API, everything that is not behind an unstable_* feature, follows semver: it changes incompatibly only in a major release.
  • Everything behind an unstable_* feature may change in a minor release. This includes all of agent_client_protocol::v2 and the other items that unstable_protocol_v2 enables. Stabilizing protocol v2 only removes the unstable_protocol_v2 gate.
  • This crate depends on one exact version of agent-client-protocol-schema (an = requirement), and a minor release may move that pin to a newer schema release. Use each version's message types through agent_client_protocol::v1::schema and agent_client_protocol::v2::schema, and the version-neutral items such as ProtocolVersion through agent_client_protocol::schema, instead of depending on agent-client-protocol-schema directly. A direct dependency must allow the exact version this crate pins, or Cargo cannot resolve the dependency graph.
  • All SDK crates share one version number, so moving a public dependency of any of them to a new major version, such as axum or reqwest in agent-client-protocol-http, is a major release of all of them.

MCP Server Attachment

The runtime-agnostic mcp_server module can build and directly serve standalone MCP servers without enabling an ACP schema extension. Attaching one to ACP with the with_mcp_server builder methods requires unstable_mcp_over_acp. Attached servers are advertised with native McpServer::Acp declarations and communicate through request-scoped mcp/message operations targeting MCP 2026-07-28, without connect/disconnect or initialization handshakes. Use agent-client-protocol-polyfill immediately before an HTTP-capable agent. Stable protocol v1 supports per-session and global proxy attachment. Draft protocol v2 supports both scopes when both unstable features are enabled: Proxy.v2().with_mcp_server(...) injects a global server into supported setup requests, v2::SessionBuilder::with_mcp_server(...) attaches one to a single session/new, and v2::ResumeSessionBuilder::with_mcp_server(...) attaches one to a single session/resume. With unstable_session_fork, v2::ForkSessionBuilder::with_mcp_server(...) attaches one to a single session/fork. In v1, v1::ActiveSession owns per-session MCP registrations: dropping it removes them unless a proxy handoff detaches them (proxy_remaining_messages or successful on_proxy_session_start). Successful v2 setup detaches MCP handlers for the connection lifetime, so dropping the returned v2::ActiveSession does not unregister them. Global proxy attachments are connection-scoped in both versions. A v2 proxy can forward any of these setup operations with the builder's on_proxy_session_start; updates and interactive requests remain independent connection traffic.

WebAssembly

The runtime-neutral protocol engine and transport abstractions compile for wasm32-wasip1 and wasm32-wasip2 without additional features. For JavaScript-hosted wasm32-unknown-unknown, enable wasm_js; it selects Web Crypto through wasm-bindgen as the UUID randomness backend. The target does not imply a JavaScript host, so this feature is not enabled by default. Other OS-less WebAssembly hosts must arrange a compatible UUID randomness backend.

Handlers, transports, and connection futures must be Send on every target, including WebAssembly. JavaScript host values such as wasm_bindgen::JsValue are !Send, so keep them outside the connection and exchange Send data with it through Channel or ByteStreams.

The native AcpAgent and Stdio implementations are not available on WebAssembly targets. See the transport architecture for the runtime-neutral embedding options.

Learning More

See the crate documentation for:

  • Cookbook — Patterns for building clients, proxies, and agents
  • Examples — Runnable stable-v1 and draft-v2 clients and agents

Related Crates

Contribution Policy

This project does not require a Contributor License Agreement (CLA). Instead, contributions are accepted under the following terms:

By contributing to this project, you agree that your contributions will be licensed under the Apache License, Version 2.0. You affirm that you have the legal right to submit your work, that you are not including code you do not have rights to, and that you understand contributions are made without requiring a Contributor License Agreement (CLA).