Extract tool discovery helpers into codex-tools (#16477)

## Why

Follow-up to #16379 and #16471.

`codex-rs/core/src/tools/spec.rs` still owned the pure discovery-shaping
helpers that turn app metadata and discoverable tool metadata into the
inputs used by `tool_search` and `tool_suggest`. Those helpers do not
need `codex-core` runtime state, so keeping them in `codex-core`
continued to blur the crate boundary this migration is trying to
tighten.

This change keeps pushing spec-only logic behind the `codex-tools` API
so `codex-core` can focus on wiring runtime handlers to the resulting
tool definitions.

## What Changed

- Added `collect_tool_search_app_infos` and
`collect_tool_suggest_entries` to
`codex-rs/tools/src/tool_discovery.rs`.
- Added a small `ToolSearchAppSource` adapter type in `codex-tools` so
`codex-core` can pass app metadata into that shared helper logic without
exposing `ToolInfo` across the crate boundary.
- Re-exported the new discovery helpers from
`codex-rs/tools/src/lib.rs`, which remains exports-only.
- Updated `codex-rs/core/src/tools/spec.rs` to use those `codex-tools`
helpers instead of maintaining local `tool_search_app_infos` and
`tool_suggest_entries` functions.
- Removed the now-redundant helper implementations from `codex-core`.

## Testing

- `cargo test -p codex-tools`
- `cargo test -p codex-core tools::spec::tests`
This commit is contained in:
Michael Bolin
2026-04-01 14:41:20 -07:00
committed by GitHub
parent 148dbb25f0
commit 1b711a5501
3 changed files with 74 additions and 57 deletions

View File

@@ -89,7 +89,10 @@ pub use tool_discovery::DiscoverableTool;
pub use tool_discovery::DiscoverableToolAction;
pub use tool_discovery::DiscoverableToolType;
pub use tool_discovery::ToolSearchAppInfo;
pub use tool_discovery::ToolSearchAppSource;
pub use tool_discovery::ToolSuggestEntry;
pub use tool_discovery::collect_tool_search_app_infos;
pub use tool_discovery::collect_tool_suggest_entries;
pub use tool_discovery::create_tool_search_tool;
pub use tool_discovery::create_tool_suggest_tool;
pub use tool_discovery::filter_tool_suggest_discoverable_tools_for_client;

View File

@@ -14,6 +14,13 @@ pub struct ToolSearchAppInfo {
pub description: Option<String>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ToolSearchAppSource<'a> {
pub server_name: &'a str,
pub connector_name: Option<&'a str>,
pub connector_description: Option<&'a str>,
}
#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum DiscoverableToolType {
@@ -178,6 +185,29 @@ pub fn create_tool_search_tool(app_tools: &[ToolSearchAppInfo], default_limit: u
}
}
pub fn collect_tool_search_app_infos<'a>(
app_tools: impl IntoIterator<Item = ToolSearchAppSource<'a>>,
codex_apps_server_name: &str,
) -> Vec<ToolSearchAppInfo> {
app_tools
.into_iter()
.filter(|tool| tool.server_name == codex_apps_server_name)
.filter_map(|tool| {
let name = tool
.connector_name
.map(str::trim)
.filter(|connector_name| !connector_name.is_empty())?
.to_string();
let description = tool
.connector_description
.map(str::trim)
.filter(|connector_description| !connector_description.is_empty())
.map(str::to_string);
Some(ToolSearchAppInfo { name, description })
})
.collect()
}
pub fn create_tool_suggest_tool(discoverable_tools: &[ToolSuggestEntry]) -> ToolSpec {
let discoverable_tool_ids = discoverable_tools
.iter()
@@ -245,6 +275,34 @@ pub fn create_tool_suggest_tool(discoverable_tools: &[ToolSuggestEntry]) -> Tool
})
}
pub fn collect_tool_suggest_entries(
discoverable_tools: &[DiscoverableTool],
) -> Vec<ToolSuggestEntry> {
discoverable_tools
.iter()
.map(|tool| match tool {
DiscoverableTool::Connector(connector) => ToolSuggestEntry {
id: connector.id.clone(),
name: connector.name.clone(),
description: connector.description.clone(),
tool_type: DiscoverableToolType::Connector,
has_skills: false,
mcp_server_names: Vec::new(),
app_connector_ids: Vec::new(),
},
DiscoverableTool::Plugin(plugin) => ToolSuggestEntry {
id: plugin.id.clone(),
name: plugin.name.clone(),
description: plugin.description.clone(),
tool_type: DiscoverableToolType::Plugin,
has_skills: plugin.has_skills,
mcp_server_names: plugin.mcp_server_names.clone(),
app_connector_ids: plugin.app_connector_ids.clone(),
},
})
.collect()
}
fn format_discoverable_tools(discoverable_tools: &[ToolSuggestEntry]) -> String {
let mut discoverable_tools = discoverable_tools.to_vec();
discoverable_tools.sort_by(|left, right| {