Expand description
§tower-mcp
Tower-native Model Context Protocol (MCP) implementation for Rust.
This crate provides a composable, middleware-friendly approach to building MCP servers and clients using the Tower service abstraction.
§Philosophy
Unlike framework-style MCP implementations, tower-mcp treats MCP as just another
protocol that can be served through Tower’s Service trait. This means:
- Standard tower middleware (tracing, metrics, rate limiting, auth) just works
- Same service can be exposed over multiple transports (stdio, HTTP, WebSocket)
- Easy integration with existing tower-based applications (axum, tonic, etc.)
§Familiar to axum Users
If you’ve used axum, tower-mcp’s API will feel familiar. We’ve adopted axum’s patterns for a consistent Rust web ecosystem experience:
- Extractor pattern: Tool handlers use extractors like
extract::State<T>,extract::Json<T>, andextract::Context- just like axum’s request extractors - Router composition:
McpRouter::merge()andMcpRouter::nest()work like axum’s router methods for combining routers - Per-route middleware: Apply Tower layers to individual tools, resources, or
prompts via
.layer()on builders - Builder pattern: Fluent builders for tools, resources, and prompts
use std::sync::Arc;
use tower_mcp::{ToolBuilder, CallToolResult};
use tower_mcp::extract::{State, Json, Context};
use schemars::JsonSchema;
use serde::Deserialize;
#[derive(Clone)]
struct AppState { db_url: String }
#[derive(Deserialize, JsonSchema)]
struct SearchInput { query: String }
// Looks just like an axum handler!
let tool = ToolBuilder::new("search")
.title("Search Database")
.description("Search the database")
.extractor_handler(
Arc::new(AppState { db_url: "postgres://...".into() }),
|State(app): State<Arc<AppState>>,
ctx: Context,
Json(input): Json<SearchInput>| async move {
ctx.report_progress(0.5, Some(1.0), Some("Searching...")).await;
Ok(CallToolResult::text(format!("Found results for: {}", input.query)))
},
)
.build();§Quick Start: Server
Build an MCP server with tools, resources, and prompts:
use tower_mcp::{BoxError, McpRouter, ToolBuilder, CallToolResult, StdioTransport};
use schemars::JsonSchema;
use serde::Deserialize;
#[derive(Debug, Deserialize, JsonSchema)]
struct GreetInput {
name: String,
}
#[tokio::main]
async fn main() -> Result<(), BoxError> {
// Define a tool
let greet = ToolBuilder::new("greet")
.title("Greet")
.description("Greet someone by name")
.handler(|input: GreetInput| async move {
Ok(CallToolResult::text(format!("Hello, {}!", input.name)))
})
.build();
// Create router and run over stdio
let router = McpRouter::new()
.server_info("my-server", "1.0.0")
.tool(greet);
StdioTransport::new(router).run().await?;
Ok(())
}§Quick Start: Client
Connect to an MCP server and call tools:
use tower_mcp::BoxError;
use tower_mcp::client::{McpClient, StdioClientTransport};
#[tokio::main]
async fn main() -> Result<(), BoxError> {
// Connect to server
let transport = StdioClientTransport::spawn("my-mcp-server", &[]).await?;
let client = McpClient::connect(transport).await?;
// Initialize and list tools
client.initialize("my-client", "1.0.0").await?;
let tools = client.list_tools().await?;
// Call a tool
let result = client.call_tool("greet", serde_json::json!({"name": "World"})).await?;
println!("{:?}", result);
Ok(())
}§Key Types
§Server
McpRouter- Routes MCP requests to tools, resources, and promptsToolBuilder- Builder for defining tools with type-safe handlersResourceBuilder- Builder for defining resourcesPromptBuilder- Builder for defining promptsStdioTransport- Stdio transport for CLI servers
§Client
McpClient- Client for connecting to MCP serversStdioClientTransport- Spawn and connect to server subprocesses
§Protocol
CallToolResult- Tool execution result with contentReadResourceResult- Resource read resultGetPromptResult- Prompt expansion resultContent- Text, image, audio, or resource content
§Feature Flags
full- Enable all optional featureshttp- HTTP/SSE transport for web serverswebsocket- WebSocket transport for bidirectional communicationchildproc- Child process transport for subprocess managementoauth- OAuth 2.1 resource server support (token validation, metadata endpoint)testing- Test utilities (TestClient) for ergonomic MCP server testingdynamic-tools- Runtime registration/deregistration of tools, prompts, and resources via [DynamicToolRegistry], [DynamicPromptRegistry], [DynamicResourceRegistry], [DynamicResourceTemplateRegistry]proxy- Multi-server aggregation proxy (McpProxy)
§Middleware Placement Guide
tower-mcp supports Tower middleware at multiple levels. Choose based on scope:
| Level | Method | Scope | Use Cases |
|---|---|---|---|
| Transport | StdioTransport::layer(), HttpTransport::layer() | All MCP requests | Global timeout, rate limit, metrics |
| axum | .into_router().layer() | HTTP layer only | CORS, compression, request logging |
| Per-tool | ToolBuilder::...layer() | Single tool | Tool-specific timeout, concurrency |
| Per-resource | ResourceBuilder::...layer() | Single resource | Caching, read timeout |
| Per-prompt | PromptBuilder::...layer() | Single prompt | Generation timeout |
§Decision Tree
Where should my middleware go?
│
├─ Affects ALL MCP requests?
│ └─ Yes → Transport: StdioTransport::layer(), HttpTransport::layer(), or WebSocketTransport::layer()
│
├─ HTTP-specific (CORS, compression, headers)?
│ └─ Yes → axum: transport.into_router().layer(...)
│
├─ Only one specific tool?
│ └─ Yes → Per-tool: ToolBuilder::...handler(...).layer(...)
│
├─ Only one specific resource?
│ └─ Yes → Per-resource: ResourceBuilder::...handler(...).layer(...)
│
└─ Only one specific prompt?
└─ Yes → Per-prompt: PromptBuilder::...handler(...).layer(...)§Example: Layered Timeouts
use std::time::Duration;
use tower::timeout::TimeoutLayer;
use tower_mcp::{McpRouter, ToolBuilder, CallToolResult, HttpTransport};
use schemars::JsonSchema;
use serde::Deserialize;
#[derive(Debug, Deserialize, JsonSchema)]
struct SearchInput { query: String }
// This tool gets a longer timeout than the global default
let slow_search = ToolBuilder::new("slow_search")
.description("Thorough search (may take a while)")
.handler(|input: SearchInput| async move {
// ... slow operation ...
Ok(CallToolResult::text("results"))
})
.layer(TimeoutLayer::new(Duration::from_secs(60))) // 60s for this tool
.build();
let router = McpRouter::new()
.server_info("example", "1.0.0")
.tool(slow_search);
// Global 30s timeout for all OTHER requests
let transport = HttpTransport::new(router)
.layer(TimeoutLayer::new(Duration::from_secs(30)));In this example:
slow_searchtool has a 60-second timeout (per-tool layer)- All other MCP requests have a 30-second timeout (transport layer)
- The per-tool layer is inner to the transport layer
§Layer Ordering
Layers wrap from outside in. The first layer added is the outermost:
Request → [Transport Layer] → [Per-tool Layer] → Handler → ResponseFor per-tool/resource/prompt, chained .layer() calls also wrap outside-in:
ToolBuilder::new("api")
.handler(...)
.layer(TimeoutLayer::new(...)) // Outer: timeout checked first
.layer(ConcurrencyLimitLayer::new(5)) // Inner: concurrency after timeout
.build()§Full Example
See examples/tool_middleware.rs
for a complete example demonstrating:
- Different timeouts per tool
- Concurrency limiting for expensive operations
- Multiple layers combined on a single tool
§Advanced Features
§Sampling (LLM Requests)
Tools can request LLM completions from the client via RequestContext::sample().
This enables AI-assisted tools like “suggest a query” or “analyze results”:
use tower_mcp::{ToolBuilder, CallToolResult, CreateMessageParams, SamplingMessage};
use tower_mcp::extract::Context;
let tool = ToolBuilder::new("suggest")
.description("Get AI suggestions")
.extractor_handler(|ctx: Context| async move {
if !ctx.can_sample() {
return Ok(CallToolResult::error("Sampling not available"));
}
let params = CreateMessageParams::new()
.message(SamplingMessage::user("Suggest 3 search queries for: rust async"))
.max_tokens(200);
let result = ctx.sample(params).await?;
let text = result.first_text().unwrap_or("No response");
Ok(CallToolResult::text(text))
})
.build();§Elicitation (User Input)
Tools can request user input via forms using RequestContext::elicit_form()
or the convenience method RequestContext::confirm():
use tower_mcp::{ToolBuilder, CallToolResult};
use tower_mcp::extract::Context;
// Simple confirmation dialog
let delete_tool = ToolBuilder::new("delete")
.description("Delete a file")
.extractor_handler(|ctx: Context| async move {
if !ctx.confirm("Are you sure you want to delete this file?").await? {
return Ok(CallToolResult::text("Cancelled"));
}
// ... perform deletion ...
Ok(CallToolResult::text("Deleted"))
})
.build();For complex forms, use ElicitFormSchema to define multiple fields.
§Progress Notifications
Long-running tools can report progress via RequestContext::report_progress():
use tower_mcp::{ToolBuilder, CallToolResult};
use tower_mcp::extract::Context;
let process_tool = ToolBuilder::new("process")
.description("Process items")
.extractor_handler(|ctx: Context| async move {
let items = vec!["a", "b", "c", "d", "e"];
let total = items.len() as f64;
for (i, item) in items.iter().enumerate() {
ctx.report_progress(i as f64, Some(total), Some(&format!("Processing {}", item))).await;
// ... process item ...
}
Ok(CallToolResult::text("Done"))
})
.build();§Router Composition
Combine multiple routers using McpRouter::merge() or McpRouter::nest():
use tower_mcp::McpRouter;
// Create domain-specific routers
let db_router = McpRouter::new()
.tool(query_tool)
.tool(insert_tool);
let api_router = McpRouter::new()
.tool(fetch_tool);
// Nest with prefixes: tools become "db.query", "db.insert", "api.fetch"
let combined = McpRouter::new()
.server_info("combined", "1.0")
.nest("db", db_router)
.nest("api", api_router);
// Or merge without prefixes
let merged = McpRouter::new()
.merge(db_router)
.merge(api_router);§Multi-Server Proxy
Aggregate multiple backend MCP servers behind a single endpoint using
McpProxy (requires the proxy feature):
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
.backend("fs", StdioClientTransport::spawn("fs-server", &[]).await?)
.await
.build()
.await?;
// Tools become `db_query`, `fs_read`, etc.
// Serve over any transport -- stdio, HTTP, WebSocket.
GenericStdioTransport::new(proxy).run().await?;The proxy supports per-backend Tower middleware, notification forwarding,
health checks, and request coalescing. See the [proxy] module for details.
§MCP Specification
This crate implements the MCP specification (2025-11-25): https://modelcontextprotocol.io/specification/2025-11-25
Re-exports§
pub use async_task::Task;pub use async_task::TaskStore;pub use client::ChannelTransport;pub use client::ClientHandler;pub use client::ClientTransport;pub use client::McpClient;pub use client::McpClientBuilder;pub use client::NotificationHandler;pub use client::StdioClientTransport;pub use context::ChannelClientRequester;pub use context::ClientRequester;pub use context::ClientRequesterHandle;pub use context::Extensions;pub use context::NotificationReceiver;pub use context::NotificationSender;pub use context::OutgoingRequest;pub use context::OutgoingRequestReceiver;pub use context::OutgoingRequestSender;pub use context::RequestContext;pub use context::RequestContextBuilder;pub use context::ServerNotification;pub use context::outgoing_request_channel;pub use filter::CapabilityFilter;pub use filter::DenialBehavior;pub use filter::Filterable;pub use filter::PromptFilter;pub use filter::ResourceFilter;pub use filter::ToolFilter;pub use jsonrpc::JsonRpcLayer;pub use jsonrpc::JsonRpcService;pub use middleware::AuditLayer;pub use middleware::AuditService;pub use middleware::McpTracingLayer;pub use middleware::McpTracingService;pub use middleware::ToolCallLoggingLayer;pub use middleware::ToolCallLoggingService;pub use prompt::BoxPromptService;pub use prompt::Prompt;pub use prompt::PromptBuilder;pub use prompt::PromptHandler;pub use prompt::PromptRequest;pub use resource::BoxResourceService;pub use resource::Resource;pub use resource::ResourceBuilder;pub use resource::ResourceHandler;pub use resource::ResourceRequest;pub use resource::ResourceTemplate;pub use resource::ResourceTemplateBuilder;pub use resource::ResourceTemplateHandler;pub use router::McpRouter;pub use router::RouterRequest;pub use router::RouterResponse;pub use router::ToolAnnotationsMap;pub use session::SessionPhase;pub use session::SessionState;pub use tool::BoxToolService;pub use tool::GuardLayer;pub use tool::NoParams;pub use tool::Tool;pub use tool::ToolBuilder;pub use tool::ToolHandler;pub use tool::ToolRequest;pub use transport::BidirectionalStdioTransport;pub use transport::CatchError;pub use transport::GenericStdioTransport;pub use transport::StdioTransport;pub use transport::SyncStdioTransport;
Modules§
- async_
task - Async task management for long-running MCP operations
- auth
- Authentication middleware helpers for MCP servers
- client
- MCP Client with bidirectional communication support.
- context
- Request context for MCP handlers
- error
- Error types (re-exported from
tower-mcp-types). - extract
- Extractor pattern for tool handlers
- filter
- Session-based capability filtering.
- jsonrpc
- JSON-RPC 2.0 service layer
- middleware
- MCP-specific tower middleware layers.
- prompt
- Prompt definition and builder API
- protocol
- MCP protocol types (re-exported from
tower-mcp-types). - resource
- Resource definition and builder API
- router
- MCP Router - routes requests to tools, resources, and prompts
- session
- MCP session state management
- tool
- Tool definition and builder API
- tracing_
layer - MCP request tracing middleware.
- transport
- MCP transport implementations
Structs§
- Boolean
Schema - Boolean field schema
- Call
Tool Params - Call
Tool Result - Result of a tool invocation.
- Cancel
Task Params - Parameters for cancelling a task
- Cancelled
Params - Parameters for cancellation notification
- Client
Capabilities - Client
Tasks Cancel Capability - Marker capability for client tasks/cancel support
- Client
Tasks Capability - Client capability for async task management
- Client
Tasks Elicitation Capability - Nested capability for task-augmented elicitation requests
- Client
Tasks Elicitation Create Capability - Marker capability for task-augmented elicitation/create support
- Client
Tasks List Capability - Marker capability for client tasks/list support
- Client
Tasks Requests Capability - Capability declaring which request types support task-augmented requests on the client
- Client
Tasks Sampling Capability - Nested capability for task-augmented sampling requests
- Client
Tasks Sampling Create Message Capability - Marker capability for task-augmented sampling/createMessage support
- Complete
Params - Parameters for completion/complete request
- Complete
Result - Result of completion/complete request
- Completion
- Completion suggestions
- Completion
Argument - Argument being completed
- Completion
Context - Context provided alongside a completion request
- Completions
Capability - Server capability for providing completions
- Content
Annotations - Annotations for content items
- Create
Message Params - Parameters for sampling/createMessage request
- Create
Message Result - Result of sampling/createMessage request
- Create
Task Result - Result of creating a task (returned when tools/call includes task params)
- Elicit
Form Params - Parameters for form-based elicitation request
- Elicit
Form Schema - Restricted JSON Schema for elicitation forms.
- Elicit
Result - Result of an elicitation request
- Elicit
UrlParams - Parameters for URL-based elicitation request.
- Elicitation
Capability - Client capability for elicitation (requesting user input)
- Elicitation
Complete Params - Parameters for elicitation complete notification
- Elicitation
Form Capability - Marker for form-based elicitation support
- Elicitation
UrlCapability - Marker for URL-based elicitation support
- Empty
Result - GetPrompt
Params - GetPrompt
Result - The result of a prompts/get request.
- GetPrompt
Result Builder - Builder for constructing
GetPromptResultwith multiple messages. - GetTask
Info Params - Parameters for getting task info
- GetTask
Result Params - Parameters for getting task result
- Implementation
- Information about a client or server implementation
- Initialize
Params - Initialize
Result - Integer
Schema - Integer field schema
- Json
RpcError Response - JSON-RPC 2.0 error response.
- Json
RpcNotification - JSON-RPC 2.0 notification (no response expected)
- Json
RpcRequest - JSON-RPC 2.0 request.
- Json
RpcResult Response - JSON-RPC 2.0 success response.
- List
Prompts Params - List
Prompts Result - List
Resource Templates Params - Parameters for listing resource templates
- List
Resource Templates Result - Result of listing resource templates
- List
Resources Params - List
Resources Result - List
Roots Params - Result of a roots/list request from the server.
- List
Roots Result - Result of roots/list request
- List
Tasks Params - Parameters for listing tasks
- List
Tasks Result - Result of listing tasks
- List
Tools Params - List
Tools Result - Logging
Capability - Logging capability declaration
- Logging
Message Params - Parameters for logging message notification
- Model
Hint - Hint for model selection
- Model
Preferences - Preferences for model selection during sampling
- Multi
Select Enum Items - Items definition for multi-select enum
- Multi
Select Enum Schema - Multi-select enum schema
- Number
Schema - Number field schema
- Progress
Params - Parameters for progress notification
- Prompt
Argument - Prompt
Definition - Prompt
Message - Prompt
Reference - Reference to a prompt for completion
- Prompts
Capability - Read
Resource Params - Read
Resource Result - Request
Meta - Request metadata that can include progress token
- Resource
Content - Content of an embedded resource.
- Resource
Definition - Resource
Reference - Reference to a resource for completion
- Resource
Template Definition - Definition of a resource template as returned by resources/templates/list
- Resources
Capability - Root
- Represents a root directory or file that the server can operate on.
- Roots
Capability - Client capability for roots (filesystem access) Capabilities related to filesystem roots.
- Sampling
Capability - Sampling
Context Capability - Marker capability for context inclusion within sampling
- Sampling
Message - Message for sampling request
- Sampling
Tool - Tool definition for use in sampling requests (SEP-1577)
- Sampling
Tools Capability - Marker capability for tool use within sampling
- Server
Capabilities - SetLog
Level Params - Parameters for setting log level
- Single
Select Enum Schema - Single-select enum schema
- String
Schema - String field schema
- Subscribe
Resource Params - Task
Object - Task object matching the MCP 2025-11-25 spec
- Task
Request Params - Parameters for task-augmented requests (the
taskfield in CallToolParams) - Task
Status Params - Notification params when task status changes
- Tasks
Cancel Capability - Marker capability for tasks/cancel support
- Tasks
Capability - Capability for async task management
- Tasks
List Capability - Marker capability for tasks/list support
- Tasks
Requests Capability - Capability declaring which request types support task-augmented requests
- Tasks
Tools Call Capability - Marker capability for task-augmented tools/call support
- Tasks
Tools Requests Capability - Nested capability for task-augmented tool requests
- Tool
Annotations - Annotations describing tool behavior for trust and safety. Clients MUST consider these untrusted unless the server is trusted.
- Tool
Choice - Tool choice mode for sampling requests (SEP-1577)
- Tool
Definition - Tool definition as returned by tools/list
- Tool
Error - Tool execution error with context
- Tool
Execution - Execution metadata for a tool definition
- Tool
Icon - Icon for tool display in user interfaces
- Tools
Capability - Unsubscribe
Resource Params
Enums§
- Completion
Reference - Reference for completion - either a prompt or resource reference
- Content
- Content types for tool results, resources, and prompts.
- Content
Role - Role indicating who content is intended for.
- Elicit
Action - User action in response to elicitation
- Elicit
Field Value - Value from an elicitation form field
- Elicit
Mode - Elicitation mode
- Elicit
Request Params - Elicitation request parameters (union of form and URL modes)
- Error
- tower-mcp error type
- Icon
Theme - Icon theme context
- Include
Context - Context inclusion mode for sampling
- Json
RpcMessage - JSON-RPC 2.0 message - can be a single request or a batch
- Json
RpcResponse - JSON-RPC 2.0 response (either success or error).
- Json
RpcResponse Message - JSON-RPC 2.0 response message - can be a single response or a batch
- LogLevel
- Log severity levels following RFC 5424 (syslog)
- McpNotification
- High-level MCP notification (parsed from JSON-RPC)
- McpRequest
- High-level MCP request (parsed from JSON-RPC)
- McpResponse
- High-level MCP response
- Primitive
Schema Definition - Primitive schema definition for form fields
- Progress
Token - Progress token - can be string or number
- Prompt
Role - Request
Id - Request ID - can be string or number per JSON-RPC spec
- Sampling
Content - Content types for sampling messages
- Sampling
Content OrArray - Content that can be either a single item or an array
- Task
Status - Status of an async task
- Task
Support Mode - Task support mode for tool execution
Traits§
- Result
Ext - Extension trait for converting errors into tower-mcp tool errors.
Type Aliases§
- BoxError
- Type-erased error type used for middleware composition.
- Result
- Result type alias for tower-mcp
- Task
Info Deprecated - Backwards-compatible alias for TaskObject
- Task
Status Changed Params - Backwards-compatible alias