use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JsonRpcRequest {
pub jsonrpc: String,
pub id: Option<RequestId>,
pub method: String,
#[serde(default)]
pub params: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JsonRpcResponse {
pub jsonrpc: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<RequestId>,
#[serde(skip_serializing_if = "Option::is_none")]
pub result: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<JsonRpcError>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JsonRpcError {
pub code: i64,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum RequestId {
Number(i64),
String(String),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Tool {
pub name: String,
pub description: String,
#[serde(rename = "inputSchema")]
pub input_schema: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolResult {
pub content: Vec<ContentBlock>,
#[serde(default)]
pub is_error: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContentBlock {
#[serde(rename = "type")]
pub content_type: String,
pub text: String,
}
impl ToolResult {
pub fn text(text: impl Into<String>) -> Self {
Self {
content: vec![ContentBlock {
content_type: "text".to_string(),
text: text.into(),
}],
is_error: false,
}
}
pub fn error(text: impl Into<String>) -> Self {
Self {
content: vec![ContentBlock {
content_type: "text".to_string(),
text: text.into(),
}],
is_error: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerInfo {
pub name: String,
pub version: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerCapabilities {
pub tools: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InitializeResult {
#[serde(rename = "protocolVersion")]
pub protocol_version: String,
pub capabilities: ServerCapabilities,
#[serde(rename = "serverInfo")]
pub server_info: ServerInfo,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_request() {
let req: JsonRpcRequest =
serde_json::from_str(r#"{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}"#)
.unwrap();
assert_eq!(req.method, "tools/list");
}
#[test]
fn serialize_response() {
let resp = JsonRpcResponse {
jsonrpc: "2.0".to_string(),
id: Some(RequestId::Number(1)),
result: Some(serde_json::json!({"ok": true})),
error: None,
};
let json = serde_json::to_string(&resp).unwrap();
assert!(json.contains("\"result\""));
}
#[test]
fn tool_result_text() {
let result = ToolResult::text("hello");
assert!(!result.is_error);
assert_eq!(result.content[0].text, "hello");
}
#[test]
fn tool_result_error() {
let result = ToolResult::error("fail");
assert!(result.is_error);
}
}