agent-client-protocol 5.0.0

Core protocol types and traits for the Agent Client Protocol
Documentation
#![cfg_attr(docsrs, feature(doc_cfg))]
#![deny(missing_docs)]

//! # agent-client-protocol -- the Agent Client Protocol (ACP) SDK
//!
//! **agent-client-protocol** is a Rust SDK for building [Agent-Client Protocol (ACP)][acp] applications.
//! 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.
//!
//! [acp]: https://agentclientprotocol.com/
//!
//! ## What can you build with agent-client-protocol?
//!
//! - **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
//!
//! ## Protocol versions
//!
//! Every connection names the ACP protocol version it speaks:
//!
//! - [`Client.v1()`](Client::v1), [`Agent.v1()`](Agent::v1) and
//!   [`Proxy.v1()`](Proxy::v1) start a stable protocol v1 connection. Its
//!   types are in [`v1`] and its messages in [`v1::schema`].
//! - With the `unstable_protocol_v2` feature, `Client.v2()`, `Agent.v2()` and
//!   `Proxy.v2()` start a draft protocol v2 connection. Its types are in
//!   `agent_client_protocol::v2` and its messages in `v2::schema`.
//!
//! The `v1` and `v2` modules use the same names for the connection, the builder,
//! the request handles and `schema` (`ConnectionTo`, `Builder`, `Responder`,
//! `Dispatch`, `SentRequest`, ...). Their session APIs differ:
//! `v1::ActiveSession` reads the session's updates from its own stream, while
//! `v2::ActiveSession` only sends commands and the updates arrive through
//! typed handlers on the connection. Some names exist in only one module, such
//! as `v1::LoadSessionBuilder` and `v1::SessionMessage`, or
//! `v2::InitializeOutcome`.
//! The crate root holds what every version shares: transports, roles, the
//! JSON-RPC traits, [`Error`], the generic request handles (which default to
//! the version-neutral [`Raw`] marker) and, with `unstable_protocol_v2`, the
//! routers that serve both versions. [`Builder::new`] remains as the raw,
//! version-neutral escape hatch: it applies no protocol-version guard.
//!
//! ## Quick Start: Connecting to an Agent
//!
//! The most common use case is connecting to an existing ACP agent as a client.
//! This example uses stable ACP protocol v1. The draft protocol v2 feature
//! provides a command-only `v2::ActiveSession` API and receives updates and interactive
//! requests through typed connection handlers because prompt acceptance and
//! inbound traffic are independent. With both v2 and MCP-over-ACP features,
//! `Proxy.v2()` supports global MCP attachment, while `v2::SessionBuilder` and
//! `v2::ResumeSessionBuilder` support per-session attachment plus non-blocking
//! proxy setup. With `unstable_session_fork`, `v2::ForkSessionBuilder` provides
//! the same shape for forked sessions and uses the response's new session ID.
//! Per-session MCP routes and runners are ready before a setup request is
//! published, as is proxy session routing for resume replay. Successful v2
//! setup detaches MCP handlers for the connection lifetime; dropping the
//! returned `v2::ActiveSession` does not unregister them. In v1, `v1::ActiveSession` owns
//! per-session MCP registrations until drop, unless a proxy handoff detaches
//! them (`proxy_remaining_messages` or successful `on_proxy_session_start`).
//! Global proxy attachments are connection-scoped in both versions.
//!
//! Here's a minimal example that initializes a v1 connection, creates a
//! session, and sends a prompt:
//!
//! ```no_run
//! use agent_client_protocol::Client;
//! use agent_client_protocol::{schema::ProtocolVersion, v1::schema::InitializeRequest};
//! use agent_client_protocol::v1::SessionSetup;
//!
//! # async fn run(transport: impl agent_client_protocol::ConnectTo<agent_client_protocol::Client>) -> agent_client_protocol::Result<()> {
//! Client.v1()
//!     .name("my-client")
//!     .connect_with(transport, async |cx| {
//!         // Step 1: Initialize the connection
//!         cx.send_request(InitializeRequest::new(ProtocolVersion::V1))
//!             .block_task().await?;
//!
//!         // Step 2: Create a session and send a prompt
//!         cx.build_session_cwd()?
//!             .block_task()
//!             .run_until(async |SessionSetup { mut session, .. }| {
//!                 session.send_prompt("What is 2 + 2?")?;
//!                 let response = session.read_to_string().await?;
//!                 println!("{}", response);
//!                 Ok(())
//!             })
//!             .await
//!     })
//!     .await
//! # }
//! ```
//!
//! For a complete working example, see [`yolo_one_shot_client.rs`][yolo].
//!
//! [yolo]: https://github.com/agentclientprotocol/rust-sdk/blob/main/src/agent-client-protocol/examples/yolo_one_shot_client.rs
//!
//! ## Cookbook
//!
//! The [`agent_client_protocol_cookbook`] crate contains practical guides and examples:
//!
//! - Connecting as a client
//! - Global MCP server
//! - Per-session MCP server with workspace context
//! - Building agents and reusable components
//! - Running proxies with the conductor
//!
//! [`agent_client_protocol_cookbook`]: https://docs.rs/agent-client-protocol-cookbook
//!
//! ## Cargo Features
//!
//! No features are enabled by default. Protocol serialization, connections,
//! sessions, custom MCP servers, and the generic transport adapters remain
//! available without opting in to native I/O or JSON Schema generation.
//!
//! - `process`: native subprocess support through `AcpAgent` and `AcpAgentConfig`.
//! - `stdio`: the native `Stdio` adapter.
//! - `schemars`: `JsonSchema` implementations on protocol types and typed MCP
//!   tool helpers in [`mcp_server`].
//!
//! For example, a native client launching an agent opts into `process`:
//!
//! ```toml
//! agent-client-protocol = { version = "5", features = ["process"] }
//! ```
//!
//! Enable `stdio` for an agent using `Stdio`, or `schemars` for typed MCP tool
//! definitions. `LineDirection` is available on native targets with either
//! `process` or `stdio`; neither feature enables the other.
//!
//! The `agent-client-protocol-rmcp` crate explicitly enables `schemars` for its
//! tool builders. Unstable protocol features remain independent opt-ins.
//! No features have been enabled by default since 3.0. When upgrading from
//! 2.x, explicitly enable every feature your application uses;
//! `default-features = false` is no longer needed for a lean dependency.
//!
//! ## 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 [`v1::schema`]
//!   and `v2::schema`, and the version-neutral items such as
//!   [`ProtocolVersion`](schema::ProtocolVersion) through [`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.
//!
//! ## 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` to select 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 instead.
//!
//! This crate does not provide a WebAssembly executor or host I/O adapter. The
//! native `process` and `stdio` features depend on process spawning and
//! blocking-thread facilities, so their dependencies and exports (including
//! `LineDirection`) remain excluded on WebAssembly even when enabled.
//!
//! Embedders provide their own runtime and transport. They can exchange
//! `TransportFrame` values through `Channel`, newline-delimited JSON through
//! `Lines`, or use `ByteStreams` with `futures::io::AsyncRead` and
//! `AsyncWrite`. The embedding runtime must drive the resulting connection
//! future.
//!
//! 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`. A later
//! release may relax these bounds on WebAssembly. That change would not affect
//! native code.
//!
//! ## Core Concepts
//!
//! The [`concepts`] module provides detailed explanations of how agent-client-protocol works,
//! including connections, sessions, callbacks, ordering guarantees, and more.
//!
//! ## Related Crates
//!
//! - [`agent-client-protocol-conductor`] - Binary for running proxy chains
//!
//! [`agent-client-protocol-conductor`]: https://crates.io/crates/agent-client-protocol-conductor

