はなちるのマイノート

Unityをメインとした技術ブログ。自分らしくまったりやっていきたいと思いますー!

【.NET】modelcontextprotocol/csharp-sdkを用いた自作MCP Serverを作成〜MCP Inspectorや自作MCP Clientで確認する方法

MCPについて

MCPについてはそこまで書かなくて良いかとおもますが、アプリケーションがLLMにコンテキストを提供する方法を定めたプロトコルです。

The Model Context Protocol (MCP) is an open protocol that standardizes how applications provide context to Large Language Models (LLMs). It enables secure integration between LLMs and various data sources and tools.

// DeepL翻訳
モデル・コンテキスト・プロトコル(MCP)は、アプリケーションが大規模言語モデル(LLM)にコンテキストを提供する方法を標準化するオープン・プロトコルである。LLMと様々なデータソースやツールとの安全な統合を可能にします。

Introduction - Model Context Protocol

「MCP ClientとMCP ServerがMCP Protocolを通じてやり取りを行う」ことだけは必要最低限覚えておくと良いでしょう。

公式ドキュメント(https://modelcontextprotocol.io/introduction)より引用


より詳細を知りたい方は公式ドキュメントを参照してください。
modelcontextprotocol.io

MCP Serverの実装

.NETで作成する場合にはマイクロソフト公式のMCP Server&Client用のC# SDKであるmodelcontextprotocol/csharp-sdkを活用するのが王道です。
github.com

Minimal APIを活用したプロジェクトを作成する

VisualStudioでもRiderでもdotnet cliでも良いのですが、webのテンプレからプロジェクトを作成します。

# プロジェクトを作成する
$ dotnet new web -f net9.0 -n McpServerSandbox  

# プロジェクトの中身
$ cd McpServerSandbox
$ tree     
.
├── McpServerSandbox.csproj
├── Program.cs
├── Properties
│   └── launchSettings.json
├── appsettings.Development.json
└── appsettings.json

依存ライブラリを導入する

$ dotnet add package ModelContextProtocol --prerelease
$ dotnet add package Microsoft.Extensions.Hosting
ModelContextProtocol

こちらは今回メインで紹介しているmodelcontextprotocol/csharp-sdkを指しています。公式のMCP Server&Client用のC# SDKですね。
www.nuget.org

Microsoft.Extensions.Hosting

.NETアプリケーションでホスティング環境を構築するためのパッケージです。
www.nuget.org

例えば今回では以下のようにサーバーを作成するのに活用しますね。

// using Microsoft.Extensions.Hostingを利用(ImplicitUsingsがenableだとMicrosoft.NET.Sdk.Webの場合は自動で定義されている)
var builder = Host.CreateApplicationBuilder(args);

learn.microsoft.com

MCP Serverの機能

公式ドキュメントを参照して欲しいですが、MCP Serverには以下の3種類の機能(厳密にはloggingとかもある)があり、それぞれ用途/スキーマが異なります。

  • prompts: 言語モデルのインタラクションをガイドする事前定義されたテンプレートまたは指示
  • resources: モデルに追加のコンテキストを提供する構造化データまたはコンテンツ
  • tools: モデルがアクションを実行したり情報を取得したりできるようにする実行可能な関数


modelcontextprotocol.io

MCP ClientとServerの通信手段

ClientとServerの通信手段として、stdio(Standard Input/Output)Streamable HTTPの2種類があります。

The protocol currently defines two standard transport mechanisms for client-server communication:

1. stdio, communication over standard in and standard out
2. Streamable HTTP

Clients SHOULD support stdio whenever possible.
It is also possible for clients and servers to implement custom transports in a pluggable fashion.

プロトコルは現在、クライアントサーバー間通信のための2つの標準トランスポートメカ ニズムを定義している:

1. stdio, standard in と standard out を介した通信
2. Streamable HTTP

クライアントは可能な限り stdio をサポートするべきである(SHOULD)。
クライアントとサーバは、プラグイン可能な方法でカスタムトランスポートを実装することも可能です。

modelcontextprotocol.io

ただMCP InspectorのTransport Type的にはSSEも別であるっぽいかもです...?

MCP InspectorのTransport Type

MCP ServerでToolsの実装

Open-Meteoという無料で利用できる天気予報APIを活用して、現在の東京の天気を返してくれるようにしたいと思います。
🌤️ Free Open-Source Weather API | Open-Meteo.com

using ModelContextProtocol.Server;
using System.ComponentModel;
using System.Text.Json;

var builder = Host.CreateApplicationBuilder(args);

// loggerの設定
builder.Logging.AddConsole(options =>
{
    options.LogToStandardErrorThreshold = LogLevel.Trace;
});

// MCP Server Setup
builder.Services
    .AddMcpServer()                     // MCSPサーバーを追加
    .WithStdioServerTransport()         // 通信手段として標準入出力(Standard Input/Output, Stdio)を使用する
    .WithToolsFromAssembly();           // Toolsを備えたMCPサーバー

// builderにHttpClientを追加
builder.Services.AddHttpClient();
    
await builder.Build().RunAsync();

// MCP Serverのツールを定義
[McpServerToolType]
public static class TemperatureTool
{
    [McpServerTool, Description("Get the current temperature in Tokyo.")]
    public static async Task<string> GetTokyoTemperature(HttpClient client)
    {
        // APIを呼び出す
        var response = await client.GetAsync("https://api.open-meteo.com/v1/forecast?latitude=35.6895&longitude=139.6917&&timezone=Asia%2FTokyo&current_weather=true");
        
        // レスポンスを取得
        response.EnsureSuccessStatusCode();
        var content = await response.Content.ReadAsStringAsync();
        
        // 気温を取得
        var jsonDocument = JsonDocument.Parse(content);
        var temperature = jsonDocument.RootElement
            .GetProperty("current_weather")
            .GetProperty("temperature")
            .GetDouble();
        
        return $"{temperature}°C";
    }
}

MCP Inspectorを利用した動作確認

MCP InspectorはMCP Serverをテスト/デバッグするための対話型のツールです。特に何かを準備する必要はなく、コマンドを打つだけで起動できるのでかなり使いやすいです。

The MCP Inspector is an interactive developer tool for testing and debugging MCP servers. While the Debugging Guide covers the Inspector as part of the overall debugging toolkit, this document provides a detailed exploration of the Inspector’s features and capabilities.

modelcontextprotocol.io

$ npx @modelcontextprotocol/inspector dotnet run --project <path/to/McpServerSandbox.csproj>
Need to install the following packages:
  @modelcontextprotocol/inspector@0.10.2
Ok to proceed? (y) 
Starting MCP inspector...
⚙️ Proxy server listening on port 6277
🔍 MCP Inspector is up and running at http://127.0.0.1:6274 🚀
実際に利用している様子

自作MCP Clientを利用した動作確認

modelcontextprotocol/csharp-sdkはMCP Clientも作成できるので、せっかくなので使ってみましょう。

コード記述

using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol.Transport;

// Standard Input/Output用
var clientTransport = new StdioClientTransport(new StdioClientTransportOptions
{
    Name = "McpServerSandbox",
    Command = "dotnet",
    Arguments = [
        "run",
        "--project",
        "/path/to/McpServerSandbox.csproj"
    ]
});

var client = await McpClientFactory.CreateAsync(clientTransport);

// toolの一覧を取得
foreach (var tool in await client.ListToolsAsync())
{
    Console.WriteLine($"{tool.Name} ({tool.Description})");
}

// GetTokyoTemperatureを実行
var result = await client.CallToolAsync(
    "GetTokyoTemperature",
    new Dictionary<string, object?>(),
    cancellationToken:CancellationToken.None);

// ログ出力
Console.WriteLine(result.Content.First(c => c.Type == "text").Text);
$ dotnet run
GetTokyoTemperature (Get the current temperature in Tokyo.)
15.1°C

MCP Serverを公開する方法について

dotnet publish /t:PublishContainerを用いた方法を紹介しています。
devblogs.microsoft.com