diff --git a/codex-rs/core/src/mcp_tool_call.rs b/codex-rs/core/src/mcp_tool_call.rs index 12b153b97d..52d1efeb44 100644 --- a/codex-rs/core/src/mcp_tool_call.rs +++ b/codex-rs/core/src/mcp_tool_call.rs @@ -679,6 +679,7 @@ pub(crate) struct McpToolApprovalMetadata { } const CODEX_APPS_META_KEY: &str = "_codex_apps"; +const OPENAI_LIBRARY_CONNECTOR_ID: &str = "connector_openai_library"; 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_THREAD_ID_META_KEY: &str = "threadId"; @@ -707,6 +708,18 @@ fn parse_openai_file_upload_options( }) } +fn openai_file_upload_options_for_tool( + server: &str, + connector_id: Option<&str>, + meta: Option<&serde_json::Map>, +) -> Option { + if server != CODEX_APPS_MCP_SERVER_NAME || connector_id != Some(OPENAI_LIBRARY_CONNECTOR_ID) { + return None; + } + + parse_openai_file_upload_options(meta) +} + fn custom_mcp_tool_approval_mode( turn_context: &TurnContext, server: &str, @@ -1216,6 +1229,11 @@ pub(crate) async fn lookup_mcp_tool_metadata( } else { None }; + let openai_file_upload_options = openai_file_upload_options_for_tool( + server, + tool_info.connector_id.as_deref(), + tool_info.tool.meta.as_deref(), + ); Some(McpToolApprovalMetadata { annotations: tool_info.tool.annotations, @@ -1236,9 +1254,7 @@ pub(crate) async fn lookup_mcp_tool_metadata( tool_info.tool.meta.as_deref(), )) .filter(|params| !params.is_empty()), - openai_file_upload_options: parse_openai_file_upload_options( - tool_info.tool.meta.as_deref(), - ), + openai_file_upload_options, }) } diff --git a/codex-rs/core/src/mcp_tool_call_tests.rs b/codex-rs/core/src/mcp_tool_call_tests.rs index 417c9acdd6..a388c3d718 100644 --- a/codex-rs/core/src/mcp_tool_call_tests.rs +++ b/codex-rs/core/src/mcp_tool_call_tests.rs @@ -793,6 +793,52 @@ fn mcp_tool_call_thread_id_meta_is_added_to_request_meta() { ); } +#[test] +fn openai_file_upload_options_are_enabled_for_library_connector() { + let meta = serde_json::json!({ + MCP_TOOL_OPENAI_FILE_UPLOAD_CONFIG_KEY: { + "store_in_library": true, + }, + }); + + assert_eq!( + openai_file_upload_options_for_tool( + CODEX_APPS_MCP_SERVER_NAME, + Some(OPENAI_LIBRARY_CONNECTOR_ID), + meta.as_object(), + ), + Some(OpenAiFileUploadOptions { + store_in_library: true, + }) + ); +} + +#[test] +fn openai_file_upload_options_ignore_untrusted_connectors() { + let meta = serde_json::json!({ + MCP_TOOL_OPENAI_FILE_UPLOAD_CONFIG_KEY: { + "store_in_library": true, + }, + }); + + assert_eq!( + openai_file_upload_options_for_tool( + CODEX_APPS_MCP_SERVER_NAME, + Some("connector_third_party_drive"), + meta.as_object(), + ), + None + ); + assert_eq!( + openai_file_upload_options_for_tool( + "docs", + Some(OPENAI_LIBRARY_CONNECTOR_ID), + meta.as_object(), + ), + None + ); +} + #[test] fn accepted_elicitation_content_converts_to_request_user_input_response() { let response = request_user_input_response_from_elicitation_content(Some(serde_json::json!(