httpmcp_rust/
metadata.rs

1use crate::protocol::*;
2use serde_json::json;
3
4/// Metadata builder for tools
5#[derive(Debug, Clone)]
6pub struct ToolMeta {
7    description: Option<String>,
8    params: Vec<ParamMeta>,
9    required: Vec<String>,
10}
11
12#[derive(Debug, Clone)]
13struct ParamMeta {
14    name: String,
15    param_type: String,
16    description: String,
17}
18
19impl ToolMeta {
20    pub fn new() -> Self {
21        Self {
22            description: None,
23            params: Vec::new(),
24            required: Vec::new(),
25        }
26    }
27
28    pub fn description(mut self, desc: impl Into<String>) -> Self {
29        self.description = Some(desc.into());
30        self
31    }
32
33    pub fn param(
34        mut self,
35        name: impl Into<String>,
36        param_type: impl Into<String>,
37        description: impl Into<String>,
38    ) -> Self {
39        self.params.push(ParamMeta {
40            name: name.into(),
41            param_type: param_type.into(),
42            description: description.into(),
43        });
44        self
45    }
46
47    pub fn required(mut self, fields: &[&str]) -> Self {
48        self.required = fields.iter().map(|s| s.to_string()).collect();
49        self
50    }
51
52    pub fn to_tool(&self, name: impl Into<String>) -> Tool {
53        let mut properties = serde_json::Map::new();
54
55        for param in &self.params {
56            properties.insert(
57                param.name.clone(),
58                json!({
59                    "type": param.param_type,
60                    "description": param.description
61                }),
62            );
63        }
64
65        let mut schema = json!({
66            "type": "object",
67            "properties": properties
68        });
69
70        if !self.required.is_empty() {
71            schema["required"] = json!(self.required);
72        }
73
74        Tool {
75            name: name.into(),
76            description: self.description.clone(),
77            input_schema: schema,
78        }
79    }
80}
81
82impl Default for ToolMeta {
83    fn default() -> Self {
84        Self::new()
85    }
86}
87
88/// Metadata builder for resources
89#[derive(Debug, Clone)]
90pub struct ResourceMeta {
91    name: String,
92    description: Option<String>,
93    mime_type: Option<String>,
94}
95
96impl ResourceMeta {
97    pub fn new() -> Self {
98        Self {
99            name: String::new(),
100            description: None,
101            mime_type: None,
102        }
103    }
104
105    pub fn name(mut self, name: impl Into<String>) -> Self {
106        self.name = name.into();
107        self
108    }
109
110    pub fn description(mut self, desc: impl Into<String>) -> Self {
111        self.description = Some(desc.into());
112        self
113    }
114
115    pub fn mime_type(mut self, mime: impl Into<String>) -> Self {
116        self.mime_type = Some(mime.into());
117        self
118    }
119
120    pub fn to_resource(&self, uri: impl Into<String>) -> Resource {
121        Resource {
122            uri: uri.into(),
123            name: self.name.clone(),
124            description: self.description.clone(),
125            mime_type: self.mime_type.clone(),
126        }
127    }
128}
129
130impl Default for ResourceMeta {
131    fn default() -> Self {
132        Self::new()
133    }
134}
135
136/// Metadata builder for prompts
137#[derive(Debug, Clone)]
138pub struct PromptMeta {
139    description: Option<String>,
140    arguments: Vec<PromptArgumentMeta>,
141}
142
143#[derive(Debug, Clone)]
144struct PromptArgumentMeta {
145    name: String,
146    description: Option<String>,
147    required: bool,
148}
149
150impl PromptMeta {
151    pub fn new() -> Self {
152        Self {
153            description: None,
154            arguments: Vec::new(),
155        }
156    }
157
158    pub fn description(mut self, desc: impl Into<String>) -> Self {
159        self.description = Some(desc.into());
160        self
161    }
162
163    pub fn arg(
164        mut self,
165        name: impl Into<String>,
166        description: impl Into<String>,
167        required: bool,
168    ) -> Self {
169        self.arguments.push(PromptArgumentMeta {
170            name: name.into(),
171            description: Some(description.into()),
172            required,
173        });
174        self
175    }
176
177    pub fn to_prompt(&self, name: impl Into<String>) -> Prompt {
178        let arguments = if self.arguments.is_empty() {
179            None
180        } else {
181            Some(
182                self.arguments
183                    .iter()
184                    .map(|arg| PromptArgument {
185                        name: arg.name.clone(),
186                        description: arg.description.clone(),
187                        required: Some(arg.required),
188                    })
189                    .collect(),
190            )
191        };
192
193        Prompt {
194            name: name.into(),
195            description: self.description.clone(),
196            arguments,
197        }
198    }
199}
200
201impl Default for PromptMeta {
202    fn default() -> Self {
203        Self::new()
204    }
205}
206
207/// Metadata builder for endpoints
208#[derive(Debug, Clone)]
209pub struct EndpointMeta {
210    route: String,
211    method: String,
212    description: Option<String>,
213}
214
215impl EndpointMeta {
216    pub fn new() -> Self {
217        Self {
218            route: String::new(),
219            method: "GET".to_string(),
220            description: None,
221        }
222    }
223
224    pub fn route(mut self, route: impl Into<String>) -> Self {
225        self.route = route.into();
226        self
227    }
228
229    pub fn method(mut self, method: impl Into<String>) -> Self {
230        self.method = method.into().to_uppercase();
231        self
232    }
233
234    pub fn description(mut self, desc: impl Into<String>) -> Self {
235        self.description = Some(desc.into());
236        self
237    }
238
239    pub fn get_route(&self) -> &str {
240        &self.route
241    }
242
243    pub fn get_method(&self) -> &str {
244        &self.method
245    }
246
247    pub fn get_description(&self) -> Option<&str> {
248        self.description.as_deref()
249    }
250}
251
252impl Default for EndpointMeta {
253    fn default() -> Self {
254        Self::new()
255    }
256}