### Install Mermaid Assets for Local Build Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/AGENTS.md Copies the Mermaid JavaScript assets to the local documentation directory. This is a one-time setup step before the first local build. ```bash mdbook-mermaid install . ``` -------------------------------- ### Initial Load and Polling Setup Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/src/agent-client-protocol-trace-viewer/src/viewer.html Loads initial events and starts a live polling mechanism to fetch updates every 500ms. Re-renders the UI if new events are detected. ```javascript loadEvents(); let livePolling = true; async function pollForUpdates() { if (!livePolling) return; try { const response = await fetch("/events"); if (response.ok) { const newEvents = await response.json(); if (newEvents.length !== events.length) { events = newEvents; const componentSet = new Set(); for (const event of events) { if (event.from) componentSet.add(event.from); if (event.to) componentSet.add(event.to); } components = Array.from(componentSet); components.sort((a, b) => { if (a === "client") return -1; if (b === "client") return 1; if (a === "agent") return 1; if (b === "agent") return -1; return a.localeCompare(b); }); render(); const container = document.getElementById("diagram-container"); container.scrollTop = container.scrollHeight; } } } catch (e) { } setTimeout(pollForUpdates, 500); } setTimeout(pollForUpdates, 500); ``` -------------------------------- ### Install mdbook and mdbook-mermaid Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/AGENTS.md Installs the necessary tools for building and rendering the documentation, including diagrams. ```bash cargo install mdbook mdbook-mermaid ``` -------------------------------- ### Client Example Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/README.md Example of a client implementation using the ACP Rust SDK. This demonstrates how a client can connect to an agent and exchange messages. ```rust use agent_client_protocol::prelude::*; use agent_client_protocol_tokio::ClientProcess; use tokio::io::{AsyncBufReadExt, AsyncWriteExt}; #[tokio::main] async fn main() -> Result<(), AgentError> { let mut client = ClientProcess::spawn().await?; // Send a message to the agent. client.send_message(Message::new( "agent/did-message", serde_json::json!({ "text": "Hello from the client!" }), )).await?; // Receive messages from the agent. loop { let message = client.receive_message().await?; match message.method.as_str() { "agent/did-message" => { println!("Received message: {:?}", message.params); } "agent/did-close" => { println!("Agent closed."); break; } _ => {}, } } Ok(()) } ``` -------------------------------- ### Build an Agent with Request Handlers Source: https://context7.com/agentclientprotocol/rust-sdk/llms.txt Use `Agent.builder()` to construct an agent. This example shows how to handle initialization, new sessions, and prompts, and reject unknown messages. ```rust use agent_client_protocol::{Agent, Client, ConnectTo, Dispatch, ConnectionTo, on_receive_request, on_receive_dispatch}; use agent_client_protocol::schema::{ InitializeRequest, InitializeResponse, AgentCapabilities, NewSessionRequest, NewSessionResponse, SessionId, PromptRequest, PromptResponse, StopReason, }; async fn run_agent( transport: impl ConnectTo, ) -> Result<(), agent_client_protocol::Error> { Agent.builder() .name("my-agent") // Handle initialization .on_receive_request( async |req: InitializeRequest, responder, _connection| { responder.respond( InitializeResponse::new(req.protocol_version) .agent_capabilities(AgentCapabilities::new()) ) }, on_receive_request!(), ) // Handle session creation .on_receive_request( async |_req: NewSessionRequest, responder, _connection| { responder.respond(NewSessionResponse::new(SessionId::new("session-1"))) }, on_receive_request!(), ) // Handle prompts .on_receive_request( async |_req: PromptRequest, responder, _connection| { // Process prompt and return response responder.respond(PromptResponse::new(StopReason::EndTurn)) }, on_receive_request!(), ) // Reject unknown messages .on_receive_dispatch( async |message: Dispatch, connection: ConnectionTo| { message.respond_with_error( agent_client_protocol::Error::method_not_found(), connection, ) }, on_receive_dispatch!(), ) .connect_to(transport) .await } ``` -------------------------------- ### Agent Example Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/README.md Example of an agent implementation using the ACP Rust SDK. This demonstrates how an agent can be set up and interact within the protocol. ```rust use agent_client_protocol::prelude::*; use agent_client_protocol_tokio::AgentProcess; use tokio::io::{AsyncBufReadExt, AsyncWriteExt}; #[tokio::main] async fn main() -> Result<(), AgentError> { let mut agent = AgentProcess::spawn().await?; // Send a message to the client. agent.send_message(Message::new( "agent/did-message", serde_json::json!({ "text": "Hello from the agent!" }), )).await?; // Receive messages from the client. loop { let message = agent.receive_message().await?; match message.method.as_str() { "agent/will-receive-message" => { println!("Received did-message: {:?}", message.params); } "agent/did-close" => { println!("Client closed."); break; } _ => {}, } } Ok(()) } ``` -------------------------------- ### Sparkle Integration Example Architecture Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/md/proxying-acp.md Illustrates how P/ACP can be used to integrate a third-party AI framework like Sparkle into an existing ACP agent setup. This diagram shows the Conductor orchestrator managing a proxy chain. ```mermaid flowchart LR Editor[Editor
Zed] subgraph Conductor[Conductor Orchestrator] Sparkle[Sparkle Component] Agent[Base Agent] MCP[Sparkle MCP Server] Sparkle -->|proxy chain| Agent Sparkle -.->|provides tools| MCP end Editor <-->|ACP| Conductor ``` -------------------------------- ### Event Listener Setup Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/src/agent-client-protocol-trace-viewer/src/viewer.html Sets up event listeners for UI controls like checkboxes and input sliders to trigger rendering and updates. ```javascript document .getElementById("show-acp") .addEventListener("change", render); document .getElementById("show-mcp") .addEventListener("change", render); document .getElementById("show-responses") .addEventListener("change", render); document .getElementById("swimlane-width") .addEventListener("input", (e) => { SWIMLANE_WIDTH = parseInt(e.target.value, 10); render(); }); ``` -------------------------------- ### Create MCP Server with Custom Tools Source: https://context7.com/agentclientprotocol/rust-sdk/llms.txt Use `McpServer::builder()` to define an MCP server with custom tools. This example defines 'echo' and 'add' tools using `tool_fn`. ```rust use agent_client_protocol::mcp_server::McpServer; use agent_client_protocol::{Conductor, tool_fn}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; #[derive(Debug, Deserialize, JsonSchema)] struct EchoParams { message: String, } #[derive(Debug, Serialize, JsonSchema)] struct EchoOutput { echoed: String, } #[derive(Debug, Deserialize, JsonSchema)] struct MathParams { a: i32, b: i32, } fn create_mcp_server() -> McpServer> { McpServer::builder("my-tools") .instructions("Tools for echo and math operations") .tool_fn( "echo", "Echoes back the input message", async |params: EchoParams, _cx| { Ok(EchoOutput { echoed: format!("Echo: {}", params.message), }) }, tool_fn!(), ) .tool_fn( "add", "Adds two numbers together", async |params: MathParams, _cx| { Ok(params.a + params.b) }, tool_fn!(), ) .build() } ``` -------------------------------- ### Equivalent Byte Stream Construction Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/md/transport-architecture.md Demonstrates the equivalence between the convenience constructor `from_streams` and the general `new` constructor when dealing with byte streams. This shows how to chain handler setup and serving with a byte stream transport. ```rust JrConnection::new() .on_receive_request(...) .serve_with((stdout, stdin)) .await?; ``` -------------------------------- ### Conventional Commit Examples Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/AGENTS.md Provides concrete examples of commit messages following the Conventional Commits specification for different types and scopes. ```git feat(conductor): add support for dynamic proxy chains ``` ```git fix(agent-client-protocol): resolve deadlock in message routing ``` ```git docs: update README with installation instructions ``` ```git chore: bump tokio to 1.40 ``` -------------------------------- ### Connect to an ACP Agent Source: https://context7.com/agentclientprotocol/rust-sdk/llms.txt Use `Client.builder()` to establish a connection to an ACP agent. The `connect_with` method handles transport setup and background message processing. Requires initializing the connection and then building and starting a session to send prompts. ```rust use agent_client_protocol::{Client, Agent, ConnectTo}; use agent_client_protocol::schema::{InitializeRequest, ProtocolVersion}; async fn connect_to_agent( transport: impl ConnectTo + 'static, prompt: &str, ) -> Result { Client.builder() .name("my-client") .connect_with(transport, async |connection| { // Step 1: Initialize the connection connection.send_request(InitializeRequest::new(ProtocolVersion::V1)) .block_task() .await?; // Step 2: Create a session and send prompt let mut session = connection.build_session_cwd()? .block_task() .start_session() .await?; session.send_prompt(prompt)?; session.read_to_string().await }) .await } ``` -------------------------------- ### Run Proxy Chains with Conductor Source: https://context7.com/agentclientprotocol/rust-sdk/llms.txt Use the `agent-client-protocol-conductor` binary to orchestrate proxy chains between client and agent. Examples include basic chaining, debug logging with trace visualization, and MCP bridge mode. ```bash # Chain format: proxy1 proxy2 ... agent agent-client-protocol-conductor agent \ "python proxy1.py" \ "python proxy2.py" \ "python base-agent.py" ``` ```bash # With debug logging and trace visualization agent-client-protocol-conductor \ --debug \ --trace trace.json \ --serve \ agent "sparkle-proxy" "my-tools-proxy" "claude-agent" ``` ```bash # MCP bridge mode: connect stdio to TCP MCP server agent-client-protocol-conductor mcp 8080 ``` -------------------------------- ### Subprocess Agent Connection with Tokio Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/md/migration_v0.11.x.md This example shows how to connect to a subprocess agent using `agent-client-protocol-tokio`. It simplifies spawning the agent process and wiring up its stdio. Use this when your old client code spawned an agent with `tokio::process::Command`. ```rust use agent_client_protocol as acp; use acp::schema::{InitializeRequest, ProtocolVersion}; use agent_client_protocol_tokio::AcpAgent; use std::str::FromStr; #[tokio::main] async fn main() -> acp::Result<()> { let agent = AcpAgent::from_str("python my_agent.py")?; acp::Client .builder() .name("my-client") .connect_with(agent, async |cx| { cx.send_request(InitializeRequest::new(ProtocolVersion::V1)) .block_task() .await?; // ...send more requests, build a session, etc. Ok(()) }) .await } ``` -------------------------------- ### Sequence Diagram Example Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/md/trace-viewer.md Illustrates the visual representation of message flow in the trace viewer. Shows components, messages, timings, and processing indicators. ```text client proxy:0 agent │ │ │ │──init────>│ 14ms │ │ 97ms │──init────────>│ │ │ 74ms │<── orange (request/response pair) │ │<╌╌╌╌╌╌╌╌╌╌╌╌╌╌│ │<╌╌╌╌╌╌╌╌╌╌│ │ │ │ │ │──prompt──>│ │ │ ║ 15ms │<── thickened timeline = processing │ │──prompt──────>│ │ │ 235ms │ │ │<──tools/list──● (MCP: circular arrowhead) │ │╌╌╌╌╌╌╌╌╌╌╌╌╌╌>│ │ │ │ ``` -------------------------------- ### Bridging Transformation Example Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/md/protocol.md This example shows the transformation of a transport configuration when an agent does not natively support MCP-over-ACP. The conductor adapts the MCP server to use a stdio transport via a bridge process. ```json { "sparkle": { "command": "conductor", "args": ["mcp", "54321"], "transport": "stdio" } } ``` -------------------------------- ### Build a Proxy with MCP Server in Rust Source: https://context7.com/agentclientprotocol/rust-sdk/llms.txt Use `Proxy.builder()` to create a proxy that sits between client and agent, adding MCP tools to sessions. This example demonstrates setting up a per-session MCP server with workspace-aware tools. ```rust use agent_client_protocol::mcp_server::McpServer; use agent_client_protocol::schema::NewSessionRequest; use agent_client_protocol::{Client, Proxy, Conductor, ConnectTo, on_receive_request, tool_fn}; use schemars::JsonSchema; use serde::Deserialize; #[derive(Debug, Deserialize, JsonSchema)] struct WorkspaceParams {} async fn run_proxy( transport: impl ConnectTo, ) -> Result<(), agent_client_protocol::Error> { Proxy.builder() .on_receive_request_from( Client, async move |request: NewSessionRequest, responder, connection| { // Extract session context let workspace_path = request.cwd.clone(); // Create per-session MCP server with workspace-aware tools let mcp_server = McpServer::builder("workspace-tools") .tool_fn( "get_workspace", "Returns the session's workspace directory", { let path = workspace_path.clone(); async move |_params: WorkspaceParams, _cx| { Ok(path.display().to_string()) } }, tool_fn!(), ) .build(); // Build session with MCP server and start proxying connection.build_session_from(request) .with_mcp_server(mcp_server)? .on_proxy_session_start(responder, async move |session_id| { tracing::info!(%session_id, "Session started with workspace tools"); Ok(()) }) }, on_receive_request!(), ) .connect_to(transport) .await } ``` -------------------------------- ### Network-Based Components with TCP Sockets Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/md/transport-architecture.md Establish communication between components over a network using TCP sockets. This example demonstrates connecting to a server, splitting the stream, and serving the connection with request handlers. ```rust let stream = TcpStream::connect("localhost:8080").await?; let (read, write) = stream.split(); JrConnection::new() .on_receive_request(handler) .serve_with((write.compat_write(), read.compat())) .await?; ``` -------------------------------- ### Original MCP server spec Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/md/proxying-acp.md This is an example of an original MCP server specification before any transformation by the conductor. ```json { "sparkle": { "transport": "http", "url": "acp:550e8400-e29b-41d4-a716-446655440000", "headers": {} } } ``` -------------------------------- ### Handle Agent Permission Requests Source: https://context7.com/agentclientprotocol/rust-sdk/llms.txt Implement `on_receive_request` to manage `RequestPermissionRequest` from agents. This example demonstrates auto-approving by selecting the first available option, falling back to cancellation if no options are present. ```rust use agent_client_protocol::{Client, Agent, ConnectTo, on_receive_request}; use agent_client_protocol::schema::{ InitializeRequest, ProtocolVersion, RequestPermissionRequest, RequestPermissionResponse, RequestPermissionOutcome, SelectedPermissionOutcome, }; async fn client_with_permissions( transport: impl ConnectTo + 'static, ) -> Result<(), agent_client_protocol::Error> { Client.builder() .name("permission-client") .on_receive_request( async move |request: RequestPermissionRequest, responder, _connection| { // Auto-approve by selecting the first option (YOLO mode) let option_id = request.options.first().map(|opt| opt.option_id.clone()); if let Some(id) = option_id { responder.respond(RequestPermissionResponse::new( RequestPermissionOutcome::Selected(SelectedPermissionOutcome::new(id)), )) } else { responder.respond(RequestPermissionResponse::new( RequestPermissionOutcome::Cancelled, )) } }, on_receive_request!(), ) .connect_with(transport, async |connection| { connection.send_request(InitializeRequest::new(ProtocolVersion::V1)) .block_task() .await?; Ok(()) }) .await } ``` -------------------------------- ### Get Content Preview Utility Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/src/agent-client-protocol-trace-viewer/src/viewer.html Extracts a concise preview string from an event object based on its method and parameters. Supports specific message types like 'session/prompt' and 'session/update'. ```javascript function getContentPreview(event) { // session/prompt - show the prompt text if (event.method === "session/prompt") { const promptText = event.params?.prompt?.[0]?.text; if (promptText) return promptText; } // session/update - varies by update type if (event.method === "session/update") { const update = event.params?.update; if (!update) return null; switch (update.sessionUpdate) { case "agent_message_chunk": return update.content?.text; case "tool_call": const toolName = update._meta?.claudeCode?.toolName || update.title; return toolName ? `📦 ${toolName}` : null; case "tool_call_update": const tool = update._meta?.claudeCode?.toolName; const status = update.status; if (tool && status) return `${status}: ${tool}`; return status; case "available_commands_update": const count = update.availableCommands?.length; return count ? `${count} commands` : null; } } return null; } ``` -------------------------------- ### Serve the Documentation Locally Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/AGENTS.md Builds and serves the documentation locally, allowing for live preview during development. ```bash mdbook serve ``` -------------------------------- ### Build the Documentation Book Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/AGENTS.md Compiles the documentation from Markdown files into static HTML. ```bash mdbook build ``` -------------------------------- ### Minimal Agent Skeleton in Rust Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/md/migration_v0.11.x.md This snippet demonstrates a basic agent implementation using the Rust SDK, including request and notification handlers for initialization and prompting. It sets up the agent's name and connects to byte streams. ```rust use agent_client_protocol as acp; use acp::schema::{ AgentCapabilities, CancelNotification, InitializeRequest, InitializeResponse, PromptRequest, PromptResponse, StopReason, }; use acp::{Client, Dispatch}; #[tokio::main] async fn main() -> acp::Result<()> { let outgoing = todo!("create the agent's outgoing byte stream"); let incoming = todo!("create the agent's incoming byte stream"); acp::Agent .builder() .name("my-agent") .on_receive_request( async move |request: InitializeRequest, responder, _cx| { responder.respond( InitializeResponse::new(request.protocol_version) .agent_capabilities(AgentCapabilities::new()), ) }, acp::on_receive_request!(), ) .on_receive_request( async move |_request: PromptRequest, responder, _cx| { responder.respond(PromptResponse::new(StopReason::EndTurn)) }, acp::on_receive_request!(), ) .on_receive_notification( async move |_notification: CancelNotification, _cx| { Ok(()) }, acp::on_receive_notification!(), ) .connect_to(acp::ByteStreams::new(outgoing, incoming)) .await } ``` -------------------------------- ### Conductor Proxy Mode Initialization Flow Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/md/conductor.md Illustrates the sequence diagram for initializing components in conductor's proxy mode. The conductor spawns all components, and initialization proceeds sequentially with a proxy capability handshake. ```mermaid sequenceDiagram participant Editor participant Conductor participant Sparkle as Component1
(Sparkle) participant Agent as Component2
(Agent) Note over Conductor: Spawns both components at startup
from CLI args Editor->>Conductor: acp/initialize [I0] Conductor->>Sparkle: acp/initialize (offers proxy capability) [I0] Note over Sparkle: Sees proxy capability offer, knows it has a successor Sparkle->>Conductor: _proxy/successor/request(acp/initialize) [I1] Note over Conductor: Unwraps request, knows Agent is last in chain Conductor->>Agent: acp/initialize (NO proxy capability - agent is last) [I1] Agent-->>Conductor: initialize response (capabilities) [I1] Conductor-->>Sparkle: _proxy/successor response [I1] Note over Sparkle: Sees Agent's capabilities, prepares response Sparkle-->>Conductor: initialize response (accepts proxy capability) [I0] Note over Conductor: Verifies Sparkle accepted proxy. If not, would fail with error. Conductor-->>Editor: initialize response [I0] ``` -------------------------------- ### Run Conductor in MCP Mode Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/md/conductor.md Starts the conductor to bridge MCP JSON-RPC over stdio to raw JSON-RPC over a specified TCP port. This is used for MCP-over-ACP communication. ```bash conductor mcp 54321 ``` -------------------------------- ### Filter Tools Dynamically with Rust SDK Source: https://context7.com/agentclientprotocol/rust-sdk/llms.txt Control tool availability at runtime using `disable_tool`, `enable_tool`, and `disable_all_tools`. This example shows conditional disabling of an admin tool. ```rust use agent_client_protocol::mcp_server::McpServer; use agent_client_protocol::{Conductor, tool_fn}; use schemars::JsonSchema; use serde::Deserialize; #[derive(Debug, Deserialize, JsonSchema)] struct Params {} fn build_filtered_server( enable_admin: bool, ) -> Result>, agent_client_protocol::Error> { let mut builder = McpServer::builder("filtered-server") .tool_fn("safe_operation", "A safe tool anyone can use", async |_p: Params, _cx| Ok("safe result"), tool_fn!()) .tool_fn("admin_operation", "Admin-only dangerous tool", async |_p: Params, _cx| Ok("admin result"), tool_fn!()) .tool_fn("experimental", "Experimental feature", async |_p: Params, _cx| Ok("experimental result"), tool_fn!()); // Conditionally disable admin tool if !enable_admin { builder = builder.disable_tool("admin_operation")?; } // Or use allow-list approach // builder = builder // .disable_all_tools() // .enable_tool("safe_operation")?; Ok(builder.build()) } ``` -------------------------------- ### Implement McpTool Trait for Custom Tools Source: https://context7.com/agentclientprotocol/rust-sdk/llms.txt Implement the `McpTool` trait directly for more control over tool behavior and access to the MCP connection context. This example implements a `FileReadTool`. ```rust use agent_client_protocol::mcp_server::{McpTool, McpConnectionTo}; use agent_client_protocol::role::Role; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use std::future::Future; #[derive(JsonSchema, Deserialize)] struct FileReadInput { path: String, } #[derive(JsonSchema, Serialize)] struct FileReadOutput { content: String, size: usize, } struct FileReadTool; impl McpTool for FileReadTool { type Input = FileReadInput; type Output = FileReadOutput; fn name(&self) -> String { "read_file".to_string() } fn description(&self) -> String { "Reads content from a file".to_string() } fn call_tool( &self, input: FileReadInput, _context: McpConnectionTo, ) -> impl Future> + Send { async move { let content = std::fs::read_to_string(&input.path) .map_err(|e| agent_client_protocol::Error::internal_error() .data(format!("Failed to read file: {e}")))?; let size = content.len(); Ok(FileReadOutput { content, size }) } } } ``` -------------------------------- ### Agent Client Protocol Connection Lifecycle Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/md/design.md Illustrates the connection states from building to running, including reactive and active modes. ```rust use agent_client_protocol::client::Client; use agent_client_protocol::agent::Agent; // Reactive mode (e.g., for Agents) async fn run_agent_connection(transport: impl agent_client_protocol::transport::Transport) { let mut connection = agent_client_protocol::builder() .connect_to(transport) .await .expect("Failed to connect"); connection.on_receive_request(|req| async move { // Handle incoming requests println!("Received request: {:?}", req); // Return a response or notification Ok(()) }); // The connection will run its dispatch loop until the transport closes. connection.wait_closed().await; } // Active mode (e.g., for Clients) async fn run_client_interaction(transport: impl agent_client_protocol::transport::Transport) { agent_client_protocol::builder() .connect_with(transport, |mut connection| async move { // Perform actions using the connection let request = agent_client_protocol::schema::PromptRequest { prompt: "Hello?".to_string() }; connection.send_request(Agent, request).await?; // The closure returns when the interaction is complete or the connection is closed. Ok(()) }) .await .expect("Client interaction failed"); } ``` -------------------------------- ### Conductor Command-Line Usage Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/md/proxying-acp.md Demonstrates how to run the conductor in agent mode to manage a proxy chain or in MCP mode to bridge stdio to TCP for MCP-over-ACP. ```bash # Agent mode - manages proxy chain conductor agent sparkle-acp claude-code-acp ``` ```bash # MCP mode - bridges stdio to TCP for MCP-over-ACP conductor mcp 54321 ``` -------------------------------- ### View Trace with Trace Viewer Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/md/trace-viewer.md Launches the trace viewer to visualize the captured message flow. This can be done during or after the conductor run. ```bash agent-client-trace-viewer ./trace.jsons ``` -------------------------------- ### Create MCP Connection (_mcp/connect) Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/md/protocol.md Use this method to establish a new MCP connection, equivalent to running a command. The `acp_url` identifies the target component. ```json { "jsonrpc": "2.0", "id": 1, "method": "_mcp/connect", "params": { "acp_url": "acp:550e8400-e29b-41d4-a716-446655440000" } } ``` ```json { "jsonrpc": "2.0", "id": 1, "result": { "connection_id": "conn-123" } } ``` -------------------------------- ### Generate Rainbow Colors for Event Pairs Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/src/agent-client-protocol-trace-viewer/src/viewer.html Provides a function to get a color from a predefined rainbow palette based on an index. This is used to visually distinguish request/response pairs in the sequence diagram. ```javascript const RAINBOW_COLORS = [ "#ff6b6b", // red "#ffa94d", // orange "#ffd43b", // yellow "#69db7c", // green "#4dabf7", // blue "#9775fa", // purple "#f783ac", // pink "#20c997", // teal "#a9e34b", // lime "#e599f7", // violet ]; function getColor(index) { return RAINBOW_COLORS[index % RAINBOW_COLORS.length]; } ``` -------------------------------- ### Editor to Conductor: Initialize Request Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/md/proxying-acp.md The initial 'initialize' request sent from the Editor to the Conductor. It includes protocol version, empty capabilities, and client information. ```json { "jsonrpc": "2.0", "id": "I0", "method": "initialize", "params": { "protocolVersion": "0.1.0", "capabilities": {}, "clientInfo": { "name": "Zed", "version": "0.1.0" } } } ``` -------------------------------- ### Byte Stream Convenience Constructor Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/md/transport-architecture.md A convenience constructor for setting up connections with byte streams. It simplifies the process of creating a connection from streams and then serving it, equivalent to using `JrConnection::new()` followed by `serve_with`. ```rust JrConnection::from_streams(stdout, stdin) .on_receive_request(...) .serve() .await?; ``` -------------------------------- ### Sparkle to Conductor: Initialize Response (Accepting Proxy) Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/md/proxying-acp.md Sparkle's final 'initialize' response to Conductor, confirming acceptance of the PROXY capability. ```json { "jsonrpc": "2.0", "id": "I0", "result": { "protocolVersion": "0.1.0", "capabilities": { "_meta": { "symposium": { "version": "1.0", "proxy": true } } }, "serverInfo": { "name": "Sparkle + claude-code-acp", "version": "0.1.0" } } } ``` -------------------------------- ### Spawn External Agent Processes in Rust Source: https://context7.com/agentclientprotocol/rust-sdk/llms.txt Use `AcpAgent` from `agent-client-protocol-tokio` to spawn and communicate with external agent processes via stdio. This example shows how to parse agent commands, use pre-configured agents, and add debug logging. ```rust use agent_client_protocol_tokio::{AcpAgent, LineDirection}; use agent_client_protocol::{Client, ConnectTo}; use agent_client_protocol::schema::{InitializeRequest, ProtocolVersion}; use std::str::FromStr; async fn connect_to_external_agent() -> Result<(), agent_client_protocol::Error> { // Parse from command string let agent = AcpAgent::from_str("python my_agent.py --verbose")?; // Or use pre-configured agents let claude_agent = AcpAgent::zed_claude_code(); let gemini_agent = AcpAgent::google_gemini(); // Connect with optional debug logging let agent_with_debug = AcpAgent::from_str("node agent.js")? .with_debug(|line, direction| { match direction { LineDirection::Stdin => eprintln!("-> {}", line), LineDirection::Stdout => eprintln!("<- {}", line), LineDirection::Stderr => eprintln!("!! {}", line), } }); // Use the agent as a transport Client.builder() .name("external-client") .connect_with(agent_with_debug, async |connection| { connection.send_request(InitializeRequest::new(ProtocolVersion::V1)) .block_task() .await?; Ok(()) }) .await } ``` -------------------------------- ### Proxy Capability Initialization Response Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/md/protocol.md The proxy component responds with this message to confirm its capability to act as a proxy and handle _proxy/successor/* messages. ```json { "jsonrpc": "2.0", "id": 1, "result": { "protocolVersion": "0.7.0", "serverInfo": {}, "capabilities": {}, "_meta": { "proxy": true } } } ``` -------------------------------- ### Define Transport Setup Trait Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/md/transport-architecture.md Defines the `IntoJrConnectionTransport` trait for bridging internal channels with I/O. Implementations consume owned resources, spawn actors via `cx.spawn()`, and operate solely on channels without knowledge of message correlation. ```rust pub trait IntoJrConnectionTransport { fn setup_transport( self, cx: &JrConnectionCx, outgoing_rx: mpsc::UnboundedReceiver, incoming_tx: mpsc::UnboundedSender, ) -> Result<(), Error>; } ``` -------------------------------- ### Client Event Handling with Builder Callbacks in Rust SDK Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/md/migration_v0.11.x.md Shows how to register request and notification handlers using the `Client.builder()` and helper macros, replacing the older `impl acp::Client for T` pattern. ```rust use agent_client_protocol as acp; use acp::schema::{ RequestPermissionOutcome, RequestPermissionRequest, RequestPermissionResponse, SessionNotification, }; #[tokio::main] async fn main() -> acp::Result<()> { let transport = todo!("create the transport that connects to your agent"); acp::Client .builder() .on_receive_request( async move |_: RequestPermissionRequest, responder, _cx| { responder.respond(RequestPermissionResponse::new( RequestPermissionOutcome::Cancelled, )) }, acp::on_receive_request!(), ) .on_receive_notification( async move |notification: SessionNotification, _cx| { println!("{:?}", notification.update); Ok(()) }, acp::on_receive_notification!(), ) .connect_with(transport, async |_cx: acp::ConnectionTo| { // send requests here, e.g. `_cx.send_request(...).block_task().await?` Ok(()) }) .await } ``` -------------------------------- ### Repository Structure Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/md/introduction.md Overview of the directory structure for the Agent Client Protocol Rust SDK. ```text src/ ├── agent-client-protocol/ # Core protocol SDK ├── agent-client-protocol-tokio/ # Tokio utilities (process spawning) ├── agent-client-protocol-rmcp/ # Integration with rmcp crate ├── agent-client-protocol-cookbook/ # Usage patterns (rendered as rustdoc) ├── agent-client-protocol-derive/ # Proc macros ├── agent-client-protocol-conductor/ # Conductor binary and library ├── agent-client-protocol-test/ # Test utilities and fixtures ├── agent-client-protocol-trace-viewer/ # Trace visualization tool └── yopo/ # "You Only Prompt Once" example client ``` -------------------------------- ### Flexible Connection Construction Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/md/transport-architecture.md Builds a connection by first setting up handlers using methods like `on_receive_request` and `on_receive_notification`, and then providing the transport mechanism using `serve_with`. This separates handler logic from transport selection. ```rust // Build connection with handlers let connection = JrConnection::new() .name("my-component") .on_receive_request(|req: InitializeRequest, cx| { cx.respond(InitializeResponse::make()) }) .on_receive_notification(|notif: SessionNotification, _cx| { Ok(()) }); // Provide transport at the end connection.serve_with(transport).await?; ``` -------------------------------- ### Trace Viewer Workflow Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/md/trace-viewer.md This section outlines the steps to run the conductor with tracing enabled and then view the generated trace file using the trace viewer. ```APIDOC ## Trace Viewer Workflow ### Description This workflow demonstrates how to enable tracing in the agent-client-protocol-conductor and then visualize the captured trace data using the agent-client-protocol-trace-viewer. ### Workflow Steps 1. **Run conductor with tracing enabled:** ```bash agent-client-protocol-conductor agent --trace ./trace.jsons "proxy1" "proxy2" "agent" ``` This command starts the conductor, enables tracing, specifies the output file for trace events (`./trace.jsons`), and lists the components to trace. 2. **View the trace:** ```bash agent-client-protocol-trace-viewer ./trace.jsons ``` This command launches the trace viewer, which will serve an interactive HTML/JS visualization of the trace data. The viewer typically opens in a web browser at `http://localhost:PORT`. ``` -------------------------------- ### Build Conductor Binary Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/src/agent-client-protocol-conductor/README.md Builds the conductor binary in release mode. The binary will be located in target/release/agent-client-protocol-conductor. ```bash cargo build --release -p agent-client-protocol-conductor ``` -------------------------------- ### Agent Initialization Request (No Proxy Capability) Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/md/protocol.md The conductor sends this request to the final component (agent), which does not require proxy capabilities. The absence of `_meta.proxy` indicates it's an agent. ```json { "method": "initialize", "params": { "protocolVersion": "0.7.0", "capabilities": {}, "_meta": {} // No proxy capability } } ``` -------------------------------- ### Initial User Prompt JSON Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/md/proxying-acp.md The first message sent from the Editor to the Conductor, containing the user's initial prompt. ```json { "jsonrpc": "2.0", "id": "P0", "method": "session/prompt", "params": { "sessionId": "S1", "messages": [ { "role": "user", "content": "Hello! Can you help me with my code?" } ] } } ``` -------------------------------- ### Run agent-client-protocol-conductor programmatically Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/md/trace-viewer.md Instantiate and run the conductor programmatically in Rust. Configure the conductor's name, components, MCP bridge mode, and trace output file. ```rust Conductor::new(name, components, mcp_bridge_mode) .trace_to("./trace.jsons") .run(transport) .await ``` -------------------------------- ### Replace Connection Construction in Rust Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/md/migration_v0.11.x.md Demonstrates the replacement of `ClientSideConnection::new` and `AgentSideConnection::new` with builder patterns and `ByteStreams` in agent-client-protocol 0.11. ```rust Client.builder().connect_with(ByteStreams::new(outgoing, incoming), async |cx| { ... }) Agent.builder().connect_to(ByteStreams::new(outgoing, incoming)).await? ``` -------------------------------- ### Old Session Management in Rust SDK Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/md/migration_v0.11.x.md Illustrates the previous method of creating and interacting with sessions, which required manual session ID management. ```rust let session = conn .new_session(NewSessionRequest::new(cwd)) .await?; conn.prompt(PromptRequest::new( session.session_id.clone(), vec!["Hello".into()], )) .await?; ``` -------------------------------- ### Sparkle to Conductor: Proxy Successor Request (Session New) Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/md/proxying-acp.md Sparkle forwards the 'session/new' request, adding its own MCP server to the tools. This demonstrates message augmentation during proxying. ```json { "jsonrpc": "2.0", "id": "U1", "method": "_proxy/successor/request", "params": { "message": { "method": "session/new", "params": { "tools": M0 + sparkle-mcp } } } } ``` -------------------------------- ### Tokio Utilities for Agent Client Protocol Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/md/design.md Provides utilities for integrating with Tokio, including process spawning and transport helpers. ```rust use agent_client_protocol_tokio::process::spawn_agent; use agent_client_protocol_tokio::transport::stream_to_transport; use tokio::io::{AsyncRead, AsyncWrite}; // Spawn an agent process and connect via stdio let (agent_process, connection) = spawn_agent("my_agent_binary").await?; // Convert Tokio streams to ACP transports async fn handle_connection(read: R, write: W) where R: AsyncRead + Unpin + Send + 'static, W: AsyncWrite + Unpin + Send + 'static, { let mut transport = stream_to_transport(read, write); // Use the transport with agent_client_protocol // ... } ``` -------------------------------- ### Editor to Conductor: session/new Request Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/md/proxying-acp.md The initial session/new request from the Editor to Conductor, specifying available tools like the MCP filesystem. ```json { "jsonrpc": "2.0", "id": "U0", "method": "session/new", "params": { "tools": { "mcpServers": { "filesystem": { "command": "mcp-filesystem", "args": [] } } } } } ``` -------------------------------- ### Agent Client Protocol Crate Organization Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/md/design.md The core SDK provides role types, connection builders, message handling functions, protocol types, and an MCP server builder. ```rust use agent_client_protocol::client::Client; use agent_client_protocol::agent::Agent; use agent_client_protocol::proxy::Proxy; use agent_client_protocol::conductor::Conductor; // Example usage of role types let client: Client = Client; let agent: Agent = Agent; let proxy: Proxy = Proxy; let conductor: Conductor = Conductor; // Connection builders let builder = agent_client_protocol::builder(); let connection = builder.connect_to(transport).await?; let connection = builder.connect_with(transport, |conn| async move { /* user code */ }).await?; // Message handling connection.on_receive_request(|req| async move { // handle request }); connection.on_receive_notification(|notif| async move { // handle notification }); connection.on_receive_dispatch(|disp| async move { // handle dispatch }); // Protocol types (example) use agent_client_protocol::schema::InitializeRequest; let request: InitializeRequest = InitializeRequest { ... }; // MCP server builder let mcp_server_builder = agent_client_protocol::mcp::server::builder(); ``` -------------------------------- ### Agent to Conductor: Initialize Response Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/md/proxying-acp.md The response from the Base Agent after initialization. It includes protocol version, capabilities, and server information. ```json { "jsonrpc": "2.0", "id": "I1", "result": { "protocolVersion": "0.1.0", "capabilities": {}, "serverInfo": { "name": "claude-code-acp", "version": "0.1.0" } } } ``` -------------------------------- ### Incoming Message Flow Diagram Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/md/transport-architecture.md Illustrates the sequence of actors and message transformations for an incoming message, from I/O source to handler or reply actor. ```text I/O Source | Transport Incoming Actor | - Parse (byte streams) | - Or forward directly (channels) v | jsonrpcmsg::Message | Incoming Protocol Actor | - Route responses → reply_actor | - Route requests → registered handlers v Handler or Reply Actor ``` -------------------------------- ### Sparkle to Conductor: Proxy Successor Request (Initialize) Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/md/proxying-acp.md Sparkle uses a '_proxy/successor/request' to forward the 'initialize' message to the next component (Conductor in this case), wrapping the original parameters. ```json { "jsonrpc": "2.0", "id": "I1", "method": "_proxy/successor/request", "params": { "message": { "method": "initialize", "params": { "protocolVersion": "0.1.0", "capabilities": {}, "clientInfo": { "name": "Sparkle", "version": "0.1.0" } } } } } ``` -------------------------------- ### Conductor to Sparkle: Initialize Request with Proxy Capability Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/md/proxying-acp.md The 'initialize' request from Conductor to Sparkle, offering PROXY capability. This allows Sparkle to forward messages to other components. ```json { "jsonrpc": "2.0", "id": "I0", "method": "initialize", "params": { "protocolVersion": "0.1.0", "capabilities": { "_meta": { "symposium": { "version": "1.0", "proxy": true } } }, "clientInfo": { "name": "Conductor", "version": "0.1.0" } } } ``` -------------------------------- ### Conductor to Agent: Initialize Request (Unwrapped) Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/md/proxying-acp.md The 'initialize' request sent from Conductor to the Base Agent. It is unwrapped from the proxy request and does not include PROXY capability. ```json { "jsonrpc": "2.0", "id": "I1", "method": "initialize", "params": { "protocolVersion": "0.1.0", "capabilities": {}, "clientInfo": { "name": "Sparkle", "version": "0.1.0" } } } ``` -------------------------------- ### Conductor to Agent: Session New Request (Augmented Tools) Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/md/proxying-acp.md The 'session/new' request sent to the Base Agent, containing the original tools plus any added by intermediate components like Sparkle. ```json { "jsonrpc": "2.0", "id": "U1", "method": "session/new", "params": { "tools": M0 + sparkle-mcp } } ``` -------------------------------- ### Connect to an ACP Agent Source: https://github.com/agentclientprotocol/rust-sdk/blob/main/src/agent-client-protocol/README.md Use this snippet to connect to an existing ACP agent as a client. It demonstrates initializing the connection and sending an initial protocol version request. ```rust use agent_client_protocol::{Client, Agent, ConnectTo}; use agent_client_protocol::schema::{InitializeRequest, ProtocolVersion}; Client.builder() .name("my-client") .connect_with(transport, async |cx| { // Initialize the connection cx.send_request(InitializeRequest::new(ProtocolVersion::V1)) .block_task() .await?; Ok(()) }) .await?; ```