ClaudeのC# SDKをIChatClientでMCPツールと統合する
C# / .NET向け公式SDKのIChatClient統合・自動ページネーション・レスポンス検証・リトライ設定を、NuGetの依存関係とあわせて実装例で解説します。
C# SDKの導入 — インストールと動作要件
Anthropic公式のC# SDKは、.NET Standard 2.0以上のアプリケーションからClaude APIへアクセスするためのライブラリです。NuGetパッケージ Anthropic として配布されています。
このSDKが担うのは、リクエストを組み立てて送り、応答を受け取るところまでです。ツールの実行結果を踏まえて次の判断を自律的に下し続けるエージェントの実行基盤は含みません。エージェントループやセッション管理まで欲しいなら、Python・TypeScript向けのAgent SDKやClaude Codeの領分です。既存の.NETアプリケーションやASP.NET Coreのサービスの一機能としてClaudeを呼ぶ構成なら、クライアントSDKが向いています。
dotnet add package AnthropicNuGetに公開されている12.53.0のnuspecでは、ターゲットは net8.0・net9.0・netstandard2.0 の3つです。net8.0とnet9.0では、依存関係は3種類です。Microsoft.Extensions.AI.Abstractions(10.5.1)、System.Net.ServerSentEvents(10.0.1)、System.Text.Json(10.0.6)が並びます。netstandard2.0 では、System.Collections.Immutable と System.Threading.Tasks.Extensions も加わります。ストリーミングのSSE処理と、IChatClientの型定義が、最初から本体の依存に入っています。
using System;
using Anthropic;
using Anthropic.Models.Messages;
AnthropicClient client = new();
MessageCreateParams parameters = new()
{
MaxTokens = 1024,
Messages =
[
new()
{
Role = Role.User,
Content = "Hello, Claude",
},
],
Model = Model.ClaudeOpus5_5,
};
var message = await client.Messages.Create(parameters);
foreach (var block in message.Content)
{
if (block.TryPickText(out var textBlock))
{
Console.WriteLine(textBlock.Text);
}
}モデル指定の列挙値は Model.ClaudeOpus5_5 のように、モデル名の版区切りをアンダースコアで書きます。応答の Content はブロックの集合で、テキストを取り出すには TryPickText で型を絞ります。
クライアント設定は環境変数 ANTHROPIC_API_KEY・ANTHROPIC_AUTH_TOKEN・ANTHROPIC_BASE_URL から自動で読み込まれます。new AnthropicClient { ApiKey = "..." } で明示的に渡すこともできます。
個人・サービスアカウントキーが複数ワークスペースにアクセスできる場合は、anthropic-workspace-id ヘッダーでワークスペースを指定します。サービスアカウントは、Default Workspaceと、追加されたワークスペースにアクセスできます。
一時的に設定を変えたいだけなら、WithOptions で上書きできます。コネクションやスレッドプールは使い回され、元のクライアントには影響しません。
var message = await client
.WithOptions(options =>
options with
{
BaseUrl = "https://example.com",
Timeout = TimeSpan.FromSeconds(42),
}
)
.Messages.Create(parameters);IChatClient統合でMCPツールをそのまま渡す
C# SDKは Microsoft.Extensions.AI.Abstractions の IChatClient を実装しています。公式ページによれば、対象は AnthropicClient と Anthropic.Services.IBetaService です。この抽象化に対応した他のライブラリと、そのまま組み合わせられます。代表例は、MCP C# SDK(ModelContextProtocol)が公開するツールを IChatClient 経由でClaudeに渡すパターンです。
MCPサーバーのツールをClaudeに渡す流れ
- 1
AnthropicClientをIChatClientにする
client.AsIChatClient("claude-opus-5-5")でIChatClientを得ます。モデルは文字列のIDで渡します。 - 2
関数呼び出しのミドルウェアを挟む
AsBuilder().UseFunctionInvocation().Build()を通します。これを挟まないと、渡したToolsはモデルに提示されるだけで、自動実行されません。 - 3
MCPサーバーに接続してツールを取る
McpClient.CreateAsyncでサーバーにつなぎ、ListToolsAsync()でツール一覧を取ります。 - 4
ChatOptionsに載せて問い合わせる
ChatOptionsのToolsに一覧を入れ、GetResponseAsyncを呼びます。
using Anthropic;
using Microsoft.Extensions.AI;
using ModelContextProtocol.Client;
AnthropicClient client = new();
IChatClient chatClient = client.AsIChatClient("claude-opus-5-5")
.AsBuilder()
.UseFunctionInvocation()
.Build();
// MCP C# SDKのMcpClientをそのまま使う
McpClient learningServer = await McpClient.CreateAsync(
new HttpClientTransport(new() { Endpoint = new("https://learn.microsoft.com/api/mcp") }));
ChatOptions options = new() { Tools = [.. await learningServer.ListToolsAsync()] };
Console.WriteLine(await chatClient.GetResponseAsync("Tell me about IChatClient", options));このコードを動かすには、Anthropic 以外にもパッケージが要ります。ModelContextProtocol はNuGet上の別パッケージです。説明文は「.NET SDK for the Model Context Protocol (MCP)」で始まります。
AsBuilder() と UseFunctionInvocation() を使うため、Microsoft.Extensions.AI も追加します。説明文は「Utilities for working with generative AI components」で、Microsoft.Extensions.AI.Abstractions に依存する別パッケージです。
dotnet add package ModelContextProtocol
dotnet add package Microsoft.Extensions.AIPython・TypeScript・Ruby・PHP・Go・Java向けのSDKページには、IChatClientに相当するフレームワーク横断の抽象化との統合の節がありません。Claude固有の形式へツール定義を変換するコードを書かずに、IChatClient を受け付ける.NET側の資産へ接続できるのがC# SDKの特徴です。MCPサーバーを自作する場合の手順はMCPサーバー自作ガイドにあります。
IChatClient は、Microsoft.Extensions.AI が複数のチャットモデルを同じインターフェースの背後に置くために定義した抽象化です。IChatClient だけを知っているミドルウェアやライブラリのコードを、プロバイダーに依存せず書けます。複数のAIプロバイダーを併用するアプリケーションで、この差し替えやすさが効きます。
ストリーミングとレスポンス検証
ストリーミングメソッドは名前に必ず Streaming サフィックスが付き、IAsyncEnumerable を返します。非ストリーミングの対応メソッドがなくても、サフィックスは付きます。
await foreach (var message in client.Messages.CreateStreaming(parameters))
{
Console.WriteLine(message);
}APIのレスポンスが期待した型と一致しないことが、まれにあります。SDKはデフォルトでは例外を投げず、該当プロパティに実際にアクセスしたときに初めて AnthropicInvalidDataException を送出します。レスポンス全体の型を事前に確認する方法は3つあります。
// 1) 取得した応答に対して明示的に検証する
var message = await client.Messages.Create(parameters);
message.Validate();
// 2) クライアント全体で有効にする
AnthropicClient client = new() { ResponseValidation = true };
// 3) 1回の呼び出しだけで有効にする
var checkedMessage = await client
.WithOptions(options => options with { ResponseValidation = true })
.Messages.Create(parameters);遅延検証のままだと、実際に読むプロパティの型が合っている限り、ほかの項目の不一致には気づかないまま実行が進みます。テストコードや、APIとの互換性を厳しく見たい統合レイヤーでは、ResponseValidation を有効にして早めに検出できます。
エラー処理・リトライ・タイムアウト
SDKはカスタムの非チェック例外を投げます。HTTPステータスごとの対応は次のとおりです。
| ステータス | 例外クラス |
|---|---|
| 400 | 例外クラスAnthropicBadRequestException |
| 401 | 例外クラスAnthropicUnauthorizedException |
| 403 | 例外クラスAnthropicForbiddenException |
| 404 | 例外クラスAnthropicNotFoundException |
| 422 | 例外クラスAnthropicUnprocessableEntityException |
| 429 | 例外クラスAnthropicRateLimitException |
| 5xx | 例外クラスAnthropic5xxException |
| その他 | 例外クラスAnthropicUnexpectedStatusCodeException |
これらはAPIエラーの基底クラス AnthropicApiException を継承します。4xx系はさらに Anthropic4xxException も継承するので、ステータスを区別せずクライアントエラーとしてまとめて捕捉するなら、この基底クラスが使えます。
APIエラー以外の例外も3つあります。
AnthropicSseException: SSEストリーミング中のエラーAnthropicIOException: I/OエラーAnthropicInvalidDataException: レスポンスの解釈の失敗
SseExceptionは、最初のHTTPレスポンスが成功したあとのストリーム中の失敗で投げられます。いずれも最上位の AnthropicException を継承しているため、SDK由来の例外を丸ごと受けたいなら、この型で捕捉します。
リトライの対象は限られています。接続エラー、408、409、429、5xxです。デフォルトでは2回まで、短い指数バックオフを挟んで自動的に再試行します。APIがリトライの可否を明示的に指示することもあります。400や401のようなクライアント側の誤りは、再試行されません。
回数は MaxRetries、タイムアウト(デフォルト10分)は Timeout で変えます。どちらもクライアント全体への設定と、WithOptions による1回の呼び出し限りの設定の両方に対応しています。
AnthropicClient client = new() { MaxRetries = 3, Timeout = TimeSpan.FromSeconds(42) };
var message = await client
.WithOptions(options => options with { MaxRetries = 0 })
.Messages.Create(parameters);自動ページネーションと生レスポンスへのアクセス
一覧取得系のメソッドには、全ページを自動で辿る Paginate と、1ページずつ手動で進める方法があります。件数が読めない一覧を全件処理するバッチなら前者、画面のページ送りのように1ページずつ取りたいUIなら後者です。
// 自動ページネーション
var page = await client.Messages.Batches.List(parameters);
await foreach (var item in page.Paginate())
{
Console.WriteLine(item);
}手動の場合は、Items プロパティで現在のページの要素を読み、HasNext() で続きがあるかを確かめ、Next() で次のページを取ります。
var page = await client.Messages.Batches.List();
while (true)
{
foreach (var item in page.Items)
{
Console.WriteLine(item);
}
if (!page.HasNext())
{
break;
}
page = await page.Next();
}レスポンスヘッダーやステータスコード、生のレスポンスボディにアクセスしたいときは、メソッド呼び出しの前に WithRawResponse を付けます。生の HttpResponseMessage は RawMessage プロパティからも取れます。
var response = await client.WithRawResponse.Messages.Create(parameters);
var statusCode = response.StatusCode;
var headers = response.Headers;非ストリーミングの応答は response.Deserialize() で通常のC#クラスに戻せます。ストリーミングの応答は response.Enumerate() で IAsyncEnumerable として扱えます。
var response = await client.WithRawResponse.Messages.Create(parameters);
Message deserialized = await response.Deserialize();
Console.WriteLine(deserialized);バイナリレスポンスをファイルへ保存する
JSON以外の生データを返すメソッドは HttpResponse を返します。ファイルのダウンロードのように、レスポンスをパースせず扱いたい場合の仕組みです。
FileDownloadParams parameters = new() { FileID = "file_id" };
var response = await client.Files.Download(parameters);内容をファイルや任意の Stream へ保存するには CopyToAsync を使います。
using var response = await client.Files.Download(parameters);
using var contentStream = await response.ReadAsStream();
using var fileStream = File.Open(path, FileMode.OpenOrCreate);
await contentStream.CopyToAsync(fileStream);Bedrock・Vertex・AWS・Foundryへのプラットフォーム統合
C# SDKは4つのクラウドを、それぞれ別のNuGetパッケージで提供します。各パッケージは Anthropic 本体に依存し、nuspecには最低限必要な本体のバージョンが書かれています。
プラットフォーム別パッケージと本体への依存
Anthropic.Vertex(0.5.3)
Google Cloud向け。
Anthropic12.41.0以上とGoogle.Apis.Oauth2.v2に依存します。Anthropic.Bedrock(0.13.6)
AnthropicBedrockMantleClientとAnthropicBedrockClientを含みます。Anthropic12.53.0以上とAWS SDKの各パッケージに依存します。Anthropic.Aws(0.5.3)
Claude Platform on AWS向けの
AnthropicAwsClientです。ベータ提供で、Anthropic12.44.0以上に依存します。Anthropic.Foundry(0.7.1)
Microsoft Foundry向けの
AnthropicFoundryClientです。認証にはDefaultAnthropicFoundryCredentials.FromEnv()か明示的な資格情報を渡します。Anthropic12.31.0以上とAzure.Identityに依存します。
下限は各パッケージで異なります。プラットフォーム側だけを新しくして Anthropic を古い版に固定していると、下限を満たせずrestoreで版の食い違いが報告されます。更新は2つをあわせて行います。
Claude Platform on AWSでは、ワークスペースIDを WorkspaceId プロパティか、環境変数 ANTHROPIC_AWS_WORKSPACE_ID で指定します。
Bedrockには2つのクライアントがあり、新規プロジェクトでは前者を使います。
Bedrockのクライアントの使い分け
AnthropicBedrockMantleClient
Messages APIのBedrockエンドポイント向けです。任意の設定として MantleAwsClientOptions を受け取ります。
AnthropicBedrockClient
bedrock-runtime 経由で InvokeModel APIを使う既存アプリのためのクライアントです。認証には AnthropicBedrockCredentialsHelper.FromEnv() か明示的な資格情報を渡します。
どのクラウドを選ぶかは、すでに稼働しているインフラで決まります。
バージョンの上げ方と固定の考え方
公式ページはこのパッケージについて、SemVerに概ね従うとしたうえで、後方互換でない変更がマイナーバージョンで出ることがあると書いています。対象は2つです。技術的には公開されているが外部利用を想定していない内部の変更と、大多数の利用者に影響しないと見込まれる変更です。
つまり Anthropic のマイナー更新でも、内部型に触れるコードは壊れうるということです。通常のメッセージ呼び出しだけを使うアプリなら影響は小さく、内部型に依存した独自拡張を持つ場合は更新のたびにビルドを確かめます。
よくあるつまずき
- レスポンス検証を有効にし忘れて、実行時に不意に例外が出る: 型不一致は該当プロパティに触れるまで表面化しません。事前検証が必要な用途では、
ResponseValidation = trueかValidate()を明示します - MCPのサンプルがビルドできない:
AnthropicだけではModelContextProtocolもMicrosoft.Extensions.AIも入りません。前の節のとおり、別々に追加します - サンプルのモデル名を写すと通らない: 列挙値は
Model.ClaudeOpus5_5、AsIChatClientの引数はclaude-opus-5-5と、書式が違います - デバッグログを本番の判定ロジックに使う:
ANTHROPIC_LOG=debugで有効になるログは、リリースごとに形式や内容が変わりうる、とされています。パースして分岐に使わず、障害調査時の一時的な確認にとどめます
まとめ
C# SDKの核は、IChatClient を実装している点です。Anthropic に Microsoft.Extensions.AI と ModelContextProtocol を足せば、MCPサーバーのツールを変換コードなしでClaudeに渡せます。リトライ・タイムアウト・レスポンス検証は、クライアント全体の設定と WithOptions による1回限りの設定のどちらでも切り替えられます。
既存の.NETアプリケーションにClaudeを組み込むならこのSDK、自律的にツールを連鎖実行するエージェントを組むならAgent SDKやClaude Codeが候補です。パッケージのバージョンが10未満のtryAGI製クライアントを使っている場合は、移行時に tryAGI.Anthropic への切り替えか公式SDKへの移行かを先に決めます。
ツール呼び出し全般の設計はClaudeツールを組み合わせるパターン、コスト面はAnthropic APIのPrompt Cachingを理解するが詳しいです。Ruby版のSDKはRuby SDKの実装ガイドで扱っています。