コンテンツにスキップ

テスト

機械翻訳

このページは英語版ドキュメントから自動翻訳されたものであり、正式な版は英語版のページです。不自然な箇所があれば、翻訳についてで報告の方法を説明しています。

Python SDK には、インメモリトランスポートを備えた Client クラスが付属しています。サーバーオブジェクトを渡せば、そのサーバーに直接接続します。

サブプロセスも、ポートも要りません。トランスポートすら使いません。FastAPI の TestClient と同じ発想です。

基本的な使い方

ツールを 1 つだけ持つシンプルなサーバーがあるとします。

server.py
from mcp.server import MCPServer

mcp = MCPServer("Calculator")


@mcp.tool()
def add(a: int, b: int) -> int:
    """Add two numbers."""
    return a + b

以下のテストを実行するには、追加の(開発用)依存関係が 2 つ必要です。

uv add --dev pytest inline-snapshot
pip install pytest inline-snapshot

Info

このドキュメントは、pytest をすでに知っていることを前提にしています。

inline-snapshot は、以下のテストで結果オブジェクト全体を 1 行でアサートするために使っているライブラリです。テストの出力を、コードにあるとおりの snapshot(...) リテラルとして記録します。使いたくない場合は import を削除し、ほかのテストと同じように、関心のあるフィールド(result.content[0].text == "3")をアサートしてください。

テストは次のとおりです。

test_server.py
import pytest
from inline_snapshot import snapshot
from mcp import Client
from mcp.types import CallToolResult, TextContent

from server import mcp


@pytest.fixture
def anyio_backend():  # (1)!
    return "asyncio"


@pytest.fixture
async def client():  # (2)!
    async with Client(mcp, raise_exceptions=True) as c:
        yield c


@pytest.mark.anyio
async def test_call_add_tool(client: Client):
    result = await client.call_tool("add", {"a": 1, "b": 2})
    # Drop the server identity stamp in `_meta`; it is not what this test is about.
    result.meta = None
    assert result == snapshot(
        CallToolResult(
            content=[TextContent(type="text", text="3")],
            structured_content={"result": 3},
        )
    )
  1. trio を使っている場合は、代わりに "trio" を返してください。詳しくは anyio のドキュメント を参照してください。
  2. このフィクスチャは接続済みのクライアントを yield します。client を受け取るテストごとに、同じサーバーへの新しいインメモリ接続が用意されます。

これで準備完了です。あとはテストを拡張して、さらに多くのシナリオをカバーしていけます。

なぜ raise_exceptions=True なのか

問題が起こりうる場所は 2 種類あり、このフラグが関わるのはそのうちの一方だけです。

ツールの内部で発生した例外は、プロトコル上の失敗ではありません。is_error=True の付いた通常の結果になり、モデルがそのメッセージを読みます。raise_exceptions はこの挙動を変えません。指定してもしなくても、call_tool は同じ is_error=True の結果を返します。これについては専用のページがあります。エラーの処理 を参照してください。

ツール本体の外側で起きた失敗は事情が異なります。Client(mcp) で得られる接続では、クライアントの目に触れる前に、サーバーがその失敗を汎用の "Internal server error" にサニタイズします。予期しないクラッシュの詳細は、リモートの呼び出し側に決して漏らしてはいけないからです。しかしテストでは、これはまさに望まない挙動です。そして raise_exceptions=True が変えるのはまさにこの点で、テストからはサニタイズ後のメッセージではなく本来のメッセージが見えるようになります。

テストでは有効にしたままにしてください。本番コードでは意味を持ちません。

デフォルトはインプロセス

Note

Client(mcp) はインプロセスで接続し、デフォルトではプロトコルの世代を問いません。サーバーを調べ、適切なプロトコル経路を選びます。テストがレガシー固有のセマンティクス(サンプリングやエリシテーション(elicitation)のプッシュ、message_handler)を検証する場合は mode="legacy" に固定し、その場合は raise_exceptions=True を外してください。レガシー接続はそもそもサニタイズを行わず、このフラグを付けると失敗がテストの中ではなくサーバータスクの中で再送出されてしまうからです。

この 1 行こそが、このドキュメントが「掲載している例は動く」と約束できる理由でもあります。すべてのサンプルファイルは SDK 自身のテストスイートで実行されており、そのほぼすべてがまさにこのクライアントを経由しています。SDK が自分自身に対して使っているのと同じツールを使っているわけです。

これで、きちんと動く、テスト済みのサーバーが手元にあります。実際のアプリケーション(Claude Desktop や IDE)に組み込む方法は 実際のホストに接続する に、それ以外の提供方法はすべて サーバーの実行 にまとまっています。