use std::sync::Arc; use serde_json::json; use crate::{ backend_client::BackendClient, error::{McpError, Result}, protocol::{JsonRpcError, JsonRpcRequest, JsonRpcResponse, ToolCallParams}, registry::ToolRegistry, }; pub struct RequestHandler { backend: Arc, registry: Arc, } impl RequestHandler { pub fn new(backend: Arc, registry: Arc) -> Self { Self { backend, registry } } pub async fn handle(&self, request: &JsonRpcRequest) -> JsonRpcResponse { if request.jsonrpc != "2.0" { return JsonRpcResponse { jsonrpc: "2.0".to_string(), id: request.id.clone(), result: None, error: Some(JsonRpcError { code: -32600, message: "Invalid Request: jsonrpc must be 2.0".to_string(), data: None, }), }; } let result = match request.method.as_str() { "initialize" => self.handle_initialize(request).await, "tools/list" => self.handle_tools_list(request).await, "tools/call" => self.handle_tools_call(request).await, "resources/list" => self.handle_resources_list(request).await, "prompts/list" => self.handle_prompts_list(request).await, method => Err(McpError::MethodNotFound(method.to_string())), }; match result { Ok(result_value) => JsonRpcResponse { jsonrpc: "2.0".to_string(), id: request.id.clone(), result: Some(result_value), error: None, }, Err(e) => { let error_obj = e.to_json_rpc_error(); JsonRpcResponse { jsonrpc: "2.0".to_string(), id: request.id.clone(), result: None, error: Some(JsonRpcError { code: error_obj["error"]["code"].as_i64().unwrap_or(-32603) as i32, message: error_obj["error"]["message"] .as_str() .unwrap_or("Unknown error") .to_string(), data: None, }), } } } } async fn handle_initialize(&self, _request: &JsonRpcRequest) -> Result { Ok(json!({ "protocolVersion": "2024-11-05", "capabilities": { "tools": { "list_changed": false }, "resources": { "subscribe": false, "list_changed": false }, "prompts": { "list_changed": false } }, "serverInfo": { "name": "vapora-mcp-server", "version": env!("CARGO_PKG_VERSION"), } })) } async fn handle_tools_list(&self, _request: &JsonRpcRequest) -> Result { let tools: Vec<_> = self .registry .list() .into_iter() .map(|t| { json!({ "name": t.name, "description": t.description, "inputSchema": { "type": t.input_schema.schema_type, "properties": t.input_schema.properties, "required": t.input_schema.required, } }) }) .collect(); Ok(json!({ "tools": tools })) } async fn handle_tools_call(&self, request: &JsonRpcRequest) -> Result { let params: ToolCallParams = serde_json::from_value(request.params.clone()).map_err(|e| { McpError::InvalidParams { method: "tools/call".to_string(), reason: e.to_string(), } })?; let result = match params.name.as_str() { "kanban_create_task" => { let project_id = params .arguments .get("project_id") .and_then(|v| v.as_str()) .ok_or(McpError::InvalidParams { method: "kanban_create_task".to_string(), reason: "Missing project_id".to_string(), })?; let title = params .arguments .get("title") .and_then(|v| v.as_str()) .ok_or(McpError::InvalidParams { method: "kanban_create_task".to_string(), reason: "Missing title".to_string(), })?; self.backend .create_task(project_id, title.to_string()) .await? } "kanban_update_task" => { let task_id = params .arguments .get("task_id") .and_then(|v| v.as_str()) .ok_or(McpError::InvalidParams { method: "kanban_update_task".to_string(), reason: "Missing task_id".to_string(), })?; let status = params .arguments .get("status") .and_then(|v| v.as_str()) .ok_or(McpError::InvalidParams { method: "kanban_update_task".to_string(), reason: "Missing status".to_string(), })?; self.backend.update_task(task_id, status).await? } "get_project_summary" => { let project_id = params .arguments .get("project_id") .and_then(|v| v.as_str()) .ok_or(McpError::InvalidParams { method: "get_project_summary".to_string(), reason: "Missing project_id".to_string(), })?; self.backend.get_project_summary(project_id).await? } "list_agents" => self.backend.list_agents().await?, "get_agent_capabilities" => { let agent_id = params .arguments .get("agent_id") .and_then(|v| v.as_str()) .ok_or(McpError::InvalidParams { method: "get_agent_capabilities".to_string(), reason: "Missing agent_id".to_string(), })?; self.backend.get_agent_capabilities(agent_id).await? } "assign_task_to_agent" => { let role = params .arguments .get("role") .and_then(|v| v.as_str()) .ok_or(McpError::InvalidParams { method: "assign_task_to_agent".to_string(), reason: "Missing role".to_string(), })?; let title = params .arguments .get("task_title") .and_then(|v| v.as_str()) .ok_or(McpError::InvalidParams { method: "assign_task_to_agent".to_string(), reason: "Missing task_title".to_string(), })?; let description = params .arguments .get("task_description") .and_then(|v| v.as_str()) .unwrap_or(""); self.backend .assign_task_to_agent(role, title.to_string(), description.to_string()) .await? } tool_name => { return Err(McpError::MethodNotFound(format!("Tool: {}", tool_name))); } }; Ok(json!({ "content": [{ "type": "text", "text": serde_json::to_string(&result)? }], "isError": false })) } async fn handle_resources_list(&self, _request: &JsonRpcRequest) -> Result { Ok(json!({ "resources": [ { "uri": "vapora://projects", "name": "Projects", "description": "All projects" }, { "uri": "vapora://tasks", "name": "Tasks", "description": "All tasks" }, { "uri": "vapora://agents", "name": "Agents", "description": "Agent registry" }, ] })) } async fn handle_prompts_list(&self, _request: &JsonRpcRequest) -> Result { Ok(json!({ "prompts": [ { "name": "analyze_task", "description": "Analyze task and suggest improvements" }, { "name": "code_review_prompt", "description": "Generate code review feedback" }, ] })) } }