agit/mcp/
protocol.rs

1//! MCP (Model Context Protocol) types.
2//!
3//! This module defines the JSON-RPC types used for MCP communication.
4//! MCP uses JSON-RPC 2.0 over stdio.
5
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8
9/// JSON-RPC 2.0 request.
10#[derive(Debug, Deserialize)]
11pub struct JsonRpcRequest {
12    pub jsonrpc: String,
13    pub id: Option<Value>,
14    pub method: String,
15    #[serde(default)]
16    pub params: Option<Value>,
17}
18
19/// JSON-RPC 2.0 response.
20#[derive(Debug, Serialize)]
21pub struct JsonRpcResponse {
22    pub jsonrpc: String,
23    #[serde(skip_serializing_if = "Option::is_none")]
24    pub id: Option<Value>,
25    #[serde(skip_serializing_if = "Option::is_none")]
26    pub result: Option<Value>,
27    #[serde(skip_serializing_if = "Option::is_none")]
28    pub error: Option<JsonRpcError>,
29}
30
31impl JsonRpcResponse {
32    /// Create a success response.
33    pub fn success(id: Option<Value>, result: Value) -> Self {
34        Self {
35            jsonrpc: "2.0".to_string(),
36            id,
37            result: Some(result),
38            error: None,
39        }
40    }
41
42    /// Create an error response.
43    pub fn error(id: Option<Value>, code: i32, message: &str) -> Self {
44        Self {
45            jsonrpc: "2.0".to_string(),
46            id,
47            result: None,
48            error: Some(JsonRpcError {
49                code,
50                message: message.to_string(),
51                data: None,
52            }),
53        }
54    }
55}
56
57/// JSON-RPC 2.0 error object.
58#[derive(Debug, Serialize)]
59pub struct JsonRpcError {
60    pub code: i32,
61    pub message: String,
62    #[serde(skip_serializing_if = "Option::is_none")]
63    pub data: Option<Value>,
64}
65
66// Standard JSON-RPC error codes
67pub const PARSE_ERROR: i32 = -32700;
68pub const INVALID_REQUEST: i32 = -32600;
69pub const METHOD_NOT_FOUND: i32 = -32601;
70pub const INVALID_PARAMS: i32 = -32602;
71pub const INTERNAL_ERROR: i32 = -32603;
72
73/// MCP Initialize request parameters.
74#[derive(Debug, Deserialize)]
75#[serde(rename_all = "camelCase")]
76pub struct InitializeParams {
77    pub protocol_version: String,
78    pub capabilities: ClientCapabilities,
79    pub client_info: ClientInfo,
80}
81
82/// Client capabilities.
83#[derive(Debug, Deserialize, Default)]
84pub struct ClientCapabilities {
85    #[serde(default)]
86    pub roots: Option<RootsCapability>,
87    #[serde(default)]
88    pub sampling: Option<Value>,
89}
90
91/// Roots capability.
92#[derive(Debug, Deserialize)]
93#[serde(rename_all = "camelCase")]
94pub struct RootsCapability {
95    #[serde(default)]
96    pub list_changed: bool,
97}
98
99/// Client info.
100#[derive(Debug, Deserialize)]
101pub struct ClientInfo {
102    pub name: String,
103    pub version: String,
104}
105
106/// MCP Initialize response result.
107#[derive(Debug, Serialize)]
108#[serde(rename_all = "camelCase")]
109pub struct InitializeResult {
110    pub protocol_version: String,
111    pub capabilities: ServerCapabilities,
112    pub server_info: ServerInfo,
113}
114
115/// Server capabilities.
116#[derive(Debug, Serialize)]
117pub struct ServerCapabilities {
118    pub tools: ToolsCapability,
119}
120
121/// Tools capability.
122#[derive(Debug, Serialize)]
123#[serde(rename_all = "camelCase")]
124pub struct ToolsCapability {
125    pub list_changed: bool,
126}
127
128/// Server info.
129#[derive(Debug, Serialize)]
130pub struct ServerInfo {
131    pub name: String,
132    pub version: String,
133}
134
135/// Tool definition.
136#[derive(Debug, Serialize, Clone)]
137#[serde(rename_all = "camelCase")]
138pub struct ToolDefinition {
139    pub name: String,
140    pub description: String,
141    pub input_schema: Value,
142}
143
144/// Tools list response.
145#[derive(Debug, Serialize)]
146pub struct ToolsListResult {
147    pub tools: Vec<ToolDefinition>,
148}
149
150/// Tool call request parameters.
151#[derive(Debug, Deserialize)]
152pub struct ToolCallParams {
153    pub name: String,
154    #[serde(default)]
155    pub arguments: Option<Value>,
156}
157
158/// Tool call result.
159#[derive(Debug, Serialize)]
160pub struct ToolCallResult {
161    pub content: Vec<ToolContent>,
162    #[serde(rename = "isError", skip_serializing_if = "Option::is_none")]
163    pub is_error: Option<bool>,
164}
165
166/// Tool content item.
167#[derive(Debug, Serialize)]
168#[serde(tag = "type")]
169pub enum ToolContent {
170    #[serde(rename = "text")]
171    Text { text: String },
172}
173
174impl ToolCallResult {
175    /// Create a success result with text content.
176    pub fn text(content: &str) -> Self {
177        Self {
178            content: vec![ToolContent::Text {
179                text: content.to_string(),
180            }],
181            is_error: None,
182        }
183    }
184
185    /// Create an error result.
186    pub fn error(message: &str) -> Self {
187        Self {
188            content: vec![ToolContent::Text {
189                text: message.to_string(),
190            }],
191            is_error: Some(true),
192        }
193    }
194}
195
196// Tool-specific parameter types
197
198/// A code location for MCP input.
199/// Represents a file with optional line range.
200#[derive(Debug, Clone, Deserialize)]
201pub struct LocationInput {
202    /// Relative file path from repository root.
203    pub file: String,
204    /// Starting line number (1-indexed).
205    #[serde(default)]
206    pub start_line: Option<u32>,
207    /// Ending line number (inclusive).
208    #[serde(default)]
209    pub end_line: Option<u32>,
210}
211
212/// A single log entry for batch logging.
213#[derive(Debug, Deserialize)]
214pub struct LogEntry {
215    pub role: String,
216    pub category: String,
217    pub content: String,
218    /// Code locations this entry relates to.
219    /// Each location can specify a file and optional line range.
220    #[serde(default)]
221    pub locations: Option<Vec<LocationInput>>,
222    // --- Legacy fields for backward compatibility ---
223    /// (Deprecated) Use `locations` instead. Optional file path.
224    #[serde(default)]
225    pub file_path: Option<String>,
226    /// (Deprecated) Use `locations` instead. Optional line number.
227    #[serde(default)]
228    pub line_number: Option<u32>,
229}
230
231/// Parameters for agit_log_step tool.
232///
233/// Supports both single-entry mode (backward compatible) and batch mode.
234#[derive(Debug, Deserialize)]
235pub struct LogStepParams {
236    /// Single entry role (for backward compatibility)
237    #[serde(default)]
238    pub role: Option<String>,
239    /// Single entry category (for backward compatibility)
240    #[serde(default)]
241    pub category: Option<String>,
242    /// Single entry content (for backward compatibility)
243    #[serde(default)]
244    pub content: Option<String>,
245    /// Code locations for single entry mode.
246    #[serde(default)]
247    pub locations: Option<Vec<LocationInput>>,
248    // --- Legacy fields for backward compatibility ---
249    /// (Deprecated) Use `locations` instead. Optional file path.
250    #[serde(default)]
251    pub file_path: Option<String>,
252    /// (Deprecated) Use `locations` instead. Optional line number.
253    #[serde(default)]
254    pub line_number: Option<u32>,
255    /// Batch of entries (preferred for multiple logs)
256    #[serde(default)]
257    pub batch: Option<Vec<LogEntry>>,
258}
259
260/// Parameters for agit_get_context tool.
261#[derive(Debug, Deserialize)]
262pub struct GetContextParams {
263    pub git_hash: String,
264}
265
266/// Parameters for agit_get_recent_summaries tool.
267#[derive(Debug, Deserialize)]
268pub struct GetRecentSummariesParams {
269    /// Number of recent summaries to return (default: 5).
270    pub count: Option<usize>,
271}