Support first-class per-thread skill/plugin selection in app-server thread/start
What variant of Codex are you using?
CLI
What feature would you like to see?
Support first-class per-thread skill/plugin selection in app-server thread/start
Summary
App-server hosts that run multiple independent Codex agent sessions need a way to choose different skill/plugin sets for each thread/start request without mutating the user's global/project config.toml and without running each agent type under a separate CODEX_HOME.
Current main has useful partial mechanisms, but none provide positive per-thread selection of installed plugins/skills by logical identity, nor a clear replace/restrict mode for the user's existing capability set.
Checked against local main at beca198b8a (telemetry: log structured direct tool-call timing (#30334)).
Why this matters
Some hosts launch many Codex sessions for different task types:
- a code-review agent with review-only skills/plugins
- a planning agent with project-management integrations
- a support/debugging agent with log or incident-response integrations
- an internal automation agent with only a narrow allowlist of tools
The host should be able to start each thread with the intended capabilities. It should not need to rewrite a user's config.toml, ask the user to toggle plugins globally, or create one synthetic CODEX_HOME per agent type just to isolate plugin/skill loading.
Current mechanisms and gaps
thread/start.selectedCapabilityRoots
selectedCapabilityRoots is an experimental field for environment-owned capability roots. The current shape is SelectedCapabilityRoot { id, location }, and CapabilityRootLocation only has Environment { environmentId, path }. The roots are stored in thread extension data and SessionMeta; current code preserves them across resume/fork and copies them for spawned child threads. The README now documents the basic behavior for environment-owned plugin or standalone-skill roots.
This is useful, but it is still path/root based and additive. The host must know an environment-native absolute path, there is no local-only or installed-capability locator, and there is no way to reference an installed plugin by marketplace/plugin ID or a skill by logical name. It also cannot replace or restrict the user-config-derived skill/plugin set.
thread/start.config
thread/start.config is loaded as a ConfigLayerSource::SessionFlags layer. That gives hosts some per-thread control:
features.plugins = falsedisables all plugin loading for that thread.[mcp_servers.*]can add or override MCP servers for that thread.[[skills.config]]name/path rules can disable specific skills for that thread, becauseskill_config_rules_from_stackreadsSessionFlags.
The plugin path is asymmetric: [plugins.*] state from SessionFlags is ignored because configured_plugins_from_stack goes through project_effective_user_config() and effective_user_config(), which only merge ConfigLayerSource::User layers. Project .codex/config.toml does not provide a project-scoped plugin profile either; it can disable all plugins with features.plugins=false, but it cannot replace the user's enabled plugin set. Skill roots have a similar limitation for adding new roots: they are derived from system/user/project config folders, and SessionFlags has no config folder.
Other related fields
thread/start.environments can make environment-owned selected roots available or unavailable, but it is not a selector for user-installed skills/plugins. thread/start.dynamic_tools adds host-defined tools for one thread, but does not restrict existing skills/plugins. permissions selects permission profiles, not skill/plugin capability sets. ThreadSettingsUpdateParams can update runtime settings after start, but cannot alter the thread's capability selection.
Combined, a host can currently turn all plugins off, add ad-hoc MCP servers, and selectively disable named skills. It cannot positively select or replace a per-thread set of installed skills/plugins by logical identity.
Request
Please either document/stabilize the existing mechanisms as the intended app-server answer for per-thread skill/plugin selection, or add a first-class thread/start API for selecting capabilities by logical identity.
Option A: Clarify or extend selectedCapabilityRoots
I've been waiting for an official surface for this kind of per-thread capability selection. Seeing selectedCapabilityRoots was encouraging, but its current shape seems focused on environment-owned roots rather than the installed skill/plugin selection use case described here.
If selectedCapabilityRoots is intended to become the official per-thread capability-selection surface, it would help to:
- add a local or installed-capability locator, so hosts do not have to express every root as an environment path
- clarify that today's behavior is additive, and add a mode that can restrict/replace the user-config-derived skill/plugin set
- support referencing installed plugins/skills by logical ID instead of only absolute paths
- document lifecycle behavior for resume/fork/spawn, trust/ownership/policy validation, and the experimental-gate stability plan
If selectedCapabilityRoots is only meant for environment-owned capability paths, please document that boundary explicitly so hosts do not treat it as a general capability profile API.
Option B: Add logical per-thread capability selection
For installed plugins/skills, a higher-level request field could select by logical IDs or named profiles:
{
"method": "thread/start",
"params": {
"capabilitySelection": {
"mode": "replace",
"plugins": ["github@openai", "linear@internal"],
"skills": ["repo-review", "incident-debugging"],
"mcpServers": ["github", "linear"]
}
}
}
This would let the app-server host choose a capability profile per agent session while Codex still owns installation lookup, policy checks, config precedence, and environment resolution.
This is the direction I am currently implementing locally because mutating config.toml cannot safely support concurrent agents with different capability sets. It would be much better for app-server to expose this as a native, documented contract.
Option C: Extend SessionFlags to cover [plugins.*]
A narrower fix would be to let configured_plugins_from_stack include the SessionFlags layer for [plugins.*] enable/disable state, similar to how skill_config_rules_from_stack already handles SessionFlags. Then a host could pass [plugins.foo] enabled = false per thread the same way it can disable skills today.
Desired behavior
An app-server host can start two threads against the same Codex server and same user CODEX_HOME, but with different effective capability sets:
thread/startfor agent A enables/selects only review-related skills/plugins.thread/startfor agent B enables/selects only planning-related skills/plugins.- The selection is persisted with the thread and preserved across resume/fork/spawn according to documented rules.
- The user's global installed/enabled plugin state is not mutated.
- The host does not need to create one
CODEX_HOMEper agent type.
Related issues
These look adjacent but not exact duplicates:
- https://github.com/openai/codex/issues/16919 asks about progressive/on-demand loading of MCP servers, apps, and plugins.
- https://github.com/openai/codex/issues/23201 asks about organization/agent-level separation and custom tools in app-server.
This issue is specifically about app-server thread/start choosing different skill/plugin capability sets per new agent thread.
Additional information
_No response_
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