mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
Centralize thread MCP state in McpRuntime (#34930)
## What changed - Make `McpRuntime` own the published MCP configuration, connections, elicitation routing, and selected capability roots for a thread. - Capture immutable MCP bindings for model steps and tool calls so in-flight work keeps a consistent connection set and approval authority while refreshed state is published atomically. - Mark MCP state dirty when relevant configuration, plugins, environments, authentication, or elicitation capabilities change, then rebuild it before the next sampling request or out-of-band MCP operation. - Separate config reloads from server invalidation: `ReloadMcpConfig` applies resolved MCP inputs, while `RefreshMcpServers` requests reinitialization from the thread's latest state. ## Testing - Cover refreshed state visibility for existing turns, stable step bindings, current approval authority, cancelled refresh retries, resource-client reconciliation, and Apps recovery between sampling requests. GitOrigin-RevId: 59eabb1aa8dc083426bd18ef4d3630508f376401
This commit is contained in:
1 parent
39a2438d16
commit
e497325a6a
54 files changed
+1792
-2038
No files matched your search
@@ -19,11 +19,13 @@ use std::env;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
|
||||
use codex_config::ConfigLayerStack;
|
||||
use codex_config::Constrained;
|
||||
use codex_config::McpServerAuth;
|
||||
use codex_config::McpServerConfig;
|
||||
use codex_config::McpServerTransportConfig;
|
||||
use codex_config::types::AppToolApproval;
|
||||
use codex_config::types::ApprovalsReviewer;
|
||||
use codex_config::types::AuthKeyringBackendKind;
|
||||
use codex_config::types::OAuthCredentialsStoreMode;
|
||||
use codex_connectors::ConnectorRuntimeManager;
|
||||
@@ -38,6 +40,7 @@ use codex_protocol::mcp::Tool;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_protocol::protocol::AskForApproval;
|
||||
use codex_protocol::protocol::McpAuthStatus;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
use rmcp::model::ElicitationCapability;
|
||||
use rmcp::model::ReadResourceRequestParams;
|
||||
use rmcp::model::ReadResourceResult;
|
||||
@@ -105,13 +108,10 @@ pub struct McpPermissionPromptAutoApproveContext {
|
||||
|
||||
/// MCP runtime settings derived from `codex_core::config::Config`.
|
||||
///
|
||||
/// This struct should contain only long-lived configuration values that the
|
||||
/// `codex-mcp` crate needs to construct server transports, enforce MCP
|
||||
/// approval/sandbox policy, locate OAuth state, and merge plugin-provided MCP
|
||||
/// servers. Request-scoped or auth-scoped state should not be stored here;
|
||||
/// thread those values explicitly into runtime entry points such as
|
||||
/// [`effective_mcp_servers`] and snapshot collection helpers so config objects
|
||||
/// do not go stale when auth changes.
|
||||
/// Each published runtime and prepared call owns one immutable copy of these
|
||||
/// settings, so its connection, approval policy, and sandbox authority cannot
|
||||
/// change independently. Auth remains separate and is supplied explicitly to
|
||||
/// runtime entry points such as [`effective_mcp_servers`].
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct McpConfig {
|
||||
/// Base URL for ChatGPT-hosted app MCP servers, copied from the root config.
|
||||
@@ -132,6 +132,14 @@ pub struct McpConfig {
|
||||
pub skill_mcp_dependency_install_enabled: bool,
|
||||
/// Approval policy used for MCP tool calls and MCP elicitation requests.
|
||||
pub approval_policy: Constrained<AskForApproval>,
|
||||
/// Permission profile captured with the connections and approval policy.
|
||||
pub permission_profile: PermissionProfile,
|
||||
/// Configuration layers used to evaluate Apps tool policy and reviewer selection.
|
||||
pub config_layer_stack: ConfigLayerStack,
|
||||
/// Default reviewer used when an Apps tool has no reviewer override.
|
||||
pub approvals_reviewer: ApprovalsReviewer,
|
||||
/// Working directories for the exact environment handles used by this runtime.
|
||||
pub environment_cwds: HashMap<String, PathUri>,
|
||||
/// Optional path to `codex-linux-sandbox` for sandboxed MCP tool execution.
|
||||
pub codex_linux_sandbox_exe: Option<PathBuf>,
|
||||
/// Whether to use legacy Landlock behavior in the MCP sandbox state.
|
||||
@@ -334,6 +342,7 @@ pub async fn read_mcp_resource(
|
||||
/*elicitation_reviewer*/ None,
|
||||
/*elicitation_lifecycle*/ None,
|
||||
crate::elicitation::ElicitationRequestRouter::default(),
|
||||
crate::runtime::McpPublicationGate::already_published(),
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -388,7 +397,7 @@ pub async fn collect_mcp_server_status_snapshot_with_detail(
|
||||
let server_names = mcp_servers.keys().cloned().collect();
|
||||
|
||||
let cancel_token = CancellationToken::new();
|
||||
let mcp_connection_manager = McpConnectionSet::new(
|
||||
let mcp_connection_set = McpConnectionSet::new(
|
||||
&mcp_servers,
|
||||
config.mcp_oauth_credentials_store_mode,
|
||||
config.auth_keyring_backend_kind,
|
||||
@@ -411,11 +420,12 @@ pub async fn collect_mcp_server_status_snapshot_with_detail(
|
||||
/*elicitation_reviewer*/ None,
|
||||
/*elicitation_lifecycle*/ None,
|
||||
crate::elicitation::ElicitationRequestRouter::default(),
|
||||
crate::runtime::McpPublicationGate::already_published(),
|
||||
)
|
||||
.await;
|
||||
|
||||
let snapshot = collect_mcp_server_status_snapshot_from_manager(
|
||||
&mcp_connection_manager,
|
||||
&mcp_connection_set,
|
||||
auth_status_entries,
|
||||
server_names,
|
||||
detail,
|
||||
@@ -642,27 +652,27 @@ fn convert_mcp_resource_templates(
|
||||
}
|
||||
|
||||
async fn collect_mcp_server_status_snapshot_from_manager(
|
||||
mcp_connection_manager: &McpConnectionSet,
|
||||
mcp_connection_set: &McpConnectionSet,
|
||||
auth_status_entries: HashMap<String, crate::mcp::auth::McpAuthStatusEntry>,
|
||||
server_names: Vec<String>,
|
||||
detail: McpSnapshotDetail,
|
||||
) -> McpServerStatusSnapshot {
|
||||
let ((server_infos, tools), resources, resource_templates) = tokio::join!(
|
||||
async {
|
||||
let server_infos = mcp_connection_manager.list_available_server_infos().await;
|
||||
let tools = mcp_connection_manager.list_all_tools().await;
|
||||
let server_infos = mcp_connection_set.list_available_server_infos().await;
|
||||
let tools = mcp_connection_set.list_all_tools().await;
|
||||
(server_infos, tools)
|
||||
},
|
||||
async {
|
||||
if detail.include_resources() {
|
||||
mcp_connection_manager.list_all_resources(|_| true).await
|
||||
mcp_connection_set.list_all_resources(|_| true).await
|
||||
} else {
|
||||
HashMap::new()
|
||||
}
|
||||
},
|
||||
async {
|
||||
if detail.include_resources() {
|
||||
mcp_connection_manager
|
||||
mcp_connection_set
|
||||
.list_all_resource_templates(|_| true)
|
||||
.await
|
||||
} else {
|
||||
|
||||
@@ -28,6 +28,10 @@ pub(crate) fn test_mcp_config(codex_home: PathBuf) -> McpConfig {
|
||||
mcp_oauth_callback_url: None,
|
||||
skill_mcp_dependency_install_enabled: true,
|
||||
approval_policy: Constrained::allow_any(AskForApproval::OnRequest),
|
||||
permission_profile: PermissionProfile::default(),
|
||||
config_layer_stack: codex_config::ConfigLayerStack::default(),
|
||||
approvals_reviewer: codex_config::types::ApprovalsReviewer::default(),
|
||||
environment_cwds: HashMap::new(),
|
||||
codex_linux_sandbox_exe: None,
|
||||
use_legacy_landlock: false,
|
||||
apps_enabled: false,
|
||||
|
||||
Reference in new issue
Block a user