/// Component abstraction for agents and proxies
pub mod component;
/// Core concepts for understanding and using agent-client-protocol
pub mod concepts;
/// JSON-RPC connection and handler infrastructure
mod jsonrpc;
/// Runtime-agnostic MCP server support, including optional attachment to ACP sessions.
pub mod mcp_server;
/// Role types for ACP connections
pub mod role;
pub mod schema;
/// Utility functions and types
pub mod util;
pub mod v1;
#[cfg(feature = "unstable_protocol_v2")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable_protocol_v2")))]
pub mod v2;

pub use jsonrpc::{
    Builder, ByteStreams, CONNECTION_TERMINATED_REASON, Channel, ConnectionTermination,
    ConnectionTo, Dispatch, DynamicHandlerGuard, HandleConnectionClose, HandleDispatchFrom,
    Handled, INCOMING_TRANSPORT_CLOSED_REASON, IntoHandled, JsonRpcMessage, JsonRpcNotification,
    JsonRpcRequest, JsonRpcResponse, Lines, NullClose, NullHandler, PreparedRequest, Protocol, Raw,
    RawJsonRpcError, RawJsonRpcId, RawJsonRpcMessage, RawJsonRpcNotification, RawJsonRpcParams,
    RawJsonRpcRequest, RawJsonRpcResponse, Responder, ResponseRouter, SentRequest, TransportFrame,
    UntypedMessage, V1, is_connection_terminated, is_incoming_transport_closed,
    run::{ChainRun, NullRun, RunWithConnectionTo},
};
#[cfg(feature = "unstable_protocol_v2")]
pub use jsonrpc::{InitializationVersionMismatch, SelectedInitializeResponse, V2};
pub use jsonrpc::{RequestCancellation, RequestCancellationHandle, is_cancel_request_notification};

