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