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