mofa_plugins/tools/
http.rs1use super::*;
2use reqwest::Client;
3use serde_json::json;
4
5pub struct HttpRequestTool {
7 definition: ToolDefinition,
8 client: Client,
9}
10
11impl Default for HttpRequestTool {
12 fn default() -> Self {
13 Self::new()
14 }
15}
16
17impl HttpRequestTool {
18 pub fn new() -> Self {
19 Self {
20 definition: ToolDefinition {
21 name: "http_request".to_string(),
22 description: "Send HTTP requests (GET, POST, PUT, DELETE) to URLs. Useful for fetching web content or calling APIs.".to_string(),
23 parameters: json!({
24 "type": "object",
25 "properties": {
26 "method": {
27 "type": "string",
28 "enum": ["GET", "POST", "PUT", "DELETE"],
29 "description": "HTTP method"
30 },
31 "url": {
32 "type": "string",
33 "description": "The URL to send the request to"
34 },
35 "headers": {
36 "type": "object",
37 "description": "Optional HTTP headers as key-value pairs",
38 "additionalProperties": { "type": "string" }
39 },
40 "body": {
41 "type": "string",
42 "description": "Optional request body for POST/PUT requests"
43 }
44 },
45 "required": ["method", "url"]
46 }),
47 requires_confirmation: false,
48 },
49 client: Client::builder()
50 .timeout(std::time::Duration::from_secs(30))
51 .build()
52 .unwrap(),
53 }
54 }
55}
56
57#[async_trait::async_trait]
58impl ToolExecutor for HttpRequestTool {
59 fn definition(&self) -> &ToolDefinition {
60 &self.definition
61 }
62
63 async fn execute(&self, arguments: serde_json::Value) -> PluginResult<serde_json::Value> {
64 let method = arguments["method"].as_str().unwrap_or("GET");
65 let url = arguments["url"]
66 .as_str()
67 .ok_or_else(|| anyhow::anyhow!("URL is required"))?;
68
69 let mut request = match method {
70 "GET" => self.client.get(url),
71 "POST" => self.client.post(url),
72 "PUT" => self.client.put(url),
73 "DELETE" => self.client.delete(url),
74 _ => return Err(anyhow::anyhow!("Unsupported HTTP method: {}", method)),
75 };
76
77 if let Some(headers) = arguments.get("headers").and_then(|h| h.as_object()) {
79 for (key, value) in headers {
80 if let Some(v) = value.as_str() {
81 request = request.header(key.as_str(), v);
82 }
83 }
84 }
85
86 if let Some(body) = arguments.get("body").and_then(|b| b.as_str()) {
88 request = request.body(body.to_string());
89 }
90
91 let response = request.send().await?;
92 let status = response.status().as_u16();
93 let headers: std::collections::HashMap<String, String> = response
94 .headers()
95 .iter()
96 .map(|(k, v)| (k.to_string(), v.to_str().unwrap_or("").to_string()))
97 .collect();
98
99 let body = response.text().await?;
100
101 let truncated_body = if body.len() > 5000 {
103 format!(
104 "{}... [truncated, total {} bytes]",
105 &body[..5000],
106 body.len()
107 )
108 } else {
109 body
110 };
111
112 Ok(json!({
113 "status": status,
114 "headers": headers,
115 "body": truncated_body
116 }))
117 }
118}