LLMクライアントの接続: ツール呼び出しメカニズムとModel Context Protocol (MCP)

## 目次
1. [LLMとは何か：単なるトークン生成器](#llmとは何か単なるトークン生成器)
2. [ツール呼び出し（Tool Calling）のメカニズム](#ツール呼び出しtool-callingのメカニズム)
3. [Model Context Protocol (MCP) の概要](#model-context-protocol-mcp-の概要)
4. [MCPの構造とコンポーネント](#mcpの構造とコンポーネント)
5. [MCPとLangChainの比較](#mcpとlangchainの比較)
6. [LangChain MCP Adaptersによる統合](#langchain-mcp-adaptersによる統合)
7. [実装例：MCPサーバーとクライアント](#実装例mcpサーバーとクライアント)
8. [まとめ](#まとめ)

## LLMとは何か：単なるトークン生成器

LLM（Large Language Model）は基本的に単純なトークン生成器です。これは一見すると自明ではないかもしれません。今日のエージェント的な振る舞いを見ると、LLMが特別な力を持っているように思えるかもしれませんが、実際にはそうではありません。🤖

**重要ポイント**:
- LLMはテキストを出力するだけです（マルチモーダルLLMの場合は画像やその他の形式も可能）
- Webの検索、リサーチの実行、Pythonの関数の呼び出しなどの機能は、LLMを実行しているアプリケーションに統合された外部ツールです
- たとえばChatGPTを使用する場合、デスクトップアプリケーションやWebアプリケーションにLLMがラップされています
- これらのアプリケーションはソフトウェアエンジニアが開発したものです

LLMは統計的な存在であり、一つずつトークンを予測しています。この点を理解することは、ツール呼び出しやMCPの仕組みを理解する上で非常に重要です。

## ツール呼び出し（Tool Calling）のメカニズム

LLMが外部ツールを「使用」する方法を理解するために、その仕組みを説明します：

1. LLMは特別なシステムプロンプトで動作します
2. 特定の質問（例：「今の天気は？」）に対して、LLMは直接回答を生成せず、ツール呼び出しのテキストを生成します
3. 例えば、`get_weather(city="Tokyo")`のような形式のテキストを生成します
4. アプリケーション（ChatGPTなど）はこの出力を解析し、実際のツール機能を呼び出します
5. ツールの実行結果は再びLLMに送られ、最終的な回答が生成されます

```plantuml
@startuml
actor User
participant "AI Application" as App
participant LLM
participant "External Tool" as Tool

User -> App: "今の天気は？"
App -> LLM: ユーザークエリ + ツール情報
LLM -> App: ツール呼び出し: get_weather(location="Tokyo")
App -> Tool: ツール実行
Tool -> App: ツール実行結果
App -> LLM: 元のクエリ + ツール実行結果
LLM -> App: 最終回答
App -> User: "東京の天気は晴れで気温は25°Cです"
@enduml
```

:::note info
**注意点**: このツール呼び出しメカニズムは100%正確に動作するわけではありません。LLMは統計的にトークンを予測しているため、常に正しいツールを正しい引数で呼び出すとは限りません。ただし、多くの場合は十分に機能します。
:::

## Model Context Protocol (MCP) の概要

Model Context Protocol (MCP)は、LLMクライアントとサーバーを接続するための標準化されたプロトコルです。Anthropicによって開発されたこのプロトコルは、特にツール、リソース、プロンプトの共有に焦点を当てています。🔄

**MCPの主な利点**：
- 開発者はツールを作成し、MCPサーバーで公開できる
- これらのツールは、function callingをサポートするすべてのアプリケーション（ChatGPT、Cloud Desktop、Cursorなど）で使用可能
- 標準化されたプロトコルにより、ツールとリソースの共有が容易になる
- アプリケーションからツール実行を分離できる

MCPは単なるツールだけでなく、以下のものも公開できます：
- リソース（ドキュメント、PDFファイル、画像など）
- API呼び出し
- プロンプト

## MCPの構造とコンポーネント

MCPの構造は、クライアント-サーバーモデルに基づいています：

```plantuml
@startuml
actor User
box "Application" #LightBlue
  participant "App/Host" as App
  participant "MCP Client" as Client
end box
participant "MCP Server" as Server
participant LLM

== 初期化フェーズ ==
App -> Client: 接続を初期化
Client -> Server: 接続リクエスト
Server -> Client: 接続確認
Server -> Client: 利用可能なツール/リソース情報

== ユーザークエリ処理 ==
User -> App: クエリ送信
App -> LLM: ユーザークエリ + 利用可能なツール情報
LLM -> App: ツール呼び出し応答
App -> Client: ツール呼び出しリクエスト
Client -> Server: ツール呼び出し実行
Server -> Client: ツール実行結果
Client -> App: ツール実行結果
App -> LLM: 元のクエリ + ツール実行結果
LLM -> App: 最終回答
App -> User: 最終回答
@enduml
```

**主要コンポーネント**:
1. **ユーザー**: アプリケーションにクエリを送信します
2. **アプリケーション/ホスト**: Cursor、Wind Surf、Cloud Desktop、またはカスタムエージェントが該当
3. **MCPクライアント**: アプリケーション内に存在し、MCPサーバーと通信
4. **MCPサーバー**: ツールやリソースを公開し、ツール実行を処理
5. **LLM**: ユーザークエリを処理し、ツール呼び出しを決定

**処理フロー**:
1. **初期化**: アプリケーション起動時、MCPクライアントはMCPサーバーへの接続を確立し、利用可能なツール情報を取得
2. **クエリ処理**: ユーザーがクエリを送信すると、アプリケーションはLLMにクエリと利用可能なツール情報を送信
3. **ツール実行**: LLMがツール呼び出しを返すと、MCPクライアントはそれをMCPサーバーに転送し、サーバーがツールを実行
4. **結果処理**: ツール実行結果はLLMに送られ、最終回答がユーザーに返される

:::note info
**重要な点**: ツールの実行はMCPサーバー側で行われます。これにより、アプリケーションからツール実行ロジックを分離でき、デバッグやスケーリングが容易になります。
:::

## MCPとLangChainの比較

MCPとLangChainは、LLMをツールと接続するためのアプローチを提供していますが、いくつかの重要な違いがあります：

**共通点**:
- 両方とも「ツール」という概念があり、外部から呼び出される関数を表します
- ツールには引数、返り値、説明が含まれます
- LangChainにはToolkit、MCPにはMCPサーバーという、ツールのコレクションの概念があります

**相違点**:
1. **スコープ**:
   - MCPはツールだけでなく、リソースやプロンプトも公開できます
   - LangChainは主にツールとチェーンに焦点を当てています

2. **バインディング**:
   - LangChainはツールを直接LLMにバインドします
   - MCPはツールをAIアプリケーション（Cursor、Wind Surf、Cloud Desktopなど）に公開します

3. **実行モデル**:
   - LangChainではツールはアプリケーション内で実行されます
   - MCPではツールはMCPサーバー内で実行されます

4. **標準化**:
   - MCPは標準化されたプロトコルを提供します
   - LangChainはPythonライブラリとしての統合を提供します

```plantuml
@startuml
rectangle "LangChain" {
  [LLM] as LC_LLM
  [Tools] as LC_Tools
  [Agent] as LC_Agent

  LC_Agent -up-> LC_LLM
  LC_Agent -down-> LC_Tools
}

rectangle "MCP" {
  [AI Application] as MCP_App
  [MCP Client] as MCP_Client
  [MCP Server] as MCP_Server
  [LLM] as MCP_LLM

  MCP_App -down-> MCP_Client
  MCP_App -up-> MCP_LLM
  MCP_Client -right-> MCP_Server
}

note bottom of LC_Tools
  ツールはアプリケーション内で実行
end note

note bottom of MCP_Server
  ツールはサーバー内で実行
end note
@enduml
```

## LangChain MCP Adaptersによる統合

LangChain MCP Adaptersは、MCPツールをLangChainやLangGraphと統合するためのオープンソースライブラリです。これにより、両方のエコシステムの利点を活用できます。🔗

**主な機能**:
- MCPツールをLangChainとLangGraphのエージェントと互換性のあるツールに変換
- 複数のMCPサーバーに接続できるMCPクライアントの提供
- LangChainとMCPの間のシームレスな統合

この統合により、次のようなメリットが得られます：
1. MCPサーバーによる**デカップリング**：ツール実行ロジックとエージェントのオーケストレーションを分離
2. **動的ツール**：アプリケーションを再デプロイすることなくツールを更新可能
3. **標準化**：標準化されたプロトコルを使用したツールとリソースの共有
4. **スケーラビリティ**：ツール実行をサーバーに分離することで、より良いスケーリングが可能

```plantuml
@startuml
package "LangChain" {
  [LangGraph Agent] as Agent
  [LangChain Tools] as LCTools
}

package "LangChain MCP Adapters" {
  [Tool Converter] as Converter
  [MCP Client] as Client
}

package "MCP Ecosystem" {
  [MCP Server 1] as Server1
  [MCP Server 2] as Server2
}

Agent -down-> LCTools
LCTools <-left- Converter
Converter <-down- Client
Client -down-> Server1
Client -down-> Server2

note right of Converter
  MCPツールをLangChainツールに変換
end note

note right of Client
  複数のMCPサーバーに接続
end note
@enduml
```

## 実装例：MCPサーバーとクライアント

以下に、MCPサーバーとLangChain MCPクライアントの基本的な実装例を示します。この例では、数学演算と天気情報を提供する2つの異なるMCPサーバーを作成し、それらをLangGraph Reactエージェントと統合します。

### 1. 環境のセットアップ

まず、必要なパッケージをインストールします：

```bash
uv add langchain-mcp-adapters langgraph langchain-openai
uv add python-dotenv
```

### 2. 簡単なMCPサーバーの作成

#### 数学演算サーバー（stdio通信）

```python
# math_server.py
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("Math")

@mcp.tool()
def add(a: int, b: int) -> int:
    """二つの数値を足します"""
    return a + b

@mcp.tool()
def multiply(a: int, b: int) -> int:
    """二つの数値を掛け合わせます"""
    return a * b

if __name__ == "__main__":
    mcp.run(transport="stdio")
```

#### 天気サーバー（SSE通信）

```python
# weather_server.py
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("Weather")

@mcp.tool()
async def get_weather(location: str) -> str:
    """指定された場所の天気を取得します。"""
    return "とても暑いです！"

if __name__ == "__main__":
    mcp.run(transport="sse")
```

### 3. MCPクライアントの実装

```python
import asyncio
import os
from dotenv import load_dotenv
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from langchain_mcp_adapters.tools import load_mcp_tools
from langgraph.prebuilt import create_react_agent
from langchain_openai import ChatOpenAI
from langchain.schema.messages import HumanMessage

# 環境変数の読み込み
load_dotenv()

async def main():
    # LLMの初期化
    model = ChatOpenAI()
    
    # MCPサーバーパラメータの設定
    stdio_server_params = StdioServerParameters(
        command="python",
        args=["/absolute/path/to/math_server.py"],
    )
    
    # MCPクライアントの初期化
    async with stdio_client(stdio_server_params) as (read, write):
        async with ClientSession(read, write) as session:
            # セッションの初期化
            await session.initialize()
            print("セッションが初期化されました")
            
            # MCPツールをLangChainツールに変換
            tools = await load_mcp_tools(session)
            
            # LangGraph Reactエージェントの作成
            agent = create_react_agent(model, tools)
            
            # エージェントへのクエリ
            agent_response = await agent.ainvoke({
                "messages": [HumanMessage(content="54足す2かける3はいくつですか？")]
            })
            
            print(agent_response)

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

### 4. 実行結果と処理フロー

このコードを実行すると、以下のようなプロセスが発生します：

1. MCPクライアントがMCPサーバーに接続
2. MCPサーバーから利用可能なツール情報を取得
3. LangChain MCPアダプターがMCPツールをLangChainツールに変換
4. LangGraph Reactエージェントがユーザークエリを処理
5. エージェントがMCPサーバーのツールを呼び出し（実行はサーバー側）
6. ツール実行結果をもとに最終回答を生成

この例では、「54足す2かける3はいくつですか？」というクエリに対して：
1. LLMが最初に「2かける3」のツール呼び出しを生成
2. MCPサーバーが計算を実行して6を返す
3. 次にLLMが「54足す6」のツール呼び出しを生成
4. MCPサーバーが計算を実行して60を返す
5. 最終的にLLMが「54足す2かける3は60です」という回答を生成

### 5. 複数のMCPサーバーへの接続

LangChain MCP Adaptersは、複数のMCPサーバーに同時に接続することもサポートしています：

```python
from langchain_mcp_adapters.client import MultiServerMCPClient

async with MultiServerMCPClient(
    {
        "math": {
            "command": "python",
            "args": ["/path/to/math_server.py"],
            "transport": "stdio",
        },
        "weather": {
            "url": "http://localhost:8000/sse",
            "transport": "sse",
        }
    }
) as client:
    agent = create_react_agent(model, client.get_tools())
    # 以降、エージェントを使用
```

## まとめ

Model Context Protocol (MCP)は、LLMとツール・リソースを接続するための強力な標準化プロトコルを提供します。LangChain MCP Adaptersを使用することで、MCPの柔軟性とLangChainのエコシステムを組み合わせることができます。🚀

**主な利点**:
1. **デカップリング**: ツール実行ロジックとエージェントオーケストレーションの分離
2. **標準化**: ツールとリソースの共有のための共通インターフェース
3. **柔軟性**: 動的なツール更新とリソース共有
4. **スケーラビリティ**: サーバー側での実行による分散化

LLMは基本的にはトークン生成器ですが、MCPのようなプロトコルを活用することで、より高度で柔軟なAIアプリケーションを構築することができます。

:::note info
**次のステップ**: MCPはまだ発展途上のプロトコルですが、ChatGPTを含む多くのプラットフォームがMCPのサポートを発表しています。今後はより多くのツールやリソースがMCPを通じて共有されることが期待されます。
:::

## 確認クイズ

以下の質問に答えて、理解度をチェックしてみましょう：

1. LLMが基本的に行うことは何ですか？
   - [ ] Webの検索
   - [ ] 関数の実行
   - [x] トークンの生成
   - [ ] データベースへのアクセス

2. MCPとLangChainの主な違いは何ですか？（複数選択可）
   - [x] MCPではツールはサーバー側で実行される
   - [x] MCPはツールだけでなくリソースやプロンプトも公開できる
   - [x] MCPはツールをAIアプリケーションに公開する
   - [ ] LangChainはツール呼び出しをサポートしていない

3. LangChain MCP Adaptersの主な目的は何ですか？
   - [ ] LangChainを完全に置き換える
   - [x] MCPツールをLangChainと互換性のあるツールに変換する
   - [ ] LLMの性能を向上させる
   - [ ] データベース接続を提供する

## 関連リソース
- [Anthropic MCP ドキュメント](https://docs.anthropic.com/claude/docs/model-context-protocol)
- [LangChain MCP Adapters リポジトリ](https://github.com/langchain-ai/langchain-mcp-adapters)
- [LangGraph ドキュメント](https://python.langchain.com/docs/langgraph)