mercutio 0.7.2

IO-less MCP server library
Documentation
//! I/O transports for MCP servers.
//!
//! This module provides handler traits and ready-made transport implementations. The transports
//! are feature-gated:
//!
//! - **`io-stdlib`**: Synchronous stdin/stdout via [`stdlib::run_stdio`]. Blocking, no async
//!   runtime required.
//! - **`io-tokio`**: Async stdin/stdout via [`tokio::run_stdio`]. Requires Tokio runtime.
//! - **`io-axum`**: HTTP transport via [`axum::mcp_router`]. Implements MCP Streamable HTTP with
//!   session management.
//!
//! The handler traits [`ToolHandler`] and [`MutToolHandler`] are always available for custom
//! transport implementations.

mod session_id;

use std::future::Future;

use thiserror::Error;

pub use self::session_id::{HTTP_SESSION_ID_HEADER, McpSessionId, ParseSessionIdError};
use crate::{ParseError, ProtocolError, ToolOutput, ToolRegistry};

/// Handles tool invocations in concurrent contexts.
///
/// Blanket impl for `Fn(Option<McpSessionId>, R) -> impl Future<Output = Result<T, E>>`.
pub trait ToolHandler<R: ToolRegistry>: Send + Sync {
    /// Error type returned by the handler.
    type Error: std::fmt::Display;

    /// Handles a tool invocation and returns the result.
    fn handle(
        &self,
        session_id: Option<McpSessionId>,
        tool: R,
    ) -> impl Future<Output = Result<ToolOutput, Self::Error>> + Send;
}

impl<R, F, Fut, T, E> ToolHandler<R> for F
where
    R: ToolRegistry + Send,
    F: Fn(Option<McpSessionId>, R) -> Fut + Send + Sync,
    Fut: Future<Output = Result<T, E>> + Send,
    T: Into<ToolOutput>,
    E: std::fmt::Display,
{
    type Error = E;

    async fn handle(&self, session_id: Option<McpSessionId>, tool: R) -> Result<ToolOutput, E> {
        self(session_id, tool).await.map(Into::into)
    }
}

/// Handles tool invocations in exclusive-access contexts.
///
/// Every [`ToolHandler`] is automatically a `MutToolHandler` via blanket impl.
pub trait MutToolHandler<R: ToolRegistry> {
    /// Error type returned by the handler.
    type Error: std::fmt::Display;

    /// Handles a tool invocation and returns the result.
    fn handle(
        &mut self,
        session_id: Option<McpSessionId>,
        tool: R,
    ) -> impl Future<Output = Result<ToolOutput, Self::Error>> + Send;
}

/// Every [`ToolHandler`] is automatically a [`MutToolHandler`].
impl<R: ToolRegistry, T: ToolHandler<R>> MutToolHandler<R> for T {
    type Error = <T as ToolHandler<R>>::Error;

    fn handle(
        &mut self,
        session_id: Option<McpSessionId>,
        tool: R,
    ) -> impl Future<Output = Result<ToolOutput, Self::Error>> + Send {
        ToolHandler::handle(self, session_id, tool)
    }
}

#[cfg(feature = "io-stdlib")]
#[cfg_attr(docsrs, doc(cfg(feature = "io-stdlib")))]
pub mod stdlib;

#[cfg(feature = "io-tokio")]
#[cfg_attr(docsrs, doc(cfg(feature = "io-tokio")))]
pub mod tokio;

#[cfg(feature = "io-axum")]
#[cfg_attr(docsrs, doc(cfg(feature = "io-axum")))]
pub mod axum;

/// Errors from I/O transports.
#[derive(Debug, Error)]
pub enum IoError {
    /// I/O operation failed.
    #[error("IO error")]
    Io(#[source] std::io::Error),

    /// Failed to serialize outgoing message.
    #[error("failed to serialize message")]
    Serialize(#[source] serde_json::Error),

    /// Failed to parse incoming message.
    #[error("failed to parse message")]
    Parse(#[source] ParseError),

    /// Protocol-level error requiring connection close.
    #[error("protocol error")]
    Protocol(#[source] ProtocolError),
}