mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
51316ead4a
## Context
This is the next step in the plugin auth-routing stack. The earlier PRs
make `PluginsManager` auth-aware and move the broad App/MCP surface
decision into that layer. This PR narrows the ChatGPT/SIWC behavior so
we only hide a plugin MCP server when it conflicts with an App
declaration of the same name.
In product terms: if a plugin exposes both an App route and MCP route
for `foo`, ChatGPT/SIWC sessions should use the App route for `foo`. If
the same plugin also exposes a separate MCP server like `foo2`, that MCP
server should remain available.
```json
// .app.json
{
"apps": {
"foo": {
"id": "connector_abc"
}
}
}
```
```json
// .mcp.json
{
"mcpServers": {
"foo": {
"url": "https://mcp.foo.com/mcp"
},
"foo2": {
"url": "https://mcp.foo2.com/mcp"
}
}
}
```
## 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
- Preserve App declaration names in loaded plugin metadata.
- Keep public effective App outputs as deduped connector IDs for
existing callers.
- For ChatGPT/SIWC, suppress only plugin MCP servers whose names match
declared App names.
## Validation
```bash
cargo fmt --all
cargo test -p codex-core-plugins plugin_auth_projection
cargo test -p codex-core-plugins effective_apps
cargo test -p codex-core-plugins read_plugin_for_config_installed_git_source_reads_from_cache_without_cloning
cargo test -p codex-core explicit_plugin_mentions_use_apps_for_chatgpt_dual_surface_plugins
cargo test -p codex-core explicit_plugin_mentions_keep_non_conflicting_mcp_for_chatgpt_auth
cargo test -p codex-app-server --test all plugin_install_filters_disallowed_apps_needing_auth
git diff --check
```
---------
Co-authored-by: Xin Lin <xl@openai.com>
101 lines
3.0 KiB
Rust
101 lines
3.0 KiB
Rust
//! Shared plugin package models, source providers, identifiers, and telemetry summaries.
|
|
|
|
use std::collections::HashSet;
|
|
|
|
pub use codex_utils_plugins::mention_syntax;
|
|
pub use codex_utils_plugins::plugin_namespace_for_skill_path;
|
|
|
|
mod load_outcome;
|
|
pub mod manifest;
|
|
mod plugin_id;
|
|
mod provider;
|
|
|
|
use codex_config::HookEventsToml;
|
|
use codex_utils_absolute_path::AbsolutePathBuf;
|
|
pub use load_outcome::EffectiveSkillRoots;
|
|
pub use load_outcome::LoadedPlugin;
|
|
pub use load_outcome::PluginLoadOutcome;
|
|
pub use load_outcome::prompt_safe_plugin_description;
|
|
pub use plugin_id::PluginId;
|
|
pub use plugin_id::PluginIdError;
|
|
pub use plugin_id::validate_plugin_segment;
|
|
pub use provider::PluginProvider;
|
|
pub use provider::PluginResourceLocator;
|
|
pub use provider::ResolvedPlugin;
|
|
pub use provider::ResolvedPluginError;
|
|
pub use provider::ResolvedPluginLocation;
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
pub struct AppConnectorId(pub String);
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub struct AppDeclaration {
|
|
pub name: String,
|
|
pub connector_id: AppConnectorId,
|
|
pub category: Option<String>,
|
|
}
|
|
|
|
pub fn app_connector_ids_from_declarations<'a>(
|
|
app_declarations: impl IntoIterator<Item = &'a AppDeclaration>,
|
|
) -> Vec<AppConnectorId> {
|
|
let mut connector_ids = Vec::new();
|
|
let mut seen_connector_ids = HashSet::new();
|
|
for app in app_declarations {
|
|
if seen_connector_ids.insert(&app.connector_id) {
|
|
connector_ids.push(app.connector_id.clone());
|
|
}
|
|
}
|
|
connector_ids
|
|
}
|
|
|
|
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
|
pub struct PluginCapabilitySummary {
|
|
pub config_name: String,
|
|
pub display_name: String,
|
|
pub description: Option<String>,
|
|
pub has_skills: bool,
|
|
pub mcp_server_names: Vec<String>,
|
|
pub app_connector_ids: Vec<AppConnectorId>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub struct PluginHookSource {
|
|
pub plugin_id: PluginId,
|
|
pub plugin_root: AbsolutePathBuf,
|
|
pub plugin_data_root: AbsolutePathBuf,
|
|
pub source_path: AbsolutePathBuf,
|
|
pub source_relative_path: String,
|
|
pub hooks: HookEventsToml,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub struct PluginTelemetryMetadata {
|
|
pub plugin_id: PluginId,
|
|
/// Optional backend identifier for remote plugins, used when analytics
|
|
/// should report the remote id instead of the local plugin cache id.
|
|
pub remote_plugin_id: Option<String>,
|
|
pub capability_summary: Option<PluginCapabilitySummary>,
|
|
}
|
|
|
|
impl PluginTelemetryMetadata {
|
|
pub fn from_plugin_id(plugin_id: &PluginId) -> Self {
|
|
Self {
|
|
plugin_id: plugin_id.clone(),
|
|
remote_plugin_id: None,
|
|
capability_summary: None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PluginCapabilitySummary {
|
|
pub fn telemetry_metadata(&self) -> Option<PluginTelemetryMetadata> {
|
|
PluginId::parse(&self.config_name)
|
|
.ok()
|
|
.map(|plugin_id| PluginTelemetryMetadata {
|
|
plugin_id,
|
|
remote_plugin_id: None,
|
|
capability_summary: Some(self.clone()),
|
|
})
|
|
}
|
|
}
|