[codex] Preserve plugin apps in connector listings (#27602)

## Context

This is PR4 in the plugin auth-routing stack. The earlier PRs make
plugin surface projection auth-aware and narrow App/MCP conflicts by App
declaration name. This PR keeps connector listing paths aligned with
that projected plugin App set.

This means ChatGPT/SIWC users will still see plugin-provided Apps in
connector listing surfaces like the Apps/connector picker, while API-key
users will not see Apps they cannot use.

## Stack

- PR1: #27652 seed plugin manager auth at construction.
- PR2: #27459 route plugin surfaces by auth mode.
- PR3: #27607 dedupe plugin MCP servers by App declaration name.
- PR4: #27602 preserve plugin Apps in connector listings.
- PR5: #27461 skip install-time plugin MCP OAuth for matching App
routes.

## Summary

- Have app-server compute effective plugin Apps from the existing
PluginsManager and pass them into connector listing.
- Keep plugin Apps visible in Apps/connector listing for ChatGPT/SIWC
users.
- Keep API-key-style auth from surfacing plugin Apps in connector
listings.

## Validation

```bash
cargo test -p codex-chatgpt connectors::tests
cargo test -p codex-app-server list_apps_includes_plugin_apps_for_chatgpt_auth
git diff --check
```
This commit is contained in:
felixxia-oai
2026-06-15 13:50:08 +01:00
committed by GitHub
parent 127224cacc
commit cededa26c5
7 changed files with 153 additions and 53 deletions

View File

@@ -13,7 +13,6 @@ clap = { workspace = true, features = ["derive"] }
codex-app-server-protocol = { workspace = true }
codex-connectors = { workspace = true }
codex-core = { workspace = true }
codex-core-plugins = { workspace = true }
codex-git-utils = { workspace = true }
codex-login = { workspace = true }
codex-model-provider = { workspace = true }

View File

@@ -19,7 +19,6 @@ pub use codex_core::connectors::list_accessible_connectors_from_mcp_tools_with_o
pub use codex_core::connectors::list_accessible_connectors_from_mcp_tools_with_options_and_status;
pub use codex_core::connectors::list_cached_accessible_connectors_from_mcp_tools;
pub use codex_core::connectors::with_app_enabled_state;
use codex_core_plugins::PluginsManager;
use codex_login::AuthManager;
use codex_login::CodexAuth;
use codex_login::default_client::originator;
@@ -69,10 +68,13 @@ pub async fn list_connectors(config: &Config) -> anyhow::Result<Vec<AppInfo>> {
}
pub async fn list_all_connectors(config: &Config) -> anyhow::Result<Vec<AppInfo>> {
list_all_connectors_with_options(config, /*force_refetch*/ false).await
list_all_connectors_with_options(config, /*force_refetch*/ false, &[]).await
}
pub async fn list_cached_all_connectors(config: &Config) -> Option<Vec<AppInfo>> {
pub async fn list_cached_all_connectors(
config: &Config,
plugin_apps: &[AppConnectorId],
) -> Option<Vec<AppInfo>> {
if !apps_enabled(config).await {
return Some(Vec::new());
}
@@ -80,22 +82,13 @@ pub async fn list_cached_all_connectors(config: &Config) -> Option<Vec<AppInfo>>
let auth = connector_auth(config).await.ok()?;
let cache_context = connector_directory_cache_context(config, &auth);
let connectors = codex_connectors::cached_directory_connectors(&cache_context)?;
let connectors = merge_plugin_connectors(
connectors,
plugin_apps_for_config(config)
.await
.into_iter()
.map(|connector_id| connector_id.0),
);
Some(filter_disallowed_connectors(
connectors,
originator().value.as_str(),
))
Some(merge_and_filter_plugin_connectors(connectors, plugin_apps))
}
pub async fn list_all_connectors_with_options(
config: &Config,
force_refetch: bool,
plugin_apps: &[AppConnectorId],
) -> anyhow::Result<Vec<AppInfo>> {
if !apps_enabled(config).await {
return Ok(Vec::new());
@@ -116,17 +109,7 @@ pub async fn list_all_connectors_with_options(
},
)
.await?;
let connectors = merge_plugin_connectors(
connectors,
plugin_apps_for_config(config)
.await
.into_iter()
.map(|connector_id| connector_id.0),
);
Ok(filter_disallowed_connectors(
connectors,
originator().value.as_str(),
))
Ok(merge_and_filter_plugin_connectors(connectors, plugin_apps))
}
fn connector_directory_cache_context(
@@ -144,12 +127,17 @@ fn connector_directory_cache_context(
)
}
async fn plugin_apps_for_config(config: &Config) -> Vec<AppConnectorId> {
let plugins_input = config.plugins_config_input();
PluginsManager::new(config.codex_home.to_path_buf())
.plugins_for_config(&plugins_input)
.await
.effective_apps()
fn merge_and_filter_plugin_connectors(
connectors: Vec<AppInfo>,
plugin_apps: &[AppConnectorId],
) -> Vec<AppInfo> {
let connectors = merge_plugin_connectors(
connectors,
plugin_apps
.iter()
.map(|connector_id| connector_id.0.clone()),
);
filter_disallowed_connectors(connectors, originator().value.as_str())
}
pub fn connectors_for_plugin_apps(