use serde::{Deserialize, Serialize};
use serde_json::Value;
pub const JSONRPC_VERSION: &str = "2.0";
pub const A2A_MEDIA_TYPE: &str = "application/a2a+json";
pub mod methods {
pub const SEND_MESSAGE: &str = "message/send";
pub const SEND_STREAMING_MESSAGE: &str = "message/stream";
pub const GET_TASK: &str = "tasks/get";
pub const LIST_TASKS: &str = "tasks/list";
pub const CANCEL_TASK: &str = "tasks/cancel";
pub const SUBSCRIBE_TASK: &str = "tasks/subscribe";
pub const CREATE_PUSH_NOTIFICATION: &str = "tasks/pushNotification/create";
pub const GET_PUSH_NOTIFICATION: &str = "tasks/pushNotification/get";
pub const LIST_PUSH_NOTIFICATIONS: &str = "tasks/pushNotification/list";
pub const DELETE_PUSH_NOTIFICATION: &str = "tasks/pushNotification/delete";
pub const GET_EXTENDED_AGENT_CARD: &str = "agent/authenticatedExtendedCard";
}
#[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>,
pub id: RequestId,
}
impl JsonRpcRequest {
pub fn new(method: impl Into<String>, params: Option<Value>) -> Self {
Self {
jsonrpc: JSONRPC_VERSION.into(),
method: method.into(),
params,
id: RequestId::Number(rand_id()),
}
}
pub fn send_message(params: Value) -> Self {
Self::new(methods::SEND_MESSAGE, Some(params))
}
pub fn send_streaming_message(params: Value) -> Self {
Self::new(methods::SEND_STREAMING_MESSAGE, Some(params))
}
pub fn get_task(task_id: &str) -> Self {
Self::new(
methods::GET_TASK,
Some(serde_json::json!({ "taskId": task_id })),
)
}
pub fn cancel_task(task_id: &str) -> Self {
Self::new(
methods::CANCEL_TASK,
Some(serde_json::json!({ "taskId": task_id })),
)
}
pub fn list_tasks(params: Value) -> Self {
Self::new(methods::LIST_TASKS, Some(params))
}
}
#[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: RequestId,
}
impl JsonRpcResponse {
pub fn success(id: RequestId, result: Value) -> Self {
Self {
jsonrpc: JSONRPC_VERSION.into(),
result: Some(result),
error: None,
id,
}
}
pub fn error(id: RequestId, error: JsonRpcError) -> Self {
Self {
jsonrpc: JSONRPC_VERSION.into(),
result: None,
error: Some(error),
id,
}
}
pub fn is_error(&self) -> bool {
self.error.is_some()
}
pub fn into_result(self) -> Result<Value, JsonRpcError> {
if let Some(error) = self.error {
Err(error)
} else {
Ok(self.result.unwrap_or(Value::Null))
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JsonRpcError {
pub code: i64,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<Value>,
}
impl JsonRpcError {
pub fn parse_error(detail: impl Into<String>) -> Self {
Self {
code: -32700,
message: "Parse error".into(),
data: Some(Value::String(detail.into())),
}
}
pub fn invalid_request(detail: impl Into<String>) -> Self {
Self {
code: -32600,
message: "Invalid Request".into(),
data: Some(Value::String(detail.into())),
}
}
pub fn method_not_found(method: &str) -> Self {
Self {
code: -32601,
message: "Method not found".into(),
data: Some(Value::String(format!("Unknown method: {method}"))),
}
}
pub fn invalid_params(detail: impl Into<String>) -> Self {
Self {
code: -32602,
message: "Invalid params".into(),
data: Some(Value::String(detail.into())),
}
}
pub fn internal_error(detail: impl Into<String>) -> Self {
Self {
code: -32603,
message: "Internal error".into(),
data: Some(Value::String(detail.into())),
}
}
pub fn task_not_found(task_id: &str) -> Self {
Self {
code: -32001,
message: "Task not found".into(),
data: Some(Value::String(format!("Task {task_id} not found"))),
}
}
pub fn task_not_cancelable(task_id: &str) -> Self {
Self {
code: -32002,
message: "Task not cancelable".into(),
data: Some(Value::String(format!(
"Task {task_id} is in a terminal state"
))),
}
}
}
impl std::fmt::Display for JsonRpcError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "JSON-RPC error {}: {}", self.code, self.message)
}
}
impl std::error::Error for JsonRpcError {}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(untagged)]
pub enum RequestId {
Number(i64),
String(String),
}
fn rand_id() -> i64 {
use std::time::{SystemTime, UNIX_EPOCH};
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| (d.as_nanos() % i64::MAX as u128) as i64)
.unwrap_or(1)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_request_serialization() {
let req = JsonRpcRequest::send_message(serde_json::json!({
"message": {
"role": "user",
"parts": [{"type": "text", "text": "Hello"}]
}
}));
let json = serde_json::to_string(&req).unwrap();
assert!(json.contains("message/send"));
assert!(json.contains("2.0"));
let parsed: JsonRpcRequest = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.method, "message/send");
}
#[test]
fn test_response_success() {
let resp = JsonRpcResponse::success(
RequestId::Number(1),
serde_json::json!({"taskId": "abc123"}),
);
assert!(!resp.is_error());
assert!(resp.into_result().is_ok());
}
#[test]
fn test_response_error() {
let resp =
JsonRpcResponse::error(RequestId::Number(1), JsonRpcError::task_not_found("abc123"));
assert!(resp.is_error());
assert!(resp.into_result().is_err());
}
}