firecrawl_mcp/controller/
batch_scrape.rs1use anyhow::Result;
2use async_claude::define_tool;
3use firecrawl_sdk::{
4 batch_scrape::{BatchScrapeUrlsInput, Webhook},
5 scrape::{ScrapeFormats, ScrapeOptions},
6};
7use rmcp::{handler::server::tool::parse_json_object, model::JsonObject};
8
9use super::FirecrawlMCP;
10
11pub const BATCH_SCRAPE_TOOL_NAME: &str = "firecrawl_batch_scrape";
12pub const BATCH_SCRAPE_TOOL_DESCRIPTION: &str = "Scrape multiple URLs in batch mode.";
13define_tool!(
14 FIRECRAWL_BATCH_SCRAPE,
15 BATCH_SCRAPE_TOOL_NAME,
16 BATCH_SCRAPE_TOOL_DESCRIPTION,
17 BatchScrapeUrlsInput
18);
19
20impl FirecrawlMCP {
21 pub async fn batch_scrape(&self, input: JsonObject) -> Result<String, rmcp::Error> {
22 let mut options = parse_json_object::<BatchScrapeUrlsInput>(input)?;
24
25 if options.webhook.is_none() {
27 options.webhook = Some(Webhook::dummy());
28 }
29
30 match &mut options.options {
31 Some(scrape_options) => {
32 scrape_options.formats = Some(vec![ScrapeFormats::Markdown]);
33 }
34 None => {
35 options.options = Some(ScrapeOptions {
36 formats: Some(vec![ScrapeFormats::Markdown]),
37 ..Default::default()
38 })
39 }
40 }
41
42 let result = self
43 .client
44 .batch_scrape_urls(
45 options.urls,
46 options.options,
47 options.poll_interval,
48 None,
49 options.webhook.unwrap(),
50 Some(true),
51 )
52 .await
53 .map_err(|e| rmcp::Error::internal_error(e.to_string(), None))?;
54
55 let formatted = result
56 .data
57 .iter()
58 .map(|d| {
59 format!(
60 "URL: {}\nTitle: {}\nContent: {}\n\n",
61 d.metadata.source_url,
62 d.metadata.title.as_ref().unwrap_or(&"".to_string()),
63 d.markdown.as_ref().unwrap_or(&"".to_string())
64 )
65 })
66 .collect::<Vec<_>>()
67 .join("\n\n");
68
69 Ok(formatted)
70 }
71}