use serde::{Deserialize, Serialize};
use serde_json::Value;
pub const JSONRPC_VERSION: &str = "2.0";
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JsonRpcRequest {
pub jsonrpc: String,
pub method: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub params: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<RequestId>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JsonRpcResponse {
pub jsonrpc: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub result: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<JsonRpcError>,
pub id: Option<RequestId>,
}
#[derive(Debug, Clone, 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, 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, PartialEq, Eq, Hash)]
#[serde(untagged)]
pub enum RequestId {
String(String),
Number(i64),
}
impl From<String> for RequestId {
fn from(s: String) -> Self {
Self::String(s)
}
}
impl From<i64> for RequestId {
fn from(n: i64) -> Self {
Self::Number(n)
}
}
impl std::fmt::Display for RequestId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::String(s) => write!(f, "{}", s),
Self::Number(n) => write!(f, "{}", n),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ErrorCode {
ParseError = -32700,
InvalidRequest = -32600,
MethodNotFound = -32601,
InvalidParams = -32602,
InternalError = -32603,
}
impl ErrorCode {
pub fn message(&self) -> &'static str {
match self {
Self::ParseError => "Parse error",
Self::InvalidRequest => "Invalid Request",
Self::MethodNotFound => "Method not found",
Self::InvalidParams => "Invalid params",
Self::InternalError => "Internal error",
}
}
pub fn as_i32(&self) -> i32 {
*self as i32
}
}
impl JsonRpcError {
pub fn new(code: ErrorCode) -> Self {
Self {
code: code.as_i32(),
message: code.message().to_string(),
data: None,
}
}
pub fn with_data(code: ErrorCode, data: Value) -> Self {
Self {
code: code.as_i32(),
message: code.message().to_string(),
data: Some(data),
}
}
pub fn custom(code: ErrorCode, message: String) -> Self {
Self {
code: code.as_i32(),
message,
data: None,
}
}
}
impl JsonRpcResponse {
pub fn success(id: Option<RequestId>, result: Value) -> Self {
Self {
jsonrpc: JSONRPC_VERSION.to_string(),
result: Some(result),
error: None,
id,
}
}
pub fn error(id: Option<RequestId>, error: JsonRpcError) -> Self {
Self {
jsonrpc: JSONRPC_VERSION.to_string(),
result: None,
error: Some(error),
id,
}
}
}
impl JsonRpcRequest {
pub fn new(method: String, params: Option<Value>, id: Option<RequestId>) -> Self {
Self {
jsonrpc: JSONRPC_VERSION.to_string(),
method,
params,
id,
}
}
}
impl JsonRpcNotification {
pub fn new(method: String, params: Option<Value>) -> Self {
Self {
jsonrpc: JSONRPC_VERSION.to_string(),
method,
params,
}
}
}
#[derive(Debug, Clone)]
pub enum JsonRpcMessage {
Request(JsonRpcRequest),
Notification(JsonRpcNotification),
}
impl JsonRpcMessage {
pub fn from_value(value: Value) -> Result<Self, JsonRpcError> {
if value.get("id").is_some() {
match serde_json::from_value::<JsonRpcRequest>(value) {
Ok(req) => Ok(Self::Request(req)),
Err(e) => {
tracing::debug!("MCP request did not parse: {e}");
Err(JsonRpcError::custom(
ErrorCode::InvalidRequest,
"Failed to parse request".to_string(),
))
}
}
} else {
match serde_json::from_value::<JsonRpcNotification>(value) {
Ok(notif) => Ok(Self::Notification(notif)),
Err(e) => {
tracing::debug!("MCP notification did not parse: {e}");
Err(JsonRpcError::custom(
ErrorCode::InvalidRequest,
"Failed to parse notification".to_string(),
))
}
}
}
}
pub fn method(&self) -> &str {
match self {
Self::Request(req) => &req.method,
Self::Notification(notif) => ¬if.method,
}
}
pub fn params(&self) -> Option<&Value> {
match self {
Self::Request(req) => req.params.as_ref(),
Self::Notification(notif) => notif.params.as_ref(),
}
}
pub fn id(&self) -> Option<&RequestId> {
match self {
Self::Request(req) => req.id.as_ref(),
Self::Notification(_) => None,
}
}
}