Add MCP server context to otel tool_result logs (#12267)

Summary
- capture the origin for each configured MCP server and expose it via
the connection manager
- plumb MCP server name/origin into tool logging and emit
codex.tool_result events with those fields
- add unit coverage for origin parsing and extend OTEL tests to assert
empty MCP fields for non-MCP tools
- currently not logging full urls or url paths to prevent logging
potentially sensitive data

Testing
- Not run (not requested)
This commit is contained in:
colby-oai
2026-02-20 10:26:19 -05:00
committed by GitHub
parent ede561b5d1
commit 2036a5f5e0
5 changed files with 148 additions and 0 deletions

View File

@@ -32,6 +32,64 @@ use tracing_test::traced_test;
use tracing_subscriber::fmt::format::FmtSpan;
use tracing_test::internal::MockWriter;
fn extract_log_field(line: &str, key: &str) -> Option<String> {
let quoted_prefix = format!("{key}=\"");
if let Some(start) = line.find(&quoted_prefix) {
let value_start = start + quoted_prefix.len();
if let Some(end_rel) = line[value_start..].find('"') {
return Some(line[value_start..value_start + end_rel].to_string());
}
}
let bare_prefix = format!("{key}=");
for token in line.split_whitespace() {
let trimmed = token.trim_end_matches(',');
if let Some(value) = trimmed.strip_prefix(&bare_prefix) {
return Some(value.to_string());
}
}
None
}
fn assert_empty_mcp_tool_fields(line: &str) -> Result<(), String> {
let mcp_server = extract_log_field(line, "mcp_server")
.ok_or_else(|| "missing mcp_server field".to_string())?;
if !mcp_server.is_empty() {
return Err(format!("expected empty mcp_server, got {mcp_server}"));
}
let mcp_server_origin = extract_log_field(line, "mcp_server_origin")
.ok_or_else(|| "missing mcp_server_origin field".to_string())?;
if !mcp_server_origin.is_empty() {
return Err(format!(
"expected empty mcp_server_origin, got {mcp_server_origin}"
));
}
Ok(())
}
#[test]
fn extract_log_field_handles_empty_bare_values() {
let line = "event.name=\"codex.tool_result\" mcp_server= mcp_server_origin=";
assert_eq!(extract_log_field(line, "mcp_server"), Some(String::new()));
assert_eq!(
extract_log_field(line, "mcp_server_origin"),
Some(String::new())
);
}
#[test]
fn extract_log_field_does_not_confuse_similar_keys() {
let line = "event.name=\"codex.tool_result\" mcp_server_origin=stdio";
assert_eq!(extract_log_field(line, "mcp_server"), None);
assert_eq!(
extract_log_field(line, "mcp_server_origin"),
Some("stdio".to_string())
);
}
#[tokio::test]
#[traced_test]
async fn responses_api_emits_api_request_event() {
@@ -687,6 +745,7 @@ async fn handle_response_item_records_tool_result_for_custom_tool_call() {
if !line.contains("success=false") {
return Err("missing success field".to_string());
}
assert_empty_mcp_tool_fields(line)?;
Ok(())
});
@@ -756,6 +815,7 @@ async fn handle_response_item_records_tool_result_for_function_call() {
if !line.contains("success=false") {
return Err("missing success field".to_string());
}
assert_empty_mcp_tool_fields(line)?;
Ok(())
});
@@ -828,6 +888,7 @@ async fn handle_response_item_records_tool_result_for_local_shell_missing_ids()
if !line.contains("success=false") {
return Err("missing success field".to_string());
}
assert_empty_mcp_tool_fields(line)?;
Ok(())
});
@@ -899,6 +960,7 @@ async fn handle_response_item_records_tool_result_for_local_shell_call() {
if !line.contains("success=false") {
return Err("missing success field".to_string());
}
assert_empty_mcp_tool_fields(line)?;
Ok(())
});