1use mcpkit_core::error::McpError;
17use mcpkit_core::protocol::Request;
18use serde_json::Value;
19
20pub mod methods {
22 pub const INITIALIZE: &str = "initialize";
24 pub const PING: &str = "ping";
26
27 pub const TOOLS_LIST: &str = "tools/list";
29 pub const TOOLS_CALL: &str = "tools/call";
31
32 pub const RESOURCES_LIST: &str = "resources/list";
34 pub const RESOURCES_READ: &str = "resources/read";
36 pub const RESOURCES_TEMPLATES_LIST: &str = "resources/templates/list";
38 pub const RESOURCES_SUBSCRIBE: &str = "resources/subscribe";
40 pub const RESOURCES_UNSUBSCRIBE: &str = "resources/unsubscribe";
42
43 pub const PROMPTS_LIST: &str = "prompts/list";
45 pub const PROMPTS_GET: &str = "prompts/get";
47
48 pub const TASKS_LIST: &str = "tasks/list";
50 pub const TASKS_GET: &str = "tasks/get";
52 pub const TASKS_CANCEL: &str = "tasks/cancel";
54
55 pub const SAMPLING_CREATE_MESSAGE: &str = "sampling/createMessage";
57
58 pub const COMPLETION_COMPLETE: &str = "completion/complete";
60
61 pub const LOGGING_SET_LEVEL: &str = "logging/setLevel";
63
64 pub const ELICITATION_CREATE: &str = "elicitation/create";
66}
67
68pub mod notifications {
70 pub const INITIALIZED: &str = "notifications/initialized";
72 pub const CANCELLED: &str = "notifications/cancelled";
74 pub const PROGRESS: &str = "notifications/progress";
76 pub const MESSAGE: &str = "notifications/message";
78 pub const RESOURCES_UPDATED: &str = "notifications/resources/updated";
80 pub const RESOURCES_LIST_CHANGED: &str = "notifications/resources/list_changed";
82 pub const TOOLS_LIST_CHANGED: &str = "notifications/tools/list_changed";
84 pub const PROMPTS_LIST_CHANGED: &str = "notifications/prompts/list_changed";
86}
87
88#[derive(Debug)]
93pub enum ParsedRequest {
94 Initialize(InitializeParams),
96 Ping,
98
99 ToolsList(ListParams),
101 ToolsCall(ToolCallParams),
103
104 ResourcesList(ListParams),
106 ResourcesRead(ResourceReadParams),
108 ResourcesTemplatesList(ListParams),
110 ResourcesSubscribe(ResourceSubscribeParams),
112 ResourcesUnsubscribe(ResourceUnsubscribeParams),
114
115 PromptsList(ListParams),
117 PromptsGet(PromptGetParams),
119
120 TasksList(ListParams),
122 TasksGet(TaskGetParams),
124 TasksCancel(TaskCancelParams),
126
127 SamplingCreateMessage(SamplingParams),
129
130 CompletionComplete(CompletionParams),
132
133 LoggingSetLevel(LogLevelParams),
135
136 Unknown(String),
138}
139
140#[derive(Debug, Default)]
142pub struct ListParams {
143 pub cursor: Option<String>,
145}
146
147#[derive(Debug)]
149pub struct InitializeParams {
150 pub protocol_version: String,
152 pub client_info: ClientInfo,
154 pub capabilities: Value,
156}
157
158#[derive(Debug)]
160pub struct ClientInfo {
161 pub name: String,
163 pub version: String,
165}
166
167#[derive(Debug)]
169pub struct ToolCallParams {
170 pub name: String,
172 pub arguments: Value,
174}
175
176#[derive(Debug)]
178pub struct ResourceReadParams {
179 pub uri: String,
181}
182
183#[derive(Debug)]
185pub struct ResourceSubscribeParams {
186 pub uri: String,
188}
189
190#[derive(Debug)]
192pub struct ResourceUnsubscribeParams {
193 pub uri: String,
195}
196
197#[derive(Debug)]
199pub struct PromptGetParams {
200 pub name: String,
202 pub arguments: Option<Value>,
204}
205
206#[derive(Debug)]
208pub struct TaskGetParams {
209 pub task_id: String,
211}
212
213#[derive(Debug)]
215pub struct TaskCancelParams {
216 pub task_id: String,
218}
219
220#[derive(Debug)]
222pub struct SamplingParams {
223 pub messages: Vec<Value>,
225 pub model_preferences: Option<Value>,
227 pub system_prompt: Option<String>,
229 pub max_tokens: Option<u32>,
231}
232
233#[derive(Debug)]
235pub struct CompletionParams {
236 pub ref_type: String,
238 pub ref_value: String,
240 pub argument: Option<CompletionArgument>,
242}
243
244#[derive(Debug)]
246pub struct CompletionArgument {
247 pub name: String,
249 pub value: String,
251}
252
253#[derive(Debug)]
255pub struct LogLevelParams {
256 pub level: String,
258}
259
260pub fn parse_request(request: &Request) -> Result<ParsedRequest, McpError> {
262 let method = request.method.as_ref();
263 let params = request.params.as_ref();
264
265 match method {
266 methods::INITIALIZE => {
267 let params =
268 params.ok_or_else(|| McpError::invalid_params(method, "missing params"))?;
269
270 Ok(ParsedRequest::Initialize(InitializeParams {
271 protocol_version: params
272 .get("protocolVersion")
273 .and_then(|v| v.as_str())
274 .unwrap_or("unknown")
275 .to_string(),
276 client_info: ClientInfo {
277 name: params
278 .get("clientInfo")
279 .and_then(|v| v.get("name"))
280 .and_then(|v| v.as_str())
281 .unwrap_or("unknown")
282 .to_string(),
283 version: params
284 .get("clientInfo")
285 .and_then(|v| v.get("version"))
286 .and_then(|v| v.as_str())
287 .unwrap_or("unknown")
288 .to_string(),
289 },
290 capabilities: params
291 .get("capabilities")
292 .cloned()
293 .unwrap_or_else(|| Value::Object(serde_json::Map::new())),
294 }))
295 }
296
297 methods::PING => Ok(ParsedRequest::Ping),
298
299 methods::TOOLS_LIST => Ok(ParsedRequest::ToolsList(parse_list_params(params))),
300
301 methods::TOOLS_CALL => {
302 let params =
303 params.ok_or_else(|| McpError::invalid_params(method, "missing params"))?;
304
305 let name = params
306 .get("name")
307 .and_then(|v| v.as_str())
308 .ok_or_else(|| McpError::invalid_params(method, "missing name"))?
309 .to_string();
310
311 let arguments = params
312 .get("arguments")
313 .cloned()
314 .unwrap_or_else(|| Value::Object(serde_json::Map::new()));
315
316 Ok(ParsedRequest::ToolsCall(ToolCallParams { name, arguments }))
317 }
318
319 methods::RESOURCES_LIST => Ok(ParsedRequest::ResourcesList(parse_list_params(params))),
320
321 methods::RESOURCES_READ => {
322 let params =
323 params.ok_or_else(|| McpError::invalid_params(method, "missing params"))?;
324
325 let uri = params
326 .get("uri")
327 .and_then(|v| v.as_str())
328 .ok_or_else(|| McpError::invalid_params(method, "missing uri"))?
329 .to_string();
330
331 Ok(ParsedRequest::ResourcesRead(ResourceReadParams { uri }))
332 }
333
334 methods::RESOURCES_TEMPLATES_LIST => Ok(ParsedRequest::ResourcesTemplatesList(
335 parse_list_params(params),
336 )),
337
338 methods::RESOURCES_SUBSCRIBE => {
339 let params =
340 params.ok_or_else(|| McpError::invalid_params(method, "missing params"))?;
341
342 let uri = params
343 .get("uri")
344 .and_then(|v| v.as_str())
345 .ok_or_else(|| McpError::invalid_params(method, "missing uri"))?
346 .to_string();
347
348 Ok(ParsedRequest::ResourcesSubscribe(ResourceSubscribeParams {
349 uri,
350 }))
351 }
352
353 methods::RESOURCES_UNSUBSCRIBE => {
354 let params =
355 params.ok_or_else(|| McpError::invalid_params(method, "missing params"))?;
356
357 let uri = params
358 .get("uri")
359 .and_then(|v| v.as_str())
360 .ok_or_else(|| McpError::invalid_params(method, "missing uri"))?
361 .to_string();
362
363 Ok(ParsedRequest::ResourcesUnsubscribe(
364 ResourceUnsubscribeParams { uri },
365 ))
366 }
367
368 methods::PROMPTS_LIST => Ok(ParsedRequest::PromptsList(parse_list_params(params))),
369
370 methods::PROMPTS_GET => {
371 let params =
372 params.ok_or_else(|| McpError::invalid_params(method, "missing params"))?;
373
374 let name = params
375 .get("name")
376 .and_then(|v| v.as_str())
377 .ok_or_else(|| McpError::invalid_params(method, "missing name"))?
378 .to_string();
379
380 let arguments = params.get("arguments").cloned();
381
382 Ok(ParsedRequest::PromptsGet(PromptGetParams {
383 name,
384 arguments,
385 }))
386 }
387
388 methods::TASKS_LIST => Ok(ParsedRequest::TasksList(parse_list_params(params))),
389
390 methods::TASKS_GET => {
391 let params =
392 params.ok_or_else(|| McpError::invalid_params(method, "missing params"))?;
393
394 let task_id = params
395 .get("taskId")
396 .and_then(|v| v.as_str())
397 .ok_or_else(|| McpError::invalid_params(method, "missing taskId"))?
398 .to_string();
399
400 Ok(ParsedRequest::TasksGet(TaskGetParams { task_id }))
401 }
402
403 methods::TASKS_CANCEL => {
404 let params =
405 params.ok_or_else(|| McpError::invalid_params(method, "missing params"))?;
406
407 let task_id = params
408 .get("taskId")
409 .and_then(|v| v.as_str())
410 .ok_or_else(|| McpError::invalid_params(method, "missing taskId"))?
411 .to_string();
412
413 Ok(ParsedRequest::TasksCancel(TaskCancelParams { task_id }))
414 }
415
416 methods::SAMPLING_CREATE_MESSAGE => {
417 let params =
418 params.ok_or_else(|| McpError::invalid_params(method, "missing params"))?;
419
420 let messages = params
421 .get("messages")
422 .and_then(|v| v.as_array())
423 .ok_or_else(|| McpError::invalid_params(method, "missing messages"))?
424 .clone();
425
426 Ok(ParsedRequest::SamplingCreateMessage(SamplingParams {
427 messages,
428 model_preferences: params.get("modelPreferences").cloned(),
429 system_prompt: params
430 .get("systemPrompt")
431 .and_then(|v| v.as_str())
432 .map(String::from),
433 max_tokens: params
434 .get("maxTokens")
435 .and_then(serde_json::Value::as_u64)
436 .map(|v| v as u32),
437 }))
438 }
439
440 methods::COMPLETION_COMPLETE => {
441 let params =
442 params.ok_or_else(|| McpError::invalid_params(method, "missing params"))?;
443
444 let ref_obj = params
445 .get("ref")
446 .ok_or_else(|| McpError::invalid_params(method, "missing ref"))?;
447
448 Ok(ParsedRequest::CompletionComplete(CompletionParams {
449 ref_type: ref_obj
450 .get("type")
451 .and_then(|v| v.as_str())
452 .unwrap_or("")
453 .to_string(),
454 ref_value: ref_obj
455 .get("uri")
456 .or_else(|| ref_obj.get("name"))
457 .and_then(|v| v.as_str())
458 .unwrap_or("")
459 .to_string(),
460 argument: params.get("argument").map(|arg| CompletionArgument {
461 name: arg
462 .get("name")
463 .and_then(|v| v.as_str())
464 .unwrap_or("")
465 .to_string(),
466 value: arg
467 .get("value")
468 .and_then(|v| v.as_str())
469 .unwrap_or("")
470 .to_string(),
471 }),
472 }))
473 }
474
475 methods::LOGGING_SET_LEVEL => {
476 let params =
477 params.ok_or_else(|| McpError::invalid_params(method, "missing params"))?;
478
479 let level = params
480 .get("level")
481 .and_then(|v| v.as_str())
482 .ok_or_else(|| McpError::invalid_params(method, "missing level"))?
483 .to_string();
484
485 Ok(ParsedRequest::LoggingSetLevel(LogLevelParams { level }))
486 }
487
488 _ => Ok(ParsedRequest::Unknown(method.to_string())),
489 }
490}
491
492fn parse_list_params(params: Option<&Value>) -> ListParams {
494 ListParams {
495 cursor: params
496 .and_then(|p| p.get("cursor"))
497 .and_then(|v| v.as_str())
498 .map(String::from),
499 }
500}
501
502use crate::context::Context;
510use crate::handler::{PromptHandler, ResourceHandler, ToolHandler};
511use mcpkit_core::types::CallToolResult;
512
513pub async fn route_tools<TH: ToolHandler + Send + Sync>(
526 handler: &TH,
527 method: &str,
528 params: Option<&serde_json::Value>,
529 ctx: &Context<'_>,
530) -> Option<Result<serde_json::Value, McpError>> {
531 match method {
532 methods::TOOLS_LIST => {
533 tracing::debug!("Listing available tools");
534 let result = handler.list_tools(ctx).await;
535 match &result {
536 Ok(tools) => tracing::debug!(count = tools.len(), "Listed tools"),
537 Err(e) => tracing::warn!(error = %e, "Failed to list tools"),
538 }
539 Some(result.map(|tools| serde_json::json!({ "tools": tools })))
540 }
541 methods::TOOLS_CALL => {
542 let result = async {
543 let params = params.ok_or_else(|| {
544 McpError::invalid_params(methods::TOOLS_CALL, "missing params")
545 })?;
546 let name = params.get("name").and_then(|v| v.as_str()).ok_or_else(|| {
547 McpError::invalid_params(methods::TOOLS_CALL, "missing tool name")
548 })?;
549 let args = params
550 .get("arguments")
551 .cloned()
552 .unwrap_or_else(|| serde_json::json!({}));
553
554 tracing::info!(tool = %name, "Calling tool");
555 let start = std::time::Instant::now();
556 let output = handler.call_tool(name, args, ctx).await;
557 let duration = start.elapsed();
558
559 match &output {
560 Ok(_) => tracing::info!(
561 tool = %name,
562 duration_ms = duration.as_millis(),
563 "Tool call completed"
564 ),
565 Err(e) => tracing::warn!(
566 tool = %name,
567 duration_ms = duration.as_millis(),
568 error = %e,
569 "Tool call failed"
570 ),
571 }
572
573 let output = output?;
574 let result: CallToolResult = output.into();
575 Ok(serde_json::to_value(result).unwrap_or_else(|_| serde_json::json!({})))
576 }
577 .await;
578 Some(result)
579 }
580 _ => None,
581 }
582}
583
584pub async fn route_resources<RH: ResourceHandler + Send + Sync>(
597 handler: &RH,
598 method: &str,
599 params: Option<&serde_json::Value>,
600 ctx: &Context<'_>,
601) -> Option<Result<serde_json::Value, McpError>> {
602 match method {
603 methods::RESOURCES_LIST => {
604 tracing::debug!("Listing available resources");
605 let result = handler.list_resources(ctx).await;
606 match &result {
607 Ok(resources) => tracing::debug!(count = resources.len(), "Listed resources"),
608 Err(e) => tracing::warn!(error = %e, "Failed to list resources"),
609 }
610 Some(result.map(|resources| serde_json::json!({ "resources": resources })))
611 }
612 methods::RESOURCES_TEMPLATES_LIST => {
613 tracing::debug!("Listing available resource templates");
614 let result = handler.list_resource_templates(ctx).await;
615 match &result {
616 Ok(templates) => {
617 tracing::debug!(count = templates.len(), "Listed resource templates");
618 }
619 Err(e) => tracing::warn!(error = %e, "Failed to list resource templates"),
620 }
621 Some(result.map(|templates| serde_json::json!({ "resourceTemplates": templates })))
622 }
623 methods::RESOURCES_READ => {
624 let result = async {
625 let params = params.ok_or_else(|| {
626 McpError::invalid_params(methods::RESOURCES_READ, "missing params")
627 })?;
628 let uri = params.get("uri").and_then(|v| v.as_str()).ok_or_else(|| {
629 McpError::invalid_params(methods::RESOURCES_READ, "missing uri")
630 })?;
631
632 tracing::info!(uri = %uri, "Reading resource");
633 let start = std::time::Instant::now();
634 let contents = handler.read_resource(uri, ctx).await;
635 let duration = start.elapsed();
636
637 match &contents {
638 Ok(_) => tracing::info!(
639 uri = %uri,
640 duration_ms = duration.as_millis(),
641 "Resource read completed"
642 ),
643 Err(e) => tracing::warn!(
644 uri = %uri,
645 duration_ms = duration.as_millis(),
646 error = %e,
647 "Resource read failed"
648 ),
649 }
650
651 let contents = contents?;
652 Ok(serde_json::json!({ "contents": contents }))
653 }
654 .await;
655 Some(result)
656 }
657 _ => None,
658 }
659}
660
661pub async fn route_prompts<PH: PromptHandler + Send + Sync>(
674 handler: &PH,
675 method: &str,
676 params: Option<&serde_json::Value>,
677 ctx: &Context<'_>,
678) -> Option<Result<serde_json::Value, McpError>> {
679 match method {
680 methods::PROMPTS_LIST => {
681 tracing::debug!("Listing available prompts");
682 let result = handler.list_prompts(ctx).await;
683 match &result {
684 Ok(prompts) => tracing::debug!(count = prompts.len(), "Listed prompts"),
685 Err(e) => tracing::warn!(error = %e, "Failed to list prompts"),
686 }
687 Some(result.map(|prompts| serde_json::json!({ "prompts": prompts })))
688 }
689 methods::PROMPTS_GET => {
690 let result = async {
691 let params = params.ok_or_else(|| {
692 McpError::invalid_params(methods::PROMPTS_GET, "missing params")
693 })?;
694 let name = params.get("name").and_then(|v| v.as_str()).ok_or_else(|| {
695 McpError::invalid_params(methods::PROMPTS_GET, "missing prompt name")
696 })?;
697 let args = params.get("arguments").and_then(|v| v.as_object()).cloned();
698
699 tracing::info!(prompt = %name, "Getting prompt");
700 let start = std::time::Instant::now();
701 let prompt_result = handler.get_prompt(name, args, ctx).await;
702 let duration = start.elapsed();
703
704 match &prompt_result {
705 Ok(_) => tracing::info!(
706 prompt = %name,
707 duration_ms = duration.as_millis(),
708 "Prompt retrieval completed"
709 ),
710 Err(e) => tracing::warn!(
711 prompt = %name,
712 duration_ms = duration.as_millis(),
713 error = %e,
714 "Prompt retrieval failed"
715 ),
716 }
717
718 let result = prompt_result?;
719 Ok(serde_json::to_value(result).unwrap_or_else(|_| serde_json::json!({})))
720 }
721 .await;
722 Some(result)
723 }
724 _ => None,
725 }
726}
727
728#[cfg(test)]
729mod tests {
730 use super::*;
731 use mcpkit_core::protocol::Request;
732
733 fn make_request(method: &'static str, params: Option<Value>) -> Request {
734 if let Some(p) = params {
735 Request::with_params(method, 1u64, p)
736 } else {
737 Request::new(method, 1u64)
738 }
739 }
740
741 #[test]
742 fn test_parse_ping() -> Result<(), Box<dyn std::error::Error>> {
743 let request = make_request("ping", None);
744 let parsed = parse_request(&request)?;
745 assert!(matches!(parsed, ParsedRequest::Ping));
746
747 Ok(())
748 }
749
750 #[test]
751 fn test_parse_tools_list() -> Result<(), Box<dyn std::error::Error>> {
752 let request = make_request("tools/list", None);
753 let parsed = parse_request(&request)?;
754 assert!(matches!(parsed, ParsedRequest::ToolsList(_)));
755
756 Ok(())
757 }
758
759 #[test]
760 fn test_parse_tools_list_with_cursor() -> Result<(), Box<dyn std::error::Error>> {
761 let request = make_request(
762 "tools/list",
763 Some(serde_json::json!({ "cursor": "abc123" })),
764 );
765 let parsed = parse_request(&request)?;
766
767 if let ParsedRequest::ToolsList(params) = parsed {
768 assert_eq!(params.cursor, Some("abc123".to_string()));
769 } else {
770 panic!("Expected ToolsList");
771 }
772
773 Ok(())
774 }
775
776 #[test]
777 fn test_parse_tools_call() -> Result<(), Box<dyn std::error::Error>> {
778 let request = make_request(
779 "tools/call",
780 Some(serde_json::json!({
781 "name": "search",
782 "arguments": {"query": "test"}
783 })),
784 );
785 let parsed = parse_request(&request)?;
786
787 if let ParsedRequest::ToolsCall(params) = parsed {
788 assert_eq!(params.name, "search");
789 assert_eq!(params.arguments["query"], "test");
790 } else {
791 panic!("Expected ToolsCall");
792 }
793
794 Ok(())
795 }
796
797 #[test]
798 fn test_parse_tools_call_missing_params() {
799 let request = make_request("tools/call", None);
800 let result = parse_request(&request);
801 assert!(result.is_err());
802 }
803
804 #[test]
805 fn test_parse_tools_call_missing_name() {
806 let request = make_request("tools/call", Some(serde_json::json!({"arguments": {}})));
807 let result = parse_request(&request);
808 assert!(result.is_err());
809 }
810
811 #[test]
812 fn test_parse_unknown_method() -> Result<(), Box<dyn std::error::Error>> {
813 let request = make_request("unknown/method", None);
814 let parsed = parse_request(&request)?;
815
816 if let ParsedRequest::Unknown(method) = parsed {
817 assert_eq!(method, "unknown/method");
818 } else {
819 panic!("Expected Unknown");
820 }
821
822 Ok(())
823 }
824
825 #[test]
826 fn test_parse_initialize() -> Result<(), Box<dyn std::error::Error>> {
827 let request = make_request(
828 "initialize",
829 Some(serde_json::json!({
830 "protocolVersion": "2025-11-25",
831 "clientInfo": {
832 "name": "test-client",
833 "version": "1.0.0"
834 },
835 "capabilities": {}
836 })),
837 );
838 let parsed = parse_request(&request)?;
839
840 if let ParsedRequest::Initialize(params) = parsed {
841 assert_eq!(params.protocol_version, "2025-11-25");
842 assert_eq!(params.client_info.name, "test-client");
843 assert_eq!(params.client_info.version, "1.0.0");
844 } else {
845 panic!("Expected Initialize");
846 }
847
848 Ok(())
849 }
850
851 #[test]
852 fn test_parse_initialize_missing_params() {
853 let request = make_request("initialize", None);
854 let result = parse_request(&request);
855 assert!(result.is_err());
856 }
857
858 #[test]
863 fn test_parse_resources_list() -> Result<(), Box<dyn std::error::Error>> {
864 let request = make_request("resources/list", None);
865 let parsed = parse_request(&request)?;
866 assert!(matches!(parsed, ParsedRequest::ResourcesList(_)));
867 Ok(())
868 }
869
870 #[test]
871 fn test_parse_resources_list_with_cursor() -> Result<(), Box<dyn std::error::Error>> {
872 let request = make_request(
873 "resources/list",
874 Some(serde_json::json!({ "cursor": "page2" })),
875 );
876 let parsed = parse_request(&request)?;
877
878 if let ParsedRequest::ResourcesList(params) = parsed {
879 assert_eq!(params.cursor, Some("page2".to_string()));
880 } else {
881 panic!("Expected ResourcesList");
882 }
883 Ok(())
884 }
885
886 #[test]
887 fn test_parse_resources_read() -> Result<(), Box<dyn std::error::Error>> {
888 let request = make_request(
889 "resources/read",
890 Some(serde_json::json!({ "uri": "file:///etc/hosts" })),
891 );
892 let parsed = parse_request(&request)?;
893
894 if let ParsedRequest::ResourcesRead(params) = parsed {
895 assert_eq!(params.uri, "file:///etc/hosts");
896 } else {
897 panic!("Expected ResourcesRead");
898 }
899 Ok(())
900 }
901
902 #[test]
903 fn test_parse_resources_read_missing_uri() {
904 let request = make_request("resources/read", Some(serde_json::json!({})));
905 let result = parse_request(&request);
906 assert!(result.is_err());
907 }
908
909 #[test]
910 fn test_parse_resources_templates_list() -> Result<(), Box<dyn std::error::Error>> {
911 let request = make_request("resources/templates/list", None);
912 let parsed = parse_request(&request)?;
913 assert!(matches!(parsed, ParsedRequest::ResourcesTemplatesList(_)));
914 Ok(())
915 }
916
917 #[test]
918 fn test_parse_resources_subscribe() -> Result<(), Box<dyn std::error::Error>> {
919 let request = make_request(
920 "resources/subscribe",
921 Some(serde_json::json!({ "uri": "file:///var/log/app.log" })),
922 );
923 let parsed = parse_request(&request)?;
924
925 if let ParsedRequest::ResourcesSubscribe(params) = parsed {
926 assert_eq!(params.uri, "file:///var/log/app.log");
927 } else {
928 panic!("Expected ResourcesSubscribe");
929 }
930 Ok(())
931 }
932
933 #[test]
934 fn test_parse_resources_unsubscribe() -> Result<(), Box<dyn std::error::Error>> {
935 let request = make_request(
936 "resources/unsubscribe",
937 Some(serde_json::json!({ "uri": "file:///var/log/app.log" })),
938 );
939 let parsed = parse_request(&request)?;
940
941 if let ParsedRequest::ResourcesUnsubscribe(params) = parsed {
942 assert_eq!(params.uri, "file:///var/log/app.log");
943 } else {
944 panic!("Expected ResourcesUnsubscribe");
945 }
946 Ok(())
947 }
948
949 #[test]
954 fn test_parse_prompts_list() -> Result<(), Box<dyn std::error::Error>> {
955 let request = make_request("prompts/list", None);
956 let parsed = parse_request(&request)?;
957 assert!(matches!(parsed, ParsedRequest::PromptsList(_)));
958 Ok(())
959 }
960
961 #[test]
962 fn test_parse_prompts_get() -> Result<(), Box<dyn std::error::Error>> {
963 let request = make_request(
964 "prompts/get",
965 Some(serde_json::json!({
966 "name": "code-review",
967 "arguments": { "language": "rust" }
968 })),
969 );
970 let parsed = parse_request(&request)?;
971
972 if let ParsedRequest::PromptsGet(params) = parsed {
973 assert_eq!(params.name, "code-review");
974 assert!(params.arguments.is_some());
975 } else {
976 panic!("Expected PromptsGet");
977 }
978 Ok(())
979 }
980
981 #[test]
982 fn test_parse_prompts_get_without_arguments() -> Result<(), Box<dyn std::error::Error>> {
983 let request = make_request(
984 "prompts/get",
985 Some(serde_json::json!({ "name": "simple-prompt" })),
986 );
987 let parsed = parse_request(&request)?;
988
989 if let ParsedRequest::PromptsGet(params) = parsed {
990 assert_eq!(params.name, "simple-prompt");
991 assert!(params.arguments.is_none());
992 } else {
993 panic!("Expected PromptsGet");
994 }
995 Ok(())
996 }
997
998 #[test]
999 fn test_parse_prompts_get_missing_name() {
1000 let request = make_request("prompts/get", Some(serde_json::json!({})));
1001 let result = parse_request(&request);
1002 assert!(result.is_err());
1003 }
1004
1005 #[test]
1010 fn test_parse_tasks_list() -> Result<(), Box<dyn std::error::Error>> {
1011 let request = make_request("tasks/list", None);
1012 let parsed = parse_request(&request)?;
1013 assert!(matches!(parsed, ParsedRequest::TasksList(_)));
1014 Ok(())
1015 }
1016
1017 #[test]
1018 fn test_parse_tasks_get() -> Result<(), Box<dyn std::error::Error>> {
1019 let request = make_request(
1020 "tasks/get",
1021 Some(serde_json::json!({ "taskId": "task-123" })),
1022 );
1023 let parsed = parse_request(&request)?;
1024
1025 if let ParsedRequest::TasksGet(params) = parsed {
1026 assert_eq!(params.task_id, "task-123");
1027 } else {
1028 panic!("Expected TasksGet");
1029 }
1030 Ok(())
1031 }
1032
1033 #[test]
1034 fn test_parse_tasks_get_missing_id() {
1035 let request = make_request("tasks/get", Some(serde_json::json!({})));
1036 let result = parse_request(&request);
1037 assert!(result.is_err());
1038 }
1039
1040 #[test]
1041 fn test_parse_tasks_cancel() -> Result<(), Box<dyn std::error::Error>> {
1042 let request = make_request(
1043 "tasks/cancel",
1044 Some(serde_json::json!({ "taskId": "task-456" })),
1045 );
1046 let parsed = parse_request(&request)?;
1047
1048 if let ParsedRequest::TasksCancel(params) = parsed {
1049 assert_eq!(params.task_id, "task-456");
1050 } else {
1051 panic!("Expected TasksCancel");
1052 }
1053 Ok(())
1054 }
1055
1056 #[test]
1061 fn test_parse_sampling_create_message() -> Result<(), Box<dyn std::error::Error>> {
1062 let request = make_request(
1063 "sampling/createMessage",
1064 Some(serde_json::json!({
1065 "messages": [
1066 { "role": "user", "content": { "type": "text", "text": "Hello" } }
1067 ],
1068 "maxTokens": 100,
1069 "systemPrompt": "You are a helpful assistant."
1070 })),
1071 );
1072 let parsed = parse_request(&request)?;
1073
1074 if let ParsedRequest::SamplingCreateMessage(params) = parsed {
1075 assert_eq!(params.messages.len(), 1);
1076 assert_eq!(params.max_tokens, Some(100));
1077 assert_eq!(
1078 params.system_prompt,
1079 Some("You are a helpful assistant.".to_string())
1080 );
1081 } else {
1082 panic!("Expected SamplingCreateMessage");
1083 }
1084 Ok(())
1085 }
1086
1087 #[test]
1088 fn test_parse_sampling_create_message_missing_messages() {
1089 let request = make_request("sampling/createMessage", Some(serde_json::json!({})));
1090 let result = parse_request(&request);
1091 assert!(result.is_err());
1092 }
1093
1094 #[test]
1099 fn test_parse_completion_complete() -> Result<(), Box<dyn std::error::Error>> {
1100 let request = make_request(
1101 "completion/complete",
1102 Some(serde_json::json!({
1103 "ref": {
1104 "type": "ref/resource",
1105 "uri": "file:///home"
1106 },
1107 "argument": {
1108 "name": "path",
1109 "value": "/home/user"
1110 }
1111 })),
1112 );
1113 let parsed = parse_request(&request)?;
1114
1115 if let ParsedRequest::CompletionComplete(params) = parsed {
1116 assert_eq!(params.ref_type, "ref/resource");
1117 assert_eq!(params.ref_value, "file:///home");
1118 assert!(params.argument.is_some());
1119 let arg = params.argument.unwrap();
1120 assert_eq!(arg.name, "path");
1121 assert_eq!(arg.value, "/home/user");
1122 } else {
1123 panic!("Expected CompletionComplete");
1124 }
1125 Ok(())
1126 }
1127
1128 #[test]
1129 fn test_parse_completion_complete_prompt_ref() -> Result<(), Box<dyn std::error::Error>> {
1130 let request = make_request(
1131 "completion/complete",
1132 Some(serde_json::json!({
1133 "ref": {
1134 "type": "ref/prompt",
1135 "name": "code-review"
1136 }
1137 })),
1138 );
1139 let parsed = parse_request(&request)?;
1140
1141 if let ParsedRequest::CompletionComplete(params) = parsed {
1142 assert_eq!(params.ref_type, "ref/prompt");
1143 assert_eq!(params.ref_value, "code-review");
1144 assert!(params.argument.is_none());
1145 } else {
1146 panic!("Expected CompletionComplete");
1147 }
1148 Ok(())
1149 }
1150
1151 #[test]
1152 fn test_parse_completion_complete_missing_ref() {
1153 let request = make_request("completion/complete", Some(serde_json::json!({})));
1154 let result = parse_request(&request);
1155 assert!(result.is_err());
1156 }
1157
1158 #[test]
1163 fn test_parse_logging_set_level() -> Result<(), Box<dyn std::error::Error>> {
1164 let request = make_request(
1165 "logging/setLevel",
1166 Some(serde_json::json!({ "level": "debug" })),
1167 );
1168 let parsed = parse_request(&request)?;
1169
1170 if let ParsedRequest::LoggingSetLevel(params) = parsed {
1171 assert_eq!(params.level, "debug");
1172 } else {
1173 panic!("Expected LoggingSetLevel");
1174 }
1175 Ok(())
1176 }
1177
1178 #[test]
1179 fn test_parse_logging_set_level_missing_level() {
1180 let request = make_request("logging/setLevel", Some(serde_json::json!({})));
1181 let result = parse_request(&request);
1182 assert!(result.is_err());
1183 }
1184
1185 #[test]
1190 fn test_method_constants() {
1191 assert_eq!(methods::INITIALIZE, "initialize");
1193 assert_eq!(methods::PING, "ping");
1194 assert_eq!(methods::TOOLS_LIST, "tools/list");
1195 assert_eq!(methods::TOOLS_CALL, "tools/call");
1196 assert_eq!(methods::RESOURCES_LIST, "resources/list");
1197 assert_eq!(methods::RESOURCES_READ, "resources/read");
1198 assert_eq!(
1199 methods::RESOURCES_TEMPLATES_LIST,
1200 "resources/templates/list"
1201 );
1202 assert_eq!(methods::RESOURCES_SUBSCRIBE, "resources/subscribe");
1203 assert_eq!(methods::RESOURCES_UNSUBSCRIBE, "resources/unsubscribe");
1204 assert_eq!(methods::PROMPTS_LIST, "prompts/list");
1205 assert_eq!(methods::PROMPTS_GET, "prompts/get");
1206 assert_eq!(methods::TASKS_LIST, "tasks/list");
1207 assert_eq!(methods::TASKS_GET, "tasks/get");
1208 assert_eq!(methods::TASKS_CANCEL, "tasks/cancel");
1209 assert_eq!(methods::SAMPLING_CREATE_MESSAGE, "sampling/createMessage");
1210 assert_eq!(methods::COMPLETION_COMPLETE, "completion/complete");
1211 assert_eq!(methods::LOGGING_SET_LEVEL, "logging/setLevel");
1212 }
1213
1214 #[test]
1219 fn test_notification_constants() {
1220 assert_eq!(notifications::INITIALIZED, "notifications/initialized");
1221 assert_eq!(notifications::CANCELLED, "notifications/cancelled");
1222 assert_eq!(notifications::PROGRESS, "notifications/progress");
1223 assert_eq!(notifications::MESSAGE, "notifications/message");
1224 assert_eq!(
1225 notifications::RESOURCES_UPDATED,
1226 "notifications/resources/updated"
1227 );
1228 assert_eq!(
1229 notifications::RESOURCES_LIST_CHANGED,
1230 "notifications/resources/list_changed"
1231 );
1232 assert_eq!(
1233 notifications::TOOLS_LIST_CHANGED,
1234 "notifications/tools/list_changed"
1235 );
1236 assert_eq!(
1237 notifications::PROMPTS_LIST_CHANGED,
1238 "notifications/prompts/list_changed"
1239 );
1240 }
1241}