use crate::{Error, Result, types::*};
use serde_json::Value;
use uuid::Uuid;
pub mod handler;
pub mod router;
pub mod session;
#[cfg(test)]
mod tests;
pub use handler::*;
pub use router::*;
pub use session::*;
pub const PROTOCOL_VERSION: &str = "2025-06-18";
pub const SUPPORTED_VERSIONS: &[&str] = &["2025-06-18"];
pub mod error_codes {
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 const CAPABILITY_NOT_SUPPORTED: i32 = -32000;
pub const RESOURCE_NOT_FOUND: i32 = -32001;
pub const TOOL_NOT_FOUND: i32 = -32002;
pub const PROMPT_NOT_FOUND: i32 = -32003;
pub const PERMISSION_DENIED: i32 = -32004;
pub const RATE_LIMITED: i32 = -32005;
}
pub struct Protocol;
impl Protocol {
pub fn is_version_supported(version: &str) -> bool {
SUPPORTED_VERSIONS.contains(&version)
}
pub fn latest_version() -> &'static str {
PROTOCOL_VERSION
}
pub fn create_request(
method: &str,
params: Option<Value>,
id: Option<RequestId>,
) -> JsonRpcRequest {
JsonRpcRequest {
jsonrpc: "2.0".to_string(),
id,
method: method.to_string(),
params,
}
}
pub fn create_response(
id: Option<RequestId>,
result: Option<Value>,
error: Option<JsonRpcError>,
) -> JsonRpcResponse {
JsonRpcResponse {
jsonrpc: "2.0".to_string(),
id,
result,
error,
}
}
pub fn create_notification(method: &str, params: Option<Value>) -> JsonRpcNotification {
JsonRpcNotification {
jsonrpc: "2.0".to_string(),
method: method.to_string(),
params,
}
}
pub fn create_error(code: i32, message: &str, data: Option<Value>) -> JsonRpcError {
JsonRpcError {
code,
message: message.to_string(),
data,
}
}
pub fn generate_request_id() -> RequestId {
RequestId::from(Uuid::new_v4())
}
pub fn parse_message(message: &str) -> Result<JsonRpcMessage> {
let value: Value = serde_json::from_str(message)?;
if value.get("method").is_some() {
if value.get("id").is_some() {
let request: JsonRpcRequest = serde_json::from_value(value)?;
Ok(JsonRpcMessage::Request(request))
} else {
let notification: JsonRpcNotification = serde_json::from_value(value)?;
Ok(JsonRpcMessage::Notification(notification))
}
} else if value.get("result").is_some() || value.get("error").is_some() {
let response: JsonRpcResponse = serde_json::from_value(value)?;
Ok(JsonRpcMessage::Response(response))
} else {
Err(Error::InvalidRequest(
"Invalid JSON-RPC message format".to_string(),
))
}
}
pub fn serialize_message(message: &JsonRpcMessage) -> Result<String> {
match message {
JsonRpcMessage::Request(req) => serde_json::to_string(req),
JsonRpcMessage::Response(resp) => serde_json::to_string(resp),
JsonRpcMessage::Notification(notif) => serde_json::to_string(notif),
}
.map_err(Into::into)
}
pub fn error_to_jsonrpc(error: &Error) -> JsonRpcError {
match error {
Error::InvalidRequest(msg) => {
Self::create_error(error_codes::INVALID_REQUEST, msg, None)
}
Error::MethodNotFound(method) => {
Self::create_error(error_codes::METHOD_NOT_FOUND, method, None)
}
Error::InvalidParams(msg) => Self::create_error(error_codes::INVALID_PARAMS, msg, None),
Error::Protocol(crate::error::ProtocolError::CapabilityNotSupported(cap)) => {
Self::create_error(error_codes::CAPABILITY_NOT_SUPPORTED, cap, None)
}
Error::Protocol(crate::error::ProtocolError::ResourceNotFound(uri)) => {
Self::create_error(error_codes::RESOURCE_NOT_FOUND, uri, None)
}
Error::Protocol(crate::error::ProtocolError::ToolNotFound(name)) => {
Self::create_error(error_codes::TOOL_NOT_FOUND, name, None)
}
Error::Protocol(crate::error::ProtocolError::PromptNotFound(name)) => {
Self::create_error(error_codes::PROMPT_NOT_FOUND, name, None)
}
Error::Protocol(crate::error::ProtocolError::PermissionDenied) => {
Self::create_error(error_codes::PERMISSION_DENIED, "Permission denied", None)
}
Error::Protocol(crate::error::ProtocolError::RateLimitExceeded) => {
Self::create_error(error_codes::RATE_LIMITED, "Rate limit exceeded", None)
}
Error::Parse(msg) => Self::create_error(error_codes::PARSE_ERROR, msg, None),
_ => Self::create_error(error_codes::INTERNAL_ERROR, &error.to_string(), None),
}
}
pub fn validate_method_name(method: &str) -> bool {
!method.is_empty()
&& method
.chars()
.all(|c| c.is_alphanumeric() || c == '/' || c == '_')
}
pub fn method_category(method: &str) -> Option<&str> {
method.split('/').next()
}
}
#[derive(Debug, Clone)]
pub enum JsonRpcMessage {
Request(JsonRpcRequest),
Response(JsonRpcResponse),
Notification(JsonRpcNotification),
}
impl JsonRpcMessage {
pub fn id(&self) -> Option<&RequestId> {
match self {
JsonRpcMessage::Request(req) => req.id.as_ref(),
JsonRpcMessage::Response(resp) => resp.id.as_ref(),
JsonRpcMessage::Notification(_) => None,
}
}
pub fn method(&self) -> Option<&str> {
match self {
JsonRpcMessage::Request(req) => Some(&req.method),
JsonRpcMessage::Response(_) => None,
JsonRpcMessage::Notification(notif) => Some(¬if.method),
}
}
pub fn is_request(&self) -> bool {
matches!(self, JsonRpcMessage::Request(_))
}
pub fn is_response(&self) -> bool {
matches!(self, JsonRpcMessage::Response(_))
}
pub fn is_notification(&self) -> bool {
matches!(self, JsonRpcMessage::Notification(_))
}
}