Skip to content

[Bug]: Preview MCP bearer token expires after 30 minutes idle and active Codex sessions cannot recover #4463

Description

@cyppe

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/server / Codex provider / preview MCP authentication

Summary

A long-lived Codex-backed thread loses access to every t3-code preview tool after the preview MCP credential has been idle for more than 30 minutes. The Codex app-server remains alive and still has T3_MCP_BEARER_TOKEN, but T3's /mcp endpoint returns HTTP 401. Repeating preview_status or preview_open cannot recover the session. Opening a fresh T3 Code thread immediately works because it receives a new credential.

This appears to be an expiry/refresh lifecycle gap rather than target-page, browser, DNS, or TLS failure.

Steps to reproduce

  1. Start a Codex-backed thread in T3 Code Desktop.
  2. Leave the thread's t3-code preview MCP credential unused for more than 30 minutes. The agent and Codex app-server may remain otherwise active.
  3. Call preview_status.
  4. Call preview_open, for example:
{
  "show": true,
  "reuseExistingTab": true,
  "url": "https://example.com"
}
  1. Observe that both calls fail immediately with Auth required.
  2. Open a fresh T3 Code agent thread and repeat the same calls.
  3. Observe that the new thread succeeds.

Expected behavior

An active provider session should retain or transparently refresh its preview MCP authorization. If a credential expires, T3 Code should rotate/reload it or restart/recover the provider session and retry. At minimum, it should surface an actionable credential-expired error.

Actual behavior

Both preview calls failed in about 23 ms:

tool call error: tool call failed for `t3-code/preview_open`

Caused by:
    Transport send error: Transport [rmcp::transport::worker::WorkerTransport<rmcp::transport::streamable_http_client::StreamableHttpClientWorker<codex_rmcp_client::http_client_adapter::StreamableHttpClientAdapter>>] error: Auth required

The local T3 server trace confirms that these were authorization failures at the local MCP endpoint:

2026-07-24T17:23:57.823Z  POST /mcp  401
2026-07-24T17:24:02.179Z  POST /mcp  401

The affected Codex app-server started at approximately 2026-07-24T16:48:04Z, about 35 minutes 53 seconds before the first failure. Its process environment still contained the T3_MCP_BEARER_TOKEN key. The token value was not logged or exposed.

A fresh thread then initialized /mcp successfully with HTTP 200/202 responses and loaded the identical requested page.

Likely root cause

The timing matches the 30-minute idle timeout in McpSessionRegistry.ts:

const DEFAULT_IDLE_TIMEOUT_MS = 30 * 60 * 1_000;
const DEFAULT_MAXIMUM_LIFETIME_MS = 8 * 60 * 60 * 1_000;

pruneExpired() removes a record when timestamp - record.lastUsedAt > idleTimeoutMs. Successful calls update lastUsedAt, but after the record is removed, the live Codex app-server continues presenting its original environment-provided bearer token. I could not find a path that refreshes the live process credential or recovers automatically after the resulting 401.

Suggested implementation directions

The best choice depends on the intended security policy, but possible approaches are:

  1. Keep the MCP credential valid while its provider session is alive, retaining the existing eight-hour maximum lifetime and explicit revocation on provider/thread teardown.
  2. Proactively rotate the credential before idle expiry and reload/restart the provider's MCP configuration.
  3. Detect an MCP authentication failure, recover the provider session with a new credential, and retry once.
  4. Surface a specific message such as Preview credential expired; restart provider session instead of the generic RMCP transport error.

I would avoid solving this only with a periodic keepalive because that effectively bypasses the intended idle policy without fixing credential lifecycle management.

Impact

Major degradation for long-running coding threads. Once expired, all collaborative preview automation becomes unavailable in that thread, and agents may incorrectly investigate the target site or browser instead of the local T3 credential.

Version or commit

T3 Code 0.0.29-nightly.20260724.891

Codex CLI 0.145.0

Environment

Arch Linux x86_64
Linux 7.1.3-arch1-1
Hyprland / Wayland
T3 Code local server: http://127.0.0.1:3773

Workaround

  • Open a fresh agent thread, or fully quit/relaunch T3 Code and resume the affected thread so a new provider credential is issued.
  • Calling a preview tool at least once within each 30-minute window keeps lastUsedAt current, but this is only a workaround.
  • codex login does not help; the rejected credential is T3 Code's session-scoped T3_MCP_BEARER_TOKEN, not the user's OpenAI login.

Additional context

This is separate from #3718. After authentication succeeded in the fresh thread, preview_open(show: true) loaded the page but returned visible: false, matching that existing visibility issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions