1use std::{fmt::Display, ops::Deref, path::PathBuf};
2
3use derive_more::{Deref, Display, FromStr};
4use schemars::JsonSchema;
5use semver::Version;
6use serde::{Deserialize, Serialize};
7use serde_json::value::RawValue;
8
9#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
10pub struct Error {
11 pub code: i32,
12 pub message: String,
13 #[serde(skip_serializing_if = "Option::is_none")]
14 pub data: Option<serde_json::Value>,
15}
16
17impl Error {
18 pub fn new(code: impl Into<(i32, String)>) -> Self {
19 let (code, message) = code.into();
20 Error {
21 code,
22 message,
23 data: None,
24 }
25 }
26
27 pub fn with_data(mut self, data: impl Into<serde_json::Value>) -> Self {
28 self.data = Some(data.into());
29 self
30 }
31
32 pub fn parse_error() -> Self {
34 Error::new(ErrorCode::PARSE_ERROR)
35 }
36
37 pub fn invalid_request() -> Self {
39 Error::new(ErrorCode::INVALID_REQUEST)
40 }
41
42 pub fn method_not_found() -> Self {
44 Error::new(ErrorCode::METHOD_NOT_FOUND)
45 }
46
47 pub fn invalid_params() -> Self {
49 Error::new(ErrorCode::INVALID_PARAMS)
50 }
51
52 pub fn internal_error() -> Self {
54 Error::new(ErrorCode::INTERNAL_ERROR)
55 }
56
57 pub fn into_internal_error(err: impl std::error::Error) -> Self {
58 Error::internal_error().with_data(err.to_string())
59 }
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
63pub struct ErrorCode {
64 code: i32,
65 message: &'static str,
66}
67
68impl ErrorCode {
69 pub const PARSE_ERROR: ErrorCode = ErrorCode {
70 code: -32700,
71 message: "Parse error",
72 };
73
74 pub const INVALID_REQUEST: ErrorCode = ErrorCode {
75 code: -32600,
76 message: "Invalid Request",
77 };
78
79 pub const METHOD_NOT_FOUND: ErrorCode = ErrorCode {
80 code: -32601,
81 message: "Method not found",
82 };
83
84 pub const INVALID_PARAMS: ErrorCode = ErrorCode {
85 code: -32602,
86 message: "Invalid params",
87 };
88
89 pub const INTERNAL_ERROR: ErrorCode = ErrorCode {
90 code: -32603,
91 message: "Internal error",
92 };
93}
94
95impl From<ErrorCode> for (i32, String) {
96 fn from(error_code: ErrorCode) -> Self {
97 (error_code.code, error_code.message.to_string())
98 }
99}
100
101impl From<ErrorCode> for Error {
102 fn from(error_code: ErrorCode) -> Self {
103 Error::new(error_code)
104 }
105}
106
107impl std::error::Error for Error {}
108
109impl Display for Error {
110 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111 if self.message.is_empty() {
112 write!(f, "{}", self.code)?;
113 } else {
114 write!(f, "{}", self.message)?;
115 }
116
117 if let Some(data) = &self.data {
118 write!(f, ": {data}")?;
119 }
120
121 Ok(())
122 }
123}
124
125impl From<anyhow::Error> for Error {
126 fn from(error: anyhow::Error) -> Self {
127 Error::into_internal_error(error.deref())
128 }
129}
130
131#[derive(Serialize, JsonSchema)]
132#[serde(rename_all = "camelCase")]
133pub struct Method {
134 pub name: &'static str,
135 pub request_type: &'static str,
136 pub param_payload: bool,
137 pub response_type: &'static str,
138 pub response_payload: bool,
139}
140
141pub trait AnyRequest: Serialize + Sized + 'static {
142 type Response: Serialize + 'static;
143 fn from_method_and_params(method: &str, params: &RawValue) -> Result<Self, Error>;
144 fn response_from_method_and_result(
145 method: &str,
146 params: &RawValue,
147 ) -> Result<Self::Response, Error>;
148}
149
150macro_rules! acp_peer {
151 (
152 $handler_trait_name:ident,
153 $request_trait_name:ident,
154 $request_enum_name:ident,
155 $response_enum_name:ident,
156 $method_map_name:ident,
157 $(($request_method:ident, $request_method_string:expr, $request_name:ident, $param_payload: tt, $response_name:ident, $response_payload: tt)),*
158 $(,)?
159 ) => {
160 macro_rules! handler_trait_call_req {
161 ($self: ident, $method: ident, false, $resp_name: ident, false, $params: ident) => {
162 {
163 $self.$method().await?;
164 Ok($response_enum_name::$resp_name($resp_name))
165 }
166 };
167 ($self: ident, $method: ident, false, $resp_name: ident, true, $params: ident) => {
168 {
169 let resp = $self.$method().await?;
170 Ok($response_enum_name::$resp_name(resp))
171 }
172 };
173 ($self: ident, $method: ident, true, $resp_name: ident, false, $params: ident) => {
174 {
175 $self.$method($params).await?;
176 Ok($response_enum_name::$resp_name($resp_name))
177 }
178 };
179 ($self: ident, $method: ident, true, $resp_name: ident, true, $params: ident) => {
180 {
181 let resp = $self.$method($params).await?;
182 Ok($response_enum_name::$resp_name(resp))
183 }
184 }
185 }
186
187 macro_rules! handler_trait_req_method {
188 ($method: ident, $req: ident, false, $resp: tt, false) => {
189 fn $method(&self) -> impl Future<Output = Result<(), Error>>;
190 };
191 ($method: ident, $req: ident, false, $resp: tt, true) => {
192 fn $method(&self) -> impl Future<Output = Result<$resp, Error>>;
193 };
194 ($method: ident, $req: ident, true, $resp: tt, false) => {
195 fn $method(&self, request: $req) -> impl Future<Output = Result<(), Error>>;
196 };
197 ($method: ident, $req: ident, true, $resp: tt, true) => {
198 fn $method(&self, request: $req) -> impl Future<Output = Result<$resp, Error>>;
199 }
200 }
201
202 pub trait $handler_trait_name {
203 fn call(&self, params: $request_enum_name) -> impl Future<Output = Result<$response_enum_name, Error>> {
204 async move {
205 match params {
206 $(#[allow(unused_variables)]
207 $request_enum_name::$request_name(params) => {
208 handler_trait_call_req!(self, $request_method, $param_payload, $response_name, $response_payload, params)
209 }),*
210 }
211 }
212 }
213
214 $(
215 handler_trait_req_method!($request_method, $request_name, $param_payload, $response_name, $response_payload);
216 )*
217 }
218
219 pub trait $request_trait_name {
220 type Response;
221 fn into_any(self) -> $request_enum_name;
222 fn response_from_any(any: $response_enum_name) -> Result<Self::Response, Error>;
223 }
224
225 #[derive(Serialize, JsonSchema)]
226 #[serde(untagged)]
227 pub enum $request_enum_name {
228 $(
229 $request_name($request_name),
230 )*
231 }
232
233 #[derive(Serialize, Deserialize, JsonSchema)]
234 #[serde(untagged)]
235 pub enum $response_enum_name {
236 $(
237 $response_name($response_name),
238 )*
239 }
240
241 macro_rules! request_from_method_and_params {
242 ($req_name: ident, false, $params: tt) => {
243 Ok($request_enum_name::$req_name($req_name))
244 };
245 ($req_name: ident, true, $params: tt) => {
246 match serde_json::from_str($params.get()) {
247 Ok(params) => Ok($request_enum_name::$req_name(params)),
248 Err(e) => Err(Error::parse_error().with_data(e.to_string())),
249 }
250 };
251 }
252
253 macro_rules! response_from_method_and_result {
254 ($resp_name: ident, false, $result: tt) => {
255 Ok($response_enum_name::$resp_name($resp_name))
256 };
257 ($resp_name: ident, true, $result: tt) => {
258 match serde_json::from_str($result.get()) {
259 Ok(result) => Ok($response_enum_name::$resp_name(result)),
260 Err(e) => Err(Error::parse_error().with_data(e.to_string())),
261 }
262 };
263 }
264
265 impl AnyRequest for $request_enum_name {
266 type Response = $response_enum_name;
267
268 fn from_method_and_params(method: &str, params: &RawValue) -> Result<Self, Error> {
269 match method {
270 $(
271 $request_method_string => {
272 request_from_method_and_params!($request_name, $param_payload, params)
273 }
274 )*
275 _ => Err(Error::method_not_found()),
276 }
277 }
278
279 fn response_from_method_and_result(method: &str, params: &RawValue) -> Result<Self::Response, Error> {
280 match method {
281 $(
282 $request_method_string => {
283 response_from_method_and_result!($response_name, $response_payload, params)
284 }
285 )*
286 _ => Err(Error::method_not_found()),
287 }
288 }
289 }
290
291 impl $request_enum_name {
292 pub fn method_name(&self) -> &'static str {
293 match self {
294 $(
295 $request_enum_name::$request_name(_) => $request_method_string,
296 )*
297 }
298 }
299 }
300
301
302
303 pub static $method_map_name: &[Method] = &[
304 $(
305 Method {
306 name: $request_method_string,
307 request_type: stringify!($request_name),
308 param_payload: $param_payload,
309 response_type: stringify!($response_name),
310 response_payload: $response_payload,
311 },
312 )*
313 ];
314
315 macro_rules! req_into_any {
316 ($self: ident, $req_name: ident, false) => {
317 $request_enum_name::$req_name($req_name)
318 };
319 ($self: ident, $req_name: ident, true) => {
320 $request_enum_name::$req_name($self)
321 };
322 }
323
324 macro_rules! resp_type {
325 ($resp_name: ident, false) => {
326 ()
327 };
328 ($resp_name: ident, true) => {
329 $resp_name
330 };
331 }
332
333 macro_rules! resp_from_any {
334 ($any: ident, $resp_name: ident, false) => {
335 match $any {
336 $response_enum_name::$resp_name(_) => Ok(()),
337 _ => Err(Error::internal_error().with_data("Unexpected Response"))
338 }
339 };
340 ($any: ident, $resp_name: ident, true) => {
341 match $any {
342 $response_enum_name::$resp_name(this) => Ok(this),
343 _ => Err(Error::internal_error().with_data("Unexpected Response"))
344 }
345 };
346 }
347
348 $(
349 impl $request_trait_name for $request_name {
350 type Response = resp_type!($response_name, $response_payload);
351
352 fn into_any(self) -> $request_enum_name {
353 req_into_any!(self, $request_name, $param_payload)
354 }
355
356 fn response_from_any(any: $response_enum_name) -> Result<Self::Response, Error> {
357 resp_from_any!(any, $response_name, $response_payload)
358 }
359 }
360 )*
361 };
362}
363
364acp_peer!(
366 Client,
367 ClientRequest,
368 AnyClientRequest,
369 AnyClientResult,
370 CLIENT_METHODS,
371 (
372 stream_assistant_message_chunk,
373 "streamAssistantMessageChunk",
374 StreamAssistantMessageChunkParams,
375 true,
376 StreamAssistantMessageChunkResponse,
377 false
378 ),
379 (
380 request_tool_call_confirmation,
381 "requestToolCallConfirmation",
382 RequestToolCallConfirmationParams,
383 true,
384 RequestToolCallConfirmationResponse,
385 true
386 ),
387 (
388 push_tool_call,
389 "pushToolCall",
390 PushToolCallParams,
391 true,
392 PushToolCallResponse,
393 true
394 ),
395 (
396 update_tool_call,
397 "updateToolCall",
398 UpdateToolCallParams,
399 true,
400 UpdateToolCallResponse,
401 false
402 ),
403 (
404 write_text_file,
405 "writeTextFile",
406 WriteTextFileParams,
407 true,
408 WriteTextFileResponse,
409 false
410 ),
411 (
412 read_text_file,
413 "readTextFile",
414 ReadTextFileParams,
415 true,
416 ReadTextFileResponse,
417 true
418 )
419);
420
421acp_peer!(
423 Agent,
424 AgentRequest,
425 AnyAgentRequest,
426 AnyAgentResult,
427 AGENT_METHODS,
428 (
429 initialize,
430 "initialize",
431 InitializeParams,
432 true,
433 InitializeResponse,
434 true
435 ),
436 (
437 authenticate,
438 "authenticate",
439 AuthenticateParams,
440 false,
441 AuthenticateResponse,
442 false
443 ),
444 (
445 send_user_message,
446 "sendUserMessage",
447 SendUserMessageParams,
448 true,
449 SendUserMessageResponse,
450 false
451 ),
452 (
453 cancel_send_message,
454 "cancelSendMessage",
455 CancelSendMessageParams,
456 false,
457 CancelSendMessageResponse,
458 false
459 )
460);
461
462#[derive(Debug, Serialize, Deserialize, JsonSchema)]
471#[serde(rename_all = "camelCase")]
472pub struct InitializeParams {
473 pub protocol_version: ProtocolVersion,
476}
477
478#[derive(Clone, Debug, Deref, Display, FromStr, Serialize, Deserialize, JsonSchema)]
479#[serde(transparent)]
480pub struct ProtocolVersion(Version);
481
482impl ProtocolVersion {
483 pub fn latest() -> Self {
484 Self(env!("CARGO_PKG_VERSION").parse().expect("Invalid version"))
485 }
486}
487
488#[derive(Debug, Serialize, Deserialize, JsonSchema)]
489#[serde(rename_all = "camelCase")]
490pub struct InitializeResponse {
491 pub protocol_version: ProtocolVersion,
495 pub is_authenticated: bool,
498}
499
500#[derive(Debug, Serialize, Deserialize, JsonSchema)]
509#[serde(rename_all = "camelCase")]
510pub struct AuthenticateParams;
511
512#[derive(Debug, Serialize, Deserialize, JsonSchema)]
513#[serde(rename_all = "camelCase")]
514pub struct AuthenticateResponse;
515
516#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
521#[serde(rename_all = "camelCase")]
522pub struct SendUserMessageParams {
523 pub chunks: Vec<UserMessageChunk>,
524}
525
526#[derive(Debug, Serialize, Deserialize, JsonSchema)]
527#[serde(rename_all = "camelCase")]
528pub struct SendUserMessageResponse;
529
530#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
532#[serde(untagged, rename_all = "camelCase")]
533pub enum UserMessageChunk {
534 Text { text: String },
536 Path { path: PathBuf },
538}
539
540#[derive(Debug, Serialize, Deserialize, JsonSchema)]
543#[serde(rename_all = "camelCase")]
544pub struct CancelSendMessageParams;
545
546#[derive(Debug, Serialize, Deserialize, JsonSchema)]
547#[serde(rename_all = "camelCase")]
548pub struct CancelSendMessageResponse;
549
550#[derive(Debug, Serialize, Deserialize, JsonSchema)]
554#[serde(rename_all = "camelCase")]
555pub struct StreamAssistantMessageChunkParams {
556 pub chunk: AssistantMessageChunk,
557}
558
559#[derive(Debug, Serialize, Deserialize, JsonSchema)]
560#[serde(rename_all = "camelCase")]
561pub struct StreamAssistantMessageChunkResponse;
562
563#[derive(Debug, Serialize, Deserialize, JsonSchema)]
564#[serde(untagged, rename_all = "camelCase")]
565pub enum AssistantMessageChunk {
566 Text { text: String },
567 Thought { thought: String },
568}
569
570#[derive(Debug, Serialize, Deserialize, JsonSchema)]
575#[serde(rename_all = "camelCase")]
576pub struct RequestToolCallConfirmationParams {
577 #[serde(flatten)]
578 pub tool_call: PushToolCallParams,
579 pub confirmation: ToolCallConfirmation,
580}
581
582#[derive(Debug, Serialize, Deserialize, JsonSchema)]
583#[serde(rename_all = "camelCase")]
584pub enum Icon {
587 FileSearch,
588 Folder,
589 Globe,
590 Hammer,
591 LightBulb,
592 Pencil,
593 Regex,
594 Terminal,
595}
596
597#[derive(Debug, Serialize, Deserialize, JsonSchema)]
598#[serde(tag = "type", rename_all = "camelCase")]
599pub enum ToolCallConfirmation {
600 #[serde(rename_all = "camelCase")]
601 Edit {
602 #[serde(skip_serializing_if = "Option::is_none")]
603 description: Option<String>,
604 },
605 #[serde(rename_all = "camelCase")]
606 Execute {
607 command: String,
608 root_command: String,
609 #[serde(skip_serializing_if = "Option::is_none")]
610 description: Option<String>,
611 },
612 #[serde(rename_all = "camelCase")]
613 Mcp {
614 server_name: String,
615 tool_name: String,
616 tool_display_name: String,
617 #[serde(skip_serializing_if = "Option::is_none")]
618 description: Option<String>,
619 },
620 #[serde(rename_all = "camelCase")]
621 Fetch {
622 urls: Vec<String>,
623 #[serde(skip_serializing_if = "Option::is_none")]
624 description: Option<String>,
625 },
626 #[serde(rename_all = "camelCase")]
627 Other { description: String },
628}
629
630#[derive(Debug, Serialize, Deserialize, JsonSchema)]
631#[serde(tag = "type", rename_all = "camelCase")]
632pub struct RequestToolCallConfirmationResponse {
633 pub id: ToolCallId,
634 pub outcome: ToolCallConfirmationOutcome,
635}
636
637#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
638#[serde(rename_all = "camelCase")]
639pub enum ToolCallConfirmationOutcome {
640 Allow,
642 AlwaysAllow,
644 AlwaysAllowMcpServer,
646 AlwaysAllowTool,
648 Reject,
650 Cancel,
652}
653
654#[derive(Debug, Serialize, Deserialize, JsonSchema)]
660#[serde(rename_all = "camelCase")]
661pub struct PushToolCallParams {
662 pub label: String,
663 pub icon: Icon,
664 #[serde(skip_serializing_if = "Option::is_none")]
665 pub content: Option<ToolCallContent>,
666 #[serde(default, skip_serializing_if = "Vec::is_empty")]
667 pub locations: Vec<ToolCallLocation>,
668}
669
670#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
671#[serde(tag = "type", rename_all = "camelCase")]
672pub struct ToolCallLocation {
673 pub path: PathBuf,
674 #[serde(skip_serializing_if = "Option::is_none")]
675 pub line: Option<u32>,
676}
677
678#[derive(Debug, Serialize, Deserialize, JsonSchema)]
679#[serde(tag = "type", rename_all = "camelCase")]
680pub struct PushToolCallResponse {
681 pub id: ToolCallId,
682}
683
684#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, Eq, PartialEq, Hash)]
685#[serde(rename_all = "camelCase")]
686pub struct ToolCallId(pub u64);
687
688#[derive(Debug, Serialize, Deserialize, JsonSchema)]
695#[serde(rename_all = "camelCase")]
696pub struct UpdateToolCallParams {
697 pub tool_call_id: ToolCallId,
698 pub status: ToolCallStatus,
699 pub content: Option<ToolCallContent>,
700}
701
702#[derive(Debug, Serialize, Deserialize, JsonSchema)]
703pub struct UpdateToolCallResponse;
704
705#[derive(Debug, Serialize, Deserialize, JsonSchema)]
706#[serde(rename_all = "camelCase")]
707pub enum ToolCallStatus {
708 Running,
710 Finished,
712 Error,
714}
715
716#[derive(Debug, Serialize, Deserialize, JsonSchema)]
717#[serde(tag = "type", rename_all = "camelCase")]
718pub enum ToolCallContent {
719 #[serde(rename_all = "camelCase")]
720 Markdown { markdown: String },
721 #[serde(rename_all = "camelCase")]
722 Diff {
723 #[serde(flatten)]
724 diff: Diff,
725 },
726}
727
728#[derive(Debug, Serialize, Deserialize, JsonSchema)]
729#[serde(rename_all = "camelCase")]
730pub struct Diff {
731 pub path: PathBuf,
732 pub old_text: Option<String>,
733 pub new_text: String,
734}
735
736#[derive(Debug, Serialize, Deserialize, JsonSchema)]
737#[serde(rename_all = "camelCase")]
738pub struct WriteTextFileParams {
739 pub path: PathBuf,
740 pub content: String,
741}
742
743#[derive(Debug, Serialize, Deserialize, JsonSchema)]
744pub struct WriteTextFileResponse;
745
746#[derive(Debug, Serialize, Deserialize, JsonSchema)]
747#[serde(rename_all = "camelCase")]
748pub struct ReadTextFileParams {
749 pub path: PathBuf,
750 #[serde(skip_serializing_if = "Option::is_none")]
751 pub line: Option<u32>,
752 #[serde(skip_serializing_if = "Option::is_none")]
753 pub limit: Option<u32>,
754}
755
756#[derive(Debug, Serialize, Deserialize, JsonSchema)]
757pub struct ReadTextFileResponse {
758 pub content: String,
759}