Skip to main content

web_retrieval/
tools.rs

1//! Tool trait implementations and registry builder.
2
3use std::sync::Arc;
4
5use agentic_tools_core::ToolRegistry;
6use agentic_tools_core::context::ToolContext;
7use agentic_tools_core::error::ToolError;
8use agentic_tools_core::tool::Tool;
9use futures::future::BoxFuture;
10
11use crate::WebTools;
12use crate::types::{WebFetchInput, WebFetchOutput, WebSearchInput, WebSearchOutput};
13
14// ============================================================================
15// WebFetchTool
16// ============================================================================
17
18/// MCP tool for fetching web pages and converting to markdown.
19#[derive(Clone)]
20pub struct WebFetchTool {
21    tools: Arc<WebTools>,
22}
23
24impl WebFetchTool {
25    /// Create a new `WebFetchTool` with shared state.
26    #[must_use]
27    pub const fn new(tools: Arc<WebTools>) -> Self {
28        Self { tools }
29    }
30}
31
32impl Tool for WebFetchTool {
33    type Input = WebFetchInput;
34    type Output = WebFetchOutput;
35
36    const NAME: &'static str = "web_fetch";
37    const DESCRIPTION: &'static str = "Fetch a URL over HTTP and convert the page to clean Markdown with metadata. Default summarize=false; set summarize=true to generate a short Haiku summary (requires Anthropic credentials).";
38
39    fn call(
40        &self,
41        input: Self::Input,
42        _ctx: &ToolContext,
43    ) -> BoxFuture<'static, Result<Self::Output, ToolError>> {
44        let tools = self.tools.clone();
45        Box::pin(async move { crate::fetch::web_fetch(&tools, input).await })
46    }
47}
48
49// ============================================================================
50// WebSearchTool
51// ============================================================================
52
53/// MCP tool for semantic web search via Exa.
54#[derive(Clone)]
55pub struct WebSearchTool {
56    tools: Arc<WebTools>,
57}
58
59impl WebSearchTool {
60    /// Create a new `WebSearchTool` with shared state.
61    #[must_use]
62    pub const fn new(tools: Arc<WebTools>) -> Self {
63        Self { tools }
64    }
65}
66
67impl Tool for WebSearchTool {
68    type Input = WebSearchInput;
69    type Output = WebSearchOutput;
70
71    const NAME: &'static str = "web_search";
72    const DESCRIPTION: &'static str = "Semantic/neural web search (Exa). Use NATURAL LANGUAGE queries (questions/descriptions). Do NOT use keyword-stuffed, Google-style queries. Returns compact, citable result cards with URLs plus a short trimmed context to orient you.";
73
74    fn call(
75        &self,
76        input: Self::Input,
77        _ctx: &ToolContext,
78    ) -> BoxFuture<'static, Result<Self::Output, ToolError>> {
79        let tools = self.tools.clone();
80        Box::pin(async move { crate::search::web_search(&tools, input).await })
81    }
82}
83
84// ============================================================================
85// Registry Builder
86// ============================================================================
87
88/// Build a `ToolRegistry` containing all web tools.
89pub fn build_registry(tools: Arc<WebTools>) -> ToolRegistry {
90    ToolRegistry::builder()
91        .register::<WebFetchTool, ()>(WebFetchTool::new(tools.clone()))
92        .register::<WebSearchTool, ()>(WebSearchTool::new(tools))
93        .finish()
94}