[MCP再入門]「MCPはAIアプリにとってのUSB-C」がしっくりこなかったあなたに

# はじめに

　MCPとは？[公式ページの冒頭](https://modelcontextprotocol.io/introduction)にはUSB-Cみたいな共通仕様、という抽象的な説明があります。この例から端を発したMCPの説明を日本語で書かれているのもいくつか見かけますし、記事としてとても人気があるようです。

　ですが、公式ページを読んでも、日本語で書かれたいろんな紹介ページを読んでも、私にはMCPの良さは全然理解できませんでした。なのに世の中はMCPブームと言っても良いぐらいMCPが流行っています。私が理解できないだけで、沢山の方がその良さを見出しています。これはまずいと思いました。私は何かを誤解してしまったのだと思い、色々と作り散らかし、同僚とディスカッションし続けてようやくMCPの有用性を腑に落ちるレベルまで理解することができました。
 
　理解できなかった理由はいくつか思い当たるのですが、とにかくMCPの良さをちゃんと理解できるまで物凄く遠回りをしました。そして理解した今ならば、私ならこうやって説明するなーというのが自分の中で湧き上がりました。

　この記事は私のようなMCPの有用性が理解できなかった人に向けてのMCP再入門しませんか、という意図で書こうと思いました。参考になればとても嬉しいです。

# どの立ち位置からMCPの有用性を把握したのいか

　まず、MCPを理解するスタンスとして、**AIアプリケーションを作成するに当たって**MCPはどう便利なのかを知りたい、というモチベーションが私にはありました。なので同じスタンスの方にはこの記事は有用だと思います。Claude DesktopやVSCodeなどの第三者が作成したアプリケーションにおけるMCPの活用についての理解したい、という方には内容が濃すぎると思います。

　ここでいう**AIアプリケーション**とは、生成AIと通信しているアプリケーションのことを指します。AIアプリケーションはWebアプリ、WebAPI、スタンドアロンなど様々なものがあると思いますが、それらのAIアプリケーションでは生成AIと通信している前提です。例えば社内向けChatbotをWebアプリケーションとして提供している会社も多いと思いますが、社内向けChatbotもAIアプリケーションです。

# MCPを知っておくべき最低限の用語

まずは最低限の用語の説明からします。私が思うMCP関連の最低限の用語は3つしかありません。

1. MCP Client
1. MCP Server
1. Tool

あと、接続方式としてSTDIOとHTTP+SSE(or Streaming)があります。接続方式についてはMCPについての理解が進むとわかってきます。では1つずつご紹介します。

## 1. MCP Client

　MCP Serverと通信するための実装をしてあるAIアプリケーションのことで、ご自身で作成するAIアプリケーションのことだと理解すればOKです。

 ## 2. MCP Server

 　Server、と名称から物理的なサーバーにデプロイして、そこへMCP Clientがアクセスするのだな、と思ってしまいますがそうではありません。ここ、大事なポイントで誤解しやすいところなので注意しましょう。MCP Serverとは、**「MCPを通じて、特定の機能を公開する軽量プログラム」** のことです。（[公式ページ](https://modelcontextprotocol.io/introduction)）

原文
>MCP Servers: Lightweight programs that each expose specific capabilities through the standardized Model Context Protocol

　なんだかピンとこないと思いますので、この後詳しくご説明します。とにかく今は、MCP ServerはMCP Clientとは別のサーバーにデプロイするわけではない、ということだけ覚えておいてください。

　MCP Serverはただのプログラムなのであらゆることが処理できます。DBにクエリを発行したり、ファイルサーバーを検索したり、GitHubにIssueを作成したりできます。それら１つずつをメソッドとして実装し、MCPを使って外部に公開します。MCP Clientは公開されたメソッドのうち、使いたいものを呼び出して使用します。

:::note info
正確には別サーバーにデプロイするMCP Serverもあります。が、それはMCPの理解が進んでから考えないとMCPを理解する遠回りになります。
:::

## 3.Tool

　MCP Serverが公開するメソッドのことをMCPではToolと呼びます。MCP ClientはMCP ServerがどんなToolを持っているのかを知りません。そこでToolには自身がどんな処理をするToolなのかを解説するメタ情報を付与しておきます。MCP ClientがToolの一覧をMCP Serverに問い合わせると、MCP Serverから返却されたToolの一覧情報の中に各Toolのメタ情報が含まれているので、MCP Clientは適切なToolを選択できるようになります。メタ情報の付与についてはこの後のコードサンプルを見ていただければとても簡単なことがわかります。

## 接続方式

　MCP ClientとMCP Serverとの間の接続方式はSTDIOとHTTP+SSE（or Streaming）の2種類が用意されています。まずはSTDIOから理解することを強くお勧めします。というかSTDIOから理解しないとMCPへの理解の遠回りになります。STDIO方式の通信についてはこの後ご紹介します。

# MCPの概念図

今までの説明を図にするとこんな感じです。公式のものと比べるとだいぶざっくりしていると思いますが、まずはこれで良いのです。理解が進むともうちょっと細かくなっていきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222127/58a4b55b-fab8-4255-a551-3e46aedf60d0.png)

# 具体例からMCPを理解する

　私の結論として、まずはClaude DesktopとMCPとの組み合わせからMCPを理解するのが近道です。じゃあ仕方ないのでまずはClaude Desktopをインストールしようか、とやっても良いのですが、私はClaude Desktopをインストールしませんでした。しなくても大丈夫です。Claude Desktopのダウンロードページを見たり、Webで検索した結果からするに、おそらくChatGPTのようなChatbotのデスクトップ版のようなものなのだな、と理解できます。これだけで理解としては十分です。

[Claude Desktop](https://modelcontextprotocol.io/quickstart/user)

 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222127/f6dc4c57-82a3-416d-8355-8bfdbc4e3cba.png)


　そしてClaude Desktopに**MCPを拡張機能として設定する**ことで、MCPを通じて様々な情報を得た結果をLLM(Claude)に渡したり、与えられたプロンプトによってはMCP越しに何かしらの処理を実行させることができるようになります。

## Claude DesktopへのMCP ServerのセットアップからMCPを理解する

　Claude DesktopへのMCP Serverの設定例を見てみましょう。[MCP公式ページのClaude DesktopへのMCP Serverセットアップをするチュートリアル](https://modelcontextprotocol.io/quickstart/user#2-add-the-filesystem-mcp-server)を見ると、json形式の設定ファイルに対して、次のようなセットアップを行うことが記載されています。

```json:claude_desktop_config.json
{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-filesystem",
        "C:\\Users\\username\\Desktop",
        "C:\\Users\\username\\Downloads"
      ]
    }
  }
}
```

　この例は[FileSystem MCP Server](https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem)というMCP公式ページでも紹介されているMCP Serverを設定しています。このMCP Serverを設定すると、ファイルの読み書きやフォルダの作成・削除、ファイルの検索もできるようになるそうです。

　この設定ファイルから重要なことがいくつかわかります。まず、commandとしてnpxが使用されています。npxはNode.jsをインストールするとついてくるnpmというツールのコマンドのシンタックスシュガーで、Node.js製のツールを自動的にダウンロードして実行するツールです。そしてargsにある@modelcontextprotocol/server-filesystem、これがNode.jsで作られたMCP Serverです。残りの2つのファイルパスはこのMCP Serverへ渡す引数です。

　正確を記すためにnpxについて長々と書きましたが、要するにClaude DesktopはNode.js製のアプリケーションを起動している、ということがわかったのです。つまり**MCP ServerはMCP Clientと同じマシンの中で稼働するプログラムに過ぎない**のです。

　ここでMCP Serverの公式定義を思い出してみましょう。MCP Serverとは
 
     MCPを通じて特定の機能を公開する軽量プログラム
     
 でした。なるほど、確かにMCP Serverはただのプログラムです。そしてこの設定を見る限り、MCP Serverは同一マシン内で起動したプロセスであり、MCP Client(Claude Desktop)とMCP Serverはプロセス間でデータのやり取りをしている訳です。

 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222127/f76296fb-7b52-4e57-a95c-8174a0778fd3.png)

　そしてこのようにMCP ClientとMCP Serverが同一マシン内でプロセス間通信を行う際に、それぞれの標準入出力を使用して通信をする方式をSTDIO、とMCPでは呼んでいます。逆にいうと、STDIO方式を採用した場合はMCP ClientとMCP Serverは必ず同一マシン内に別プロセスとして存在します。

 　詳しい方はご存知だと思いますが、標準入出力を使用してプロセス間通信を行いたい場合、親プロセスからもう１つのアプリケーションを子プロセスとして起動すると標準入出力の接続が簡単にできます。なので、Claude Desktopもこれに倣ってMCP Serverを自分で子プロセスとして立ち上げる方式をとっていると思われます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222127/6e9507d4-43a1-4af3-bdc7-ae2501c89c5e.png)


 　この例ではMCP ServerはNode.js製のアプリケーションとして実装されているのでnpxコマンドを使用しました。でも、コマンドを叩いてプロセスを起動しているわけですから、MCP ServerはNode.jsアプリである必要はありません。Python、.NET、Go、SDKさえあれば何でも良い訳です。

