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 demo MCP server for querying crates.io is deployed at:
https://crates-mcp-demo.fly.dev
Connect with any MCP client that supports HTTP transport, or add to Claude Code's .mcp.json:
The demo includes 7 tools (search, info, versions, dependencies, reverse deps, downloads, owners), 2 prompts (analyze, compare), and 1 resource (recent searches). See examples/crates-mcp for the full source.
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 |
|---|---|
crates-mcp-local |
Query crates.io for Rust crate info |
markdownlint-mcp |
Lint markdown with 66 rules |
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:
- "Search for async runtime crates" (crates-mcp)
- "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
- Tool annotations (behavior hints for trust/safety)
- Transports: stdio, HTTP (with SSE and stream resumption), WebSocket, child process
- Resources: list, read, subscribe/unsubscribe with change notifications
- Prompts: list and get with argument support
- Authentication: API key and Bearer token middleware helpers
- Elicitation: Server-to-client user input requests (form and URL modes)
- 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 for long-running operations
Installation
Add to your Cargo.toml:
[]
= "0.3"
Feature Flags
| Feature | Description |
|---|---|
full |
Enable all optional features |
http |
HTTP transport with SSE support (adds axum, hyper dependencies) |
websocket |
WebSocket transport for full-duplex communication |
childproc |
Child process transport for spawning subprocess MCP servers |
client |
MCP client support for connecting to external servers |
Example with features:
[]
= { = "0.2", = ["full"] }
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
.
.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. Current compliance:
- JSON-RPC 2.0 message format
- Protocol version negotiation
- Capability negotiation
- Initialize/initialized lifecycle
- tools/list and tools/call
- Tool annotations
- Batch requests
- resources/list, resources/read, resources/subscribe
- prompts/list, prompts/get
- Icons on tools/resources/prompts (SEP-973)
- Implementation metadata
- Sampling with tools/toolChoice (SEP-1577)
- Elicitation (user input requests)
- Session management
- Progress notifications
- Request cancellation
- Completion (autocomplete)
- Roots (filesystem discovery)
- Sampling (all transports)
- Async tasks (task ID, status tracking, TTL cleanup)
- SSE event IDs and stream resumption (SEP-1699)
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