Skip to main content

agent_client_protocol/
lib.rs

1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![deny(missing_docs)]
3
4//! # agent-client-protocol -- the Agent Client Protocol (ACP) SDK
5//!
6//! **agent-client-protocol** is a Rust SDK for building [Agent-Client Protocol (ACP)][acp] applications.
7//! ACP is a protocol for communication between AI agents and their clients (IDEs, CLIs, etc.),
8//! enabling features like tool use, permission requests, and streaming responses.
9//!
10//! [acp]: https://agentclientprotocol.com/
11//!
12//! ## What can you build with agent-client-protocol?
13//!
14//! - **Clients** that talk to ACP agents (like building your own Claude Code interface)
15//! - **Proxies** that add capabilities to existing agents (like adding custom tools via MCP)
16//! - **Agents** that respond to prompts with AI-powered responses
17//!
18//! ## Quick Start: Connecting to an Agent
19//!
20//! The most common use case is connecting to an existing ACP agent as a client.
21//! This example uses stable ACP protocol v1. The draft protocol v2 feature
22//! provides a command-only `V2Session` API and receives updates and interactive
23//! requests through typed connection handlers because prompt acceptance and
24//! inbound traffic are independent. With both v2 and MCP-over-ACP features,
25//! `Proxy.v2()` supports global MCP attachment, while `V2SessionBuilder` and
26//! `V2ResumeSessionBuilder` support per-session attachment plus non-blocking
27//! proxy setup. With `unstable_session_fork`, `V2ForkSessionBuilder` provides
28//! the same shape for forked sessions and uses the response's new session ID.
29//! Per-session MCP routes and runners are ready before a setup request is
30//! published, as is proxy session routing for resume replay. Successful v2
31//! setup detaches MCP handlers for the connection lifetime; dropping the
32//! returned `V2Session` does not unregister them. In v1, `ActiveSession` owns
33//! per-session MCP registrations until drop, unless a proxy handoff detaches
34//! them (`proxy_remaining_messages` or successful `on_proxy_session_start`).
35//! Global proxy attachments are connection-scoped in both versions.
36//!
37//! Here's a minimal example that initializes a v1 connection, creates a
38//! session, and sends a prompt:
39//!
40//! ```no_run
41//! use agent_client_protocol::Client;
42//! use agent_client_protocol::schema::{ProtocolVersion, v1::InitializeRequest};
43//!
44//! # async fn run(transport: impl agent_client_protocol::ConnectTo<agent_client_protocol::Client>) -> agent_client_protocol::Result<()> {
45//! Client.builder()
46//!     .name("my-client")
47//!     .connect_with(transport, async |cx| {
48//!         // Step 1: Initialize the connection
49//!         cx.send_request(InitializeRequest::new(ProtocolVersion::V1))
50//!             .block_task().await?;
51//!
52//!         // Step 2: Create a session and send a prompt
53//!         cx.build_session_cwd()?
54//!             .block_task()
55//!             .run_until(async |mut session| {
56//!                 session.send_prompt("What is 2 + 2?")?;
57//!                 let response = session.read_to_string().await?;
58//!                 println!("{}", response);
59//!                 Ok(())
60//!             })
61//!             .await
62//!     })
63//!     .await
64//! # }
65//! ```
66//!
67//! For a complete working example, see [`yolo_one_shot_client.rs`][yolo].
68//!
69//! [yolo]: https://github.com/agentclientprotocol/rust-sdk/blob/main/src/agent-client-protocol/examples/yolo_one_shot_client.rs
70//!
71//! ## Cookbook
72//!
73//! The [`agent_client_protocol_cookbook`] crate contains practical guides and examples:
74//!
75//! - Connecting as a client
76//! - Global MCP server
77//! - Per-session MCP server with workspace context
78//! - Building agents and reusable components
79//! - Running proxies with the conductor
80//!
81//! [`agent_client_protocol_cookbook`]: https://docs.rs/agent-client-protocol-cookbook
82//!
83//! ## Cargo Features
84//!
85//! No features are enabled by default. Protocol serialization, connections,
86//! sessions, custom MCP servers, and the generic transport adapters remain
87//! available without opting in to native I/O or JSON Schema generation.
88//!
89//! - `process`: native subprocess support through `AcpAgent` and `AcpAgentConfig`.
90//! - `stdio`: the native `Stdio` adapter.
91//! - `schemars`: `JsonSchema` implementations on protocol types and typed MCP
92//!   tool helpers in [`mcp_server`].
93//!
94//! For example, a native client launching an agent opts into `process`:
95//!
96//! ```toml
97//! agent-client-protocol = { version = "3", features = ["process"] }
98//! ```
99//!
100//! Enable `stdio` for an agent using `Stdio`, or `schemars` for typed MCP tool
101//! definitions. `LineDirection` is available on native targets with either
102//! `process` or `stdio`; neither feature enables the other.
103//!
104//! The `agent-client-protocol-rmcp` crate explicitly enables `schemars` for its
105//! tool builders. Unstable protocol features remain independent opt-ins.
106//! When upgrading from 2.x, explicitly enable every feature your application
107//! uses; `default-features = false` is no longer needed for a lean dependency.
108//!
109//! ## WebAssembly
110//!
111//! The runtime-neutral protocol engine and transport abstractions compile for
112//! `wasm32-wasip1` and `wasm32-wasip2` without additional features. For
113//! JavaScript-hosted `wasm32-unknown-unknown`, enable `wasm_js` to select Web
114//! Crypto through `wasm-bindgen` as the UUID randomness backend. The target
115//! does not imply a JavaScript host, so this feature is not enabled by default;
116//! other OS-less WebAssembly hosts must arrange a compatible UUID randomness
117//! backend instead.
118//!
119//! This crate does not provide a WebAssembly executor or host I/O adapter. The
120//! native `process` and `stdio` features depend on process spawning and
121//! blocking-thread facilities, so their dependencies and exports (including
122//! `LineDirection`) remain excluded on WebAssembly even when enabled.
123//!
124//! Embedders provide their own runtime and transport. They can exchange
125//! `TransportFrame` values through `Channel`, newline-delimited JSON through
126//! `Lines`, or use `ByteStreams` with `futures::io::AsyncRead` and
127//! `AsyncWrite`. The embedding runtime must drive the resulting connection
128//! future.
129//!
130//! ## Core Concepts
131//!
132//! The [`concepts`] module provides detailed explanations of how agent-client-protocol works,
133//! including connections, sessions, callbacks, ordering guarantees, and more.
134//!
135//! ## Related Crates
136//!
137//! - [`agent-client-protocol-conductor`] - Binary for running proxy chains
138//!
139//! [`agent-client-protocol-conductor`]: https://crates.io/crates/agent-client-protocol-conductor
140
141/// Capability management for the `_meta.symposium` object
142mod capabilities;
143/// Component abstraction for agents and proxies
144pub mod component;
145/// Core concepts for understanding and using agent-client-protocol
146pub mod concepts;
147/// JSON-RPC connection and handler infrastructure
148mod jsonrpc;
149/// Runtime-agnostic MCP server support, including optional attachment to ACP sessions.
150pub mod mcp_server;
151/// Role types for ACP connections
152pub mod role;
153/// ACP protocol schema types - all message types, requests, responses, and supporting types
154pub mod schema;
155/// Utility functions and types
156pub mod util;
157
158pub use capabilities::*;
159
160pub use jsonrpc::{
161    Builder, ByteStreams, Channel, ConnectionContext, ConnectionTo, Dispatch, DynamicHandlerGuard,
162    HandleConnectionClose, HandleDispatchFrom, Handled, INCOMING_TRANSPORT_CLOSED_REASON,
163    IntoHandled, JsonRpcMessage, JsonRpcNotification, JsonRpcRequest, JsonRpcResponse, Lines,
164    NullClose, NullHandler, RawConnectionContext, RawJsonRpcError, RawJsonRpcMessage,
165    RawJsonRpcParams, RawJsonRpcResponse, Responder, ResponseRouter, SentRequest, TransportBatch,
166    TransportBatchEntry, TransportFrame, UntypedMessage, is_incoming_transport_closed,
167    run::{ChainRun, NullRun, RunWithConnectionTo},
168};
169pub use jsonrpc::{RequestCancellation, is_cancel_request_notification};
170#[cfg(feature = "unstable_protocol_v2")]
171pub use jsonrpc::{V2Builder, V2ConnectionContext, V2ConnectionTo};
172
173#[cfg(feature = "unstable_protocol_v2")]
174pub use role::acp::{AgentProtocolRouter, ClientProtocolConnector, ProxyProtocolRouter};
175pub use role::{
176    Role, RoleId, UntypedRole,
177    acp::{Agent, Client, Conductor, Proxy},
178};
179
180pub use component::{ConnectTo, ConnectionDriver, DynConnectTo};
181
182/// Implementation details used by the derive macros.
183#[doc(hidden)]
184pub mod __private {
185    pub use serde;
186    pub use serde_json;
187}
188
189// Re-export BoxFuture for implementing SDK traits that return boxed futures.
190pub use futures::future::BoxFuture;
191
192// Re-export commonly used infrastructure types for convenience
193pub use schema::v1::{Error, ErrorCode, Result};
194
195// Re-export derive macros for custom JSON-RPC types
196pub use agent_client_protocol_derive::{JsonRpcNotification, JsonRpcRequest, JsonRpcResponse};
197
198mod session;
199pub use session::*;
200
201#[cfg(all(feature = "process", not(target_family = "wasm")))]
202mod acp_agent;
203#[cfg(all(feature = "process", not(target_family = "wasm")))]
204#[cfg_attr(
205    docsrs,
206    doc(cfg(all(feature = "process", not(target_family = "wasm"))))
207)]
208pub use acp_agent::{AcpAgent, AcpAgentConfig};
209
210#[cfg(all(
211    any(feature = "process", feature = "stdio"),
212    not(target_family = "wasm")
213))]
214mod line_direction;
215#[cfg(all(
216    any(feature = "process", feature = "stdio"),
217    not(target_family = "wasm")
218))]
219pub use line_direction::LineDirection;
220
221#[cfg(all(feature = "stdio", not(target_family = "wasm")))]
222mod stdio;
223#[cfg(all(feature = "stdio", not(target_family = "wasm")))]
224#[cfg_attr(docsrs, doc(cfg(all(feature = "stdio", not(target_family = "wasm")))))]
225pub use stdio::Stdio;
226
227/// This is a hack that must be given as the final argument of
228/// the MCP server builder's `tool_fn_mut` method when defining tools.
229///
230/// The `agent-client-protocol-rmcp` crate provides the builder this macro is
231/// typically used with.
232/// Look away, lest ye be blinded by its vileness!
233///
234/// Fine, if you MUST know, it's a horrific workaround for not having
235/// [return-type notation](https://github.com/rust-lang/rust/issues/109417)
236/// and for [this !@$#!%! bug](https://github.com/rust-lang/rust/issues/110338).
237/// Trust me, the need for it hurts me more than it hurts you. --nikomatsakis
238#[macro_export]
239macro_rules! tool_fn_mut {
240    () => {
241        |func, params, context| Box::pin(func(params, context))
242    };
243}
244
245/// This is a hack that must be given as the final argument of
246/// the MCP server builder's `tool_fn` method when defining stateless concurrent tools.
247///
248/// The `agent-client-protocol-rmcp` crate provides the builder this macro is
249/// typically used with.
250/// See [`tool_fn_mut!`] for the gory details.
251#[macro_export]
252macro_rules! tool_fn {
253    () => {
254        |func, params, context| Box::pin(func(params, context))
255    };
256}
257
258/// This macro is used for the value of the `to_future_hack` parameter of
259/// [`Builder::on_receive_request`] and [`Builder::on_receive_request_from`].
260///
261/// It expands to `|f, req, responder, cx| Box::pin(f(req, responder, cx))`.
262///
263/// This is needed until [return-type notation](https://github.com/rust-lang/rust/issues/109417)
264/// is stabilized.
265#[macro_export]
266macro_rules! on_receive_request {
267    () => {
268        |f: &mut _, req, responder, cx| Box::pin(f(req, responder, cx))
269    };
270}
271
272/// This macro is used for the value of the `to_future_hack` parameter of
273/// [`Builder::on_receive_notification`] and [`Builder::on_receive_notification_from`].
274///
275/// It expands to `|f, notif, cx| Box::pin(f(notif, cx))`.
276///
277/// This is needed until [return-type notation](https://github.com/rust-lang/rust/issues/109417)
278/// is stabilized.
279#[macro_export]
280macro_rules! on_receive_notification {
281    () => {
282        |f: &mut _, notif, cx| Box::pin(f(notif, cx))
283    };
284}
285
286/// This macro is used for the value of the `to_future_hack` parameter of
287/// [`Builder::on_receive_dispatch`] and [`Builder::on_receive_dispatch_from`].
288///
289/// It expands to `|f, dispatch, cx| Box::pin(f(dispatch, cx))`.
290///
291/// This is needed until [return-type notation](https://github.com/rust-lang/rust/issues/109417)
292/// is stabilized.
293#[macro_export]
294macro_rules! on_receive_dispatch {
295    () => {
296        |f: &mut _, dispatch, cx| Box::pin(f(dispatch, cx))
297    };
298}