コンテンツにスキップ

OpenTelemetry

機械翻訳

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

サーバーはすでにトレースされています。何も追加する必要はありません。

作成するサーバーはどれも、処理するメッセージごとに OpenTelemetry のスパンを発行します。自分で書いたわけでも、インポートしたわけでもありません。MCPServer(...) を呼び出した瞬間から、そこにあります。

server.py
from mcp.server import MCPServer

mcp = MCPServer("Bookshop")


@mcp.tool()
def search_books(query: str) -> str:
    """Search the catalog by title or author."""
    return f"Found 3 books matching {query!r}."

これで完全な、トレース済みのサーバーです。search_books を呼び出すと、そのためのスパンが作成されます。低レベルの Server でも同じです。トレースはどちらにも組み込まれています。

得られるもの

受信したメッセージはすべて、メソッドとその対象にちなんだ名前の SERVER スパンになります。search_books に対する tools/calltools/call search_books というスパンになり、単なる tools/list はそのまま tools/list です。

各スパンはいくつかの属性を持ちます。

  • mcp.method.namemcp.protocol.version。すべてのスパンに付きます。
  • jsonrpc.request.id。リクエストに付きます(通知には ID がありません)。
  • ハンドラーが例外を送出すると、スパンのステータスがエラーになります。is_error=True のツール結果でも同様です。

そしてツール呼び出しのトレースは非常によくある要望なので、tools/call のスパンは OpenTelemetry の GenAI セマンティック規約に従います。

  • gen_ai.operation.name"execute_tool" が設定されます。
  • gen_ai.tool.name。呼び出されるツールが設定されます。

同じ考え方で、prompts/get のスパンには gen_ai.prompt.name が付きます。一覧系のメソッドには名前を付ける対象がないため、gen_ai.* のキーは付きません。

Tip

トレース UI がツール呼び出しを他のエージェントと同じようにグループ化してくれるのは、これらの GenAI 属性のおかげです。このグループ化は追加のコードなしで手に入ります。

必要になるまでコストはかからない

「デフォルトで有効」が安心できるデフォルトである理由はここにあります。

SDK が依存しているのは、OpenTelemetry の軽量な半分である opentelemetry-api だけです。SDK もエクスポーターもインストールされていなければ、スパンの作成は何もしません。つまり、サーバーが今まさに発行しているスパンのコストはほぼゼロで、誰もそれを収集していません。

実際に「見たく」なった日には、残りの半分をインストールして、送り先を指定します。

uv add opentelemetry-sdk opentelemetry-exporter-otlp

通常の OpenTelemetry のやり方でエクスポーターを設定すれば、SDK が静かに作成してきたスパンがすべて見えるようになります。サーバーのコードは変わりません。1 行たりともです。

Info

Pydantic Logfire はそうしたバックエンドの 1 つで、設定まで代わりにやってくれます。pip install logfirelogfire.configure() とするだけで、MCP のスパンがライブビューに表示されます。OpenTelemetry の上に構築されているので、以下の内容もすべてそのまま当てはまります。

通信路をまたぐトレース

トレースが最も役に立つのは、リクエストをクライアントからサーバーまで、1 つにつながった図として追えるときです。

クライアントとサーバーの両方が SDK を使っていれば、そのつながりは自動的に得られます。クライアントが W3C トレースコンテキストをリクエストに注入し、サーバーがそれを読み取るので、サーバーのスパンは同じトレース内でクライアントのスパンの下にネストされます。これが SEP-414 で、特に何もしなくても使えます。

受信したメッセージにトレースコンテキストが含まれていない場合、たとえば SDK ではないクライアントからのリクエストでは、サーバーのスパンは孤立した新しいトレースを開始するのではなく、サーバー側ですでに現在のスパンになっているものを親にします。

無効にする

トレースはミドルウェアであり、サーバーのリストの先頭にあります。スパンをまったく発行しないサーバーが本当に必要なら、取り除いてください。

from mcp.server._otel import OpenTelemetryMiddleware

mcp._lowlevel_server.middleware[:] = [
    m for m in mcp._lowlevel_server.middleware if not isinstance(m, OpenTelemetryMiddleware)
]

Warning

このインポートには先頭にアンダースコアが付いていますが、これは意図的なものです。このクラスは、Server.middleware が暫定的であるのと同じく暫定的なものなので、インポートパスは変わるものと考えてください。これが必要になることはほとんどありません。エクスポーターをインストールしていなければスパンはコストがかからないので、通常は有効のままにしてエクスポーターをインストールしない、というのが答えです。

まとめ

  • すべての MCPServer とすべての低レベルの Server は、受信したメッセージごとに SERVER スパンを 1 つ、デフォルトで発行します。何も書く必要はありません。
  • スパンには mcp.method.namemcp.protocol.version が付きます。tools/callprompts/get にはさらに GenAI 属性も付くので、ツール呼び出しは他のエージェントと同じようにグループ化されます。
  • OpenTelemetry の SDK とエクスポーターをインストールするまでコストはかからず、インストールすればサーバーを変更することなく見えるようになります。
  • 両側が SDK を使っていれば、クライアントからサーバーへのトレースコンテキストは自動的に伝播します。

そもそもリクエストを実行してよいかどうかを決めるのが、認可です。