@@ -134,6 +134,19 @@ def setup_observability() -> None:
134134 )
135135 )
136136
137+ # Instrument httpx for automatic traceparent propagation on outgoing requests.
138+ # langchain-mcp-adapters uses httpx for streamable_http transport, so each MCP
139+ # tool call will automatically carry the current span's traceparent header
140+ try :
141+ from opentelemetry .instrumentation .httpx import HTTPXClientInstrumentor
142+
143+ HTTPXClientInstrumentor ().instrument ()
144+ logger .info ("httpx instrumented for automatic trace context propagation" )
145+ except ImportError :
146+ logger .warning (
147+ "opentelemetry-instrumentation-httpx not available - MCP tool calls will not propagate trace context"
148+ )
149+
137150 # Instrument OpenAI for GenAI semantic conventions
138151 try :
139152 from opentelemetry .instrumentation .openai import OpenAIInstrumentor
@@ -406,7 +419,7 @@ def create_tracing_middleware():
406419
407420 async def tracing_middleware (request : Request , call_next ):
408421 # Skip non-API paths (health checks, agent card, etc.)
409- if request .url .path in ["/health" , "/ready" , "/.well-known/agent-card.json" ]:
422+ if request .url .path in ["/health" , "/ready" , "/.well-known/agent-card.json" , "/.well-known/agent.json" ]:
410423 return await call_next (request )
411424
412425 tracer = get_tracer ()
@@ -431,10 +444,12 @@ async def tracing_middleware(request: Request, call_next):
431444 except Exception as e :
432445 logger .debug (f"Could not parse request body: { e } " )
433446
434- # Break parent chain to make this a true root span
435- # Without this, the span would inherit parent from W3C Trace Context headers
436- empty_ctx = context .Context ()
437- detach_token = context .attach (empty_ctx )
447+ # Extract incoming W3C Trace Context from request headers to connect
448+ # agent spans to MCP gateway spans. Callers without traceparent still
449+ # get root spans (extract returns empty context), while callers with traceparent
450+ # (like MCP gateway) get connected end-to-end traces
451+ incoming_ctx = extract (dict (request .headers ))
452+ detach_token = context .attach (incoming_ctx )
438453
439454 try :
440455 # Create root span with correct GenAI naming convention
0 commit comments