diff --git a/notes/fresh-workspace-handoff.md b/notes/fresh-workspace-handoff.md index 0ab315ca..8f0022ee 100644 --- a/notes/fresh-workspace-handoff.md +++ b/notes/fresh-workspace-handoff.md @@ -844,6 +844,14 @@ exports `with_codex_apps_mcp`, keeps a ChatGPT-auth-keyed app tool cache, or normalizes app callable names/namespaces/titles. MCP remains JSON-RPC at the edge; only user-declared MCP servers are runtime MCP authority. +The plugin provenance reflex has been removed from MCP as well. `McpConfig` +does not carry plugin capability summaries, `McpConnectionManager::new` no +longer accepts `ToolPluginProvenance`, `codex-mcp` no longer depends on +`codex-plugin` or `codex-utils-plugins`, and MCP tool descriptions are not +decorated with Codex plugin product names. If Epiphany later wants MCP +attribution, it needs an Epiphany-owned typed adapter surface, not this old +product metadata graft. + The Phase 6 freshness slice is landed. It exposes read-only `thread/epiphany/freshness` from live retrieval summaries plus graph frontier/churn state and, for loaded threads, watcher-backed invalidation diff --git a/state/ledgers.msgpack b/state/ledgers.msgpack index 091213f6..eda6d24e 100644 Binary files a/state/ledgers.msgpack and b/state/ledgers.msgpack differ diff --git a/state/map.yaml b/state/map.yaml index bda5a3e0..effa0add 100644 --- a/state/map.yaml +++ b/state/map.yaml @@ -294,6 +294,10 @@ current_status: `with_codex_apps_mcp`, keeps a ChatGPT-auth-keyed Codex Apps tool cache, exposes `hard_refresh_codex_apps_tools_cache`, filters non-app MCP tools, normalizes app callable names/namespaces/titles, or masks app file params at - app-specific return boundaries. MCP remains JSON-RPC at the edge; user - declared MCP servers are the only runtime MCP authority. + app-specific return boundaries. The live MCP manager/config shape also no + longer carries plugin provenance: `ToolPluginProvenance`, + `plugin_capability_summaries`, and the `codex-plugin` / `codex-utils-plugins` + dependencies were removed from `codex-mcp`, and MCP tool descriptions are no + longer decorated from plugin product metadata. MCP remains JSON-RPC at the + edge; user-declared MCP servers are the only runtime MCP authority. next_action: Continue starving coordinator/runtime execution and operator surfaces off app-server JSON-RPC and out of `codex_message_processor.rs`; keep the vestigial Codex organ narrowed to subscription auth/model transport. diff --git a/vendor/codex/codex-rs/Cargo.lock b/vendor/codex/codex-rs/Cargo.lock index c267c6b1..979530dc 100644 --- a/vendor/codex/codex-rs/Cargo.lock +++ b/vendor/codex/codex-rs/Cargo.lock @@ -2884,11 +2884,9 @@ dependencies = [ "codex-exec-server", "codex-login", "codex-otel", - "codex-plugin", "codex-protocol", "codex-rmcp-client", "codex-utils-absolute-path", - "codex-utils-plugins", "futures", "pretty_assertions", "regex-lite", diff --git a/vendor/codex/codex-rs/codex-mcp/Cargo.toml b/vendor/codex/codex-rs/codex-mcp/Cargo.toml index 0aec1f3a..3d172fba 100644 --- a/vendor/codex/codex-rs/codex-mcp/Cargo.toml +++ b/vendor/codex/codex-rs/codex-mcp/Cargo.toml @@ -19,10 +19,8 @@ codex-config = { workspace = true } codex-exec-server = { workspace = true } codex-login = { workspace = true } codex-otel = { workspace = true } -codex-plugin = { workspace = true } codex-protocol = { workspace = true } codex-rmcp-client = { workspace = true } -codex-utils-plugins = { workspace = true } futures = { workspace = true } regex-lite = { workspace = true } rmcp = { workspace = true, default-features = false, features = ["base64", "macros", "schemars", "server"] } diff --git a/vendor/codex/codex-rs/codex-mcp/src/lib.rs b/vendor/codex/codex-rs/codex-mcp/src/lib.rs index 8c83cb63..a08c7453 100644 --- a/vendor/codex/codex-rs/codex-mcp/src/lib.rs +++ b/vendor/codex/codex-rs/codex-mcp/src/lib.rs @@ -11,7 +11,6 @@ pub use mcp::McpOAuthScopesSource; pub use mcp::McpServerStatusSnapshot; pub use mcp::McpSnapshotDetail; pub use mcp::ResolvedMcpOAuthScopes; -pub use mcp::ToolPluginProvenance; pub use mcp::canonical_mcp_server_key; pub use mcp::collect_mcp_server_status_snapshot; pub use mcp::collect_mcp_server_status_snapshot_with_detail; @@ -32,7 +31,6 @@ pub use mcp::read_mcp_resource; pub use mcp::resolve_oauth_scopes; pub use mcp::should_retry_without_scopes; pub use mcp::split_qualified_tool_name; -pub use mcp::tool_plugin_provenance; pub use mcp_connection_manager::DEFAULT_STARTUP_TIMEOUT; pub use mcp_connection_manager::MCP_SANDBOX_STATE_META_CAPABILITY; pub use mcp_connection_manager::McpConnectionManager; diff --git a/vendor/codex/codex-rs/codex-mcp/src/mcp/mod.rs b/vendor/codex/codex-rs/codex-mcp/src/mcp/mod.rs index c6f97c71..3b2ba41c 100644 --- a/vendor/codex/codex-rs/codex-mcp/src/mcp/mod.rs +++ b/vendor/codex/codex-rs/codex-mcp/src/mcp/mod.rs @@ -21,7 +21,6 @@ use codex_config::Constrained; use codex_config::McpServerConfig; use codex_config::types::OAuthCredentialsStoreMode; use codex_login::CodexAuth; -use codex_plugin::PluginCapabilitySummary; use codex_protocol::mcp::Resource; use codex_protocol::mcp::ResourceTemplate; use codex_protocol::mcp::Tool; @@ -120,73 +119,8 @@ pub struct McpConfig { pub codex_linux_sandbox_exe: Option, /// Whether to use legacy Landlock behavior in the MCP sandbox state. pub use_legacy_landlock: bool, - /// Whether the app MCP integration is enabled by config. - /// - /// ChatGPT auth is checked separately at runtime before the built-in apps - /// MCP server is added. - pub apps_enabled: bool, /// User-configured and plugin-provided MCP servers keyed by server name. pub configured_mcp_servers: HashMap, - /// Plugin metadata used to attribute MCP tools/connectors to plugin display names. - pub plugin_capability_summaries: Vec, -} - -#[derive(Debug, Clone, Default, PartialEq, Eq)] -pub struct ToolPluginProvenance { - plugin_display_names_by_connector_id: HashMap>, - plugin_display_names_by_mcp_server_name: HashMap>, -} - -impl ToolPluginProvenance { - pub fn plugin_display_names_for_connector_id(&self, connector_id: &str) -> &[String] { - self.plugin_display_names_by_connector_id - .get(connector_id) - .map(Vec::as_slice) - .unwrap_or(&[]) - } - - pub fn plugin_display_names_for_mcp_server_name(&self, server_name: &str) -> &[String] { - self.plugin_display_names_by_mcp_server_name - .get(server_name) - .map(Vec::as_slice) - .unwrap_or(&[]) - } - - fn from_capability_summaries(capability_summaries: &[PluginCapabilitySummary]) -> Self { - let mut tool_plugin_provenance = Self::default(); - for plugin in capability_summaries { - for connector_id in &plugin.app_connector_ids { - tool_plugin_provenance - .plugin_display_names_by_connector_id - .entry(connector_id.0.clone()) - .or_default() - .push(plugin.display_name.clone()); - } - - for server_name in &plugin.mcp_server_names { - tool_plugin_provenance - .plugin_display_names_by_mcp_server_name - .entry(server_name.clone()) - .or_default() - .push(plugin.display_name.clone()); - } - } - - for plugin_names in tool_plugin_provenance - .plugin_display_names_by_connector_id - .values_mut() - .chain( - tool_plugin_provenance - .plugin_display_names_by_mcp_server_name - .values_mut(), - ) - { - plugin_names.sort_unstable(); - plugin_names.dedup(); - } - - tool_plugin_provenance - } } pub fn configured_mcp_servers(config: &McpConfig) -> HashMap { @@ -200,10 +134,6 @@ pub fn effective_mcp_servers( configured_mcp_servers(config) } -pub fn tool_plugin_provenance(config: &McpConfig) -> ToolPluginProvenance { - ToolPluginProvenance::from_capability_summaries(&config.plugin_capability_summaries) -} - pub async fn read_mcp_resource( config: &McpConfig, auth: Option<&CodexAuth>, @@ -226,7 +156,6 @@ pub async fn read_mcp_resource( tx_event, SandboxPolicy::new_read_only_policy(), runtime_environment, - tool_plugin_provenance(config), ) .await; @@ -267,7 +196,6 @@ pub async fn collect_mcp_snapshot_with_detail( detail: McpSnapshotDetail, ) -> McpListToolsResponseEvent { let mcp_servers = effective_mcp_servers(config, auth); - let tool_plugin_provenance = tool_plugin_provenance(config); if mcp_servers.is_empty() { return McpListToolsResponseEvent { tools: HashMap::new(), @@ -292,7 +220,6 @@ pub async fn collect_mcp_snapshot_with_detail( tx_event, SandboxPolicy::new_read_only_policy(), runtime_environment, - tool_plugin_provenance, ) .await; @@ -340,7 +267,6 @@ pub async fn collect_mcp_server_status_snapshot_with_detail( detail: McpSnapshotDetail, ) -> McpServerStatusSnapshot { let mcp_servers = effective_mcp_servers(config, auth); - let tool_plugin_provenance = tool_plugin_provenance(config); if mcp_servers.is_empty() { return McpServerStatusSnapshot { tools_by_server: HashMap::new(), @@ -365,7 +291,6 @@ pub async fn collect_mcp_server_status_snapshot_with_detail( tx_event, SandboxPolicy::new_read_only_policy(), runtime_environment, - tool_plugin_provenance, ) .await; diff --git a/vendor/codex/codex-rs/codex-mcp/src/mcp/mod_tests.rs b/vendor/codex/codex-rs/codex-mcp/src/mcp/mod_tests.rs index 8db52a9d..daf77da0 100644 --- a/vendor/codex/codex-rs/codex-mcp/src/mcp/mod_tests.rs +++ b/vendor/codex/codex-rs/codex-mcp/src/mcp/mod_tests.rs @@ -1,8 +1,5 @@ use super::*; use codex_config::Constrained; -use codex_login::CodexAuth; -use codex_plugin::AppConnectorId; -use codex_plugin::PluginCapabilitySummary; use codex_protocol::protocol::AskForApproval; use pretty_assertions::assert_eq; use std::collections::HashMap; @@ -19,9 +16,7 @@ fn test_mcp_config(codex_home: PathBuf) -> McpConfig { approval_policy: Constrained::allow_any(AskForApproval::OnFailure), codex_linux_sandbox_exe: None, use_legacy_landlock: false, - apps_enabled: false, configured_mcp_servers: HashMap::new(), - plugin_capability_summaries: Vec::new(), } } @@ -84,105 +79,10 @@ fn group_tools_by_server_strips_prefix_and_groups() { assert_eq!(group_tools_by_server(&tools), expected); } -#[test] -fn tool_plugin_provenance_collects_app_and_mcp_sources() { - let provenance = ToolPluginProvenance::from_capability_summaries(&[ - PluginCapabilitySummary { - display_name: "alpha-plugin".to_string(), - app_connector_ids: vec![AppConnectorId("connector_example".to_string())], - mcp_server_names: vec!["alpha".to_string()], - ..PluginCapabilitySummary::default() - }, - PluginCapabilitySummary { - display_name: "beta-plugin".to_string(), - app_connector_ids: vec![ - AppConnectorId("connector_example".to_string()), - AppConnectorId("connector_gmail".to_string()), - ], - mcp_server_names: vec!["beta".to_string()], - ..PluginCapabilitySummary::default() - }, - ]); - - assert_eq!( - provenance, - ToolPluginProvenance { - plugin_display_names_by_connector_id: HashMap::from([ - ( - "connector_example".to_string(), - vec!["alpha-plugin".to_string(), "beta-plugin".to_string()], - ), - ( - "connector_gmail".to_string(), - vec!["beta-plugin".to_string()], - ), - ]), - plugin_display_names_by_mcp_server_name: HashMap::from([ - ("alpha".to_string(), vec!["alpha-plugin".to_string()]), - ("beta".to_string(), vec!["beta-plugin".to_string()]), - ]), - } - ); -} - -#[test] -fn codex_apps_mcp_url_for_base_url_keeps_existing_paths() { - assert_eq!( - codex_apps_mcp_url_for_base_url("https://chatgpt.com/backend-api"), - "https://chatgpt.com/backend-api/wham/apps" - ); - assert_eq!( - codex_apps_mcp_url_for_base_url("https://chat.openai.com"), - "https://chat.openai.com/backend-api/wham/apps" - ); - assert_eq!( - codex_apps_mcp_url_for_base_url("http://localhost:8080/api/codex"), - "http://localhost:8080/api/codex/apps" - ); - assert_eq!( - codex_apps_mcp_url_for_base_url("http://localhost:8080"), - "http://localhost:8080/api/codex/apps" - ); -} - -#[test] -fn codex_apps_mcp_url_uses_legacy_codex_apps_path() { - let config = test_mcp_config(PathBuf::from("/tmp")); - - assert_eq!( - codex_apps_mcp_url(&config), - "https://chatgpt.com/backend-api/wham/apps" - ); -} - -#[test] -fn codex_apps_server_config_uses_legacy_codex_apps_path() { - let mut config = test_mcp_config(PathBuf::from("/tmp")); - let auth = CodexAuth::create_dummy_chatgpt_auth_for_testing(); - - let mut servers = with_codex_apps_mcp(HashMap::new(), /*auth*/ None, &config); - assert!(!servers.contains_key(CODEX_APPS_MCP_SERVER_NAME)); - - config.apps_enabled = true; - - servers = with_codex_apps_mcp(servers, Some(&auth), &config); - let server = servers - .get(CODEX_APPS_MCP_SERVER_NAME) - .expect("codex apps should be present when apps is enabled"); - let url = match &server.transport { - McpServerTransportConfig::StreamableHttp { url, .. } => url, - _ => panic!("expected streamable http transport for codex apps"), - }; - - assert_eq!(url, "https://chatgpt.com/backend-api/wham/apps"); -} - #[tokio::test] -async fn effective_mcp_servers_preserve_user_servers_and_add_codex_apps() { +async fn effective_mcp_servers_preserve_user_servers() { let codex_home = tempfile::tempdir().expect("tempdir"); let mut config = test_mcp_config(codex_home.path().to_path_buf()); - config.apps_enabled = true; - let auth = CodexAuth::create_dummy_chatgpt_auth_for_testing(); config.configured_mcp_servers.insert( "sample".to_string(), @@ -233,15 +133,13 @@ async fn effective_mcp_servers_preserve_user_servers_and_add_codex_apps() { }, ); - let effective = effective_mcp_servers(&config, Some(&auth)); + let effective = effective_mcp_servers(&config, None); let sample = effective.get("sample").expect("user server should exist"); let docs = effective .get("docs") .expect("configured server should exist"); - let codex_apps = effective - .get(CODEX_APPS_MCP_SERVER_NAME) - .expect("codex apps server should exist"); + assert_eq!(effective.len(), 2); match &sample.transport { McpServerTransportConfig::StreamableHttp { url, .. } => { @@ -255,10 +153,4 @@ async fn effective_mcp_servers_preserve_user_servers_and_add_codex_apps() { } other => panic!("expected streamable http transport, got {other:?}"), } - match &codex_apps.transport { - McpServerTransportConfig::StreamableHttp { url, .. } => { - assert_eq!(url, "https://chatgpt.com/backend-api/wham/apps"); - } - other => panic!("expected streamable http transport, got {other:?}"), - } } diff --git a/vendor/codex/codex-rs/codex-mcp/src/mcp_connection_manager.rs b/vendor/codex/codex-rs/codex-mcp/src/mcp_connection_manager.rs index c271a1a8..8a3e9bff 100644 --- a/vendor/codex/codex-rs/codex-mcp/src/mcp_connection_manager.rs +++ b/vendor/codex/codex-rs/codex-mcp/src/mcp_connection_manager.rs @@ -6,7 +6,6 @@ //! in a single aggregated map using the model-visible fully-qualified tool name //! as the key. -use std::borrow::Cow; use std::collections::HashMap; use std::collections::HashSet; use std::env; @@ -19,11 +18,9 @@ use std::time::Instant; use crate::McpAuthStatusEntry; use crate::mcp::McpConfig; -use crate::mcp::ToolPluginProvenance; use crate::mcp::configured_mcp_servers; use crate::mcp::effective_mcp_servers; use crate::mcp::mcp_permission_prompt_is_auto_approved; -use crate::mcp::tool_plugin_provenance; pub(crate) use crate::mcp_tool_names::qualify_tools; use anyhow::Context; use anyhow::Result; @@ -319,7 +316,6 @@ impl ManagedClient { #[derive(Clone)] struct AsyncManagedClient { client: Shared>>, - tool_plugin_provenance: Arc, } impl AsyncManagedClient { @@ -333,7 +329,6 @@ impl AsyncManagedClient { cancel_token: CancellationToken, tx_event: Sender, elicitation_requests: ElicitationRequestManager, - tool_plugin_provenance: Arc, runtime_environment: McpRuntimeEnvironment, ) -> Self { let tool_filter = ToolFilter::from_config(&config); @@ -379,10 +374,7 @@ impl AsyncManagedClient { }; let client = fut.boxed().shared(); - Self { - client, - tool_plugin_provenance, - } + Self { client } } async fn client(&self) -> Result { @@ -390,58 +382,10 @@ impl AsyncManagedClient { } async fn listed_tools(&self) -> Option> { - let annotate_tools = |tools: Vec| { - let mut tools = tools; - for tool in &mut tools { - let plugin_names = match tool.connector_id.as_deref() { - Some(connector_id) => self - .tool_plugin_provenance - .plugin_display_names_for_connector_id(connector_id), - None => self - .tool_plugin_provenance - .plugin_display_names_for_mcp_server_name(tool.server_name.as_str()), - }; - tool.plugin_display_names = plugin_names.to_vec(); - - if plugin_names.is_empty() { - continue; - } - - let plugin_source_note = if plugin_names.len() == 1 { - format!("This tool is part of plugin `{}`.", plugin_names[0]) - } else { - format!( - "This tool is part of plugins {}.", - plugin_names - .iter() - .map(|plugin_name| format!("`{plugin_name}`")) - .collect::>() - .join(", ") - ) - }; - let description = tool - .tool - .description - .as_deref() - .map(str::trim) - .unwrap_or(""); - let annotated_description = if description.is_empty() { - plugin_source_note - } else if matches!(description.chars().last(), Some('.' | '!' | '?')) { - format!("{description} {plugin_source_note}") - } else { - format!("{description}. {plugin_source_note}") - }; - tool.tool.description = Some(Cow::Owned(annotated_description)); - } - tools - }; - - let tools = match self.client().await { + match self.client().await { Ok(client) => Some(client.listed_tools()), Err(_) => None, - }; - tools.map(annotate_tools) + } } } @@ -510,10 +454,6 @@ impl McpConnectionManager { effective_mcp_servers(config, auth) } - pub fn tool_plugin_provenance(&self, config: &McpConfig) -> ToolPluginProvenance { - tool_plugin_provenance(config) - } - pub fn new_uninitialized( approval_policy: &Constrained, sandbox_policy: &Constrained, @@ -558,7 +498,6 @@ impl McpConnectionManager { tx_event: Sender, initial_sandbox_policy: SandboxPolicy, runtime_environment: McpRuntimeEnvironment, - tool_plugin_provenance: ToolPluginProvenance, ) -> (Self, CancellationToken) { let cancel_token = CancellationToken::new(); let mut clients = HashMap::new(); @@ -566,7 +505,6 @@ impl McpConnectionManager { let mut join_set = JoinSet::new(); let elicitation_requests = ElicitationRequestManager::new(approval_policy.value(), initial_sandbox_policy); - let tool_plugin_provenance = Arc::new(tool_plugin_provenance); let startup_submit_id = submit_id.clone(); let mcp_servers = mcp_servers.clone(); for (server_name, cfg) in mcp_servers.into_iter().filter(|(_, cfg)| cfg.enabled) { @@ -590,7 +528,6 @@ impl McpConnectionManager { cancel_token.clone(), tx_event.clone(), elicitation_requests.clone(), - Arc::clone(&tool_plugin_provenance), runtime_environment.clone(), ); clients.insert(server_name.clone(), async_managed_client.clone()); diff --git a/vendor/codex/codex-rs/core/src/config/config_tests.rs b/vendor/codex/codex-rs/core/src/config/config_tests.rs index 55f65db0..6bf4ca54 100644 --- a/vendor/codex/codex-rs/core/src/config/config_tests.rs +++ b/vendor/codex/codex-rs/core/src/config/config_tests.rs @@ -2603,17 +2603,13 @@ async fn to_mcp_config_quarantines_codex_product_surfaces() -> std::io::Result<( .await?; let mcp_config = config.to_mcp_config(); - assert!(!mcp_config.apps_enabled); assert!(!mcp_config.skill_mcp_dependency_install_enabled); - assert!(mcp_config.plugin_capability_summaries.is_empty()); let _ = config.features.disable(Feature::Apps); - let mcp_config = config.to_mcp_config(); - assert!(!mcp_config.apps_enabled); + assert_eq!(config.to_mcp_config().configured_mcp_servers.len(), 0); let _ = config.features.enable(Feature::Apps); - let mcp_config = config.to_mcp_config(); - assert!(!mcp_config.apps_enabled); + assert_eq!(config.to_mcp_config().configured_mcp_servers.len(), 0); Ok(()) } diff --git a/vendor/codex/codex-rs/core/src/config/mod.rs b/vendor/codex/codex-rs/core/src/config/mod.rs index ca2f2913..a7a0c8ec 100644 --- a/vendor/codex/codex-rs/core/src/config/mod.rs +++ b/vendor/codex/codex-rs/core/src/config/mod.rs @@ -817,9 +817,7 @@ impl Config { approval_policy: self.permissions.approval_policy.clone(), codex_linux_sandbox_exe: self.codex_linux_sandbox_exe.clone(), use_legacy_landlock: self.features.use_legacy_landlock(), - apps_enabled: false, configured_mcp_servers: self.mcp_servers.get().clone(), - plugin_capability_summaries: Vec::new(), } } diff --git a/vendor/codex/codex-rs/core/src/session/mcp.rs b/vendor/codex/codex-rs/core/src/session/mcp.rs index 9b98cc4f..c62c16a7 100644 --- a/vendor/codex/codex-rs/core/src/session/mcp.rs +++ b/vendor/codex/codex-rs/core/src/session/mcp.rs @@ -229,7 +229,6 @@ impl Session { .unwrap_or_else(|| self.services.environment_manager.local_environment()), turn_context.cwd.to_path_buf(), ), - ToolPluginProvenance::default(), ) .await; { diff --git a/vendor/codex/codex-rs/core/src/session/mod.rs b/vendor/codex/codex-rs/core/src/session/mod.rs index c25777a8..deb49174 100644 --- a/vendor/codex/codex-rs/core/src/session/mod.rs +++ b/vendor/codex/codex-rs/core/src/session/mod.rs @@ -56,7 +56,6 @@ use codex_login::default_client::originator; use codex_mcp::McpConnectionManager; use codex_mcp::McpRuntimeEnvironment; use codex_mcp::ToolInfo; -use codex_mcp::ToolPluginProvenance; #[cfg(test)] use codex_models_manager::collaboration_mode_presets::CollaborationModesConfig; use codex_models_manager::manager::ModelsManager; diff --git a/vendor/codex/codex-rs/core/src/session/session.rs b/vendor/codex/codex-rs/core/src/session/session.rs index ca628413..b146b967 100644 --- a/vendor/codex/codex-rs/core/src/session/session.rs +++ b/vendor/codex/codex-rs/core/src/session/session.rs @@ -850,7 +850,6 @@ impl Session { .unwrap_or_else(|| sess.services.environment_manager.local_environment()), session_configuration.cwd.to_path_buf(), ), - ToolPluginProvenance::default(), ) .instrument(info_span!( "session_init.mcp_manager_init",