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:
jif authored and copyberry committed 2026-07-23 11:27:34 +00:00
1 parent 39a2438d16
commit e497325a6a
54 files changed
+1792 -2038

No files matched your search

@@ -32,16 +32,11 @@ pub(crate) fn effective_plugins_changed_callback(
thread_manager.skills_service().clear_cache();
let refresh_thread_manager = Arc::clone(&thread_manager);
let refresh_config_manager = config_manager.clone();
tokio::spawn(async move {
if refresh_thread_manager.list_thread_ids().await.is_empty() {
return;
}
crate::mcp_refresh::queue_best_effort_refresh(
&refresh_thread_manager,
&refresh_config_manager,
)
.await;
crate::mcp_refresh::invalidate_loaded_threads(&refresh_thread_manager).await;
});
if change.materialized_remote_plugins.is_empty() {