使用Rust构建MCP Server Stdio类型

33 阅读6分钟

使用 Rust 构建 MCP Server 供 Claude Code 使用

本文档详细介绍如何使用 Rust 语言构建一个 MCP (Model Context Protocol) Server,并将其配置到 Claude Code 中使用。

目录

  1. 什么是 MCP
  2. 项目结构
  3. 核心代码解析
  4. 构建和运行
  5. 配置到 Claude Code
  6. 常见问题
  7. 参考资源

什么是 MCP

MCP (Model Context Protocol) 是一个开放协议,允许 AI 助手(如 Claude)与外部工具和数据源进行通信。通过 MCP Server,你可以:

  • 为 Claude 提供自定义工具(tools)
  • 让 Claude 访问外部系统(如数据库、API、监控系统等)
  • 扩展 Claude 的能力,使其能够执行特定任务

MCP Server 通信方式

  • stdio: 通过标准输入/输出进行通信(本文重点)
  • SSE: 通过服务器发送事件进行通信

项目结构

prometheus-mcp-server/
├── Cargo.toml              # Rust 项目配置文件
└── src/
    ├── main.rs             # 程序入口,负责启动 MCP Server
    └── searcher/
        ├── mod.rs          # 模块声明
        └── basic_component.rs  # 核心 MCP 实现

Cargo.toml 依赖说明

[package]
name = "prometheus-mcp-server"
version = "0.1.0"
edition = "2021"

[dependencies]
# 异步运行时
tokio = { version = "1.48", features = ["full"] }
# HTTP 客户端
reqwest = { version = "0.12", features = ["json"] }
# 序列化/反序列化
serde = { version = "1.0", features = ["derive"] }
# JSON 支持
serde_json = "1.0"
# 日志框架
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter", "std", "fmt"] }
# MCP SDK (核心依赖)
rmcp = { version = "0.12.0", features = ["server", "macros", "transport-io"] }
# 时间处理
chrono = "0.4"
# 错误处理
anyhow = "1.0"
# JSON Schema 生成
schemars = "1.2"

关键依赖是 rmcp,这是 Rust 语言的 MCP SDK。


核心代码解析

1. src/main.rs - 程序��口(完整代码)

use anyhow::{Context, Result};
use rmcp::ServiceExt;
use tokio::io::{stdin, stdout};
use tracing::info;
use tracing_subscriber::fmt::{format::Writer, time::FormatTime};

use crate::searcher::basic_component::BasicComponent;

pub mod searcher;

#[tokio::main]
async fn main() -> Result<()> {
    tracing_subscriber::fmt()
        .with_timer(LocalTimer)
        .with_thread_ids(true)
        .with_thread_names(true)
        .with_line_number(true)
        .init();
    info!("start prometheus mcp server...");

    BasicComponent::new()
        .serve((stdin(), stdout()))
        .await
        .context("Failed to serve MCP server")?
        .waiting()
        .await
        .context("Error while waiting for server shutdown")?;
    Ok(())
}

struct LocalTimer;

const fn east_utf8() -> Option<chrono::FixedOffset> {
    chrono::FixedOffset::east_opt(8 * 3600)
}

impl FormatTime for LocalTimer {
    fn format_time(&self, w: &mut Writer<'_>) -> std::fmt::Result {
        let now = chrono::Utc::now().with_timezone(&east_utf8().unwrap());
        write!(w, "{}", now.format("%FT%T%.3f"))
    }
}

关键点

  • 使用 tokio 异步运行时
  • 通过 stdin()stdout() 进行 stdio 通信
  • 调用 serve() 方法启动 MCP 服务
  • 自定义 LocalTimer 实现本地时区(东八区)的时间戳格式化

2. src/searcher/mod.rs - 模块声明(完整代码)

pub mod basic_component;

3. src/searcher/basic_component.rs - MCP Server 实现(完整代码)

use rmcp::{
    ErrorData, RoleServer, ServerHandler,
    handler::server::{tool::ToolRouter, wrapper::Parameters},
    model::{Implementation, ProtocolVersion, ServerCapabilities, ServerInfo, ToolsCapability},
    schemars::JsonSchema,
    service::{NotificationContext, RequestContext},
    tool, tool_handler, tool_router,
};
use serde::{Deserialize, Serialize};
use tracing::info;

///
/// 放置基础组件的指标访问
///

#[derive(Serialize, Deserialize, JsonSchema)]
pub struct MCPNodeMemoryRequest {
    #[schemars(description = "开始时间戳,单位毫秒")]
    start: u64,
    #[schemars(description = "结束时间戳,单位毫秒")]
    end: u64,
}

impl MCPNodeMemoryRequest {
    pub fn start(&self) -> u64 {
        self.start
    }
    pub fn end(&self) -> u64 {
        self.end
    }
}

pub struct BasicComponent {
    tool_router: ToolRouter<BasicComponent>,
}

#[tool_router(router = tool_router)]
impl BasicComponent {
    pub fn new() -> Self {
        Self {
            tool_router: Self::tool_router(),
        }
    }

    #[tool(description = "获取start和end之间的一个时间点的毫秒字符串")]
    pub async fn middle_time(
        &self,
        Parameters(request): Parameters<MCPNodeMemoryRequest>,
    ) -> String {
        info!(
            "MCPServer触发请求,start is {} end is:{}!",
            request.start(),
            request.end()
        );
        chrono::Local::now().to_string()
    }
}

