Extending the Agent Without Losing the Boundary
In openai/codex, adding a capability is not the same as placing another executable on disk. This chapter explains how Codex couples a tool's model-facing specification to its runtime, then brings skills, plugins, and MCP integrations into a particular turn through validation, selection, and environment-aware projection. The practical result is a way to evaluate an extension by what the model can see, what the host can dispatch, and which approval or permission path still applies.
The distinction matters because an agent's usefulness grows with its reach, but so does the cost of choosing and governing that reach. A repository may contain many skills. A user may have access to several external services. A process may register tools that should be available only inside a nested execution surface. If every capability were presented to every model request, the initial prompt would become an inventory rather than a task-specific plan.
Tools are contracts, not a global inventory
A Codex tool has two inseparable sides. Its specification describes the name, input shape, and behavior that the model may request. Its executable runtime handles the invocation and returns a result. The ToolExecutor abstraction keeps these sides together, so a host can add routing, hooks, or telemetry around the tool without maintaining a separate promise about what the handler actually does.
That coupling closes a subtle failure mode. Suppose a model sees a function named search_repository, but the runtime has replaced its handler with one that interprets paths differently. The model's choice would be based on one contract while the host executed another. In Codex, the tool runtime supplies its own specification and handler, and the router dispatches the model's call through that registered pairing. The implementation still has to validate inputs and honor the active environment, but the public shape and executable identity begin together.
The contract is also more than a name and a JSON schema. The runtime can state whether a handler supports parallel calls and can supply search metadata when the tool is deferred. That metadata lets the model discover a capability later without pretending that the capability belonged in the initial prompt. The same registry can therefore support ordinary function calls, custom calls, tool-search calls, and nested calls while preserving one dispatch identity for each runtime.
The second design choice is exposure. Codex distinguishes several ways a registered tool can reach the model. A direct tool is included in the initial model-visible list. A deferred tool is registered for later discovery through tool search. A Code Mode tool is available through a nested execution surface rather than as an ordinary initial tool. A hidden tool remains registered for dispatch but is not model-visible. Some surfaces can be combined in the underlying exposure flags, while the final router decides the effective presentation for the current step.
These categories separate three things that are often conflated: inventory, visibility, and callability. A tool can exist in the runtime inventory without being visible. It can be discoverable without consuming space in the initial list. It can be callable inside a nested script without being directly available to the outer model. This is a control decision, not merely a user-interface choice, because the model can propose only the capabilities represented in its current tool surface.
The distinction also limits accidental coupling between outer and inner execution. A tool exposed only to nested Code Mode calls is not automatically a normal model tool. A model-only deferred surface need not be available to a nested script. A hidden handler can support internal orchestration without becoming part of the model's choice set. These are separate surfaces because different callers need different contracts and different review paths.
The per-step router is where that decision becomes concrete. Codex builds a tool plan from the current step, environment, model, and available integrations. It stores the model-visible specifications, the mapping for nested tools, the tool mode, and the dispatch registry.