rust-mcp-sdk 0.9.0

An asynchronous SDK and framework for building MCP-Servers and MCP-Clients, leveraging the rust-mcp-schema for type safe MCP Schema Objects.
Documentation
use crate::common::resources::{BlobTextResource, PlainTextResource, PokemonImageResource};

use super::tools::GreetingTools;
use async_trait::async_trait;
use rust_mcp_schema::{CompleteResult, ListResourceTemplatesResult, ListResourcesResult};
use rust_mcp_sdk::{
    mcp_server::{enforce_compatible_protocol_version, ServerHandlerCore},
    schema::{
        schema_utils::CallToolError, ListToolsResult, NotificationFromClient, RequestFromClient,
        ResultFromServer, RpcError,
    },
    McpServer,
};
use std::sync::Arc;

// Custom Handler to handle MCP Messages
pub struct ExampleServerHandlerCore;

// To check out a list of all the methods in the trait that you can override, take a look at
// https://github.com/rust-mcp-stack/rust-mcp-sdk/blob/main/crates/rust-mcp-sdk/src/mcp_handlers/mcp_server_handler.rs

#[async_trait]
#[allow(unused)]
impl ServerHandlerCore for ExampleServerHandlerCore {
    // Process incoming requests from the client
    async fn handle_request(
        &self,
        request: RequestFromClient,
        runtime: Arc<dyn McpServer>,
    ) -> std::result::Result<ResultFromServer, RpcError> {
        let method_name = &request.method().to_owned();
        match request {
            // Handle the initialization request
            RequestFromClient::InitializeRequest(params) => {
                let mut server_info = runtime.server_info().to_owned();
                if let Some(updated_protocol_version) = enforce_compatible_protocol_version(
                    &params.protocol_version,
                    &server_info.protocol_version,
                )
                .map_err(|err| RpcError::internal_error().with_message(err.to_string()))?
                {
                    server_info.protocol_version = params.protocol_version;
                }
                return Ok(server_info.into());
            }

            // Handle ListToolsRequest, return list of available tools
            RequestFromClient::ListToolsRequest(_params) => Ok(ListToolsResult {
                meta: None,
                next_cursor: None,
                tools: GreetingTools::tools(),
            }
            .into()),

            // Handles incoming CallToolRequest and processes it using the appropriate tool.
            RequestFromClient::CallToolRequest(params) => {
                let tool_name = params.name.to_string();
                // Attempt to convert request parameters into GreetingTools enum
                let tool_params = GreetingTools::try_from(params)
                    .map_err(|_| CallToolError::unknown_tool(tool_name.clone()))?;
                // Match the tool variant and execute its corresponding logic
                let result = match tool_params {
                    GreetingTools::SayHelloTool(say_hello_tool) => say_hello_tool
                        .call_tool()
                        .map_err(|err| RpcError::internal_error().with_message(err.to_string()))?,
                    GreetingTools::SayGoodbyeTool(say_goodbye_tool) => say_goodbye_tool
                        .call_tool()
                        .map_err(|err| RpcError::internal_error().with_message(err.to_string()))?,
                };
                Ok(result.into())
            }

            // return list of available resources
            RequestFromClient::ListResourcesRequest(params) => Ok(ListResourcesResult {
                meta: None,
                next_cursor: None,
                resources: vec![PlainTextResource::resource(), BlobTextResource::resource()],
            }
            .into()),

            // return list of available resource templates
            RequestFromClient::ListResourceTemplatesRequest(params) => {
                Ok(ListResourceTemplatesResult {
                    meta: None,
                    next_cursor: None,
                    resource_templates: vec![PokemonImageResource::resource_template()],
                }
                .into())
            }

            RequestFromClient::ReadResourceRequest(params) => {
                if PlainTextResource::resource_uri().starts_with(&params.uri) {
                    return PlainTextResource::get_resource().await.map(|r| r.into());
                }
                if BlobTextResource::resource_uri().starts_with(&params.uri) {
                    return BlobTextResource::get_resource().await.map(|r| r.into());
                }

                if PokemonImageResource::matches_url(&params.uri) {
                    return PokemonImageResource::get_resource(&params.uri)
                        .await
                        .map(|r| r.into());
                }

                Err(RpcError::invalid_request()
                    .with_message(format!("No resource was found for '{}'.", params.uri)))
            }

            RequestFromClient::CompleteRequest(params) => {
                if params.argument.name.eq("pokemon-id") {
                    Ok(CompleteResult {
                        completion: PokemonImageResource::completion(&params.argument.value),
                        meta: None,
                    }
                    .into())
                } else {
                    Err(RpcError::method_not_found().with_message(format!(
                        "No handler is implemented for '{}'.",
                        params.argument.name,
                    )))
                }
            }

            // Return Method not found for any other requests
            _ => Err(RpcError::method_not_found()
                .with_message(format!("No handler is implemented for '{method_name}'.",))),
            // Handle custom requests
            RequestFromClient::CustomRequest(_) => Err(RpcError::method_not_found()
                .with_message("No handler is implemented for custom requests.".to_string())),
        }
    }

    // Process incoming client notifications
    async fn handle_notification(
        &self,
        notification: NotificationFromClient,
        _: Arc<dyn McpServer>,
    ) -> std::result::Result<(), RpcError> {
        Ok(())
    }

    // Process incoming client errors
    async fn handle_error(
        &self,
        error: &RpcError,
        _: Arc<dyn McpServer>,
    ) -> std::result::Result<(), RpcError> {
        Ok(())
    }
}