Include session id in MCP turn metadata

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
nicholasclark-openai
2026-03-19 11:18:05 -07:00
co-authored by Codex
parent fb816b1322
commit e84f54294a
6 changed files with 39 additions and 10 deletions
+4
View File
@@ -1285,6 +1285,7 @@ impl Session {
#[allow(clippy::too_many_arguments)]
fn make_turn_context(
conversation_id: ThreadId,
auth_manager: Option<Arc<AuthManager>>,
session_telemetry: &SessionTelemetry,
provider: ModelProviderInfo,
@@ -1335,6 +1336,7 @@ impl Session {
let cwd = session_configuration.cwd.clone();
let turn_metadata_state = Arc::new(TurnMetadataState::new(
conversation_id.to_string(),
sub_id.clone(),
cwd.clone(),
session_configuration.sandbox_policy.get(),
@@ -2391,6 +2393,7 @@ impl Session {
.skills_for_config(&per_turn_config),
);
let mut turn_context: TurnContext = Self::make_turn_context(
self.conversation_id,
Some(Arc::clone(&self.services.auth_manager)),
&self.services.session_telemetry,
session_configuration.provider.clone(),
@@ -5217,6 +5220,7 @@ async fn spawn_review_thread(
let per_turn_config = Arc::new(per_turn_config);
let review_turn_id = sub_id.to_string();
let turn_metadata_state = Arc::new(TurnMetadataState::new(
sess.conversation_id.to_string(),
review_turn_id.clone(),
parent_turn_context.cwd.clone(),
parent_turn_context.sandbox_policy.get(),
+2
View File
@@ -2513,6 +2513,7 @@ pub(crate) async fn make_session_and_context() -> (Session, TurnContext) {
let skills_outcome = Arc::new(services.skills_manager.skills_for_config(&per_turn_config));
let turn_context = Session::make_turn_context(
conversation_id,
Some(Arc::clone(&auth_manager)),
&session_telemetry,
session_configuration.provider.clone(),
@@ -3307,6 +3308,7 @@ pub(crate) async fn make_session_and_context_with_dynamic_tools_and_rx(
let skills_outcome = Arc::new(services.skills_manager.skills_for_config(&per_turn_config));
let turn_context = Arc::new(Session::make_turn_context(
conversation_id,
Some(Arc::clone(&auth_manager)),
&session_telemetry,
session_configuration.provider.clone(),
+18 -10
View File
@@ -441,7 +441,14 @@ fn sanitize_mcp_tool_result_for_model_preserves_image_when_supported() {
#[tokio::test]
async fn mcp_tool_call_request_meta_includes_turn_metadata_for_custom_server() {
let (_session, turn_context) = make_session_and_context().await;
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");
let meta =
build_mcp_tool_call_request_meta(&turn_context, "custom_server", /*metadata*/ None)
@@ -450,17 +457,21 @@ async fn mcp_tool_call_request_meta_includes_turn_metadata_for_custom_server() {
assert_eq!(
meta,
serde_json::json!({
crate::X_CODEX_TURN_METADATA_HEADER: {
"turn_id": turn_context.sub_id,
"sandbox": "workspace-write",
},
crate::X_CODEX_TURN_METADATA_HEADER: expected_turn_metadata,
})
);
}
#[tokio::test]
async fn codex_apps_tool_call_request_meta_includes_turn_metadata_and_codex_apps_meta() {
let (_session, turn_context) = make_session_and_context().await;
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");
let metadata = McpToolApprovalMetadata {
annotations: None,
connector_id: Some("calendar".to_string()),
@@ -487,10 +498,7 @@ async fn codex_apps_tool_call_request_meta_includes_turn_metadata_and_codex_apps
Some(&metadata),
),
Some(serde_json::json!({
crate::X_CODEX_TURN_METADATA_HEADER: {
"turn_id": turn_context.sub_id,
"sandbox": "workspace-write",
},
crate::X_CODEX_TURN_METADATA_HEADER: expected_turn_metadata,
MCP_TOOL_CODEX_APPS_META_KEY: {
"resource_uri": "connector://calendar/tools/calendar_create_event",
"contains_mcp_source": true,
+8
View File
@@ -53,6 +53,8 @@ impl From<WorkspaceGitMetadata> for TurnMetadataWorkspace {
#[derive(Clone, Debug, Serialize, Default)]
pub(crate) struct TurnMetadataBag {
#[serde(default, skip_serializing_if = "Option::is_none")]
session_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
turn_id: Option<String>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
@@ -68,6 +70,7 @@ impl TurnMetadataBag {
}
fn build_turn_metadata_bag(
session_id: Option<String>,
turn_id: Option<String>,
sandbox: Option<String>,
repo_root: Option<String>,
@@ -81,6 +84,7 @@ fn build_turn_metadata_bag(
}
TurnMetadataBag {
session_id,
turn_id,
workspaces,
sandbox,
@@ -104,6 +108,7 @@ pub async fn build_turn_metadata_header(cwd: &Path, sandbox: Option<&str>) -> Op
}
build_turn_metadata_bag(
/*session_id*/ None,
/*turn_id*/ None,
sandbox.map(ToString::to_string),
repo_root,
@@ -128,6 +133,7 @@ pub(crate) struct TurnMetadataState {
impl TurnMetadataState {
pub(crate) fn new(
session_id: String,
turn_id: String,
cwd: PathBuf,
sandbox_policy: &SandboxPolicy,
@@ -136,6 +142,7 @@ impl TurnMetadataState {
let repo_root = get_git_repo_root(&cwd).map(|root| root.to_string_lossy().into_owned());
let sandbox = Some(sandbox_tag(sandbox_policy, windows_sandbox_level).to_string());
let base_metadata = build_turn_metadata_bag(
Some(session_id),
Some(turn_id),
sandbox,
/*repo_root*/ None,
@@ -194,6 +201,7 @@ impl TurnMetadataState {
};
let enriched_metadata = build_turn_metadata_bag(
state.base_metadata.session_id.clone(),
state.base_metadata.turn_id.clone(),
state.base_metadata.sandbox.clone(),
Some(repo_root),
+3
View File
@@ -67,6 +67,7 @@ fn turn_metadata_state_uses_platform_sandbox_tag() {
let sandbox_policy = SandboxPolicy::new_read_only_policy();
let state = TurnMetadataState::new(
"session-a".to_string(),
"turn-a".to_string(),
cwd,
&sandbox_policy,
@@ -76,7 +77,9 @@ fn turn_metadata_state_uses_platform_sandbox_tag() {
let header = state.current_header_value().expect("header");
let json: Value = serde_json::from_str(&header).expect("json");
let sandbox_name = json.get("sandbox").and_then(Value::as_str);
let session_id = json.get("session_id").and_then(Value::as_str);
let expected_sandbox = sandbox_tag(&sandbox_policy, WindowsSandboxLevel::Disabled);
assert_eq!(sandbox_name, Some(expected_sandbox));
assert_eq!(session_id, Some("session-a"));
}
+4
View File
@@ -446,6 +446,10 @@ async fn tool_search_returns_deferred_tools_without_follow_up_tool_injection() -
"connector_id": "calendar",
}))
);
assert_eq!(
apps_tool_call.pointer("/params/_meta/x-codex-turn-metadata/session_id"),
Some(&json!(test.session_configured.session_id.to_string()))
);
assert!(
apps_tool_call
.pointer("/params/_meta/x-codex-turn-metadata/turn_id")