httpmcp-rust

⚠️ Beta Status: This library is currently in beta. The API is still evolving and may have breaking changes. Not recommended for production use yet.
A fast and simple Rust library for building MCP (Model Context Protocol) servers using Streamable HTTP.
Features
- ✅ Simple API - Function-based registration with builder pattern
- ✅ Fast - Built on actix-web with async/await
- ✅ Type-safe - Strong typing throughout
- ✅ Extensible - Easy to add custom resources, tools, and prompts
- ✅ Full MCP Support - All protocol features (resources, tools, prompts, logging)
- ✅ Custom HTTP Endpoints - Add REST API endpoints on the same port as MCP
- ✅ Multipart File Uploads - Handle file uploads with
.multipart_endpoint()
- ✅ Headers & Context - Access request headers, remote IP, request ID
- ✅ Middleware - Built-in CORS and OAuth 2.0 configuration
- 🚧 Beta Features - SSE resumption, OAuth validation (in development)
Quick Start
[dependencies]
httpmcp-rust = "0.1"
tokio = { version = "1", features = ["full"] }
serde_json = "1.0"
use httpmcp_rust::{HttpMcpServer, RequestContext, ResourceMeta, ToolMeta, Result};
use httpmcp_rust::protocol::{Resource, ResourceContents};
use serde_json::{json, Value};
use std::collections::HashMap;
async fn list_resources(
_cursor: Option<String>,
_ctx: RequestContext,
) -> Result<(Vec<Resource>, Option<String>)> {
Ok((vec![Resource {
uri: "file:///example.txt".to_string(),
name: "Example".to_string(),
description: Some("Example file".to_string()),
mime_type: Some("text/plain".to_string()),
}], None))
}
async fn read_resource(uri: String, _ctx: RequestContext) -> Result<Vec<ResourceContents>> {
Ok(vec![ResourceContents {
uri,
mime_type: Some("text/plain".to_string()),
text: Some("Hello, MCP!".to_string()),
blob: None,
}])
}
async fn echo_tool(args: HashMap<String, Value>, _ctx: RequestContext) -> Result<Value> {
Ok(json!({"echo": args.get("message")}))
}
#[tokio::main]
async fn main() -> std::io::Result<()> {
HttpMcpServer::builder()
.name("my-server")
.version("1.0.0")
.resource(
"file:///example.txt",
ResourceMeta::new().name("Example").mime_type("text/plain"),
list_resources,
read_resource,
)
.tool(
"echo",
ToolMeta::new()
.description("Echo a message")
.param("message", "string", "Message to echo")
.required(&["message"]),
echo_tool,
)
.build()?
.run("127.0.0.1:8080")
.await
}
Usage Guide
Server Builder
Configure your MCP server using the builder pattern:
let server = HttpMcpServer::builder()
.name("my-server") .version("1.0.0") .resource(
"file:///example.txt",
ResourceMeta::new().name("Example").mime_type("text/plain"),
list_resources,
read_resource,
)
.tool(
"add",
ToolMeta::new()
.description("Add two numbers")
.param("a", "number", "First number")
.param("b", "number", "Second number")
.required(&["a", "b"]),
add_tool,
)
.prompt(
"code_review",
PromptMeta::new()
.description("Review code")
.arg("code", "Code to review", true),
code_review_prompt,
)
.enable_cors(true) .build()?;
server.run("127.0.0.1:8080").await?;
Implementing Handlers
Resource Handlers
use httpmcp_rust::{RequestContext, ResourceMeta, Result};
use httpmcp_rust::protocol::{Resource, ResourceContents};
async fn list_resources(
_cursor: Option<String>,
ctx: RequestContext,
) -> Result<(Vec<Resource>, Option<String>)> {
let auth = ctx.get_authorization();
Ok((vec![
Resource {
uri: "file:///example.txt".to_string(),
name: "Example".to_string(),
description: Some("Example file".to_string()),
mime_type: Some("text/plain".to_string()),
}
], None))
}
async fn read_resource(
uri: String,
ctx: RequestContext,
) -> Result<Vec<ResourceContents>> {
Ok(vec![ResourceContents {
uri,
mime_type: Some("text/plain".to_string()),
text: Some("Hello, MCP!".to_string()),
blob: None,
}])
}
Tool Handlers
use httpmcp_rust::{RequestContext, ToolMeta, Result};
use std::collections::HashMap;
use serde_json::{json, Value};
async fn add_tool(
args: HashMap<String, Value>,
ctx: RequestContext,
) -> Result<Value> {
let a = args.get("a").and_then(|v| v.as_f64()).unwrap_or(0.0);
let b = args.get("b").and_then(|v| v.as_f64()).unwrap_or(0.0);
Ok(json!({
"result": a + b
}))
}
Prompt Handlers
use httpmcp_rust::{RequestContext, PromptMeta, Result};
use httpmcp_rust::protocol::{PromptMessage, PromptContent};
use std::collections::HashMap;
async fn code_review_prompt(
_name: String,
args: Option<HashMap<String, String>>,
ctx: RequestContext,
) -> Result<(Option<String>, Vec<PromptMessage>)> {
let code = args.and_then(|mut a| a.remove("code")).unwrap_or_default();
let messages = vec![PromptMessage {
role: "user".to_string(),
content: PromptContent::Text {
text: format!("Review this code:\n\n{}", code),
},
}];
Ok((Some("Code review".to_string()), messages))
}
Custom HTTP Endpoint Handlers (JSON)
Add REST API endpoints alongside MCP protocol on the same port:
use httpmcp_rust::{EndpointMeta, HttpMcpServer, RequestContext, Result};
use actix_web::HttpResponse;
use serde_json::json;
#[tokio::main]
async fn main() -> std::io::Result<()> {
HttpMcpServer::builder()
.name("my-server")
.version("1.0.0")
.endpoint(
EndpointMeta::new()
.route("/health")
.method("GET")
.description("Health check endpoint"),
|_ctx: RequestContext, _body| async move {
Ok(HttpResponse::Ok().json(json!({
"status": "healthy",
"version": "1.0.0"
})))
},
)
.endpoint(
EndpointMeta::new()
.route("/api/data")
.method("POST")
.description("Create data"),
|_ctx: RequestContext, body| async move {
Ok(HttpResponse::Created().json(json!({
"message": "Created successfully",
"data": body
})))
},
)
.build()?
.run("127.0.0.1:8080")
.await
}
Multipart File Upload Endpoints
Handle file uploads using multipart/form-data:
use httpmcp_rust::{EndpointMeta, HttpMcpServer, RequestContext};
use actix_multipart::Multipart;
use actix_web::HttpResponse;
use futures::stream::StreamExt;
use serde_json::json;
#[tokio::main]
async fn main() -> std::io::Result<()> {
HttpMcpServer::builder()
.name("upload-server")
.version("1.0.0")
.multipart_endpoint(
EndpointMeta::new()
.route("/upload")
.method("POST")
.description("Upload CSV file"),
|_ctx: RequestContext, multipart: Multipart| {
async move {
let mut multipart = multipart;
let mut file_contents = Vec::new();
let mut filename = String::from("unknown");
while let Some(field) = multipart.next().await {
let mut field = field.map_err(|e| {
httpmcp_rust::McpError::InvalidParams(format!("Multipart error: {}", e))
})?;
if let Some(content_disposition) = field.content_disposition() {
if let Some(fname) = content_disposition.get_filename() {
filename = fname.to_string();
}
}
while let Some(chunk) = field.next().await {
let data = chunk.map_err(|e| {
httpmcp_rust::McpError::InvalidParams(format!("Chunk error: {}", e))
})?;
file_contents.extend_from_slice(&data);
}
}
let content = String::from_utf8(file_contents).map_err(|e| {
httpmcp_rust::McpError::InvalidParams(format!("Invalid UTF-8: {}", e))
})?;
Ok(HttpResponse::Ok().json(json!({
"success": true,
"filename": filename,
"size_bytes": content.len()
})))
}
},
)
.build()?
.run("127.0.0.1:8080")
.await
}
Test custom endpoints:
curl http://localhost:8080/health
curl -X POST http://localhost:8080/api/data \
-H "Content-Type: application/json" \
-d '{"name": "test"}'
curl -X POST http://localhost:8080/upload \
-F "file=@data.csv"
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"ping"}'
Request Context
Access headers and request metadata in all handlers:
async fn read_resource(uri: String, ctx: RequestContext) -> Result<Vec<ResourceContents>> {
let auth = ctx.get_authorization();
let token = ctx.get_bearer_token();
let tenant = ctx.get_custom_header("x-tenant-id");
println!("Request ID: {}", ctx.request_id);
println!("Method: {}", ctx.method);
println!("Path: {}", ctx.path);
println!("Remote: {:?}", ctx.remote_addr);
Ok(vec![])
}
Headers Example
Use headers for authentication, tenant isolation, or custom metadata:
use httpmcp_rust::{HttpMcpServer, RequestContext, ToolMeta, Result};
use serde_json::{json, Value};
use std::collections::HashMap;
async fn secure_tool(args: HashMap<String, Value>, ctx: RequestContext) -> Result<Value> {
let token = ctx.get_bearer_token()
.ok_or_else(|| httpmcp_rust::McpError::Unauthorized("Missing token".to_string()))?;
if !validate_token(token) {
return Err(httpmcp_rust::McpError::Unauthorized("Invalid token".to_string()));
}
let tenant_id = ctx.get_custom_header("x-tenant-id")
.unwrap_or("default");
tracing::info!(
"Request {} from {:?} for tenant {}",
ctx.request_id,
ctx.remote_addr,
tenant_id
);
Ok(json!({"status": "success"}))
}
fn validate_token(token: &str) -> bool {
!token.is_empty()
}
#[tokio::main]
async fn main() -> std::io::Result<()> {
HttpMcpServer::builder()
.name("secure-server")
.version("1.0.0")
.tool(
"secure_tool",
ToolMeta::new().description("Tool with auth"),
secure_tool,
)
.build()?
.run("127.0.0.1:8080")
.await
}
Middleware Configuration
Enable CORS and OAuth 2.0:
use httpmcp_rust::HttpMcpServer;
#[tokio::main]
async fn main() -> std::io::Result<()> {
let server = HttpMcpServer::builder()
.name("middleware-example")
.version("1.0.0")
.enable_cors(true)
.with_oauth(
"your-client-id",
"your-client-secret",
"https://auth.example.com/token",
"https://auth.example.com/authorize",
)
.build()?;
server.run("127.0.0.1:8080").await
}
Test with headers:
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-token" \
-H "x-tenant-id: acme-corp" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "secure_tool",
"arguments": {}
}
}'
Examples
Available Examples
1. Simple Server (simple_server.rs)
- Basic resources and tools
- Minimal setup
2. Full Server (full_server.rs)
- All MCP capabilities
- Custom headers
- File system resources
- Calculator tools
- Code review prompts
3. Travel Planner (travel_planner.rs) 🌟
- Real-world domain example
- Complete travel planning system
- Resources: destinations, itineraries, bookings, guides
- Tools: flight search, hotel search, weather, budget calculator, currency converter
- Prompts: trip planning, budget advice, packing lists
- Includes custom health endpoint
4. Endpoint Example (endpoint_example.rs)
- Custom HTTP REST API endpoints
- Health check, user list, data creation endpoints
- Shows how to mix MCP protocol with custom REST APIs on the same port
5. Multipart Upload Example (multipart_upload.rs)
- File upload handling with multipart/form-data
- CSV file processing and parsing
- Demonstrates
.multipart_endpoint() usage
Run Examples
cargo run --example simple_server
cargo run --example full_server
cargo run --example travel_planner
cargo run --example endpoint_example
cargo run --example multipart_upload
./examples/travel_planner_test.sh
Testing with curl
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {},
"clientInfo": {"name": "test", "version": "1.0"}
}
}'
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "resources/list"
}'
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "echo",
"arguments": {"message": "Hello"}
}
}'
curl -N http://localhost:8080/mcp \
-H "Accept: text/event-stream"
Architecture
httpmcp-rust/
├── src/
│ ├── lib.rs # Public API
│ ├── server.rs # HttpMcpServer builder
│ ├── transport.rs # HTTP + SSE handlers
│ ├── jsonrpc.rs # JSON-RPC types
│ ├── protocol.rs # MCP protocol types
│ ├── context.rs # RequestContext
│ ├── error.rs # Error handling
│ ├── handlers/ # Trait definitions
│ │ ├── resources.rs
│ │ ├── tools.rs
│ │ ├── prompts.rs
│ │ └── lifecycle.rs
│ ├── auth/ # OAuth 2.0
│ ├── sse/ # Server-Sent Events
│ └── middleware/ # CORS, validation
└── examples/
├── simple_server.rs
└── full_server.rs
Features
✅ Completed
- JSON-RPC 2.0 support
- All MCP protocol methods (resources, tools, prompts)
- HTTP POST endpoint
- SSE GET endpoint with event IDs
- RequestContext with headers access
- OAuth 2.0 configuration
- CORS middleware
- Request validation
- Type-safe error handling
- Comprehensive examples
🚧 TODO
- Full OAuth token validation
- SSE resumption logic
- Rate limiting
- Metrics/observability
- More examples
- Integration tests
MCP Protocol Support
This library implements the Model Context Protocol specification:
- ✅ Initialization & lifecycle
- ✅ Resources (list, read, templates, subscribe)
- ✅ Tools (list, call)
- ✅ Prompts (list, get)
- ✅ Logging (setLevel)
- ✅ Ping/pong
- ✅ JSON-RPC 2.0
- ✅ HTTP with SSE transport
Contributing
Contributions are welcome! Please read CONTRIBUTING.md for details on our code of conduct and the process for submitting pull requests.
Development
git clone https://github.com/renaiss-ai/httpmcp-rust.git
cd httpmcp-rust
cargo test
cargo run --example simple_server
cargo run --example full_server
cargo run --example travel_planner
cargo fmt
cargo clippy -- -D warnings
License
Licensed under either of:
at your option.
Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
Resources