snm_brightdata_client/tools/
crypto.rs1use crate::tool::{Tool, ToolResult, McpContent};
3use crate::error::BrightDataError;
4use async_trait::async_trait;
5use reqwest::Client;
6use serde_json::{json, Value};
7use std::env;
8use std::time::Duration;
9
10pub struct CryptoDataTool;
11
12#[async_trait]
13impl Tool for CryptoDataTool {
14 fn name(&self) -> &str {
15 "get_crypto_data"
16 }
17
18 fn description(&self) -> &str {
19 "Get cryptocurrency data including prices, market cap, trading volumes"
20 }
21
22 fn input_schema(&self) -> Value {
23 json!({
24 "type": "object",
25 "properties": {
26 "query": {
27 "type": "string",
28 "description": "Crypto symbol (BTC, ETH, ADA), crypto name (Bitcoin, Ethereum), comparison query (BTC vs ETH), or market overview (crypto market today, top cryptocurrencies)"
29 }
30 },
31 "required": ["query"]
32 })
33 }
34
35 async fn execute(&self, parameters: Value) -> Result<ToolResult, BrightDataError> {
36 let query = parameters
37 .get("query")
38 .and_then(|v| v.as_str())
39 .ok_or_else(|| BrightDataError::ToolError("Missing 'query' parameter".into()))?;
40
41 let result = self.fetch_crypto_data(query).await?;
42
43 let content_text = result.get("content").and_then(|c| c.as_str()).unwrap_or("No crypto data found");
44 let mcp_content = vec![McpContent::text(format!(
45 "₿ **Cryptocurrency Data for {}**\n\n{}",
46 query,
47 content_text
48 ))];
49
50 Ok(ToolResult::success_with_raw(mcp_content, result))
51 }
52}
53
54impl CryptoDataTool {
55 async fn fetch_crypto_data(&self, query: &str) -> Result<Value, BrightDataError> {
56 let api_token = env::var("BRIGHTDATA_API_TOKEN")
57 .or_else(|_| env::var("API_TOKEN"))
58 .map_err(|_| BrightDataError::ToolError("Missing BRIGHTDATA_API_TOKEN".into()))?;
59
60 let base_url = env::var("BRIGHTDATA_BASE_URL")
61 .unwrap_or_else(|_| "https://api.brightdata.com".to_string());
62
63 let zone = env::var("WEB_UNLOCKER_ZONE")
64 .unwrap_or_else(|_| "default".to_string());
65
66 let search_url = format!(
68 "https://www.google.com/search?q={} cryptocurrency price market cap coinmarketcap",
69 urlencoding::encode(query)
70 );
71
72 let payload = json!({
73 "url": search_url,
74 "zone": zone,
75 "format": "raw",
76 "data_format": "markdown"
77 });
78
79 let client = Client::builder()
80 .timeout(Duration::from_secs(120))
81 .build()
82 .map_err(|e| BrightDataError::ToolError(e.to_string()))?;
83
84 let response = client
85 .post(&format!("{}/request", base_url))
86 .header("Authorization", format!("Bearer {}", api_token))
87 .header("Content-Type", "application/json")
88 .json(&payload)
89 .send()
90 .await
91 .map_err(|e| BrightDataError::ToolError(format!("Crypto data request failed: {}", e)))?;
92
93 let status = response.status();
94 if !status.is_success() {
95 let error_text = response.text().await.unwrap_or_default();
96 return Err(BrightDataError::ToolError(format!(
97 "BrightData crypto data error {}: {}",
98 status, error_text
99 )));
100 }
101
102 let content = response.text().await
103 .map_err(|e| BrightDataError::ToolError(e.to_string()))?;
104
105 Ok(json!({
106 "content": content,
107 "query": query,
108 "success": true
109 }))
110 }
111}