use alloc::string::{String, ToString};
use core::fmt;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_json::Value;
pub const JSONRPC_VERSION: &str = "2.0";
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct JsonRpcVersion;
impl Serialize for JsonRpcVersion {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(JSONRPC_VERSION)
}
}
impl<'de> Deserialize<'de> for JsonRpcVersion {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let version = String::deserialize(deserializer)?;
if version == JSONRPC_VERSION {
Ok(JsonRpcVersion)
} else {
Err(serde::de::Error::custom(alloc::format!(
"Invalid JSON-RPC version: expected '{}', got '{}'",
JSONRPC_VERSION,
version
)))
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(untagged)]
pub enum RequestId {
String(String),
Number(i64),
}
impl fmt::Display for RequestId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::String(s) => write!(f, "{}", s),
Self::Number(n) => write!(f, "{}", n),
}
}
}
impl From<String> for RequestId {
fn from(s: String) -> Self {
Self::String(s)
}
}
impl From<&str> for RequestId {
fn from(s: &str) -> Self {
Self::String(s.to_string())
}
}
impl From<i64> for RequestId {
fn from(n: i64) -> Self {
Self::Number(n)
}
}
impl From<i32> for RequestId {
fn from(n: i32) -> Self {
Self::Number(n as i64)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JsonRpcRequest {
pub jsonrpc: JsonRpcVersion,
pub method: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub params: Option<Value>,
pub id: RequestId,
}
impl JsonRpcRequest {
#[must_use]
pub fn new(method: impl Into<String>, params: Option<Value>, id: impl Into<RequestId>) -> Self {
Self {
jsonrpc: JsonRpcVersion,
method: method.into(),
params,
id: id.into(),
}
}
#[must_use]
pub fn without_params(method: impl Into<String>, id: impl Into<RequestId>) -> Self {
Self::new(method, None, id)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JsonRpcNotification {
pub jsonrpc: JsonRpcVersion,
pub method: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub params: Option<Value>,
}
impl JsonRpcNotification {
#[must_use]
pub fn new(method: impl Into<String>, params: Option<Value>) -> Self {
Self {
jsonrpc: JsonRpcVersion,
method: method.into(),
params,
}
}
#[must_use]
pub fn without_params(method: impl Into<String>) -> Self {
Self::new(method, None)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct JsonRpcError {
pub code: i32,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<Value>,
}
impl JsonRpcError {
#[must_use]
pub fn new(code: i32, message: impl Into<String>) -> Self {
Self {
code,
message: message.into(),
data: None,
}
}
#[must_use]
pub fn with_data(code: i32, message: impl Into<String>, data: Value) -> Self {
Self {
code,
message: message.into(),
data: Some(data),
}
}
#[must_use]
pub fn parse_error() -> Self {
Self::new(-32700, "Parse error")
}
#[must_use]
pub fn invalid_request() -> Self {
Self::new(-32600, "Invalid Request")
}
#[must_use]
pub fn method_not_found(method: &str) -> Self {
Self::new(-32601, alloc::format!("Method not found: {}", method))
}
#[must_use]
pub fn invalid_params(details: &str) -> Self {
Self::new(-32602, alloc::format!("Invalid params: {}", details))
}
#[must_use]
pub fn internal_error(details: &str) -> Self {
Self::new(-32603, alloc::format!("Internal error: {}", details))
}
#[must_use]
pub const fn code(&self) -> i32 {
self.code
}
#[must_use]
pub const fn is_parse_error(&self) -> bool {
self.code == -32700
}
#[must_use]
pub const fn is_invalid_request(&self) -> bool {
self.code == -32600
}
}
impl fmt::Display for JsonRpcError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "[{}] {}", self.code, self.message)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct ResponseId(pub Option<RequestId>);
impl ResponseId {
#[must_use]
pub fn from_request(id: RequestId) -> Self {
Self(Some(id))
}
#[must_use]
pub fn null() -> Self {
Self(None)
}
#[must_use]
pub fn as_request_id(&self) -> Option<&RequestId> {
self.0.as_ref()
}
#[must_use]
pub fn is_null(&self) -> bool {
self.0.is_none()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum JsonRpcResponsePayload {
Success {
result: Value,
},
Error {
error: JsonRpcError,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JsonRpcResponse {
pub jsonrpc: JsonRpcVersion,
#[serde(flatten)]
pub payload: JsonRpcResponsePayload,
pub id: ResponseId,
}
impl JsonRpcResponse {
#[must_use]
pub fn success(result: Value, id: RequestId) -> Self {
Self {
jsonrpc: JsonRpcVersion,
payload: JsonRpcResponsePayload::Success { result },
id: ResponseId::from_request(id),
}
}
#[must_use]
pub fn error_response(error: JsonRpcError, id: RequestId) -> Self {
Self {
jsonrpc: JsonRpcVersion,
payload: JsonRpcResponsePayload::Error { error },
id: ResponseId::from_request(id),
}
}
#[must_use]
pub fn parse_error(message: Option<String>) -> Self {
let error = JsonRpcError {
code: -32700,
message: message.unwrap_or_else(|| "Parse error".to_string()),
data: None,
};
Self {
jsonrpc: JsonRpcVersion,
payload: JsonRpcResponsePayload::Error { error },
id: ResponseId::null(),
}
}
#[must_use]
pub fn is_success(&self) -> bool {
matches!(self.payload, JsonRpcResponsePayload::Success { .. })
}
#[must_use]
pub fn is_error(&self) -> bool {
matches!(self.payload, JsonRpcResponsePayload::Error { .. })
}
#[must_use]
pub fn result(&self) -> Option<&Value> {
match &self.payload {
JsonRpcResponsePayload::Success { result } => Some(result),
JsonRpcResponsePayload::Error { .. } => None,
}
}
#[must_use]
pub fn error(&self) -> Option<&JsonRpcError> {
match &self.payload {
JsonRpcResponsePayload::Success { .. } => None,
JsonRpcResponsePayload::Error { error } => Some(error),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum JsonRpcErrorCode {
ParseError,
InvalidRequest,
MethodNotFound,
InvalidParams,
InternalError,
ApplicationError(i32),
}
impl JsonRpcErrorCode {
#[must_use]
pub const fn code(&self) -> i32 {
match self {
Self::ParseError => -32700,
Self::InvalidRequest => -32600,
Self::MethodNotFound => -32601,
Self::InvalidParams => -32602,
Self::InternalError => -32603,
Self::ApplicationError(code) => *code,
}
}
#[must_use]
pub const 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",
Self::ApplicationError(_) => "Application error",
}
}
}
impl fmt::Display for JsonRpcErrorCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} ({})", self.message(), self.code())
}
}
impl From<i32> for JsonRpcErrorCode {
fn from(code: i32) -> Self {
match code {
-32700 => Self::ParseError,
-32600 => Self::InvalidRequest,
-32601 => Self::MethodNotFound,
-32602 => Self::InvalidParams,
-32603 => Self::InternalError,
other => Self::ApplicationError(other),
}
}
}
impl From<JsonRpcErrorCode> for JsonRpcError {
fn from(code: JsonRpcErrorCode) -> Self {
Self {
code: code.code(),
message: code.message().to_string(),
data: None,
}
}
}
#[derive(Debug, Clone, Deserialize)]
pub struct JsonRpcIncoming {
#[allow(dead_code)]
pub jsonrpc: String,
#[serde(default)]
pub id: Option<Value>,
pub method: String,
#[serde(default)]
pub params: Option<Value>,
}
impl JsonRpcIncoming {
#[must_use]
pub fn is_request(&self) -> bool {
self.id.is_some()
}
#[must_use]
pub fn is_notification(&self) -> bool {
self.id.is_none()
}
pub fn parse(input: &str) -> Result<Self, serde_json::Error> {
serde_json::from_str(input)
}
}
#[derive(Debug, Clone, Serialize)]
pub struct JsonRpcOutgoing {
pub jsonrpc: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub result: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<JsonRpcError>,
}
impl JsonRpcOutgoing {
#[must_use]
pub fn success(id: Option<Value>, result: Value) -> Self {
Self {
jsonrpc: "2.0".to_string(),
id,
result: Some(result),
error: None,
}
}
#[must_use]
pub fn error(id: Option<Value>, error: impl Into<JsonRpcError>) -> Self {
Self {
jsonrpc: "2.0".to_string(),
id,
result: None,
error: Some(error.into()),
}
}
#[must_use]
pub fn notification_ack() -> Self {
Self {
jsonrpc: "2.0".to_string(),
id: None,
result: None,
error: None,
}
}
#[must_use]
pub fn should_send(&self) -> bool {
self.id.is_some() || self.result.is_some() || self.error.is_some()
}
pub fn to_json(&self) -> Result<String, serde_json::Error> {
serde_json::to_string(self)
}
}
impl From<crate::error::McpError> for JsonRpcError {
fn from(err: crate::error::McpError) -> Self {
Self {
code: err.jsonrpc_code(),
message: err.message.clone(),
data: None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_request_creation() {
let req = JsonRpcRequest::new("test", None, "id-1");
assert_eq!(req.method, "test");
assert!(req.params.is_none());
}
#[test]
fn test_response_success() {
let resp = JsonRpcResponse::success(serde_json::json!({"ok": true}), "id-1".into());
assert!(resp.is_success());
assert!(!resp.is_error());
}
#[test]
fn test_error_codes() {
assert_eq!(JsonRpcErrorCode::ParseError.code(), -32700);
assert_eq!(
JsonRpcErrorCode::from(-32601),
JsonRpcErrorCode::MethodNotFound
);
}
#[test]
fn test_request_id_conversion() {
let id1: RequestId = "test".into();
assert!(matches!(id1, RequestId::String(_)));
let id2: RequestId = 42i32.into();
assert!(matches!(id2, RequestId::Number(42)));
}
#[test]
fn test_incoming_request() {
let input = r#"{"jsonrpc": "2.0", "id": 1, "method": "ping"}"#;
let incoming = JsonRpcIncoming::parse(input).unwrap();
assert!(incoming.is_request());
assert!(!incoming.is_notification());
assert_eq!(incoming.method, "ping");
}
#[test]
fn test_incoming_notification() {
let input = r#"{"jsonrpc": "2.0", "method": "notifications/initialized"}"#;
let incoming = JsonRpcIncoming::parse(input).unwrap();
assert!(!incoming.is_request());
assert!(incoming.is_notification());
}
#[test]
fn test_outgoing_success() {
let response = JsonRpcOutgoing::success(Some(serde_json::json!(1)), serde_json::json!({}));
assert!(response.should_send());
assert!(response.result.is_some());
assert!(response.error.is_none());
}
#[test]
fn test_outgoing_notification_ack() {
let response = JsonRpcOutgoing::notification_ack();
assert!(!response.should_send());
}
}