Skip to main content
Glama

CodeGraph CLI MCP Server

by Jakedismo
message.rs9.44 kB
use serde::{Deserialize, Serialize}; use serde_json::Value; use std::collections::HashMap; pub type RequestId = Value; #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(tag = "jsonrpc")] pub enum JsonRpcMessage { #[serde(rename = "2.0")] V2(JsonRpcV2Message), } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(untagged)] pub enum JsonRpcV2Message { Request(JsonRpcRequest), Response(JsonRpcResponse), Notification(JsonRpcNotification), } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct JsonRpcRequest { pub id: RequestId, pub method: String, #[serde(skip_serializing_if = "Option::is_none")] pub params: Option<Value>, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct JsonRpcResponse { pub id: RequestId, #[serde(flatten)] pub result: JsonRpcResult, } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(untagged)] pub enum JsonRpcResult { Success { result: Value }, Error { error: JsonRpcError }, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct JsonRpcError { pub code: i32, pub message: String, #[serde(skip_serializing_if = "Option::is_none")] pub data: Option<Value>, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct JsonRpcNotification { pub method: String, #[serde(skip_serializing_if = "Option::is_none")] pub params: Option<Value>, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct McpInitializeParams { #[serde(rename = "protocolVersion")] pub protocol_version: String, pub capabilities: McpCapabilities, #[serde(rename = "clientInfo")] pub client_info: McpClientInfo, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct McpCapabilities { #[serde(skip_serializing_if = "Option::is_none")] pub experimental: Option<HashMap<String, Value>>, #[serde(skip_serializing_if = "Option::is_none")] pub sampling: Option<Value>, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct McpClientInfo { pub name: String, pub version: String, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct McpInitializeResult { #[serde(rename = "protocolVersion")] pub protocol_version: String, pub capabilities: McpServerCapabilities, #[serde(rename = "serverInfo")] pub server_info: McpServerInfo, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct McpServerCapabilities { #[serde(skip_serializing_if = "Option::is_none")] pub logging: Option<Value>, #[serde(skip_serializing_if = "Option::is_none")] pub prompts: Option<McpPromptsCapability>, #[serde(skip_serializing_if = "Option::is_none")] pub resources: Option<McpResourcesCapability>, #[serde(skip_serializing_if = "Option::is_none")] pub tools: Option<McpToolsCapability>, #[serde(skip_serializing_if = "Option::is_none")] pub experimental: Option<HashMap<String, Value>>, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct McpPromptsCapability { #[serde(rename = "listChanged", skip_serializing_if = "Option::is_none")] pub list_changed: Option<bool>, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct McpResourcesCapability { #[serde(skip_serializing_if = "Option::is_none")] pub subscribe: Option<bool>, #[serde(rename = "listChanged", skip_serializing_if = "Option::is_none")] pub list_changed: Option<bool>, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct McpToolsCapability { #[serde(rename = "listChanged", skip_serializing_if = "Option::is_none")] pub list_changed: Option<bool>, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct McpServerInfo { pub name: String, pub version: String, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct McpPingRequest; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct McpPingResult; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct McpCancelledNotification { #[serde(rename = "requestId")] pub request_id: RequestId, #[serde(skip_serializing_if = "Option::is_none")] pub reason: Option<String>, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct McpProgressNotification { #[serde(rename = "progressToken")] pub progress_token: Value, pub progress: f64, #[serde(skip_serializing_if = "Option::is_none")] pub total: Option<f64>, } impl JsonRpcRequest { pub fn new(id: RequestId, method: impl Into<String>, params: Option<Value>) -> Self { Self { id, method: method.into(), params, } } } impl JsonRpcResponse { pub fn success(id: RequestId, result: Value) -> Self { Self { id, result: JsonRpcResult::Success { result }, } } pub fn error(id: RequestId, error: JsonRpcError) -> Self { Self { id, result: JsonRpcResult::Error { error }, } } } impl JsonRpcNotification { pub fn new(method: impl Into<String>, params: Option<Value>) -> Self { Self { method: method.into(), params, } } } impl JsonRpcError { pub const PARSE_ERROR: i32 = -32700; pub const INVALID_REQUEST: i32 = -32600; pub const METHOD_NOT_FOUND: i32 = -32601; pub const INVALID_PARAMS: i32 = -32602; pub const INTERNAL_ERROR: i32 = -32603; pub fn parse_error() -> Self { Self { code: Self::PARSE_ERROR, message: "Parse error".to_string(), data: None, } } pub fn invalid_request() -> Self { Self { code: Self::INVALID_REQUEST, message: "Invalid Request".to_string(), data: None, } } pub fn method_not_found() -> Self { Self { code: Self::METHOD_NOT_FOUND, message: "Method not found".to_string(), data: None, } } pub fn invalid_params() -> Self { Self { code: Self::INVALID_PARAMS, message: "Invalid params".to_string(), data: None, } } pub fn internal_error() -> Self { Self { code: Self::INTERNAL_ERROR, message: "Internal error".to_string(), data: None, } } pub fn custom(code: i32, message: impl Into<String>) -> Self { Self { code, message: message.into(), data: None, } } pub fn with_data(mut self, data: Value) -> Self { self.data = Some(data); self } } pub fn validate_message(message: &JsonRpcMessage) -> crate::Result<()> { match message { JsonRpcMessage::V2(msg) => match msg { JsonRpcV2Message::Request(req) => { // jsonrpc version is handled by enum tag validation if req.method.is_empty() { return Err(crate::McpError::InvalidMessage( "Method cannot be empty".to_string(), )); } } JsonRpcV2Message::Response(_res) => { // jsonrpc version is handled by enum tag validation // Response validation is handled by the result enum } JsonRpcV2Message::Notification(notif) => { // jsonrpc version is handled by enum tag validation if notif.method.is_empty() { return Err(crate::McpError::InvalidMessage( "Method cannot be empty".to_string(), )); } } }, } Ok(()) } #[cfg(test)] mod tests { use super::*; use serde_json::json; #[test] fn test_serialize_request() { let req = JsonRpcRequest::new(json!(1), "initialize", Some(json!({"test": true}))); let serialized = serde_json::to_string(&req).unwrap(); assert!(serialized.contains("\"jsonrpc\":\"2.0\"")); assert!(serialized.contains("\"id\":1")); assert!(serialized.contains("\"method\":\"initialize\"")); } #[test] fn test_serialize_response() { let res = JsonRpcResponse::success(json!(1), json!({"result": "success"})); let serialized = serde_json::to_string(&res).unwrap(); assert!(serialized.contains("\"jsonrpc\":\"2.0\"")); assert!(serialized.contains("\"id\":1")); assert!(serialized.contains("\"result\"")); } #[test] fn test_serialize_notification() { let notif = JsonRpcNotification::new("cancelled", Some(json!({"requestId": 1}))); let serialized = serde_json::to_string(&notif).unwrap(); assert!(serialized.contains("\"jsonrpc\":\"2.0\"")); assert!(serialized.contains("\"method\":\"cancelled\"")); assert!(!serialized.contains("\"id\"")); } #[test] fn test_validate_message() { let valid_req = JsonRpcRequest::new(json!(1), "test", None); let msg = JsonRpcMessage::V2(JsonRpcV2Message::Request(valid_req)); assert!(validate_message(&msg).is_ok()); let invalid_req = JsonRpcRequest { jsonrpc: "1.0".to_string(), id: json!(1), method: "test".to_string(), params: None, }; let msg = JsonRpcMessage::V2(JsonRpcV2Message::Request(invalid_req)); assert!(validate_message(&msg).is_err()); } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Jakedismo/codegraph-rust'

If you have feedback or need assistance with the MCP directory API, please join our Discord server