mirror of
https://github.com/openai/codex.git
synced 2026-10-18 17:18:52 +00:00
Refresh MCP config independently across threads (#35216)
## What changed - Add a best-effort MCP configuration refresh that logs per-thread load errors and continues refreshing the remaining threads. - Use the best-effort path after account or plugin changes before invalidating MCP runtimes. ## Testing - Add a test confirming that a thread with a configuration load failure does not prevent healthy threads from receiving updated configuration. GitOrigin-RevId: 34708d46f63fbe8617567ced16b4c199456ab078
This commit is contained in:
@@ -4,6 +4,7 @@ use codex_core::ThreadManager;
|
||||
use codex_core::config::Config;
|
||||
use std::io;
|
||||
use std::sync::Arc;
|
||||
use tracing::warn;
|
||||
|
||||
pub(crate) async fn reload_mcp_config(
|
||||
thread_manager: &Arc<ThreadManager>,
|
||||
@@ -27,6 +28,29 @@ pub(crate) async fn reload_mcp_config(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn reload_mcp_config_best_effort(
|
||||
thread_manager: &Arc<ThreadManager>,
|
||||
config_manager: &ConfigManager,
|
||||
) {
|
||||
for thread_id in thread_manager.list_thread_ids().await {
|
||||
let thread = match thread_manager.get_thread(thread_id).await {
|
||||
Ok(thread) => thread,
|
||||
Err(err) => {
|
||||
warn!(%thread_id, %err, "failed to load thread for MCP configuration refresh");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let config = match load_refresh_config(thread.as_ref(), config_manager).await {
|
||||
Ok(config) => config,
|
||||
Err(err) => {
|
||||
warn!(%thread_id, %err, "failed to load thread MCP configuration");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
thread.refresh_mcp_config(config).await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn load_refresh_config(
|
||||
thread: &CodexThread,
|
||||
config_manager: &ConfigManager,
|
||||
@@ -97,6 +121,31 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn best_effort_refresh_updates_healthy_threads() -> anyhow::Result<()> {
|
||||
let (temp_dir, thread_manager, config_manager, loader) = refresh_test_state().await?;
|
||||
std::fs::write(
|
||||
temp_dir.path().join(codex_config::CONFIG_TOML_FILE),
|
||||
"[features]\nsecret_auth_storage = true\n",
|
||||
)?;
|
||||
|
||||
reload_mcp_config_best_effort(&thread_manager, &config_manager).await;
|
||||
|
||||
assert_eq!(loader.good_loads.load(Ordering::Relaxed), 1);
|
||||
assert_eq!(loader.bad_loads.load(Ordering::Relaxed), 1);
|
||||
for thread_id in thread_manager.list_thread_ids().await {
|
||||
let thread = thread_manager.get_thread(thread_id).await?;
|
||||
let config = thread.config().await;
|
||||
let expected = if config.cwd.ends_with("good") {
|
||||
AuthKeyringBackendKind::Secrets
|
||||
} else {
|
||||
AuthKeyringBackendKind::Direct
|
||||
};
|
||||
assert_eq!(config.auth_keyring_backend_kind(), expected);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn invalidation_does_not_reload_thread_config() -> anyhow::Result<()> {
|
||||
let (_temp_dir, thread_manager, _config_manager, loader) = refresh_test_state().await?;
|
||||
|
||||
@@ -259,11 +259,8 @@ impl AccountRequestProcessor {
|
||||
tokio::spawn(async move {
|
||||
thread_manager.plugins_manager().clear_cache();
|
||||
thread_manager.skills_service().clear_cache();
|
||||
if let Err(err) =
|
||||
crate::mcp_refresh::reload_mcp_config(&thread_manager, &config_manager).await
|
||||
{
|
||||
warn!(%err, "failed to reload MCP configuration after account or plugin change");
|
||||
}
|
||||
crate::mcp_refresh::reload_mcp_config_best_effort(&thread_manager, &config_manager)
|
||||
.await;
|
||||
thread_manager.invalidate_mcp_runtimes().await;
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user