use super::{Capabilities, Implementation, ProtocolVersion};
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct InitializeRequest {
#[serde(rename = "protocolVersion")]
pub protocol_version: ProtocolVersion,
pub capabilities: Capabilities,
#[serde(rename = "clientInfo")]
pub client_info: Implementation,
}
impl InitializeRequest {
pub fn new(
protocol_version: ProtocolVersion,
capabilities: Capabilities,
client_info: Implementation,
) -> Self {
Self {
protocol_version,
capabilities,
client_info,
}
}
pub fn basic(client_name: impl Into<String>, client_version: impl Into<String>) -> Self {
Self::new(
ProtocolVersion::default(),
Capabilities::default(),
Implementation::new(client_name, client_version),
)
}
pub fn with_client_metadata(mut self, key: impl Into<String>, value: Value) -> Self {
self.client_info.metadata.insert(key.into(), value);
self
}
pub fn is_supported_version(&self) -> bool {
self.protocol_version.is_supported()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct InitializeResponse {
#[serde(rename = "protocolVersion")]
pub protocol_version: ProtocolVersion,
pub capabilities: Capabilities,
#[serde(rename = "serverInfo")]
pub server_info: Implementation,
#[serde(skip_serializing_if = "Option::is_none")]
pub instructions: Option<String>,
}
impl InitializeResponse {
pub fn new(
protocol_version: ProtocolVersion,
capabilities: Capabilities,
server_info: Implementation,
instructions: Option<String>,
) -> Self {
Self {
protocol_version,
capabilities,
server_info,
instructions,
}
}
pub fn basic(server_name: impl Into<String>, server_version: impl Into<String>) -> Self {
Self::new(
ProtocolVersion::default(),
Capabilities::default(),
Implementation::new(server_name, server_version),
None,
)
}
pub fn with_instructions(mut self, instructions: impl Into<String>) -> Self {
self.instructions = Some(instructions.into());
self
}
pub fn with_server_metadata(mut self, key: impl Into<String>, value: Value) -> Self {
self.server_info.metadata.insert(key.into(), value);
self
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct InitializedNotification {
#[serde(flatten)]
pub metadata: std::collections::HashMap<String, Value>,
}
impl InitializedNotification {
pub fn new() -> Self {
Self {
metadata: std::collections::HashMap::new(),
}
}
pub fn with_metadata(metadata: std::collections::HashMap<String, Value>) -> Self {
Self { metadata }
}
pub fn add_metadata(mut self, key: impl Into<String>, value: Value) -> Self {
self.metadata.insert(key.into(), value);
self
}
}
impl Default for InitializedNotification {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct PingRequest {
#[serde(flatten)]
pub metadata: std::collections::HashMap<String, Value>,
}
impl PingRequest {
pub fn new() -> Self {
Self {
metadata: std::collections::HashMap::new(),
}
}
pub fn with_timestamp(timestamp: impl Into<String>) -> Self {
let mut ping = Self::new();
ping.metadata
.insert("timestamp".to_string(), Value::String(timestamp.into()));
ping
}
pub fn add_metadata(mut self, key: impl Into<String>, value: Value) -> Self {
self.metadata.insert(key.into(), value);
self
}
}
impl Default for PingRequest {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct PongResponse {
#[serde(flatten)]
pub metadata: std::collections::HashMap<String, Value>,
}
impl PongResponse {
pub fn new() -> Self {
Self {
metadata: std::collections::HashMap::new(),
}
}
pub fn echo(ping: &PingRequest) -> Self {
Self {
metadata: ping.metadata.clone(),
}
}
pub fn add_metadata(mut self, key: impl Into<String>, value: Value) -> Self {
self.metadata.insert(key.into(), value);
self
}
}
impl Default for PongResponse {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_initialize_request_creation() {
let request = InitializeRequest::basic("test-client", "1.0.0");
assert_eq!(request.protocol_version, ProtocolVersion::default());
assert_eq!(request.client_info.name, "test-client");
assert_eq!(request.client_info.version, "1.0.0");
assert!(request.is_supported_version());
}
#[test]
fn test_initialize_request_with_metadata() {
let request = InitializeRequest::basic("test-client", "1.0.0")
.with_client_metadata("platform", json!("rust"))
.with_client_metadata("os", json!("linux"));
assert_eq!(
request.client_info.metadata.get("platform").unwrap(),
&json!("rust")
);
assert_eq!(
request.client_info.metadata.get("os").unwrap(),
&json!("linux")
);
}
#[test]
fn test_initialize_response_creation() {
let response = InitializeResponse::basic("test-server", "2.0.0")
.with_instructions("Use tools carefully")
.with_server_metadata("max_tools", json!(10));
assert_eq!(response.server_info.name, "test-server");
assert_eq!(
response.instructions,
Some("Use tools carefully".to_string())
);
assert_eq!(
response.server_info.metadata.get("max_tools").unwrap(),
&json!(10)
);
}
#[test]
fn test_initialized_notification() {
let notification =
InitializedNotification::new().add_metadata("timestamp", json!("2024-01-15T10:30:00Z"));
assert_eq!(
notification.metadata.get("timestamp").unwrap(),
&json!("2024-01-15T10:30:00Z")
);
}
#[test]
fn test_ping_pong() {
let ping =
PingRequest::with_timestamp("2024-01-15T10:30:00Z").add_metadata("sequence", json!(1));
let pong =
PongResponse::echo(&ping).add_metadata("response_time", json!("2024-01-15T10:30:01Z"));
assert_eq!(
pong.metadata.get("timestamp").unwrap(),
&json!("2024-01-15T10:30:00Z")
);
assert_eq!(pong.metadata.get("sequence").unwrap(), &json!(1));
assert_eq!(
pong.metadata.get("response_time").unwrap(),
&json!("2024-01-15T10:30:01Z")
);
}
#[test]
fn test_serialization_roundtrip() {
let request = InitializeRequest::basic("test", "1.0.0");
let json = serde_json::to_string(&request).unwrap();
let deserialized: InitializeRequest = serde_json::from_str(&json).unwrap();
assert_eq!(request, deserialized);
}
}