tmcp 0.3.0

Complete, ergonomic implementation of the Model Context Protocol (MCP)
Documentation

tmcp

A complete Rust implementation of the Model Context Protocol (MCP), providing both client and server capabilities for building AI-integrated applications.

Overview

tmcp offers an ergonomic API for implementing MCP servers and clients with support for tools, resources, and prompts. The library uses async/await patterns with Tokio and provides procedural macros to eliminate boilerplate.

Features

  • Derive Macros: Simple #[mcp_server] attribute for automatic implementation
  • Multiple Transports: TCP, HTTP (with SSE), and stdio support
  • Type Safety: Strongly typed protocol messages with serde
  • Async-First: Built on Tokio for high-performance async I/O

Transport Options

  • TCP: server.listen_tcp("127.0.0.1:3000")
  • HTTP: server.listen_http("127.0.0.1:3000") (uses SSE for server->client)
  • Stdio: server.listen_stdio() for subprocess integration

Building Servers: Macro vs Trait

tmcp provides two approaches for implementing MCP servers:

The #[mcp_server] Macro

Best for simple servers that primarily expose tools. The macro automatically:

  • Generates [ServerHandler] trait implementation
  • Derives tool schemas from function signatures using schemars
  • Registers tools in list_tools and routes calls in call_tool
  • Provides sensible defaults for initialize
use schemars::JsonSchema;
use serde::Deserialize;
use tmcp::{mcp_server, schema::CallToolResult, tool, ServerCtx, ToolResult};

#[derive(Debug, Deserialize, JsonSchema)]
struct GreetParams {
    name: String,
}

#[mcp_server]
impl MyServer {
    #[tool]
    async fn greet(&self, _ctx: &ServerCtx, params: GreetParams) -> ToolResult {
        Ok(CallToolResult::new().with_text_content(format!(
            "Hello, {}!",
            params.name
        )))
    }
}

The [ServerHandler] Trait

Use the trait directly when you need:

  • Custom initialization: Validate clients, negotiate capabilities, or reject connections
  • Per-connection state: Access to ServerCtx in all methods for client-specific data
  • Resources and prompts: Full access to MCP features beyond tools
  • Fine-grained error handling: Custom error responses and logging
use tmcp::{ServerHandler, ServerCtx, Result};
use async_trait::async_trait;

struct MyServer;

#[async_trait]
impl ServerHandler for MyServer {
    async fn initialize(&self, ctx: &ServerCtx, ...) -> Result<InitializeResult> {
        // Custom capability negotiation
    }

    async fn list_tools(&self, ctx: &ServerCtx, ...) -> Result<ListToolsResult> {
        // Dynamic tool registration
    }
}

See [ServerHandler] documentation for the default behavior philosophy.