fix(server): MCP app cards work again after a restart unloads their Codex thread#29497

closed6
main
fix/issue-29433
opened8d agobyJJulius Marminge
View on GitHubTwo-way sync enabledPR last imported 15h ago

Problem

After T3's server restarts, an MCP app card in an older Codex thread stops working once another Codex thread has brought the shared Codex app-server back: clicking it shows "Not saved: The app's MCP server request failed." The restarted app-server has not loaded that conversation, so mcpServerStatus/list and mcpServer/tool/call fail with -32600 thread not found.

Reproduced on main (edc37c96c8) in the web dev server with the reporter's sample rating server and Codex 0.163.0: show the card in thread A and save a rating, restart the server, run a turn in thread B, reopen A and click a rating.

Change

ProviderSessionManager now wraps the session runtime's mcpApps the way it already wraps resumeThread. Before an MCP app call, the wrapper does three things:

  • It attaches the card's thread, which issues its T3 MCP credential.
  • It holds the thread loaded and resumes it if this session hasn't loaded that provider thread. This uses the same load path the turn path uses, now factored into one loadThread.
  • After the call, it schedules the thread's idle unload again.

No turn is started.

The load key is split into provider thread and settings:

  • A card call reuses any existing load of the thread, so a thread loaded by a turn isn't resumed again on every click.
  • A load made by a card records no model or policy, so the next turn still resumes with its own.

Resuming through the manager rather than the adapter matters for the thread's next turn. The resume that loads the thread carries T3's MCP config, and Codex ignores config overrides when it resumes a thread that is already loaded.

Loading a thread for a card issues its MCP credential. So McpAppRequests reports a stopped session in three cases rather than loading the thread:

  • the thread is archived;
  • the thread is deleted;
  • the card's provider thread is no longer the thread's active one, for example after a provider switch.

Scope and approval

Fixes pingdotgg/t3code#29433

Triaged via the automated issue flow.

