Skip to main content

Module proxies

Module proxies 

Source
Expand description

Building proxies that intercept and modify messages.

A proxy sits between a client and an agent, intercepting messages in both directions. This is how you add capabilities like MCP tools, logging, or message transformation.

§The Proxy Role Type

Proxies use the Proxy role type, which has two peers:

  • Client - messages from/to the client direction
  • Agent - messages from/to the agent direction

Unlike simpler links, there’s no default peer - you must always specify which direction you’re communicating with.

§Default Forwarding

By default, Proxy forwards all messages it doesn’t handle. This means a minimal proxy that does nothing is just:

Proxy.builder()
    .connect_to(transport)
    .await?;

All messages pass through unchanged.

§Intercepting Messages

To intercept specific messages, use on_receive_*_from with explicit peers:

Proxy.builder()
    // Intercept requests from the client
    .on_receive_request_from(Client, async |req: ProcessRequest, responder, cx| {
        // Modify the request
        let modified = ProcessRequest {
            data: format!("prefix: {}", req.data),
        };

        // Forward to agent and relay the response back
        cx.send_request_to(Agent, modified)
            .forward_response_to(responder)
    }, agent_client_protocol::on_receive_request!())
    .connect_to(transport)
    .await?;

Messages you don’t handle are forwarded automatically.

§Adding MCP Servers

A common use case is adding tools via MCP. You can add them globally (available in all sessions) or per-session.

§Global MCP Server

Proxy.builder()
    .with_mcp_server(my_mcp_server)
    .connect_to(transport)
    .await?;

§Per-Session MCP Server

Proxy.builder()
    .on_receive_request_from(Client, async |req: NewSessionRequest, responder, cx| {
        let my_mcp_server = McpServer::<Conductor, _>::builder("tools").build();
        cx.build_session_from(req)
            .with_mcp_server(my_mcp_server)?
            .on_proxy_session_start(responder, async |session_id| {
                // Session started with MCP server attached
                Ok(())
            })
    }, agent_client_protocol::on_receive_request!())
    .connect_to(transport)
    .await?;

§The Conductor

Proxies don’t run standalone - they’re orchestrated by a conductor. The conductor:

  • Spawns proxy processes
  • Chains them together
  • Connects the final proxy to the agent

The agent-client-protocol-conductor crate provides a conductor binary. You configure it with a list of proxies to run.

§Proxy Chains

Multiple proxies can be chained:

Client <-> Proxy A <-> Proxy B <-> Agent

Each proxy sees messages from its perspective:

  • Client is “toward the client” (Proxy A, or conductor if first)
  • Agent is “toward the agent” (Proxy B, or agent if last)

Messages flow through each proxy in order. Each can inspect, modify, or handle messages before they continue.

§Summary

TaskApproach
Forward everythingJust connect_to(transport)
Intercept specific messageson_receive_*_from with explicit peers
Add global toolswith_mcp_server on builder
Add per-session toolswith_mcp_server on session builder