#[cfg(feature = "unstable_protocol_v2")]
pub use role::acp::{AgentProtocolRouter, ClientProtocolConnector, ProxyProtocolRouter};
pub use role::{
    Role, RoleId, UntypedRole,
    acp::{Agent, Client, Conductor, Proxy},
};

pub use component::{ConnectTo, ConnectionDriver, DynConnectTo};

/// Implementation details used by the derive macros.
#[doc(hidden)]
pub mod __private {
    pub use crate::util::{json_cast, json_cast_params};
    pub use serde;
    pub use serde_json;
}

// Re-export BoxFuture for implementing SDK traits that return boxed futures.
pub use futures::future::BoxFuture;

mod error;
pub use error::{Error, ErrorCode, Result};

// Re-export derive macros for custom JSON-RPC types
pub use agent_client_protocol_derive::{JsonRpcNotification, JsonRpcRequest, JsonRpcResponse};

mod session;

#[cfg(all(feature = "process", not(target_family = "wasm")))]
mod acp_agent;
#[cfg(all(feature = "process", not(target_family = "wasm")))]
#[cfg_attr(
    docsrs,
    doc(cfg(all(feature = "process", not(target_family = "wasm"))))
)]
pub use acp_agent::{AcpAgent, AcpAgentConfig};

#[cfg(all(
    any(feature = "process", feature = "stdio"),
    not(target_family = "wasm")
))]
mod line_direction;
#[cfg(all(
    any(feature = "process", feature = "stdio"),
    not(target_family = "wasm")
))]
pub use line_direction::LineDirection;

#[cfg(all(feature = "stdio", not(target_family = "wasm")))]
mod stdio;
#[cfg(all(feature = "stdio", not(target_family = "wasm")))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "stdio", not(target_family = "wasm")))))]
pub use stdio::Stdio;

/// This is a hack that must be given as the final argument of
/// the MCP server builder's `tool_fn_mut` method when defining tools.
///
/// The `agent-client-protocol-rmcp` crate provides the builder this macro is
/// typically used with.
/// Look away, lest ye be blinded by its vileness!
///
/// Fine, if you MUST know, it's a horrific workaround for not having
/// [return-type notation](https://github.com/rust-lang/rust/issues/109417)
/// and for [this !@$#!%! bug](https://github.com/rust-lang/rust/issues/110338).
/// Trust me, the need for it hurts me more than it hurts you. --nikomatsakis
#[macro_export]
macro_rules! tool_fn_mut {
    () => {
        |func, params, context| Box::pin(func(params, context))
    };
}

/// This is a hack that must be given as the final argument of
/// the MCP server builder's `tool_fn` method when defining stateless concurrent tools.
///
/// The `agent-client-protocol-rmcp` crate provides the builder this macro is
/// typically used with.
/// See [`tool_fn_mut!`] for the gory details.
#[macro_export]
macro_rules! tool_fn {
    () => {
        |func, params, context| Box::pin(func(params, context))
    };
}

/// This macro is used for the value of the `to_future_hack` parameter of
/// [`Builder::on_receive_request`] and [`Builder::on_receive_request_from`].
///
/// It expands to `|f, req, responder, cx| Box::pin(f(req, responder, cx))`.
///
/// This is needed until [return-type notation](https://github.com/rust-lang/rust/issues/109417)
/// is stabilized.
#[macro_export]
macro_rules! on_receive_request {
    () => {
        |f: &mut _, req, responder, cx| Box::pin(f(req, responder, cx))
    };
}

/// This macro is used for the value of the `to_future_hack` parameter of
/// [`Builder::on_receive_notification`] and [`Builder::on_receive_notification_from`].
///
/// It expands to `|f, notif, cx| Box::pin(f(notif, cx))`.
///
/// This is needed until [return-type notation](https://github.com/rust-lang/rust/issues/109417)
/// is stabilized.
#[macro_export]
macro_rules! on_receive_notification {
    () => {
        |f: &mut _, notif, cx| Box::pin(f(notif, cx))
    };
}

/// This macro is used for the value of the `to_future_hack` parameter of
/// [`Builder::on_receive_dispatch`] and [`Builder::on_receive_dispatch_from`].
///
/// It expands to `|f, dispatch, cx| Box::pin(f(dispatch, cx))`.
///
/// This is needed until [return-type notation](https://github.com/rust-lang/rust/issues/109417)
/// is stabilized.
#[macro_export]
macro_rules! on_receive_dispatch {
    () => {
        |f: &mut _, dispatch, cx| Box::pin(f(dispatch, cx))
    };
}