1use crate::mock::{MockPrompt, MockResource, MockTool};
6use mcpkit_core::types::{Prompt, PromptArgument, Resource, Tool, ToolAnnotations, ToolOutput};
7
8pub fn sample_tools() -> Vec<MockTool> {
16 vec![
17 MockTool::new("echo")
18 .description("Echo back the input")
19 .input_schema(serde_json::json!({
20 "type": "object",
21 "properties": {
22 "message": { "type": "string" }
23 },
24 "required": ["message"]
25 }))
26 .handler(|args| {
27 let message = args
28 .get("message")
29 .and_then(|v| v.as_str())
30 .unwrap_or("(no message)");
31 Ok(ToolOutput::text(message))
32 }),
33 MockTool::new("add")
34 .description("Add two numbers")
35 .input_schema(serde_json::json!({
36 "type": "object",
37 "properties": {
38 "a": { "type": "number" },
39 "b": { "type": "number" }
40 },
41 "required": ["a", "b"]
42 }))
43 .annotations(ToolAnnotations::read_only())
44 .handler(|args| {
45 let a = args.get("a").and_then(|v| v.as_f64()).unwrap_or(0.0);
46 let b = args.get("b").and_then(|v| v.as_f64()).unwrap_or(0.0);
47 Ok(ToolOutput::text(format!("{}", a + b)))
48 }),
49 MockTool::new("multiply")
50 .description("Multiply two numbers")
51 .input_schema(serde_json::json!({
52 "type": "object",
53 "properties": {
54 "a": { "type": "number" },
55 "b": { "type": "number" }
56 },
57 "required": ["a", "b"]
58 }))
59 .annotations(ToolAnnotations::read_only())
60 .handler(|args| {
61 let a = args.get("a").and_then(|v| v.as_f64()).unwrap_or(0.0);
62 let b = args.get("b").and_then(|v| v.as_f64()).unwrap_or(0.0);
63 Ok(ToolOutput::text(format!("{}", a * b)))
64 }),
65 MockTool::new("fail")
66 .description("Always fails")
67 .returns_error("This tool always fails"),
68 ]
69}
70
71pub fn sample_resources() -> Vec<MockResource> {
78 vec![
79 MockResource::new("test://readme", "README")
80 .description("A sample README file")
81 .mime_type("text/markdown")
82 .content("# Test Server\n\nThis is a test server."),
83 MockResource::new("test://config", "Configuration")
84 .description("Server configuration")
85 .mime_type("application/json")
86 .content(r#"{"debug": true, "port": 8080}"#),
87 MockResource::new("test://data", "Sample Data")
88 .description("Sample data for testing")
89 .mime_type("application/json")
90 .content(r#"{"items": [1, 2, 3], "count": 3}"#),
91 ]
92}
93
94pub fn sample_prompts() -> Vec<MockPrompt> {
100 vec![
101 MockPrompt::new("summarize")
102 .description("Summarize the given text")
103 .template("Please summarize the following text:\n\n{{text}}"),
104 MockPrompt::new("translate")
105 .description("Translate text to another language")
106 .template("Translate the following text to {{language}}:\n\n{{text}}"),
107 ]
108}
109
110pub fn tool_definition(name: &str, description: &str) -> Tool {
112 Tool::new(name).description(description)
113}
114
115pub fn resource_definition(uri: &str, name: &str) -> Resource {
117 Resource::new(uri, name)
118}
119
120pub fn prompt_definition(name: &str) -> Prompt {
122 Prompt::new(name)
123}
124
125pub fn prompt_with_args(
127 name: &str,
128 description: &str,
129 args: Vec<(&str, &str, bool)>,
130) -> Prompt {
131 let mut prompt = Prompt::new(name).description(description);
132 for (arg_name, arg_desc, required) in args {
133 let arg = if required {
134 PromptArgument::required(arg_name, arg_desc)
135 } else {
136 PromptArgument::optional(arg_name, arg_desc)
137 };
138 prompt = prompt.argument(arg);
139 }
140 prompt
141}
142
143pub fn calculator_tools() -> Vec<MockTool> {
147 vec![
148 MockTool::new("add")
149 .description("Add two numbers together")
150 .input_schema(serde_json::json!({
151 "type": "object",
152 "properties": {
153 "a": { "type": "number", "description": "First number" },
154 "b": { "type": "number", "description": "Second number" }
155 },
156 "required": ["a", "b"]
157 }))
158 .handler(|args| {
159 let a = args.get("a").and_then(|v| v.as_f64()).unwrap_or(0.0);
160 let b = args.get("b").and_then(|v| v.as_f64()).unwrap_or(0.0);
161 Ok(ToolOutput::text(format!("{}", a + b)))
162 }),
163 MockTool::new("subtract")
164 .description("Subtract two numbers")
165 .handler(|args| {
166 let a = args.get("a").and_then(|v| v.as_f64()).unwrap_or(0.0);
167 let b = args.get("b").and_then(|v| v.as_f64()).unwrap_or(0.0);
168 Ok(ToolOutput::text(format!("{}", a - b)))
169 }),
170 MockTool::new("multiply")
171 .description("Multiply two numbers")
172 .handler(|args| {
173 let a = args.get("a").and_then(|v| v.as_f64()).unwrap_or(0.0);
174 let b = args.get("b").and_then(|v| v.as_f64()).unwrap_or(0.0);
175 Ok(ToolOutput::text(format!("{}", a * b)))
176 }),
177 MockTool::new("divide")
178 .description("Divide two numbers")
179 .handler(|args| {
180 let a = args.get("a").and_then(|v| v.as_f64()).unwrap_or(0.0);
181 let b = args.get("b").and_then(|v| v.as_f64()).unwrap_or(0.0);
182 if b == 0.0 {
183 Ok(ToolOutput::error("Cannot divide by zero"))
184 } else {
185 Ok(ToolOutput::text(format!("{}", a / b)))
186 }
187 }),
188 ]
189}
190
191pub fn filesystem_resources() -> Vec<MockResource> {
195 vec![
196 MockResource::new("file:///project/src/main.rs", "main.rs")
197 .mime_type("text/x-rust")
198 .content("fn main() {\n println!(\"Hello, world!\");\n}"),
199 MockResource::new("file:///project/Cargo.toml", "Cargo.toml")
200 .mime_type("text/x-toml")
201 .content("[package]\nname = \"test\"\nversion = \"0.1.0\""),
202 MockResource::new("file:///project/README.md", "README.md")
203 .mime_type("text/markdown")
204 .content("# Test Project\n\nA test project."),
205 ]
206}
207
208#[cfg(test)]
209mod tests {
210 use super::*;
211
212 #[test]
213 fn test_sample_tools() {
214 let tools = sample_tools();
215 assert_eq!(tools.len(), 4);
216 assert!(tools.iter().any(|t| t.name == "echo"));
217 assert!(tools.iter().any(|t| t.name == "add"));
218 }
219
220 #[test]
221 fn test_sample_resources() {
222 let resources = sample_resources();
223 assert_eq!(resources.len(), 3);
224 assert!(resources.iter().any(|r| r.uri == "test://readme"));
225 }
226
227 #[test]
228 fn test_calculator_tools() {
229 let tools = calculator_tools();
230 assert_eq!(tools.len(), 4);
231
232 let add = tools.iter().find(|t| t.name == "add").unwrap();
234 let result = add.call(serde_json::json!({"a": 5, "b": 3})).unwrap();
235 match result {
236 ToolOutput::Success(r) => {
237 if let mcpkit_core::types::Content::Text(tc) = &r.content[0] {
238 assert_eq!(tc.text, "8");
239 }
240 }
241 _ => panic!("Expected success"),
242 }
243
244 let divide = tools.iter().find(|t| t.name == "divide").unwrap();
246 let result = divide.call(serde_json::json!({"a": 5, "b": 0})).unwrap();
247 match result {
248 ToolOutput::RecoverableError { message, .. } => {
249 assert!(message.contains("zero"));
250 }
251 _ => panic!("Expected error"),
252 }
253 }
254
255 #[test]
256 fn test_prompt_with_args() {
257 let prompt = prompt_with_args(
258 "test",
259 "A test prompt",
260 vec![
261 ("required_arg", "A required argument", true),
262 ("optional_arg", "An optional argument", false),
263 ],
264 );
265
266 assert_eq!(prompt.name, "test");
267 let args = prompt.arguments.unwrap();
268 assert_eq!(args.len(), 2);
269 assert!(args[0].required.unwrap());
270 assert!(!args[1].required.unwrap());
271 }
272}