agent_client_protocol/
agent.rs

1//! Methods and notifications the agent handles/receives.
2//!
3//! This module defines the Agent trait and all associated types for implementing
4//! an AI coding agent that follows the Agent Client Protocol (ACP).
5
6use std::rc::Rc;
7use std::{path::PathBuf, sync::Arc};
8
9use anyhow::Result;
10use schemars::JsonSchema;
11use serde::{Deserialize, Serialize};
12use serde_json::value::RawValue;
13
14use crate::ext::ExtRequest;
15use crate::{
16    ClientCapabilities, ContentBlock, Error, ExtNotification, ExtResponse, ProtocolVersion,
17    SessionId,
18};
19
20/// Defines the interface that all ACP-compliant agents must implement.
21///
22/// Agents are programs that use generative AI to autonomously modify code. They handle
23/// requests from clients and execute tasks using language models and tools.
24#[async_trait::async_trait(?Send)]
25pub trait Agent {
26    /// Establishes the connection with a client and negotiates protocol capabilities.
27    ///
28    /// This method is called once at the beginning of the connection to:
29    /// - Negotiate the protocol version to use
30    /// - Exchange capability information between client and agent
31    /// - Determine available authentication methods
32    ///
33    /// The agent should respond with its supported protocol version and capabilities.
34    ///
35    /// See protocol docs: [Initialization](https://agentclientprotocol.com/protocol/initialization)
36    async fn initialize(&self, args: InitializeRequest) -> Result<InitializeResponse, Error>;
37
38    /// Authenticates the client using the specified authentication method.
39    ///
40    /// Called when the agent requires authentication before allowing session creation.
41    /// The client provides the authentication method ID that was advertised during initialization.
42    ///
43    /// After successful authentication, the client can proceed to create sessions with
44    /// `new_session` without receiving an `auth_required` error.
45    ///
46    /// See protocol docs: [Initialization](https://agentclientprotocol.com/protocol/initialization)
47    async fn authenticate(&self, args: AuthenticateRequest) -> Result<AuthenticateResponse, Error>;
48
49    /// Creates a new conversation session with the agent.
50    ///
51    /// Sessions represent independent conversation contexts with their own history and state.
52    ///
53    /// The agent should:
54    /// - Create a new session context
55    /// - Connect to any specified MCP servers
56    /// - Return a unique session ID for future requests
57    ///
58    /// May return an `auth_required` error if the agent requires authentication.
59    ///
60    /// See protocol docs: [Session Setup](https://agentclientprotocol.com/protocol/session-setup)
61    async fn new_session(&self, args: NewSessionRequest) -> Result<NewSessionResponse, Error>;
62
63    /// Loads an existing session to resume a previous conversation.
64    ///
65    /// This method is only available if the agent advertises the `loadSession` capability.
66    ///
67    /// The agent should:
68    /// - Restore the session context and conversation history
69    /// - Connect to the specified MCP servers
70    /// - Stream the entire conversation history back to the client via notifications
71    ///
72    /// See protocol docs: [Loading Sessions](https://agentclientprotocol.com/protocol/session-setup#loading-sessions)
73    async fn load_session(&self, args: LoadSessionRequest) -> Result<LoadSessionResponse, Error>;
74
75    /// Sets the current mode for a session.
76    ///
77    /// Allows switching between different agent modes (e.g., "ask", "architect", "code")
78    /// that affect system prompts, tool availability, and permission behaviors.
79    ///
80    /// The mode must be one of the modes advertised in `availableModes` during session
81    /// creation or loading. Agents may also change modes autonomously and notify the
82    /// client via `current_mode_update` notifications.
83    ///
84    /// This method can be called at any time during a session, whether the Agent is
85    /// idle or actively generating a response.
86    ///
87    /// See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)
88    async fn set_session_mode(
89        &self,
90        args: SetSessionModeRequest,
91    ) -> Result<SetSessionModeResponse, Error>;
92
93    /// Processes a user prompt within a session.
94    ///
95    /// This method handles the whole lifecycle of a prompt:
96    /// - Receives user messages with optional context (files, images, etc.)
97    /// - Processes the prompt using language models
98    /// - Reports language model content and tool calls to the Clients
99    /// - Requests permission to run tools
100    /// - Executes any requested tool calls
101    /// - Returns when the turn is complete with a stop reason
102    ///
103    /// See protocol docs: [Prompt Turn](https://agentclientprotocol.com/protocol/prompt-turn)
104    async fn prompt(&self, args: PromptRequest) -> Result<PromptResponse, Error>;
105
106    /// Cancels ongoing operations for a session.
107    ///
108    /// This is a notification sent by the client to cancel an ongoing prompt turn.
109    ///
110    /// Upon receiving this notification, the Agent SHOULD:
111    /// - Stop all language model requests as soon as possible
112    /// - Abort all tool call invocations in progress
113    /// - Send any pending `session/update` notifications
114    /// - Respond to the original `session/prompt` request with `StopReason::Cancelled`
115    ///
116    /// See protocol docs: [Cancellation](https://agentclientprotocol.com/protocol/prompt-turn#cancellation)
117    async fn cancel(&self, args: CancelNotification) -> Result<(), Error>;
118
119    /// **UNSTABLE**
120    ///
121    /// This capability is not part of the spec yet, and may be removed or changed at any point.
122    ///
123    /// Select a model for a given session.
124    #[cfg(feature = "unstable")]
125    async fn set_session_model(
126        &self,
127        args: SetSessionModelRequest,
128    ) -> Result<SetSessionModelResponse, Error>;
129
130    /// Handles extension method requests from the client.
131    ///
132    /// Extension methods provide a way to add custom functionality while maintaining
133    /// protocol compatibility.
134    ///
135    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
136    async fn ext_method(&self, args: ExtRequest) -> Result<ExtResponse, Error>;
137
138    /// Handles extension notifications from the client.
139    ///
140    /// Extension notifications provide a way to send one-way messages for custom functionality
141    /// while maintaining protocol compatibility.
142    ///
143    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
144    async fn ext_notification(&self, args: ExtNotification) -> Result<(), Error>;
145}
146
147#[async_trait::async_trait(?Send)]
148impl<T: Agent> Agent for Rc<T> {
149    async fn initialize(&self, args: InitializeRequest) -> Result<InitializeResponse, Error> {
150        self.as_ref().initialize(args).await
151    }
152    async fn authenticate(&self, args: AuthenticateRequest) -> Result<AuthenticateResponse, Error> {
153        self.as_ref().authenticate(args).await
154    }
155    async fn new_session(&self, args: NewSessionRequest) -> Result<NewSessionResponse, Error> {
156        self.as_ref().new_session(args).await
157    }
158    async fn load_session(&self, args: LoadSessionRequest) -> Result<LoadSessionResponse, Error> {
159        self.as_ref().load_session(args).await
160    }
161    async fn set_session_mode(
162        &self,
163        args: SetSessionModeRequest,
164    ) -> Result<SetSessionModeResponse, Error> {
165        self.as_ref().set_session_mode(args).await
166    }
167    async fn prompt(&self, args: PromptRequest) -> Result<PromptResponse, Error> {
168        self.as_ref().prompt(args).await
169    }
170    async fn cancel(&self, args: CancelNotification) -> Result<(), Error> {
171        self.as_ref().cancel(args).await
172    }
173    #[cfg(feature = "unstable")]
174    async fn set_session_model(
175        &self,
176        args: SetSessionModelRequest,
177    ) -> Result<SetSessionModelResponse, Error> {
178        self.as_ref().set_session_model(args).await
179    }
180    async fn ext_method(&self, args: ExtRequest) -> Result<ExtResponse, Error> {
181        self.as_ref().ext_method(args).await
182    }
183    async fn ext_notification(&self, args: ExtNotification) -> Result<(), Error> {
184        self.as_ref().ext_notification(args).await
185    }
186}
187
188#[async_trait::async_trait(?Send)]
189impl<T: Agent> Agent for Arc<T> {
190    async fn initialize(&self, args: InitializeRequest) -> Result<InitializeResponse, Error> {
191        self.as_ref().initialize(args).await
192    }
193    async fn authenticate(&self, args: AuthenticateRequest) -> Result<AuthenticateResponse, Error> {
194        self.as_ref().authenticate(args).await
195    }
196    async fn new_session(&self, args: NewSessionRequest) -> Result<NewSessionResponse, Error> {
197        self.as_ref().new_session(args).await
198    }
199    async fn load_session(&self, args: LoadSessionRequest) -> Result<LoadSessionResponse, Error> {
200        self.as_ref().load_session(args).await
201    }
202    async fn set_session_mode(
203        &self,
204        args: SetSessionModeRequest,
205    ) -> Result<SetSessionModeResponse, Error> {
206        self.as_ref().set_session_mode(args).await
207    }
208    async fn prompt(&self, args: PromptRequest) -> Result<PromptResponse, Error> {
209        self.as_ref().prompt(args).await
210    }
211    async fn cancel(&self, args: CancelNotification) -> Result<(), Error> {
212        self.as_ref().cancel(args).await
213    }
214    #[cfg(feature = "unstable")]
215    async fn set_session_model(
216        &self,
217        args: SetSessionModelRequest,
218    ) -> Result<SetSessionModelResponse, Error> {
219        self.as_ref().set_session_model(args).await
220    }
221    async fn ext_method(&self, args: ExtRequest) -> Result<ExtResponse, Error> {
222        self.as_ref().ext_method(args).await
223    }
224    async fn ext_notification(&self, args: ExtNotification) -> Result<(), Error> {
225        self.as_ref().ext_notification(args).await
226    }
227}
228
229// Initialize
230
231/// Request parameters for the initialize method.
232///
233/// Sent by the client to establish connection and negotiate capabilities.
234///
235/// See protocol docs: [Initialization](https://agentclientprotocol.com/protocol/initialization)
236#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
237#[schemars(extend("x-side" = "agent", "x-method" = INITIALIZE_METHOD_NAME))]
238#[serde(rename_all = "camelCase")]
239pub struct InitializeRequest {
240    /// The latest protocol version supported by the client.
241    pub protocol_version: ProtocolVersion,
242    /// Capabilities supported by the client.
243    #[serde(default)]
244    pub client_capabilities: ClientCapabilities,
245    /// Extension point for implementations
246    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
247    pub meta: Option<serde_json::Value>,
248}
249
250/// Response from the initialize method.
251///
252/// Contains the negotiated protocol version and agent capabilities.
253///
254/// See protocol docs: [Initialization](https://agentclientprotocol.com/protocol/initialization)
255#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
256#[schemars(extend("x-side" = "agent", "x-method" = INITIALIZE_METHOD_NAME))]
257#[serde(rename_all = "camelCase")]
258pub struct InitializeResponse {
259    /// The protocol version the client specified if supported by the agent,
260    /// or the latest protocol version supported by the agent.
261    ///
262    /// The client should disconnect, if it doesn't support this version.
263    pub protocol_version: ProtocolVersion,
264    /// Capabilities supported by the agent.
265    #[serde(default)]
266    pub agent_capabilities: AgentCapabilities,
267    /// Authentication methods supported by the agent.
268    #[serde(default)]
269    pub auth_methods: Vec<AuthMethod>,
270    /// Extension point for implementations
271    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
272    pub meta: Option<serde_json::Value>,
273}
274
275// Authentication
276
277/// Request parameters for the authenticate method.
278///
279/// Specifies which authentication method to use.
280#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
281#[schemars(extend("x-side" = "agent", "x-method" = AUTHENTICATE_METHOD_NAME))]
282#[serde(rename_all = "camelCase")]
283pub struct AuthenticateRequest {
284    /// The ID of the authentication method to use.
285    /// Must be one of the methods advertised in the initialize response.
286    pub method_id: AuthMethodId,
287    /// Extension point for implementations
288    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
289    pub meta: Option<serde_json::Value>,
290}
291
292/// Response to authenticate method
293#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema)]
294#[serde(rename_all = "camelCase")]
295#[schemars(extend("x-side" = "agent", "x-method" = AUTHENTICATE_METHOD_NAME))]
296pub struct AuthenticateResponse {
297    /// Extension point for implementations
298    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
299    pub meta: Option<serde_json::Value>,
300}
301
302/// Unique identifier for an authentication method.
303#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash)]
304#[serde(transparent)]
305pub struct AuthMethodId(pub Arc<str>);
306
307/// Describes an available authentication method.
308#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
309#[serde(rename_all = "camelCase")]
310pub struct AuthMethod {
311    /// Unique identifier for this authentication method.
312    pub id: AuthMethodId,
313    /// Human-readable name of the authentication method.
314    pub name: String,
315    /// Optional description providing more details about this authentication method.
316    pub description: Option<String>,
317    /// Extension point for implementations
318    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
319    pub meta: Option<serde_json::Value>,
320}
321
322// New session
323
324/// Request parameters for creating a new session.
325///
326/// See protocol docs: [Creating a Session](https://agentclientprotocol.com/protocol/session-setup#creating-a-session)
327#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
328#[schemars(extend("x-side" = "agent", "x-method" = SESSION_NEW_METHOD_NAME))]
329#[serde(rename_all = "camelCase")]
330pub struct NewSessionRequest {
331    /// The working directory for this session. Must be an absolute path.
332    pub cwd: PathBuf,
333    /// List of MCP (Model Context Protocol) servers the agent should connect to.
334    pub mcp_servers: Vec<McpServer>,
335    /// Extension point for implementations
336    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
337    pub meta: Option<serde_json::Value>,
338}
339
340/// Response from creating a new session.
341///
342/// See protocol docs: [Creating a Session](https://agentclientprotocol.com/protocol/session-setup#creating-a-session)
343#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
344#[schemars(extend("x-side" = "agent", "x-method" = SESSION_NEW_METHOD_NAME))]
345#[serde(rename_all = "camelCase")]
346pub struct NewSessionResponse {
347    /// Unique identifier for the created session.
348    ///
349    /// Used in all subsequent requests for this conversation.
350    pub session_id: SessionId,
351    /// Initial mode state if supported by the Agent
352    ///
353    /// See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)
354    #[serde(default, skip_serializing_if = "Option::is_none")]
355    pub modes: Option<SessionModeState>,
356    /// **UNSTABLE**
357    ///
358    /// This capability is not part of the spec yet, and may be removed or changed at any point.
359    ///
360    /// Initial model state if supported by the Agent
361    #[cfg(feature = "unstable")]
362    #[serde(default, skip_serializing_if = "Option::is_none")]
363    pub models: Option<SessionModelState>,
364    /// Extension point for implementations
365    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
366    pub meta: Option<serde_json::Value>,
367}
368
369// Load session
370
371/// Request parameters for loading an existing session.
372///
373/// Only available if the Agent supports the `loadSession` capability.
374///
375/// See protocol docs: [Loading Sessions](https://agentclientprotocol.com/protocol/session-setup#loading-sessions)
376#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
377#[schemars(extend("x-side" = "agent", "x-method" = SESSION_LOAD_METHOD_NAME))]
378#[serde(rename_all = "camelCase")]
379pub struct LoadSessionRequest {
380    /// List of MCP servers to connect to for this session.
381    pub mcp_servers: Vec<McpServer>,
382    /// The working directory for this session.
383    pub cwd: PathBuf,
384    /// The ID of the session to load.
385    pub session_id: SessionId,
386    /// Extension point for implementations
387    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
388    pub meta: Option<serde_json::Value>,
389}
390
391/// Response from loading an existing session.
392#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema)]
393#[schemars(extend("x-side" = "agent", "x-method" = SESSION_LOAD_METHOD_NAME))]
394#[serde(rename_all = "camelCase")]
395pub struct LoadSessionResponse {
396    /// Initial mode state if supported by the Agent
397    ///
398    /// See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)
399    #[serde(default, skip_serializing_if = "Option::is_none")]
400    pub modes: Option<SessionModeState>,
401    /// **UNSTABLE**
402    ///
403    /// This capability is not part of the spec yet, and may be removed or changed at any point.
404    ///
405    /// Initial model state if supported by the Agent
406    #[cfg(feature = "unstable")]
407    #[serde(default, skip_serializing_if = "Option::is_none")]
408    pub models: Option<SessionModelState>,
409    /// Extension point for implementations
410    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
411    pub meta: Option<serde_json::Value>,
412}
413
414// Session modes
415
416/// The set of modes and the one currently active.
417#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
418#[serde(rename_all = "camelCase")]
419pub struct SessionModeState {
420    /// The current mode the Agent is in.
421    pub current_mode_id: SessionModeId,
422    /// The set of modes that the Agent can operate in
423    pub available_modes: Vec<SessionMode>,
424    /// Extension point for implementations
425    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
426    pub meta: Option<serde_json::Value>,
427}
428
429/// A mode the agent can operate in.
430///
431/// See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)
432#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
433#[serde(rename_all = "camelCase")]
434pub struct SessionMode {
435    pub id: SessionModeId,
436    pub name: String,
437    #[serde(default, skip_serializing_if = "Option::is_none")]
438    pub description: Option<String>,
439    /// Extension point for implementations
440    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
441    pub meta: Option<serde_json::Value>,
442}
443
444/// Unique identifier for a Session Mode.
445#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash)]
446#[serde(transparent)]
447pub struct SessionModeId(pub Arc<str>);
448
449impl std::fmt::Display for SessionModeId {
450    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
451        write!(f, "{}", self.0)
452    }
453}
454
455/// Request parameters for setting a session mode.
456#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
457#[schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_MODE_METHOD_NAME))]
458#[serde(rename_all = "camelCase")]
459pub struct SetSessionModeRequest {
460    /// The ID of the session to set the mode for.
461    pub session_id: SessionId,
462    /// The ID of the mode to set.
463    pub mode_id: SessionModeId,
464    /// Extension point for implementations
465    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
466    pub meta: Option<serde_json::Value>,
467}
468
469/// Response to `session/set_mode` method.
470#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema)]
471#[schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_MODE_METHOD_NAME))]
472#[serde(rename_all = "camelCase")]
473pub struct SetSessionModeResponse {
474    pub meta: Option<serde_json::Value>,
475}
476
477// MCP
478
479/// Configuration for connecting to an MCP (Model Context Protocol) server.
480///
481/// MCP servers provide tools and context that the agent can use when
482/// processing prompts.
483///
484/// See protocol docs: [MCP Servers](https://agentclientprotocol.com/protocol/session-setup#mcp-servers)
485#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
486#[serde(tag = "type", rename_all = "snake_case")]
487pub enum McpServer {
488    /// HTTP transport configuration
489    ///
490    /// Only available when the Agent capabilities indicate `mcp_capabilities.http` is `true`.
491    #[serde(rename_all = "camelCase")]
492    Http {
493        /// Human-readable name identifying this MCP server.
494        name: String,
495        /// URL to the MCP server.
496        url: String,
497        /// HTTP headers to set when making requests to the MCP server.
498        headers: Vec<HttpHeader>,
499    },
500    /// SSE transport configuration
501    ///
502    /// Only available when the Agent capabilities indicate `mcp_capabilities.sse` is `true`.
503    #[serde(rename_all = "camelCase")]
504    Sse {
505        /// Human-readable name identifying this MCP server.
506        name: String,
507        /// URL to the MCP server.
508        url: String,
509        /// HTTP headers to set when making requests to the MCP server.
510        headers: Vec<HttpHeader>,
511    },
512    /// Stdio transport configuration
513    ///
514    /// All Agents MUST support this transport.
515    #[serde(untagged, rename_all = "camelCase")]
516    Stdio {
517        /// Human-readable name identifying this MCP server.
518        name: String,
519        /// Path to the MCP server executable.
520        command: PathBuf,
521        /// Command-line arguments to pass to the MCP server.
522        args: Vec<String>,
523        /// Environment variables to set when launching the MCP server.
524        env: Vec<EnvVariable>,
525    },
526}
527
528/// An environment variable to set when launching an MCP server.
529#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
530#[serde(rename_all = "camelCase")]
531pub struct EnvVariable {
532    /// The name of the environment variable.
533    pub name: String,
534    /// The value to set for the environment variable.
535    pub value: String,
536    /// Extension point for implementations
537    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
538    pub meta: Option<serde_json::Value>,
539}
540
541/// An HTTP header to set when making requests to the MCP server.
542#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
543#[serde(rename_all = "camelCase")]
544pub struct HttpHeader {
545    /// The name of the HTTP header.
546    pub name: String,
547    /// The value to set for the HTTP header.
548    pub value: String,
549    /// Extension point for implementations
550    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
551    pub meta: Option<serde_json::Value>,
552}
553
554// Prompt
555
556/// Request parameters for sending a user prompt to the agent.
557///
558/// Contains the user's message and any additional context.
559///
560/// See protocol docs: [User Message](https://agentclientprotocol.com/protocol/prompt-turn#1-user-message)
561#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
562#[schemars(extend("x-side" = "agent", "x-method" = SESSION_PROMPT_METHOD_NAME))]
563#[serde(rename_all = "camelCase")]
564pub struct PromptRequest {
565    /// The ID of the session to send this user message to
566    pub session_id: SessionId,
567    /// The blocks of content that compose the user's message.
568    ///
569    /// As a baseline, the Agent MUST support [`ContentBlock::Text`] and [`ContentBlock::ResourceLink`],
570    /// while other variants are optionally enabled via [`PromptCapabilities`].
571    ///
572    /// The Client MUST adapt its interface according to [`PromptCapabilities`].
573    ///
574    /// The client MAY include referenced pieces of context as either
575    /// [`ContentBlock::Resource`] or [`ContentBlock::ResourceLink`].
576    ///
577    /// When available, [`ContentBlock::Resource`] is preferred
578    /// as it avoids extra round-trips and allows the message to include
579    /// pieces of context from sources the agent may not have access to.
580    pub prompt: Vec<ContentBlock>,
581    /// Extension point for implementations
582    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
583    pub meta: Option<serde_json::Value>,
584}
585
586/// Response from processing a user prompt.
587///
588/// See protocol docs: [Check for Completion](https://agentclientprotocol.com/protocol/prompt-turn#4-check-for-completion)
589#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
590#[schemars(extend("x-side" = "agent", "x-method" = SESSION_PROMPT_METHOD_NAME))]
591#[serde(rename_all = "camelCase")]
592pub struct PromptResponse {
593    /// Indicates why the agent stopped processing the turn.
594    pub stop_reason: StopReason,
595    /// Extension point for implementations
596    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
597    pub meta: Option<serde_json::Value>,
598}
599
600/// Reasons why an agent stops processing a prompt turn.
601///
602/// See protocol docs: [Stop Reasons](https://agentclientprotocol.com/protocol/prompt-turn#stop-reasons)
603#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
604#[serde(rename_all = "snake_case")]
605pub enum StopReason {
606    /// The turn ended successfully.
607    EndTurn,
608    /// The turn ended because the agent reached the maximum number of tokens.
609    MaxTokens,
610    /// The turn ended because the agent reached the maximum number of allowed
611    /// agent requests between user turns.
612    MaxTurnRequests,
613    /// The turn ended because the agent refused to continue. The user prompt
614    /// and everything that comes after it won't be included in the next
615    /// prompt, so this should be reflected in the UI.
616    Refusal,
617    /// The turn was cancelled by the client via `session/cancel`.
618    ///
619    /// This stop reason MUST be returned when the client sends a `session/cancel`
620    /// notification, even if the cancellation causes exceptions in underlying operations.
621    /// Agents should catch these exceptions and return this semantically meaningful
622    /// response to confirm successful cancellation.
623    Cancelled,
624}
625
626// Model
627
628/// **UNSTABLE**
629///
630/// This capability is not part of the spec yet, and may be removed or changed at any point.
631///
632/// The set of models and the one currently active.
633#[cfg(feature = "unstable")]
634#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
635#[serde(rename_all = "camelCase")]
636pub struct SessionModelState {
637    /// The current model the Agent is in.
638    pub current_model_id: ModelId,
639    /// The set of models that the Agent can use
640    pub available_models: Vec<ModelInfo>,
641    /// Extension point for implementations
642    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
643    pub meta: Option<serde_json::Value>,
644}
645
646/// **UNSTABLE**
647///
648/// This capability is not part of the spec yet, and may be removed or changed at any point.
649///
650/// A unique identifier for a model.
651#[cfg(feature = "unstable")]
652#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash)]
653#[serde(transparent)]
654pub struct ModelId(pub Arc<str>);
655
656#[cfg(feature = "unstable")]
657impl std::fmt::Display for ModelId {
658    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
659        write!(f, "{}", self.0)
660    }
661}
662
663/// **UNSTABLE**
664///
665/// This capability is not part of the spec yet, and may be removed or changed at any point.
666///
667/// Information about a selectable model.
668#[cfg(feature = "unstable")]
669#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
670#[serde(rename_all = "camelCase")]
671pub struct ModelInfo {
672    /// Unique identifier for the model.
673    pub model_id: ModelId,
674    /// Human-readable name of the model.
675    pub name: String,
676    /// Optional description of the model.
677    #[serde(default, skip_serializing_if = "Option::is_none")]
678    pub description: Option<String>,
679    /// Extension point for implementations
680    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
681    pub meta: Option<serde_json::Value>,
682}
683
684/// **UNSTABLE**
685///
686/// This capability is not part of the spec yet, and may be removed or changed at any point.
687///
688/// Request parameters for setting a session model.
689#[cfg(feature = "unstable")]
690#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
691#[schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_MODEL_METHOD_NAME))]
692#[serde(rename_all = "camelCase")]
693pub struct SetSessionModelRequest {
694    /// The ID of the session to set the model for.
695    pub session_id: SessionId,
696    /// The ID of the model to set.
697    pub model_id: ModelId,
698    /// Extension point for implementations
699    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
700    pub meta: Option<serde_json::Value>,
701}
702
703/// **UNSTABLE**
704///
705/// This capability is not part of the spec yet, and may be removed or changed at any point.
706///
707/// Response to `session/set_model` method.
708#[cfg(feature = "unstable")]
709#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema)]
710#[schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_MODEL_METHOD_NAME))]
711#[serde(rename_all = "camelCase")]
712pub struct SetSessionModelResponse {
713    /// Extension point for implementations
714    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
715    pub meta: Option<serde_json::Value>,
716}
717
718// Capabilities
719
720/// Capabilities supported by the agent.
721///
722/// Advertised during initialization to inform the client about
723/// available features and content types.
724///
725/// See protocol docs: [Agent Capabilities](https://agentclientprotocol.com/protocol/initialization#agent-capabilities)
726#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema)]
727#[serde(rename_all = "camelCase")]
728pub struct AgentCapabilities {
729    /// Whether the agent supports `session/load`.
730    #[serde(default)]
731    pub load_session: bool,
732    /// Prompt capabilities supported by the agent.
733    #[serde(default)]
734    pub prompt_capabilities: PromptCapabilities,
735    /// MCP capabilities supported by the agent.
736    #[serde(default)]
737    pub mcp_capabilities: McpCapabilities,
738    /// Extension point for implementations
739    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
740    pub meta: Option<serde_json::Value>,
741}
742
743/// Prompt capabilities supported by the agent in `session/prompt` requests.
744///
745/// Baseline agent functionality requires support for [`ContentBlock::Text`]
746/// and [`ContentBlock::ResourceLink`] in prompt requests.
747///
748/// Other variants must be explicitly opted in to.
749/// Capabilities for different types of content in prompt requests.
750///
751/// Indicates which content types beyond the baseline (text and resource links)
752/// the agent can process.
753///
754/// See protocol docs: [Prompt Capabilities](https://agentclientprotocol.com/protocol/initialization#prompt-capabilities)
755#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema)]
756#[serde(rename_all = "camelCase")]
757pub struct PromptCapabilities {
758    /// Agent supports [`ContentBlock::Image`].
759    #[serde(default)]
760    pub image: bool,
761    /// Agent supports [`ContentBlock::Audio`].
762    #[serde(default)]
763    pub audio: bool,
764    /// Agent supports embedded context in `session/prompt` requests.
765    ///
766    /// When enabled, the Client is allowed to include [`ContentBlock::Resource`]
767    /// in prompt requests for pieces of context that are referenced in the message.
768    #[serde(default)]
769    pub embedded_context: bool,
770    /// Extension point for implementations
771    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
772    pub meta: Option<serde_json::Value>,
773}
774
775/// MCP capabilities supported by the agent
776#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema)]
777#[serde(rename_all = "camelCase")]
778pub struct McpCapabilities {
779    /// Agent supports [`McpServer::Http`].
780    #[serde(default)]
781    pub http: bool,
782    /// Agent supports [`McpServer::Sse`].
783    #[serde(default)]
784    pub sse: bool,
785    /// Extension point for implementations
786    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
787    pub meta: Option<serde_json::Value>,
788}
789
790// Method schema
791
792/// Names of all methods that agents handle.
793///
794/// Provides a centralized definition of method names used in the protocol.
795#[derive(Debug, Clone, Serialize, Deserialize)]
796pub struct AgentMethodNames {
797    /// Method for initializing the connection.
798    pub initialize: &'static str,
799    /// Method for authenticating with the agent.
800    pub authenticate: &'static str,
801    /// Method for creating a new session.
802    pub session_new: &'static str,
803    /// Method for loading an existing session.
804    pub session_load: &'static str,
805    /// Method for setting the mode for a session.
806    pub session_set_mode: &'static str,
807    /// Method for sending a prompt to the agent.
808    pub session_prompt: &'static str,
809    /// Notification for cancelling operations.
810    pub session_cancel: &'static str,
811    /// Method for selecting a model for a given session.
812    #[cfg(feature = "unstable")]
813    pub model_select: &'static str,
814}
815
816/// Constant containing all agent method names.
817pub const AGENT_METHOD_NAMES: AgentMethodNames = AgentMethodNames {
818    initialize: INITIALIZE_METHOD_NAME,
819    authenticate: AUTHENTICATE_METHOD_NAME,
820    session_new: SESSION_NEW_METHOD_NAME,
821    session_load: SESSION_LOAD_METHOD_NAME,
822    session_set_mode: SESSION_SET_MODE_METHOD_NAME,
823    session_prompt: SESSION_PROMPT_METHOD_NAME,
824    session_cancel: SESSION_CANCEL_METHOD_NAME,
825    #[cfg(feature = "unstable")]
826    model_select: SESSION_SET_MODEL_METHOD_NAME,
827};
828
829/// Method name for the initialize request.
830pub(crate) const INITIALIZE_METHOD_NAME: &str = "initialize";
831/// Method name for the authenticate request.
832pub(crate) const AUTHENTICATE_METHOD_NAME: &str = "authenticate";
833/// Method name for creating a new session.
834pub(crate) const SESSION_NEW_METHOD_NAME: &str = "session/new";
835/// Method name for loading an existing session.
836pub(crate) const SESSION_LOAD_METHOD_NAME: &str = "session/load";
837/// Method name for setting the mode for a session.
838pub(crate) const SESSION_SET_MODE_METHOD_NAME: &str = "session/set_mode";
839/// Method name for sending a prompt.
840pub(crate) const SESSION_PROMPT_METHOD_NAME: &str = "session/prompt";
841/// Method name for the cancel notification.
842pub(crate) const SESSION_CANCEL_METHOD_NAME: &str = "session/cancel";
843/// Method name for selecting a model for a given session.
844#[cfg(feature = "unstable")]
845pub(crate) const SESSION_SET_MODEL_METHOD_NAME: &str = "session/set_model";
846
847/// All possible requests that a client can send to an agent.
848///
849/// This enum is used internally for routing RPC requests. You typically won't need
850/// to use this directly - instead, use the methods on the [`Agent`] trait.
851///
852/// This enum encompasses all method calls from client to agent.
853#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
854#[serde(untagged)]
855#[schemars(extend("x-docs-ignore" = true))]
856pub enum ClientRequest {
857    InitializeRequest(InitializeRequest),
858    AuthenticateRequest(AuthenticateRequest),
859    NewSessionRequest(NewSessionRequest),
860    LoadSessionRequest(LoadSessionRequest),
861    SetSessionModeRequest(SetSessionModeRequest),
862    PromptRequest(PromptRequest),
863    #[cfg(feature = "unstable")]
864    SetSessionModelRequest(SetSessionModelRequest),
865    ExtMethodRequest(ExtRequest),
866}
867
868/// All possible responses that an agent can send to a client.
869///
870/// This enum is used internally for routing RPC responses. You typically won't need
871/// to use this directly - the responses are handled automatically by the connection.
872///
873/// These are responses to the corresponding `ClientRequest` variants.
874#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
875#[serde(untagged)]
876#[schemars(extend("x-docs-ignore" = true))]
877pub enum AgentResponse {
878    InitializeResponse(InitializeResponse),
879    AuthenticateResponse(#[serde(default)] AuthenticateResponse),
880    NewSessionResponse(NewSessionResponse),
881    LoadSessionResponse(#[serde(default)] LoadSessionResponse),
882    SetSessionModeResponse(#[serde(default)] SetSessionModeResponse),
883    PromptResponse(PromptResponse),
884    #[cfg(feature = "unstable")]
885    SetSessionModelResponse(SetSessionModelResponse),
886    ExtMethodResponse(#[schemars(with = "serde_json::Value")] Arc<RawValue>),
887}
888
889/// All possible notifications that a client can send to an agent.
890///
891/// This enum is used internally for routing RPC notifications. You typically won't need
892/// to use this directly - use the notification methods on the [`Agent`] trait instead.
893///
894/// Notifications do not expect a response.
895#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
896#[serde(untagged)]
897#[schemars(extend("x-docs-ignore" = true))]
898pub enum ClientNotification {
899    CancelNotification(CancelNotification),
900    ExtNotification(ExtNotification),
901}
902
903/// Notification to cancel ongoing operations for a session.
904///
905/// See protocol docs: [Cancellation](https://agentclientprotocol.com/protocol/prompt-turn#cancellation)
906#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
907#[schemars(extend("x-side" = "agent", "x-method" = SESSION_CANCEL_METHOD_NAME))]
908#[serde(rename_all = "camelCase")]
909pub struct CancelNotification {
910    /// The ID of the session to cancel operations for.
911    pub session_id: SessionId,
912    /// Extension point for implementations
913    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
914    pub meta: Option<serde_json::Value>,
915}
916
917#[cfg(test)]
918mod test_serialization {
919    use super::*;
920    use serde_json::json;
921
922    #[test]
923    fn test_mcp_server_stdio_serialization() {
924        let server = McpServer::Stdio {
925            name: "test-server".to_string(),
926            command: PathBuf::from("/usr/bin/server"),
927            args: vec!["--port".to_string(), "3000".to_string()],
928            env: vec![EnvVariable {
929                name: "API_KEY".to_string(),
930                value: "secret123".to_string(),
931                meta: None,
932            }],
933        };
934
935        let json = serde_json::to_value(&server).unwrap();
936        assert_eq!(
937            json,
938            json!({
939                "name": "test-server",
940                "command": "/usr/bin/server",
941                "args": ["--port", "3000"],
942                "env": [
943                    {
944                        "name": "API_KEY",
945                        "value": "secret123"
946                    }
947                ]
948            })
949        );
950
951        let deserialized: McpServer = serde_json::from_value(json).unwrap();
952        match deserialized {
953            McpServer::Stdio {
954                name,
955                command,
956                args,
957                env,
958            } => {
959                assert_eq!(name, "test-server");
960                assert_eq!(command, PathBuf::from("/usr/bin/server"));
961                assert_eq!(args, vec!["--port", "3000"]);
962                assert_eq!(env.len(), 1);
963                assert_eq!(env[0].name, "API_KEY");
964                assert_eq!(env[0].value, "secret123");
965            }
966            _ => panic!("Expected Stdio variant"),
967        }
968    }
969
970    #[test]
971    fn test_mcp_server_http_serialization() {
972        let server = McpServer::Http {
973            name: "http-server".to_string(),
974            url: "https://api.example.com".to_string(),
975            headers: vec![
976                HttpHeader {
977                    name: "Authorization".to_string(),
978                    value: "Bearer token123".to_string(),
979                    meta: None,
980                },
981                HttpHeader {
982                    name: "Content-Type".to_string(),
983                    value: "application/json".to_string(),
984                    meta: None,
985                },
986            ],
987        };
988
989        let json = serde_json::to_value(&server).unwrap();
990        assert_eq!(
991            json,
992            json!({
993                "type": "http",
994                "name": "http-server",
995                "url": "https://api.example.com",
996                "headers": [
997                    {
998                        "name": "Authorization",
999                        "value": "Bearer token123"
1000                    },
1001                    {
1002                        "name": "Content-Type",
1003                        "value": "application/json"
1004                    }
1005                ]
1006            })
1007        );
1008
1009        let deserialized: McpServer = serde_json::from_value(json).unwrap();
1010        match deserialized {
1011            McpServer::Http { name, url, headers } => {
1012                assert_eq!(name, "http-server");
1013                assert_eq!(url, "https://api.example.com");
1014                assert_eq!(headers.len(), 2);
1015                assert_eq!(headers[0].name, "Authorization");
1016                assert_eq!(headers[0].value, "Bearer token123");
1017                assert_eq!(headers[1].name, "Content-Type");
1018                assert_eq!(headers[1].value, "application/json");
1019            }
1020            _ => panic!("Expected Http variant"),
1021        }
1022    }
1023
1024    #[test]
1025    fn test_mcp_server_sse_serialization() {
1026        let server = McpServer::Sse {
1027            name: "sse-server".to_string(),
1028            url: "https://sse.example.com/events".to_string(),
1029            headers: vec![HttpHeader {
1030                name: "X-API-Key".to_string(),
1031                value: "apikey456".to_string(),
1032                meta: None,
1033            }],
1034        };
1035
1036        let json = serde_json::to_value(&server).unwrap();
1037        assert_eq!(
1038            json,
1039            json!({
1040                "type": "sse",
1041                "name": "sse-server",
1042                "url": "https://sse.example.com/events",
1043                "headers": [
1044                    {
1045                        "name": "X-API-Key",
1046                        "value": "apikey456"
1047                    }
1048                ]
1049            })
1050        );
1051
1052        let deserialized: McpServer = serde_json::from_value(json).unwrap();
1053        match deserialized {
1054            McpServer::Sse { name, url, headers } => {
1055                assert_eq!(name, "sse-server");
1056                assert_eq!(url, "https://sse.example.com/events");
1057                assert_eq!(headers.len(), 1);
1058                assert_eq!(headers[0].name, "X-API-Key");
1059                assert_eq!(headers[0].value, "apikey456");
1060            }
1061            _ => panic!("Expected Sse variant"),
1062        }
1063    }
1064}