#[tool_handler(router = self.tool_router)]
impl ServerHandler for BasicComponent {
    fn get_info(&self) -> ServerInfo {
        ServerInfo {
            protocol_version: ProtocolVersion::V_2024_11_05,
            capabilities: ServerCapabilities {
                tools: Some(ToolsCapability {
                    list_changed: Some(false),
                }),
                ..Default::default()
            },
            instructions: Some(
                "Prometheus MCP Sserver providing metrics search for business!".into(),
            ),
            server_info: Implementation {
                name: "prometheus-mcp-server".into(),
                version: "0.1.0".into(),
                ..Default::default()
            },
        }
    }

    async fn ping(&self, _ctx: RequestContext<RoleServer>) -> Result<(), ErrorData> {
        info!("Received ping request");
        Ok(())
    }

    async fn on_initialized(&self, _ctx: NotificationContext<RoleServer>) {
        info!("Client initialized successfully");
    }
}

关键宏

  • #[tool_router] - 标记 impl 块,自动生成工具路由
  • #[tool(description = "...")] - 标记函数为可调用工具,并添加描述
  • #[tool_handler(router = self.tool_router)] - 标记 ServerHandler 实现
  • Parameters(request) - 自动解析 JSON 参数为结构体

代码结构说明

  1. MCPNodeMemoryRequest - 请求参数结构体,使用 JsonSchema derive 自动生成 JSON Schema
  2. MCPNodeMemoryRequest 的 impl 块提供 getter 方法 start()end()
  3. BasicComponent struct 包含 tool_router 字段用于工具路由
  4. #[tool_router] impl 块中定义 MCP 工具(如 middle_time
  5. #[tool_handler] impl 块实现 ServerHandler trait,提供服务器信息和生命周期回调

构建和运行

1. 构建项目

在项目根目录执行:

# 开发版本构建
cargo build

# 发布版本构建(优化性能)
cargo build --release

# 仅构建 prometheus-mcp-server
cargo build -p prometheus-mcp-server --release

2. 运行 Server

# 开发版本
因为是stdio类型,所以就等着claude code直接调用就行了

注意:stdio 类型的 MCP Server 启动后会等待通过 stdin 接收 JSON-RPC 消息,不会有其他输出。

3. 测试 Server

可以通过以下方式测试:

# 发送初始化消息
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}' | cargo run -p prometheus-mcp-server

配置到 Claude Code

1. 找到 Claude Code 配置文件

Claude Code 的 MCP 配置文件位置:

  • Linux: ~/.claude.json #对应claude code版本为2.1.7

2. 编辑配置文件

在配置文件中添加 MCP Server 配置:

#注意添加的位置,下面是全局的,也可以对项目级别
{
  "mcpServers": {
    "prometheus-metrics": {
      "command": "/path/to/prometheus-mcp-server",
      "args": [],
      "env": {
        "RUST_LOG": "info",
        "PROMETHEUS_URL": "http://your-prometheus:9090"
      }
    }
  },
  #这个是Project级别
  "projects": {
    "/media/liuxu/data/code/github/binlog-cdc": {
      "allowedTools": [],
      "mcpContextUris": [],
      "mcpServers": {
        "prometheus-mcp-server": {
          "type": "stdio",
          "command": "/media/user/data/code/bitbucket/cpaas-cicd-event-rust/target/release/prometheus-mcp-server",
          "args": [],
          "env": {}
        }
      }
      ......
   }
}

配置说明

字段说明示例
commandMCP Server 可执行文件的绝对路径/home/user/projects/cpaas-cicd-event-rust/target/release/prometheus-mcp-server
args命令行参数数组["--port", "9090"]
env环境变量{"RUST_LOG": "debug"}

3. 获取可执行文件路径

# 获取项目根目录的绝对路径
pwd

# 构建后,可执行文件位于
# /path/to/project/target/release/prometheus-mcp-server

4. 完整配置示例

假设项目位于 /home/user/projects/cpaas-cicd-event-rust/

{
  "mcpServers": {
    "prometheus-metrics": {
      "command": "/home/user/projects/cpaas-cicd-event-rust/target/release/prometheus-mcp-server",
      "args": [],
      "env": {
        "RUST_LOG": "info"
      }
    }
  }
}

5. 重启 Claude Code

配置完成后,重启 Claude Code 使配置生效。

6. 验证配置

在 Claude Code 中,你的 MCP Server 提供的工具应该可以使用了。可以尝试��

使用 middle_time 工具,参数 start=1704067200000, end=1704153600000

常见问题

Q: MCP Server 启动后没有输出?

A: stdio 类型的 MCP Server 通过标准输入/输出与 Claude Code 通信,不应该有其他日志输出。建议使用 RUST_LOG=error 减少日志。

Q: Claude Code 无法连接到 MCP Server?

A: 检查以下几点:

  1. 可执行文件路径是否正确
  2. 文件是否有执行权限 (chmod +x prometheus-mcp-server)
  3. 查看 Claude Code 日志是否有错误信息

Q: 如何调试 MCP Server?

A: 可以单独测试 Server:

echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{...}}' | ./prometheus-mcp-server

Q: 如何添加更多工具?

A: 在 impl 块中添加更多 #[tool] 标记的函数:

#[tool(description = "第二个工具")]
pub async fn second_tool(&self, Parameters(req): Parameters<MCPNodeMemoryRequest>) -> String {
    // 实现逻辑
}

参考资源


项目文件位置

  • 本文档: prometheus-mcp-server/如何构建MCP服务器.md
  • 源码目录: prometheus-mcp-server/src/
  • 配置文件: prometheus-mcp-server/Cargo.toml