このリポジトリは、Model Context Protocol (MCP) を利用したカスタムサーバーの実装例です。エコーツールとYahoo!ニュース取得機能を提供します。
MCPは、AIモデルが外部環境とやり取りするためのプロトコルです。このプロジェクトでは、以下の機能を実装しています:
- エコーメッセージ送信と時刻表示
- Yahoo!ニュースの自動取得
- Python 3.8以上
- pipまたはpipenv
- リポジトリをクローンする:
git clone [リポジトリURL]
cd [リポジトリ名]
- 依存パッケージのインストール:
pip install mcp-python-sdk playwright
- Playwrightブラウザのインストール:
playwright install chromium
- MCPクライアント設定ファイルの生成:
python generate_config.py
.
├── server.py # メインMCPサーバー
├── echo_tools.py # エコー関連機能
├── news_tools.py # ニュース取得機能
├── generate_config.py # MCPクライアント設定生成スクリプト
├── .gitignore # Git無視リスト
├── pyproject.toml # Pythonプロジェクト設定
├── README.md # このファイル
└── mcp_client_config.json # (生成されるファイル、Git管理外)
MCPサーバーを起動するには:
python server.py
- Cursorアプリを起動する
- コマンドパレットを開く(Cmd+Shift+P)
MCP: Settings
を選択mcp_client_config.json
の内容をコピー&ペーストするか、設定ファイルへのパスを指定します。 (注意:generate_config.py
が生成したファイルの内容を使用してください)
メッセージをエコーバックしたり、現在時刻を表示したりします。
例:
- 「こんにちは」→「こんにちは」を返す
- 「時刻」または「time」→現在時刻を返す
Yahoo! Japanから最新のニュース記事を取得します。
MCPサーバーはPythonのデコレータを活用して、関数をMCPの各種機能として登録します。
デコレータはPythonの強力な機能で、既存の関数やクラスを変更せずに拡張できます。シンプルに言えば、デコレータは関数を引数として受け取り、処理を追加して新しい関数を返す「関数のラッパー」です。
def my_decorator(func):
def wrapper(*args, **kwargs):
print("関数が呼ばれる前の処理")
result = func(*args, **kwargs)
print("関数が呼ばれた後の処理")
return result
return wrapper
@my_decorator
def say_hello():
print("Hello!")
# この呼び出しは以下と同等:
# say_hello = my_decorator(say_hello)
say_hello() # 装飾された関数を実行
MCPサーバーでは、主に3種類のデコレータを使用します:
@mcp.tool()
- AIモデルが呼び出せるツールを定義@mcp.resource()
- 動的なリソースへのアクセスを提供@mcp.prompt()
- プロンプトテンプレートを定義
これらのデコレータは、関数をMCPサーバーに登録し、クライアント(AIモデル)からアクセス可能にします。
@mcp.tool()
デコレータは、関数をAIモデルが利用可能なツールとして登録します。
@mcp.tool()
def echo_tool(message: str) -> str:
"""Echo a message or return current time if 'time' is included."""
# 関数の実装
return result
- デコレータが関数を変換し、MCP内部レジストリに登録
- AIモデルがツールのリストを要求するとき、登録されたツールが返される
- AIモデルがツールを呼び出すとき、MCPサーバーは登録された関数を実行
- 関数の戻り値がAIモデルに返される
MCPは関数のシグネチャ(型ヒントと引数名)を解析して、AIモデルに適切な引数情報を提供します:
@mcp.tool()
def search_tool(query: str, max_results: int = 10) -> list:
"""検索を実行するツール"""
# queryとmax_resultsを使用した処理
return results
この例では、AIモデルに「query(必須)」と「max_results(オプション、デフォルト値10)」の2つの引数が必要であることが伝わります。
@mcp.resource()
デコレータは、動的なリソースへのアクセスを提供します。
@mcp.resource("user://{user_id}")
def get_user(user_id: str) -> dict:
"""特定のユーザー情報を取得"""
# user_idを使用してユーザー情報を取得
return user_info
resourceデコレータは、URLパターンを使用して動的なリソースにアクセスできます。{param}
形式のプレースホルダーがURLパスにマッピングされます。
@mcp.prompt()
デコレータは、再利用可能なプロンプトテンプレートを定義します。
@mcp.prompt()
def translate_prompt(text: str, target_language: str) -> str:
"""翻訳プロンプトを生成"""
return f"以下のテキストを{target_language}に翻訳してください:\n\n{text}"
デコレータには追加のパラメータを渡すことができます:
@mcp.tool(name="custom_search", description="カスタム検索を実行")
def search_function(query: str) -> list:
# 実装
return results
非同期関数も同様にツールとして登録できます:
@mcp.tool()
async def fetch_data(url: str) -> dict:
"""外部APIからデータを取得"""
# 非同期リクエスト処理
return data
複雑な引数構造はPydanticモデルで定義できます:
from pydantic import BaseModel, Field
class SearchParameters(BaseModel):
query: str = Field(description="検索クエリ")
filters: dict = Field(default={}, description="検索フィルター")
limit: int = Field(default=10, ge=1, le=100, description="結果の最大数")
@mcp.tool()
def advanced_search(params: SearchParameters) -> list:
"""高度な検索機能を提供"""
# paramsを使用した処理
return results
MCPデコレータの内部では以下のことが行われています:
- 関数のメタデータ(引数、戻り値の型、docstring)を解析
- 関数をMCPの内部レジストリに登録
- JSON Schema形式で引数情報を生成
- クライアントからのリクエストをハンドリングするラッパー関数を生成
これにより、AIモデルは利用可能なツールを発見し、それらの使い方を理解し、適切に呼び出すことができます。
LLM(大規模言語モデル)は自然言語の理解に優れているため、厳密な型システムよりもdocstringの説明を読み取って解釈する能力が高いという特徴があります。例えば:
@mcp.tool()
def echo_tool_wrapper(message: str) -> str:
"""Echo a message or return current time if 'time' or '時刻' is included."""
return echo_tool(message)
上記の例では、関数の型シグネチャ(message: str -> str
)からは「特定のキーワードで特殊な挙動をする」という情報は得られませんが、docstringには「'time'や'時刻'が含まれると現在時刻を返す」という情報が含まれています。
LLMはこのdocstringを読み取り、以下の情報を総合的に理解します:
- 関数名:
echo_tool_wrapper
- 引数の型:
message: str
- 戻り値の型:
str
- 説明: "Echo a message or return current time if 'time' or '時刻' is included."
単純な条件(特定の文字列を含むかどうか)については、docstringの説明だけで十分にLLMに伝わります。より複雑なパターンや多数のオプションがある場合は、詳細な型定義(Pydanticモデルなど)が役立つことがありますが、シンプルなケースではdocstringで十分と言えるでしょう。
このようにMCPでは、LLMの自然言語理解能力を活かして、シンプルなインターフェースでも高度な機能を提供できます。
- ツールロジックを実装したファイルを作成
- 必要に応じてPydanticモデルでツール引数を定義
- 機能を実装する関数を作成
server.py
でラッパー関数を定義し、@mcp.tool()
デコレータを付加
from pydantic import BaseModel, Field
class ToolArguments(BaseModel):
arg1: str = Field(title="引数1")
arg2: int = Field(title="引数2")
新しいツールを追加するには:
- 新しいファイル(例:
my_tools.py
)を作成:
async def my_custom_tool(param1: str, param2: int) -> str:
# ツールのロジックを実装
return f"結果: {param1}, {param2}"
server.py
に追加:
from my_tools import my_custom_tool
@mcp.tool()
async def my_custom_tool_wrapper(param1: str, param2: int) -> str:
"""ツールの説明をここに書く"""
return await my_custom_tool(param1, param2)
Webスクレイピングなど時間がかかる処理は非同期(async/await
)で実装し、MCPサーバーのパフォーマンスを維持しましょう。
@mcp.tool()
async def async_tool() -> str:
# 非同期処理
return result
ツール実行中の例外を適切に処理し、クラッシュを防ぎましょう:
try:
# 処理
except Exception as e:
return f"エラーが発生しました: {str(e)}"
ツールデコレータ内のdocstringは、AIがツールを理解するために重要です。詳細かつ明確な説明を書きましょう。
PlaywrightでWebスクレイピングを行う場合:
- セレクタは変更される可能性があるため、複数の検索方法を実装する
- headless=Falseでデバッグする
- ページ読み込み待機を適切に設定する
すべての関数に適切な型ヒントを付け、MCPサーバーが正しく引数の型を認識できるようにしましょう。
- Pythonパスが正しく設定されているか確認
- 必要なライブラリがすべてインストールされているか確認
playwright install
を実行して、ブラウザが正しくインストールされているか確認してください。
「This event loop is already running」エラーが発生する場合、非同期APIを正しく使用しているか確認してください。
別オリジンからAPIを呼び出す場合、必要に応じてCORS設定を追加してください。
- MCPを使用することで、AIモデルに外部ツールを簡単に統合できる
- コードを適切にモジュール化することで保守性が向上する
- Playwrightを使用したWebスクレイピングは強力だが、HTMLの変更に対応する柔軟性が必要
- 非同期処理を適切に実装することで、パフォーマンスが向上する
- 型ヒントを活用することで、AIモデルがツールの使い方を理解しやすくなる