mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
Report MCP error codes with server attribution (#29969)
## Why MCP error-code telemetry special-cased Codex Apps: its reported error codes were retained, while codes from every other MCP server were replaced with `unknown`. Error reporting should behave consistently for every MCP server. The server name already identifies where an error came from, so telemetry does not need a separate Codex Apps classification. This follows up on [#28976](https://github.com/openai/codex/pull/28976), which introduced MCP error-code telemetry. ## What changed - Add the MCP server name to call, duration, and error metrics. - Retain bounded, sanitized tool error codes from every MCP server. - Remove `McpErrorCodeSource` and the Codex Apps ownership lookup from telemetry collection. - Use the same metric-tagging path for blocked, rejected, and executed MCP calls. ## Test plan - Verify the complete metric tag set includes the sanitized MCP server name. - Verify error codes from ordinary MCP servers are retained, bounded, and sanitized. - Preserve coverage for request failures, tool-result failures, nested auth failures, and span attributes.
This commit is contained in:
1 parent
a74771340d
commit
cef5444a80
4 files changed
+101
-106
No files matched your search
@@ -92,9 +92,7 @@ use url::Url;
|
||||
|
||||
mod telemetry;
|
||||
|
||||
use telemetry::MCP_CALL_COUNT_METRIC;
|
||||
use telemetry::McpCallMetricOutcome;
|
||||
use telemetry::McpErrorCodeSource;
|
||||
use telemetry::emit_mcp_call_metrics;
|
||||
use telemetry::mcp_call_metric_outcome;
|
||||
use telemetry::record_mcp_call_outcome_span_telemetry;
|
||||
@@ -182,6 +180,13 @@ pub(crate) async fn handle_mcp_tool_call(
|
||||
.await
|
||||
};
|
||||
|
||||
let connector_id = metadata
|
||||
.as_ref()
|
||||
.and_then(|metadata| metadata.connector_id.clone());
|
||||
let connector_name = metadata
|
||||
.as_ref()
|
||||
.and_then(|metadata| metadata.connector_name.clone());
|
||||
|
||||
if server == CODEX_APPS_MCP_SERVER_NAME && !app_tool_policy.enabled {
|
||||
let result = notify_mcp_tool_call_skip(
|
||||
sess.as_ref(),
|
||||
@@ -194,10 +199,15 @@ pub(crate) async fn handle_mcp_tool_call(
|
||||
)
|
||||
.await;
|
||||
let status = if result.is_ok() { "ok" } else { "error" };
|
||||
turn_context.session_telemetry.counter(
|
||||
MCP_CALL_COUNT_METRIC,
|
||||
/*inc*/ 1,
|
||||
&[("status", status)],
|
||||
let outcome = McpCallMetricOutcome::from_status(status);
|
||||
emit_mcp_call_metrics(
|
||||
turn_context.as_ref(),
|
||||
&outcome,
|
||||
&server,
|
||||
&tool_name,
|
||||
connector_id.as_deref(),
|
||||
connector_name.as_deref(),
|
||||
/*duration*/ None,
|
||||
);
|
||||
return HandledMcpToolCall {
|
||||
result: CallToolResult::from_result(result),
|
||||
@@ -205,13 +215,6 @@ pub(crate) async fn handle_mcp_tool_call(
|
||||
.unwrap_or_else(|| JsonValue::Object(serde_json::Map::new())),
|
||||
};
|
||||
}
|
||||
let connector_id = metadata
|
||||
.as_ref()
|
||||
.and_then(|metadata| metadata.connector_id.clone());
|
||||
let connector_name = metadata
|
||||
.as_ref()
|
||||
.and_then(|metadata| metadata.connector_name.clone());
|
||||
|
||||
notify_mcp_tool_call_started(
|
||||
sess.as_ref(),
|
||||
turn_context.as_ref(),
|
||||
@@ -279,6 +282,7 @@ pub(crate) async fn handle_mcp_tool_call(
|
||||
emit_mcp_call_metrics(
|
||||
turn_context.as_ref(),
|
||||
&outcome,
|
||||
&server,
|
||||
&tool_name,
|
||||
connector_id.as_deref(),
|
||||
connector_name.as_deref(),
|
||||
@@ -348,17 +352,11 @@ async fn handle_approved_mcp_tool_call(
|
||||
let arguments_value = invocation.arguments.clone();
|
||||
let connector_id = metadata.and_then(|metadata| metadata.connector_id.as_deref());
|
||||
let connector_name = metadata.and_then(|metadata| metadata.connector_name.as_deref());
|
||||
let (server_origin, error_code_source) = {
|
||||
let server_origin = {
|
||||
let mcp_connection_manager = sess.services.mcp_connection_manager.load_full();
|
||||
let server_origin = mcp_connection_manager
|
||||
mcp_connection_manager
|
||||
.server_origin(&server)
|
||||
.map(str::to_string);
|
||||
let error_code_source = if mcp_connection_manager.is_host_owned_codex_apps_server(&server) {
|
||||
McpErrorCodeSource::HostedPluginService
|
||||
} else {
|
||||
McpErrorCodeSource::Untrusted
|
||||
};
|
||||
(server_origin, error_code_source)
|
||||
.map(str::to_string)
|
||||
};
|
||||
|
||||
let start = Instant::now();
|
||||
@@ -392,7 +390,7 @@ async fn handle_approved_mcp_tool_call(
|
||||
.await
|
||||
}
|
||||
.await;
|
||||
record_mcp_result_span_telemetry(&Span::current(), &result, error_code_source);
|
||||
record_mcp_result_span_telemetry(&Span::current(), &result);
|
||||
result
|
||||
}
|
||||
.instrument(mcp_tool_call_span(
|
||||
@@ -424,10 +422,11 @@ async fn handle_approved_mcp_tool_call(
|
||||
.await;
|
||||
maybe_track_codex_app_used(sess, turn_context, &server, &tool_name).await;
|
||||
|
||||
let outcome = mcp_call_metric_outcome(&result, error_code_source);
|
||||
let outcome = mcp_call_metric_outcome(&result);
|
||||
emit_mcp_call_metrics(
|
||||
turn_context,
|
||||
&outcome,
|
||||
&server,
|
||||
&tool_name,
|
||||
connector_id,
|
||||
connector_name,
|
||||
@@ -501,12 +500,8 @@ fn record_server_fields(span: &Span, url: Option<&str>) {
|
||||
}
|
||||
}
|
||||
|
||||
fn record_mcp_result_span_telemetry(
|
||||
span: &Span,
|
||||
result: &Result<CallToolResult, String>,
|
||||
error_code_source: McpErrorCodeSource,
|
||||
) {
|
||||
record_mcp_call_outcome_span_telemetry(span, result, error_code_source);
|
||||
fn record_mcp_result_span_telemetry(span: &Span, result: &Result<CallToolResult, String>) {
|
||||
record_mcp_call_outcome_span_telemetry(span, result);
|
||||
|
||||
let Some(span_telemetry) = result
|
||||
.as_ref()
|
||||
|
||||
Reference in new issue
Block a user