use serde::{Deserialize, Serialize};
use serde_json::Value;
pub const MCP_PROTOCOL_VERSION: &str = "2025-03-26";
#[derive(Debug, Clone, Serialize)]
pub struct Request<'a> {
pub jsonrpc: &'static str,
pub id: u64,
pub method: &'a str,
#[serde(skip_serializing_if = "Option::is_none")]
pub params: Option<Value>,
}
impl<'a> Request<'a> {
pub fn new(id: u64, method: &'a str, params: Option<Value>) -> Self {
Self {
jsonrpc: "2.0",
id,
method,
params,
}
}
}
#[derive(Debug, Clone, Serialize)]
pub struct Notification<'a> {
pub jsonrpc: &'static str,
pub method: &'a str,
#[serde(skip_serializing_if = "Option::is_none")]
pub params: Option<Value>,
}
impl<'a> Notification<'a> {
pub fn new(method: &'a str, params: Option<Value>) -> Self {
Self {
jsonrpc: "2.0",
method,
params,
}
}
}
#[derive(Debug, Clone, Deserialize)]
pub struct Response {
#[allow(dead_code)]
pub jsonrpc: String,
pub id: Option<u64>,
#[serde(default)]
pub result: Option<Value>,
#[serde(default)]
pub error: Option<RpcError>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct RpcError {
pub code: i32,
pub message: String,
#[allow(dead_code)]
#[serde(default)]
pub data: Option<Value>,
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct InitializeParams<'a> {
pub protocol_version: &'a str,
pub capabilities: Value,
pub client_info: ClientInfo<'a>,
}
#[derive(Debug, Clone, Serialize)]
pub struct ClientInfo<'a> {
pub name: &'a str,
pub version: &'a str,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct InitializeResult {
#[allow(dead_code)]
#[serde(default)]
pub protocol_version: Option<String>,
#[allow(dead_code)]
#[serde(default)]
pub capabilities: Value,
#[serde(default)]
pub server_info: Option<ServerInfo>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ServerInfo {
pub name: String,
#[allow(dead_code)]
#[serde(default)]
pub version: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ToolsListResult {
pub tools: Vec<McpToolDecl>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct McpToolDecl {
pub name: String,
#[serde(default)]
pub description: Option<String>,
#[serde(default)]
pub input_schema: Option<Value>,
}
#[derive(Debug, Clone, Serialize)]
pub struct ToolCallParams<'a> {
pub name: &'a str,
pub arguments: Value,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ToolCallResult {
#[serde(default)]
pub content: Vec<ContentBlock>,
#[serde(default)]
pub is_error: Option<bool>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(tag = "type", rename_all = "lowercase")]
pub enum ContentBlock {
Text { text: String },
Image { data: String, mime_type: String },
Resource { resource: Value },
#[serde(other)]
Unknown,
}
impl ToolCallResult {
pub fn flatten(self) -> Value {
let mut text = String::new();
let mut images: Vec<Value> = Vec::new();
for block in self.content {
match block {
ContentBlock::Text { text: t } => {
if !text.is_empty() {
text.push('\n');
}
text.push_str(&t);
}
ContentBlock::Image { data, mime_type } => {
images.push(serde_json::json!({
"mime_type": mime_type,
"data_base64": data,
}));
}
ContentBlock::Resource { resource } => {
if !text.is_empty() {
text.push('\n');
}
text.push_str(&resource.to_string());
}
ContentBlock::Unknown => {}
}
}
let mut out = serde_json::Map::new();
if !text.is_empty() {
out.insert("text".into(), Value::String(text));
}
if !images.is_empty() {
out.insert("images".into(), Value::Array(images));
}
if let Some(true) = self.is_error {
out.insert("is_error".into(), Value::Bool(true));
}
Value::Object(out)
}
}