1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PlantUMLコードを検証するMCPサーバーを実装してみた

Last updated at Posted at 2025-04-10

概要

visual studio codeでエージェントモードが提供されたため、お試しでMCPサーバーを作成してみました。

GitHub Copilotでバイブコーディング:エージェントモードとMCPサポートがVS Codeユーザーに提供開始

作ったのはPlantUMLコードを検証するMCPサーバーです。
LLMはPlantUMLコードを生成してくれるんですが、実際に画像に変換する際にエラーがでることが多かったからです。

通信方式はServer-Sent Events (SSE)を採用しました。

動作イメージは以下な感じです。

docker版増やしました。

参考: Dockerizing your .NET C# MCP Server for AI Clients like Claude Desktop

イメージ作成

cd plantuml-mcp-server-stdio
dotnet publish /t:PublishContainer

host設定例

settings.json
    "mcp": {
        "servers": {
            "my-plantuml-mcp-server-docker": {
                "type": "stdio",
                "command": "docker",
                "args": [
                    "run",
                    "--rm",
                    "-i",
                    "--network=host",
                    "plantuml-mcp-server-stdio",
                    "PlantumlBaseUrl=http://your_plantuml_server/"
                ],
            },
        }
    }

MCP サーバーを自作して GitHub Copilot の Agent に可読性の低いクラス名を作ってもらう
が大変参考になりました。ありがとうございました。

実装

使用した言語、ライブラリ、ツール

  • C#
  • .NET 9.0
  • ModelContextProtocol.AspNetCore:0.1.0-preview.6
  • docker
  • github Copilot

MCPサーバーの実装例

Model Context Protocol (MCP)のC#用SDKがあり、それを使用しました。

Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddCommandLine(args);

builder.Services.AddMcpServer()
    .WithToolsFromAssembly();

var app = builder.Build();

app.MapMcp();

app.Run("http://0.0.0.0:3000");

余計な処理もありますけど、これだけでMCPサーバーとして待ち受けます。
ツールについては後述します。

app.MapMcp();

でエンドポイントに割り付けています。
デフォルトは/sseです。今回でいうと
http://localhost:3000/sse
が設定するurlになります。

src/ModelContextProtocol.AspNetCore/McpEndpointRouteBuilderExtensions.cs

に書かれています。
/messageというのもあるみたいです。

Toolの実装例

MCPにはToolsというコンセプトがあって、概要を機械翻訳すると

LLMがサーバーを通してアクションを実行できるようにする

ツールはモデル・コンテキスト・プロトコル(MCP)の強力なプリミティブで、サーバーが実行可能な機能をクライアントに公開できるようにします。ツールを通じて、LLMは外部システムと対話し、計算を実行し、実世界でアクションを起こすことができます。

だそうです。
今回はToolを実装してみました。
他にもPromptsResourcesがあるみたいです。

namespace PlantUmlTools
{
    [McpServerToolType]
    public class PlantumlTool
    {
        [McpServerTool, Description("Validates the provided PlantUML message. If valid, returns 'Ok'. If invalid, returns detailed error information including error description, error line, and other metadata.")]
        public async Task<string> ValidatePlantUml(string message)
        {
            try
            {
                var encodedMessage = EncodePlantUml(message);
                Uri url = new (PlantUmlBaseUrl, $"txt/{encodedMessage}");
                using var httpClient = new HttpClient();

                var response = await httpClient.GetAsync(url);
                var responseContent = await response.Content.ReadAsStringAsync();

                var headers = response.Headers.ToDictionary(h => h.Key, h => string.Join(", ", h.Value));

                var errorDescription = headers.ContainsKey("X-PlantUML-Diagram-Description") && headers["X-PlantUML-Diagram-Description"].Contains("Error");
                if (errorDescription == false)
                {
                    return "Ok";
                }

                var errorDetails = new StringBuilder();
                errorDetails.AppendLine($"FAIL");
                errorDetails.AppendLine($"{headers["X-PlantUML-Diagram-Description"]}");

                if (headers.ContainsKey("X-PlantUML-Diagram-Error"))
                {
                    errorDetails.AppendLine($"{headers["X-PlantUML-Diagram-Error"]}");
                }

                if (headers.ContainsKey("X-PlantUML-Diagram-Error-Line"))
                {
                    errorDetails.AppendLine($"Error-Line: {headers["X-PlantUML-Diagram-Error-Line"]}");
                }

                return errorDetails.ToString().Trim();
            }
            catch (Exception ex)
            {
                return ex.Message; // Return the exception message if an error occurs
            }
        }

    }
}

実装はとても簡単でした。
登録したいクラスにMcpServerToolType属性を付与して、McpServerTool属性を登録したいメソッドに付与するだけです。
Description属性に説明を加えるとLLMが理解するのに役立つ、気がします。。
後はやりたいことをゴリゴリ書くだけです。

Toolの中身

以降はPlantUMLとのやりとりの話になります。
PlantUMLサーバーは異常時にはヘッダーにX-PlantUML-Diagram-ErrorとX-PlantUML-Diagram-Error-Lineを付与してくれます。(参考: how to detect error in plantuml server rendering)

  • 正常時
    image.png

  • 異常時
    image.png

それを利用してLLMから引数としてplantumlコードをもらい、PlantUML Serverに投げてコードの検証をお願いしています。

正常の場合はOKだけ返して、異常の場合はヘッダーの内容を付与して返してます。

使い方

0. cloneして移動

git clone https://github.com/kwhrkzk/plantuml-validator-mcp-server.git
cd plantuml-validator-mcp-server

1. Docker Compose実行

以下のコマンドを実行してサーバーを起動します。

docker compose up -d

2. VSCodeのMCP設定

Use MCP servers in VS Code (Preview)が参考になります。

image.png

MCP:List Servers コマンド

VSCodeのMCP (Model Context Protocol) 拡張機能で利用可能なコマンドの一つです。このコマンドは、現在設定されているMCPサーバーのリストを表示します。

主な用途
MCPサーバーの確認: 現在VSCodeに設定されているMCPサーバーの一覧を確認できます。
サーバーの状態確認: 各サーバーの接続状態や設定内容を確認するのに役立ちます。

MCP: Add Server コマンド

VSCodeのMCP (Model Context Protocol) 拡張機能で利用可能なコマンドの一つで、新しいMCPサーバーを設定に追加するために使用されます。

主な用途
新しいサーバーの登録: MCPサーバーをVSCodeの設定に追加します。
サーバーの接続設定: サーバーのURLやタイプ(例: SSE)を指定して、MCPサーバーとの通信を可能にします。

settings.json
    "mcp": {
        "servers": {
            "my-plantuml-mcp-server": {
                "type": "sse",
                "url": "http://localhost:3000/sse"
            }
        }
    }

エージェントモードでスパナみたいなマークを押してサーバーと接続するとツールが見えていたら準備完了です。
image.png

image.png

実行例

image.png

その後何回かやりとりしましたけど出来上がったのが上のシーケンス図になります。

所感

Model Context Protocol (MCP)すごいですね。
XXXと連携できたらなぁという課題を一気に解決してくれる気がします。
実装も簡単で、実現したいところだけを書くだけでした。
コードもほとんどgithub Copilotがやってくれました。

PostgreSQLのMCPサーバーもあるようなのでよくわからないDBのテーブル構造の解析なんかにも使える気がしてきました。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?