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 + std::fmt::Debug + '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, Debug)]
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 update_plan,
405 "updatePlan",
406 UpdatePlanParams,
407 true,
408 UpdatePlanResponse,
409 false
410 ),
411 (
412 write_text_file,
413 "writeTextFile",
414 WriteTextFileParams,
415 true,
416 WriteTextFileResponse,
417 false
418 ),
419 (
420 read_text_file,
421 "readTextFile",
422 ReadTextFileParams,
423 true,
424 ReadTextFileResponse,
425 true
426 ),
427);
428
429acp_peer!(
431 Agent,
432 AgentRequest,
433 AnyAgentRequest,
434 AnyAgentResult,
435 AGENT_METHODS,
436 (
437 initialize,
438 "initialize",
439 InitializeParams,
440 true,
441 InitializeResponse,
442 true
443 ),
444 (
445 authenticate,
446 "authenticate",
447 AuthenticateParams,
448 false,
449 AuthenticateResponse,
450 false
451 ),
452 (
453 send_user_message,
454 "sendUserMessage",
455 SendUserMessageParams,
456 true,
457 SendUserMessageResponse,
458 false
459 ),
460 (
461 cancel_send_message,
462 "cancelSendMessage",
463 CancelSendMessageParams,
464 false,
465 CancelSendMessageResponse,
466 false
467 )
468);
469
470#[derive(Debug, Serialize, Deserialize, JsonSchema)]
479#[serde(rename_all = "camelCase")]
480pub struct InitializeParams {
481 pub protocol_version: ProtocolVersion,
484}
485
486#[derive(Clone, Debug, Deref, Display, FromStr, Serialize, Deserialize, JsonSchema)]
487#[serde(transparent)]
488pub struct ProtocolVersion(Version);
489
490impl ProtocolVersion {
491 pub fn latest() -> Self {
492 Self(env!("CARGO_PKG_VERSION").parse().expect("Invalid version"))
493 }
494}
495
496#[derive(Debug, Serialize, Deserialize, JsonSchema)]
497#[serde(rename_all = "camelCase")]
498pub struct InitializeResponse {
499 pub protocol_version: ProtocolVersion,
503 pub is_authenticated: bool,
506}
507
508#[derive(Debug, Serialize, Deserialize, JsonSchema)]
517#[serde(rename_all = "camelCase")]
518pub struct AuthenticateParams;
519
520#[derive(Debug, Serialize, Deserialize, JsonSchema)]
521#[serde(rename_all = "camelCase")]
522pub struct AuthenticateResponse;
523
524#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
529#[serde(rename_all = "camelCase")]
530pub struct SendUserMessageParams {
531 pub chunks: Vec<UserMessageChunk>,
532}
533
534#[derive(Debug, Serialize, Deserialize, JsonSchema)]
535#[serde(rename_all = "camelCase")]
536pub struct SendUserMessageResponse;
537
538#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
540#[serde(untagged, rename_all = "camelCase")]
541pub enum UserMessageChunk {
542 Text { text: String },
544 Path { path: PathBuf },
546}
547
548#[derive(Debug, Serialize, Deserialize, JsonSchema)]
551#[serde(rename_all = "camelCase")]
552pub struct CancelSendMessageParams;
553
554#[derive(Debug, Serialize, Deserialize, JsonSchema)]
555#[serde(rename_all = "camelCase")]
556pub struct CancelSendMessageResponse;
557
558#[derive(Debug, Serialize, Deserialize, JsonSchema)]
562#[serde(rename_all = "camelCase")]
563pub struct StreamAssistantMessageChunkParams {
564 pub chunk: AssistantMessageChunk,
565}
566
567#[derive(Debug, Serialize, Deserialize, JsonSchema)]
568#[serde(rename_all = "camelCase")]
569pub struct StreamAssistantMessageChunkResponse;
570
571#[derive(Debug, Serialize, Deserialize, JsonSchema)]
572#[serde(untagged, rename_all = "camelCase")]
573pub enum AssistantMessageChunk {
574 Text { text: String },
575 Thought { thought: String },
576}
577
578#[derive(Debug, Serialize, Deserialize, JsonSchema)]
583#[serde(rename_all = "camelCase")]
584pub struct RequestToolCallConfirmationParams {
585 #[serde(flatten)]
586 pub tool_call: PushToolCallParams,
587 pub confirmation: ToolCallConfirmation,
588}
589
590#[derive(Debug, Serialize, Deserialize, JsonSchema)]
591#[serde(rename_all = "camelCase")]
592pub enum Icon {
595 FileSearch,
596 Folder,
597 Globe,
598 Hammer,
599 LightBulb,
600 Pencil,
601 Regex,
602 Terminal,
603}
604
605#[derive(Debug, Serialize, Deserialize, JsonSchema)]
606#[serde(tag = "type", rename_all = "camelCase")]
607pub enum ToolCallConfirmation {
608 #[serde(rename_all = "camelCase")]
609 Edit {
610 #[serde(skip_serializing_if = "Option::is_none")]
611 description: Option<String>,
612 },
613 #[serde(rename_all = "camelCase")]
614 Execute {
615 command: String,
616 root_command: String,
617 #[serde(skip_serializing_if = "Option::is_none")]
618 description: Option<String>,
619 },
620 #[serde(rename_all = "camelCase")]
621 Mcp {
622 server_name: String,
623 tool_name: String,
624 tool_display_name: String,
625 #[serde(skip_serializing_if = "Option::is_none")]
626 description: Option<String>,
627 },
628 #[serde(rename_all = "camelCase")]
629 Fetch {
630 urls: Vec<String>,
631 #[serde(skip_serializing_if = "Option::is_none")]
632 description: Option<String>,
633 },
634 #[serde(rename_all = "camelCase")]
635 Other { description: String },
636}
637
638#[derive(Debug, Serialize, Deserialize, JsonSchema)]
639#[serde(tag = "type", rename_all = "camelCase")]
640pub struct RequestToolCallConfirmationResponse {
641 pub id: ToolCallId,
642 pub outcome: ToolCallConfirmationOutcome,
643}
644
645#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
646#[serde(rename_all = "camelCase")]
647pub enum ToolCallConfirmationOutcome {
648 Allow,
650 AlwaysAllow,
652 AlwaysAllowMcpServer,
654 AlwaysAllowTool,
656 Reject,
658 Cancel,
660}
661
662#[derive(Debug, Serialize, Deserialize, JsonSchema)]
668#[serde(rename_all = "camelCase")]
669pub struct PushToolCallParams {
670 pub label: String,
671 pub icon: Icon,
672 #[serde(skip_serializing_if = "Option::is_none")]
673 pub content: Option<ToolCallContent>,
674 #[serde(default, skip_serializing_if = "Vec::is_empty")]
675 pub locations: Vec<ToolCallLocation>,
676}
677
678#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
679#[serde(tag = "type", rename_all = "camelCase")]
680pub struct ToolCallLocation {
681 pub path: PathBuf,
682 #[serde(skip_serializing_if = "Option::is_none")]
683 pub line: Option<u32>,
684}
685
686#[derive(Debug, Serialize, Deserialize, JsonSchema)]
687#[serde(tag = "type", rename_all = "camelCase")]
688pub struct PushToolCallResponse {
689 pub id: ToolCallId,
690}
691
692#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, Eq, PartialEq, Hash)]
693#[serde(rename_all = "camelCase")]
694pub struct ToolCallId(pub u64);
695
696#[derive(Debug, Serialize, Deserialize, JsonSchema)]
703#[serde(rename_all = "camelCase")]
704pub struct UpdateToolCallParams {
705 pub tool_call_id: ToolCallId,
706 pub status: ToolCallStatus,
707 pub content: Option<ToolCallContent>,
708}
709
710#[derive(Debug, Serialize, Deserialize, JsonSchema)]
711pub struct UpdateToolCallResponse;
712
713#[derive(Debug, Serialize, Deserialize, JsonSchema)]
718#[serde(rename_all = "camelCase")]
719pub struct UpdatePlanParams {
720 pub entries: Vec<PlanEntry>,
722}
723
724#[derive(Debug, Serialize, Deserialize, JsonSchema)]
725pub struct UpdatePlanResponse;
726
727#[derive(Debug, Serialize, Deserialize, JsonSchema)]
732#[serde(rename_all = "camelCase")]
733pub struct PlanEntry {
734 pub content: String,
736 pub priority: PlanEntryPriority,
738 pub status: PlanEntryStatus,
740}
741
742#[derive(Deserialize, Serialize, JsonSchema, Debug)]
747#[serde(rename_all = "snake_case")]
748pub enum PlanEntryPriority {
749 High,
750 Medium,
751 Low,
752}
753
754#[derive(Deserialize, Serialize, JsonSchema, Debug)]
758#[serde(rename_all = "snake_case")]
759pub enum PlanEntryStatus {
760 Pending,
761 InProgress,
762 Completed,
763}
764
765#[derive(Debug, Serialize, Deserialize, JsonSchema)]
766#[serde(rename_all = "camelCase")]
767pub enum ToolCallStatus {
768 Running,
770 Finished,
772 Error,
774}
775
776#[derive(Debug, Serialize, Deserialize, JsonSchema)]
777#[serde(tag = "type", rename_all = "camelCase")]
778pub enum ToolCallContent {
779 #[serde(rename_all = "camelCase")]
780 Markdown { markdown: String },
781 #[serde(rename_all = "camelCase")]
782 Diff {
783 #[serde(flatten)]
784 diff: Diff,
785 },
786}
787
788#[derive(Debug, Serialize, Deserialize, JsonSchema)]
789#[serde(rename_all = "camelCase")]
790pub struct Diff {
791 pub path: PathBuf,
792 pub old_text: Option<String>,
793 pub new_text: String,
794}
795
796#[derive(Debug, Serialize, Deserialize, JsonSchema)]
797#[serde(rename_all = "camelCase")]
798pub struct WriteTextFileParams {
799 pub path: PathBuf,
800 pub content: String,
801}
802
803#[derive(Debug, Serialize, Deserialize, JsonSchema)]
804pub struct WriteTextFileResponse;
805
806#[derive(Debug, Serialize, Deserialize, JsonSchema)]
807#[serde(rename_all = "camelCase")]
808pub struct ReadTextFileParams {
809 pub path: PathBuf,
810 #[serde(skip_serializing_if = "Option::is_none")]
811 pub line: Option<u32>,
812 #[serde(skip_serializing_if = "Option::is_none")]
813 pub limit: Option<u32>,
814}
815
816#[derive(Debug, Serialize, Deserialize, JsonSchema)]
817pub struct ReadTextFileResponse {
818 pub content: String,
819}