# Tools: catalogs, eligibility, and discovery

An agent may have a hundred tools installed while the next model request advertises only six. That can be deliberate: some tools are unavailable to this agent, some require discovery, and others are callable only through an orchestration interface. “Registered,” “visible,” and “authorized” describe different states.

Suppose a user asks to find an invoice and email a summary. The model needs to discover the invoice reader and understand its arguments. Finding a mail tool does not authorize sending that email. A good harness reduces irrelevant choices while preserving a separate execution-time authority check.

## Separate the catalog from permission

Think of tool preparation as several decisions, without assuming every harness implements them as separate modules:

| Decision | Example |
| --- | --- |
| Registration | An implementation exists for `invoice_lookup`. |
| Eligibility | This agent, session, provider, or role can use it. |
| Presentation | Its schema appears directly, is deferred, or is exposed through code mode. |
| Discovery | Search returns its name, description, and usable calling contract. |
| Admission | This particular call passes validation, permission, and runtime checks. |

Hiding a schema is useful prompt construction, but is not sufficient access control. A guessed name or nested invocation must still reach the appropriate execution checks. Conversely, deferring a tool does not mean denying it: the model may be expected to discover it. See [tool safety classification](tool-safety.md) for the separate authorization question.

## How the four catalogs are built

| Harness | Catalog and filtering | Additional tools during work |
| --- | --- | --- |
| Codex | A finalized tool plan separates registered runtimes from model-visible specifications and code-mode identities. Policy, model support, exposure settings, and collisions affect the plan. | Deferred lexical search returns loadable specifications; code-mode search filters candidates against that step's allowed nested catalog. [Plan][c-plan], [search][c-search] |
| pi | The coding SDK selects built-ins and extension tools with configurable defaults, allowlists, and exclusions. MCP tools can remain registered for deferred access. | `tool_search` ranks inactive deferred/code-mode tools and activates matches for the next request. [Selection][p-select], [loading][p-search] |
| Goose | Extension catalogs are filtered by configured availability, namespaced, tagged, and schema-normalized. Model visibility and code-mode presentation add filters. | Extension-management tools discover available extensions and enable them; this changes which capabilities are available through the session's extension machinery. [Catalog][g-catalog], [presentation][g-present], [management][g-discover] |
| Sidekick | Native definitions come from enabled agent configuration, with governed-action grants, delegation limits, and panel/deliberation roles shaping the surface. | Attached skills use stable wrapper tools and load instructions as results; a general native tool-search/hydration mechanism was not established in this review. [Shaping][s-shape], [skills][s-surface] |

These are inspected implementation paths, not guarantees about every provider or deployment. Sidekick's source is private and its links require access. Its native path is distinct from an external harness hosted through an Outpost.

## Capability negotiation: what can this run actually offer?

**Capability negotiation** means resolving which features are usable for the selected model, provider, configuration, and execution context. It need not involve a network handshake: the inspected paths often use model metadata, compatibility flags, and local policy. Technical support and permission are separate questions. A provider may accept a tool schema while the agent lacks authority to execute its operation.

For example, imagine switching models after discovering an invoice tool. The new model might support ordinary function calls but not the old model's tool-search protocol. The harness must choose a supported representation for the next request, preserve call/result identity, and retain execution-time permission checks. Silently dropping a required constraint would change the contract.

| Harness | Concrete capability decision | What it does not establish |
| --- | --- | --- |
| Codex | The configurable MCP exposure path combines model search support, effective tool mode, and exposure exclusions. Deferred exposure is removed when its conditions do not hold; remaining exposures determine whether a tool is direct, nested, or hidden. Strict third-party code-only mode has a separate override of those exclusions. Search also requires policy permission and an eligible deferred candidate. [Exposure resolution][c-capabilities], [search gate][c-plan] | Lack of search support does not guarantee every deferred tool becomes direct: other exposure restrictions still apply. |
| pi | The OpenAI Responses adapter uses compatibility flags for transcript-anchored tool additions, tool search, strict schemas, and grammar tools. The shared converter chooses grammar or ordinary function representation; required strict-schema constraints can fail explicitly when unsupported. [Request preparation][p-capabilities], [grammar conversion][p-convert], [strict requirement][p-strict] | Tool registration or a compatibility flag is not proof that an arbitrary endpoint implements the advertised behavior. |
| Goose | Inference preparation filters app-only tools and, when compiled and activated, code-mode visibility. A configured `toolshim` moves tool definitions into the system prompt and sends an empty native tool list, providing a different calling representation. [Inference and provider preparation][g-present] | Prompt-based tool calling is not the same protocol guarantee as native structured tool calls, and the inspected switch is configuration-driven. |
| Sidekick | Native governed-tool descriptions use prerequisite-filtered grants; role restrictions narrow panel and hidden deliberation surfaces. The call-time capability gate separately reads current grants and fails closed for missing or unreadable configuration. [Surface][s-shape], [roles][s-role], [live grants][s-capabilities] | Here “capability” includes application authority. These grants do not establish model/provider protocol support. |

