Tracing the Agentic Loop: Monitoring Multi-Round-Trip MCP Calls With OpenTelemetry
The latest MCP spec mandates W3C tracing. Use Quarkus and OpenTelemetry to easily visualize disjointed, multi-round-trip AI agent workflows in production.
Join the DZone community and get the full member experience.
Join For FreeThe upcoming release of the July 28 Model Context Protocol (MCP) specification completely revolutionized how developers build AI tools. By shifting from stateful, long-lived connections to a streamlined, stateless HTTP engine, we finally gained the ability to run AI tools as scalable, modern microservices.
However, statelessness introduces a massive operational headache: observability.
When you move from a monolithic setup to a decentralized, stateless agentic network, debugging becomes incredibly difficult. A single user prompt might trigger an AI agent to execute multiple tools, coordinate with external APIs, check a database, and ask the user for clarification before finally returning an answer. Without a clear path to trace these calls, debugging a failing agent feels like searching for a needle in a digital haystack.
Fortunately, the July 28 MCP spec natively addresses this by mandating support for W3C Trace Context propagation. By pairing this specification with the robust, cloud-native capabilities of Quarkus and OpenTelemetry, you can easily map, monitor, and master the chaos of complex, multi-round-trip agentic workflows.
The Chaos of the Multi-Round-Trip Agentic Loop
Standard microservice architectures rely on a predictable, linear request-response lifecycle. A user hits a gateway; the gateway calls a backend service; the backend service queries a database; and the result bubbles back up to the client. If a failure occurs, a single Trace ID ties the entire chain together.
AI agents do not behave this way. They operate in highly unpredictable loops.
The most challenging addition to the July 28 spec is the formalization of the InputRequiredResult. This is a mechanism designed for human-in-the-loop interactions. If an AI agent decides it needs to execute a high-risk tool (like initiating a bank transfer or deleting a cloud resource), the MCP server can pause the execution and return InputRequiredResult to the host model. The host model then stops, prompts the human user for validation, waits for a response, and eventually sends a completely new HTTP request to resume the execution.
[Agent Host] ──(1. Run Tool)──> [Quarkus MCP]
[Agent Host] <──(2. Input Required)── [Quarkus MCP] (Execution Halts)
│
▼ (User inputs confirmation manually)
│
[Agent Host] ──(3. Resume Tool with Input)──> [Quarkus MCP] (New Container Instance)
Because the execution stops and resumes across entirely different HTTP calls — and potentially entirely different container instances in a scaled cloud environment — standard context propagation fails. Without a standardized distributed tracing strategy, your observability backend will treat this single, cohesive workflow as a series of disconnected, anonymous requests.
Enter W3C Trace Context: The Secret to Agent Observability
To solve the fragmented tracking problem, the new MCP specification officially leverages W3C Trace Context. When an AI host or orchestrator initiates a tool call, it is required to inject tracing metadata directly into the JSON-RPC request body inside the _meta object.
The W3C format standardizes how tracing information is passed across boundaries using two key fields:
traceparent: A single, hyphen-delimited string containing the trace version, a unique 16-byte Trace ID, a 8-byte parent Span ID, and trace flags.tracestate: An optional header designed to pass system-specific configuration or routing data without breaking compliance.
By forcing the host LLM to pass these values, the MCP server can extract the trace context, bind it to its own local OpenTelemetry context, and continue the span.
Integrating OpenTelemetry Into Your Quarkus MCP Server
Quarkus makes implementing this complex tracing logic exceptionally elegant. Instead of writing verbose tracing boilerplate, you can leverage the quarkus-opentelemetry extension, which provides full integration with the OpenTelemetry SDK out of the box.
First, include the OpenTelemetry dependency in your pom.xml file:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-opentelemetry</artifactId>
</dependency>
Next, configure your tracing export endpoints in application.properties. Quarkus will automatically handle the asynchronous exporting of spans to your collectors (such as Jaeger, Zipkin, or Grafana Tempo):
quarkus.otel.enabled=true
quarkus.otel.exporter.otlp.endpoint=http://jaeger-collector:4317
quarkus.otel.service.name=mcp-customer-tool-service
Writing the Tracing Interceptor in Java
To automatically extract the W3C context from the incoming JSON payload and link it to our Quarkus OpenTelemetry tracer, we can build a simple interceptor. This ensures that every tool execution is correctly nested under the client’s original execution span:
package com.example.mcp.telemetry;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.propagation.TextMapGetter;
import jakarta.inject.Inject;
import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerRequestFilter;
import jakarta.ws.rs.ext.Provider;
import io.vertx.core.json.JsonObject;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@Provider
public class McpTracingFilter implements ContainerRequestFilter {
@Inject
Tracer tracer;
@Inject
io.opentelemetry.api.OpenTelemetry openTelemetry;
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
byte[] bodyBytes = requestContext.getEntityStream().readAllBytes();
requestContext.setEntityStream(new ByteArrayInputStream(bodyBytes));
try {
JsonObject bodyJson = new JsonObject(new String(bodyBytes));
JsonObject meta = bodyJson.getJsonObject("params").getJsonObject("_meta");
if (meta != null && meta.containsKey("traceparent")) {
// Extract the W3C Trace Context using the OpenTelemetry Propagator
Context extractedContext = openTelemetry.getPropagator().getTextMapPropagator()
.extract(Context.current(), meta, new TextMapGetter<JsonObject>() {
@Override
public Iterable<String> keys(JsonObject carrier) {
return carrier.getMap().keySet();
}
@Override
public String get(JsonObject carrier, String key) {
return carrier.getString(key);
}
});
// Start a new span parented by the extracted context
Span span = tracer.spanBuilder("mcp_tool_execution")
.setParent(extractedContext)
.startSpan();
// Store the span in the request context to close it later
requestContext.setProperty("mcp-span", span);
span.makeCurrent();
}
} catch (Exception e) {
// Fallback gracefully if tracing context is missing or corrupted
}
}
}
Visualizing the Agentic Waterfall in Production
Once you have configured the interceptor, the benefits are immediate. When an agent initiates a multi-turn session, your OpenTelemetry backend will construct a unified, hierarchical trace.
If you view the trace in Jaeger or Grafana Tempo, you will see a clear, structured waterfall diagram:
- Root Span (
agent_session_start): Initiated by the AI orchestrator when the user submits their primary prompt. - Child Span (
execute_customer_lookup): Spanned when the agent calls your Quarkus MCP service to retrieve database records. - Database Span (
SELECT FROM customers): Automatically tracked by Quarkus Hibernate Reactive, showing the exact SQL performance. - Pause Span (
InputRequiredResult): Records the latency of the system waiting for the user to click "Confirm." - Resume Span (
finalize_transaction): Shows the second stateless container picking up the trace using the same Trace ID to complete the job.
With this level of visibility, you can pinpoint exactly which step of the loop failed, identify which specific tool execution caused the agent to slow down, and trace any downstream database or API issues directly back to the original LLM decision.
Summary
Decentralized, stateless architectures solved our scaling problems, but they threatened our ability to monitor applications. By incorporating W3C Trace Context into the July 28 specification, the creators of MCP have provided the blueprints for enterprise-grade AI observability.
By leveraging the built-in, reactive telemetry capabilities of Quarkus and OpenTelemetry, Java developers can shine a light directly into the black box of autonomous agent execution. This ensures your systems remain secure, fast, and completely auditable.
Opinions expressed by DZone contributors are their own.
Comments