Include originating item IDs in MCP request metadata (#40866)

## What changed

- Add the originating Responses item ID to MCP tool request metadata as
  `_meta.itemId`.
- Preserve the initiating `exec` item ID across code-mode cells so nested MCP
  calls carry the same origin.

## Testing

- Cover metadata generation for direct and code-mode MCP tool calls.

GitOrigin-RevId: 1161cac64f1cc3a6d4dac4368e63804bd9d2c417
This commit is contained in:
jif authored and copyberry committed 2026-08-26 10:39:05 +00:00
1 parent 74772623db
commit f5420174da
9 files changed
+175 -49

No files matched your search

+25 -19
View File
@@ -39,6 +39,7 @@ use codex_mcp::ToolInfo;
use codex_mcp::auth_elicitation_completed_result;
use codex_mcp::build_auth_elicitation_plan;
use codex_mcp::mcp_permission_prompt_is_auto_approved;
use codex_protocol::ResponseItemId;
use codex_protocol::approvals::ElicitationRequest;
use codex_protocol::items::McpToolCallError;
use codex_protocol::items::McpToolCallItem;
@@ -116,6 +117,7 @@ pub(crate) async fn handle_mcp_tool_call(
step_context: &Arc<StepContext>,
cancellation_token: &CancellationToken,
call_id: String,
originating_item_id: Option<ResponseItemId>,
tool_info: &ToolInfo,
hook_tool_name: HookToolName,
invocation_tool_name: ToolName,
@@ -259,6 +261,7 @@ pub(crate) async fn handle_mcp_tool_call(
&sess,
step_context.as_ref(),
&call_id,
originating_item_id.as_ref(),
invocation,
prepared_call,
metadata,
@@ -332,6 +335,7 @@ pub(crate) async fn handle_mcp_tool_call(
&sess,
step_context.as_ref(),
&call_id,
originating_item_id.as_ref(),
invocation,
prepared_call,
metadata,
@@ -394,6 +398,7 @@ async fn handle_approved_mcp_tool_call(
sess: &Arc<Session>,
step_context: &StepContext,
call_id: &str,
originating_item_id: Option<&ResponseItemId>,
invocation: McpInvocation,
prepared_call: PreparedMcpCall,
metadata: McpToolApprovalMetadata,
@@ -471,9 +476,10 @@ async fn handle_approved_mcp_tool_call(
call_id,
Some(&metadata),
);
let request_meta = with_mcp_tool_call_thread_id_meta(
let request_meta = with_mcp_tool_call_ids_meta(
request_meta,
&sess.thread_id.to_string(),
originating_item_id,
);
let request_meta = augment_mcp_tool_request_meta_with_sandbox_state(
step_context,
@@ -1125,6 +1131,7 @@ const MCP_TOOL_OPENAI_OUTPUT_TEMPLATE_META_KEY: &str = "openai/outputTemplate";
const MCP_TOOL_UI_RESOURCE_URI_META_KEY: &str = "ui/resourceUri";
const MCP_TOOL_LINK_ID_META_KEY: &str = "link_id";
const MCP_TOOL_PLUGIN_ID_META_KEY: &str = "plugin_id";
const MCP_TOOL_ITEM_ID_META_KEY: &str = "itemId";
const MCP_TOOL_THREAD_ID_META_KEY: &str = "threadId";
const MCP_TOOL_CONNECTED_ACCOUNT_EMAIL_META_KEY: &str = "connected_account_email";
const MCP_TOOL_RESOURCE_URI_META_KEY: &str = "resource_uri";
@@ -1229,28 +1236,27 @@ fn build_mcp_tool_call_request_meta(
(!request_meta.is_empty()).then_some(serde_json::Value::Object(request_meta))
}
fn with_mcp_tool_call_thread_id_meta(
fn with_mcp_tool_call_ids_meta(
meta: Option<serde_json::Value>,
thread_id: &str,
originating_item_id: Option<&ResponseItemId>,
) -> Option<serde_json::Value> {
match meta {
Some(serde_json::Value::Object(mut map)) => {
map.insert(
MCP_TOOL_THREAD_ID_META_KEY.to_string(),
serde_json::Value::String(thread_id.to_string()),
);
Some(serde_json::Value::Object(map))
}
None => {
let mut map = serde_json::Map::new();
map.insert(
MCP_TOOL_THREAD_ID_META_KEY.to_string(),
serde_json::Value::String(thread_id.to_string()),
);
Some(serde_json::Value::Object(map))
}
other => other,
let mut map = match meta {
Some(serde_json::Value::Object(map)) => map,
None => serde_json::Map::new(),
other => return other,
};
map.insert(
MCP_TOOL_THREAD_ID_META_KEY.to_string(),
serde_json::Value::String(thread_id.to_string()),
);
if let Some(item_id) = originating_item_id {
map.insert(
MCP_TOOL_ITEM_ID_META_KEY.to_string(),
serde_json::Value::String(item_id.to_string()),
);
}
Some(serde_json::Value::Object(map))
}
#[derive(Clone, Copy)]