diff --git a/agent-client-protocol-schema/src/v1/elicitation.rs b/agent-client-protocol-schema/src/v1/elicitation.rs index 17eac7380..5aad80755 100644 --- a/agent-client-protocol-schema/src/v1/elicitation.rs +++ b/agent-client-protocol-schema/src/v1/elicitation.rs @@ -2094,78 +2094,6 @@ impl CompleteElicitationNotification { } } -/// **UNSTABLE** -/// -/// This capability is not part of the spec yet, and may be removed or changed at any point. -/// -/// Data payload for the `UrlElicitationRequired` error, describing the URL elicitations -/// the user must complete. -#[serde_as] -#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] -#[serde(rename_all = "camelCase")] -#[non_exhaustive] -pub struct UrlElicitationRequiredData { - /// The URL elicitations the user must complete. - #[serde_as(deserialize_as = "DefaultOnError>")] - #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))] - pub elicitations: Vec, -} - -impl UrlElicitationRequiredData { - /// Builds [`UrlElicitationRequiredData`] with the required fields set; optional fields start unset or empty. - #[must_use] - pub fn new(elicitations: Vec) -> Self { - Self { elicitations } - } -} - -/// **UNSTABLE** -/// -/// This capability is not part of the spec yet, and may be removed or changed at any point. -/// -/// A single URL elicitation item within the `UrlElicitationRequired` error data. -#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] -#[serde(rename_all = "camelCase")] -#[non_exhaustive] -pub struct UrlElicitationRequiredItem { - /// The elicitation mode (always `"url"` for this item type). - pub mode: ElicitationUrlOnlyMode, - /// The unique identifier for this elicitation. - pub elicitation_id: ElicitationId, - /// The URL the user should be directed to. - #[schemars(extend("format" = "uri"))] - pub url: String, - /// A human-readable message describing what input is needed. - pub message: String, -} - -/// Type discriminator for URL-only elicitation error items. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Default)] -#[serde(rename_all = "snake_case")] -#[non_exhaustive] -pub enum ElicitationUrlOnlyMode { - /// URL elicitation mode. - #[default] - Url, -} - -impl UrlElicitationRequiredItem { - /// Builds [`UrlElicitationRequiredItem`] with the required fields set; optional fields start unset or empty. - #[must_use] - pub fn new( - elicitation_id: impl Into, - url: impl Into, - message: impl Into, - ) -> Self { - Self { - mode: ElicitationUrlOnlyMode::Url, - elicitation_id: elicitation_id.into(), - url: url.into(), - message: message.into(), - } - } -} - #[cfg(test)] mod tests { use super::*; @@ -2524,27 +2452,6 @@ mod tests { assert!(roundtripped.url.is_some()); } - #[test] - fn url_elicitation_required_data_serialization() { - let data = UrlElicitationRequiredData::new(vec![UrlElicitationRequiredItem::new( - "elic_1", - "https://example.com/auth", - "Please authenticate", - )]); - - let json = serde_json::to_value(&data).unwrap(); - assert_eq!(json["elicitations"][0]["mode"], "url"); - assert_eq!(json["elicitations"][0]["elicitationId"], "elic_1"); - assert_eq!(json["elicitations"][0]["url"], "https://example.com/auth"); - - let roundtripped: UrlElicitationRequiredData = serde_json::from_value(json).unwrap(); - assert_eq!(roundtripped.elicitations.len(), 1); - assert_eq!( - roundtripped.elicitations[0].mode, - ElicitationUrlOnlyMode::Url - ); - } - #[test] fn schema_default_sets_object_type() { let schema = ElicitationSchema::default(); diff --git a/agent-client-protocol-schema/src/v1/error.rs b/agent-client-protocol-schema/src/v1/error.rs index ac4c55a7a..da468fa89 100644 --- a/agent-client-protocol-schema/src/v1/error.rs +++ b/agent-client-protocol-schema/src/v1/error.rs @@ -114,17 +114,6 @@ impl Error { ErrorCode::AuthRequired.into() } - /// **UNSTABLE** - /// - /// This capability is not part of the spec yet, and may be removed or changed at any point. - /// - /// The agent requires user input via a URL-based elicitation before it can proceed. - #[cfg(feature = "unstable_elicitation")] - #[must_use] - pub fn url_elicitation_required() -> Self { - ErrorCode::UrlElicitationRequired.into() - } - /// A given resource, such as a file, was not found. #[must_use] pub fn resource_not_found(uri: Option) -> Self { @@ -193,16 +182,6 @@ pub enum ErrorCode { #[schemars(transform = error_code_transform)] #[strum(to_string = "Resource not found")] ResourceNotFound, // -32002 - #[cfg(feature = "unstable_elicitation")] - /// **UNSTABLE** - /// - /// This capability is not part of the spec yet, and may be removed or changed at any point. - /// - /// The agent requires user input via a URL-based elicitation before it can proceed. - #[schemars(transform = error_code_transform)] - #[strum(to_string = "URL elicitation required")] - UrlElicitationRequired, // -32042 - /// Other undefined error code. #[schemars(untagged)] #[strum(to_string = "Unknown error")] @@ -220,8 +199,6 @@ impl From for ErrorCode { -32800 => ErrorCode::RequestCancelled, -32000 => ErrorCode::AuthRequired, -32002 => ErrorCode::ResourceNotFound, - #[cfg(feature = "unstable_elicitation")] - -32042 => ErrorCode::UrlElicitationRequired, _ => ErrorCode::Other(value), } } @@ -238,8 +215,6 @@ impl From for i32 { ErrorCode::RequestCancelled => -32800, ErrorCode::AuthRequired => -32000, ErrorCode::ResourceNotFound => -32002, - #[cfg(feature = "unstable_elicitation")] - ErrorCode::UrlElicitationRequired => -32042, ErrorCode::Other(value) => value, } } @@ -266,8 +241,6 @@ fn error_code_transform(schema: &mut Schema) { "RequestCancelled" => ErrorCode::RequestCancelled, "AuthRequired" => ErrorCode::AuthRequired, "ResourceNotFound" => ErrorCode::ResourceNotFound, - #[cfg(feature = "unstable_elicitation")] - "UrlElicitationRequired" => ErrorCode::UrlElicitationRequired, _ => panic!("Unexpected error code name {name}"), }; let mut description = schema diff --git a/agent-client-protocol-schema/src/v2/conversion.rs b/agent-client-protocol-schema/src/v2/conversion.rs index c907392e0..b3f8a7f3b 100644 --- a/agent-client-protocol-schema/src/v2/conversion.rs +++ b/agent-client-protocol-schema/src/v2/conversion.rs @@ -8607,92 +8607,6 @@ impl IntoV2 for crate::v1::CompleteElicitationNotification { } } -#[cfg(feature = "unstable_elicitation")] -impl IntoV1 for super::UrlElicitationRequiredData { - type Output = crate::v1::UrlElicitationRequiredData; - - fn into_v1(self) -> Result { - let Self { elicitations } = self; - Ok(crate::v1::UrlElicitationRequiredData { - elicitations: elicitations.into_v1()?, - }) - } -} - -#[cfg(feature = "unstable_elicitation")] -impl IntoV2 for crate::v1::UrlElicitationRequiredData { - type Output = super::UrlElicitationRequiredData; - - fn into_v2(self) -> Result { - let Self { elicitations } = self; - Ok(super::UrlElicitationRequiredData { - elicitations: elicitations.into_v2()?, - }) - } -} - -#[cfg(feature = "unstable_elicitation")] -impl IntoV1 for super::UrlElicitationRequiredItem { - type Output = crate::v1::UrlElicitationRequiredItem; - - fn into_v1(self) -> Result { - let Self { - mode, - elicitation_id, - url, - message, - } = self; - Ok(crate::v1::UrlElicitationRequiredItem { - mode: mode.into_v1()?, - elicitation_id: elicitation_id.into_v1()?, - url: url.into_v1()?, - message: message.into_v1()?, - }) - } -} - -#[cfg(feature = "unstable_elicitation")] -impl IntoV2 for crate::v1::UrlElicitationRequiredItem { - type Output = super::UrlElicitationRequiredItem; - - fn into_v2(self) -> Result { - let Self { - mode, - elicitation_id, - url, - message, - } = self; - Ok(super::UrlElicitationRequiredItem { - mode: mode.into_v2()?, - elicitation_id: elicitation_id.into_v2()?, - url: url.into_v2()?, - message: message.into_v2()?, - }) - } -} - -#[cfg(feature = "unstable_elicitation")] -impl IntoV1 for super::ElicitationUrlOnlyMode { - type Output = crate::v1::ElicitationUrlOnlyMode; - - fn into_v1(self) -> Result { - Ok(match self { - Self::Url => crate::v1::ElicitationUrlOnlyMode::Url, - }) - } -} - -#[cfg(feature = "unstable_elicitation")] -impl IntoV2 for crate::v1::ElicitationUrlOnlyMode { - type Output = super::ElicitationUrlOnlyMode; - - fn into_v2(self) -> Result { - Ok(match self { - Self::Url => super::ElicitationUrlOnlyMode::Url, - }) - } -} - impl IntoV1 for super::ContentBlock { type Output = crate::v1::ContentBlock; diff --git a/agent-client-protocol-schema/src/v2/elicitation.rs b/agent-client-protocol-schema/src/v2/elicitation.rs index df83a1dd7..dd0339f22 100644 --- a/agent-client-protocol-schema/src/v2/elicitation.rs +++ b/agent-client-protocol-schema/src/v2/elicitation.rs @@ -2035,78 +2035,6 @@ impl CompleteElicitationNotification { } } -/// **UNSTABLE** -/// -/// This capability is not part of the spec yet, and may be removed or changed at any point. -/// -/// Data payload for the `UrlElicitationRequired` error, describing the URL elicitations -/// the user must complete. -#[serde_as] -#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] -#[serde(rename_all = "camelCase")] -#[non_exhaustive] -pub struct UrlElicitationRequiredData { - /// The URL elicitations the user must complete. - #[serde_as(deserialize_as = "DefaultOnError>")] - #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))] - pub elicitations: Vec, -} - -impl UrlElicitationRequiredData { - /// Builds [`UrlElicitationRequiredData`] with the required fields set; optional fields start unset or empty. - #[must_use] - pub fn new(elicitations: Vec) -> Self { - Self { elicitations } - } -} - -/// **UNSTABLE** -/// -/// This capability is not part of the spec yet, and may be removed or changed at any point. -/// -/// A single URL elicitation item within the `UrlElicitationRequired` error data. -#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] -#[serde(rename_all = "camelCase")] -#[non_exhaustive] -pub struct UrlElicitationRequiredItem { - /// The elicitation mode (always `"url"` for this item type). - pub mode: ElicitationUrlOnlyMode, - /// The unique identifier for this elicitation. - pub elicitation_id: ElicitationId, - /// The URL the user should be directed to. - #[schemars(extend("format" = "uri"))] - pub url: String, - /// A human-readable message describing what input is needed. - pub message: String, -} - -/// Type discriminator for URL-only elicitation error items. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Default)] -#[serde(rename_all = "snake_case")] -#[non_exhaustive] -pub enum ElicitationUrlOnlyMode { - /// URL elicitation mode. - #[default] - Url, -} - -impl UrlElicitationRequiredItem { - /// Builds [`UrlElicitationRequiredItem`] with the required fields set; optional fields start unset or empty. - #[must_use] - pub fn new( - elicitation_id: impl Into, - url: impl Into, - message: impl Into, - ) -> Self { - Self { - mode: ElicitationUrlOnlyMode::Url, - elicitation_id: elicitation_id.into(), - url: url.into(), - message: message.into(), - } - } -} - #[cfg(test)] mod tests { use super::*; @@ -2462,27 +2390,6 @@ mod tests { assert!(roundtripped.url.is_some()); } - #[test] - fn url_elicitation_required_data_serialization() { - let data = UrlElicitationRequiredData::new(vec![UrlElicitationRequiredItem::new( - "elic_1", - "https://example.com/auth", - "Please authenticate", - )]); - - let json = serde_json::to_value(&data).unwrap(); - assert_eq!(json["elicitations"][0]["mode"], "url"); - assert_eq!(json["elicitations"][0]["elicitationId"], "elic_1"); - assert_eq!(json["elicitations"][0]["url"], "https://example.com/auth"); - - let roundtripped: UrlElicitationRequiredData = serde_json::from_value(json).unwrap(); - assert_eq!(roundtripped.elicitations.len(), 1); - assert_eq!( - roundtripped.elicitations[0].mode, - ElicitationUrlOnlyMode::Url - ); - } - #[test] fn schema_default_sets_object_type() { let schema = ElicitationSchema::default(); diff --git a/agent-client-protocol-schema/src/v2/error.rs b/agent-client-protocol-schema/src/v2/error.rs index ac4c55a7a..da468fa89 100644 --- a/agent-client-protocol-schema/src/v2/error.rs +++ b/agent-client-protocol-schema/src/v2/error.rs @@ -114,17 +114,6 @@ impl Error { ErrorCode::AuthRequired.into() } - /// **UNSTABLE** - /// - /// This capability is not part of the spec yet, and may be removed or changed at any point. - /// - /// The agent requires user input via a URL-based elicitation before it can proceed. - #[cfg(feature = "unstable_elicitation")] - #[must_use] - pub fn url_elicitation_required() -> Self { - ErrorCode::UrlElicitationRequired.into() - } - /// A given resource, such as a file, was not found. #[must_use] pub fn resource_not_found(uri: Option) -> Self { @@ -193,16 +182,6 @@ pub enum ErrorCode { #[schemars(transform = error_code_transform)] #[strum(to_string = "Resource not found")] ResourceNotFound, // -32002 - #[cfg(feature = "unstable_elicitation")] - /// **UNSTABLE** - /// - /// This capability is not part of the spec yet, and may be removed or changed at any point. - /// - /// The agent requires user input via a URL-based elicitation before it can proceed. - #[schemars(transform = error_code_transform)] - #[strum(to_string = "URL elicitation required")] - UrlElicitationRequired, // -32042 - /// Other undefined error code. #[schemars(untagged)] #[strum(to_string = "Unknown error")] @@ -220,8 +199,6 @@ impl From for ErrorCode { -32800 => ErrorCode::RequestCancelled, -32000 => ErrorCode::AuthRequired, -32002 => ErrorCode::ResourceNotFound, - #[cfg(feature = "unstable_elicitation")] - -32042 => ErrorCode::UrlElicitationRequired, _ => ErrorCode::Other(value), } } @@ -238,8 +215,6 @@ impl From for i32 { ErrorCode::RequestCancelled => -32800, ErrorCode::AuthRequired => -32000, ErrorCode::ResourceNotFound => -32002, - #[cfg(feature = "unstable_elicitation")] - ErrorCode::UrlElicitationRequired => -32042, ErrorCode::Other(value) => value, } } @@ -266,8 +241,6 @@ fn error_code_transform(schema: &mut Schema) { "RequestCancelled" => ErrorCode::RequestCancelled, "AuthRequired" => ErrorCode::AuthRequired, "ResourceNotFound" => ErrorCode::ResourceNotFound, - #[cfg(feature = "unstable_elicitation")] - "UrlElicitationRequired" => ErrorCode::UrlElicitationRequired, _ => panic!("Unexpected error code name {name}"), }; let mut description = schema diff --git a/docs/protocol/v1/draft/schema.mdx b/docs/protocol/v1/draft/schema.mdx index f4020e664..bb8ccec3c 100644 --- a/docs/protocol/v1/draft/schema.mdx +++ b/docs/protocol/v1/draft/schema.mdx @@ -4552,15 +4552,6 @@ and use the reserved range (-32000 to -32099) for protocol-specific errors. **Resource not found**: A given resource, such as a file, was not found. - -**URL elicitation required**: **UNSTABLE** - -This capability is not part of the spec yet, and may be removed or changed at any point. - -The agent requires user input via a URL-based elicitation before it can proceed. - - - Other undefined error code. diff --git a/docs/protocol/v2/draft/schema.mdx b/docs/protocol/v2/draft/schema.mdx index 86f5d0db9..47d9f30ae 100644 --- a/docs/protocol/v2/draft/schema.mdx +++ b/docs/protocol/v2/draft/schema.mdx @@ -4095,15 +4095,6 @@ and use the reserved range (-32000 to -32099) for protocol-specific errors. **Resource not found**: A given resource, such as a file, was not found. - -**URL elicitation required**: **UNSTABLE** - -This capability is not part of the spec yet, and may be removed or changed at any point. - -The agent requires user input via a URL-based elicitation before it can proceed. - - - Other undefined error code. diff --git a/docs/rfds/elicitation.mdx b/docs/rfds/elicitation.mdx index 78d722d9e..8733f2ec3 100644 --- a/docs/rfds/elicitation.mdx +++ b/docs/rfds/elicitation.mdx @@ -196,7 +196,6 @@ This distinction is reflected in the client capabilities model, allowing clients - Clients declaring the `elicitation` capability MUST support at least one mode (`form` or `url`). - Agents MUST NOT send elicitation requests with modes that are not supported by the client. - For URL mode, the `url` parameter MUST contain a valid URL. -- Agents MUST NOT return the `URLElicitationRequiredError` (code `-32042`) except when URL mode elicitation is required. Unknown elicitation `mode` values are reserved for future ACP variants or implementation-specific extensions. Implementation-specific modes MUST begin with `_`. Clients that do not understand a mode should preserve the raw payload when storing, replaying, proxying, or forwarding elicitation requests, but MUST NOT render it as `form` or `url` or otherwise treat it as supported. @@ -537,34 +536,6 @@ sequenceDiagram Note over Agent: Continue processing with new information ``` -#### URL Mode With Elicitation Required Error Flow - -```mermaid -sequenceDiagram - participant UserAgent as User Agent (Browser) - participant User - participant Client - participant Agent - - Client->>Agent: Request (e.g., tool call) - - Note over Agent: Agent needs authorization - Agent->>Client: URLElicitationRequiredError - Note over Client: Client notes the original request can be retried after elicitation - - Client->>User: Present consent to open URL - User-->>Client: Provide consent - - Client->>UserAgent: Open URL - - Note over User,UserAgent: User interaction - - UserAgent-->>Agent: Interaction complete - Agent-->>Client: elicitation/complete (optional) - - Client->>Agent: Retry original request (optional) -``` - ### Completion Notifications for URL Mode Following MCP, agents MAY send an `elicitation/complete` notification when an out-of-band interaction started by URL mode elicitation is completed: @@ -590,39 +561,8 @@ Clients: - MAY use this notification to automatically retry requests, update UI, or continue an interaction - SHOULD provide manual controls for the user to retry or cancel if the notification never arrives -### URL Elicitation Required Error - -When a request cannot be processed until a URL mode elicitation is completed, the agent MAY return a `URLElicitationRequiredError` (code `-32042`). This allows clients to understand that a specific elicitation is required before retrying the original request. - -```json -{ - "jsonrpc": "2.0", - "id": 2, - "error": { - "code": -32042, - "message": "This request requires authorization.", - "data": { - "elicitations": [ - { - "mode": "url", - "elicitationId": "github-oauth-001", - "url": "https://agent.example.com/connect?elicitationId=github-oauth-001", - "message": "Authorization is required to access your GitHub repositories." - } - ] - } - } -} -``` - -Any elicitations returned in the error MUST be URL mode elicitations with an `elicitationId`. Clients may automatically retry the failed request after receiving a completion notification. - ### Error Handling -Agents MUST return standard JSON-RPC errors for common failure cases: - -- When a request cannot be processed until a URL mode elicitation is completed: `-32042` (`URLElicitationRequiredError`) - Clients MUST return standard JSON-RPC errors for common failure cases: - When the agent sends an `elicitation/create` request with a mode not declared in client capabilities: `-32602` (Invalid params) @@ -849,7 +789,8 @@ For v1, we recommend starting with JSON Schema validation only. If more complex ## Revision history -- 2026-02-06: Spec alignment review. Fixed OAuth URL examples to use agent connect endpoints (not direct OAuth provider URLs) per MCP phishing prevention guidance. Added normative requirements section (MUST support at least one mode, MUST NOT send unsupported modes, url MUST be valid). Added Error Handling section with `-32042` and `-32602` error codes. Added message flow diagrams (form mode, URL mode, URL mode with error). Expanded safe URL handling requirements (pre-fetch prohibition, Punycode warnings, non-clickable URLs in form fields). Added server-side schema validation SHOULD requirement. Added Statefulness subsection with normative requirements for state association and user identification. -- 2026-02-05: Major revision to align with MCP draft elicitation specification. Updated enum schema to use `oneOf`/`anyOf` with `const`/`title` instead of `enumNames`. Added multi-select array support. Added `pattern` field for strings. Added URLElicitationRequiredError (-32042) section. Added completion notifications section. Expanded security considerations including phishing prevention. Updated all examples to match MCP draft spec format. +- 2026-07-01: Removed the separate URL-required error flow; URL elicitations use `elicitation/create` and optional `elicitation/complete`. +- 2026-02-06: Spec alignment review. Fixed OAuth URL examples to use agent connect endpoints (not direct OAuth provider URLs) per MCP phishing prevention guidance. Added normative requirements section (MUST support at least one mode, MUST NOT send unsupported modes, url MUST be valid). Added Error Handling section for unsupported elicitation modes. Added message flow diagrams (form mode and URL mode). Expanded safe URL handling requirements (pre-fetch prohibition, Punycode warnings, non-clickable URLs in form fields). Added server-side schema validation SHOULD requirement. Added Statefulness subsection with normative requirements for state association and user identification. +- 2026-02-05: Major revision to align with MCP draft elicitation specification. Updated enum schema to use `oneOf`/`anyOf` with `const`/`title` instead of `enumNames`. Added multi-select array support. Added `pattern` field for strings. Added completion notifications section. Expanded security considerations including phishing prevention. Updated all examples to match MCP draft spec format. - 2026-02-05: Initial MCP alignment. Removed explicit "input types" in favor of restricted JSON Schema (client decides rendering). Added `mode` field (`form`/`url`). Updated capability model to use `form`/`url` sub-objects per MCP SEP-1036. Added three-action response model (`accept`/`decline`/`cancel`). Removed `password` type (MCP prohibits sensitive data in form mode). - 2026-01-12: Initial draft based on community discussions in PR #340 (user selection), PR #210 (session config alignment), and PR #330 (authentication use cases). Aligned with MCP elicitation patterns. diff --git a/schema/v1/schema.unstable.json b/schema/v1/schema.unstable.json index e61ea2ab1..8f55e7747 100644 --- a/schema/v1/schema.unstable.json +++ b/schema/v1/schema.unstable.json @@ -4999,13 +4999,6 @@ "format": "int32", "const": -32002 }, - { - "title": "URL elicitation required", - "description": "**URL elicitation required**: **UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nThe agent requires user input via a URL-based elicitation before it can proceed.", - "type": "integer", - "format": "int32", - "const": -32042 - }, { "title": "Other", "description": "Other undefined error code.", diff --git a/schema/v2/schema.unstable.json b/schema/v2/schema.unstable.json index db451dcdd..acc20fd18 100644 --- a/schema/v2/schema.unstable.json +++ b/schema/v2/schema.unstable.json @@ -5246,13 +5246,6 @@ "format": "int32", "const": -32002 }, - { - "title": "URL elicitation required", - "description": "**URL elicitation required**: **UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nThe agent requires user input via a URL-based elicitation before it can proceed.", - "type": "integer", - "format": "int32", - "const": -32042 - }, { "title": "Other", "description": "Other undefined error code.",