From c01d1522fc679eb61a101ee802dbc42c4da0eb5c Mon Sep 17 00:00:00 2001 From: Ben Brandt Date: Wed, 1 Jul 2026 09:59:00 +0200 Subject: [PATCH 1/3] feat(unstable-v2): Rename protocol ID fields for clarity --- agent-client-protocol-schema/src/v1/agent.rs | 10 +- agent-client-protocol-schema/src/v1/mcp.rs | 4 +- agent-client-protocol-schema/src/v2/agent.rs | 187 ++++++++++-------- agent-client-protocol-schema/src/v2/client.rs | 6 +- .../src/v2/conversion.rs | 170 +++++++++------- agent-client-protocol-schema/src/v2/mcp.rs | 6 +- agent-client-protocol-schema/src/v2/nes.rs | 113 ++++++++--- agent-client-protocol-schema/src/v2/plan.rs | 68 +++---- docs/protocol/v1/draft/schema.mdx | 18 +- docs/protocol/v2/agent-plan.mdx | 10 +- docs/protocol/v2/authentication.mdx | 6 +- docs/protocol/v2/draft/agent-plan.mdx | 16 +- docs/protocol/v2/draft/authentication.mdx | 6 +- docs/protocol/v2/draft/prompt-lifecycle.mdx | 2 +- docs/protocol/v2/draft/schema.mdx | 159 ++++++++------- .../v2/draft/session-config-options.mdx | 22 +-- docs/protocol/v2/prompt-lifecycle.mdx | 2 +- docs/protocol/v2/schema.mdx | 20 +- docs/protocol/v2/session-config-options.mdx | 16 +- schema/v1/schema.unstable.json | 8 +- schema/v2/schema.json | 24 +-- schema/v2/schema.unstable.json | 168 ++++++++++------ 22 files changed, 614 insertions(+), 427 deletions(-) diff --git a/agent-client-protocol-schema/src/v1/agent.rs b/agent-client-protocol-schema/src/v1/agent.rs index e0d838725..4ca869818 100644 --- a/agent-client-protocol-schema/src/v1/agent.rs +++ b/agent-client-protocol-schema/src/v1/agent.rs @@ -3048,7 +3048,7 @@ pub struct McpServerAcp { /// /// Providers MUST NOT reuse an ID for multiple ACP-transport MCP servers that are visible /// on the same ACP connection. - pub id: McpServerAcpId, + pub server_id: McpServerAcpId, /// The _meta property is reserved by ACP to allow clients and agents to attach additional /// metadata to their interactions. Implementations MUST NOT make assumptions about values at /// these keys. @@ -3068,7 +3068,7 @@ impl McpServerAcp { pub fn new(name: impl Into, id: impl Into) -> Self { Self { name: name.into(), - id: id.into(), + server_id: id.into(), meta: None, } } @@ -5530,7 +5530,11 @@ mod test_serialization { let deserialized: McpServer = serde_json::from_value(json).unwrap(); match deserialized { - McpServer::Acp(McpServerAcp { name, id, meta: _ }) => { + McpServer::Acp(McpServerAcp { + name, + server_id: id, + meta: _, + }) => { assert_eq!(name, "project-tools"); assert_eq!(id, McpServerAcpId::new("project-tools-id")); } diff --git a/agent-client-protocol-schema/src/v1/mcp.rs b/agent-client-protocol-schema/src/v1/mcp.rs index 0f93fe828..2906191c0 100644 --- a/agent-client-protocol-schema/src/v1/mcp.rs +++ b/agent-client-protocol-schema/src/v1/mcp.rs @@ -44,7 +44,7 @@ impl McpConnectionId { #[non_exhaustive] pub struct ConnectMcpRequest { /// The ACP MCP server ID that was provided by the component declaring the MCP server. - pub acp_id: McpServerAcpId, + pub server_id: McpServerAcpId, /// The _meta property is reserved by ACP to allow clients and agents to attach additional /// metadata to their interactions. Implementations MUST NOT make assumptions about values at /// these keys. @@ -62,7 +62,7 @@ impl ConnectMcpRequest { #[must_use] pub fn new(acp_id: impl Into) -> Self { Self { - acp_id: acp_id.into(), + server_id: acp_id.into(), meta: None, } } diff --git a/agent-client-protocol-schema/src/v2/agent.rs b/agent-client-protocol-schema/src/v2/agent.rs index 09fb99eae..268324f0b 100644 --- a/agent-client-protocol-schema/src/v2/agent.rs +++ b/agent-client-protocol-schema/src/v2/agent.rs @@ -520,14 +520,14 @@ pub enum AuthMethod { impl AuthMethod { /// The unique identifier for this authentication method. #[must_use] - pub fn id(&self) -> &AuthMethodId { + pub fn method_id(&self) -> &AuthMethodId { match self { - Self::Agent(a) => &a.id, - Self::Other(a) => &a.id, + Self::Agent(a) => &a.method_id, + Self::Other(a) => &a.method_id, #[cfg(feature = "unstable_auth_methods")] - Self::EnvVar(e) => &e.id, + Self::EnvVar(e) => &e.method_id, #[cfg(feature = "unstable_auth_methods")] - Self::Terminal(t) => &t.id, + Self::Terminal(t) => &t.method_id, } } @@ -592,7 +592,7 @@ pub struct OtherAuthMethod { #[serde(rename = "type")] pub type_: String, /// Unique identifier for this authentication method. - pub id: AuthMethodId, + pub method_id: AuthMethodId, /// Human-readable name of the authentication method. pub name: String, /// Optional description providing more details about this authentication method. @@ -620,18 +620,18 @@ impl OtherAuthMethod { #[must_use] pub fn new( type_: impl Into, - id: impl Into, + method_id: impl Into, name: impl Into, mut fields: BTreeMap, ) -> Self { fields.remove("type"); - fields.remove("id"); + fields.remove("methodId"); fields.remove("name"); fields.remove("description"); fields.remove("_meta"); Self { type_: type_.into(), - id: id.into(), + method_id: method_id.into(), name: name.into(), description: None, meta: None, @@ -668,7 +668,7 @@ impl<'de> Deserialize<'de> for OtherAuthMethod { struct RawOtherAuthMethod { #[serde(rename = "type")] type_: String, - id: AuthMethodId, + method_id: AuthMethodId, name: String, description: Option, #[serde(rename = "_meta")] @@ -687,7 +687,7 @@ impl<'de> Deserialize<'de> for OtherAuthMethod { Ok(Self { type_: raw.type_, - id: raw.id, + method_id: raw.method_id, name: raw.name, description: raw.description, meta: raw.meta, @@ -729,7 +729,7 @@ fn other_auth_method_schema(schema: &mut Schema) { #[non_exhaustive] pub struct AuthMethodAgent { /// Unique identifier for this authentication method. - pub id: AuthMethodId, + pub method_id: AuthMethodId, /// Human-readable name of the authentication method. pub name: String, /// Optional description providing more details about this authentication method. @@ -752,9 +752,9 @@ pub struct AuthMethodAgent { impl AuthMethodAgent { /// Builds [`AuthMethodAgent`] with the required fields set; optional fields start unset or empty. #[must_use] - pub fn new(id: impl Into, name: impl Into) -> Self { + pub fn new(method_id: impl Into, name: impl Into) -> Self { Self { - id: id.into(), + method_id: method_id.into(), name: name.into(), description: None, meta: None, @@ -795,7 +795,7 @@ impl AuthMethodAgent { #[non_exhaustive] pub struct AuthMethodEnvVar { /// Unique identifier for this authentication method. - pub id: AuthMethodId, + pub method_id: AuthMethodId, /// Human-readable name of the authentication method. pub name: String, /// Optional description providing more details about this authentication method. @@ -829,12 +829,12 @@ impl AuthMethodEnvVar { /// Builds [`AuthMethodEnvVar`] with the required fields set; optional fields start unset or empty. #[must_use] pub fn new( - id: impl Into, + method_id: impl Into, name: impl Into, vars: Vec, ) -> Self { Self { - id: id.into(), + method_id: method_id.into(), name: name.into(), description: None, vars, @@ -997,7 +997,7 @@ impl AuthEnvVar { #[non_exhaustive] pub struct AuthMethodTerminal { /// Unique identifier for this authentication method. - pub id: AuthMethodId, + pub method_id: AuthMethodId, /// Human-readable name of the authentication method. pub name: String, /// Optional description providing more details about this authentication method. @@ -1031,9 +1031,9 @@ pub struct AuthMethodTerminal { impl AuthMethodTerminal { /// Builds [`AuthMethodTerminal`] with the required fields set; optional fields start unset or empty. #[must_use] - pub fn new(id: impl Into, name: impl Into) -> Self { + pub fn new(method_id: impl Into, name: impl Into) -> Self { Self { - id: id.into(), + method_id: method_id.into(), name: name.into(), description: None, args: Vec::new(), @@ -2147,7 +2147,7 @@ impl SessionConfigSelectOption { #[non_exhaustive] pub struct SessionConfigSelectGroup { /// Unique identifier for this group. - pub group: SessionConfigGroupId, + pub group_id: SessionConfigGroupId, /// Human-readable label for this group. pub name: String, /// The set of option values in this group. @@ -2170,12 +2170,12 @@ impl SessionConfigSelectGroup { /// Builds [`SessionConfigSelectGroup`] with the required fields set; optional fields start unset or empty. #[must_use] pub fn new( - group: impl Into, + group_id: impl Into, name: impl Into, options: Vec, ) -> Self { Self { - group: group.into(), + group_id: group_id.into(), name: name.into(), options, meta: None, @@ -2409,7 +2409,7 @@ fn other_session_config_kind_schema(schema: &mut Schema) { #[non_exhaustive] pub struct SessionConfigOption { /// Unique identifier for the configuration option. - pub id: SessionConfigId, + pub config_id: SessionConfigId, /// Human-readable label for the option. pub name: String, /// Optional description for the Client to display to the user. @@ -2441,12 +2441,12 @@ impl SessionConfigOption { /// Builds [`SessionConfigOption`] with the required fields set; optional fields start unset or empty. #[must_use] pub fn new( - id: impl Into, + config_id: impl Into, name: impl Into, kind: SessionConfigKind, ) -> Self { Self { - id: id.into(), + config_id: config_id.into(), name: name.into(), description: None, category: None, @@ -2458,13 +2458,13 @@ impl SessionConfigOption { /// Builds a select-style session configuration option with its current value and choices. #[must_use] pub fn select( - id: impl Into, + config_id: impl Into, name: impl Into, current_value: impl Into, options: impl Into, ) -> Self { Self::new( - id, + config_id, name, SessionConfigKind::Select(SessionConfigSelect::new(current_value, options)), ) @@ -2476,12 +2476,12 @@ impl SessionConfigOption { #[cfg(feature = "unstable_boolean_config")] #[must_use] pub fn boolean( - id: impl Into, + config_id: impl Into, name: impl Into, current_value: bool, ) -> Self { Self::new( - id, + config_id, name, SessionConfigKind::Boolean(SessionConfigBoolean::new(current_value)), ) @@ -3084,7 +3084,7 @@ pub struct McpServerAcp { /// /// Providers MUST NOT reuse an ID for multiple ACP-transport MCP servers that are visible /// on the same ACP connection. - pub id: McpServerAcpId, + pub server_id: McpServerAcpId, /// The _meta property is reserved by ACP to allow clients and agents to attach additional /// metadata to their interactions. Implementations MUST NOT make assumptions about values at /// these keys. @@ -3101,10 +3101,10 @@ pub struct McpServerAcp { impl McpServerAcp { /// Builds [`McpServerAcp`] with the required fields set; optional fields start unset or empty. #[must_use] - pub fn new(name: impl Into, id: impl Into) -> Self { + pub fn new(name: impl Into, server_id: impl Into) -> Self { Self { name: name.into(), - id: id.into(), + server_id: server_id.into(), meta: None, } } @@ -3611,6 +3611,27 @@ impl ProviderCurrentConfig { } } +/// **UNSTABLE** +/// +/// This capability is not part of the spec yet, and may be removed or changed at any point. +/// +/// Unique identifier for a configurable LLM provider. +#[cfg(feature = "unstable_llm_providers")] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, Display, From)] +#[serde(transparent)] +#[from(Arc, String, &'static str)] +#[non_exhaustive] +pub struct ProviderId(pub Arc); + +#[cfg(feature = "unstable_llm_providers")] +impl ProviderId { + /// Wraps a protocol string as a typed [`ProviderId`]. + #[must_use] + pub fn new(id: impl Into>) -> Self { + Self(id.into()) + } +} + /// **UNSTABLE** /// /// This capability is not part of the spec yet, and may be removed or changed at any point. @@ -3624,13 +3645,13 @@ impl ProviderCurrentConfig { #[non_exhaustive] pub struct ProviderInfo { /// Provider identifier, for example "main" or "openai". - pub id: String, + pub provider_id: ProviderId, /// Supported protocol types for this provider. #[serde_as(deserialize_as = "DefaultOnError>")] #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))] pub supported: Vec, /// Whether this provider is mandatory and cannot be disabled via `providers/disable`. - /// If true, clients must not call `providers/disable` for this id. + /// If true, clients must not call `providers/disable` for this provider ID. pub required: bool, /// Current effective non-secret routing config. /// Null or omitted means provider is disabled. @@ -3655,13 +3676,13 @@ impl ProviderInfo { /// Builds [`ProviderInfo`] with the required fields set; optional fields start unset or empty. #[must_use] pub fn new( - id: impl Into, + provider_id: impl Into, supported: Vec, required: bool, current: impl IntoOption, ) -> Self { Self { - id: id.into(), + provider_id: provider_id.into(), supported, required, current: current.into_option(), @@ -3784,7 +3805,7 @@ impl ListProvidersResponse { /// /// Request parameters for `providers/set`. /// -/// Replaces the full configuration for one provider id. +/// Replaces the full configuration for one provider ID. #[cfg(feature = "unstable_llm_providers")] #[serde_as] #[skip_serializing_none] @@ -3793,8 +3814,8 @@ impl ListProvidersResponse { #[serde(rename_all = "camelCase")] #[non_exhaustive] pub struct SetProviderRequest { - /// Provider id to configure. - pub id: String, + /// Provider ID to configure. + pub provider_id: ProviderId, /// Protocol type for this provider. pub api_type: LlmProtocol, /// Base URL for requests sent through this provider. @@ -3821,9 +3842,13 @@ pub struct SetProviderRequest { impl SetProviderRequest { /// Builds [`SetProviderRequest`] with the required request fields set; optional fields start unset or empty. #[must_use] - pub fn new(id: impl Into, api_type: LlmProtocol, base_url: impl Into) -> Self { + pub fn new( + provider_id: impl Into, + api_type: LlmProtocol, + base_url: impl Into, + ) -> Self { Self { - id: id.into(), + provider_id: provider_id.into(), api_type, base_url: base_url.into(), headers: HashMap::new(), @@ -3909,8 +3934,8 @@ impl SetProviderResponse { #[serde(rename_all = "camelCase")] #[non_exhaustive] pub struct DisableProviderRequest { - /// Provider id to disable. - pub id: String, + /// Provider ID to disable. + pub provider_id: ProviderId, /// The _meta property is reserved by ACP to allow clients and agents to attach additional /// metadata to their interactions. Implementations MUST NOT make assumptions about values at /// these keys. @@ -3927,9 +3952,9 @@ pub struct DisableProviderRequest { impl DisableProviderRequest { /// Builds [`DisableProviderRequest`] with the required request fields set; optional fields start unset or empty. #[must_use] - pub fn new(id: impl Into) -> Self { + pub fn new(provider_id: impl Into) -> Self { Self { - id: id.into(), + provider_id: provider_id.into(), meta: None, } } @@ -6091,7 +6116,7 @@ mod test_serialization { assert_eq!( json, json!({ - "id": "default-auth", + "methodId": "default-auth", "name": "Default Auth", "type": "agent" }) @@ -6101,8 +6126,10 @@ mod test_serialization { let deserialized: AuthMethod = serde_json::from_value(json).unwrap(); match deserialized { - AuthMethod::Agent(AuthMethodAgent { id, name, .. }) => { - assert_eq!(id.0.as_ref(), "default-auth"); + AuthMethod::Agent(AuthMethodAgent { + method_id, name, .. + }) => { + assert_eq!(method_id.0.as_ref(), "default-auth"); assert_eq!(name, "Default Auth"); } _ => panic!("Expected Agent variant"), @@ -6112,7 +6139,7 @@ mod test_serialization { #[test] fn test_auth_method_agent_deserialization() { let json = json!({ - "id": "agent-auth", + "methodId": "agent-auth", "name": "Agent Auth", "type": "agent" }); @@ -6125,7 +6152,7 @@ mod test_serialization { fn test_auth_method_agent_requires_type() { assert!( serde_json::from_value::(json!({ - "id": "agent-auth", + "methodId": "agent-auth", "name": "Agent Auth" })) .is_err() @@ -6136,7 +6163,7 @@ mod test_serialization { fn test_auth_method_agent_rejects_null_type() { assert!( serde_json::from_value::(json!({ - "id": "agent-auth", + "methodId": "agent-auth", "name": "Agent Auth", "type": null })) @@ -6148,7 +6175,7 @@ mod test_serialization { fn test_auth_method_unknown_does_not_hide_malformed_agent() { assert!( serde_json::from_value::(json!({ - "id": "agent-auth", + "methodId": "agent-auth", "type": "agent" })) .is_err() @@ -6158,14 +6185,14 @@ mod test_serialization { #[test] fn test_auth_method_unknown_variant_roundtrip() { let method: AuthMethod = serde_json::from_value(json!({ - "id": "oauth", + "methodId": "oauth", "name": "OAuth", "type": "_oauth", "authorizationUrl": "https://example.com/auth" })) .unwrap(); - assert_eq!(method.id().0.as_ref(), "oauth"); + assert_eq!(method.method_id().0.as_ref(), "oauth"); assert_eq!(method.name(), "OAuth"); let AuthMethod::Other(unknown) = method else { panic!("expected unknown auth method"); @@ -6179,7 +6206,7 @@ mod test_serialization { assert_eq!( serde_json::to_value(AuthMethod::Other(unknown)).unwrap(), json!({ - "id": "oauth", + "methodId": "oauth", "name": "OAuth", "type": "_oauth", "authorizationUrl": "https://example.com/auth" @@ -6192,7 +6219,7 @@ mod test_serialization { fn test_auth_method_unknown_does_not_hide_malformed_known_variant() { assert!( serde_json::from_value::(json!({ - "id": "api-key", + "methodId": "api-key", "name": "API Key", "type": "env_var" })) @@ -6351,7 +6378,7 @@ mod test_serialization { assert_eq!( json, json!({ - "id": "api-key", + "methodId": "api-key", "name": "API Key", "type": "env_var", "vars": [{"name": "API_KEY"}] @@ -6369,13 +6396,13 @@ mod test_serialization { let deserialized: AuthMethod = serde_json::from_value(json).unwrap(); match deserialized { AuthMethod::EnvVar(AuthMethodEnvVar { - id, + method_id, name: method_name, vars, link, .. }) => { - assert_eq!(id.0.as_ref(), "api-key"); + assert_eq!(method_id.0.as_ref(), "api-key"); assert_eq!(method_name, "API Key"); assert_eq!(vars.len(), 1); assert_eq!(vars[0].name, "API_KEY"); @@ -6399,7 +6426,7 @@ mod test_serialization { assert_eq!( json, json!({ - "id": "api-key", + "methodId": "api-key", "name": "API Key", "type": "env_var", "vars": [{"name": "API_KEY"}], @@ -6438,7 +6465,7 @@ mod test_serialization { assert_eq!( json, json!({ - "id": "azure-openai", + "methodId": "azure-openai", "name": "Azure OpenAI", "type": "env_var", "vars": [ @@ -6480,7 +6507,7 @@ mod test_serialization { assert_eq!( json, json!({ - "id": "tui-auth", + "methodId": "tui-auth", "name": "Terminal Auth", "type": "terminal" }) @@ -6512,7 +6539,7 @@ mod test_serialization { assert_eq!( json, json!({ - "id": "tui-auth", + "methodId": "tui-auth", "name": "Terminal Auth", "type": "terminal", "args": ["--interactive", "--color"], @@ -6772,7 +6799,7 @@ mod test_serialization { assert_eq!( json, json!({ - "id": "brave_mode", + "configId": "brave_mode", "name": "Brave Mode", "description": "Skip confirmation prompts", "type": "boolean", @@ -6784,7 +6811,7 @@ mod test_serialization { ); let deserialized: SessionConfigOption = serde_json::from_value(json).unwrap(); - assert_eq!(deserialized.id.to_string(), "brave_mode"); + assert_eq!(deserialized.config_id.to_string(), "brave_mode"); assert_eq!(deserialized.name, "Brave Mode"); match deserialized.kind { SessionConfigKind::Boolean(ref b) => assert!(!b.current_value), @@ -6826,7 +6853,7 @@ mod test_serialization { #[test] fn test_session_config_option_unknown_kind_roundtrip() { let option: SessionConfigOption = serde_json::from_value(json!({ - "id": "verbosity", + "configId": "verbosity", "name": "Verbosity", "type": "_slider", "currentValue": 3, @@ -6838,7 +6865,7 @@ mod test_serialization { })) .unwrap(); - assert_eq!(option.id.to_string(), "verbosity"); + assert_eq!(option.config_id.to_string(), "verbosity"); assert_eq!(option.meta.as_ref().unwrap()["source"], "test"); let SessionConfigKind::Other(unknown) = &option.kind else { panic!("expected unknown config kind"); @@ -6885,7 +6912,7 @@ mod test_serialization { fn test_session_config_option_unknown_does_not_hide_malformed_known_kind() { assert!( serde_json::from_value::(json!({ - "id": "model", + "configId": "model", "name": "Model", "type": "select" })) @@ -6986,7 +7013,7 @@ mod test_serialization { assert_eq!( json, json!({ - "id": "main", + "providerId": "main", "supported": ["anthropic", "openai"], "required": true, "current": { @@ -6997,7 +7024,7 @@ mod test_serialization { ); let deserialized: ProviderInfo = serde_json::from_value(json).unwrap(); - assert_eq!(deserialized.id, "main"); + assert_eq!(deserialized.provider_id.to_string(), "main"); assert_eq!(deserialized.supported.len(), 2); assert!(deserialized.required); assert!(deserialized.current.is_some()); @@ -7021,14 +7048,14 @@ mod test_serialization { assert_eq!( json, json!({ - "id": "secondary", + "providerId": "secondary", "supported": ["openai"], "required": false }) ); let deserialized: ProviderInfo = serde_json::from_value(json).unwrap(); - assert_eq!(deserialized.id, "secondary"); + assert_eq!(deserialized.provider_id.to_string(), "secondary"); assert!(!deserialized.required); assert!(deserialized.current.is_none()); } @@ -7038,7 +7065,7 @@ mod test_serialization { fn test_provider_info_missing_current_defaults_to_none() { // current is optional; omitting it should decode as None let json = json!({ - "id": "main", + "providerId": "main", "supported": ["anthropic"], "required": true }); @@ -7053,7 +7080,7 @@ mod test_serialization { // both must deserialize into None so the disabled state is preserved // regardless of which form the peer chose to send. let json = json!({ - "id": "main", + "providerId": "main", "supported": ["anthropic"], "required": true, "current": null @@ -7077,7 +7104,7 @@ mod test_serialization { let json = serde_json::to_value(&response).unwrap(); assert_eq!(json["providers"].as_array().unwrap().len(), 1); - assert_eq!(json["providers"][0]["id"], "main"); + assert_eq!(json["providers"][0]["providerId"], "main"); let deserialized: ListProvidersResponse = serde_json::from_value(json).unwrap(); assert_eq!(deserialized.providers.len(), 1); @@ -7099,7 +7126,7 @@ mod test_serialization { assert_eq!( json, json!({ - "id": "main", + "providerId": "main", "apiType": "openai", "baseUrl": "https://api.openai.com/v1", "headers": { @@ -7109,7 +7136,7 @@ mod test_serialization { ); let deserialized: SetProviderRequest = serde_json::from_value(json).unwrap(); - assert_eq!(deserialized.id, "main"); + assert_eq!(deserialized.provider_id.to_string(), "main"); assert_eq!(deserialized.api_type, LlmProtocol::OpenAi); assert_eq!(deserialized.base_url, "https://api.openai.com/v1"); assert_eq!(deserialized.headers.len(), 1); @@ -7136,10 +7163,10 @@ mod test_serialization { let request = DisableProviderRequest::new("secondary"); let json = serde_json::to_value(&request).unwrap(); - assert_eq!(json, json!({ "id": "secondary" })); + assert_eq!(json, json!({ "providerId": "secondary" })); let deserialized: DisableProviderRequest = serde_json::from_value(json).unwrap(); - assert_eq!(deserialized.id, "secondary"); + assert_eq!(deserialized.provider_id.to_string(), "secondary"); } #[cfg(feature = "unstable_llm_providers")] diff --git a/agent-client-protocol-schema/src/v2/client.rs b/agent-client-protocol-schema/src/v2/client.rs index d951ea01c..d8080a894 100644 --- a/agent-client-protocol-schema/src/v2/client.rs +++ b/agent-client-protocol-schema/src/v2/client.rs @@ -2447,7 +2447,7 @@ mod tests { "sessionUpdate": "plan_update", "plan": { "type": "items", - "id": "plan-1", + "planId": "plan-1", "entries": [ { "content": "Step 1", @@ -2469,7 +2469,7 @@ mod tests { serde_json::to_value(SessionUpdate::PlanRemoved(PlanRemoved::new("plan-1"))).unwrap(), json!({ "sessionUpdate": "plan_removed", - "id": "plan-1" + "planId": "plan-1" }) ); } @@ -2573,7 +2573,7 @@ mod tests { assert_eq!( serde_json::to_value(ConnectMcpRequest::new("server-1")).unwrap(), - json!({ "acpId": "server-1" }) + json!({ "mcpServerId": "server-1" }) ); assert_eq!( serde_json::to_value(ConnectMcpResponse::new("conn-1")).unwrap(), diff --git a/agent-client-protocol-schema/src/v2/conversion.rs b/agent-client-protocol-schema/src/v2/conversion.rs index 20a9df7df..ef68371f5 100644 --- a/agent-client-protocol-schema/src/v2/conversion.rs +++ b/agent-client-protocol-schema/src/v2/conversion.rs @@ -615,7 +615,7 @@ impl IntoV1 for super::PlanUpdate { Ok(match plan { super::PlanUpdateContent::Items(items) => { let super::PlanItems { - id: _, + plan_id: _, entries, meta: items_meta, } = items; @@ -709,9 +709,13 @@ impl IntoV1 for super::PlanItems { type Output = crate::v1::PlanItems; fn into_v1(self) -> Result { - let Self { id, entries, meta } = self; + let Self { + plan_id, + entries, + meta, + } = self; Ok(crate::v1::PlanItems { - id: id.into_v1()?, + id: plan_id.into_v1()?, entries: into_v1_vec_skip_errors(entries), meta: meta.into_v1()?, }) @@ -725,7 +729,7 @@ impl IntoV2 for crate::v1::PlanItems { fn into_v2(self) -> Result { let Self { id, entries, meta } = self; Ok(super::PlanItems { - id: id.into_v2()?, + plan_id: id.into_v2()?, entries: into_v2_vec_skip_errors(entries), meta: meta.into_v2()?, }) @@ -737,9 +741,9 @@ impl IntoV1 for super::PlanFile { type Output = crate::v1::PlanFile; fn into_v1(self) -> Result { - let Self { id, uri, meta } = self; + let Self { plan_id, uri, meta } = self; Ok(crate::v1::PlanFile { - id: id.into_v1()?, + id: plan_id.into_v1()?, uri: uri.into_v1()?, meta: meta.into_v1()?, }) @@ -753,7 +757,7 @@ impl IntoV2 for crate::v1::PlanFile { fn into_v2(self) -> Result { let Self { id, uri, meta } = self; Ok(super::PlanFile { - id: id.into_v2()?, + plan_id: id.into_v2()?, uri: uri.into_v2()?, meta: meta.into_v2()?, }) @@ -765,9 +769,13 @@ impl IntoV1 for super::PlanMarkdown { type Output = crate::v1::PlanMarkdown; fn into_v1(self) -> Result { - let Self { id, content, meta } = self; + let Self { + plan_id, + content, + meta, + } = self; Ok(crate::v1::PlanMarkdown { - id: id.into_v1()?, + id: plan_id.into_v1()?, content: content.into_v1()?, meta: meta.into_v1()?, }) @@ -781,7 +789,7 @@ impl IntoV2 for crate::v1::PlanMarkdown { fn into_v2(self) -> Result { let Self { id, content, meta } = self; Ok(super::PlanMarkdown { - id: id.into_v2()?, + plan_id: id.into_v2()?, content: content.into_v2()?, meta: meta.into_v2()?, }) @@ -793,9 +801,9 @@ impl IntoV1 for super::PlanRemoved { type Output = crate::v1::PlanRemoved; fn into_v1(self) -> Result { - let Self { id, meta } = self; + let Self { plan_id, meta } = self; Ok(crate::v1::PlanRemoved { - id: id.into_v1()?, + id: plan_id.into_v1()?, meta: meta.into_v1()?, }) } @@ -808,7 +816,7 @@ impl IntoV2 for crate::v1::PlanRemoved { fn into_v2(self) -> Result { let Self { id, meta } = self; Ok(super::PlanRemoved { - id: id.into_v2()?, + plan_id: id.into_v2()?, meta: meta.into_v2()?, }) } @@ -1673,9 +1681,9 @@ impl IntoV1 for super::ConnectMcpRequest { type Output = crate::v1::ConnectMcpRequest; fn into_v1(self) -> Result { - let Self { acp_id, meta } = self; + let Self { server_id, meta } = self; Ok(crate::v1::ConnectMcpRequest { - acp_id: acp_id.into_v1()?, + server_id: server_id.into_v1()?, meta: meta.into_v1()?, }) } @@ -1686,9 +1694,9 @@ impl IntoV2 for crate::v1::ConnectMcpRequest { type Output = super::ConnectMcpRequest; fn into_v2(self) -> Result { - let Self { acp_id, meta } = self; + let Self { server_id, meta } = self; Ok(super::ConnectMcpRequest { - acp_id: acp_id.into_v2()?, + server_id: server_id.into_v2()?, meta: meta.into_v2()?, }) } @@ -2994,13 +3002,13 @@ impl IntoV1 for super::AuthMethodAgent { fn into_v1(self) -> Result { let Self { - id, + method_id, name, description, meta, } = self; Ok(crate::v1::AuthMethodAgent { - id: id.into_v1()?, + id: method_id.into_v1()?, name: name.into_v1()?, description: description.into_v1()?, meta: meta.into_v1()?, @@ -3019,7 +3027,7 @@ impl IntoV2 for crate::v1::AuthMethodAgent { meta, } = self; Ok(super::AuthMethodAgent { - id: id.into_v2()?, + method_id: id.into_v2()?, name: name.into_v2()?, description: description.into_v2()?, meta: meta.into_v2()?, @@ -3033,7 +3041,7 @@ impl IntoV1 for super::AuthMethodEnvVar { fn into_v1(self) -> Result { let Self { - id, + method_id, name, description, vars, @@ -3041,7 +3049,7 @@ impl IntoV1 for super::AuthMethodEnvVar { meta, } = self; Ok(crate::v1::AuthMethodEnvVar { - id: id.into_v1()?, + id: method_id.into_v1()?, name: name.into_v1()?, description: description.into_v1()?, vars: vars.into_v1()?, @@ -3065,7 +3073,7 @@ impl IntoV2 for crate::v1::AuthMethodEnvVar { meta, } = self; Ok(super::AuthMethodEnvVar { - id: id.into_v2()?, + method_id: id.into_v2()?, name: name.into_v2()?, description: description.into_v2()?, vars: vars.into_v2()?, @@ -3125,7 +3133,7 @@ impl IntoV1 for super::AuthMethodTerminal { fn into_v1(self) -> Result { let Self { - id, + method_id, name, description, args, @@ -3153,7 +3161,7 @@ impl IntoV1 for super::AuthMethodTerminal { Ok(env) })?; Ok(crate::v1::AuthMethodTerminal { - id: id.into_v1()?, + id: method_id.into_v1()?, name: name.into_v1()?, description: description.into_v1()?, args: args.into_v1()?, @@ -3182,7 +3190,7 @@ impl IntoV2 for crate::v1::AuthMethodTerminal { .collect::>>()?; env.sort_by(|left, right| left.name.cmp(&right.name)); Ok(super::AuthMethodTerminal { - id: id.into_v2()?, + method_id: id.into_v2()?, name: name.into_v2()?, description: description.into_v2()?, args: args.into_v2()?, @@ -3785,13 +3793,13 @@ impl IntoV1 for super::SessionConfigSelectGroup { fn into_v1(self) -> Result { let Self { - group, + group_id, name, options, meta, } = self; Ok(crate::v1::SessionConfigSelectGroup { - group: group.into_v1()?, + group: group_id.into_v1()?, name: name.into_v1()?, options: options.into_v1()?, meta: meta.into_v1()?, @@ -3810,7 +3818,7 @@ impl IntoV2 for crate::v1::SessionConfigSelectGroup { meta, } = self; Ok(super::SessionConfigSelectGroup { - group: group.into_v2()?, + group_id: group.into_v2()?, name: name.into_v2()?, options: options.into_v2()?, meta: meta.into_v2()?, @@ -3960,7 +3968,7 @@ impl IntoV1 for super::SessionConfigOption { fn into_v1(self) -> Result { let Self { - id, + config_id, name, description, category, @@ -3968,7 +3976,7 @@ impl IntoV1 for super::SessionConfigOption { meta, } = self; Ok(crate::v1::SessionConfigOption { - id: id.into_v1()?, + id: config_id.into_v1()?, name: name.into_v1()?, description: description.into_v1()?, category: into_v1_default_on_error(category), @@ -3991,7 +3999,7 @@ impl IntoV2 for crate::v1::SessionConfigOption { meta, } = self; Ok(super::SessionConfigOption { - id: id.into_v2()?, + config_id: id.into_v2()?, name: name.into_v2()?, description: description.into_v2()?, category: into_v2_default_on_error(category), @@ -4214,10 +4222,14 @@ impl IntoV1 for super::McpServerAcp { type Output = crate::v1::McpServerAcp; fn into_v1(self) -> Result { - let Self { name, id, meta } = self; + let Self { + name, + server_id, + meta, + } = self; Ok(crate::v1::McpServerAcp { name: name.into_v1()?, - id: id.into_v1()?, + server_id: server_id.into_v1()?, meta: meta.into_v1()?, }) } @@ -4228,10 +4240,14 @@ impl IntoV2 for crate::v1::McpServerAcp { type Output = super::McpServerAcp; fn into_v2(self) -> Result { - let Self { name, id, meta } = self; + let Self { + name, + server_id, + meta, + } = self; Ok(super::McpServerAcp { name: name.into_v2()?, - id: id.into_v2()?, + server_id: server_id.into_v2()?, meta: meta.into_v2()?, }) } @@ -4534,20 +4550,29 @@ impl IntoV2 for crate::v1::ProviderCurrentConfig { } } +#[cfg(feature = "unstable_llm_providers")] +impl IntoV1 for super::ProviderId { + type Output = String; + + fn into_v1(self) -> Result { + Ok(self.0.to_string()) + } +} + #[cfg(feature = "unstable_llm_providers")] impl IntoV1 for super::ProviderInfo { type Output = crate::v1::ProviderInfo; fn into_v1(self) -> Result { let Self { - id, + provider_id, supported, required, current, meta, } = self; Ok(crate::v1::ProviderInfo { - id: id.into_v1()?, + id: provider_id.into_v1()?, supported: into_v1_vec_skip_errors(supported), required: required.into_v1()?, current: current.into_v1()?, @@ -4569,7 +4594,7 @@ impl IntoV2 for crate::v1::ProviderInfo { meta, } = self; Ok(super::ProviderInfo { - id: id.into_v2()?, + provider_id: super::ProviderId::new(id.into_v2()?), supported: into_v2_vec_skip_errors(supported), required: required.into_v2()?, current: current.into_v2()?, @@ -4634,14 +4659,14 @@ impl IntoV1 for super::SetProviderRequest { fn into_v1(self) -> Result { let Self { - id, + provider_id, api_type, base_url, headers, meta, } = self; Ok(crate::v1::SetProviderRequest { - id: id.into_v1()?, + id: provider_id.into_v1()?, api_type: api_type.into_v1()?, base_url: base_url.into_v1()?, headers: headers.into_v1()?, @@ -4663,7 +4688,7 @@ impl IntoV2 for crate::v1::SetProviderRequest { meta, } = self; Ok(super::SetProviderRequest { - id: id.into_v2()?, + provider_id: super::ProviderId::new(id.into_v2()?), api_type: api_type.into_v2()?, base_url: base_url.into_v2()?, headers: headers.into_v2()?, @@ -4701,9 +4726,9 @@ impl IntoV1 for super::DisableProviderRequest { type Output = crate::v1::DisableProviderRequest; fn into_v1(self) -> Result { - let Self { id, meta } = self; + let Self { provider_id, meta } = self; Ok(crate::v1::DisableProviderRequest { - id: id.into_v1()?, + id: provider_id.into_v1()?, meta: meta.into_v1()?, }) } @@ -4716,7 +4741,7 @@ impl IntoV2 for crate::v1::DisableProviderRequest { fn into_v2(self) -> Result { let Self { id, meta } = self; Ok(super::DisableProviderRequest { - id: id.into_v2()?, + provider_id: super::ProviderId::new(id.into_v2()?), meta: meta.into_v2()?, }) } @@ -7177,20 +7202,29 @@ impl IntoV2 for crate::v1::NesSuggestion { } } +#[cfg(feature = "unstable_nes")] +impl IntoV1 for super::NesSuggestionId { + type Output = String; + + fn into_v1(self) -> Result { + Ok(self.0.to_string()) + } +} + #[cfg(feature = "unstable_nes")] impl IntoV1 for super::NesEditSuggestion { type Output = crate::v1::NesEditSuggestion; fn into_v1(self) -> Result { let Self { - id, + suggestion_id, uri, edits, cursor_position, meta, } = self; Ok(crate::v1::NesEditSuggestion { - id: id.into_v1()?, + id: suggestion_id.into_v1()?, uri: uri.into_v1()?, edits: edits.into_v1()?, cursor_position: into_v1_default_on_error(cursor_position), @@ -7212,7 +7246,7 @@ impl IntoV2 for crate::v1::NesEditSuggestion { meta, } = self; Ok(super::NesEditSuggestion { - id: id.into_v2()?, + suggestion_id: super::NesSuggestionId::new(id.into_v2()?), uri: uri.into_v2()?, edits: edits.into_v2()?, cursor_position: into_v2_default_on_error(cursor_position), @@ -7263,13 +7297,13 @@ impl IntoV1 for super::NesJumpSuggestion { fn into_v1(self) -> Result { let Self { - id, + suggestion_id, uri, position, meta, } = self; Ok(crate::v1::NesJumpSuggestion { - id: id.into_v1()?, + id: suggestion_id.into_v1()?, uri: uri.into_v1()?, position: position.into_v1()?, meta: meta.into_v1()?, @@ -7289,7 +7323,7 @@ impl IntoV2 for crate::v1::NesJumpSuggestion { meta, } = self; Ok(super::NesJumpSuggestion { - id: id.into_v2()?, + suggestion_id: super::NesSuggestionId::new(id.into_v2()?), uri: uri.into_v2()?, position: position.into_v2()?, meta: meta.into_v2()?, @@ -7303,14 +7337,14 @@ impl IntoV1 for super::NesRenameSuggestion { fn into_v1(self) -> Result { let Self { - id, + suggestion_id, uri, position, new_name, meta, } = self; Ok(crate::v1::NesRenameSuggestion { - id: id.into_v1()?, + id: suggestion_id.into_v1()?, uri: uri.into_v1()?, position: position.into_v1()?, new_name: new_name.into_v1()?, @@ -7332,7 +7366,7 @@ impl IntoV2 for crate::v1::NesRenameSuggestion { meta, } = self; Ok(super::NesRenameSuggestion { - id: id.into_v2()?, + suggestion_id: super::NesSuggestionId::new(id.into_v2()?), uri: uri.into_v2()?, position: position.into_v2()?, new_name: new_name.into_v2()?, @@ -7347,7 +7381,7 @@ impl IntoV1 for super::NesSearchAndReplaceSuggestion { fn into_v1(self) -> Result { let Self { - id, + suggestion_id, uri, search, replace, @@ -7355,7 +7389,7 @@ impl IntoV1 for super::NesSearchAndReplaceSuggestion { meta, } = self; Ok(crate::v1::NesSearchAndReplaceSuggestion { - id: id.into_v1()?, + id: suggestion_id.into_v1()?, uri: uri.into_v1()?, search: search.into_v1()?, replace: replace.into_v1()?, @@ -7379,7 +7413,7 @@ impl IntoV2 for crate::v1::NesSearchAndReplaceSuggestion { meta, } = self; Ok(super::NesSearchAndReplaceSuggestion { - id: id.into_v2()?, + suggestion_id: super::NesSuggestionId::new(id.into_v2()?), uri: uri.into_v2()?, search: search.into_v2()?, replace: replace.into_v2()?, @@ -7396,12 +7430,12 @@ impl IntoV1 for super::AcceptNesNotification { fn into_v1(self) -> Result { let Self { session_id, - id, + suggestion_id, meta, } = self; Ok(crate::v1::AcceptNesNotification { session_id: session_id.into_v1()?, - id: id.into_v1()?, + id: suggestion_id.into_v1()?, meta: meta.into_v1()?, }) } @@ -7419,7 +7453,7 @@ impl IntoV2 for crate::v1::AcceptNesNotification { } = self; Ok(super::AcceptNesNotification { session_id: session_id.into_v2()?, - id: id.into_v2()?, + suggestion_id: super::NesSuggestionId::new(id.into_v2()?), meta: meta.into_v2()?, }) } @@ -7432,13 +7466,13 @@ impl IntoV1 for super::RejectNesNotification { fn into_v1(self) -> Result { let Self { session_id, - id, + suggestion_id, reason, meta, } = self; Ok(crate::v1::RejectNesNotification { session_id: session_id.into_v1()?, - id: id.into_v1()?, + id: suggestion_id.into_v1()?, reason: into_v1_default_on_error(reason), meta: meta.into_v1()?, }) @@ -7458,7 +7492,7 @@ impl IntoV2 for crate::v1::RejectNesNotification { } = self; Ok(super::RejectNesNotification { session_id: session_id.into_v2()?, - id: id.into_v2()?, + suggestion_id: super::NesSuggestionId::new(id.into_v2()?), reason: into_v2_default_on_error(reason), meta: meta.into_v2()?, }) @@ -9645,13 +9679,6 @@ mod tests { v1::SessionUpdate::UserMessageChunk(content_chunk("u", "msg_user")), v1::SessionUpdate::AgentMessageChunk(content_chunk("a", "msg_agent")), v1::SessionUpdate::AgentThoughtChunk(content_chunk("t", "msg_thought")), - #[cfg(feature = "unstable_plan_operations")] - v1::SessionUpdate::PlanUpdate(v1::PlanUpdate::new(v1::PlanUpdateContent::markdown( - "plan-1", - "## Steps\n- [ ] Test conversion", - ))), - #[cfg(feature = "unstable_plan_operations")] - v1::SessionUpdate::PlanRemoved(v1::PlanRemoved::new("plan-1")), v1::SessionUpdate::SessionInfoUpdate(v1::SessionInfoUpdate::new().title("hi")), v1::SessionUpdate::UsageUpdate( v1::UsageUpdate::new(53_000, 200_000).cost(v1::Cost::new(0.045, "USD")), @@ -9944,7 +9971,7 @@ mod tests { "sessionUpdate": "plan_update", "plan": { "type": "items", - "id": LEGACY_V1_PLAN_ID, + "planId": LEGACY_V1_PLAN_ID, "entries": [ { "content": "step", @@ -10263,6 +10290,7 @@ mod tests { assert_v2_to_v1_error( v2::NesSuggestion::Other(v2::OtherNesSuggestion::new( "_preview", + "preview-1", std::collections::BTreeMap::new(), )), "v2 NesSuggestion variant `_preview` cannot be represented in v1", diff --git a/agent-client-protocol-schema/src/v2/mcp.rs b/agent-client-protocol-schema/src/v2/mcp.rs index 8ff63a242..c3bcdc8f5 100644 --- a/agent-client-protocol-schema/src/v2/mcp.rs +++ b/agent-client-protocol-schema/src/v2/mcp.rs @@ -43,7 +43,7 @@ impl McpConnectionId { #[non_exhaustive] pub struct ConnectMcpRequest { /// The ACP MCP server ID that was provided by the component declaring the MCP server. - pub acp_id: McpServerAcpId, + pub server_id: McpServerAcpId, /// The _meta property is reserved by ACP to allow clients and agents to attach additional /// metadata to their interactions. Implementations MUST NOT make assumptions about values at /// these keys. @@ -59,9 +59,9 @@ pub struct ConnectMcpRequest { impl ConnectMcpRequest { /// Builds [`ConnectMcpRequest`] with the required request fields set; optional fields start unset or empty. #[must_use] - pub fn new(acp_id: impl Into) -> Self { + pub fn new(server_id: impl Into) -> Self { Self { - acp_id: acp_id.into(), + server_id: server_id.into(), meta: None, } } diff --git a/agent-client-protocol-schema/src/v2/nes.rs b/agent-client-protocol-schema/src/v2/nes.rs index 958c62f7e..74f785ea0 100644 --- a/agent-client-protocol-schema/src/v2/nes.rs +++ b/agent-client-protocol-schema/src/v2/nes.rs @@ -4,8 +4,9 @@ //! document events, and a suggestion request/response flow. NES sessions are //! independent of chat sessions and have their own lifecycle. -use std::collections::BTreeMap; +use std::{collections::BTreeMap, sync::Arc}; +use derive_more::{Display, From}; use schemars::{JsonSchema, Schema}; use serde::{Deserialize, Serialize}; use serde_with::{DefaultOnError, VecSkipError, serde_as, skip_serializing_none}; @@ -23,6 +24,25 @@ pub(crate) const NES_SUGGEST_METHOD_NAME: &str = "nes/suggest"; pub(crate) const NES_ACCEPT_METHOD_NAME: &str = "nes/accept"; /// Method name for rejecting a suggestion. pub(crate) const NES_REJECT_METHOD_NAME: &str = "nes/reject"; + +/// **UNSTABLE** +/// +/// This capability is not part of the spec yet, and may be removed or changed at any point. +/// +/// Unique identifier for an NES suggestion. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, Display, From)] +#[serde(transparent)] +#[from(Arc, String, &'static str)] +#[non_exhaustive] +pub struct NesSuggestionId(pub Arc); + +impl NesSuggestionId { + /// Wraps a protocol string as a typed [`NesSuggestionId`]. + #[must_use] + pub fn new(id: impl Into>) -> Self { + Self(id.into()) + } +} /// Method name for closing an NES session. pub(crate) const NES_CLOSE_METHOD_NAME: &str = "nes/close"; /// Notification name for document open events. @@ -2379,6 +2399,8 @@ pub struct OtherNesSuggestion { /// extensions. Unknown values that do not begin with `_` are reserved for /// future ACP variants. pub kind: String, + /// Unique identifier for accept/reject tracking. + pub suggestion_id: NesSuggestionId, /// Additional fields from the unknown NES suggestion payload. #[serde(flatten)] pub fields: BTreeMap, @@ -2387,10 +2409,16 @@ pub struct OtherNesSuggestion { impl OtherNesSuggestion { /// Builds [`OtherNesSuggestion`] from an unknown discriminator and preserves the remaining extension fields. #[must_use] - pub fn new(kind: impl Into, mut fields: BTreeMap) -> Self { + pub fn new( + kind: impl Into, + suggestion_id: impl Into, + mut fields: BTreeMap, + ) -> Self { fields.remove("kind"); + fields.remove("suggestionId"); Self { kind: kind.into(), + suggestion_id: suggestion_id.into(), fields, } } @@ -2408,6 +2436,12 @@ impl<'de> Deserialize<'de> for OtherNesSuggestion { let serde_json::Value::String(kind) = kind else { return Err(serde::de::Error::custom("`kind` must be a string")); }; + let suggestion_id = fields + .remove("suggestionId") + .ok_or_else(|| serde::de::Error::missing_field("suggestionId"))?; + let serde_json::Value::String(suggestion_id) = suggestion_id else { + return Err(serde::de::Error::custom("`suggestionId` must be a string")); + }; if is_known_nes_suggestion_kind(&kind) { return Err(serde::de::Error::custom(format!( @@ -2415,7 +2449,11 @@ impl<'de> Deserialize<'de> for OtherNesSuggestion { ))); } - Ok(Self { kind, fields }) + Ok(Self { + kind, + suggestion_id: NesSuggestionId::new(suggestion_id), + fields, + }) } } @@ -2439,7 +2477,7 @@ fn other_nes_suggestion_schema(schema: &mut Schema) { #[non_exhaustive] pub struct NesEditSuggestion { /// Unique identifier for accept/reject tracking. - pub id: String, + pub suggestion_id: NesSuggestionId, /// The URI of the file to edit. pub uri: String, /// The text edits to apply. @@ -2466,9 +2504,13 @@ pub struct NesEditSuggestion { impl NesEditSuggestion { /// Builds [`NesEditSuggestion`] with the required fields set; optional fields start unset or empty. #[must_use] - pub fn new(id: impl Into, uri: impl Into, edits: Vec) -> Self { + pub fn new( + suggestion_id: impl Into, + uri: impl Into, + edits: Vec, + ) -> Self { Self { - id: id.into(), + suggestion_id: suggestion_id.into(), uri: uri.into(), edits, cursor_position: None, @@ -2549,7 +2591,7 @@ impl NesTextEdit { #[non_exhaustive] pub struct NesJumpSuggestion { /// Unique identifier for accept/reject tracking. - pub id: String, + pub suggestion_id: NesSuggestionId, /// The file to navigate to. pub uri: String, /// The target position within the file. @@ -2569,9 +2611,13 @@ pub struct NesJumpSuggestion { impl NesJumpSuggestion { /// Builds [`NesJumpSuggestion`] with the required fields set; optional fields start unset or empty. #[must_use] - pub fn new(id: impl Into, uri: impl Into, position: Position) -> Self { + pub fn new( + suggestion_id: impl Into, + uri: impl Into, + position: Position, + ) -> Self { Self { - id: id.into(), + suggestion_id: suggestion_id.into(), uri: uri.into(), position, meta: None, @@ -2598,7 +2644,7 @@ impl NesJumpSuggestion { #[non_exhaustive] pub struct NesRenameSuggestion { /// Unique identifier for accept/reject tracking. - pub id: String, + pub suggestion_id: NesSuggestionId, /// The file URI containing the symbol. pub uri: String, /// The position of the symbol to rename. @@ -2621,13 +2667,13 @@ impl NesRenameSuggestion { /// Builds [`NesRenameSuggestion`] with the required fields set; optional fields start unset or empty. #[must_use] pub fn new( - id: impl Into, + suggestion_id: impl Into, uri: impl Into, position: Position, new_name: impl Into, ) -> Self { Self { - id: id.into(), + suggestion_id: suggestion_id.into(), uri: uri.into(), position, new_name: new_name.into(), @@ -2655,7 +2701,7 @@ impl NesRenameSuggestion { #[non_exhaustive] pub struct NesSearchAndReplaceSuggestion { /// Unique identifier for accept/reject tracking. - pub id: String, + pub suggestion_id: NesSuggestionId, /// The file URI to search within. pub uri: String, /// The text or pattern to find. @@ -2683,13 +2729,13 @@ impl NesSearchAndReplaceSuggestion { /// Builds [`NesSearchAndReplaceSuggestion`] with the required fields set; optional fields start unset or empty. #[must_use] pub fn new( - id: impl Into, + suggestion_id: impl Into, uri: impl Into, search: impl Into, replace: impl Into, ) -> Self { Self { - id: id.into(), + suggestion_id: suggestion_id.into(), uri: uri.into(), search: search.into(), replace: replace.into(), @@ -2730,7 +2776,7 @@ pub struct AcceptNesNotification { /// The session ID for this notification. pub session_id: SessionId, /// The ID of the accepted suggestion. - pub id: String, + pub suggestion_id: NesSuggestionId, /// The _meta property is reserved by ACP to allow clients and agents to attach additional /// metadata to their interactions. Implementations MUST NOT make assumptions about values at /// these keys. @@ -2746,10 +2792,13 @@ pub struct AcceptNesNotification { impl AcceptNesNotification { /// Builds [`AcceptNesNotification`] with the required notification fields set; optional fields start unset or empty. #[must_use] - pub fn new(session_id: impl Into, id: impl Into) -> Self { + pub fn new( + session_id: impl Into, + suggestion_id: impl Into, + ) -> Self { Self { session_id: session_id.into(), - id: id.into(), + suggestion_id: suggestion_id.into(), meta: None, } } @@ -2777,7 +2826,7 @@ pub struct RejectNesNotification { /// The session ID for this notification. pub session_id: SessionId, /// The ID of the rejected suggestion. - pub id: String, + pub suggestion_id: NesSuggestionId, /// The reason for rejection. #[serde_as(deserialize_as = "DefaultOnError")] #[schemars(extend("x-deserialize-default-on-error" = true))] @@ -2798,10 +2847,13 @@ pub struct RejectNesNotification { impl RejectNesNotification { /// Builds [`RejectNesNotification`] with the required notification fields set; optional fields start unset or empty. #[must_use] - pub fn new(session_id: impl Into, id: impl Into) -> Self { + pub fn new( + session_id: impl Into, + suggestion_id: impl Into, + ) -> Self { Self { session_id: session_id.into(), - id: id.into(), + suggestion_id: suggestion_id.into(), reason: None, meta: None, } @@ -3143,7 +3195,7 @@ mod tests { json, json!({ "kind": "edit", - "id": "sugg_001", + "suggestionId": "sugg_001", "uri": "file:///path/to/other_file.rs", "edits": [ { @@ -3166,7 +3218,7 @@ mod tests { fn test_nes_suggestion_unknown_variant() { let suggestion: NesSuggestion = serde_json::from_value(json!({ "kind": "_preview", - "id": "sugg_001", + "suggestionId": "sugg_001", "label": "Preview generated file" })) .unwrap(); @@ -3176,12 +3228,13 @@ mod tests { }; assert_eq!(unknown.kind, "_preview"); - assert_eq!(unknown.fields.get("id"), Some(&json!("sugg_001"))); + assert_eq!(unknown.suggestion_id.to_string(), "sugg_001"); + assert!(!unknown.fields.contains_key("suggestionId")); assert_eq!( serde_json::to_value(NesSuggestion::Other(unknown)).unwrap(), json!({ "kind": "_preview", - "id": "sugg_001", + "suggestionId": "sugg_001", "label": "Preview generated file" }) ); @@ -3210,7 +3263,7 @@ mod tests { json, json!({ "kind": "jump", - "id": "sugg_002", + "suggestionId": "sugg_002", "uri": "file:///path/to/other_file.rs", "position": { "line": 15, "character": 4 } }) @@ -3234,7 +3287,7 @@ mod tests { json, json!({ "kind": "rename", - "id": "sugg_003", + "suggestionId": "sugg_003", "uri": "file:///path/to/file.rs", "position": { "line": 5, "character": 10 }, "newName": "calculateTotal" @@ -3262,7 +3315,7 @@ mod tests { json, json!({ "kind": "searchAndReplace", - "id": "sugg_004", + "suggestionId": "sugg_004", "uri": "file:///path/to/file.rs", "search": "oldFunction", "replace": "newFunction", @@ -3357,7 +3410,7 @@ mod tests { let json = serde_json::to_value(¬ification).unwrap(); assert_eq!( json, - json!({ "sessionId": "session_123", "id": "sugg_001" }) + json!({ "sessionId": "session_123", "suggestionId": "sugg_001" }) ); } @@ -3368,7 +3421,7 @@ mod tests { let json = serde_json::to_value(¬ification).unwrap(); assert_eq!( json, - json!({ "sessionId": "session_123", "id": "sugg_001", "reason": "rejected" }) + json!({ "sessionId": "session_123", "suggestionId": "sugg_001", "reason": "rejected" }) ); } diff --git a/agent-client-protocol-schema/src/v2/plan.rs b/agent-client-protocol-schema/src/v2/plan.rs index fd150b66a..8c6724925 100644 --- a/agent-client-protocol-schema/src/v2/plan.rs +++ b/agent-client-protocol-schema/src/v2/plan.rs @@ -121,7 +121,7 @@ pub struct OtherPlanUpdateContent { #[serde(rename = "type")] pub type_: String, /// The plan ID to update. - pub id: PlanId, + pub plan_id: PlanId, /// Additional fields from the unknown plan update content payload. #[serde(flatten)] pub fields: BTreeMap, @@ -132,14 +132,14 @@ impl OtherPlanUpdateContent { #[must_use] pub fn new( type_: impl Into, - id: impl Into, + plan_id: impl Into, mut fields: BTreeMap, ) -> Self { fields.remove("type"); - fields.remove("id"); + fields.remove("planId"); Self { type_: type_.into(), - id: id.into(), + plan_id: plan_id.into(), fields, } } @@ -157,11 +157,11 @@ impl<'de> Deserialize<'de> for OtherPlanUpdateContent { let serde_json::Value::String(type_) = type_ else { return Err(serde::de::Error::custom("`type` must be a string")); }; - let id = fields - .remove("id") - .ok_or_else(|| serde::de::Error::missing_field("id"))?; - let serde_json::Value::String(id) = id else { - return Err(serde::de::Error::custom("`id` must be a string")); + let plan_id = fields + .remove("planId") + .ok_or_else(|| serde::de::Error::missing_field("planId"))?; + let serde_json::Value::String(plan_id) = plan_id else { + return Err(serde::de::Error::custom("`planId` must be a string")); }; if is_known_plan_update_content_type(&type_) { @@ -172,7 +172,7 @@ impl<'de> Deserialize<'de> for OtherPlanUpdateContent { Ok(Self { type_, - id: PlanId::new(id), + plan_id: PlanId::new(plan_id), fields, }) } @@ -195,22 +195,22 @@ const KNOWN_PLAN_UPDATE_CONTENT_TYPES: &[&str] = &["items", "file", "markdown"]; impl PlanUpdateContent { /// Builds a plan update that replaces the itemized entries for a plan. #[must_use] - pub fn items(id: impl Into, entries: Vec) -> Self { - Self::Items(PlanItems::new(id, entries)) + pub fn items(plan_id: impl Into, entries: Vec) -> Self { + Self::Items(PlanItems::new(plan_id, entries)) } /// Builds a plan update that points clients at an external plan file URI. #[cfg(feature = "unstable_plan_operations")] #[must_use] - pub fn file(id: impl Into, uri: impl Into) -> Self { - Self::File(PlanFile::new(id, uri)) + pub fn file(plan_id: impl Into, uri: impl Into) -> Self { + Self::File(PlanFile::new(plan_id, uri)) } /// Builds a plan update whose plan content is inline Markdown. #[cfg(feature = "unstable_plan_operations")] #[must_use] - pub fn markdown(id: impl Into, content: impl Into) -> Self { - Self::Markdown(PlanMarkdown::new(id, content)) + pub fn markdown(plan_id: impl Into, content: impl Into) -> Self { + Self::Markdown(PlanMarkdown::new(plan_id, content)) } } @@ -222,7 +222,7 @@ impl PlanUpdateContent { #[non_exhaustive] pub struct PlanItems { /// The plan ID to update. - pub id: PlanId, + pub plan_id: PlanId, /// The list of tasks to be accomplished. /// /// When updating an item-based plan, the agent must send a complete list of all entries @@ -245,9 +245,9 @@ pub struct PlanItems { impl PlanItems { /// Builds [`PlanItems`] with the required fields set; optional fields start unset or empty. #[must_use] - pub fn new(id: impl Into, entries: Vec) -> Self { + pub fn new(plan_id: impl Into, entries: Vec) -> Self { Self { - id: id.into(), + plan_id: plan_id.into(), entries, meta: None, } @@ -278,7 +278,7 @@ impl PlanItems { #[non_exhaustive] pub struct PlanFile { /// The plan ID to update. - pub id: PlanId, + pub plan_id: PlanId, /// The URI of the file containing the plan. pub uri: String, /// The _meta property is reserved by ACP to allow clients and agents to attach additional @@ -297,9 +297,9 @@ pub struct PlanFile { impl PlanFile { /// Builds [`PlanFile`] with the required fields set; optional fields start unset or empty. #[must_use] - pub fn new(id: impl Into, uri: impl Into) -> Self { + pub fn new(plan_id: impl Into, uri: impl Into) -> Self { Self { - id: id.into(), + plan_id: plan_id.into(), uri: uri.into(), meta: None, } @@ -330,7 +330,7 @@ impl PlanFile { #[non_exhaustive] pub struct PlanMarkdown { /// The plan ID to update. - pub id: PlanId, + pub plan_id: PlanId, /// Markdown content for the plan. pub content: String, /// The _meta property is reserved by ACP to allow clients and agents to attach additional @@ -349,9 +349,9 @@ pub struct PlanMarkdown { impl PlanMarkdown { /// Builds [`PlanMarkdown`] with the required fields set; optional fields start unset or empty. #[must_use] - pub fn new(id: impl Into, content: impl Into) -> Self { + pub fn new(plan_id: impl Into, content: impl Into) -> Self { Self { - id: id.into(), + plan_id: plan_id.into(), content: content.into(), meta: None, } @@ -382,7 +382,7 @@ impl PlanMarkdown { #[non_exhaustive] pub struct PlanRemoved { /// The plan ID to remove. - pub id: PlanId, + pub plan_id: PlanId, /// The _meta property is reserved by ACP to allow clients and agents to attach additional /// metadata to their interactions. Implementations MUST NOT make assumptions about values at /// these keys. @@ -399,9 +399,9 @@ pub struct PlanRemoved { impl PlanRemoved { /// Builds [`PlanRemoved`] with the required fields set; optional fields start unset or empty. #[must_use] - pub fn new(id: impl Into) -> Self { + pub fn new(plan_id: impl Into) -> Self { Self { - id: id.into(), + plan_id: plan_id.into(), meta: None, } } @@ -545,7 +545,7 @@ mod tests { fn plan_update_content_preserves_unknown_variant() { let content: PlanUpdateContent = serde_json::from_value(serde_json::json!({ "type": "_timeline", - "id": "plan-1", + "planId": "plan-1", "events": [] })) .unwrap(); @@ -555,13 +555,13 @@ mod tests { }; assert_eq!(unknown.type_, "_timeline"); - assert_eq!(unknown.id.to_string(), "plan-1"); - assert!(!unknown.fields.contains_key("id")); + assert_eq!(unknown.plan_id.to_string(), "plan-1"); + assert!(!unknown.fields.contains_key("planId")); assert_eq!( serde_json::to_value(PlanUpdateContent::Other(unknown)).unwrap(), serde_json::json!({ "type": "_timeline", - "id": "plan-1", + "planId": "plan-1", "events": [] }) ); @@ -578,14 +578,14 @@ mod tests { assert!( serde_json::from_value::(serde_json::json!({ "type": "file", - "id": "plan-1" + "planId": "plan-1" })) .is_err() ); assert!( serde_json::from_value::(serde_json::json!({ "type": "markdown", - "id": "plan-1" + "planId": "plan-1" })) .is_err() ); diff --git a/docs/protocol/v1/draft/schema.mdx b/docs/protocol/v1/draft/schema.mdx index c79671aee..72dfb16ff 100644 --- a/docs/protocol/v1/draft/schema.mdx +++ b/docs/protocol/v1/draft/schema.mdx @@ -1967,7 +1967,7 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/draft/extensibility) -McpServerAcpId} required> +McpServerAcpId} required> The ACP MCP server ID that was provided by the component declaring the MCP server. @@ -4847,15 +4847,15 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/draft/extensibility) -McpServerAcpId} required> + + Human-readable name identifying this MCP server. + +McpServerAcpId} required> Unique identifier for this MCP server, generated by the component providing it. Providers MUST NOT reuse an ID for multiple ACP-transport MCP servers that are visible on the same ACP connection. - - - Human-readable name identifying this MCP server. The discriminator value. Must be `"acp"`. @@ -4918,16 +4918,16 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/draft/extensibility) -McpServerAcpId} required> + + Human-readable name identifying this MCP server. + +McpServerAcpId} required> Unique identifier for this MCP server, generated by the component providing it. Providers MUST NOT reuse an ID for multiple ACP-transport MCP servers that are visible on the same ACP connection. - - Human-readable name identifying this MCP server. - ## McpServerAcpId diff --git a/docs/protocol/v2/agent-plan.mdx b/docs/protocol/v2/agent-plan.mdx index 03c71162b..8e7da726f 100644 --- a/docs/protocol/v2/agent-plan.mdx +++ b/docs/protocol/v2/agent-plan.mdx @@ -7,7 +7,7 @@ Plans are execution strategies for complex tasks that require multiple steps. Agents may share plans with Clients through [`session/update`](/protocol/v2/prompt-lifecycle#3-agent-reports-output) notifications, providing real-time visibility into their thinking and progress. -`plan_update` carries a `plan` object with a `type` discriminator and a required `id` so Clients can track multiple plans independently. +`plan_update` carries a `plan` object with a `type` discriminator and a required `planId` so Clients can track multiple plans independently. The plan content type is `items`. Additional plan operations remain unstable while the Plan Operations RFD is in progress. @@ -27,7 +27,7 @@ When the language model creates an execution plan, the Agent **SHOULD** report i "sessionUpdate": "plan_update", "plan": { "type": "items", - "id": "plan-1", + "planId": "plan-1", "entries": [ { "content": "Analyze the existing codebase structure", @@ -55,7 +55,7 @@ When the language model creates an execution plan, the Agent **SHOULD** report i The plan content type. - + A unique identifier for this plan within the session. @@ -68,7 +68,7 @@ When the language model creates an execution plan, the Agent **SHOULD** report i `plan.type` values can be custom or future variants when Clients can preserve or display them generically. Custom plan content types **MUST** begin with `_`; unknown non-underscore plan content types are reserved for future ACP variants. -Every plan content variant, including custom or future variants, **MUST** carry an `id`. +Every plan content variant, including custom or future variants, **MUST** carry a `planId`. ## Plan Entries @@ -102,7 +102,7 @@ Custom or future status values can be used when Clients can preserve or display ## Updating Plans -As the Agent progresses through the plan, it **SHOULD** report updates by sending more `session/update` notifications with the same `sessionUpdate: "plan_update"` structure and plan `id`. +As the Agent progresses through the plan, it **SHOULD** report updates by sending more `session/update` notifications with the same `sessionUpdate: "plan_update"` structure and `planId`. For item-based plans, the Agent **MUST** send a complete list of all plan entries in each update and their current status. The Client **MUST** replace the current contents of that plan completely. diff --git a/docs/protocol/v2/authentication.mdx b/docs/protocol/v2/authentication.mdx index e15284402..c5c62aa97 100644 --- a/docs/protocol/v2/authentication.mdx +++ b/docs/protocol/v2/authentication.mdx @@ -34,7 +34,7 @@ sequenceDiagram ## Advertising Authentication -Agents advertise authentication options in the `authMethods` field of the `initialize` response. Each method has an `id` that the Client passes back to the Agent in a later `auth/login` request. +Agents advertise authentication options in the `authMethods` field of the `initialize` response. Each method has a `methodId` that the Client passes back to the Agent in a later `auth/login` request. ```json highlight={8-15} { @@ -45,7 +45,7 @@ Agents advertise authentication options in the `authMethods` field of the `initi "capabilities": {}, "authMethods": [ { - "id": "agent-login", + "methodId": "agent-login", "name": "Agent login", "type": "agent", "description": "Sign in using the agent's login flow" @@ -61,7 +61,7 @@ The standard authentication method type is `agent`, where the Agent handles auth ```json { - "id": "agent-login", + "methodId": "agent-login", "name": "Agent login", "type": "agent", "description": "Sign in using the agent's login flow" diff --git a/docs/protocol/v2/draft/agent-plan.mdx b/docs/protocol/v2/draft/agent-plan.mdx index bf16e33d3..4f46ec471 100644 --- a/docs/protocol/v2/draft/agent-plan.mdx +++ b/docs/protocol/v2/draft/agent-plan.mdx @@ -7,7 +7,7 @@ Plans are execution strategies for complex tasks that require multiple steps. Agents may share plans with Clients through [`session/update`](/protocol/v2/draft/prompt-lifecycle#3-agent-reports-output) notifications, providing real-time visibility into their thinking and progress. -`plan_update` carries a `plan` object with a `type` discriminator, and every plan format includes a required `id` so Clients can track multiple plans independently. +`plan_update` carries a `plan` object with a `type` discriminator, and every plan format includes a required `planId` so Clients can track multiple plans independently. The stable plan content type is `items`. The draft unstable schema also includes additional plan operations while the Plan Operations RFD is in progress. @@ -27,7 +27,7 @@ When the language model creates an execution plan, the Agent **SHOULD** report i "sessionUpdate": "plan_update", "plan": { "type": "items", - "id": "plan-1", + "planId": "plan-1", "entries": [ { "content": "Analyze the existing codebase structure", @@ -55,7 +55,7 @@ When the language model creates an execution plan, the Agent **SHOULD** report i The plan content type. - + A unique identifier for this plan within the session. @@ -82,7 +82,7 @@ Clients using the draft unstable schema **MUST** tolerate these variants. Tolera "sessionUpdate": "plan_update", "plan": { "type": "markdown", - "id": "implementation-plan", + "planId": "implementation-plan", "content": "## Steps\n- [ ] Refactor module\n- [ ] Add tests" } } @@ -102,7 +102,7 @@ Clients using the draft unstable schema **MUST** tolerate these variants. Tolera "sessionUpdate": "plan_update", "plan": { "type": "file", - "id": "design-doc", + "planId": "design-doc", "uri": "file:///tmp/plan.md" } } @@ -124,7 +124,7 @@ Agents can remove a plan by sending `plan_removed` with the plan ID: "sessionId": "sess_abc123def456", "update": { "sessionUpdate": "plan_removed", - "id": "plan-1" + "planId": "plan-1" } } } @@ -134,7 +134,7 @@ Agents can remove a plan by sending `plan_removed` with the plan ID: `plan.type` values can be custom or future variants when Clients can preserve or display them generically. Custom plan content types **MUST** begin with `_`; unknown non-underscore plan content types are reserved for future ACP variants. -Every plan content variant, including custom or future variants, **MUST** carry an `id`. +Every plan content variant, including custom or future variants, **MUST** carry a `planId`. ## Plan Entries @@ -168,7 +168,7 @@ Custom or future status values can be used when Clients can preserve or display ## Updating Plans -As the Agent progresses through the plan, it **SHOULD** report updates by sending more `session/update` notifications with the same `sessionUpdate: "plan_update"` structure and plan `id`. +As the Agent progresses through the plan, it **SHOULD** report updates by sending more `session/update` notifications with the same `sessionUpdate: "plan_update"` structure and `planId`. For item-based plans, the Agent **MUST** send a complete list of all plan entries in each update and their current status. The Client **MUST** replace the current contents of that plan completely. diff --git a/docs/protocol/v2/draft/authentication.mdx b/docs/protocol/v2/draft/authentication.mdx index 40a41f15f..55741f1ae 100644 --- a/docs/protocol/v2/draft/authentication.mdx +++ b/docs/protocol/v2/draft/authentication.mdx @@ -34,7 +34,7 @@ sequenceDiagram ## Advertising Authentication -Agents advertise authentication options in the `authMethods` field of the `initialize` response. Each method has an `id` that the Client passes back to the Agent in a later `auth/login` request. +Agents advertise authentication options in the `authMethods` field of the `initialize` response. Each method has a `methodId` that the Client passes back to the Agent in a later `auth/login` request. ```json highlight={8-15} { @@ -45,7 +45,7 @@ Agents advertise authentication options in the `authMethods` field of the `initi "capabilities": {}, "authMethods": [ { - "id": "agent-login", + "methodId": "agent-login", "name": "Agent login", "type": "agent", "description": "Sign in using the agent's login flow" @@ -61,7 +61,7 @@ The standard authentication method type is `agent`, where the Agent handles auth ```json { - "id": "agent-login", + "methodId": "agent-login", "name": "Agent login", "type": "agent", "description": "Sign in using the agent's login flow" diff --git a/docs/protocol/v2/draft/prompt-lifecycle.mdx b/docs/protocol/v2/draft/prompt-lifecycle.mdx index a58350cd8..51bad0bb5 100644 --- a/docs/protocol/v2/draft/prompt-lifecycle.mdx +++ b/docs/protocol/v2/draft/prompt-lifecycle.mdx @@ -170,7 +170,7 @@ The Agent reports the model's output to the Client via `session/update` notifica "sessionUpdate": "plan_update", "plan": { "type": "items", - "id": "plan-1", + "planId": "plan-1", "entries": [ { "content": "Check for syntax errors", diff --git a/docs/protocol/v2/draft/schema.mdx b/docs/protocol/v2/draft/schema.mdx index b6d3a7da0..e9504cf5c 100644 --- a/docs/protocol/v2/draft/schema.mdx +++ b/docs/protocol/v2/draft/schema.mdx @@ -487,13 +487,13 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) - - - The ID of the accepted suggestion. SessionId} required> The session ID for this notification. +NesSuggestionId} required> + The ID of the accepted suggestion. + ### nes/close @@ -569,9 +569,6 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) - - - The ID of the rejected suggestion. NesRejectReason | null} > The reason for rejection. @@ -579,6 +576,9 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/d SessionId} required> The session ID for this notification. +NesSuggestionId} required> + The ID of the rejected suggestion. + ### nes/start @@ -731,8 +731,8 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) - - Provider id to disable. +ProviderId} required> + Provider ID to disable. #### DisableProviderResponse @@ -827,7 +827,7 @@ This capability is not part of the spec yet, and may be removed or changed at an Request parameters for `providers/set`. -Replaces the full configuration for one provider id. +Replaces the full configuration for one provider ID. **Type:** Object @@ -851,8 +851,8 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/d Full headers map for this provider. May include authorization, routing, or other integration-specific headers. - - Provider id to configure. +ProviderId} required> + Provider ID to configure. #### SetProviderResponse @@ -1778,7 +1778,7 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) -McpServerAcpId} required> +McpServerAcpId} required> The ACP MCP server ID that was provided by the component declaring the MCP server. @@ -2409,12 +2409,12 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/d Optional description providing more details about this authentication method. -AuthMethodId} required> - Unique identifier for this authentication method. - Optional link to a page where the user can obtain their credentials. +AuthMethodId} required> + Unique identifier for this authentication method. + Human-readable name of the authentication method. @@ -2454,7 +2454,7 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/d EnvVariable[]} > Additional environment variables to set when running the agent binary for terminal auth. -AuthMethodId} required> +AuthMethodId} required> Unique identifier for this authentication method. @@ -2485,7 +2485,7 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/d Optional description providing more details about this authentication method. -AuthMethodId} required> +AuthMethodId} required> Unique identifier for this authentication method. @@ -2522,7 +2522,7 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/d Optional description providing more details about this authentication method. -AuthMethodId} required> +AuthMethodId} required> Unique identifier for this authentication method. @@ -2561,7 +2561,7 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/d Optional description providing more details about this authentication method. -AuthMethodId} required> +AuthMethodId} required> Unique identifier for this authentication method. @@ -2593,12 +2593,12 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/d Optional description providing more details about this authentication method. -AuthMethodId} required> - Unique identifier for this authentication method. - Optional link to a page where the user can obtain their credentials. +AuthMethodId} required> + Unique identifier for this authentication method. + Human-readable name of the authentication method. @@ -2643,7 +2643,7 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/d EnvVariable[]} > Additional environment variables to set when running the agent binary for terminal auth. -AuthMethodId} required> +AuthMethodId} required> Unique identifier for this authentication method. @@ -4391,15 +4391,15 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) -McpServerAcpId} required> + + Human-readable name identifying this MCP server. + +McpServerAcpId} required> Unique identifier for this MCP server, generated by the component providing it. Providers MUST NOT reuse an ID for multiple ACP-transport MCP servers that are visible on the same ACP connection. - - - Human-readable name identifying this MCP server. The discriminator value. Must be `"acp"`. @@ -4490,16 +4490,16 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) -McpServerAcpId} required> + + Human-readable name identifying this MCP server. + +McpServerAcpId} required> Unique identifier for this MCP server, generated by the component providing it. Providers MUST NOT reuse an ID for multiple ACP-transport MCP servers that are visible on the same ACP connection. - - Human-readable name identifying this MCP server. - ## McpServerAcpId @@ -5011,7 +5011,7 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/d NesTextEdit[]} required> The text edits to apply. - +NesSuggestionId} required> Unique identifier for accept/reject tracking. @@ -5102,13 +5102,13 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) - - - Unique identifier for accept/reject tracking. Position} required> The target position within the file. +NesSuggestionId} required> + Unique identifier for accept/reject tracking. + The file to navigate to. @@ -5314,9 +5314,6 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) - - - Unique identifier for accept/reject tracking. The new name for the symbol. @@ -5324,6 +5321,9 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/d Position} required> The position of the symbol to rename. +NesSuggestionId} required> + Unique identifier for accept/reject tracking. + The file URI containing the symbol. @@ -5386,9 +5386,6 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) - - - Unique identifier for accept/reject tracking. Whether `search` is a regular expression. Defaults to `false`. @@ -5399,6 +5396,9 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/d The text or pattern to find. +NesSuggestionId} required> + Unique identifier for accept/reject tracking. + The file URI to search within. @@ -5463,12 +5463,12 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/d NesTextEdit[]} required> The text edits to apply. - - Unique identifier for accept/reject tracking. - The discriminator value. Must be `"edit"`. +NesSuggestionId} required> + Unique identifier for accept/reject tracking. + The URI of the file to edit. @@ -5488,9 +5488,6 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) - - - Unique identifier for accept/reject tracking. The discriminator value. Must be `"jump"`. @@ -5498,6 +5495,9 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/d Position} required> The target position within the file. +NesSuggestionId} required> + Unique identifier for accept/reject tracking. + The file to navigate to. @@ -5517,9 +5517,6 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) - - - Unique identifier for accept/reject tracking. The discriminator value. Must be `"rename"`. @@ -5530,6 +5527,9 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/d Position} required> The position of the symbol to rename. +NesSuggestionId} required> + Unique identifier for accept/reject tracking. + The file URI containing the symbol. @@ -5549,9 +5549,6 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) - - - Unique identifier for accept/reject tracking. Whether `search` is a regular expression. Defaults to `false`. @@ -5565,6 +5562,9 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/d The text or pattern to find. +NesSuggestionId} required> + Unique identifier for accept/reject tracking. + The file URI to search within. @@ -5593,10 +5593,23 @@ extensions. Unknown values that do not begin with `_` are reserved for future ACP variants. +NesSuggestionId} required> + Unique identifier for accept/reject tracking. + +## NesSuggestionId + +**UNSTABLE** + +This capability is not part of the spec yet, and may be removed or changed at any point. + +Unique identifier for an NES suggestion. + +**Type:** `string` + ## NesTextEdit A text edit within a suggestion. @@ -5911,7 +5924,7 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) -PlanId} required> +PlanId} required> The plan ID to update. @@ -5947,7 +5960,7 @@ When updating an item-based plan, the agent must send a complete list of all ent with their current status. The client replaces that plan with each update. -PlanId} required> +PlanId} required> The plan ID to update. @@ -5974,7 +5987,7 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/d Markdown content for the plan. -PlanId} required> +PlanId} required> The plan ID to update. @@ -5998,7 +6011,7 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) -PlanId} required> +PlanId} required> The plan ID to remove. @@ -6048,7 +6061,7 @@ When updating an item-based plan, the agent must send a complete list of all ent with their current status. The client replaces that plan with each update. -PlanId} required> +PlanId} required> The plan ID to update. @@ -6075,7 +6088,7 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) -PlanId} required> +PlanId} required> The plan ID to update. @@ -6108,7 +6121,7 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/d Markdown content for the plan. -PlanId} required> +PlanId} required> The plan ID to update. @@ -6131,7 +6144,7 @@ otherwise ignore it or display it generically. -PlanId} required> +PlanId} required> The plan ID to update. @@ -6347,6 +6360,16 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/d Base URL currently used by this provider. +## ProviderId + +**UNSTABLE** + +This capability is not part of the spec yet, and may be removed or changed at any point. + +Unique identifier for a configurable LLM provider. + +**Type:** `string` + ## ProviderInfo **UNSTABLE** @@ -6371,12 +6394,12 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/d Current effective non-secret routing config. Null or omitted means provider is disabled. - +ProviderId} required> Provider identifier, for example "main" or "openai". Whether this provider is mandatory and cannot be disabled via `providers/disable`. -If true, clients must not call `providers/disable` for this id. +If true, clients must not call `providers/disable` for this provider ID. LlmProtocol[]} required> Supported protocol types for this provider. @@ -6784,12 +6807,12 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/d SessionConfigOptionCategory | null} > Optional semantic category for this option (UX only). +SessionConfigId} required> + Unique identifier for the configuration option. + Optional description for the Client to display to the user. -SessionConfigId} required> - Unique identifier for the configuration option. - Human-readable label for the option. @@ -6944,7 +6967,7 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) -SessionConfigGroupId} required> +SessionConfigGroupId} required> Unique identifier for this group. @@ -7493,7 +7516,7 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) -PlanId} required> +PlanId} required> The plan ID to remove. diff --git a/docs/protocol/v2/draft/session-config-options.mdx b/docs/protocol/v2/draft/session-config-options.mdx index 0c7115256..2de43cd90 100644 --- a/docs/protocol/v2/draft/session-config-options.mdx +++ b/docs/protocol/v2/draft/session-config-options.mdx @@ -17,7 +17,7 @@ During [Session Setup](/protocol/v2/draft/session-setup) the Agent **MAY** retur "sessionId": "sess_abc123def456", "configOptions": [ { - "id": "mode", + "configId": "mode", "name": "Session Mode", "description": "Controls how the agent requests permission", "category": "mode", @@ -37,7 +37,7 @@ During [Session Setup](/protocol/v2/draft/session-setup) the Agent **MAY** retur ] }, { - "id": "model", + "configId": "model", "name": "Model", "category": "model", "type": "select", @@ -56,7 +56,7 @@ During [Session Setup](/protocol/v2/draft/session-setup) the Agent **MAY** retur ] }, { - "id": "context_size", + "configId": "context_size", "name": "Context Size", "category": "model_config", "type": "select", @@ -73,7 +73,7 @@ During [Session Setup](/protocol/v2/draft/session-setup) the Agent **MAY** retur ] }, { - "id": "brave_mode", + "configId": "brave_mode", "name": "Brave Mode", "description": "Skip confirmation prompts and act autonomously", "type": "boolean", @@ -92,7 +92,7 @@ During [Session Setup](/protocol/v2/draft/session-setup) the Agent **MAY** retur ### ConfigOption - + Unique identifier for this configuration option. Used when setting values. @@ -145,7 +145,7 @@ on/off toggles: ```json { - "id": "brave_mode", + "configId": "brave_mode", "name": "Brave Mode", "description": "Skip confirmation prompts and act autonomously", "type": "boolean", @@ -222,7 +222,7 @@ Clients can change a config option value by calling the `session/set_config_opti - The `id` of the configuration option to change + The `configId` of the configuration option to change @@ -261,7 +261,7 @@ The Agent **MUST** respond with the complete list of all configuration options a "result": { "configOptions": [ { - "id": "mode", + "configId": "mode", "name": "Session Mode", "type": "select", "currentValue": "code", @@ -277,7 +277,7 @@ The Agent **MUST** respond with the complete list of all configuration options a ] }, { - "id": "model", + "configId": "model", "name": "Model", "type": "select", "currentValue": "model-1", @@ -318,7 +318,7 @@ The Agent can also change configuration options and notify the Client by sending "sessionUpdate": "config_option_update", "configOptions": [ { - "id": "mode", + "configId": "mode", "name": "Session Mode", "type": "select", "currentValue": "code", @@ -334,7 +334,7 @@ The Agent can also change configuration options and notify the Client by sending ] }, { - "id": "model", + "configId": "model", "name": "Model", "type": "select", "currentValue": "model-2", diff --git a/docs/protocol/v2/prompt-lifecycle.mdx b/docs/protocol/v2/prompt-lifecycle.mdx index d433097f9..0185edd3d 100644 --- a/docs/protocol/v2/prompt-lifecycle.mdx +++ b/docs/protocol/v2/prompt-lifecycle.mdx @@ -170,7 +170,7 @@ The Agent reports the model's output to the Client via `session/update` notifica "sessionUpdate": "plan_update", "plan": { "type": "items", - "id": "plan-1", + "planId": "plan-1", "entries": [ { "content": "Check for syntax errors", diff --git a/docs/protocol/v2/schema.mdx b/docs/protocol/v2/schema.mdx index 3e2d2be7f..5329d46d9 100644 --- a/docs/protocol/v2/schema.mdx +++ b/docs/protocol/v2/schema.mdx @@ -1116,7 +1116,7 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/e Optional description providing more details about this authentication method. -AuthMethodId} required> +AuthMethodId} required> Unique identifier for this authentication method. @@ -1153,7 +1153,7 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/e Optional description providing more details about this authentication method. -AuthMethodId} required> +AuthMethodId} required> Unique identifier for this authentication method. @@ -1192,7 +1192,7 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/e Optional description providing more details about this authentication method. -AuthMethodId} required> +AuthMethodId} required> Unique identifier for this authentication method. @@ -2416,7 +2416,7 @@ When updating an item-based plan, the agent must send a complete list of all ent with their current status. The client replaces that plan with each update. -PlanId} required> +PlanId} required> The plan ID to update. @@ -2466,7 +2466,7 @@ When updating an item-based plan, the agent must send a complete list of all ent with their current status. The client replaces that plan with each update. -PlanId} required> +PlanId} required> The plan ID to update. @@ -2489,7 +2489,7 @@ otherwise ignore it or display it generically. -PlanId} required> +PlanId} required> The plan ID to update. @@ -2959,12 +2959,12 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/e SessionConfigOptionCategory | null} > Optional semantic category for this option (UX only). +SessionConfigId} required> + Unique identifier for the configuration option. + Optional description for the Client to display to the user. -SessionConfigId} required> - Unique identifier for the configuration option. - Human-readable label for the option. @@ -3100,7 +3100,7 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility) -SessionConfigGroupId} required> +SessionConfigGroupId} required> Unique identifier for this group. diff --git a/docs/protocol/v2/session-config-options.mdx b/docs/protocol/v2/session-config-options.mdx index c75064117..878e05b5a 100644 --- a/docs/protocol/v2/session-config-options.mdx +++ b/docs/protocol/v2/session-config-options.mdx @@ -17,7 +17,7 @@ During [Session Setup](/protocol/v2/session-setup) the Agent **MAY** return a li "sessionId": "sess_abc123def456", "configOptions": [ { - "id": "mode", + "configId": "mode", "name": "Session Mode", "description": "Controls how the agent requests permission", "category": "mode", @@ -37,7 +37,7 @@ During [Session Setup](/protocol/v2/session-setup) the Agent **MAY** return a li ] }, { - "id": "model", + "configId": "model", "name": "Model", "category": "model", "type": "select", @@ -68,7 +68,7 @@ During [Session Setup](/protocol/v2/session-setup) the Agent **MAY** return a li ### ConfigOption - + Unique identifier for this configuration option. Used when setting values. @@ -179,7 +179,7 @@ Clients can change a config option value by calling the `session/set_config_opti - The `id` of the configuration option to change + The `configId` of the configuration option to change @@ -196,7 +196,7 @@ The Agent **MUST** respond with the complete list of all configuration options a "result": { "configOptions": [ { - "id": "mode", + "configId": "mode", "name": "Session Mode", "type": "select", "currentValue": "code", @@ -212,7 +212,7 @@ The Agent **MUST** respond with the complete list of all configuration options a ] }, { - "id": "model", + "configId": "model", "name": "Model", "type": "select", "currentValue": "model-1", @@ -253,7 +253,7 @@ The Agent can also change configuration options and notify the Client by sending "sessionUpdate": "config_option_update", "configOptions": [ { - "id": "mode", + "configId": "mode", "name": "Session Mode", "type": "select", "currentValue": "code", @@ -269,7 +269,7 @@ The Agent can also change configuration options and notify the Client by sending ] }, { - "id": "model", + "configId": "model", "name": "Model", "type": "select", "currentValue": "model-2", diff --git a/schema/v1/schema.unstable.json b/schema/v1/schema.unstable.json index 3f46791fe..7141cc6b1 100644 --- a/schema/v1/schema.unstable.json +++ b/schema/v1/schema.unstable.json @@ -2076,7 +2076,7 @@ "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nRequest parameters for `mcp/connect`.", "type": "object", "properties": { - "acpId": { + "serverId": { "description": "The ACP MCP server ID that was provided by the component declaring the MCP server.", "allOf": [ { @@ -2091,7 +2091,7 @@ "additionalProperties": true } }, - "required": ["acpId"], + "required": ["serverId"], "x-side": "client", "x-method": "mcp/connect" }, @@ -6734,7 +6734,7 @@ "description": "Human-readable name identifying this MCP server.", "type": "string" }, - "id": { + "serverId": { "description": "Unique identifier for this MCP server, generated by the component providing it.\n\nProviders MUST NOT reuse an ID for multiple ACP-transport MCP servers that are visible\non the same ACP connection.", "allOf": [ { @@ -6749,7 +6749,7 @@ "additionalProperties": true } }, - "required": ["name", "id"] + "required": ["name", "serverId"] }, "McpServerStdio": { "description": "Stdio transport configuration for MCP.", diff --git a/schema/v2/schema.json b/schema/v2/schema.json index 810cf1284..0c0d1e5da 100644 --- a/schema/v2/schema.json +++ b/schema/v2/schema.json @@ -2077,7 +2077,7 @@ "description": "Custom or future authentication method type.\n\nValues beginning with `_` are reserved for implementation-specific\nextensions. Unknown values that do not begin with `_` are reserved for\nfuture ACP variants.", "type": "string" }, - "id": { + "methodId": { "description": "Unique identifier for this authentication method.", "allOf": [ { @@ -2101,7 +2101,7 @@ "additionalProperties": true } }, - "required": ["type", "id", "name"], + "required": ["type", "methodId", "name"], "not": { "anyOf": [ { @@ -2128,7 +2128,7 @@ "description": "Agent handles authentication itself.\n\nThe `type` discriminator value is `agent`.", "type": "object", "properties": { - "id": { + "methodId": { "description": "Unique identifier for this authentication method.", "allOf": [ { @@ -2152,7 +2152,7 @@ "additionalProperties": true } }, - "required": ["id", "name"] + "required": ["methodId", "name"] }, "LoginAuthResponse": { "description": "Response to the `auth/login` method.", @@ -2218,7 +2218,7 @@ "description": "A session configuration option selector and its current state.", "type": "object", "properties": { - "id": { + "configId": { "description": "Unique identifier for the configuration option.", "allOf": [ { @@ -2254,7 +2254,7 @@ "additionalProperties": true } }, - "required": ["id", "name"], + "required": ["configId", "name"], "anyOf": [ { "description": "Single-value selector (dropdown).", @@ -2397,7 +2397,7 @@ "description": "A group of possible values for a session configuration option.", "type": "object", "properties": { - "group": { + "groupId": { "description": "Unique identifier for this group.", "allOf": [ { @@ -2425,7 +2425,7 @@ "additionalProperties": true } }, - "required": ["group", "name", "options"] + "required": ["groupId", "name", "options"] }, "SessionConfigGroupId": { "description": "Unique identifier for a session configuration option value group.", @@ -3561,7 +3561,7 @@ "description": "Custom or future plan update content type.\n\nValues beginning with `_` are reserved for implementation-specific\nextensions. Unknown values that do not begin with `_` are reserved for\nfuture ACP variants.", "type": "string" }, - "id": { + "planId": { "description": "The plan ID to update.", "allOf": [ { @@ -3570,7 +3570,7 @@ ] } }, - "required": ["type", "id"], + "required": ["type", "planId"], "not": { "anyOf": [ { @@ -3703,7 +3703,7 @@ "description": "A plan represented as structured entries.", "type": "object", "properties": { - "id": { + "planId": { "description": "The plan ID to update.", "allOf": [ { @@ -3727,7 +3727,7 @@ "additionalProperties": true } }, - "required": ["id", "entries"] + "required": ["planId", "entries"] }, "PlanUpdate": { "description": "A content update for a plan identified by ID.", diff --git a/schema/v2/schema.unstable.json b/schema/v2/schema.unstable.json index abd1da8f6..72a43e755 100644 --- a/schema/v2/schema.unstable.json +++ b/schema/v2/schema.unstable.json @@ -2277,7 +2277,7 @@ "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nRequest parameters for `mcp/connect`.", "type": "object", "properties": { - "acpId": { + "serverId": { "description": "The ACP MCP server ID that was provided by the component declaring the MCP server.", "allOf": [ { @@ -2292,7 +2292,7 @@ "additionalProperties": true } }, - "required": ["acpId"], + "required": ["serverId"], "x-side": "client", "x-method": "mcp/connect" }, @@ -3619,7 +3619,7 @@ "description": "Custom or future authentication method type.\n\nValues beginning with `_` are reserved for implementation-specific\nextensions. Unknown values that do not begin with `_` are reserved for\nfuture ACP variants.", "type": "string" }, - "id": { + "methodId": { "description": "Unique identifier for this authentication method.", "allOf": [ { @@ -3643,7 +3643,7 @@ "additionalProperties": true } }, - "required": ["type", "id", "name"], + "required": ["type", "methodId", "name"], "not": { "anyOf": [ { @@ -3724,7 +3724,7 @@ "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nEnvironment variable authentication method.\n\nThe user provides credentials that the client passes to the agent as environment variables.", "type": "object", "properties": { - "id": { + "methodId": { "description": "Unique identifier for this authentication method.", "allOf": [ { @@ -3762,7 +3762,7 @@ "additionalProperties": true } }, - "required": ["id", "name", "vars"] + "required": ["methodId", "name", "vars"] }, "EnvVariable": { "description": "An environment variable to set when launching an MCP server.", @@ -3789,7 +3789,7 @@ "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nTerminal-based authentication method.\n\nThe client runs an interactive terminal for the user to authenticate via a TUI.", "type": "object", "properties": { - "id": { + "methodId": { "description": "Unique identifier for this authentication method.", "allOf": [ { @@ -3831,13 +3831,13 @@ "additionalProperties": true } }, - "required": ["id", "name"] + "required": ["methodId", "name"] }, "AuthMethodAgent": { "description": "Agent handles authentication itself.\n\nThe `type` discriminator value is `agent`.", "type": "object", "properties": { - "id": { + "methodId": { "description": "Unique identifier for this authentication method.", "allOf": [ { @@ -3861,7 +3861,7 @@ "additionalProperties": true } }, - "required": ["id", "name"] + "required": ["methodId", "name"] }, "LoginAuthResponse": { "description": "Response to the `auth/login` method.", @@ -3905,9 +3905,13 @@ "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nInformation about a configurable LLM provider.", "type": "object", "properties": { - "id": { + "providerId": { "description": "Provider identifier, for example \"main\" or \"openai\".", - "type": "string" + "allOf": [ + { + "$ref": "#/$defs/ProviderId" + } + ] }, "supported": { "description": "Supported protocol types for this provider.", @@ -3919,7 +3923,7 @@ "x-deserialize-skip-invalid-items": true }, "required": { - "description": "Whether this provider is mandatory and cannot be disabled via `providers/disable`.\nIf true, clients must not call `providers/disable` for this id.", + "description": "Whether this provider is mandatory and cannot be disabled via `providers/disable`.\nIf true, clients must not call `providers/disable` for this provider ID.", "type": "boolean" }, "current": { @@ -3941,7 +3945,11 @@ "additionalProperties": true } }, - "required": ["id", "supported", "required"] + "required": ["providerId", "supported", "required"] + }, + "ProviderId": { + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nUnique identifier for a configurable LLM provider.", + "type": "string" }, "LlmProtocol": { "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nWell-known API protocol identifiers for LLM providers.\n\nAgents and clients MUST handle unknown protocol identifiers gracefully.\n\nProtocol names beginning with `_` are free for custom use, like other ACP extension methods.\nProtocol names that do not begin with `_` are reserved for the ACP spec.", @@ -4081,7 +4089,7 @@ "description": "A session configuration option selector and its current state.", "type": "object", "properties": { - "id": { + "configId": { "description": "Unique identifier for the configuration option.", "allOf": [ { @@ -4117,7 +4125,7 @@ "additionalProperties": true } }, - "required": ["id", "name"], + "required": ["configId", "name"], "anyOf": [ { "description": "Single-value selector (dropdown).", @@ -4286,7 +4294,7 @@ "description": "A group of possible values for a session configuration option.", "type": "object", "properties": { - "group": { + "groupId": { "description": "Unique identifier for this group.", "allOf": [ { @@ -4314,7 +4322,7 @@ "additionalProperties": true } }, - "required": ["group", "name", "options"] + "required": ["groupId", "name", "options"] }, "SessionConfigGroupId": { "description": "Unique identifier for a session configuration option value group.", @@ -4693,9 +4701,17 @@ "kind": { "description": "Custom or future NES suggestion kind.\n\nValues beginning with `_` are reserved for implementation-specific\nextensions. Unknown values that do not begin with `_` are reserved for\nfuture ACP variants.", "type": "string" + }, + "suggestionId": { + "description": "Unique identifier for accept/reject tracking.", + "allOf": [ + { + "$ref": "#/$defs/NesSuggestionId" + } + ] } }, - "required": ["kind"], + "required": ["kind", "suggestionId"], "not": { "anyOf": [ { @@ -4747,6 +4763,10 @@ "propertyName": "kind" } }, + "NesSuggestionId": { + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nUnique identifier for an NES suggestion.", + "type": "string" + }, "NesTextEdit": { "description": "A text edit within a suggestion.", "type": "object", @@ -4830,9 +4850,13 @@ "description": "A text edit suggestion.", "type": "object", "properties": { - "id": { + "suggestionId": { "description": "Unique identifier for accept/reject tracking.", - "type": "string" + "allOf": [ + { + "$ref": "#/$defs/NesSuggestionId" + } + ] }, "uri": { "description": "The URI of the file to edit.", @@ -4866,15 +4890,19 @@ "additionalProperties": true } }, - "required": ["id", "uri", "edits"] + "required": ["suggestionId", "uri", "edits"] }, "NesJumpSuggestion": { "description": "A jump-to-location suggestion.", "type": "object", "properties": { - "id": { + "suggestionId": { "description": "Unique identifier for accept/reject tracking.", - "type": "string" + "allOf": [ + { + "$ref": "#/$defs/NesSuggestionId" + } + ] }, "uri": { "description": "The file to navigate to.", @@ -4895,15 +4923,19 @@ "additionalProperties": true } }, - "required": ["id", "uri", "position"] + "required": ["suggestionId", "uri", "position"] }, "NesRenameSuggestion": { "description": "A rename symbol suggestion.", "type": "object", "properties": { - "id": { + "suggestionId": { "description": "Unique identifier for accept/reject tracking.", - "type": "string" + "allOf": [ + { + "$ref": "#/$defs/NesSuggestionId" + } + ] }, "uri": { "description": "The file URI containing the symbol.", @@ -4928,15 +4960,19 @@ "additionalProperties": true } }, - "required": ["id", "uri", "position", "newName"] + "required": ["suggestionId", "uri", "position", "newName"] }, "NesSearchAndReplaceSuggestion": { "description": "A search-and-replace suggestion.", "type": "object", "properties": { - "id": { + "suggestionId": { "description": "Unique identifier for accept/reject tracking.", - "type": "string" + "allOf": [ + { + "$ref": "#/$defs/NesSuggestionId" + } + ] }, "uri": { "description": "The file URI to search within.", @@ -4962,7 +4998,7 @@ "additionalProperties": true } }, - "required": ["id", "uri", "search", "replace"] + "required": ["suggestionId", "uri", "search", "replace"] }, "CloseNesResponse": { "description": "Response from closing an NES session.", @@ -6052,7 +6088,7 @@ "description": "Custom or future plan update content type.\n\nValues beginning with `_` are reserved for implementation-specific\nextensions. Unknown values that do not begin with `_` are reserved for\nfuture ACP variants.", "type": "string" }, - "id": { + "planId": { "description": "The plan ID to update.", "allOf": [ { @@ -6061,7 +6097,7 @@ ] } }, - "required": ["type", "id"], + "required": ["type", "planId"], "not": { "anyOf": [ { @@ -6194,7 +6230,7 @@ "description": "A plan represented as structured entries.", "type": "object", "properties": { - "id": { + "planId": { "description": "The plan ID to update.", "allOf": [ { @@ -6218,13 +6254,13 @@ "additionalProperties": true } }, - "required": ["id", "entries"] + "required": ["planId", "entries"] }, "PlanFile": { "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nA plan represented by a file URI.", "type": "object", "properties": { - "id": { + "planId": { "description": "The plan ID to update.", "allOf": [ { @@ -6243,13 +6279,13 @@ "additionalProperties": true } }, - "required": ["id", "uri"] + "required": ["planId", "uri"] }, "PlanMarkdown": { "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nA plan represented as raw markdown content.", "type": "object", "properties": { - "id": { + "planId": { "description": "The plan ID to update.", "allOf": [ { @@ -6268,7 +6304,7 @@ "additionalProperties": true } }, - "required": ["id", "content"] + "required": ["planId", "content"] }, "PlanUpdate": { "description": "A content update for a plan identified by ID.", @@ -6295,7 +6331,7 @@ "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nRemoval notice for a plan identified by ID.", "type": "object", "properties": { - "id": { + "planId": { "description": "The plan ID to remove.", "allOf": [ { @@ -6310,7 +6346,7 @@ "additionalProperties": true } }, - "required": ["id"] + "required": ["planId"] }, "AvailableCommand": { "description": "Information about a command.", @@ -7106,12 +7142,16 @@ "x-method": "providers/list" }, "SetProviderRequest": { - "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nRequest parameters for `providers/set`.\n\nReplaces the full configuration for one provider id.", + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nRequest parameters for `providers/set`.\n\nReplaces the full configuration for one provider ID.", "type": "object", "properties": { - "id": { - "description": "Provider id to configure.", - "type": "string" + "providerId": { + "description": "Provider ID to configure.", + "allOf": [ + { + "$ref": "#/$defs/ProviderId" + } + ] }, "apiType": { "description": "Protocol type for this provider.", @@ -7140,7 +7180,7 @@ "additionalProperties": true } }, - "required": ["id", "apiType", "baseUrl"], + "required": ["providerId", "apiType", "baseUrl"], "x-side": "agent", "x-method": "providers/set" }, @@ -7148,9 +7188,13 @@ "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nRequest parameters for `providers/disable`.", "type": "object", "properties": { - "id": { - "description": "Provider id to disable.", - "type": "string" + "providerId": { + "description": "Provider ID to disable.", + "allOf": [ + { + "$ref": "#/$defs/ProviderId" + } + ] }, "_meta": { "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility)", @@ -7159,7 +7203,7 @@ "additionalProperties": true } }, - "required": ["id"], + "required": ["providerId"], "x-side": "agent", "x-method": "providers/disable" }, @@ -7376,7 +7420,7 @@ "description": "Human-readable name identifying this MCP server.", "type": "string" }, - "id": { + "serverId": { "description": "Unique identifier for this MCP server, generated by the component providing it.\n\nProviders MUST NOT reuse an ID for multiple ACP-transport MCP servers that are visible\non the same ACP connection.", "allOf": [ { @@ -7391,7 +7435,7 @@ "additionalProperties": true } }, - "required": ["name", "id"] + "required": ["name", "serverId"] }, "McpServerStdio": { "description": "Stdio transport configuration for MCP.", @@ -8983,9 +9027,13 @@ } ] }, - "id": { + "suggestionId": { "description": "The ID of the accepted suggestion.", - "type": "string" + "allOf": [ + { + "$ref": "#/$defs/NesSuggestionId" + } + ] }, "_meta": { "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility)", @@ -8994,7 +9042,7 @@ "additionalProperties": true } }, - "required": ["sessionId", "id"], + "required": ["sessionId", "suggestionId"], "x-side": "agent", "x-method": "nes/accept" }, @@ -9010,9 +9058,13 @@ } ] }, - "id": { + "suggestionId": { "description": "The ID of the rejected suggestion.", - "type": "string" + "allOf": [ + { + "$ref": "#/$defs/NesSuggestionId" + } + ] }, "reason": { "description": "The reason for rejection.", @@ -9033,7 +9085,7 @@ "additionalProperties": true } }, - "required": ["sessionId", "id"], + "required": ["sessionId", "suggestionId"], "x-side": "agent", "x-method": "nes/reject" }, From 13fc21cb02c1693de6726fdb012da0da52621991 Mon Sep 17 00:00:00 2001 From: Ben Brandt Date: Wed, 1 Jul 2026 10:14:51 +0200 Subject: [PATCH 2/3] feat(unstable): type provider and NES IDs --- agent-client-protocol-schema/src/v1/agent.rs | 75 +++++++++----- agent-client-protocol-schema/src/v1/client.rs | 6 +- agent-client-protocol-schema/src/v1/mcp.rs | 4 +- agent-client-protocol-schema/src/v1/nes.rs | 46 ++++++--- agent-client-protocol-schema/src/v1/plan.rs | 36 +++---- agent-client-protocol-schema/src/v2/client.rs | 2 +- .../src/v2/conversion.rs | 88 +++++++++++------ docs/protocol/v1/draft/agent-plan.mdx | 10 +- docs/protocol/v1/draft/schema.mdx | 66 ++++++++----- docs/rfds/custom-llm-endpoint.mdx | 26 ++--- schema/v1/schema.unstable.json | 98 ++++++++++++++----- 11 files changed, 296 insertions(+), 161 deletions(-) diff --git a/agent-client-protocol-schema/src/v1/agent.rs b/agent-client-protocol-schema/src/v1/agent.rs index 4ca869818..1079d3eb1 100644 --- a/agent-client-protocol-schema/src/v1/agent.rs +++ b/agent-client-protocol-schema/src/v1/agent.rs @@ -3591,6 +3591,27 @@ impl ProviderCurrentConfig { } } +/// **UNSTABLE** +/// +/// This capability is not part of the spec yet, and may be removed or changed at any point. +/// +/// Unique identifier for a configurable LLM provider. +#[cfg(feature = "unstable_llm_providers")] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, Display, From)] +#[serde(transparent)] +#[from(Arc, String, &'static str)] +#[non_exhaustive] +pub struct ProviderId(pub Arc); + +#[cfg(feature = "unstable_llm_providers")] +impl ProviderId { + /// Wraps a protocol string as a typed [`ProviderId`]. + #[must_use] + pub fn new(id: impl Into>) -> Self { + Self(id.into()) + } +} + /// **UNSTABLE** /// /// This capability is not part of the spec yet, and may be removed or changed at any point. @@ -3604,13 +3625,13 @@ impl ProviderCurrentConfig { #[non_exhaustive] pub struct ProviderInfo { /// Provider identifier, for example "main" or "openai". - pub id: String, + pub provider_id: ProviderId, /// Supported protocol types for this provider. #[serde_as(deserialize_as = "DefaultOnError>")] #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))] pub supported: Vec, /// Whether this provider is mandatory and cannot be disabled via `providers/disable`. - /// If true, clients must not call `providers/disable` for this id. + /// If true, clients must not call `providers/disable` for this provider ID. pub required: bool, /// Current effective non-secret routing config. /// Null or omitted means provider is disabled. @@ -3635,13 +3656,13 @@ impl ProviderInfo { /// Builds [`ProviderInfo`] with the required fields set; optional fields start unset or empty. #[must_use] pub fn new( - id: impl Into, + provider_id: impl Into, supported: Vec, required: bool, current: impl IntoOption, ) -> Self { Self { - id: id.into(), + provider_id: provider_id.into(), supported, required, current: current.into_option(), @@ -3764,7 +3785,7 @@ impl ListProvidersResponse { /// /// Request parameters for `providers/set`. /// -/// Replaces the full configuration for one provider id. +/// Replaces the full configuration for one provider ID. #[cfg(feature = "unstable_llm_providers")] #[serde_as] #[skip_serializing_none] @@ -3773,8 +3794,8 @@ impl ListProvidersResponse { #[serde(rename_all = "camelCase")] #[non_exhaustive] pub struct SetProviderRequest { - /// Provider id to configure. - pub id: String, + /// Provider ID to configure. + pub provider_id: ProviderId, /// Protocol type for this provider. pub api_type: LlmProtocol, /// Base URL for requests sent through this provider. @@ -3801,9 +3822,13 @@ pub struct SetProviderRequest { impl SetProviderRequest { /// Builds [`SetProviderRequest`] with the required request fields set; optional fields start unset or empty. #[must_use] - pub fn new(id: impl Into, api_type: LlmProtocol, base_url: impl Into) -> Self { + pub fn new( + provider_id: impl Into, + api_type: LlmProtocol, + base_url: impl Into, + ) -> Self { Self { - id: id.into(), + provider_id: provider_id.into(), api_type, base_url: base_url.into(), headers: HashMap::new(), @@ -3889,8 +3914,8 @@ impl SetProviderResponse { #[serde(rename_all = "camelCase")] #[non_exhaustive] pub struct DisableProviderRequest { - /// Provider id to disable. - pub id: String, + /// Provider ID to disable. + pub provider_id: ProviderId, /// The _meta property is reserved by ACP to allow clients and agents to attach additional /// metadata to their interactions. Implementations MUST NOT make assumptions about values at /// these keys. @@ -3907,9 +3932,9 @@ pub struct DisableProviderRequest { impl DisableProviderRequest { /// Builds [`DisableProviderRequest`] with the required request fields set; optional fields start unset or empty. #[must_use] - pub fn new(id: impl Into) -> Self { + pub fn new(provider_id: impl Into) -> Self { Self { - id: id.into(), + provider_id: provider_id.into(), meta: None, } } @@ -5524,7 +5549,7 @@ mod test_serialization { json!({ "type": "acp", "name": "project-tools", - "id": "project-tools-id" + "serverId": "project-tools-id" }) ); @@ -6420,7 +6445,7 @@ mod test_serialization { assert_eq!( json, json!({ - "id": "main", + "providerId": "main", "supported": ["anthropic", "openai"], "required": true, "current": { @@ -6431,7 +6456,7 @@ mod test_serialization { ); let deserialized: ProviderInfo = serde_json::from_value(json).unwrap(); - assert_eq!(deserialized.id, "main"); + assert_eq!(deserialized.provider_id.to_string(), "main"); assert_eq!(deserialized.supported.len(), 2); assert!(deserialized.required); assert!(deserialized.current.is_some()); @@ -6455,14 +6480,14 @@ mod test_serialization { assert_eq!( json, json!({ - "id": "secondary", + "providerId": "secondary", "supported": ["openai"], "required": false }) ); let deserialized: ProviderInfo = serde_json::from_value(json).unwrap(); - assert_eq!(deserialized.id, "secondary"); + assert_eq!(deserialized.provider_id.to_string(), "secondary"); assert!(!deserialized.required); assert!(deserialized.current.is_none()); } @@ -6472,7 +6497,7 @@ mod test_serialization { fn test_provider_info_missing_current_defaults_to_none() { // current is optional; omitting it should decode as None let json = json!({ - "id": "main", + "providerId": "main", "supported": ["anthropic"], "required": true }); @@ -6487,7 +6512,7 @@ mod test_serialization { // both must deserialize into None so the disabled state is preserved // regardless of which form the peer chose to send. let json = json!({ - "id": "main", + "providerId": "main", "supported": ["anthropic"], "required": true, "current": null @@ -6511,7 +6536,7 @@ mod test_serialization { let json = serde_json::to_value(&response).unwrap(); assert_eq!(json["providers"].as_array().unwrap().len(), 1); - assert_eq!(json["providers"][0]["id"], "main"); + assert_eq!(json["providers"][0]["providerId"], "main"); let deserialized: ListProvidersResponse = serde_json::from_value(json).unwrap(); assert_eq!(deserialized.providers.len(), 1); @@ -6533,7 +6558,7 @@ mod test_serialization { assert_eq!( json, json!({ - "id": "main", + "providerId": "main", "apiType": "openai", "baseUrl": "https://api.openai.com/v1", "headers": { @@ -6543,7 +6568,7 @@ mod test_serialization { ); let deserialized: SetProviderRequest = serde_json::from_value(json).unwrap(); - assert_eq!(deserialized.id, "main"); + assert_eq!(deserialized.provider_id.to_string(), "main"); assert_eq!(deserialized.api_type, LlmProtocol::OpenAi); assert_eq!(deserialized.base_url, "https://api.openai.com/v1"); assert_eq!(deserialized.headers.len(), 1); @@ -6570,10 +6595,10 @@ mod test_serialization { let request = DisableProviderRequest::new("secondary"); let json = serde_json::to_value(&request).unwrap(); - assert_eq!(json, json!({ "id": "secondary" })); + assert_eq!(json, json!({ "providerId": "secondary" })); let deserialized: DisableProviderRequest = serde_json::from_value(json).unwrap(); - assert_eq!(deserialized.id, "secondary"); + assert_eq!(deserialized.provider_id.to_string(), "secondary"); } #[cfg(feature = "unstable_llm_providers")] diff --git a/agent-client-protocol-schema/src/v1/client.rs b/agent-client-protocol-schema/src/v1/client.rs index 57909c683..a2d29e4f5 100644 --- a/agent-client-protocol-schema/src/v1/client.rs +++ b/agent-client-protocol-schema/src/v1/client.rs @@ -2866,7 +2866,7 @@ mod tests { "sessionUpdate": "plan_update", "plan": { "type": "items", - "id": "plan-1", + "planId": "plan-1", "entries": [ { "content": "Step 1", @@ -2882,7 +2882,7 @@ mod tests { serde_json::to_value(SessionUpdate::PlanRemoved(PlanRemoved::new("plan-1"))).unwrap(), json!({ "sessionUpdate": "plan_removed", - "id": "plan-1" + "planId": "plan-1" }) ); @@ -2934,7 +2934,7 @@ mod tests { assert_eq!( serde_json::to_value(ConnectMcpRequest::new("server-1")).unwrap(), - json!({ "acpId": "server-1" }) + json!({ "serverId": "server-1" }) ); assert_eq!( serde_json::to_value(ConnectMcpResponse::new("conn-1")).unwrap(), diff --git a/agent-client-protocol-schema/src/v1/mcp.rs b/agent-client-protocol-schema/src/v1/mcp.rs index 2906191c0..39741bfdb 100644 --- a/agent-client-protocol-schema/src/v1/mcp.rs +++ b/agent-client-protocol-schema/src/v1/mcp.rs @@ -60,9 +60,9 @@ pub struct ConnectMcpRequest { impl ConnectMcpRequest { /// Builds [`ConnectMcpRequest`] with the required request fields set; optional fields start unset or empty. #[must_use] - pub fn new(acp_id: impl Into) -> Self { + pub fn new(server_id: impl Into) -> Self { Self { - server_id: acp_id.into(), + server_id: server_id.into(), meta: None, } } diff --git a/agent-client-protocol-schema/src/v1/nes.rs b/agent-client-protocol-schema/src/v1/nes.rs index 198773d03..c5beb4e43 100644 --- a/agent-client-protocol-schema/src/v1/nes.rs +++ b/agent-client-protocol-schema/src/v1/nes.rs @@ -4,6 +4,9 @@ //! document events, and a suggestion request/response flow. NES sessions are //! independent of chat sessions and have their own lifecycle. +use std::sync::Arc; + +use derive_more::{Display, From}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use serde_with::{DefaultOnError, VecSkipError, serde_as, skip_serializing_none}; @@ -35,6 +38,21 @@ pub(crate) const DOCUMENT_DID_SAVE_METHOD_NAME: &str = "document/didSave"; /// Notification name for document focus events. pub(crate) const DOCUMENT_DID_FOCUS_METHOD_NAME: &str = "document/didFocus"; +/// Unique identifier for a next edit suggestion. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, Display, From)] +#[serde(transparent)] +#[from(Arc, String, &'static str)] +#[non_exhaustive] +pub struct NesSuggestionId(pub Arc); + +impl NesSuggestionId { + /// Wraps a protocol string as a typed [`NesSuggestionId`]. + #[must_use] + pub fn new(id: impl Into>) -> Self { + Self(id.into()) + } +} + // Position primitives /// The encoding used for character offsets in positions. @@ -2345,7 +2363,7 @@ pub enum NesSuggestion { #[non_exhaustive] pub struct NesEditSuggestion { /// Unique identifier for accept/reject tracking. - pub id: String, + pub id: NesSuggestionId, /// The URI of the file to edit. pub uri: String, /// The text edits to apply. @@ -2372,7 +2390,11 @@ pub struct NesEditSuggestion { impl NesEditSuggestion { /// Builds [`NesEditSuggestion`] with the required fields set; optional fields start unset or empty. #[must_use] - pub fn new(id: impl Into, uri: impl Into, edits: Vec) -> Self { + pub fn new( + id: impl Into, + uri: impl Into, + edits: Vec, + ) -> Self { Self { id: id.into(), uri: uri.into(), @@ -2455,7 +2477,7 @@ impl NesTextEdit { #[non_exhaustive] pub struct NesJumpSuggestion { /// Unique identifier for accept/reject tracking. - pub id: String, + pub id: NesSuggestionId, /// The file to navigate to. pub uri: String, /// The target position within the file. @@ -2475,7 +2497,7 @@ pub struct NesJumpSuggestion { impl NesJumpSuggestion { /// Builds [`NesJumpSuggestion`] with the required fields set; optional fields start unset or empty. #[must_use] - pub fn new(id: impl Into, uri: impl Into, position: Position) -> Self { + pub fn new(id: impl Into, uri: impl Into, position: Position) -> Self { Self { id: id.into(), uri: uri.into(), @@ -2504,7 +2526,7 @@ impl NesJumpSuggestion { #[non_exhaustive] pub struct NesRenameSuggestion { /// Unique identifier for accept/reject tracking. - pub id: String, + pub id: NesSuggestionId, /// The file URI containing the symbol. pub uri: String, /// The position of the symbol to rename. @@ -2527,7 +2549,7 @@ impl NesRenameSuggestion { /// Builds [`NesRenameSuggestion`] with the required fields set; optional fields start unset or empty. #[must_use] pub fn new( - id: impl Into, + id: impl Into, uri: impl Into, position: Position, new_name: impl Into, @@ -2561,7 +2583,7 @@ impl NesRenameSuggestion { #[non_exhaustive] pub struct NesSearchAndReplaceSuggestion { /// Unique identifier for accept/reject tracking. - pub id: String, + pub id: NesSuggestionId, /// The file URI to search within. pub uri: String, /// The text or pattern to find. @@ -2589,7 +2611,7 @@ impl NesSearchAndReplaceSuggestion { /// Builds [`NesSearchAndReplaceSuggestion`] with the required fields set; optional fields start unset or empty. #[must_use] pub fn new( - id: impl Into, + id: impl Into, uri: impl Into, search: impl Into, replace: impl Into, @@ -2636,7 +2658,7 @@ pub struct AcceptNesNotification { /// The session ID for this notification. pub session_id: SessionId, /// The ID of the accepted suggestion. - pub id: String, + pub id: NesSuggestionId, /// The _meta property is reserved by ACP to allow clients and agents to attach additional /// metadata to their interactions. Implementations MUST NOT make assumptions about values at /// these keys. @@ -2652,7 +2674,7 @@ pub struct AcceptNesNotification { impl AcceptNesNotification { /// Builds [`AcceptNesNotification`] with the required notification fields set; optional fields start unset or empty. #[must_use] - pub fn new(session_id: impl Into, id: impl Into) -> Self { + pub fn new(session_id: impl Into, id: impl Into) -> Self { Self { session_id: session_id.into(), id: id.into(), @@ -2683,7 +2705,7 @@ pub struct RejectNesNotification { /// The session ID for this notification. pub session_id: SessionId, /// The ID of the rejected suggestion. - pub id: String, + pub id: NesSuggestionId, /// The reason for rejection. #[serde_as(deserialize_as = "DefaultOnError")] #[schemars(extend("x-deserialize-default-on-error" = true))] @@ -2704,7 +2726,7 @@ pub struct RejectNesNotification { impl RejectNesNotification { /// Builds [`RejectNesNotification`] with the required notification fields set; optional fields start unset or empty. #[must_use] - pub fn new(session_id: impl Into, id: impl Into) -> Self { + pub fn new(session_id: impl Into, id: impl Into) -> Self { Self { session_id: session_id.into(), id: id.into(), diff --git a/agent-client-protocol-schema/src/v1/plan.rs b/agent-client-protocol-schema/src/v1/plan.rs index 954e87468..dcc01808d 100644 --- a/agent-client-protocol-schema/src/v1/plan.rs +++ b/agent-client-protocol-schema/src/v1/plan.rs @@ -162,20 +162,20 @@ pub enum PlanUpdateContent { impl PlanUpdateContent { /// Builds a plan update that replaces the itemized entries for a plan. #[must_use] - pub fn items(id: impl Into, entries: Vec) -> Self { - Self::Items(PlanItems::new(id, entries)) + pub fn items(plan_id: impl Into, entries: Vec) -> Self { + Self::Items(PlanItems::new(plan_id, entries)) } /// Builds a plan update that points clients at an external plan file URI. #[must_use] - pub fn file(id: impl Into, uri: impl Into) -> Self { - Self::File(PlanFile::new(id, uri)) + pub fn file(plan_id: impl Into, uri: impl Into) -> Self { + Self::File(PlanFile::new(plan_id, uri)) } /// Builds a plan update whose plan content is inline Markdown. #[must_use] - pub fn markdown(id: impl Into, content: impl Into) -> Self { - Self::Markdown(PlanMarkdown::new(id, content)) + pub fn markdown(plan_id: impl Into, content: impl Into) -> Self { + Self::Markdown(PlanMarkdown::new(plan_id, content)) } } @@ -192,7 +192,7 @@ impl PlanUpdateContent { #[non_exhaustive] pub struct PlanItems { /// The plan ID to update. - pub id: PlanId, + pub plan_id: PlanId, /// The list of tasks to be accomplished. /// /// When updating an item-based plan, the agent must send a complete list of all entries @@ -216,9 +216,9 @@ pub struct PlanItems { impl PlanItems { /// Builds [`PlanItems`] with the required fields set; optional fields start unset or empty. #[must_use] - pub fn new(id: impl Into, entries: Vec) -> Self { + pub fn new(plan_id: impl Into, entries: Vec) -> Self { Self { - id: id.into(), + plan_id: plan_id.into(), entries, meta: None, } @@ -249,7 +249,7 @@ impl PlanItems { #[non_exhaustive] pub struct PlanFile { /// The plan ID to update. - pub id: PlanId, + pub plan_id: PlanId, /// The URI of the file containing the plan. pub uri: String, /// The _meta property is reserved by ACP to allow clients and agents to attach additional @@ -268,9 +268,9 @@ pub struct PlanFile { impl PlanFile { /// Builds [`PlanFile`] with the required fields set; optional fields start unset or empty. #[must_use] - pub fn new(id: impl Into, uri: impl Into) -> Self { + pub fn new(plan_id: impl Into, uri: impl Into) -> Self { Self { - id: id.into(), + plan_id: plan_id.into(), uri: uri.into(), meta: None, } @@ -301,7 +301,7 @@ impl PlanFile { #[non_exhaustive] pub struct PlanMarkdown { /// The plan ID to update. - pub id: PlanId, + pub plan_id: PlanId, /// Markdown content for the plan. pub content: String, /// The _meta property is reserved by ACP to allow clients and agents to attach additional @@ -320,9 +320,9 @@ pub struct PlanMarkdown { impl PlanMarkdown { /// Builds [`PlanMarkdown`] with the required fields set; optional fields start unset or empty. #[must_use] - pub fn new(id: impl Into, content: impl Into) -> Self { + pub fn new(plan_id: impl Into, content: impl Into) -> Self { Self { - id: id.into(), + plan_id: plan_id.into(), content: content.into(), meta: None, } @@ -353,7 +353,7 @@ impl PlanMarkdown { #[non_exhaustive] pub struct PlanRemoved { /// The plan ID to remove. - pub id: PlanId, + pub plan_id: PlanId, /// The _meta property is reserved by ACP to allow clients and agents to attach additional /// metadata to their interactions. Implementations MUST NOT make assumptions about values at /// these keys. @@ -370,9 +370,9 @@ pub struct PlanRemoved { impl PlanRemoved { /// Builds [`PlanRemoved`] with the required fields set; optional fields start unset or empty. #[must_use] - pub fn new(id: impl Into) -> Self { + pub fn new(plan_id: impl Into) -> Self { Self { - id: id.into(), + plan_id: plan_id.into(), meta: None, } } diff --git a/agent-client-protocol-schema/src/v2/client.rs b/agent-client-protocol-schema/src/v2/client.rs index d8080a894..03ea7a3d8 100644 --- a/agent-client-protocol-schema/src/v2/client.rs +++ b/agent-client-protocol-schema/src/v2/client.rs @@ -2573,7 +2573,7 @@ mod tests { assert_eq!( serde_json::to_value(ConnectMcpRequest::new("server-1")).unwrap(), - json!({ "mcpServerId": "server-1" }) + json!({ "serverId": "server-1" }) ); assert_eq!( serde_json::to_value(ConnectMcpResponse::new("conn-1")).unwrap(), diff --git a/agent-client-protocol-schema/src/v2/conversion.rs b/agent-client-protocol-schema/src/v2/conversion.rs index ef68371f5..8d4ef98cd 100644 --- a/agent-client-protocol-schema/src/v2/conversion.rs +++ b/agent-client-protocol-schema/src/v2/conversion.rs @@ -715,7 +715,7 @@ impl IntoV1 for super::PlanItems { meta, } = self; Ok(crate::v1::PlanItems { - id: plan_id.into_v1()?, + plan_id: plan_id.into_v1()?, entries: into_v1_vec_skip_errors(entries), meta: meta.into_v1()?, }) @@ -727,9 +727,13 @@ impl IntoV2 for crate::v1::PlanItems { type Output = super::PlanItems; fn into_v2(self) -> Result { - let Self { id, entries, meta } = self; + let Self { + plan_id, + entries, + meta, + } = self; Ok(super::PlanItems { - plan_id: id.into_v2()?, + plan_id: plan_id.into_v2()?, entries: into_v2_vec_skip_errors(entries), meta: meta.into_v2()?, }) @@ -743,7 +747,7 @@ impl IntoV1 for super::PlanFile { fn into_v1(self) -> Result { let Self { plan_id, uri, meta } = self; Ok(crate::v1::PlanFile { - id: plan_id.into_v1()?, + plan_id: plan_id.into_v1()?, uri: uri.into_v1()?, meta: meta.into_v1()?, }) @@ -755,9 +759,9 @@ impl IntoV2 for crate::v1::PlanFile { type Output = super::PlanFile; fn into_v2(self) -> Result { - let Self { id, uri, meta } = self; + let Self { plan_id, uri, meta } = self; Ok(super::PlanFile { - plan_id: id.into_v2()?, + plan_id: plan_id.into_v2()?, uri: uri.into_v2()?, meta: meta.into_v2()?, }) @@ -775,7 +779,7 @@ impl IntoV1 for super::PlanMarkdown { meta, } = self; Ok(crate::v1::PlanMarkdown { - id: plan_id.into_v1()?, + plan_id: plan_id.into_v1()?, content: content.into_v1()?, meta: meta.into_v1()?, }) @@ -787,9 +791,13 @@ impl IntoV2 for crate::v1::PlanMarkdown { type Output = super::PlanMarkdown; fn into_v2(self) -> Result { - let Self { id, content, meta } = self; + let Self { + plan_id, + content, + meta, + } = self; Ok(super::PlanMarkdown { - plan_id: id.into_v2()?, + plan_id: plan_id.into_v2()?, content: content.into_v2()?, meta: meta.into_v2()?, }) @@ -803,7 +811,7 @@ impl IntoV1 for super::PlanRemoved { fn into_v1(self) -> Result { let Self { plan_id, meta } = self; Ok(crate::v1::PlanRemoved { - id: plan_id.into_v1()?, + plan_id: plan_id.into_v1()?, meta: meta.into_v1()?, }) } @@ -814,9 +822,9 @@ impl IntoV2 for crate::v1::PlanRemoved { type Output = super::PlanRemoved; fn into_v2(self) -> Result { - let Self { id, meta } = self; + let Self { plan_id, meta } = self; Ok(super::PlanRemoved { - plan_id: id.into_v2()?, + plan_id: plan_id.into_v2()?, meta: meta.into_v2()?, }) } @@ -4552,10 +4560,19 @@ impl IntoV2 for crate::v1::ProviderCurrentConfig { #[cfg(feature = "unstable_llm_providers")] impl IntoV1 for super::ProviderId { - type Output = String; + type Output = crate::v1::ProviderId; fn into_v1(self) -> Result { - Ok(self.0.to_string()) + Ok(crate::v1::ProviderId(self.0.into_v1()?)) + } +} + +#[cfg(feature = "unstable_llm_providers")] +impl IntoV2 for crate::v1::ProviderId { + type Output = super::ProviderId; + + fn into_v2(self) -> Result { + Ok(super::ProviderId(self.0.into_v2()?)) } } @@ -4572,7 +4589,7 @@ impl IntoV1 for super::ProviderInfo { meta, } = self; Ok(crate::v1::ProviderInfo { - id: provider_id.into_v1()?, + provider_id: provider_id.into_v1()?, supported: into_v1_vec_skip_errors(supported), required: required.into_v1()?, current: current.into_v1()?, @@ -4587,14 +4604,14 @@ impl IntoV2 for crate::v1::ProviderInfo { fn into_v2(self) -> Result { let Self { - id, + provider_id, supported, required, current, meta, } = self; Ok(super::ProviderInfo { - provider_id: super::ProviderId::new(id.into_v2()?), + provider_id: provider_id.into_v2()?, supported: into_v2_vec_skip_errors(supported), required: required.into_v2()?, current: current.into_v2()?, @@ -4666,7 +4683,7 @@ impl IntoV1 for super::SetProviderRequest { meta, } = self; Ok(crate::v1::SetProviderRequest { - id: provider_id.into_v1()?, + provider_id: provider_id.into_v1()?, api_type: api_type.into_v1()?, base_url: base_url.into_v1()?, headers: headers.into_v1()?, @@ -4681,14 +4698,14 @@ impl IntoV2 for crate::v1::SetProviderRequest { fn into_v2(self) -> Result { let Self { - id, + provider_id, api_type, base_url, headers, meta, } = self; Ok(super::SetProviderRequest { - provider_id: super::ProviderId::new(id.into_v2()?), + provider_id: provider_id.into_v2()?, api_type: api_type.into_v2()?, base_url: base_url.into_v2()?, headers: headers.into_v2()?, @@ -4728,7 +4745,7 @@ impl IntoV1 for super::DisableProviderRequest { fn into_v1(self) -> Result { let Self { provider_id, meta } = self; Ok(crate::v1::DisableProviderRequest { - id: provider_id.into_v1()?, + provider_id: provider_id.into_v1()?, meta: meta.into_v1()?, }) } @@ -4739,9 +4756,9 @@ impl IntoV2 for crate::v1::DisableProviderRequest { type Output = super::DisableProviderRequest; fn into_v2(self) -> Result { - let Self { id, meta } = self; + let Self { provider_id, meta } = self; Ok(super::DisableProviderRequest { - provider_id: super::ProviderId::new(id.into_v2()?), + provider_id: provider_id.into_v2()?, meta: meta.into_v2()?, }) } @@ -7204,10 +7221,19 @@ impl IntoV2 for crate::v1::NesSuggestion { #[cfg(feature = "unstable_nes")] impl IntoV1 for super::NesSuggestionId { - type Output = String; + type Output = crate::v1::NesSuggestionId; fn into_v1(self) -> Result { - Ok(self.0.to_string()) + Ok(crate::v1::NesSuggestionId(self.0.into_v1()?)) + } +} + +#[cfg(feature = "unstable_nes")] +impl IntoV2 for crate::v1::NesSuggestionId { + type Output = super::NesSuggestionId; + + fn into_v2(self) -> Result { + Ok(super::NesSuggestionId(self.0.into_v2()?)) } } @@ -7246,7 +7272,7 @@ impl IntoV2 for crate::v1::NesEditSuggestion { meta, } = self; Ok(super::NesEditSuggestion { - suggestion_id: super::NesSuggestionId::new(id.into_v2()?), + suggestion_id: id.into_v2()?, uri: uri.into_v2()?, edits: edits.into_v2()?, cursor_position: into_v2_default_on_error(cursor_position), @@ -7323,7 +7349,7 @@ impl IntoV2 for crate::v1::NesJumpSuggestion { meta, } = self; Ok(super::NesJumpSuggestion { - suggestion_id: super::NesSuggestionId::new(id.into_v2()?), + suggestion_id: id.into_v2()?, uri: uri.into_v2()?, position: position.into_v2()?, meta: meta.into_v2()?, @@ -7366,7 +7392,7 @@ impl IntoV2 for crate::v1::NesRenameSuggestion { meta, } = self; Ok(super::NesRenameSuggestion { - suggestion_id: super::NesSuggestionId::new(id.into_v2()?), + suggestion_id: id.into_v2()?, uri: uri.into_v2()?, position: position.into_v2()?, new_name: new_name.into_v2()?, @@ -7413,7 +7439,7 @@ impl IntoV2 for crate::v1::NesSearchAndReplaceSuggestion { meta, } = self; Ok(super::NesSearchAndReplaceSuggestion { - suggestion_id: super::NesSuggestionId::new(id.into_v2()?), + suggestion_id: id.into_v2()?, uri: uri.into_v2()?, search: search.into_v2()?, replace: replace.into_v2()?, @@ -7453,7 +7479,7 @@ impl IntoV2 for crate::v1::AcceptNesNotification { } = self; Ok(super::AcceptNesNotification { session_id: session_id.into_v2()?, - suggestion_id: super::NesSuggestionId::new(id.into_v2()?), + suggestion_id: id.into_v2()?, meta: meta.into_v2()?, }) } @@ -7492,7 +7518,7 @@ impl IntoV2 for crate::v1::RejectNesNotification { } = self; Ok(super::RejectNesNotification { session_id: session_id.into_v2()?, - suggestion_id: super::NesSuggestionId::new(id.into_v2()?), + suggestion_id: id.into_v2()?, reason: into_v2_default_on_error(reason), meta: meta.into_v2()?, }) diff --git a/docs/protocol/v1/draft/agent-plan.mdx b/docs/protocol/v1/draft/agent-plan.mdx index a85aa94e1..d83ad4dee 100644 --- a/docs/protocol/v1/draft/agent-plan.mdx +++ b/docs/protocol/v1/draft/agent-plan.mdx @@ -52,7 +52,7 @@ When the language model creates an execution plan, the Agent **SHOULD** report i Agents **MUST NOT** send `plan_update` or `plan_removed` unless the Client advertised `plan`. If the Client omits `plan`, the Agent **MUST** fall back to the existing `plan` update. -`plan_update` carries a `plan` object with a `type` discriminator. Every plan format includes a required `id` so Clients can track multiple plans independently. +`plan_update` carries a `plan` object with a `type` discriminator. Every plan format includes a required `planId` so Clients can track multiple plans independently. ### Item-Based Plans @@ -66,7 +66,7 @@ Agents **MUST NOT** send `plan_update` or `plan_removed` unless the Client adver "sessionUpdate": "plan_update", "plan": { "type": "items", - "id": "plan-1", + "planId": "plan-1", "entries": [ { "content": "Analyze the existing codebase structure", @@ -92,7 +92,7 @@ Agents **MUST NOT** send `plan_update` or `plan_removed` unless the Client adver "sessionUpdate": "plan_update", "plan": { "type": "markdown", - "id": "implementation-plan", + "planId": "implementation-plan", "content": "## Steps\n- [ ] Refactor module\n- [ ] Add tests" } } @@ -112,7 +112,7 @@ Agents **MUST NOT** send `plan_update` or `plan_removed` unless the Client adver "sessionUpdate": "plan_update", "plan": { "type": "file", - "id": "design-doc", + "planId": "design-doc", "uri": "file:///tmp/plan.md" } } @@ -132,7 +132,7 @@ Agents can remove a plan by sending `plan_removed` with the plan ID: "sessionId": "sess_abc123def456", "update": { "sessionUpdate": "plan_removed", - "id": "plan-1" + "planId": "plan-1" } } } diff --git a/docs/protocol/v1/draft/schema.mdx b/docs/protocol/v1/draft/schema.mdx index 72dfb16ff..1daff3eea 100644 --- a/docs/protocol/v1/draft/schema.mdx +++ b/docs/protocol/v1/draft/schema.mdx @@ -491,7 +491,7 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/draft/extensibility) - +NesSuggestionId} required> The ID of the accepted suggestion. SessionId} required> @@ -573,7 +573,7 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/draft/extensibility) - +NesSuggestionId} required> The ID of the rejected suggestion. NesRejectReason | null} > @@ -734,8 +734,8 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/draft/extensibility) - - Provider id to disable. +ProviderId} required> + Provider ID to disable. #### DisableProviderResponse @@ -830,7 +830,7 @@ This capability is not part of the spec yet, and may be removed or changed at an Request parameters for `providers/set`. -Replaces the full configuration for one provider id. +Replaces the full configuration for one provider ID. **Type:** Object @@ -854,8 +854,8 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/d Full headers map for this provider. May include authorization, routing, or other integration-specific headers. - - Provider id to configure. +ProviderId} required> + Provider ID to configure. #### SetProviderResponse @@ -5434,7 +5434,7 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/d NesTextEdit[]} required> The text edits to apply. - +NesSuggestionId} required> Unique identifier for accept/reject tracking. @@ -5526,7 +5526,7 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/draft/extensibility) - +NesSuggestionId} required> Unique identifier for accept/reject tracking. Position} required> @@ -5729,7 +5729,7 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/draft/extensibility) - +NesSuggestionId} required> Unique identifier for accept/reject tracking. @@ -5801,7 +5801,7 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/draft/extensibility) - +NesSuggestionId} required> Unique identifier for accept/reject tracking. @@ -5877,7 +5877,7 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/d NesTextEdit[]} required> The text edits to apply. - +NesSuggestionId} required> Unique identifier for accept/reject tracking. @@ -5903,7 +5903,7 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/draft/extensibility) - +NesSuggestionId} required> Unique identifier for accept/reject tracking. @@ -5932,7 +5932,7 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/draft/extensibility) - +NesSuggestionId} required> Unique identifier for accept/reject tracking. @@ -5964,7 +5964,7 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/draft/extensibility) - +NesSuggestionId} required> Unique identifier for accept/reject tracking. @@ -5986,6 +5986,12 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/d +## NesSuggestionId + +Unique identifier for a next edit suggestion. + +**Type:** `string` + ## NesTextEdit A text edit within a suggestion. @@ -6315,7 +6321,7 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/draft/extensibility) -PlanId} required> +PlanId} required> The plan ID to update. @@ -6359,7 +6365,7 @@ When updating an item-based plan, the agent must send a complete list of all ent with their current status. The client replaces that plan with each update. -PlanId} required> +PlanId} required> The plan ID to update. @@ -6386,7 +6392,7 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/d Markdown content for the plan. -PlanId} required> +PlanId} required> The plan ID to update. @@ -6410,7 +6416,7 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/draft/extensibility) -PlanId} required> +PlanId} required> The plan ID to remove. @@ -6468,7 +6474,7 @@ When updating an item-based plan, the agent must send a complete list of all ent with their current status. The client replaces that plan with each update. -PlanId} required> +PlanId} required> The plan ID to update. @@ -6491,7 +6497,7 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/draft/extensibility) -PlanId} required> +PlanId} required> The plan ID to update. @@ -6520,7 +6526,7 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/d Markdown content for the plan. -PlanId} required> +PlanId} required> The plan ID to update. @@ -6671,6 +6677,16 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/d Base URL currently used by this provider. +## ProviderId + +**UNSTABLE** + +This capability is not part of the spec yet, and may be removed or changed at any point. + +Unique identifier for a configurable LLM provider. + +**Type:** `string` + ## ProviderInfo **UNSTABLE** @@ -6695,12 +6711,12 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/d Current effective non-secret routing config. Null or omitted means provider is disabled. - +ProviderId} required> Provider identifier, for example "main" or "openai". Whether this provider is mandatory and cannot be disabled via `providers/disable`. -If true, clients must not call `providers/disable` for this id. +If true, clients must not call `providers/disable` for this provider ID. LlmProtocol[]} required> Supported protocol types for this provider. @@ -7731,7 +7747,7 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/draft/extensibility) -PlanId} required> +PlanId} required> The plan ID to remove. diff --git a/docs/rfds/custom-llm-endpoint.mdx b/docs/rfds/custom-llm-endpoint.mdx index 39c1468ee..b40ed5f4e 100644 --- a/docs/rfds/custom-llm-endpoint.mdx +++ b/docs/rfds/custom-llm-endpoint.mdx @@ -77,7 +77,7 @@ sequenceDiagram 1. Client initializes and checks `agentCapabilities.providers`. 2. Client calls `providers/list` to discover available providers, their current routing targets (or disabled state), supported protocol types, and whether they are required. -3. Client calls `providers/set` to apply new (required) configuration for a specific provider id. +3. Client calls `providers/set` to apply new (required) configuration for a specific provider ID. 4. Client may call `providers/disable` when a non-required provider should be disabled. 5. Client creates or loads sessions. @@ -123,16 +123,18 @@ interface ProviderCurrentConfig { baseUrl: string; } +type ProviderId = string; + interface ProviderInfo { /** Provider identifier, for example "main" or "openai". */ - id: string; + providerId: ProviderId; /** Supported protocol types for this provider. */ supported: LlmProtocol[]; /** * Whether this provider is mandatory and cannot be disabled via providers/disable. - * If true, clients must not call providers/disable for this id. + * If true, clients must not call providers/disable for this provider ID. */ required: boolean; @@ -166,12 +168,12 @@ interface ProvidersListResponse { ### `providers/set` -`providers/set` updates the full configuration for one provider id. +`providers/set` updates the full configuration for one provider ID. ```typescript interface SetProviderRequest { - /** Provider id to configure. */ - id: string; + /** Provider ID to configure. */ + providerId: ProviderId; /** Protocol type for this provider. */ apiType: LlmProtocol; @@ -200,8 +202,8 @@ interface SetProviderResponse { ```typescript interface DisableProviderRequest { - /** Provider id to disable. */ - id: string; + /** Provider ID to disable. */ + providerId: ProviderId; /** Extension metadata */ _meta?: Record; @@ -255,7 +257,7 @@ interface DisableProviderResponse { "result": { "providers": [ { - "id": "main", + "providerId": "main", "supported": ["bedrock", "vertex", "azure", "anthropic"], "required": true, "current": { @@ -264,7 +266,7 @@ interface DisableProviderResponse { } }, { - "id": "openai", + "providerId": "openai", "supported": ["openai"], "required": false, "current": null @@ -282,7 +284,7 @@ interface DisableProviderResponse { "id": 2, "method": "providers/set", "params": { - "id": "main", + "providerId": "main", "apiType": "anthropic", "baseUrl": "https://llm-gateway.corp.example.com/anthropic/v1", "headers": { @@ -310,7 +312,7 @@ interface DisableProviderResponse { "id": 3, "method": "providers/disable", "params": { - "id": "openai" + "providerId": "openai" } } ``` diff --git a/schema/v1/schema.unstable.json b/schema/v1/schema.unstable.json index 7141cc6b1..76af7aba5 100644 --- a/schema/v1/schema.unstable.json +++ b/schema/v1/schema.unstable.json @@ -3530,9 +3530,13 @@ "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nInformation about a configurable LLM provider.", "type": "object", "properties": { - "id": { + "providerId": { "description": "Provider identifier, for example \"main\" or \"openai\".", - "type": "string" + "allOf": [ + { + "$ref": "#/$defs/ProviderId" + } + ] }, "supported": { "description": "Supported protocol types for this provider.", @@ -3544,7 +3548,7 @@ "x-deserialize-skip-invalid-items": true }, "required": { - "description": "Whether this provider is mandatory and cannot be disabled via `providers/disable`.\nIf true, clients must not call `providers/disable` for this id.", + "description": "Whether this provider is mandatory and cannot be disabled via `providers/disable`.\nIf true, clients must not call `providers/disable` for this provider ID.", "type": "boolean" }, "current": { @@ -3566,7 +3570,11 @@ "additionalProperties": true } }, - "required": ["id", "supported", "required"] + "required": ["providerId", "supported", "required"] + }, + "ProviderId": { + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nUnique identifier for a configurable LLM provider.", + "type": "string" }, "LlmProtocol": { "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nWell-known API protocol identifiers for LLM providers.\n\nAgents and clients MUST handle unknown protocol identifiers gracefully.\n\nProtocol names beginning with `_` are free for custom use, like other ACP extension methods.\nProtocol names that do not begin with `_` are reserved for the ACP spec.", @@ -4507,6 +4515,10 @@ "propertyName": "kind" } }, + "NesSuggestionId": { + "description": "Unique identifier for a next edit suggestion.", + "type": "string" + }, "NesTextEdit": { "description": "A text edit within a suggestion.", "type": "object", @@ -4592,7 +4604,11 @@ "properties": { "id": { "description": "Unique identifier for accept/reject tracking.", - "type": "string" + "allOf": [ + { + "$ref": "#/$defs/NesSuggestionId" + } + ] }, "uri": { "description": "The URI of the file to edit.", @@ -4634,7 +4650,11 @@ "properties": { "id": { "description": "Unique identifier for accept/reject tracking.", - "type": "string" + "allOf": [ + { + "$ref": "#/$defs/NesSuggestionId" + } + ] }, "uri": { "description": "The file to navigate to.", @@ -4663,7 +4683,11 @@ "properties": { "id": { "description": "Unique identifier for accept/reject tracking.", - "type": "string" + "allOf": [ + { + "$ref": "#/$defs/NesSuggestionId" + } + ] }, "uri": { "description": "The file URI containing the symbol.", @@ -4696,7 +4720,11 @@ "properties": { "id": { "description": "Unique identifier for accept/reject tracking.", - "type": "string" + "allOf": [ + { + "$ref": "#/$defs/NesSuggestionId" + } + ] }, "uri": { "description": "The file URI to search within.", @@ -5416,7 +5444,7 @@ "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nA plan represented as structured entries.", "type": "object", "properties": { - "id": { + "planId": { "description": "The plan ID to update.", "allOf": [ { @@ -5440,13 +5468,13 @@ "additionalProperties": true } }, - "required": ["id", "entries"] + "required": ["planId", "entries"] }, "PlanFile": { "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nA plan represented by a file URI.", "type": "object", "properties": { - "id": { + "planId": { "description": "The plan ID to update.", "allOf": [ { @@ -5465,13 +5493,13 @@ "additionalProperties": true } }, - "required": ["id", "uri"] + "required": ["planId", "uri"] }, "PlanMarkdown": { "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nA plan represented as raw markdown content.", "type": "object", "properties": { - "id": { + "planId": { "description": "The plan ID to update.", "allOf": [ { @@ -5490,7 +5518,7 @@ "additionalProperties": true } }, - "required": ["id", "content"] + "required": ["planId", "content"] }, "PlanUpdate": { "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nA content update for a plan identified by ID.", @@ -5517,7 +5545,7 @@ "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nRemoval notice for a plan identified by ID.", "type": "object", "properties": { - "id": { + "planId": { "description": "The plan ID to remove.", "allOf": [ { @@ -5532,7 +5560,7 @@ "additionalProperties": true } }, - "required": ["id"] + "required": ["planId"] }, "AvailableCommand": { "description": "Information about a command.", @@ -6475,12 +6503,16 @@ "x-method": "providers/list" }, "SetProviderRequest": { - "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nRequest parameters for `providers/set`.\n\nReplaces the full configuration for one provider id.", + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nRequest parameters for `providers/set`.\n\nReplaces the full configuration for one provider ID.", "type": "object", "properties": { - "id": { - "description": "Provider id to configure.", - "type": "string" + "providerId": { + "description": "Provider ID to configure.", + "allOf": [ + { + "$ref": "#/$defs/ProviderId" + } + ] }, "apiType": { "description": "Protocol type for this provider.", @@ -6509,7 +6541,7 @@ "additionalProperties": true } }, - "required": ["id", "apiType", "baseUrl"], + "required": ["providerId", "apiType", "baseUrl"], "x-side": "agent", "x-method": "providers/set" }, @@ -6517,9 +6549,13 @@ "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nRequest parameters for `providers/disable`.", "type": "object", "properties": { - "id": { - "description": "Provider id to disable.", - "type": "string" + "providerId": { + "description": "Provider ID to disable.", + "allOf": [ + { + "$ref": "#/$defs/ProviderId" + } + ] }, "_meta": { "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", @@ -6528,7 +6564,7 @@ "additionalProperties": true } }, - "required": ["id"], + "required": ["providerId"], "x-side": "agent", "x-method": "providers/disable" }, @@ -8550,7 +8586,11 @@ }, "id": { "description": "The ID of the accepted suggestion.", - "type": "string" + "allOf": [ + { + "$ref": "#/$defs/NesSuggestionId" + } + ] }, "_meta": { "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", @@ -8577,7 +8617,11 @@ }, "id": { "description": "The ID of the rejected suggestion.", - "type": "string" + "allOf": [ + { + "$ref": "#/$defs/NesSuggestionId" + } + ] }, "reason": { "description": "The reason for rejection.", From 610a7b2ea1cf9b6db87e5c807c62aedd677a2f01 Mon Sep 17 00:00:00 2001 From: Ben Brandt Date: Wed, 1 Jul 2026 10:17:05 +0200 Subject: [PATCH 3/3] Update RFD --- docs/rfds/v2/overview.mdx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/rfds/v2/overview.mdx b/docs/rfds/v2/overview.mdx index 0b3c20d4a..3eb54666f 100644 --- a/docs/rfds/v2/overview.mdx +++ b/docs/rfds/v2/overview.mdx @@ -62,6 +62,7 @@ Other RFDs will progress separately and are not dependent on breaking changes (s - Make stdio an explicit `session.mcp.stdio` capability so Agents that cannot launch local subprocesses can opt out. - Require MCP server configurations to include a `type` discriminator, including `type: "stdio"`, so unknown future transports can be preserved as extension/future variants. - Keep HTTP as the remote MCP server transport capability. +- Unify ID naming and typing across the v2 schema. Protocol fields should use domain-specific ID names such as `messageId`, `toolCallId`, `planId`, `providerId`, and `serverId` rather than a generic `id` whenever the field identifies a protocol entity or references another resource. ### RFDs to be Written @@ -108,6 +109,7 @@ With the needed breaking changes, as much as possible I am targeting having a co ## Revision history +- 2026-06-30: Recorded the v2 ID unification principle: prefer domain-specific ID field names. - 2026-06-25: Recorded the v2 initialize information cleanup: required role-agnostic `info` in both params and results, replacing `clientInfo` and `agentInfo`. - 2026-06-25: Recorded the v2 authentication method cleanup: grouped `auth/login` and required `auth/logout` method names replace v1's top-level `authenticate` and capability-gated `logout`, with matching generated type names for login and logout auth requests and responses. - 2026-06-09: Added tool-call content chunks for streaming individual `ToolCallContent` items.