Skip to main content

agentic_reality_mcp/protocol/
handler.rs

1//! Protocol handler for MCP JSON-RPC messages.
2
3use crate::session::SessionManager;
4use crate::tools::ToolRegistry;
5use crate::types::error::McpError;
6use serde_json::Value;
7use std::sync::Arc;
8use tokio::sync::Mutex;
9
10use super::compact;
11
12/// MCP protocol handler.
13pub struct ProtocolHandler {
14    session: Arc<Mutex<SessionManager>>,
15}
16
17impl ProtocolHandler {
18    pub fn new(session: Arc<Mutex<SessionManager>>) -> Self {
19        Self { session }
20    }
21
22    /// Handle an incoming JSON-RPC request.
23    pub async fn handle_request(&self, request: Value) -> Value {
24        let method = request
25            .get("method")
26            .and_then(|m| m.as_str())
27            .map_or("", |value| value);
28        let id = request
29            .get("id")
30            .cloned()
31            .map_or(Value::Null, |value| value);
32        let params = request
33            .get("params")
34            .cloned()
35            .map_or(Value::Object(serde_json::Map::new()), |value| value);
36
37        let result = match method {
38            "initialize" => self.handle_initialize(&params).await,
39            "tools/list" => self.handle_list_tools().await,
40            "tools/call" => self.handle_tool_call(&params).await,
41            "resources/list" => self.handle_list_resources().await,
42            "prompts/list" => self.handle_list_prompts().await,
43            _ => Err(McpError::MethodNotFound {
44                method: method.to_string(),
45            }),
46        };
47
48        match result {
49            Ok(value) => serde_json::json!({
50                "jsonrpc": "2.0",
51                "id": id,
52                "result": value,
53            }),
54            Err(e) => serde_json::json!({
55                "jsonrpc": "2.0",
56                "id": id,
57                "error": {
58                    "code": e.code(),
59                    "message": e.to_string(),
60                },
61            }),
62        }
63    }
64
65    async fn handle_initialize(&self, _params: &Value) -> Result<Value, McpError> {
66        Ok(serde_json::json!({
67            "protocolVersion": "2024-11-05",
68            "capabilities": {
69                "tools": { "listChanged": false },
70                "resources": { "subscribe": false, "listChanged": false },
71                "prompts": { "listChanged": false },
72            },
73            "serverInfo": {
74                "name": "agentic-reality",
75                "version": env!("CARGO_PKG_VERSION"),
76            },
77        }))
78    }
79
80    async fn handle_list_tools(&self) -> Result<Value, McpError> {
81        let tools = if compact::is_compact_mode() {
82            compact::compact_tool_definitions()
83        } else {
84            ToolRegistry::list_tools()
85        };
86        Ok(serde_json::json!({ "tools": tools }))
87    }
88
89    async fn handle_tool_call(&self, params: &Value) -> Result<Value, McpError> {
90        let name =
91            params
92                .get("name")
93                .and_then(|n| n.as_str())
94                .ok_or_else(|| McpError::InvalidParams {
95                    message: "missing tool name".into(),
96                })?;
97        let arguments = params.get("arguments").cloned();
98
99        // Normalize compact facade calls to underlying tool names
100        let (tool_name, arguments) = if compact::is_compact_facade(name) {
101            match compact::normalize_compact_call(&name.to_string(), &arguments) {
102                Some((real_name, real_args)) => (real_name, real_args),
103                None => {
104                    return Err(McpError::InvalidParams {
105                        message: format!(
106                            "invalid operation for compact facade '{}'",
107                            name
108                        ),
109                    });
110                }
111            }
112        } else {
113            (name.to_string(), arguments)
114        };
115
116        ToolRegistry::call(&tool_name, arguments, &self.session).await
117    }
118
119    async fn handle_list_resources(&self) -> Result<Value, McpError> {
120        Ok(serde_json::json!({ "resources": [] }))
121    }
122
123    async fn handle_list_prompts(&self) -> Result<Value, McpError> {
124        Ok(serde_json::json!({ "prompts": [] }))
125    }
126}