[plugins] Support configuration tool suggest allowlist. (#15022)

- [x] Support configuration tool suggest allowlist.

Supports both plugins and connectors.
This commit is contained in:
Matthew Zeng
2026-03-17 23:58:27 -07:00
committed by GitHub
parent 84f4e7b39d
commit 40a7d1d15b
8 changed files with 252 additions and 6 deletions
+13 -2
View File
@@ -1,4 +1,5 @@
use anyhow::Context;
use std::collections::HashSet;
use tracing::warn;
use super::OPENAI_CURATED_MARKETPLACE_NAME;
@@ -6,6 +7,7 @@ use super::PluginCapabilitySummary;
use super::PluginReadRequest;
use super::PluginsManager;
use crate::config::Config;
use crate::config::types::ToolSuggestDiscoverableType;
use crate::features::Feature;
const TOOL_SUGGEST_DISCOVERABLE_PLUGIN_ALLOWLIST: &[&str] = &[
@@ -28,6 +30,13 @@ pub(crate) fn list_tool_suggest_discoverable_plugins(
}
let plugins_manager = PluginsManager::new(config.codex_home.clone());
let configured_plugin_ids = config
.tool_suggest
.discoverables
.iter()
.filter(|discoverable| discoverable.kind == ToolSuggestDiscoverableType::Plugin)
.map(|discoverable| discoverable.id.as_str())
.collect::<HashSet<_>>();
let marketplaces = plugins_manager
.list_marketplaces_for_config(config, &[])
.context("failed to list plugin marketplaces for tool suggestions")?;
@@ -41,10 +50,12 @@ pub(crate) fn list_tool_suggest_discoverable_plugins(
let mut discoverable_plugins = Vec::<PluginCapabilitySummary>::new();
for plugin in curated_marketplace.plugins {
if plugin.installed
|| !TOOL_SUGGEST_DISCOVERABLE_PLUGIN_ALLOWLIST.contains(&plugin.id.as_str())
|| (!TOOL_SUGGEST_DISCOVERABLE_PLUGIN_ALLOWLIST.contains(&plugin.id.as_str())
&& !configured_plugin_ids.contains(plugin.id.as_str()))
{
continue;
}
let plugin_id = plugin.id.clone();
let plugin_name = plugin.name.clone();
@@ -56,7 +67,7 @@ pub(crate) fn list_tool_suggest_discoverable_plugins(
},
) {
Ok(plugin) => discoverable_plugins.push(plugin.plugin.into()),
Err(err) => warn!("failed to load curated plugin suggestion {plugin_id}: {err:#}"),
Err(err) => warn!("failed to load discoverable plugin suggestion {plugin_id}: {err:#}"),
}
}
discoverable_plugins.sort_by(|left, right| {
@@ -117,3 +117,40 @@ async fn list_tool_suggest_discoverable_plugins_omits_installed_curated_plugins(
assert_eq!(discoverable_plugins, Vec::<DiscoverablePluginInfo>::new());
}
#[tokio::test]
async fn list_tool_suggest_discoverable_plugins_includes_configured_plugin_ids() {
let codex_home = tempdir().expect("tempdir should succeed");
let curated_root = crate::plugins::curated_plugins_repo_path(codex_home.path());
write_openai_curated_marketplace(&curated_root, &["sample"]);
write_file(
&codex_home.path().join(crate::config::CONFIG_TOML_FILE),
r#"[features]
plugins = true
[tool_suggest]
discoverables = [{ type = "plugin", id = "sample@openai-curated" }]
"#,
);
let config = load_plugins_config(codex_home.path()).await;
let discoverable_plugins = list_tool_suggest_discoverable_plugins(&config)
.unwrap()
.into_iter()
.map(DiscoverablePluginInfo::from)
.collect::<Vec<_>>();
assert_eq!(
discoverable_plugins,
vec![DiscoverablePluginInfo {
id: "sample@openai-curated".to_string(),
name: "sample".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!["connector_calendar".to_string()],
}]
);
}