　実際、[Webコンテンツを取得するMCP Serverであるfetch](https://github.com/modelcontextprotocol/servers/tree/main/src/fetch)はPython製なので、npxではなくuvxコマンドを使用するように設定します。

## MCP Serverとは結局何なのか

　Claude DesktopとMCP Serverのセットアップからわかったことは、MCP Serverはただのプログラムでした。しかもMCP Client(Claude Desktop)から同一マシン内で起動する子プロセスです。
 
　結局MCP Serverとはなんでしょうか。MCP Serverが公開するメソッド（Tool）を使用するとデータを取得したり、何か処理をさせることが可能な訳ですが、これは要するにAI Agentのことだと理解すれば良いでしょう。

　AI Agentの明確な定義をどこかの団体がしてくれている訳ではない（と思う）のですが、要するにデータを取得したり、何か処理をしてくれる独立した「何か」です。「何か」は製品やSDKによって変わります。例えば
 
 * OpenAI Agent SDKの場合、Agentはオブジェクト
 * Azure AI Agent Serviceの場合、Agentはサービス

 です。
 
 そして
 
     MCP Serverの場合、Agentはプロセス
 
 と理解すれば良いことがわかります。

まとめると、

**MCP ServerとはMCP Clientと同一マシン内で別プロセスとして稼働するAgentである**

となります。~~これをServerと呼ぶのはやめてほしかったですね。~~
※追記：Serverという言葉の定義からするとMCP Serverという名前付けが間違っているわけではありません。ご質問をいただいたのでこの一文の意図についてはコメントに記載してありますが、完全に余計な一言でしたので削除します。

# MCP の実装の難易度はどうか

　さてMCPの本質はもう掴んでいます。次に気になるのはMCPの実装難易度です。これが高い場合は採用を見送ることになるでしょう。実際に実装を見てみます。

## 超シンプルなMCP Serverを実装する

MCP Clientから先に作りたい気持ちがあると思いますが、適当なMCP Serverがないのでまずは超シンプルなMCP Serverを作ります。

<details><summary>Python</summary>

適当にMCP Server用のフォルダを作成して移動します。

```bash
uv init
uv add mcp[cli]
```

```python:main.py
from mcp.server.fastmcp import FastMCP

# Initialize FastMCP server
mcp = FastMCP("weather")

@mcp.tool()
async def get_alerts(prefecture: str) -> str:
    """Get weather alerts for a Japan prefecture.

    Args:
        prefecture: Japan prefecture name (e.g. Tokyo, Osaka)
    """

    return "現在警報はありません"

@mcp.tool()
async def get_forecast(latitude: float, longitude: float) -> str:
    """Get weather forecast for a location.

    Args:
        latitude: Latitude of the location
        longitude: Longitude of the location
    """

    return "東京は雨です"

if __name__ == "__main__":
    # Initialize and run the server
    mcp.run(transport='stdio')
```
</details>

　get_alertsとget_forecastの２つのToolを公開するシンプルなMCP Serverです。MCP Clientから問い合わせると、どんなToolをMCP Serverが持っているのかがわかるので、MCP Clientは取得したTool群のうち、プロンプトにとって適切なものを実行することになります。
 　一番下にtransport='stdio'となっていますが、これはMCP Clientとのデータのやり取りに標準入出力を使用することを意味しています。

　このMCP Serverを```uv run main.py```と動かしても何も起こりません。子プロセスとして立ち上げて標準入出力を接続するMCP Clientが必要だからです。そこで、公式に用意されているテスト用のMCP Clientである[@modelcontextprotocol/inspector](https://github.com/modelcontextprotocol/inspector)を使います。

 　使い方は簡単です。ローカル環境にNode.jsが入っていれば、次のコマンドを叩くだけです。ただし、今作ったばかりの**MCP Serverのルートディレクトリでコマンドを叩く**必要があることに注意してください。
  
```bash
npx @modelcontextprotocol/inspector
```

　npx コマンドはローカルにモジュールがなかったら一時的にモジュールをダウンロードして実行してくれます。

:::note info
npxでダウンロードしたモジュールの保存先は
Mac: ~/.npm/_npx
Windows: %LocalAppData%/npm-cache/_npx
だそうです。正確には

```bash
npm config get cache
```
でキャッシュのパスを得ると良さそうです。ダウンロードしたモジュールを削除するにはこのパスの中の_npxフォルダを削除する必要があるようですが、最新のnpm@11では[npxに対するコマンド](https://docs.npmjs.com/cli/v11/commands/npm-cache)が用意されているようなので、そちらを使用して消した方が良さそうです。
:::

Inspectorを起動すると、次のようなログがコンソールに表示されるはずです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222127/5fb8c23c-25e4-42a1-85c5-7ec4e03631ec.png)

　真ん中あたりに表示されたhttp://127.0.0.1:6274 をブラウザで開きます。次にcommandに```uv```、Arugumentsに```run main.py```と入力してConnectボタンをクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222127/aad73f99-7055-48c5-83b6-321369edf20f.png)

すると、ボタンの下にConnectedと表示されるはずです。次に画面上部のToolsをクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222127/7d603a98-311a-4241-8b77-12279a4d0a15.png)

List Toolsをクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222127/e5eea809-8f28-49a2-8e3a-83cccf1600f1.png)

