@@ -17,13 +17,15 @@ use crate::McpAuthStatusEntry;
1717use crate :: connector_runtime:: CodexAppsToolsCache ;
1818use crate :: connector_runtime:: CodexAppsToolsCacheKey ;
1919use crate :: connector_runtime:: CodexAppsToolsFetchSource ;
20+ use crate :: connector_runtime:: ConnectorRuntimeSnapshot ;
2021use crate :: elicitation:: ElicitationRequestManager ;
2122use crate :: elicitation:: ElicitationRequestRouter ;
2223use crate :: elicitation:: ElicitationReviewerHandle ;
2324use crate :: mcp:: CODEX_APPS_MCP_SERVER_NAME ;
2425use crate :: mcp:: ToolPluginProvenance ;
2526use crate :: rmcp_client:: AsyncManagedClient ;
2627use crate :: rmcp_client:: CODEX_APPS_REFRESH_DURATION_METRIC ;
28+ use crate :: rmcp_client:: CodexAppsStartupMode ;
2729use crate :: rmcp_client:: DEFAULT_STARTUP_TIMEOUT ;
2830use crate :: rmcp_client:: MCP_TOOLS_LIST_DURATION_METRIC ;
2931use crate :: rmcp_client:: ManagedClient ;
@@ -144,6 +146,7 @@ impl McpConnectionManager {
144146 elicitation_reviewer : Option < ElicitationReviewerHandle > ,
145147 elicitation_lifecycle : Option < crate :: ElicitationLifecycle > ,
146148 elicitation_router : ElicitationRequestRouter ,
149+ codex_apps_startup_mode : CodexAppsStartupMode ,
147150 ) -> Self {
148151 let mut required_servers = mcp_servers
149152 . iter ( )
@@ -223,6 +226,7 @@ impl McpConnectionManager {
223226 runtime_auth_provider,
224227 client_elicitation_capability. clone ( ) ,
225228 supports_openai_form_elicitation,
229+ codex_apps_startup_mode,
226230 ) ;
227231 clients. insert ( server_name. clone ( ) , async_managed_client. clone ( ) ) ;
228232 let tx_event = tx_event. clone ( ) ;
@@ -528,13 +532,98 @@ impl McpConnectionManager {
528532 normalize_tools_for_model_with_prefix ( tools, self . prefix_mcp_tool_names )
529533 }
530534
535+ /// Force-refreshes the connector runtime exactly once under the active
536+ /// context's shared lock and returns the exact committed snapshot.
537+ pub async fn hard_refresh_codex_apps_runtime ( & self ) -> Result < Arc < ConnectorRuntimeSnapshot > > {
538+ let refresh_start = Instant :: now ( ) ;
539+ let async_client = self
540+ . clients
541+ . get ( CODEX_APPS_MCP_SERVER_NAME )
542+ . ok_or_else ( || anyhow ! ( "unknown MCP server '{CODEX_APPS_MCP_SERVER_NAME}'" ) ) ?;
543+ let result = async {
544+ let cache_context = async_client
545+ . codex_apps_tools_cache_context
546+ . as_ref ( )
547+ . ok_or_else ( || anyhow ! ( "connector runtime manager is unavailable" ) ) ?;
548+ let _refresh_guard = cache_context
549+ . lock_explicit_refresh ( )
550+ . await
551+ . context ( "connector runtime context changed before refresh" ) ?;
552+ let managed_client = self . client_by_name ( CODEX_APPS_MCP_SERVER_NAME ) . await ?;
553+ let list_start = Instant :: now ( ) ;
554+ let fetch_ticket = cache_context. begin_fetch ( CodexAppsToolsFetchSource :: HardRefresh ) ;
555+ let tools = list_tools_for_client_uncached (
556+ CODEX_APPS_MCP_SERVER_NAME ,
557+ /*is_codex_apps_mcp_server*/ true ,
558+ /*codex_apps_refresh_trigger*/ "explicit" ,
559+ & managed_client. client ,
560+ managed_client. tool_timeout ,
561+ managed_client. server_instructions . as_deref ( ) ,
562+ )
563+ . await
564+ . with_context ( || {
565+ format ! ( "failed to refresh tools for MCP server '{CODEX_APPS_MCP_SERVER_NAME}'" )
566+ } ) ?;
567+ let snapshot = cache_context
568+ . publish_runtime_if_newest_accepted (
569+ fetch_ticket,
570+ & managed_client. server_info ,
571+ tools,
572+ )
573+ . context ( "connector runtime context changed while publishing refresh" ) ?;
574+ emit_duration (
575+ MCP_TOOLS_LIST_DURATION_METRIC ,
576+ list_start. elapsed ( ) ,
577+ & [ ( "cache" , "miss" ) ] ,
578+ ) ;
579+ Ok ( snapshot)
580+ }
581+ . await ;
582+ let outcome = if result. is_ok ( ) { "success" } else { "error" } ;
583+ let retained_previous = if result. is_err ( )
584+ && async_client
585+ . codex_apps_tools_cache_context
586+ . as_ref ( )
587+ . and_then ( super :: connector_runtime:: ConnectorRuntimeContext :: current_snapshot)
588+ . is_some ( )
589+ {
590+ "true"
591+ } else {
592+ "false"
593+ } ;
594+ emit_duration (
595+ CODEX_APPS_REFRESH_DURATION_METRIC ,
596+ refresh_start. elapsed ( ) ,
597+ & [
598+ ( "path" , "new" ) ,
599+ ( "trigger" , "explicit" ) ,
600+ ( "outcome" , outcome) ,
601+ ( "retained_previous_snapshot" , retained_previous) ,
602+ ] ,
603+ ) ;
604+ result
605+ }
606+
531607 /// Force-refresh codex apps tools by bypassing the in-process cache.
532608 ///
533609 /// On success, the refreshed tools replace shared cache contents when the
534610 /// cache is enabled and the latest filtered tools are returned directly to
535611 /// the caller. On failure, existing shared cache contents remain unchanged.
536612 pub async fn hard_refresh_codex_apps_tools_cache ( & self ) -> Result < Vec < ToolInfo > > {
537613 let refresh_start = Instant :: now ( ) ;
614+ let async_client = self
615+ . clients
616+ . get ( CODEX_APPS_MCP_SERVER_NAME )
617+ . ok_or_else ( || anyhow ! ( "unknown MCP server '{CODEX_APPS_MCP_SERVER_NAME}'" ) ) ?;
618+ let _refresh_guard = match async_client. codex_apps_tools_cache_context . as_ref ( ) {
619+ Some ( cache_context) => Some (
620+ cache_context
621+ . lock_explicit_refresh ( )
622+ . await
623+ . context ( "connector runtime context changed before refresh" ) ?,
624+ ) ,
625+ None => None ,
626+ } ;
538627 let managed_client = self . client_by_name ( CODEX_APPS_MCP_SERVER_NAME ) . await ?;
539628
540629 let list_start = Instant :: now ( ) ;
@@ -555,16 +644,21 @@ impl McpConnectionManager {
555644 format ! ( "failed to refresh tools for MCP server '{CODEX_APPS_MCP_SERVER_NAME}'" )
556645 } ) ?;
557646
558- let tools =
559- match (
560- managed_client. codex_apps_tools_cache_context . as_ref ( ) ,
561- fetch_ticket,
562- ) {
563- ( Some ( cache_context) , Some ( fetch_ticket) ) => cache_context
564- . publish_if_newest_accepted ( fetch_ticket, & managed_client. server_info , tools) ?,
565- ( None , None ) => tools,
566- _ => unreachable ! ( "Codex Apps fetch ticket requires cache context" ) ,
567- } ;
647+ let tools = match (
648+ managed_client. codex_apps_tools_cache_context . as_ref ( ) ,
649+ fetch_ticket,
650+ ) {
651+ ( Some ( cache_context) , Some ( fetch_ticket) ) => cache_context
652+ . publish_runtime_if_newest_accepted (
653+ fetch_ticket,
654+ & managed_client. server_info ,
655+ tools,
656+ )
657+ . map ( |snapshot| snapshot. tools ( ) . to_vec ( ) )
658+ . context ( "connector runtime context changed while publishing refresh" ) ?,
659+ ( None , None ) => tools,
660+ _ => unreachable ! ( "Codex Apps fetch ticket requires cache context" ) ,
661+ } ;
568662 emit_duration (
569663 MCP_TOOLS_LIST_DURATION_METRIC ,
570664 list_start. elapsed ( ) ,
0 commit comments