agentic-reality-mcp 0.1.0

MCP server for AgenticReality — universal LLM access to existential grounding
Documentation
//! Protocol handler for MCP JSON-RPC messages.

use crate::session::SessionManager;
use crate::tools::ToolRegistry;
use crate::types::error::McpError;
use serde_json::Value;
use std::sync::Arc;
use tokio::sync::Mutex;

use super::compact;

/// MCP protocol handler.
pub struct ProtocolHandler {
    session: Arc<Mutex<SessionManager>>,
}

impl ProtocolHandler {
    pub fn new(session: Arc<Mutex<SessionManager>>) -> Self {
        Self { session }
    }

    /// Handle an incoming JSON-RPC request.
    pub async fn handle_request(&self, request: Value) -> Value {
        let method = request
            .get("method")
            .and_then(|m| m.as_str())
            .map_or("", |value| value);
        let id = request
            .get("id")
            .cloned()
            .map_or(Value::Null, |value| value);
        let params = request
            .get("params")
            .cloned()
            .map_or(Value::Object(serde_json::Map::new()), |value| value);

        let result = match method {
            "initialize" => self.handle_initialize(&params).await,
            "tools/list" => self.handle_list_tools().await,
            "tools/call" => self.handle_tool_call(&params).await,
            "resources/list" => self.handle_list_resources().await,
            "prompts/list" => self.handle_list_prompts().await,
            _ => Err(McpError::MethodNotFound {
                method: method.to_string(),
            }),
        };

        match result {
            Ok(value) => serde_json::json!({
                "jsonrpc": "2.0",
                "id": id,
                "result": value,
            }),
            Err(e) => serde_json::json!({
                "jsonrpc": "2.0",
                "id": id,
                "error": {
                    "code": e.code(),
                    "message": e.to_string(),
                },
            }),
        }
    }

    async fn handle_initialize(&self, _params: &Value) -> Result<Value, McpError> {
        Ok(serde_json::json!({
            "protocolVersion": "2024-11-05",
            "capabilities": {
                "tools": { "listChanged": false },
                "resources": { "subscribe": false, "listChanged": false },
                "prompts": { "listChanged": false },
            },
            "serverInfo": {
                "name": "agentic-reality",
                "version": env!("CARGO_PKG_VERSION"),
            },
        }))
    }

    async fn handle_list_tools(&self) -> Result<Value, McpError> {
        let tools = if compact::is_compact_mode() {
            compact::compact_tool_definitions()
        } else {
            ToolRegistry::list_tools()
        };
        Ok(serde_json::json!({ "tools": tools }))
    }

    async fn handle_tool_call(&self, params: &Value) -> Result<Value, McpError> {
        let name =
            params
                .get("name")
                .and_then(|n| n.as_str())
                .ok_or_else(|| McpError::InvalidParams {
                    message: "missing tool name".into(),
                })?;
        let arguments = params.get("arguments").cloned();

        // Normalize compact facade calls to underlying tool names
        let (tool_name, arguments) = if compact::is_compact_facade(name) {
            match compact::normalize_compact_call(&name.to_string(), &arguments) {
                Some((real_name, real_args)) => (real_name, real_args),
                None => {
                    return Err(McpError::InvalidParams {
                        message: format!(
                            "invalid operation for compact facade '{}'",
                            name
                        ),
                    });
                }
            }
        } else {
            (name.to_string(), arguments)
        };

        ToolRegistry::call(&tool_name, arguments, &self.session).await
    }

    async fn handle_list_resources(&self) -> Result<Value, McpError> {
        Ok(serde_json::json!({ "resources": [] }))
    }

    async fn handle_list_prompts(&self) -> Result<Value, McpError> {
        Ok(serde_json::json!({ "prompts": [] }))
    }
}