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        Box::pin(async move { crate::fetch::web_fetch(&tools, input).await })
49    }
50}
51
52// ============================================================================
53// WebSearchTool
54// ============================================================================
55
56/// MCP tool for semantic web search via Exa.
57#[derive(Clone)]
58pub struct WebSearchTool {
59    tools: Arc<WebTools>,
60}
61
62impl WebSearchTool {
63    /// Create a new `WebSearchTool` with shared state.
64    #[must_use]
65    pub const fn new(tools: Arc<WebTools>) -> Self {
66        Self { tools }
67    }
68}
69
70impl Tool for WebSearchTool {
71    type Input = WebSearchInput;
72    type Output = WebSearchOutput;
73
74    const NAME: &'static str = "web_search";
75    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.";
76
77    fn call(
78        &self,
79        input: Self::Input,
80        _ctx: &ToolContext,
81    ) -> BoxFuture<'static, Result<Self::Output, ToolError>> {
82        let tools = Arc::clone(&self.tools);
83        Box::pin(async move { crate::search::web_search(&tools, input).await })
84    }
85}
86
87// ============================================================================
88// Registry Builder
89// ============================================================================
90
91/// Build a `ToolRegistry` containing all web tools.
92pub fn build_registry(tools: Arc<WebTools>) -> ToolRegistry {
93    ToolRegistry::builder()
94        .register::<WebFetchTool, ()>(WebFetchTool::new(Arc::clone(&tools)))
95        .register::<WebSearchTool, ()>(WebSearchTool::new(tools))
96        .finish()
97}