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
impl McpProxyBuilder
Sourcepub fn separator(self, sep: impl Into<String>) -> Self
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".
Sourcepub fn notification_sender(self, tx: NotificationSender) -> Self
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);Sourcepub fn instructions(self, instructions: impl Into<String>) -> Self
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.
Sourcepub fn backend_client(
self,
namespace: impl Into<String>,
client: McpClient,
) -> Self
pub fn backend_client( self, namespace: impl Into<String>, client: McpClient, ) -> Self
Sourcepub async fn backend(
self,
namespace: impl Into<String>,
transport: impl ClientTransport,
) -> Self
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().
Sourcepub async fn backend_try(
self,
namespace: impl Into<String>,
transport: impl ClientTransport,
) -> Result<Self>
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?;Sourcepub fn backend_layer<L>(self, layer: L) -> Selfwhere
L: Layer<BoxCloneService<RouterRequest, RouterResponse, Infallible>> + Send + 'static,
L::Service: Service<RouterRequest, Response = RouterResponse> + Clone + Send + 'static,
<L::Service as Service<RouterRequest>>::Error: Display + Send,
<L::Service as Service<RouterRequest>>::Future: Send,
pub fn backend_layer<L>(self, layer: L) -> Selfwhere
L: Layer<BoxCloneService<RouterRequest, RouterResponse, Infallible>> + Send + 'static,
L::Service: Service<RouterRequest, Response = RouterResponse> + Clone + Send + 'static,
<L::Service as Service<RouterRequest>>::Error: Display + Send,
<L::Service as Service<RouterRequest>>::Future: Send,
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.
Sourcepub async fn build(self) -> Result<ProxyBuildResult>
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.
Sourcepub async fn build_strict(self) -> Result<McpProxy>
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).