MCP C# SDKは、Model Context Protocol(MCP)の公式C#実装で、NuGetのModelContextProtocol系パッケージとして配布されています。2026年10月時点の最新は2.2.0(2026-08-13)で、2.0.0(2026-07-28)からMCP仕様2026-07-28に対応しました。この記事では、.NET SDK 10.0.401の手元環境で2.2.0を実際にビルドして動かした結果をもとに、サーバーとクライアントの作り方、ASP.NET CoreでHTTP公開するときの設定、1.xから上げるときに引っかかる箇所を説明します。MCPそのものの仕組みはMCP(Model Context Protocol)とは?AIと外部ツールをつなぐ標準規格の仕組みにまとめています。
まとめ:MCP C# SDK 2.2で押さえる要点
- 最新は2.2.0(2026-08-13)。1.0.0は2026-02-25の公開で、1.x系は1.4.1(2026-07-09)が最後です。1.xには重大な不具合の修正しか入りません。
- パッケージはstdioのサーバーとクライアントなら
ModelContextProtocol、HTTPサーバーならModelContextProtocol.AspNetCore。AspNetCoreだけは.NET 8以上が必要です。 - ツールはクラスに
[McpServerToolType]、メソッドに[McpServerTool]を付けるだけで公開されます。メソッド名GetTotalはツール名get_totalに変換されました。 - HTTPサーバーは2.0から既定がステートレス。v1の感覚で
Stateless = falseにすると、2.xのクライアントでも旧仕様2025-11-25に下がって接続されます(実測)。新旧を同じURLで受けるならStatefulForInitializeClientsを使います。 - ツール実行中のユーザー確認は、ステートレスでは
ElicitAsyncが例外になり、InputRequiredExceptionを投げるMRTR方式に置き換える必要があります。サンプリング・Roots・ロギングのAPIは警告MCP9005付きの非推奨です。
MCP C# SDKの現行版と2.0で変わったこと
MCP C# SDKはMCPの公式GitHub組織にあるリポジトリmodelcontextprotocol/csharp-sdkで開発され、READMEには「Maintained in collaboration with Microsoft」とあります。主要パッケージModelContextProtocolのNuGet累計ダウンロードは2026年10月時点で3,000万件を超えています。
| 版 | 公開日 | 対応する仕様 | 位置づけ |
|---|---|---|---|
| 1.0.0 | 2026-02-25 | 2025-11-25 | 最初の安定版 |
| 1.4.1 | 2026-07-09 | 2025-11-25 | 1.x系の最終版 |
| 2.0.0 | 2026-07-28 | 2026-07-28 | ステートレス既定・MRTR |
| 2.1.0 | 2026-08-05 | 2026-07-28 | subscriptions/listen追加 |
| 2.2.0 | 2026-08-13 | 2026-07-28 | 新旧混在のHTTPモード追加 |
2.0.0のリリースノートは冒頭で「MCP 2026-07-28仕様に安定対応した」と書き、破壊的変更を10項目挙げています。中身は大きく3つで、HTTPサーバーの既定がステートレスになったこと、サーバーからクライアントへ問い合わせる仕組みがMRTR(Multi Round-Trip Requests)に替わったこと、実験機能だったTasksが別パッケージへ移ったことです。Microsoftの発表ブログ(2026-07-28)は「v2.0 is backward compatible」と書き、安定版で非推奨でない1.xのAPIはそのままコンパイルでき、2.xのクライアントは古いサーバーに対して旧来のinitializeハンドシェイクへ自動で切り替えます。仕様側の変更点はStreamable HTTPとは?MCPのトランスポートとSSEの違い、2026-07-28仕様の変更点で扱っています。
ライセンスはApache License 2.0と表示されていますが、2.2.0タグのLICENSEファイルは冒頭で「MITからApache-2.0へ移行中」と明記し、再ライセンスの同意が取れていない過去の貢献はMITのまま残るとしています。社内の法務確認では、この前文までを読んでもらうのが確実です。
NuGetパッケージ5種の選び方と対応フレームワーク
2.2.0のパッケージは5つで、上の3つが本体、下の2つが2.0で追加された拡張です。迷ったらModelContextProtocolから始め、HTTPで公開する段階でModelContextProtocol.AspNetCoreを足すのが公式のGetting Startedの推奨です。
| パッケージ | 使う場面 | 対象フレームワーク・前提 |
|---|---|---|
| ModelContextProtocol.Core | クライアントだけ・依存を最小に | net8.0〜10.0 / netstandard2.0 |
| ModelContextProtocol | stdioサーバー・クライアント(DI・ホスティング込み) | net8.0〜10.0 / netstandard2.0 |
| ModelContextProtocol.AspNetCore | HTTPサーバー | net8.0〜10.0 |
| ModelContextProtocol.Extensions.Tasks | 長時間のツール実行をタスク化 | 2026-07-28仕様が前提 |
| ModelContextProtocol.Extensions.Apps | クライアント内に対話型UIを出す | 実験的(MCPEXP003) |
netstandard2.0版があるので、クライアントは.NET Frameworkのアプリからも使えます。2.0.0では「.NET Framework UI MCP clients」を有効にする修正も入りました。一方でHTTPサーバーはASP.NET Coreが前提なので、.NET Framework上のIISアプリにそのまま載せることはできません。Tasksの仕様はMCP Tasks拡張とは?tasks/getポーリングとサーバ主導の非同期設計を解説、AppsはMCP Appsとは?サンドボックスiframeで動く対話型UI拡張の仕組みと採用判断を参照してください。
stdioで動くMCPサーバーを作る手順
Claude DesktopやVS Codeから子プロセスとして起動してもらう、ローカル用のサーバーです。コンソールアプリにパッケージを2つ足します。
dotnet new console -n InvoiceMcp
cd InvoiceMcp
dotnet add package ModelContextProtocol --version 2.2.0
dotnet add package Microsoft.Extensions.Hosting
Program.csを次の内容に置き換えます。税抜金額から税込金額を返すツールを1つ公開する例です。
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using ModelContextProtocol.Server;
using System.ComponentModel;
var builder = Host.CreateApplicationBuilder(args);
builder.Logging.AddConsole(o => o.LogToStandardErrorThreshold = LogLevel.Trace);
builder.Services
.AddMcpServer()
.WithStdioServerTransport()
.WithToolsFromAssembly();
await builder.Build().RunAsync();
[McpServerToolType]
public static class InvoiceTools
{
[McpServerTool, Description("税抜金額から税込金額を計算して返す。")]
public static decimal GetTotal(
[Description("税抜金額(円)")] decimal amount,
[Description("税率。0.1なら10%")] decimal rate = 0.1m)
=> Math.Round(amount * (1 + rate), 0, MidpointRounding.AwayFromZero);
}
WithToolsFromAssembly()がアセンブリ内の[McpServerToolType]を探し、付いているメソッドをツールとして登録します。ホストの組み立ては.NET Generic Host(汎用ホスト)とは?CreateApplicationBuilderでDI・設定・ログ・常駐化と同じ仕組みです。stdioでは標準出力がプロトコルの通り道になるので、公式サンプルはログをLogToStandardErrorThresholdで標準エラーへ回しています。
次章のクライアントから接続したところ、次のように公開されました。
- ツール名はメソッド名
GetTotalからget_totalに変わる。クライアントから呼ぶときはこの名前を使う [Description]は入力スキーマのdescriptionになる。既定値のあるrateはrequiredから外れ、"default":0.1が付くamount=1980で呼ぶと2178が返り、stdio接続の仕様版は2026-07-28で決まった
dotnet new mcpserverテンプレートで始める場合
MicrosoftのテンプレートMicrosoft.McpServer.ProjectTemplates(1.2.1)を入れると、dotnet new mcpserverで雛形を作れます。-t remoteでHTTP版、--aotでネイティブAOT、-f net8.0で対象フレームワークを選べます。NuGetでツールとして配布する設定(PackAsToolとPackageType McpServer)と、MCPレジストリ用の.mcp/server.jsonも生成されます。
注意点は、2026年10月8日に生成した雛形の参照が2.1.0だったことです(stdio版はModelContextProtocol 2.1.0、HTTP版はModelContextProtocol.AspNetCore 2.1.0)。2.2.0で入ったHttpServerSessionMode.StatefulForInitializeClientsはAspNetCoreパッケージ側の機能なので、HTTP版の雛形では生成後にdotnet add package ModelContextProtocol.AspNetCore --version 2.2.0で上げてください。
C#でMCPクライアントを作る:接続・ツール一覧・呼び出し
クライアントもModelContextProtocolパッケージ1つで書けます。上のサーバーをReleaseビルドしておき、隣のフォルダーにクライアント用のコンソールアプリを作ります。
cd InvoiceMcp
dotnet build -c Release
cd ..
dotnet new console -n InvoiceClient
cd InvoiceClient
dotnet add package ModelContextProtocol --version 2.2.0
Program.csを次の内容にして、InvoiceClientフォルダーでdotnet runを実行します。サーバーを子プロセスとして起動し、ツールを呼ぶ例です。
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol;
var transport = new StdioClientTransport(new StdioClientTransportOptions
{
Name = "invoice",
Command = "dotnet",
Arguments = ["../InvoiceMcp/bin/Release/net10.0/InvoiceMcp.dll"],
});
await using var client = await McpClient.CreateAsync(transport);
Console.WriteLine(client.NegotiatedProtocolVersion);
foreach (var tool in await client.ListToolsAsync())
Console.WriteLine(tool.Name);
var result = await client.CallToolAsync(
"get_total",
new Dictionary<string, object?> { ["amount"] = 1980 });
Console.WriteLine(result.Content.OfType<TextContentBlock>().First().Text);
HTTPのサーバーへつなぐときは、トランスポートをnew HttpClientTransport(new HttpClientTransportOptions { Endpoint = new Uri(...) })に替えるだけです。2.xのクライアントは最初にserver/discoverで相手の対応版を確かめ、古いサーバーならinitializeハンドシェイクに切り替えます。
ListToolsAsync()が返すMcpClientToolは、Microsoft.Extensions.AIのAIFunctionを継承しています。AIFunction f = tools[0];がそのままコンパイルできるので、IChatClientに渡すChatOptions.Toolsへ並べれば、MCPサーバーのツール定義をLLMに渡せます。ただしToolsに並べるだけでは定義が渡るだけで、モデルが要求したツールを実行して結果を返す処理は別に要ります。Microsoft.Extensions.AIパッケージのUseFunctionInvocation()を付けてchatClientを組むと、この往復を自動で行います。
IChatClient chatClient = new ChatClientBuilder(providerClient)
.UseFunctionInvocation()
.Build();
IList<McpClientTool> tools = await client.ListToolsAsync();
var response = await chatClient.GetResponseAsync(
"1,980円の税込金額は?",
new ChatOptions { Tools = [.. tools] });
providerClientは、OpenAIやAzure OpenAIなど各LLMプロバイダーのSDKから得たIChatClientです。
ASP.NET CoreでHTTPサーバーにする:SessionModeの3択
リモートから使わせるサーバーはModelContextProtocol.AspNetCoreで作ります。空のWebアプリにパッケージを足します。
dotnet new web -n InvoiceWeb
cd InvoiceWeb
dotnet add package ModelContextProtocol.AspNetCore --version 2.2.0
Program.csを次の内容にします。ツールクラスはstdio版のInvoiceToolsをそのまま使えます。
using ModelContextProtocol.AspNetCore;
using ModelContextProtocol.Server;
using System.ComponentModel;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddMcpServer()
.WithHttpTransport(o =>
{
o.SessionMode = HttpServerSessionMode.Stateless;
})
.WithToolsFromAssembly();
var app = builder.Build();
app.MapMcp("/mcp");
app.Run("http://localhost:3001");
[McpServerToolType]
public static class InvoiceTools
{
[McpServerTool, Description("税抜金額から税込金額を計算して返す。")]
public static decimal GetTotal(decimal amount, decimal rate = 0.1m)
=> Math.Round(amount * (1 + rate), 0, MidpointRounding.AwayFromZero);
}
SessionModeは2.2.0で3値になりました。同じサーバーを3つの設定で起動し、2.2.0のC#クライアントと、旧仕様2025-11-25のinitializeを送るcurlで接続した結果が次の表です。
| SessionMode | 2.2.0クライアント | 旧仕様のinitialize | GET /mcp |
|---|---|---|---|
| Stateless(既定) | 2026-07-28・セッションなし | 200・セッションIDなし | 405 |
| Stateful | 2025-11-25に下がる・セッションあり | セッションID発行 | 400 |
| StatefulForInitializeClients | 2026-07-28・セッションなし | セッションID発行 | 400 |
要注意は2行目です。v1時代のコードにはStateless = falseと書いたものが多く、2.xでもこの指定はStatefulとして生きています。ステートフルなサーバーは2026-07-28の要求を-32022 UnsupportedProtocolVersionで断る作りなので、クライアントを2.xに上げても接続は旧仕様のままになり、MRTRなど新仕様の機能は使えません。
選び方は次のとおりです。
- 新規ならStateless。セッションを持たないので、ロードバランサーの背後で複数台に増やしてもスティッキーセッションが要りません。
- 既存のステートフルなサーバーはStatefulForInitializeClients。古いクライアントにはセッションを渡し、新しいクライアントはステートレスで受けられます。
- Statefulを残すのは、旧仕様の方式でサーバーから任意のタイミングで通知を送る、
resources/subscribeで購読を受ける、クライアントごとに状態を分ける、のいずれかに頼っている場合です。公式ドキュメントのリソース購読の例もStatefulを指定しています。2026-07-28仕様では、クライアントが開くsubscriptions/listenの長時間ストリームで通知を受ける方式が別に用意され、2.1.0でその処理を差し替えるWithSubscriptionsListenHandlerが入りました。
公開時はAllowedHostsを実際のホスト名に絞ってください。公式のGetting Startedは、KestrelがHostヘッダーを既定では検証しないため、ローカルのサーバーでもDNSリバインディング対策としてループバックの名前だけを許可するよう求めています。OAuthで保護する場合、2.0からは認可サーバーのメタデータがcode_challenge_methods_supportedにS256を宣言していないと認可が失敗し、RFC 9207のissuer不一致も拒否されます。クライアント登録の方式はCIMD(Client ID Metadata Documents)とは?MCPでDCRに代わるOAuthクライアント登録の仕組みと実装で説明しています。
ツール実行中のユーザー確認:ElicitAsyncからMRTRへ
「本当に削除しますか」のように、ツールの途中でクライアント側へ入力を求める処理は、2.xで書き方が変わりました。v1までのserver.ElicitAsync(...)は、サーバーからクライアントへ別のリクエストを送るため、セッションが必要です。ステートレスのサーバーで呼ぶと、サーバーログにSystem.InvalidOperationException: Elicitation is not supported in stateless mode.が出て、クライアントにはisErrorがtrueの結果だけが返りました。
2026-07-28仕様では、ツールが「入力が要る」という結果をいったん返し、クライアントが回答を付けて同じツールを呼び直すMRTRに置き換わります。C#ではInputRequiredExceptionを投げます。次のメソッドは、上のInvoiceToolsのような[McpServerToolType]クラスの中に追加します。ファイル先頭にusing ModelContextProtocol.Protocol;とusing System.Text.Json;が必要です。
[McpServerTool, Description("請求書を削除する")]
public static string DeleteInvoice(
McpServer server,
RequestContext<CallToolRequestParams> context,
string id)
{
if (context.Params!.InputResponses is { } responses)
{
var r = responses["confirm"].Deserialize(InputResponse.ElicitResultJsonTypeInfo);
var ok = r?.Action == "accept"
&& r.Content is { } content
&& content.TryGetValue("ok", out var v)
&& v.ValueKind == JsonValueKind.True;
return ok ? $"{id} の削除を確認しました" : "中止しました";
}
if (!server.IsMrtrSupported)
return "このクライアントは確認の問い合わせに対応していません";
throw new InputRequiredException(
inputRequests: new Dictionary<string, InputRequest>
{
["confirm"] = InputRequest.ForElicitation(new ElicitRequestParams
{
Message = $"{id} を削除しますか",
RequestedSchema = new()
{
Properties = new Dictionary<string, ElicitRequestParams.PrimitiveSchemaDefinition>
{
["ok"] = new ElicitRequestParams.BooleanSchema()
}
}
})
},
requestState: $"delete:{id}");
}
クライアント側は、McpClientOptions.Handlers.ElicitationHandlerを登録しておけば、呼び直しはSDKが自動で行います。チェックを付けずに送信された場合も「承認(accept)」で返り、そのときokはfalseなので、Actionだけでなく入力値まで見て判定します。3つのSessionModeすべてでこのツールを呼んだところ、どれも成功し、ハンドラーが呼ばれたのは1回でした。旧仕様で接続したステートフルなセッションでも、SDKが従来のリクエストに変換して動かします。
サンプリング(サーバーがクライアント側のLLMを使う機能)、Roots、ロギングは2026-07-28仕様で非推奨になりました。SampleAsync・AsSamplingChatClient・RequestRootsAsyncを使うコードは警告MCP9005を出してビルドされます。仕様の移行先は、サンプリングがLLMプロバイダーのAPIを直接呼ぶ形、Rootsがツール引数やリソースURIで渡す形、ロギングがstdioでは標準エラー・それ以外はOpenTelemetryです。仕様上は2027-07-28以降の版まで削除されません。
1.xから2.xへ上げるときの確認項目
安定版で非推奨でないAPIだけを使っていれば、パッケージを上げてもビルドは通ります。ただし既定値と検証の厳しさが変わったため、ビルドが通っても挙動が変わる箇所があります。
ビルドは通るが挙動が変わる設定
- HTTPの既定がステートレスに:
Statelessを書いていなかったサーバーは、GETのSSEストリームとサーバー発の通知を失います。ステートフル専用のオプションには警告MCP9006が出ます。 - 構造化出力が値そのままに:
UseStructuredContent = trueで数値などオブジェクト以外を返すツールは、{ "result": 72 }ではなく72を返すようになりました。resultプロパティを読んでいるクライアントは壊れます。 ToolのJSONにinputSchemaが必須に:自作のプロキシやテスト用の固定JSONで省略しているとJsonExceptionになります。空の{}で足ります。- OAuthの検証が厳格に:PKCEのS256宣言、issuerの一致、DCRで
application_typeが送られるようになった点を認可サーバー側で確認します。
急いで2.xに上げない方がよい2つのケース
1つ目は、1.4.x の実験機能Tasksを使っているサーバーです。2.xのTasksはModelContextProtocol.Extensions.Tasksへ移り、APIも通信形式も互換がありません。発表ブログも、後方互換の唯一の例外としてTasksを挙げています。2.xのクライアントから1.xのタスク対応サーバーを呼んでも普通のツール結果として返るだけなので、クライアントとサーバーの両方を同時に移行する計画が要ります。
2つ目は、サーバーからのサンプリングに処理の中心を置いているサーバーです。2.xでも動きますが、ステートレスでは例外になり、仕様からもいずれ消える機能です。上げる作業より先に、LLMを直接呼ぶ設計への作り替えを決める必要があります。どちらにも当たらないなら、1.xに残る理由はほとんどありません。1.xのサポートは、保守者が重大と判断した不具合を1.xの最新マイナー版にパッチとして出すだけです。
他の言語のSDKも同じ仕様改定に合わせて大きく変わりました。TypeScriptはMCP TypeScript SDK v2とは?パッケージ分割とステートレス化の移行要点、PythonはFastMCP 4とは?sessionless対応と廃止APIから読む3系からの移行判断、GoはMCP Go SDKの使い方|v1.8.0でサーバーを作る手順とmcp-goとの違いで扱っています。GoのSDKも既定のステートフルのままだとHTTPで2025-11-25に下がり、C#と同じ落とし穴があります。
よくある質問
MCP C# SDKは公式のSDKですか?
はい。MCPの公式GitHub組織にあるmodelcontextprotocol/csharp-sdkで、Microsoftと協力して保守されています。NuGetのパッケージIDはModelContextProtocolです。
MCP C# SDKに必要な.NETのバージョンは?
2.2.0は.NET 8・9・10とnetstandard2.0を対象にしています。HTTPサーバー用のModelContextProtocol.AspNetCoreだけは.NET 8以上が必要です。新しく作るなら長期サポート版の.NET 10(LTS)が無難です。
C#でFastMCPのような書き方はできますか?
できます。PythonのFastMCPがデコレーターで関数をツールにするのと同じく、C# SDKはメソッドに[McpServerTool]属性を付けるだけでツールになり、引数の型と[Description]から入力スキーマが自動で作られます。別のライブラリを入れる必要はありません。
v1のコードは2.xでもそのまま動きますか?
安定版で非推奨でないAPIはそのままコンパイルでき、実行もできます。ただしHTTPの既定がステートレスに変わり、ステートレスではElicitAsyncとSampleAsyncが例外になります。セッションに頼るサーバーはSessionModeを明示してから上げてください。
Semantic KernelやMicrosoft Agent FrameworkからMCPのツールを使えますか?
使えます。C# SDKのクライアントが返すMcpClientToolはMicrosoft.Extensions.AIのAIFunctionを継承しているので、IChatClient系の仕組みにそのまま渡せます。エージェント側の構成はMicrosoft Agent Framework 1.0とは|Semantic KernelとAutoGenを統合した.NET/Python共通エージェントSDKを参照してください。