Treat these as examples of resolution at different layers, not one standardized negotiation mechanism. For provider serialization, streaming, and history conversion, continue to [provider adaptation](provider-adaptation.md).

For a harness you build, resolve presentation before constructing the request and check authority again before execution. Record why a capability was selected, adapted, or refused. Test a model switch, a revoked grant, a missing executor, and a required schema feature unsupported by the provider. A fallback is useful only when it preserves the contract your caller requires; otherwise return an explicit incompatibility. Changes to the advertised surface also belong in your [prompt-cache strategy](prompt-caching.md).

## Discovery can change the next request

Pi provides a clear trace. Initially, a deferred invoice tool is registered but absent from the active model tool list. The model calls `tool_search` with an invoice-related query. The search ranks eligible inactive candidates using BM25, a lexical matching method, appends matches to the active list, and returns a short description. The next model request then declares the tools. Activation is recorded as a tool change. The search tool's own description remains stable as registrations change. [Pi search and activation][p-search]

That is **dynamic hydration**: loading the detailed callable surface when needed. It saves upfront schema space at the cost of a discovery step and the possibility of a poor match. A successful metadata search says a tool looks relevant; it does not prove that the tool will return useful results.

Codex also separates deferred search from direct exposure. Its search path produces loadable specifications, and its code-mode path applies the exact step's nested-tool policy before limiting ranked matches. Search availability itself depends on model support and tool policy. The runtime therefore cannot assume that every provider presents every discovery mechanism identically. [Codex search eligibility][c-plan], [ranked results][c-search]

Goose's extension-management surface is a different level of discovery: it identifies extensions available to enable, then manages their activation. An extension may contribute several tools. Treating this as identical to searching an already registered individual-tool catalog would conceal the connection and lifecycle work involved. [Goose extension management][g-discover]

Goose's optional code mode separately offers `list_functions` and `get_function_details` for the callable catalog in its lease snapshot. Its catalog excludes app-only tools and other unsupported entries; execution checks names against that catalog. Listing details for a nested call is different from promoting a function into the model's direct tool list. [Code-mode discovery][g-code-discover]

## Stable wrappers are another design

Sidekick's attached skills illustrate a different tradeoff. When an agent has resolvable skills, context construction includes `Skill`, `ReadSkillReference`, and `UseSkillTool`. Loading a skill appends instruction material as a tool result rather than adding new function definitions to the model's tools array. This preserves a stable calling surface for that mechanism. [Stable skill surface][s-surface]

`UseSkillTool` resolves the attached binding, checks that the requested tool is declared by the skill, and dispatches through the normal system-tool path. At this pin it supports built-in system tools, not arbitrary configured CLI or user Python tools. It does not grant authority merely because instructions mention an operation. [Skill dispatcher][s-dispatch]

This is not a claim that Sidekick's entire catalog is immutable. Agent configuration, role, delegation depth, and other context determine which definitions are assembled. For governed tools, granted actions shape descriptions; a tool with no allowed actions can be omitted. Panel peers and hidden deliberation passes lose interaction/delegation tools inappropriate to their execution role. [Action shaping][s-shape], [role restrictions][s-role]

## Refresh catalogs without changing a pending call

Suppose `lookup` is discovered, then its server refreshes before execution. Does the admitted call still address the implementation and schema the model saw? Catalog refresh is a correctness boundary as well as a cache concern.

Codex captures MCP binding and output-policy information for the admitted call. Goose caches public extension tools by version and scope, while normalizing advertised names and metadata. Pi's discovery distinguishes registered tools from the active set. These mechanisms address different lifetimes; none should be reduced to “cache tools forever.” [Codex call snapshot][c-binding], [Goose scoped catalog][g-cache], [pi activation][p-search]

Stable ordering also matters independently of eligibility. Goose explicitly sorts the final inference list to support prompt caching, then adapts tool presentation for its provider configuration. A stable order does not mean a stable membership set, and provider adaptation does not substitute for permission enforcement. [Goose inference preparation][g-present]

## Builder exercise: explain every missing tool

