1pub mod metadata;
2pub mod tool_collection;
3
4pub use metadata::ToolMetadata;
5pub use tool_collection::ToolCollection;
6
7use crate::error::Error;
8use crate::http_client::HttpClient;
9use reqwest::header::HeaderMap;
10use rmcp::model::{CallToolResult, Tool as McpTool};
11use rmcp_actix_web::transport::AuthorizationHeader;
12use serde_json::Value;
13use url::Url;
14
15#[derive(Clone)]
17pub struct Tool {
18 pub metadata: ToolMetadata,
19 http_client: HttpClient,
20}
21
22impl Tool {
23 pub fn new(
25 metadata: ToolMetadata,
26 base_url: Option<Url>,
27 default_headers: Option<HeaderMap>,
28 ) -> Result<Self, Error> {
29 let mut http_client = HttpClient::new();
30
31 if let Some(url) = base_url {
32 http_client = http_client.with_base_url(url)?;
33 }
34
35 if let Some(headers) = default_headers {
36 http_client = http_client.with_default_headers(headers);
37 }
38
39 Ok(Self {
40 metadata,
41 http_client,
42 })
43 }
44
45 pub async fn call(
47 &self,
48 arguments: &Value,
49 auth_header: Option<AuthorizationHeader>,
50 ) -> Result<CallToolResult, crate::error::ToolCallError> {
51 use rmcp::model::Content;
52 use serde_json::json;
53
54 let client = if let Some(auth) = auth_header {
56 self.http_client.with_authorization(&auth.0)
57 } else {
58 self.http_client.clone()
59 };
60
61 match client.execute_tool_call(&self.metadata, arguments).await {
63 Ok(response) => {
64 let structured_content = if self.metadata.output_schema.is_some() {
66 match response.json() {
68 Ok(json_value) => {
69 Some(json!({
71 "status": response.status_code,
72 "body": json_value
73 }))
74 }
75 Err(_) => None, }
77 } else {
78 None
79 };
80
81 let content = if let Some(ref structured) = structured_content {
83 match serde_json::to_string(structured) {
87 Ok(json_string) => vec![Content::text(json_string)],
88 Err(e) => {
89 let error = crate::error::ToolCallError::Execution(
91 crate::error::ToolCallExecutionError::ResponseParsingError {
92 reason: format!("Failed to serialize structured content: {e}"),
93 raw_response: None,
94 },
95 );
96 return Err(error);
97 }
98 }
99 } else {
100 vec![Content::text(response.to_mcp_content())]
101 };
102
103 Ok(CallToolResult {
105 content,
106 structured_content,
107 is_error: Some(!response.is_success),
108 })
109 }
110 Err(e) => {
111 Err(e)
113 }
114 }
115 }
116
117 pub async fn execute(
119 &self,
120 arguments: &Value,
121 auth_header: Option<AuthorizationHeader>,
122 ) -> Result<crate::http_client::HttpResponse, crate::error::ToolCallError> {
123 let client = if let Some(auth) = auth_header {
125 self.http_client.with_authorization(&auth.0)
126 } else {
127 self.http_client.clone()
128 };
129
130 client.execute_tool_call(&self.metadata, arguments).await
133 }
134}
135
136impl From<&Tool> for McpTool {
138 fn from(tool: &Tool) -> Self {
139 (&tool.metadata).into()
140 }
141}