use serde::{Deserialize, Serialize};
use serde_json::Value;
pub const PROTOCOL_VERSION: u16 = 2;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Capabilities {
#[serde(default = "default_true")]
pub tools: bool,
#[serde(default)]
pub widgets: bool,
#[serde(default)]
pub mind: bool,
#[serde(default)]
pub vox: bool,
#[serde(default)]
pub resources: bool,
#[serde(default)]
pub prompts: bool,
#[serde(default)]
pub sampling: bool,
#[serde(default)]
pub elicitation: bool,
#[serde(default)]
pub streaming: bool,
}
fn default_true() -> bool {
true
}
impl Default for Capabilities {
fn default() -> Self {
Self {
tools: true,
widgets: false,
mind: false,
vox: false,
resources: false,
prompts: false,
sampling: false,
elicitation: false,
streaming: false,
}
}
}
impl Capabilities {
pub fn host_all() -> Self {
Self {
tools: true,
widgets: true,
mind: true,
vox: true,
resources: true,
prompts: true,
sampling: true,
elicitation: true,
streaming: true,
}
}
pub fn intersect(&self, other: &Self) -> Self {
Self {
tools: self.tools && other.tools,
widgets: self.widgets && other.widgets,
mind: self.mind && other.mind,
vox: self.vox && other.vox,
resources: self.resources && other.resources,
prompts: self.prompts && other.prompts,
sampling: self.sampling && other.sampling,
elicitation: self.elicitation && other.elicitation,
streaming: self.streaming && other.streaming,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HostInfo {
pub name: String,
pub version: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExtensionInfo {
pub name: String,
pub version: String,
pub sdk_version: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InitializeParams {
pub protocol_version: u16,
pub host_info: HostInfo,
pub capabilities: Capabilities,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InitializeResult {
pub protocol_version: u16,
pub extension_info: ExtensionInfo,
pub capabilities: Capabilities,
#[serde(default)]
pub tools: Vec<Value>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_capabilities() {
let caps = Capabilities::default();
assert!(caps.tools);
assert!(!caps.widgets);
assert!(!caps.sampling);
}
#[test]
fn test_host_all() {
let caps = Capabilities::host_all();
assert!(caps.tools);
assert!(caps.widgets);
assert!(caps.sampling);
assert!(caps.elicitation);
}
#[test]
fn test_intersect() {
let host = Capabilities::host_all();
let ext = Capabilities {
tools: true,
widgets: true,
mind: false,
..Default::default()
};
let active = host.intersect(&ext);
assert!(active.tools);
assert!(active.widgets);
assert!(!active.mind);
assert!(!active.sampling);
}
#[test]
fn test_initialize_params_roundtrip() {
let params = InitializeParams {
protocol_version: PROTOCOL_VERSION,
host_info: HostInfo {
name: "omegon".to_string(),
version: "0.16.0".to_string(),
},
capabilities: Capabilities::host_all(),
};
let json = serde_json::to_string(¶ms).unwrap();
let parsed: InitializeParams = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.protocol_version, 2);
assert_eq!(parsed.host_info.name, "omegon");
assert!(parsed.capabilities.sampling);
}
#[test]
fn test_initialize_result_roundtrip() {
let result = InitializeResult {
protocol_version: PROTOCOL_VERSION,
extension_info: ExtensionInfo {
name: "scribe".to_string(),
version: "0.2.0".to_string(),
sdk_version: "0.16.0".to_string(),
},
capabilities: Capabilities {
tools: true,
resources: true,
..Default::default()
},
tools: vec![serde_json::json!({
"name": "list_issues",
"label": "List Issues",
"description": "List issues",
"parameters": {"type": "object", "properties": {}}
})],
};
let json = serde_json::to_string(&result).unwrap();
let parsed: InitializeResult = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.extension_info.name, "scribe");
assert!(parsed.capabilities.resources);
assert!(!parsed.capabilities.sampling);
assert_eq!(parsed.tools.len(), 1);
}
}