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<(), 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"))]
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"))]
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"))]
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/// 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"))]
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"))]
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 /// **UNSTABLE**
258 ///
259 /// This field is not part of the spec, and may be removed or changed at any point.
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"))]
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(Debug, Clone, Serialize, Deserialize, JsonSchema)]
291#[serde(rename_all = "camelCase")]
292pub struct LoadSessionResponse {
293 /// **UNSTABLE**
294 ///
295 /// This field is not part of the spec, and may be removed or changed at any point.
296 #[serde(default, skip_serializing_if = "Option::is_none")]
297 pub modes: Option<SessionModeState>,
298 /// Extension point for implementations
299 #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
300 pub meta: Option<serde_json::Value>,
301}
302
303// Session modes
304
305/// **UNSTABLE**
306///
307/// This type is not part of the spec, and may be removed or changed at any point.
308#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
309#[serde(rename_all = "camelCase")]
310pub struct SessionModeState {
311 pub current_mode_id: SessionModeId,
312 pub available_modes: Vec<SessionMode>,
313 /// Extension point for implementations
314 #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
315 pub meta: Option<serde_json::Value>,
316}
317
318/// **UNSTABLE**
319///
320/// This type is not part of the spec, and may be removed or changed at any point.
321#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
322#[serde(rename_all = "camelCase")]
323pub struct SessionMode {
324 pub id: SessionModeId,
325 pub name: String,
326 #[serde(default, skip_serializing_if = "Option::is_none")]
327 pub description: Option<String>,
328 /// Extension point for implementations
329 #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
330 pub meta: Option<serde_json::Value>,
331}
332
333/// **UNSTABLE**
334///
335/// This type is not part of the spec, and may be removed or changed at any point.
336#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash)]
337#[serde(rename_all = "camelCase")]
338pub struct SessionModeId(pub Arc<str>);
339
340impl std::fmt::Display for SessionModeId {
341 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
342 write!(f, "{}", self.0)
343 }
344}
345
346/// **UNSTABLE**
347///
348/// This type is not part of the spec, and may be removed or changed at any point.
349#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
350#[schemars(extend("x-docs-ignore" = true))]
351#[serde(rename_all = "camelCase")]
352pub struct SetSessionModeRequest {
353 pub session_id: SessionId,
354 pub mode_id: SessionModeId,
355 /// Extension point for implementations
356 #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
357 pub meta: Option<serde_json::Value>,
358}
359
360/// **UNSTABLE**
361///
362/// This type is not part of the spec, and may be removed or changed at any point.
363#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
364#[serde(rename_all = "camelCase")]
365pub struct SetSessionModeResponse {}
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"))]
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"))]
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 #[cfg(feature = "unstable")]
605 pub session_set_mode: &'static str,
606 /// Method for sending a prompt to the agent.
607 pub session_prompt: &'static str,
608 /// Notification for cancelling operations.
609 pub session_cancel: &'static str,
610}
611
612/// Constant containing all agent method names.
613pub const AGENT_METHOD_NAMES: AgentMethodNames = AgentMethodNames {
614 initialize: INITIALIZE_METHOD_NAME,
615 authenticate: AUTHENTICATE_METHOD_NAME,
616 session_new: SESSION_NEW_METHOD_NAME,
617 session_load: SESSION_LOAD_METHOD_NAME,
618 #[cfg(feature = "unstable")]
619 session_set_mode: SESSION_SET_MODE_METHOD_NAME,
620 session_prompt: SESSION_PROMPT_METHOD_NAME,
621 session_cancel: SESSION_CANCEL_METHOD_NAME,
622};
623
624/// Method name for the initialize request.
625pub(crate) const INITIALIZE_METHOD_NAME: &str = "initialize";
626/// Method name for the authenticate request.
627pub(crate) const AUTHENTICATE_METHOD_NAME: &str = "authenticate";
628/// Method name for creating a new session.
629pub(crate) const SESSION_NEW_METHOD_NAME: &str = "session/new";
630/// Method name for loading an existing session.
631pub(crate) const SESSION_LOAD_METHOD_NAME: &str = "session/load";
632/// Method name for setting the mode for a session.
633#[cfg(feature = "unstable")]
634pub(crate) const SESSION_SET_MODE_METHOD_NAME: &str = "session/set_mode";
635/// Method name for sending a prompt.
636pub(crate) const SESSION_PROMPT_METHOD_NAME: &str = "session/prompt";
637/// Method name for the cancel notification.
638pub(crate) const SESSION_CANCEL_METHOD_NAME: &str = "session/cancel";
639
640/// All possible requests that a client can send to an agent.
641///
642/// This enum is used internally for routing RPC requests. You typically won't need
643/// to use this directly - instead, use the methods on the [`Agent`] trait.
644///
645/// This enum encompasses all method calls from client to agent.
646#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
647#[serde(untagged)]
648#[schemars(extend("x-docs-ignore" = true))]
649pub enum ClientRequest {
650 InitializeRequest(InitializeRequest),
651 AuthenticateRequest(AuthenticateRequest),
652 NewSessionRequest(NewSessionRequest),
653 LoadSessionRequest(LoadSessionRequest),
654 #[cfg(feature = "unstable")]
655 SetSessionModeRequest(SetSessionModeRequest),
656 PromptRequest(PromptRequest),
657 ExtMethodRequest(ExtMethod),
658}
659
660/// All possible responses that an agent can send to a client.
661///
662/// This enum is used internally for routing RPC responses. You typically won't need
663/// to use this directly - the responses are handled automatically by the connection.
664///
665/// These are responses to the corresponding ClientRequest variants.
666#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
667#[serde(untagged)]
668#[schemars(extend("x-docs-ignore" = true))]
669pub enum AgentResponse {
670 InitializeResponse(InitializeResponse),
671 AuthenticateResponse,
672 NewSessionResponse(NewSessionResponse),
673 LoadSessionResponse(LoadSessionResponse),
674 #[cfg(feature = "unstable")]
675 SetSessionModeResponse(SetSessionModeResponse),
676 PromptResponse(PromptResponse),
677 ExtMethodResponse(#[schemars(with = "serde_json::Value")] Arc<RawValue>),
678}
679
680/// All possible notifications that a client can send to an agent.
681///
682/// This enum is used internally for routing RPC notifications. You typically won't need
683/// to use this directly - use the notification methods on the [`Agent`] trait instead.
684///
685/// Notifications do not expect a response.
686#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
687#[serde(untagged)]
688#[schemars(extend("x-docs-ignore" = true))]
689pub enum ClientNotification {
690 CancelNotification(CancelNotification),
691 ExtNotification(ExtMethod),
692}
693
694/// Notification to cancel ongoing operations for a session.
695///
696/// See protocol docs: [Cancellation](https://agentclientprotocol.com/protocol/prompt-turn#cancellation)
697#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
698#[schemars(extend("x-side" = "agent", "x-method" = "session/cancel"))]
699#[serde(rename_all = "camelCase")]
700pub struct CancelNotification {
701 /// The ID of the session to cancel operations for.
702 pub session_id: SessionId,
703 /// Extension point for implementations
704 #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
705 pub meta: Option<serde_json::Value>,
706}
707
708#[cfg(test)]
709mod test_serialization {
710 use super::*;
711 use serde_json::json;
712
713 #[test]
714 fn test_mcp_server_stdio_serialization() {
715 let server = McpServer::Stdio {
716 name: "test-server".to_string(),
717 command: PathBuf::from("/usr/bin/server"),
718 args: vec!["--port".to_string(), "3000".to_string()],
719 env: vec![EnvVariable {
720 name: "API_KEY".to_string(),
721 value: "secret123".to_string(),
722 meta: None,
723 }],
724 };
725
726 let json = serde_json::to_value(&server).unwrap();
727 assert_eq!(
728 json,
729 json!({
730 "name": "test-server",
731 "command": "/usr/bin/server",
732 "args": ["--port", "3000"],
733 "env": [
734 {
735 "name": "API_KEY",
736 "value": "secret123"
737 }
738 ]
739 })
740 );
741
742 let deserialized: McpServer = serde_json::from_value(json).unwrap();
743 match deserialized {
744 McpServer::Stdio {
745 name,
746 command,
747 args,
748 env,
749 } => {
750 assert_eq!(name, "test-server");
751 assert_eq!(command, PathBuf::from("/usr/bin/server"));
752 assert_eq!(args, vec!["--port", "3000"]);
753 assert_eq!(env.len(), 1);
754 assert_eq!(env[0].name, "API_KEY");
755 assert_eq!(env[0].value, "secret123");
756 }
757 _ => panic!("Expected Stdio variant"),
758 }
759 }
760
761 #[test]
762 fn test_mcp_server_http_serialization() {
763 let server = McpServer::Http {
764 name: "http-server".to_string(),
765 url: "https://api.example.com".to_string(),
766 headers: vec![
767 HttpHeader {
768 name: "Authorization".to_string(),
769 value: "Bearer token123".to_string(),
770 meta: None,
771 },
772 HttpHeader {
773 name: "Content-Type".to_string(),
774 value: "application/json".to_string(),
775 meta: None,
776 },
777 ],
778 };
779
780 let json = serde_json::to_value(&server).unwrap();
781 assert_eq!(
782 json,
783 json!({
784 "type": "http",
785 "name": "http-server",
786 "url": "https://api.example.com",
787 "headers": [
788 {
789 "name": "Authorization",
790 "value": "Bearer token123"
791 },
792 {
793 "name": "Content-Type",
794 "value": "application/json"
795 }
796 ]
797 })
798 );
799
800 let deserialized: McpServer = serde_json::from_value(json).unwrap();
801 match deserialized {
802 McpServer::Http { name, url, headers } => {
803 assert_eq!(name, "http-server");
804 assert_eq!(url, "https://api.example.com");
805 assert_eq!(headers.len(), 2);
806 assert_eq!(headers[0].name, "Authorization");
807 assert_eq!(headers[0].value, "Bearer token123");
808 assert_eq!(headers[1].name, "Content-Type");
809 assert_eq!(headers[1].value, "application/json");
810 }
811 _ => panic!("Expected Http variant"),
812 }
813 }
814
815 #[test]
816 fn test_mcp_server_sse_serialization() {
817 let server = McpServer::Sse {
818 name: "sse-server".to_string(),
819 url: "https://sse.example.com/events".to_string(),
820 headers: vec![HttpHeader {
821 name: "X-API-Key".to_string(),
822 value: "apikey456".to_string(),
823 meta: None,
824 }],
825 };
826
827 let json = serde_json::to_value(&server).unwrap();
828 assert_eq!(
829 json,
830 json!({
831 "type": "sse",
832 "name": "sse-server",
833 "url": "https://sse.example.com/events",
834 "headers": [
835 {
836 "name": "X-API-Key",
837 "value": "apikey456"
838 }
839 ]
840 })
841 );
842
843 let deserialized: McpServer = serde_json::from_value(json).unwrap();
844 match deserialized {
845 McpServer::Sse { name, url, headers } => {
846 assert_eq!(name, "sse-server");
847 assert_eq!(url, "https://sse.example.com/events");
848 assert_eq!(headers.len(), 1);
849 assert_eq!(headers[0].name, "X-API-Key");
850 assert_eq!(headers[0].value, "apikey456");
851 }
852 _ => panic!("Expected Sse variant"),
853 }
854 }
855}