Verification

  • Session manager: ProviderSessionManager.test.ts gets two new tests.

    • "loads an unloaded provider thread with its MCP session before an MCP app call" fails on main: no resume native-app mcp=true happens before listTools. It passes here, and also checks that an idle unload follows.
    • "MCP app calls reuse any load of the thread without hiding a turn's resume" covers the load-key rule in both directions.
  • App requests: McpAppRequests.test.ts covers archived, deleted and provider-switched threads. Each reports session-stopped without calling the provider.

  • Suites: McpAppRequests.test.ts, ProviderSessionManager.test.ts and CodexAdapterV2.test.ts pass, 190 tests. vp run check:changed passes.

  • Real app: the web dev server with a fresh database, Codex 0.163.0, and the reporter's sample server. Runtime transcript of the Codex app-server requests:

    Before, on main edc37c96c8, the card shows "Not saved: The app's MCP server request failed." and the saved rating is unchanged:

    1mcpServerStatus/list Failure CodexAppServerRequestError: thread not found: <native-thread-id>

    After, on the session-manager fix (880157ce3f; the later commits only add the guards above), the card shows "Saved" and the state file changes:

    1thread/resume Success (thread B's turn)
    2turn/start Success
    3thread/resume Success (card thread A, loaded by the session manager)
    4mcpServerStatus/list Success
    5mcpServer/tool/call Success
    6thread/resume Success (next turn in A)
    7turn/start Success

    That next turn in A called t3_thread_list successfully, so T3's MCP tools stay available after a card-only load.

  • Unchanged by design: when no Codex session is live after a restart, the card still reports "The app's thread is not running…".

  • Not checked: macOS and Windows desktop, mobile clients, and Codex versions other than 0.163.0. The fix is server-only, and every client uses the same RPCs.

Model and harness: Claude Opus 5.5 through Claude Code (T3 Code), with Codex gpt-6.1-sol reviewers.

🤖 Generated with Claude Code

Checks

22 successful · 18 skipped
All checks passed
0bc8ce1

Added 6 commits

  • Merge remote-tracking branch 'origin/main' into fix/issue-294330bc8ce1
  • fix(server): archived threads' MCP app cards stay stopped12b9145
  • fix(server): retired threads' MCP app cards stay stopped267c91c
  • test(server): reproduce #29433 MCP app calls on an unloaded shared-session thread6374320
  • fix(server): MCP app cards load their Codex thread after a restart880157c
  • test(server): build the archived timestamp with DateTimee251f6f
Synced from GitHub

Approvability

Verdict: Approved at 0bc8ce1

Macroscope's review found this PR approvable — MCP app cards reload their existing provider thread before app operations, restoring functionality after unloads. Archive/delete/provider-switch guards and existing tool restrictions bound the change without altering product defaults.

You can add or adjust custom eligibility rules. Learn more.

Synced from GitHub

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ℹ️ The exact PR base did not have a successful artifact. Baseline uses the latest successful main measurement shown below.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire5.0 KiB5.0 KiB+6 B (+0.1%)6.8 KiB✅
CodexThread snapshot wire3.8 KiB3.8 KiB0 B (0.0%)4.9 KiB✅
CodexLive turn WebSocket wire1.2 KiB1.3 KiB+6 B (+0.5%)2.0 KiB✅
CodexLive turn WebSocket decoded21.6 KiB21.6 KiB0 B (0.0%)24.4 KiB✅
CodexLive turn messages220 (0.0%)8✅
ClaudeTotal thread wire5.0 KiB5.1 KiB+6 B (+0.1%)6.8 KiB✅
ClaudeThread snapshot wire3.8 KiB3.8 KiB0 B (0.0%)4.9 KiB✅
ClaudeLive turn WebSocket wire1.3 KiB1.3 KiB+6 B (+0.5%)2.0 KiB✅
ClaudeLive turn WebSocket decoded21.9 KiB21.9 KiB0 B (0.0%)24.4 KiB✅
ClaudeLive turn messages220 (0.0%)8✅

Baseline: 8657eec · PR result: 0bc8ce1 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 108.5 KiB
  • Claude decoded thread snapshot: 108.8 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

Synced from GitHub

Review in Change Stack →

No actionable comments were generated in the recent review.

External image Recent review info
External image Run configuration
External image Commits

Reviewing files that changed from the base of the PR and between 0847a448fdca5a688720050ca706cbbd37e6347e and 0bc8ce1463f53a4137e0af023eb5f92c148bde73.

External image Files selected for processing: 4
  • apps/server/src/mcpApps/McpAppRequests.test.ts
  • apps/server/src/mcpApps/McpAppRequests.ts
  • apps/server/src/orchestration-v2/ProviderSessionManager.test.ts
  • apps/server/src/orchestration-v2/ProviderSessionManager.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 2 remain after this review.

Walkthrough

MCP app request resolution now stops requests for archived, deleted, or no-longer-active provider threads. Provider-session management loads threads for MCP app operations, tracks load state by runtime identity and configuration, and reuses or resumes threads according to the operation. Tests cover retired threads and shared-session load behavior.

Changes: MCP thread recovery

Layer / File(s)Summary
Reject retired MCP threads
apps/server/src/mcpApps/McpAppRequests.ts, apps/server/src/mcpApps/McpAppRequests.test.ts
resolve returns session-stopped for archived or deleted threads, threads whose active provider thread differs from the resolved thread, and provider threads without a session ID. Tests check the result for callTool and toolInfo and verify that no tool call occurs.
Load provider threads for MCP operations
apps/server/src/orchestration-v2/ProviderSessionManager.ts, apps/server/src/orchestration-v2/ProviderSessionManager.test.ts
Load state now stores provider-runtime identity and configuration separately. MCP app operations load or reuse provider threads and schedule unloading after the operation. Tests cover MCP session availability, shared sessions, and a subsequent turn resume.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Bug fix · Severity of issue fixed: Medium

Sequence Diagram(s)

mermaid
1sequenceDiagram
2 participant MCPAppOperation
3 participant ProviderSessionManager
4 participant ProviderRuntime
5 MCPAppOperation->>ProviderSessionManager: Run MCP app operation
6 ProviderSessionManager->>ProviderRuntime: resumeThread when thread is not loaded
7 ProviderRuntime-->>ProviderSessionManager: Return provider thread
8 ProviderSessionManager->>ProviderRuntime: Run MCP app operation

Suggested reviewers: maria-rcks, t3dotgg

Merge Risk: External image Minimal · up to 0bc8c

No merge-blocking issue was identified in the MCP thread-recovery changes; proceed with normal checks.

Pre-merge checks: Passed 4
Check nameStatusExplanation
Title checkPassedThe title names the main change: MCP app cards in Codex threads work again after a restart unloads the thread. The changeset adds a thread load before MCP app calls and guards in McpAppRequests that r…
Description checkPassedThe description follows the template. It has Problem, Change, Scope and approval, and Verification sections. The Problem section gives the failure, the reproduction steps, and the error text. The Chan…
Linked Issues checkPassedIssue #29433 requires MCP app discovery and calls to recover after the provider thread is unloaded, without a new chat turn. ProviderSessionManager.ts now wraps mcpApps, attaches the card thread, …
Out of Scope Changes checkPassedThe changes remain within issue #29433. The load-key refactor supports reuse of an already loaded thread and prevents a card-only load from suppressing the next turn resume. The McpAppRequests guard…
Full details: Title check

Explanation

The title names the main change: MCP app cards in Codex threads work again after a restart unloads the thread. The changeset adds a thread load before MCP app calls and guards in McpAppRequests that return session-stopped. The title is specific and follows conventional commit style, so it passes.

Full details: Description check

Explanation

The description follows the template. It has Problem, Change, Scope and approval, and Verification sections. The Problem section gives the failure, the reproduction steps, and the error text. The Change section explains the ProviderSessionManager wrapper, the load-key split, and the three session-stopped guards. Scope links issue #29433 and states it was triaged through an automated flow. Verification lists the new tests, the suites run, the real-app transcript, and the unchecked platforms. The template asks for a maintainer approval comment for scope, and the author gives only an automated triage reference, so that item is weak. The rest of the description is complete, so the check passes.

Full details: Linked Issues check

Explanation

Issue #29433 requires MCP app discovery and calls to recover after the provider thread is unloaded, without a new chat turn. ProviderSessionManager.ts now wraps mcpApps, attaches the card thread, loads it through resumeThread, performs the MCP operation, and schedules idle unload. The shared load path reuses an existing provider-thread load and preserves the next turn's model and policy resume. McpAppRequests.ts returns session-stopped for archived, deleted, and provider-switched threads before it calls the provider. ProviderSessionManager.test.ts verifies load-before-call, MCP-session attachment, idle unload, load reuse, and a later turn resume. The reported tests also cover the stopped-session cases.

Full details: Out of Scope Changes check

Explanation

The changes remain within issue #29433. The load-key refactor supports reuse of an already loaded thread and prevents a card-only load from suppressing the next turn resume. The McpAppRequests guards prevent credential restoration for archived, deleted, or provider-switched threads. The added tests verify these behaviors. No unrelated production behavior or unrelated feature appears in the reviewed changes.

Finishing Touches
Generate docstrings
  • Commit to this branch
  • Create a new PR
Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks @juliusmarminge for the earlier fix and detailed verification. Closing this in favor of #29498 by @amr807, which addresses the same unloaded Codex MCP app failure and has a more recently updated branch. This branch is over a week old and main has changed the affected files since its base.