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, CONNECTION_TERMINATED_REASON, Channel, ConnectionContext,
162 ConnectionTermination, ConnectionTo, Dispatch, DynamicHandlerGuard, HandleConnectionClose,
163 HandleDispatchFrom, Handled, INCOMING_TRANSPORT_CLOSED_REASON, IntoHandled, JsonRpcMessage,
164 JsonRpcNotification, JsonRpcRequest, JsonRpcResponse, Lines, NullClose, NullHandler,
165 PreparedRequest, RawConnectionContext, RawJsonRpcError, RawJsonRpcMessage, RawJsonRpcParams,
166 RawJsonRpcResponse, Responder, ResponseRouter, SentRequest, TransportBatch,
167 TransportBatchEntry, TransportFrame, UntypedMessage, is_connection_terminated,
168 is_incoming_transport_closed,
169 run::{ChainRun, NullRun, RunWithConnectionTo},
170};
171#[cfg(feature = "unstable_protocol_v2")]
172pub use jsonrpc::{
173 InitializationVersionMismatch, InitializeOutcome, SelectedInitializeResponse, V2Builder,
174 V2ConnectionContext, V2ConnectionTo,
175};
176pub use jsonrpc::{RequestCancellation, RequestCancellationHandle, is_cancel_request_notification};
177
178#[cfg(feature = "unstable_protocol_v2")]
179pub use role::acp::{AgentProtocolRouter, ClientProtocolConnector, ProxyProtocolRouter};
180pub use role::{
181 Role, RoleId, UntypedRole,
182 acp::{Agent, Client, Conductor, Proxy},
183};
184
185pub use component::{ConnectTo, ConnectionDriver, DynConnectTo};
186
187/// Implementation details used by the derive macros.
188#[doc(hidden)]
189pub mod __private {
190 pub use serde;
191 pub use serde_json;
192}
193
194// Re-export BoxFuture for implementing SDK traits that return boxed futures.
195pub use futures::future::BoxFuture;
196
197// Re-export commonly used infrastructure types for convenience
198pub use schema::v1::{Error, ErrorCode, Result};
199
200// Re-export derive macros for custom JSON-RPC types
201pub use agent_client_protocol_derive::{JsonRpcNotification, JsonRpcRequest, JsonRpcResponse};
202
203mod session;
204pub use session::*;
205
206#[cfg(all(feature = "process", not(target_family = "wasm")))]
207mod acp_agent;
208#[cfg(all(feature = "process", not(target_family = "wasm")))]
209#[cfg_attr(
210 docsrs,
211 doc(cfg(all(feature = "process", not(target_family = "wasm"))))
212)]
213pub use acp_agent::{AcpAgent, AcpAgentConfig};
214
215#[cfg(all(
216 any(feature = "process", feature = "stdio"),
217 not(target_family = "wasm")
218))]
219mod line_direction;
220#[cfg(all(
221 any(feature = "process", feature = "stdio"),
222 not(target_family = "wasm")
223))]
224pub use line_direction::LineDirection;
225
226#[cfg(all(feature = "stdio", not(target_family = "wasm")))]
227mod stdio;
228#[cfg(all(feature = "stdio", not(target_family = "wasm")))]
229#[cfg_attr(docsrs, doc(cfg(all(feature = "stdio", not(target_family = "wasm")))))]
230pub use stdio::Stdio;
231
232/// This is a hack that must be given as the final argument of
233/// the MCP server builder's `tool_fn_mut` method when defining tools.
234///
235/// The `agent-client-protocol-rmcp` crate provides the builder this macro is
236/// typically used with.
237/// Look away, lest ye be blinded by its vileness!
238///
239/// Fine, if you MUST know, it's a horrific workaround for not having
240/// [return-type notation](https://github.com/rust-lang/rust/issues/109417)
241/// and for [this !@$#!%! bug](https://github.com/rust-lang/rust/issues/110338).
242/// Trust me, the need for it hurts me more than it hurts you. --nikomatsakis
243#[macro_export]
244macro_rules! tool_fn_mut {
245 () => {
246 |func, params, context| Box::pin(func(params, context))
247 };
248}
249
250/// This is a hack that must be given as the final argument of
251/// the MCP server builder's `tool_fn` method when defining stateless concurrent tools.
252///
253/// The `agent-client-protocol-rmcp` crate provides the builder this macro is
254/// typically used with.
255/// See [`tool_fn_mut!`] for the gory details.
256#[macro_export]
257macro_rules! tool_fn {
258 () => {
259 |func, params, context| Box::pin(func(params, context))
260 };
261}
262
263/// This macro is used for the value of the `to_future_hack` parameter of
264/// [`Builder::on_receive_request`] and [`Builder::on_receive_request_from`].
265///
266/// It expands to `|f, req, responder, cx| Box::pin(f(req, responder, cx))`.
267///
268/// This is needed until [return-type notation](https://github.com/rust-lang/rust/issues/109417)
269/// is stabilized.
270#[macro_export]
271macro_rules! on_receive_request {
272 () => {
273 |f: &mut _, req, responder, cx| Box::pin(f(req, responder, cx))
274 };
275}
276
277/// This macro is used for the value of the `to_future_hack` parameter of
278/// [`Builder::on_receive_notification`] and [`Builder::on_receive_notification_from`].
279///
280/// It expands to `|f, notif, cx| Box::pin(f(notif, cx))`.
281///
282/// This is needed until [return-type notation](https://github.com/rust-lang/rust/issues/109417)
283/// is stabilized.
284#[macro_export]
285macro_rules! on_receive_notification {
286 () => {
287 |f: &mut _, notif, cx| Box::pin(f(notif, cx))
288 };
289}
290
291/// This macro is used for the value of the `to_future_hack` parameter of
292/// [`Builder::on_receive_dispatch`] and [`Builder::on_receive_dispatch_from`].
293///
294/// It expands to `|f, dispatch, cx| Box::pin(f(dispatch, cx))`.
295///
296/// This is needed until [return-type notation](https://github.com/rust-lang/rust/issues/109417)
297/// is stabilized.
298#[macro_export]
299macro_rules! on_receive_dispatch {
300 () => {
301 |f: &mut _, dispatch, cx| Box::pin(f(dispatch, cx))
302 };
303}