use serde::{Deserialize, Serialize};
pub const MCP_VERSION: &str = "1.0";
pub const JSON_RPC_VERSION: &str = "2.0";
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerInfo {
pub name: String,
pub version: String,
#[serde(default = "default_protocol_version")]
pub protocol_version: String,
}
fn default_protocol_version() -> String {
MCP_VERSION.to_string()
}
impl Default for ServerInfo {
fn default() -> Self {
Self {
name: "god-graph-mcp".to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
protocol_version: MCP_VERSION.to_string(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum JsonSchemaType {
Object,
Array,
String,
Number,
Integer,
Boolean,
Null,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JsonSchema {
#[serde(rename = "type")]
pub schema_type: JsonSchemaType,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub properties: Option<std::collections::HashMap<String, Box<JsonSchema>>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub required: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub items: Option<Box<JsonSchema>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub default: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub minimum: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub maximum: Option<f64>,
}
impl JsonSchema {
pub fn string() -> Self {
Self {
schema_type: JsonSchemaType::String,
description: None,
properties: None,
required: None,
items: None,
default: None,
minimum: None,
maximum: None,
}
}
pub fn number() -> Self {
Self {
schema_type: JsonSchemaType::Number,
description: None,
properties: None,
required: None,
items: None,
default: None,
minimum: None,
maximum: None,
}
}
pub fn integer() -> Self {
Self {
schema_type: JsonSchemaType::Integer,
description: None,
properties: None,
required: None,
items: None,
default: None,
minimum: None,
maximum: None,
}
}
pub fn boolean() -> Self {
Self {
schema_type: JsonSchemaType::Boolean,
description: None,
properties: None,
required: None,
items: None,
default: None,
minimum: None,
maximum: None,
}
}
pub fn object() -> Self {
Self {
schema_type: JsonSchemaType::Object,
description: None,
properties: Some(std::collections::HashMap::new()),
required: None,
items: None,
default: None,
minimum: None,
maximum: None,
}
}
pub fn with_description(mut self, desc: impl Into<String>) -> Self {
self.description = Some(desc.into());
self
}
pub fn with_property(mut self, name: impl Into<String>, schema: JsonSchema) -> Self {
match self.properties {
Some(ref mut props) => {
props.insert(name.into(), Box::new(schema));
}
None => {
let mut props = std::collections::HashMap::new();
props.insert(name.into(), Box::new(schema));
self.properties = Some(props);
}
}
self
}
pub fn with_required(mut self, names: Vec<&str>) -> Self {
self.required = Some(names.into_iter().map(String::from).collect());
self
}
pub fn with_default(mut self, value: serde_json::Value) -> Self {
self.default = Some(value);
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Tool {
pub name: String,
pub description: String,
pub input_schema: JsonSchema,
}
impl Tool {
pub fn new(name: impl Into<String>, description: impl Into<String>, input_schema: JsonSchema) -> Self {
Self {
name: name.into(),
description: description.into(),
input_schema,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolResult {
pub content: Vec<Content>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_error: Option<bool>,
}
impl ToolResult {
pub fn text(text: impl Into<String>) -> Self {
Self {
content: vec![Content::text(text)],
is_error: None,
}
}
pub fn error(message: impl Into<String>) -> Self {
Self {
content: vec![Content::text(message)],
is_error: Some(true),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "lowercase")]
pub enum Content {
Text {
text: String,
},
Image {
data: String,
mime_type: String,
},
Resource {
resource: Resource,
},
}
impl Content {
pub fn text(text: impl Into<String>) -> Self {
Self::Text { text: text.into() }
}
pub fn image(data: impl Into<String>, mime_type: impl Into<String>) -> Self {
Self::Image {
data: data.into(),
mime_type: mime_type.into(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Resource {
pub uri: String,
pub name: String,
#[serde(flatten)]
pub content: ResourceContent,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ResourceContent {
Text {
text: String,
},
Blob {
blob: String,
mime_type: String,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JsonRpcRequest {
pub jsonrpc: String,
pub id: Option<serde_json::Value>,
pub method: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub params: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JsonRpcResponse {
pub jsonrpc: String,
pub id: Option<serde_json::Value>,
#[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: i32,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Capabilities {
#[serde(skip_serializing_if = "Option::is_none")]
pub tools: Option<ToolsCapability>,
#[serde(skip_serializing_if = "Option::is_none")]
pub resources: Option<ResourcesCapability>,
#[serde(skip_serializing_if = "Option::is_none")]
pub prompts: Option<PromptsCapability>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ToolsCapability {
#[serde(skip_serializing_if = "Option::is_none")]
pub list_changed: Option<bool>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ResourcesCapability {
#[serde(skip_serializing_if = "Option::is_none")]
pub subscribe: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub list_changed: Option<bool>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PromptsCapability {
#[serde(skip_serializing_if = "Option::is_none")]
pub list_changed: Option<bool>,
}