Preserve MCP trace context when tracing is disabled (#60506)

## Why

MCP requests need to retain inbound W3C trace context even when no OpenTelemetry tracing layer is installed.

## What changed

- Carry validated request trace context through app-server MCP tasks, connection startup, and resource listing tasks, while preferring active span context.
- Forward fallback `traceparent` and `tracestate` headers for MCP HTTP requests, including discovery and initialization.
- Use HTTP trace headers as a fallback for exec-server RPC calls when active span context is unavailable.

## Testing

Add an app-server integration test that disables trace exporting and verifies that `mcpServer/resource/read` forwards the inbound `traceparent` and `tracestate` to the MCP server.

GitOrigin-RevId: c886a55ce0a3992f1b2e04614efec80a20cb4b49
This commit is contained in:
Joe Savona
2026-11-10 03:01:14 +00:00
committed by copyberry
parent aa39ded153
commit 3cd64c8d7c
13 changed files with 272 additions and 46 deletions
+2 -1
View File
@@ -1097,7 +1097,8 @@ impl McpConnectionSet {
// Dormant servers must not hold the initial startup summary open.
deferred_workers.push(startup);
} else {
eager_workers.push(startup.boxed());
let trace = codex_rmcp_client::McpRequestTrace::current();
eager_workers.push(trace.scope(startup).boxed());
}
}
let manager = Self {
@@ -12,6 +12,7 @@ use std::time::Duration;
use anyhow::Context;
use anyhow::Result;
use anyhow::anyhow;
use codex_rmcp_client::McpRequestTrace;
use rmcp::model::ListResourceTemplatesResult;
use rmcp::model::ListResourcesResult;
use rmcp::model::PaginatedRequestParams;
@@ -57,7 +58,7 @@ impl McpConnectionSet {
continue;
};
let timeout = view.tool_timeout;
join_set.spawn(async move {
let task = async move {
let resources = collect_paginated("resources/list", timeout, |params| {
let client = client.clone();
async move {
@@ -67,7 +68,8 @@ impl McpConnectionSet {
})
.await;
(server_name, resources)
});
};
join_set.spawn(McpRequestTrace::current().scope(task));
}
let mut resources = HashMap::new();
@@ -101,7 +103,7 @@ impl McpConnectionSet {
continue;
};
let timeout = view.tool_timeout;
join_set.spawn(async move {
let task = async move {
let templates = collect_paginated("resources/templates/list", timeout, |params| {
let client = client.clone();
async move {
@@ -111,7 +113,8 @@ impl McpConnectionSet {
})
.await;
(server_name, templates)
});
};
join_set.spawn(McpRequestTrace::current().scope(task));
}
let mut templates = HashMap::new();
+1 -3
View File
@@ -103,7 +103,6 @@ use rmcp::model::Tool as RmcpTool;
use tokio::sync::watch;
use tokio::time::Instant as TokioInstant;
use tokio_util::sync::CancellationToken;
use tracing::Instrument;
use tracing::instrument;
use tracing::warn;
@@ -447,8 +446,7 @@ impl ManagedClientStartup {
outcome
};
originator
.scope(Box::pin(startup))
.in_current_span()
.scope(codex_rmcp_client::McpRequestTrace::current().scope(Box::pin(startup)))
.boxed()
.shared()
}