🦀

MCPのRust SDKを使ってみた

2025/03/29に公開

MCPのRust SDKでDocComment生成ツールを作ってみた

背景

関数や構造体、enum を作ったり修正したときに「あとでちゃんと doc comment 書こう」と思っても、PR作る直前になって、まあいっかとなってしまうことがよくあります。

それなら自動で書いてくれたら助かるなと思って、今回その仕組みを Rust SDK + MCP で作ってみました。とはいえ、正直これ Cursor の .cursorrules 使えばもっと簡単にできると思います。

今回はあくまで MCP や公式 Rust SDK の使い方を勉強してみたかったのが主な目的です。

MCP には Prompts / Tools / Resources といった機能があって、最初は Prompts を使おうとしてたんですが、挙動がよくわからなかったので、最終的には example を参考にして Tools 機能だけで組んでます。

実装

リポジトリ

実装コードは以下で公開しています。
https://github.com/blue-orange-yellow/scribe-crab

ファイル構成

scribe-crab/
├── src/
│   ├── main.rs
│   └── doc_generator.rs
├── .format.md
├── Cargo.toml

doc_generator.rs

use std::{env, fs};
use rmcp::{
    Error as McpError,
    ServerHandler,
    const_string,
    model::{*, tool::{CallToolResult}},
    tool,
};

#[derive(Debug, Clone)]
pub struct DocGenerator;

#[tool(tool_box)]
impl DocGenerator {
    pub fn new() -> Self {
        Self {}
    }

    #[tool(description = "Generate a documentation comment using the format in FORMAT_PATH")]
    fn generate_doc_comment(
        &self,
        #[tool(param, description = "The Rust function code to document")] code: String,
    ) -> Result<CallToolResult, McpError> {
        let format_path = env::var("FORMAT_PATH")
            .map_err(|_| McpError::internal_error("FORMAT_PATH is not set", None))?;

        let format = fs::read_to_string(&format_path)
            .map_err(|e| McpError::internal_error("Failed to read format file", Some(e.to_string().into())))?;

        let prompt = format!(
            "Use the following documentation format:\n\n{}\n\nNow write a documentation comment for this function:\n\n{}",
            format, code
        );

        Ok(CallToolResult::success(vec![Content::text(prompt)]))
    }
}

const_string!(GenerateDocComment = "generate_doc_comment");

#[tool(tool_box)]
impl ServerHandler for DocGenerator {
    fn get_info(&self) -> ServerInfo {
        ServerInfo {
            protocol_version: ProtocolVersion::V_2024_11_05,
            capabilities: ServerCapabilities::builder()
                .enable_tools()
                .build(),
            server_info: Implementation::from_build_env(),
            instructions: Some("Generates doc comments for Rust functions using a format file".into()),
        }
    }
}

main.rs

use scribe_crab::doc_generator::DocGenerator;
use rmcp::transport::stdio;
use tracing_subscriber::fmt::init;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    init();
    let server = DocGenerator::new();
    let transport = stdio::stdio();
    server.serve(transport).await?.waiting().await?;
    Ok(())
}

.format.md

/// 概要
///
/// # 引数
///
/// # 戻り値
///
/// # 主な利用シーン
///

開発中のつまずきポイント

.env が読み込まれない問題

最初 dotenvy.envFORMAT_PATH=.format.md を書いてのですが、MCPサーバーが Cursor から起動されると .env がなぜか読み込んでくれなかったです。理由はちょっとよくわかりません。。。

対処: mcp.json で環境変数を指定

{
  "mcpServers": {
    "scribe-crab": {
      "command": "/path/to/scribe-crab",
      "cwd": "/path/to/scribe-crab",
      "env": {
        "FORMAT_PATH": "/path/to/scribe-crab/.format.md"
      }
    }
  }
}

補足

このツールは doc comment を直接生成するのではなく、「こういうフォーマットでこのコードのdocを書いてね」という Cursor Agent に指示してあげるだけです。最終的な生成は LLM 側に任せる構成です。
そしてLLMがその結果を元にコメントを生成してくれる感じになります。

普通に .cursorrules にフォーマット定義して、生成してもらうほうが断然いいと思います;;

まとめ

最低限の構成でも MCP サーバーを立てるだけなら割と簡単で、Rust SDK の使い方も少し学べました。Prompsとかほんとは使ってみたかったんですけどね。

完全に自分用メモですが、同じような構成で何か作る人の参考になれば嬉しいです。

参考リンク

Discussion