🎛️

通信方式に stdio を使用する C# MCP サーバーを Streamable HTTP (あるいは SSE) 化する

に公開

はじめに

これまで C# で MCP(Model Context Protocol)サーバーを実装する際に、その通信方式として、標準入出力(stdio)を使ってきました。しかし今回、別の通信方式として Streamable HTTP へ切り替えてみたので、その手順をまとめます。実のところ、ModelContextProtocol.AspNetCore NuGet パッケージのおかげで、非常に簡単に移行できます。

以下で、具体的な変更手順と、MCP クライアントからの接続方法を解説します。

Streamable HTTP への変更手順

おおまかな手順は以下のとおりです。

  • プロジェクトファイル(.csproj)での SDK 指定の変更
  • NuGet パッケージの追加/削除
  • Program.cs(ホスト設定)の書き換え
  • 通信方式を指定するメソッド呼び出しの変更
  • エンドポイントマッピングの追加

以下、順を追って詳しく見ていきます。

1. プロジェクトファイル(.csproj)の SDK 指定の変更

もとのプロジェクトファイル (.csproj) は、シンプルなコンソールアプリケーションプロジェクトですので、以下のように始まっています。

*.csproj
<Project Sdk="Microsoft.NET.Sdk">
  ...

これを Streamable HTTP 対応化するために、まずはただのコンソールアプリケーションプロジェクトではなく、ASP.NET Core Web アプリケーションプロジェクトにします。そのために、使用する SDK の指定を変更します。具体的には、Sdk の指定を以下のように書き換えます。

*.csproj
- <Project Sdk="Microsoft.NET.Sdk">
+ <Project Sdk="Microsoft.NET.Sdk.Web">
  ...

2. NuGet パッケージの追加/削除

"ModelContextProtocol.AspNetCore" NuGet パッケージ参照の追加

"ModelContextProtocol.AspNetCore" NuGet パッケージの参照を、プロジェクトに追加します。ターミナルから dotnet CLI を使って追加してもよいですし、

dotnet add package ModelContextProtocol.AspNetCore

プロジェクトファイル (.csproj) に直接 <PackageReference> を書き足しても構いません。

*.csproj
 <Project Sdk="Microsoft.NET.Sdk.Web">
   ...
   <ItemGroup>
     <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.5" />
     <PackageReference Include="ModelContextProtocol" Version="0.2.0-preview.1" />
+    <PackageReference Include="ModelContextProtocol.AspNetCore" Version="0.2.0-preview.1" />
   ...

Microsoft.Extensions.Hosting NuGet パッケージ参照の削除

また、プロジェクトファイルの Sdk 指定を "Microsoft.NET.Sdk.Web" に変更したことに伴い、Microsoft.Extensions.Hosting NuGet パッケージの明示的な参照は不要になります。そのためビルド時に警告が発せられるようになりますので、Microsoft.Extensions.Hosting NuGet パッケージの参照は、プロジェクトファイル (.csproj) から削除しておきます。

*.csproj
 <Project Sdk="Microsoft.NET.Sdk.Web">
   ...
   <ItemGroup>
-    <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.5" />
     <PackageReference Include="ModelContextProtocol" Version="0.2.0-preview.1" />
     <PackageReference Include="ModelContextProtocol.AspNetCore" Version="0.2.0-preview.1" />
   ...

3. Program.cs(ホスト設定)の書き換え

汎用ホストから Web アプリケーションホストへの変更

もともとの stdio ベースの実装はシンプルな汎用ホスト上に構築されています。すなわち、Program.cs 内にて、Host.CreateApplicationBuilder メソッドにてホスト構築しています。

今回は Streamable HTTP 対応化するためにこのコンソールアプリケーションプロジェクトを ASP.NET Core Web アプリケーション化するので、構築するホストも、 WebApplication.CreateBuilder メソッドを使って構築するように変更します。

Program.cs
  ...
- var builder = Host.CreateApplicationBuilder(args);
+ var builder = WebApplication.CreateBuilder(args);
  builder.Services
      .AddMcpServer()
      .WithStdioServerTransport()
      .WithToolsFromAssembly();

  var app = builder.Build();
  await app.RunAsync();

通信方式の指定を変更

さらに MCP サーバーの構成として、通信方式に標準入出力 (sdtio) を使用する旨の指定である WithStdioServerTransport() メソッド呼び出しを、Streamable HTTP (および SSE) を使用する旨の指定である WithHttpTransport() メソッド呼び出しに変更します。

Program.cs
  ...
  var builder = WebApplication.CreateBuilder(args);
  builder.Services
      .AddMcpServer()
-     .WithStdioServerTransport()
+     .WithHttpTransport()
      .WithToolsFromAssembly();

  var app = builder.Build();
  await app.RunAsync();

MCP サーバーの URL エンドポイントを追加

そしてホストビルダーを構成完了後に Build してアプリケーションオブジェクトを生成したあと、そのアプリケーションオブジェクトに対して、MCP サーバーとしてのエンドポイントをマッピングするために MapMcp() メソッド呼び出しを追加します。

Program.cs
  ...
  var builder = WebApplication.CreateBuilder(args);
  builder.Services
      .AddMcpServer()
      .WithHttpTransport()
      .WithToolsFromAssembly();

  var app = builder.Build();
+ app.MapMcp();
  await app.RunAsync();

ちなみに、MapMcp() メソッドは引数に URL プレフィクスを指定することもできます (下記例)。この引数が指定された場合は、MCP サーバーのエンドポイントは、指定されたプレフィクスの URL パスになります。

Program.cs
...
app.MapMcp("foo/bar");
...

以上で完了となります。

MCP クライアントからの接続方法

では、実際にブラウザや MCP Inspector などのクライアントから接続してみましょう。手順は以下のとおりです。

