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//! ## Protocol versions
19//!
20//! Every connection names the ACP protocol version it speaks:
21//!
22//! - [`Client.v1()`](Client::v1), [`Agent.v1()`](Agent::v1) and
23//! [`Proxy.v1()`](Proxy::v1) start a stable protocol v1 connection. Its
24//! types are in [`v1`] and its messages in [`v1::schema`].
25//! - With the `unstable_protocol_v2` feature, `Client.v2()`, `Agent.v2()` and
26//! `Proxy.v2()` start a draft protocol v2 connection. Its types are in
27//! `agent_client_protocol::v2` and its messages in `v2::schema`.
28//!
29//! The `v1` and `v2` modules use the same names for the connection, the builder,
30//! the request handles and `schema` (`ConnectionTo`, `Builder`, `Responder`,
31//! `Dispatch`, `SentRequest`, ...). Their session APIs differ:
32//! `v1::ActiveSession` reads the session's updates from its own stream, while
33//! `v2::ActiveSession` only sends commands and the updates arrive through
34//! typed handlers on the connection. Some names exist in only one module, such
35//! as `v1::LoadSessionBuilder` and `v1::SessionMessage`, or
36//! `v2::InitializeOutcome`.
37//! The crate root holds what every version shares: transports, roles, the
38//! JSON-RPC traits, [`Error`], the generic request handles (which default to
39//! the version-neutral [`Raw`] marker) and, with `unstable_protocol_v2`, the
40//! routers that serve both versions. [`Builder::new`] remains as the raw,
41//! version-neutral escape hatch: it applies no protocol-version guard.
42//!
43//! ## Quick Start: Connecting to an Agent
44//!
45//! The most common use case is connecting to an existing ACP agent as a client.
46//! This example uses stable ACP protocol v1. The draft protocol v2 feature
47//! provides a command-only `v2::ActiveSession` API and receives updates and interactive
48//! requests through typed connection handlers because prompt acceptance and
49//! inbound traffic are independent. With both v2 and MCP-over-ACP features,
50//! `Proxy.v2()` supports global MCP attachment, while `v2::SessionBuilder` and
51//! `v2::ResumeSessionBuilder` support per-session attachment plus non-blocking
52//! proxy setup. With `unstable_session_fork`, `v2::ForkSessionBuilder` provides
53//! the same shape for forked sessions and uses the response's new session ID.
54//! Per-session MCP routes and runners are ready before a setup request is
55//! published, as is proxy session routing for resume replay. Successful v2
56//! setup detaches MCP handlers for the connection lifetime; dropping the
57//! returned `v2::ActiveSession` does not unregister them. In v1, `v1::ActiveSession` owns
58//! per-session MCP registrations until drop, unless a proxy handoff detaches
59//! them (`proxy_remaining_messages` or successful `on_proxy_session_start`).
60//! Global proxy attachments are connection-scoped in both versions.
61//!
62//! Here's a minimal example that initializes a v1 connection, creates a
63//! session, and sends a prompt:
64//!
65//! ```no_run
66//! use agent_client_protocol::Client;
67//! use agent_client_protocol::{schema::ProtocolVersion, v1::schema::InitializeRequest};
68//! use agent_client_protocol::v1::SessionSetup;
69//!
70//! # async fn run(transport: impl agent_client_protocol::ConnectTo<agent_client_protocol::Client>) -> agent_client_protocol::Result<()> {
71//! Client.v1()
72//! .name("my-client")
73//! .connect_with(transport, async |cx| {
74//! // Step 1: Initialize the connection
75//! cx.send_request(InitializeRequest::new(ProtocolVersion::V1))
76//! .block_task().await?;
77//!
78//! // Step 2: Create a session and send a prompt
79//! cx.build_session_cwd()?
80//! .block_task()
81//! .run_until(async |SessionSetup { mut session, .. }| {
82//! session.send_prompt("What is 2 + 2?")?;
83//! let response = session.read_to_string().await?;
84//! println!("{}", response);
85//! Ok(())
86//! })
87//! .await
88//! })
89//! .await
90//! # }
91//! ```
92//!
93//! For a complete working example, see [`yolo_one_shot_client.rs`][yolo].
94//!
95//! [yolo]: https://github.com/agentclientprotocol/rust-sdk/blob/main/src/agent-client-protocol/examples/yolo_one_shot_client.rs
96//!
97//! ## Cookbook
98//!
99//! The [`agent_client_protocol_cookbook`] crate contains practical guides and examples:
100//!
101//! - Connecting as a client
102//! - Global MCP server
103//! - Per-session MCP server with workspace context
104//! - Building agents and reusable components
105//! - Running proxies with the conductor
106//!
107//! [`agent_client_protocol_cookbook`]: https://docs.rs/agent-client-protocol-cookbook
108//!
109//! ## Cargo Features
110//!
111//! No features are enabled by default. Protocol serialization, connections,
112//! sessions, custom MCP servers, and the generic transport adapters remain
113//! available without opting in to native I/O or JSON Schema generation.
114//!
115//! - `process`: native subprocess support through `AcpAgent` and `AcpAgentConfig`.
116//! - `stdio`: the native `Stdio` adapter.
117//! - `schemars`: `JsonSchema` implementations on protocol types and typed MCP
118//! tool helpers in [`mcp_server`].
119//!
120//! For example, a native client launching an agent opts into `process`:
121//!
122//! ```toml
123//! agent-client-protocol = { version = "5", features = ["process"] }
124//! ```
125//!
126//! Enable `stdio` for an agent using `Stdio`, or `schemars` for typed MCP tool
127//! definitions. `LineDirection` is available on native targets with either
128//! `process` or `stdio`; neither feature enables the other.
129//!
130//! The `agent-client-protocol-rmcp` crate explicitly enables `schemars` for its
131//! tool builders. Unstable protocol features remain independent opt-ins.
132//! No features have been enabled by default since 3.0. When upgrading from
133//! 2.x, explicitly enable every feature your application uses;
134//! `default-features = false` is no longer needed for a lean dependency.
135//!
136//! ## Stability
137//!
138//! - The stable API, everything that is not behind an `unstable_*` feature,
139//! follows semver: it changes incompatibly only in a major release.
140//! - Everything behind an `unstable_*` feature may change in a minor release.
141//! This includes all of `agent_client_protocol::v2` and the other items that
142//! `unstable_protocol_v2` enables. Stabilizing protocol v2 only removes the
143//! `unstable_protocol_v2` gate.
144//! - This crate depends on one exact version of `agent-client-protocol-schema`
145//! (an `=` requirement), and a minor release may move that pin to a newer
146//! schema release. Use each version's message types through [`v1::schema`]
147//! and `v2::schema`, and the version-neutral items such as
148//! [`ProtocolVersion`](schema::ProtocolVersion) through [`schema`], instead
149//! of depending on `agent-client-protocol-schema` directly. A direct
150//! dependency must allow the exact version this crate pins, or Cargo cannot
151//! resolve the dependency graph.
152//! - All SDK crates share one version number, so moving a public dependency of
153//! any of them to a new major version, such as `axum` or `reqwest` in
154//! `agent-client-protocol-http`, is a major release of all of them.
155//!
156//! ## WebAssembly
157//!
158//! The runtime-neutral protocol engine and transport abstractions compile for
159//! `wasm32-wasip1` and `wasm32-wasip2` without additional features. For
160//! JavaScript-hosted `wasm32-unknown-unknown`, enable `wasm_js` to select Web
161//! Crypto through `wasm-bindgen` as the UUID randomness backend. The target
162//! does not imply a JavaScript host, so this feature is not enabled by default;
163//! other OS-less WebAssembly hosts must arrange a compatible UUID randomness
164//! backend instead.
165//!
166//! This crate does not provide a WebAssembly executor or host I/O adapter. The
167//! native `process` and `stdio` features depend on process spawning and
168//! blocking-thread facilities, so their dependencies and exports (including
169//! `LineDirection`) remain excluded on WebAssembly even when enabled.
170//!
171//! Embedders provide their own runtime and transport. They can exchange
172//! `TransportFrame` values through `Channel`, newline-delimited JSON through
173//! `Lines`, or use `ByteStreams` with `futures::io::AsyncRead` and
174//! `AsyncWrite`. The embedding runtime must drive the resulting connection
175//! future.
176//!
177//! Handlers, transports, and connection futures must be `Send` on every
178//! target, including WebAssembly. JavaScript host values such as
179//! `wasm_bindgen::JsValue` are `!Send`, so keep them outside the connection and
180//! exchange `Send` data with it through `Channel` or `ByteStreams`. A later
181//! release may relax these bounds on WebAssembly. That change would not affect
182//! native code.
183//!
184//! ## Core Concepts
185//!
186//! The [`concepts`] module provides detailed explanations of how agent-client-protocol works,
187//! including connections, sessions, callbacks, ordering guarantees, and more.
188//!
189//! ## Related Crates
190//!
191//! - [`agent-client-protocol-conductor`] - Binary for running proxy chains
192//!
193//! [`agent-client-protocol-conductor`]: https://crates.io/crates/agent-client-protocol-conductor
194
195/// Component abstraction for agents and proxies
196pub mod component;
197/// Core concepts for understanding and using agent-client-protocol
198pub mod concepts;
199/// JSON-RPC connection and handler infrastructure
200mod jsonrpc;
201/// Runtime-agnostic MCP server support, including optional attachment to ACP sessions.
202pub mod mcp_server;
203/// Role types for ACP connections
204pub mod role;
205pub mod schema;
206/// Utility functions and types
207pub mod util;
208pub mod v1;
209#[cfg(feature = "unstable_protocol_v2")]
210#[cfg_attr(docsrs, doc(cfg(feature = "unstable_protocol_v2")))]
211pub mod v2;
212
213pub use jsonrpc::{
214 Builder, ByteStreams, CONNECTION_TERMINATED_REASON, Channel, ConnectionTermination,
215 ConnectionTo, Dispatch, DynamicHandlerGuard, HandleConnectionClose, HandleDispatchFrom,
216 Handled, INCOMING_TRANSPORT_CLOSED_REASON, IntoHandled, JsonRpcMessage, JsonRpcNotification,
217 JsonRpcRequest, JsonRpcResponse, Lines, NullClose, NullHandler, PreparedRequest, Protocol, Raw,
218 RawJsonRpcError, RawJsonRpcId, RawJsonRpcMessage, RawJsonRpcNotification, RawJsonRpcParams,
219 RawJsonRpcRequest, RawJsonRpcResponse, Responder, ResponseRouter, SentRequest, TransportFrame,
220 UntypedMessage, V1, is_connection_terminated, is_incoming_transport_closed,
221 run::{ChainRun, NullRun, RunWithConnectionTo},
222};
223#[cfg(feature = "unstable_protocol_v2")]
224pub use jsonrpc::{InitializationVersionMismatch, SelectedInitializeResponse, V2};
225pub use jsonrpc::{RequestCancellation, RequestCancellationHandle, is_cancel_request_notification};
226
227#[cfg(feature = "unstable_protocol_v2")]
228pub use role::acp::{AgentProtocolRouter, ClientProtocolConnector, ProxyProtocolRouter};
229pub use role::{
230 Role, RoleId, UntypedRole,
231 acp::{Agent, Client, Conductor, Proxy},
232};
233
234pub use component::{ConnectTo, ConnectionDriver, DynConnectTo};
235
236/// Implementation details used by the derive macros.
237#[doc(hidden)]
238pub mod __private {
239 pub use crate::util::{json_cast, json_cast_params};
240 pub use serde;
241 pub use serde_json;
242}
243
244// Re-export BoxFuture for implementing SDK traits that return boxed futures.
245pub use futures::future::BoxFuture;
246
247mod error;
248pub use error::{Error, ErrorCode, Result};
249
250// Re-export derive macros for custom JSON-RPC types
251pub use agent_client_protocol_derive::{JsonRpcNotification, JsonRpcRequest, JsonRpcResponse};
252
253mod session;
254
255#[cfg(all(feature = "process", not(target_family = "wasm")))]
256mod acp_agent;
257#[cfg(all(feature = "process", not(target_family = "wasm")))]
258#[cfg_attr(
259 docsrs,
260 doc(cfg(all(feature = "process", not(target_family = "wasm"))))
261)]
262pub use acp_agent::{AcpAgent, AcpAgentConfig};
263
264#[cfg(all(
265 any(feature = "process", feature = "stdio"),
266 not(target_family = "wasm")
267))]
268mod line_direction;
269#[cfg(all(
270 any(feature = "process", feature = "stdio"),
271 not(target_family = "wasm")
272))]
273pub use line_direction::LineDirection;
274
275#[cfg(all(feature = "stdio", not(target_family = "wasm")))]
276mod stdio;
277#[cfg(all(feature = "stdio", not(target_family = "wasm")))]
278#[cfg_attr(docsrs, doc(cfg(all(feature = "stdio", not(target_family = "wasm")))))]
279pub use stdio::Stdio;
280
281/// This is a hack that must be given as the final argument of
282/// the MCP server builder's `tool_fn_mut` method when defining tools.
283///
284/// The `agent-client-protocol-rmcp` crate provides the builder this macro is
285/// typically used with.
286/// Look away, lest ye be blinded by its vileness!
287///
288/// Fine, if you MUST know, it's a horrific workaround for not having
289/// [return-type notation](https://github.com/rust-lang/rust/issues/109417)
290/// and for [this !@$#!%! bug](https://github.com/rust-lang/rust/issues/110338).
291/// Trust me, the need for it hurts me more than it hurts you. --nikomatsakis
292#[macro_export]
293macro_rules! tool_fn_mut {
294 () => {
295 |func, params, context| Box::pin(func(params, context))
296 };
297}
298
299/// This is a hack that must be given as the final argument of
300/// the MCP server builder's `tool_fn` method when defining stateless concurrent tools.
301///
302/// The `agent-client-protocol-rmcp` crate provides the builder this macro is
303/// typically used with.
304/// See [`tool_fn_mut!`] for the gory details.
305#[macro_export]
306macro_rules! tool_fn {
307 () => {
308 |func, params, context| Box::pin(func(params, context))
309 };
310}
311
312/// This macro is used for the value of the `to_future_hack` parameter of
313/// [`Builder::on_receive_request`] and [`Builder::on_receive_request_from`].
314///
315/// It expands to `|f, req, responder, cx| Box::pin(f(req, responder, cx))`.
316///
317/// This is needed until [return-type notation](https://github.com/rust-lang/rust/issues/109417)
318/// is stabilized.
319#[macro_export]
320macro_rules! on_receive_request {
321 () => {
322 |f: &mut _, req, responder, cx| Box::pin(f(req, responder, cx))
323 };
324}
325
326/// This macro is used for the value of the `to_future_hack` parameter of
327/// [`Builder::on_receive_notification`] and [`Builder::on_receive_notification_from`].
328///
329/// It expands to `|f, notif, cx| Box::pin(f(notif, cx))`.
330///
331/// This is needed until [return-type notation](https://github.com/rust-lang/rust/issues/109417)
332/// is stabilized.
333#[macro_export]
334macro_rules! on_receive_notification {
335 () => {
336 |f: &mut _, notif, cx| Box::pin(f(notif, cx))
337 };
338}
339
340/// This macro is used for the value of the `to_future_hack` parameter of
341/// [`Builder::on_receive_dispatch`] and [`Builder::on_receive_dispatch_from`].
342///
343/// It expands to `|f, dispatch, cx| Box::pin(f(dispatch, cx))`.
344///
345/// This is needed until [return-type notation](https://github.com/rust-lang/rust/issues/109417)
346/// is stabilized.
347#[macro_export]
348macro_rules! on_receive_dispatch {
349 () => {
350 |f: &mut _, dispatch, cx| Box::pin(f(dispatch, cx))
351 };
352}