diff --git a/docs/docs.json b/docs/docs.json
index a5e89575..14f2dbd3 100644
--- a/docs/docs.json
+++ b/docs/docs.json
@@ -58,6 +58,7 @@
"protocol/file-system",
"protocol/terminals",
"protocol/agent-plan",
+ "protocol/extensibility",
"protocol/schema"
]
},
diff --git a/docs/protocol/extensibility.mdx b/docs/protocol/extensibility.mdx
new file mode 100644
index 00000000..5749587d
--- /dev/null
+++ b/docs/protocol/extensibility.mdx
@@ -0,0 +1,127 @@
+---
+title: "Extensibility"
+description: "Adding custom data and capabilities"
+---
+
+The Agent Client Protocol provides built-in extension mechanisms that allow implementations to add custom functionality while maintaining compatibility with the core protocol. These mechanisms ensure that Agents and Clients can innovate without breaking interoperability.
+
+## The `_meta` Field
+
+All types in the protocol include a `_meta` field that implementations can use to attach custom information. This includes requests, responses, notifications, and even nested types like content blocks, tool calls, plan entries, and capability objects.
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1,
+ "method": "session/prompt",
+ "params": {
+ "sessionId": "sess_abc123def456",
+ "prompt": [
+ {
+ "type": "text",
+ "text": "Hello, world!"
+ }
+ ],
+ "_meta": {
+ "zed.dev/debugMode": true
+ }
+ }
+}
+```
+
+Implementations **MUST NOT** add any custom fields at the root of a type that's part of the specification. All possible names are reserved for future protocol versions.
+
+## Extension Methods
+
+The protocol reserves any method name starting with an underscore (`_`) for custom extensions. This allows implementations to add new functionality without the risk of conflicting with future protocol versions.
+
+Extension methods follow standard [JSON-RPC 2.0](https://www.jsonrpc.org/specification) semantics:
+
+- **[Requests](https://www.jsonrpc.org/specification#request_object)** - Include an `id` field and expect a response
+- **[Notifications](https://www.jsonrpc.org/specification#notification)** - Omit the `id` field and are one-way (fire-and-forget)
+
+### Custom Requests
+
+In addition to the requests specified by the protocol, implementations **MAY** expose and call custom JSON-RPC requests as long as their name starts with an underscore (`_`).
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1,
+ "method": "_zed.dev/workspace/buffers",
+ "params": {
+ "language": "rust"
+ }
+}
+```
+
+Upon receiveing a custom request, implementations **MUST** respond accordingly with the provided `id`:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1,
+ "result": {
+ "buffers": [
+ { "id": 0, "path": "/home/user/project/src/main.rs" },
+ { "id": 1, "path": "/home/user/project/src/editor.rs" }
+ ]
+ }
+}
+```
+
+If the receiveing end doesn't recognize the custom method name, it should respond with the standard "Method not found" error:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1,
+ "error": {
+ "code": -32601,
+ "message": "Method not found"
+ }
+}
+```
+
+To avoid such cases, extensions **SHOULD** advertise their [custom capabilities](#advertising-custom-capabilities) so that callers can check their availability first and adapt their interface accordingly.
+
+### Custom Notifications
+
+Custom notifications are regular JSON-RPC notifications that start with an underscore (`_`). Like all notifications, they omit the `id` field:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "_zed.dev/file_opened",
+ "params": {
+ "path": "/home/user/project/src/editor.rs"
+ }
+}
+```
+
+Unlike with custom requests, implementations **SHOULD** ignore unrecognized notifications.
+
+## Advertising Custom Capabilities
+
+Implementations **SHOULD** use the `_meta` field in capability objects to advertise support for extensions and their methods:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 0,
+ "result": {
+ "protocolVersion": 1,
+ "agentCapabilities": {
+ "loadSession": true,
+ "_meta": {
+ "zed.dev": {
+ "workspace": true,
+ "fileNotifications": true
+ }
+ }
+ }
+ }
+}
+```
+
+This allows implementations to negotiate custom features during initialization without breaking compatibility with standard Clients and Agents.
diff --git a/docs/protocol/initialization.mdx b/docs/protocol/initialization.mdx
index 405fb9a1..88ce91e1 100644
--- a/docs/protocol/initialization.mdx
+++ b/docs/protocol/initialization.mdx
@@ -99,6 +99,8 @@ Capabilities are high-level and are not attached to a specific base protocol con
Capabilities may specify the availability of protocol methods, notifications, or a subset of their parameters. They may also signal behaviors of the Agent or Client implementation.
+Implementations can also [advertise custom capabilities](./extensibility#advertising-custom-capabilities) using the `_meta` field to indicate support for protocol extensions.
+
### Client Capabilities
The Client **SHOULD** specify whether it supports the following capabilities:
diff --git a/docs/protocol/overview.mdx b/docs/protocol/overview.mdx
index 487fb251..c340de26 100644
--- a/docs/protocol/overview.mdx
+++ b/docs/protocol/overview.mdx
@@ -149,8 +149,19 @@ All methods follow standard JSON-RPC 2.0 [error handling](https://www.jsonrpc.or
- Errors include an `error` object with `code` and `message`
- Notifications never receive responses (success or error)
+## Extensibility
+
+The protocol provides built-in mechanisms for adding custom functionality while maintaining compatibility:
+
+- Add custom data using `_meta` fields
+- Create custom methods with `_method` and `_notification`
+- Advertise custom capabilities during initialization
+
+Learn about [protocol extensibility](./extensibility) to understand how to use these mechanisms.
+
## Next Steps
- Learn about [Initialization](./initialization) to understand version and capability negotiation
- Understand [Session Setup](./session-setup) for creating and loading sessions
- Review the [Prompt Turn](./prompt-turn) lifecycle
+- Explore [Extensibility](./extensibility) to add custom features
diff --git a/docs/protocol/schema.mdx b/docs/protocol/schema.mdx
index 08afa666..b24163ea 100644
--- a/docs/protocol/schema.mdx
+++ b/docs/protocol/schema.mdx
@@ -32,6 +32,9 @@ Specifies which authentication method to use.
**Properties:**
+
+ Extension point for implementations
+
AuthMethodId}
@@ -67,6 +70,9 @@ See protocol docs: [Initialization](https://agentclientprotocol.com/protocol/ini
**Properties:**
+
+ Extension point for implementations
+
ClientCapabilities} >
Capabilities supported by the client.
@@ -89,6 +95,9 @@ See protocol docs: [Initialization](https://agentclientprotocol.com/protocol/ini
**Properties:**
+
+ Extension point for implementations
+
AgentCapabilities} >
Capabilities supported by the agent.
@@ -135,6 +144,9 @@ See protocol docs: [Cancellation](https://agentclientprotocol.com/protocol/promp
**Properties:**
+
+ Extension point for implementations
+
SessionId}
@@ -170,6 +182,9 @@ See protocol docs: [Loading Sessions](https://agentclientprotocol.com/protocol/s
**Properties:**
+
+ Extension point for implementations
+
The working directory for this session.
@@ -222,6 +237,9 @@ See protocol docs: [Creating a Session](https://agentclientprotocol.com/protocol
**Properties:**
+
+ Extension point for implementations
+
The working directory for this session. Must be an absolute path.
@@ -250,6 +268,9 @@ See protocol docs: [Creating a Session](https://agentclientprotocol.com/protocol
**Properties:**
+
+ Extension point for implementations
+
SessionModeState | null>} >
**UNSTABLE**
@@ -291,6 +312,9 @@ See protocol docs: [User Message](https://agentclientprotocol.com/protocol/promp
**Properties:**
+
+ Extension point for implementations
+
ContentBlock[]>} required>
The blocks of content that compose the user's message.
@@ -321,6 +345,9 @@ See protocol docs: [Check for Completion](https://agentclientprotocol.com/protoc
**Properties:**
+
+ Extension point for implementations
+
StopReason}
@@ -357,6 +384,9 @@ Only available if the client supports the `fs.readTextFile` capability.
**Properties:**
+
+ Extension point for implementations
+
Maximum number of lines to read.
@@ -396,6 +426,9 @@ Only available if the client supports the `fs.writeTextFile` capability.
**Properties:**
+
+ Extension point for implementations
+
The text content to write to the file.
@@ -436,6 +469,9 @@ See protocol docs: [Requesting Permission](https://agentclientprotocol.com/proto
**Properties:**
+
+ Extension point for implementations
+
+ Extension point for implementations
+
RequestPermissionOutcome}
@@ -508,6 +547,9 @@ See protocol docs: [Agent Reports Output](https://agentclientprotocol.com/protoc
**Properties:**
+
+ Extension point for implementations
+
SessionId}
@@ -549,6 +591,9 @@ Request to create a new terminal and execute a command.
**Properties:**
+
+ Extension point for implementations
+
"string"[]>} >
Array of command arguments.
@@ -586,6 +631,9 @@ Response containing the ID of the created terminal.
**Properties:**
+
+ Extension point for implementations
+
The unique identifier for the created terminal.
@@ -614,6 +662,9 @@ Request to kill a terminal command without releasing the terminal.
**Properties:**
+
+ Extension point for implementations
+
SessionId}
@@ -643,6 +694,9 @@ Request to get the current output and status of a terminal.
**Properties:**
+
+ Extension point for implementations
+
SessionId}
@@ -662,6 +716,9 @@ Response containing the terminal output and exit status.
**Properties:**
+
+ Extension point for implementations
+
+ Extension point for implementations
+
SessionId}
@@ -732,6 +792,9 @@ Request to wait for a terminal command to exit.
**Properties:**
+
+ Extension point for implementations
+
SessionId}
@@ -751,6 +814,9 @@ Response containing the exit status of a terminal command.
**Properties:**
+
+ Extension point for implementations
+
The process exit code (may be null if terminated by signal).
@@ -774,6 +840,9 @@ See protocol docs: [Agent Capabilities](https://agentclientprotocol.com/protocol
**Properties:**
+
+ Extension point for implementations
+
Whether the agent supports `session/load`.
@@ -801,6 +870,9 @@ Optional annotations for the client. The client can use annotations to inform ho
**Properties:**
+
+ Extension point for implementations
+
@@ -813,6 +885,9 @@ Audio provided to or from an LLM.
**Properties:**
+
+ Extension point for implementations
+
+ Extension point for implementations
+
Optional description providing more details about this authentication method.
@@ -863,6 +941,9 @@ Information about a command.
**Properties:**
+
+ Extension point for implementations
+
Human-readable description of what the command does.
@@ -907,6 +988,9 @@ Binary resource contents.
**Properties:**
+
+ Extension point for implementations
+
@@ -924,6 +1008,9 @@ See protocol docs: [Client Capabilities](https://agentclientprotocol.com/protoco
**Properties:**
+
+ Extension point for implementations
+
FileSystemCapability} >
File system capabilities supported by the client.
Determines which file operations the agent can request.
@@ -965,6 +1052,9 @@ All agents MUST support text content blocks in prompts.
+
+ Extension point for implementations
+
+
+ Extension point for implementations
+
+
+ Extension point for implementations
+
+
+ Extension point for implementations
+
+
+ Extension point for implementations
+
+ Extension point for implementations
+
+
+ Extension point for implementations
+
@@ -1140,6 +1248,9 @@ Resource content that can be embedded in a message.
+
+ Extension point for implementations
+
@@ -1155,6 +1266,9 @@ An environment variable to set when launching an MCP server.
**Properties:**
+
+ Extension point for implementations
+
The name of the environment variable.
@@ -1172,6 +1286,9 @@ See protocol docs: [FileSystem](https://agentclientprotocol.com/protocol/initial
**Properties:**
+
+ Extension point for implementations
+
Whether the Client supports `fs/read_text_file` requests.
@@ -1193,6 +1310,9 @@ An HTTP header to set when making requests to the MCP server.
**Properties:**
+
+ Extension point for implementations
+
The name of the HTTP header.
@@ -1208,6 +1328,9 @@ An image provided to or from an LLM.
**Properties:**
+
+ Extension point for implementations
+
+ Extension point for implementations
+
SessionModeState | null>} >
**UNSTABLE**
@@ -1246,6 +1372,9 @@ MCP capabilities supported by the agent
**Properties:**
+
+ Extension point for implementations
+
Agent supports `McpServer::Http`.
@@ -1385,6 +1514,9 @@ An option presented to the user when requesting permission.
**Properties:**
+
+ Extension point for implementations
+
PermissionOptionKind}
@@ -1447,6 +1579,9 @@ See protocol docs: [Agent Plan](https://agentclientprotocol.com/protocol/agent-p
**Properties:**
+
+ Extension point for implementations
+
PlanEntry[]>} required>
The list of tasks to be accomplished.
@@ -1467,6 +1602,9 @@ See protocol docs: [Plan Entries](https://agentclientprotocol.com/protocol/agent
**Properties:**
+
+ Extension point for implementations
+
Human-readable description of what this task aims to accomplish.
@@ -1546,6 +1684,9 @@ See protocol docs: [Prompt Capabilities](https://agentclientprotocol.com/protoco
**Properties:**
+
+ Extension point for implementations
+
Agent supports `ContentBlock::Audio`.
@@ -1590,6 +1731,9 @@ Response containing the contents of a text file.
**Properties:**
+
+ Extension point for implementations
+
## RequestPermissionOutcome
@@ -1639,6 +1783,9 @@ A resource that the server is capable of reading, included in a prompt or tool c
**Properties:**
+
+ Extension point for implementations
+
+ Extension point for implementations
+
+ Extension point for implementations
+
+
+ Extension point for implementations
+
+
+ Extension point for implementations
+
Replace the content collection.
@@ -1924,6 +2083,9 @@ See protocol docs: [Agent Plan](https://agentclientprotocol.com/protocol/agent-p
+
+ Extension point for implementations
+
PlanEntry[]>} required>
The list of tasks to be accomplished.
@@ -2025,6 +2187,9 @@ Exit status of a terminal command.
**Properties:**
+
+ Extension point for implementations
+
The process exit code (may be null if terminated by signal).
@@ -2043,6 +2208,9 @@ Text provided to or from an LLM.
**Properties:**
+
+ Extension point for implementations
+
+ Extension point for implementations
+
@@ -2081,6 +2252,9 @@ See protocol docs: [Tool Calls](https://agentclientprotocol.com/protocol/tool-ca
**Properties:**
+
+ Extension point for implementations
+
+
+ Extension point for implementations
+
The new content after modification.
@@ -2216,6 +2393,9 @@ See protocol docs: [Following the Agent](https://agentclientprotocol.com/protoco
**Properties:**
+
+ Extension point for implementations
+
Optional line number within the file.
@@ -2264,6 +2444,9 @@ See protocol docs: [Updating](https://agentclientprotocol.com/protocol/tool-call
**Properties:**
+
+ Extension point for implementations
+
Replace the content collection.
diff --git a/rust/acp.rs b/rust/acp.rs
index d2459afb..fb66ed2e 100644
--- a/rust/acp.rs
+++ b/rust/acp.rs
@@ -54,6 +54,7 @@ mod agent;
mod client;
mod content;
mod error;
+mod ext;
mod plan;
mod rpc;
#[cfg(test)]
@@ -66,6 +67,7 @@ pub use agent::*;
pub use client::*;
pub use content::*;
pub use error::*;
+pub use ext::*;
pub use plan::*;
pub use stream_broadcast::{
StreamMessage, StreamMessageContent, StreamMessageDirection, StreamReceiver,
@@ -232,6 +234,32 @@ impl Agent for ClientSideConnection {
Some(ClientNotification::CancelNotification(notification)),
)
}
+
+ async fn ext_method(
+ &self,
+ method: Arc,
+ params: Arc,
+ ) -> Result, Error> {
+ self.conn
+ .request(
+ format!("_{method}"),
+ Some(ClientRequest::ExtMethodRequest(ExtMethod {
+ method,
+ params,
+ })),
+ )
+ .await
+ }
+
+ async fn ext_notification(&self, method: Arc, params: Arc) -> Result<(), Error> {
+ self.conn.notify(
+ format!("_{method}"),
+ Some(ClientNotification::ExtNotification(ExtMethod {
+ method,
+ params,
+ })),
+ )
+ }
}
/// Marker type representing the client side of an ACP connection.
@@ -276,7 +304,16 @@ impl Side for ClientSide {
TERMINAL_WAIT_FOR_EXIT_METHOD_NAME => serde_json::from_str(params.get())
.map(AgentRequest::WaitForTerminalExitRequest)
.map_err(Into::into),
- _ => Err(Error::method_not_found()),
+ _ => {
+ if let Some(custom_method) = method.strip_prefix('_') {
+ Ok(AgentRequest::ExtMethodRequest(ExtMethod {
+ method: custom_method.into(),
+ params: RawValue::from_string(params.get().to_string())?.into(),
+ }))
+ } else {
+ Err(Error::method_not_found())
+ }
+ }
}
}
@@ -290,7 +327,16 @@ impl Side for ClientSide {
SESSION_UPDATE_NOTIFICATION => serde_json::from_str(params.get())
.map(AgentNotification::SessionNotification)
.map_err(Into::into),
- _ => Err(Error::method_not_found()),
+ _ => {
+ if let Some(custom_method) = method.strip_prefix('_') {
+ Ok(AgentNotification::ExtNotification(ExtMethod {
+ method: custom_method.into(),
+ params: RawValue::from_string(params.get().to_string())?.into(),
+ }))
+ } else {
+ Err(Error::method_not_found())
+ }
+ }
}
}
}
@@ -330,6 +376,10 @@ impl MessageHandler for T {
self.kill_terminal_command(args).await?;
Ok(ClientResponse::KillTerminalResponse)
}
+ AgentRequest::ExtMethodRequest(args) => {
+ let response = self.ext_method(args.method, args.params).await?;
+ Ok(ClientResponse::ExtMethodResponse(response))
+ }
}
}
@@ -338,6 +388,9 @@ impl MessageHandler for T {
AgentNotification::SessionNotification(notification) => {
self.session_notification(notification).await?;
}
+ AgentNotification::ExtNotification(args) => {
+ self.ext_notification(args.method, args.params).await?;
+ }
}
Ok(())
}
@@ -497,6 +550,29 @@ impl Client for AgentSideConnection {
Some(AgentNotification::SessionNotification(notification)),
)
}
+
+ async fn ext_method(
+ &self,
+ method: Arc,
+ params: Arc,
+ ) -> Result, Error> {
+ self.conn
+ .request(
+ format!("_{method}"),
+ Some(AgentRequest::ExtMethodRequest(ExtMethod { method, params })),
+ )
+ .await
+ }
+
+ async fn ext_notification(&self, method: Arc, params: Arc) -> Result<(), Error> {
+ self.conn.notify(
+ format!("_{method}"),
+ Some(AgentNotification::ExtNotification(ExtMethod {
+ method,
+ params,
+ })),
+ )
+ }
}
/// Marker type representing the agent side of an ACP connection.
@@ -536,7 +612,16 @@ impl Side for AgentSide {
SESSION_PROMPT_METHOD_NAME => serde_json::from_str(params.get())
.map(ClientRequest::PromptRequest)
.map_err(Into::into),
- _ => Err(Error::method_not_found()),
+ _ => {
+ if let Some(custom_method) = method.strip_prefix('_') {
+ Ok(ClientRequest::ExtMethodRequest(ExtMethod {
+ method: custom_method.into(),
+ params: RawValue::from_string(params.get().to_string())?.into(),
+ }))
+ } else {
+ Err(Error::method_not_found())
+ }
+ }
}
}
@@ -550,7 +635,16 @@ impl Side for AgentSide {
SESSION_CANCEL_METHOD_NAME => serde_json::from_str(params.get())
.map(ClientNotification::CancelNotification)
.map_err(Into::into),
- _ => Err(Error::method_not_found()),
+ _ => {
+ if let Some(custom_method) = method.strip_prefix('_') {
+ Ok(ClientNotification::ExtNotification(ExtMethod {
+ method: custom_method.into(),
+ params: RawValue::from_string(params.get().to_string())?.into(),
+ }))
+ } else {
+ Err(Error::method_not_found())
+ }
+ }
}
}
}
@@ -583,6 +677,10 @@ impl MessageHandler for T {
let response = self.set_session_mode(args).await?;
Ok(AgentResponse::SetSessionModeResponse(response))
}
+ ClientRequest::ExtMethodRequest(args) => {
+ let response = self.ext_method(args.method, args.params).await?;
+ Ok(AgentResponse::ExtMethodResponse(response))
+ }
}
}
@@ -591,6 +689,9 @@ impl MessageHandler for T {
ClientNotification::CancelNotification(notification) => {
self.cancel(notification).await?;
}
+ ClientNotification::ExtNotification(args) => {
+ self.ext_notification(args.method, args.params).await?;
+ }
}
Ok(())
}
diff --git a/rust/agent.rs b/rust/agent.rs
index 25655c8f..0b9da83e 100644
--- a/rust/agent.rs
+++ b/rust/agent.rs
@@ -8,7 +8,9 @@ use std::{path::PathBuf, sync::Arc};
use anyhow::Result;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
+use serde_json::value::RawValue;
+use crate::ext::ExtMethod;
use crate::{ClientCapabilities, ContentBlock, Error, ProtocolVersion, SessionId};
/// Defines the interface that all ACP-compliant agents must implement.
@@ -114,6 +116,30 @@ pub trait Agent {
///
/// See protocol docs: [Cancellation](https://agentclientprotocol.com/protocol/prompt-turn#cancellation)
fn cancel(&self, args: CancelNotification) -> impl Future