mcp_ectors/examples/
counter_router.rs1use serde_json::Value;
2use std::sync::Arc;
3use tokio::sync::Mutex;
4use actix::ResponseFuture;
5
6use crate::router::{router::CapabilitiesBuilder, Router};
7use mcp_spec::{handler::ResourceError, prompt::Prompt, protocol::{CallToolResult, GetPromptResult, ReadResourceResult, ServerCapabilities}, Content, Resource, ResourceContents::TextResourceContents, Tool, ToolError};
8
9#[derive(Clone)]
11pub struct CounterRouter {
12 counter: Arc<Mutex<i32>>,
13}
14
15impl CounterRouter {
16 pub fn new() -> Self {
17 Self {
18 counter: Arc::new(Mutex::new(0)),
19 }
20 }
21
22 async fn increment(&self) -> Result<i32, ()> {
23 let mut counter = self.counter.lock().await;
24 *counter += 1;
25 Ok(*counter)
26 }
27
28 async fn decrement(&self) -> Result<i32, ()> {
29 let mut counter = self.counter.lock().await;
30 *counter -= 1;
31 Ok(*counter)
32 }
33
34 async fn get_value(&self) -> Result<i32, ()> {
35 let counter = self.counter.lock().await;
36 Ok(*counter)
37 }
38
39 fn create_resource_text(&self, uri: &str, name: &str) -> Resource {
40 Resource::new(uri, Some("text/plain".to_string()), Some(name.to_string())).unwrap()
41 }
42
43}
44
45
46impl Router for CounterRouter {
47 fn name(&self) -> String {
48 "counter".to_string()
49 }
50
51 fn instructions(&self) -> String {
52 "This server provides a counter tool that can increment and decrement values. The counter starts at 0 and can be modified using the 'increment' and 'decrement' tools. Use 'get_value' to check the current count.".to_string()
53 }
54
55 fn capabilities(&self) -> ServerCapabilities {
56 CapabilitiesBuilder::new()
57 .with_tools(false)
58 .with_resources(false, false)
59 .build()
60 }
61
62 fn list_tools(&self) -> Vec<Tool> {
63 vec![
64 Tool::new(
65 "increment".to_string(),
66 "Increment the counter by 1".to_string(),
67 serde_json::json!({
68 "type": "object",
69 "properties": {},
70 "required": []
71 }),
72 ),
73 Tool::new(
74 "decrement".to_string(),
75 "Decrement the counter by 1".to_string(),
76 serde_json::json!({
77 "type": "object",
78 "properties": {},
79 "required": []
80 }),
81 ),
82 Tool::new(
83 "get_value".to_string(),
84 "Get the current counter value".to_string(),
85 serde_json::json!({
86 "type": "object",
87 "properties": {},
88 "required": []
89 }),
90 ),
91 ]
92 }
93
94 fn call_tool(
95 &self,
96 tool_name: &str,
97 _arguments: Value,
98 ) -> ResponseFuture<Result<CallToolResult, ToolError>> {
99 let this = self.clone();
100 let tool_name = tool_name.to_string();
101
102 Box::pin(async move {
103 match tool_name.as_str() {
104 "increment" => {
105 let value = this.increment().await.expect("increment does not work");
106 let result = CallToolResult{ content: vec![Content::text(value.to_string())], is_error: Some(false) };
107 Ok(result)
108 }
109 "decrement" => {
110 let value = this.decrement().await.expect("decrement does not work");
111 let result = CallToolResult{ content: vec![Content::text(value.to_string())], is_error: Some(false) };
112 Ok(result)
113 }
114 "get_value" => {
115 let value = this.get_value().await.expect("get value does not work");
116 let result = CallToolResult{ content: vec![Content::text(value.to_string())], is_error: Some(false) };
117 Ok(result)
118 }
119 _ => Err(ToolError::NotFound(format!("Tool {} not found", tool_name))),
120 }
121 })
122 }
123
124 fn list_resources(&self) -> Vec<Resource> {
125 vec![
126 self.create_resource_text("str://///Users/maarten/ai/test", "cwd"),
127 self.create_resource_text("memo://insights", "memo-name"),
128 ]
129 }
130
131 fn read_resource(
132 &self,
133 uri: &str,
134 ) -> ResponseFuture<Result<ReadResourceResult, ResourceError>> {
135 let uri = uri.to_string();
136 Box::pin(async move {
137 match uri.as_str() {
138 "str://///Users/maarten/ai/test/" => {
139 let cwd = TextResourceContents{ uri, mime_type: Some("text/plain".to_string()), text: "/Users/maarten/ai/test/".to_string() };
140 let result = ReadResourceResult{ contents: vec![cwd] };
141 Ok(result)
142 }
143 "memo://insights" => {
144 let cwd = TextResourceContents{ uri, mime_type: Some("text/plain".to_string()), text: "Business Intelligence Memo\n\nAnalysis has revealed 5 key insights ...".to_string() };
145 let result = ReadResourceResult{ contents: vec![cwd] };
146 Ok(result)
147 }
148 _ => Err(ResourceError::NotFound(format!(
149 "Resource {} not found",
150 uri
151 ))),
152 }
153 })
154 }
155
156 fn list_prompts(&self) -> Vec<Prompt> {
157 vec![]
158 }
159
160 fn get_prompt(&self, _prompt_name: &str) -> ResponseFuture<Result<GetPromptResult, ResourceError>> {
161
162 let result = GetPromptResult{ description: None, messages: vec![] };
163 Box::pin(async move {
164 Ok(result)
165 })
166 }
167}