Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions rust/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -640,14 +640,90 @@ pub(crate) const SESSION_SET_MODEL_METHOD_NAME: &str = "session/set_model";
#[serde(untagged)]
#[schemars(extend("x-docs-ignore" = true))]
pub enum ClientRequest {
/// Establishes the connection with a client and negotiates protocol capabilities.
///
/// This method is called once at the beginning of the connection to:
/// - Negotiate the protocol version to use
/// - Exchange capability information between client and agent
/// - Determine available authentication methods
///
/// The agent should respond with its supported protocol version and capabilities.
///
/// See protocol docs: [Initialization](https://agentclientprotocol.com/protocol/initialization)
InitializeRequest(InitializeRequest),
/// Authenticates the client using the specified authentication method.
///
/// Called when the agent requires authentication before allowing session creation.
/// The client provides the authentication method ID that was advertised during initialization.
///
/// After successful authentication, the client can proceed to create sessions with
/// `new_session` without receiving an `auth_required` error.
///
/// See protocol docs: [Initialization](https://agentclientprotocol.com/protocol/initialization)
AuthenticateRequest(AuthenticateRequest),
/// Creates a new conversation session with the agent.
///
/// Sessions represent independent conversation contexts with their own history and state.
///
/// The agent should:
/// - Create a new session context
/// - Connect to any specified MCP servers
/// - Return a unique session ID for future requests
///
/// May return an `auth_required` error if the agent requires authentication.
///
/// See protocol docs: [Session Setup](https://agentclientprotocol.com/protocol/session-setup)
NewSessionRequest(NewSessionRequest),
/// Loads an existing session to resume a previous conversation.
///
/// This method is only available if the agent advertises the `loadSession` capability.
///
/// The agent should:
/// - Restore the session context and conversation history
/// - Connect to the specified MCP servers
/// - Stream the entire conversation history back to the client via notifications
///
/// See protocol docs: [Loading Sessions](https://agentclientprotocol.com/protocol/session-setup#loading-sessions)
LoadSessionRequest(LoadSessionRequest),
/// Sets the current mode for a session.
///
/// Allows switching between different agent modes (e.g., "ask", "architect", "code")
/// that affect system prompts, tool availability, and permission behaviors.
///
/// The mode must be one of the modes advertised in `availableModes` during session
/// creation or loading. Agents may also change modes autonomously and notify the
/// client via `current_mode_update` notifications.
///
/// This method can be called at any time during a session, whether the Agent is
/// idle or actively generating a response.
///
/// See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)
SetSessionModeRequest(SetSessionModeRequest),
/// Processes a user prompt within a session.
///
/// This method handles the whole lifecycle of a prompt:
/// - Receives user messages with optional context (files, images, etc.)
/// - Processes the prompt using language models
/// - Reports language model content and tool calls to the Clients
/// - Requests permission to run tools
/// - Executes any requested tool calls
/// - Returns when the turn is complete with a stop reason
///
/// See protocol docs: [Prompt Turn](https://agentclientprotocol.com/protocol/prompt-turn)
PromptRequest(PromptRequest),
#[cfg(feature = "unstable")]
/// **UNSTABLE**
///
/// This capability is not part of the spec yet, and may be removed or changed at any point.
///
/// Select a model for a given session.
SetSessionModelRequest(SetSessionModelRequest),
/// Handles extension method requests from the client.
///
/// Extension methods provide a way to add custom functionality while maintaining
/// protocol compatibility.
///
/// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
ExtMethodRequest(ExtRequest),
}