すると、先ほどMCP Serverで実装した２つのメソッドget_alertsとget_forecastが表示されます。試しにget_alertsをクリックしてみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222127/1223fe34-6a82-4001-a69a-f831cfa0131b.png)

すると右側にget_alertsの説明と引数のprefectureを入力するテキストボックスが表示されます。MCP Serverで実装した通りです。試しにTokyoと入れてRun Toolをクリックしてみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222127/e83130e4-e73c-4d26-8b47-21d473ae35dd.png)

固定値の"現在警報はありません"が表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222127/59681d7d-0606-4168-be14-a9136c2b29b0.png)

これで超シンプルなMCP Serverが問題なく実装できていることが確認できました。

## 超シンプルなMCP Clientを実装する

　では、自前のAIアプリケーションをMCP ClientとしてMCP Serverを使うようにする実装をしましょう。ただ単にMCP Serverのメソッドを叩くこともできるのですが、それだとあまり意味はありません。私たちはAIアプリケーションにMCPを組み込みたいのです。なのでやりたいことは次の流れです。

1. ユーザーからプロンプトを受け取る
1. MCP Serverからtoolの一覧を受け取る
1. 生成AIのモデルを使って応答生成に必要なtoolをFunction Callingで特定
1. MCP Serverに引数を渡して処理を実行、結果を受け取る
1. 受け取った結果を使って生成AIで応答を生成し、返却

