Commit Graph
50 Commits
Author SHA1 Message Date
Eric Traut f73a478c87 Keep global app-server config independent of the launch directory (#51872)
## Why

Global requests can inherit project settings from the app-server's launch directory. This can prevent marketplace removal or expose project-only feature settings, and config reloads can fail if that directory is deleted.

## What changed

- Load configuration without project context when no working directory is supplied, using `CODEX_HOME` to materialize `Config.cwd`.
- Use global configuration for marketplace mutations, marketplace upgrade reloads, and MCP requests without thread context.
- Pass the launch directory explicitly during startup to preserve project-aware startup configuration.

## Testing

Update regression tests to verify that marketplace removal ignores launch-project configuration and leaves the project config intact. Extend feature-list coverage to verify that global requests ignore project features, thread requests retain them, and global requests work after the launch directory is deleted on Unix.

GitOrigin-RevId: b77063a7ea0b09fd0a79cfc4040db9f1d02606b3
2026-10-07 23:19:26 +00:00
Celia Chen 05ea5f757e Add attributed MCP OAuth credential storage telemetry (#49392)
## What changed

- Instrument MCP OAuth loads, saves, deletes, fallback cleanup, and refresh persistence with credential-free storage metrics, distinguishing configured policy from operations on a pinned store.
- Retain the originating client across background tasks, blocking storage operations, and session recovery, including recovery after startup found no credentials.
- Preserve typed keyring and lock error categories for metrics and remove raw error details from storage warnings.

## Testing

Add tests for policy and pinned-store outcomes, best-effort cleanup failures, wrapped lock errors, and successful or failed refresh persistence without duplicate observations. Add recovery tests that verify client attribution after anonymous startup and across threads with different originators.

GitOrigin-RevId: 5ea4528bcdcf500c8ffdf1d9c7008ea52112c726
2026-09-29 23:01:46 +00:00
Nick Steele 4994306e9f Enable enterprise MCP sign-in and account-scoped grant cleanup (#49276)
## 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
2026-09-29 14:46:33 +00:00
Nick Steele af0d68a236 Restrict enterprise MCP auth and fail closed on config refresh (#49260)
## Why

Configuration reloads must honor current managed restrictions without granting new enterprise MCP authority to existing sessions or overwriting newer configuration with stale snapshots.

## What changed

- Allow enterprise managed authentication only for configured MCP servers. Remove plugin `ema_auth` overlays from the schema and disable servers that still use them.
- Refresh ordinary MCP settings while preserving session settings and initially admitted enterprise registrations. Disable enterprise MCP when its registration changes, policy revokes access, or configuration loading or resolution fails; require a new session to restore enterprise authority.
- Publish refreshes only when their captured configuration is still current, with bounded retries in app-server reloads. Preserve updated managed restrictions even when a refresh is rejected.
- Finish processing other thread refreshes before `config/mcpServer/reload` reports a rejection, and document that an error can accompany partially applied changes.

## Testing

Add regression coverage for stale refreshes, bounded retries, managed-policy revocation, persistent enterprise disablement, ordinary MCP transport changes, and rejection of plugin enterprise authentication. Verify that denied enterprise servers receive no requests.

GitOrigin-RevId: 34429cd339c1ec152a2ee8697cb29f0cff5d9e5f
2026-09-29 13:08:37 +00:00
victor-openai ea64727556 Add single-server MCP status discovery with thread connection reuse (#48783)
## Why

Inspecting one MCP server should not require full-inventory discovery or a separate connection when a thread already has one.

## What changed

- Add optional `serverName` to `mcpServerStatus/list` and update generated schemas and bindings.
- With `threadId`, apply any pending runtime refresh and reuse the thread's current connection and tool catalog, waiting only for the selected server during discovery.
- Without `threadId`, create a discovery connection for only the selected server. Unknown names return an empty page; omitting `serverName` preserves full-inventory discovery.

## Testing

Add a regression test covering both status detail modes, checking that tool metadata is preserved and that status reads do not repeat `initialize` or `tools/list` requests.

GitOrigin-RevId: 1b361b67a993ab45cae493e88c146f50cfaf753a
2026-09-27 21:07:54 +00:00
willwang-openai 83b56bc5ad Support client secrets for pre-registered MCP OAuth clients (#47891)
## Why

MCP OAuth configuration supported a pre-registered client ID but could not supply a client secret for token exchange or refresh.

## What changed

- Add `oauth.client_secret` and `codex mcp add --oauth-client-secret`, requiring a nonempty secret and client ID. Persist the secret in server configuration and accept `clientSecret` in plugin configuration.
- Pass configured credentials through CLI, app-server, and plugin login flows and token refresh. Require a new login when a configured confidential client's ID differs from stored credentials.
- Redact secrets in debug output and keep them out of authorization URLs and persisted OAuth token records.
- Invalidate cached OAuth connections when the configured client ID or secret changes.

## Testing

Add coverage for configuration validation and round trips, CLI argument redaction and subsequent login, app-server login, connection invalidation, and refresh with `client_secret_basic` and `client_secret_post` in both refresh modes. Verify secret exclusion from token records and reject mismatched client IDs before contacting the provider.

GitOrigin-RevId: 85bb0e410e9e68e6cd68eb6359badfc7eac78147
2026-09-24 17:26:18 +00:00
victor-openai 3b50349e1a Add explicit app and account targets to MCP resource reads (#47248)
## What changed

Allow `mcpServer/resource/read` to specify `target: {connectorId, linkId}` for direct hosted app reads without tool discovery, when supported by the backend. Require an explicit `linkId`: a string selects an account, while `null` requests no-auth access subject to backend policy.

Restrict direct targets to `codex_apps` and reject empty connector IDs, empty link IDs, and synthetic link IDs. Preserve originating app/account scope when `originCallId` and `threadId` are supplied, and retain discovery when `target` is omitted.

Update protocol schemas and TypeScript and Python models, preserving required-but-nullable `linkId` semantics in Python code generation.

## Testing

Extend resource-read tests to check target metadata with and without a thread, including explicit no-auth targets and omitted targets. Add Python model tests for string and null `linkId` values and rejection of a missing `linkId`.

GitOrigin-RevId: 8cf5595fbdd937193aeb529901fc95299f6e18ad
2026-09-22 10:42:50 +00:00
Alex Kotliarskyi b23324986d Expose HTTP origins in MCP server status (#47159)
## What changed

Add nullable `httpOrigin` to `McpServerStatus`, derived from the effective configured endpoint for both configured and plugin-provided servers. Return only the HTTP(S) origin, excluding credentials, path, query, and fragment; return `null` for stdio transports, invalid URLs, and non-HTTP(S) schemes. Update the generated protocol schemas and TypeScript and Python types.

## Testing

Extend status integration tests to cover configured and plugin-provided HTTP servers, verify that endpoint paths and query parameters are omitted, and assert `null` for a stdio server. Update protocol tests to check null serialization and compatibility with older inventory responses.

GitOrigin-RevId: 033ebdeefe829e3a48b2d4ab7d8866dac4cda5c5
2026-09-22 02:24:25 +00:00
acrognale-oai 58e2e8cf3c Add workspace routing support for Responses requests (#45812)
## What changed

- Add an optional `WorkspaceRoutingResolver` to `AuthManager` and pass session configuration into routing lookups. Callers without a resolver retain existing routing behavior.
- Apply resolved backend origins and account routing overrides to eligible ChatGPT Responses HTTP requests and WebSocket handshakes. Preserve endpoint paths, validate routing values, and reject HTTP redirects for routed requests, including `NO_CONSTRAINT` routes.
- Key cached WebSocket connections by destination, routing header, and auth revision. Rebuild request setup after credential refreshes and reject account changes during setup or routing discovery.
- Serialize routing lookups per session and retain whether the session has previously been routed.

## Testing

Add tests for path preservation, provider exclusions, concurrent routing lookups, unavailable resolvers, workspace mismatches, redirect rejection, and credential refresh or account changes during request setup.

GitOrigin-RevId: 2b6609019e84a315745c94858955f3f16426fe7f
2026-09-16 00:20:40 +00:00
Charlie Marsh e9633d7a02 Avoid cloning MCP server status snapshot data (#45428)
Remove entries from the owned snapshot maps when building MCP server status
responses, moving server metadata, tools, resources, and auth statuses into
the response instead of cloning them. Preserve pagination and missing-entry
defaults.

GitOrigin-RevId: 012301f55ddb9c52c7934a638bdd0310f11077fa
2026-09-14 13:56:18 +00:00
Nick Steele 654b0a77d0 Add trusted enterprise MCP auth configuration (#44832)
## Why

Enterprise MCP authorization must remain controlled by host, user, or managed configuration. Project settings and plugin declarations must not redirect enterprise credentials or downgrade the selected authentication mode.

## What changed

- Add `auth = "ema_auth"`, shared `mcp_enterprise_managed_auth` IdP configuration, and per-plugin enterprise registration settings.
- Require enterprise registrations to come from one non-project configuration layer, preserve managed IdP precedence, and reject project overrides that change authorization or re-enable disabled enterprise servers.
- Add the disabled-by-default `use_xaa` feature flag and require non-project opt-in or a managed requirement.
- Reject plugin declarations that select `ema_auth`. Keep EMA connections unsupported and block ordinary MCP OAuth login and credential fallback for this mode.
- Extend the configuration schema and serialization for the new settings.

## Testing

Add coverage for IdP precedence, incomplete registrations, alternate credential validation, project-only opt-in, plugin self-declaration rejection, configuration round-tripping, and trusted-project attempts to downgrade enterprise auth to OAuth or ChatGPT auth.

GitOrigin-RevId: f193bdfda74208c98e496a23bf2916955b131a1c
2026-09-11 14:25:56 +00:00
Gabriel Peal 7a6f469dcf Expose advertised MCP server capabilities in status responses (#44826)
## What changed

Add `serverCapabilities` to `mcpServerStatus/list` responses in both `full` and `toolsAndAuthOnly` modes, including thread-scoped reads. Return the initialized server's capabilities object, including its `extensions` map, or `null` when unavailable.

Capture capabilities during initialization so they remain available when tool discovery fails. Clear them on each new connection attempt and keep them independent of shared tool caches. Update the protocol schemas, generated TypeScript and Python types, and documentation.

## Testing

Add coverage for extension capabilities surviving tool-discovery failures in both detail modes. Extend existing tests to check advertised extensions, absent capabilities after initialization failure, and serialization of unavailable capabilities as `null`.

GitOrigin-RevId: 8e9d67c10fac3a54e82bef8be5d99caaf82597d1
2026-09-11 13:55:24 +00:00
victor-openai 8f31b64c7f Report MCP tool discovery errors in server status (#42598)
## Why

An empty tool map does not distinguish a successfully returned empty catalog from a server whose startup or tool discovery failed.

## What changed

- Add nullable `toolsError` to each `mcpServerStatus/list` entry when no catalog is available because startup or tool discovery failed.
- Keep `toolsError` null for returned catalogs, including cached and empty catalogs, and continue returning healthy servers when another server fails.
- Update the protocol schemas and generated TypeScript type.

## Testing

- Extend the app-server status integration test with a broken MCP server and verify its error without losing the healthy server's inventory.

GitOrigin-RevId: 30b4ca4d9a45cec6998f5f626d3fddbee90a525d
2026-09-03 18:36:58 +00:00
rka-oai 035295b46e Improve sandboxing, MCP errors, and cached approvals (#41196)
Expand Windows deny-read globs robustly across protected directories while preserving filesystem enforcement. Preserve structured MCP tool and resource errors through app-server responses. Bind cached Guardian classifications to current local and root authorization state so stale scores cannot approve actions.

Add regression coverage for the sandbox resolver, structured protocol errors, and authorization changes.

GitOrigin-RevId: 4b80ed724d869afeca79204222d8465fa99d3a24
2026-08-27 20:05:08 +00:00
stevenlee-oai 9be8d6e1c3 Harden MCP OAuth callback handling (#40691)
## 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
2026-08-25 20:26:23 +00:00
rhan-oai b6333bb1bb Enforce subagent ownership across app-server inputs (#40464)
## Why

Parent-owned Multi-Agent V2 subagents must remain controlled by their parent,
but several app-server endpoints could still mutate them directly.

## What changed

- Centralize the direct-input ownership check and apply it to turn injection,
  MCP calls, reviews, compaction, rollback and revert, shell and Guardian
  actions, realtime requests, and goal mutations.
- Enforce the same policy for goal mutations on unloaded threads while keeping
  goal reads and turn interruption available.
- Document the affected endpoint behavior and continue exposing
  `canAcceptDirectInput: false` for parent-owned subagents.

## Testing

- Cover direct-input rejection for both legacy and paginated thread history.
- Cover goal mutation ownership checks for unloaded V2 subagents, V1
  subagents, and non-subagent V2 threads.

GitOrigin-RevId: 791b7af98490aed5eab4e672b90661acad11ec23
2026-08-24 19:44:14 +00:00
Eric Traut 343074d420 Report runtime MCP connection status (#40068)
## Why

MCP inventory can be cached or collected separately from a thread's live
connections, so tool availability alone does not describe the current runtime
state.

## What changed

- Add a nullable `runtimeStatus` to `mcpServerStatus/list` for thread-scoped
  requests, covering not-started, starting, connected, authentication-required,
  failed, cancelled, and disabled connections.
- Observe published connection state without starting or reconnecting servers,
  and return an unknown status when no thread is supplied or the active
  configuration no longer matches the published registration.
- Show connection state and tool counts in the compact `/mcp` view while
  retaining the detailed inventory in `/mcp verbose` and compatibility with
  servers that omit `runtimeStatus`.

## Testing

- Cover runtime status transitions, deferred and disabled servers, closed
  transports, configuration changes, protocol compatibility, and TUI rendering.

GitOrigin-RevId: e3bb6efe652f0fa8b3c97d5c53e4729b3a87cd91
2026-08-22 05:54:43 +00:00
jif 00a7b888b2 Discover HTTP MCP servers from selected executors (#39941)
## What changed

- Read `mcp_servers` configuration and requirements from each selected remote
  executor and add eligible HTTP servers to the thread's MCP runtime.
- Bind discovered servers to the thread's concrete executor snapshot and apply
  environment MCP policy and requirements. Discovery is best effort, and
  executor-local servers are not treated as required at startup.
- Ignore unsupported stdio servers and HTTP configurations that depend on
  environment-provided headers or header helpers.

## Testing

- Added an app-server integration test covering discovery, authenticated HTTP
  tool invocation, requirements enforcement, and exclusion of stdio servers.

GitOrigin-RevId: 6e1cdcebdbb1cc21a5a2285fbc5617d8d5997182
2026-08-21 14:23:33 +00:00
victor-openai 097825f75a Add app-server MCP event streaming (#39761)
## What changed

- Add experimental `mcpServer/event/stream/start` and `mcpServer/event/stream/stop` requests for hosted apps, plus `mcpServer/event/stream/notification` forwarding.
- Scope subscriptions to the owning app-server connection and subscribed thread, enforce unique IDs and a per-connection limit, and clean them up when the thread is unsubscribed or the connection closes.
- Wait for the MCP active notification before completing startup, retry streams that close immediately, and terminate them when authentication or hosted runtime ownership changes.

## Testing

- Add an app-server integration test covering activation, event forwarding, duplicate subscription rejection, and explicit cancellation.

GitOrigin-RevId: 8a6fc1615adfc5af7e67def3b824fa5909ab3e7b
2026-08-20 17:49:35 +00:00
victor-openai a1dc95d5af Scope MCP resource reads by connector (#39244)
## 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
2026-08-18 18:01:39 +00:00
jif 880f1135ea Scope MCP app resource reads to their originating call (#39187)
## 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
2026-08-18 11:51:21 +00:00
willwang-openai 1da59ad257 Support per-server MCP OAuth callback ports (#38448)
## What changed

- Add `oauth.callback_port` to MCP server configuration and preserve it when
  serializing configuration edits.
- Accept `oauth.callbackPort` from plugin MCP declarations and skill dependency
  metadata.
- Prefer the server-specific callback port over `mcp_oauth_callback_port` for
  CLI login, app-server, plugin installation, executor, and skill dependency
  OAuth flows.

## Testing

- Cover configuration parsing, serialization, and fallback behavior.
- Verify plugin, executor, and skill OAuth registrations use their configured
  callback ports instead of the global port.

GitOrigin-RevId: 7f65e5e7869358307e49779f1b75e8672b607736
2026-08-13 22:42:12 +00:00
stevenlee-oai 4c89139da9 Add CIMD support to MCP OAuth registration (#38089)
## 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
2026-08-12 00:01:01 +00:00
stevenlee-oai 6dc3ac8721 Add per-login MCP OAuth client registration selection (#38052)
## 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
2026-08-11 18:35:03 +00:00
victor-openai 78d3665d15 Expose plugin ownership in MCP server status (#37850)
## What changed

- Add `pluginId` to each `mcpServerStatus/list` result.
- Populate it for MCP servers contributed by installed or selected plugins, and
  return `null` for servers from other sources.
- Update the generated protocol schemas, TypeScript bindings, and app-server
  documentation.

## Testing

- Cover plugin-owned servers in plugin installation and executor flows.
- Verify non-plugin servers and protocol serialization return a null
  `pluginId`.

GitOrigin-RevId: 0e4b912252cf51be56dcb63e0c1c7365a5e870ee
2026-08-10 17:14:51 +00:00
jacobzhou-oai 56b82e676c Enforce Agent Plugin runtime boundaries (#37027)
## 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
2026-08-05 04:54:33 +00:00
jif 164b3bfeab Isolate MCP OAuth credentials by environment (#36310)
## 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
2026-07-31 11:23:46 +00:00
Celia Chen 709283b432 Use configured HTTP clients for all MCP OAuth requests (#35814)
## 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
2026-07-28 19:11:36 +00:00
Celia Chen 89a3b89c4c Route MCP auth discovery through runtime HTTP clients (#35239)
## 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
2026-07-24 20:04:23 +00:00
jif 3645a4397c Refresh MCP runtimes across thread startup (#35204)
## Why

MCP configuration can change while a thread is still starting, before it appears in the thread manager's loaded-thread list. In that window, invalidation could miss the new thread and leave it with a stale MCP runtime.

## What changed

- Centralize MCP runtime invalidation in `ThreadManager` so it refreshes loaded threads and marks in-progress thread startups for refresh after publication.
- Use the centralized invalidation path for account and plugin changes.
- Invalidate MCP runtimes after successful server and plugin OAuth logins.

## Testing

- Add a regression test that pauses thread startup, invalidates MCP runtimes, and verifies that the newly published thread refreshes its MCP projection.

GitOrigin-RevId: 8944f5a21816a1405e038a0d4c08b66362ea9368
2026-07-24 15:06:02 +00:00
jif e19e65317a Reuse MCP connections across runtime refreshes (#34952)
## 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
2026-07-23 14:55:31 +00:00
jif e497325a6a Centralize thread MCP state in McpRuntime (#34930)
## 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
2026-07-23 11:27:34 +00:00
jif 42c5d3c80d Reuse MCP tool catalogs across sessions (#33184)
## 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
2026-07-14 22:25:39 +00:00
Matthew Zeng 2f7d89b141 Extract connector runtime snapshot management (#32698)
## 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
2026-07-13 05:02:52 +00:00
jif 3095ea9c3d Project selected plugin runtime by environment availability (#30093)
## Why

Selected plugin metadata is stable, but MCP processes are live runtime
state. They need different lifetimes:

- the MCP extension caches manifest, MCP, and connector declarations for
each stable selected root;
- each model step projects that cached metadata through the roots that
resolved as ready for that exact step;
- the MCP manager is rebuilt only when that availability projection
changes.

This matches executor skills: both features consume the same resolved
step roots instead of inferring readiness from the turn's selected
environments.

## Behavior

```text
E1 not ready for this step
  -> no E1 MCP servers or connectors
  -> cached plugin metadata stays in ext/mcp

E1 becomes ready
  -> reuse cached metadata
  -> publish one MCP runtime containing E1 capabilities

same ready roots on the next step
  -> reuse the exact runtime; no rediscovery and no MCP restart

resume
  -> create new extension thread state and a new MCP runtime
```

All model-facing consumers use the same step snapshot:

```text
resolved selected roots
        |
        v
extension MCP/connector projection
        |
        v
{ MCP config, connector snapshot, MCP manager }
        |
        +-> advertise model tools
        +-> build app/connector tools
        +-> execute MCP calls
```

## Cache contract

The existing MCP extension owns a cache keyed by the full
`SelectedCapabilityRoot`:

```rust
let state = thread_store.get_or_init(SelectedExecutorPluginMcpState::default);
```

The cache lives with extension thread state. Environment availability
filters projection but does not invalidate metadata. Resume creates new
thread state. There is no file watcher or executor generation because
contents behind a stable environment/root are assumed stable.

## What changes

- Keeps executor plugin discovery and cached metadata in `ext/mcp`.
- Caches MCP and connector declarations together per selected root.
- Uses the step's already-resolved capability roots, including lazy
environments that are not turn environments.
- Reuses the current MCP runtime when the ready-root projection is
unchanged.
- Uses the same step MCP manager and connector snapshot for
model-visible tools and execution.
- Resolves direct thread-scoped MCP requests from the current
selected-root projection.

## Deliberately out of scope

- `app/list` remains based on the latest global host-plugin state; this
PR does not make its response or notifications thread-specific.
- `required = true` startup semantics do not apply to delayed executor
MCP activation.
- No filesystem/content invalidation.
- No transport-disconnect watcher.
- No executor generations or environment replacement semantics.
- No client sharing across complete manager replacements.

## Stack

1. Extension-owned World State sections.
2. Project executor skills through World State.
3. Pin one MCP runtime to each model step.
4. **This PR:** project selected MCP and connector state from
extension-owned metadata.
5. Integration coverage for selected capability availability and resume.

## Verification

-
`selected_plugin_servers_use_managed_requirements_for_the_selected_root_id`
- The stacked integration PR covers unavailable to ready activation,
unchanged-runtime reuse, skills, MCP tools, connector attribution, and
cold resume.
2026-06-26 01:36:44 +01:00
jif ee9e0f6387 Pin MCP runtimes to model steps (#30101)
## Why

An MCP refresh can replace the session's current manager while a model
step is still running. The step must execute calls through the same
manager whose tools it advertised.

## Boundary

```text
current session MCP runtime
          |
          | capture once for this model step
          v
StepContext.mcp
  - exact MCP config
  - exact connection manager
  - exact runtime environment context
```

```rust
pub struct McpRuntimeSnapshot {
    config: Arc<McpConfig>,
    manager: Arc<McpConnectionManager>,
    runtime_context: McpRuntimeContext,
}
```

## Example

```text
step A captures runtime A and advertises A's tools
refresh publishes runtime B
step A tool call -> runtime A
next step        -> runtime B
```

Capturing the snapshot is only an `Arc` clone. It does not restart MCPs
or make an RPC.

## What changes

- Captures one MCP runtime in `StepContext`.
- Uses it for tool planning, tool calls, resources, approvals, connector
attribution, and elicitation.
- Publishes replacement runtimes atomically.
- Lets an old runtime live only while an in-flight step or request still
holds its `Arc`.

Most of this diff is mechanical routing from the session-global manager
to `step_context.mcp`; it does not introduce selected-plugin discovery
yet.

## What does not change

- No plugin or extension migration.
- No new MCP cache policy.
- No environment file watching.
- No client sharing between separate managers.

## Stack

1. Extension-owned World State sections.
2. Project executor skills through World State.
3. **This PR:** pin one MCP runtime to each model step.
4. Project selected MCP/app/connector metadata by environment
availability.
5. One end-to-end integration scenario.
2026-06-26 00:53:07 +01:00
Owen Lin 703793c22e feat(core, mcp): cache codex_apps tools in memory (#29003)
## 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).
2026-06-25 20:54:48 +00:00
jif b215961a56 Support OAuth for HTTP MCP servers from selected executor plugins (#28529)
## 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.
2026-06-25 10:31:17 +01:00
Celia Chen 9915d34684 feat: use encrypted local secrets for MCP OAuth (#27541)
## Summary

- store MCP OAuth credentials in the configured auth credential backend
- support encrypted-local OAuth storage, including legacy keyring
migration
- propagate the credential backend through MCP refresh, session, CLI,
and app-server paths

## Stack

1. #27504 — config and feature flag
2. #27535 — auth-specific secret namespaces
3. #27539 — encrypted CLI auth storage
4. this PR — encrypted MCP OAuth storage

This is a parallel review stack; the original #17931 remains unchanged.

## Tests

- `just test -p codex-rmcp-client` (the transport round-trip test passed
after building the required `codex` binary and retrying)
- `just test -p codex-mcp`
- `just test -p codex-app-server
refresh_config_uses_latest_auth_keyring_backend`
- `just test -p codex-core
refresh_mcp_servers_is_deferred_until_next_turn`
- `just test -p codex-cli mcp`
- `just fix -p codex-rmcp-client -p codex-mcp -p codex-core -p codex-cli
-p codex-app-server -p codex-protocol`
- `just bazel-lock-check`
2026-06-12 22:03:51 +00:00
jif 693082f3c4 Make MCP server contributions thread-scoped (#27670)
## Why

`selectedCapabilityRoots` belongs to one thread, but MCP contributors
previously received only the global Codex config. That left no clean way
for a selected executor capability to contribute MCP servers to its own
thread.

## What this PR does

- Gives MCP contributors a small context containing the config and, for
a running thread, its frozen host-seeded inputs.
- Uses the same thread inputs during startup, status queries, refreshes,
and skill dependency checks.
- Keeps threadless MCP operations and the existing hosted Apps behavior
unchanged.
- Adds coverage showing that two threads resolve independent
registrations and that later lifecycle mutations do not change the
frozen MCP inputs.

This PR does not discover plugin manifests, add MCP servers, or launch
anything new. It only establishes the thread-scoped registration
boundary.

## Follow-ups

- Resolve selected executor plugin roots through their owning
environment filesystem.
- Convert their stdio MCP declarations into environment-bound
registrations and add an executor MCP end-to-end test.

## Verification

- `just fmt`
- `cargo check --tests -p codex-protocol -p codex-extension-api -p
codex-mcp-extension -p codex-core -p codex-app-server`

Tests and Clippy were not run.
2026-06-12 11:20:34 +02:00
jif 4ec3b8eeea Route hosted Apps MCP through extensions (#27191)
## Stack

- Base: #27184
- This PR is the second vertical and should be reviewed against
`jif/external-plugins-1`, not `main`.

## Why

CCA is moving toward a split runtime where the orchestrator may have no
filesystem or executor, but it still needs to activate remotely hosted
plugin components. HTTP MCP servers are the simplest complete example:
they need configuration and host authentication, but they do not need an
executor process.

The Apps MCP endpoint is currently synthesized by a special-purpose
loader inside the MCP runtime. That works locally, but it leaves hosted
MCP activation outside the extension model being established in #27184.
It also makes the Apps path a poor foundation for plugins whose skills,
MCP servers, connectors, and hooks may come from different sources or
execute in different places.

This PR moves that one behavior behind an extension-owned contribution
while preserving the existing local fallback. It deliberately does not
introduce a generic plugin activation framework.

## What changed

### MCP extension contribution

`codex-extension-api` gains an ordered `McpServerContributor` contract.
A contributor returns typed `Set` or `Remove` overlays for MCP server
configuration; later contributors win for the names they own.

The contract stays at the existing MCP configuration boundary.
Extensions do not create a second connection manager or transport
abstraction.

### Hosted Apps MCP extension

A new `codex-mcp-extension` contributes the reserved `codex_apps` server
from the existing Apps feature, ChatGPT base URL, path override, and
product SKU configuration.

When `apps_mcp_path_override` is enabled for `https://chatgpt.com`, the
resulting streamable HTTP endpoint is
`https://chatgpt.com/backend-api/ps/mcp`. The existing ChatGPT-auth gate
remains authoritative, so this server can run in an orchestrator-only
process without being exposed for API-key sessions.

### One resolved runtime view

`McpManager` now distinguishes three views:

- **configured:** config- and plugin-backed servers before extension
overlays;
- **runtime:** configured servers plus host-installed extension
contributions;
- **effective:** runtime servers after auth gating and compatibility
built-ins.

App-server installs the hosted MCP extension and uses the runtime view
for thread startup, refresh, status, threadless resource reads,
connector discovery, and MCP OAuth lookup. This keeps
`mcpServer/oauth/login` consistent with the servers exposed by the other
MCP APIs. The hosted Apps server itself continues to use existing
ChatGPT host authentication rather than MCP OAuth.

## Compatibility

Hosts that do not install the MCP extension retain the existing Apps MCP
synthesis path. This preserves current local-only, CLI, and
standalone-host behavior while app-server exercises the extension path.

Disabling Apps removes the reserved `codex_apps` entry, and losing
ChatGPT auth removes it from the effective runtime view. Executor
availability is not consulted for this HTTP transport.

## Follow-ups

The next vertical will resolve a manifest-declared stdio MCP server from
an executor-selected plugin root and execute it in the environment that
owns that root. Later verticals can add backend-owned skills, connector
metadata, hooks, durable selection semantics, and incremental local
convergence without changing the component-specific runtime boundaries
introduced here.

## Verification

Focused coverage was added for:

- contributing the hosted Apps MCP at `/backend-api/ps/mcp` without an
executor;
- requiring ChatGPT auth in the effective runtime view;
- removing a reserved configured Apps server when the Apps feature is
disabled.

`cargo check -p codex-app-server -p codex-mcp-extension -p
codex-extension-api -p codex-mcp` passed. Tests and Clippy were not run
locally under the current development instruction; CI provides the full
validation pass.
2026-06-09 22:44:16 +02:00
Gabriel Peal 8a827d6426 Expose MCP server info as part of server status (#24698)
# Summary

Expose MCP server info via App Server (when available) so apps can
render a richer MCP experience
2026-05-28 09:38:34 -07:00
Eric Traut 0f91e869bd Use thread config for TUI MCP inventory (#24532)
## Summary
`/mcp` in the TUI should reflect the current loaded thread, including
project-local MCP servers from that thread config. Before this change,
`mcpServerStatus/list` only read the latest global MCP config, so the
active chat could miss project-local servers.

This adds optional `threadId` to `mcpServerStatus/list`. When present,
app-server resolves the loaded thread and lists MCP status from the
refreshed effective config for that thread; when omitted, existing
global config behavior stays unchanged.

The TUI now sends the active chat thread id for `/mcp` and `/mcp
verbose`, carries that origin through the async inventory result, and
ignores stale completions if the user has switched threads before the
fetch returns. The app-server schemas were regenerated.

## Follow-up
Once this app-server API change lands, the desktop app should make the
same `threadId` plumbing so its MCP inventory also uses the current
thread config.

Fixes #23874
2026-05-26 07:44:04 -07:00
starr-openai 298e5cfce1 Route MCP servers through explicit environments (#23583)
## Summary
- route each configured MCP server through an explicit per-server
`environment_id` instead of a manager-wide remote toggle
- default omitted `environment_id` to `local`, resolve named ids through
`EnvironmentManager`, and fail only the affected MCP server when an
explicit id is unknown
- keep local stdio on the existing local launcher path for now, while
named-environment stdio uses the selected environment backend and
requires an absolute `cwd`
- allow local HTTP MCP servers to keep using the ambient HTTP client
when no local `Environment` is configured; named-environment HTTP MCPs
use that environment's HTTP client

## Validation
- devbox Bazel build: `bazel build --bes_backend= --bes_results_url=
//codex-rs/cli:codex //codex-rs/rmcp-client:test_stdio_server
//codex-rs/rmcp-client:test_streamable_http_server`
- devbox app-server config matrix with real `config.toml` /
`environments.toml` files covering omitted local, explicit local,
omitted local under remote default, explicit remote stdio, local HTTP
without local env, explicit remote HTTP, local stdio without local env,
unknown explicit env, and remote stdio without `cwd`
2026-05-21 17:19:54 +02:00
starr-openai 5c43a64e2b Make local environment optional in EnvironmentManager (#23369)
## Summary
- make `EnvironmentManager` local environment/runtime paths optional
- simplify constructor surface around snapshot materialization
- rename local env accessors to `require_local_environment` /
`try_local_environment`

## Validation
- devbox Bazel build for touched crate surfaces
- `//codex-rs/exec-server:exec-server-unit-tests`
- `//codex-rs/app-server-client:app-server-client-unit-tests`
- filtered touched `//codex-rs/core:core-unit-tests` cases
2026-05-19 12:55:34 -07:00
Matthew Zeng d8ddeb6869 Support explicit MCP OAuth client IDs (#22575)
## Why
Some MCP OAuth providers require a pre-registered public client ID and
cannot rely on dynamic client registration. Codex already supports MCP
OAuth, but it had no way to supply that client ID from config into the
PKCE flow.

## What changed
- add `oauth.client_id` under `[mcp_servers.<server>]` config, including
config editing and schema generation
- thread the configured client ID through CLI, app-server, plugin login,
and MCP skill dependency OAuth entrypoints
- configure RMCP authorization with the explicit client when present,
while preserving the existing dynamic-registration path when it is
absent
- add focused coverage for config parsing/serialization and OAuth URL
generation

## Verification
- `cargo test -p codex-config -p codex-rmcp-client -p codex-mcp -p
codex-core-plugins`
- `cargo test -p codex-core blocking_replace_mcp_servers_round_trips
--lib`
- `cargo test -p codex-core
replace_mcp_servers_streamable_http_serializes_oauth_resource --lib`
- `cargo test -p codex-core config_schema_matches_fixture --lib`

## Notes
Broader local package runs still hit unrelated pre-existing stack
overflows in:
- `codex-app-server::in_process_start_clamps_zero_channel_capacity`
-
`codex-core::resume_agent_from_rollout_uses_edge_data_when_descendant_metadata_source_is_stale`
2026-05-14 11:52:43 -07:00
jif-oai 32b1ae7099 chore: drop built-in MCPs (#22173)
Drop something that was never used
2026-05-11 19:45:08 +02:00
aaronl-openai 9f06d171e2 Preserve session MCP config on refresh (#21055)
# Overview
MCP refreshes were rebuilding active threads from fresh disk-backed
config only, which dropped thread-start session overlays such as
app-injected MCP servers. This keeps refreshes current with disk config
while preserving the thread-local config that only the active thread
knows about.

# Changes
- Rebuild refreshed config per active thread using that thread's current
`cwd`, rather than fanning out one app-server config to every thread.
- Preserve each thread's `SessionFlags` layer while replacing reloadable
config layers with freshly loaded config, then derive the MCP refresh
payload from the rebuilt result.
- Move MCP refresh orchestration into app-server so manual refreshes
fail loudly while background refreshes remain best-effort, and route
plugin-triggered refreshes through the same per-thread reload path.
- Add regression coverage for session overlays, fresh project config,
plugin-derived MCP config, current requirements, and strict vs
best-effort refresh behavior.

# Verification
- Passed focused Rust coverage for the thread-config rebuild behavior
and deferred MCP refresh flow, plus `cargo test -p codex-app-server
--lib`.
- Verified end to end in the Codex dev app against the locally built
CLI: registered an MCP via thread config, verified that it could be used
successfully before refresh, manually triggered MCP refresh, and
verified that it continued to be available afterward.
2026-05-05 21:09:28 -07:00
pakrym-oai b6d4c4ea6b [codex] Use shared app-server JSON-RPC error helpers (#21221)
## Why

App-server had repeated hand-built JSON-RPC error objects for standard
error shapes. Using the shared helpers keeps the common
`invalid_request`, `invalid_params`, and `internal_error` construction
in one place and reduces the chance of new call sites drifting from the
common error payload shape.

## What changed

- Replaced manual standard JSON-RPC error object creation with
`internal_error(...)`, `invalid_request(...)`, and `invalid_params(...)`
across app-server request processors and runtime paths.
- Removed local duplicate helper definitions from search and review
request handling.
- Preserved existing structured `data` payloads by creating the shared
helper error first and then attaching the existing metadata.
- Left custom non-standard errors and raw error-code assertions intact.

## Validation

- `cargo test -p codex-app-server`
2026-05-05 12:13:59 -07:00
pakrym-oai 33b19bcfde [codex] Split app-server request processors (#20940)
## Why

The app-server request path had grown around a large
`CodexMessageProcessor` plus separate API wrapper/helper modules. That
made the dependency graph hard to see and forced unrelated request
families to share broad processor state.

This PR makes the split mechanical and command-prefix oriented so
request families own only the dependencies they use.

## What changed

- Replaced `CodexMessageProcessor` with command-prefix request
processors under `app-server/src/request_processors/`.
- Removed the old config, device-key, external-agent-config, and fs API
wrapper files by moving their API handling into processors.
- Split apps, plugins, marketplace, catalog, account, MCP, command exec,
fs, git, feedback, thread, turn, thread goals, and Windows sandbox
handling into dedicated processors.
- Kept shared lifecycle, summary conversion, token usage replay, and
shared error mapping only where multiple processors use them; single-use
helpers were inlined into their owning processor.
- Removed the fallback processor path and moved processor tests to
`_tests` files.

## Validation

- `cargo test -p codex-app-server`
- `cargo check -p codex-app-server`
- `just fix -p codex-app-server`
2026-05-04 09:34:11 -07:00