Expand Down Expand Up @@ -682,7 +758,24 @@ pub enum AgentResponse {
#[serde(untagged)]
#[schemars(extend("x-docs-ignore" = true))]
pub enum ClientNotification {
/// Cancels ongoing operations for a session.
///
/// This is a notification sent by the client to cancel an ongoing prompt turn.
///
/// Upon receiving this notification, the Agent SHOULD:
/// - Stop all language model requests as soon as possible
/// - Abort all tool call invocations in progress
/// - Send any pending `session/update` notifications
/// - Respond to the original `session/prompt` request with `StopReason::Cancelled`
///
/// See protocol docs: [Cancellation](https://agentclientprotocol.com/protocol/prompt-turn#cancellation)
CancelNotification(CancelNotification),
/// Handles extension notifications from the client.
///
/// Extension notifications provide a way to send one-way messages for custom functionality
/// while maintaining protocol compatibility.
///
/// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
ExtNotification(ExtNotification),
}

Expand Down
139 changes: 88 additions & 51 deletions rust/bin/generate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,14 @@ mod markdown_generator {

writeln!(&mut self.output, "## Agent").unwrap();
writeln!(&mut self.output).unwrap();
writeln!(&mut self.output, "{}", side_docs.agent_trait).unwrap();
writeln!(
&mut self.output,
"Defines the interface that all ACP-compliant agents must implement.

Agents are programs that use generative AI to autonomously modify code. They handle
requests from clients and execute tasks using language models and tools."
)
.unwrap();
writeln!(&mut self.output).unwrap();

for (method, types) in agent_types {
Expand All @@ -235,7 +242,15 @@ mod markdown_generator {

writeln!(&mut self.output, "## Client").unwrap();
writeln!(&mut self.output).unwrap();
writeln!(&mut self.output, "{}", side_docs.client_trait).unwrap();
writeln!(
&mut self.output,
"Defines the interface that ACP-compliant clients must implement.

Clients are typically code editors (IDEs, text editors) that provide the interface
between users and AI agents. They manage the environment, handle user interactions,
and control access to resources."
)
.unwrap();

for (method, types) in client_types {
self.generate_method(&method, side_docs.client_method_doc(&method), types);
Expand Down Expand Up @@ -775,42 +790,44 @@ mod markdown_generator {
}

struct SideDocs {
agent_trait: String,
agent_methods: HashMap<String, String>,
client_trait: String,
client_methods: HashMap<String, String>,
}

impl SideDocs {
fn agent_method_doc(&self, method_name: &str) -> &String {
match method_name {
"initialize" => self.agent_methods.get("initialize").unwrap(),
"authenticate" => self.agent_methods.get("authenticate").unwrap(),
"session/new" => self.agent_methods.get("new_session").unwrap(),
"session/load" => self.agent_methods.get("load_session").unwrap(),
"session/set_mode" => self.agent_methods.get("set_session_mode").unwrap(),
"session/prompt" => self.agent_methods.get("prompt").unwrap(),
"session/cancel" => self.agent_methods.get("cancel").unwrap(),
"session/set_model" => self.agent_methods.get("set_session_model").unwrap(),
"initialize" => self.agent_methods.get("InitializeRequest").unwrap(),
"authenticate" => self.agent_methods.get("AuthenticateRequest").unwrap(),
"session/new" => self.agent_methods.get("NewSessionRequest").unwrap(),
"session/load" => self.agent_methods.get("LoadSessionRequest").unwrap(),
"session/set_mode" => self.agent_methods.get("SetSessionModeRequest").unwrap(),
"session/prompt" => self.agent_methods.get("PromptRequest").unwrap(),
"session/cancel" => self.agent_methods.get("CancelNotification").unwrap(),
"session/set_model" => self.agent_methods.get("SetSessionModelRequest").unwrap(),
_ => panic!("Introduced a method? Add it here :)"),
}
}

fn client_method_doc(&self, method_name: &str) -> &String {
match method_name {
"session/request_permission" => {
self.client_methods.get("request_permission").unwrap()
}
"fs/write_text_file" => self.client_methods.get("write_text_file").unwrap(),
"fs/read_text_file" => self.client_methods.get("read_text_file").unwrap(),
"session/update" => self.client_methods.get("session_notification").unwrap(),
"terminal/create" => self.client_methods.get("create_terminal").unwrap(),
"terminal/output" => self.client_methods.get("terminal_output").unwrap(),
"terminal/release" => self.client_methods.get("release_terminal").unwrap(),
"terminal/wait_for_exit" => {
self.client_methods.get("wait_for_terminal_exit").unwrap()
self.client_methods.get("RequestPermissionRequest").unwrap()
}
"terminal/kill" => self.client_methods.get("kill_terminal_command").unwrap(),
"fs/write_text_file" => self.client_methods.get("WriteTextFileRequest").unwrap(),
"fs/read_text_file" => self.client_methods.get("ReadTextFileRequest").unwrap(),
"session/update" => self.client_methods.get("SessionNotification").unwrap(),
"terminal/create" => self.client_methods.get("CreateTerminalRequest").unwrap(),
"terminal/output" => self.client_methods.get("TerminalOutputRequest").unwrap(),
"terminal/release" => self.client_methods.get("ReleaseTerminalRequest").unwrap(),
"terminal/wait_for_exit" => self
.client_methods
.get("WaitForTerminalExitRequest")
.unwrap(),
"terminal/kill" => self
.client_methods
.get("KillTerminalCommandRequest")
.unwrap(),
_ => panic!("Introduced a method? Add it here :)"),
}
}
Expand Down Expand Up @@ -845,48 +862,68 @@ mod markdown_generator {
let doc: Value = serde_json::from_str(&json_content).unwrap();

let mut side_docs = SideDocs {
agent_trait: String::new(),
agent_methods: HashMap::new(),
client_trait: String::new(),
client_methods: HashMap::new(),
};

if let Some(index) = doc["index"].as_object() {
for (_, item) in index {
if item["name"].as_str() == Some("Agent") {
if let Some(docs) = item["docs"].as_str() {
side_docs.agent_trait = docs.to_string();
if item["name"].as_str() == Some("ClientRequest")
&& let Some(variants) = item["inner"]["enum"]["variants"].as_array()
{
for variant_id in variants {
if let Some(variant) = doc["index"][variant_id.to_string()].as_object()
&& let Some(name) = variant["name"].as_str()
{
side_docs.agent_methods.insert(
name.to_string(),
variant["docs"].as_str().unwrap_or_default().to_string(),
);
}
}
}

if let Some(items) = item["inner"]["trait"]["items"].as_array() {
for method_id in items {
if let Some(method) = doc["index"][method_id.to_string()].as_object()
&& let Some(name) = method["name"].as_str()
{
side_docs.agent_methods.insert(
name.to_string(),
method["docs"].as_str().unwrap_or_default().to_string(),
);
}
if item["name"].as_str() == Some("ClientNotification")
&& let Some(variants) = item["inner"]["enum"]["variants"].as_array()
{
for variant_id in variants {
if let Some(variant) = doc["index"][variant_id.to_string()].as_object()
&& let Some(name) = variant["name"].as_str()
{
side_docs.agent_methods.insert(
name.to_string(),
variant["docs"].as_str().unwrap_or_default().to_string(),
);
}
}
}

if item["name"].as_str() == Some("Client") {
if let Some(docs) = item["docs"].as_str() {
side_docs.client_trait = docs.to_string();
if item["name"].as_str() == Some("AgentRequest")
&& let Some(variants) = item["inner"]["enum"]["variants"].as_array()
{
for variant_id in variants {
if let Some(variant) = doc["index"][variant_id.to_string()].as_object()
&& let Some(name) = variant["name"].as_str()
{
side_docs.client_methods.insert(
name.to_string(),
variant["docs"].as_str().unwrap_or_default().to_string(),
);
}
}
}

if let Some(items) = item["inner"]["trait"]["items"].as_array() {
for method_id in items {
if let Some(method) = doc["index"][method_id.to_string()].as_object()
&& let Some(name) = method["name"].as_str()
{
side_docs.client_methods.insert(
name.to_string(),
method["docs"].as_str().unwrap_or_default().to_string(),
);
}
if item["name"].as_str() == Some("AgentNotification")
&& let Some(variants) = item["inner"]["enum"]["variants"].as_array()
{
for variant_id in variants {
if let Some(variant) = doc["index"][variant_id.to_string()].as_object()
&& let Some(name) = variant["name"].as_str()
{
side_docs.client_methods.insert(
name.to_string(),
variant["docs"].as_str().unwrap_or_default().to_string(),
);
}
}
}
Expand Down
Loading