## 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`
## Summary
- Allow remote installed-plugin cache refresh to start whenever plugins
are enabled.
- Allow remote installed-plugin bundle sync to start whenever plugins
are enabled.
- Remove the extra local `remote_plugin_enabled` guard from those
background sync paths.
## Context
Server-side installed plugin state and optional bundle URL behavior are
owned by plugin-service `/public/plugins/installed`, so these local sync
paths only need the overall plugin enablement gate.
## Test plan
- `just fmt`
- `cargo test -p codex-core-plugins`
- Adds localVersion to plugin summaries and remoteVersion to share
context, including generated API schemas.
- Hydrates local and remote plugin versions from manifests and remote
release metadata.
- Adds default-on plugin_sharing gate for shared-with-me listing and
plugin/share/save, with disabled-path errors
and focused coverage.
## Summary
- Populate `plugin/list` interface metadata for installed Git-sourced
marketplace plugins from the active cached plugin bundle.
- Preserve marketplace category precedence so list behavior matches
`plugin/read`.
- Keep existing fallback behavior when the cache or manifest is missing
or invalid.
## Test Plan
- `cd codex-rs && just fmt`
- `cd codex-rs && cargo test -p codex-core-plugins
list_marketplaces_installed_git_source_reads_metadata_from_cache_without_cloning`
- `cd codex-rs && cargo test -p codex-app-server
plugin_list_returns_installed_git_source_interface_from_cache`
- `cd codex-rs && just fix -p codex-core-plugins`
- `cd codex-rs && just fix -p codex-app-server`
- `git diff --check`
Server-truth check: OpenAI monorepo app-server generated types already
expose `PluginSummary.interface`, and the webview consumes it for plugin
cards. This PR keeps the protocol/schema unchanged and fills the
existing field from the cached installed bundle for Git-backed
cross-repo plugins.
Supersedes the abandoned #19859, rebuilt on latest `main`.
# Why
PR #19705 adds discovery for hooks bundled with plugins, but `/plugins`
still only shows skills, apps, and MCP servers. This follow-up makes
bundled hooks visible in the same plugin detail view so users can
inspect the full plugin surface in one place.
We also need `PluginHookSummary` to populate Plugin Hooks in the app;
`hooks/list` is not enough there because plugin detail needs to show
hooks for disabled plugins too.
# What
- extend `plugin/read` with `PluginHookSummary` entries for bundled
hooks
- summarize plugin hooks while loading plugin details
- render a `Hooks` row in the `/plugins` detail popup
<img width="3456" height="848" alt="CleanShot 2026-04-27 at 11 45 34@2x"
src="https://github.com/user-attachments/assets/fe3a38d6-a260-4351-8513-fb04c93d725b"
/>
## Summary
- Add plugin manifest keywords to core plugin marketplace/detail models
- Expose keywords on app-server v2 PluginSummary and generated
schema/types
- Populate keywords in plugin/list and plugin/read responses for local
plugins
Depends on https://github.com/openai/openai/pull/891087
## Validation
- just fmt
- just write-app-server-schema
- cargo test -p codex-app-server-protocol
- cargo test -p codex-core-plugins
- cargo test -p codex-app-server
plugin_list_keeps_valid_marketplaces_when_another_marketplace_fails_to_load
- cargo test -p codex-app-server
plugin_read_returns_plugin_details_with_bundle_contents