MCP(Model Context Protocol)は、AIエージェントが外部ツールやデータベース、APIとやり取りするための共通規格として注目されています。今回のDZone記事は、そのMCPをOpenTelemetryでどう監視するか、しかも「1回の呼び出しで終わらない」「途中で人間の確認を挟んで再開する」ようなややこしいケースに焦点を当てています。
ここ、地味にかなり重要です。AIエージェントの実運用って、きれいな1往復のAPI呼び出しでは終わらないんですよね。ツールを呼ぶ、追加情報が必要になる、ユーザーに確認を求める、また別のHTTPリクエストで再開する……みたいな流れになりがちです。人間から見ると「同じ仕事の続き」なのに、監視基盤から見ると「別々のリクエスト」に見えてしまう。これ、運用担当としてはかなり嫌なやつです。
_meta に入った traceparent を取り出して、OpenTelemetry の span を親子関係でつなぐのがポイント普通のWebアプリは、だいたい流れが素直です。ユーザーが画面を開く → サーバーが処理する → DBを読む → 結果を返す。途中で失敗しても、トレースIDを見ればどこで落ちたか追いやすい。
でもAIエージェントはそうはいきません。記事では、1つのユーザー入力から、複数のツール呼び出し、外部API、DB参照、場合によってはユーザーへの確認まで発生すると説明しています。しかも、その一連の流れが同じプロセスの中で一直線に進むとは限らない。途中で止まり、あとで別のHTTPリクエストとして再開することもあるわけです。
これは監視の世界ではかなり厄介です。OpenTelemetryのような仕組みは「1つの流れ」を追うのが得意ですが、流れが途中で切断されると、別々のリクエストとしてしか見えません。人間から見ればひとつの仕事なのに、機械にはばらばらに見える。このズレが、デバッグを一気にしんどくします。
個人的には、ここがAIエージェント運用の本当の難所だと思います。モデルの賢さより、むしろ**“あとから説明できるか”**のほうが、実務では効いてくることが多いです。
記事の中心にあるのが InputRequiredResult です。これは、たとえば高リスクな操作を行う前に人間の確認を挟むための仕組みです。銀行送金やクラウド資源の削除みたいな処理を、AIが勝手に進めるのは怖い。そこでMCPサーバーが処理を止め、「確認してください」と返し、ユーザーの入力を待ってから再開する。
この流れ自体はとても自然です。むしろ本番運用では必須でしょう。問題は、その再開が別のHTTPリクエストになることです。しかもスケールされたクラウド環境では、再開先が別のコンテナになることもある。
ここで従来の「ローカルな文脈」は切れます。つまり、前回の続きだと誰も知らない。観測基盤にとっては完全に別件です。だからこそ、トレース情報を明示的に引き継ぐ必要がある、というのがこの記事の主張です。
そこで登場するのが W3C Trace Context です。これは、分散システムの中で「この処理は誰から来たどの処理の続きか」を伝えるための標準規格です。
記事では、MCP仕様がこのW3C Trace Contextのサポートを求めていると説明しています。具体的には、traceparent と tracestate という情報を使います。
traceparent は、ざっくり言うと「この処理の親は誰か」を示す名札です。トレースのバージョン、Trace ID、親のSpan ID、フラグなどが入っています。tracestate は、追加のベンダー情報やルーティング情報を運ぶための補助的なフィールドです。
これが何を変えるかというと、MCPサーバー側で「これは新規リクエストではなく、あの途中再開の続きだ」と判断できるようになることです。OpenTelemetryはその文脈を受け取って、親子関係のあるspanとして記録できます。結果として、ばらばらだった処理が一本のトレースにまとまる。これはかなり気持ちがいいはずです。
記事は実装例として Quarkus を使っています。QuarkusはJava向けのクラウドネイティブなフレームワークで、OpenTelemetryとの統合がやりやすいのが売りです。ここで quarkus-opentelemetry 拡張を入れるだけで、面倒なボイラープレートをかなり減らせます。
設定もシンプルです。application.properties に quarkus.otel.enabled=true を入れ、OTLPの送信先を指定し、サービス名を設定する。Jaeger や Grafana Tempo のような観測基盤へ span を送れるようにするわけです。
正直、このあたりは「ちゃんと標準に乗ると楽だな」と思わせてくれる部分です。監視の話って、つい難しく見えますが、標準化された規約とフレームワークの組み合わせがあると、急に現実味が出てきます。
記事中のJavaコードでは、ContainerRequestFilter を使ってリクエスト本文を読み、params._meta の中に traceparent があるか確認しています。そして、OpenTelemetryの propagator を使ってその文脈を抽出し、新しい span をその親として開始しています。
つまりやっていることは、すごく乱暴に言えばこうです。
「このMCPリクエスト、前回の続きの番号ついてるよね? じゃあその番号を見て、ちゃんと親子関係をつなごう」
たったそれだけのことなんですが、実運用ではこれが効きます。ログだけでは追えない、断続的で、しかも人間の介入を挟むフローを、トレースとして可視化できるからです。
しかも、記事は「トレース情報が壊れていたり無かったりしても、落とさずに穏やかに処理する」設計にも触れています。こういう地味な配慮は大事です。観測のための仕組みが本体処理を壊したら本末転倒なので。
記事の後半では、Jaeger や Grafana Tempo で見ると、AIエージェントの処理がウォーターフォール図のように見えると説明しています。Root Span があり、その下にツール呼び出しの child span がぶら下がる。途中で確認待ちが入っても、うまくつながっていれば同じセッションとして見える。
これ、開発者体験としてかなり大きいです。AIエージェントって、動いている最中はブラックボックス感が強いんですよね。でもトレースでつながると、「どこで止まったか」「どのツールが長いか」「ユーザー確認でどれだけ待ったか」が見えてくる。運用の不安が、少し現実的な問題に変わります。
私はこの手の可視化は、性能改善より先に信頼の土台になると思っています。何が起きているか説明できないシステムは、結局、本番で怖い。AIが絡むならなおさらです。
この記事の面白さは、OpenTelemetryそのものの解説よりも、AIエージェント時代に観測がどう変わるかをかなり具体的に示している点にあります。MCPが単なるツール連携の規格ではなく、「途中で止まり、再開し、人間が介入する」ことを前提にした世界へ進んでいる。その現実を、トレースの話でうまく表現しているんです。
一方で、実装の難しさは残ります。traceparent をちゃんと伝える、途中で切れても再開時に同じ文脈を復元する、コンテナが変わっても追えるようにする。ここは口で言うよりずっと面倒です。でも、だからこそ標準化の価値がある。MCPとW3C Trace Contextの組み合わせは、AIエージェントを「動くもの」から「運用できるもの」に寄せるための、かなり実務的な一歩だと思います。
参考: Monitoring Multi-Round-Trip MCP Calls With OpenTelemetry