1pub mod mcp_types;
2pub use mcp_types::*;
3
4use std::{fmt, path::PathBuf, sync::Arc};
5
6use schemars::{JsonSchema, generate::SchemaSettings};
7use serde::{Deserialize, Serialize};
8
9#[derive(Serialize)]
10pub struct AgentMethods {
11 pub new_session: &'static str,
12 pub load_session: &'static str,
13 pub prompt: &'static str,
14 pub session_update: &'static str,
15}
16
17pub const AGENT_METHODS: AgentMethods = AgentMethods {
18 new_session: "acp/new_session",
19 load_session: "acp/load_session",
20 prompt: "acp/prompt",
21 session_update: "acp/session_update",
22};
23
24#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
27#[serde(rename_all = "camelCase")]
28pub struct NewSessionArguments {
29 pub mcp_servers: Vec<McpServer>,
30 pub client_tools: ClientTools,
31 pub cwd: PathBuf,
32}
33
34impl NewSessionArguments {
35 pub fn schema() -> serde_json::Value {
36 schema_for::<Self>()
37 }
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
41#[serde(rename_all = "camelCase")]
42pub struct NewSessionOutput {
43 pub session_id: SessionId,
44}
45
46impl NewSessionOutput {
47 pub fn schema() -> serde_json::Value {
48 schema_for::<Self>()
49 }
50}
51#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
54#[serde(rename_all = "camelCase")]
55pub struct LoadSessionArguments {
56 pub mcp_servers: Vec<McpServer>,
57 pub client_tools: ClientTools,
58 pub cwd: PathBuf,
59 pub session_id: SessionId,
60}
61
62impl LoadSessionArguments {
63 pub fn schema() -> serde_json::Value {
64 schema_for::<Self>()
65 }
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash)]
69#[serde(transparent)]
70pub struct SessionId(pub Arc<str>);
71
72impl fmt::Display for SessionId {
73 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74 write!(f, "{}", self.0)
75 }
76}
77
78#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
79#[serde(rename_all = "camelCase")]
80pub struct McpServer {
81 pub name: String,
82 pub command: PathBuf,
83 pub args: Vec<String>,
84 pub env: Vec<EnvVariable>,
85}
86
87#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
88#[serde(rename_all = "camelCase")]
89pub struct EnvVariable {
90 pub name: String,
91 pub value: String,
92}
93
94#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash)]
95#[serde(rename_all = "camelCase")]
96pub struct McpToolId {
97 pub mcp_server: String,
98 pub tool_name: String,
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
104#[serde(rename_all = "camelCase")]
105pub struct PromptArguments {
106 pub session_id: SessionId,
107 pub prompt: Vec<ContentBlock>,
108}
109
110impl PromptArguments {
111 pub fn schema() -> serde_json::Value {
112 schema_for::<Self>()
113 }
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
119#[serde(rename_all = "camelCase")]
120pub struct SessionNotification {
121 pub session_id: SessionId,
122 #[serde(flatten)]
123 pub update: SessionUpdate,
124}
125
126#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
127#[serde(tag = "sessionUpdate", rename_all = "camelCase")]
128pub enum SessionUpdate {
129 UserMessageChunk { content: ContentBlock },
130 AgentMessageChunk { content: ContentBlock },
131 AgentThoughtChunk { content: ContentBlock },
132 ToolCall(ToolCall),
133 ToolCallUpdate(ToolCallUpdate),
134 Plan(Plan),
135}
136
137#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
138#[serde(rename_all = "camelCase")]
139pub struct ToolCall {
140 #[serde(rename = "toolCallId")]
141 pub id: ToolCallId,
142 pub label: String,
143 pub kind: ToolKind,
144 pub status: ToolCallStatus,
145 #[serde(default, skip_serializing_if = "Vec::is_empty")]
146 pub content: Vec<ToolCallContent>,
147 #[serde(default, skip_serializing_if = "Vec::is_empty")]
148 pub locations: Vec<ToolCallLocation>,
149 #[serde(default, skip_serializing_if = "Option::is_none")]
150 pub raw_input: Option<serde_json::Value>,
151}
152
153#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
154#[serde(rename_all = "camelCase")]
155pub struct ToolCallUpdate {
156 #[serde(rename = "toolCallId")]
157 pub id: ToolCallId,
158 #[serde(flatten)]
159 pub fields: ToolCallUpdateFields,
160}
161
162#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema)]
163#[serde(rename_all = "camelCase")]
164pub struct ToolCallUpdateFields {
165 #[serde(default, skip_serializing_if = "Option::is_none")]
166 pub kind: Option<ToolKind>,
167 #[serde(default, skip_serializing_if = "Option::is_none")]
168 pub status: Option<ToolCallStatus>,
169 #[serde(default, skip_serializing_if = "Option::is_none")]
170 pub label: Option<String>,
171 #[serde(default, skip_serializing_if = "Option::is_none")]
172 pub content: Option<Vec<ToolCallContent>>,
173 #[serde(default, skip_serializing_if = "Option::is_none")]
174 pub locations: Option<Vec<ToolCallLocation>>,
175 #[serde(default, skip_serializing_if = "Option::is_none")]
176 pub raw_input: Option<serde_json::Value>,
177}
178
179#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash)]
180#[serde(transparent)]
181pub struct ToolCallId(pub Arc<str>);
182
183#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
184#[serde(rename_all = "camelCase")]
185pub enum ToolKind {
186 Read,
187 Edit,
188 Delete,
189 Move,
190 Search,
191 Execute,
192 Think,
193 Fetch,
194 Other,
195}
196
197#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
198#[serde(rename_all = "camelCase")]
199pub enum ToolCallStatus {
200 Pending,
202 InProgress,
204 Completed,
206 Failed,
208}
209
210#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
211#[serde(tag = "type", rename_all = "camelCase")]
212pub enum ToolCallContent {
213 Content {
214 content: ContentBlock,
215 },
216 Diff {
217 #[serde(flatten)]
218 diff: Diff,
219 },
220}
221
222impl<T: Into<ContentBlock>> From<T> for ToolCallContent {
223 fn from(content: T) -> Self {
224 ToolCallContent::Content {
225 content: content.into(),
226 }
227 }
228}
229
230impl From<Diff> for ToolCallContent {
231 fn from(diff: Diff) -> Self {
232 ToolCallContent::Diff { diff }
233 }
234}
235
236#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
237#[serde(rename_all = "camelCase")]
238pub struct Diff {
239 pub path: PathBuf,
240 pub old_text: Option<String>,
241 pub new_text: String,
242}
243
244#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
245#[serde(tag = "type", rename_all = "camelCase")]
246pub struct ToolCallLocation {
247 pub path: PathBuf,
248 #[serde(default, skip_serializing_if = "Option::is_none")]
249 pub line: Option<u32>,
250}
251
252#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
253#[serde(rename_all = "camelCase")]
254pub struct Plan {
255 pub entries: Vec<PlanEntry>,
256}
257
258#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
263#[serde(rename_all = "camelCase")]
264pub struct PlanEntry {
265 pub content: String,
267 pub priority: PlanEntryPriority,
269 pub status: PlanEntryStatus,
271}
272
273#[derive(Deserialize, Serialize, JsonSchema, Debug, Clone)]
278#[serde(rename_all = "snake_case")]
279pub enum PlanEntryPriority {
280 High,
281 Medium,
282 Low,
283}
284
285#[derive(Deserialize, Serialize, JsonSchema, Debug, Clone)]
289#[serde(rename_all = "snake_case")]
290pub enum PlanEntryStatus {
291 Pending,
292 InProgress,
293 Completed,
294}
295
296#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
299#[serde(rename_all = "camelCase")]
300pub struct ClientTools {
301 pub request_permission: Option<McpToolId>,
302 pub write_text_file: Option<McpToolId>,
303 pub read_text_file: Option<McpToolId>,
304}
305
306#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
309#[serde(rename_all = "camelCase")]
310pub struct RequestPermissionArguments {
311 pub session_id: SessionId,
312 pub tool_call: ToolCall,
313 pub options: Vec<PermissionOption>,
314}
315
316impl RequestPermissionArguments {
317 pub fn schema() -> serde_json::Value {
318 schema_for::<Self>()
319 }
320}
321
322#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
323pub struct PermissionOption {
324 #[serde(rename = "optionId")]
325 pub id: PermissionOptionId,
326 pub label: String,
327 pub kind: PermissionOptionKind,
328}
329
330#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash)]
331#[serde(transparent)]
332pub struct PermissionOptionId(pub Arc<str>);
333
334impl fmt::Display for PermissionOptionId {
335 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
336 self.0.fmt(f)
337 }
338}
339
340#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema)]
341#[serde(rename_all = "camelCase")]
342pub enum PermissionOptionKind {
343 AllowOnce,
344 AllowAlways,
345 RejectOnce,
346 RejectAlways,
347}
348
349#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
350#[serde(rename_all = "camelCase")]
351pub struct RequestPermissionOutput {
352 pub outcome: RequestPermissionOutcome,
354}
355
356#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
357#[serde(tag = "outcome", rename_all = "camelCase")]
358pub enum RequestPermissionOutcome {
359 Canceled,
360 #[serde(rename_all = "camelCase")]
361 Selected {
362 option_id: PermissionOptionId,
363 },
364}
365
366#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
369#[serde(rename_all = "camelCase")]
370pub struct WriteTextFileArguments {
371 pub session_id: SessionId,
372 pub path: PathBuf,
373 pub content: String,
374}
375
376impl WriteTextFileArguments {
377 pub fn schema() -> serde_json::Value {
378 schema_for::<Self>()
379 }
380}
381
382#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
385#[serde(rename_all = "camelCase")]
386pub struct ReadTextFileArguments {
387 pub session_id: SessionId,
388 pub path: PathBuf,
389 #[serde(default, skip_serializing_if = "Option::is_none")]
390 pub line: Option<u32>,
391 #[serde(default, skip_serializing_if = "Option::is_none")]
392 pub limit: Option<u32>,
393}
394
395impl ReadTextFileArguments {
396 pub fn schema() -> serde_json::Value {
397 schema_for::<Self>()
398 }
399}
400
401#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
402#[serde(rename_all = "camelCase")]
403pub struct ReadTextFileOutput {
404 pub content: String,
405}
406
407impl ReadTextFileOutput {
408 pub fn schema() -> serde_json::Value {
409 schema_for::<Self>()
410 }
411}
412
413fn schema_for<T: JsonSchema>() -> serde_json::Value {
414 let mut settings = SchemaSettings::draft2020_12();
415 settings.inline_subschemas = true;
416 settings.into_generator().into_root_schema_for::<T>().into()
417}