Start with a familiar workflow. You ask Codex to write an article,
explicitly invoke $article and $image,
keep Browser and GitHub plugins enabled, expose a large MCP tool
set, and maybe spawn a subagent to review the draft. All of that is
"capability" in everyday language, but the source does not treat it
as one blob.
Codex separates the shapes. Some capability becomes prompt context, some becomes tool specs, some becomes a package boundary, and some becomes another thread. Those paths still serve the same user turn, but they are owned and projected differently.
The useful reading model is simple: extensions enter the
current turn as bounded runtime inputs. skills
enter context, plugins provide package boundaries and
roots, MCP enters tool exposure, and subagents
enter the thread control plane.
Evidence boundary.
This article only describes behavior visible in the public
openai/codex source: skill discovery and injection, plugin
manifests and load outcomes, MCP tool exposure, tool_search,
dynamic tools, the multi-agent tool surface, and AgentControl.
References to apps, plugins, and subagents mean those public
runtime structures.
This part follows six questions:
- Why separate context capability, tool capability, and thread capability?
- Which
TurnContextfields receive extension inputs? - How does a skill move from catalog entry to full
SKILL.mdinjection? - How does a plugin package skills, MCP servers, apps, and hooks?
- When are MCP tools exposed directly, and when are they deferred to
tool_search? - Why is a subagent a child thread rather than an ordinary tool result?
1. Four Capability Shapes
The hardest part of this area is vocabulary. The UI may call many things "capabilities", but the runtime needs sharper boundaries.
| Shape | User-facing entry | Runtime entry | Owner to follow |
|---|---|---|---|
| Working method | $article, $image, or a structured skill selection. |
The catalog is visible first; full SKILL.md content is injected only when selected. |
SkillsManager, build_skill_injections. |
| Capability bundle | Browser, GitHub, Documents, and other plugins. | The manifest declares skills, MCP servers, apps, and hooks; loading produces effective roots and summaries. | PluginManifest, PluginLoadOutcome. |
| Callable tool | MCP server, app connector, or dynamic tool. | Converted into model-visible tool specs; large tool sets can be deferred behind tool_search. |
build_mcp_tool_exposure, ToolSearchHandler, ToolRouter. |
| Parallel work unit | spawn_agent, send_message, wait_agent. |
Creates or resumes a child thread with copied runtime boundaries and inter-agent communication. | AgentControl, multi-agent handlers, SessionSource::SubAgent. |
Keep this table close while reading the code. A skill answers "how should this work be done?", a plugin answers "which package owns these abilities?", MCP answers "what can the model call?", and a subagent answers "does this need a separate context track?".
1.1 Follow One Turn Through Those Shapes
Use one realistic request as the representative unit: "use
$article to write the Codex memory chapter, use
Browser if a page must be checked, and ask a subagent to review
the draft." Codex does not merge that into one universal
capability. At turn start, the request is split into bounded
runtime materials.
| Stage | Unit at that moment | Owner and visibility | What changes next |
|---|---|---|---|
| Raw input | User text plus explicit mentions. | The input layer recognizes $article, Browser, and subagent intent. |
It selects entries to prepare; it does not produce tool results. |
| Capability snapshot | turn_skills, extension_data, and dynamic_tools in TurnContext. |
The runtime owns it inside the turn boundary. | Injection and tool planning read the same snapshot. |
| Context injection | The selected SKILL.md content for $article. |
Model-visible guidance. | It changes how the model should work; it is not a function call. |
| Tool exposure | Browser or other MCP/app tool specs. | The model sees direct tools or deferred tools retrieved through tool_search. |
Actual calls still pass through the tool router and permission layer. |
| Child thread | A review task created by spawn_agent. |
The parent starts it; the child owns its own context and event stream. | Results return to the parent through inter-agent communication. |
This chain is more useful than vocabulary alone. A single request can involve skills, plugins, MCP, and subagents, but they take effect at different layers. Once you separate context injection, tool exposure, and child-thread control, the source has a clear path to follow.
2. TurnContext Holds the Inputs
Earlier parts focused on TurnContext as the place where
context, permissions, and token policy meet. For extensions, the
relevant fields are dynamic_tools, extension_data,
turn_skills, multi_agent_version,
parent_thread_id, and session_source.
make_turn_context assembles the turn's config, model
information, permission profile, dynamic tools, extension data, and
loaded skills into one structure. Two details matter here:
HostLoadedSkills is inserted into extension_data
for host extensions, and dynamic_tools is carried from
session configuration into the later tool plan.
Codex forms a turn capability snapshot before the model request: current config, loaded skills, extension data, multi-agent mode, and permission boundaries. Injection and tool planning both build on that snapshot.
3. Skills: Catalog First, Full Text on Demand
Skill exposure has a light first layer. Codex renders available
skill names, descriptions, paths, and usage rules into developer
context through AvailableSkillsInstructions. That tells
the model what can be selected without paying the cost of loading
every long skill body.
The heavier layer appears after an explicit mention. build_skills_and_plugins
collects skill mentions from the current user input, then calls
build_skill_injections. That function resolves the
skill metadata, reads the full SKILL.md, and wraps the
contents as SkillInstructions. The full working method
enters context only when the turn needs it.
| Stage | Model-visible content | Why it is shaped this way |
|---|---|---|
| Catalog | Skill name, description, path, and trigger guidance. | The model can choose without loading every long instruction file. |
| Explicit mention | Full SKILL.md contents. |
The user or structured input has confirmed that the skill is relevant. |
| Injection shape | SkillInstructions as a contextual user fragment. |
A skill is a work method and constraint, so it enters context rather than the tool list. |
That is why skill quality matters so much. The runtime can put the file in the right place, but the file itself still needs to encode workflow, boundaries, and verification criteria clearly.
4. Plugins Package Several Kinds of Capability
Plugins solve a broader packaging problem. A plugin may contribute
skills, scripts, MCP servers, app connectors, and hooks. In the
source,
PluginManifest.paths
names those components as
separate resources: skills, mcp_servers,
apps, and hooks.
pub struct PluginManifestPaths<Resource> {
pub skills: Option<Resource>,
pub mcp_servers: Option<Resource>,
pub apps: Option<Resource>,
pub hooks: Option<PluginManifestHooks<Resource>>,
}
This small source shape is the plugin mental model. A plugin manifest groups several resources under one package identity; it does not collapse skill context, MCP tools, app connectors, and hooks into one new kind of runtime ability. The loader still routes each resource to its own channel.
After loading, PluginLoadOutcome exposes effective
skill roots, plugin skill roots, MCP servers, apps, hook sources,
and capability summaries. A summary is a compact model-facing
description. The actual capability still enters through its own
path: skill context, MCP tool, app tool, or hook.
The plugin instructions make this boundary explicit: a plugin is a local bundle of skills, MCP servers, and apps. Solving work still means using the underlying skill, MCP tool, or app tool.
build_skills_and_plugins loads plugins from the current
config, resolves explicit plugin mentions, and, when needed, loads
raw MCP/app inventory for the turn. build_plugin_injections
then turns a plugin mention into guidance about the package's
currently usable abilities.
5. MCP and tool_search: Discover Before Loading Everything
Part IV covered the ordinary tool-call path. The extension layer
adds an exposure decision before the model sees MCP tools.
build_mcp_tool_exposure filters model-visible MCP tools
and then decides whether to expose them directly or defer them.
The threshold is concrete: DIRECT_MCP_TOOL_EXPOSURE_THRESHOLD
is 100. If search is enabled and the configuration requests
always-defer, or the tool set reaches that threshold, Codex does
not place the whole set in the direct tool list. It exposes
tool_search, and the model retrieves relevant specs by
query.
| Tool source | Before model exposure | Runtime boundary |
|---|---|---|
| Regular MCP tool | Filtered for model visibility and exposed directly when the set is small enough. | Calls still go through McpHandler and approval metadata. |
| Large MCP set | Indexed as deferred tools and retrieved through tool_search. |
The search step reduces context noise without bypassing the call path. |
| App connector tool | Visible only when the connector is allowed and enabled for this turn. | Tool metadata keeps connector and plugin information. |
| Dynamic tool | Added from TurnContext.dynamic_tools during tool planning. |
Still becomes a normal handler routed by ToolRouter. |
The practical point is scale. Once tool libraries become large, the key question is not just whether the model can call them; it is how many specs should be placed in front of the model before it knows what it needs.
6. Subagents Add a Controlled Thread
Multi-agent operations are exposed through tools:
spawn_agent, send_message,
followup_task, wait_agent, and
list_agents. Their effect is different from a normal
function result. A spawned agent becomes a child thread managed by
AgentControl.
handle_spawn_agent parses task_name,
agent_type, model options, reasoning effort,
service tier, and fork_turns. It then builds a child
config from the parent turn, applies allowed model or role
overrides, and copies runtime boundaries such as approval policy,
sandbox, cwd, permission profile, and environments.
The spawn path uses SessionSource::SubAgent and
ThreadSpawn metadata. When history is forked, it reads
parent history and keeps all turns, the last N turns, or none,
depending on fork_turns. Completion is reported back
to the parent through InterAgentCommunication in the
v2 path, or by injecting a parent-thread message for older paths.
A subagent is a controlled thread, not just a function call. It has its own context, status, history, and completion notification, while still inheriting the parent's runtime boundaries.
7. Rejoin the Main Turn
The four paths now connect back to the series map. User input
enters a turn; TurnContext fixes the current snapshot;
skill and plugin guidance become contextual injection; MCP,
dynamic tools, and collaboration tools enter the tool plan; tool
calls, agent activity, and model output continue into events and
client projection.
| What you see | Where to read | Series connection |
|---|---|---|
| "Use $article for this post" | collect_explicit_skill_mentions, then SkillInstructions. |
Part II: context management. |
| "Enable Browser or GitHub plugin" | Manifest and load outcome, then contributed skill roots, MCP servers, and apps. | This part's plugin boundary, plus Part IV tools. |
| "Search the tool catalog first" | build_mcp_tool_exposure and ToolSearchHandler. |
Part IV: tool specs and routing. |
| "Spawn a reviewer subagent" | Multi-agent handlers, AgentControl, and SessionSource::SubAgent. |
Part III events and Part VI client projection. |
Seen this way, Codex extensions are not an external plugin layer bolted around the runtime. They are bounded inputs to the same turn: context when the model needs a working method, tool specs when it needs callable capability, and child threads when the work needs a separate context track.
8. The Series Map After Part VII
With seven parts in place, a single request can be read end to end: entry converts user intent into typed operations; session creates a turn; context management prepares model-visible material; tool specs and routers define callable surface; permission and sandboxing gate side effects; events project runtime facts to clients; extensions and multi-agent explain how extra capability joins that same path.
Part VIII follows hooks and lifecycle. Before or after a
user prompt, before or after a tool call, and when a turn stops,
Codex allows more code to add context or intercept side effects.
At that point, the hooks entry in a plugin manifest
becomes an execution path.
Source References
- Pinned openai/codex source snapshot
- TurnContext fields for dynamic tools, extension data, turn skills, and multi-agent runtime
- make_turn_context builds the turn capability snapshot
- build_skills_and_plugins assembles skill, plugin, and extension injection
- AvailableSkillsInstructions
- Available skills added to session prompt sections
- SkillsManager computes skill roots from config
- build_skill_injections reads full SKILL.md contents
- collect_explicit_skill_mentions
- SkillInstructions contextual fragment
- PluginManifest paths for skills, MCP, apps, and hooks
- LoadedPlugin and PluginLoadOutcome
- Effective skill roots, MCP servers, apps, hooks, and summaries
- AvailablePluginsInstructions
- build_plugin_injections
- app-server extension registry
- build_mcp_tool_exposure and deferred threshold
- ToolSearchHandler
- Tool planning for MCP resources, plugin install tools, collaboration tools, MCP handlers, and dynamic tools
- MCP tool approval metadata and request metadata
- handle_spawn_agent builds child agents
- SpawnAgentArgs and fork_turns
- build_agent_spawn_config and runtime overrides
- AgentControl and inter-agent communication
- Subagent completion watcher and ThreadSpawn metadata