1. サーバーを起動しておく

まずは ASP.NET Core Web アプリケーションとして実装した C# MCP サーバープロジェクトをビルド&起動しておきます。例えば C# MCP サーバープロジェクトがあるフォルダをカレントディレクトリとして開いたターミナルから、dotnet run コマンドを実行します。

dotnet run

あるいは以下のようにリリースビルドとして発行し、実行ファイルを起動しても構いません。

dotnet publish
dotnet exec ./bin/Release/net9.0/MyMcpServer.dll

とくに起動プロファイル (launchsettings.json) の指定がない場合、デフォルトでは http://localhost:5000 で待ち受け開始するはずです。もちろん、起動プロファイルの指定であったり URL の指定があれば、それに従って起動して待ち受け開始します。

2. MCP Inspector からの接続例

MCP クライアントの例として、MCP Inspector から接続する場合の手順は以下のようになります。

MCP Inspector を起動し Web UI を開いたら、以下のように接続構成を指定します。

  • Transport Type = "Streamable HTTP"
  • URL = C# MCP サーバーの待ち受け URL (例: http://localhost:5000/)

以上で "Connect" ボタンをクリックすれば、MCP Inspector から C# MCP サーバーへ、Streamable HTTP で接続されるはずです。

なお、通信方式として Streamable HTTP ではなく、SSE で接続したい場合は、URL には最後に「/sse」を追加して指定します。

  • Transport Type = "SSE"
  • URL = 例: http://localhost:5000/sse

さらに、Program.cs 内での MapMcp() メソッド呼び出し時にプレフィクスを指定していた場合は、いずれもそのプレフィクスを URL に含めるのを忘れないようにしてください。例えば以下のようにプレフィクスに "foo/bar" を指定していた場合、

Program.cs
...
app.MapMcp("foo/bar");
...

MCP クライアントから接続する際の URL 指定は、以下のとおりとなります。

  • Streamable HTTP の場合 = 例) http://localhost:5000/foo/bar
  • SSE の場合 = 例) http://localhost:5000/foo/bar/sse

注意(TLS の場合)

https://localhost:5001 のようにホスト名 = localhost の TLS(HTTPS)でリッスンしている場合は、MCP Inspector および VSCode の GitHub Copilot からの使用において、自己署名証明書の検証エラーとなってしまいました。環境変数 NODE_TLS_REJECT_UNAUTHORIZED0 を設定しておいてから MCP Inspector を起動するなど、いくつか回避方法はあるようですが、当然、リスクもありますのでご注意ください。

おまけ: GitHub Copilot の Agent モードでやらせてみたけど…

実は、最初に stdio ベースから Streamable HTTP(および SSE)に変更しようと思い立ったとき、「せっかくだから GitHub Copilot の Agent モードにやらせてみよう」と考え、実際にやってみました。モデルは GTP-4.1 を使用しました。手順は以下のとおりです。

  1. Visual Studio Code でプロジェクトフォルダを開く。
  2. GitHub Copilot のチャットウィンドウを起動し、Agent モードに切り替える。
  3. 「この C# 製 MCP Server アプリケーションについて、現在、通信方式は stdio として実装されているところを、Streamable HTTP および SSE に変更してください」と指示。

しかしながら結果はまったくダメダメでした。いろいろと参考となりそうな既存のコードをネット上で探してくれたようですが、AI が予想・想定した検索キーワードでは該当情報を得られず、結局、builder.WithStdioServerTransport() メソッド呼び出しの箇所を builder.WithSseServerTransport() という、存在しない架空のメソッド呼び出しに置き換えておしまいでした。

その後も ModelContextProtocol.AspNetCore NuGet パッケージの存在を教えてあげたり、しばし GitHub Copilot とやりとりしましたが、埓が明かなくてあきらめました。

モデルを GPT-4.1 から Claude Sonet 4 に切り替えたところ、今度は、Streamable HTTP/SSE のトランスポート層をスクラッチで実装しはじめてしまったので、これもとりやめました。

Agent の性能の問題というよりは、C# による MCP サーバー実装の情報がこれら Agent にうまく行き渡っていないのが問題のようですね。今後に期待です。

おわりに

以上、C# で構築した MCP サーバーを stdio ベースから SSE/Streamable HTTP ベースに移行する手順をまとめました。今回のポイントは以下のとおりです。

  • .csproj の SDK 指定を Microsoft.NET.Sdk → Microsoft.NET.Sdk.Web に変更する
  • ModelContextProtocol.AspNetCore NuGet パッケージを追加し、Microsoft.Extensions.Hosting は不要になるので削除する
  • Program.cs 内にて、Host.CreateDefaultBuilder()WebApplication.CreateBuilder(args)、さらに WithStdioServerTransport()WithHttpTransport() に書き換える
  • app.MapMcp() を呼び出してエンドポイントを登録する
  • SSE で接続したい場合は URL に /sse を付与する

stdio ベースだと、C# MCP サーバープログラムは MCP クライアントによって起動されるため、その C# MCP サーバーをデバッガ上でブレークポイントで停止させるなどのデバッグ作業が億劫になります (起動中の .NET プロセスに、Visual Studio などのデバッガをアタッチすればいいだけの話ではあるのですが)。

その点、Streamable HTTP/SSE 方式であれば、Visual Studio や VSCode などの開発環境からデバッグ実行を開始するだけで済むので気軽ですね。

いずれにせよ、C# で MCP サーバーを実装する場合は、ModelContextProtocol NuGet パッケージのおかげで、stdio ベースでも Streamable HTTP ベースでもどちらでも実装コードはたいして違わないので (もちろん、他の言語・プラットフォームでも同様かとは思いますが)、要件や需要に応じて選択すればよいですね。

Happy Coding :)

Discussion