Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions artifacts/github/bundles/openai-codex-pr-27902.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
{
"analysis_mode": "pr_first",
"commits": [
{
"author": "felixxia-oai",
"committed_at": "2026-06-12T21:51:49Z",
"message": "Centralize plugin auth capability filtering",
"sha": "867e7dfb0167f048017a79a8da2ce8e5ecb9b51e",
"url": "https://github.com/openai/codex/commit/867e7dfb0167f048017a79a8da2ce8e5ecb9b51e"
},
{
"author": "felixxia-oai",
"committed_at": "2026-06-15T23:22:24Z",
"message": "Simplify plugin auth capability helper",
"sha": "47889f50220135ea1d170b620ef79aa9e3dd34b2",
"url": "https://github.com/openai/codex/commit/47889f50220135ea1d170b620ef79aa9e3dd34b2"
},
{
"author": "felixxia-oai",
"committed_at": "2026-06-15T23:59:29Z",
"message": "Rename plugin app MCP routing module",
"sha": "17cd2e0d77b51fecfef350a41194cd659c224756",
"url": "https://github.com/openai/codex/commit/17cd2e0d77b51fecfef350a41194cd659c224756"
}
],
"default_branch": "main",
"docs_refs": [],
"examples_refs": [],
"extracted_flags": [
"MCP",
"API",
"OPENAI_BUNDLED_MARKETPLACE_NAME",
"OPENAI_CURATED_MARKETPLACE_NAME"
],
"files": [
{
"additions": 32,
"deletions": 0,
"patch_excerpt": "@@ -0,0 +1,32 @@\n+use codex_app_server_protocol::AuthMode;\n+use codex_plugin::AppDeclaration;\n+use std::collections::HashMap;\n+use std::collections::HashSet;\n+\n+pub fn apps_route_available(auth_mode: Option<AuthMode>) -> bool {\n+ auth_mode.is_some_and(AuthMode::uses_codex_backend)\n+}\n+\n+pub(crate) fn apply_app_mcp_routing_policy<M>(\n+ apps: &mut Vec<AppDeclaration>,\n+ mcp_servers: &mut HashMap<String, M>,\n+ auth_mode: Option<AuthMode>,\n+ plugin_active: bool,\n+) {\n+ if !apps_route_available(auth_mode) {\n+ apps.clear();\n+ return;\n+ }\n+\n+ if plugin_active && !apps.is_empty() {\n+ let app_declaration_names = apps\n+ .iter()\n+ .map(|app| app.name.as_str())\n+ .collect::<HashSet<_>>();\n+ mcp_servers.retain(|name, _| !app_declaration_names.contains(name.as_str()));\n+ }\n+}\n+\n+#[cfg(test)]\n+#[path = \"app_mcp_rout...",
"path": "codex-rs/core-plugins/src/app_mcp_routing.rs",
"status": "added"
},
{
"additions": 99,
"deletions": 0,
"patch_excerpt": "@@ -0,0 +1,99 @@\n+use super::*;\n+use codex_plugin::AppConnectorId;\n+use pretty_assertions::assert_eq;\n+use std::collections::HashMap;\n+\n+fn app(name: &str) -> AppDeclaration {\n+ AppDeclaration {\n+ name: name.to_string(),\n+ connector_id: AppConnectorId(format!(\"connector_{name}\")),\n+ category: None,\n+ }\n+}\n+\n+fn mcp_servers(mcp_servers: impl IntoIterator<Item = (&'static str, i32)>) -> HashMap<String, i32> {\n+ mcp_servers\n+ .into_iter()\n+ .map(|(name, value)| (name.to_string(), value))\n+ .collect::<HashMap<_, _>>()\n+}\n+\n+fn sorted_app_names(apps: &[AppDeclaration]) -> Vec<String> {\n+ let mut names = apps.iter().map(|app| app.name.clone()).collect::<Vec<_>>();\n+ names.sort();\n+ names\n+}\n+\n+fn sorted_mcp_server_names(mcp_servers: &HashMap<String, i32>) -> Vec<String> {\n+ let mut names = mcp_servers.keys().cloned().collect::<Vec<...",
"path": "codex-rs/core-plugins/src/app_mcp_routing_tests.rs",
"status": "added"
},
{
"additions": 2,
"deletions": 0,
"patch_excerpt": "@@ -1,3 +1,4 @@\n+mod app_mcp_routing;\n mod discoverable;\n pub mod installed_marketplaces;\n pub mod loader;\n@@ -24,6 +25,7 @@ pub const OPENAI_BUNDLED_MARKETPLACE_NAME: &str = \"openai-bundled\";\n pub type LoadedPlugin = codex_plugin::LoadedPlugin<codex_config::McpServerConfig>;\n pub type PluginLoadOutcome = codex_plugin::PluginLoadOutcome<codex_config::McpServerConfig>;\n \n+pub use app_mcp_routing::apps_route_available;\n pub use discoverable::ToolSuggestDiscoverablePlugin;\n pub use discoverable::ToolSuggestPluginDiscoveryInput;\n pub use loader::PluginHookLoadOutcome;",
"path": "codex-rs/core-plugins/src/lib.rs",
"status": "modified"
},
{
"additions": 10,
"deletions": 11,
"patch_excerpt": "@@ -1,4 +1,6 @@\n use crate::OPENAI_CURATED_MARKETPLACE_NAME;\n+use crate::app_mcp_routing::apply_app_mcp_routing_policy;\n+use crate::app_mcp_routing::apps_route_available;\n use crate::manifest::PluginManifestHooks;\n use crate::manifest::PluginManifestPaths;\n use crate::manifest::load_plugin_manifest;\n@@ -1085,20 +1087,17 @@ pub async fn load_plugin_mcp_servers(\n auth_mode: Option<AuthMode>,\n ) -> HashMap<String, McpServerConfig> {\n let mut mcp_servers = load_declared_plugin_mcp_servers(plugin_root).await;\n- if !auth_mode.is_some_and(AuthMode::uses_codex_backend) || mcp_servers.is_empty() {\n+ if !apps_route_available(auth_mode) || mcp_servers.is_empty() {\n return mcp_servers;\n }\n \n- let app_declarations = load_plugin_apps(plugin_root).await;\n- if app_declarations.is_empty() {\n- return mcp_servers;\n- }\n-\n- let app_declaration_names = app_declarat...",
"path": "codex-rs/core-plugins/src/loader.rs",
"status": "modified"
},
{
"additions": 8,
"deletions": 15,
"patch_excerpt": "@@ -1,5 +1,6 @@\n use super::PluginLoadOutcome;\n use crate::OPENAI_CURATED_MARKETPLACE_NAME;\n+use crate::app_mcp_routing::apply_app_mcp_routing_policy;\n use crate::installed_marketplaces::installed_marketplace_roots_from_layer_stack;\n use crate::loader::PluginHookLoadOutcome;\n use crate::loader::configured_curated_plugin_ids_from_codex_home;\n@@ -210,23 +211,15 @@ fn project_plugin_load_outcome_for_auth(\n outcome: PluginLoadOutcome,\n auth_mode: Option<AuthMode>,\n ) -> PluginLoadOutcome {\n- let apps_route_available = auth_mode.is_some_and(AuthMode::uses_codex_backend);\n let mut plugins = outcome.plugins().to_vec();\n for plugin in &mut plugins {\n- if apps_route_available {\n- if plugin.is_active() && !plugin.apps.is_empty() {\n- let app_declaration_names = plugin\n- .apps\n- .iter()\n- .ma...",
"path": "codex-rs/core-plugins/src/manager.rs",
"status": "modified"
}
],
"linked_issues": [],
"notes": [
"Built from GitHub pull-request, commits, files, and repo endpoints."
],
"primary_pr": {
"body": "## Summary\r\n\r\nThis is the first step in making plugin auth routing consistent. The rule should not live as one-off checks in every place that loads or displays plugin capabilities.\r\n\r\nThis PR introduces a small resolver for the auth-level policy: given a plugin's declared apps, MCP servers, current auth mode, and active state, return the capabilities that are actually usable in that context.\r\n\r\n## Why\r\n\r\nProduct rule:\r\n- SiWC auth can use app connectors, so app declarations stay available.\r\n- API-key/direct auth cannot use app connectors, so app declarations are removed.\r\n- When an active plugin has both an app and an MCP server with the same name, the app route wins for Codex-backed auth and the conflicting MCP server is hidden.\r\n\r\nPutting that rule in `capabilities.rs` gives the rest of the stack one place to ask instead of duplicating auth checks in loader, manager, marketplace, and details code.\r\n\r\n## Validation\r\n\r\n- `cargo fmt`\r\n- `cargo test -p codex-core-plugins`",
"labels": [],
"merged_at": "2026-06-16T00:13:28Z",
"number": 27902,
"state": "merged",
"title": "[codex] Centralize plugin auth capability filtering",
"url": "https://github.com/openai/codex/pull/27902"
},
"repo": "openai/codex",
"schema": "github_change_bundle/v1"
}
49 changes: 49 additions & 0 deletions artifacts/github/impact/openai-codex-pr-27902.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"schema": "upstream_impact/v1",
"slug": "openai-codex-pr-27902",
"repo": "openai/codex",
"source_refs": {
"items": [
{
"kind": "pull_request",
"title": "[codex] Centralize plugin auth capability filtering",
"url": "https://github.com/openai/codex/pull/27902",
"meta": "Merged 2026-06-16T00:13:28Z"
},
{
"kind": "pull_request",
"title": "Source-backed Decodex upstream review",
"url": "https://github.com/openai/codex/pull/27902",
"meta": "artifacts/github/reviews/openai-codex-pr-27902.review.json"
}
]
},
"observed_change": "Codex core plugin loading now centralizes auth-aware app/MCP routing so effective plugin capabilities depend on auth mode and active plugin state.",
"public_signal_decision": "publish",
"control_plane_impact": "compat_risk",
"publisher_angle": "operator_impact",
"confidence": "confirmed",
"evidence": [
"The upstream PR defines one product rule for app connector availability and same-name app/MCP conflicts.",
"`apps_route_available` treats ChatGPT and AgentIdentity as app-route-capable and treats API-key or missing auth as unavailable.",
"`apply_app_mcp_routing_policy` clears app declarations when apps are unavailable for the auth mode.",
"For active plugins with app declarations, the shared policy removes MCP servers whose names conflict with app declaration names.",
"Loader and manager code now use the shared routing helper instead of duplicating auth checks.",
"The source-backed review is recorded at `artifacts/github/reviews/openai-codex-pr-27902.review.json`."
],
"candidate_followups": [
"Audit Decodex plugin discovery and plugin detail views for places that present declared manifest capabilities instead of auth-filtered effective capabilities.",
"When Decodex is operating with API-key/direct auth, suppress app connector affordances that upstream would clear from effective plugin capabilities.",
"When Codex-backed auth exposes an active app route with the same name as an MCP server, prefer the app route in Control Plane presentation and avoid duplicate routing."
],
"social_notes": [
"Frame this as the upstream rule behind plugin capabilities changing by auth mode.",
"Avoid implying all downstream UI surfaces already consume the new helper; the PR is the core routing foundation.",
"The useful public detail is the three-part rule: Codex-backed auth keeps apps, API-key/direct auth drops apps, and active same-name app routes hide MCP servers."
],
"caveats": [
"The PR consolidates core plugin policy but may be followed by additional marketplace or details changes.",
"No manifest schema migration is recorded; the risk is effective capability visibility, not plugin package format.",
"The upstream policy uses `AuthMode::uses_codex_backend`, so local integrations should follow that semantic boundary rather than hard-coding auth-mode names."
]
}
Loading