From c243abfc815e9d4684414f63fbc663340a90e637 Mon Sep 17 00:00:00 2001 From: Noah MacCallum Date: Fri, 29 May 2026 15:32:11 -0700 Subject: [PATCH] Match plugin suggestions against resolved connector candidates --- codex-rs/core/src/connectors.rs | 17 +++-- codex-rs/core/src/plugins/discoverable.rs | 22 +++++++ .../core/src/plugins/discoverable_tests.rs | 63 ++++++++++++++++--- codex-rs/core/src/plugins/mod.rs | 2 +- 4 files changed, 90 insertions(+), 14 deletions(-) diff --git a/codex-rs/core/src/connectors.rs b/codex-rs/core/src/connectors.rs index 6b64755584..dd5250e8b2 100644 --- a/codex-rs/core/src/connectors.rs +++ b/codex-rs/core/src/connectors.rs @@ -22,7 +22,7 @@ use tracing::warn; use crate::config::Config; use crate::mcp::McpManager; -use crate::plugins::list_tool_suggest_discoverable_plugins; +use crate::plugins::list_tool_suggest_discoverable_plugins_with_connector_candidates; use crate::session::INITIAL_SUBMIT_ID; use codex_config::AppsRequirementsToml; use codex_config::types::AppToolApproval; @@ -148,11 +148,16 @@ pub(crate) async fn list_tool_suggest_discoverable_tools_with_auth( ) .into_iter() .map(DiscoverableTool::from); - let discoverable_plugins = - list_tool_suggest_discoverable_plugins(config, auth, loaded_plugin_app_connector_ids) - .await? - .into_iter() - .map(DiscoverableTool::from); + let candidate_app_connector_ids = connector_ids.iter().cloned().collect::>(); + let discoverable_plugins = list_tool_suggest_discoverable_plugins_with_connector_candidates( + config, + auth, + loaded_plugin_app_connector_ids, + &candidate_app_connector_ids, + ) + .await? + .into_iter() + .map(DiscoverableTool::from); Ok(discoverable_connectors .chain(discoverable_plugins) .collect()) diff --git a/codex-rs/core/src/plugins/discoverable.rs b/codex-rs/core/src/plugins/discoverable.rs index 687c3fe256..4ca6109d05 100644 --- a/codex-rs/core/src/plugins/discoverable.rs +++ b/codex-rs/core/src/plugins/discoverable.rs @@ -21,10 +21,26 @@ const TOOL_SUGGEST_DISCOVERABLE_MARKETPLACE_ALLOWLIST: &[&str] = &[ OPENAI_CURATED_MARKETPLACE_NAME, ]; +#[cfg(test)] pub(crate) async fn list_tool_suggest_discoverable_plugins( config: &Config, auth: Option<&CodexAuth>, loaded_plugin_app_connector_ids: &[String], +) -> anyhow::Result> { + list_tool_suggest_discoverable_plugins_with_connector_candidates( + config, + auth, + loaded_plugin_app_connector_ids, + &[], + ) + .await +} + +pub(crate) async fn list_tool_suggest_discoverable_plugins_with_connector_candidates( + config: &Config, + auth: Option<&CodexAuth>, + loaded_plugin_app_connector_ids: &[String], + candidate_app_connector_ids: &[String], ) -> anyhow::Result> { if !config.features.enabled(Feature::Plugins) { return Ok(Vec::new()); @@ -63,6 +79,12 @@ pub(crate) async fn list_tool_suggest_discoverable_plugins( .cloned() .map(AppConnectorId), ) + .chain( + candidate_app_connector_ids + .iter() + .cloned() + .map(AppConnectorId), + ) .collect::>(); let remote_plugin_service_config = RemotePluginServiceConfig { chatgpt_base_url: config.chatgpt_base_url.clone(), diff --git a/codex-rs/core/src/plugins/discoverable_tests.rs b/codex-rs/core/src/plugins/discoverable_tests.rs index 672817cf05..0edef85c66 100644 --- a/codex-rs/core/src/plugins/discoverable_tests.rs +++ b/codex-rs/core/src/plugins/discoverable_tests.rs @@ -102,15 +102,12 @@ async fn list_tool_suggest_discoverable_plugins_matches_on_resolved_template_app let databricks_app_id = "asdk_app_databricks_workspace"; let server = MockServer::start().await; Mock::given(method("GET")) - .and(path( - "/backend-api/ps/connectors/by_template_id/templated_apps_Databricks", - )) + .and(path("/backend-api/connectors/directory/list_workspace")) .and(header("authorization", "Bearer Access Token")) .and(header("chatgpt-account-id", "account_id")) - .respond_with( - ResponseTemplate::new(200) - .set_body_string(format!(r#"{{"connector_ids":["{databricks_app_id}"]}}"#)), - ) + .respond_with(ResponseTemplate::new(200).set_body_string(format!( + r#"{{"apps":[{{"id":"{databricks_app_id}","template_id":"templated_apps_Databricks"}}]}}"# + ))) .mount(&server) .await; @@ -151,6 +148,58 @@ async fn list_tool_suggest_discoverable_plugins_matches_on_resolved_template_app ); } +#[tokio::test] +async fn list_tool_suggest_discoverable_plugins_matches_on_resolved_connector_candidate_apps() { + let databricks_app_id = "asdk_app_databricks_workspace"; + let server = MockServer::start().await; + Mock::given(method("GET")) + .and(path("/backend-api/connectors/directory/list_workspace")) + .and(header("authorization", "Bearer Access Token")) + .and(header("chatgpt-account-id", "account_id")) + .respond_with(ResponseTemplate::new(200).set_body_string(format!( + r#"{{"apps":[{{"id":"{databricks_app_id}","template_id":"templated_apps_Databricks"}}]}}"# + ))) + .mount(&server) + .await; + + let codex_home = tempdir().expect("tempdir should succeed"); + let curated_root = curated_plugins_repo_path(codex_home.path()); + write_openai_curated_marketplace(&curated_root, &["databricks-source"]); + write_plugin_app( + &curated_root, + "databricks-source", + "databricks", + "templated_apps_Databricks", + ); + write_plugins_feature_config(codex_home.path()); + + let mut config = load_plugins_config(codex_home.path()).await; + config.chatgpt_base_url = format!("{}/backend-api", server.uri()); + let auth = CodexAuth::create_dummy_chatgpt_auth_for_testing(); + let discoverable_plugins = list_tool_suggest_discoverable_plugins_with_connector_candidates( + &config, + Some(&auth), + &[], + &[databricks_app_id.to_string()], + ) + .await + .unwrap(); + + assert_eq!( + discoverable_plugins, + vec![DiscoverablePluginInfo { + id: "databricks-source@openai-curated".to_string(), + name: "databricks-source".to_string(), + description: Some( + "Plugin that includes skills, MCP servers, and app connectors".to_string(), + ), + has_skills: true, + mcp_server_names: vec!["sample-docs".to_string()], + app_connector_ids: vec![databricks_app_id.to_string()], + }] + ); +} + #[tokio::test] async fn list_tool_suggest_discoverable_plugins_filters_microsoft_by_installed_apps() { let codex_home = tempdir().expect("tempdir should succeed"); diff --git a/codex-rs/core/src/plugins/mod.rs b/codex-rs/core/src/plugins/mod.rs index 11e2c338bc..f348e53540 100644 --- a/codex-rs/core/src/plugins/mod.rs +++ b/codex-rs/core/src/plugins/mod.rs @@ -7,7 +7,7 @@ pub(crate) mod test_support; pub(crate) use codex_plugin::PluginCapabilitySummary; -pub(crate) use discoverable::list_tool_suggest_discoverable_plugins; +pub(crate) use discoverable::list_tool_suggest_discoverable_plugins_with_connector_candidates; pub(crate) use injection::build_plugin_injections; pub(crate) use render::render_explicit_plugin_instructions;