　Function Callingとは、複数のメソッドの情報（何ができるメソッドなのか）を生成AIに渡して、処理すべきメソッドを判定させる仕組みのことです。メソッドはAIアプリ側にあるので生成AIがメソッドの実行まではしてくれません。

　これらの処理を自動化する場合はSemantic KernelやLangChainなどのフレームワークを使用するか、Agent用のSDKを使用します。ここではMCPに対応しているSDKの実装例としてOpenAI Agent SDKを使用する場合ご紹介します。

 ### OpenAI Agent SDK

 Azure上にデプロイしたOpenAI GPT-4oを使用します。

 ```bash
 uv init
 uv sync
 uv add openai-agents
 ```

まずはMCP Serverを使用しないで単純にGPT-4oと通信するだけの実装をします。

```python:main.py
from openai import AsyncAzureOpenAI
from openai.types.responses import ResponseTextDeltaEvent
from agents import set_default_openai_client, Agent, Runner
import asyncio

api_version = "2025-03-01-preview"
endpoint = "YOUR_OPENAI_ENDPOINT"
apikey = "YOUR_OPENAI_APIKEY"

async def main():
    openai_client = AsyncAzureOpenAI(
        api_key=apikey,
        api_version=api_version,
        azure_endpoint=endpoint,
    )

    # Set the default OpenAI client for the Agents SDK
    # set trace off because of recomendation from OpenAI
    set_default_openai_client(openai_client, use_for_tracing=False)

    try:

        agent = Agent(
            name="Assistant",
            instructions="You are a helpful assistant.",
            model="gpt-4o"
        )

        result = Runner.run_streamed(agent, "東京に行きます。天気予報は？")

        async for event in result.stream_events():
            if event.type == "raw_response_event" and isinstance(event.data, ResponseTextDeltaEvent):
                print(event.data.delta, end="", flush=True)
    finally:
        await openai_client.close()

if __name__ == "__main__":
    asyncio.run(main())
```

