use serde::{Deserialize, Serialize};
use serde_json::Value;
pub const LATEST_PROTOCOL_VERSION: &str = "2025-06-18";
pub const SUPPORTED_PROTOCOL_VERSIONS: &[&str] = &["2025-06-18", "2025-03-26"];
#[derive(Debug, Deserialize)]
pub struct Request {
pub jsonrpc: String,
pub id: Option<Value>,
pub method: String,
#[serde(default)]
pub params: Value,
}
#[derive(Debug, Serialize)]
pub struct Response {
pub jsonrpc: &'static str,
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<RpcError>,
}
#[derive(Debug, Serialize)]
pub struct RpcError {
pub code: i32,
pub message: String,
}
impl Response {
pub fn ok(id: Option<Value>, result: Value) -> Self {
Self {
jsonrpc: "2.0",
id,
result: Some(result),
error: None,
}
}
pub fn error(id: Option<Value>, code: i32, message: impl Into<String>) -> Self {
Self {
jsonrpc: "2.0",
id,
result: None,
error: Some(RpcError {
code,
message: message.into(),
}),
}
}
}
#[derive(Debug, Serialize)]
pub struct Tool {
pub name: &'static str,
pub description: &'static str,
#[serde(rename = "inputSchema")]
pub input_schema: Value,
#[serde(skip_serializing_if = "Option::is_none")]
pub annotations: Option<Value>,
}
pub fn negotiate_protocol(requested: Option<&str>) -> &'static str {
requested
.and_then(|value| {
SUPPORTED_PROTOCOL_VERSIONS
.iter()
.copied()
.find(|v| *v == value)
})
.unwrap_or(LATEST_PROTOCOL_VERSION)
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn serializes_success_without_error() {
let value =
serde_json::to_value(Response::ok(Some(json!(1)), json!({"pong": true}))).unwrap();
assert_eq!(value["jsonrpc"], "2.0");
assert_eq!(value["result"]["pong"], true);
assert!(value.get("error").is_none());
}
#[test]
fn negotiates_known_and_unknown_versions() {
assert_eq!(negotiate_protocol(Some("2025-03-26")), "2025-03-26");
assert_eq!(
negotiate_protocol(Some("1900-01-01")),
LATEST_PROTOCOL_VERSION
);
}
}