Movatterモバイル変換


[0]ホーム

URL:


LoginSignup
160

Go to list of users who liked

165

Share on X(Twitter)

Share on Facebook

Add to Hatena Bookmark

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

MCP 基礎知識 & MCP 公式の MCP サーバ自作チュートリアル (C#) やってみた

Last updated atPosted at 2025-08-13

この記事は 100% 人間(私)が書きました。

(というか もともと私の記事は全て「自分の勉強メモ」の意味が強いので、(特別な記載のない限り)全記事 人間 100 です。)

MCP 公式ドキュメントの
MCP サーバ自作チュートリアルに
C# があるのを発見しました!(Python しか無いと思ってた)

チュートリアル

コード

ちょっとやってみるか〜

image.png

作るもの

シンプルなお天気予報 MCP サーバ。

2 つの ツール を持ちます:

  • get_alerts
  • get_forecast

(これもともと Python だったので、ネーミングが Python っぽい(スネークケース)ですね)

事前知識

事前知識①: MCP の機能 3 兄弟

MCP の 3 つのプリミティブな機能:

機能名説明原文
1リソース読み取り専用のコンテンツFile-like data that can be read by clients (like API responses or file contents)
2ツールLLM から (ユーザの承認によって) 呼ばれる (call) 機能 (functions)。この三兄弟で一番使うと思うFunctions that can be called by the LLM (with user approval)
3プロンプトプロンプトテンプレートを提供。(良質なプロンプトは長くなりがちだけど、このテンプレート機能により クソ長呪文プロンプト をユーザが使いやすくなる)Pre-written templates that help users accomplish specific tasks

このチュートリアルでは、↑の MCP 機能三兄弟の ②ツール に焦点を当てていきます。

事前知識②: MCP の通信(トランスポート)

通信規格

MCP サーバと MCP クライアントの間で情報のやりとりする仕組み(トランスポート)については、

  1. 標準入出力 (stdio)
  2. Streamable HTTP (すとりーまぶる HTTP) (旧: HTTP + SSE)

の 2 つが定義されています。

通信方法いつ使う
1標準入出力 (stdio)ローカル MCP サーバ(MCP ホストと同一コンピュータ上に MCP サーバが起動している)
2Streamable HTTP
/HTTP + SSE
リモート MCP サーバ (MCP ホストと MCP サーバの通信を HTTP を介してやりとり)

送受信されるデータの形式: JSON-RPC 2.0

送受信される全てのデータの形式はJSON-RPC 2.0 という形式にしたがっています。
呼び出したいメソッド名とその引数を JSON オブジェクトとして送ります。

JSON-RPC は、 RPC (Remote Procedure Call) を実現するプロトコルのひとつです。

Remote Procedure Call (遠隔 手続き 呼び出し) とは、
他のコンピュータのプログラムを呼び出して実行させるための技術、またはそのためのプロトコルです。

JSON-RPC 2.0 の仕様については、

皆様は 実際の中身を見た方が早いと思うので
公式ドキュメントから リクエスト/レスポンス例を引用します

リクエスト
{"jsonrpc":"2.0","method":"subtract","params":[42,23],"id":1}

これは「subtract」という関数に、引数 42, 23 を渡しているということです

(subtract とは、英語で「引き算」を意味します。)

レスポンス
{"jsonrpc":"2.0","result":19,"id":1}

subtract(42, 23) の結果 (result) の19 が返ってきています。

ちなみに"jsonrpc": "2.0" ですが、
1.0 では このフィールドは存在しなかったのですが
2.0 では (互換性問題を解消するため) jsonrpc フィールドが追加されました。(「2.2 Differences between 1.0 and 2.0」より)

事前注意:標準入出力ベースのサーバを作るときは標準出力を使わない!

標準入出力ベースのサーバ (STDIO-based servers) を作るときは
標準出力 (Standard output) を使わないようにしましょう。

標準出力を書くと JSON-RPC messages に干渉し、サーバを壊してしまうおそれがあります。

(HTTP-based servers 実装時は気にしなくて良いです。HTTP レスポンスに何も干渉しないので)

チュートリアル開始

.NET 環境の確認

このチュートリアルでは .NET 8 以上が求められているので、環境を確認しましょう。

dotnet--version

image.png

空の C# プロジェクトを new する

# 作業ディレクトリを作るmkdirweathercdweather# C# プロジェクトの初期化dotnet new console

無事に C# コンソールプロジェクトの new が完了したら、
そのプロジェクトを Visual Studio や VS Code などで開くと、
こんな感じになります↓(画面は VS Code のもの)

image.png

諸々のパッケージを入れる

MCP SDK と .NET Hosting の NuGet パッケージを入れます

(もし git 管理する人は、この辺で.gitignore を追加しておきましょう)

# MCP SDK の NuGet package を入れるdotnet add package ModelContextProtocol--prerelease# .NET Hosting の NuGet package を入れるdotnet add package Microsoft.Extensions.Hosting

サーバの構築

Program.cs を開き、中身をこれに書き換えます

Program.cs
usingMicrosoft.Extensions.DependencyInjection;usingMicrosoft.Extensions.Hosting;usingModelContextProtocol;usingSystem.Net.Http.Headers;varbuilder=Host.CreateEmptyApplicationBuilder(settings:null);builder.Services.AddMcpServer().WithStdioServerTransport().WithToolsFromAssembly();builder.Services.AddSingleton(_=>{varclient=newHttpClient(){BaseAddress=newUri("https://api.weather.gov")};client.DefaultRequestHeaders.UserAgent.Add(newProductInfoHeaderValue("weather-tool","1.0"));returnclient;});varapp=builder.Build();awaitapp.RunAsync();

このコードは、Model Context Protocol SDK を使用して
標準入出力トランスポートを備えた MCP サーバを作成しています。

標準入トランスポートのサーバ (servers using STDIO transport) を作る際は、
ApplicationHostBuilder を作るときCreateDefaultBuilder ではなくCreateEmptyApplicationBuilder を使うようにしましょう。
これにより、サーバがコンソールに余計なメッセージを書き込まなくなります。

天気 API ヘルパー関数

JSON リクエストの処理を簡素化する、
HttpClient の拡張クラスを作ります。

Tools というフォルダを作って、その下にHttpClientExt.cs を作ります。

Tools/HttpClientExt.cs
usingSystem.Text.Json;internalstaticclassHttpClientExt{publicstaticasyncTask<JsonDocument>ReadJsonDocumentAsync(thisHttpClientclient,stringrequestUri){usingvarresponse=awaitclient.GetAsync(requestUri);response.EnsureSuccessStatusCode();returnawaitJsonDocument.ParseAsync(awaitresponse.Content.ReadAsStreamAsync());}}

次に、
National Weather Service API からの応答をクエリして変換するハンドラーを持つクラスを定義します。

Tools/WeatherTools.cs を作成します

Tools/WeatherTools.cs
usingModelContextProtocol.Server;usingSystem.ComponentModel;usingSystem.Globalization;usingSystem.Text.Json;namespaceQuickstartWeatherServer.Tools;[McpServerToolType]publicstaticclassWeatherTools{[McpServerTool,Description("Get weather alerts for a US state.")]publicstaticasyncTask<string>GetAlerts(HttpClientclient,[Description("The US state to get alerts for.")]stringstate){usingvarjsonDocument=awaitclient.ReadJsonDocumentAsync($"/alerts/active/area/{state}");varjsonElement=jsonDocument.RootElement;varalerts=jsonElement.GetProperty("features").EnumerateArray();if(!alerts.Any()){return"No active alerts for this state.";}returnstring.Join("\n--\n",alerts.Select(alert=>{JsonElementproperties=alert.GetProperty("properties");return$"""Event:{properties.GetProperty("event").GetString()}Area:{properties.GetProperty("areaDesc").GetString()}Severity:{properties.GetProperty("severity").GetString()}Description:{properties.GetProperty("description").GetString()}Instruction:{properties.GetProperty("instruction").GetString()}""";}));}[McpServerTool,Description("Get weather forecast for a location.")]publicstaticasyncTask<string>GetForecast(HttpClientclient,[Description("Latitude of the location.")]doublelatitude,[Description("Longitude of the location.")]doublelongitude){varpointUrl=string.Create(CultureInfo.InvariantCulture,$"/points/{latitude},{longitude}");usingvarjsonDocument=awaitclient.ReadJsonDocumentAsync(pointUrl);varforecastUrl=jsonDocument.RootElement.GetProperty("properties").GetProperty("forecast").GetString()??thrownewException($"No forecast URL provided by{client.BaseAddress}points/{latitude},{longitude}");usingvarforecastDocument=awaitclient.ReadJsonDocumentAsync(forecastUrl);varperiods=forecastDocument.RootElement.GetProperty("properties").GetProperty("periods").EnumerateArray();returnstring.Join("\n---\n",periods.Select(period=>$"""{period.GetProperty("name").GetString()}Temperature:{period.GetProperty("temperature").GetInt32()}°FWind:{period.GetProperty("windSpeed").GetString()}{period.GetProperty("windDirection").GetString()}Forecast:{period.GetProperty("detailedForecast").GetString()}"""));}}

サーバの実行

最後に、次のコマンドを使用してサーバを実行します。

dotnet run

これにより、サーバーが起動し、標準入出力でのリクエストをリッスンします。

自作サーバのテスト:MCP インスペクターを使う

原文では初手 Claude Desktop でテストしていますが、
まずは MCP インスペクターで疎通確認をしたほうが良いと思いましたので、追記します。

MCP インスペクターとは、MCP サーバのテスト・デバッグするための開発者ツールです。

実行するプロジェクトと同じディレクトリでこれを叩きます

npx @modelcontextprotocol/inspector

image.png

するとローカルで web サーバが起動してブラウザが立ち上がります。

image.png

接続設定を以下のように入れます:

項目
Transport TypeSTDIO
Commanddotnet
Argumentsrun

そして(MCP サーバが dot net run された状態で)「Connect」を押すと
接続されます。

右の「List Tools」を押すとエンドポイントの一覧が表示されます。(ここでは「get_forecast」と「get_alearts」の2つ)

image.png

ちゃんと動いているようですね!

Claude Desktop で、自作サーバのテストをする

今回は、MCP クライアントとして Claude Desktop を使います。

Claude Desktop の設定ファイルclaude_desktop_config.json を編集します。

VS Code で編集する場合のコマンドはこれです:

mac の人
code ~/Library/Application\Support/Claude/claude_desktop_config.json
Windows の人
code$env:AppData\Claude\claude_desktop_config.json

image.png

{"mcpServers":{"weather":{"command":"dotnet","args":["run","--project","/プロジェクトへの絶対PATH","--no-build"]}}}

トラブルシューティング

Claude Desktop から MCP サーバに繋がらない

image.png

原文ではコマンド記載についてdotnet だけですが、
私の環境だとこれでは動かず、
dotnet をフルパス指定 (/usr/local/share/dotnet/dotnet) (mac) したら動きました。

詳細は別記事に書きました

Claude Desktop との接続を確認

テキストボックスの左下の "Search and tools" アイコンを押します。

image.png

そこで「weather」と自作 MCP サーバが出てきたら OK です。

自作 MCP サーバを叩いてみる

ツール設定アイコンが表示された場合は、Claude Desktop で次のコマンドを実行してサーバーをテストできます。

  • サクラメントの天気はどうですか?
  • テキサス州で発令中の気象警報は何ですか?

するとこんなダイアログが表示されます

image.png

「"weather" という MCP サーバの "get_forecast" というツールを使用してもいいですか?」

「許可する」を押すと、MCP サーバが叩かれて、
こんな感じになります

image.png
 

やったぜ!
(ちなみに、この MCP サーバが叩いているお天気 API は、米国だけしか対応していないので、テストできるのは米国のエリアのみです)

何が起きているのか

  1. 人間 (あなた) が Claude Desktop で質問を入力する
  2. クライアント (Claude Desktop) があなたの質問を Claude (LLM) に送る
  3. Claude (LLM) は利用可能なツールを分析し、どのツールを使用するかを決定
  4. クライアント (Claude Desktop) は MCP サーバーを通じて選択されたツールを実行する
  5. 結果は Claude (LLM) に送り返される
  6. Claude (LLM) は自然言語で応答する
  7. クライアント (Claude Desktop) に応答が表示される

まとめ

楽しい

追記

有識者(mattn 先生)から MCP のツールの命名についてのアドバイスをいただきました!

要約:MCP のツールの名前はグローバルに相当するから、他のツールとの名前のコンフリクトを避けるために、長くなってもいいからもっと詳細な名前をつけたほうがいい

以下引用

おそらく今後、他の MCP を自作された際に気付くかもですが、MCP のツールの名前はグローバルに相当するので、複数の MCP が混在するとバッティングする可能性や、コンテキストが異なる事で get_alerts は選ばれなくなる可能性が高いです。
若干ウザい名前になってもいいので get_weather_forecast や get_weather_alerts という名前にした方が良いかも。
こうすることで get_earthquake_alerts 等と区別できる様になって、地震や天気の会話でそれぞれが選ばれやすくなります。
https://x.com/mattn_jp/status/1955872341273272753

なるほどオブザイヤー!!!!!
ありがとうございます!!!

追記2:トレンド2位ありがとうございます!

この記事がトレンド入りしていました!!
たくさんの方に読んでいただけて大変嬉しいです!ありがとうございます!

image.png

160

Go to list of users who liked

165
4

Go to list of comments

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
160

Go to list of users who liked

165

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?


[8]ページ先頭

©2009-2025 Movatter.jp