spec_kit_mcp/mcp/
protocol.rs1use anyhow::{Context, Result};
6use serde_json::{json, Value};
7
8use super::types::*;
9
10pub struct ProtocolHandler;
12
13impl ProtocolHandler {
14 pub fn new() -> Self {
16 Self
17 }
18
19 pub fn validate_request(&self, request: &JsonRpcRequest) -> Result<()> {
21 if request.jsonrpc != "2.0" {
23 anyhow::bail!("Invalid JSON-RPC version: {}", request.jsonrpc);
24 }
25
26 if request.method.is_empty() {
28 anyhow::bail!("Method cannot be empty");
29 }
30
31 Ok(())
32 }
33
34 pub fn parse_tool_call(&self, params: Option<Value>) -> Result<ToolCallParams> {
36 let params = params.ok_or_else(|| anyhow::anyhow!("Missing parameters"))?;
37
38 serde_json::from_value(params).context("Failed to parse tool call parameters")
39 }
40
41 pub fn create_tool_list_response(
43 &self,
44 id: RequestId,
45 tools: Vec<ToolDefinition>,
46 ) -> JsonRpcResponse {
47 JsonRpcResponse::success(
48 id,
49 json!({
50 "tools": tools
51 }),
52 )
53 }
54
55 pub fn create_tool_result_response(
57 &self,
58 id: RequestId,
59 result: ToolResult,
60 ) -> JsonRpcResponse {
61 JsonRpcResponse::success(id, serde_json::to_value(result).unwrap())
62 }
63
64 pub fn create_error_response(&self, id: RequestId, error: anyhow::Error) -> JsonRpcResponse {
66 let error_msg = format!("{:#}", error);
67
68 tracing::error!(error = %error_msg, "Request failed");
69
70 JsonRpcResponse::error(id, JsonRpcError::internal_error(error_msg))
71 }
72
73 pub fn handle_initialize(&self, id: RequestId) -> JsonRpcResponse {
75 tracing::info!("Handling initialize request");
76
77 JsonRpcResponse::success(
78 id,
79 json!({
80 "protocolVersion": "2024-11-05",
81 "serverInfo": {
82 "name": "spec-kit-mcp",
83 "version": env!("CARGO_PKG_VERSION")
84 },
85 "capabilities": {
86 "tools": {}
87 }
88 }),
89 )
90 }
91
92 pub fn handle_ping(&self, id: RequestId) -> JsonRpcResponse {
94 JsonRpcResponse::success(id, json!({}))
95 }
96}
97
98impl Default for ProtocolHandler {
99 fn default() -> Self {
100 Self::new()
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn test_validate_request_valid() {
110 let handler = ProtocolHandler::new();
111 let request = JsonRpcRequest {
112 jsonrpc: "2.0".to_string(),
113 id: RequestId::Number(1),
114 method: "test".to_string(),
115 params: None,
116 };
117
118 assert!(handler.validate_request(&request).is_ok());
119 }
120
121 #[test]
122 fn test_validate_request_invalid_version() {
123 let handler = ProtocolHandler::new();
124 let request = JsonRpcRequest {
125 jsonrpc: "1.0".to_string(),
126 id: RequestId::Number(1),
127 method: "test".to_string(),
128 params: None,
129 };
130
131 assert!(handler.validate_request(&request).is_err());
132 }
133
134 #[test]
135 fn test_validate_request_empty_method() {
136 let handler = ProtocolHandler::new();
137 let request = JsonRpcRequest {
138 jsonrpc: "2.0".to_string(),
139 id: RequestId::Number(1),
140 method: "".to_string(),
141 params: None,
142 };
143
144 assert!(handler.validate_request(&request).is_err());
145 }
146
147 #[test]
148 fn test_initialize_response() {
149 let handler = ProtocolHandler::new();
150 let response = handler.handle_initialize(RequestId::Number(1));
151
152 assert!(response.error.is_none());
153 assert!(response.result.is_some());
154
155 let result = response.result.unwrap();
156 assert_eq!(result["serverInfo"]["name"], "spec-kit-mcp");
157 }
158
159 #[test]
160 fn test_ping_response() {
161 let handler = ProtocolHandler::new();
162 let response = handler.handle_ping(RequestId::Number(1));
163
164 assert!(response.error.is_none());
165 assert!(response.result.is_some());
166 }
167}