## What changed
- Upgrade `rmcp` from 3.0.0 to 3.1.2.
- Use `rmcp`'s native JSON-RPC decoding and remove the local compatibility layer for multi-round-trip tool results.
- Preserve response metadata on `input_required` SSE results.
- Support OAuth protected-resource metadata discovery and include the discovered resource audience when refreshing tokens.
## Testing
- Add coverage for metadata preservation on modern SSE `input_required` responses.
- Add an OAuth startup scenario that discovers protected-resource metadata and verifies the refresh-token resource audience.
GitOrigin-RevId: 689f009b4a33552b640304111b74585b4009eeb8
## Why
MCP requests can contain sensitive headers and tool-call bodies. Following a
cross-origin redirect could disclose them to another server.
## What changed
- Follow redirects only when every hop remains on the configured server's
origin, and require HTTPS for redirects on non-loopback hostnames.
- Preserve standard redirect method and body handling while sharing the
original timeout and enforcing a 10-hop limit.
- Avoid replaying plaintext proxy credentials across redirects, while leaving
OAuth requests that stop at redirects unchanged.
## Testing
Add coverage for same-origin and cross-origin redirects, redirect methods,
headers and bodies, proxy credentials, timeouts, hop limits, and legacy MCP
tool calls.
GitOrigin-RevId: 3e267911a1665ad8c2fc63825099a758770f4d68
## Why
Headers configured for an MCP resource can contain credentials and must not be
forwarded to a different origin during OAuth discovery, registration, or token
exchange.
## What changed
- Apply configured resource headers only to OAuth requests on the MCP resource
origin, while allowing OAuth-generated headers to take precedence.
- Follow credentialed OAuth redirects only when each destination remains on the
resource origin. Preserve redirect method semantics, the overall timeout, the
response size limit, and a maximum of 10 redirects.
- Use the Codex MCP user agent for requests to authorization-server origins.
## Testing
Add coverage for cross-origin header isolation, same-origin dynamic client
registration redirects, OAuth header precedence, token refresh, timeout
accounting, and response size limits.
GitOrigin-RevId: 609d06e2b065e4aa9ca5382b4ae60455f9e26db9
## What changed
- Preserve valid image content from completed `node_repl` Code Mode responses alongside text evidence for Guardian reviews.
- Enable multimodal evidence for models that require automatic `node_repl` review, or when both `guardian_enhanced_node_repl_transcripts` and `guardian_node_repl_transcript_images` are enabled.
- Bound retained and rendered evidence, deduplicate images already present in review history, and fall back to text-only evidence when the reviewer lacks image support or prompt capacity.
- Keep review evidence out of the parent turn input.
## Testing
- Cover feature gating, mixed text and image ordering, truncation and eviction, unsupported reviewers, prompt pressure, image detail normalization, and reused review sessions.
GitOrigin-RevId: a3e482402f7b3484401b231dadc662e6e3504fdf
## What changed
- Capture accepted, successful `node_repl` results from Code Mode and include them as bounded, untrusted evidence in Guardian review prompts.
- Enable the enhanced transcript through `guardian_enhanced_node_repl_transcripts` or when the selected model requires automatic Node REPL review.
- Keep this evidence out of the parent model history, exclude encrypted and failed results, and avoid resending admitted evidence when a Guardian session is reused.
- Clear retained evidence and invalidate review sessions when a thread is rolled back.
## Testing
- Add unit coverage for evidence ordering, escaping, truncation, and empty responses.
- Add an integration test covering feature-disabled, feature-enabled, and model-required behavior, including filtering and review-session reuse.
GitOrigin-RevId: edc3dca5d5b88d472a492f35531ec46889a89d72
## What changed
- Add `http_headers_helper` configuration for local streamable HTTP MCP servers. The configured shell command runs once per connection and returns a JSON object of headers that is cached across requests.
- Apply helper headers to MCP startup and OAuth flows while restricting them to the server origin, stopping redirects, rejecting reserved or duplicate headers, and enforcing output and execution limits.
- Reject helpers for remote or managed-disabled servers, use the local environment working directory, and redact helper commands from `codex mcp list` and `codex mcp get` output.
## Testing
- Cover configuration validation, helper lifecycle and output parsing, origin isolation, OAuth discovery and token refresh, managed requirements, environment selection, and CLI redaction.
GitOrigin-RevId: 84e0e26ce75520b0869d37c72b1678e033bd6818
## What changed
- Make automatic MCP OAuth registration prefer Client ID Metadata Documents
(CIMD) when the authorization server advertises support for public clients and
Codex is using its native loopback callback. Fall back to advertised Dynamic
Client Registration (DCR) otherwise.
- Add explicit `cimd` and `dcr` registration overrides to the CLI and app-server
OAuth login API. Validate CIMD metadata and callback URLs before starting the
authorization flow.
- Use a callback-specific Codex client metadata URL for CIMD and retain the
exact redirect URI through authorization and token exchange.
## Testing
- Cover automatic and forced CIMD selection, DCR fallback, invalid metadata and
redirects, token refresh, authenticated MCP requests, and conformance
regression checks.
GitOrigin-RevId: 4238372ca53b0f38e781e141ab5da97e0a6ddf45
## What changed
- Add `--oauth-client-registration` with `auto` and `dcr` choices to `codex mcp add` and `codex mcp login`.
- Add the equivalent optional `clientRegistration` field to `mcpServer/oauth/login` and the generated protocol schemas.
- Keep automatic dynamic client registration as the default, prefer a configured OAuth client ID when present, and apply the selection only to the current login without persisting it in MCP configuration.
## Testing
- Cover dynamic registration and configured-client OAuth flows through the app server.
- Verify the CLI does not persist the per-login registration choice.
GitOrigin-RevId: 653a4a1054ff43c2c5d52ae863b6a96d21692a53
## Why
Local stdio MCP servers may start in a different working directory from Codex,
so inherited relative CA bundle paths can otherwise resolve to the wrong file.
## What changed
- Forward the supported custom CA environment variables to local stdio MCP
servers, convert inherited non-empty paths to absolute paths, and let explicit
MCP environment overrides take precedence.
- Match CA variable names and `PATH` case-insensitively where required, avoiding
duplicate environment entries and preserving executable discovery on Windows.
## Testing
- Add an end-to-end stdio MCP test covering relative inherited CA paths, empty
values, explicit overrides, alternate key casing, and a different server
working directory.
- Exercise executable resolution with Windows-style `Path` casing.
GitOrigin-RevId: ea1bc0ae63bc0dc780acb8363b2ed102120f282e
## What changed
- Cover non-blocking credential probes when the file or secrets store is locked, including retaining only a matching prior credential snapshot and recovering after the lock is released.
- Extend the streamable HTTP OAuth round trip to verify that user turns continue during store contention, newly discovered servers recover after contention, and logged-out servers do not reuse authenticated connections.
- Cover reconciliation of authentication failures and effective `Authorization` headers.
GitOrigin-RevId: 8e377644001bd0be04dc9f79841080094d09d199
## Why
Refreshing MCP connection identities should not stall the async executor while another process holds the OAuth credential-store lock.
## What changed
- Probe file and secrets credential stores without waiting during runtime refreshes, retaining the last known credentials when the store is contended.
- Reuse unchanged authentication failures while credentials are temporarily unavailable instead of restarting the connection.
- Run blocking credential loads on a blocking worker during MCP client construction.
- Allow OAuth fallback when a configured authorization header is missing, empty, or invalid.
GitOrigin-RevId: 38dad5606da9a1ceb30fe945000ec9151a5058fa
## Why
Concurrent MCP startup and status checks should not serialize when they only read the shared credential store. Repeated reads of the encrypted MCP OAuth store also needlessly decrypted unchanged contents.
## What changed
- Use shared locks for `File` and `Secrets` credential reads while keeping saves and deletes exclusive.
- Cache decrypted MCP OAuth secrets by store path, ciphertext, and passphrase, and invalidate the cache after writes.
## Testing
- Cover concurrent readers, reader/writer exclusion, shared credential loads, and cache invalidation after updates and deletes.
GitOrigin-RevId: f13512e6404d4919879ba5ba77a3e34e52b35640
## Why
Model-reachable child processes should not inherit Codex launch context.
## What changed
- Treat `OPENAI_FEDERATION_RULE_ID` and `OPENAI_IDENTITY_TOKEN_FILE` as non-inheritable environment variables, with case-insensitive matching.
- Remove them after shell environment policy overrides and before spawning commands across execution, MCP, hooks, Git helpers, and remote helper processes.
## Testing
- Cover inherited and explicitly configured variants, including mixed-case names.
- Verify the variables are absent from real child environments and app-server command and process execution.
GitOrigin-RevId: 2535527893985fef0995617f4c5b2462bea7c136
## What changed
- Expose hosted Plugin Runtime event definitions through `McpResourceClient::list_events`.
- Add cancellable `events/stream` subscriptions that route lifecycle notifications to the matching request and cancel when the stream is dropped.
- Bound event notification and queue sizes, time out stalled response headers, and close streamable HTTP responses locally on cancellation.
## Testing
- Add streamable HTTP integration coverage for isolated concurrent subscriptions, local cancellation, and stalled response-header timeouts.
GitOrigin-RevId: 98c6fffa8f004eb2638c1b0705f659222cbbdfdb
## Why
A local stdio MCP server can exit while its descendants remain alive. Cleaning up
only the server process can therefore leak processes after client shutdown.
## What changed
- Contain locally launched MCP servers in non-breakaway Windows job objects and
terminate the job during shutdown so descendants exit with the server.
- Preserve compatibility when job creation or assignment is unavailable by
retrying without containment and terminating an owned process handle instead.
- Use the process-group member fallback when terminating piped processes on
macOS.
## Testing
- Cover descendant cleanup and breakaway prevention for both local MCP protocol
modes on Windows.
- Cover the Windows nested-job assignment fallback and verify the suspended
process resumes before handle-based termination.
GitOrigin-RevId: 33d3cc122de8f7ce0ae69a649b12ea0ff64b7c6f
## Why
An OAuth-backed Streamable HTTP MCP server can fail startup when its stored credentials are rejected. If a client then completes OAuth sign-in and replaces those credentials, the failed server needs to become available without restarting the Codex session.
## What changed
- Track the credential store and OAuth tokens used for each MCP connection, and detect replacements after authentication failures.
- Refresh affected MCP servers before the next turn and include their recovered tools in the model request.
- Classify HTTP authentication challenges and rejected refresh tokens as requiring reauthentication, with sign-in guidance appropriate to local and client-managed environments.
## Testing
- Cover recovery from expired credentials through an externally written replacement token.
- Cover authentication-error classification, startup messaging, and `Auto` store migration from file credentials to the keyring.
GitOrigin-RevId: 9b090089d4e7a44ffc182f4e25da20a18e97b70f
## What changed
Extend the executor-backed Streamable HTTP timeout test to cover both legacy
`initialize` and MCP 2026 `server/discover` requests. Verify that each stalled
handshake times out and releases the serial executor for subsequent requests.
GitOrigin-RevId: 3493904433c95265b9f645813723e890b7610abb
## Why
A streamable HTTP MCP handshake could time out while its executor-backed HTTP
request continued running, leaving the serial executor blocked for later
requests.
## What changed
- Track the remaining initialization deadline for streamable HTTP transports.
- Apply it to `initialize`, `notifications/initialized`, and discovery HTTP
requests, then clear it when the handshake finishes.
## Testing
- Added a regression test that stalls the remote MCP endpoint, waits for the
handshake timeout, and verifies that a subsequent executor request completes.
GitOrigin-RevId: d4980a332e898309c885cbaa4fdd598924b440e1
## Why
Signaling an MCP server's process group can fail with `EPERM` on macOS,
preventing owned descendants from being terminated.
## What changed
- When a group signal is denied, enumerate the group's processes and signal
members that still belong to the expected process group.
- Apply the fallback to both `SIGTERM` and `SIGKILL` during MCP stdio server
cleanup, while retaining the existing group signaling behavior elsewhere.
- Reject invalid process group IDs and avoid signaling processes that have
moved to another group.
## Testing
Added macOS tests covering cleanup with a live or exited group leader,
`SIGTERM`-resistant descendants, escalation to `SIGKILL`, and unsafe group IDs.
GitOrigin-RevId: db722475b94dece74244ce8109b3e92f6cfb202a
## What changed
- Track Agent Plugin manifests through plugin, skill, and MCP loading so their capabilities use format-specific behavior without changing legacy plugins.
- Discover only direct-child skills, exclude app and hook capabilities, isolate MCP data, and reject MCP configuration files that are non-regular or resolve outside the plugin root.
- Bound model-visible skill instructions, plugin instructions, MCP descriptions, schemas, individual tools, and the aggregate Agent Plugin MCP tool set.
- Stop MCP and OAuth redirects when Agent Plugins send configured or authorization headers, while retaining existing redirect behavior for legacy MCP servers.
## Testing
- Add coverage for capability filtering, skill discovery, isolated MCP data and reserved-path expansion, unsafe MCP configuration files, context limits, and redirect handling.
GitOrigin-RevId: c9af66b051269f3226628ca280a58d32c808c38f
## What changed
- Add `parse_agent_plugin_mcp_config` to translate Agent Plugins v1 `mcp.json` files into Codex MCP server configuration.
- Normalize `stdio` and streamable HTTP transports, including `PLUGIN_ROOT` and `PLUGIN_DATA` expansion, contained plugin paths, secure endpoint validation, and filtering of client-owned HTTP headers.
- Keep valid sibling servers when another server is invalid, while returning per-server parse errors.
- Preserve UTF-8 HTTP header values when forwarding streamable HTTP protocol headers.
## Testing
- Cover transport mapping, placeholder expansion, path containment, schema and field validation, per-server errors, platform-specific environment handling, and UTF-8 headers.
GitOrigin-RevId: ed4ab0fcf495afbb381ce48beb93989629444c56
## Why
MCP servers running in an executor must not reuse host-owned OAuth credentials,
even when they have the same configured server name and URL.
## What changed
- Derive environment-scoped credential names for executor-owned MCP servers and
use them consistently for login, logout, authentication, and connection setup.
- Mark executor-owned entries in the file credential store and fail closed when
loading or saving an entry could cross the host/executor boundary.
- Preserve compatibility with existing local OAuth credentials, including local
server names that overlap the new reserved prefixes.
## Testing
- Extend the executor MCP integration test to verify that executor requests use
the executor token, never send the host token, and persist both credentials
separately.
GitOrigin-RevId: 4fc92d0533b0fe2e0df34f6d47b81e3d20d07807
## Why
OAuth discovery failures do not establish that an MCP server lacks OAuth
support. Reporting those failures as `unsupported` conflates an inconclusive
check with a confirmed result.
## What changed
- Add an `unknown` MCP authentication status across the protocol, app server,
CLI, and TUI.
- Preserve OAuth discovery errors so callers can report `unknown`, while
retaining `unsupported` for servers known not to support OAuth.
- Document the distinction in the app server API.
## Testing
- Verify transient HTTP discovery errors are preserved.
- Verify `codex mcp list --json` reports `unknown` when discovery is rate
limited.
GitOrigin-RevId: e4562985971606740538e542ec7eeee502111964
## Why
MCP catalog discovery must not allow a server to keep pagination running or
growing without bounds.
## What changed
- Apply the shared pagination collector to tool, resource, and resource-template
discovery.
- Limit each catalog to 100 pages and 1,024 items, and reject pagination cursors
larger than 64 KiB or any repeated cursor.
- Bound the entire pagination operation by the configured tool timeout, falling
back to 30 seconds when no timeout is configured.
## Testing
Add unit coverage for every limit and an end-to-end MCP test that preserves
valid multi-page tools while excluding a server that returns an oversized
cursor.
GitOrigin-RevId: 961a1bfa5fe8a265daf56e342a919c980cd720fa
## What changed
- Update the Rust MCP SDK from `3.0.0-beta.3` to `3.0.0` and adapt to its renamed metadata and server discovery types.
- Accept discovery responses without server identity metadata, using the configured server name as a fallback, and rely on the SDK's native support for namespaced server identity metadata.
- Preserve typed OAuth HTTP errors so transport failures, cross-origin redirects, and transient HTTP responses are reported instead of being treated as anonymous access.
## Testing
- Cover modern discovery with namespaced or missing server identity over HTTP, SSE, and stdio.
- Cover OAuth discovery error propagation for transport failures, redirects, and transient status codes.
GitOrigin-RevId: 12c1e45136cca89ce4fb15986c2b5df14608682a
## What changed
- Limit model-facing MCP namespace descriptions to 1,000 bytes without splitting UTF-8 characters or mutating the stored server metadata.
- Cap the aggregate source-description list in `tool_search` at 4 KiB while retaining source names and discovery instructions.
- Cover direct and deferred MCP tool exposure, including multibyte descriptions and tool visibility.
GitOrigin-RevId: 92736ec39475a1d6b3b03f5e3063e28225d8a5f6
## Why
Some legacy MCP servers reject `server/discover` before creating a session and
return a null-ID JSON-RPC error with a non-JSON content type or without echoing
the rejected protocol version. These responses prevented the client from
falling back to legacy initialization.
## What changed
- Parse HTTP 400 discovery errors without a session ID regardless of their
declared content type.
- Fall back to `initialize` when error code `-32000` reports a missing session
or advertises only known legacy protocol versions, including supported
versions other than `2025-06-18`.
- Continue rejecting unrelated error codes, correlated responses, non-400
statuses, malformed version lists, and lists containing modern or unknown
versions.
## Testing
Added MCP discovery coverage for legacy error variants, server-selected legacy
protocol versions, non-JSON content types, and non-legacy rejection cases.
GitOrigin-RevId: 4d3ee2d7d852ce0d0ab3f486da23cb653a7ff5ca
## What changed
- Require callers to provide an HTTP client for MCP OAuth discovery and login, removing the separate direct `reqwest` path.
- Use the shared `http` and `url` types throughout the MCP client and drop its direct `reqwest` dependency.
- Preserve configured MCP headers when routing OAuth discovery through the provided client.
## Testing
- Add coverage that routed OAuth discovery forwards configured headers.
GitOrigin-RevId: 1345f56bd794626533133447597a5908e57b9b48
## What changed
- Drive multi-round `tools/call` and `resources/read` requests through
`input_required` responses, preserving opaque request state and elicitation
metadata across JSON, SSE, and stdio transports.
- Decode modern discovery and elicitation result shapes while retaining legacy
initialization, elicitation defaults, and discovery fallback behavior.
- Apply the 8 MiB modern-protocol response limit to JSON, SSE events, and stdio
messages, and reject executor stream recovery when output sequence gaps would
corrupt the protocol stream.
## Testing
Added integration coverage for discovery, multi-round requests, legacy
fallbacks, message limits, stdio launchers, SSE responses, session recovery,
and executor output recovery.
GitOrigin-RevId: d26a08883a2825e8ce2408ac498114b33859225e
## What changed
- Add an opt-in `mcp_2026_07_28` protocol mode while preserving the legacy
lifecycle by default.
- Negotiate the new protocol over streamable HTTP with `server/discover`,
including bounded responses, redirect protection, and fallback only when a
response establishes that the endpoint is legacy-only.
- Require stdio servers to opt in with
`CODEX_MCP_PROTOCOL_VERSION=2026-07-28`, and add a bounded local stdio
transport for the modern lifecycle.
- Consume paginated tool, resource, and resource-template catalogs in modern
mode, reject repeated cursors, and retain discovered server identity.
- Reconnect reusable MCP clients when their selected protocol mode changes.
## Testing
- Cover HTTP JSON and SSE discovery, legacy fallback and rejection cases,
redirects, retries, response limits, and pagination.
- Cover local and executor stdio discovery, protocol markers, message limits,
and legacy compatibility.
GitOrigin-RevId: f6a78816e127d2a482292d63b91c8384f1595903
## What changed
- Update the `rmcp` model and transport integrations for `3.0.0-beta.3`.
- Preserve legacy `elicitation/create` form requests, schema defaults, metadata, and wire-format compatibility while adopting the new elicitation types.
- Harden OAuth discovery by using GET-first discovery without starting an MCP session, preventing cross-origin header redirects, validating authorization-server and callback issuers, and retaining compatibility with metadata that omits an issuer.
- Keep stored OAuth credentials readable and avoid requiring reauthorization after transient refresh failures.
## Testing
- Add coverage for legacy elicitation round trips, OAuth discovery variants, callback issuer validation, credential migration, and refresh failures.
GitOrigin-RevId: 756197d26f3fd347c28c400228f6b3d06ed493b7
## Why
MCP authentication checks need to use the same HTTP routing as the MCP
transport so servers reached through configured proxies can be discovered
reliably.
## What changed
- Resolve OAuth discovery and authentication status through each server's
runtime HTTP client for both local and managed environments.
- Keep local discovery capped at five seconds while allowing explicit login
requests to retain their requested timeout.
- Resolve refreshed MCP configuration and its runtime context from the same
snapshot.
## Testing
- Cover OAuth discovery through an environment proxy and macOS system proxy
resolution.
- Cover proxied MCP startup and runtime refresh with updated authorization
headers.
- Verify capped and preserved OAuth discovery timeout policies.
GitOrigin-RevId: 461fb1d4786e547df8b1e6b2215a8ac40438a3aa
## What changed
- Rename `ReqwestHttpClient` to `RouteAwareHttpClient` to reflect that delegated HTTP uses Codex's shared route-aware transport.
- Use `codex_http_client` response and error types plus transport-neutral `http` and `url` types, removing the exec server's direct `reqwest` dependency.
## Testing
- Cover fragment stripping and Unicode hostname normalization for delegated HTTP requests.
GitOrigin-RevId: 8b0fc60a76004feb57198bfb4afb1371c9ceb1bd
## Why
Remote environment connections need to honor Codex's effective outbound proxy policy, including when a rendezvous connection reconnects.
## What changed
- Pass the configured `HttpClientFactory` into remote environment transports and use `WebSocketConnector` for exec-server and rendezvous WebSockets.
- Resolve proxy routes asynchronously so these connections can use the configured system proxy.
- Add connector options that preserve Tungstenite's default TLS behavior and enable `TCP_NODELAY` for latency-sensitive rendezvous traffic.
## Testing
- Verify prepared remote environments connect through a configured system proxy.
- Verify initial and reconnected encrypted relay peers use the system proxy.
- Cover default TLS selection and opt-in `TCP_NODELAY` behavior in the WebSocket client.
GitOrigin-RevId: 8a8da2116e37cb3a891269d0c0b037986fecdd3c
## Why
Delegated HTTP requests need to honor the same outbound proxy policy as the
Codex process that starts the exec server.
## What changed
- Pass the configured `HttpClientFactory` through local and remote exec-server
startup and use route-aware client pools for delegated HTTP and local MCP
requests.
- Preserve per-request timeouts and follow-or-stop redirect behavior while
keeping request URLs and sensitive response headers out of diagnostics.
## Testing
- Cover configured system-proxy routing across the exec-server transport.
- Cover both redirect policies and verify that success and failure logs do not
expose request or response secrets.
GitOrigin-RevId: 4af6aec1d265c4db62dfcb6e1fb076fb31736137
## Why
MCP connection reconciliation can otherwise reuse a client after its service or
underlying transport has stopped.
## What changed
- Add `RmcpClient::is_closed` to report closed client, service, and transport
states.
- Exclude closed clients when selecting a reusable MCP connection so
reconciliation starts a replacement.
## Testing
- Add a reconciliation test that disconnects an in-process MCP transport and
verifies that its connection is replaced.
GitOrigin-RevId: 366a85c9959dbe1763923d7e33a4ecf52d9c3c6a
## Why
Refreshing MCP runtime state should not restart an unchanged, ready server or
relist its tools.
## What changed
- Reconcile refreshed MCP configuration against the published connection set
and reuse connections whose transport, environment, authentication, OAuth
credentials, and client capabilities are unchanged.
- Keep tool filters, timeouts, metadata, and plugin provenance in the published
server view so those settings can change without reconnecting.
- Update elicitation authority in place for reused connections, and reconnect
when connection-defining inputs or live OAuth credentials change.
## Testing
Added coverage for unchanged-server reuse, view-only and elicitation-policy
updates, connection identity changes, OAuth credential changes, and avoiding
redundant tool listing.
GitOrigin-RevId: f04f5db5fbdde127b6a14f6aa9673112c0b557e7
## What changed
- Send `codex-mcp-client/<version>` as the default user agent for streamable HTTP and OAuth requests.
- Preserve user agents supplied through configured HTTP headers.
## Testing
- Verify the default user agent on OAuth discovery, token refresh, and MCP initialization requests.
- Verify that a configured user agent overrides the default.
GitOrigin-RevId: 659ef8f126df97b3c1b4d01e9e542a673b5ef42b
## Why
Remote executions need managed-network proxy listeners in the executor so their
loopback proxy addresses are reachable by the launched process.
## What changed
- Add a capability-gated exec-server protocol field for executor-local proxy
launch configuration, including network policy, audit metadata, and execution
attribution.
- Start the proxy while preparing a remote process, replace inherited proxy
environment variables with its local addresses, and derive the sandbox
context from its listeners.
- Keep the proxy alive until inherited output streams close, then shut it down.
- Reject unsupported remote settings such as MITM and credential injection.
## Testing
- Cover configuration round trips and rejection of unsupported settings.
- Verify executor-local startup, blocked-domain enforcement, protocol
compatibility, and proxy lifetime through process closure.
GitOrigin-RevId: c984f54e3e600aa9ebcbf8cf4574046e2c199d11
## What changed
- Convert MCP text content marked with `_meta["codex/encryptedContent"] = true`
into `encrypted_content` function call output items.
- Prefer the content-item payload whenever encrypted content is present so that
`structured_content` does not replace the encrypted output.
## Testing
- Add a stdio MCP integration test that round-trips mixed plaintext and
encrypted tool output.
GitOrigin-RevId: c3997a16b06ad38ff22b21016ede35b97974d9ec
## Why
Starting a new session should not have to wait for an unchanged stdio MCP
server to initialize before its tools can be presented to the model.
## What changed
- Cache recent tool catalogs for matching stdio MCP server configurations and
use them while a new live connection starts in the background.
- Bound the process-wide cache to 32 entries with a 30-minute TTL, and key
entries by the server configuration, environment, working directory, and
elicitation capabilities that affect the catalog.
- Keep connection-specific instructions and tool annotations out of cached
catalogs. Resolve tool calls against the live connection so tools that are
unavailable or hidden in the new session cannot be invoked from stale data.
- Publish only the newest completed catalog fetch and do not cache HTTP servers
or stdio configurations that depend on remotely sourced environment values.
## Testing
- Add coverage for cache expiration, environment changes, catalog sanitization,
and out-of-order refreshes.
- Add an end-to-end test that starts inference from cached definitions while a
replacement MCP process initializes, then verifies calls and visibility
against the live process.
GitOrigin-RevId: 2ed0f29ba53ace12532730f951b175c588aa3ae7
## What changed
- Guard executor-backed MCP stdio sends with a single-permit semaphore so a
second JSON-RPC message cannot start writing while the first write is pending.
- Add a regression test that drives two concurrent sends and verifies the
process receives each newline-delimited message in order without overlap.
GitOrigin-RevId: 30c2e51827ecf57b5eb23d9ad1e876734d6e784e
## Why
OAuth metadata discovery for an optional HTTP MCP server can stall, delaying
`thread/start` even though that server is not required.
## What changed
- Remove eager MCP authentication-status discovery from session and connection
manager startup.
- When an MCP connection reports an authentication error, inspect configured and
stored credentials locally to preserve login and reauthentication failure
reasons without making another discovery request.
- Continue using server configuration directly for startup error messages and
timeout hints.
## Testing
Add an app-server regression test with an optional HTTP MCP endpoint that accepts
a connection but never responds, and verify that `thread/start` completes without
waiting for its configured startup timeout.
GitOrigin-RevId: 56fe0188cc1ceaf746b0b65e8b7dd1fd2e13420d
## What changed
- Extend the lock-contention test helper with a callback that runs while the
aggregate store lock is held.
- Use a scoped worker thread and return its result directly after releasing the
lock.
- Reuse the helper for concurrent update tests covering both file and secrets
credential stores.
GitOrigin-RevId: 74b8b5370456e90ecd39fc2af2f0853594ca4f60
## What changed
- Apply each server's `startup_timeout_sec` (or the default) while creating the
MCP client, so the deadline also covers transport setup.
- Launch local stdio servers on a blocking task so synchronous command
resolution and process creation do not prevent the deadline from firing.
- Recognize the new client-startup timeout error and show the existing
`startup_timeout_sec` configuration hint.
## Testing
- Extend the timeout error display test to cover the client-startup timeout.
GitOrigin-RevId: 1967c62f943d55f6aa18792d4488e52c22f1e717
## Why
Concurrent Codex processes can otherwise refresh the same rotating token, and a
cancelled or partially persisted refresh can leave durable and in-memory MCP
credentials out of sync.
## What changed
- Serialize each credential's read-refresh-write transaction across processes,
reread the authoritative store after locking, and adopt credentials refreshed
by another process.
- Keep refresh persistence running after caller cancellation, bound lock and
provider waits independently, and preserve omitted refresh tokens and scopes.
- Fail MCP startup and operations when refresh or persistence fails instead of
continuing with stale credentials, while requiring reauthorization for
missing, unusable, or rejected refresh tokens.
- Exclude OAuth refresh time from the MCP initialization timeout.
## Testing
Add coverage for lock contention, concurrent refreshes, rejected and missing
credentials, storage failures, caller cancellation, and provider timeouts.
GitOrigin-RevId: 4d29b879bec646d2ceb922b526dc793b1a1f5423
[Codex Thread
019edd6d-6f14-74e2-853c-345d1803d4a6](https://codex-thread-link.openai.chatgpt-team.site/thread/019edd6d-6f14-74e2-853c-345d1803d4a6)
## Stack
Review and merge in order. Every layer is independently correct and
documents its safe stopping point.
1. [openai/codex#30292](https://github.com/openai/codex/pull/30292) —
aggregate File/Secrets store locking
2. [openai/codex#30293](https://github.com/openai/codex/pull/30293) —
resolve and lifecycle-pin the exact OAuth store
3. [openai/codex#30416](https://github.com/openai/codex/pull/30416) —
serialized authoritative refresh transaction
4. [openai/codex#30294](https://github.com/openai/codex/pull/30294) —
Codex-owned transport refresh and one-shot 401 recovery
5. [openai/codex#30295](https://github.com/openai/codex/pull/30295) —
login/logout transaction serialization
6. [openai/codex#30296](https://github.com/openai/codex/pull/30296) —
diagnostic-only Auto store drift reporting
**This PR is layer 2.**
## Why
`Auto` is keyring-first with a File fallback, but re-evaluating that
policy during transport reconstruction or persistence can make one MCP
client read from one store and later write to another. With rotating
refresh tokens, the second store may contain an older token. This layer
makes the source selected at client startup explicit and keeps that
authority stable for the client lifecycle.
## What this PR does
- Keeps `resolve_oauth_tokens_from_store_policy` as the single
configured-policy entry point and returns both credentials and the
concrete File or Keyring source that supplied them.
- Puts exact `load`, `save`, and `delete` operations on
`ResolvedOAuthCredentialStore`, making “resolve configured policy” and
“use the selected authority” distinct at call sites.
- Pins the first concrete source in `pinned_credential_store` in the
transport recipe, so initialization retries and session reconstruction
cannot re-evaluate `Auto` and adopt another store.
- Gives `OAuthPersistor` the resolved store and keeps subsequent
persistence and removal on that authority.
- Uses a typed keyring-load error to distinguish aggregate-store
coordination failures from ordinary backend failures; a coordination
failure is surfaced instead of triggering File fallback.
- Keeps login-time `Auto` behavior unchanged: prefer Keyring, fall back
to File when unavailable, and clean up legacy File state after a
successful keyring save.
- Adds structured server/backend context when fallback cleanup fails.
## Explicit decisions and non-goals
- The selection is lifecycle-local and in memory. This PR does not add a
durable backend selector, migration, reconciliation registry, or global
source of truth outside `CODEX_HOME`.
- `Auto` may choose File at the start of a later process if keyring
availability changes. Once this client resolves, a selected-store
failure is returned instead of hot-switching.
- Different `CODEX_HOME` instances remain independent even when they can
access the same Direct keyring credential.
- Cross-process refresh serialization is intentionally not part of this
layer.
## Safe stopping point
This PR can merge alone. A single MCP client no longer hot-switches
credential stores across transport rebuilds or persistence. Two
processes can still refresh the same selected credential concurrently
until layer 3.
## Review size
The net layer is 9 files, +668/−144. The production change remains
focused on store resolution and lifecycle pinning; the largest follow-up
is integration coverage that drives real session recovery.
## Validation
- `just test -p codex-rmcp-client` (99 passed; 5 expected skips)
- Real-client 404 recovery coverage with different Keyring and File
tokens; captured bearer headers prove the stale File token is never sent
- Mutation check: removing the lifecycle pin makes that integration
regression fail by observing the stale File token
## Why
Remote MCP stdio buffers executor-provided stdout until a JSON-RPC line
completes and stderr until a diagnostic line completes. A malicious or
broken executor can keep sending chunks without a newline, making
orchestrator memory grow without bound.
Executor event boundaries are arbitrary, so a valid message before an
oversized line must not be lost merely because both arrived in one
event.
## What changed
- Limit one stdout JSON-RPC line to 8 MiB.
- Limit one stderr diagnostic line to 1 MiB.
- Check each logical line before copying its bytes into the retained
buffer.
- Preserve complete stdout messages that precede an oversized line and
deliver them before closing the stream.
- Close the MCP transport on overflow; existing transport cleanup
terminates the executor-managed process.
- Preserve multiple lines per chunk, CRLF framing, and final
unterminated messages at EOF.
- Box the pending stdio transport so the added framing state does not
inflate every `ClientState` value.
- Add regression coverage for cross-chunk overflow, bounded lines in a
larger chunk, and a valid prefix before an oversized line.
## Scope
This bounds pre-newline buffering for executor-backed MCP stdio only. It
does not change local stdio framing or address memory amplification
while parsing a complete JSON message.
## Summary
MCP operation timeout errors currently print the full debug precision of
the remaining timeout budget. That makes a configured 30-second timeout
show up as something like `29.999999875s`.
This PR rounds the displayed duration to a whole unit, so the error is
clean and stable:
```text
timed out awaiting tools/list after 30s
```
The timeout behavior itself is unchanged; this only affects the
human-facing error text. A regression test covers the reported
`tools/list` case.