use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_json::Value;
use std::fmt;
use crate::types::RequestId;
pub const JSONRPC_VERSION: &str = "2.0";
#[derive(Debug, Clone, PartialEq, Eq)]
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(format!(
"Invalid JSON-RPC version: expected '{JSONRPC_VERSION}', got '{version}'"
)))
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JsonRpcRequest {
pub jsonrpc: JsonRpcVersion,
pub method: String,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub params: Option<Value>,
pub id: RequestId,
}
#[derive(Debug, Clone, Serialize)]
#[serde(untagged)]
pub enum JsonRpcResponsePayload {
Success {
result: Value,
},
Error {
error: JsonRpcError,
},
}
impl<'de> Deserialize<'de> for JsonRpcResponsePayload {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let mut object = serde_json::Map::<String, Value>::deserialize(deserializer)?;
let result_present = object.contains_key("result");
let error_present = object.contains_key("error");
match (result_present, error_present) {
(true, false) => Ok(Self::Success {
result: object.remove("result").unwrap_or(Value::Null),
}),
(false, true) => {
let error_value = object.remove("error").unwrap_or(Value::Null);
let error =
JsonRpcError::deserialize(error_value).map_err(serde::de::Error::custom)?;
Ok(Self::Error { error })
}
(true, true) => Err(serde::de::Error::custom(
"JSON-RPC response must contain exactly one of `result` or `error`, not both",
)),
(false, false) => Err(serde::de::Error::custom(
"JSON-RPC response must contain exactly one of `result` or `error`",
)),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JsonRpcResponse {
pub jsonrpc: JsonRpcVersion,
#[serde(flatten)]
pub payload: JsonRpcResponsePayload,
pub id: ResponseId,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct ResponseId(pub Option<RequestId>);
impl ResponseId {
pub fn from_request(id: RequestId) -> Self {
Self(Some(id))
}
pub fn null() -> Self {
Self(None)
}
pub fn as_request_id(&self) -> Option<&RequestId> {
self.0.as_ref()
}
pub fn is_null(&self) -> bool {
self.0.is_none()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JsonRpcNotification {
pub jsonrpc: JsonRpcVersion,
pub method: String,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub params: Option<Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct JsonRpcError {
pub code: i32,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub data: Option<Value>,
}
impl JsonRpcError {
const SERVER_ERROR_RANGE: std::ops::RangeInclusive<i32> = -32099..=-32000;
const STANDARD_CODES: &'static [i32] = &[-32700, -32600, -32601, -32602, -32603];
pub fn new(code: i32, message: impl Into<String>) -> Self {
if !Self::is_valid_code(code) {
tracing::warn!(
code,
"JSON-RPC error code outside reserved server-error range -32099..=-32000 \
and not a standardized JSON-RPC 2.0 code; this risks colliding with \
future spec assignments"
);
}
Self {
code,
message: Self::cap_message(message.into()),
data: None,
}
}
pub fn with_validated_code(
code: i32,
message: impl Into<String>,
) -> Result<Self, &'static str> {
if !Self::is_valid_code(code) {
return Err(
"JSON-RPC error code must be a standardized code or in the -32099..=-32000 server-error range",
);
}
Ok(Self {
code,
message: Self::cap_message(message.into()),
data: None,
})
}
fn is_valid_code(code: i32) -> bool {
Self::SERVER_ERROR_RANGE.contains(&code) || Self::STANDARD_CODES.contains(&code)
}
const MESSAGE_BYTE_CAP: usize = 1024;
fn cap_message(s: String) -> String {
if s.len() <= Self::MESSAGE_BYTE_CAP {
return s;
}
let mut end = Self::MESSAGE_BYTE_CAP;
while end > 0 && !s.is_char_boundary(end) {
end -= 1;
}
let elided = s.len() - end;
let mut out = String::with_capacity(end + 32);
out.push_str(&s[..end]);
out.push_str(&format!("…[truncated, {elided} bytes elided]"));
out
}
fn cap_data_value(data: Value) -> Value {
match data {
Value::String(s) => Value::String(Self::cap_message(s)),
Value::Array(values) => {
Value::Array(values.into_iter().map(Self::cap_data_value).collect())
}
Value::Object(map) => {
let capped = map
.into_iter()
.map(|(k, v)| (k, Self::cap_data_value(v)))
.collect();
Value::Object(capped)
}
other => other,
}
}
pub fn with_data(code: i32, message: impl Into<String>, data: Value) -> Self {
if !Self::is_valid_code(code) {
tracing::warn!(
code,
"JSON-RPC error code outside reserved server-error range -32099..=-32000"
);
}
Self {
code,
message: Self::cap_message(message.into()),
data: Some(Self::cap_data_value(data)),
}
}
pub fn parse_error() -> Self {
Self::new(-32700, "Parse error")
}
pub fn parse_error_with_details(details: impl Into<String>) -> Self {
Self::with_data(
-32700,
"Parse error",
serde_json::json!({ "details": details.into() }),
)
}
pub fn invalid_request() -> Self {
Self::new(-32600, "Invalid Request")
}
pub fn invalid_request_with_reason(reason: impl Into<String>) -> Self {
Self::with_data(
-32600,
"Invalid Request",
serde_json::json!({ "reason": reason.into() }),
)
}
pub fn method_not_found(method: &str) -> Self {
Self::new(-32601, format!("Method not found: {method}"))
}
pub fn invalid_params(details: &str) -> Self {
Self::new(-32602, format!("Invalid params: {details}"))
}
pub fn internal_error(details: &str) -> Self {
Self::new(-32603, format!("Internal error: {details}"))
}
pub fn is_parse_error(&self) -> bool {
self.code == -32700
}
pub fn is_invalid_request(&self) -> bool {
self.code == -32600
}
pub fn is_method_not_found(&self) -> bool {
self.code == -32601
}
pub fn is_invalid_params(&self) -> bool {
self.code == -32602
}
pub fn is_internal_error(&self) -> bool {
self.code == -32603
}
pub fn is_server_error(&self) -> bool {
(-32099..=-32000).contains(&self.code)
}
pub fn code(&self) -> i32 {
self.code
}
pub fn standard_kind(&self) -> Option<JsonRpcErrorCode> {
match self.code {
-32700 => Some(JsonRpcErrorCode::ParseError),
-32600 => Some(JsonRpcErrorCode::InvalidRequest),
-32601 => Some(JsonRpcErrorCode::MethodNotFound),
-32602 => Some(JsonRpcErrorCode::InvalidParams),
-32603 => Some(JsonRpcErrorCode::InternalError),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum JsonRpcErrorCode {
ParseError,
InvalidRequest,
MethodNotFound,
InvalidParams,
InternalError,
ApplicationError(i32),
}
impl JsonRpcErrorCode {
pub 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,
}
}
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",
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<JsonRpcErrorCode> for JsonRpcError {
fn from(code: JsonRpcErrorCode) -> Self {
Self {
code: code.code(),
message: code.message().to_string(),
data: None,
}
}
}
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),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum JsonRpcMessage {
Request(JsonRpcRequest),
Response(JsonRpcResponse),
Notification(JsonRpcNotification),
}
impl JsonRpcRequest {
pub fn new(method: String, params: Option<Value>, id: RequestId) -> Self {
Self {
jsonrpc: JsonRpcVersion,
method,
params,
id,
}
}
pub fn without_params(method: String, id: RequestId) -> Self {
Self::new(method, None, id)
}
pub fn with_params<P: Serialize>(
method: String,
params: P,
id: RequestId,
) -> Result<Self, serde_json::Error> {
let params_value = serde_json::to_value(params)?;
Ok(Self::new(method, Some(params_value), id))
}
}
impl JsonRpcResponse {
pub fn success(result: Value, id: RequestId) -> Self {
Self {
jsonrpc: JsonRpcVersion,
payload: JsonRpcResponsePayload::Success { result },
id: ResponseId::from_request(id),
}
}
pub fn error_response(error: JsonRpcError, id: RequestId) -> Self {
Self {
jsonrpc: JsonRpcVersion,
payload: JsonRpcResponsePayload::Error { error },
id: ResponseId::from_request(id),
}
}
pub fn parse_error(message: Option<String>) -> Self {
let error = JsonRpcError {
code: JsonRpcErrorCode::ParseError.code(),
message: message.unwrap_or_else(|| JsonRpcErrorCode::ParseError.message().to_string()),
data: None,
};
Self {
jsonrpc: JsonRpcVersion,
payload: JsonRpcResponsePayload::Error { error },
id: ResponseId::null(),
}
}
pub fn is_success(&self) -> bool {
matches!(self.payload, JsonRpcResponsePayload::Success { .. })
}
pub fn is_error(&self) -> bool {
matches!(self.payload, JsonRpcResponsePayload::Error { .. })
}
pub fn result(&self) -> Option<&Value> {
match &self.payload {
JsonRpcResponsePayload::Success { result } => Some(result),
JsonRpcResponsePayload::Error { .. } => None,
}
}
pub fn error(&self) -> Option<&JsonRpcError> {
match &self.payload {
JsonRpcResponsePayload::Success { .. } => None,
JsonRpcResponsePayload::Error { error } => Some(error),
}
}
pub fn request_id(&self) -> Option<&RequestId> {
self.id.as_request_id()
}
pub fn is_parse_error(&self) -> bool {
self.id.is_null()
}
pub fn result_mut(&mut self) -> Option<&mut Value> {
match &mut self.payload {
JsonRpcResponsePayload::Success { result } => Some(result),
JsonRpcResponsePayload::Error { .. } => None,
}
}
pub fn error_mut(&mut self) -> Option<&mut JsonRpcError> {
match &mut self.payload {
JsonRpcResponsePayload::Success { .. } => None,
JsonRpcResponsePayload::Error { error } => Some(error),
}
}
pub fn set_result(&mut self, result: Value) {
self.payload = JsonRpcResponsePayload::Success { result };
}
pub fn set_error(&mut self, error: JsonRpcError) {
self.payload = JsonRpcResponsePayload::Error { error };
}
}
impl JsonRpcNotification {
pub fn new(method: String, params: Option<Value>) -> Self {
Self {
jsonrpc: JsonRpcVersion,
method,
params,
}
}
pub fn without_params(method: String) -> Self {
Self::new(method, None)
}
pub fn with_params<P: Serialize>(method: String, params: P) -> Result<Self, serde_json::Error> {
let params_value = serde_json::to_value(params)?;
Ok(Self::new(method, Some(params_value)))
}
}
pub mod utils {
use super::*;
#[derive(Debug)]
pub enum ParseMessageError {
BatchUnsupported,
Json(serde_json::Error),
}
impl core::fmt::Display for ParseMessageError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::BatchUnsupported => {
f.write_str("JSON-RPC batches are not supported in MCP 2025-11-25")
}
Self::Json(e) => write!(f, "{e}"),
}
}
}
impl std::error::Error for ParseMessageError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::BatchUnsupported => None,
Self::Json(e) => Some(e),
}
}
}
impl From<serde_json::Error> for ParseMessageError {
fn from(e: serde_json::Error) -> Self {
Self::Json(e)
}
}
pub fn parse_message(json: &str) -> Result<JsonRpcMessage, serde_json::Error> {
serde_json::from_str(json)
}
pub fn parse_message_typed(json: &str) -> Result<JsonRpcMessage, ParseMessageError> {
if json.trim_start().as_bytes().first() == Some(&b'[') {
return Err(ParseMessageError::BatchUnsupported);
}
Ok(serde_json::from_str(json)?)
}
pub fn serialize_message(message: &JsonRpcMessage) -> Result<String, serde_json::Error> {
serde_json::to_string(message)
}
pub fn extract_method(json: &str) -> Option<String> {
if let Ok(value) = serde_json::from_str::<serde_json::Value>(json)
&& let Some(method) = value.get("method")
{
return method.as_str().map(String::from);
}
None
}
}
pub mod http {
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HttpJsonRpcRequest {
pub jsonrpc: String,
#[serde(default)]
pub id: Option<Value>,
pub method: String,
#[serde(default)]
pub params: Option<Value>,
}
impl HttpJsonRpcRequest {
pub fn is_valid(&self) -> bool {
self.jsonrpc == "2.0" && !self.method.is_empty()
}
pub fn is_notification(&self) -> bool {
self.id.is_none()
}
pub fn id_string(&self) -> Option<String> {
self.id.as_ref().map(|v| match v {
Value::String(s) => s.clone(),
Value::Number(n) => n.to_string(),
_ => v.to_string(),
})
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HttpJsonRpcResponse {
pub jsonrpc: String,
#[serde(default)]
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<super::JsonRpcError>,
}
impl HttpJsonRpcResponse {
pub fn success(id: Option<Value>, result: Value) -> Self {
Self {
jsonrpc: "2.0".to_string(),
id,
result: Some(result),
error: None,
}
}
pub fn error(id: Option<Value>, error: super::JsonRpcError) -> Self {
Self {
jsonrpc: "2.0".to_string(),
id,
result: None,
error: Some(error),
}
}
pub fn error_from_code(id: Option<Value>, code: i32, message: impl Into<String>) -> Self {
Self::error(id, super::JsonRpcError::new(code, message))
}
pub fn invalid_request(id: Option<Value>, reason: impl Into<String>) -> Self {
Self::error(id, super::JsonRpcError::invalid_request_with_reason(reason))
}
pub fn parse_error(details: Option<String>) -> Self {
Self::error(
None,
details
.map(super::JsonRpcError::parse_error_with_details)
.unwrap_or_else(super::JsonRpcError::parse_error),
)
}
pub fn internal_error(id: Option<Value>, details: &str) -> Self {
Self::error(id, super::JsonRpcError::internal_error(details))
}
pub fn method_not_found(id: Option<Value>, method: &str) -> Self {
Self::error(id, super::JsonRpcError::method_not_found(method))
}
pub fn is_error(&self) -> bool {
self.error.is_some()
}
pub fn is_success(&self) -> bool {
self.result.is_some() && self.error.is_none()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_http_request_parsing() {
let json = r#"{"jsonrpc":"2.0","method":"test","id":1,"params":{"key":"value"}}"#;
let request: HttpJsonRpcRequest = serde_json::from_str(json).unwrap();
assert!(request.is_valid());
assert!(!request.is_notification());
assert_eq!(request.method, "test");
}
#[test]
fn test_http_request_invalid_version() {
let json = r#"{"jsonrpc":"1.0","method":"test","id":1}"#;
let request: HttpJsonRpcRequest = serde_json::from_str(json).unwrap();
assert!(!request.is_valid());
}
#[test]
fn test_http_response_success() {
let response = HttpJsonRpcResponse::success(
Some(Value::Number(1.into())),
serde_json::json!({"result": "ok"}),
);
assert!(response.is_success());
assert!(!response.is_error());
}
#[test]
fn test_http_response_error() {
let response = HttpJsonRpcResponse::invalid_request(
Some(Value::String("req-1".into())),
"jsonrpc must be 2.0",
);
assert!(!response.is_success());
assert!(response.is_error());
}
#[test]
fn test_http_response_serialization() {
let response = HttpJsonRpcResponse::success(
Some(Value::Number(1.into())),
serde_json::json!({"data": "test"}),
);
let json = serde_json::to_string(&response).unwrap();
assert!(json.contains(r#""jsonrpc":"2.0""#));
assert!(json.contains(r#""result""#));
assert!(!json.contains(r#""error""#));
}
}
}
#[cfg(test)]
#[path = "jsonrpc/tests.rs"]
mod extended_tests;
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_jsonrpc_version() {
let version = JsonRpcVersion;
let json = serde_json::to_string(&version).unwrap();
assert_eq!(json, "\"2.0\"");
let parsed: JsonRpcVersion = serde_json::from_str(&json).unwrap();
assert_eq!(parsed, version);
}
#[test]
fn test_request_creation() {
let request = JsonRpcRequest::new(
"test_method".to_string(),
Some(json!({"key": "value"})),
RequestId::String("test-id".to_string()),
);
assert_eq!(request.method, "test_method");
assert!(request.params.is_some());
}
#[test]
fn test_response_creation() {
let response = JsonRpcResponse::success(
json!({"result": "success"}),
RequestId::String("test-id".to_string()),
);
assert!(response.is_success());
assert!(!response.is_error());
assert!(response.result().is_some());
assert!(response.error().is_none());
assert!(!response.is_parse_error());
}
#[test]
fn test_error_response() {
let error = JsonRpcError::from(JsonRpcErrorCode::MethodNotFound);
let response =
JsonRpcResponse::error_response(error, RequestId::String("test-id".to_string()));
assert!(!response.is_success());
assert!(response.is_error());
assert!(response.result().is_none());
assert!(response.error().is_some());
assert!(!response.is_parse_error());
}
#[test]
fn test_response_payload_accepts_null_result() {
let raw = r#"{"jsonrpc":"2.0","id":1,"result":null}"#;
let response: JsonRpcResponse = serde_json::from_str(raw).unwrap();
assert!(response.is_success());
assert_eq!(response.result(), Some(&Value::Null));
}
#[test]
fn test_parse_error_response() {
let response = JsonRpcResponse::parse_error(Some("Invalid JSON".to_string()));
assert!(!response.is_success());
assert!(response.is_error());
assert!(response.result().is_none());
assert!(response.error().is_some());
assert!(response.is_parse_error());
assert!(response.request_id().is_none());
let error = response.error().unwrap();
assert_eq!(error.code, JsonRpcErrorCode::ParseError.code());
assert_eq!(error.message, "Invalid JSON");
}
#[test]
fn test_notification() {
let notification = JsonRpcNotification::without_params("test_notification".to_string());
assert_eq!(notification.method, "test_notification");
assert!(notification.params.is_none());
}
#[test]
fn test_serialization() {
let request = JsonRpcRequest::new(
"test_method".to_string(),
Some(json!({"param": "value"})),
RequestId::String("123".to_string()),
);
let json = serde_json::to_string(&request).unwrap();
let parsed: JsonRpcRequest = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.method, request.method);
assert_eq!(parsed.params, request.params);
}
#[test]
fn test_utils() {
let json = r#"{"jsonrpc":"2.0","method":"test","id":"123"}"#;
assert_eq!(utils::extract_method(json), Some("test".to_string()));
}
#[test]
fn test_error_codes() {
let parse_error = JsonRpcErrorCode::ParseError;
assert_eq!(parse_error.code(), -32700);
assert_eq!(parse_error.message(), "Parse error");
let app_error = JsonRpcErrorCode::ApplicationError(-32001);
assert_eq!(app_error.code(), -32001);
}
}