From 2d82a0cb07b28275443de1477708ed6a6df8489f Mon Sep 17 00:00:00 2001 From: SaladDay <1203511142@qq.com> Date: Fri, 9 Oct 2026 19:32:35 +0000 Subject: [PATCH] Refresh invalidated Codex MCP clients without reloading configuration --- apps/daemon/internal/agent/codex/mcp_cancellation.go | 3 +-- .../internal/agent/codex/mcp_cancellation_test.go | 10 +++------- .../internal/agent/codex/preparation_helpers_test.go | 5 ----- packages/codex-runtime/README.md | 2 +- packages/codex-runtime/invalidate-mcp.patch | 3 ++- 5 files changed, 7 insertions(+), 16 deletions(-) diff --git a/apps/daemon/internal/agent/codex/mcp_cancellation.go b/apps/daemon/internal/agent/codex/mcp_cancellation.go index 88b0e1305..dffd0f512 100644 --- a/apps/daemon/internal/agent/codex/mcp_cancellation.go +++ b/apps/daemon/internal/agent/codex/mcp_cancellation.go @@ -152,6 +152,5 @@ func (s *Session) cleanupCancelledMCP(ctx context.Context) error { if json.Unmarshal(raw, &receipt) != nil || !slices.Equal(receipt.ServerNames, labels) { return errors.New("codex: native MCP invalidation unconfirmed") } - _, err = request("config/mcpServer/reload", nil) - return err + return nil } diff --git a/apps/daemon/internal/agent/codex/mcp_cancellation_test.go b/apps/daemon/internal/agent/codex/mcp_cancellation_test.go index 307df25b8..ed2665849 100644 --- a/apps/daemon/internal/agent/codex/mcp_cancellation_test.go +++ b/apps/daemon/internal/agent/codex/mcp_cancellation_test.go @@ -107,8 +107,8 @@ func TestMCPChildFirstObservedTerminalUsesNativeRootAttribution(t *testing.T) { } } -func TestExecutorCancelledMCPRequiresScopeCloseThenInvalidationAndReload(t *testing.T) { - for _, mode := range []string{"mcp-confirmed", "mcp-caller-cancelled", "mcp-stop-failed", "mcp-invalidate-unconfirmed", "mcp-reload-failed"} { +func TestExecutorCancelledMCPRequiresScopeCloseThenInvalidation(t *testing.T) { + for _, mode := range []string{"mcp-confirmed", "mcp-caller-cancelled", "mcp-stop-failed", "mcp-invalidate-unconfirmed"} { t.Run(mode, func(t *testing.T) { e, root := executorFixture(t, mode) e.base.cfg.view.MCP = cancellationMCPBindings("active", "late", "healthy") @@ -227,11 +227,7 @@ func TestExecutorCancelledMCPRequiresScopeCloseThenInvalidationAndReload(t *test if mode == "mcp-stop-failed" { wantInvalidate = 0 } - wantReload := 0 - if confirmed || mode == "mcp-reload-failed" { - wantReload = 1 - } - if counts["mcpServer/invalidate"] != wantInvalidate || counts["config/mcpServer/reload"] != wantReload || counts["turn/interrupt"] != 1 || counts["thread/start"] != 1 || counts["initialize"] != 1 || counts["mcpServerStatus/list"] != 0 || len(pids) != 1 { + if counts["mcpServer/invalidate"] != wantInvalidate || counts["config/mcpServer/reload"] != 0 || counts["turn/interrupt"] != 1 || counts["thread/start"] != 1 || counts["initialize"] != 1 || counts["mcpServerStatus/list"] != 0 || len(pids) != 1 { t.Fatal(counts, pids) } }) diff --git a/apps/daemon/internal/agent/codex/preparation_helpers_test.go b/apps/daemon/internal/agent/codex/preparation_helpers_test.go index 9af0e33e1..8eb0de6fa 100644 --- a/apps/daemon/internal/agent/codex/preparation_helpers_test.go +++ b/apps/daemon/internal/agent/codex/preparation_helpers_test.go @@ -213,11 +213,6 @@ func TestPreparationFakeCodexProcess(t *testing.T) { if executorMode == "mcp-invalidate-unconfirmed" { result = McpServerInvalidateResponse{} } - case "config/mcpServer/reload": - if executorMode == "mcp-reload-failed" { - _ = output.Encode(map[string]any{"id": frame.ID, "error": map[string]any{"code": -32603, "message": "reload rejected"}}) - continue - } case "environment/status": if os.Getenv("OAC_TEST_PREPARATION_BLOCK") == "1" { for { diff --git a/packages/codex-runtime/README.md b/packages/codex-runtime/README.md index e44d974eb..c5e298b1c 100644 --- a/packages/codex-runtime/README.md +++ b/packages/codex-runtime/README.md @@ -14,6 +14,6 @@ Every captured thread's current configuration is checked before invalidation sta Success returns `{ "serverNames": [""] }`, sorted and deduplicated, after selected client shutdown completes. Ready clients have entered the native `ClientState::Closed`; pending startup is cancelled. Existing prepared calls retain their original client and cannot redirect to a replacement. This receipt acknowledges local invalidation, not remote tool completion or replacement readiness. -After the host `StopMCP` receipt and native invalidation receipt, the adapter calls maintained `config/mcpServer/reload`. Its dirty state applies through the existing native refresh flow before a subsequent Turn uses MCP. Reconciliation replaces closed clients and reuses unchanged healthy connections. No eager `mcpServerStatus/list` discovery, synthetic model Turn, polling delay or whole app-server restart establishes this fence. +Invalidation marks each captured Session’s MCP runtime dirty while holding its existing refresh semaphore. Before a subsequent Turn uses MCP, the maintained native refresh flow reconciles its owned configuration, replaces closed clients and reuses unchanged healthy connections. The cancellation path does not reload configuration files or eagerly discover replacements; no synthetic model Turn, polling delay or whole app-server restart establishes this fence. The patch's `targeted_invalidation_*` Rust tests exercise closed-client replacement and unaffected connection identity through native reconciliation using controlled in-process transports. The Go adapter tests exercise cancellation latching, late starts, native call identity, host closure ordering, explicit declaration, receipt failures and Executor reuse. Native compilation and focused Rust tests must run in the qualified source build; source applicability alone does not qualify the binary. diff --git a/packages/codex-runtime/invalidate-mcp.patch b/packages/codex-runtime/invalidate-mcp.patch index c619a816a..71c80a65e 100644 --- a/packages/codex-runtime/invalidate-mcp.patch +++ b/packages/codex-runtime/invalidate-mcp.patch @@ -366,7 +366,7 @@ diff --git a/codex-rs/core/src/session/mcp.rs b/codex-rs/core/src/session/mcp.rs index 46af2d3..9753960 100644 --- a/codex-rs/core/src/session/mcp.rs +++ b/codex-rs/core/src/session/mcp.rs -@@ -89,6 +89,29 @@ impl ElicitationReviewer for GuardianMcpElicitationReviewer { +@@ -89,6 +89,30 @@ impl ElicitationReviewer for GuardianMcpElicitationReviewer { } impl Session { @@ -388,6 +388,7 @@ index 46af2d3..9753960 100644 + .mcp_runtime + .invalidate_servers(&names) + .await; ++ session.mark_mcp_runtime_dirty(); + Ok::<_, anyhow::Error>(()) + }) + .await?