# クライアント開発者向け

すべての MCP サーバーと統合できる独自のクライアントの構築を開始します。

このチュートリアルでは、MCP サーバーに接続する LLM 搭載のチャットボット クライアントを構築する方法を学習します。最初のサーバー構築の基本をガイドする[サーバー クイックスタート](https://modelcontextprotocol.io/quickstart/server)を完​​了しておくと役立ちます。

- Python 

[このチュートリアルの完全なコードはここにあります。](https://github.com/modelcontextprotocol/quickstart-resources/tree/main/mcp-client-python)

## システム要件

開始する前に、システムが次の要件を満たしていることを確認してください。

- MacまたはWindowsコンピュータ
- 最新のPythonバージョンがインストールされている
- 最新バージョンの`uv`がインストールされています

## 環境の設定

まず、 `uv`を使用して新しい Python プロジェクトを作成します。

```bash
# Create project directory
uv init mcp-client
cd mcp-client

# Create virtual environment
uv venv

# Activate virtual environment
# On Windows:
.venv\Scripts\activate
# On Unix or MacOS:
source .venv/bin/activate

# Install required packages
uv add mcp anthropic python-dotenv

# Remove boilerplate files
rm hello.py

# Create our main file
touch client.py
```

## APIキーの設定

[Anthropic Console](https://console.anthropic.com/settings/keys)から Anthropic API キーが必要になります。

保存するための`.env`ファイルを作成します。

```bash
# Create .env file
touch .env
```

キーを`.env`ファイルに追加します。

```bash
ANTHROPIC_API_KEY=<your key here>
```

`.gitignore`に`.env`を追加します:

```bash
echo ".env" >> .gitignore
```

`ANTHROPIC_API_KEY`安全に保管してください。

## クライアントの作成

### 基本的なクライアント構造

まず、インポートを設定して基本的なクライアント クラスを作成しましょう。

```python
import asyncio
from typing import Optional
from contextlib import AsyncExitStack

from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

from anthropic import Anthropic
from dotenv import load_dotenv

load_dotenv()  # load environment variables from .env

class MCPClient:
    def __init__(self):
        # Initialize session and client objects
        self.session: Optional[ClientSession] = None
        self.exit_stack = AsyncExitStack()
        self.anthropic = Anthropic()
    # methods will go here
```

### サーバー接続管理

次に、MCP サーバーに接続するメソッドを実装します。

```python
async def connect_to_server(self, server_script_path: str):
    """Connect to an MCP server

    Args:
        server_script_path: Path to the server script (.py or .js)
    """
    is_python = server_script_path.endswith('.py')
    is_js = server_script_path.endswith('.js')
    if not (is_python or is_js):
        raise ValueError("Server script must be a .py or .js file")

    command = "python" if is_python else "node"
    server_params = StdioServerParameters(
        command=command,
        args=[server_script_path],
        env=None
    )

    stdio_transport = await self.exit_stack.enter_async_context(stdio_client(server_params))
    self.stdio, self.write = stdio_transport
    self.session = await self.exit_stack.enter_async_context(ClientSession(self.stdio, self.write))

    await self.session.initialize()

    # List available tools
    response = await self.session.list_tools()
    tools = response.tools
    print("\nConnected to server with tools:", [tool.name for tool in tools])
```

### クエリ処理ロジック

次に、クエリを処理し、ツール呼び出しを処理するためのコア機能を追加しましょう。

```python
async def process_query(self, query: str) -> str:
    """Process a query using Claude and available tools"""
    messages = [
        {
            "role": "user",
            "content": query
        }
    ]

    response = await self.session.list_tools()
    available_tools = [{
        "name": tool.name,
        "description": tool.description,
        "input_schema": tool.inputSchema
    } for tool in response.tools]

    # Initial Claude API call
    response = self.anthropic.messages.create(
        model="claude-3-5-sonnet-20241022",
        max_tokens=1000,
        messages=messages,
        tools=available_tools
    )

    # Process response and handle tool calls
    final_text = []

    assistant_message_content = []
    for content in response.content:
        if content.type == 'text':
            final_text.append(content.text)
            assistant_message_content.append(content)
        elif content.type == 'tool_use':
            tool_name = content.name
            tool_args = content.input

            # Execute tool call
            result = await self.session.call_tool(tool_name, tool_args)
            final_text.append(f"[Calling tool {tool_name} with args {tool_args}]")

            assistant_message_content.append(content)
            messages.append({
                "role": "assistant",
                "content": assistant_message_content
            })
            messages.append({
                "role": "user",
                "content": [
                    {
                        "type": "tool_result",
                        "tool_use_id": content.id,
                        "content": result.content
                    }
                ]
            })

            # Get next response from Claude
            response = self.anthropic.messages.create(
                model="claude-3-5-sonnet-20241022",
                max_tokens=1000,
                messages=messages,
                tools=available_tools
            )

            final_text.append(response.content[0].text)

    return "\n".join(final_text)
```

### インタラクティブチャットインターフェース

次に、チャット ループとクリーンアップ機能を追加します。

```python
async def chat_loop(self):
    """Run an interactive chat loop"""
    print("\nMCP Client Started!")
    print("Type your queries or 'quit' to exit.")

    while True:
        try:
            query = input("\nQuery: ").strip()

            if query.lower() == 'quit':
                break

            response = await self.process_query(query)
            print("\n" + response)

        except Exception as e:
            print(f"\nError: {str(e)}")

async def cleanup(self):
    """Clean up resources"""
    await self.exit_stack.aclose()
```

### メインエントリーポイント

最後に、メインの実行ロジックを追加します。

```python
async def main():
    if len(sys.argv) < 2:
        print("Usage: python client.py <path_to_server_script>")
        sys.exit(1)

    client = MCPClient()
    try:
        await client.connect_to_server(sys.argv[1])
        await client.chat_loop()
    finally:
        await client.cleanup()

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

完全な`client.py`ファイルは[ここにあります。](https://gist.github.com/zckly/f3f28ea731e096e53b39b47bf0a2d4b1)

## 主要コンポーネントの説明

### 1. クライアントの初期化

- `MCPClient`クラスはセッション管理とAPIクライアントを初期化します
- 適切なリソース管理のために`AsyncExitStack`を使用する
- Claude とのやり取りのために Anthropic クライアントを設定します

### 2. サーバー接続

- PythonとNode.jsサーバーの両方をサポート
- サーバースクリプトの種類を検証します
- 適切なコミュニケーションチャネルを設定する
- セッションを初期化し、利用可能なツールを一覧表示します

### 3. クエリ処理

- 会話の文脈を維持する
- クロードの応答とツール呼び出しを処理する
- クロードとツール間のメッセージフローを管理します
- 結果を統合して一貫した回答を導き出す

### 4. インタラクティブインターフェース

- シンプルなコマンドラインインターフェースを提供します
- ユーザー入力を処理し、応答を表示します
- 基本的なエラー処理を含む
- 優雅な退出を可能にする

### 5. リソース管理

- リソースの適切なクリーンアップ
- 接続の問題に対するエラー処理
- 正常なシャットダウン手順

##  一般的なカスタマイズポイント

1. **ツールの取り扱い**
   - 特定のツールタイプを処理するために`process_query()`を変更する
   - ツール呼び出しのカスタムエラー処理を追加する
   - ツール固有の応答フォーマットを実装する
2. **応答処理**
   - ツール結果のフォーマットをカスタマイズする
   - 応答フィルタリングまたは変換を追加する
   -  カスタムログを実装する
3. **ユーザーインターフェース**
   - GUIまたはWebインターフェースを追加する
   - 豊富なコンソール出力を実装する
   - コマンド履歴または自動補完を追加する

## クライアントの実行

任意の MCP サーバーでクライアントを実行するには:

```bash
uv run client.py path/to/server.py # python server
uv run client.py path/to/build/index.js # node server
```

サーバーのクイックスタートから天気のチュートリアルを続行する場合、コマンドは次のようになります: `python client.py .../weather/src/weather/server.py`

クライアントは次のことを行います。

1. 指定されたサーバーに接続する
2. 利用可能なツールの一覧
3. インタラクティブなチャット セッションを開始すると、次のことが可能になります。
   - クエリを入力
   -  ツールの実行を確認する
   - Claude からの応答を取得する

サーバーのクイックスタートから天気サーバーに接続した場合の表示例を次に示します。

![img](https://mintlify.s3.us-west-1.amazonaws.com/mcp/images/client-claude-cli-python.png)

## 仕組み

クエリを送信する場合:

1. クライアントはサーバーから利用可能なツールのリストを取得します
2. あなたの質問はツールの説明とともにクロードに送信されます
3. Claude はどのツール（ある場合）を使用するかを決定します
4. クライアントはサーバーを介して要求されたツール呼び出しを実行します。
5. 結果はクロードに返送される
6. Claude は自然言語の応答を提供します
7. 応答が表示されます

## ベストプラクティス

1. **エラー処理**
   - ツール呼び出しは常にtry-catchブロックで囲む
   - 意味のあるエラーメッセージを提供する
   - 接続の問題を適切に処理する
2. **リソース管理**
   - 適切なクリーンアップには`AsyncExitStack`を使用する
   - 完了したら接続を閉じる
   - サーバーの切断を処理する
3. **安全**
   - APIキーを`.env`に安全に保存する
   - サーバー応答を検証する
   - ツールの権限には注意する

## トラブルシューティング

### サーバーパスの問題

- サーバースクリプトへのパスが正しいことを再確認してください
- 相対パスが機能しない場合は絶対パスを使用してください
- Windowsユーザーの場合、パスにはスラッシュ（/）またはエスケープされたバックスラッシュ（\）を使用してください。
- サーバー ファイルの拡張子が正しいことを確認します (Python の場合は .py、Node.js の場合は .js)

正しいパスの使用例:

```bash
# Relative path
uv run client.py ./server/weather.py

# Absolute path
uv run client.py /Users/username/projects/mcp-server/weather.py

# Windows path (either format works)
uv run client.py C:/projects/mcp-server/weather.py
uv run client.py C:\\projects\\mcp-server\\weather.py
```

### 応答タイミング

- 最初の応答が返ってくるまでに最大30秒かかる場合があります
- これは正常であり、次の場合に発生します:
  - サーバーが初期化される
  - Claude はクエリを処理します
  - ツールが実行されています
- その後の対応は通常より速くなります
- この最初の待機期間中はプロセスを中断しないでください

### 一般的なエラーメッセージ

次のような表示が出た場合:

- `FileNotFoundError` : サーバーパスを確認してください
- `Connection refused` : サーバーが実行中であり、パスが正しいことを確認してください
- `Tool execution failed` : ツールに必要な環境変数が設定されていることを確認してください
- `Timeout error` : クライアント構成のタイムアウトを増やすことを検討してください
