mirror of
https://github.com/openai/codex.git
synced 2026-11-10 21:00:47 +00:00
Refresh Apps tools outside the MCP runtime gate (#60639)
## Why Holding the session MCP gate during `tools/list` blocks other runtime access. Releasing it requires preventing stale refresh results from publishing after configuration or authentication changes. ## What changed - Capture the Apps client under the session gate, then release the gate before fetching tools. - Validate runtime and authentication state before publishing the shared cache and client catalog; retry a superseded refresh once. - Wait for in-flight Apps refreshes before capturing model tools, preparing Apps calls, or shutting down the runtime. ## Testing Add regression coverage for stale refreshes after runtime invalidation or logout, publication validation after call preparation drains, and a model step that waits for refreshed Apps tools while MCP status queries remain available. GitOrigin-RevId: 42d4be8c4a9218933f8e3d07fc7f32f2da9cbd9d
This commit is contained in:
committed by
copyberry
parent
057d9140a8
commit
3f797555cd
@@ -0,0 +1,91 @@
|
||||
#![cfg(test)]
|
||||
//! Verifies that an in-flight Apps refresh cannot publish after invalidation.
|
||||
|
||||
use super::*;
|
||||
use tokio::sync::Notify;
|
||||
|
||||
#[tokio::test]
|
||||
async fn stale_apps_refresh_does_not_publish() -> anyhow::Result<()> {
|
||||
let home = tempfile::tempdir()?;
|
||||
let auth = CodexAuth::from_external_chatgpt_tokens(
|
||||
"header.e30.token",
|
||||
"account",
|
||||
/*chatgpt_plan_type*/ None,
|
||||
)?;
|
||||
let auth_manager =
|
||||
AuthManager::from_auth_for_testing_with_home(auth.clone(), home.path().to_path_buf());
|
||||
let cache = ConnectorRuntimeManager::<ToolInfo>::default().context(
|
||||
home.path().to_path_buf(),
|
||||
ConnectorRuntimeContextKey::personal(
|
||||
/*account_id*/ None, /*chatgpt_user_id*/ None,
|
||||
),
|
||||
);
|
||||
let started = Arc::new(Notify::new());
|
||||
let release = Arc::new(Notify::new());
|
||||
let connections = crate::connection_manager::tests::create_test_manager_with_ready_apps_client(
|
||||
cache.clone(),
|
||||
"search",
|
||||
Some(started.clone()),
|
||||
Some(release.clone()),
|
||||
)
|
||||
.await?;
|
||||
connections.list_all_tools().await;
|
||||
let revision = connections
|
||||
.stable_catalog_revisions(/*required_servers*/ &[], &HashSet::new())
|
||||
.await
|
||||
.expect("Apps catalog is ready");
|
||||
let runtime = McpRuntime::empty(/*prefix_mcp_tool_names*/ true);
|
||||
runtime.current.store(Arc::new(PublishedMcpRuntime {
|
||||
connections: connections.clone(),
|
||||
config: Some(Arc::new(crate::mcp::tests::test_mcp_config(
|
||||
home.path().to_path_buf(),
|
||||
))),
|
||||
auth_token: Some(auth.get_token()?),
|
||||
auth: Some(auth),
|
||||
auth_manager: Some(auth_manager.clone()),
|
||||
auth_owner: None,
|
||||
auth_generation: Arc::new(()),
|
||||
plugins_available: false,
|
||||
ready_selected_capability_roots: Vec::new(),
|
||||
environment_selections: Arc::default(),
|
||||
ready_environments: HashMap::new(),
|
||||
cached_binding: Mutex::new(None),
|
||||
}));
|
||||
|
||||
let refresh = runtime.capture_apps_tool_refresh();
|
||||
tokio::pin!(refresh);
|
||||
tokio::select! {
|
||||
_ = started.notified() => {}
|
||||
_ = &mut refresh => panic!("refresh completed before tools/list was released"),
|
||||
}
|
||||
runtime.invalidate_apps_tool_refresh();
|
||||
release.notify_one();
|
||||
assert!(
|
||||
refresh.await?.is_none(),
|
||||
"runtime invalidation supersedes the fetch"
|
||||
);
|
||||
|
||||
let refresh = runtime.capture_apps_tool_refresh();
|
||||
tokio::pin!(refresh);
|
||||
tokio::select! {
|
||||
_ = started.notified() => {}
|
||||
_ = &mut refresh => panic!("refresh completed before tools/list was released"),
|
||||
}
|
||||
auth_manager.logout().await?;
|
||||
release.notify_one();
|
||||
assert!(refresh.await?.is_none(), "logout supersedes the fetch");
|
||||
|
||||
assert!(
|
||||
!cache.has_current_tools(),
|
||||
"stale tools reached the shared cache"
|
||||
);
|
||||
assert!(
|
||||
connections
|
||||
.stable_catalog_revisions(/*required_servers*/ &[], &HashSet::new())
|
||||
.await
|
||||
.as_ref()
|
||||
== Some(&revision),
|
||||
"a stale fetch invalidated the existing client catalog"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user