結果はこんな感じです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222127/84190fdc-d1e3-47bb-838b-7c9e88f72c3e.png)

では、このコードを次のように修正して、先ほど作った超シンプルMCP Serverを使用してみましょう。

```diff_python:main.py
from openai import AsyncAzureOpenAI
from openai.types.responses import ResponseTextDeltaEvent
from agents import set_default_openai_client, Agent, Runner
import asyncio
+from agents.mcp import MCPServerStdio

api_version = "2025-03-01-preview"
endpoint = "YOUR_OPENAI_ENDPOINT"
apikey = "YOUR_OPENAI_APIKEY"

async def main():
    openai_client = AsyncAzureOpenAI(
        api_key=apikey,
        api_version=api_version,
        azure_endpoint=endpoint,
    )

    # Set the default OpenAI client for the Agents SDK
    # set trace off because of recomendation from OpenAI
    set_default_openai_client(openai_client, use_for_tracing=False)

+    server = MCPServerStdio(
+        name="Weather Server, via uv",
+        params={
+            "command": "uv",
+            "args": ["run", "YOUR/UltraSimpleMCPSErver/PATH/main.py"],
+        },
+    )

    try:
+       await server.connect()

        agent = Agent(
            name="Assistant",
            instructions="You are a helpful assistant.",
+           mcp_servers=[server],
            model="gpt-4o"
        )

        result = Runner.run_streamed(agent, "東京に行きます。天気予報は？")

        async for event in result.stream_events():
            if event.type == "raw_response_event" and isinstance(event.data, ResponseTextDeltaEvent):
                print(event.data.delta, end="", flush=True)
    finally:
+       await server.cleanup()
        await openai_client.close()

if __name__ == "__main__":
    asyncio.run(main())
```

