## What changed
- Prefer an eligible account grant over saved enterprise credentials, with a fallback to saved enterprise sign-in during initial connection if the account grant is rejected.
- Remember rejected account grants per enterprise MCP registration across threads and report authentication failures as requiring reauthentication. Keep administrator configuration errors distinct from sign-in failures.
- Compare account grants independently of access and ID token rotation, recheck the latest identity eligibility after exchange, and retry once when the refresh grant changes.
- Make MCP authentication status reflect usable account grants, saved enterprise credentials, and completed connection failures.
## Testing
Extend regression coverage for enterprise sign-in failures and recovery, MCP authentication status, account grant identity across token refreshes, and retries after transient credential failures.
GitOrigin-RevId: eeac65f668d5d2d55e9d8bf4547fbcbdd6a6c42d
## What changed
- Prefer stored enterprise credentials when available; otherwise use a validated Codex account OAuth grant whose issuer matches the enterprise MCP configuration.
- Pin each connection to its credential source and OAuth client, reject stale account grants, and hold the account refresh lease only during the IdP exchange.
- Report actionable sign-in and issuer configuration errors, and classify token failures by credential source.
GitOrigin-RevId: 26673f276a31e4ca5290f6b120f0ac44c77d7239
## What changed
- Use V2 discovery inventories for plugin MCP contributions when supplied, including empty or failed inventories; retain legacy discovery when no V2 snapshot is supplied.
- Merge plugins by canonical identity, using cloud metadata and enablement for remote plugins and attaching executor installations only when versions match. Preserve configured executor-local plugins and executor enablement when cloud data is unavailable.
- Keep installation order stable across refreshes and run merged MCP declarations on the first retained executor installation while preserving attribution for the others.
- Carry local and remote identities through selected-plugin snapshots, skill attribution, and active-plugin telemetry. Track disabled plugin IDs alongside disabled roots.
- Preserve a complete cloud catalog when a partial refresh arrives, and cap tool-description plugin attribution at 1,024 bytes while retaining all plugin associations.
## Testing
Add coverage for catalog parsing and reconciliation, enablement and discovery-mode selection, installation refresh ordering, executor-specific MCP normalization, selected-plugin snapshots, and bounded UTF-8 attribution text.
GitOrigin-RevId: a8ad352de6a2b8dc4bfd1ff6a5c62ee8ed1d1664
## Why
Message roles and content classifications do not identify who supplied each context part. Source attribution needs to survive prompt assembly and history truncation, including messages that combine text from different producers.
## What changed
- Add per-part `ContentItemMetadata` for user input, developer instructions, `AGENTS.md`, skills, harness context, command hooks, and tool context with optional callable namespaces.
- Carry attribution alongside content and classifications through contextual fragments, extension prompts, hook output, and agent messages. Keep tool-provided context-window hints separate from harness text.
- Preserve the source of retained text slices during compaction and attribute generated truncation notices to the harness. Default missing historical attribution to unknown.
- Attribute host MCP calls using cached tool namespaces only when catalog revisions remain stable, without starting servers or refreshing catalogs for attribution.
- Send the new metadata to Responses Lite and strip it from requests to endpoints that do not support it.
## Testing
Extend compaction tests to cover mixed sources, missing or extra metadata entries, and discarded media. Update Responses Lite request assertions to verify endpoint-specific attribution handling.
GitOrigin-RevId: b0ebc929b7c60f3ec49d1affbf897312d63edcc8
## Why
MCP servers must forward sandbox policy unchanged to `codex sandbox --sandbox-state-json`, while interpreting launcher information to find the Codex CLI. Keep those contracts separate so consumers can preserve unknown policy fields.
## What changed
- Accept the experimental `codex/sandbox-state` capability alongside the deprecated `codex/sandbox-state-meta` capability, and send identical policy values under both `_meta` keys.
- Move `codexExecutable` out of the policy into `codex/sandbox-launcher`, supplied only when a verified CLI path is available for a local stdio server.
- Replace stale sandbox metadata while retaining unrelated entries, and remove stale launcher metadata when no launcher is available.
- Document the metadata contract and how servers should forward policy and retain authority for long-lived subprocesses.
## Testing
Update unit and integration coverage for policy serialization, identical state aliases, launcher availability across execution environments, and stale metadata replacement.
GitOrigin-RevId: e544c06e6e023953b1fc8069b7b1affa065262d1
## What changed
- Remove `ContentItemMetadata`, provenance and namespace types, and the `content_item_metadata` protocol field while retaining `content_item_kinds` classifications.
- Simplify context fragments, extension contributions, and hook output to carry content without source attribution. Return MCP tool results directly without an attribution wrapper.
- Render context-window hints together in a single text fragment.
## Testing
Update Responses and Responses Lite request assertions to expect content-kind classifications without per-content attribution, and adapt context, hook, and extension tests to the simplified types.
GitOrigin-RevId: 201894cd4c93c6d4b4c41c845e7a9af1dd3c95ca
## What changed
- Add `ContentItemMetadata` to distinguish user input, developer instructions, `AGENTS.md`, skills, harness context, and tool-supplied context independently of message role and content classification.
- Keep attribution attached to content through prompt assembly, history processing, and hook-output spilling. Distinguish command hooks from MCP hooks and retain tool namespaces when known.
- Treat missing historical attribution as unknown and send per-content metadata only to Responses Lite.
## Testing
Update request and history assertions to cover attribution persistence and Responses Lite metadata handling.
GitOrigin-RevId: 24e56005f5bdb98bf82f2b3943f29c4c81f3d587
## Why
MCP servers receiving sandbox state need a usable Codex executable to launch commands with the issuing call's policy. HTTP and remote servers cannot assume access to the host's filesystem.
## What changed
- Include optional `codexExecutable` metadata for local stdio servers when the host supplies an absolute, UTF-8 path to a CLI supporting `sandbox --sandbox-state-json`. Cache capability probes by executable path and bound them to five seconds.
- Document forwarding the sandbox state unchanged and requiring an explicit opt-out rather than silently falling back to PATH lookup or unsandboxed execution when the launcher is absent.
- Honor `useMxc` from forwarded sandbox state when selecting the Windows sandbox backend and network proxy policies, preserving a parent's selection made through CLI overrides.
## Testing
Add coverage for launcher eligibility across local, remote, and HTTP servers, invalid or missing executable paths, and binaries without sandbox-state support. Extend metadata integration and serialization tests to cover the optional launcher field, legacy state, and unknown future fields.
GitOrigin-RevId: ea8cd3770616e5a620b423a01052a7bc828bceb9
## Why
The app server previously rejected OAuth login for enterprise-managed MCP servers. These servers need an explicit sign-in flow that ties saved authorization to the active Codex user and workspace and prevents canceled or superseded attempts from saving credentials.
## What changed
- Enable enterprise sign-in through `mcpServer/oauth/login` for configured servers with a connected thread, trusted IdP registration, and `features.use_xaa` enabled. Scope shared enterprise credentials by user, workspace, issuer, and client ID.
- Add `loginId` to MCP OAuth login responses and completion notifications, preserving compatibility with older servers that omit it.
- Support enterprise login replacement and `account/login/cancel`, wait for callback listener shutdown before replacement, and recheck account and configuration before saving grants. Fence stale attempts across processes and account changes.
- Remove shared enterprise authorization during account logout and `codex mcp logout`, including when the feature is disabled. Continue account logout if enterprise cleanup fails, and invalidate MCP runtimes on account changes.
Start a fresh session after successful enterprise sign-in to use the saved grant.
## Testing
Add regression coverage for login replacement and cancellation, callback port reuse, account changes during setup, cross-process logout invalidation, persisted workspace policy selection, cleanup failures, and responses from older servers without `loginId`.
GitOrigin-RevId: 7ed4caaab4659ed1e67064262cf5cee48e3a26ac
## Why
MCP requests requiring user input previously failed in subagents with guidance to ask the parent agent, blocking browser sign-in, form input, and interactive tool approval in the child thread.
## What changed
Remove the root-only MCP elicitation guard and `allow_user_interaction` plumbing so subagents can surface prompts and wait for responses under the existing approval policy and automatic review rules. Connector authentication can now return retry guidance after acceptance or preserve the original failure when declined.
Keep legacy tool approvals through `request_user_input` restricted to the root thread.
## Testing
Update unit and integration coverage for subagent prompt resolution, form responses, connector authentication acceptance and decline, automatic approval, strict review, and approval-policy updates on reused connections. Update the browser sign-in scenario snapshot to show the subagent continuing after an accepted response.
GitOrigin-RevId: c226fc6dfde724ed22b91dc04dd32bbc15245bee
## What changed
- Support an optional cloud provider alongside executor plugins, refreshing its thread-scoped catalog at each regular turn when plugins are enabled.
- Use cloud connector metadata for app attribution without starting cloud-declared MCP servers.
- Scope cached catalogs to authentication and Apps availability. Retain the previous catalog on discovery failure within the same scope, clear stale catalogs on auth changes, and reject results completed under outdated credentials.
- Add a cancellable regular-task startup phase that reconciles MCP before discovery and reapplies contributor state afterward without invalidating resource caches.
## Testing
Add regression coverage for per-turn refresh, empty catalogs, discovery failures, plugin disabling, auth changes during discovery, MCP reconciliation and cancellation, and cloud app attribution alongside executor MCP tools.
GitOrigin-RevId: 8469eb109ea316e21cbc4122c52ad8000caeef03
## Why
MCP requests that need human input, including browser sign-in, must be handled by the root thread. Subagents need guidance to hand these blockers to their parent without prompting the user or automatically accepting requests that require input.
## What changed
- Reject interactive MCP elicitations and tool approval prompts in subagents with guidance to ask the parent and wait before retrying.
- Recognize browser authentication and `codex_requires_user_input` metadata even when the form schema is empty, while preserving automatic permission approvals and review decisions.
- Carry user-interaction eligibility through MCP runtime creation and connection reuse, and guard prompt registration.
- Preserve connector authentication diagnostics alongside handoff guidance, subject to normal tool-output limits.
## Testing
Add unit and integration coverage for blocked subagent prompts, root browser authentication, automatic approval and review, connection reuse, and authentication diagnostic preservation and truncation. Add a request-history snapshot for browser-auth handoff guidance.
GitOrigin-RevId: 83e146b6e5c1a2a22a34681ad45615f504b3c96c
## Why
Orchestrator skill caches were tied to the entire MCP connection set, so runtime updates could discard cached catalogs and contents even when the Apps connection was reused. Plugin changes still need to refresh those resources without requiring a reconnect.
## What changed
Key orchestrator skill caches by the Apps server connection and an explicit invalidation generation. Preserve cached resources while that identity stays unchanged, and invalidate them on MCP runtime refresh requests or configuration refreshes. Replacing the connection also changes the cache identity.
## Testing
Extend the production-turn integration test to verify cache reuse across ordinary turns and approval-policy changes, refreshed skill contents after plugin invalidation without reconnecting, and fresh resource requests after a forced reconnect.
GitOrigin-RevId: 43ce059972369c4c735742e8224b60a692244b02
## Why
`connector_auth_failure_from_tool_result` requires an install URL to return an auth failure, even when the tool result contains valid auth failure metadata.
## What changed
Export `is_connector_auth_failure_from_tool_result` from `codex-mcp` to detect auth failures independently of an install URL. Share metadata validation with the existing parser, requiring an error result, an explicit auth failure flag, and a nonempty connector ID that does not conflict with the metadata.
## Testing
Add unit tests for detection without an install URL, rejection of missing or mismatched connector identities and non-auth errors or successful results, and detection for all three supported auth reasons.
GitOrigin-RevId: 6e4e177f26d9a34c56874b0360bae63f6c89107e
## What changed
- Apply `disabled_plugin_ids` to plugin skills, recommendations, hooks, and MCP servers without modifying shared plugin state.
- Activate selection changes when the next task starts, keeping pending settings from changing the active runtime.
- Hide app tools contributed only by disabled plugins from model tool catalogs and search. Preserve connectors shared with enabled plugins and existing direct Apps RPC behavior.
- Include plugin identity in MCP approval keys and use that identity when persisting plugin tool approvals.
## Testing
Add regression coverage for disabling and re-enabling plugin skills and tools, deferred activation of settings, shared connector ownership, selected plugin servers, executor hooks, and plugin-specific approval keys and persistence.
GitOrigin-RevId: 1153ff6aa7ab6953d50e838da05203904ecd2956
## Why
Codex Apps applies resource MIME filters to each request separately, so continuation requests must include the filter as well as the cursor.
## What changed
Add `McpResourceClient::list_codex_apps_resources` with a required `mime_type` and optional cursor. Send the filter as a top-level `mimeType` parameter in `resources/list` requests and return resources with the next cursor.
Require a host-owned `codex_apps` registration before sending filtered requests, rejecting extension servers that use the same name.
## Testing
Add integration tests covering MIME filters on both pagination requests, returned resource pages, and rejection of extension name collisions without sending a resource request.
GitOrigin-RevId: 4fb33da56c6ef99286564ead7f74b77d8b337335
## What changed
Advertise the experimental `codex/auth-change` capability for stdio MCP connections with an auth manager. When the server opts in, send `notifications/codex/authChanged` after initialization and on subsequent auth changes, with credential and owner generation counters and no credentials.
Track owner changes separately from credential refreshes so consumers can detect login, logout, or user, workspace, and auth-mode changes even when notifications coalesce. Treat credential changes with incomplete owner identity as owner changes as well.
Tie the notification watcher to the managed client's lifetime, limit each send to five seconds, and close the connection if a subsequent notification fails.
## Testing
Add tests for credential refreshes versus owner changes, coalesced logout and account switches, capability opt-in, notification payloads, and watcher cleanup.
GitOrigin-RevId: 35a99f5252bd48ca60a71fbdd1f708a8158e812c
## Why
Refreshed Apps catalogs need to survive connection reuse. Catalog revisions scoped to the connection set can lose refreshed tools during reconciliation and invalidate prepared calls for unrelated servers.
## What changed
- Store tools, revisions, and refresh coordination on each MCP client, and key cached bindings by client identity and revision.
- Preserve existing binding snapshots while rejecting stale calls before preparation. Wait for active calls to finish before publishing a refreshed catalog.
- Add `CodexThread::refresh_codex_apps_tools` to reconcile the runtime and return the refreshed client's raw Apps inventory alongside tool names eligible under that runtime's generic MCP policy. Keep hidden and disabled tools in the inventory for callers to apply app-specific policy.
## Testing
Add regression coverage for refreshed catalogs surviving client reuse, unrelated calls remaining valid, binding cache invalidation, and refresh snapshots retaining the client's own inventory when another client wins the shared-cache race. Update stale-call and preparation tests to use client-owned catalogs.
GitOrigin-RevId: f2308c81f7474eb31f3380c091c977030123cda2
## Why
Event subscriptions may need to outlive the MCP runtime associated with a task.
## What changed
- Add `McpEventStreamOpener`, which retains the hosted Apps MCP connection settings and opens each event stream with an independent client.
- Keep subscription cancellation tied to account changes and removal of the task's event server, including when cancellation ownership is forwarded across runtime replacement.
- Expose the opener and cancellation forwarding through `McpResourceClient`.
## Testing
Add an HTTP transport test that shuts down the runtime, refreshes credentials for the same account, and verifies that a new event stream connects with the current authorization and receives notifications.
GitOrigin-RevId: e102aa382e63121661ff0567f7523515e459922d
## What changed
- Add `mcp_optional_startup_grace_ms` with a default of 1,000 ms to control how long tool catalog capture waits for optional MCP servers.
- Treat a value of `0` as disabling the shared grace, so optional servers use their configured `startup_timeout_sec` instead.
- Apply updated grace values during runtime and MCP configuration refreshes, and reset cached startup deadlines when the configured duration changes.
## Testing
- Cover custom, disabled, refreshed, and shared startup grace behavior in MCP connection and core integration tests.
GitOrigin-RevId: 936196215ed6e0a29c9ec72cfee17663978df8ef
## What changed
- Add `TrustedAccessContext` to fetch account-bound cyber verified-access grants and attach them as host-owned `openai/entitlementContext` metadata.
- Replace caller-supplied entitlement metadata and return an `unknown` status when authentication, identity consistency, response validation, size limits, or the lookup deadline cannot be verified.
- Add `HttpResponseBodyStream::from_chunks` for lightweight in-memory streaming clients.
## Testing
- Cover grant mapping, unsupported authentication, account and workspace changes, malformed or oversized responses, duplicate programs, and lookup timeouts.
GitOrigin-RevId: 343b6a6608c98c45a34939ef507c2183516a11c1
## Why
MCP servers can share an OAuth callback URL. Without a validated issuer or a
server-specific callback path, an authorization response could be associated
with the wrong server.
## What changed
- Use stable callbacks when authorization metadata advertises issuer-bound
responses, and validate the returned issuer before exchanging the code.
- Retain server-specific callback IDs for providers without issuer support,
including fallback to the global or default callback for legacy registered
clients.
- Persist registered callback URLs for MCP servers and plugins, and insert the
active listener port into portless loopback redirects.
## Testing
Add coverage for issuer validation, callback-mode discovery, registered and
legacy clients, plugin OAuth, CLI persistence, and loopback listener ports.
GitOrigin-RevId: 2878c92e237fc17fd3def0bd2e1cce3e104a3db8
## Why
Executor-owned HTTP MCP servers need to read their bearer credentials from the
selected executor environment instead of the host process.
## What changed
- Preserve `bearer_token_env_var` for executor-owned HTTP MCP configurations and
resolve it when the executor sends each request.
- Extend delegated HTTP headers with executor-local environment references while
rejecting missing, empty, or protected credential variables.
- Keep transport-provided bearer authentication compatible with MCP redirect and
OAuth handling without sending a placeholder authorization value.
## Testing
- Cover authenticated executor-owned MCP requests end to end.
- Cover delegated header resolution and rejection of protected variables.
- Cover parsing executor-owned bearer configuration and transport-provided bearer
behavior.
GitOrigin-RevId: 442bf7382198ffb69eba797eb36d4c74faabda88
## What changed
- Add environment-provided MCP restrictions for configured and plugin-provided servers.
- Disable attachment-scoped servers while their environment configuration is pending, failed, or unselected, while preserving selected-plugin access and the controller-owned Apps server.
- Apply the resolved policy to runtime startup, model tool exposure, telemetry, OAuth flows, and skill dependency installation.
## Testing
- Cover policy filtering for configured and plugin servers, environment state transitions, Apps ownership, and managed OAuth rejection.
GitOrigin-RevId: 7ca5d4dda14068e758497f88835d5cba685e159e
## What changed
- Add an optional `connectorId` to `mcpServer/resource/read`.
- Forward the selected connector through `x-codex-turn-metadata` for threaded and threadless resource reads, restricting hosted app resources to that connector.
- Update the generated protocol schemas and app-server documentation.
GitOrigin-RevId: 5cc94d703abbbb6086665910c1c9ed5dc7075210
## Why
App widgets need to read resources using the app and account context of the
tool call that produced them, including after a thread is restored.
## What changed
- Add an optional `originCallId` to `mcpServer/resource/read` requests and
return it on successful scoped reads.
- Track bounded provenance for successful app tool calls in thread history and
use the current tool binding, account link, and app policy when reading the
associated widget resource.
- Reject missing, mismatched, failed, or ambiguous origins, while keeping
app-only tools available for widget reads without exposing them to the model.
## Testing
Add app-server coverage for scoped widget reads across both history modes,
ephemeral and persistent threads, server restarts, app-only visibility, and
invalid origin cases.
GitOrigin-RevId: 11eaefc066b2a3b639599e4cbdef680233d150a2
## 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
- 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
Subagents can reuse cached MCP tool definitions without starting every optional
server before any of its tools are used.
## What changed
- Defer startup for subagent-owned, optional MCP servers when their cache has at
least one visible, allowed tool, and trigger startup on the first operation
that needs the live client.
- Keep root sessions, required servers, selected plugin servers, and explicit
reconnects eager.
- Exclude empty catalogs from cache eligibility and keep dormant servers out of
the initial startup summary.
## Testing
- Extend MCP tool-cache coverage for eager root startup, unused and filtered
subagent servers, deferred startup status events, and startup that survives an
interrupted first tool call.
GitOrigin-RevId: e7528ff08312583a6747cbc6550ea5b6eabda9a6
## Why
App-server clients need to advertise structured MCP extension settings, including supported MCP App UI MIME types, rather than only opting into OpenAI form elicitation with a boolean.
## What changed
- Add an `extensions` map to initialize capabilities and preserve `mcpServerOpenaiFormElicitation` as a legacy alias for `openai/form`.
- Capture the declared extension profile when a thread is started, resumed, or forked, propagate it to subagents, and advertise it to downstream MCP servers during initialization.
- Keep the profile stable for the lifetime of the loaded session instead of changing it on later turns or direct tool calls.
## Testing
- Cover extension conversion, downstream MCP initialization, session isolation, legacy form support, and subagent inheritance.
GitOrigin-RevId: fbcedbb74ce788e574b0f884a4c45c4cedb9de54
## 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
## 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
- 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
- Make `McpRuntime` own the published MCP configuration, connections, elicitation routing, and selected capability roots for a thread.
- Capture immutable MCP bindings for model steps and tool calls so in-flight work keeps a consistent connection set and approval authority while refreshed state is published atomically.
- Mark MCP state dirty when relevant configuration, plugins, environments, authentication, or elicitation capabilities change, then rebuild it before the next sampling request or out-of-band MCP operation.
- Separate config reloads from server invalidation: `ReloadMcpConfig` applies resolved MCP inputs, while `RefreshMcpServers` requests reinitialization from the thread's latest state.
## Testing
- Cover refreshed state visibility for existing turns, stable step bindings, current approval authority, cancelled refresh retries, resource-client reconciliation, and Apps recovery between sampling requests.
GitOrigin-RevId: 59eabb1aa8dc083426bd18ef4d3630508f376401
## What changed
- Use `McpConnectionSet` as the primary name for the collection of active MCP
server connections throughout `codex-mcp` and its callers.
- Keep `McpConnectionManager` as a backward-compatible type alias.
GitOrigin-RevId: 9753838b4fd0a0b6429a9c4af0c077137616f388
## Why
An MCP connection or tool catalog can change after a model step captures its
available tools. Calls from that step must not be rerouted to a replacement
client or run against a catalog revision the model did not see.
## What changed
- Add `McpBinding` to capture the ready clients, visible tools, resources, and
server metadata for one sampling request.
- Prepare calls against the captured client and reject them if the tool catalog
changes before call preparation and execution finish.
- Keep resource access and cache identity tied to the binding's exact connection
set, and omit servers that do not have a ready client.
- Preserve each client's unfiltered tool catalog so bindings can apply the
current filter consistently.
## Testing
Add coverage for connection replacement, stale catalog rejection, serialized
call preparation and refresh, ready-client capture, and binding-scoped resource
identity.
GitOrigin-RevId: a25da4d2c7f5ed06c41caa0c6d15c464c1698314
## What changed
- Add `McpBindingClients` to hold the ready MCP clients captured for a model step.
- Move concurrent, paginated resource and resource-template listing into the new helper.
- Keep server filtering in `McpConnectionManager` while preserving per-client timeouts, duplicate-cursor detection, and warning behavior.
GitOrigin-RevId: 0e3f8f135126b17d73058ca84e98493a27543ca0
## What changed
- Add a thread-owned `McpRuntime` that atomically publishes connection-set
replacements and owns shutdown.
- Route `McpResourceClient` snapshots and cache keys through that runtime so
extension clients observe refreshed MCP servers.
- Keep model-step configuration snapshots separate from ownership of live MCP
connections.
## Testing
- Add coverage that captures an extension's session resource client, refreshes
the MCP server configuration, and verifies that the existing client sees the
newly published server.
GitOrigin-RevId: 351b2e25594b8eb6b91e50ff8d06b7d49768ae1c
## What changed
- Add `source=codex` to requests sent to the OpenAI developer docs MCP endpoint.
- Preserve OAuth discovery URLs and leave clients for other MCP servers unchanged.
## Testing
- Add unit coverage for regular and streaming HTTP requests, OAuth discovery, and non-docs MCP clients.
GitOrigin-RevId: d04e19fe6fc2db0b5cd7bfb429f0cb821c49b28a
## 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
- Move the Codex Apps tool cache into a reusable `codex-connectors` runtime manager keyed by account and workspace.
- Represent cached tools as atomically published snapshots with refresh timestamps while preserving the newest accepted fetch generation.
- Harden disk persistence with bounded reads, atomic file replacement, and serialized writes so an older fetch cannot overwrite newer state.
## Testing
Add coverage for identity isolation, snapshot timestamps, oversized cache files, atomic replacement, and concurrent persistence ordering.
GitOrigin-RevId: 5ea2234469daae3abf54b030244c3251de62ca5a
## Summary
Codex Apps file parameters are exposed to the model as local paths,
uploaded at execution time, and rewritten into provided-file payloads
before the MCP tool call.
The rewrite currently includes the documented optional fields
`mime_type` and `file_name` for every file parameter. Apps with strict
schemas can reject those fields when they are not declared.
## Changes
- Derive the supported optional file fields from each
`openai/fileParams` parameter's raw input schema before replacing it
with the model-visible local-path schema.
- Always include `download_url` and `file_id`.
- Include `mime_type` and `file_name` only when that specific file
parameter's schema accepts them, including schemas that allow additional
properties.
- Handle scalar and array file parameters, including items-only arrays,
composed schemas, and local JSON Schema references.
- Preserve the existing restriction that only the host-owned Codex Apps
MCP server can use this upload path.
This supports tools with different file contracts in the same app: one
parameter can accept the optional fields while another remains strict.
## Validation
- `just test -p codex-mcp`
- `just test -p codex-core mcp_openai_file`
- `just test -p codex-core codex_apps_file_params_`
- `just fix -p codex-mcp`
- `just fix -p codex-core`
- `just fmt`
- `git diff --check`
- Manually verified in the Codex Electron app that:
- a strict file schema receives only `download_url` and `file_id`
- a rich file schema also receives `mime_type` and `file_name`
Related: #31330
## Why
Code-mode tool results could return to the model while an MCP
elicitation was still waiting for user input. This differed from
parallel tool calling and could let the model continue before the user
resolved the request.
We need one session-level view of outstanding elicitations so tool
runtimes can consistently hold results until every pending elicitation
is resolved.
## What changed
- Added a counted, session-owned ElicitationService with RAII
registrations.
- Registered both core-originated and server-originated MCP elicitations
with the service.
- Migrated out-of-band elicitation tracking and unified exec timeout
pausing to the shared service.
- Made code-mode functions.exec and functions.wait capture their runtime
result normally, then hold it before returning while an elicitation is
outstanding.
- Kept terminate: true immediate; only its result is held.
- Preserved model-visible wall time across the elicitation hold.
- Kept the behavior session-scoped, with concurrent elicitations holding
the pause until all registrations are released.
## Why
An MCP tool call can still be waiting for an elicitation response when
an environment update replaces the thread's MCP runtime.
Before this change:
```text
runtime A starts a tool call and asks the user
environment becomes ready, so runtime B is published
client answers the prompt through runtime B
runtime B cannot find runtime A's pending responder
```
The response is lost and the original tool call stays blocked.
## What changed
All MCP runtimes for one thread now share a small elicitation router:
```text
runtime A ---\
shared router: response token -> exact pending responder
runtime B ---/
```
When Codex surfaces an MCP elicitation, it assigns a unique opaque
response token. The router records which pending request owns that
token. A replacement runtime reuses the same router, so the latest
runtime can deliver a response to a request started by the previous
runtime.
The Codex-owned token also prevents two runtime connections that reuse
the same MCP server request ID from receiving each other's responses.
This does not retain or search old MCP managers. Only the pending
responder map is shared.
## Covered scenario
The integration test exercises the complete failure mode:
1. A thread starts while its selected environment is still unavailable.
2. A configured MCP server starts a tool call and asks the client for
input.
3. The environment becomes ready, causing Codex to publish a replacement
MCP runtime.
4. The client answers the original prompt after the replacement.
5. The original tool call receives that answer and completes.
A focused routing test also creates two runtimes with the same server
request ID and verifies that each response reaches the exact request
that emitted its token.
## Scope
This PR changes only elicitation response routing across MCP runtime
replacement. It does not change when runtimes are rebuilt, which
environments contribute MCP configuration, or how environment
availability is detected.
## Description
This makes Codex Apps tool reads use a shared in-memory snapshot instead
of rereading the disk cache every time `list_all_tools()` runs. Disk
still seeds the cache on startup and gets updated after successful
fetches, but it is no longer the live read path.
The core change is that `McpManager` now owns a process-scoped
`CodexAppsToolsCache`. Codex threads in the same app-server process now
share this Codex Apps in-memory tools snapshot. The snapshot is keyed by
the Codex home plus the Codex Apps identity: the active Codex auth
user/workspace and the effective Codex Apps MCP source config.
There's already code to hard-refresh the cache, so we respect it in this
PR.
## Local benchmark
I ran a local steady-state microbenchmark of the exact repeated Codex
Apps cached-tools read this PR removes, using the same real local cache
payload in both trees: `3,678,138` bytes and `381` tools. The cache file
was already warm in the OS page cache, so this measures same-process
reread/deserialization work rather than cold-disk latency or full turn
latency. Each run is 25 iterations (mimicking a turn that makes 25
inference calls).
| Version | Run 1 | Run 2 | Avg |
|---|---:|---:|---:|
| `origin/main` disk read + JSON deserialize + `filter_tools` | `50.755
ms` | `52.894 ms` | `51.825 ms` |
| This branch in-memory `current_tools` + `filter_tools` | `0.740 ms` |
`0.778 ms` | `0.759 ms` |
That removes about `51 ms` from each repeated Codex Apps cached-tools
read on this machine, roughly `68x` faster for that subpath. It is
useful evidence for the hot path this PR changes, but not a claim that
every production turn gets `51 ms` faster; end-to-end impact also
depends on the rest of `list_all_tools()` and tool-payload construction.
This is on my M2 Max macbook, so with a slower disk this would be much
worse (and indeed we did see this really blew up turn runtime with a
slow disk).
## Why
#28522 routes selected-plugin HTTP MCP traffic through the owning
executor, but OAuth bootstrap and refresh still used host-local clients.
Executor-only servers therefore cannot complete discovery or login
through the same network boundary as the MCP connection.
## What changed
- adapt `codex_exec_server::HttpClient` to RMCP 1.8's `OAuthHttpClient`
contract
- let RMCP own discovery, dynamic registration, PKCE, token exchange,
and refresh
- route auth status, persisted-token startup, and app-server login
through the server runtime while preserving the existing local discovery
path
- add optional `threadId` to `mcpServer/oauth/login` and echo it in the
completion notification
- implement RMCP's redirect policy and 1 MiB OAuth response limit over
executor HTTP
- cover selected-thread OAuth discovery and login through an
executor-only route
Depends on #28522.
## Why
Executor-owned plugin roots are `PathUri`, but MCP config normalization
still converts them into a native `Path` using the app-server host's
rules. Relative `cwd` values can therefore resolve against the wrong
filesystem when host and executor path conventions differ.
This PR keeps executor MCP paths URI-native until the selected
environment launches the server, while retaining the existing host
parser behavior.
## What changed
- Keep one shared MCP normalization path with narrow host-`Path` and
executor-`PathUri` entrypoints.
- Preserve native host resolution for locally installed plugin MCP
configs.
- For executor configs, default `cwd` to the plugin root and resolve
relative working directories with the root URI's path convention.
- Accept explicit executor `file:` URIs only when they remain within the
selected plugin root.
- Preserve the selected environment id and existing remote
environment-variable ownership rules.
- Route the executor plugin provider through the URI-native entrypoint
without converting the root on the host.
- Ensure `codex doctor` does not probe executor-owned stdio commands or
foreign working directories on the host.
- Cover foreign Windows roots, relative and absolute executor working
directories, traversal rejection, runtime resolution, and doctor
behavior.
```text
plugin root: file:///C:/plugins/demo
configured cwd: scripts
|
v
resolved cwd: file:///C:/plugins/demo/scripts
|
v
launch through the selected executor
```
No new provider or filesystem abstraction is introduced.
## Stack
1. #29614 — add lexical `PathUri` containment.
2. #29620 — share URI-native manifest path resolution.
3. #28918 — keep selected plugin roots and resources URI-native.
4. #29626 — load executor skills without host path conversion.
5. **This PR** — resolve executor MCP working directories without host
path conversion.
## Why
Hosted orchestrator skills are read through the remote MCP resource
server. Within one thread, the same catalog or skill resource can be
requested multiple times by prompt injection and the `skills.list` /
`skills.read` tools. Re-fetching adds latency and can make those
surfaces observe different remote contents during the same thread.
This is a follow-up to #28333: orchestrator skills remain limited to
threads without a local executor, and those threads now get a stable
per-thread view of the remote skill data they use.
## What changed
- Reuse the existing per-thread orchestrator catalog snapshot for
`skills.list` and `skills.read` availability checks.
- Cache successful orchestrator resource reads by authority, package,
and resource so prompt injection and tool calls share the same contents.
- Keep the cache memory-only and bounded to 100 resources and 8 MiB per
thread.
- Leave host and executor skill reads unchanged, and do not cache failed
remote reads.
## Verification
- Extended the app-server MCP resource integration test to read the same
hosted skill resource twice and verify that the remote server receives
one read.
- The same test verifies that catalog discovery and the selected skill's
main prompt are each fetched only once per thread.
## Why
**In short:** this PR resolves already-discovered MCP registrations. It
does not read selected plugins or discover their MCP servers.
The resolved MCP catalog currently builds config and auto-discovered
plugin registrations before runtime contributors are applied. A
thread-selected plugin needs a distinct precedence tier in that same
initial resolution pass: otherwise a disabled lower-precedence winner
can leave stale name-level state behind, and the winning MCP tools
cannot be attributed to the selected package reliably.
This PR adds that catalog boundary before executor discovery is
connected.
## What changed
- Added an explicit selected-plugin registration tier between
auto-discovered plugins and explicit config.
- Collected selected-plugin contributions before the initial catalog
build, while leaving compatibility and generic extension overlays in
their existing runtime phase.
- Retained the winning plugin ID and display name directly on
plugin-owned catalog registrations.
- Derived MCP tool provenance from the winning catalog entry instead of
joining against local-only plugin summaries.
- Retained the winning selected server's tool approval policy in the
running connection manager, so a selected registration cannot inherit
approval behavior from a losing local plugin.
- Kept remembered approval session-scoped for selected plugins until
there is an authority-aware persistence contract; Codex will not write
approval back to an unrelated local plugin.
- Preserved existing name-level disabled vetoes for discovered plugins
and config, while keeping a selected package's own disabled registration
scoped to that registration.
- Preserved deterministic selection order and existing config,
compatibility, and extension precedence.
The resulting order is:
```text
auto-discovered plugin
< selected plugin
< explicit config
< compatibility registration
< extension overlay
```
## Behavior and scope
This is a catalog and provenance change only. No production host
contributes selected-plugin MCP registrations yet, so existing local MCP
behavior remains unchanged.
The stacked follow-up, #27870, installs the executor plugin provider
that produces these registrations. App-server activation remains a
separate final step.
## Verification
Focused tests cover precedence, deterministic selected-plugin conflicts,
disabled-veto behavior across catalog phases, managed requirements
before selected-plugin resolution, winning-server approval policy, and
attribution when local and selected packages share an ID or server name.
CI owns execution of the test suite.
## Why
We want a thread-selected plugin to eventually expose stdio MCP servers
that run on the executor owning that plugin.
The existing plugin MCP parser lived inside `core-plugins` and was
coupled to the host filesystem loader. Reusing it from an executor
provider would either duplicate MCP normalization or make the plugin
package layer own MCP runtime semantics. This PR creates the shared
MCP-owned boundary first.
In simple terms:
```text
plugin .mcp.json
|
v
shared parser in codex-mcp
|
+-- Declared placement: preserve current local-plugin behavior
|
+-- Environment placement: produce config bound to one executor
```
This builds on the authority-bound plugin descriptors from #27692. It
intentionally does not discover, register, or launch executor MCP
servers yet.
## What changed
- Moved plugin MCP file parsing and normalization from `core-plugins`
into `codex-mcp`.
- Kept support for both existing file shapes: a top-level server map and
an object containing `mcpServers`.
- Kept per-server failure isolation: one invalid server does not discard
valid siblings, while malformed top-level JSON still fails the whole
file.
- Updated the existing local plugin loader to use `Declared` placement,
preserving its current transport, OAuth, relative `cwd`, and error
behavior.
- Added `Environment` placement for the next stacked PR:
- the selected environment ID overrides anything declared by the plugin;
- missing stdio `cwd` defaults to the plugin root;
- relative `cwd` is resolved beneath the plugin root and cannot traverse
outside it;
- bare or source-less environment-variable references resolve on a
non-local executor;
- explicit orchestrator environment-variable forwarding is rejected for
executor-owned plugins.
## User impact
None in this PR. Existing local plugin MCP loading follows the same
behavior through the shared parser. The executor placement mode is not
connected to thread startup until the follow-up registration PR.
## Assumptions
- A selected capability root's environment is authoritative. A plugin
cannot redirect its stdio process to the orchestrator or another
executor.
- Relative working directories belong under the plugin package root.
Explicit absolute working directories remain valid within the owning
environment.
- For a non-local executor, unqualified environment-variable names refer
to that executor. Reading an orchestrator variable requires an explicit
contract and is rejected for now.
- Parsing only produces normalized `McpServerConfig` values. Process
startup remains owned by the existing MCP runtime and connection
manager.
## Follow-ups
1. Add the executor MCP provider and catalog registration: read the
selected plugin's MCP config through the same executor filesystem,
support stdio only, freeze the result per active thread, apply managed
policy, and resolve name collisions as discovered plugin < selected
plugin < explicit config.
2. Install that provider in app-server and add an end-to-end test
proving `thread/start.selectedCapabilityRoots` launches and calls the
MCP tool on the selected executor, preserves the frozen registration
across refresh, and does not expose it to an unselected thread.
3. After the initial executor-stdio vertical, define
resume/fork/environment-replacement semantics, executor HTTP placement,
warning delivery, common MCP tool-context bounds, and move remaining MCP
source composition above core.
## Verification
- `cargo check -p codex-mcp -p codex-core-plugins --tests`
- `just bazel-lock-check`
- Added focused parser coverage for legacy local normalization, executor
authority, working-directory handling, and environment-variable
sourcing.