Skip to main content

Crate tower_mcp

Crate tower_mcp 

Source
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>, and extract::Context - just like axum’s request extractors
  • Router composition: McpRouter::merge() and McpRouter::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

§Client

§Protocol

§Feature Flags

  • full - Enable all optional features
  • http - HTTP/SSE transport for web servers
  • websocket - WebSocket transport for bidirectional communication
  • childproc - Child process transport for subprocess management
  • oauth - OAuth 2.1 resource server support (token validation, metadata endpoint)
  • testing - Test utilities (TestClient) for ergonomic MCP server testing
  • dynamic-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:

LevelMethodScopeUse Cases
TransportStdioTransport::layer(), HttpTransport::layer()All MCP requestsGlobal timeout, rate limit, metrics
axum.into_router().layer()HTTP layer onlyCORS, compression, request logging
Per-toolToolBuilder::...layer()Single toolTool-specific timeout, concurrency
Per-resourceResourceBuilder::...layer()Single resourceCaching, read timeout
Per-promptPromptBuilder::...layer()Single promptGeneration 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_search tool 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 → Response

For 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§

BooleanSchema
Boolean field schema
CallToolParams
CallToolResult
Result of a tool invocation.
CancelTaskParams
Parameters for cancelling a task
CancelledParams
Parameters for cancellation notification
ClientCapabilities
ClientTasksCancelCapability
Marker capability for client tasks/cancel support
ClientTasksCapability
Client capability for async task management
ClientTasksElicitationCapability
Nested capability for task-augmented elicitation requests
ClientTasksElicitationCreateCapability
Marker capability for task-augmented elicitation/create support
ClientTasksListCapability
Marker capability for client tasks/list support
ClientTasksRequestsCapability
Capability declaring which request types support task-augmented requests on the client
ClientTasksSamplingCapability
Nested capability for task-augmented sampling requests
ClientTasksSamplingCreateMessageCapability
Marker capability for task-augmented sampling/createMessage support
CompleteParams
Parameters for completion/complete request
CompleteResult
Result of completion/complete request
Completion
Completion suggestions
CompletionArgument
Argument being completed
CompletionContext
Context provided alongside a completion request
CompletionsCapability
Server capability for providing completions
ContentAnnotations
Annotations for content items
CreateMessageParams
Parameters for sampling/createMessage request
CreateMessageResult
Result of sampling/createMessage request
CreateTaskResult
Result of creating a task (returned when tools/call includes task params)
ElicitFormParams
Parameters for form-based elicitation request
ElicitFormSchema
Restricted JSON Schema for elicitation forms.
ElicitResult
Result of an elicitation request
ElicitUrlParams
Parameters for URL-based elicitation request.
ElicitationCapability
Client capability for elicitation (requesting user input)
ElicitationCompleteParams
Parameters for elicitation complete notification
ElicitationFormCapability
Marker for form-based elicitation support
ElicitationUrlCapability
Marker for URL-based elicitation support
EmptyResult
GetPromptParams
GetPromptResult
The result of a prompts/get request.
GetPromptResultBuilder
Builder for constructing GetPromptResult with multiple messages.
GetTaskInfoParams
Parameters for getting task info
GetTaskResultParams
Parameters for getting task result
Implementation
Information about a client or server implementation
InitializeParams
InitializeResult
IntegerSchema
Integer field schema
JsonRpcErrorResponse
JSON-RPC 2.0 error response.
JsonRpcNotification
JSON-RPC 2.0 notification (no response expected)
JsonRpcRequest
JSON-RPC 2.0 request.
JsonRpcResultResponse
JSON-RPC 2.0 success response.
ListPromptsParams
ListPromptsResult
ListResourceTemplatesParams
Parameters for listing resource templates
ListResourceTemplatesResult
Result of listing resource templates
ListResourcesParams
ListResourcesResult
ListRootsParams
Result of a roots/list request from the server.
ListRootsResult
Result of roots/list request
ListTasksParams
Parameters for listing tasks
ListTasksResult
Result of listing tasks
ListToolsParams
ListToolsResult
LoggingCapability
Logging capability declaration
LoggingMessageParams
Parameters for logging message notification
ModelHint
Hint for model selection
ModelPreferences
Preferences for model selection during sampling
MultiSelectEnumItems
Items definition for multi-select enum
MultiSelectEnumSchema
Multi-select enum schema
NumberSchema
Number field schema
ProgressParams
Parameters for progress notification
PromptArgument
PromptDefinition
PromptMessage
PromptReference
Reference to a prompt for completion
PromptsCapability
ReadResourceParams
ReadResourceResult
RequestMeta
Request metadata that can include progress token
ResourceContent
Content of an embedded resource.
ResourceDefinition
ResourceReference
Reference to a resource for completion
ResourceTemplateDefinition
Definition of a resource template as returned by resources/templates/list
ResourcesCapability
Root
Represents a root directory or file that the server can operate on.
RootsCapability
Client capability for roots (filesystem access) Capabilities related to filesystem roots.
SamplingCapability
SamplingContextCapability
Marker capability for context inclusion within sampling
SamplingMessage
Message for sampling request
SamplingTool
Tool definition for use in sampling requests (SEP-1577)
SamplingToolsCapability
Marker capability for tool use within sampling
ServerCapabilities
SetLogLevelParams
Parameters for setting log level
SingleSelectEnumSchema
Single-select enum schema
StringSchema
String field schema
SubscribeResourceParams
TaskObject
Task object matching the MCP 2025-11-25 spec
TaskRequestParams
Parameters for task-augmented requests (the task field in CallToolParams)
TaskStatusParams
Notification params when task status changes
TasksCancelCapability
Marker capability for tasks/cancel support
TasksCapability
Capability for async task management
TasksListCapability
Marker capability for tasks/list support
TasksRequestsCapability
Capability declaring which request types support task-augmented requests
TasksToolsCallCapability
Marker capability for task-augmented tools/call support
TasksToolsRequestsCapability
Nested capability for task-augmented tool requests
ToolAnnotations
Annotations describing tool behavior for trust and safety. Clients MUST consider these untrusted unless the server is trusted.
ToolChoice
Tool choice mode for sampling requests (SEP-1577)
ToolDefinition
Tool definition as returned by tools/list
ToolError
Tool execution error with context
ToolExecution
Execution metadata for a tool definition
ToolIcon
Icon for tool display in user interfaces
ToolsCapability
UnsubscribeResourceParams

Enums§

CompletionReference
Reference for completion - either a prompt or resource reference
Content
Content types for tool results, resources, and prompts.
ContentRole
Role indicating who content is intended for.
ElicitAction
User action in response to elicitation
ElicitFieldValue
Value from an elicitation form field
ElicitMode
Elicitation mode
ElicitRequestParams
Elicitation request parameters (union of form and URL modes)
Error
tower-mcp error type
IconTheme
Icon theme context
IncludeContext
Context inclusion mode for sampling
JsonRpcMessage
JSON-RPC 2.0 message - can be a single request or a batch
JsonRpcResponse
JSON-RPC 2.0 response (either success or error).
JsonRpcResponseMessage
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
PrimitiveSchemaDefinition
Primitive schema definition for form fields
ProgressToken
Progress token - can be string or number
PromptRole
RequestId
Request ID - can be string or number per JSON-RPC spec
SamplingContent
Content types for sampling messages
SamplingContentOrArray
Content that can be either a single item or an array
TaskStatus
Status of an async task
TaskSupportMode
Task support mode for tool execution

Traits§

ResultExt
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
TaskInfoDeprecated
Backwards-compatible alias for TaskObject
TaskStatusChangedParams
Backwards-compatible alias