Make tool executor specs mandatory (#23870)

## Why

`ToolExecutor` is the runtime contract that keeps a callable tool and
its model-visible spec together. Leaving `spec()` optional lets a
registered runtime silently omit that half of the contract, and it also
overloads a missing spec as an exposure decision for tools that should
stay dispatchable without being shown to the model.

## What

- Make `ToolExecutor::spec()` required and update core, extension, and
test tool executors to return a concrete `ToolSpec`.
- Add `ToolExposure::Hidden` for dispatch-only tools. The legacy
`shell_command` runtime in unified-exec sessions now uses that explicit
exposure instead of hiding itself by omitting a spec.
- Build MCP tool specs when `McpHandler` is constructed so invalid MCP
specs are skipped before the handler is registered.
- Keep tool planning aligned with the new contract for direct, deferred,
hidden, code-mode, dynamic, and namespaced tool paths.

## Testing

- Added tool-plan coverage that invalid MCP tool specs are not
registered.
- Updated shell-family coverage for the hidden legacy `shell_command`
runtime and the affected tool executor test fixtures.
This commit is contained in:
jif-oai authored and GitHub committed 2026-05-21 15:25:56 +02:00
1 parent 94442b7f95
commit 516f134641
50 files changed
+304 -193

No files matched your search

+3 -3
View File
@@ -43,11 +43,11 @@ where
memory_tool_name(LIST_TOOL_NAME)
}
fn spec(&self) -> Option<ToolSpec> {
Some(memory_function_tool::<ListArgs, ListMemoriesResponse>(
fn spec(&self) -> ToolSpec {
memory_function_tool::<ListArgs, ListMemoriesResponse>(
LIST_TOOL_NAME,
"List immediate files and directories under a path in the Codex memories store.",
))
)
}
async fn handle(
+3 -3
View File
@@ -42,11 +42,11 @@ where
memory_tool_name(READ_TOOL_NAME)
}
fn spec(&self) -> Option<ToolSpec> {
Some(memory_function_tool::<ReadArgs, ReadMemoryResponse>(
fn spec(&self) -> ToolSpec {
memory_function_tool::<ReadArgs, ReadMemoryResponse>(
READ_TOOL_NAME,
"Read a Codex memory file by relative path, optionally starting at a 1-indexed line offset and limiting the number of lines returned.",
))
)
}
async fn handle(
+3 -3
View File
@@ -51,11 +51,11 @@ where
memory_tool_name(SEARCH_TOOL_NAME)
}
fn spec(&self) -> Option<ToolSpec> {
Some(memory_function_tool::<SearchArgs, SearchMemoriesResponse>(
fn spec(&self) -> ToolSpec {
memory_function_tool::<SearchArgs, SearchMemoriesResponse>(
SEARCH_TOOL_NAME,
"Search Codex memory files for substring matches, optionally normalizing separators or requiring all query substrings on the same line or within a line window.",
))
)
}
async fn handle(