syncable_cli/agent/ide/
types.rs

1//! MCP Protocol Types
2//!
3//! JSON-RPC and MCP notification types for IDE communication.
4
5use serde::{Deserialize, Serialize};
6
7/// JSON-RPC 2.0 request
8#[derive(Debug, Serialize)]
9pub struct JsonRpcRequest {
10    pub jsonrpc: &'static str,
11    pub id: u64,
12    pub method: String,
13    pub params: serde_json::Value,
14}
15
16impl JsonRpcRequest {
17    pub fn new(id: u64, method: &str, params: serde_json::Value) -> Self {
18        Self {
19            jsonrpc: "2.0",
20            id,
21            method: method.to_string(),
22            params,
23        }
24    }
25}
26
27/// JSON-RPC 2.0 response
28#[derive(Debug, Deserialize)]
29pub struct JsonRpcResponse {
30    pub jsonrpc: String,
31    pub id: Option<u64>,
32    #[serde(default)]
33    pub result: Option<serde_json::Value>,
34    #[serde(default)]
35    pub error: Option<JsonRpcError>,
36}
37
38/// JSON-RPC error
39#[derive(Debug, Deserialize)]
40pub struct JsonRpcError {
41    pub code: i32,
42    pub message: String,
43    #[serde(default)]
44    pub data: Option<serde_json::Value>,
45}
46
47/// JSON-RPC notification (no id)
48#[derive(Debug, Deserialize)]
49pub struct JsonRpcNotification {
50    pub jsonrpc: String,
51    pub method: String,
52    pub params: serde_json::Value,
53}
54
55/// MCP Initialize request parameters
56#[derive(Debug, Serialize)]
57pub struct InitializeParams {
58    #[serde(rename = "protocolVersion")]
59    pub protocol_version: String,
60    #[serde(rename = "clientInfo")]
61    pub client_info: ClientInfo,
62    pub capabilities: ClientCapabilities,
63}
64
65#[derive(Debug, Serialize)]
66pub struct ClientInfo {
67    pub name: String,
68    pub version: String,
69}
70
71#[derive(Debug, Serialize)]
72pub struct ClientCapabilities {
73    // Empty for now, can be extended
74}
75
76/// MCP Tool call parameters
77#[derive(Debug, Serialize)]
78pub struct ToolCallParams {
79    pub name: String,
80    pub arguments: serde_json::Value,
81}
82
83/// MCP Tool call result
84#[derive(Debug, Deserialize)]
85pub struct ToolCallResult {
86    #[serde(default)]
87    pub content: Vec<ToolContent>,
88    #[serde(rename = "isError", default)]
89    pub is_error: bool,
90}
91
92#[derive(Debug, Deserialize)]
93pub struct ToolContent {
94    #[serde(rename = "type")]
95    pub content_type: String,
96    #[serde(default)]
97    pub text: Option<String>,
98}
99
100/// IDE Diff Accepted notification parameters
101#[derive(Debug, Deserialize)]
102pub struct IdeDiffAcceptedParams {
103    #[serde(rename = "filePath")]
104    pub file_path: String,
105    pub content: String,
106}
107
108/// IDE Diff Rejected notification parameters
109#[derive(Debug, Deserialize)]
110pub struct IdeDiffRejectedParams {
111    #[serde(rename = "filePath")]
112    pub file_path: String,
113}
114
115/// IDE Diff Closed notification parameters (for backwards compatibility)
116#[derive(Debug, Deserialize)]
117pub struct IdeDiffClosedParams {
118    #[serde(rename = "filePath")]
119    pub file_path: String,
120    #[serde(default)]
121    pub content: Option<String>,
122}
123
124/// IDE Context notification parameters
125#[derive(Debug, Deserialize)]
126pub struct IdeContextParams {
127    #[serde(rename = "workspaceState", default)]
128    pub workspace_state: Option<WorkspaceState>,
129}
130
131#[derive(Debug, Deserialize)]
132pub struct WorkspaceState {
133    #[serde(rename = "openFiles", default)]
134    pub open_files: Vec<OpenFile>,
135    #[serde(rename = "isTrusted", default)]
136    pub is_trusted: Option<bool>,
137}
138
139#[derive(Debug, Deserialize)]
140pub struct OpenFile {
141    pub path: String,
142    pub timestamp: u64,
143    #[serde(rename = "isActive", default)]
144    pub is_active: bool,
145    #[serde(rename = "selectedText", default)]
146    pub selected_text: Option<String>,
147}
148
149/// Connection config read from port file
150#[derive(Debug, Deserialize)]
151pub struct ConnectionConfig {
152    pub port: u16,
153    #[serde(rename = "workspacePath", default)]
154    pub workspace_path: Option<String>,
155    #[serde(rename = "authToken", default)]
156    pub auth_token: Option<String>,
157}
158
159/// Open diff request arguments
160#[derive(Debug, Serialize)]
161pub struct OpenDiffArgs {
162    #[serde(rename = "filePath")]
163    pub file_path: String,
164    #[serde(rename = "newContent")]
165    pub new_content: String,
166}
167
168/// Close diff request arguments
169#[derive(Debug, Serialize)]
170pub struct CloseDiffArgs {
171    #[serde(rename = "filePath")]
172    pub file_path: String,
173    #[serde(
174        rename = "suppressNotification",
175        skip_serializing_if = "Option::is_none"
176    )]
177    pub suppress_notification: Option<bool>,
178}
179
180/// Get diagnostics request arguments
181#[derive(Debug, Serialize)]
182pub struct GetDiagnosticsArgs {
183    /// Optional file URI to get diagnostics for. If not provided, gets all diagnostics.
184    #[serde(skip_serializing_if = "Option::is_none")]
185    pub uri: Option<String>,
186}
187
188/// Diagnostic severity levels (matches VS Code DiagnosticSeverity)
189#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
190pub enum DiagnosticSeverity {
191    Error = 0,
192    Warning = 1,
193    Information = 2,
194    Hint = 3,
195}
196
197impl DiagnosticSeverity {
198    pub fn from_number(n: u8) -> Self {
199        match n {
200            0 => Self::Error,
201            1 => Self::Warning,
202            2 => Self::Information,
203            _ => Self::Hint,
204        }
205    }
206
207    pub fn as_str(&self) -> &'static str {
208        match self {
209            Self::Error => "error",
210            Self::Warning => "warning",
211            Self::Information => "info",
212            Self::Hint => "hint",
213        }
214    }
215}
216
217/// A diagnostic message from the language server
218#[derive(Debug, Clone, Deserialize, Serialize)]
219pub struct Diagnostic {
220    /// The file path where the diagnostic occurred
221    pub file: String,
222    /// Line number (1-based)
223    pub line: u32,
224    /// Column number (1-based)
225    pub column: u32,
226    /// End line number (1-based)
227    #[serde(rename = "endLine")]
228    pub end_line: Option<u32>,
229    /// End column number (1-based)
230    #[serde(rename = "endColumn")]
231    pub end_column: Option<u32>,
232    /// Severity level
233    pub severity: DiagnosticSeverity,
234    /// The diagnostic message
235    pub message: String,
236    /// Source of the diagnostic (e.g., "rust-analyzer", "eslint")
237    #[serde(default)]
238    pub source: Option<String>,
239    /// Diagnostic code
240    #[serde(default)]
241    pub code: Option<String>,
242}
243
244/// Response from getDiagnostics
245#[derive(Debug, Clone, Deserialize, Serialize)]
246pub struct DiagnosticsResponse {
247    pub diagnostics: Vec<Diagnostic>,
248    pub total_errors: u32,
249    pub total_warnings: u32,
250}