httpmcp-rust 0.1.0

A fast, simple, production-ready library for building MCP servers using Streamable HTTP
Documentation

httpmcp-rust

CI Crates.io Documentation License Rust Version

A fast, simple, and production-ready 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
  • Production-ready - OAuth 2.0, SSE with resumption, proper error handling
  • Type-safe - Strong typing throughout
  • Extensible - Easy to add custom resources, tools, and prompts
  • Full MCP Support - All protocol features (resources, tools, prompts, logging)
  • Headers & Context - Access request headers, remote IP, request ID
  • Middleware - Built-in CORS, validation, and custom middleware support

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;

// 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
}

Usage Guide

Server Builder

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?;

Implementing Handlers

Resource Handlers

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,
    }])
}

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))
}

Request Context

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![])
}

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> {
    // 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
}

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 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": {}
    }
  }'

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

Run Examples

# 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

# Test travel planner with automated suite
./examples/travel_planner_test.sh

Testing with curl

# 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"

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

# 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

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