コンテンツにスキップ

Context

機械翻訳

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

ツールの引数はモデルから渡されます。それ以外のすべて(処理中のリクエスト、ツールが属するサーバー、クライアントに話しかける手段)は、1 つのオブジェクトから得られます。それが Context です。

自分で組み立てる必要も、設定する必要もありません。要求するだけです。

要求する

任意のツールに、Context で注釈したパラメーターを追加してください。

server.py
from mcp.server import MCPServer
from mcp.server.mcpserver import Context

mcp = MCPServer("Bookshop")


@mcp.tool()
def search_books(query: str, ctx: Context) -> str:
    """Search the catalog by title or author."""
    return f"[request {ctx.request_id}] Found 3 books matching {query!r}."
  • SDK はリクエストごとに新しい Context を組み立てて渡します。
  • パラメーターの名前は関係ありませんctxcontextc のどれでもよく、SDK は注釈を見て見つけます。
  • リソースやプロンプトでも、同じように宣言できます。
  • ctx.request_id は、関数がいま処理しているリクエストの id です。

Info

FastAPI を使ったことがあれば、この仕組みには見覚えがあるはずです。フレームワーク自身の型(あちらでは Request、こちらでは Context)でパラメーターを宣言すると、フレームワークがそれを供給します。登録するものも設定するものもありません。型注釈がこの仕組みのすべてです。

モデルからは見えない

ここはしっかり身につけておきたい部分です。tools/listsearch_books について報告する入力スキーマは次のとおりです。

{
  "type": "object",
  "properties": {
    "query": {"title": "Query", "type": "string"}
  },
  "required": ["query"],
  "title": "search_booksArguments"
}

プロパティは 1 つです。ctx は引数ではありません。スキーマには決して現れず、モデルに知らされることもなく、どのクライアントも値を入れられません。これは作成者と SDK の間の取り決めであり、通信上には現れません。

試してみる

MCP Inspector でサーバーを実行してください。

uv run mcp dev server.py

search_books のフォームには query フィールドが 1 つだけあります。dune を指定して呼び出してください。

[request 3] Found 3 books matching 'dune'.

この数字は、たまたまそのときのリクエストの番号です。もう一度ツールを呼び出すと変わります。リクエストごとに専用の Context が作られるからです。

何が得られるか

注入されるオブジェクトは小さなものです。request_id のほかに次のものがあります。

  • await ctx.read_resource(uri):ツールの中からサーバー自身のリソースを 1 つ読みます。次のセクションで扱います。
  • await ctx.report_progress(progress, total, message):長い呼び出しの最中に、進捗を呼び出し側へ逐次送ります。詳しくは 進捗 を参照してください。
  • await ctx.elicit(message, schema)await ctx.elicit_url(...):ツールを一時停止してユーザーに質問します。これが エリシテーション(elicitation) です。
  • ctx.session:このクライアントとの会話のサーバー側です。クライアントに送る通知はここにあり、最後のセクションで使います。
  • ctx.headers:トランスポートが運んだリクエストヘッダー、stdio では None です。カスタムヘッダーは (ctx.headers or {}).get("x-...") で読めます。ヘッダーはクライアントが与える入力です。ロケールや機能フラグには使えますが、身元の確認には決して使わないでください。
  • ctx.request_context:リクエストごとの生のレコードです。実際に手を伸ばすフィールドは lifespan_context、つまり起動コードが yield したオブジェクトです(ライフスパン を参照)。

ロギングは意図的にこの一覧に入れていません。サーバーは、ほかの Python プログラムと同じく Python の logging モジュールでログを記録します。その理由は短いページ ロギング にまとめてあります。

Tip

注入が行われるのは登録した関数だけです。ツールが呼び出すヘルパーに専用の Context は渡されないので、ctx を通常の引数として渡してください。どこか別の場所から取り出せる暗黙の「現在のコンテキスト」はありません。

自分のリソースを読む

サーバーのリソースはクライアントだけのものではありません。ツールからも読めます。

server.py
from mcp.server import MCPServer
from mcp.server.mcpserver import Context

