chasm_cli/mcp/
server.rs

1// Copyright (c) 2024-2026 Nervosys LLC
2// SPDX-License-Identifier: Apache-2.0
3//! MCP Server - Main server implementation using stdio transport
4
5#![allow(dead_code, unused_imports)]
6
7use super::resources;
8use super::tools;
9use super::types::*;
10use serde_json::json;
11use std::io::{self, BufRead, Write};
12
13/// MCP Server for Chat System Manager
14pub struct McpServer {
15    initialized: bool,
16}
17
18impl McpServer {
19    pub fn new() -> Self {
20        Self { initialized: false }
21    }
22
23    /// Run the MCP server using stdio transport
24    pub fn run(&mut self) -> io::Result<()> {
25        let stdin = io::stdin();
26        let stdout = io::stdout();
27        let mut stdout = stdout.lock();
28
29        eprintln!("[csm-mcp] Server starting...");
30
31        for line in stdin.lock().lines() {
32            let line = line?;
33
34            if line.is_empty() {
35                continue;
36            }
37
38            eprintln!("[csm-mcp] Received: {}", &line[..line.len().min(100)]);
39
40            match serde_json::from_str::<JsonRpcRequest>(&line) {
41                Ok(request) => {
42                    let response = self.handle_request(request);
43                    let response_str = serde_json::to_string(&response)?;
44                    eprintln!(
45                        "[csm-mcp] Sending: {}",
46                        &response_str[..response_str.len().min(100)]
47                    );
48                    writeln!(stdout, "{}", response_str)?;
49                    stdout.flush()?;
50                }
51                Err(e) => {
52                    eprintln!("[csm-mcp] Parse error: {}", e);
53                    let error_response =
54                        JsonRpcResponse::error(None, -32700, format!("Parse error: {}", e));
55                    writeln!(stdout, "{}", serde_json::to_string(&error_response)?)?;
56                    stdout.flush()?;
57                }
58            }
59        }
60
61        Ok(())
62    }
63
64    fn handle_request(&mut self, request: JsonRpcRequest) -> JsonRpcResponse {
65        match request.method.as_str() {
66            "initialize" => self.handle_initialize(request),
67            "initialized" => {
68                // Notification, no response needed
69                JsonRpcResponse::success(request.id, json!({}))
70            }
71            "tools/list" => self.handle_tools_list(request),
72            "tools/call" => self.handle_tools_call(request),
73            "resources/list" => self.handle_resources_list(request),
74            "resources/read" => self.handle_resources_read(request),
75            "ping" => JsonRpcResponse::success(request.id, json!({})),
76            _ => JsonRpcResponse::error(
77                request.id,
78                -32601,
79                format!("Method not found: {}", request.method),
80            ),
81        }
82    }
83
84    fn handle_initialize(&mut self, request: JsonRpcRequest) -> JsonRpcResponse {
85        self.initialized = true;
86
87        let result = InitializeResult {
88            protocol_version: "2024-11-05".to_string(),
89            capabilities: ServerCapabilities {
90                tools: Some(ToolsCapability {
91                    list_changed: Some(false),
92                }),
93                resources: Some(ResourcesCapability {
94                    list_changed: Some(false),
95                    subscribe: Some(false),
96                }),
97                prompts: None,
98            },
99            server_info: ServerInfo {
100                name: "csm-mcp".to_string(),
101                version: Some(env!("CARGO_PKG_VERSION").to_string()),
102            },
103        };
104
105        JsonRpcResponse::success(request.id, serde_json::to_value(result).unwrap())
106    }
107
108    fn handle_tools_list(&self, request: JsonRpcRequest) -> JsonRpcResponse {
109        let tools = tools::list_tools();
110        JsonRpcResponse::success(request.id, json!({ "tools": tools }))
111    }
112
113    fn handle_tools_call(&self, request: JsonRpcRequest) -> JsonRpcResponse {
114        let params: Result<CallToolParams, _> = serde_json::from_value(request.params.clone());
115
116        match params {
117            Ok(params) => {
118                let result = tools::call_tool(&params.name, &params.arguments);
119                JsonRpcResponse::success(request.id, serde_json::to_value(result).unwrap())
120            }
121            Err(e) => JsonRpcResponse::error(request.id, -32602, format!("Invalid params: {}", e)),
122        }
123    }
124
125    fn handle_resources_list(&self, request: JsonRpcRequest) -> JsonRpcResponse {
126        let resources = resources::list_resources();
127        JsonRpcResponse::success(request.id, json!({ "resources": resources }))
128    }
129
130    fn handle_resources_read(&self, request: JsonRpcRequest) -> JsonRpcResponse {
131        let params: Result<ReadResourceParams, _> = serde_json::from_value(request.params.clone());
132
133        match params {
134            Ok(params) => {
135                let result = resources::read_resource(&params.uri);
136                JsonRpcResponse::success(request.id, serde_json::to_value(result).unwrap())
137            }
138            Err(e) => JsonRpcResponse::error(request.id, -32602, format!("Invalid params: {}", e)),
139        }
140    }
141}
142
143impl Default for McpServer {
144    fn default() -> Self {
145        Self::new()
146    }
147}