feat: hot reload mcp servers (#8957)

### Summary
* Added `mcpServer/refresh` command to inform app servers and active
threads to refresh mcpServer on next turn event.
* Added `pending_mcp_server_refresh_config` to codex core so that if the
value is populated, we reinitialize the mcp server manager on the thread
level.
* The config is updated on `mcpServer/refresh` command which we iterate
through threads and provide with the latest config value after last
write.
This commit is contained in:
Shijie Rao
2026-01-12 11:17:50 -08:00
committed by GitHub
parent 034d489c34
commit 3e91a95ce1
8 changed files with 247 additions and 6 deletions

View File

@@ -21,6 +21,7 @@ use crate::skills::SkillsManager;
use codex_protocol::ThreadId;
use codex_protocol::openai_models::ModelPreset;
use codex_protocol::protocol::InitialHistory;
use codex_protocol::protocol::McpServerRefreshConfig;
use codex_protocol::protocol::Op;
use codex_protocol::protocol::RolloutItem;
use codex_protocol::protocol::SessionSource;
@@ -30,6 +31,7 @@ use std::sync::Arc;
#[cfg(any(test, feature = "test-support"))]
use tempfile::TempDir;
use tokio::sync::RwLock;
use tracing::warn;
/// Represents a newly created Codex thread (formerly called a conversation), including the first event
/// (which is [`EventMsg::SessionConfigured`]).
@@ -144,6 +146,27 @@ impl ThreadManager {
self.state.threads.read().await.keys().copied().collect()
}
pub async fn refresh_mcp_servers(&self, refresh_config: McpServerRefreshConfig) {
let threads = self
.state
.threads
.read()
.await
.values()
.cloned()
.collect::<Vec<_>>();
for thread in threads {
if let Err(err) = thread
.submit(Op::RefreshMcpServers {
config: refresh_config.clone(),
})
.await
{
warn!("failed to request MCP server refresh: {err}");
}
}
}
pub async fn get_thread(&self, thread_id: ThreadId) -> CodexResult<Arc<CodexThread>> {
self.state.get_thread(thread_id).await
}