mcp = MCPServer("Bookshop")


@mcp.resource("catalog://genres")
def genres() -> str:
    """The genres the catalog is organised into."""
    return "fiction, non-fiction, poetry"


@mcp.tool()
async def describe_catalog(ctx: Context) -> str:
    """Describe how the catalog is organised."""
    [contents] = await ctx.read_resource("catalog://genres")
    return f"The catalog is organised into: {contents.content}"

ctx.read_resourceresources/read を処理するのと同じレジストリを通して URI を解決するので、ツールはクライアントが受け取るのと同じものを得ます。コンテンツブロックごとに 1 つの ReadResourceContents を持つイテラブルです。この URI の場合は 1 つです。

contents.content    # 'fiction, non-fiction, poetry'
contents.mime_type  # 'text/plain'
  • contentgenres() が返したものそのままです。情報源は 1 つです。クライアントはリソースを閲覧し、ツールはそれを消費し、誰も文字列をコピーしません。
  • describe_catalog の唯一のパラメーターは Context なので、その入力スキーマにはプロパティが 1 つもありません。モデルは {} で呼び出します。

一覧が変わったことをクライアントに伝える

サーバーが提供するものは、インポート時に固定されるわけではありません。実行時にツールを登録し、それをクライアントに伝えます。

server.py
from mcp.server import MCPServer
from mcp.server.mcpserver import Context

mcp = MCPServer("Bookshop")


def recommend_book(genre: str) -> str:
    """Recommend a book in the given genre."""
    return f"In {genre}, try 'Dune'."


@mcp.tool()
async def enable_recommendations(ctx: Context) -> str:
    """Switch on the recommendation tool."""
    mcp.add_tool(recommend_book)
    await ctx.session.send_tool_list_changed()
    return "Recommendations are now available."
  • mcp.add_tool(recommend_book) は普通の関数をツールとして登録します。名前、説明、スキーマは @mcp.tool() を使った場合とまったく同じように導出されます。
  • await ctx.session.send_tool_list_changed()notifications/tools/list_changed を送ります。これを受け取ったクライアントは tools/list を再度呼び出し、recommend_book を目にします。

同種のメソッドには send_resource_list_changed()send_prompt_list_changed()、そして特定の 1 つのリソースの変更を知らせる send_resource_updated(uri) があります。

2026-07-28 の接続では、クライアントは自分が開いた subscriptions/listen ストリーム上でしか変更通知を受け取らないため、上記の send_* メソッドはそれらのストリームに届きません。Context の公開メソッドは、購読中のすべてのストリームに一度に配信します。await ctx.notify_tools_changed()await ctx.notify_prompts_changed()await ctx.notify_resources_changed()await ctx.notify_resource_updated(uri) です。レプリカをまたいだスケールアウトも含め、詳しくは サブスクリプション を参照してください。

Check

誰かが enable_recommendations を実行するまで、約束しているツールは存在しません。それでも呼び出すと、結果はモデルが読めるエラーです。

Unknown tool: recommend_book

enable_recommendations を実行すると、まったく同じ呼び出しが成功します。ツールの一覧は本当に動的です。tools/list は「いま」登録されているものをそのまま反映します。

まとめ

  • パラメーターに Context を注釈すると(ツールでも、リソースでも、プロンプトでも)、SDK がそれを注入します。名前は自由です。
  • モデルからは見えません。入力スキーマに含まれるのは、常に本物の引数だけです。
  • ctx.request_id はリクエストを識別し、ctx.request_context.lifespan_context は起動時に yield したものです。
  • await ctx.read_resource(uri) を使うと、ツールからサーバー自身のリソースを読めます。
  • ctx.session はクライアントへ戻るチャネルです。send_tool_list_changed() とその同種のメソッドは、変更した一覧を取得し直すようクライアントに伝えます。
  • 進捗の報告とエリシテーションも Context が出発点です。それぞれに専用のページがあります。

モデルが目にすることのない、自分の関数で埋めるパラメーターが 依存関係 です。