pub trait ConnectTo<R: Role>: Send + 'static {
// Required method
fn connect_to(
self,
client: impl ConnectTo<R::Counterpart>,
) -> impl Future<Output = Result<()>> + Send;
// Provided method
fn into_channel_and_future(
self,
) -> (Channel, BoxFuture<'static, Result<()>>)
where Self: Sized { ... }
}Expand description
A component that can exchange JSON-RPC messages to an endpoint playing the role R
(e.g., an ACP Agent or an MCP Server).
This trait represents anything that can communicate via JSON-RPC messages over channels - agents, proxies, in-process connections, or any ACP-speaking component.
The type parameter R is the role that this component serves (its counterpart).
For example:
- An agent implements
Serve<Client>- it serves clients - A proxy implements
Serve<Conductor>- it serves conductors - Transports like
ChannelimplementServe<R>for allRsince they’re role-agnostic
§Component Types
The trait is implemented by several built-in types representing different communication patterns:
ByteStreams: A component communicating over byte streams (stdin/stdout, sockets, etc.)Channel: A component communicating via in-process message channels (for testing or direct connections)AcpAgent: An external agent running in a separate process with stdio communication- Custom components: Proxies, transformers, or any ACP-aware service
§Two Ways to Serve
Components can be used in two ways:
serve(client)- Serve by forwarding to another component (most components implement this)into_server()- Convert into a channel endpoint and server future (base cases implement this)
Most components only need to implement serve(client) - the into_server() method has a default
implementation that creates an intermediate channel and calls serve.
§Implementation Example
use agent_client_protocol::{Agent, Result, Serve, role::Client};
struct MyAgent {
config: AgentConfig,
}
impl Serve<Client> for MyAgent {
async fn serve(self, client: impl Serve<Client::Counterpart>) -> Result<()> {
// Set up connection that forwards to client
Agent.builder()
.name("my-agent")
.on_receive_request(async |req: MyRequest, cx| {
// Handle request
cx.respond(MyResponse { status: "ok".into() })
})
.serve(client)
.await
}
}§Heterogeneous Collections
For storing different component types in the same collection, use DynConnectTo:
use agent_client_protocol::Client;
let components: Vec<DynConnectTo<Client>> = vec![
DynConnectTo::new(proxy1),
DynConnectTo::new(proxy2),
DynConnectTo::new(agent),
];Required Methods§
Sourcefn connect_to(
self,
client: impl ConnectTo<R::Counterpart>,
) -> impl Future<Output = Result<()>> + Send
fn connect_to( self, client: impl ConnectTo<R::Counterpart>, ) -> impl Future<Output = Result<()>> + Send
Serve this component by forwarding to a client component.
Most components implement this method to set up their connection and forward messages to the provided client.
§Arguments
client- The component to forward messages to (implementsServe<R::Counterpart>)
§Returns
A future that resolves when the component stops serving, either successfully
or with an error. The future must be Send.
Provided Methods§
Sourcefn into_channel_and_future(self) -> (Channel, BoxFuture<'static, Result<()>>)where
Self: Sized,
fn into_channel_and_future(self) -> (Channel, BoxFuture<'static, Result<()>>)where
Self: Sized,
Convert this component into a channel endpoint and server future.
This method returns:
- A
Channelthat can be used to communicate with this component - A
BoxFuturethat runs the component’s server logic
The default implementation creates an intermediate channel pair and calls serve
on one endpoint while returning the other endpoint for the caller to use.
Base cases like Channel and ByteStreams override this to avoid unnecessary copying.
§Returns
A tuple of (Channel, BoxFuture) where the channel is for the caller to use
and the future must be spawned to run the server.
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.