| Crates.io | httpmcp-rust |
| lib.rs | httpmcp-rust |
| version | 0.1.4 |
| created_at | 2025-10-21 20:01:03.632516+00 |
| updated_at | 2025-10-22 08:44:11.674246+00 |
| description | A fast, simple library for building MCP servers using Streamable HTTP (Beta) |
| homepage | |
| repository | https://github.com/renaiss-ai/httpmcp-rust |
| max_upload_size | |
| id | 1894360 |
| size | 242,194 |
⚠️ 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.
.multipart_endpoint()[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;
// Define handler functions
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")
// Register resource with metadata
.resource(
"file:///example.txt",
ResourceMeta::new().name("Example").mime_type("text/plain"),
list_resources,
read_resource,
)
// Register tool with metadata
.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
}
Configure your MCP server using the builder pattern:
let server = HttpMcpServer::builder()
.name("my-server") // Server name
.version("1.0.0") // Server version
// Register resources with metadata
.resource(
"file:///example.txt",
ResourceMeta::new().name("Example").mime_type("text/plain"),
list_resources,
read_resource,
)
// Register tools with metadata
.tool(
"add",
ToolMeta::new()
.description("Add two numbers")
.param("a", "number", "First number")
.param("b", "number", "Second number")
.required(&["a", "b"]),
add_tool,
)
// Register prompts with metadata
.prompt(
"code_review",
PromptMeta::new()
.description("Review code")
.arg("code", "Code to review", true),
code_review_prompt,
)
.enable_cors(true) // Enable CORS
.build()?;
server.run("127.0.0.1:8080").await?;
use httpmcp_rust::{RequestContext, ResourceMeta, Result};
use httpmcp_rust::protocol::{Resource, ResourceContents};
// List handler - returns available resources
async fn list_resources(
_cursor: Option<String>,
ctx: RequestContext,
) -> Result<(Vec<Resource>, Option<String>)> {
// Access headers if needed
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))
}
// Read handler - returns resource contents
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,
}])
}
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
}))
}
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))
}
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")
// Add custom GET endpoint
.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"
})))
},
)
// Add custom POST endpoint
.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
}
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");
// Process multipart form fields
while let Some(field) = multipart.next().await {
let mut field = field.map_err(|e| {
httpmcp_rust::McpError::InvalidParams(format!("Multipart error: {}", e))
})?;
// Get filename
if let Some(content_disposition) = field.content_disposition() {
if let Some(fname) = content_disposition.get_filename() {
filename = fname.to_string();
}
}
// Read field data
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);
}
}
// Process file contents
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:
# Health check
curl http://localhost:8080/health
# POST JSON data
curl -X POST http://localhost:8080/api/data \
-H "Content-Type: application/json" \
-d '{"name": "test"}'
# Upload file
curl -X POST http://localhost:8080/upload \
-F "file=@data.csv"
# MCP protocol still works on /mcp
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"ping"}'
Access headers and request metadata in all handlers:
async fn read_resource(uri: String, ctx: RequestContext) -> Result<Vec<ResourceContents>> {
// Get authorization header
let auth = ctx.get_authorization();
// Get bearer token
let token = ctx.get_bearer_token();
// Get custom headers
let tenant = ctx.get_custom_header("x-tenant-id");
// Access request metadata
println!("Request ID: {}", ctx.request_id);
println!("Method: {}", ctx.method);
println!("Path: {}", ctx.path);
println!("Remote: {:?}", ctx.remote_addr);
// Your logic here
Ok(vec![])
}
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> {
// Check authorization
let token = ctx.get_bearer_token()
.ok_or_else(|| httpmcp_rust::McpError::Unauthorized("Missing token".to_string()))?;
// Validate token (pseudo-code)
if !validate_token(token) {
return Err(httpmcp_rust::McpError::Unauthorized("Invalid token".to_string()));
}
// Get tenant ID for multi-tenancy
let tenant_id = ctx.get_custom_header("x-tenant-id")
.unwrap_or("default");
// Log request details
tracing::info!(
"Request {} from {:?} for tenant {}",
ctx.request_id,
ctx.remote_addr,
tenant_id
);
// Your logic here
Ok(json!({"status": "success"}))
}
fn validate_token(token: &str) -> bool {
// Token validation logic
!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
}
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 for browser-based clients
.enable_cors(true)
// Configure OAuth 2.0 (basic setup)
.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:
# Call with authorization
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": {}
}
}'
1. Simple Server (simple_server.rs)
2. Full Server (full_server.rs)
3. Travel Planner (travel_planner.rs) 🌟
4. Endpoint Example (endpoint_example.rs)
5. Multipart Upload Example (multipart_upload.rs)
.multipart_endpoint() usage# Simple server
cargo run --example simple_server
# Full-featured server
cargo run --example full_server
# Travel planner (recommended to see full capabilities)
cargo run --example travel_planner
# Endpoint example (custom REST API + MCP)
cargo run --example endpoint_example
# Multipart upload example
cargo run --example multipart_upload
# Test travel planner with automated suite
./examples/travel_planner_test.sh
# Initialize connection
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"}
}
}'
# List resources
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "resources/list"
}'
# Call a tool
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"}
}
}'
# SSE stream
curl -N http://localhost:8080/mcp \
-H "Accept: text/event-stream"
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
This library implements the Model Context Protocol specification:
Contributions are welcome! Please read CONTRIBUTING.md for details on our code of conduct and the process for submitting pull requests.
# Clone the repository
git clone https://github.com/renaiss-ai/httpmcp-rust.git
cd httpmcp-rust
# Run tests
cargo test
# Run examples
cargo run --example simple_server
cargo run --example full_server
cargo run --example travel_planner
# Format code
cargo fmt
# Run clippy
cargo clippy -- -D warnings
Licensed under either of:
at your option.
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.