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