Skip to main content

McpProxyBuilder

Struct McpProxyBuilder 

Source
pub struct McpProxyBuilder { /* private fields */ }
Expand description

Builder for constructing an McpProxy.

§Example

use tower_mcp::proxy::McpProxy;
use tower_mcp::client::StdioClientTransport;

let proxy = McpProxy::builder("my-proxy", "1.0.0")
    .backend("db", StdioClientTransport::spawn("db-server", &[]).await?)
    .await
    .separator(".")
    .build()
    .await?;

§Per-Backend Middleware

Apply Tower middleware to individual backends using backend_layer():

use std::time::Duration;
use tower::timeout::TimeoutLayer;

let proxy = McpProxy::builder("proxy", "1.0.0")
    .backend("slow-api", slow_transport).await
    .backend_layer(TimeoutLayer::new(Duration::from_secs(60)))
    .backend("fast-db", fast_transport).await
    .backend_layer(TimeoutLayer::new(Duration::from_secs(5)))
    .build()
    .await?;

Implementations§

Source§

impl McpProxyBuilder

Source

pub fn separator(self, sep: impl Into<String>) -> Self

Set the namespace separator (default: _).

The separator is inserted between the backend namespace and the tool/resource/prompt name. For example, with separator "_" and namespace "db", a tool named "query" becomes "db_query".

Source

pub fn notification_sender(self, tx: NotificationSender) -> Self

Set a notification sender for forwarding backend list-changed notifications to downstream clients.

When a backend emits tools/list_changed, resources/list_changed, or prompts/list_changed, the proxy refreshes its cache and then forwards the notification through this sender so transports can relay it to connected clients.

§Example
use tower_mcp::context::notification_channel;

let (notif_tx, notif_rx) = notification_channel(32);
let proxy = McpProxy::builder("proxy", "1.0.0")
    .notification_sender(notif_tx)
    .backend("db", transport).await
    .build().await?;

let mut transport = GenericStdioTransport::with_notifications(proxy, notif_rx);
Source

pub fn instructions(self, instructions: impl Into<String>) -> Self

Set custom instructions for the proxy’s initialize response.

When set, this overrides the default behavior of aggregating backend instructions. Use this to provide a curated description of the proxy’s capabilities.

Source

pub fn backend_client( self, namespace: impl Into<String>, client: McpClient, ) -> Self

Add a backend from a connected McpClient.

Note: backends added this way will not have automatic cache refresh on list-changed notifications. Use backend() with a transport for full notification support.

Source

pub async fn backend( self, namespace: impl Into<String>, transport: impl ClientTransport, ) -> Self

Add a backend from a ClientTransport.

The transport will be connected immediately with a notification handler that watches for list-changed events. Initialization happens during build().

Source

pub async fn backend_try( self, namespace: impl Into<String>, transport: impl ClientTransport, ) -> Result<Self>

Add a backend from a transport, returning an error on connection failure.

Unlike backend() which silently skips failed connections, this method returns an error so the caller can decide how to handle it.

§Example
let builder = McpProxy::builder("proxy", "1.0.0")
    .backend_try("db", transport).await?;
Source

pub fn backend_layer<L>(self, layer: L) -> Self

Apply a Tower layer to the most recently added backend.

The layer wraps the backend’s dispatch service, allowing standard Tower middleware (timeout, rate limit, concurrency limit, etc.) to be applied per-backend.

Repeated calls stack: each layer wraps the service composed so far, so the layer added last sees a request first. In the example below a request passes the timeout, then the rate limiter, then reaches the backend, and a ServiceBuilder composing the same middleware in one call remains equivalent.

Layers that produce errors (e.g., TimeoutLayer) are automatically wrapped with CatchError to convert errors into JSON-RPC error responses, maintaining the Error = Infallible contract.

§Example
use std::time::Duration;
use tower::limit::RateLimitLayer;
use tower::timeout::TimeoutLayer;

let proxy = McpProxy::builder("proxy", "1.0.0")
    .backend("slow", transport).await
    .backend_layer(RateLimitLayer::new(50, Duration::from_secs(1)))
    .backend_layer(TimeoutLayer::new(Duration::from_secs(30)))
    .build()
    .await?;
§Panics

Panics if no backend has been added yet.

Source

pub async fn build(self) -> Result<ProxyBuildResult>

Build the proxy, initializing all backends concurrently.

Each backend runs the MCP initialize handshake and discovers its capabilities (tools, resources, prompts). Backends that fail to initialize are logged and skipped.

Returns a ProxyBuildResult containing the proxy and any backends that were skipped due to initialization failures. Check result.skipped to see which backends failed and why.

For backends added via backend(), a background task is spawned that watches for list-changed notifications and automatically refreshes the affected cache.

§Errors

Returns an error if no backends were configured or if all backends failed to initialize.

Source

pub async fn build_strict(self) -> Result<McpProxy>

Build the proxy, failing if any backend fails to initialize.

Unlike build() which skips failed backends, this method returns an error if any backend fails to connect or initialize.

§Errors

Returns the first initialization failure encountered (after waiting for all backends to attempt initialization).

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<A, B, T> HttpServerConnExec<A, B> for T
where B: Body,

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more