tmcp 0.4.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`
//!
//! ```ignore
//! 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
//!
//! ```ignore
//! 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.

#![warn(missing_docs)]

/// Argument envelope used by tool calls and prompt arguments.
mod arguments;
/// Client implementation and transport orchestration.
mod client;
/// JSON-RPC codec for stream framing.
mod codec;
/// Connection traits for clients and servers.
mod connection;
/// Client/server context types.
mod context;
/// Error types and Result alias.
mod error;
/// HTTP transport implementation.
mod http;
/// JSON-RPC message definitions.
mod jsonrpc;
/// Request/response routing and tracking.
mod request_handler;
/// Server implementation and handle types.
mod server;
/// Tool registration and progressive discovery support.
mod toolset;
/// Transport traits and adapters.
mod transport;

/// OAuth and authorization helpers.
pub mod auth;
/// Public schema types for MCP messages.
pub mod schema;
/// Test utilities for building tmcp integration tests.
pub mod testutils;

pub use arguments::Arguments;
pub use client::{Client, SpawnedServer};
pub use connection::{ClientHandler, ServerHandler};
pub use context::{ClientCtx, ServerCtx};
pub use error::{
    Error, Result, TOOL_ERROR_INTERNAL, TOOL_ERROR_INVALID_INPUT, TOOL_ERROR_NOT_FOUND,
    TOOL_ERROR_TIMEOUT, ToolError, ToolResult,
};
pub use schema::ToolResponse;
pub use server::{Server, ServerHandle, TcpServerHandle};
// Export user-facing macros directly from the crate root
pub use tmcp_macros::{Group, ToolResponse, group, mcp_server, tool, tool_params, tool_result};
pub use toolset::{
    ActivationHook, Group, GroupConfig, GroupInfo, ToolSet, ToolSetView, Visibility,
};
#[doc(hidden)]
pub use toolset::{GroupDispatch, GroupRegistration, ToolFuture};

// Keep the full macros module available for internal use
/// Re-exported macros module for internal use.
mod macros {
    pub use ::tmcp_macros::*;
}

#[cfg(test)]
mod tests {
    use super::schema::*;

    #[test]
    fn test_jsonrpc_request_serialization() {
        let request = JSONRPCRequest {
            jsonrpc: JSONRPC_VERSION.to_string(),
            id: RequestId::Number(1),
            request: Request {
                method: "initialize".to_string(),
                params: None,
            },
        };

        let json = serde_json::to_string(&request).unwrap();
        let parsed: JSONRPCRequest = serde_json::from_str(&json).unwrap();

        assert_eq!(parsed.jsonrpc, JSONRPC_VERSION);
        assert_eq!(parsed.id, RequestId::Number(1));
        assert_eq!(parsed.request.method, "initialize");
    }

    #[test]
    fn test_role_serialization() {
        let role = Role::User;
        let json = serde_json::to_string(&role).unwrap();
        assert_eq!(json, "\"user\"");

        let role = Role::Assistant;
        let json = serde_json::to_string(&role).unwrap();
        assert_eq!(json, "\"assistant\"");
    }
}