Create a registry with a reader, a sender, and a tool available only to a specialist agent. Start with only the reader visible. Make discovery return the sender's contract without granting send permission. Then change the specialist's role and refresh the catalog while an earlier call is pending.

For every tool, expose a diagnostic reason: absent implementation, excluded by policy, deferred, incompatible presentation, or awaiting runtime authorization. Check that direct calls and [code-mode](code-mode.md) calls obey the same intended authority boundary. Record which catalog revision or binding an admitted call uses.

Keep these responsibilities independent. Registration supplies execution; discovery supplies information; activation supplies model visibility; authorization supplies permission for the operation. This separation makes a missing tool understandable and a newly discovered tool safe to evaluate.

[c-plan]: https://github.com/openai/codex/blob/8c076942cb70fba8e5c4732c23f1faabc4aed484/codex-rs/core/src/tools/spec_plan.rs#L368-L459
[c-capabilities]: https://github.com/openai/codex/blob/8c076942cb70fba8e5c4732c23f1faabc4aed484/codex-rs/core/src/tools/spec_plan.rs#L236-L285
[p-capabilities]: https://github.com/earendil-works/pi/blob/482c1381a00d8d00bc81697e4a980c593db6803e/packages/ai/src/api/openai-responses.ts#L331-L385
[p-convert]: https://github.com/earendil-works/pi/blob/482c1381a00d8d00bc81697e4a980c593db6803e/packages/ai/src/api/openai-responses-shared.ts#L382-L435
[p-strict]: https://github.com/earendil-works/pi/blob/482c1381a00d8d00bc81697e4a980c593db6803e/packages/ai/src/api/constrained-sampling.ts#L204-L248
[s-capabilities]: https://github.com/datex/sidekick/blob/463d653124e1c2cda46a3f14f01f3d6f384326b1/backend/src/sidekick_worker/tools/capability_gate.py#L97-L183
[c-search]: https://github.com/openai/codex/blob/8c076942cb70fba8e5c4732c23f1faabc4aed484/codex-rs/core/src/tools/handlers/tool_search.rs#L288-L355
[c-binding]: https://github.com/openai/codex/blob/8c076942cb70fba8e5c4732c23f1faabc4aed484/codex-rs/core/src/tools/handlers/mcp.rs#L135-L325
[p-select]: https://github.com/earendil-works/pi/blob/482c1381a00d8d00bc81697e4a980c593db6803e/packages/coding-agent/src/core/sdk.ts#L64-L92
[p-search]: https://github.com/earendil-works/pi/blob/482c1381a00d8d00bc81697e4a980c593db6803e/packages/coding-agent/src/extensions/tool-search/tool.ts#L178-L241
[g-catalog]: https://github.com/aaif-goose/goose/blob/34a222e3f2b95d8b4a43482b53c2cc957ecce26d/crates/goose/src/agents/extension_manager/mod.rs#L300-L342
[g-cache]: https://github.com/aaif-goose/goose/blob/34a222e3f2b95d8b4a43482b53c2cc957ecce26d/crates/goose/src/agents/extension_manager/mod.rs#L269-L297
[g-present]: https://github.com/aaif-goose/goose/blob/34a222e3f2b95d8b4a43482b53c2cc957ecce26d/crates/goose/src/agents/reply_parts.rs#L184-L220
[g-discover]: https://github.com/aaif-goose/goose/blob/34a222e3f2b95d8b4a43482b53c2cc957ecce26d/crates/goose/src/agents/platform_extensions/ext_manager.rs#L78-L129
[g-code-discover]: https://github.com/aaif-goose/goose/blob/34a222e3f2b95d8b4a43482b53c2cc957ecce26d/crates/goose/src/agents/platform_extensions/code_execution.rs#L51-L145
[s-shape]: https://github.com/datex/sidekick/blob/463d653124e1c2cda46a3f14f01f3d6f384326b1/backend/src/sidekick/services/context_builder_service.py#L1742-L1818
[s-surface]: https://github.com/datex/sidekick/blob/463d653124e1c2cda46a3f14f01f3d6f384326b1/backend/src/sidekick/services/context_builder_service.py#L1873-L1917
[s-role]: https://github.com/datex/sidekick/blob/463d653124e1c2cda46a3f14f01f3d6f384326b1/backend/src/sidekick/services/context_builder_service.py#L2039-L2075
[s-dispatch]: https://github.com/datex/sidekick/blob/463d653124e1c2cda46a3f14f01f3d6f384326b1/backend/src/sidekick_worker/tools/skill_executor.py#L349-L412
