Skip to main content

ConnectTo

Trait ConnectTo 

Source
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 Channel implement Serve<R> for all R since 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:

  1. serve(client) - Serve by forwarding to another component (most components implement this)
  2. 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§

Source

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 (implements Serve<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§

Source

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 Channel that can be used to communicate with this component
  • A BoxFuture that 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.

Implementors§

Source§

impl<OB, IB, R: Role> ConnectTo<R> for ByteStreams<OB, IB>
where OB: AsyncWrite + Send + 'static, IB: AsyncRead + Send + 'static,

Source§

impl<OutgoingSink, IncomingStream, R: Role> ConnectTo<R> for Lines<OutgoingSink, IncomingStream>
where OutgoingSink: Sink<String, Error = Error> + Send + 'static, IncomingStream: Stream<Item = Result<String>> + Send + 'static,

Source§

impl<R, H, Run> ConnectTo<<R as Role>::Counterpart> for Builder<R, H, Run>
where R: Role, H: HandleDispatchFrom<R::Counterpart> + 'static, Run: RunWithConnectionTo<R::Counterpart> + 'static,

Source§

impl<R: Role> ConnectTo<R> for Channel

Source§

impl<R: Role> ConnectTo<R> for DynConnectTo<R>

Source§

impl<Run> ConnectTo<Client> for McpServer<Client, Run>
where Run: RunWithConnectionTo<Client> + 'static,