use serde::{Deserialize, Serialize};
use serde_json::Value;
pub(crate) const PROTOCOL_VERSION: &str = "2025-03-26";
pub(crate) const METHOD_INITIALIZE: &str = "initialize";
pub(crate) const METHOD_INITIALIZED: &str = "notifications/initialized";
pub(crate) const METHOD_TOOLS_LIST: &str = "tools/list";
pub(crate) const METHOD_TOOLS_CALL: &str = "tools/call";
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub(crate) struct Implementation {
pub(crate) name: String,
pub(crate) version: String,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
pub(crate) struct ClientCapabilities {}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub(crate) struct ServerCapabilities {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub(crate) tools: Option<ToolsCapability>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub(crate) struct ToolsCapability {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub(crate) list_changed: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub(crate) struct InitializeRequestParams {
pub(crate) protocol_version: String,
pub(crate) capabilities: ClientCapabilities,
pub(crate) client_info: Implementation,
}
impl Default for InitializeRequestParams {
fn default() -> Self {
Self {
protocol_version: PROTOCOL_VERSION.to_string(),
capabilities: ClientCapabilities::default(),
client_info: Implementation {
name: "magi-code".to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
},
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub(crate) struct InitializeResult {
pub(crate) protocol_version: String,
pub(crate) capabilities: ServerCapabilities,
pub(crate) server_info: Implementation,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub(crate) instructions: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub(crate) struct Tool {
pub(crate) name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub(crate) title: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub(crate) description: Option<String>,
#[serde(default = "default_input_schema")]
pub(crate) input_schema: Value,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub(crate) output_schema: Option<Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub(crate) annotations: Option<ToolAnnotations>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub(crate) struct ToolAnnotations {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub(crate) title: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub(crate) read_only_hint: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub(crate) destructive_hint: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub(crate) idempotent_hint: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub(crate) open_world_hint: Option<bool>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub(crate) struct ListToolsParams {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub(crate) cursor: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub(crate) struct ListToolsResult {
pub(crate) tools: Vec<Tool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub(crate) next_cursor: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub(crate) struct CallToolParams {
pub(crate) name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub(crate) arguments: Option<Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub(crate) struct CallToolResult {
pub(crate) content: Vec<ContentBlock>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub(crate) structured_content: Option<Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub(crate) is_error: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(
tag = "type",
rename_all = "snake_case",
rename_all_fields = "camelCase"
)]
pub(crate) enum ContentBlock {
Text { text: String },
Image { data: String, mime_type: String },
Audio { data: String, mime_type: String },
Resource { resource: ResourceContents },
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub(crate) struct ResourceContents {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub(crate) uri: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub(crate) mime_type: Option<String>,
#[serde(flatten)]
pub(crate) extra: serde_json::Map<String, Value>,
}
fn default_input_schema() -> Value {
serde_json::json!({"type":"object","properties":{}})
}