From 5a421fed160183395f020656e6f0dbcb6792f72a Mon Sep 17 00:00:00 2001 From: Ben Brandt Date: Sat, 14 Nov 2026 12:41:27 +0100 Subject: [PATCH] feat(unstable-v2): add skill command entries and skill content blocks Implement the Agent Skills RFD in the v2 schema behind the `unstable_agent_skills` feature. - Add `AvailableCommand::Skill`, a `skill` command entry with `name`, `description`, and an optional absolute `path` to the skill's file. - Add `ContentBlock::Skill`, a `skill` content block that names the skill a prompt invokes. - Add the `session.commands.skill` Client capability and the `session.prompt.skill` Agent capability, with its `inline` level. The Client's `session` capability group is now shared with prompt suggestions. - List `skill` as a known discriminator in both unions, so a malformed entry or block never falls through to the `Other` fallback. - Document the entry, the block, and both capabilities in the draft v2 protocol pages. - Remove `input` from the `skill` entry in the RFD. The Agent Skills specification defines no arguments for a skill. --- agent-client-protocol-schema/Cargo.toml | 2 + agent-client-protocol-schema/src/v2/agent.rs | 185 +++++++ agent-client-protocol-schema/src/v2/client.rs | 465 +++++++++++++++++- .../src/v2/content.rs | 130 ++++- .../tests/experimental_compatibility.rs | 76 ++- docs/protocol/v2/draft/content.mdx | 23 + docs/protocol/v2/draft/extensibility.mdx | 19 +- docs/protocol/v2/draft/initialization.mdx | 37 ++ docs/protocol/v2/draft/schema.mdx | 303 ++++++++++++ docs/protocol/v2/draft/slash-commands.mdx | 99 +++- docs/rfds/agent-skills.mdx | 21 +- schema/v2/schema.unstable.json | 198 ++++++++ 12 files changed, 1528 insertions(+), 30 deletions(-) diff --git a/agent-client-protocol-schema/Cargo.toml b/agent-client-protocol-schema/Cargo.toml index 41cdc0bcb..96d2c2b84 100644 --- a/agent-client-protocol-schema/Cargo.toml +++ b/agent-client-protocol-schema/Cargo.toml @@ -27,6 +27,7 @@ default = ["schemars"] # omit the dependency and those impls with `default-features = false`. schemars = ["dep:schemars", "serde_with/schemars_1"] unstable = [ + "unstable_agent_skills", "unstable_auth_status", "unstable_llm_providers", "unstable_mcp_over_acp", @@ -46,6 +47,7 @@ unstable = [ # It introduces a parallel `v2` module with a different wire version, so it # must be opted into explicitly. unstable_protocol_v2 = [] +unstable_agent_skills = [] unstable_auth_status = [] unstable_llm_providers = [] unstable_mcp_over_acp = [] diff --git a/agent-client-protocol-schema/src/v2/agent.rs b/agent-client-protocol-schema/src/v2/agent.rs index 4c0120d12..94532874e 100644 --- a/agent-client-protocol-schema/src/v2/agent.rs +++ b/agent-client-protocol-schema/src/v2/agent.rs @@ -6929,6 +6929,20 @@ pub struct PromptCapabilities { #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))] #[serde(default)] pub embedded_context: Option, + /// **UNSTABLE** + /// + /// This capability is not part of the spec yet, and may be removed or changed at any point. + /// + /// Agent supports `skill` content blocks. + /// + /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support. + /// Supplying `{}` means a prompt may contain one `skill` content block, as its first block. + /// An agent that sends `skill` entries in `availableCommands` MUST advertise this capability. + #[cfg(feature = "unstable_agent_skills")] + #[serde_as(deserialize_as = "DefaultOnError")] + #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))] + #[serde(default)] + pub skill: Option, /// The _meta property is reserved by ACP to allow clients and agents to attach additional /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata. /// Implementations MUST NOT assign meaning to keys they do not recognize. @@ -6984,6 +6998,21 @@ impl PromptCapabilities { self } + /// **UNSTABLE** + /// + /// This capability is not part of the spec yet, and may be removed or changed at any point. + /// + /// Agent supports `skill` content blocks. + /// + /// Omitted and `null` are equivalent and mean the agent does not advertise support. + /// Supplying `{}` means a prompt may contain one `skill` content block, as its first block. + #[cfg(feature = "unstable_agent_skills")] + #[must_use] + pub fn skill(mut self, skill: impl IntoOption) -> Self { + self.skill = skill.into_option(); + self + } + /// The _meta property is reserved by ACP to allow clients and agents to attach additional /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they /// do not recognize. @@ -7116,6 +7145,121 @@ impl PromptEmbeddedContextCapabilities { } } +/// **UNSTABLE** +/// +/// This capability is not part of the spec yet, and may be removed or changed at any point. +/// +/// Capabilities for `skill` content blocks in prompt requests. +/// +/// Supplying `{}` means a prompt may contain one `skill` content block, as +/// its first block. The agent opts in to more with the `inline` field. +#[cfg(feature = "unstable_agent_skills")] +#[serde_as] +#[skip_serializing_none] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] +#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct PromptSkillCapabilities { + /// Agent supports `skill` content blocks at any position in a prompt. + /// + /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support. + /// Supplying `{}` means a prompt may contain any number of `skill` content blocks, at any + /// position. + #[serde_as(deserialize_as = "DefaultOnError")] + #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))] + #[serde(default)] + pub inline: Option, + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata. + /// Implementations MUST NOT assign meaning to keys they do not recognize. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility) + #[serde_as(deserialize_as = "DefaultOnError")] + #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))] + #[serde(default)] + #[serde(rename = "_meta")] + pub meta: Option, +} + +#[cfg(feature = "unstable_agent_skills")] +impl PromptSkillCapabilities { + /// Builds an empty [`PromptSkillCapabilities`]. Use builder methods to advertise supported sub-capabilities. + #[must_use] + pub fn new() -> Self { + Self::default() + } + + /// Agent supports `skill` content blocks at any position in a prompt. + /// + /// Omitted and `null` are equivalent and mean the agent does not advertise support. + /// Supplying `{}` means a prompt may contain any number of `skill` content blocks, at any + /// position. + #[must_use] + pub fn inline(mut self, inline: impl IntoOption) -> Self { + self.inline = inline.into_option(); + self + } + + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they + /// do not recognize. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility) + #[must_use] + pub fn meta(mut self, meta: impl IntoOption) -> Self { + self.meta = meta.into_option(); + self + } +} + +/// **UNSTABLE** +/// +/// This capability is not part of the spec yet, and may be removed or changed at any point. +/// +/// Capabilities for `skill` content blocks at any position in a prompt. +/// +/// Supplying `{}` means a prompt may contain any number of `skill` content +/// blocks, at any position. +#[cfg(feature = "unstable_agent_skills")] +#[serde_as] +#[skip_serializing_none] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] +#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[non_exhaustive] +pub struct PromptSkillInlineCapabilities { + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata. + /// Implementations MUST NOT assign meaning to keys they do not recognize. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility) + #[serde_as(deserialize_as = "DefaultOnError")] + #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))] + #[serde(default)] + #[serde(rename = "_meta")] + pub meta: Option, +} + +#[cfg(feature = "unstable_agent_skills")] +impl PromptSkillInlineCapabilities { + /// Builds an empty [`PromptSkillInlineCapabilities`]. Use builder methods to advertise supported sub-capabilities. + #[must_use] + pub fn new() -> Self { + Self::default() + } + + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they + /// do not recognize. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility) + #[must_use] + pub fn meta(mut self, meta: impl IntoOption) -> Self { + self.meta = meta.into_option(); + self + } +} + /// MCP capabilities supported by the agent for session lifecycle requests. #[serde_as] #[skip_serializing_none] @@ -10248,6 +10392,47 @@ mod test_serialization { assert!(deserialized.embedded_context.is_some()); } + #[cfg(feature = "unstable_agent_skills")] + #[test] + fn prompt_skill_capability_has_two_levels() { + // `{}` allows one leading skill block. `inline` allows them anywhere. + let leading = PromptCapabilities::new().skill(PromptSkillCapabilities::new()); + assert_eq!( + serde_json::to_value(&leading).unwrap(), + json!({ "skill": {} }) + ); + + let inline = PromptCapabilities::new() + .skill(PromptSkillCapabilities::new().inline(PromptSkillInlineCapabilities::new())); + let wire = json!({ "skill": { "inline": {} } }); + assert_eq!(serde_json::to_value(&inline).unwrap(), wire); + assert_eq!( + serde_json::from_value::(wire).unwrap(), + inline + ); + + for unsupported in [ + json!({}), + json!({ "skill": null }), + json!({ "skill": false }), + json!({ "skill": true }), + ] { + let caps: PromptCapabilities = serde_json::from_value(unsupported).unwrap(); + assert!(caps.skill.is_none()); + } + + // A malformed `inline` does not withdraw the first level. + for leading_only in [ + json!({ "skill": { "inline": null } }), + json!({ "skill": { "inline": true } }), + ] { + assert_eq!( + serde_json::from_value::(leading_only).unwrap(), + leading + ); + } + } + #[test] fn test_mcp_capabilities_serialize_supported_transports_as_objects() { let caps = McpCapabilities::new() diff --git a/agent-client-protocol-schema/src/v2/client.rs b/agent-client-protocol-schema/src/v2/client.rs index 23200ac4f..0bbf26774 100644 --- a/agent-client-protocol-schema/src/v2/client.rs +++ b/agent-client-protocol-schema/src/v2/client.rs @@ -2803,6 +2803,16 @@ impl AvailableCommandsUpdate { pub enum AvailableCommand { /// A slash command the user can invoke by including it in a prompt. Command(Command), + /// **UNSTABLE** + /// + /// This capability is not part of the spec yet, and may be removed or changed at any point. + /// + /// An Agent Skill the user can invoke with a `skill` content block. + /// + /// Agents MUST only send this entry when the Client advertised + /// the `session.commands.skill` Client capability. + #[cfg(feature = "unstable_agent_skills")] + Skill(Skill), /// Custom or future command kind. /// /// Values beginning with `_` are reserved for implementation-specific @@ -2823,6 +2833,8 @@ impl AvailableCommand { pub fn name(&self) -> &str { match self { Self::Command(c) => &c.name, + #[cfg(feature = "unstable_agent_skills")] + Self::Skill(c) => &c.name, Self::Other(c) => &c.name, } } @@ -2832,6 +2844,8 @@ impl AvailableCommand { pub fn description(&self) -> &str { match self { Self::Command(c) => &c.description, + #[cfg(feature = "unstable_agent_skills")] + Self::Skill(c) => &c.description, Self::Other(c) => &c.description, } } @@ -2845,6 +2859,8 @@ impl AvailableCommand { pub fn meta(&self) -> Option<&Meta> { match self { Self::Command(c) => c.meta.as_ref(), + #[cfg(feature = "unstable_agent_skills")] + Self::Skill(c) => c.meta.as_ref(), Self::Other(c) => c.fields.get("_meta").and_then(serde_json::Value::as_object), } } @@ -2930,7 +2946,11 @@ impl<'de> Deserialize<'de> for OtherAvailableCommand { } } -const KNOWN_AVAILABLE_COMMAND_TYPES: &[&str] = &["command"]; +const KNOWN_AVAILABLE_COMMAND_TYPES: &[&str] = &[ + "command", + #[cfg(feature = "unstable_agent_skills")] + "skill", +]; fn is_known_available_command_type(type_: &str) -> bool { KNOWN_AVAILABLE_COMMAND_TYPES.contains(&type_) @@ -3011,6 +3031,91 @@ impl Command { } } +/// **UNSTABLE** +/// +/// This capability is not part of the spec yet, and may be removed or changed at any point. +/// +/// An Agent Skill the user can invoke. +/// +/// The `type` discriminator value is `skill`. +/// +/// The Client invokes a skill with a `skill` content block that names it. The +/// `/name` syntax applies only to `command` entries. An Agent that sends +/// `skill` entries MUST advertise the `session.prompt.skill` capability. +/// +/// Agents MUST only send `skill` entries when the Client advertised the +/// `session.commands.skill` Client capability. Agents SHOULD advertise only +/// skills the user can invoke. +#[cfg(feature = "unstable_agent_skills")] +#[serde_as] +#[skip_serializing_none] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct Skill { + /// Skill name. The Client uses it to refer to the skill in a `skill` content block. + /// + /// MUST NOT contain whitespace and MUST be unique within the command list, + /// across commands and skills. It need not match the `name` in the skill's + /// frontmatter. + pub name: String, + /// Human-readable description of what the skill does, shown to users. + pub description: String, + /// The absolute path of the file that defines the skill, normally its + /// `SKILL.md`, on the machine the Agent runs on. + /// + /// Omitted and `null` are equivalent and mean the skill has no such file, + /// as with a skill that is built into the Agent or served by an MCP server. + #[serde_as(deserialize_as = "DefaultOnError")] + #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))] + #[serde(default)] + pub path: Option, + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata. + /// Implementations MUST NOT assign meaning to keys they do not recognize. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility) + #[serde_as(deserialize_as = "DefaultOnError")] + #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))] + #[serde(default)] + #[serde(rename = "_meta")] + pub meta: Option, +} + +#[cfg(feature = "unstable_agent_skills")] +impl Skill { + /// Builds [`Skill`] with the required fields set. Optional fields start unset or empty. + #[must_use] + pub fn new(name: impl Into, description: impl Into) -> Self { + Self { + name: name.into(), + description: description.into(), + path: None, + meta: None, + } + } + + /// The absolute path of the file that defines the skill, normally its + /// `SKILL.md`, on the machine the Agent runs on. + #[must_use] + pub fn path(mut self, path: impl IntoOption) -> Self { + self.path = path.into_option(); + self + } + + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they + /// do not recognize. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility) + #[must_use] + pub fn meta(mut self, meta: impl IntoOption) -> Self { + self.meta = meta.into_option(); + self + } +} + /// The input specification for a command. #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] @@ -3854,7 +3959,10 @@ pub struct ClientCapabilities { /// Optional. Omitted and `null` are equivalent and mean the client does not advertise any /// session-related capabilities. This object only groups capabilities. Supplying `{}` /// advertises nothing. - #[cfg(feature = "unstable_prompt_suggestions")] + #[cfg(any( + feature = "unstable_prompt_suggestions", + feature = "unstable_agent_skills" + ))] #[serde_as(deserialize_as = "DefaultOnError")] #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))] #[serde(default)] @@ -3901,7 +4009,10 @@ impl ClientCapabilities { /// This capability is not part of the spec yet, and may be removed or changed at any point. /// /// Session-related capabilities supported by the client. - #[cfg(feature = "unstable_prompt_suggestions")] + #[cfg(any( + feature = "unstable_prompt_suggestions", + feature = "unstable_agent_skills" + ))] #[must_use] pub fn session(mut self, session: impl IntoOption) -> Self { self.session = session.into_option(); @@ -3929,7 +4040,10 @@ impl ClientCapabilities { /// This object only groups capabilities. Unlike the agent's `session` /// capability, it is not a support marker, and supplying `{}` advertises /// nothing. -#[cfg(feature = "unstable_prompt_suggestions")] +#[cfg(any( + feature = "unstable_prompt_suggestions", + feature = "unstable_agent_skills" +))] #[serde_as] #[skip_serializing_none] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] @@ -3942,10 +4056,20 @@ pub struct ClientSessionCapabilities { /// Optional. Omitted and `null` are equivalent and mean the client does not advertise support. /// Supplying `{}` means the client can offer prompt suggestions made of /// `text` and `resource_link` content blocks. + #[cfg(feature = "unstable_prompt_suggestions")] #[serde_as(deserialize_as = "DefaultOnError")] #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))] #[serde(default)] pub prompt_suggestions: Option, + /// Command types the client understands in addition to `command`. + /// + /// Optional. Omitted and `null` are equivalent and mean the client does not advertise support + /// for any of them. This object only groups capabilities. Supplying `{}` advertises nothing. + #[cfg(feature = "unstable_agent_skills")] + #[serde_as(deserialize_as = "DefaultOnError")] + #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))] + #[serde(default)] + pub commands: Option, /// The _meta property is reserved by ACP to allow clients and agents to attach additional /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata. /// Implementations MUST NOT assign meaning to keys they do not recognize. @@ -3958,7 +4082,10 @@ pub struct ClientSessionCapabilities { pub meta: Option, } -#[cfg(feature = "unstable_prompt_suggestions")] +#[cfg(any( + feature = "unstable_prompt_suggestions", + feature = "unstable_agent_skills" +))] impl ClientSessionCapabilities { /// Builds an empty [`ClientSessionCapabilities`]. Use builder methods to advertise supported sub-capabilities. #[must_use] @@ -3967,6 +4094,7 @@ impl ClientSessionCapabilities { } /// Advertises support for `prompt_suggestions_update` session updates. + #[cfg(feature = "unstable_prompt_suggestions")] #[must_use] pub fn prompt_suggestions( mut self, @@ -3976,6 +4104,126 @@ impl ClientSessionCapabilities { self } + /// Command types the client understands in addition to `command`. + #[cfg(feature = "unstable_agent_skills")] + #[must_use] + pub fn commands(mut self, commands: impl IntoOption) -> Self { + self.commands = commands.into_option(); + self + } + + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they + /// do not recognize. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility) + #[must_use] + pub fn meta(mut self, meta: impl IntoOption) -> Self { + self.meta = meta.into_option(); + self + } +} + +/// **UNSTABLE** +/// +/// This capability is not part of the spec yet, and may be removed or changed at any point. +/// +/// Command types the client understands in addition to `command`. +/// +/// Agents MUST NOT send a command type listed here unless the client +/// advertised it. This object only groups capabilities. Supplying `{}` +/// advertises nothing. +#[cfg(feature = "unstable_agent_skills")] +#[serde_as] +#[skip_serializing_none] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] +#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct CommandsCapabilities { + /// Support for `skill` command entries. + /// + /// Optional. Omitted and `null` are equivalent and mean the client does not advertise support. + /// Supplying `{}` means the agent may send `skill` entries in `availableCommands`. The + /// client invokes them with `skill` content blocks. + #[serde_as(deserialize_as = "DefaultOnError")] + #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))] + #[serde(default)] + pub skill: Option, + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata. + /// Implementations MUST NOT assign meaning to keys they do not recognize. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility) + #[serde_as(deserialize_as = "DefaultOnError")] + #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))] + #[serde(default)] + #[serde(rename = "_meta")] + pub meta: Option, +} + +#[cfg(feature = "unstable_agent_skills")] +impl CommandsCapabilities { + /// Builds an empty [`CommandsCapabilities`]. Use builder methods to advertise supported sub-capabilities. + #[must_use] + pub fn new() -> Self { + Self::default() + } + + /// Advertises support for `skill` command entries. + #[must_use] + pub fn skill(mut self, skill: impl IntoOption) -> Self { + self.skill = skill.into_option(); + self + } + + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they + /// do not recognize. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility) + #[must_use] + pub fn meta(mut self, meta: impl IntoOption) -> Self { + self.meta = meta.into_option(); + self + } +} + +/// **UNSTABLE** +/// +/// This capability is not part of the spec yet, and may be removed or changed at any point. +/// +/// Client support for `skill` command entries. +/// +/// Supplying `{}` means the agent may send `skill` entries in +/// `availableCommands`. The client invokes them with `skill` content blocks. +#[cfg(feature = "unstable_agent_skills")] +#[serde_as] +#[skip_serializing_none] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] +#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[non_exhaustive] +pub struct CommandsSkillCapabilities { + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata. + /// Implementations MUST NOT assign meaning to keys they do not recognize. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility) + #[serde_as(deserialize_as = "DefaultOnError")] + #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))] + #[serde(default)] + #[serde(rename = "_meta")] + pub meta: Option, +} + +#[cfg(feature = "unstable_agent_skills")] +impl CommandsSkillCapabilities { + /// Builds an empty [`CommandsSkillCapabilities`]. Use builder methods to advertise supported sub-capabilities. + #[must_use] + pub fn new() -> Self { + Self::default() + } + /// The _meta property is reserved by ACP to allow clients and agents to attach additional /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they /// do not recognize. @@ -6626,6 +6874,213 @@ mod tests { ); } + #[cfg(feature = "unstable_agent_skills")] + #[test] + fn skill_command_uses_type_discriminator() { + use serde_json::json; + + let skill = AvailableCommand::Skill( + Skill::new("pdf-processing", "Extract text and tables from PDF files").path( + AbsolutePath::new("/home/alice/.agents/skills/pdf-processing/SKILL.md"), + ), + ); + + let wire = json!({ + "type": "skill", + "name": "pdf-processing", + "description": "Extract text and tables from PDF files", + "path": "/home/alice/.agents/skills/pdf-processing/SKILL.md" + }); + assert_eq!(serde_json::to_value(&skill).unwrap(), wire); + + let roundtripped: AvailableCommand = serde_json::from_value(wire).unwrap(); + assert_eq!(roundtripped, skill); + assert_eq!(roundtripped.name(), "pdf-processing"); + assert_eq!( + roundtripped.description(), + "Extract text and tables from PDF files" + ); + assert!(roundtripped.meta().is_none()); + + // A skill with no file omits `path`. + assert_eq!( + serde_json::to_value(AvailableCommand::Skill(Skill::new( + "simplify", + "Simplify the changed code" + ))) + .unwrap(), + json!({ + "type": "skill", + "name": "simplify", + "description": "Simplify the changed code" + }) + ); + } + + #[cfg(feature = "unstable_agent_skills")] + #[test] + fn skill_command_treats_invalid_display_fields_as_absent() { + use serde_json::json; + + let expected = AvailableCommand::Skill(Skill::new("pdf-processing", "Extract text")); + for (field, value) in [ + ("path", json!(null)), + ("path", json!(7)), + ("path", json!({ "uri": "file:///SKILL.md" })), + ("_meta", json!(null)), + ("_meta", json!("oops")), + ] { + let mut wire = json!({ + "type": "skill", + "name": "pdf-processing", + "description": "Extract text" + }); + wire[field] = value; + assert_eq!( + serde_json::from_value::(wire).unwrap(), + expected, + "{field}" + ); + } + } + + #[cfg(feature = "unstable_agent_skills")] + #[test] + fn skill_command_has_no_input_specification() { + use serde_json::json; + + // Skills take no input. A field the entry does not define is ignored. + let skill: AvailableCommand = serde_json::from_value(json!({ + "type": "skill", + "name": "pdf-processing", + "description": "Extract text", + "input": { "type": "text", "hint": "file or instructions" } + })) + .unwrap(); + assert_eq!( + skill, + AvailableCommand::Skill(Skill::new("pdf-processing", "Extract text")) + ); + assert!(serde_json::to_value(&skill).unwrap().get("input").is_none()); + } + + #[cfg(feature = "unstable_agent_skills")] + #[test] + fn malformed_skill_command_is_skipped_and_never_preserved_as_unknown() { + use serde_json::json; + + for malformed in [ + json!({ "type": "skill", "name": "pdf-processing" }), + json!({ "type": "skill", "description": "Extract text" }), + json!({ "type": "skill", "name": 1, "description": "Extract text" }), + ] { + assert!(serde_json::from_value::(malformed.clone()).is_err()); + assert!(serde_json::from_value::(malformed).is_err()); + } + + // In a command list, the malformed entry is skipped and the rest are kept. + let update: AvailableCommandsUpdate = serde_json::from_value(json!({ + "availableCommands": [ + { "type": "skill", "name": "pdf-processing", "description": "Extract text" }, + { "type": "skill", "name": "broken" }, + { "type": "command", "name": "test", "description": "Run project tests" } + ] + })) + .unwrap(); + assert_eq!( + update.available_commands, + vec![ + AvailableCommand::Skill(Skill::new("pdf-processing", "Extract text")), + AvailableCommand::Command(Command::new("test", "Run project tests")), + ] + ); + } + + #[cfg(feature = "unstable_agent_skills")] + #[test] + fn skill_commands_capability_is_advertised_only_by_an_object() { + use serde_json::json; + + let capabilities = ClientCapabilities::new().session( + ClientSessionCapabilities::new() + .commands(CommandsCapabilities::new().skill(CommandsSkillCapabilities::new())), + ); + let wire = json!({ "session": { "commands": { "skill": {} } } }); + assert_eq!(serde_json::to_value(&capabilities).unwrap(), wire); + assert_eq!( + serde_json::from_value::(wire).unwrap(), + capabilities + ); + + // `session` and `commands` only group capabilities. `{}` advertises nothing. + for unsupported in [ + json!({}), + json!({ "session": null }), + json!({ "session": {} }), + json!({ "session": { "commands": null } }), + json!({ "session": { "commands": {} } }), + json!({ "session": { "commands": true } }), + json!({ "session": { "commands": { "skill": null } } }), + json!({ "session": { "commands": { "skill": true } } }), + json!({ "session": { "commands": { "skill": "supported" } } }), + ] { + let capabilities: ClientCapabilities = serde_json::from_value(unsupported).unwrap(); + assert!( + capabilities + .session + .and_then(|session| session.commands) + .and_then(|commands| commands.skill) + .is_none() + ); + } + } + + #[cfg(all( + feature = "unstable_agent_skills", + feature = "unstable_prompt_suggestions" + ))] + #[test] + fn client_session_capabilities_are_independent_of_each_other() { + use serde_json::json; + + // A malformed capability does not withdraw the others in the group. + let capabilities: ClientCapabilities = serde_json::from_value(json!({ + "session": { + "promptSuggestions": true, + "commands": { "skill": {} } + } + })) + .unwrap(); + let session = capabilities.session.unwrap(); + assert!(session.prompt_suggestions.is_none()); + assert!(session.commands.unwrap().skill.is_some()); + } + + #[cfg(all(feature = "schemars", feature = "unstable_agent_skills"))] + #[test] + fn skill_schemas_keep_acted_on_fields_strict() { + use serde_json::json; + + let skill = serde_json::to_value(schemars::schema_for!(Skill)).unwrap(); + assert_eq!(skill["required"], json!(["name", "description"])); + for lenient in ["path", "_meta"] { + assert_eq!( + skill["properties"][lenient]["x-deserialize-default-on-error"], true, + "{lenient}" + ); + } + + // The Agent acts on the referenced name, so it has no lenient fallback. + let reference = + serde_json::to_value(schemars::schema_for!(crate::v2::SkillReference)).unwrap(); + assert_eq!(reference["required"], json!(["name"])); + assert!( + reference["properties"]["name"] + .get("x-deserialize-default-on-error") + .is_none() + ); + } + #[test] fn available_command_input_preserves_unknown_typed_variant() { use serde_json::json; diff --git a/agent-client-protocol-schema/src/v2/content.rs b/agent-client-protocol-schema/src/v2/content.rs index beb76ce8a..de53e7141 100644 --- a/agent-client-protocol-schema/src/v2/content.rs +++ b/agent-client-protocol-schema/src/v2/content.rs @@ -104,6 +104,16 @@ pub enum ContentBlock { /// /// Requires the `embeddedContext` prompt capability when included in prompts. Resource(EmbeddedResource), + /// **UNSTABLE** + /// + /// This capability is not part of the spec yet, and may be removed or changed at any point. + /// + /// A reference to a skill that the user invokes with this prompt. + /// + /// Requires the `skill` prompt capability when included in prompts. A skill + /// reference has no meaning outside a user message. + #[cfg(feature = "unstable_agent_skills")] + Skill(SkillReference), /// Custom or future content block. /// /// Values beginning with `_` are reserved for implementation-specific @@ -174,11 +184,18 @@ impl<'de> Deserialize<'de> for OtherContentBlock { } } +const KNOWN_CONTENT_BLOCK_TYPES: &[&str] = &[ + "text", + "image", + "audio", + "resource_link", + "resource", + #[cfg(feature = "unstable_agent_skills")] + "skill", +]; + fn is_known_content_block_type(type_: &str) -> bool { - matches!( - type_, - "text" | "image" | "audio" | "resource_link" | "resource" - ) + KNOWN_CONTENT_BLOCK_TYPES.contains(&type_) } #[cfg(feature = "schemars")] @@ -186,7 +203,7 @@ fn other_content_block_schema(schema: &mut Schema) { super::schema_util::reject_known_string_discriminators( schema, "type", - &["text", "image", "audio", "resource_link", "resource"], + KNOWN_CONTENT_BLOCK_TYPES, ); } @@ -731,6 +748,72 @@ impl ResourceLink { } } +/// **UNSTABLE** +/// +/// This capability is not part of the spec yet, and may be removed or changed at any point. +/// +/// A reference to a skill that the user invokes with a prompt. +/// +/// The Client sends it in `session/prompt` to invoke a `skill` entry from the +/// session's command list. The Agent MUST treat each reference as the user +/// explicitly invoking that skill for this prompt, and applies the skill to +/// the prompt as a whole. The reference carries no input. +/// +/// An Agent that advertises the `session.prompt.skill` capability accepts one +/// skill reference per prompt, as the first content block. An Agent that also +/// advertises `session.prompt.skill.inline` accepts any number of them, at any +/// position. Clients MUST NOT send a skill reference that the Agent's +/// capability does not allow. +/// +/// The `type` discriminator value is `skill`. +#[cfg(feature = "unstable_agent_skills")] +#[serde_as] +#[skip_serializing_none] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] +#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct SkillReference { + /// The `name` of a `skill` entry in the session's current command list. + /// + /// Required and non-null. The Agent MUST reject the prompt with `-32602` + /// when it cannot resolve the name to a skill the user can invoke. + pub name: String, + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata. + /// Implementations MUST NOT assign meaning to keys they do not recognize. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility) + #[serde_as(deserialize_as = "DefaultOnError")] + #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))] + #[serde(default)] + #[serde(rename = "_meta")] + pub meta: Option, +} + +#[cfg(feature = "unstable_agent_skills")] +impl SkillReference { + /// Builds [`SkillReference`] with the name of the skill to invoke. Optional metadata starts unset. + #[must_use] + pub fn new(name: impl Into) -> Self { + Self { + name: name.into(), + meta: None, + } + } + + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they + /// do not recognize. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility) + #[must_use] + pub fn meta(mut self, meta: impl IntoOption) -> Self { + self.meta = meta.into_option(); + self + } +} + /// An optionally-sized icon that can be displayed in a user interface. #[serde_as] #[skip_serializing_none] @@ -1104,6 +1187,43 @@ mod tests { ); } + #[cfg(feature = "unstable_agent_skills")] + #[test] + fn skill_reference_uses_type_discriminator() { + let block = ContentBlock::Skill(SkillReference::new("pdf-processing")); + let json = serde_json::to_value(&block).unwrap(); + assert_eq!( + json, + serde_json::json!({ "type": "skill", "name": "pdf-processing" }) + ); + assert_eq!(serde_json::from_value::(json).unwrap(), block); + + // `null` and malformed metadata mean no metadata. + for meta in [serde_json::Value::Null, serde_json::json!("oops")] { + let parsed: ContentBlock = serde_json::from_value(serde_json::json!({ + "type": "skill", + "name": "pdf-processing", + "_meta": meta + })) + .unwrap(); + assert_eq!(parsed, block); + } + } + + #[cfg(feature = "unstable_agent_skills")] + #[test] + fn malformed_skill_reference_does_not_fall_through_to_other() { + // The Agent acts on `name`, so it stays strict. + for payload in [ + serde_json::json!({ "type": "skill" }), + serde_json::json!({ "type": "skill", "name": null }), + serde_json::json!({ "type": "skill", "name": 1 }), + ] { + assert!(serde_json::from_value::(payload.clone()).is_err()); + assert!(serde_json::from_value::(payload).is_err()); + } + } + #[test] fn test_image_content_roundtrip() { let content = ImageContent::new("base64data", "image/png"); diff --git a/agent-client-protocol-schema/tests/experimental_compatibility.rs b/agent-client-protocol-schema/tests/experimental_compatibility.rs index 24c853509..598d0355e 100644 --- a/agent-client-protocol-schema/tests/experimental_compatibility.rs +++ b/agent-client-protocol-schema/tests/experimental_compatibility.rs @@ -3,7 +3,7 @@ #![cfg(feature = "unstable_protocol_v2")] -use agent_client_protocol_schema::v2::*; +use agent_client_protocol_schema::{MaybeUndefined, v2::*}; use serde_json::{Value, json}; fn decode_update(wire: &Value) -> SessionUpdate { @@ -69,6 +69,63 @@ fn experimental_prompt_suggestions_roundtrip_with_or_without_local_support() { } } +#[test] +fn experimental_skill_commands_roundtrip_with_or_without_local_support() { + let wire = json!({ + "sessionUpdate": "available_commands_update", + "availableCommands": [ + { + "type": "skill", + "name": "pdf-processing", + "description": "Extract text and tables from PDF files", + "path": "/home/alice/.agents/skills/pdf-processing/SKILL.md" + }, + {"type": "command", "name": "test", "description": "Run project tests"} + ] + }); + let update = decode_update(&wire); + let SessionUpdate::AvailableCommandsUpdate(commands) = &update else { + panic!("expected available commands update"); + }; + let [skill, command] = commands.available_commands.as_slice() else { + panic!("expected both entries to be kept"); + }; + #[cfg(feature = "unstable_agent_skills")] + assert!(matches!(skill, AvailableCommand::Skill(_))); + #[cfg(not(feature = "unstable_agent_skills"))] + assert!(matches!(skill, AvailableCommand::Other(_))); + assert_eq!(skill.name(), "pdf-processing"); + assert!(matches!(command, AvailableCommand::Command(_))); + assert_eq!(serde_json::to_value(update).unwrap(), wire); +} + +#[test] +fn experimental_skill_references_roundtrip_with_or_without_local_support() { + let wire = json!({ + "sessionUpdate": "user_message", + "messageId": "msg_user_1", + "content": [ + {"type": "skill", "name": "pdf-processing"}, + {"type": "text", "text": "Summarize report.pdf"} + ] + }); + let update = decode_update(&wire); + let SessionUpdate::UserMessage(message) = &update else { + panic!("expected user message"); + }; + let MaybeUndefined::Value(content) = &message.content else { + panic!("expected content"); + }; + let [skill, _text] = content.as_slice() else { + panic!("expected both blocks to be kept"); + }; + #[cfg(feature = "unstable_agent_skills")] + assert!(matches!(skill, ContentBlock::Skill(_))); + #[cfg(not(feature = "unstable_agent_skills"))] + assert!(matches!(skill, ContentBlock::Other(_))); + assert_eq!(serde_json::to_value(update).unwrap(), wire); +} + #[test] fn unknown_standard_and_custom_updates_preserve_their_payloads() { for tag in ["future_display_update", "_acme_display_update"] { @@ -162,3 +219,20 @@ fn session_update_fallback_schema_excludes_only_locally_recognized_variants() { ); } } + +#[cfg(feature = "schemars")] +#[test] +fn skill_fallback_schemas_exclude_only_locally_recognized_variants() { + for schema in [ + serde_json::to_value(schemars::schema_for!(OtherAvailableCommand)).unwrap(), + serde_json::to_value(schemars::schema_for!(OtherContentBlock)).unwrap(), + ] { + let excluded = schema["not"]["anyOf"].as_array().unwrap(); + assert_eq!( + excluded + .iter() + .any(|variant| variant["properties"]["type"]["const"] == "skill"), + cfg!(feature = "unstable_agent_skills") + ); + } +} diff --git a/docs/protocol/v2/draft/content.mdx b/docs/protocol/v2/draft/content.mdx index a79686edc..5559badab 100644 --- a/docs/protocol/v2/draft/content.mdx +++ b/docs/protocol/v2/draft/content.mdx @@ -17,6 +17,8 @@ Content from the Agent is untrusted. See [Rendering Agent Content](/protocol/v2/ ACP's `ContentBlock` is compatible with, and a superset of, the [Model Context Protocol (MCP) 2026-07-28 `ContentBlock`](https://modelcontextprotocol.io/specification/2026-07-28/schema#contentblock), so Agents can forward content from MCP tool outputs unchanged. ACP adds an optional `uri` on image content, `_meta` on icons and annotations, extensible `Role` and `IconTheme` values, and the open `type` fallback described below. +The unstable [`skill` content block](#skill-reference-unstable) is an ACP addition that MCP does not define. + A content type from a later MCP revision that ACP doesn't define isn't forwarded as is. Agents convert it to a supported type, such as an embedded resource, or omit it. Content block `type` values can be custom or future variants when the receiver can preserve or display them generically. Custom content block types **MUST** begin with `_`. Unknown non-underscore content block types are reserved for future ACP variants. In prompts, Clients send only baseline content types (`text` and `resource_link`) and content types the Agent advertised, including custom types advertised through a [custom capability](/protocol/v2/draft/extensibility#advertising-custom-capabilities), and Agents reject others with `-32602`. @@ -227,3 +229,24 @@ All Agents **MUST** support resource links when included in prompts. Optional metadata about how the content should be used or displayed. [Learn more](https://modelcontextprotocol.io/specification/2026-07-28/schema#annotations). + +### Skill reference (unstable) + +A reference to a [skill](/protocol/v2/draft/slash-commands#skills-unstable) that the user invokes with the prompt. + +```json +{ + "type": "skill", + "name": "pdf-processing" +} +``` + + Requires the `skill` [prompt +capability](/protocol/v2/draft/initialization#session-prompt-capabilities) when +included in prompts. + + + The `name` of a `skill` entry in the session's current command list + + +A skill reference appears only in user messages: in the `prompt` of `session/prompt`, and in the `user_message` updates that report or replay that prompt. See [Invoking a skill](/protocol/v2/draft/slash-commands#invoking-a-skill) for where the block may appear in a prompt and how the Agent handles it. diff --git a/docs/protocol/v2/draft/extensibility.mdx b/docs/protocol/v2/draft/extensibility.mdx index 1d04d1e8d..19c4846df 100644 --- a/docs/protocol/v2/draft/extensibility.mdx +++ b/docs/protocol/v2/draft/extensibility.mdx @@ -28,15 +28,16 @@ Each feature defines its negotiation requirements, whether stable or experimenta For example: -| Feature | Negotiation and receiver behavior | -| --------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [Session deletion](/protocol/v2/draft/session-delete) | The Agent advertises `session.delete`. Clients must not call `session/delete` without it. | -| [Elicitation](/protocol/v2/draft/elicitation#checking-support) | The Client advertises each supported mode under `elicitation`. Agents must not request other modes. | -| [Provider configuration](/protocol/v2/draft/initialization#agent-capabilities) (experimental) | The Agent advertises `capabilities.providers`. Clients must not call provider configuration methods without it. | -| [Notices](/protocol/v2/draft/prompt-lifecycle#session-notices) and [compaction](/protocol/v2/draft/prompt-lifecycle#session-compaction) | No Client capability is required in v2. Clients without feature-specific handling may preserve, ignore, or generically display the updates. | -| [Markdown plans](/protocol/v2/draft/agent-plan#unstable-plan-operations) (experimental) | No Client capability is required in v2. Clients without this plan representation use the unknown plan-content fallback. Sending one does not establish that it will be rendered. | -| [End-turn token usage](/protocol/v2/draft/prompt-lifecycle#end-turn-token-usage-unstable) (experimental) | Optional `usage` on an idle `state_update`, not on the prompt response. No capability is required. Clients may ignore it. | -| [Prompt suggestions](/protocol/v2/draft/prompt-lifecycle#prompt-suggestions-unstable) (experimental) | The Client advertises `session.promptSuggestions`. Agents must not send `prompt_suggestions_update` without it. | +| Feature | Negotiation and receiver behavior | +| --------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [Session deletion](/protocol/v2/draft/session-delete) | The Agent advertises `session.delete`. Clients must not call `session/delete` without it. | +| [Elicitation](/protocol/v2/draft/elicitation#checking-support) | The Client advertises each supported mode under `elicitation`. Agents must not request other modes. | +| [Provider configuration](/protocol/v2/draft/initialization#agent-capabilities) (experimental) | The Agent advertises `capabilities.providers`. Clients must not call provider configuration methods without it. | +| [Notices](/protocol/v2/draft/prompt-lifecycle#session-notices) and [compaction](/protocol/v2/draft/prompt-lifecycle#session-compaction) | No Client capability is required in v2. Clients without feature-specific handling may preserve, ignore, or generically display the updates. | +| [Markdown plans](/protocol/v2/draft/agent-plan#unstable-plan-operations) (experimental) | No Client capability is required in v2. Clients without this plan representation use the unknown plan-content fallback. Sending one does not establish that it will be rendered. | +| [End-turn token usage](/protocol/v2/draft/prompt-lifecycle#end-turn-token-usage-unstable) (experimental) | Optional `usage` on an idle `state_update`, not on the prompt response. No capability is required. Clients may ignore it. | +| [Prompt suggestions](/protocol/v2/draft/prompt-lifecycle#prompt-suggestions-unstable) (experimental) | The Client advertises `session.promptSuggestions`. Agents must not send `prompt_suggestions_update` without it. | +| [Skills](/protocol/v2/draft/slash-commands#skills-unstable) (experimental) | The Client advertises `session.commands.skill`. Agents must not send `skill` command entries without it. The Agent advertises `session.prompt.skill`. Clients must not send `skill` content blocks that it does not allow. | These rules are specific to v2. Features with capability gates in v1 retain those gates on v1 connections. diff --git a/docs/protocol/v2/draft/initialization.mdx b/docs/protocol/v2/draft/initialization.mdx index 09c18db80..01eb009d6 100644 --- a/docs/protocol/v2/draft/initialization.mdx +++ b/docs/protocol/v2/draft/initialization.mdx @@ -245,6 +245,29 @@ The Client opts in to other content types with the following fields. They have t Learn more about Prompt Suggestions + + Groups the command types the Client understands in addition to `command`. + Omitted or `null` means the Client advertises none of them. This object is not + a support marker, and `{}` advertises nothing. + + + + The Client understands `skill` entries in `availableCommands` and invokes them + with `skill` content blocks. Omitted or `null` means unsupported, and `{}` + means supported. Agents must not send `skill` entries without this capability. + + + + Learn more about Skills + + ### Agent capabilities The Agent **SHOULD** specify whether it supports the following capabilities: @@ -387,6 +410,20 @@ Optionally, they **MAY** support richer types of [content](/protocol/v2/draft/co embedded context in prompts. + + **Unstable.** The prompt may include a [`skill` content + block](/protocol/v2/draft/content#skill-reference-unstable). Omitted or `null` + means the Agent does not advertise support. Supplying `{}` means a prompt may + contain one `skill` block, as its first block. An Agent that sends `skill` + entries in `availableCommands` **MUST** advertise this capability. + + + + **Unstable.** Omitted or `null` means the Agent does not advertise support. + Supplying `{}` means a prompt may contain any number of `skill` blocks, at any + position. + + #### Session MCP capabilities diff --git a/docs/protocol/v2/draft/schema.mdx b/docs/protocol/v2/draft/schema.mdx index 2e27b3e51..3fc3b4b34 100644 --- a/docs/protocol/v2/draft/schema.mdx +++ b/docs/protocol/v2/draft/schema.mdx @@ -3332,6 +3332,52 @@ MUST NOT contain whitespace and MUST be unique within the command list. + +**UNSTABLE** + +This capability is not part of the spec yet, and may be removed or changed at any point. + +An Agent Skill the user can invoke with a `skill` content block. + +Agents MUST only send this entry when the Client advertised +the `session.commands.skill` Client capability. + + + + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Omitted and `null` are equivalent and mean no metadata. +Implementations MUST NOT assign meaning to keys they do not recognize. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) + + + + Human-readable description of what the skill does, shown to users. + + + Skill name. The Client uses it to refer to the skill in a `skill` content block. + +MUST NOT contain whitespace and MUST be unique within the command list, +across commands and skills. It need not match the `name` in the skill's +frontmatter. + + +AbsolutePath | null} > + The absolute path of the file that defines the skill, normally its +`SKILL.md`, on the machine the Agent runs on. + +Omitted and `null` are equivalent and mean the skill has no such file, +as with a skill that is built into the Agent or served by an MCP server. + + + + The discriminator value. Must be `"skill"`. + + + + + Custom or future command kind. @@ -3800,6 +3846,13 @@ Implementations MUST NOT assign meaning to keys they do not recognize. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) + +CommandsCapabilities | null} > + Command types the client understands in addition to `command`. + +Optional. Omitted and `null` are equivalent and mean the client does not advertise support +for any of them. This object only groups capabilities. Supplying `\{\}` advertises nothing. + PromptSuggestionsCapabilities | null} > Support for `prompt_suggestions_update` session updates. @@ -3873,6 +3926,63 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/d The associated tool call, when known. Omitted and `null` are equivalent. +## CommandsCapabilities + +**UNSTABLE** + +This capability is not part of the spec yet, and may be removed or changed at any point. + +Command types the client understands in addition to `command`. + +Agents MUST NOT send a command type listed here unless the client +advertised it. This object only groups capabilities. Supplying `\{\}` +advertises nothing. + +**Type:** Object + +**Properties:** + + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Omitted and `null` are equivalent and mean no metadata. +Implementations MUST NOT assign meaning to keys they do not recognize. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) + + +CommandsSkillCapabilities | null} > + Support for `skill` command entries. + +Optional. Omitted and `null` are equivalent and mean the client does not advertise support. +Supplying `\{\}` means the agent may send `skill` entries in `availableCommands`. The +client invokes them with `skill` content blocks. + + + +## CommandsSkillCapabilities + +**UNSTABLE** + +This capability is not part of the spec yet, and may be removed or changed at any point. + +Client support for `skill` command entries. + +Supplying `\{\}` means the agent may send `skill` entries in +`availableCommands`. The client invokes them with `skill` content blocks. + +**Type:** Object + +**Properties:** + + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Omitted and `null` are equivalent and mean no metadata. +Implementations MUST NOT assign meaning to keys they do not recognize. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) + + + ## CompactionId Unique identifier for a context compaction within a session. @@ -4247,6 +4357,40 @@ Omitted and `null` are equivalent and mean no annotations are provided. + +**UNSTABLE** + +This capability is not part of the spec yet, and may be removed or changed at any point. + +A reference to a skill that the user invokes with this prompt. + +Requires the `skill` prompt capability when included in prompts. A skill +reference has no meaning outside a user message. + + + + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Omitted and `null` are equivalent and mean no metadata. +Implementations MUST NOT assign meaning to keys they do not recognize. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) + + + + The `name` of a `skill` entry in the session's current command list. + +Required and non-null. The Agent MUST reject the prompt with `-32602` +when it cannot resolve the name to a skill the user can invoke. + + + + The discriminator value. Must be `"skill"`. + + + + + Custom or future content block. @@ -7228,6 +7372,18 @@ Supplying `\{\}` means the agent supports embedded context in prompts. Optional. Omitted and `null` are equivalent and mean the agent does not advertise support. Supplying `\{\}` means the agent supports image content in prompts. + +PromptSkillCapabilities | null} > + **UNSTABLE** + +This capability is not part of the spec yet, and may be removed or changed at any point. + +Agent supports `skill` content blocks. + +Optional. Omitted and `null` are equivalent and mean the agent does not advertise support. +Supplying `\{\}` means a prompt may contain one `skill` content block, as its first block. +An agent that sends `skill` entries in `availableCommands` MUST advertise this capability. + ## PromptEmbeddedContextCapabilities @@ -7268,6 +7424,62 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/d +## PromptSkillCapabilities + +**UNSTABLE** + +This capability is not part of the spec yet, and may be removed or changed at any point. + +Capabilities for `skill` content blocks in prompt requests. + +Supplying `\{\}` means a prompt may contain one `skill` content block, as +its first block. The agent opts in to more with the `inline` field. + +**Type:** Object + +**Properties:** + + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Omitted and `null` are equivalent and mean no metadata. +Implementations MUST NOT assign meaning to keys they do not recognize. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) + + +PromptSkillInlineCapabilities | null} > + Agent supports `skill` content blocks at any position in a prompt. + +Optional. Omitted and `null` are equivalent and mean the agent does not advertise support. +Supplying `\{\}` means a prompt may contain any number of `skill` content blocks, at any +position. + + + +## PromptSkillInlineCapabilities + +**UNSTABLE** + +This capability is not part of the spec yet, and may be removed or changed at any point. + +Capabilities for `skill` content blocks at any position in a prompt. + +Supplying `\{\}` means a prompt may contain any number of `skill` content +blocks, at any position. + +**Type:** Object + +**Properties:** + + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Omitted and `null` are equivalent and mean no metadata. +Implementations MUST NOT assign meaning to keys they do not recognize. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) + + + ## PromptSuggestion **UNSTABLE** @@ -10216,6 +10428,97 @@ future ACP variants. +## Skill + +**UNSTABLE** + +This capability is not part of the spec yet, and may be removed or changed at any point. + +An Agent Skill the user can invoke. + +The `type` discriminator value is `skill`. + +The Client invokes a skill with a `skill` content block that names it. The +`/name` syntax applies only to `command` entries. An Agent that sends +`skill` entries MUST advertise the `session.prompt.skill` capability. + +Agents MUST only send `skill` entries when the Client advertised the +`session.commands.skill` Client capability. Agents SHOULD advertise only +skills the user can invoke. + +**Type:** Object + +**Properties:** + + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Omitted and `null` are equivalent and mean no metadata. +Implementations MUST NOT assign meaning to keys they do not recognize. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) + + + + Human-readable description of what the skill does, shown to users. + + + Skill name. The Client uses it to refer to the skill in a `skill` content block. + +MUST NOT contain whitespace and MUST be unique within the command list, +across commands and skills. It need not match the `name` in the skill's +frontmatter. + + +AbsolutePath | null} > + The absolute path of the file that defines the skill, normally its +`SKILL.md`, on the machine the Agent runs on. + +Omitted and `null` are equivalent and mean the skill has no such file, +as with a skill that is built into the Agent or served by an MCP server. + + + +## SkillReference + +**UNSTABLE** + +This capability is not part of the spec yet, and may be removed or changed at any point. + +A reference to a skill that the user invokes with a prompt. + +The Client sends it in `session/prompt` to invoke a `skill` entry from the +session's command list. The Agent MUST treat each reference as the user +explicitly invoking that skill for this prompt, and applies the skill to +the prompt as a whole. The reference carries no input. + +An Agent that advertises the `session.prompt.skill` capability accepts one +skill reference per prompt, as the first content block. An Agent that also +advertises `session.prompt.skill.inline` accepts any number of them, at any +position. Clients MUST NOT send a skill reference that the Agent's +capability does not allow. + +The `type` discriminator value is `skill`. + +**Type:** Object + +**Properties:** + + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Omitted and `null` are equivalent and mean no metadata. +Implementations MUST NOT assign meaning to keys they do not recognize. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility) + + + + The `name` of a `skill` entry in the session's current command list. + +Required and non-null. The Agent MUST reject the prompt with `-32602` +when it cannot resolve the name to a skill the user can invoke. + + + ## StateUpdate A snapshot of the session's aggregate foreground activity. diff --git a/docs/protocol/v2/draft/slash-commands.mdx b/docs/protocol/v2/draft/slash-commands.mdx index e1eb10129..b8f620ef5 100644 --- a/docs/protocol/v2/draft/slash-commands.mdx +++ b/docs/protocol/v2/draft/slash-commands.mdx @@ -90,10 +90,11 @@ The notification's `availableCommands` array is the complete replacement list, n ### AvailableCommand -Each command carries a required `type` discriminator. Currently the only type is `command`, a slash command the user invokes by including it in a prompt. +Each command carries a required `type` discriminator. The stable type is `command`, a slash command the user invokes by including it in a prompt. The unstable `skill` type is described in [Skills](#skills-unstable). The fields below are those of a `command` entry. - The kind of command. Currently always `"command"`. + The kind of command. `"command"`, or `"skill"` for a + [skill](#skills-unstable). @@ -162,3 +163,97 @@ occurs. A command with no user-action wait uses `running` → `idle`, and **SHOULD** include `stopReason: "end_turn"` for successful completion. Completing an individual command while other foreground activity continues does not require an idle boundary. + +## Skills (unstable) + +A `skill` entry advertises an [Agent Skill](https://agentskills.io/specification) that the user can invoke. Clients use the type to label skills, list them apart from the Agent's other commands, and open the file that defines a skill. See the [RFD](/rfds/agent-skills). + +The Agent **MUST NOT** send `skill` entries unless the Client advertised support by supplying `capabilities.session.commands.skill` in its `initialize` request. Omitting `session`, `commands`, or `skill`, or setting any of them to `null`, means the Client does not advertise support. For those Clients, an Agent can advertise its skills as `command` entries. + +An Agent that sends `skill` entries **MUST** advertise the [`session.prompt.skill` capability](/protocol/v2/draft/initialization#session-prompt-capabilities). + +Skills are delivered like other commands, in `availableCommands` in setup responses and `available_commands_update` notifications: + +```json +{ + "type": "skill", + "name": "pdf-processing", + "description": "Extract text and tables from PDF files", + "path": "/home/alice/.agents/skills/pdf-processing/SKILL.md" +} +``` + + + The name the Client uses to refer to the skill. **MUST NOT** contain + whitespace and **MUST** be unique within the list, across commands and skills. + It need not match the `name` in the skill's frontmatter. An Agent with two + skills of the same name, or a skill and a command of the same name, qualifies + one of them, for example as `plugin:pdf-processing`. + + + + Human-readable description of what the skill does, shown to users. A skill's + frontmatter description is written for the model and can be long. An Agent + that has a shorter description meant for users **SHOULD** send that one. + + + + Optional absolute path, on the machine the Agent runs on, of the file that + defines the skill, normally its `SKILL.md`. Agents omit it for skills with no + such file, such as skills built into the Agent or served by an MCP server. + Clients can use it to show where a skill lives or to open it. A Client that + doesn't share a filesystem with the Agent can't open it. + + +A `skill` entry has no `input` specification. For `path` and `_meta`, omission and `null` are equivalent. A Client treats an invalid value in one of these fields as absent and keeps the entry. + +Agents **SHOULD** advertise only skills the user can invoke. + +`name`, `description`, and `path` come from the Agent and, through it, from whoever wrote the skill. Clients treat them as untrusted, as they do command names and descriptions, and open `path` only on a user action. + +### Invoking a skill + +A Client invokes a skill by putting a [`skill` content block](/protocol/v2/draft/content#skill-reference-unstable) in the prompt. The `/name` syntax in [Running commands](#running-commands) applies only to `command` entries. + +```json +{ + "jsonrpc": "2.0", + "id": 3, + "method": "session/prompt", + "params": { + "sessionId": "sess_abc123def456", + "prompt": [ + { "type": "skill", "name": "pdf-processing" }, + { + "type": "text", + "text": "Pull the tables out of report.pdf and summarize them." + } + ] + } +} +``` + +The Agent's `session.prompt.skill` capability says where the block may appear: + +- `skill: {}` means a prompt may contain one `skill` block, and it has to be the first block. +- `skill: { "inline": {} }` means a prompt may contain any number of `skill` blocks, at any position: + + ```json + [ + { "type": "text", "text": "Pull the tables out of report.pdf with " }, + { "type": "skill", "name": "pdf-processing" }, + { "type": "text", "text": ", then chart them with " }, + { "type": "skill", "name": "charts" }, + { "type": "text", "text": "." } + ] + ``` + +The same rules apply at both levels: + +- `name` is the `name` of a `skill` entry in the session's current command list. The block carries no input. +- The Agent **MUST** treat each block as the user explicitly invoking that skill for this prompt, and applies the skill to the prompt as a whole. How it does that is up to the Agent. It can, for example, load the skill's instructions for the turn, or run the skill with the rest of the prompt as its input. +- Clients **MUST NOT** send a `skill` block that the Agent's capability doesn't allow. The Agent **MUST** reject a prompt that contains one, or one whose `name` it can't resolve to a skill the user can invoke, with `-32602` and without inserting it. +- The block is part of the inserted user message, so the Agent reports it in `user_message` and replays it. When replaying to a Client that didn't advertise `session.commands.skill`, the Agent **MAY** replace it with text. +- A `resource_link` or embedded resource that points at a skill's file is ordinary context. It does not invoke the skill. + +How the Client lets the user pick a skill, with `$`, `@`, `/`, or a menu, is up to the Client. diff --git a/docs/rfds/agent-skills.mdx b/docs/rfds/agent-skills.mdx index d2ac5cc9a..1d033103a 100644 --- a/docs/rfds/agent-skills.mdx +++ b/docs/rfds/agent-skills.mdx @@ -11,7 +11,7 @@ Author(s): [@benbrandt](https://github.com/benbrandt), building on [#370](https: Give [Agent Skills](https://agentskills.io/specification) their own type in ACP v2, so Clients can show them as skills and invoke them with a structured reference instead of `/name` text. - A Client advertises a new `session.commands.skill` capability to say it understands skills. -- An Agent then advertises its skills as `skill` entries in `availableCommands`. A `skill` entry has a command's fields plus an optional `path` to the skill's `SKILL.md`. Clients that don't advertise the capability keep getting skills as plain commands. +- An Agent then advertises its skills as `skill` entries in `availableCommands`. A `skill` entry has a `name`, a `description`, and an optional `path` to the skill's `SKILL.md`. Clients that don't advertise the capability keep getting skills as plain commands. - A Client invokes a skill with a new `skill` content block that names it. Every Agent that sends `skill` entries accepts the block at the start of a prompt. Agents that advertise `session.prompt.skill.inline` accept it anywhere in a prompt, any number of times. It adds no new protocol surface for Clients that want to supply skills to an Agent. That works today through `additionalDirectories`, and in future through MCP servers that implement the MCP [Skills extension](https://modelcontextprotocol.io/seps/2640-skills-extension). @@ -78,13 +78,11 @@ An Agent can then send `skill` entries in `availableCommands`: "type": "skill", "name": "pdf-processing", "description": "Extract text and tables from PDF files", - "input": { "type": "text", "hint": "file or instructions" }, "path": "/home/alice/.agents/skills/pdf-processing/SKILL.md" } ``` -- `name`, `description`, and `input` follow the same rules as on `command`. `name` is unique across the whole list, commands and skills together, and it is how the Client refers to the skill. It need not match the `name` in the skill's frontmatter. An Agent with two skills of the same name, or a skill and a command of the same name, qualifies one of them, for example as `plugin:pdf-processing`. How it does so is up to the Agent. -- `input` hints at what the user is expected to write along with the skill. +- `name` and `description` follow the same rules as on `command`. `name` is unique across the whole list, commands and skills together, and it is how the Client refers to the skill. It need not match the `name` in the skill's frontmatter. An Agent with two skills of the same name, or a skill and a command of the same name, qualifies one of them, for example as `plugin:pdf-processing`. How it does so is up to the Agent. - `description` is shown to users. A skill's frontmatter description is written for the model and can run to a thousand characters, so an Agent that has a shorter description meant for users **SHOULD** send that one. - `path` is optional. It is the absolute path, on the machine the Agent runs on, of the file that defines the skill, normally its `SKILL.md`. Agents omit it for skills with no such file, such as skills built into the Agent or served by an MCP server. Clients can use it to show where a skill lives or to open it. A Client that doesn't share a filesystem with the Agent can't open it. - Agents **SHOULD** advertise only skills the user can invoke. Skills that only the model can use, such as Claude Code skills with `user-invocable: false`, are left out, because a Client can't do anything with them. @@ -194,11 +192,10 @@ Everything here is additive to v2 and gated by capabilities on both sides, so it - Add `AvailableCommand::Skill(Skill)`, and add `"skill"` to the known `AvailableCommand` discriminators, so a malformed skill entry is skipped instead of being treated as an unknown type. `Skill` has these fields: - `name: String` - `description: String` - - `input: Option` - `path: Option` - `_meta` - For `input`, `path`, and `_meta`, omitted and `null` are equivalent. They only affect display, so an invalid value is treated as absent instead of dropping the entry. + For `path` and `_meta`, omitted and `null` are equivalent. They only affect display, so an invalid value is treated as absent instead of dropping the entry. - Add `ContentBlock::Skill` with a required `name` string and `_meta`, and add `"skill"` to the known content block types. - Slash commands, content, and initialization pages: document the entry, the block, and both capabilities. @@ -289,14 +286,20 @@ An earlier version had an optional `source` saying where a skill came from: buil MCP-served skills need it most, because SEP-2640 expects hosts to show which server a skill came from. It belongs with that follow-up. +### Why no `input` on a skill entry? + +An earlier version gave `skill` entries the optional `input` hint that `command` entries have. The Agent Skills specification defines no arguments for a skill. Claude Code adds them as its own extension, with `argument-hint` and `$ARGUMENTS`, and the other Agents surveyed have no equivalent. + +The hint also doesn't fit the block. A `skill` block carries no input, and with `inline` it can sit in the middle of a sentence. + ### Why not pass through frontmatter, `allowed-tools`, or a model-invocable flag? None of these change what a Client can do with a skill. -- `license`, `compatibility`, and `metadata` are display-only. They can go in `_meta`, or become optional fields later if Clients need them. +- `license`, `compatibility`, and `metadata` are display-only. - `allowed-tools` is a permission grant that the Agent enforces. Showing it to the Client invites treating it as a trust signal, and SEP-2640 requires hosts to ignore it for remote skills unless the user approves. - Only Qwen Code exposes a model-invocable flag today. -- Display names, icons, and brand colors exist only in Codex. `_meta` can carry them until more Agents have them. +- Display names, icons, and brand colors exist only in Codex. ### Why not standardize the `$` mention syntax? @@ -320,4 +323,6 @@ v1's `AvailableCommand` has no `type` discriminator, and its `ContentBlock` is a ## Revision history +- 2026-11-14: Schema landed. The `skill` command entry, the `skill` content block, the `session.commands.skill` Client capability, and the `session.prompt.skill` Agent capability are in the v2 unstable schema, behind the `unstable_agent_skills` feature. The rules are in the draft v2 [Slash Commands](/protocol/v2/draft/slash-commands#skills-unstable) and [Content](/protocol/v2/draft/content#skill-reference-unstable) pages. +- 2026-11-14: Remove `input` from the `skill` entry. The Agent Skills specification defines no arguments for a skill. - 2026-11-13: Initial draft, reworking [#370](https://github.com/agentclientprotocol/agent-client-protocol/pull/370) around `AvailableCommand` and the MCP Skills extension. diff --git a/schema/v2/schema.unstable.json b/schema/v2/schema.unstable.json index c18eeb0b5..607fdf8e0 100644 --- a/schema/v2/schema.unstable.json +++ b/schema/v2/schema.unstable.json @@ -777,6 +777,22 @@ } ] }, + { + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nA reference to a skill that the user invokes with this prompt.\n\nRequires the `skill` prompt capability when included in prompts. A skill\nreference has no meaning outside a user message.", + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "skill" + } + }, + "required": ["type"], + "allOf": [ + { + "$ref": "#/$defs/SkillReference" + } + ] + }, { "title": "other", "description": "Custom or future content block.\n\nValues beginning with `_` are reserved for implementation-specific\nextensions. Unknown values that do not begin with `_` are reserved for\nfuture ACP variants.\n\nReceivers that do not understand this content block type should preserve\nthe raw payload when storing, replaying, proxying, or forwarding content,\nand otherwise ignore it or display it generically. Agents MUST reject a\n`session/prompt` request with a content block type they do not support\nwith `-32602`.", @@ -839,6 +855,16 @@ } }, "required": ["type"] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "skill" + } + }, + "required": ["type"] } ] }, @@ -1283,6 +1309,23 @@ }, "required": ["resource"] }, + "SkillReference": { + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nA reference to a skill that the user invokes with a prompt.\n\nThe Client sends it in `session/prompt` to invoke a `skill` entry from the\nsession's command list. The Agent MUST treat each reference as the user\nexplicitly invoking that skill for this prompt, and applies the skill to\nthe prompt as a whole. The reference carries no input.\n\nAn Agent that advertises the `session.prompt.skill` capability accepts one\nskill reference per prompt, as the first content block. An Agent that also\nadvertises `session.prompt.skill.inline` accepts any number of them, at any\nposition. Clients MUST NOT send a skill reference that the Agent's\ncapability does not allow.\n\nThe `type` discriminator value is `skill`.", + "type": "object", + "properties": { + "name": { + "description": "The `name` of a `skill` entry in the session's current command list.\n\nRequired and non-null. The Agent MUST reject the prompt with `-32602`\nwhen it cannot resolve the name to a skill the user can invoke.", + "type": "string" + }, + "_meta": { + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Omitted and `null` are equivalent and mean no metadata.\nImplementations MUST NOT assign meaning to keys they do not recognize.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility)", + "type": ["object", "null"], + "x-deserialize-default-on-error": true, + "additionalProperties": true + } + }, + "required": ["name"] + }, "Content": { "description": "Standard content block (text, images, resources).", "type": "object", @@ -3323,6 +3366,18 @@ ], "x-deserialize-default-on-error": true }, + "skill": { + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nAgent supports `skill` content blocks.\n\nOptional. Omitted and `null` are equivalent and mean the agent does not advertise support.\nSupplying `{}` means a prompt may contain one `skill` content block, as its first block.\nAn agent that sends `skill` entries in `availableCommands` MUST advertise this capability.", + "anyOf": [ + { + "$ref": "#/$defs/PromptSkillCapabilities" + }, + { + "type": "null" + } + ], + "x-deserialize-default-on-error": true + }, "_meta": { "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Omitted and `null` are equivalent and mean no metadata.\nImplementations MUST NOT assign meaning to keys they do not recognize.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility)", "type": ["object", "null"], @@ -3367,6 +3422,42 @@ } } }, + "PromptSkillCapabilities": { + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nCapabilities for `skill` content blocks in prompt requests.\n\nSupplying `{}` means a prompt may contain one `skill` content block, as\nits first block. The agent opts in to more with the `inline` field.", + "type": "object", + "properties": { + "inline": { + "description": "Agent supports `skill` content blocks at any position in a prompt.\n\nOptional. Omitted and `null` are equivalent and mean the agent does not advertise support.\nSupplying `{}` means a prompt may contain any number of `skill` content blocks, at any\nposition.", + "anyOf": [ + { + "$ref": "#/$defs/PromptSkillInlineCapabilities" + }, + { + "type": "null" + } + ], + "x-deserialize-default-on-error": true + }, + "_meta": { + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Omitted and `null` are equivalent and mean no metadata.\nImplementations MUST NOT assign meaning to keys they do not recognize.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility)", + "type": ["object", "null"], + "x-deserialize-default-on-error": true, + "additionalProperties": true + } + } + }, + "PromptSkillInlineCapabilities": { + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nCapabilities for `skill` content blocks at any position in a prompt.\n\nSupplying `{}` means a prompt may contain any number of `skill` content\nblocks, at any position.", + "type": "object", + "properties": { + "_meta": { + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Omitted and `null` are equivalent and mean no metadata.\nImplementations MUST NOT assign meaning to keys they do not recognize.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility)", + "type": ["object", "null"], + "x-deserialize-default-on-error": true, + "additionalProperties": true + } + } + }, "McpCapabilities": { "description": "MCP capabilities supported by the agent for session lifecycle requests.", "type": "object", @@ -4464,6 +4555,22 @@ } ] }, + { + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nAn Agent Skill the user can invoke with a `skill` content block.\n\nAgents MUST only send this entry when the Client advertised\nthe `session.commands.skill` Client capability.", + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "skill" + } + }, + "required": ["type"], + "allOf": [ + { + "$ref": "#/$defs/Skill" + } + ] + }, { "title": "other", "description": "Custom or future command kind.\n\nValues beginning with `_` are reserved for implementation-specific\nextensions. Unknown values that do not begin with `_` are reserved for\nfuture ACP variants.\n\nClients that do not understand this command type should preserve the raw\npayload when storing, replaying, proxying, or forwarding command\nmetadata, and otherwise ignore the command. They MUST NOT display it as a\nslash command.", @@ -4494,6 +4601,16 @@ } }, "required": ["type"] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "skill" + } + }, + "required": ["type"] } ] }, @@ -4599,6 +4716,39 @@ }, "required": ["name", "description"] }, + "Skill": { + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nAn Agent Skill the user can invoke.\n\nThe `type` discriminator value is `skill`.\n\nThe Client invokes a skill with a `skill` content block that names it. The\n`/name` syntax applies only to `command` entries. An Agent that sends\n`skill` entries MUST advertise the `session.prompt.skill` capability.\n\nAgents MUST only send `skill` entries when the Client advertised the\n`session.commands.skill` Client capability. Agents SHOULD advertise only\nskills the user can invoke.", + "type": "object", + "properties": { + "name": { + "description": "Skill name. The Client uses it to refer to the skill in a `skill` content block.\n\nMUST NOT contain whitespace and MUST be unique within the command list,\nacross commands and skills. It need not match the `name` in the skill's\nfrontmatter.", + "type": "string" + }, + "description": { + "description": "Human-readable description of what the skill does, shown to users.", + "type": "string" + }, + "path": { + "description": "The absolute path of the file that defines the skill, normally its\n`SKILL.md`, on the machine the Agent runs on.\n\nOmitted and `null` are equivalent and mean the skill has no such file,\nas with a skill that is built into the Agent or served by an MCP server.", + "anyOf": [ + { + "$ref": "#/$defs/AbsolutePath" + }, + { + "type": "null" + } + ], + "x-deserialize-default-on-error": true + }, + "_meta": { + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Omitted and `null` are equivalent and mean no metadata.\nImplementations MUST NOT assign meaning to keys they do not recognize.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility)", + "type": ["object", "null"], + "x-deserialize-default-on-error": true, + "additionalProperties": true + } + }, + "required": ["name", "description"] + }, "ListSessionsResponse": { "description": "Response from listing sessions.", "type": "object", @@ -8266,6 +8416,18 @@ ], "x-deserialize-default-on-error": true }, + "commands": { + "description": "Command types the client understands in addition to `command`.\n\nOptional. Omitted and `null` are equivalent and mean the client does not advertise support\nfor any of them. This object only groups capabilities. Supplying `{}` advertises nothing.", + "anyOf": [ + { + "$ref": "#/$defs/CommandsCapabilities" + }, + { + "type": "null" + } + ], + "x-deserialize-default-on-error": true + }, "_meta": { "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Omitted and `null` are equivalent and mean no metadata.\nImplementations MUST NOT assign meaning to keys they do not recognize.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility)", "type": ["object", "null"], @@ -8358,6 +8520,42 @@ } } }, + "CommandsCapabilities": { + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nCommand types the client understands in addition to `command`.\n\nAgents MUST NOT send a command type listed here unless the client\nadvertised it. This object only groups capabilities. Supplying `{}`\nadvertises nothing.", + "type": "object", + "properties": { + "skill": { + "description": "Support for `skill` command entries.\n\nOptional. Omitted and `null` are equivalent and mean the client does not advertise support.\nSupplying `{}` means the agent may send `skill` entries in `availableCommands`. The\nclient invokes them with `skill` content blocks.", + "anyOf": [ + { + "$ref": "#/$defs/CommandsSkillCapabilities" + }, + { + "type": "null" + } + ], + "x-deserialize-default-on-error": true + }, + "_meta": { + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Omitted and `null` are equivalent and mean no metadata.\nImplementations MUST NOT assign meaning to keys they do not recognize.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility)", + "type": ["object", "null"], + "x-deserialize-default-on-error": true, + "additionalProperties": true + } + } + }, + "CommandsSkillCapabilities": { + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nClient support for `skill` command entries.\n\nSupplying `{}` means the agent may send `skill` entries in\n`availableCommands`. The client invokes them with `skill` content blocks.", + "type": "object", + "properties": { + "_meta": { + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Omitted and `null` are equivalent and mean no metadata.\nImplementations MUST NOT assign meaning to keys they do not recognize.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility)", + "type": ["object", "null"], + "x-deserialize-default-on-error": true, + "additionalProperties": true + } + } + }, "LoginAuthRequest": { "description": "Request parameters for the `auth/login` method.\n\nSpecifies which authentication method to use.\n\nAgents MUST support this method when their `initialize` response included\na non-empty `authMethods` list. Clients MUST NOT call this method\nwhen `authMethods` was omitted or empty.", "type": "object",