From fcd2273de7d1dc593a9696c935502851ca954d8a Mon Sep 17 00:00:00 2001 From: charlesgong-openai Date: Wed, 29 Jul 2026 00:46:01 +0000 Subject: [PATCH] Resolve imported connectors by MCP server name (#35856) ## Why Imported session attribution can identify an MCP server by its configured name instead of the UUID stored in the session manifest. ## What changed Match attributed MCP servers against normalized manifest names without regard to case, while retaining UUID matching. ## Testing Add focused coverage for both name- and UUID-based attribution. GitOrigin-RevId: 827443f9e7f14028e17b5c5775ca372bfe2e217e --- .../tests/suite/v2/external_agent_config.rs | 2 +- .../src/detect/sessions/connectors_cla.rs | 22 ++++-- .../detect/sessions/connectors_cla_tests.rs | 74 +++++++++++++++++++ 3 files changed, 91 insertions(+), 7 deletions(-) create mode 100644 codex-rs/external-agent-migration/src/detect/sessions/connectors_cla_tests.rs diff --git a/codex-rs/app-server/tests/suite/v2/external_agent_config.rs b/codex-rs/app-server/tests/suite/v2/external_agent_config.rs index 70d923ec8c..cf84e53e16 100644 --- a/codex-rs/app-server/tests/suite/v2/external_agent_config.rs +++ b/codex-rs/app-server/tests/suite/v2/external_agent_config.rs @@ -1563,7 +1563,7 @@ async fn external_agent_config_import_creates_session_rollouts() -> Result<()> { "type": "assistant", "cwd": &project_root, "timestamp": source_updated_at_text, - "attributionMcpServer": "gmail-server", + "attributionMcpServer": "gmail", "message": { "content": "first answer" }, }) .to_string(), diff --git a/codex-rs/external-agent-migration/src/detect/sessions/connectors_cla.rs b/codex-rs/external-agent-migration/src/detect/sessions/connectors_cla.rs index 60002897ad..ee5ff8ac46 100644 --- a/codex-rs/external-agent-migration/src/detect/sessions/connectors_cla.rs +++ b/codex-rs/external-agent-migration/src/detect/sessions/connectors_cla.rs @@ -65,17 +65,23 @@ pub fn detect_imported_cla_session_connectors( let connector_names = connector_names_by_session.entry(session_id).or_default(); for server in manifest.remote_mcp_servers_config { - let Some(uuid) = server.uuid else { - continue; - }; - if !attributed_server_ids.contains(&uuid) { - continue; - } let Some(name) = crate::sessions::normalized_connector_display_name(server.name.as_deref()) else { continue; }; + // Depending on the source client, attributionMcpServer contains either the + // manifest UUID or its configured server name. + let matches_uuid = server + .uuid + .as_deref() + .is_some_and(|uuid| attributed_server_ids.contains(uuid)); + let matches_name = attributed_server_ids + .iter() + .any(|server_id| server_id.eq_ignore_ascii_case(&name)); + if !matches_uuid && !matches_name { + continue; + } connector_names.entry(name.to_lowercase()).or_insert(name); } } @@ -114,3 +120,7 @@ fn json_files_recursively(root: &Path) -> Vec { } files } + +#[cfg(test)] +#[path = "connectors_cla_tests.rs"] +mod tests; diff --git a/codex-rs/external-agent-migration/src/detect/sessions/connectors_cla_tests.rs b/codex-rs/external-agent-migration/src/detect/sessions/connectors_cla_tests.rs new file mode 100644 index 0000000000..aa61cde80f --- /dev/null +++ b/codex-rs/external-agent-migration/src/detect/sessions/connectors_cla_tests.rs @@ -0,0 +1,74 @@ +use super::*; +use pretty_assertions::assert_eq; +use std::collections::BTreeSet; +use tempfile::TempDir; + +#[test] +fn resolves_attributed_server_name_from_session_manifest() { + let metadata_root = TempDir::new().expect("tempdir"); + let manifests_root = metadata_root.path().join(SESSION_MANIFESTS_DIR); + std::fs::create_dir_all(&manifests_root).expect("manifest directory"); + std::fs::write( + manifests_root.join("session.json"), + serde_json::json!({ + "cliSessionId": "session-1", + "remoteMcpServersConfig": [ + { + "uuid": "c58ac595-58b5-48f8-ac77-d8d01523dede", + "name": "Figma", + }, + ], + }) + .to_string(), + ) + .expect("manifest"); + let attributions = vec![ImportedSessionConnectorAttribution { + session_id: "session-1".to_string(), + server_ids: BTreeSet::from(["figma".to_string()]), + }]; + + let connector_names = detect_imported_cla_session_connectors( + &attributions, + &[metadata_root.path().to_path_buf()], + ); + + assert_eq!( + connector_names, + BTreeMap::from([("session-1".to_string(), vec!["Figma".to_string()])]) + ); +} + +#[test] +fn still_resolves_attributed_server_uuid_from_session_manifest() { + let metadata_root = TempDir::new().expect("tempdir"); + let manifests_root = metadata_root.path().join(SESSION_MANIFESTS_DIR); + std::fs::create_dir_all(&manifests_root).expect("manifest directory"); + std::fs::write( + manifests_root.join("session.json"), + serde_json::json!({ + "cliSessionId": "session-1", + "remoteMcpServersConfig": [ + { + "uuid": "c58ac595-58b5-48f8-ac77-d8d01523dede", + "name": "Figma", + }, + ], + }) + .to_string(), + ) + .expect("manifest"); + let attributions = vec![ImportedSessionConnectorAttribution { + session_id: "session-1".to_string(), + server_ids: BTreeSet::from(["c58ac595-58b5-48f8-ac77-d8d01523dede".to_string()]), + }]; + + let connector_names = detect_imported_cla_session_connectors( + &attributions, + &[metadata_root.path().to_path_buf()], + ); + + assert_eq!( + connector_names, + BTreeMap::from([("session-1".to_string(), vec!["Figma".to_string()])]) + ); +}