use serde::{Deserialize, Serialize};
pub const PROTOCOL_VERSION: u32 = 1;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProtocolVersion(pub u32);
pub mod methods {
pub const INITIALIZE: &str = "initialize";
pub const SESSION_NEW: &str = "session/new";
pub const SESSION_INITIALIZE: &str = "session/initialize";
pub const SESSION_LOAD: &str = "session/load";
pub const SESSION_PROMPT: &str = "session/prompt";
pub const SESSION_CANCEL: &str = "session/cancel";
pub const SESSION_LIST: &str = "session/list";
pub const SESSION_SET_MODE: &str = "session/set_mode";
pub const SESSION_SET_CONFIG_OPTION: &str = "session/set_config_option";
pub const AUTHENTICATE: &str = "authenticate";
pub const SESSION_UPDATE: &str = "session/update";
pub const SESSION_REQUEST_PERMISSION: &str = "session/request_permission";
pub const FS_READ_TEXT_FILE: &str = "fs/read_text_file";
pub const FS_WRITE_TEXT_FILE: &str = "fs/write_text_file";
pub const TERMINAL_CREATE: &str = "terminal/create";
pub const TERMINAL_OUTPUT: &str = "terminal/output";
pub const TERMINAL_KILL: &str = "terminal/kill";
pub const TERMINAL_RELEASE: &str = "terminal/release";
pub const TERMINAL_WAIT_FOR_EXIT: &str = "terminal/wait_for_exit";
}
pub type SessionId = String;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
#[serde(rename_all = "snake_case")]
pub enum StopReason {
#[default]
EndTurn,
MaxTokens,
Cancelled,
Incomplete,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum ToolKind {
Read,
Edit,
Delete,
Move,
Search,
Execute,
Think,
Fetch,
Other,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum ToolCallStatus {
Pending,
InProgress,
Completed,
Failed,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum Role {
User,
Assistant,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase", default)]
pub struct FileSystemCapabilities {
pub read_text_file: bool,
pub write_text_file: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase", default)]
pub struct TerminalCapabilities {
pub create: bool,
pub output: bool,
pub kill: bool,
pub release: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase", default)]
pub struct ClientCapabilities {
#[serde(skip_serializing_if = "Option::is_none")]
pub fs: Option<FileSystemCapabilities>,
#[serde(skip_serializing_if = "Option::is_none")]
pub terminal: Option<TerminalCapabilities>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClientInfo {
pub name: String,
pub version: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase", default)]
pub struct PromptCapabilities {
pub image: bool,
pub audio: bool,
pub embedded_context: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase", default)]
pub struct McpCapabilities {
pub http: bool,
pub sse: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase", default)]
pub struct SessionListCapabilities {
pub list: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase", default)]
pub struct SessionForkCapabilities {}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase", default)]
pub struct SessionResumeCapabilities {}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase", default)]
pub struct SessionCapabilities {
#[serde(skip_serializing_if = "Option::is_none")]
pub list: Option<SessionListCapabilities>,
#[serde(skip_serializing_if = "Option::is_none")]
pub fork: Option<SessionForkCapabilities>,
#[serde(skip_serializing_if = "Option::is_none")]
pub resume: Option<SessionResumeCapabilities>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase", default)]
pub struct AgentCapabilities {
#[serde(skip_serializing_if = "Option::is_none")]
pub load_session: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub prompt_capabilities: Option<PromptCapabilities>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mcp_capabilities: Option<McpCapabilities>,
#[serde(skip_serializing_if = "Option::is_none")]
pub session_capabilities: Option<SessionCapabilities>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase", default)]
pub struct Implementation {
pub name: String,
pub version: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase", default)]
pub struct AuthMethod {
pub id: String,
pub name: Option<String>,
pub description: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase", default)]
pub struct AvailableCommand {
pub name: String,
pub description: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase", default)]
pub struct AvailableModes {
pub id: String,
pub name: String,
pub description: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase", default)]
pub struct SessionModeState {
#[serde(default)]
pub current_mode_id: Option<String>,
#[serde(default)]
pub available_modes: Vec<AvailableModes>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase", default)]
pub struct SessionConfigOption {
#[serde(rename = "type")]
pub type_: String,
pub id: String,
pub name: String,
pub description: Option<String>,
pub category: Option<String>,
pub current_value: String,
pub options: Option<Vec<serde_json::Value>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionInfo {
pub session_id: SessionId,
pub cwd: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub updated_at: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub _meta: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ListSessionsResponse {
pub sessions: Vec<SessionInfo>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_cursor: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ContentBlock {
Text {
text: String,
},
Image {
source: ImageSource,
#[serde(skip_serializing_if = "Option::is_none")]
media_type: Option<String>,
},
Resource {
resource: EmbeddedResource,
},
ResourceLink {
url: String,
#[serde(skip_serializing_if = "Option::is_none")]
media_type: Option<String>,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ImageSource {
#[serde(rename = "type")]
pub type_: String,
pub data: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmbeddedResource {
pub uri: String,
#[serde(rename = "type")]
pub type_: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub mime_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub text: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ToolCall {
pub id: String,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub input: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub _meta: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ToolCallUpdate {
pub id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<ToolCallStatus>,
#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub raw_input: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub raw_output: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub kind: Option<ToolKind>,
#[serde(skip_serializing_if = "Option::is_none")]
pub locations: Option<Vec<String>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum PermissionOptionKind {
AllowOnce,
AllowAlways,
RejectOnce,
RejectAlways,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PermissionOption {
pub option_id: String,
pub kind: PermissionOptionKind,
#[serde(skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "outcome", rename_all = "camelCase")]
pub enum RequestPermissionOutcome {
Selected {
#[serde(rename = "optionId")]
option_id: String,
},
Cancelled,
}
impl RequestPermissionOutcome {
pub fn selected(option_id: impl Into<String>) -> Self {
RequestPermissionOutcome::Selected {
option_id: option_id.into(),
}
}
pub fn cancelled() -> Self {
RequestPermissionOutcome::Cancelled
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RequestPermissionResponse {
pub outcome: RequestPermissionOutcome,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PlanEntry {
pub id: String,
pub content: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case", tag = "session_update")]
pub enum SessionNotificationPayload {
Plan {
entries: Vec<PlanEntry>,
},
UserMessage {
content: Vec<ContentBlock>,
},
UserMessageChunk {
content: TextContent,
},
AgentMessage {
content: Vec<ContentBlock>,
},
AgentMessageChunk {
content: TextContent,
},
AgentThoughtChunk {
content: TextContent,
},
ToolCall {
tool_call_id: String,
title: Option<String>,
status: ToolCallStatus,
#[serde(skip_serializing_if = "Option::is_none")]
raw_input: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
kind: Option<ToolKind>,
#[serde(skip_serializing_if = "Option::is_none")]
locations: Option<Vec<String>>,
},
ToolCallUpdate {
tool_call_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
status: Option<ToolCallStatus>,
#[serde(skip_serializing_if = "Option::is_none")]
content: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
raw_output: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
locations: Option<Vec<String>>,
},
ModeChange {
mode_id: String,
},
CurrentModeUpdate {
current_mode_id: String,
},
ConfigOptionUpdate {
config_options: Vec<SessionConfigOption>,
},
SessionInfoUpdate {
#[serde(skip_serializing_if = "Option::is_none")]
title: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
updated_at: Option<String>,
},
UsageUpdate {
used: u32,
size: u32,
#[serde(skip_serializing_if = "Option::is_none")]
_meta: Option<serde_json::Value>,
},
AvailableCommandsUpdate {
#[serde(skip_serializing_if = "Option::is_none")]
available_commands: Option<Vec<AvailableCommand>>,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionNotification {
pub session_id: SessionId,
pub update: SessionNotificationPayload,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TextContent {
#[serde(rename = "type")]
pub type_: String,
pub text: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct InitializeRequest {
pub protocol_version: u32,
pub client_capabilities: ClientCapabilities,
pub client_info: ClientInfo,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase", default)]
pub struct InitializeResponse {
pub protocol_version: u32,
pub agent_capabilities: AgentCapabilities,
pub agent_info: Implementation,
pub auth_methods: Vec<AuthMethod>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct McpServerConfig {
pub name: String,
pub command: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub args: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub env: Option<Vec<McpEnvVar>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct McpEnvVar {
pub name: String,
pub value: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct McpHttpServerConfig {
#[serde(rename = "type")]
pub type_: String,
pub name: String,
pub url: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub headers: Option<Vec<McpHeader>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct McpHeader {
pub name: String,
pub value: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NewSessionRequest {
pub cwd: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub mcp_servers: Option<Vec<McpServerConfig>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub _meta: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase", default)]
pub struct NewSessionResponse {
pub session_id: SessionId,
pub config_options: Option<Vec<SessionConfigOption>>,
pub modes: Option<SessionModeState>,
pub models: Option<SessionModels>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase", default)]
pub struct SessionModels {
pub current_model_id: String,
pub available_models: Vec<AvailableModel>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase", default)]
pub struct AvailableModel {
pub model_id: String,
pub name: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LoadSessionRequest {
pub session_id: SessionId,
#[serde(skip_serializing_if = "Option::is_none")]
pub cwd: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mcp_servers: Option<Vec<McpServerConfig>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub _meta: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LoadSessionResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub config_options: Option<Vec<SessionConfigOption>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub modes: Option<SessionModeState>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PromptRequest {
pub session_id: SessionId,
pub prompt: Vec<ContentBlock>,
#[serde(skip_serializing_if = "Option::is_none")]
pub _meta: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PromptResult {
pub content: Vec<ContentBlock>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tool_calls: Option<Vec<ToolCall>>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct Usage {
#[serde(skip_serializing_if = "Option::is_none")]
pub input_tokens: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub output_tokens: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub total_tokens: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cached_read_tokens: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cached_write_tokens: Option<u32>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct PromptResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub result: Option<PromptResult>,
#[serde(default)]
pub stop_reason: StopReason,
#[serde(skip_serializing_if = "Option::is_none")]
pub usage: Option<Usage>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CancelNotification {
pub session_id: SessionId,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ListSessionsRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub cwd: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub _meta: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SetSessionModeRequest {
pub session_id: SessionId,
pub mode_id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SetSessionModeResponse {}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SetSessionConfigOptionRequest {
pub session_id: SessionId,
pub config_id: String,
pub value: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase", default)]
pub struct SetSessionConfigOptionResponse {
pub config_options: Option<Vec<SessionConfigOption>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AuthenticateRequest {
pub method_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub credentials: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AuthenticateResponse {}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RequestPermissionRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub options: Option<Vec<PermissionOption>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tool_call: Option<ToolCall>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ReadTextFileRequest {
pub path: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ReadTextFileResponse {
pub contents: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct WriteTextFileRequest {
pub path: String,
pub contents: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct WriteTextFileResponse {}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CreateTerminalRequest {
pub session_id: SessionId,
#[serde(skip_serializing_if = "Option::is_none")]
pub command: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub args: Option<Vec<String>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CreateTerminalResponse {
pub terminal_id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TerminalOutputRequest {
pub session_id: SessionId,
pub terminal_id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TerminalOutputResponse {
pub exit: Option<i32>,
pub stdout: String,
pub stderr: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct KillTerminalRequest {
pub session_id: SessionId,
pub terminal_id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct KillTerminalResponse {}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ReleaseTerminalRequest {
pub session_id: SessionId,
pub terminal_id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ReleaseTerminalResponse {}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct WaitForTerminalExitRequest {
pub session_id: SessionId,
pub terminal_id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct WaitForTerminalExitResponse {
pub exit: Option<i32>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_initialize_request_roundtrip() {
let req = InitializeRequest {
protocol_version: 1,
client_capabilities: ClientCapabilities::default(),
client_info: ClientInfo {
name: "test-client".to_string(),
version: "1.0.0".to_string(),
},
};
let json = serde_json::to_string(&req).expect("serialize InitializeRequest");
let parsed: InitializeRequest = serde_json::from_str(&json).expect("deserialize InitializeRequest");
assert_eq!(parsed.client_info.name, "test-client");
assert_eq!(parsed.protocol_version, 1);
}
#[test]
fn test_initialize_response_roundtrip() {
let resp = InitializeResponse {
protocol_version: 1,
agent_capabilities: AgentCapabilities::default(),
agent_info: Implementation {
name: "rsclaw".to_string(),
version: "0.1.0".to_string(),
},
auth_methods: vec![],
};
let json = serde_json::to_string(&resp).expect("serialize InitializeResponse");
let parsed: InitializeResponse = serde_json::from_str(&json).expect("deserialize InitializeResponse");
assert_eq!(parsed.agent_info.name, "rsclaw");
assert_eq!(parsed.protocol_version, 1);
}
#[test]
fn test_new_session_request_roundtrip() {
let req = NewSessionRequest {
cwd: "/project".to_string(),
mcp_servers: None,
_meta: None,
};
let json = serde_json::to_string(&req).expect("serialize NewSessionRequest");
let parsed: NewSessionRequest = serde_json::from_str(&json).expect("deserialize NewSessionRequest");
assert_eq!(parsed.cwd, "/project");
}
#[test]
fn test_prompt_request_roundtrip() {
let req = PromptRequest {
session_id: "sess_abc123".to_string(),
prompt: vec![ContentBlock::Text {
text: "Hello world".to_string(),
}],
_meta: None,
};
let json = serde_json::to_string(&req).expect("serialize PromptRequest");
let parsed: PromptRequest = serde_json::from_str(&json).expect("deserialize PromptRequest");
assert_eq!(parsed.session_id, "sess_abc123");
}
#[test]
fn test_stop_reason_variants() {
for reason in [
StopReason::EndTurn,
StopReason::MaxTokens,
StopReason::Cancelled,
StopReason::Incomplete,
] {
let json = serde_json::to_string(&reason).expect("serialize StopReason");
let parsed: StopReason = serde_json::from_str(&json).expect("deserialize StopReason");
assert_eq!(reason, parsed);
}
}
#[test]
fn test_session_id_type() {
let sid: SessionId = "custom_session_123".to_string();
let json = serde_json::to_string(&sid).expect("serialize SessionId");
assert_eq!(json, "\"custom_session_123\"");
let parsed: SessionId = serde_json::from_str(&json).expect("deserialize SessionId");
assert_eq!(parsed, "custom_session_123");
}
#[test]
fn test_content_block_variants() {
let text = ContentBlock::Text {
text: "Hello".to_string(),
};
let json = serde_json::to_string(&text).expect("serialize ContentBlock");
assert!(json.contains("\"type\":\"text\""));
let parsed: ContentBlock = serde_json::from_str(&json).expect("deserialize ContentBlock");
assert!(matches!(parsed, ContentBlock::Text { .. }));
}
#[test]
fn test_auth_method_roundtrip() {
let method = AuthMethod {
id: "interactive".to_string(),
name: Some("Interactive Login".to_string()),
description: None,
};
let json = serde_json::to_string(&method).expect("serialize AuthMethod");
let parsed: AuthMethod = serde_json::from_str(&json).expect("deserialize AuthMethod");
assert_eq!(parsed.id, "interactive");
assert_eq!(parsed.name.expect("name should be Some"), "Interactive Login");
}
#[test]
fn test_tool_kind_variants() {
for kind in [
ToolKind::Read,
ToolKind::Edit,
ToolKind::Delete,
ToolKind::Move,
ToolKind::Search,
ToolKind::Execute,
ToolKind::Think,
ToolKind::Fetch,
ToolKind::Other,
] {
let json = serde_json::to_string(&kind).expect("serialize ToolKind");
let parsed: ToolKind = serde_json::from_str(&json).expect("deserialize ToolKind");
assert_eq!(kind, parsed);
}
}
#[test]
fn test_role_variants() {
for role in [Role::User, Role::Assistant] {
let json = serde_json::to_string(&role).expect("serialize Role");
let parsed: Role = serde_json::from_str(&json).expect("deserialize Role");
assert_eq!(role, parsed);
}
}
#[test]
fn test_client_capabilities_default() {
let caps = ClientCapabilities::default();
assert!(caps.fs.is_none());
assert!(caps.terminal.is_none());
}
#[test]
fn test_fs_read_text_file_roundtrip() {
let req = ReadTextFileRequest {
path: "/etc/passwd".to_string(),
};
let json = serde_json::to_string(&req).expect("serialize ReadTextFileRequest");
let parsed: ReadTextFileRequest = serde_json::from_str(&json).expect("deserialize ReadTextFileRequest");
assert_eq!(parsed.path, "/etc/passwd");
let resp = ReadTextFileResponse {
contents: "file contents".to_string(),
};
let json = serde_json::to_string(&resp).expect("serialize ReadTextFileResponse");
assert!(json.contains("file contents"));
}
#[test]
fn test_terminal_create_roundtrip() {
let req = CreateTerminalRequest {
session_id: "sess_abc".to_string(),
command: Some("bash".to_string()),
args: Some(vec!["-c".to_string(), "ls".to_string()]),
};
let json = serde_json::to_string(&req).expect("serialize CreateTerminalRequest");
let parsed: CreateTerminalRequest = serde_json::from_str(&json).expect("deserialize CreateTerminalRequest");
assert!(parsed.args.is_some());
let resp = CreateTerminalResponse {
terminal_id: "term_1".to_string(),
};
let json = serde_json::to_string(&resp).expect("serialize CreateTerminalResponse");
let parsed: CreateTerminalResponse = serde_json::from_str(&json).expect("deserialize CreateTerminalResponse");
assert_eq!(parsed.terminal_id, "term_1");
}
}