[codex] Forward Codex Apps tool call IDs to backend metadata (#19207)

## Summary
- include the outer tool `call_id` in Codex Apps MCP request metadata
under `_meta._codex_apps.call_id`
- preserve existing Codex Apps metadata like `resource_uri` and
`contains_mcp_source`
- add request metadata coverage for both the existing-metadata and
no-existing-metadata cases

## Why
The paired backend change in
[openai/openai#850796](https://github.com/openai/openai/pull/850796)
updates MCP compliance logging to prefer `_meta._codex_apps.call_id`
instead of the JSON-RPC request id. This client change sends that outer
tool call id so the backend can record the model/tool call identifier
when it is available.

This is wire-compatible with older backends because `_meta._codex_apps`
is already reserved backend-only metadata. Backends that do not read
`call_id` will ignore the extra field.

## Testing
- `cargo test -p codex-core request_meta`
- `just fmt`
- `just fix -p codex-core`
This commit is contained in:
rreichel3-oai
2026-04-24 18:49:34 -04:00
committed by GitHub
Unverified
parent 1e560f33e1
commit 219c65dc2f
4 changed files with 54 additions and 9 deletions
+15 -6
View File
@@ -159,8 +159,12 @@ pub(crate) async fn handle_mcp_tool_call(
.unwrap_or_else(|| JsonValue::Object(serde_json::Map::new())),
};
}
let request_meta =
build_mcp_tool_call_request_meta(turn_context.as_ref(), &server, metadata.as_ref());
let request_meta = build_mcp_tool_call_request_meta(
turn_context.as_ref(),
&server,
&call_id,
metadata.as_ref(),
);
let connector_id = metadata
.as_ref()
.and_then(|metadata| metadata.connector_id.clone());
@@ -694,6 +698,7 @@ fn custom_mcp_tool_approval_mode(
fn build_mcp_tool_call_request_meta(
turn_context: &TurnContext,
server: &str,
call_id: &str,
metadata: Option<&McpToolApprovalMetadata>,
) -> Option<serde_json::Value> {
let mut request_meta = serde_json::Map::new();
@@ -705,10 +710,14 @@ fn build_mcp_tool_call_request_meta(
);
}
if server == CODEX_APPS_MCP_SERVER_NAME
&& let Some(codex_apps_meta) =
metadata.and_then(|metadata| metadata.codex_apps_meta.clone())
{
if server == CODEX_APPS_MCP_SERVER_NAME {
let mut codex_apps_meta = metadata
.and_then(|metadata| metadata.codex_apps_meta.clone())
.unwrap_or_default();
codex_apps_meta.insert(
"call_id".to_string(),
serde_json::Value::String(call_id.to_string()),
);
request_meta.insert(
MCP_TOOL_CODEX_APPS_META_KEY.to_string(),
serde_json::Value::Object(codex_apps_meta),
+36 -3
View File
@@ -669,9 +669,13 @@ async fn mcp_tool_call_request_meta_includes_turn_metadata_for_custom_server() {
)
.expect("turn metadata json");
let meta =
build_mcp_tool_call_request_meta(&turn_context, "custom_server", /*metadata*/ None)
.expect("custom servers should receive turn metadata");
let meta = build_mcp_tool_call_request_meta(
&turn_context,
"custom_server",
"call-custom",
/*metadata*/ None,
)
.expect("custom servers should receive turn metadata");
assert_eq!(
meta,
@@ -716,11 +720,13 @@ async fn codex_apps_tool_call_request_meta_includes_turn_metadata_and_codex_apps
build_mcp_tool_call_request_meta(
&turn_context,
CODEX_APPS_MCP_SERVER_NAME,
"call_abc123xyz789",
Some(&metadata),
),
Some(serde_json::json!({
crate::X_CODEX_TURN_METADATA_HEADER: expected_turn_metadata,
MCP_TOOL_CODEX_APPS_META_KEY: {
"call_id": "call_abc123xyz789",
"resource_uri": "connector://calendar/tools/calendar_create_event",
"contains_mcp_source": true,
"connector_id": "calendar",
@@ -729,6 +735,33 @@ async fn codex_apps_tool_call_request_meta_includes_turn_metadata_and_codex_apps
);
}
#[tokio::test]
async fn codex_apps_tool_call_request_meta_includes_call_id_without_existing_codex_apps_meta() {
let (_, turn_context) = make_session_and_context().await;
let expected_turn_metadata = serde_json::from_str::<serde_json::Value>(
&turn_context
.turn_metadata_state
.current_header_value()
.expect("turn metadata header"),
)
.expect("turn metadata json");
assert_eq!(
build_mcp_tool_call_request_meta(
&turn_context,
CODEX_APPS_MCP_SERVER_NAME,
"call_abc123xyz789",
/*metadata*/ None,
),
Some(serde_json::json!({
crate::X_CODEX_TURN_METADATA_HEADER: expected_turn_metadata,
MCP_TOOL_CODEX_APPS_META_KEY: {
"call_id": "call_abc123xyz789",
},
}))
);
}
#[test]
fn mcp_tool_call_thread_id_meta_is_added_to_request_meta() {
assert_eq!(
@@ -222,6 +222,7 @@ async fn codex_apps_file_params_upload_local_paths_before_mcp_tool_call() -> Res
assert_eq!(
apps_tool_call.pointer("/params/_meta/_codex_apps"),
Some(&json!({
"call_id": "extract-call-1",
"resource_uri": DOCUMENT_EXTRACT_TEXT_RESOURCE_URI,
"contains_mcp_source": true,
"connector_id": "calendar",
+2
View File
@@ -555,6 +555,7 @@ async fn tool_search_returns_deferred_tools_without_follow_up_tool_injection() -
.structured_content,
Some(json!({
"_codex_apps": {
"call_id": "calendar-call-1",
"resource_uri": CALENDAR_CREATE_EVENT_RESOURCE_URI,
"contains_mcp_source": true,
"connector_id": "calendar",
@@ -586,6 +587,7 @@ async fn tool_search_returns_deferred_tools_without_follow_up_tool_injection() -
assert_eq!(
apps_tool_call.pointer("/params/_meta/_codex_apps"),
Some(&json!({
"call_id": "calendar-call-1",
"resource_uri": CALENDAR_CREATE_EVENT_RESOURCE_URI,
"contains_mcp_source": true,
"connector_id": "calendar",