tower-mcp
Tower-native Model Context Protocol (MCP) implementation for Rust.
Overview
tower-mcp provides a composable, middleware-friendly approach to building MCP servers using the Tower service abstraction. 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)
Familiar to axum Users
If you've used axum, tower-mcp's API will feel familiar:
- Extractor pattern: Tool handlers use extractors like
State<T>,Json<T>, andContext - Router composition:
McpRouter::merge()andMcpRouter::nest()work like axum's router methods - Per-handler middleware: Apply Tower layers to individual tools, resources, or prompts via
.layer() - Builder pattern: Fluent builders for tools, resources, and prompts
Live Demo
A full-featured MCP server for querying crates.io is available as a standalone project: cratesio-mcp. It includes tools, prompts, and resources for crate search, docs.rs integration, and vulnerability auditing via OSV.dev.
A demo instance is deployed at https://cratesio-mcp.fly.dev -- connect with any MCP client that supports HTTP transport.
Try the Examples
Clone the repo and run your MCP-enabled agent (like Claude Code) in the
tower-mcp directory. The .mcp.json configures several example servers:
| Server | Description |
|---|---|
markdownlint-mcp |
Lint markdown with 66 rules |
codegen-mcp |
Helps AI agents build tower-mcp servers |
weather |
Weather forecasts via NWS API |
conformance |
Full MCP spec conformance server (39/39 tests) |
# Run your MCP agent here - servers will be available automatically
For a guided tour, ask your agent to read examples/README.md.
Or jump straight in:
- "Lint examples/README.md for issues" (markdownlint-mcp)
- "What's the weather in Seattle?" (weather)
Status
Active development - Core protocol, routing, and transports are implemented. Used in production for MCP server deployments.
Implemented
- JSON-RPC 2.0 message types, validation, and batch request handling
- MCP protocol types (tools, resources, prompts)
- Tool builder with type-safe handlers and JSON Schema generation via schemars
McpTooltrait for complex toolsMcpRouterimplementing Tower'sServicetraitJsonRpcServicelayer for protocol framing- Session state management with reconnection support
- Protocol version negotiation (supports
2025-11-25with2025-03-26backward compat) - Tool annotations (behavior hints for trust/safety)
- Transports: stdio (
StdioTransport,SyncStdioTransport,BidirectionalStdioTransport,GenericStdioTransport), HTTP (with SSE and stream resumption), WebSocket, child process - Resources: list, read, subscribe/unsubscribe with change notifications
- Resource templates:
resources/templates/listwith URI template matching (RFC 6570) - Prompts: list and get with argument support
- Logging:
notifications/messageandlogging/setLevelwith structured log data - Authentication: API key and Bearer token middleware helpers
- Elicitation: Server-to-client user input requests (form mode via
elicit()and URL mode viaelicit_url()) - Client support: MCP client for connecting to external servers
- Progress notifications: Via
RequestContextin tool handlers - Request cancellation: Via
CancellationTokenin tool handlers - Completion: Autocomplete for prompt arguments and resource URIs
- Sampling types:
CreateMessageParams/CreateMessageResultfor LLM requests - Sampling runtime: Full support on stdio, WebSocket, and HTTP transports
- Async tasks: Task ID generation, status tracking, TTL-based cleanup, per-tool
task_supportmode - Per-tool guards: Request-level access control for individual tools
- Capability filters: Session-based tool/resource/prompt visibility
ResultExt: Ergonomic error handling in tool handlers- Auto-generated instructions: Server instructions derived from registered capabilities
- Convenience helpers:
Content::text(),CallToolResult::from_list(), JSON helpers - Tool-level testing: Unit test tools directly via
Tool::call() - Infallible builds:
ToolBuilder::build()is infallible;try_new()available for runtime names - Cursor-based pagination:
McpRouter::page_size()for paginated list responses - Tool output schema:
ToolBuilder::output_schema()for structured output validation - Server metadata:
server_title(),server_description(),server_icons(),server_website_url() McpTracingLayer: Built-in Tower middleware for structured request logginglist_changednotifications:notify_tools_list_changed(),notify_resources_list_changed(),notify_prompts_list_changed()- Dynamic tools: Runtime tool registration/deregistration via
DynamicToolRegistry(feature:dynamic-tools) - Experimental capabilities:
experimentalfield on both client and server capabilities - Extension support:
extensionsfield on server and client capabilities for declared extension negotiation
Installation
Add to your Cargo.toml:
[]
= "0.6"
Feature Flags
| Feature | Description |
|---|---|
full |
Enable all optional features |
http |
HTTP transport with SSE support (adds axum, hyper) |
websocket |
WebSocket transport for full-duplex communication |
childproc |
Child process transport for spawning subprocess MCP servers |
oauth |
OAuth 2.1 resource server support (JWT validation) |
jwks |
JWKS endpoint fetching for remote key sets (requires oauth) |
testing |
Test utilities (TestClient) for in-process testing |
dynamic-tools |
Runtime tool registration/deregistration via DynamicToolRegistry |
Example with features:
[]
= { = "0.6", = ["full"] }
Types Only
If you only need MCP protocol types and error types -- without tower, tokio, or axum --
use the tower-mcp-types crate directly.
This is useful for editor integrations, code generators, protocol validators, or
any context where you want to serialize/deserialize MCP messages without a runtime.
[]
= "0.1"
tower-mcp-types provides all types from tower_mcp::protocol and tower_mcp::error
with minimal dependencies (serde, serde_json, thiserror, base64). The full
tower-mcp crate re-exports everything from tower-mcp-types, so there is no
duplication if you use both.
Quick Start
use ;
use JsonSchema;
use Deserialize;
// Define your input type - schema is auto-generated
// Build a tool with type-safe handler
let greet = new
.description
.handler
.build;
// Create router with tools
let router = new
.server_info
.instructions
.tool;
// The router implements tower::Service and can be composed with middleware
Tool Definition
Builder Pattern (Recommended)
use ;
use JsonSchema;
use Deserialize;
let add = new
.description
.read_only // Hint: this tool doesn't modify state
.handler
.build;
Trait-Based (For Complex Tools)
use McpTool;
use ;
use JsonSchema;
use ;
use Arc;
// Convert to Tool and register
let calc = Calculator ;
let router = new.tool;
Handler with Extractors (State, Context, JSON)
Use axum-style extractors to access state, context, and typed input:
use Arc;
use ;
use ;
let state = new;
let search = new
.description
.extractor_handler
.build;
Tool with Icons and Title
let tool = new
.title // Human-readable display name
.description
.icon
.read_only
.idempotent
.build;
Per-Tool Middleware
Apply Tower layers to individual tools:
use Duration;
use TimeoutLayer;
let slow_tool = new
.description
.handler
.layer // 60s for this tool
.build;
Raw JSON Handler (Escape Hatch)
Use RawArgs extractor when you need the raw JSON:
use RawArgs;
let echo = new
.description
.extractor_handler
.build;
Resource Definition
use ResourceBuilder;
// Static resource with inline content
let config = new
.name
.description
.json
.build;
// Dynamic resource with handler
let status = new
.name
.description
.handler
.build;
let router = new
.resource
.resource;
Prompt Definition
use ;
let greet = new
.description
.required_arg
.optional_arg
.handler
.build;
let router = new.prompt;
Router Composition
Combine routers like in axum:
// Merge routers (combines all tools/resources/prompts)
let api_router = new
.tool
.tool;
let admin_router = new
.tool
.tool;
let combined = new
.merge
.merge;
// Nest with prefix (adds prefix to all tool names)
let v1 = new.tool;
let v2 = new.tool;
let versioned = new
.nest // Tools become "v1_legacy_tool"
.nest; // Tools become "v2_new_tool"
Router-Level State
Share state across all handlers using with_state():
use Arc;
use Extension;
let state = new;
// Tools access state via Extension<T> extractor
let tool = new
.extractor_handler
.build;
let router = new
.with_state // Makes AppState available to all handlers
.tool;
Transports
Stdio (CLI/local)
use ;
let router = new
.server_info
.tool;
// Serve over stdin/stdout
new.serve.await?;
HTTP with SSE
use ;
let router = new
.server_info
.tool;
let transport = new;
let app = transport.into_router;
// Serve with axum
let listener = bind.await?;
serve.await?;
With Authentication Middleware
use extract_api_key;
use middleware;
// Add auth layer to the HTTP transport
let app = transport.into_router
.layer;
Architecture
+-----------------+
| Your App |
+-----------------+
|
+-----------------+
| Tower Middleware| <-- tracing, metrics, auth, etc.
+-----------------+
|
+-----------------+
| JsonRpcService | <-- JSON-RPC 2.0 framing
+-----------------+
|
+-----------------+
| McpRouter | <-- Request dispatch
+-----------------+
|
+------------+------------+
| | |
+--------+ +--------+ +--------+
| Tool 1 | | Tool 2 | | Tool N |
+--------+ +--------+ +--------+
Design Philosophy
| Aspect | tower-mcp |
|---|---|
| Style | Library, not framework |
| Tool definition | Builder pattern or trait-based |
| Middleware | Native tower layers |
| Transport | Pluggable (stdio, HTTP, WebSocket, child process) |
| Integration | Composable with axum/tonic |
Protocol Compliance
tower-mcp targets the MCP specification 2025-11-25 with backward compatibility for 2025-03-26. Current compliance:
- JSON-RPC 2.0 message format
- Protocol version negotiation (supports
2025-11-25and2025-03-26) - Capability negotiation
- Initialize/initialized lifecycle
- tools/list and tools/call
- Tool annotations
- Batch requests
- resources/list, resources/read, resources/subscribe
- resources/templates/list
- prompts/list, prompts/get
- Logging (notifications/message, logging/setLevel)
- Icons on tools/resources/prompts (SEP-973)
- Implementation metadata
- Sampling with tools/toolChoice (SEP-1577)
- Elicitation (form and URL modes)
- Session management
- Progress notifications
- Request cancellation
- Completion (autocomplete)
- Roots (filesystem discovery)
- Sampling (all transports)
- Async tasks (task ID, status tracking, TTL cleanup, per-tool task support mode)
- SSE event IDs and stream resumption (SEP-1699)
-
_metafield on all protocol types
We track all MCP Specification Enhancement Proposals (SEPs) as GitHub issues. A weekly workflow syncs status from the upstream spec repository.
Development
# Format, lint, and test
License
MIT OR Apache-2.0