A Rust SDK for the Model Context Protocol (MCP) that simplifies server development through a unified #[mcp_server] macro.
- Unified
#[mcp_server]macro for defining tools, resources, and prompts - Runtime-agnostic async support (Tokio, smol)
- Typestate builders for compile-time validation of server configuration
- Context-based error handling with
McpErrorand.context()chains - MCP 2025-11-25 protocol including Tasks, Elicitation, and OAuth 2.1
- Tower-compatible middleware via built-in Layer pattern
mcpkit implements MCP 2025-11-25 — the latest published protocol specification — and negotiates all four published protocol versions. mcpkit's differentiation is its developer experience: a single unified #[mcp_server] macro, a runtime-agnostic core (Tokio or smol), and a broader built-in transport list (stdio, WebSocket, HTTP, Unix sockets, in-memory). mcpkit supports the newest MCP features:
| Feature | Added In | Description |
|---|---|---|
| Tasks | 2025-11-25 | Long-running operations with progress tracking and cancellation |
| Elicitation | 2025-06-18 | Server-initiated requests for user input |
| OAuth 2.1 | 2025-03-26 | Modern authentication with mandatory PKCE |
| Tool Annotations | 2025-03-26 | readOnly, destructive, idempotent hints for tools |
| Structured Output | 2025-06-18 | Type-safe JSON responses with schema validation |
See the detailed comparison for a full overview of Rust MCP SDK options.
Add the dependency to your Cargo.toml:
[dependencies]
mcpkit = "0.7"
tokio = { version = "1", features = ["full"] }
serde_json = "1"Create a simple MCP server:
use mcpkit::prelude::*;
use mcpkit::transport::stdio::StdioTransport;
struct Calculator;
#[mcp_server(name = "calculator", version = "1.0.0")]
impl Calculator {
#[tool(description = "Add two numbers")]
async fn add(&self, a: f64, b: f64) -> ToolOutput {
ToolOutput::text((a + b).to_string())
}
#[tool(description = "Multiply two numbers")]
async fn multiply(&self, a: f64, b: f64) -> ToolOutput {
ToolOutput::text((a * b).to_string())
}
}
#[tokio::main]
async fn main() -> Result<(), McpError> {
// Create transport - you choose the runtime!
let transport = StdioTransport::new();
// Build server with registered handlers
let server = ServerBuilder::new(Calculator)
.with_tools(Calculator)
.build();
// Serve on the transport
server.serve(transport).await
}Note: This SDK is runtime-agnostic. You provide the transport, which lets you use Tokio, smol, or any other async runtime. The examples use Tokio, but the SDK itself doesn't depend on any specific runtime.
| Feature | Description |
|---|---|
| Protocol | MCP 2025-11-25 (supports all 4 protocol versions) |
| Macro | Single #[mcp_server] with #[tool], #[resource], #[prompt] |
| Parameters | Extracted directly from function signatures |
| Errors | Unified McpError with .context() chains |
| Transports | stdio, WebSocket, HTTP/SSE, Unix sockets, gRPC, Windows pipes |
| Middleware | Built-in Tower-compatible Layer system |
| Runtime | Agnostic (Tokio, smol) |
For comparisons with other Rust MCP SDKs (rmcp, rust-mcp-sdk, mcp-protocol-sdk), see the detailed comparison.
mcpkit/
├── mcpkit/ # Facade crate (use this)
├── crates/
│ ├── mcpkit-core/ # Protocol types, traits
│ ├── mcpkit-transport/ # Transport abstractions
│ │ ├── stdio # Standard I/O transport
│ │ ├── http # Streamable HTTP transport
│ │ ├── websocket # WebSocket transport
│ │ ├── grpc # gRPC with bidirectional streaming
│ │ ├── unix # Unix domain sockets
│ │ └── windows # Windows named pipes
│ ├── mcpkit-server/ # Server implementation
│ ├── mcpkit-client/ # Client implementation
│ ├── mcpkit-macros/ # Procedural macros
│ ├── mcpkit-testing/ # Test utilities
│ ├── mcpkit-axum/ # Axum web framework integration
│ ├── mcpkit-actix/ # Actix-web framework integration
│ ├── mcpkit-rocket/ # Rocket web framework integration
│ └── mcpkit-warp/ # Warp web framework integration
└── examples/ # Example servers
use mcpkit::prelude::*;
struct MyServer;
#[mcp_server(name = "minimal", version = "1.0.0")]
impl MyServer {
#[tool(description = "Say hello")]
async fn hello(&self, name: Option<String>) -> ToolOutput {
let name = name.unwrap_or_else(|| "World".to_string());
ToolOutput::text(format!("Hello, {}!", name))
}
}use mcpkit::prelude::*;
struct ConfigServer;
#[mcp_server(name = "config", version = "1.0.0")]
impl ConfigServer {
#[resource(
uri_pattern = "config://app/{key}",
name = "Configuration",
mime_type = "application/json"
)]
async fn get_config(&self, uri: &str) -> ResourceContents {
ResourceContents::text(uri, r#"{"debug": true}"#)
}
}use mcpkit::prelude::*;
struct PromptServer;
#[mcp_server(name = "prompts", version = "1.0.0")]
impl PromptServer {
#[prompt(description = "Review code for issues")]
async fn code_review(&self, code: String, language: Option<String>) -> GetPromptResult {
let lang = language.unwrap_or_else(|| "unknown".to_string());
GetPromptResult {
description: Some("Code review prompt".to_string()),
messages: vec![
PromptMessage::user(format!(
"Please review the following {} code:\n```{}\n{}```",
lang, lang, code
))
],
}
}
}The SDK is runtime-agnostic. You choose the transport and the async runtime.
use mcpkit_transport::stdio::StdioTransport;
let transport = StdioTransport::new();use mcpkit_transport::http::HttpTransport;
let transport = HttpTransport::new(HttpTransportConfig::new("http://localhost:8080"));use mcpkit_transport::websocket::WebSocketTransport;
let transport = WebSocketTransport::new(WebSocketConfig::new("ws://localhost:9000"));#[cfg(unix)]
use mcpkit_transport::unix::UnixTransport;
#[cfg(unix)]
let transport = UnixTransport::new("/tmp/mcp.sock");use mcpkit_transport::grpc::{GrpcTransport, GrpcConfig};
let config = GrpcConfig::new("http://localhost:50051");
let transport = GrpcTransport::connect(config).await?;#[cfg(windows)]
use mcpkit_transport::windows::{NamedPipeTransport, NamedPipeConfig};
#[cfg(windows)]
let config = NamedPipeConfig::new(r"\\.\pipe\mcp-server");
let transport = NamedPipeTransport::connect(config).await?;use mcpkit_transport::stdio::StdioTransport;
use mcpkit_transport::middleware::{LoggingLayer, TimeoutLayer, LayerStack};
use std::time::Duration;
use log::Level;
let transport = StdioTransport::new();
let stack = LayerStack::new(transport)
.with(LoggingLayer::new(Level::Debug))
.with(TimeoutLayer::new(Duration::from_secs(30)));use mcpkit::prelude::*;
fn process() -> Result<(), McpError> {
let result = something_risky()
.context("while processing request")?;
Ok(())
}This SDK implements MCP protocol version 2025-11-25.
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE)
- MIT License (LICENSE-MIT)
at your option.
- Getting Started
- Working with Tools
- Working with Resources
- Working with Prompts
- Error Handling
- Using Middleware
- Transport Options
- Architecture Decision Records
Contributions are welcome! Please read our Contributing Guide before submitting a Pull Request.
For security issues, please see our Security Policy.