## Why This is the next narrow step toward moving concrete tool families out of core. After #22138 introduced `codex-tool-api`, we still needed a real end-to-end seam that lets an extension own an executable tool definition once and have core install it without the temporary `extension-api` wrapper or a dependency on `codex-tools`. `codex-tool-api` is the small extension-facing execution contract, while `codex-tools` still has a different job: host-side shared tool metadata and planning logic that is not “run this contributed tool”, like spec shaping, namespaces, discovery, code-mode augmentation, and MCP/dynamic-to-Responses API conversion ## What changed - Moved the shared leaf tool-spec and JSON Schema types into `codex-tool-api`, so the executable contract now lives with [`ToolBundle`](c538758095/codex-rs/tool-api/src/bundle.rs (L19-L70)). - Replaced the temporary extension-side tool wrapper with direct `ToolBundle` use in `codex-extension-api`. - Taught core to collect contributed bundles, include them in spec planning, register them through [`ToolRegistryBuilder::register_tool_bundle`](c538758095/codex-rs/core/src/tools/registry.rs (L653-L667)), and dispatch them through the existing router/runtime path. - Added focused coverage for contributed tools becoming model-visible and dispatchable, plus spec-planning coverage for contributed function and freeform tools. ## Verification - Added `extension_tool_bundles_are_model_visible_and_dispatchable` in `core/src/tools/router_tests.rs`. - Added spec-plan coverage in `core/src/tools/spec_plan_tests.rs` for contributed extension bundles. ## Related - Follow-up to #22138
codex-tool-api
codex-tool-api is the minimal extension-facing contract for contributed
function tools that can be injected into Codex without making codex-core
depend on the tool owner's crate.
Crates that define contributed tools should depend on this crate. It owns:
- the executable bundle contract:
ToolBundle,ToolExecutor,ToolCall, andToolError - the one model-visible spec an extension may contribute directly:
FunctionToolSpec
The contract is intentionally narrow: contributed tools receive a call id plus raw JSON arguments and return a JSON value. If a feature needs richer host integration, its extension is expected to do that wiring before exposing the tool rather than widening this crate around the hardest native tools.
The intended dependency direction is:
tool-owning extension crate --> codex-tool-api <-- codex-core
codex-tools has a different job. It remains the host-side owner of Responses
API tool models, schema parsing, namespaces, discovery, MCP/dynamic conversion,
code-mode shaping, and other aggregate host concerns. A crate that only wants
to contribute one ordinary function tool through an extension should not need
to depend on codex-tools.