Otel について
mcp-otelは、MCPサーバーの開発者やクライアント向けの小規模なライブラリです。リクエストメタデータのW3CトレースコンテキストをOpenTelemetryスパンにつなぎ、初期化やエクスポーターの選択はアプリケーションに任せます。
主な機能
- 01_meta経由のW3Cコンテキスト伝播
- 02ツールハンドラーをサーバースパンでラップ
- 03トレースコンテキストの抽出・注入
- 04スパンの状態と例外の記録
- 05カスタムスパン属性の設定
活用シーン
- 01ホスト、ツール、下流のスパンを連結
- 02選択したMCPツールハンドラーの計装
- 03MCPクライアントからのトレースコンテキスト転送
MCP 対応
MCPの_metaからtraceparent、tracestate、baggageを読み書きします。サーバーではツール呼び出しスパンを作成し、クライアントでは現在のトレースコンテキストをリクエストに注入できます。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
ツール
ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。
詳細
StudioMeyer MCP Stack の一部 — マヨルカで開発 🌴 · ご利用の際は ⭐ をお願いします
mcp-otel
Model Context Protocol 用の W3C Trace Context ブリッジです。MCP の _meta フィールドを通じてトレースコンテキストを伝播し、OpenTelemetry のスパンを出力します。これにより、Host → MCP サーバー → ツール → 下流 HTTP と流れるリクエストが、Jaeger、Tempo、Honeycomb、Datadog で 1 つのつながったトレースとして表示されます。
これが作られた理由
MCP の 2026-07-28 のリリース候補版で、MCP の分散トレーシングが確定しました。
- SEP-414 では、プレフィックスのない
_metaキーtraceparent、tracestate、baggageを、W3C Trace Context と W3C Baggage 用に予約しています。これらのキーはすべてのリクエストのparams._metaに含まれ、MCP のトランスポートは変更せずにそのまま引き継ぐ必要があります。 - SEP-2577 では、MCP のロギング機能が非推奨となり、今後のオブザーバビリティの手段として OpenTelemetry が示されました。
仕様に関する記事:https://blog.modelcontextprotocol.io/posts/2026-07-28-release-candidate/
つまり、プロトコルによって、トレースコンテキストの格納場所と使用するテレメトリーシステムは定められました。しかし、実際の連携を行う小さなライブラリはありませんでした。呼び出し元のコンテキストを _meta から読み取り、ツール呼び出しに適切な親を持つ OpenTelemetry スパンを開始し、下流の呼び出しをそのスパンにつなげるライブラリです。それが mcp-otel です。
意図的に薄いレイヤーとして作られています。OpenTelemetry の設定、エクスポーターの同梱、トレーサーの隠蔽は行いません。サンプリング、リソース、エクスポーターは引き続き自由に制御できます。mcp-otel が行うのは、_meta とスパンの橋渡しだけです。
インストール
npm install mcp-otel @opentelemetry/api
# @modelcontextprotocol/sdk is an optional peer — only needed if you use the
# tool-handler wrapper against the SDK (the low-level helpers don't import it).
ピア依存関係:
| パッケージ | 範囲 | 必須? |
|---|---|---|
@opentelemetry/api | ^1.9.0 | はい |
@modelcontextprotocol/sdk | >=1.10.0 <2 | 任意(instrumentToolHandler の extra の形式でのみ使用) |
Node 20 以上。ESM と CommonJS の両方を提供しています。
クイックスタート — サーバー側
起動時に一度だけ OpenTelemetry を設定します(通常の OTel ブートストラップです。mcp-otel は設定を行いません)。その後、各ツールハンドラーをラップします。
import { instrumentToolHandler } from "mcp-otel";
import { z } from "zod";
server.registerTool(
"weather.lookup",
{ description: "Current weather", inputSchema: { city: z.string() } },
// The only change: wrap the handler. Same (args, extra) => result signature.
instrumentToolHandler("weather.lookup", async ({ city }) => {
// trace.getActiveSpan() here is the MCP server span.
// Any span you start, or any downstream fetch that injects the active
// context, nests under it automatically.
return { content: [{ type: "text", text: `Weather for ${city}` }] };
}),
);
instrumentToolHandler は次の処理を行います。
- 呼び出し元の
_metaからtraceparent/tracestate/baggageを読み取ります (公開されている SDK 1.x の形式であるextra._metaと、extra.mcpReq._metaを確認します)。 - 呼び出し元のスパンを親とする、
tools/call <toolName>という名前のSERVERスパンを開始します。 - OTel コンテキストを通じて、そのスパンをアクティブにした状態でハンドラーを実行します。
- スパンを
OK/ERRORステータスで終了し、スローされた例外を記録します。
スパンに設定される属性:mcp.method("tools/call")、mcp.tool.name、mcp.request.id、および利用可能な場合は mcp.session.id。gen_ai.system などの GenAI 規約に沿った属性を設定するには、attributes を渡します。
明示的なプリミティブを使う場合は、_meta オブジェクトを直接扱える runInToolSpan を使用します。
import { runInToolSpan } from "mcp-otel";
const result = await runInToolSpan(
meta, // request.params._meta
{ toolName: "weather.lookup", requestId },
async (span) => {
span.setAttribute("weather.city", city);
return doWork(city);
},
);
クイックスタート — クライアント側
MCP クライアントを作成する場合は、サーバーがトレースを引き継げるよう、アクティブなトレースコンテキストをリクエストの _meta に注入します。
import { injectTraceContext } from "mcp-otel";
const result = await client.callTool({
name: "weather.lookup",
arguments: { city: "Palma" },
_meta: injectTraceContext({}), // writes traceparent/tracestate/baggage from the active context
});
injectTraceContext(meta, context?) は _meta オブジェクトを変更して返します。デフォルトでは、現在アクティブな OpenTelemetry コンテキストを使用します。既存の _meta キー(progressToken など)は保持されます。
Jaeger に送信する
mcp-otel は標準の OpenTelemetry スパンを出力するため、どのエクスポーターでも使用できます。最小限の Jaeger セットアップ:
docker run -d --name jaeger \
-p 16686:16686 -p 4318:4318 \
jaegertracing/all-in-one
// otel-setup.ts — import this once, before anything else.
import { NodeSDK } from "@opentelemetry/sdk-node";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { resourceFromAttributes } from "@opentelemetry/resources";
import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
const sdk = new NodeSDK({
resource: resourceFromAttributes({ [ATTR_SERVICE_NAME]: "my-mcp-server" }),
traceExporter: new OTLPTraceExporter({ url: "http://localhost:4318/v1/traces" }),
});
sdk.start();
NodeSDK は AsyncLocalStorageContextManager をインストールします。これにより、mcp-otel はアクティブなスパンを下流の呼び出しに伝播できます。http://localhost:16686 を開くと、host → tools/call <name> → downstream がひとつのトレースとして表示されます。
コンテキスト伝播に関する注意: 下流スパンの自動ネストには、コンテキストマネージャーがインストールされている必要があります。これは
NodeSDK/NodeTracerProvider.register()が行う処理です。コンテキストマネージャーがないプロセス(例:素のBasicTracerProvider)でも、スパンは作成され、呼び出し元を親として正しく設定されますが、ハンドラー内のcontext.active()にはスパンが含まれません。これは標準の OpenTelemetry の契約であり、このライブラリ特有の挙動ではありません。
実行可能な例
インフラ不要の例(メモリー内エクスポーターを使用し、スパンツリーを出力します)は、examples/connected-trace.mjs にあります。
npm run build
node examples/connected-trace.mjs
次のように出力されます。
- host.chat-turn [INTERNAL]
- tools/call weather.lookup [SERVER]
- GET /weather [CLIENT]
All 3 spans share one traceId: yes
実際の SDK の配線は examples/mcp-server.ts にあります。
API リファレンス
計装
instrumentToolHandler(toolName, handler, options?)→ ラップされたハンドラー。(args, extra) => resultとしてそのまま使用できるラッパーです。オプション:method(デフォルトは"tools/call")、tracer、attributes、および抽出関数のオーバーライドであるgetMeta/getRequestId/getSessionId。runInToolSpan(meta, options, body)→Promise<T>。低レベルのプリミティブです。metaのトレースコンテキストを親とするSERVERスパン内でbody(span)を実行します。オプション:toolName、method、requestId、sessionId、attributes、tracer。
伝播ヘルパー
extractTraceContext(meta, base?)→Context。_meta(traceparent+tracestate+baggage)から OTel の親コンテキストを構築します。決定論的な動作のため、baseのデフォルトはROOT_CONTEXTです。injectTraceContext(meta, context?)→Meta。context(デフォルトはアクティブなコンテキスト)のトレースキーを_metaに書き込みます。metaを変更し、返します。parseTraceparent(s)→SpanContext | null。W3C 準拠のパースを行います。バージョン00は長さを厳密に検証します(フィールドが正確に 4 つ)。より高いバージョンは前方互換性を考慮してパースされます(仕様に従い、最初の 4 フィールドを読み取り、後続のフィールドは無視します)。これにより、Trace Context の進化に合わせて引き続き動作します。不正な値、バージョンff、またはすべてゼロの値(無効な trace-id / parent-id)の場合はnullを返します。結果のisRemoteはtrueです。formatTraceparent(spanContext)→string | null。バージョン00のtraceparentにシリアライズします。スパンコンテキストが無効な場合はnullを返します。spanContextToContext(spanContext, base?)→Context。すでに保持しているスパンコンテキストを、インジェクション用のコンテキストにラップします。traceContextFields()→string[]。このライブラリが読み書きする_metaのキーです(_metaを転送する前にこれらを除去する場合に便利です)。
定数
TRACEPARENT_META_KEY、TRACESTATE_META_KEY、BAGGAGE_META_KEY、TRACE_META_KEYS、および属性キー ATTR_MCP_METHOD、ATTR_MCP_TOOL_NAME、ATTR_MCP_REQUEST_ID、ATTR_MCP_SESSION_ID。
_meta であり、HTTP ヘッダーではない理由
MCP は Streamable HTTP で実行できるのに、通常のウェブサービスのように HTTP リクエストヘッダーから traceparent を読み取らないのはなぜか、と疑問に思うかもしれません。
- MCP はトランスポートに依存しません。 同じサーバーロジックを stdio、Streamable HTTP などで実行できます。stdio にはヘッダーがありません。トレースコンテキストを
_metaに格納すれば、すべてのトランスポートで同じように伝播できます。これこそが、仕様で HTTP に頼るのではなく、_meta内に これらのキーを確保した理由です。 - 1 つの HTTP 接続で複数の論理リクエストを処理します。 Streamable HTTP では、1 つの長時間接続で複数の JSON-RPC メッセージを多重化します。トランスポートレベルのヘッダーが表すのは 接続 であり、各ツール呼び出しではありません。トレースコンテキストは個々のリクエストに属します。MCP ではそのリクエストは JSON-RPC メッセージであり、メッセージごとのメタデータは
_metaです。 - ホストが HTTP クライアントであるとは限りません。 ルートスパンを所有するもの(LLM ホスト/エージェント)と、ソケットを開くものは、別のプロセスである場合があります。
_metaは論理リクエストとともにそれらのホップを越えて伝播しますが、ホップごとの HTTP ヘッダーではそうなりません。
つまり _meta は、リクエストごとに設定でき、かつトランスポートに依存しない唯一の場所です。そのため SEP-414 は _meta を標準化し、このライブラリもそこで読み書きします。
対応しないこと
- OpenTelemetry の初期化、エクスポーターの選択、サンプラーの設定は行いません。それらはアプリの役割です。また、これらを対象外にすることで、SDK 側のバージョンに縛られません。
- トランスポートを自動計装したり、SDK にグローバルなパッチを適用したりはしません。ツールハンドラーごとに明示的に有効にするか、ヘルパーを自分で呼び出してください。
- メトリクスやログは生成しません。SEP-2577 の OTel 方針に従い、スパンのみを扱います。
StudioMeyer MCP ツールキットの一部
MCP サーバーの構築と運用に役立つ、用途を絞った本番品質のツール群です。自由に組み合わせてお使いください。
- mcp-armor — ランタイム防御サイドカー:ツール呼び出しをスキャンし、署名済みマニフェストを検証して、既知の悪意ある CVE をブロックします
- mcp-gauntlet — デプロイ前に使う
mcp-fuzz(スキーマ対応ファザー)とmcp-storm(負荷テスター) - mcp-otel (このツール) — W3C Trace Context → OpenTelemetry ブリッジ
- mcp-cache-kit — リークを防ぐ SEP-2549 キャッシュ(
ttlMs+cacheScope) - skilldoctor — エージェントのスキルファイル用リンター + セキュリティスキャナー
ライセンス
MIT © StudioMeyer
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。