use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct JsonRpcRequest {
pub jsonrpc: String,
pub id: RequestId,
pub method: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub params: Option<Value>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct JsonRpcResponse {
pub jsonrpc: String,
pub id: RequestId,
#[serde(skip_serializing_if = "Option::is_none")]
pub result: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<crate::errors::McpError>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct JsonRpcNotification {
pub jsonrpc: String,
pub method: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub params: Option<Value>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum RequestId {
String(String),
Number(i64),
Null,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ServerCapabilities {
#[serde(skip_serializing_if = "Option::is_none")]
pub tools: Option<ToolsCapability>,
#[serde(skip_serializing_if = "Option::is_none")]
pub resources: Option<ResourcesCapability>,
#[serde(skip_serializing_if = "Option::is_none")]
pub prompts: Option<PromptsCapability>,
#[serde(skip_serializing_if = "Option::is_none")]
pub logging: Option<LoggingCapability>,
#[serde(skip_serializing_if = "Option::is_none")]
pub experimental: Option<HashMap<String, Value>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ClientCapabilities {
#[serde(skip_serializing_if = "Option::is_none")]
pub roots: Option<RootsCapability>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sampling: Option<SamplingCapability>,
#[serde(skip_serializing_if = "Option::is_none")]
pub experimental: Option<HashMap<String, Value>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ToolsCapability {
#[serde(skip_serializing_if = "Option::is_none")]
pub list_changed: Option<bool>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ResourcesCapability {
#[serde(skip_serializing_if = "Option::is_none")]
pub subscribe: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub list_changed: Option<bool>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PromptsCapability {
#[serde(skip_serializing_if = "Option::is_none")]
pub list_changed: Option<bool>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct LoggingCapability {}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RootsCapability {
#[serde(skip_serializing_if = "Option::is_none")]
pub list_changed: Option<bool>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SamplingCapability {}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Implementation {
pub name: String,
pub version: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct InitializeParams {
#[serde(rename = "protocolVersion")]
pub protocol_version: String,
pub capabilities: ClientCapabilities,
#[serde(rename = "clientInfo")]
pub client_info: Implementation,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct InitializeResult {
#[serde(rename = "protocolVersion")]
pub protocol_version: String,
pub capabilities: ServerCapabilities,
#[serde(rename = "serverInfo")]
pub server_info: Implementation,
#[serde(skip_serializing_if = "Option::is_none")]
pub instructions: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PingRequest {}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct EmptyResult {}
impl Default for ServerCapabilities {
fn default() -> Self {
Self {
tools: None,
resources: None,
prompts: None,
logging: None,
experimental: None,
}
}
}
impl Default for ClientCapabilities {
fn default() -> Self {
Self {
roots: None,
sampling: None,
experimental: None,
}
}
}
impl JsonRpcRequest {
pub fn new(id: RequestId, method: impl Into<String>) -> Self {
Self {
jsonrpc: crate::JSONRPC_VERSION.to_string(),
id,
method: method.into(),
params: None,
}
}
pub fn with_params(id: RequestId, method: impl Into<String>, params: Value) -> Self {
Self {
jsonrpc: crate::JSONRPC_VERSION.to_string(),
id,
method: method.into(),
params: Some(params),
}
}
}
impl JsonRpcResponse {
pub fn success(id: RequestId, result: Value) -> Self {
Self {
jsonrpc: crate::JSONRPC_VERSION.to_string(),
id,
result: Some(result),
error: None,
}
}
pub fn error(id: RequestId, error: crate::errors::McpError) -> Self {
Self {
jsonrpc: crate::JSONRPC_VERSION.to_string(),
id,
result: None,
error: Some(error),
}
}
}
impl JsonRpcNotification {
pub fn new(method: impl Into<String>) -> Self {
Self {
jsonrpc: crate::JSONRPC_VERSION.to_string(),
method: method.into(),
params: None,
}
}
pub fn with_params(method: impl Into<String>, params: Value) -> Self {
Self {
jsonrpc: crate::JSONRPC_VERSION.to_string(),
method: method.into(),
params: Some(params),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_request_serialization() {
let request = JsonRpcRequest::new(RequestId::String("test".to_string()), "test/method");
let json = serde_json::to_string(&request).unwrap();
let deserialized: JsonRpcRequest = serde_json::from_str(&json).unwrap();
assert_eq!(request, deserialized);
}
#[test]
fn test_response_serialization() {
let response = JsonRpcResponse::success(RequestId::Number(1), json!({"success": true}));
let json = serde_json::to_string(&response).unwrap();
let deserialized: JsonRpcResponse = serde_json::from_str(&json).unwrap();
assert_eq!(response, deserialized);
}
#[test]
fn test_capabilities_serialization() {
let caps = ServerCapabilities {
tools: Some(ToolsCapability {
list_changed: Some(true),
}),
..Default::default()
};
let json = serde_json::to_string(&caps).unwrap();
let deserialized: ServerCapabilities = serde_json::from_str(&json).unwrap();
assert_eq!(caps, deserialized);
}
}