sacp/lib.rs
1#![deny(missing_docs)]
2
3//! # sacp -- the Symposium Agent Client Protocol (ACP) SDK
4//!
5//! **sacp** is a Rust SDK for building [Agent-Client Protocol (ACP)][acp] applications.
6//! ACP is a protocol for communication between AI agents and their clients (IDEs, CLIs, etc.),
7//! enabling features like tool use, permission requests, and streaming responses.
8//!
9//! [acp]: https://agentclientprotocol.com/
10//!
11//! ## What can you build with sacp?
12//!
13//! - **Clients** that talk to ACP agents (like building your own Claude Code interface)
14//! - **Proxies** that add capabilities to existing agents (like adding custom tools via MCP)
15//! - **Agents** that respond to prompts with AI-powered responses
16//!
17//! ## Quick Start: Connecting to an Agent
18//!
19//! The most common use case is connecting to an existing ACP agent as a client.
20//! Here's a minimal example that initializes a connection, creates a session,
21//! and sends a prompt:
22//!
23//! ```no_run
24//! use sacp::ClientToAgent;
25//! use sacp::schema::{InitializeRequest, ProtocolVersion};
26//!
27//! # async fn run(transport: impl sacp::Component<sacp::AgentToClient>) -> Result<(), sacp::Error> {
28//! ClientToAgent::builder()
29//! .name("my-client")
30//! .run_until(transport, async |cx| {
31//! // Step 1: Initialize the connection
32//! cx.send_request(InitializeRequest::new(ProtocolVersion::LATEST))
33//! .block_task().await?;
34//!
35//! // Step 2: Create a session and send a prompt
36//! cx.build_session_cwd()?
37//! .block_task()
38//! .run_until(async |mut session| {
39//! session.send_prompt("What is 2 + 2?")?;
40//! let response = session.read_to_string().await?;
41//! println!("{}", response);
42//! Ok(())
43//! })
44//! .await
45//! })
46//! .await
47//! # }
48//! ```
49//!
50//! For a complete working example, see [`yolo_one_shot_client.rs`][yolo].
51//!
52//! [yolo]: https://github.com/symposium-dev/symposium-acp/blob/main/src/sacp/examples/yolo_one_shot_client.rs
53//!
54//! ## Cookbook
55//!
56//! The [`sacp_cookbook`] crate contains practical guides and examples:
57//!
58//! - Connecting as a client
59//! - Global MCP server
60//! - Per-session MCP server with workspace context
61//! - Building agents and reusable components
62//! - Running proxies with the conductor
63//!
64//! [`sacp_cookbook`]: https://docs.rs/sacp-cookbook
65//!
66//! ## Core Concepts
67//!
68//! The [`concepts`] module provides detailed explanations of how sacp works,
69//! including connections, sessions, callbacks, ordering guarantees, and more.
70//!
71//! ## Related Crates
72//!
73//! - [`sacp-tokio`] - Tokio utilities for spawning agent processes
74//! - [`sacp-conductor`] - Binary for running proxy chains
75//!
76//! [`sacp-tokio`]: https://crates.io/crates/sacp-tokio
77//! [`sacp-conductor`]: https://crates.io/crates/sacp-conductor
78
79/// Capability management for the `_meta.symposium` object
80mod capabilities;
81/// Component abstraction for agents and proxies
82pub mod component;
83/// Core concepts for understanding and using sacp
84pub mod concepts;
85/// Cookbook of common patterns for building ACP components
86pub mod cookbook;
87/// JSON-RPC handler types for building custom message handlers
88pub mod handler;
89/// JSON-RPC connection and handler infrastructure
90mod jsonrpc;
91/// Link types for JSON-RPC connections
92pub mod link;
93/// MCP declarations (minimal)
94pub mod mcp;
95/// MCP server support for providing MCP tools over ACP
96pub mod mcp_server;
97/// Peer types for JSON-RPC connections
98pub mod peer;
99/// ACP protocol schema types - all message types, requests, responses, and supporting types
100pub mod schema;
101/// Utility functions and types
102pub mod util;
103
104pub use capabilities::*;
105
106/// JSON-RPC message types.
107///
108/// This module re-exports types from the `jsonrpcmsg` crate that are transitively
109/// reachable through the public API (e.g., via [`Channel`]).
110///
111/// Users of the `sacp` crate can use these types without adding a direct dependency
112/// on `jsonrpcmsg`.
113pub mod jsonrpcmsg {
114 pub use jsonrpcmsg::{Error, Id, Message, Params, Request, Response};
115}
116
117pub use jsonrpc::{
118 ByteStreams, Channel, Handled, IntoHandled, JrConnection, JrConnectionBuilder, JrConnectionCx,
119 JrMessage, JrMessageHandler, JrNotification, JrRequest, JrRequestCx, JrResponse,
120 JrResponsePayload, Lines, MessageCx, NullHandler, UntypedMessage,
121 responder::{ChainResponder, JrResponder, NullResponder},
122};
123
124pub use link::{AgentToClient, ClientToAgent, HasDefaultPeer, HasPeer, JrLink, ProxyToConductor};
125
126pub use peer::{AgentPeer, ClientPeer, ConductorPeer, JrPeer};
127
128pub use component::{Component, DynComponent};
129
130// Re-export BoxFuture for implementing Component traits
131pub use futures::future::BoxFuture;
132
133// Re-export the six primary message enum types at the root
134pub use schema::{
135 AgentNotification, AgentRequest, AgentResponse, ClientNotification, ClientRequest,
136 ClientResponse,
137};
138
139// Re-export commonly used infrastructure types for convenience
140pub use schema::{Error, ErrorCode};
141
142// Re-export derive macros for custom JSON-RPC types
143pub use sacp_derive::{JrNotification, JrRequest, JrResponsePayload};
144
145mod session;
146pub use session::*;
147
148/// This is a hack that must be given as the final argument of
149/// [`McpServerBuilder::tool_fn`](`crate::mcp_server::McpServerBuilder::tool_fn`) when defining tools.
150/// Look away, lest ye be blinded by its vileness!
151///
152/// Fine, if you MUST know, it's a horrific workaround for not having
153/// [return-type notation](https://github.com/rust-lang/rust/issues/109417)
154/// and for [this !@$#!%! bug](https://github.com/rust-lang/rust/issues/110338).
155/// Trust me, the need for it hurts me more than it hurts you. --nikomatsakis
156#[macro_export]
157macro_rules! tool_fn_mut {
158 () => {
159 |func, params, context| Box::pin(func(params, context))
160 };
161}
162
163/// This is a hack that must be given as the final argument of
164/// [`McpServerBuilder::tool_fn`](`crate::mcp_server::McpServerBuilder::tool_fn`) when defining stateless concurrent tools.
165/// See [`tool_fn_mut!`] for the gory details.
166#[macro_export]
167macro_rules! tool_fn {
168 () => {
169 |func, params, context| Box::pin(func(params, context))
170 };
171}
172
173/// This macro is used for the value of the `to_future_hack` parameter of
174/// [`JrConnectionBuilder::on_receive_request`] and [`JrConnectionBuilder::on_receive_request_from`].
175///
176/// It expands to `|f, req, req_cx, cx| Box::pin(f(req, req_cx, cx))`.
177///
178/// This is needed until [return-type notation](https://github.com/rust-lang/rust/issues/109417)
179/// is stabilized.
180#[macro_export]
181macro_rules! on_receive_request {
182 () => {
183 |f: &mut _, req, req_cx, cx| Box::pin(f(req, req_cx, cx))
184 };
185}
186
187/// This macro is used for the value of the `to_future_hack` parameter of
188/// [`JrConnectionBuilder::on_receive_notification`] and [`JrConnectionBuilder::on_receive_notification_from`].
189///
190/// It expands to `|f, notif, cx| Box::pin(f(notif, cx))`.
191///
192/// This is needed until [return-type notation](https://github.com/rust-lang/rust/issues/109417)
193/// is stabilized.
194#[macro_export]
195macro_rules! on_receive_notification {
196 () => {
197 |f: &mut _, notif, cx| Box::pin(f(notif, cx))
198 };
199}
200
201/// This macro is used for the value of the `to_future_hack` parameter of
202/// [`JrConnectionBuilder::on_receive_message`] and [`JrConnectionBuilder::on_receive_message_from`].
203///
204/// It expands to `|f, msg_cx, cx| Box::pin(f(msg_cx, cx))`.
205///
206/// This is needed until [return-type notation](https://github.com/rust-lang/rust/issues/109417)
207/// is stabilized.
208#[macro_export]
209macro_rules! on_receive_message {
210 () => {
211 |f: &mut _, msg_cx, cx| Box::pin(f(msg_cx, cx))
212 };
213}