argsの```"YOUR/UltraSimpleMCPSErver/PATH/main.py"```ですが、これはご自身が作った超シンプルMCP Serverのルートディレクトリパス/main.pyに変更してください。相対パスでも多分大丈夫です。

実行すると結果はこのようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222127/69f2d715-9d5d-40e7-86a4-81ba862cbd67.png)

ちゃんとMCP Serverを使ってくれましたね。

# 結局MCPは有効なのだろうか

　そろそろ考察しましょう。AIアプリケーション（MCP Client）がMCP Server（という小さなプログラム）を同一マシンの別プロセスとして呼び出すことで、AIアプリケーションを実に簡単に、しかも大幅に拡張できることがわかりました。

　まずわかっているのはClaude DesktopのようなAIデスクトップアプリケーションをユーザーが自分で拡張できるようになります。VS CodeもGitHub CopilotのAgentモードでMCP Serverを組み込む機能を[Previewリリース](https://code.visualstudio.com/docs/copilot/chat/mcp-servers)しました。AgentモードでLLMにあれこれ指示を出すと、MCP Serverをよしなに使って様々な処理をやってくれます。

 　例えば

  * GitHubにリポジトリ作って / Issue作って / コミット&プッシュして
  * PostgreSQLのこのテーブルのスキーマを見せて / ダミーデータを作って
  * FigmaのこのデザインをReactで実装して

などがVS Codeで可能になる訳です。

　拡張性を見込めるのはデスクトップアプリだけではないでしょう。生成AI（の使用）を組み込んだWebアプリケーションの場合もとても簡単に機能拡張できるようになります。例えば[Copilot Studio](https://www.microsoft.com/en-us/microsoft-copilot/blog/copilot-studio/introducing-model-context-protocol-mcp-in-copilot-studio-simplified-integration-with-ai-apps-and-agents/?msockid=2a46d5d3470f6e5f00bec67a46756fca)ではRemote MCP Serverをエージェントのアクションとして追加することで、Microsoft 365 Copilot Chatで 自作のMCP Serverを使用できるようになりました。

　もっと身近な例で考えてみます。社内向けの生成AIのChatbotがあなたの会社にはありませんか？自社で生成AIのChatbotを構築しているなら、MCP Client対応すれば様々な機能拡張が簡単にできるようになる、ということです。夢が広がりますね。

　ポイントは既に沢山の便利そうなMCP Serverが公開してある、という点です。それらをダウンロードするだけで、すぐにアプリケーションに組み込んで機能拡張できるのです。これはものすごく大きなメリットです。どうしても必要な機能を持つMCP Serverがない場合のみ、自分で作成すれば良いのです。

　MCPの有用性は確かに高いと思います。この汎用性を活かすならRAGもMCP Serverで作り直してToolの１つにしてしまった方が良いでしょう。

 ## リモートMCP Serverは有用なのか

　もう１つ考察しておきましょう。MCP Serverとの接続方式にはSTDIO以外にもう１つ、HTTP+SSE（or Streaming）というものがあります。その名前まま、HTTPとServerSendEventによる接続です。この接続方式を採用すれば、MCP ServerをMCP Clientの外部のサーバーにデプロイしてあっても接続することができます。

　このリモートMCP Serverに有用性はあるのでしょうか。例えばWebAPIのエンドポイントを公開しているSaaS製品がリモートMCP Serverを公開する価値はあるでしょうか。今のところ、その価値はないでしょう。GitHubのMCP Serverを見てみると、STDIOでの接続になっています。ローカルマシンのMCP ServerからGitHubのWebAPIを叩けば良いのだからリモートMCP Serverを用意する必要性がどこにもありません。APIKEYやTokenをローカルに格納できるというセキュリティ面でのメリットもあります。

　もしリモートMCP Serverが活躍するシナリオがあるとしたら、MCP Server越しにしかリモート環境内部のリソースにアクセスできないような場合かもしれません。でも私には具体例が思いつきませんでした。

# 最後に

　いかがだったでしょうか。MCPの有用性が腑に落ちていただけたなら嬉しいです。それにしてもOpenAI Agent SDKしか試していませんが、AIアプリケーションへの組み込みが驚くほど簡単だと思いませんか？本当にすごいですね。他にもSemantic KernelやLangChain、AutoGenなどもMCP対応が行われています。是非試して記事にしてください。読みたいです。長文記事を最後までお読みいただきまして、ありがとうございました。

# Appendix: HTTP+SSE方式の実装

HTTP+SSE方式はあまり出番がないと思いますが、一応サンプル実装はしてみましたので残しておきます。

## MCP Server

HTTP+SSEの通信方式はStreamingに取って代わることが決定しているようですが、SDKが準備されるまでは少し時間がかかりそうです。

<details><summary>Python</summary>

mcp[cli] ver:1.6.0
Starlette ver:0.46.1
uvicorn ver:0.34.0

```text:requirements.txt
mcp[cli]
Starlette
uvicorn
```
```bash
pip install -r requirements.txt
```

```python:server.py
from mcp.server.fastmcp import FastMCP
from starlette.applications import Starlette
from starlette.routing import Mount

mcp = FastMCP("Echo")

@mcp.resource("echo://{message}")
def echo_resource(message: str) -> str:
    """Echo a message as a resource"""
    return f"Resource echo: {message}"

@mcp.tool()
def echo_tool(message: str) -> str:
    """Echo a message as a tool"""
    return f"Tool echo: {message}"

@mcp.prompt()
def echo_prompt(message: str) -> str:
    """Create an echo prompt"""
    return f"Please process this message: {message}"

# Starletteアプリにマウント
app = Starlette(
    routes=[
        Mount('/', app=mcp.sse_app()),
    ]
)

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="localhost", port=3000)
```

</details>

## MCP Client
<details><summary>python</summary>

mcp ver:1.6.0
asyncio ver:3.4.3
```text:requirements.txt
mcp
asyncio
```

```bash
pip install -r requirements.txt
```

```python:client.py
from mcp import ClientSession
from mcp.client.sse import sse_client

async def main():
    async with sse_client("http://localhost:3000/sse") as streams:
        async with ClientSession(streams[0], streams[1]) as session:
            await session.initialize()

            # ツール一覧取得
            listTools = await session.list_tools()
            print("Available tools:")
            for tool in listTools.tools:
                print(f" - {tool.name}, Description: {tool.description}, Schema: {tool.model_json_schema()}")

            # Echo ツールを取得
            echo_tool = next((tool for tool in listTools.tools if tool.name == "echo_tool"), None)

            # Echo ツールを使う
            response = await session.call_tool(
                name=echo_tool.name,
                arguments={"message": "Hello, world!"})

            print("")            
            print("Echo response:", response.content)

if __name__ == "__main__":
    import asyncio
    asyncio.run(main())
```
</details>

<details><summary>C#</summary>

ModelContextProtocol ver:0.1.0-preview.7
```bash
dotnet add package ModelContextProtocol
```

```csharp:Progiram.cs
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol.Transport;

await using var client = await McpClientFactory.CreateAsync(
    clientTransport: new SseClientTransport(
        new SseClientTransportOptions()
        {
            Endpoint = new Uri("http://localhost:3000/sse"),
        })
);

var tools = await client.ListToolsAsync();

foreach (var tool in tools)
{
    Console.WriteLine($"{tool.Name}, Description: {tool.Description}, Schema: {tool.JsonSchema}");
}

// Echo ツールを取得
var echoTool = tools.FirstOrDefault(t => t.Name == "echo_tool")
                             ?? throw new InvalidOperationException();

// Echo ツールを実行
var response = await client.CallToolAsync(echoTool.Name, new Dictionary<string, object?>
{
    ["message"] = "Hello!",
});

// レスポンスを表示
foreach (var content in response.Content)
{
    Console.WriteLine($"{content.Type}, {content.Text}");
}
```
</details>