Codex Handbook
core/src/tools/handlers/tool_search_spec.rs 113 lines
use codex_tools::JsonSchema;use codex_tools::TOOL_SEARCH_TOOL_NAME;use codex_tools::ToolSearchSourceInfo;use codex_tools::ToolSpec;use std::collections::BTreeMap;pub(crate) fn create_tool_search_tool(    searchable_sources: &[ToolSearchSourceInfo],    default_limit: usize,) -> ToolSpec {    let properties = BTreeMap::from([        (            "query".to_string(),            JsonSchema::string(Some("Search query for deferred tools.".to_string())),        ),        (            "limit".to_string(),            JsonSchema::number(Some(format!(                "Maximum number of tools to return. Defaults to {default_limit}."            ))),        ),    ]);    let mut source_descriptions = BTreeMap::new();    for source in searchable_sources {        source_descriptions            .entry(source.name.clone())            .and_modify(|existing: &mut Option<String>| {                if existing.is_none() {                    *existing = source.description.clone();                }            })            .or_insert(source.description.clone());    }    let source_descriptions = if source_descriptions.is_empty() {        "None currently enabled.".to_string()    } else {        source_descriptions            .into_iter()            .map(|(name, description)| match description {                Some(description) => format!("- {name}: {description}"),                None => format!("- {name}"),            })            .collect::<Vec<_>>()            .join("\n")    };    let description = format!(        "# Tool discovery\n\nSearches over deferred tool metadata with BM25 and exposes matching tools for the next model call.\n\nYou have access to tools from the following sources:\n{source_descriptions}\nSome of the tools may not have been provided to you upfront, and you should use this tool (`{TOOL_SEARCH_TOOL_NAME}`) to search for the required tools. For MCP tool discovery, always use `{TOOL_SEARCH_TOOL_NAME}` instead of `list_mcp_resources` or `list_mcp_resource_templates`."    );    ToolSpec::ToolSearch {        execution: "client".to_string(),        description,        parameters: JsonSchema::object(            properties,            Some(vec!["query".to_string()]),            Some(false.into()),        ),    }}#[cfg(test)]mod tests {    use super::*;    use codex_tools::JsonSchema;    use pretty_assertions::assert_eq;    use std::collections::BTreeMap;    #[test]    fn create_tool_search_tool_deduplicates_and_renders_enabled_sources() {        assert_eq!(            create_tool_search_tool(                &[                    ToolSearchSourceInfo {                        name: "Google Drive".to_string(),                        description: Some(                            "Use Google Drive as the single entrypoint for Drive, Docs, Sheets, and Slides work."                                .to_string(),                        ),                    },                    ToolSearchSourceInfo {                        name: "Google Drive".to_string(),                        description: None,                    },                    ToolSearchSourceInfo {                        name: "docs".to_string(),                        description: None,                    },                ],                /*default_limit*/ 8,            ),            ToolSpec::ToolSearch {                execution: "client".to_string(),                description: "# Tool discovery\n\nSearches over deferred tool metadata with BM25 and exposes matching tools for the next model call.\n\nYou have access to tools from the following sources:\n- Google Drive: Use Google Drive as the single entrypoint for Drive, Docs, Sheets, and Slides work.\n- docs\nSome of the tools may not have been provided to you upfront, and you should use this tool (`tool_search`) to search for the required tools. For MCP tool discovery, always use `tool_search` instead of `list_mcp_resources` or `list_mcp_resource_templates`.".to_string(),                parameters: JsonSchema::object(BTreeMap::from([                        (                            "limit".to_string(),                            JsonSchema::number(Some(                                    "Maximum number of tools to return. Defaults to 8."                                        .to_string(),                                ),),                        ),                        (                            "query".to_string(),                            JsonSchema::string(Some("Search query for deferred tools.".to_string()),),                        ),                    ]), Some(vec!["query".to_string()]), Some(false.into())),            }        );    }}