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}