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;
13use crate::types::WebFetchOutput;
14use crate::types::WebSearchInput;
15use crate::types::WebSearchOutput;
16
17// ============================================================================
18// WebFetchTool
19// ============================================================================
20
21/// MCP tool for fetching web pages and converting to markdown.
22#[derive(Clone)]
23pub struct WebFetchTool {
24    tools: Arc<WebTools>,
25}
26
27impl WebFetchTool {
28    /// Create a new `WebFetchTool` with shared state.
29    #[must_use]
30    pub const fn new(tools: Arc<WebTools>) -> Self {
31        Self { tools }
32    }
33}
34
35impl Tool for WebFetchTool {
36    type Input = WebFetchInput;
37    type Output = WebFetchOutput;
38
39    const NAME: &'static str = "web_fetch";
40    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).";
41
42    fn call(
43        &self,
44        input: Self::Input,
45        ctx: &ToolContext,
46    ) -> BoxFuture<'static, Result<Self::Output, ToolError>> {
47        let tools = Arc::clone(&self.tools);
48        let ctx = ctx.clone();
49        Box::pin(async move { crate::fetch::web_fetch(&tools, input, &ctx).await })
50    }
51}
52
53// ============================================================================
54// WebSearchTool
55// ============================================================================
56
57/// MCP tool for semantic web search via Exa.
58#[derive(Clone)]
59pub struct WebSearchTool {
60    tools: Arc<WebTools>,
61}
62
63impl WebSearchTool {
64    /// Create a new `WebSearchTool` with shared state.
65    #[must_use]
66    pub const fn new(tools: Arc<WebTools>) -> Self {
67        Self { tools }
68    }
69}
70
71impl Tool for WebSearchTool {
72    type Input = WebSearchInput;
73    type Output = WebSearchOutput;
74
75    const NAME: &'static str = "web_search";
76    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.";
77
78    fn call(
79        &self,
80        input: Self::Input,
81        ctx: &ToolContext,
82    ) -> BoxFuture<'static, Result<Self::Output, ToolError>> {
83        let tools = Arc::clone(&self.tools);
84        let ctx = ctx.clone();
85        Box::pin(async move { crate::search::web_search(&tools, input, &ctx).await })
86    }
87}
88
89// ============================================================================
90// Registry Builder
91// ============================================================================
92
93/// Build a `ToolRegistry` containing all web tools.
94pub fn build_registry(tools: Arc<WebTools>) -> ToolRegistry {
95    ToolRegistry::builder()
96        .register::<WebFetchTool, ()>(WebFetchTool::new(Arc::clone(&tools)))
97        .register::<WebSearchTool, ()>(WebSearchTool::new(tools))
98        .finish()
99}