# ACP Registry is stabilized Source: https://agentclientprotocol.com/announcements/acp-agent-registry-stabilized Announcement that the ACP Registry RFD has been completed and the registry is stabilized. **Published:** March 9, 2026 The ACP Registry RFD has moved to Completed and the initial version of the registry is stabilized. The registry gives ACP clients a standard way to discover, install, and configure compatible agents. It introduces a curated catalog and a shared manifest format so editors and other clients do not need to invent their own integration metadata. If you want to explore the shipped experience, start with [ACP Registry](/get-started/registry). For the design history and rationale, see the [ACP Agent Registry RFD](/rfds/acp-agent-registry). # Implementation information for agents and clients Source: https://agentclientprotocol.com/announcements/implementation-information Protocol update introducing optional implementation metadata during initialization. **Published:** October 24, 2025 ACP now allows agents and clients to provide information about themselves to the other party during initialization. The [InitializeRequest](/protocol/v1/schema#initializerequest) message now includes an optional clientInfo field, and the [InitializeResponse](/protocol/v1/schema#initializeresponse) message includes an optional agentInfo field. This information can be used by clients to show users which agent is running and what version, by both sides to track usage metrics for which agents and clients are most popular among their users, and to help track down issues tied to particular implementation versions. This follows the existing pattern laid out in the [Model Context Protocol](https://modelcontextprotocol.io/specification/2025-06-18/basic/lifecycle#initialization). This is being introduced as an optional field for now for backwards compatibility. It is possible it will be made required in a future version of the protocol, like MCP, so that both sides can count on this information being available. For the user-facing protocol guide, see [Implementation Information](/protocol/v1/initialization#implementation-information). # Logout Method is stabilized Source: https://agentclientprotocol.com/announcements/logout-method-stabilized Announcement that the logout method is now part of the stable ACP protocol. **Published:** May 21, 2026 The [Logout Method RFD](/rfds/logout-method) has moved to Completed and the `logout` method is stabilized. When advertised via `agentCapabilities.auth.logout`, Clients can now ask Agents to end the current authenticated state and return the connection to a state where future authentication-gated requests require `authenticate` again. For the protocol documentation, see [Logging Out](/protocol/v1/authentication#logging-out). # Sergey Ignatov joins ACP as Lead Maintainer Source: https://agentclientprotocol.com/announcements/sergey-ignatov-lead-maintainer Governance announcement about Sergey Ignatov joining ACP as a Lead Maintainer. **Published:** February 18, 2026 It is with great pleasure that I announce that Sergey Ignatov, from JetBrains, will be joining me as a Lead Maintainer for ACP. Agus Zubiaga will remain a key part of the leadership group as a Core Maintainer. This is in recognition of Sergey’s many contributions to the protocol. We would not be where we are today without his efforts. In the past few months, Zed and JetBrains have been able to collaborate closely on the protocol, and it is time to reflect that fact more formally in our governance structure. I have enjoyed the chance to work with Sergey and the entire JetBrains team. There is still much work to be done, but with all of the agent and client teams working together, I am confident we can continue to make the ACP vision a reality: allowing you to collaborate with the agent or agents you love wherever you are already working. # Session Close is stabilized Source: https://agentclientprotocol.com/announcements/session-close-stabilized Announcement that the session/close method is now part of the stable ACP protocol. **Published:** April 23, 2026 The [Session Close RFD](/rfds/session-close) has moved to Completed and the `session/close` method is stabilized. When advertised via `sessionCapabilities.close`, Clients can now ask Agents to cancel any ongoing work for a session and free the resources associated with it, without having to terminate the whole ACP process. This lets long-running Clients keep memory, threads, and subprocesses under control as users accumulate sessions over time. For the protocol documentation, see [Closing Active Sessions](/protocol/v1/session-setup#closing-active-sessions). # Session Config Options are stabilized Source: https://agentclientprotocol.com/announcements/session-config-options-stabilized Announcement that session-level configuration selectors are now part of the stable ACP protocol. **Published:** February 4, 2026 The Session Config Options RFD has moved to Completed and is stabilized. Session Config Options give agents a flexible way to expose session-level configuration such as models, modes, reasoning levels, and other selectors. Instead of hard-coding a small set of protocol-level controls, clients can render the options an agent provides and keep them in sync as they change. The stable protocol documentation is available in [Session Config Options](/protocol/v1/session-config-options), and the design history remains in the [Session Config Options RFD](/rfds/session-config-options). # Session Info Update is stabilized Source: https://agentclientprotocol.com/announcements/session-info-update-stabilized Announcement that the session_info_update notification is now part of the stable ACP protocol. **Published:** March 9, 2026 The Session Info Update RFD has moved to Completed and the session\_info\_update notification is stabilized. This lets agents push session metadata updates to clients in real time, including generated titles and related metadata, so session lists can stay current without polling. The stable protocol behavior is documented in [Session List](/protocol/v1/session-list#updating-session-metadata), and the design history remains in the [Session Info Update RFD](/rfds/session-info-update). # Session List is stabilized Source: https://agentclientprotocol.com/announcements/session-list-stabilized Announcement that the session/list method is now part of the stable ACP protocol. **Published:** March 9, 2026 The Session List RFD has moved to Completed and the session/list method is stabilized. This gives clients a standard way to discover sessions known to an agent, making features like session history, session switching, and cleanup much easier to implement consistently across ACP clients. For the shipped protocol, see [Session List](/protocol/v1/session-list). For the design history, see the [Session List RFD](/rfds/session-list). # Session Resume is stabilized Source: https://agentclientprotocol.com/announcements/session-resume-stabilized Announcement that the session/resume method is now part of the stable ACP protocol. **Published:** April 22, 2026 The Session Resume RFD has moved to Completed and the `session/resume` method is stabilized. Unlike `session/load`, `session/resume` reconnects clients to an existing session without replaying the conversation history. This is a simpler primitive for agents that can restore context but don't implement full history replay, and it gives proxies and adapters a foundation they can build `session/load` semantics on top of. For the shipped protocol, see [Resuming Sessions](/protocol/v1/session-setup#resuming-sessions). For the design history, see the [Session Resume RFD](/rfds/session-resume). # Transports Working Group Source: https://agentclientprotocol.com/announcements/transports-working-group Announcing the new Transports Working Group, to stabilize new transport formats. **Published:** April 22, 2026 I'm excited to announce that we have a new Transports working group! Remote Agent support is a key focus of ACP, and in order to make this more of a reality, we need to standardize all of the approaches to transports people have been trying. We have started work on a Draft RFD for how this could work both via WebSockets and HTTP. Anna Zhdan will be representing from the Core Maintainers along with Alex Hancock from the Goose team who has been spearheading the RFD effort. A big thanks to both of you for starting this and I look forward to seeing what is next! # Brand Source: https://agentclientprotocol.com/brand Assets for the Agent Client Protocol brand. Following these guidelines helps maintain brand integrity while supporting ACP's mission to standardize communication between code editors and coding agents. [Download all of the assets here](https://cdn.agentclientprotocol.com/acp-brand.zip) ## Logo The ACP logo can be used with the "ACP" logomark with or without a gradient. We provide a vertical and horizontal variant for display flexibility. Agentic Client Protocol Logo ## Logomark The logomark has a variation that includes a slight gradient on the bottom right part of it. We don't enforce the use of one version over the other, meaning you can choose to use either of them as you wish, as long as you respect using it in pure white or pure black. Agentic Client Protocol Logomark # Code of Conduct Source: https://agentclientprotocol.com/community/code-of-conduct ## Our Pledge We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards Examples of behavior that contributes to a positive environment for our community include: * Demonstrating empathy and kindness toward other people * Being respectful of differing opinions, viewpoints, and experiences * Giving and gracefully accepting constructive feedback * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience * Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: * The use of sexualized language or imagery, and sexual attention or advances of any kind * Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or email address, without their explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at [hi@zed.dev](mailto:hi@zed.dev). All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. ## Enforcement Guidelines Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### 2. Warning **Community Impact**: A violation through a single incident or series of actions. **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3. Temporary Ban **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within the community. ## Attribution [homepage]: https://www.contributor-covenant.org This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at [https://www.contributor-covenant.org/version/2/0/code\_of\_conduct.html](https://www.contributor-covenant.org/version/2/0/code_of_conduct.html). Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). For answers to common questions about this code of conduct, see [the Contributor Covenant FAQ](https://www.contributor-covenant.org/faq). For translations, see [Contributor Covenant Translations](https://www.contributor-covenant.org/translations). # Contributor Communication Source: https://agentclientprotocol.com/community/communication Communication methods for Agent Client Protocol contributors This document explains how to communicate and collaborate within the Agent Client Protocol (ACP) project. ## Communication Channels In short: * **[Zulip](https://agentclientprotocol.zulipchat.com/)**: For real-time or ad-hoc discussions. * **[RFDs](https://agentclientprotocol.com/rfds/about)**: For proposed changes to the specification. * **[GitHub Discussions](https://github.com/orgs/agentclientprotocol/discussions)**: For structured, longer-form discussions. * **[GitHub Issues](https://github.com/agentclientprotocol/agent-client-protocol/issues)**: For actionable tasks, bug reports, and feature requests. All communication is governed by our [Code of Conduct](https://agentclientprotocol.com/community/code-of-conduct). We expect all participants to maintain respectful, professional, and inclusive interactions across all channels. ### Zulip For real-time contributor discussion and collaboration. The server is designed around **ACP contributors** and is not intended to be a place for general ACP support. The Zulip server will have both public and private channels. [Join the Zulip here](https://agentclientprotocol.zulipchat.com/). #### Public Channels (Default) * **Purpose**: Open community engagement, collaborative development, and transparent project coordination. * Primary use cases: * **Public SDK and tooling development** * **Community onboarding** and contribution guidance. * **Community feedback** and collaborative brainstorming. * Avoid: * ACP user support: participants are expected to read official documentation and start new GitHub Discussions for questions or support. * Service or product marketing: interactions on this Zulip are expected to be vendor-neutral and not used for brand-building or sales. Mentions of brands or products are discouraged outside of being used as examples or responses to conversations that start off focused on the specification. #### Private channels (Exceptions) * **Purpose**: Confidential coordination and sensitive matters that cannot be discussed publicly. Access will be restricted to designated maintainers. * **Strict criteria for private use**: * **Security incidents** (CVEs, protocol vulnerabilities). * **People matters** (maintainer-related discussions, code of conduct policies). * Select channels will be configured to be **read-only**. This can be good for example for maintainer decision making. * Coordination requiring **immediate** or otherwise **focused response** with a limited audience. * **Transparency**: * **All technical and governance decisions** affecting the community **must be documented** in RFDs, GitHub Discussions and/or Issues. * **Some matters related to individual contributors** may remain private when appropriate (e.g., personal circumstances, disciplinary actions, or other sensitive individual matters). * Private channels are to be used as **temporary "incident rooms,"** not for routine development. Any significant discussion on Zulip leads to a potential decision or proposal must be moved to an RFD, GitHub Discussion, or GitHub Issue to create a persistent, searchable record. Proposals will then be promoted to full-fledged PRs with associated work items as needed. ### RFDs Please refer to the [RFD process](https://agentclientprotocol.com/rfds/about) for how this is managed. This is the primary way to actually create changes to the protocol. Conversation about a given RFD can take place within the relevant PRs created to move the RFD forward. Or, a discussion within Zulip can be created in the `rfds` channel to discuss in real-time with other contributors. ### GitHub Discussions For structured, long-form discussion and debate on project direction, features, improvements, and community topics. When to use: * Announcements and release communications * Community polls and consensus-building processes * Feature requests with context and rationale * If a particular repository does not have GitHub Discussions enabled, feel free to open a GitHub Issue instead. ### GitHub Issues For bug reports, feature tracking, and actionable development tasks. When to use: * Bug reports with reproducible steps * Documentation improvements with specific scope * CI/CD problems and infrastructure issues * Release tasks and milestone tracking ### Security Issues **Do not post security issues publicly.** Instead: 1. Contact lead and/or core maintainers, or [hi@zed.dev](mailto:hi@zed.dev) directly. 2. Follow responsible disclosure guidelines. # Contributing Source: https://agentclientprotocol.com/community/contributing How to participate in the development of ACP We welcome contributions from the community! All contributors must adhere to our [Code of Conduct](./code-of-conduct). For questions and discussions, please use GitHub Discussions. # Governance Source: https://agentclientprotocol.com/community/governance How the ACP project is governed

The following is an interim governance model to provide clearer roles and responsibilities between the various parties collaborating on ACP.

ACP is jointly governed by Zed and JetBrains, who collaborate to ensure the protocol serves the broader ecosystem. We aim to operate transparently and make decisions in the best interests of ACP and its community, while working toward transitioning to an independent foundation.

This document describes the governance model for the Agent Client Protocol (ACP). It defines the roles, responsibilities, and processes that guide how the project operates and makes decisions. ## Principles We aim to operate transparently and make decisions in the best interests of ACP and its community. ## Technical Governance The ACP project adopts a hierarchical structure, similar to MCP, Rust Foundation projects, and other open source projects: * A community of **contributors** who file issues, make pull requests, and contribute to the project. * A small set of **maintainers** drive components within the ACP project, such as SDKs, documentation, and others. * Contributors and maintainers are overseen by **core maintainers**, who drive the overall project direction. * The core maintainers have two **lead core maintainers** who are the catch-all decision makers. * Maintainers, core maintainers, and lead core maintainers form the **ACP steering group**. All maintainers are expected to have a strong bias towards ACP's design philosophy. ### Channels Technical Governance is facilitated through shared [communication channels](/community/communication) of all **maintainers, core maintainers** and **lead maintainers**. Each maintainer group can choose additional communication channels, but all decisions and their supporting discussions must be recorded and made transparently available in one of the main communication channels. ### Contributors Anyone who submits code, documentation or other improvements is counted as a contributor. Contributors do not have formal decision-making power, but are encouraged to participate in discussions about project direction and propose changes. ### Maintainers Maintainers are responsible for [Working or Interest Groups](/community/working-interest-groups) within the ACP project. These generally are independent repositories such as language-specific SDKs, but can also extend to subdirectories of a repository, such as the ACP documentation. Maintainers may adopt their own rules and procedures for making decisions. Maintainers are expected to make decisions for their respective projects independently, but can defer or escalate to the core maintainers when needed. Maintainers are responsible for the: * Thoughtful and productive engagement with community contributors, * Maintaining and improving their respective area of the ACP project, * Supporting documentation, roadmaps and other adjacent parts of the ACP project, * Present ideas from community to core. New maintainers are added by a consensus or majority vote of the current core maintainers, based on sustained and high-quality contributions to the project, alignment with its goals, and a demonstrated commitment to community standards. Prospective maintainers should have a track record of participation in discussions, issue triage, and code or documentation improvements. Maintainers have write and/or admin access to their respective repositories. A maintainer may step down at any time by notifying the other maintainers. In rare cases, a maintainer may be removed by a consensus or a two-thirds supermajority vote of the other maintainers if they are inactive for an extended period, violate the Code of Conduct, or act against the project’s interests. Maintainers can be removed by core maintainers or lead core maintainers at any time and without reason. ### Core Maintainers Core maintainers are contributors who have write access to ACP’s source code. They review and merge contributions, participate in project decision-making, and help to onboard and mentor new contributors. The core maintainers are expected to have a deep understanding of the Agent Client Protocol and its specification. Their responsibilities include: * Designing, reviewing and steering the evolution of the ACP specification, as well as all other parts of the ACP project, such as documentation, * Articulating a cohesive long-term vision for the project, * Mediating and resolving contentious issues with fairness and transparency, seeking consensus where possible while making decisive choices when necessary, * Appoint or remove maintainers, * Stewardship of the ACP project in the best interest of ACP. The core maintainers as a group have the power to veto any decisions made by maintainers by majority vote. The core maintainers have power to resolve disputes as they see fit. The core maintainers should publicly articulate their decision-making. The core group is responsible for adopting their own procedures for making decisions. New core maintainers are added by a consensus or majority vote of the current core and lead maintainers, based on sustained and high-quality contributions to the project, alignment with its goals, and a demonstrated commitment to community standards. Prospective maintainers should have a track record of participation in discussions, issue triage, and code or documentation improvements. Core maintainers generally have write and admin access to all ACP repositories, but should use the same contribution (usually pull-requests) mechanism as outside contributors. Exceptions can be made based on security considerations. A core maintainer may step down at any time by notifying the other core maintainers. In rare cases, a core maintainer may be removed by a consensus or a two-thirds supermajority vote of the other core maintainers if they are inactive for an extended period, violate the Code of Conduct, or act against the project’s interests. ### Lead Maintainers (BDFL) ACP has two lead maintainers: Ben Brandt (Zed Industries) and Sergey Ignatov (JetBrains). Lead Maintainers can veto any decision by core maintainers or maintainers. This model is also commonly known as Benevolent Dictator for Life (BDFL) in the open source community. The Lead Maintainers should publicly articulate their decision-making and give clear reasoning for their decisions. Lead maintainers are part of the core maintainer group. Lead Maintainers are administrators on all infrastructure for the ACP project where possible. This includes but is not restricted to all communication channels, GitHub organizations and repositories. The Lead Maintainers are the primary contacts, and responsible for: * Representing ACP in official matters. * Coordinating major decisions about project direction. * Signing off project expenses. A lead maintainer may step down by notifying the other lead maintainer(s). They may recommend a replacement, but it is the remaining lead maintainer(s) responsibility to select a replacement. If all lead maintainers step down, the core maintainers will select new lead maintainers by consensus or majority vote. ### Decision Process The core maintainer group meets every two weeks to discuss and vote on proposals, as well as discuss any topics needed. The [ACP RFD process](https://agentclientprotocol.com/rfds/about) and Zulip chat can be used to discuss and vote on smaller proposals as needed. ## Processes Core and lead maintainers are responsible for all aspects of Agent Client Protocol, including documentation, issues, suggestions for content, and all other parts under the [ACP project](https://github.com/agentclientprotocol). Maintainers are responsible for documentation, issues, and suggestions of content for their area of the ACP project, but are encouraged to partake in general maintenance of the ACP projects. Maintainers, core maintainers, and lead maintainers should use the same contribution process as external contributors, rather than making direct changes to repos. This provides insight into intent and opportunity for discussion. ### Working and Interest Groups ACP collaboration and contributions are organized around two structures: [Working Groups and Interest Groups](/community/working-interest-groups). Interest Groups are responsible for identifying and articulating problems that ACP should address, primarily by facilitating open discussions within the community. In contrast, Working Groups focus on developing concrete solutions by collaboratively producing deliverables, such as RFDs or community-owned implementations of the specification. While input from Interest Groups can help justify the formation of a Working Group, it is not a strict requirement. Similarly, contributions from either Interest Groups or Working Groups are encouraged, but not mandatory, when submitting RFDs or other community proposals. We strongly encourage all contributors interested in working on a specific RFD to first collaborate within an Interest Group. This collaborative process helps ensure that the proposed RFD aligns with protocol needs and is the right direction for its adopters. ### Governance Principles All groups are self-governed while adhering to these core principles: 1. Clear contribution and decision-making processes 2. Open communication and transparent decisions They must: * Document their contribution process * Maintain transparent communication * Make decisions publicly (groups must publish meeting notes and proposals) Projects and working groups without specified processes default to: * GitHub pull requests and issues for contributions * A public channel in the official [ACP Contributor Zulip](/community/communication#zulip) ### Maintenance Responsibilities Components without dedicated maintainers (such as documentation) fall under core maintainer responsibility. These follow standard contribution guidelines through pull requests, with maintainers handling reviews and escalating to core maintainer review for any significant changes. Core maintainers and maintainers are encouraged to improve any part of the ACP project, regardless of formal maintenance assignments. ## Communication ### Core Maintainer Meetings The core maintainer group meets on a bi-weekly basis to discuss proposals and the project. Notes on proposals should be made public. The meetings themselves are invite-only. If you have a topic for the Core Maintainers and want to be added to the agenda, let the core maintainers know what you want to discuss in advance in Zulip, and you will be invited to join the next meeting. ### Public Chat The ACP project maintains a [public Zulip Chat](/community/communication#zulip) with open chats for different groups. The ACP project may have private channels for certain communications. ## Nominating, Confirming and Removing Maintainers ### The Principles * Membership in module maintainer groups is given to individuals on merit basis after they demonstrated strong expertise of their area of work through contributions, reviews, and discussions and are aligned with the overall ACP direction. * For membership in the maintainer group the individual has to demonstrate strong and continued alignment with the overall ACP principles. * No term limits for module maintainers or core maintainers * Light criteria of moving sub-project maintenance to 'emeritus' status if they don't actively participate over long periods of time. Each maintainer group may define the inactive period that's appropriate for their area. ### Nomination and Removal * Core Maintainers are responsible for adding and removing maintainers. They will take the consideration of existing maintainers into account. * The lead maintainers are responsible for adding and removing core maintainers. #### Nomination Process If a Maintainer (or Core / Lead Maintainer) wishes to propose a nomination for the Core / Lead Maintainers’ consideration, they should follow the following process: 1. Collect evidence for the nomination. This will generally come in the form of a history of merged PRs on the repositories for which maintainership is being considered. 2. Discuss among maintainers of the relevant group(s) as to whether they would be supportive of approving the nomination. 3. DM a Core Maintainer to create a private channel in Zulip, in the format `nomination-{name}-{group}`. Add all core maintainers, lead maintainers, and co-maintainers on the relevant group. 4. Provide context for the individual under nomination. See below for suggestions on what to include here. 5. Create a Zulip topic and ask Core / Lead Maintainers to vote Yes / No on the nomination. Reaching consensus is encouraged though not required. 6. After Core / Lead Maintainers discuss and/or vote, if the nomination is favorable, relevant members with permissions to update GitHub and Zulip roles will add the nominee to the appropriate groups. The nominator should announce the new maintainership in the relevant Zulip channel. 7. The temporary Zulip channel will be deleted a week later. Suggestions for the kind of information to share with core maintainers when nominating someone: * GitHub profile link, LinkedIn profile link, Zulip username * For what group(s) are you nominating the individual for maintainership * Whether the group(s) agree that this person should be elevated to maintainership * Description of their contributions to date (including links to most substantial contributions) * Description of expected contributions moving forward (e.g. Are they eager to be a maintainer? Will they have capacity to do so?) * Other context about the individual (e.g. current employer, motivations behind ACP involvement) * Anything else you think may be relevant to consider for the nomination ## Current Maintainers Refer to [the maintainer list](https://github.com/agentclientprotocol/agent-client-protocol/blob/main/MAINTAINERS.md). ## Security Policy and Vulnerability Disclosure * Zed will triage all potential security and vulnerability issues between the Zed team and other maintainers * Reports can be submitted to [security@zed.dev](mailto:security@zed.dev) ## Legal, Licensing, and Contributor Terms * All repositories in the ACP organization should be licensed under the Apache 2.0 License. * This project does not require a Contributor License Agreement (CLA). Instead, contributions are accepted under the following terms: > By contributing to this project, you agree that your contributions will be licensed under the [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0). You affirm that you have the legal right to submit your work, that you are not including code you do not have rights to, and that you understand contributions are made without requiring a Contributor License Agreement (CLA). # Working and Interest Groups Source: https://agentclientprotocol.com/community/working-interest-groups Learn about the two forms of collaborative groups within the Agent Client Protocol's governance structure - Working Groups and Interest Groups. Within the ACP contributor community we maintain two types of collaboration formats - **Interest** and **Working** groups. **Interest Groups** are responsible for identifying and articulating problems that ACP should address, primarily by facilitating open discussions within the community. In contrast, **Working Groups** focus on developing concrete solutions by collaboratively producing deliverables, such as RFDs or community-owned implementations of the specification. While input from Interest Groups can help justify the formation of a Working Group, it is not a strict requirement. Similarly, contributions from either Interest Groups or Working Groups are encouraged, but not mandatory, when submitting RFDs or other community proposals. We strongly encourage all contributors interested in working on a specific RFD to first collaborate within an Interest Group. This collaborative process helps ensure that the proposed RFD aligns with community needs and is the right direction for the protocol. Long-term projects in the ACP ecosystem, such as SDKs or other components are maintained by dedicated Working Groups. ## Purpose These groups exist to: * **Facilitate high-signal spaces for focused discussions** - contributors who opt into notifications, expertise sharing, and regular meetings can engage with topics that are highly relevant to them, enabling meaningful contributions and opportunities to learn from others. * **Establish clear expectations and leadership roles** - guide collaborative efforts and ensure steady progress toward concrete deliverables that advance ACP evolution and adoption. ## Mechanisms ## Meeting Calendar All Interest Group and Working Group meetings are published on the public ACP community calendar (work in progress). Facilitators are responsible for posting their meeting schedules to this calendar in advance to ensure discoverability and enable broader community participation. ### Interest Groups (IGs) **Goal:** Facilitate discussion and knowledge-sharing among ACP contributors who share interests in a specific ACP sub-topic or context. The primary focus is on identifying and gathering problems that may be worth addressing through RFDs or other community artifacts, while encouraging open exploration of protocol issues and opportunities. **Expectations**: * Regular conversations in the Interest Group Zulip channel * **AND/OR** a recurring live meeting regularly attended by Interest Group members * Meeting dates and times published in advance on the ACP community calendar when applicable, and tagged with their primary topic and interest group Zulip channel name * Notes publicly shared after meetings, and submitted to the [meetings repository](https://github.com/agentclientprotocol/meetings) **Lifecycle**: * Creation begins by proposing a new Interest Group to the core/lead maintainers with the template below * Majority positive vote by core maintainers over a 72h period approves creation of the group. * The creation of the group can be reversed at any time (e.g., after new information surfaces). Core and lead maintainers can veto. * Facilitator(s) and Maintainer(s) responsible for organizing IG into meeting expectations * Facilitator is an informal role responsible for shepherding or speaking for a group * Maintainer is an official representative from the ACP steering group. A maintainer is not required for every group, but can help advocate for specific changes or initiatives. * IG is retired only when Core or Lead Maintainers determine it's no longer active and/or needed * Successful IGs do not have a time limit or expiration date - as long as they are active and maintained, they will remain available **Creation Template**: * Facilitator(s) * Maintainer(s) (optional) * IGs with potentially similar goals/discussions * How this IG differentiates itself from the related IGs * First topic you to discuss within the IG Participation in an Interest Group (IG) is not required to start a Working Group (WG) or to create a RFD. However, building consensus within IGs can be valuable when justifying the formation of a WG. Likewise, referencing support from IGs or WGs can strengthen a RFD and its chances of success. ### Working Groups (WG) **Goal:** Facilitate collaboration within the ACP community on a RFD, a themed series of RFDs, or an otherwise officially endorsed project. **Expectations**: * Meaningful progress towards at least one RFD or spec-related implementation **OR** hold maintenance responsibilities for a project (e.g., SDKs) * Facilitators are responsible for keeping track of progress and communicating status when appropriate * Meeting dates and times published in advance on the ACP community calendar when applicable, and tagged with their primary topic and working group Zulip channel name * Notes publicly shared after meetings, and submitted to the [meetings repository](https://github.com/agentclientprotocol/meetings) **Lifecycle**: * Creation begins by proposing a new Interest Group to the core/lead maintainers with the template below * Majority positive vote by core maintainers over a 72h period approves creation of the group. * The creation of the group can be reversed at any time (e.g., after new information surfaces). Core and lead maintainers can veto. * Facilitator(s) and Maintainer(s) responsible for organizing WG into meeting expectations * Facilitator is an informal role responsible for shepherding or speaking for a group * Maintainer is an official representative from the ACP steering group. A maintainer is not required for every group, but can help advocate for specific changes or initiatives * WG is retired when either: * Community moderators or Core and Lead Maintainers decide it is no longer active and/or needed * The WG no longer has an active Issue/PR for a month or more, or has completed all Issues/PRs it intended to pursue. **Creation Template**: * Facilitator(s) * Maintainer(s) (optional) * Explanation of interest/use cases, ideally originating from an IG discussion; however that is not a requirement * First Issue/PR/RFD that the WG will work on ## WG/IG Facilitators A **Facilitator** role in a WG or IG does *not* result in a [maintainership role](https://github.com/agentclientprotocol/agent-client-protocol/blob/main/MAINTAINERS.md) across the ACP organization. It is an informal role into which anyone can self-nominate. A Facilitator is responsible for helping shepherd discussions and collaboration within an Interest or Working Group. Lead and Core Maintainers reserve the right to modify the list of Facilitators and Maintainers for any WG/IG at any time. ## FAQ ### How do I get involved contributing to ACP? These IG and WG abstractions help provide an elegant on-ramp: 1. [Join the Zulip](/community/communication#zulip) and follow conversations in IGs relevant to you. Attend live calls. Participate. 2. Offer to facilitate calls. Contribute your use cases in RFD proposals and other work. 3. When you're comfortable contributing to deliverables, jump in to contribute to WG work. 4. Active and valuable contributors will be nominated by WG maintainers as new maintainers. ### Where can I find a list of all current WGs and IGs? On the [ACP Zulip Chat](/community/communication#zulip) there is a section of channels for each Working and Interest Group. # Agents Source: https://agentclientprotocol.com/get-started/agents Agents implementing the Agent Client Protocol. The following agents can be used with an ACP Client: * [AgentPool](https://phil65.github.io/agentpool/advanced/acp-integration/) * [Augment Code](https://docs.augmentcode.com/cli/acp) * [AutoDev](https://github.com/phodal/auto-dev) * [Blackbox AI](https://docs.blackbox.ai/features/blackbox-cli/introduction) * [Bub](https://github.com/bubbuild/bub) (via [bub-acp-server](https://github.com/bubbuild/bub-contrib/tree/main/packages/bub-acp-server)) * [Claude Agent](https://platform.claude.com/docs/en/agent-sdk/overview) (via [Zed's SDK adapter](https://github.com/zed-industries/claude-agent-acp)) * [Cline](https://cline.bot/) * [Codex CLI](https://developers.openai.com/codex/cli) (via [Zed's adapter](https://github.com/zed-industries/codex-acp)) * [Code Assistant](https://github.com/stippi/code-assistant?tab=readme-ov-file#configuration) * [crow-cli](https://crow-ai.dev) * [Cursor](https://cursor.com/docs/cli/acp) * [Docker's cagent](https://github.com/docker/cagent) * [fast-agent](https://fast-agent.ai/acp) * [Factory Droid](https://factory.ai/) * [fount](https://github.com/steve02081504/fount) * [Gemini CLI](https://github.com/google-gemini/gemini-cli) * [GitHub Copilot](https://github.com/features/copilot) (in [public preview](https://github.blog/changelog/2026-01-28-acp-support-in-copilot-cli-is-now-in-public-preview/)) * [Goose](https://block.github.io/goose/docs/guides/acp-clients) * [Hermes Agent](https://hermes-agent.nousresearch.com/docs/user-guide/features/acp) * [Junie by JetBrains](https://junie.jetbrains.com/) * [Kimi CLI](https://github.com/MoonshotAI/kimi-cli) * [Kiro CLI](https://kiro.dev/docs/cli/acp/) * [Minion Code](https://github.com/femto/minion-code) * [Mistral Vibe](https://github.com/mistralai/mistral-vibe) * [OpenClaw](https://docs.openclaw.ai/cli/acp) * [OpenCode](https://github.com/sst/opencode) * [OpenHands](https://docs.openhands.dev/openhands/usage/run-openhands/acp) * [Pi](https://github.com/badlogic/pi-mono/tree/main/packages/coding-agent) (via [pi-acp adapter](https://github.com/svkozak/pi-acp)) * [Poolside](https://github.com/poolsideai/pool) * [Qoder CLI](https://docs.qoder.com/cli/acp) * [Qwen Code](https://github.com/QwenLM/qwen-code) * [siGit Code](https://github.com/getsigit/sigit) * [Stakpak](https://github.com/stakpak/agent?tab=readme-ov-file#agent-client-protocol-acp) * [stdio Bus](https://github.com/stdiobus/stdiobus) * [VT Code](https://github.com/vinhnx/vtcode/blob/main/README.md#zed-ide-integration-agent-client-protocol) # Architecture Source: https://agentclientprotocol.com/get-started/architecture Overview of the Agent Client Protocol architecture. The Agent Client Protocol defines a standard interface for communication between AI agents and client applications. The architecture is designed to be flexible, extensible, and platform-agnostic. ## Design Philosophy The protocol architecture follows several key principles: 1. **MCP-friendly**: The protocol is built on JSON-RPC, and re-uses MCP types where possible so that integrators don't need to build yet-another representation for common data types. 2. **UX-first**: It is designed to solve the UX challenges of interacting with AI agents; ensuring there's enough flexibility to render clearly the agents intent, but is no more abstract than it needs to be. 3. **Trusted**: ACP works when you're using a code editor to talk to a model you trust. You still have controls over the agent's tool calls, but the code editor gives the agent access to local files and MCP servers. ## Setup When the user tries to connect to an agent, the editor boots the agent sub-process on demand, and all communication happens over stdin/stdout. Each connection can support several concurrent sessions, so you can have multiple trains of thought going on at once. Server Client setup ACP makes heavy use of JSON-RPC notifications to allow the agent to stream updates to the UI in real-time. It also uses JSON-RPC's bidirectional requests to allow the agent to make requests of the code editor: for example to request permissions for a tool call. ## MCP Commonly the code editor will have user-configured MCP servers. When forwarding the prompt from the user, it passes configuration for these to the agent. This allows the agent to connect directly to the MCP server. MCP Server connection The code editor may itself also wish to export MCP based tools. Instead of trying to run MCP and ACP on the same socket, the code editor can provide its own MCP server as configuration. As agents may only support MCP over stdio, the code editor can provide a small proxy that tunnels requests back to itself: MCP connection to self # Clients Source: https://agentclientprotocol.com/get-started/clients Clients, frameworks, connectors, and related tools around the Agent Client Protocol. The following projects implement ACP directly, connect ACP agents to other environments, or support adjacent coding-agent workflows. ## Editors and IDEs * [Chrome ACP](https://github.com/Areo-Joe/chrome-acp) (Chrome extension / PWA) * Emacs via [agent-shell.el](https://github.com/xenodium/agent-shell) * [JetBrains](https://www.jetbrains.com/help/ai-assistant/acp.html) * [neovim](https://neovim.io) * through the [CodeCompanion](https://github.com/olimorris/codecompanion.nvim) plugin * through the [carlos-algms/agentic.nvim](https://github.com/carlos-algms/agentic.nvim) plugin * through the [yetone/avante.nvim](https://github.com/yetone/avante.nvim) plugin * through the [hermes.nvim](https://github.com/Ruddickmg/hermes.nvim) plugin * [Obsidian](https://obsidian.md) * through the [Agent Client](https://github.com/RAIT-09/obsidian-agent-client) plugin * through the [Agent Console](https://github.com/donivatamazondotcom/obsidian-agent-console) plugin — a tabbed multi-session workspace: run several ACP agents in parallel with restorable, searchable sessions and quick prompts * [Pulsar](https://pulsar-edit.dev) — through the [pulsar-acp-agent](https://github.com/hovancik/pulsar-acp-agent) package * [Unity ACP Client](https://github.com/3DLabInstruments/UnityACPClient)(Unity plugin) * [Unity Agent Client](https://github.com/nuskey8/UnityAgentClient) (Unity editor) * Visual Studio Code * [ACP Client](https://github.com/formulahendry/vscode-acp) extension * [ACP Pro Extension](https://marketplace.visualstudio.com/items?itemName=duclvz.acp-pro) * VS Code–compatible IDEs (Cursor, Windsurf, Trae,..): [ACP Pro Extension](https://open-vsx.org/extension/duclvz/acp-pro) * [Zed](https://zed.dev/docs/ai/external-agents) ## CLI and TUI * [acpx (CLI)](https://github.com/openclaw/acpx) * [Hash (shell)](https://github.com/tfcace/hash) * [Nori CLI](https://github.com/tilework-tech/nori-cli) * [pool](https://github.com/poolsideai/pool) * [Toad](https://www.batrachian.ai/) ## Desktop and Web * [ACP UI](https://github.com/formulahendry/acp-ui) (Windows, macOS, Linux, iOS, Android, Web) * [Agent Studio](https://github.com/sxhxliang/agent-studio) * [AgentRQ](https://github.com/agentrq/agentrq) — Human-in-the-loop realtime task management and collaboration (Web) * [AionUi](https://github.com/iOfficeAI/AionUi) * [aizen](https://aizen.win) * [Braide](https://braide.dev) - Parallel sessions, worktrees, personas and interactive agent responses (supports macOS, Windows, Linux) * [Codeg](https://github.com/xintaofei/codeg) — collaborative multi-agent coding workbench that unifies ACP agents (Claude Code, Codex, Gemini CLI, OpenCode, and more) with session aggregation; desktop app, self-hosted server, or Docker (macOS, Windows, Linux, Web) * [DeepChat](https://github.com/ThinkInAIXYZ/deepchat) * [Devin Desktop](https://devin.ai/desktop) * [fabriqa.ai](https://fabriqa.ai) * [gemini-cli-desktop](https://github.com/Piebald-AI/gemini-cli-desktop) * [Harnss](https://github.com/OpenSource03/harnss) * [Jockey](https://github.com/recailai/jockey) — open-source multi-agent orchestrator (Tauri + Rust + SolidJS) that coordinates Claude Code, Gemini CLI, and Codex CLI via ACP * [Lody](https://lody.ai) * [Minion Mind](https://minion-mind.nebulame.com/) — through the [Agent Client](https://github.com/RAIT-09/obsidian-agent-client) plugin * [Mitto](https://github.com/inercia/mitto) * [Ngent](https://github.com/beyond5959/ngent) * [RayClaw](https://github.com/rayclaw/rayclaw?tab=readme-ov-file#acp-agent-client-protocol) * [RLM Code](https://github.com/SuperagenticAI/rlm-code) * [Sidequery *(coming soon)*](https://sidequery.dev) * [Tidewave](https://tidewave.ai/) * [Web Browser with AI SDK](https://github.com/mcpc-tech/ai-elements-remix-template) (powered by [@mcpc/acp-ai-provider](https://github.com/mcpc-tech/mcpc/tree/main/packages/acp-ai-provider)) * [Kangaroo](https://github.com/dbkangaroo/kangaroo) — database IDE with Agent Client Protocol support * [ACP Components](https://github.com/zvzuola/acp-components) - A universal frontend component library for building AI Agent interfaces based on the ACP * [ACP Inspector](https://github.com/newioapp/acp-inspector) — desktop debugger/inspector for the ACP protocol (macOS, Linux) * [Newio](https://newio.app) — a multi-channel, multi-player agent native messaging app ## Notebook and data tools * [agent-client-kernel](https://github.com/wiki3-ai/agent-client-kernel) (Jupyter notebooks) * DuckDB — through the [sidequery/duckdb-acp](https://github.com/sidequery/duckdb-acp) extension * [marimo notebook](https://github.com/marimo-team/marimo) ## Mobile clients These mobile-first tools bring ACP and related coding-agent workflows to phones and tablets: * [Agmente](https://agmente.halliharp.com) ([GitHub](https://github.com/rebornix/Agmente)) (iOS) * [Ferngeist](https://github.com/arafatamim/Ferngeist) (Android) * [Happy](https://happy.engineering/) ([GitHub](https://github.com/slopus/happy)) (iOS, Android, Web) * [Mobvibe](https://github.com/Eric-Song-Nop/mobvibe) (iOS, Android, Web) ## Messaging * [ACP Discord](https://github.com/broven/acp-discord) (Discord) * [duckdb-claude-slack](https://github.com/sidequery/duckdb-claude-slack) (Slack) * [Juan](https://github.com/DiscreteTom/juan) (Slack) * [OpenACP](https://github.com/Open-ACP/OpenACP) (Telegram, Discord, Slack) — self-hosted bridge for ACP agents; streams tool calls and responses in real time * [Telegram ACP Bot](https://github.com/mgaitan/telegram-acp-bot) (Telegram) — through the [`telegram-acp-bot`](https://github.com/mgaitan/telegram-acp-bot) connector * [Telegram-ACP](https://github.com/SuperKenVery/Telegram-ACP/) (Telegram) — Supports multi-thread chat and message streaming * [ACP Router](https://github.com/vcoderun/acprouter) (Telegram) - an ACP client surface for driving ACP agents from Telegram, with rich diffs, approvals and many more. * [WeChat ACP](https://github.com/formulahendry/wechat-acp) (WeChat) * [Sniptail](https://github.com/Justkog/sniptail) (Discord, Slack) - self-hosted chat bridge for running coding agents across your team’s repositories * [Lark ACP](https://github.com/4t145/lark-acp) (Lark/飞书) * [Zooid](https://github.com/zooid-ai/zooid) (Matrix) - coding agent runtime paired with a Matrix client/ACP bridge for interacting with agents * [Pomerium AgentOps](https://github.com/pomerium/agentops) (Slack) ## Frameworks These frameworks add ACP support through dedicated integrations or adapters: * [AgentPool](https://phil65.github.io/agentpool/) — with built-in ACP integration for IDEs and external ACP agents * [fast-agent](https://fast-agent.ai/acp/) — through [`fast-agent-acp`](https://fast-agent.ai/acp/) * [ACP Kit](https://github.com/vcoderun/acpkit/) - adapter toolkit for exposing existing [Pydantic AI](https://pypi.org/project/pydantic-acp/) / [LangChain](https://pypi.org/project/langchain-acp/) agent runtimes through ACP. * [Koog](https://docs.koog.ai/agent-client-protocol/) — through the [`agents-features-acp`](https://github.com/JetBrains/koog/tree/develop/examples/notebooks/acp) integration * [LangChain / LangGraph](https://docs.langchain.com/oss/python/deepagents/acp) — through [Deep Agents ACP](https://docs.langchain.com/oss/python/deepagents/acp) * [LlamaIndex](https://github.com/AstraBert/workflows-acp) — through the [`workflows-acp`](https://github.com/AstraBert/workflows-acp) adapter for Agent Workflows * [LLMling-Agent](https://github.com/phil65/llmling-agent) — with built-in ACP support for running agents through ACP clients * [Mastra](https://mastra.ai/docs/agents/acp) — through the [`@mastra/acp`](https://mastra.ai/docs/agents/acp) package for wrapping external ACP agents as tools or subagents ## Connectors These connectors bridge ACP into other environments and transport layers: * [ACP to AG-UI](https://github.com/namanrajpal/acp-to-agui) — bridges any ACP agent to web frontends via [AG-UI](https://docs.ag-ui.com) events over SSE; works with CopilotKit, AG-UI HttpAgent, or custom UIs (Web) * [AgentRQ](https://github.com/agentrq/acp-gateway) — bridges stdio-based ACP agents to the AgentRQ - Human-in-the-loop task collaboration service using MCP server. * [Aptove Bridge](https://github.com/aptove/bridge) — bridges stdio-based ACP agents to the Aptove mobile client over WebSocket * [ACP Remote](https://github.com/vcoderun/acpkit/tree/main/packages/transports/acpremote) - remote WebSocket transport for ACP agents and clients. * [OpenClaw](https://docs.openclaw.ai/cli/acp) — through the [`openclaw acp`](https://docs.openclaw.ai/cli/acp) bridge to an OpenClaw Gateway * [stdio Bus](https://stdiobus.com) – deterministic stdio-based kernel providing transport-level routing for ACP/MCP-style agent protocols. # Introduction Source: https://agentclientprotocol.com/get-started/introduction Get started with the Agent Client Protocol. The Agent Client Protocol (ACP) standardizes communication between code editors/IDEs and coding agents and is suitable for both local and remote scenarios. ## Why ACP? AI coding agents and editors are tightly coupled but interoperability isn't the default. Each editor must build custom integrations for every agent they want to support, and agents must implement editor-specific APIs to reach users. This creates several problems: * Integration overhead: Every new agent-editor combination requires custom work * Limited compatibility: Agents work with only a subset of available editors * Developer lock-in: Choosing an agent often means accepting their available interfaces ACP solves this by providing a standardized protocol for agent-editor communication, similar to how the [Language Server Protocol (LSP)](https://microsoft.github.io/language-server-protocol/) standardized language server integration. Agents that implement ACP work with any compatible editor. Editors that support ACP gain access to the entire ecosystem of ACP-compatible agents. This decoupling allows both sides to innovate independently while giving developers the freedom to choose the best tools for their workflow. ## Overview ACP assumes that the user is primarily in their editor, and wants to reach out and use agents to assist them with specific tasks. ACP is suitable for both local and remote scenarios: * **Local agents** run as sub-processes of the code editor, communicating via JSON-RPC over stdio. * **Remote agents** can be hosted in the cloud or on separate infrastructure, communicating over HTTP or WebSocket Full support for remote agents is a work in progress. We are actively collaborating with agentic platforms to ensure the protocol addresses the specific requirements of cloud-hosted and remote deployment scenarios. The protocol re-uses the JSON representations used in MCP where possible, but includes custom types for useful agentic coding UX elements, like displaying diffs. The default format for user-readable text is Markdown, which allows enough flexibility to represent rich formatting without requiring that the code editor is capable of rendering HTML. # ACP Registry Source: https://agentclientprotocol.com/get-started/registry The easiest way to find and install ACP-compatible agents. ## Overview The ACP Registry is an easy way for developers to distribute their ACP-compatible agents to any client that speaks the protocol. This is a curated set of agents, including only the ones that [support authentication](/rfds/auth-methods). Visit [the registry repository on GitHub](https://github.com/agentclientprotocol/registry) to learn more about it. ## Available Agents ## Using the Registry Clients can fetch the registry programmatically: ```bash theme={null} curl https://cdn.agentclientprotocol.com/registry/v1/latest/registry.json ``` The registry JSON contains all agent metadata including distribution information for automatic installation. ## Submit your Agent To add your agent to the registry: 1. Fork the [registry repository on GitHub](https://github.com/agentclientprotocol/registry) 2. Create a folder with your agent's ID (lowercase, hyphens allowed) 3. Add an `agent.json` file following [the schema](https://github.com/agentclientprotocol/registry/blob/main/agent.schema.json) 4. Optionally add an `icon.svg` (16x16 recommended) 5. Submit a pull request See the [contributing guide](https://github.com/agentclientprotocol/registry/blob/main/CONTRIBUTING.md) for details. # Community Source: https://agentclientprotocol.com/libraries/community Community managed libraries for the Agent Client Protocol ## Crystal * [acp.cr](https://github.com/hahwul/acp.cr) ## Dart * [acp\_dart](https://github.com/SkrOYC/acp-dart) ## .NET * [acp-csharp](https://github.com/nuskey8/acp-csharp) * [Acp.Net](https://github.com/MertBasar0/acp-net) ## Emacs * [acp.el](https://github.com/xenodium/acp.el) ## Go * [acp-go-sdk](https://github.com/coder/acp-go-sdk) * [acp-go](https://github.com/ironpark/acp-go) * [acp](https://github.com/eino-contrib/acp) ## React * [use-acp](https://github.com/marimo-team/use-acp) ## TypeScript * [acp-ts-sdk](https://github.com/langwatch/acp-ts-sdk) ## Swift * [swift-acp](https://github.com/wiedymi/swift-acp) * [swift-sdk](https://github.com/aptove/swift-sdk) * [acp-swift-sdk](https://github.com/rebornix/acp-swift-sdk) ## Vala * [Agent Client Protocol Vala SDK](https://gitcode.com/ai-outputs/AgentClientProtocol) # Java Source: https://agentclientprotocol.com/libraries/java Java library for the Agent Client Protocol The [java-sdk](https://github.com/agentclientprotocol/java-sdk) provides Java models, JSON-RPC plumbing, and side-specific connection helpers for building ACP-compatible agents and clients. The repository includes runnable examples for client and agent implementations, plus Spring AI integrations, under [`examples/`](https://github.com/agentclientprotocol/java-sdk/tree/main/examples). For dependency setup and API details, see the repository README and published Javadocs from the project. # Kotlin Source: https://agentclientprotocol.com/libraries/kotlin Kotlin library for the Agent Client Protocol The [kotlin-sdk](https://github.com/agentclientprotocol/kotlin-sdk) provides implementations of both sides of the Agent Client Protocol that you can use to build your own agent server or client. **It currently supports JVM, other targets are in progress.** To get started, add the repository to your build file: ```kotlin theme={null} repositories { mavenCentral() } ``` Add the dependency: ```kotlin theme={null} dependencies { implementation("com.agentclientprotocol:acp:0.1.0-SNAPSHOT") } ``` The [sample](https://github.com/agentclientprotocol/kotlin-sdk/tree/master/samples/kotlin-acp-client-sample) demonstrates how to implement both sides of the protocol. # Python Source: https://agentclientprotocol.com/libraries/python Python library for the Agent Client Protocol The [agentclientprotocol/python-sdk](https://github.com/agentclientprotocol/python-sdk) repository packages Pydantic models, async base classes, and JSON-RPC plumbing so you can build ACP-compatible agents and clients in Python. It mirrors the official ACP schema and ships helper utilities for both sides of the protocol. To get started, add the SDK to your project: ```bash theme={null} pip install agent-client-protocol ``` (Using [uv](https://github.com/astral-sh/uv)? Run `uv add agent-client-protocol`.) The repository includes runnable examples for agents, clients, Gemini CLI bridges, and dual-agent/client demos under [`examples/`](https://github.com/agentclientprotocol/python-sdk/tree/main/examples). Browse the full documentation—including the quickstart, contrib helpers, and API reference—at [agentclientprotocol.github.io/python-sdk](https://agentclientprotocol.github.io/python-sdk/). # Rust Source: https://agentclientprotocol.com/libraries/rust Rust library for the Agent Client Protocol The [agent-client-protocol](https://crates.io/crates/agent-client-protocol) Rust crate provides implementations of both sides of the Agent Client Protocol that you can use to build your own agent server or client. To get started, add the crate as a dependency to your project's `Cargo.toml`: ```bash theme={null} cargo add agent-client-protocol ``` Depending on what kind of tool you're building, you'll need to implement either the [Agent](https://docs.rs/agent-client-protocol/latest/agent_client_protocol/trait.Agent.html) trait or the [Client](https://docs.rs/agent-client-protocol/latest/agent_client_protocol/trait.Client.html) trait to define the interaction with the ACP counterpart. The [agent](https://github.com/agentclientprotocol/rust-sdk/blob/main/src/agent-client-protocol/examples/agent.rs) and [client](https://github.com/agentclientprotocol/rust-sdk/blob/main/src/agent-client-protocol/examples/client.rs) example binaries provide runnable examples of how to do this, which you can use as a starting point. You can read the full documentation for the `agent-client-protocol` crate on [docs.rs](https://docs.rs/agent-client-protocol/latest/agent_client_protocol/). ## Users The `agent-client-protocol` crate powers the integration with external agents in the [Zed](https://zed.dev) editor. # TypeScript Source: https://agentclientprotocol.com/libraries/typescript TypeScript library for the Agent Client Protocol The [@agentclientprotocol/sdk](https://www.npmjs.com/package/@agentclientprotocol/sdk) npm package provides implementations of both sides of the Agent Client Protocol that you can use to build your own agent server or client. To get started, add the package as a dependency to your project: ```bash theme={null} npm install @agentclientprotocol/sdk ``` Depending on what kind of tool you're building, you'll need to use either the [AgentSideConnection](https://agentclientprotocol.github.io/typescript-sdk/classes/AgentSideConnection.html) class or the [ClientSideConnection](https://agentclientprotocol.github.io/typescript-sdk/classes/ClientSideConnection.html) class to establish communication with the ACP counterpart. You can find example implementations of both sides in the [main repository](https://github.com/agentclientprotocol/typescript-sdk/tree/main/src/examples). These can be run from your terminal or from an ACP Client like [Zed](https://zed.dev), making them great starting points for your own integration! Browse the [TypeScript library reference](https://agentclientprotocol.github.io/typescript-sdk) for detailed API documentation. For a complete, production-ready implementation of an ACP agent, check out [Gemini CLI](https://github.com/google-gemini/gemini-cli/blob/main/packages/cli/src/zed-integration/zedIntegration.ts). # Agent Plan Source: https://agentclientprotocol.com/protocol/v1/agent-plan How Agents communicate their execution plans Plans are execution strategies for complex tasks that require multiple steps. Agents may share plans with Clients through [`session/update`](/protocol/v1/prompt-turn#3-agent-reports-output) notifications, providing real-time visibility into their thinking and progress. ## Creating Plans When the language model creates an execution plan, the Agent **SHOULD** report it to the Client: ```json theme={null} { "jsonrpc": "2.0", "method": "session/update", "params": { "sessionId": "sess_abc123def456", "update": { "sessionUpdate": "plan", "entries": [ { "content": "Analyze the existing codebase structure", "priority": "high", "status": "pending" }, { "content": "Identify components that need refactoring", "priority": "high", "status": "pending" }, { "content": "Create unit tests for critical functions", "priority": "medium", "status": "pending" } ] } } } ``` An array of [plan entries](#plan-entries) representing the tasks to be accomplished ## Plan Entries Each plan entry represents a specific task or goal within the overall execution strategy: A human-readable description of what this task aims to accomplish The relative importance of this task. * `high` * `medium` * `low` The current [execution status](#status) of this task * `pending` * `in_progress` * `completed` ## Updating Plans As the Agent progresses through the plan, it **SHOULD** report updates by sending more `session/update` notifications with the same structure. The Agent **MUST** send a complete list of all plan entries in each update and their current status. The Client **MUST** replace the current plan completely. ### Dynamic Planning Plans can evolve during execution. The Agent **MAY** add, remove, or modify plan entries as it discovers new requirements or completes tasks, allowing it to adapt based on what it learns. # Authentication Source: https://agentclientprotocol.com/protocol/v1/authentication Authenticating with agents and logging out ACP authentication is negotiated during [initialization](/protocol/v1/initialization). Agents advertise available authentication methods in `authMethods`, Clients choose one by calling `authenticate`, and Agents that support ending an authenticated state advertise the `logout` capability.
```mermaid theme={null} sequenceDiagram participant Client participant Agent Client->>Agent: initialize Agent-->>Client: initialize response (authMethods, auth.logout) alt Agent requires authentication Client->>Agent: authenticate (methodId) Agent-->>Client: authenticate response end Note over Client,Agent: Authenticated requests may proceed alt User logs out Client->>Agent: logout Agent-->>Client: logout response end Note over Client,Agent: New sessions require authentication again ```
## Advertising Authentication Agents advertise authentication options in the `authMethods` field of the `initialize` response. Each method has an `id` that the Client passes back to the Agent in a later `authenticate` request. Agents that support `logout` also advertise `agentCapabilities.auth.logout`: ```json highlight={7-11,12-18} theme={null} { "jsonrpc": "2.0", "id": 0, "result": { "protocolVersion": 1, "agentCapabilities": { "auth": { "logout": {} } }, "authMethods": [ { "id": "agent-login", "name": "Agent login", "description": "Sign in using the agent's login flow" } ] } } ``` If `agentCapabilities.auth.logout` is omitted or `null`, the Agent does not support `logout` and Clients **MUST NOT** call it. Supplying `{}` means the Agent supports the method. ### Authentication Method Types The default authentication method type is `agent`, where the Agent handles authentication itself. When no `type` is present, the method is treated as `agent`: ```json theme={null} { "id": "agent-login", "name": "Agent login", "description": "Sign in using the agent's login flow" } ``` An explicit `"type": "agent"` is also accepted but not required. See the [schema](/protocol/v1/schema#authmethod) for the full stable `AuthMethod` definition. ## Authenticating When an Agent requires authentication before allowing session creation, the Client calls `authenticate` with one of the advertised authentication method IDs: ```json theme={null} { "jsonrpc": "2.0", "id": 1, "method": "authenticate", "params": { "methodId": "agent-login" } } ``` The ID of the authentication method to use. This value must match one of the methods advertised in the `initialize` response. On success, the Agent returns an empty result: ```json theme={null} { "jsonrpc": "2.0", "id": 1, "result": {} } ``` After successful authentication, the Client can create new sessions without receiving an `auth_required` error for authentication-gated requests. ## Logging Out The `logout` method allows Clients to end the current authenticated state. Clients should only call it after verifying the Agent advertised `agentCapabilities.auth.logout` during initialization. ```json theme={null} { "jsonrpc": "2.0", "id": 2, "method": "logout", "params": {} } ``` On success, the Agent returns an empty result: ```json theme={null} { "jsonrpc": "2.0", "id": 2, "result": {} } ``` After a successful `logout`, new sessions that require authentication will require the Client to call `authenticate` again. ## Active Sessions The protocol does not guarantee what happens to already-running sessions after `logout`. Agents may terminate them, keep them running, or return `auth_required` errors for future session activity. Clients **SHOULD** be prepared for active session operations to fail with authentication-related errors after logout and should prompt the user to authenticate again when appropriate. # Cancellation Source: https://agentclientprotocol.com/protocol/v1/cancellation Mechanisms for request cancellation ACP uses JSON-RPC 2.0 for making requests and getting responses. The JSON-RPC specification doesn't define any standard mechanism for request cancellation and keeps it up to the implementation. ## `$/cancel_request` Notification In order to provide a consistent approach to cancellation, ACP defines a `$/cancel_request` notification that can be sent to cancel requests. Cancellation remains optional as it might not be implementable in all clients or servers. For example if the implementation uses a single threaded synchronous programming language then there is little it can do to react to a `$/cancel_request` notification. When a `$/cancel_request` notification is received by a supporting implementation, the implementation: * **MAY** cancel the corresponding request activity and all nested activities related to that request * **MAY** finish sending any pending notifications before responding * **MUST** send one of these responses for the original request: * A valid response with appropriate data (such as partial results or cancellation marker) * An error response with code [`-32800` (Request Cancelled)](/protocol/v1/schema#errorcode) The calling side **MAY** implement graceful cancellation processing by waiting for the response from the remote side. Cancellation **MAY** also be done explicitly on a per-feature basis within the protocol to cover specific scenarios, such as cancellation of a [prompt turn](/protocol/v1/prompt-turn#cancellation). ## Internal Cancellation Requests can also be cancelled internally by the executing party without receiving `$/cancel_request`: * **Client-side examples**: User closes IDE, switches to different project, file becomes unavailable * **Agent-side examples**: LLM context limit reached, internal timeout, resource constraints When internal cancellation occurs, the executing party **SHOULD**: * Send the same `-32800` (Cancelled) error response as if `$/cancel_request` was received * Ensure consistent behavior regardless of cancellation source ## Example: Cascading Cancellation Flow ```mermaid theme={null} sequenceDiagram participant Client participant Agent Note over Client,Agent: 1. Session prompt in progress Client->>Agent: session/prompt (id=1, "Analyze file X") Agent-->>Client: session/update (agent started processing) Note over Client,Agent: 2. Agent makes concurrent requests Agent->>Client: terminal/create (id=2, "grep pattern file.txt") Agent->>Client: session/request_permission (id=3, "read sensitive file") Note over Client,Agent: 3. Client cancels the prompt turn Client->>Agent: session/cancel (sessionId) Note over Client,Agent: 4. Agent cascades cancellation internally Agent->>Client: $/cancel_request (id=2) [terminal request] Agent->>Client: $/cancel_request (id=3) [permission request] Note over Client,Agent: 5. Client confirms individual cancellations Client->>Agent: response to id=2 (error -32800 "Cancelled") Client->>Agent: response to id=3 (error -32800 "Cancelled") Note over Client,Agent: 6. Agent completes prompt cancellation Agent->>Client: response to id=1 (stopReason: "cancelled") ``` # Content Source: https://agentclientprotocol.com/protocol/v1/content Understanding content blocks in the Agent Client Protocol Content blocks represent displayable information that flows through the Agent Client Protocol. They provide a structured way to handle various types of user-facing content—whether it's text from language models, images for analysis, or embedded resources for context. Content blocks appear in: * User prompts sent via [`session/prompt`](/protocol/v1/prompt-turn#1-user-message) * Language model output streamed through [`session/update`](/protocol/v1/prompt-turn#3-agent-reports-output) notifications * Progress updates and results from [tool calls](/protocol/v1/tool-calls) ## Content Types The Agent Client Protocol uses the same `ContentBlock` structure as the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/specification/2025-06-18/schema#contentblock). This design choice enables Agents to seamlessly forward content from MCP tool outputs without transformation. ### Text Content Plain text messages form the foundation of most interactions. ```json theme={null} { "type": "text", "text": "What's the weather like today?" } ``` All Agents **MUST** support text content blocks when included in prompts. The text content to display Optional metadata about how the content should be used or displayed. [Learn more](https://modelcontextprotocol.io/specification/2025-06-18/server/resources#annotations). ### Image Content Images can be included for visual context or analysis. ```json theme={null} { "type": "image", "mimeType": "image/png", "data": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAAB..." } ``` Requires the `image` [prompt capability](/protocol/v1/initialization#prompt-capabilities) when included in prompts. Base64-encoded image data The MIME type of the image (e.g., "image/png", "image/jpeg") Optional URI reference for the image source Optional metadata about how the content should be used or displayed. [Learn more](https://modelcontextprotocol.io/specification/2025-06-18/server/resources#annotations). ### Audio Content Audio data for transcription or analysis. ```json theme={null} { "type": "audio", "mimeType": "audio/wav", "data": "UklGRiQAAABXQVZFZm10IBAAAAABAAEAQB8AAAB..." } ``` Requires the `audio` [prompt capability](/protocol/v1/initialization#prompt-capabilities) when included in prompts. Base64-encoded audio data The MIME type of the audio (e.g., "audio/wav", "audio/mp3") Optional metadata about how the content should be used or displayed. [Learn more](https://modelcontextprotocol.io/specification/2025-06-18/server/resources#annotations). ### Embedded Resource Complete resource contents embedded directly in the message. ```json theme={null} { "type": "resource", "resource": { "uri": "file:///home/user/script.py", "mimeType": "text/x-python", "text": "def hello():\n print('Hello, world!')" } } ``` This is the preferred way to include context in prompts, such as when using @-mentions to reference files or other resources. By embedding the content directly in the request, Clients can include context from sources that the Agent may not have direct access to. Requires the `embeddedContext` [prompt capability](/protocol/v1/initialization#prompt-capabilities) when included in prompts. The embedded resource contents, which can be either: The URI identifying the resource The text content of the resource Optional MIME type of the text content The URI identifying the resource Base64-encoded binary data Optional MIME type of the blob Optional metadata about how the content should be used or displayed. [Learn more](https://modelcontextprotocol.io/specification/2025-06-18/server/resources#annotations). ### Resource Link References to resources that the Agent can access. ```json theme={null} { "type": "resource_link", "uri": "file:///home/user/document.pdf", "name": "document.pdf", "mimeType": "application/pdf", "size": 1024000 } ``` The URI of the resource A human-readable name for the resource The MIME type of the resource Optional display title for the resource Optional description of the resource contents Optional size of the resource in bytes Optional metadata about how the content should be used or displayed. [Learn more](https://modelcontextprotocol.io/specification/2025-06-18/server/resources#annotations). # Extensibility Source: https://agentclientprotocol.com/protocol/v1/extensibility 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 with type `{ [key: string]: unknown }` 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 theme={null} { "jsonrpc": "2.0", "id": 1, "method": "session/prompt", "params": { "sessionId": "sess_abc123def456", "prompt": [ { "type": "text", "text": "Hello, world!" } ], "_meta": { "traceparent": "00-80e1afed08e019fc1110464cfa66635c-7a085853722dc6d2-01", "zed.dev/debugMode": true } } } ``` Clients may propagate fields to the agent for correlation purposes, such as `requestId`. The following root-level keys in `_meta` **SHOULD** be reserved for [W3C trace context](https://www.w3.org/TR/trace-context/) to guarantee interop with existing MCP implementations and OpenTelemetry tooling: * `traceparent` * `tracestate` * `baggage` 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 ### 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 theme={null} { "jsonrpc": "2.0", "id": 1, "method": "_zed.dev/workspace/buffers", "params": { "language": "rust" } } ``` Upon receiving a custom request, implementations **MUST** respond accordingly with the provided `id`: ```json theme={null} { "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 receiving end doesn't recognize the custom method name, it should respond with the standard "Method not found" error: ```json theme={null} { "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 behavior or 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 theme={null} { "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 theme={null} { "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. # File System Source: https://agentclientprotocol.com/protocol/v1/file-system Client filesystem access methods The filesystem methods allow Agents to read and write text files within the Client's environment. These methods enable Agents to access unsaved editor state and allow Clients to track file modifications made during agent execution. ## Checking Support Before attempting to use filesystem methods, Agents **MUST** verify that the Client supports these capabilities by checking the [Client Capabilities](/protocol/v1/initialization#client-capabilities) field in the `initialize` response: ```json highlight={8,9} theme={null} { "jsonrpc": "2.0", "id": 0, "result": { "protocolVersion": 1, "clientCapabilities": { "fs": { "readTextFile": true, "writeTextFile": true } } } } ``` If `readTextFile` or `writeTextFile` is `false` or not present, the Agent **MUST NOT** attempt to call the corresponding filesystem method. ## Reading Files The `fs/read_text_file` method allows Agents to read text file contents from the Client's filesystem, including unsaved changes in the editor. ```json theme={null} { "jsonrpc": "2.0", "id": 3, "method": "fs/read_text_file", "params": { "sessionId": "sess_abc123def456", "path": "/home/user/project/src/main.py", "line": 10, "limit": 50 } } ``` The [Session ID](/protocol/v1/session-setup#session-id) for this request Absolute path to the file to read Optional line number to start reading from (1-based) Optional maximum number of lines to read The Client responds with the file contents: ```json theme={null} { "jsonrpc": "2.0", "id": 3, "result": { "content": "def hello_world():\n print('Hello, world!')\n" } } ``` ## Writing Files The `fs/write_text_file` method allows Agents to write or update text files in the Client's filesystem. ```json theme={null} { "jsonrpc": "2.0", "id": 4, "method": "fs/write_text_file", "params": { "sessionId": "sess_abc123def456", "path": "/home/user/project/config.json", "content": "{\n \"debug\": true,\n \"version\": \"1.0.0\"\n}" } } ``` The [Session ID](/protocol/v1/session-setup#session-id) for this request Absolute path to the file to write. The Client **MUST** create the file if it doesn't exist. The text content to write to the file The Client responds with an empty result on success: ```json theme={null} { "jsonrpc": "2.0", "id": 4, "result": null } ``` # Initialization Source: https://agentclientprotocol.com/protocol/v1/initialization How all Agent Client Protocol connections begin The Initialization phase allows [Clients](/protocol/v1/overview#client) and [Agents](/protocol/v1/overview#agent) to negotiate protocol versions, capabilities, and authentication methods.
```mermaid theme={null} sequenceDiagram participant Client participant Agent Note over Client, Agent: Connection established Client->>Agent: initialize Note right of Agent: Negotiate protocol
version & capabilities Agent-->>Client: initialize response Note over Client,Agent: Ready for session setup ```
Before a Session can be created, Clients **MUST** initialize the connection by calling the `initialize` method with: * The latest [protocol version](#protocol-version) supported * The [capabilities](#client-capabilities) supported They **SHOULD** also provide a name and version to the Agent. ```json theme={null} { "jsonrpc": "2.0", "id": 0, "method": "initialize", "params": { "protocolVersion": 1, "clientCapabilities": { "fs": { "readTextFile": true, "writeTextFile": true }, "terminal": true }, "clientInfo": { "name": "my-client", "title": "My Client", "version": "1.0.0" } } } ``` The Agent **MUST** respond with the chosen [protocol version](#protocol-version) and the [capabilities](#agent-capabilities) it supports. It **SHOULD** also provide a name and version to the Client as well: ```json theme={null} { "jsonrpc": "2.0", "id": 0, "result": { "protocolVersion": 1, "agentCapabilities": { "loadSession": true, "promptCapabilities": { "image": true, "audio": true, "embeddedContext": true }, "mcpCapabilities": { "http": true, "sse": true } }, "agentInfo": { "name": "my-agent", "title": "My Agent", "version": "1.0.0" }, "authMethods": [] } } ``` ## Protocol version The protocol versions that appear in the `initialize` requests and responses are a single integer that identifies a **MAJOR** protocol version. This version is only incremented when breaking changes are introduced. Clients and Agents **MUST** agree on a protocol version and act according to its specification. See [Capabilities](#capabilities) to learn how non-breaking features are introduced. ### Version Negotiation The `initialize` request **MUST** include the latest protocol version the Client supports. If the Agent supports the requested version, it **MUST** respond with the same version. Otherwise, the Agent **MUST** respond with the latest version it supports. If the Client does not support the version specified by the Agent in the `initialize` response, the Client **SHOULD** close the connection and inform the user about it. ## Capabilities Capabilities describe features supported by the Client and the Agent. All capabilities included in the `initialize` request are **OPTIONAL**. Clients and Agents **SHOULD** support all possible combinations of their peer's capabilities. The introduction of new capabilities is not considered a breaking change. Therefore, Clients and Agents **MUST** treat all capabilities omitted in the `initialize` request as **UNSUPPORTED**. Capabilities are high-level and are not attached to a specific base protocol concept. 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](/protocol/v1/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: #### File System The `fs/read_text_file` method is available. The `fs/write_text_file` method is available. Learn more about File System methods #### Terminal All `terminal/*` methods are available, allowing the Agent to execute and manage shell commands. Learn more about Terminals ### Agent Capabilities The Agent **SHOULD** specify whether it supports the following capabilities: The [`session/load`](/protocol/v1/session-setup#loading-sessions) method is available. Object indicating the different types of [content](/protocol/v1/content) that may be included in `session/prompt` requests. Authentication-related capabilities supported by the Agent. #### Prompt capabilities As a baseline, all Agents **MUST** support `ContentBlock::Text` and `ContentBlock::ResourceLink` in `session/prompt` requests. Optionally, they **MAY** support richer types of [content](/protocol/v1/content) by specifying the following capabilities: The prompt may include `ContentBlock::Image` The prompt may include `ContentBlock::Audio` The prompt may include `ContentBlock::Resource` #### MCP capabilities The Agent supports connecting to MCP servers over HTTP. The Agent supports connecting to MCP servers over SSE. Note: This transport has been deprecated by the MCP spec. #### Authentication Capabilities The [`logout`](/protocol/v1/authentication#logging-out) method is available. Learn more about Authentication #### Session Capabilities As a baseline, all Agents **MUST** support `session/new`, `session/prompt`, `session/cancel`, and `session/update`. Optionally, they **MAY** support other session methods and notifications by specifying additional capabilities. The [`session/delete`](/protocol/v1/session-delete) method is available. Omitted or `null` both mean the Agent does not advertise support. Supplying an empty object means the Agent supports deleting sessions from `session/list`. The Agent supports `additionalDirectories` on supported session lifecycle requests. Omitted or `null` both mean the Agent does not advertise support. Supplying `{}` means the Agent supports additional workspace roots. `session/load` is still handled by the top-level `load_session` capability. This will be unified in future versions of the protocol. ## Implementation Information Both Clients and Agents **SHOULD** provide information about their implementation in the `clientInfo` and `agentInfo` fields respectively. Both take the following three fields: Intended for programmatic or logical use, but can be used as a display name fallback if title isn’t present. Intended for UI and end-user contexts — optimized to be human-readable and easily understood. If not provided, the name should be used for display. Version of the implementation. Can be displayed to the user or used for debugging or metrics purposes. Note: in future versions of the protocol, this information will be required. *** Once the connection is initialized, you're ready to [create a session](/protocol/v1/session-setup) and begin the conversation with the Agent. # Overview Source: https://agentclientprotocol.com/protocol/v1/overview How the Agent Client Protocol works The Agent Client Protocol allows [Agents](#agent) and [Clients](#client) to communicate by exposing methods that each side can call and sending notifications to inform each other of events. ## Communication Model The protocol follows the [JSON-RPC 2.0](https://www.jsonrpc.org/specification) specification with two types of messages: * **Methods**: Request-response pairs that expect a result or error * **Notifications**: One-way messages that don't expect a response ## Message Flow A typical flow follows this pattern: * Client → Agent: `initialize` to establish connection * Client → Agent: `authenticate` if required by the Agent * Client → Agent: `session/new` to create a new session * Client → Agent: `session/load` to resume an existing session if supported * Client → Agent: `session/prompt` to send user message * Agent → Client: `session/update` notifications for progress updates * Agent → Client: File operations or permission requests as needed * Client → Agent: `session/cancel` to interrupt processing if needed * Turn ends and the Agent sends the `session/prompt` response with a stop reason ## Agent Agents are programs that use generative AI to autonomously modify code. They typically run as subprocesses of the Client. ### Baseline Methods [Negotiate versions and exchange capabilities.](/protocol/v1/initialization). Authenticate with the Agent (if required). [Create a new conversation session](/protocol/v1/session-setup#creating-a-session). [Send user prompts](/protocol/v1/prompt-turn#1-user-message) to the Agent. ### Optional Methods [Load an existing session](/protocol/v1/session-setup#loading-sessions) (requires `loadSession` capability). [End the current authenticated state](/protocol/v1/authentication#logging-out) (requires `agentCapabilities.auth.logout` capability). [Switch between agent operating modes](/protocol/v1/session-modes#setting-the-current-mode). ### Notifications [Cancel ongoing operations](/protocol/v1/prompt-turn#cancellation) (no response expected). ## Client Clients provide the interface between users and agents. They are typically code editors (IDEs, text editors) but can also be other UIs for interacting with agents. Clients manage the environment, handle user interactions, and control access to resources. ### Baseline Methods [Request user authorization](/protocol/v1/tool-calls#requesting-permission) for tool calls. ### Optional Methods [Read file contents](/protocol/v1/file-system#reading-files) (requires `fs.readTextFile` capability). [Write file contents](/protocol/v1/file-system#writing-files) (requires `fs.writeTextFile` capability). [Create a new terminal](/protocol/v1/terminals) (requires `terminal` capability). Get terminal output and exit status (requires `terminal` capability). Release a terminal (requires `terminal` capability). Wait for terminal command to exit (requires `terminal` capability). Kill terminal command without releasing (requires `terminal` capability). ### Notifications [Send session updates](/protocol/v1/prompt-turn#3-agent-reports-output) to inform the Client of changes (no response expected). This includes: - [Message chunks](/protocol/v1/content) (agent, user, thought) - [Tool calls and updates](/protocol/v1/tool-calls) - [Plans](/protocol/v1/agent-plan) - [Available commands updates](/protocol/v1/slash-commands#advertising-commands) * [Mode changes](/protocol/v1/session-modes#from-the-agent) ## Argument requirements * All file paths in the protocol **MUST** be absolute. * Line numbers are 1-based ## Error Handling All methods follow standard JSON-RPC 2.0 [error handling](https://www.jsonrpc.org/specification#error_object): * Successful responses include a `result` field * Errors include an `error` object with `code` and `message` * Notifications never receive responses (success or error) ## Conventions Unless explicitly defined otherwise in the schema, ACP-defined JSON object property keys use `camelCase`. String values carried by discriminator fields use `snake_case`. The JSON-RPC envelope fields (`jsonrpc`, `id`, `method`, `params`, `result`, and `error`) follow the JSON-RPC 2.0 specification. ## Extensibility The protocol provides built-in mechanisms for adding custom functionality while maintaining compatibility: * Add custom data using `_meta` fields * Create custom methods by prefixing their name with underscore (`_`) * Advertise custom capabilities during initialization Learn about [protocol extensibility](/protocol/v1/extensibility) to understand how to use these mechanisms. ## Next Steps * Learn about [Initialization](/protocol/v1/initialization) to understand version and capability negotiation * Understand [Session Setup](/protocol/v1/session-setup) for creating and loading sessions * Review the [Prompt Turn](/protocol/v1/prompt-turn) lifecycle * Explore [Extensibility](/protocol/v1/extensibility) to add custom features # Prompt Turn Source: https://agentclientprotocol.com/protocol/v1/prompt-turn Understanding the core conversation flow A prompt turn represents a complete interaction cycle between the [Client](/protocol/v1/overview#client) and [Agent](/protocol/v1/overview#agent), starting with a user message and continuing until the Agent completes its response. This may involve multiple exchanges with the language model and tool invocations. Before sending prompts, Clients **MUST** first complete the [initialization](/protocol/v1/initialization) phase and [session setup](/protocol/v1/session-setup). ## The Prompt Turn Lifecycle A prompt turn follows a structured flow that enables rich interactions between the user, Agent, and any connected tools.
```mermaid theme={null} sequenceDiagram participant Client participant Agent Note over Agent,Client: Session ready Note left of Client: User sends message Client->>Agent: session/prompt (user message) Note right of Agent: Process with LLM loop Until completion Note right of Agent: LLM responds with
content/tool calls Agent->>Client: session/update (plan) Agent->>Client: session/update (agent_message_chunk) opt Tool calls requested Agent->>Client: session/update (tool_call) opt Permission required Agent->>Client: session/request_permission Note left of Client: User grants/denies Client-->>Agent: Permission response end Agent->>Client: session/update (tool_call status: in_progress) Note right of Agent: Execute tool Agent->>Client: session/update (tool_call status: completed) Note right of Agent: Send tool results
back to LLM end opt User cancelled during execution Note left of Client: User cancels prompt Client->>Agent: session/cancel Note right of Agent: Abort operations Agent-->>Client: session/prompt response (cancelled) end end Agent-->>Client: session/prompt response (stopReason) ``` ### 1. User Message The turn begins when the Client sends a `session/prompt`: ```json theme={null} { "jsonrpc": "2.0", "id": 2, "method": "session/prompt", "params": { "sessionId": "sess_abc123def456", "prompt": [ { "type": "text", "text": "Can you analyze this code for potential issues?" }, { "type": "resource", "resource": { "uri": "file:///home/user/project/main.py", "mimeType": "text/x-python", "text": "def process_data(items):\n for item in items:\n print(item)" } } ] } } ``` The [ID](/protocol/v1/session-setup#session-id) of the session to send this message to. The contents of the user message, e.g. text, images, files, etc. Clients **MUST** restrict types of content according to the [Prompt Capabilities](/protocol/v1/initialization#prompt-capabilities) established during [initialization](/protocol/v1/initialization). Learn more about Content ### 2. Agent Processing Upon receiving the prompt request, the Agent processes the user's message and sends it to the language model, which **MAY** respond with text content, tool calls, or both. ### 3. Agent Reports Output The Agent reports the model's output to the Client via `session/update` notifications. This may include the Agent's plan for accomplishing the task: ```json expandable theme={null} { "jsonrpc": "2.0", "method": "session/update", "params": { "sessionId": "sess_abc123def456", "update": { "sessionUpdate": "plan", "entries": [ { "content": "Check for syntax errors", "priority": "high", "status": "pending" }, { "content": "Identify potential type issues", "priority": "medium", "status": "pending" }, { "content": "Review error handling patterns", "priority": "medium", "status": "pending" }, { "content": "Suggest improvements", "priority": "low", "status": "pending" } ] } } } ``` Learn more about Agent Plans The Agent then reports text responses from the model: ```json theme={null} { "jsonrpc": "2.0", "method": "session/update", "params": { "sessionId": "sess_abc123def456", "update": { "sessionUpdate": "agent_message_chunk", "messageId": "msg_agent_c42b9", "content": { "type": "text", "text": "I'll analyze your code for potential issues. Let me examine it..." } } } } ``` #### Message IDs The Agent **MAY** include an opaque, unique `messageId` on message chunks. Chunks with the same `messageId` belong to the same message; a changed `messageId` indicates a new message. If the model requested tool calls, these are also reported immediately: ```json theme={null} { "jsonrpc": "2.0", "method": "session/update", "params": { "sessionId": "sess_abc123def456", "update": { "sessionUpdate": "tool_call", "toolCallId": "call_001", "title": "Analyzing Python code", "kind": "other", "status": "pending" } } } ``` #### Session Usage Updates The Agent **MAY** also report current session context and cumulative cost state with a `usage_update`: ```json theme={null} { "jsonrpc": "2.0", "method": "session/update", "params": { "sessionId": "sess_abc123def456", "update": { "sessionUpdate": "usage_update", "used": 53000, "size": 200000, "cost": { "amount": 0.045, "currency": "USD" } } } } ``` `used` and `size` are required and non-null token counts for the current session context. `cost` is optional and, if present, `amount` and `currency` are required. `currency` is an ISO 4217 currency code like `"USD"`. ### 4. Check for Completion If there are no pending tool calls, the turn ends and the Agent **MUST** respond to the original `session/prompt` request with a `StopReason`: ```json theme={null} { "jsonrpc": "2.0", "id": 2, "result": { "stopReason": "end_turn" } } ``` Agents **MAY** stop the turn at any point by returning the corresponding [`StopReason`](#stop-reasons). ### 5. Tool Invocation and Status Reporting Before proceeding with execution, the Agent **MAY** request permission from the Client via the `session/request_permission` method. Once permission is granted (if required), the Agent **SHOULD** invoke the tool and report a status update marking the tool as `in_progress`: ```json theme={null} { "jsonrpc": "2.0", "method": "session/update", "params": { "sessionId": "sess_abc123def456", "update": { "sessionUpdate": "tool_call_update", "toolCallId": "call_001", "status": "in_progress" } } } ``` As the tool runs, the Agent **MAY** send additional updates, providing real-time feedback about tool execution progress. While tools execute on the Agent, they **MAY** leverage Client capabilities such as the file system (`fs`) methods to access resources within the Client's environment. When the tool completes, the Agent sends another update with the final status and any content: ```json theme={null} { "jsonrpc": "2.0", "method": "session/update", "params": { "sessionId": "sess_abc123def456", "update": { "sessionUpdate": "tool_call_update", "toolCallId": "call_001", "status": "completed", "content": [ { "type": "content", "content": { "type": "text", "text": "Analysis complete:\n- No syntax errors found\n- Consider adding type hints for better clarity\n- The function could benefit from error handling for empty lists" } } ] } } } ``` Learn more about Tool Calls ### 6. Continue Conversation The Agent sends the tool results back to the language model as another request. The cycle returns to [step 2](#2-agent-processing), continuing until the language model completes its response without requesting additional tool calls or the turn gets stopped by the Agent or cancelled by the Client. ## Stop Reasons When an Agent stops a turn, it must specify the corresponding `StopReason`: The language model finishes responding without requesting more tools The maximum token limit is reached The maximum number of model requests in a single turn is exceeded The Agent refuses to continue The Client cancels the turn ## Cancellation Clients **MAY** cancel an ongoing prompt turn at any time by sending a `session/cancel` notification: ```json theme={null} { "jsonrpc": "2.0", "method": "session/cancel", "params": { "sessionId": "sess_abc123def456" } } ``` The Client **SHOULD** preemptively mark all non-finished tool calls pertaining to the current turn as `cancelled` as soon as it sends the `session/cancel` notification. The Client **MUST** respond to all pending `session/request_permission` requests with the `cancelled` outcome. When the Agent receives this notification, it **SHOULD** stop all language model requests and all tool call invocations as soon as possible. After all ongoing operations have been successfully aborted and pending updates have been sent, the Agent **MUST** respond to the original `session/prompt` request with the `cancelled` [stop reason](#stop-reasons). API client libraries and tools often throw an exception when their operation is aborted, which may propagate as an error response to `session/prompt`. Clients often display unrecognized errors from the Agent to the user, which would be undesirable for cancellations as they aren't considered errors. Agents **MUST** catch these errors and return the semantically meaningful `cancelled` stop reason, so that Clients can reliably confirm the cancellation. The Agent **MAY** send `session/update` notifications with content or tool call updates after receiving the `session/cancel` notification, but it **MUST** ensure that it does so before responding to the `session/prompt` request. The Client **SHOULD** still accept tool call updates received after sending `session/cancel`. *** Once a prompt turn completes, the Client may send another `session/prompt` to continue the conversation, building on the context established in previous turns. # Schema Source: https://agentclientprotocol.com/protocol/v1/schema Schema definitions for the Agent Client Protocol The schema file can be downloaded directly from the [latest GitHub release](https://github.com/agentclientprotocol/agent-client-protocol/releases/latest/download/schema.json). ## Agent 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. ### authenticate 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/v1/initialization) #### AuthenticateRequest Request parameters for the authenticate method. Specifies which authentication method to use. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) AuthMethodId}> The ID of the authentication method to use. Must be one of the methods advertised in the initialize response. #### AuthenticateResponse Response to the `authenticate` method. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) ### initialize 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/v1/initialization) #### InitializeRequest Request parameters for the initialize method. Sent by the client to establish connection and negotiate capabilities. See protocol docs: [Initialization](https://agentclientprotocol.com/protocol/v1/initialization) **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) ClientCapabilities}> Capabilities supported by the client. * Default: `{"fs":{"readTextFile":false,"writeTextFile":false},"terminal":false}` Implementation | null}> Information about the Client name and version sent to the Agent. Note: in future versions of the protocol, this will be required. ProtocolVersion}> The latest protocol version supported by the client. #### InitializeResponse Response to the `initialize` method. Contains the negotiated protocol version and agent capabilities. See protocol docs: [Initialization](https://agentclientprotocol.com/protocol/v1/initialization) **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) AgentCapabilities}> Capabilities supported by the agent. * Default: `{"loadSession":false,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false},"mcpCapabilities":{"http":false,"sse":false},"sessionCapabilities":{},"auth":{}}` Implementation | null}> Information about the Agent name and version sent to the Client. Note: in future versions of the protocol, this will be required. AuthMethod[]}> Authentication methods supported by the agent. * Default: `[]` ProtocolVersion}> The protocol version the client specified if supported by the agent, or the latest protocol version supported by the agent. The client should disconnect, if it doesn't support this version. ### logout Logs out of the current authenticated state. After a successful logout, all new sessions will require authentication. There is no guarantee about the behavior of already running sessions. #### LogoutRequest Request parameters for the logout method. Terminates the current authenticated session. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) #### LogoutResponse Response to the `logout` method. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) ### session/cancel 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/v1/prompt-turn#cancellation) #### CancelNotification Notification to cancel ongoing operations for a session. See protocol docs: [Cancellation](https://agentclientprotocol.com/protocol/v1/prompt-turn#cancellation) **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) SessionId}> The ID of the session to cancel operations for. ### session/close Closes an active session and frees up any resources associated with it. This method is only available if the agent advertises the `sessionCapabilities.close` capability. The agent must cancel any ongoing work (as if `session/cancel` was called) and then free up any resources associated with the session. #### CloseSessionRequest Request parameters for closing an active session. If supported, the agent **must** cancel any ongoing work related to the session (treat it as if `session/cancel` was called) and then free up any resources associated with the session. Only available if the Agent supports the `sessionCapabilities.close` capability. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) SessionId}> The ID of the session to close. #### CloseSessionResponse Response from closing a session. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) ### session/delete Deletes an existing session from `session/list`. This method is only available if the agent advertises the `sessionCapabilities.delete` capability. #### DeleteSessionRequest Request parameters for deleting an existing session from `session/list`. Only available if the Agent supports the `sessionCapabilities.delete` capability. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) SessionId}> The ID of the session to delete. #### DeleteSessionResponse Response from deleting a session. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) ### session/list Lists existing sessions known to the agent. This method is only available if the agent advertises the `sessionCapabilities.list` capability. The agent should return metadata about sessions with optional filtering and pagination support. #### ListSessionsRequest Request parameters for listing existing sessions. Only available if the Agent supports the `sessionCapabilities.list` capability. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) Opaque cursor token from a previous response's nextCursor field for cursor-based pagination Filter sessions by working directory. Must be an absolute path. #### ListSessionsResponse Response from listing sessions. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) Opaque cursor token. If present, pass this in the next request's cursor parameter to fetch the next page. If absent, there are no more results. SessionInfo[]}> Array of session information objects ### session/load 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/v1/session-setup#loading-sessions) #### LoadSessionRequest Request parameters for loading an existing session. Only available if the Agent supports the `loadSession` capability. See protocol docs: [Loading Sessions](https://agentclientprotocol.com/protocol/v1/session-setup#loading-sessions) **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) "string"[]}> Additional workspace roots to activate for this session. Each path must be absolute. When omitted or empty, no additional roots are activated. When non-empty, this is the complete resulting additional-root list for the loaded session. It may differ from any previously used or reported list as long as the request `cwd` matches the session's `cwd`. The working directory for this session. Must be an absolute path. McpServer[]}> List of MCP servers to connect to for this session. SessionId}> The ID of the session to load. #### LoadSessionResponse Response from loading an existing session. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) SessionConfigOption[] | null}> Initial session configuration options if supported by the Agent. SessionModeState | null}> Initial mode state if supported by the Agent See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/v1/session-modes) ### session/new 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/v1/session-setup) #### NewSessionRequest Request parameters for creating a new session. See protocol docs: [Creating a Session](https://agentclientprotocol.com/protocol/v1/session-setup#creating-a-session) **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) "string"[]}> Additional workspace roots for this session. Each path must be absolute. These expand the session's filesystem scope without changing `cwd`, which remains the base for relative paths. When omitted or empty, no additional roots are activated for the new session. The working directory for this session. Must be an absolute path. McpServer[]}> List of MCP (Model Context Protocol) servers the agent should connect to. #### NewSessionResponse Response from creating a new session. See protocol docs: [Creating a Session](https://agentclientprotocol.com/protocol/v1/session-setup#creating-a-session) **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) SessionConfigOption[] | null}> Initial session configuration options if supported by the Agent. SessionModeState | null}> Initial mode state if supported by the Agent See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/v1/session-modes) SessionId}> Unique identifier for the created session. Used in all subsequent requests for this conversation. ### session/prompt 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/v1/prompt-turn) #### PromptRequest Request parameters for sending a user prompt to the agent. Contains the user's message and any additional context. See protocol docs: [User Message](https://agentclientprotocol.com/protocol/v1/prompt-turn#1-user-message) **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) ContentBlock[]}> The blocks of content that compose the user's message. As a baseline, the Agent MUST support `ContentBlock::Text` and `ContentBlock::ResourceLink`, while other variants are optionally enabled via `PromptCapabilities`. The Client MUST adapt its interface according to `PromptCapabilities`. The client MAY include referenced pieces of context as either `ContentBlock::Resource` or `ContentBlock::ResourceLink`. When available, `ContentBlock::Resource` is preferred as it avoids extra round-trips and allows the message to include pieces of context from sources the agent may not have access to. SessionId}> The ID of the session to send this user message to #### PromptResponse Response from processing a user prompt. See protocol docs: [Check for Completion](https://agentclientprotocol.com/protocol/v1/prompt-turn#4-check-for-completion) **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) StopReason}> Indicates why the agent stopped processing the turn. ### session/resume Resumes an existing session without returning previous messages. This method is only available if the agent advertises the `sessionCapabilities.resume` capability. The agent should resume the session context, allowing the conversation to continue without replaying the message history (unlike `session/load`). #### ResumeSessionRequest Request parameters for resuming an existing session. Resumes an existing session without returning previous messages (unlike `session/load`). This is useful for agents that can resume sessions but don't implement full session loading. Only available if the Agent supports the `sessionCapabilities.resume` capability. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) "string"[]}> Additional workspace roots to activate for this session. Each path must be absolute. When omitted or empty, no additional roots are activated. When non-empty, this is the complete resulting additional-root list for the resumed session. It may differ from any previously used or reported list as long as the request `cwd` matches the session's `cwd`. The working directory for this session. Must be an absolute path. McpServer[]}> List of MCP servers to connect to for this session. SessionId}> The ID of the session to resume. #### ResumeSessionResponse Response from resuming an existing session. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) SessionConfigOption[] | null}> Initial session configuration options if supported by the Agent. SessionModeState | null}> Initial mode state if supported by the Agent See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/v1/session-modes) ### session/set\_config\_option Sets the current value for a session configuration option. #### SetSessionConfigOptionRequest Request parameters for setting a session configuration option. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) SessionConfigId}> The ID of the configuration option to set. SessionId}> The ID of the session to set the configuration option for. SessionConfigValueId}> The ID of the configuration option value to set. #### SetSessionConfigOptionResponse Response to `session/set_config_option` method. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) SessionConfigOption[]}> The full set of configuration options and their current values. ### session/set\_mode 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/v1/session-modes) #### SetSessionModeRequest Request parameters for setting a session mode. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) SessionModeId}> The ID of the mode to set. SessionId}> The ID of the session to set the mode for. #### SetSessionModeResponse Response to `session/set_mode` method. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) ## Client 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. ### fs/read\_text\_file Reads content from a text file in the client's file system. Only available if the client advertises the `fs.readTextFile` capability. Allows the agent to access file contents within the client's environment. See protocol docs: [Client](https://agentclientprotocol.com/protocol/v1/overview#client) #### ReadTextFileRequest Request to read content from a text file. Only available if the client supports the `fs.readTextFile` capability. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) Maximum number of lines to read. * Minimum: `0` Line number to start reading from (1-based). * Minimum: `0` Absolute path to the file to read. SessionId}> The session ID for this request. #### ReadTextFileResponse Response containing the contents of a text file. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) Content payload returned by this response. ### fs/write\_text\_file Writes content to a text file in the client's file system. Only available if the client advertises the `fs.writeTextFile` capability. Allows the agent to create or modify files within the client's environment. See protocol docs: [Client](https://agentclientprotocol.com/protocol/v1/overview#client) #### WriteTextFileRequest Request to write content to a text file. Only available if the client supports the `fs.writeTextFile` capability. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) The text content to write to the file. Absolute path to the file to write. SessionId}> The session ID for this request. #### WriteTextFileResponse Response to `fs/write_text_file` **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) ### session/request\_permission Requests permission from the user for a tool call operation. Called by the agent when it needs user authorization before executing a potentially sensitive operation. The client should present the options to the user and return their decision. If the client cancels the prompt turn via `session/cancel`, it MUST respond to this request with `RequestPermissionOutcome::Cancelled`. See protocol docs: [Requesting Permission](https://agentclientprotocol.com/protocol/v1/tool-calls#requesting-permission) #### RequestPermissionRequest Request for user permission to execute a tool call. Sent when the agent needs authorization before performing a sensitive operation. See protocol docs: [Requesting Permission](https://agentclientprotocol.com/protocol/v1/tool-calls#requesting-permission) **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) PermissionOption[]}> Available permission options for the user to choose from. SessionId}> The session ID for this request. ToolCallUpdate}> Details about the tool call requiring permission. #### RequestPermissionResponse Response to a permission request. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) RequestPermissionOutcome}> The user's decision on the permission request. ### session/update Handles session update notifications from the agent. This is a notification endpoint (no response expected) that receives real-time updates about session progress, including message chunks, tool calls, and execution plans. Note: Clients SHOULD continue accepting tool call updates even after sending a `session/cancel` notification, as the agent may send final updates before responding with the cancelled stop reason. See protocol docs: [Agent Reports Output](https://agentclientprotocol.com/protocol/v1/prompt-turn#3-agent-reports-output) #### SessionNotification Notification containing a session update from the agent. Used to stream real-time progress and results during prompt processing. See protocol docs: [Agent Reports Output](https://agentclientprotocol.com/protocol/v1/prompt-turn#3-agent-reports-output) **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) SessionId}> The ID of the session this update pertains to. SessionUpdate}> The actual update content. ### terminal/create Executes a command in a new terminal Only available if the `terminal` Client capability is set to `true`. Returns a `TerminalId` that can be used with other terminal methods to get the current output, wait for exit, and kill the command. The `TerminalId` can also be used to embed the terminal in a tool call by using the `ToolCallContent::Terminal` variant. The Agent is responsible for releasing the terminal by using the `terminal/release` method. See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/v1/terminals) #### CreateTerminalRequest Request to create a new terminal and execute a command. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) "string"[]}> Array of command arguments. The command to execute. Working directory for the command. Must be an absolute path. EnvVariable[]}> Environment variables for the command. Maximum number of output bytes to retain. When the limit is exceeded, the Client truncates from the beginning of the output to stay within the limit. The Client MUST ensure truncation happens at a character boundary to maintain valid string output, even if this means the retained output is slightly less than the specified limit. * Minimum: `0` SessionId}> The session ID for this request. #### CreateTerminalResponse Response containing the ID of the created terminal. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) TerminalId}> The unique identifier for the created terminal. ### terminal/kill Kills the terminal command without releasing the terminal While `terminal/release` will also kill the command, this method will keep the `TerminalId` valid so it can be used with other methods. This method can be helpful when implementing command timeouts which terminate the command as soon as elapsed, and then get the final output so it can be sent to the model. Note: Call `terminal/release` when `TerminalId` is no longer needed. See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/v1/terminals) #### KillTerminalRequest Request to kill a terminal without releasing it. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) SessionId}> The session ID for this request. TerminalId}> The ID of the terminal to kill. #### KillTerminalResponse Response to `terminal/kill` method **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) ### terminal/output Gets the terminal output and exit status Returns the current content in the terminal without waiting for the command to exit. If the command has already exited, the exit status is included. See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/v1/terminals) #### TerminalOutputRequest Request to get the current output and status of a terminal. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) SessionId}> The session ID for this request. TerminalId}> The ID of the terminal to get output from. #### TerminalOutputResponse Response containing the terminal output and exit status. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) TerminalExitStatus | null}> Exit status if the command has completed. The terminal output captured so far. Whether the output was truncated due to byte limits. ### terminal/release Releases a terminal The command is killed if it hasn't exited yet. Use `terminal/wait_for_exit` to wait for the command to exit before releasing the terminal. After release, the `TerminalId` can no longer be used with other `terminal/*` methods, but tool calls that already contain it, continue to display its output. The `terminal/kill` method can be used to terminate the command without releasing the terminal, allowing the Agent to call `terminal/output` and other methods. See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/v1/terminals) #### ReleaseTerminalRequest Request to release a terminal and free its resources. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) SessionId}> The session ID for this request. TerminalId}> The ID of the terminal to release. #### ReleaseTerminalResponse Response to terminal/release method **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) ### terminal/wait\_for\_exit Waits for the terminal command to exit and return its exit status See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/v1/terminals) #### WaitForTerminalExitRequest Request to wait for a terminal command to exit. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) SessionId}> The session ID for this request. TerminalId}> The ID of the terminal to wait for. #### WaitForTerminalExitResponse Response containing the exit status of a terminal command. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) The process exit code (may be null if terminated by signal). * Minimum: `0` The signal that terminated the process (may be null if exited normally). ## Protocol Level Defines the interface that ACP-compliant agents and clients must both implement. Notifications whose methods start with '$/' are messages which are protocol implementation dependent and might not be implementable in all clients or agents. For example if the implementation uses a single threaded synchronous programming language then there is little it can do to react to a `$/cancel\_request\` notification. If an agent or client receives notifications starting with '\$/' it is free to ignore the notification. ### \$/cancel\_request Cancels an ongoing request. This is a notification sent by the side that sent a request to cancel that request. Upon receiving this notification, the receiver: 1. MAY cancel the corresponding request activity and all nested activities 2. MAY send any pending notifications. 3. MUST send one of these responses for the original request: * Valid response with appropriate data (partial results or cancellation marker) * Error response with code `-32800` (Cancelled) See protocol docs: [Cancellation](https://agentclientprotocol.com/protocol/v1/cancellation) #### CancelRequestNotification Notification to cancel an ongoing request. See protocol docs: [Cancellation](https://agentclientprotocol.com/protocol/v1/cancellation) **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) RequestId}> The ID of the request to cancel. ## AgentAuthCapabilities Authentication-related capabilities supported by the agent. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) LogoutCapabilities | null}> Whether the agent supports the logout method. Optional. Omitted or `null` both mean the agent does not advertise support. Supplying `\{\}` means the agent supports the logout method. ## AgentCapabilities Capabilities supported by the agent. Advertised during initialization to inform the client about available features and content types. See protocol docs: [Agent Capabilities](https://agentclientprotocol.com/protocol/v1/initialization#agent-capabilities) **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) AgentAuthCapabilities}> Authentication-related capabilities supported by the agent. * Default: `{}` Whether the agent supports `session/load`. * Default: `false` McpCapabilities}> MCP capabilities supported by the agent. * Default: `{"http":false,"sse":false}` PromptCapabilities}> Prompt capabilities supported by the agent. * Default: `{"image":false,"audio":false,"embeddedContext":false}` SessionCapabilities}> Session lifecycle and prompt capabilities advertised by the agent. * Default: `{}` ## Annotations Optional annotations for the client. The client can use annotations to inform how objects are used or displayed **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) Role[] | null}> Intended recipients for this content, such as the user or assistant. Timestamp indicating when the underlying resource was last modified. Relative importance of this content when clients choose what to surface. ## AudioContent Audio provided to or from an LLM. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) Annotations | null}> Optional annotations that help clients decide how to display or route this content. Base64-encoded media payload. MIME type describing the encoded media payload. ## AuthMethod Describes an available authentication method. The `type` field acts as the discriminator in the serialized JSON form. When no `type` is present, the method is treated as `agent`. Agent handles authentication itself. This is the default when no `type` is specified. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) Optional description providing more details about this authentication method. AuthMethodId}> Unique identifier for this authentication method. Human-readable name of the authentication method. ## AuthMethodAgent Agent handles authentication itself. This is the default authentication method type. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) Optional description providing more details about this authentication method. AuthMethodId}> Unique identifier for this authentication method. Human-readable name of the authentication method. ## AuthMethodId Typed identifier used for auth method values on the wire. **Type:** `string` ## AvailableCommand Information about a command. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) Human-readable description of what the command does. AvailableCommandInput | null}> Input for the command if required Command name (e.g., `create_plan`, `research_codebase`). ## AvailableCommandInput The input specification for a command. All text that was typed after the command name is provided as input. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) A hint to display when the input hasn't been provided yet ## AvailableCommandsUpdate Available commands are ready or have changed **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) AvailableCommand[]}> Commands the agent can execute ## BlobResourceContents Binary resource contents. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) Base64-encoded bytes for a binary resource payload. MIME type describing the encoded media payload. URI associated with this resource or media payload. ## ClientCapabilities Capabilities supported by the client. Advertised during initialization to inform the agent about available features and methods. See protocol docs: [Client Capabilities](https://agentclientprotocol.com/protocol/v1/initialization#client-capabilities) **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) FileSystemCapabilities}> File system capabilities supported by the client. Determines which file operations the agent can request. * Default: `{"readTextFile":false,"writeTextFile":false}` Whether the Client support all `terminal/*` methods. * Default: `false` ## ConfigOptionUpdate Session configuration options have been updated. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) SessionConfigOption[]}> The full set of configuration options and their current values. ## Content Standard content block (text, images, resources). **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) ContentBlock}> The actual content block. ## ContentBlock Content blocks represent displayable information in the Agent Client Protocol. They provide a structured way to handle various types of user-facing content—whether it's text from language models, images for analysis, or embedded resources for context. Content blocks appear in: * User prompts sent via `session/prompt` * Language model output streamed through `session/update` notifications * Progress updates and results from tool calls This structure is compatible with the Model Context Protocol (MCP), enabling agents to seamlessly forward content from MCP tool outputs without transformation. See protocol docs: [Content](https://agentclientprotocol.com/protocol/v1/content) **Type:** Union Text content. May be plain text or formatted with Markdown. All agents MUST support text content blocks in prompts. Clients SHOULD render this text as Markdown. The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) Annotations | null}> Optional annotations that help clients decide how to display or route this content. Text payload carried by this content block. The discriminator value. Must be `"text"`. Images for visual context or analysis. Requires the `image` prompt capability when included in prompts. The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) Annotations | null}> Optional annotations that help clients decide how to display or route this content. Base64-encoded media payload. MIME type describing the encoded media payload. The discriminator value. Must be `"image"`. URI associated with this resource or media payload. Audio data for transcription or analysis. Requires the `audio` prompt capability when included in prompts. The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) Annotations | null}> Optional annotations that help clients decide how to display or route this content. Base64-encoded media payload. MIME type describing the encoded media payload. The discriminator value. Must be `"audio"`. References to resources that the agent can access. All agents MUST support resource links in prompts. The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) Annotations | null}> Optional annotations that help clients decide how to display or route this content. Optional human-readable details shown with this protocol object. MIME type describing the encoded media payload. Human-readable name shown for this protocol object. Optional size of the linked resource in bytes, if known. Optional display title for end-user UI. The discriminator value. Must be `"resource_link"`. URI associated with this resource or media payload. Complete resource contents embedded directly in the message. Preferred for including context as it avoids extra round-trips. Requires the `embeddedContext` prompt capability when included in prompts. The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) Annotations | null}> Optional annotations that help clients decide how to display or route this content. EmbeddedResourceResource}> Embedded resource payload, either text or binary data. The discriminator value. Must be `"resource"`. ## ContentChunk A streamed item of content **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) ContentBlock}> A single item of content MessageId | null}> A unique identifier for the message this chunk belongs to. All chunks belonging to the same message share the same `messageId`. A change in `messageId` indicates a new message has started. ## Cost Cost information for a session. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) Total cumulative cost for session. ISO 4217 currency code (e.g., "USD", "EUR"). ## CurrentModeUpdate The current mode of the session has changed See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/v1/session-modes) **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) SessionModeId}> The ID of the current mode ## Diff A diff representing file modifications. Shows changes to files in a format suitable for display in the client UI. See protocol docs: [Content](https://agentclientprotocol.com/protocol/v1/tool-calls#content) **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) The new content after modification. The original content (None for new files). The absolute file path being modified. ## EmbeddedResource The contents of a resource, embedded into a prompt or tool call result. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) Annotations | null}> Optional annotations that help clients decide how to display or route this content. EmbeddedResourceResource}> Embedded resource payload, either text or binary data. ## EmbeddedResourceResource Resource content that can be embedded in a message. **Type:** Union Text resource contents embedded directly in the message. The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) MIME type describing the encoded media payload. Text payload carried by this content block. URI associated with this resource or media payload. Binary resource contents embedded directly in the message. The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) Base64-encoded bytes for a binary resource payload. MIME type describing the encoded media payload. URI associated with this resource or media payload. ## EnvVariable An environment variable to set when launching an MCP server. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) The name of the environment variable. The value to set for the environment variable. ## Error JSON-RPC error object. Represents an error that occurred during method execution, following the JSON-RPC 2.0 error object specification with optional additional data. See protocol docs: [JSON-RPC Error Object](https://www.jsonrpc.org/specification#error_object) **Type:** Object **Properties:** ErrorCode}> A number indicating the error type that occurred. This must be an integer as defined in the JSON-RPC specification. Optional primitive or structured value that contains additional information about the error. This may include debugging information or context-specific details. A string providing a short description of the error. The message should be limited to a concise single sentence. ## ErrorCode Predefined error codes for common JSON-RPC and ACP-specific errors. These codes follow the JSON-RPC 2.0 specification for standard errors and use the reserved range (-32000 to -32099) for protocol-specific errors. **Type:** Union **Parse error**: Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text. **Invalid request**: The JSON sent is not a valid Request object. **Method not found**: The method does not exist or is not available. **Invalid params**: Invalid method parameter(s). **Internal error**: Internal JSON-RPC error. Reserved for implementation-defined server errors. **Request cancelled**: Execution of the method was aborted either due to a cancellation request from the caller or because of resource constraints or shutdown. **Authentication required**: Authentication is required before this operation can be performed. **Resource not found**: A given resource, such as a file, was not found. Other undefined error code. ## ExtNotification Allows the Agent to send an arbitrary notification that is not part of the ACP spec. 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/v1/extensibility) ## ExtRequest Allows for sending an arbitrary request that is not part of the ACP spec. Extension methods provide a way to add custom functionality while maintaining protocol compatibility. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) ## ExtResponse Allows for sending an arbitrary response to an `ExtRequest` that is not part of the ACP spec. Extension methods provide a way to add custom functionality while maintaining protocol compatibility. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) ## FileSystemCapabilities File system capabilities that a client may support. See protocol docs: [FileSystem](https://agentclientprotocol.com/protocol/v1/initialization#filesystem) **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) Whether the Client supports `fs/read_text_file` requests. * Default: `false` Whether the Client supports `fs/write_text_file` requests. * Default: `false` ## HttpHeader An HTTP header to set when making requests to the MCP server. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) The name of the HTTP header. The value to set for the HTTP header. ## ImageContent An image provided to or from an LLM. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) Annotations | null}> Optional annotations that help clients decide how to display or route this content. Base64-encoded media payload. MIME type describing the encoded media payload. URI associated with this resource or media payload. ## Implementation Metadata about the implementation of the client or agent. Describes the name and version of an ACP implementation, with an optional title for UI representation. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) Intended for programmatic or logical use, but can be used as a display name fallback if title isn’t present. Intended for UI and end-user contexts — optimized to be human-readable and easily understood. If not provided, the name should be used for display. Version of the implementation. Can be displayed to the user or used for debugging or metrics purposes. (e.g. "1.0.0"). ## LogoutCapabilities Logout capabilities supported by the agent. Supplying `\{\}` means the agent supports the logout method. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) ## McpCapabilities MCP capabilities supported by the agent **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) Agent supports `McpServer::Http`. * Default: `false` Agent supports `McpServer::Sse`. * Default: `false` ## McpServer Configuration for connecting to an MCP (Model Context Protocol) server. MCP servers provide tools and context that the agent can use when processing prompts. See protocol docs: [MCP Servers](https://agentclientprotocol.com/protocol/v1/session-setup#mcp-servers) **Type:** Union HTTP transport configuration Only available when the Agent capabilities indicate `mcp_capabilities.http` is `true`. The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) HttpHeader[]}> HTTP headers to set when making requests to the MCP server. Human-readable name identifying this MCP server. The discriminator value. Must be `"http"`. URL to the MCP server. SSE transport configuration Only available when the Agent capabilities indicate `mcp_capabilities.sse` is `true`. The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) HttpHeader[]}> HTTP headers to set when making requests to the MCP server. Human-readable name identifying this MCP server. The discriminator value. Must be `"sse"`. URL to the MCP server. Stdio transport configuration All Agents MUST support this transport. The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) "string"[]}> Command-line arguments to pass to the MCP server. Absolute path to the MCP server executable. EnvVariable[]}> Environment variables to set when launching the MCP server. Human-readable name identifying this MCP server. ## McpServerHttp HTTP transport configuration for MCP. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) HttpHeader[]}> HTTP headers to set when making requests to the MCP server. Human-readable name identifying this MCP server. URL to the MCP server. ## McpServerSse SSE transport configuration for MCP. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) HttpHeader[]}> HTTP headers to set when making requests to the MCP server. Human-readable name identifying this MCP server. URL to the MCP server. ## McpServerStdio Stdio transport configuration for MCP. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) "string"[]}> Command-line arguments to pass to the MCP server. Absolute path to the MCP server executable. EnvVariable[]}> Environment variables to set when launching the MCP server. Human-readable name identifying this MCP server. ## MessageId Unique identifier for a message within a session. **Type:** `string` ## PermissionOption An option presented to the user when requesting permission. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) PermissionOptionKind}> Hint about the nature of this permission option. Human-readable label to display to the user. PermissionOptionId}> Unique identifier for this permission option. ## PermissionOptionId Unique identifier for a permission option. **Type:** `string` ## PermissionOptionKind The type of permission option being presented to the user. Helps clients choose appropriate icons and UI treatment. **Type:** Union Allow this operation only this time. Allow this operation and remember the choice. Reject this operation only this time. Reject this operation and remember the choice. ## Plan An execution plan for accomplishing complex tasks. Plans consist of multiple entries representing individual tasks or goals. Agents report plans to clients to provide visibility into their execution strategy. Plans can evolve during execution as the agent discovers new requirements or completes tasks. See protocol docs: [Agent Plan](https://agentclientprotocol.com/protocol/v1/agent-plan) **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) PlanEntry[]}> The list of tasks to be accomplished. When updating a plan, the agent must send a complete list of all entries with their current status. The client replaces the entire plan with each update. ## PlanEntry A single entry in the execution plan. Represents a task or goal that the assistant intends to accomplish as part of fulfilling the user's request. See protocol docs: [Plan Entries](https://agentclientprotocol.com/protocol/v1/agent-plan#plan-entries) **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) Human-readable description of what this task aims to accomplish. PlanEntryPriority}> The relative importance of this task. Used to indicate which tasks are most critical to the overall goal. PlanEntryStatus}> Current execution status of this task. ## PlanEntryPriority Priority levels for plan entries. Used to indicate the relative importance or urgency of different tasks in the execution plan. See protocol docs: [Plan Entries](https://agentclientprotocol.com/protocol/v1/agent-plan#plan-entries) **Type:** Union High priority task - critical to the overall goal. Medium priority task - important but not critical. Low priority task - nice to have but not essential. ## PlanEntryStatus Status of a plan entry in the execution flow. Tracks the lifecycle of each task from planning through completion. See protocol docs: [Plan Entries](https://agentclientprotocol.com/protocol/v1/agent-plan#plan-entries) **Type:** Union The task has not started yet. The task is currently being worked on. The task has been successfully completed. ## PromptCapabilities Prompt capabilities supported by the agent in `session/prompt` requests. Baseline agent functionality requires support for `ContentBlock::Text` and `ContentBlock::ResourceLink` in prompt requests. Other variants must be explicitly opted in to. Capabilities for different types of content in prompt requests. Indicates which content types beyond the baseline (text and resource links) the agent can process. See protocol docs: [Prompt Capabilities](https://agentclientprotocol.com/protocol/v1/initialization#prompt-capabilities) **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) Agent supports `ContentBlock::Audio`. * Default: `false` Agent supports embedded context in `session/prompt` requests. When enabled, the Client is allowed to include `ContentBlock::Resource` in prompt requests for pieces of context that are referenced in the message. * Default: `false` Agent supports `ContentBlock::Image`. * Default: `false` ## ProtocolVersion Protocol version identifier. This version is only bumped for breaking changes. Non-breaking changes should be introduced via capabilities. **Type:** `integer (uint16)` | Constraint | Value | | ---------- | ------- | | Minimum | `0` | | Maximum | `65535` | ## RequestId JSON RPC Request Id An identifier established by the Client that MUST contain a String, Number, or NULL value if included. If it is not included it is assumed to be a notification. The value SHOULD normally not be Null \[1] and Numbers SHOULD NOT contain fractional parts \[2] The Server MUST reply with the same value in the Response object if included. This member is used to correlate the context between the two objects. \[1] The use of Null as a value for the id member in a Request object is discouraged, because this specification uses a value of Null for Responses with an unknown id. Also, because JSON-RPC 1.0 uses an id value of Null for Notifications this could cause confusion in handling. \[2] Fractional parts may be problematic, since many decimal fractions cannot be represented exactly as binary fractions. **Type:** Union The JSON-RPC `null` request id. A numeric JSON-RPC request id. A string JSON-RPC request id. ## RequestPermissionOutcome The outcome of a permission request. **Type:** Union The prompt turn was cancelled before the user responded. When a client sends a `session/cancel` notification to cancel an ongoing prompt turn, it MUST respond to all pending `session/request_permission` requests with this `Cancelled` outcome. See protocol docs: [Cancellation](https://agentclientprotocol.com/protocol/v1/prompt-turn#cancellation) The discriminator value. Must be `"cancelled"`. The user selected one of the provided options. The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) PermissionOptionId}> The ID of the option the user selected. The discriminator value. Must be `"selected"`. ## ResourceLink A resource that the server is capable of reading, included in a prompt or tool call result. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) Annotations | null}> Optional annotations that help clients decide how to display or route this content. Optional human-readable details shown with this protocol object. MIME type describing the encoded media payload. Human-readable name shown for this protocol object. Optional size of the linked resource in bytes, if known. Optional display title for end-user UI. URI associated with this resource or media payload. ## Role The sender or recipient of messages and data in a conversation. **Type:** Union The assistant side of a conversation. The user side of a conversation. ## SelectedPermissionOutcome The user selected one of the provided options. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) PermissionOptionId}> The ID of the option the user selected. ## SessionAdditionalDirectoriesCapabilities Capabilities for additional session directories support. Supplying `\{\}` means the agent supports the `additionalDirectories` field on supported session lifecycle requests. Agents that also support `session/list` may return `SessionInfo.additionalDirectories` to report the complete ordered additional-root list associated with a listed session. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) ## SessionCapabilities Session capabilities supported by the agent. As a baseline, all Agents **MUST** support `session/new`, `session/prompt`, `session/cancel`, and `session/update`. Optionally, they **MAY** support other session methods and notifications by specifying additional capabilities. Note: `session/load` is still handled by the top-level `load_session` capability. This will be unified in future versions of the protocol. See protocol docs: [Session Capabilities](https://agentclientprotocol.com/protocol/v1/initialization#session-capabilities) **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) SessionAdditionalDirectoriesCapabilities | null}> Whether the agent supports `additionalDirectories` on supported session lifecycle requests. Optional. Omitted or `null` both mean the agent does not advertise support. Supplying `\{\}` means the agent supports `additionalDirectories` on supported session lifecycle requests. Agents that also support `session/list` may return `SessionInfo.additionalDirectories` to report the complete ordered additional-root list associated with a listed session. SessionCloseCapabilities | null}> Whether the agent supports `session/close`. Optional. Omitted or `null` both mean the agent does not advertise support. Supplying `\{\}` means the agent supports closing sessions. SessionDeleteCapabilities | null}> Whether the agent supports `session/delete`. Optional. Omitted or `null` both mean the agent does not advertise support. Supplying `\{\}` means the agent supports deleting sessions from `session/list`. SessionListCapabilities | null}> Whether the agent supports `session/list`. Optional. Omitted or `null` both mean the agent does not advertise support. Supplying `\{\}` means the agent supports listing sessions. SessionResumeCapabilities | null}> Whether the agent supports `session/resume`. Optional. Omitted or `null` both mean the agent does not advertise support. Supplying `\{\}` means the agent supports resuming sessions. ## SessionCloseCapabilities Capabilities for the `session/close` method. Supplying `\{\}` means the agent supports closing sessions. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) ## SessionConfigGroupId Unique identifier for a session configuration option value group. **Type:** `string` ## SessionConfigId Unique identifier for a session configuration option. **Type:** `string` ## SessionConfigOption A session configuration option selector and its current state. Single-value selector (dropdown). **Type:** Object **Properties:** SessionConfigValueId}> The currently selected value. SessionConfigSelectOptions}> The set of selectable options. The discriminator value. Must be `"select"`. ## SessionConfigOptionCategory Semantic category for a session configuration option. This is intended to help Clients distinguish broadly common selectors (e.g. model selector vs session mode selector vs thought/reasoning level) for UX purposes (keyboard shortcuts, icons, placement). It MUST NOT be required for correctness. Clients MUST handle missing or unknown categories gracefully. Category names beginning with `_` are free for custom use, like other ACP extension methods. Category names that do not begin with `_` are reserved for the ACP spec. **Type:** Union Session mode selector. Model selector. Model-related configuration parameter. Thought/reasoning level selector. Unknown / uncategorized selector. ## SessionConfigSelect A single-value selector (dropdown) session configuration option payload. **Type:** Object **Properties:** SessionConfigValueId}> The currently selected value. SessionConfigSelectOptions}> The set of selectable options. ## SessionConfigSelectGroup A group of possible values for a session configuration option. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) SessionConfigGroupId}> Unique identifier for this group. Human-readable label for this group. SessionConfigSelectOption[]}> The set of option values in this group. ## SessionConfigSelectOption A possible value for a session configuration option. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) Optional description for this option value. Human-readable label for this option value. SessionConfigValueId}> Unique identifier for this option value. ## SessionConfigSelectOptions Possible values for a session configuration option. **Type:** Union A flat list of options with no grouping. A list of options grouped under headers. ## SessionConfigValueId Unique identifier for a session configuration option value. **Type:** `string` ## SessionDeleteCapabilities Capabilities for the `session/delete` method. Supplying `\{\}` means the agent supports deleting sessions from `session/list`. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) ## SessionId A unique identifier for a conversation session between a client and agent. Sessions maintain their own context, conversation history, and state, allowing multiple independent interactions with the same agent. See protocol docs: [Session ID](https://agentclientprotocol.com/protocol/v1/session-setup#session-id) **Type:** `string` ## SessionInfo Information about a session returned by session/list **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) "string"[]}> Additional workspace roots reported for this session. Each path must be absolute. When present, this is the complete ordered additional-root list reported by the Agent. Omitted and empty values are equivalent: the response reports no additional roots. The working directory for this session. Must be an absolute path. SessionId}> Unique identifier for the session Human-readable title for the session ISO 8601 timestamp of last activity ## SessionInfoUpdate Update to session metadata. All fields are optional to support partial updates. Agents send this notification to update session information like title or custom metadata. This allows clients to display dynamic session names and track session state changes. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) Human-readable title for the session. Set to null to clear. ISO 8601 timestamp of last activity. Set to null to clear. ## SessionListCapabilities Capabilities for the `session/list` method. Supplying `\{\}` means the agent supports listing sessions. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) ## SessionMode A mode the agent can operate in. See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/v1/session-modes) **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) Optional human-readable details shown with this protocol object. SessionModeId}> Stable identifier used to refer to this protocol object in later messages. Human-readable name shown for this protocol object. ## SessionModeId Unique identifier for a Session Mode. **Type:** `string` ## SessionModeState The set of modes and the one currently active. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) SessionMode[]}> The set of modes that the Agent can operate in SessionModeId}> The current mode the Agent is in. ## SessionResumeCapabilities Capabilities for the `session/resume` method. Supplying `\{\}` means the agent supports resuming sessions. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) ## SessionUpdate Different types of updates that can be sent during session processing. These updates provide real-time feedback about the agent's progress. See protocol docs: [Agent Reports Output](https://agentclientprotocol.com/protocol/v1/prompt-turn#3-agent-reports-output) **Type:** Union A chunk of the user's message being streamed. The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) ContentBlock}> A single item of content MessageId | null}> A unique identifier for the message this chunk belongs to. All chunks belonging to the same message share the same `messageId`. A change in `messageId` indicates a new message has started. The discriminator value. Must be `"user_message_chunk"`. A chunk of the agent's response being streamed. The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) ContentBlock}> A single item of content MessageId | null}> A unique identifier for the message this chunk belongs to. All chunks belonging to the same message share the same `messageId`. A change in `messageId` indicates a new message has started. The discriminator value. Must be `"agent_message_chunk"`. A chunk of the agent's internal reasoning being streamed. The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) ContentBlock}> A single item of content MessageId | null}> A unique identifier for the message this chunk belongs to. All chunks belonging to the same message share the same `messageId`. A change in `messageId` indicates a new message has started. The discriminator value. Must be `"agent_thought_chunk"`. Notification that a new tool call has been initiated. The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) ToolCallContent[]}> Content produced by the tool call. ToolKind}> The category of tool being invoked. Helps clients choose appropriate icons and UI treatment. ToolCallLocation[]}> File locations affected by this tool call. Enables "follow-along" features in clients. Raw input parameters sent to the tool. Raw output returned by the tool. The discriminator value. Must be `"tool_call"`. ToolCallStatus}> Current execution status of the tool call. Human-readable title describing what the tool is doing. ToolCallId}> Unique identifier for this tool call within the session. Update on the status or results of a tool call. The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) ToolCallContent[] | null}> Replace the content collection. ToolKind | null}> Update the tool kind. ToolCallLocation[] | null}> Replace the locations collection. Update the raw input. Update the raw output. The discriminator value. Must be `"tool_call_update"`. ToolCallStatus | null}> Update the execution status. Update the human-readable title. ToolCallId}> The ID of the tool call being updated. The agent's execution plan for complex tasks. See protocol docs: [Agent Plan](https://agentclientprotocol.com/protocol/v1/agent-plan) The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) PlanEntry[]}> The list of tasks to be accomplished. When updating a plan, the agent must send a complete list of all entries with their current status. The client replaces the entire plan with each update. The discriminator value. Must be `"plan"`. Available commands are ready or have changed The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) AvailableCommand[]}> Commands the agent can execute The discriminator value. Must be `"available_commands_update"`. The current mode of the session has changed See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/v1/session-modes) The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) SessionModeId}> The ID of the current mode The discriminator value. Must be `"current_mode_update"`. Session configuration options have been updated. The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) SessionConfigOption[]}> The full set of configuration options and their current values. The discriminator value. Must be `"config_option_update"`. Session metadata has been updated (title, timestamps, custom metadata) The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) The discriminator value. Must be `"session_info_update"`. Human-readable title for the session. Set to null to clear. ISO 8601 timestamp of last activity. Set to null to clear. Context window and cost update for the session. The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) Cost | null}> Cumulative session cost (optional). The discriminator value. Must be `"usage_update"`. Total context window size in tokens. * Minimum: `0` Tokens currently in context. * Minimum: `0` ## StopReason Reasons why an agent stops processing a prompt turn. See protocol docs: [Stop Reasons](https://agentclientprotocol.com/protocol/v1/prompt-turn#stop-reasons) **Type:** Union The turn ended successfully. The turn ended because the agent reached the maximum number of tokens. The turn ended because the agent reached the maximum number of allowed agent requests between user turns. The turn ended because the agent refused to continue. The user prompt and everything that comes after it won't be included in the next prompt, so this should be reflected in the UI. The turn was cancelled by the client via `session/cancel`. This stop reason MUST be returned when the client sends a `session/cancel` notification, even if the cancellation causes exceptions in underlying operations. Agents should catch these exceptions and return this semantically meaningful response to confirm successful cancellation. ## Terminal Embed a terminal created with `terminal/create` by its id. The terminal must be added before calling `terminal/release`. See protocol docs: [Terminal](https://agentclientprotocol.com/protocol/v1/terminals) **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) TerminalId}> Identifier of the terminal instance to embed in the content stream. ## TerminalExitStatus Exit status of a terminal command. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) The process exit code (may be null if terminated by signal). * Minimum: `0` The signal that terminated the process (may be null if exited normally). ## TerminalId Typed identifier used for terminal values on the wire. **Type:** `string` ## TextContent Text provided to or from an LLM. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) Annotations | null}> Optional annotations that help clients decide how to display or route this content. Text payload carried by this content block. ## TextResourceContents Text-based resource contents. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) MIME type describing the encoded media payload. Text payload carried by this content block. URI associated with this resource or media payload. ## ToolCall Represents a tool call that the language model has requested. Tool calls are actions that the agent executes on behalf of the language model, such as reading files, executing code, or fetching data from external sources. See protocol docs: [Tool Calls](https://agentclientprotocol.com/protocol/v1/tool-calls) **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) ToolCallContent[]}> Content produced by the tool call. ToolKind}> The category of tool being invoked. Helps clients choose appropriate icons and UI treatment. ToolCallLocation[]}> File locations affected by this tool call. Enables "follow-along" features in clients. Raw input parameters sent to the tool. Raw output returned by the tool. ToolCallStatus}> Current execution status of the tool call. Human-readable title describing what the tool is doing. ToolCallId}> Unique identifier for this tool call within the session. ## ToolCallContent Content produced by a tool call. Tool calls can produce different types of content including standard content blocks (text, images) or file diffs. See protocol docs: [Content](https://agentclientprotocol.com/protocol/v1/tool-calls#content) **Type:** Union Standard content block (text, images, resources). The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) ContentBlock}> The actual content block. The discriminator value. Must be `"content"`. File modification shown as a diff. The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) The new content after modification. The original content (None for new files). The absolute file path being modified. The discriminator value. Must be `"diff"`. Embed a terminal created with `terminal/create` by its id. The terminal must be added before calling `terminal/release`. See protocol docs: [Terminal](https://agentclientprotocol.com/protocol/v1/terminals) The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) TerminalId}> Identifier of the terminal instance to embed in the content stream. The discriminator value. Must be `"terminal"`. ## ToolCallId Unique identifier for a tool call within a session. **Type:** `string` ## ToolCallLocation A file location being accessed or modified by a tool. Enables clients to implement "follow-along" features that track which files the agent is working with in real-time. See protocol docs: [Following the Agent](https://agentclientprotocol.com/protocol/v1/tool-calls#following-the-agent) **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) Optional line number within the file. * Minimum: `0` The absolute file path being accessed or modified. ## ToolCallStatus Execution status of a tool call. Tool calls progress through different statuses during their lifecycle. See protocol docs: [Status](https://agentclientprotocol.com/protocol/v1/tool-calls#status) **Type:** Union The tool call hasn't started running yet because the input is either streaming or we're awaiting approval. The tool call is currently running. The tool call completed successfully. The tool call failed with an error. ## ToolCallUpdate An update to an existing tool call. Used to report progress and results as tools execute. All fields except the tool call ID are optional - only changed fields need to be included. See protocol docs: [Updating](https://agentclientprotocol.com/protocol/v1/tool-calls#updating) **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) ToolCallContent[] | null}> Replace the content collection. ToolKind | null}> Update the tool kind. ToolCallLocation[] | null}> Replace the locations collection. Update the raw input. Update the raw output. ToolCallStatus | null}> Update the execution status. Update the human-readable title. ToolCallId}> The ID of the tool call being updated. ## ToolKind Categories of tools that can be invoked. Tool kinds help clients choose appropriate icons and optimize how they display tool execution progress. See protocol docs: [Creating](https://agentclientprotocol.com/protocol/v1/tool-calls#creating) **Type:** Union Reading files or data. Modifying files or content. Removing files or data. Moving or renaming files. Searching for information. Running commands or code. Internal reasoning or planning. Retrieving external data. Switching the current session mode. Other tool types (default). ## UnstructuredCommandInput All text that was typed after the command name is provided as input. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) A hint to display when the input hasn't been provided yet ## UsageUpdate Context window and cost update for a session. **Type:** Object **Properties:** The \_meta property is reserved by ACP to allow clients and agents to attach additional metadata to their interactions. Implementations MUST NOT make assumptions about values at these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v1/extensibility) Cost | null}> Cumulative session cost (optional). Total context window size in tokens. * Minimum: `0` Tokens currently in context. * Minimum: `0` # Session Config Options Source: https://agentclientprotocol.com/protocol/v1/session-config-options Flexible configuration selectors for agent sessions Agents can provide an arbitrary list of configuration options for a session, allowing Clients to offer users customizable selectors for things like models, modes, reasoning levels, and more. Session Config Options are the preferred way to expose session-level configuration. If an Agent provides `configOptions`, Clients **SHOULD** use them instead of the [`modes`](/protocol/v1/session-modes) field. Modes will be removed in a future version of the protocol. ## Initial State During [Session Setup](/protocol/v1/session-setup) the Agent **MAY** return a list of configuration options and their current values: ```json theme={null} { "jsonrpc": "2.0", "id": 1, "result": { "sessionId": "sess_abc123def456", "configOptions": [ { "id": "mode", "name": "Session Mode", "description": "Controls how the agent requests permission", "category": "mode", "type": "select", "currentValue": "ask", "options": [ { "value": "ask", "name": "Ask", "description": "Request permission before making any changes" }, { "value": "code", "name": "Code", "description": "Write and modify code with full tool access" } ] }, { "id": "model", "name": "Model", "category": "model", "type": "select", "currentValue": "model-1", "options": [ { "value": "model-1", "name": "Model 1", "description": "The fastest model" }, { "value": "model-2", "name": "Model 2", "description": "The most powerful model" } ] } ] } } ``` The list of configuration options available for this session. The order of this array represents the Agent's preferred priority. Clients **SHOULD** respect this ordering when displaying options. ### ConfigOption Unique identifier for this configuration option. Used when setting values. Human-readable label for the option Optional description providing more details about what this option controls Optional [semantic category](#option-categories) to help Clients provide consistent UX. The type of input control. Currently only `select` is supported. The currently selected value for this option The available values for this option ### ConfigOptionValue The value identifier used when setting this option Human-readable name to display Optional description of what this value does ## Option Categories Each config option **MAY** include a `category` field. Categories are semantic metadata intended to help Clients provide consistent UX, such as attaching keyboard shortcuts, choosing icons, or deciding placement. Categories are for UX purposes only and **MUST NOT** be required for correctness. Clients **MUST** handle missing or unknown categories gracefully. Category names beginning with `_` are free for custom use (e.g., `_my_custom_category`). Category names that do not begin with `_` are reserved for the ACP spec. | Category | Description | | --------------- | ------------------------------------------------------------------------ | | `mode` | Session mode selector | | `model` | Model selector | | `model_config` | Model-related parameter, such as context size or speed/quality trade-off | | `thought_level` | Thought/reasoning level selector | Clients **SHOULD** render `model_config` options near the `model` selector, such as in the same popover or panel. No capability negotiation is required for category values. When multiple options share the same category, Clients **SHOULD** use the array ordering to resolve ties, preferring earlier options in the list for prominent placement or keyboard shortcuts. ## Option Ordering The order of the `configOptions` array is significant. Agents **SHOULD** place higher-priority options first in the list. Clients **SHOULD**: * Display options in the order provided by the Agent * Use ordering to resolve ties when multiple options share the same category * If displaying a limited number of options, prefer those at the beginning of the list ## Default Values and Graceful Degradation Agents **MUST** always provide a default value for every configuration option. This ensures the Agent can operate correctly even if: * The Client doesn't support configuration options * The Client chooses not to display certain options * The Client receives an option type it doesn't recognize If a Client receives an option with an unrecognized `type`, it **SHOULD** ignore that option. The Agent will continue using its default value. ## Setting a Config Option The current value of a config option can be changed at any point during a session, whether the Agent is idle or generating a response. ### From the Client Clients can change a config option value by calling the `session/set_config_option` method: ```json theme={null} { "jsonrpc": "2.0", "id": 2, "method": "session/set_config_option", "params": { "sessionId": "sess_abc123def456", "configId": "mode", "value": "code" } } ``` The ID of the session The `id` of the configuration option to change The new value to set. Must be one of the values listed in the option's `options` array. The Agent **MUST** respond with the complete list of all configuration options and their current values: ```json theme={null} { "jsonrpc": "2.0", "id": 2, "result": { "configOptions": [ { "id": "mode", "name": "Session Mode", "type": "select", "currentValue": "code", "options": [...] }, { "id": "model", "name": "Model", "type": "select", "currentValue": "model-1", "options": [...] } ] } } ``` The response always contains the **complete** configuration state. This allows Agents to reflect dependent changes. For example, if changing the model affects available reasoning options, or if an option's available values change based on another selection. ### From the Agent The Agent can also change configuration options and notify the Client by sending a `config_option_update` session notification: ```json theme={null} { "jsonrpc": "2.0", "method": "session/update", "params": { "sessionId": "sess_abc123def456", "update": { "sessionUpdate": "config_option_update", "configOptions": [ { "id": "mode", "name": "Session Mode", "type": "select", "currentValue": "code", "options": [...] }, { "id": "model", "name": "Model", "type": "select", "currentValue": "model-2", "options": [...] } ] } } } ``` This notification also contains the complete configuration state. Common reasons an Agent might update configuration options include: * Switching modes after completing a planning phase * Falling back to a different model due to rate limits or errors * Adjusting available options based on context discovered during execution ## Relationship to Session Modes Session Config Options supersede the older [Session Modes](/protocol/v1/session-modes) API. However, during the transition period, Agents that provide mode-like configuration **SHOULD** send both: * `configOptions` with a `category: "mode"` option for Clients that support config options * `modes` for Clients that only support the older API If an Agent provides both `configOptions` and `modes` in the session response: * Clients that support config options **SHOULD** use `configOptions` exclusively and ignore `modes` * Clients that don't support config options **SHOULD** fall back to `modes` * Agents **SHOULD** keep both in sync to ensure consistent behavior regardless of which field the Client uses Learn about the Session Modes API # Session Delete Source: https://agentclientprotocol.com/protocol/v1/session-delete Removing sessions from session history The `session/delete` method allows Clients to remove sessions from an Agent's `session/list` results. This gives users a standard way to manage session history across ACP Clients and Agents. Before deleting sessions, Clients **MUST** first complete the [initialization](/protocol/v1/initialization) phase and verify the Agent supports this capability.
```mermaid theme={null} sequenceDiagram participant Client participant Agent Note over Agent,Client: Initialized Client->>Agent: session/list Agent-->>Client: session/list response (sessions) alt User deletes a session Client->>Agent: session/delete (sessionId) Agent-->>Client: session/delete response end Client->>Agent: session/list Agent-->>Client: session/list response (without deleted session) ```
## Checking Support Before attempting to delete a session, Clients **MUST** verify that the Agent supports this capability by checking the `sessionCapabilities.delete` field in the `initialize` response: ```json highlight={7-9} theme={null} { "jsonrpc": "2.0", "id": 0, "result": { "protocolVersion": 1, "agentCapabilities": { "sessionCapabilities": { "delete": {} } } } } ``` If `sessionCapabilities.delete` is omitted or `null`, the Agent does not support deleting sessions and Clients **MUST NOT** attempt to call `session/delete`. Supplying `{}` means the Agent supports the method. ## Deleting a Session Clients delete a session by calling `session/delete` with the session ID to remove from session history: ```json theme={null} { "jsonrpc": "2.0", "id": 3, "method": "session/delete", "params": { "sessionId": "sess_abc123def456" } } ``` Unique identifier for the session to delete. On success, the Agent returns an empty result: ```json theme={null} { "jsonrpc": "2.0", "id": 3, "result": {} } ``` ## Semantics * Deleted sessions no longer appear in future `session/list` results. * Deleting an already-deleted session, or a session that never existed, **SHOULD** succeed silently. * Agents may implement soft delete or hard delete. ACP only specifies the user-facing session-list behavior. * Behavior for `session/load` on a deleted session is implementation-defined. * Behavior for deleting an active session is implementation-defined. # Session List Source: https://agentclientprotocol.com/protocol/v1/session-list Discovering existing sessions The `session/list` method allows Clients to discover sessions known to an Agent. Clients can use this to display session history and switch between sessions. Agents can also push session metadata updates to Clients in real-time via the `session_info_update` notification, keeping session titles and metadata in sync without polling. Before listing sessions, Clients **MUST** first complete the [initialization](/protocol/v1/initialization) phase to verify the Agent supports this capability.
```mermaid theme={null} sequenceDiagram participant Client participant Agent Note over Agent,Client: Initialized Client->>Agent: session/list Agent-->>Client: session/list response (sessions) alt User selects a session Client->>Agent: session/load (sessionId) Note over Agent,Client: Replay conversation history... Agent-->>Client: session/load response end Note over Client,Agent: Ready for prompts ```
## Checking Support Before attempting to list sessions, Clients **MUST** verify that the Agent supports this capability by checking the `sessionCapabilities.list` field in the `initialize` response: ```json highlight={7-9} theme={null} { "jsonrpc": "2.0", "id": 0, "result": { "protocolVersion": 1, "agentCapabilities": { "sessionCapabilities": { "list": {} } } } } ``` If `sessionCapabilities.list` is not present, the Agent does not support listing sessions and Clients **MUST NOT** attempt to call `session/list`. Agents that also advertise `sessionCapabilities.additionalDirectories` may include `additionalDirectories` in returned `SessionInfo` objects to report additional workspace roots for listed sessions. If the Agent advertises the `sessionCapabilities.delete` capability, Clients can remove sessions from future `session/list` results with [`session/delete`](/protocol/v1/session-delete). ## Listing Sessions Clients discover existing sessions by calling the `session/list` method with optional filtering and pagination parameters: ```json theme={null} { "jsonrpc": "2.0", "id": 2, "method": "session/list", "params": { "cwd": "/home/user/project", "cursor": "eyJwYWdlIjogMn0=" } } ``` All parameters are optional. A request with an empty `params` object returns the first page of sessions. Filter sessions by working directory. Must be an absolute path. Only sessions with a matching `cwd` are returned. Opaque cursor token from a previous response's `nextCursor` field for cursor-based pagination. See [Pagination](#pagination). The Agent **MUST** respond with a list of sessions and optional pagination metadata: ```json theme={null} { "jsonrpc": "2.0", "id": 2, "result": { "sessions": [ { "sessionId": "sess_abc123def456", "cwd": "/home/user/project", "title": "Implement session list API", "updatedAt": "2025-10-29T14:22:15Z", "_meta": { "messageCount": 12, "hasErrors": false } }, { "sessionId": "sess_xyz789ghi012", "cwd": "/home/user/another-project", "title": "Debug authentication flow", "updatedAt": "2025-10-28T16:45:30Z" }, { "sessionId": "sess_uvw345rst678", "cwd": "/home/user/project", "updatedAt": "2025-10-27T15:30:00Z" } ], "nextCursor": "eyJwYWdlIjogM30=" } } ``` Array of session information objects. Unique identifier for the session. Working directory for the session. Always an absolute path. If the Agent advertises `sessionCapabilities.additionalDirectories`, it MAY include this field to report the complete ordered additional-root list associated with the listed session. Omitted and empty values are equivalent: this `SessionInfo` response reports no additional roots. Clients MUST NOT merge this field with prior values or infer additional roots from agent-specific state. Human-readable title for the session. May be auto-generated from the first prompt. ISO 8601 timestamp of the last activity in the session. Agent-specific metadata. See [Extensibility](/protocol/v1/extensibility). Opaque cursor token. If present, pass this in the next request's `cursor` parameter to fetch the next page. If absent, there are no more results. When no sessions match the criteria, the Agent **MUST** return an empty `sessions` array. ## Pagination `session/list` uses cursor-based pagination. The request includes an optional `cursor`, and the response includes `nextCursor` when more results are available. * Clients **MUST** treat a missing `nextCursor` as the end of results * Clients **MUST** treat cursors as opaque tokens — do not parse, modify, or persist them * Agents **SHOULD** return an error if the cursor is invalid * Agents **SHOULD** enforce reasonable page sizes internally ## Updating Session Metadata Agents can update session metadata in real-time by sending a `session_info_update` notification via `session/update`. This follows the same pattern as other session notifications like [`available_commands_update`](/protocol/v1/slash-commands) and [`current_mode_update`](/protocol/v1/session-modes). ```json theme={null} { "jsonrpc": "2.0", "method": "session/update", "params": { "sessionId": "sess_abc123def456", "update": { "sessionUpdate": "session_info_update", "title": "Implement user authentication", "_meta": { "tags": ["feature", "auth"], "priority": "high" } } } } ``` All fields are optional. Only include fields that have changed — omitted fields are left unchanged. Human-readable title for the session. Set to `null` to clear. ISO 8601 timestamp of last activity. Set to `null` to clear. Agent-specific metadata. See [Extensibility](/protocol/v1/extensibility). The `sessionId`, `cwd`, and `additionalDirectories` fields are **not** included in the update. `sessionId` is already in the notification's `params`, `cwd` is immutable after session setup, and ACP does not currently define mid-session mutation for `additionalDirectories`. Agents typically send this notification after the first meaningful exchange to auto-generate a title. ## Interaction with Other Session Methods `session/list` is a discovery mechanism only — it does **not** restore or modify sessions: 1. Client calls `session/list` to discover available sessions 2. User selects a session from the list 3. Client calls [`session/load`](/protocol/v1/session-setup#loading-sessions) with the chosen `sessionId` to resume the conversation # Session Modes Source: https://agentclientprotocol.com/protocol/v1/session-modes Switch between different agent operating modes You can now use [Session Config Options](/protocol/v1/session-config-options). Dedicated session mode methods will be removed in a future version of the protocol. Until then, you can offer both to clients for backwards compatibility. Agents can provide a set of modes they can operate in. Modes often affect the system prompts used, the availability of tools, and whether they request permission before running. ## Initial state During [Session Setup](/protocol/v1/session-setup) the Agent **MAY** return a list of modes it can operate in and the currently active mode: ```json theme={null} { "jsonrpc": "2.0", "id": 1, "result": { "sessionId": "sess_abc123def456", "modes": { "currentModeId": "ask", "availableModes": [ { "id": "ask", "name": "Ask", "description": "Request permission before making any changes" }, { "id": "architect", "name": "Architect", "description": "Design and plan software systems without implementation" }, { "id": "code", "name": "Code", "description": "Write and modify code with full tool access" } ] } } } ``` The current mode state for the session ### SessionModeState The ID of the mode that is currently active The set of modes that the Agent can operate in ### SessionMode Unique identifier for this mode Human-readable name of the mode Optional description providing more details about what this mode does ## Setting the current mode The current mode can be changed at any point during a session, whether the Agent is idle or generating a response. ### From the Client Typically, Clients display the available modes to the user and allow them to change the current one, which they can do by calling the [`session/set_mode`](/protocol/v1/schema#session%2Fset-mode) method. ```json theme={null} { "jsonrpc": "2.0", "id": 2, "method": "session/set_mode", "params": { "sessionId": "sess_abc123def456", "modeId": "code" } } ``` The ID of the session to set the mode for The ID of the mode to switch to. Must be one of the modes listed in `availableModes` ### From the Agent The Agent can also change its own mode and let the Client know by sending the `current_mode_update` session notification: ```json theme={null} { "jsonrpc": "2.0", "method": "session/update", "params": { "sessionId": "sess_abc123def456", "update": { "sessionUpdate": "current_mode_update", "modeId": "code" } } } ``` #### Exiting plan modes A common case where an Agent might switch modes is from within a special "exit mode" tool that can be provided to the language model during plan/architect modes. The language model can call this tool when it determines it's ready to start implementing a solution. This "switch mode" tool will usually request permission before running, which it can do just like any other tool: ```json theme={null} { "jsonrpc": "2.0", "id": 3, "method": "session/request_permission", "params": { "sessionId": "sess_abc123def456", "toolCall": { "toolCallId": "call_switch_mode_001", "title": "Ready for implementation", "kind": "switch_mode", "status": "pending", "content": [ { "type": "text", "text": "## Implementation Plan..." } ] }, "options": [ { "optionId": "code", "name": "Yes, and auto-accept all actions", "kind": "allow_always" }, { "optionId": "ask", "name": "Yes, and manually accept actions", "kind": "allow_once" }, { "optionId": "reject", "name": "No, stay in architect mode", "kind": "reject_once" } ] } } ``` When an option is chosen, the tool runs, setting the mode and sending the `current_mode_update` notification mentioned above. Learn more about permission requests # Session Setup Source: https://agentclientprotocol.com/protocol/v1/session-setup Creating and loading sessions Sessions represent a specific conversation or thread between the [Client](/protocol/v1/overview#client) and [Agent](/protocol/v1/overview#agent). Each session maintains its own context, conversation history, and state, allowing multiple independent interactions with the same Agent. Before creating a session, Clients **MUST** first complete the [initialization](/protocol/v1/initialization) phase to establish protocol compatibility and capabilities.
```mermaid theme={null} sequenceDiagram participant Client participant Agent Note over Agent,Client: Initialized alt Client->>Agent: session/new Note over Agent: Create session context Note over Agent: Connect to MCP servers Agent-->>Client: session/new response (sessionId) else Client->>Agent: session/load (sessionId) Note over Agent: Restore session context Note over Agent: Connect to MCP servers Note over Agent,Client: Replay conversation history... Agent->>Client: session/update Agent->>Client: session/update Note over Agent,Client: All content streamed Agent-->>Client: session/load response else Client->>Agent: session/resume (sessionId) Note over Agent: Restore session context Note over Agent: Connect to MCP servers Agent-->>Client: session/resume response end Note over Client,Agent: Ready for prompts ```
## Creating a Session Clients create a new session by calling the `session/new` method with: * The [working directory](#working-directory) for the session * A list of [MCP servers](#mcp-servers) the Agent should connect to ```json theme={null} { "jsonrpc": "2.0", "id": 1, "method": "session/new", "params": { "cwd": "/home/user/project", "mcpServers": [ { "name": "filesystem", "command": "/path/to/mcp-server", "args": ["--stdio"], "env": [] } ] } } ``` The Agent **MUST** respond with a unique [Session ID](#session-id) that identifies this conversation: ```json theme={null} { "jsonrpc": "2.0", "id": 1, "result": { "sessionId": "sess_abc123def456" } } ``` ## Loading Sessions Agents that support the `loadSession` capability allow Clients to resume previous conversations. This feature enables persistence across restarts and sharing sessions between different Client instances. ### Checking Support Before attempting to load a session, Clients **MUST** verify that the Agent supports this capability by checking the `loadSession` field in the `initialize` response: ```json highlight={7} theme={null} { "jsonrpc": "2.0", "id": 0, "result": { "protocolVersion": 1, "agentCapabilities": { "loadSession": true } } } ``` If `loadSession` is `false` or not present, the Agent does not support loading sessions and Clients **MUST NOT** attempt to call `session/load`. ### Loading a Session To load an existing session, Clients **MUST** call the `session/load` method with: * The [Session ID](#session-id) to resume * [MCP servers](#mcp-servers) to connect to * The [working directory](#working-directory) ```json theme={null} { "jsonrpc": "2.0", "id": 1, "method": "session/load", "params": { "sessionId": "sess_789xyz", "cwd": "/home/user/project", "mcpServers": [ { "name": "filesystem", "command": "/path/to/mcp-server", "args": ["--mode", "filesystem"], "env": [] } ] } } ``` The Agent **MUST** replay the entire conversation to the Client in the form of `session/update` notifications (like `session/prompt`). For example, a user message from the conversation history: ```json theme={null} { "jsonrpc": "2.0", "method": "session/update", "params": { "sessionId": "sess_789xyz", "update": { "sessionUpdate": "user_message_chunk", "messageId": "msg_user_8f7a1", "content": { "type": "text", "text": "What's the capital of France?" } } } } ``` Followed by the agent's response: ```json theme={null} { "jsonrpc": "2.0", "method": "session/update", "params": { "sessionId": "sess_789xyz", "update": { "sessionUpdate": "agent_message_chunk", "messageId": "msg_agent_c42b9", "content": { "type": "text", "text": "The capital of France is Paris." } } } } ``` If the Agent provides message IDs during replay, each `messageId` is an opaque, unique identifier for the replayed message. When **all** the conversation entries have been streamed to the Client, the Agent **MUST** respond to the original `session/load` request. ```json theme={null} { "jsonrpc": "2.0", "id": 1, "result": null } ``` The Client can then continue sending prompts as if the session was never interrupted. ## Resuming Sessions Agents that advertise `sessionCapabilities.resume` allow Clients to reconnect to an existing session without replaying the conversation history. ### Checking Support Before attempting to resume a session, Clients **MUST** verify that the Agent supports this capability by checking for the `sessionCapabilities.resume` field in the `initialize` response: ```json highlight={7-9} theme={null} { "jsonrpc": "2.0", "id": 0, "result": { "protocolVersion": 1, "agentCapabilities": { "sessionCapabilities": { "resume": {} } } } } ``` If `sessionCapabilities.resume` is not present, the Agent does not support resuming sessions and Clients **MUST NOT** attempt to call `session/resume`. ### Resuming a Session To resume an existing session without replaying prior messages, Clients **MUST** call the `session/resume` method with: * The [Session ID](#session-id) to resume * [MCP servers](#mcp-servers) to connect to * The [working directory](#working-directory) ```json theme={null} { "jsonrpc": "2.0", "id": 2, "method": "session/resume", "params": { "sessionId": "sess_789xyz", "cwd": "/home/user/project", "mcpServers": [ { "name": "filesystem", "command": "/path/to/mcp-server", "args": ["--mode", "filesystem"], "env": [] } ] } } ``` Unlike `session/load`, the Agent **MUST NOT** replay the conversation history via `session/update` notifications before responding. Instead, it restores the session context, reconnects to the requested MCP servers, and returns once the session is ready to continue. ```json theme={null} { "jsonrpc": "2.0", "id": 2, "result": {} } ``` The response **MAY** also include initial mode, model, or session configuration state when those features are supported by the Agent. ## Closing Active Sessions Agents that advertise `sessionCapabilities.close` allow Clients to tell the Agent to cancel any ongoing work for a session and free any resources associated with that active session. ### Checking Support Before attempting to close a session, Clients **MUST** verify that the Agent supports this capability by checking the `sessionCapabilities.close` field in the `initialize` response: ```json highlight={7-9} theme={null} { "jsonrpc": "2.0", "id": 0, "result": { "protocolVersion": 1, "agentCapabilities": { "sessionCapabilities": { "close": {} } } } } ``` If `sessionCapabilities.close` is not present, the Agent does not support closing sessions and Clients **MUST NOT** attempt to call `session/close`. ### Closing a Session To close an active session, Clients **MUST** call the `session/close` method with the session ID: ```json theme={null} { "jsonrpc": "2.0", "id": 2, "method": "session/close", "params": { "sessionId": "sess_789xyz" } } ``` The ID of the active session to close. The Agent **MUST** cancel any ongoing work for that session as if [`session/cancel`](/protocol/v1/prompt-turn#cancellation) had been called, then free the resources associated with the session. On success, the Agent responds with an empty result object: ```json theme={null} { "jsonrpc": "2.0", "id": 2, "result": {} } ``` Agents MAY return an error if the session does not exist or is not currently active. ## Additional Workspace Roots Agents that advertise `sessionCapabilities.additionalDirectories` allow Clients to include `additionalDirectories` on supported session lifecycle requests to expand the session's effective filesystem root set. Supported stable lifecycle requests include `session/new`, `session/load`, and `session/resume`. ```json theme={null} { "jsonrpc": "2.0", "id": 2, "method": "session/load", "params": { "sessionId": "sess_789xyz", "cwd": "/home/user/project", "additionalDirectories": [ "/home/user/shared-lib", "/home/user/product-docs" ], "mcpServers": [] } } ``` When present, `additionalDirectories` has the following behavior: * `cwd` remains the primary working directory and the base for relative paths * each `additionalDirectories` entry **MUST** be an absolute path * omitting the field or providing an empty array activates no additional roots for the resulting session * on `session/load` and `session/resume`, Clients must send the full intended additional-root list again; that list may differ from any previous or reported list as long as the request `cwd` matches the session's `cwd`, and omitting the field or providing an empty array does not restore stored roots implicitly Clients **MUST** only send `additionalDirectories` when the Agent advertises `sessionCapabilities.additionalDirectories`. ## Session ID The session ID returned by `session/new` is a unique identifier for the conversation context. Clients use this ID to: * Send prompt requests via `session/prompt` * Cancel ongoing operations via `session/cancel` * Load previous sessions via `session/load` (if the Agent supports the `loadSession` capability) * Resume previous sessions via `session/resume` (if the Agent supports the `sessionCapabilities.resume` capability) * Close active sessions via `session/close` (if the Agent supports the `sessionCapabilities.close` capability) ## Working Directory The `cwd` (current working directory) parameter establishes the primary file system context for the session. This directory: * **MUST** be an absolute path * **MUST** be used for the session regardless of where the Agent subprocess was spawned * **MUST** remain the base for relative-path resolution * **MUST** be part of the session's effective root set When `sessionCapabilities.additionalDirectories` is in use, the session's effective root set is `[cwd, ...additionalDirectories]`. This root set **SHOULD** serve as a boundary for tool operations on the file system. ## MCP Servers The [Model Context Protocol (MCP)](https://modelcontextprotocol.io) allows Agents to access external tools and data sources. When creating a session, Clients **MAY** include connection details for MCP servers that the Agent should connect to. MCP servers can be connected to using different transports. All Agents **MUST** support the stdio transport, while HTTP and SSE transports are optional capabilities that can be checked during initialization. While they are not required to by the spec, new Agents **SHOULD** support the HTTP transport to ensure compatibility with modern MCP servers. ### Transport Types #### Stdio Transport All Agents **MUST** support connecting to MCP servers via stdio (standard input/output). This is the default transport mechanism. A human-readable identifier for the server The absolute path to the MCP server executable Command-line arguments to pass to the server Environment variables to set when launching the server The name of the environment variable. The value of the environment variable. Example stdio transport configuration: ```json theme={null} { "name": "filesystem", "command": "/path/to/mcp-server", "args": ["--stdio"], "env": [ { "name": "API_KEY", "value": "secret123" } ] } ``` #### HTTP Transport When the Agent supports `mcpCapabilities.http`, Clients can specify MCP servers configurations using the HTTP transport. Must be `"http"` to indicate HTTP transport A human-readable identifier for the server The URL of the MCP server HTTP headers to include in requests to the server The name of the HTTP header. The value to set for the HTTP header. Example HTTP transport configuration: ```json theme={null} { "type": "http", "name": "api-server", "url": "https://api.example.com/mcp", "headers": [ { "name": "Authorization", "value": "Bearer token123" }, { "name": "Content-Type", "value": "application/json" } ] } ``` #### SSE Transport When the Agent supports `mcpCapabilities.sse`, Clients can specify MCP servers configurations using the SSE transport. This transport was deprecated by the MCP spec. Must be `"sse"` to indicate SSE transport A human-readable identifier for the server The URL of the SSE endpoint HTTP headers to include when establishing the SSE connection The name of the HTTP header. The value to set for the HTTP header. Example SSE transport configuration: ```json theme={null} { "type": "sse", "name": "event-stream", "url": "https://events.example.com/mcp", "headers": [ { "name": "X-API-Key", "value": "apikey456" } ] } ``` ### Checking Transport Support Before using HTTP or SSE transports, Clients **MUST** verify the Agent's capabilities during initialization: ```json highlight={7-10} theme={null} { "jsonrpc": "2.0", "id": 0, "result": { "protocolVersion": 1, "agentCapabilities": { "mcpCapabilities": { "http": true, "sse": true } } } } ``` If `mcpCapabilities.http` is `false` or not present, the Agent does not support HTTP transport. If `mcpCapabilities.sse` is `false` or not present, the Agent does not support SSE transport. Agents **SHOULD** connect to all MCP servers specified by the Client. Clients **MAY** use this ability to provide tools directly to the underlying language model by including their own MCP server. # Slash Commands Source: https://agentclientprotocol.com/protocol/v1/slash-commands Advertise available slash commands to clients Agents can advertise a set of slash commands that users can invoke. These commands provide quick access to specific agent capabilities and workflows. Commands are run as part of regular [prompt](/protocol/v1/prompt-turn) requests where the Client includes the command text in the prompt. ## Advertising commands After creating a session, the Agent **MAY** send a list of available commands via the `available_commands_update` session notification: ```json theme={null} { "jsonrpc": "2.0", "method": "session/update", "params": { "sessionId": "sess_abc123def456", "update": { "sessionUpdate": "available_commands_update", "availableCommands": [ { "name": "web", "description": "Search the web for information", "input": { "hint": "query to search for" } }, { "name": "test", "description": "Run tests for the current project" }, { "name": "plan", "description": "Create a detailed implementation plan", "input": { "hint": "description of what to plan" } } ] } } } ``` The list of commands available in this session ### AvailableCommand The command name (e.g., "web", "test", "plan") Human-readable description of what the command does Optional input specification for the command ### AvailableCommandInput Currently supports unstructured text input: A hint to display when the input hasn't been provided yet ## Dynamic updates The Agent can update the list of available commands at any time during a session by sending another `available_commands_update` notification. This allows commands to be added based on context, removed when no longer relevant, or modified with updated descriptions. ## Running commands Commands are included as regular user messages in prompt requests: ```json theme={null} { "jsonrpc": "2.0", "id": 3, "method": "session/prompt", "params": { "sessionId": "sess_abc123def456", "prompt": [ { "type": "text", "text": "/web agent client protocol" } ] } } ``` The Agent recognizes the command prefix and processes it accordingly. Commands may be accompanied by any other user message content types (images, audio, etc.) in the same prompt array. # Terminals Source: https://agentclientprotocol.com/protocol/v1/terminals Executing and managing terminal commands The terminal methods allow Agents to execute shell commands within the Client's environment. These methods enable Agents to run build processes, execute scripts, and interact with command-line tools while providing real-time output streaming and process control. ## Checking Support Before attempting to use terminal methods, Agents **MUST** verify that the Client supports this capability by checking the [Client Capabilities](/protocol/v1/initialization#client-capabilities) field in the `initialize` response: ```json highlight={7} theme={null} { "jsonrpc": "2.0", "id": 0, "result": { "protocolVersion": 1, "clientCapabilities": { "terminal": true } } } ``` If `terminal` is `false` or not present, the Agent **MUST NOT** attempt to call any terminal methods. ## Executing Commands The `terminal/create` method starts a command in a new terminal: ```json theme={null} { "jsonrpc": "2.0", "id": 5, "method": "terminal/create", "params": { "sessionId": "sess_abc123def456", "command": "npm", "args": ["test", "--coverage"], "env": [ { "name": "NODE_ENV", "value": "test" } ], "cwd": "/home/user/project", "outputByteLimit": 1048576 } } ``` The [Session ID](/protocol/v1/session-setup#session-id) for this request The command to execute Array of command arguments Environment variables for the command. Each variable has: * `name`: The environment variable name * `value`: The environment variable value Working directory for the command. Must be an absolute path. Maximum number of output bytes to retain. Once exceeded, earlier output is truncated to stay within this limit. When the limit is exceeded, the Client truncates from the beginning of the output to stay within the limit. The Client **MUST** ensure truncation happens at a character boundary to maintain valid string output, even if this means the retained output is slightly less than the specified limit. The Client returns a Terminal ID immediately without waiting for completion: ```json theme={null} { "jsonrpc": "2.0", "id": 5, "result": { "terminalId": "term_xyz789" } } ``` This allows the command to run in the background while the Agent performs other operations. After creating the terminal, the Agent can use the `terminal/wait_for_exit` method to wait for the command to complete. The Agent **MUST** release the terminal using `terminal/release` when it's no longer needed. ## Embedding in Tool Calls Terminals can be embedded directly in [tool calls](/protocol/v1/tool-calls) to provide real-time output to users: ```json theme={null} { "jsonrpc": "2.0", "method": "session/update", "params": { "sessionId": "sess_abc123def456", "update": { "sessionUpdate": "tool_call", "toolCallId": "call_002", "title": "Running tests", "kind": "execute", "status": "in_progress", "content": [ { "type": "terminal", "terminalId": "term_xyz789" } ] } } } ``` When a terminal is embedded in a tool call, the Client displays live output as it's generated and continues to display it even after the terminal is released. ## Getting Output The `terminal/output` method retrieves the current terminal output without waiting for the command to complete: ```json theme={null} { "jsonrpc": "2.0", "id": 6, "method": "terminal/output", "params": { "sessionId": "sess_abc123def456", "terminalId": "term_xyz789" } } ``` The Client responds with the current output and exit status (if the command has finished): ```json theme={null} { "jsonrpc": "2.0", "id": 6, "result": { "output": "Running tests...\n✓ All tests passed (42 total)\n", "truncated": false, "exitStatus": { "exitCode": 0, "signal": null } } } ``` The terminal output captured so far Whether the output was truncated due to byte limits Present only if the command has exited. Contains: * `exitCode`: The process exit code (may be null) * `signal`: The signal that terminated the process (may be null) ## Waiting for Exit The `terminal/wait_for_exit` method returns once the command completes: ```json theme={null} { "jsonrpc": "2.0", "id": 7, "method": "terminal/wait_for_exit", "params": { "sessionId": "sess_abc123def456", "terminalId": "term_xyz789" } } ``` The Client responds once the command exits: ```json theme={null} { "jsonrpc": "2.0", "id": 7, "result": { "exitCode": 0, "signal": null } } ``` The process exit code (may be null if terminated by signal) The signal that terminated the process (may be null if exited normally) ## Killing Commands The `terminal/kill` method terminates a command without releasing the terminal: ```json theme={null} { "jsonrpc": "2.0", "id": 8, "method": "terminal/kill", "params": { "sessionId": "sess_abc123def456", "terminalId": "term_xyz789" } } ``` After killing a command, the terminal remains valid and can be used with: * `terminal/output` to get the final output * `terminal/wait_for_exit` to get the exit status The Agent **MUST** still call `terminal/release` when it's done using it. ### Building a Timeout Agents can implement command timeouts by combining terminal methods: 1. Create a terminal with `terminal/create` 2. Start a timer for the desired timeout duration 3. Concurrently wait for either the timer to expire or `terminal/wait_for_exit` to return 4. If the timer expires first: * Call `terminal/kill` to terminate the command * Call `terminal/output` to retrieve any final output * Include the output in the response to the model 5. Call `terminal/release` when done ## Releasing Terminals The `terminal/release` kills the command if still running and releases all resources: ```json theme={null} { "jsonrpc": "2.0", "id": 9, "method": "terminal/release", "params": { "sessionId": "sess_abc123def456", "terminalId": "term_xyz789" } } ``` After release the terminal ID becomes invalid for all other `terminal/*` methods. If the terminal was added to a tool call, the client **SHOULD** continue to display its output after release. # Tool Calls Source: https://agentclientprotocol.com/protocol/v1/tool-calls How Agents report tool call execution Tool calls represent actions that language models request Agents to perform during a [prompt turn](/protocol/v1/prompt-turn). When an LLM determines it needs to interact with external systems—like reading files, running code, or fetching data—it generates tool calls that the Agent executes on its behalf. Agents report tool calls through [`session/update`](/protocol/v1/prompt-turn#3-agent-reports-output) notifications, allowing Clients to display real-time progress and results to users. While Agents handle the actual execution, they may leverage Client capabilities like [permission requests](#requesting-permission) or [file system access](/protocol/v1/file-system) to provide a richer, more integrated experience. ## Creating When the language model requests a tool invocation, the Agent **SHOULD** report it to the Client: ```json theme={null} { "jsonrpc": "2.0", "method": "session/update", "params": { "sessionId": "sess_abc123def456", "update": { "sessionUpdate": "tool_call", "toolCallId": "call_001", "title": "Reading configuration file", "kind": "read", "status": "pending" } } } ``` A unique identifier for this tool call within the session A human-readable title describing what the tool is doing The category of tool being invoked. * `read` - Reading files or data - `edit` - Modifying files or content - `delete` - Removing files or data - `move` - Moving or renaming files - `search` - Searching for information - `execute` - Running commands or code - `think` - Internal reasoning or planning - `fetch` - Retrieving external data * `other` - Other tool types (default) Tool kinds help Clients choose appropriate icons and optimize how they display tool execution progress. The current [execution status](#status) (defaults to `pending`) [Content produced](#content) by the tool call [File locations](#following-the-agent) affected by this tool call The raw input parameters sent to the tool The raw output returned by the tool ## Updating As tools execute, Agents send updates to report progress and results. Updates use the `session/update` notification with `tool_call_update`: ```json theme={null} { "jsonrpc": "2.0", "method": "session/update", "params": { "sessionId": "sess_abc123def456", "update": { "sessionUpdate": "tool_call_update", "toolCallId": "call_001", "status": "in_progress", "content": [ { "type": "content", "content": { "type": "text", "text": "Found 3 configuration files..." } } ] } } } ``` All fields except `toolCallId` are optional in updates. Only the fields being changed need to be included. ## Requesting Permission The Agent **MAY** request permission from the user before executing a tool call by calling the `session/request_permission` method: ```json theme={null} { "jsonrpc": "2.0", "id": 5, "method": "session/request_permission", "params": { "sessionId": "sess_abc123def456", "toolCall": { "toolCallId": "call_001" }, "options": [ { "optionId": "allow-once", "name": "Allow once", "kind": "allow_once" }, { "optionId": "reject-once", "name": "Reject", "kind": "reject_once" } ] } } ``` The session ID for this request The tool call update containing details about the operation Available [permission options](#permission-options) for the user to choose from The Client responds with the user's decision: ```json theme={null} { "jsonrpc": "2.0", "id": 5, "result": { "outcome": { "outcome": "selected", "optionId": "allow-once" } } } ``` Clients **MAY** automatically allow or reject permission requests according to the user settings. If the current prompt turn gets [cancelled](/protocol/v1/prompt-turn#cancellation), the Client **MUST** respond with the `"cancelled"` outcome: ```json theme={null} { "jsonrpc": "2.0", "id": 5, "result": { "outcome": { "outcome": "cancelled" } } } ``` The user's decision, either: - `cancelled` - The [prompt turn was cancelled](/protocol/v1/prompt-turn#cancellation) - `selected` with an `optionId` - The ID of the selected permission option ### Permission Options Each permission option provided to the Client contains: Unique identifier for this option Human-readable label to display to the user A hint to help Clients choose appropriate icons and UI treatment for each option. * `allow_once` - Allow this operation only this time * `allow_always` - Allow this operation and remember the choice * `reject_once` - Reject this operation only this time * `reject_always` - Reject this operation and remember the choice ## Status Tool calls progress through different statuses during their lifecycle: The tool call hasn't started running yet because the input is either streaming or awaiting approval The tool call is currently running The tool call completed successfully The tool call failed with an error ## Content Tool calls can produce different types of content: ### Regular Content Standard [content blocks](/protocol/v1/content) like text, images, or resources: ```json theme={null} { "type": "content", "content": { "type": "text", "text": "Analysis complete. Found 3 issues." } } ``` ### Diffs File modifications shown as diffs: ```json theme={null} { "type": "diff", "path": "/home/user/project/src/config.json", "oldText": "{\n \"debug\": false\n}", "newText": "{\n \"debug\": true\n}" } ``` The absolute file path being modified The original content (null for new files) The new content after modification ### Terminals Live terminal output from command execution: ```json theme={null} { "type": "terminal", "terminalId": "term_xyz789" } ``` The ID of a terminal created with `terminal/create` When a terminal is embedded in a tool call, the Client displays live output as it's generated and continues to display it even after the terminal is released. Learn more about Terminals ## Following the Agent Tool calls can report file locations they're working with, enabling Clients to implement "follow-along" features that track which files the Agent is accessing or modifying in real-time. ```json theme={null} { "path": "/home/user/project/src/main.py", "line": 42 } ``` The absolute file path being accessed or modified Optional line number within the file # Transports Source: https://agentclientprotocol.com/protocol/v1/transports Mechanisms for agents and clients to communicate with each other ACP uses JSON-RPC to encode messages. JSON-RPC messages **MUST** be UTF-8 encoded. The protocol currently defines the following transport mechanisms for agent-client communication: 1. [stdio](#stdio), communication over standard in and standard out 2. *[Streamable HTTP](#streamable-http) (draft proposal in progress)* Agents and clients **SHOULD** support stdio whenever possible. It is also possible for agents and clients to implement [custom transports](#custom-transports). ## stdio In the **stdio** transport: * The client launches the agent as a subprocess. * The agent reads JSON-RPC messages from its standard input (`stdin`) and sends messages to its standard output (`stdout`). * Messages are individual JSON-RPC requests, notifications, or responses. * Messages are delimited by newlines (`\n`), and **MUST NOT** contain embedded newlines. * The agent **MAY** write UTF-8 strings to its standard error (`stderr`) for logging purposes. Clients **MAY** capture, forward, or ignore this logging. * The agent **MUST NOT** write anything to its `stdout` that is not a valid ACP message. * The client **MUST NOT** write anything to the agent's `stdin` that is not a valid ACP message. ```mermaid theme={null} sequenceDiagram participant Client participant Agent Process Client->>+Agent Process: Launch subprocess loop Message Exchange Client->>Agent Process: Write to stdin Agent Process->>Client: Write to stdout Agent Process--)Client: Optional logs on stderr end Client->>Agent Process: Close stdin, terminate subprocess deactivate Agent Process ``` ## *Streamable HTTP* *In discussion, draft proposal in progress.* ## Custom Transports Agents and clients **MAY** implement additional custom transport mechanisms to suit their specific needs. The protocol is transport-agnostic and can be implemented over any communication channel that supports bidirectional message exchange. Implementers who choose to support custom transports **MUST** ensure they preserve the JSON-RPC message format and lifecycle requirements defined by ACP. Custom transports **SHOULD** document their specific connection establishment and message exchange patterns to aid interoperability. # Publications Source: https://agentclientprotocol.com/publications ACP publications, talks, presentations, and videos ## Talks November 26, 2025 · Ben Brandt January 28, 2026 · Anna Zhdan January 29, 2026 · Sergey Ignatov February 11, 2026 · Anna Zhdan February 25, 2026 · Anna Zhdan March 11, 2026 · Ben Brandt, Sergey Ignatov March 21, 2026 · Jun Han April 11, 2026 · Jun Han June 2, 2026 · Sergey Ignatov ## Videos Conversation with Ben Brandt, Sergey Ignatov, and Jan-Niklas Wortmann Jun Han # Requests for Dialog (RFDs) Source: https://agentclientprotocol.com/rfds/about Our process for introducing changes to the protocol A "Request for Dialog" (RFD) is ACP's version of the RFC process. RFDs are the primary mechanism for proposing new features, collecting community input on an issue, and documenting design decisions. ## When to write an RFD You should consider writing an RFD if you intend to make a "substantial" change to ACP or its documentation. What constitutes a "substantial" change is evolving based on community norms and varies depending on what part of the ecosystem you are proposing to change. Some changes do not require an RFD: * Rephrasing, reorganizing or refactoring * Addition or removal of warnings * Additions that strictly improve objective, numerical quality criteria (speedup, better browser support) * Fixing objectively incorrect behavior ## The RFD Process ### 1. Propose by opening a PR [Fork the repo](https://github.com/agentclientprotocol/agent-client-protocol) and copy `docs/rfds/TEMPLATE.md` to `docs/rfds/my-feature.md` (using kebab-case naming). The RFD can start minimal - just an elevator pitch and status quo are enough to begin dialog. Pull requests become the discussion forum where ideas get refined through collaborative iteration. ### 2. Merge to "Draft" when championed RFD proposals are merged into the "Draft" section if a core team member decides to champion them. The champion becomes the point-of-contact and will work with authors to make it reality. Draft means the proposal belongs in the RFD process, but it may still be waiting for design work, implementation work, or maintainer bandwidth. Experimental implementation may begin, properly feature-gated with the RFD name, to prove out the design before broader adoption. RFDs are living documents that track implementation progress. PRs working towards an RFD will typically update it to reflect changes in design or direction. ### 2b. Move to "To be removed" RFDs that have never landed may be closed at the discretion of a core team member. RFDs that have landed in draft form are moved to "To be removed" instead until there has been time to remove them fully from the codebase, then they are removed entirely. ### 3. Move to "Active" when it has current maintainer or working-group focus When core maintainers or a working group are actively spending bandwidth on an RFD, the champion can open a PR to move it to "Active." Active RFDs are the proposals the project is currently trying to move forward. This is a visibility signal, not a stability commitment or final approval. Active RFDs should have a current owner, a plausible implementation or design plan, and should be updated as the work changes. If work pauses or the owner no longer has bandwidth, the RFD can move back to Draft. ### 4. Move to "Preview" when fully implemented When the champion feels the RFD is ready for broader review, they open a PR to move it to "Preview." This signals the community to provide feedback. The PR stays open for a few days before the champion decides whether to land it. ### 5. Completed Once in preview, the RFD can be moved to "completed" with a final PR. The core team should comment and express concerns, but **final decision is always made by the core team lead**. Depending on what the RFD is about, "completed" is the only state that can represent a 1-way door (if there is a stability commitment involved), as changes might require a breaking change to the protocol after this point. Preview RFDs don't have to be completed. They may also go back to draft to await further changes or even be moved to "To be removed". ### 6. Lifecycle summary #### RFD Lifecycle * **Early drafts**: Initial ideas, brainstorming, early exploration * **Mature drafts**: Well-formed proposals ready for broader review * **Active**: Current implementation or design work is being driven by core maintainers or a working group * **Preview**: Fully implemented and ready for broad review before completion * **To be removed (yet?)**: Decided against for now, but preserved for future consideration * **Completed**: Implementation finished and merged ## Governance The project currently has a design team with the [Zed team as the lead (BDFL)](../community/governance). Champions from the core team guide RFDs through the process, but final decisions rest with the team lead. This structure maintains velocity while anticipating future governance expansion. ## Discussion and Moderation Detailed discussions often happen on [Zulip](https://agentclientprotocol.zulipchat.com/), with PR comments for process decisions. The results of detailed discussions should be incorporated into the relevant RFD. RFD champions actively curate discussions by collecting questions in the FAQ section. If PR discussions become too long, they should be closed, feedback summarized, and reopened with links to the original. ## Licensing All RFDs are licensed under Apache 2.0. The project remains open source. ## # ACP Agent Registry Source: https://agentclientprotocol.com/rfds/acp-agent-registry **Author:** [@ignatov](https://github.com/ignatov) **Champion:** [@benbrandt](https://github.com/benbrandt) ## Elevator pitch ACP needs a single, trusted registry of agents so clients can discover integrations, understand their capabilities, and configure them automatically. This RFD proposes (1) a canonical manifest format that every agent must publish, (2) a dedicated `agentclientprotocol/registry` repo where maintainers contribute those manifests, and (3) tooling that aggregates and publishes a searchable catalog for editors and other clients. ## Status quo There is no canonical listing of ACP-compatible agents. Information lives in scattered READMEs or proprietary feeds, which makes it hard to: * Let users discover agents directly inside ACP-aware clients. * Ensure protocol-version compatibility or capability coverage. * Keep metadata consistent (hosting model, license, etc.). Every editor builds bespoke manifests or scrapes GitHub, leading to duplication and stale data. ## Agent manifest format (core proposal) Each agent advertises itself via a manifest stored as `/agent.json` in the registry repo. Fields marked with **\*** are required: | Field | Description | | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `id` **\*** | Unique agent identifier. Lowercase letters, digits, and hyphens; must start with a letter (pattern: `^[a-z][a-z0-9-]*$`). Also the folder name in the registry repo. | | `name` **\*** | Human-readable display name. | | `version` **\*** | Semantic version of the agent release (e.g. `1.0.0`). | | `description` **\*** | Brief description of the agent's functionality and purpose. | | `distribution` **\*** | Object describing how to obtain and run the agent. Supports three distribution types: `binary` (platform-specific archives), `npx` (Node packages), and `uvx` (Python packages). At least one distribution type must be provided. See [Distribution](#distribution) for details. | | `repository` | Source code repository URL. | | `authors` | Array of author/organization names. | | `license` | SPDX license identifier or `"proprietary"`. | | `icon` | Path to icon file (relative path or absolute URL). Must be SVG format, 16×16, monochrome using `currentColor` (enables light/dark theme adaptation). See [Icon requirements](#icon-requirements). | ### Distribution The `distribution` object supports three mutually independent strategies. An agent may provide one or more: #### `binary` Platform-specific archive downloads. Keyed by `-` targets: | Target | OS | Architecture | | ----------------- | ------- | ------------ | | `darwin-aarch64` | macOS | ARM64 | | `darwin-x86_64` | macOS | x86-64 | | `linux-aarch64` | Linux | ARM64 | | `linux-x86_64` | Linux | x86-64 | | `windows-aarch64` | Windows | ARM64 | | `windows-x86_64` | Windows | x86-64 | When using `binary` distribution, builds **must be provided for all three operating systems** (darwin, linux, windows). CI will reject entries that only cover a subset. Each target is an object with: | Field | Required | Description | | --------- | -------- | ----------------------------------- | | `archive` | Yes | URL to download archive | | `cmd` | Yes | Command to execute after extraction | | `args` | No | Array of command-line arguments | | `env` | No | Object of environment variables | #### `npx` / `uvx` Package-manager-based distribution (Node via `npx`, Python via `uvx`). Each is an object with: | Field | Required | Description | | --------- | -------- | ----------------------------------------- | | `package` | Yes | Package name (with optional version spec) | | `args` | No | Array of command-line arguments | | `env` | No | Object of environment variables | ### Icon requirements Icons must meet the following requirements to pass CI validation: * **SVG format** — only `.svg` files are accepted. * **16×16 dimensions** — via `width`/`height` attributes or `viewBox`. * **Monochrome using `currentColor`** — all `fill` and `stroke` values must use `currentColor` or `none`. Hardcoded colors (e.g. `fill="#FF5500"`, `fill="red"`) are rejected. Using `currentColor` lets icons adapt automatically to the client's light or dark theme. ### Example: binary distribution ```jsonc theme={null} { "id": "someagent", "name": "SomeAgent", "version": "1.0.0", "description": "Agent for code editing", "repository": "https://github.com/example/someagent", "authors": ["Example Team"], "license": "MIT", "icon": "icon.svg", "distribution": { "binary": { "darwin-aarch64": { "archive": "https://github.com/example/someagent/releases/latest/download/someagent-darwin-arm64.zip", "cmd": "./someagent", "args": ["acp"], }, "darwin-x86_64": { "archive": "https://github.com/example/someagent/releases/latest/download/someagent-darwin-x64.zip", "cmd": "./someagent", "args": ["acp"], }, "linux-aarch64": { "archive": "https://github.com/example/someagent/releases/latest/download/someagent-linux-arm64.zip", "cmd": "./someagent", "args": ["acp"], }, "linux-x86_64": { "archive": "https://github.com/example/someagent/releases/latest/download/someagent-linux-x64.zip", "cmd": "./someagent", "args": ["acp"], }, "windows-x86_64": { "archive": "https://github.com/example/someagent/releases/latest/download/someagent-windows-x64.zip", "cmd": "./someagent.exe", "args": ["acp"], "env": { "SOMEAGENT_MODE_KEY": "", }, }, }, }, } ``` ### Example: package distribution ```jsonc theme={null} { "id": "pyagent", "name": "PyAgent", "version": "2.1.0", "description": "A Python-based ACP agent", "repository": "https://github.com/example/pyagent", "license": "Apache-2.0", "distribution": { "uvx": { "package": "pyagent@latest", "args": ["--mode", "acp"], }, }, } ``` ## Registry schema The aggregated `registry.json` file conforms to the registry schema and contains: | Field | Description | | --------- | --------------------------------------------------------------------------------------------------------- | | `version` | Registry schema version (semver, e.g. `1.0.0`). | | `agents` | Array of agent entries (each following the agent manifest schema above, sourced from `agent.json` files). | ## Authentication requirements To be listed in the registry, an agent **must support at least one** of the following authentication methods: * **Agent Auth** — the agent handles the OAuth flow independently (opens the user's browser, runs a local callback server, exchanges the authorization code for tokens). * **Terminal Auth** — the agent provides an interactive terminal-based setup experience (launched with additional args/env specified in the auth method). CI verifies this by checking that the agent returns an `authMethods` array in its `initialize` response, with at least one method. See the [ACP auth methods RFD](./auth-methods) for the full specification. ## What we propose to do about it 1. **Manifest spec** (above) becomes normative; we publish the JSON Schema and validator script so maintainers can lint locally. 2. **Registry repository** `github.com/agentclientprotocol/registry`: * Structure: `/agent.json`, optional `icon.svg`, optional `README.md`. * CI validates manifests on every PR: schema compliance, slug uniqueness, icon format (16×16 SVG, monochrome `currentColor`), URL accessibility for all distribution URLs, authentication support via ACP handshake, and binary OS coverage. * Push to `main` triggers a build that aggregates all entries into `registry.json` and publishes versioned + `latest` GitHub releases. 3. **Aggregated outputs**: * `registry.json`: deterministic list of all agents with icons copied to `dist/.svg`. 4. **Distribution & search**: * Clients fetch `registry.json` from `https://cdn.agentclientprotocol.com/registry/v1/latest/registry.json`. * Static site offers filters for deployment model, license, and distribution type. ## Shiny future * Agent maintainers make PRs to update their manifests; CI keeps data clean. * Automated version updates run hourly, checking npm, PyPI, and GitHub releases for new versions of registered agents and opening PRs automatically. * Editors/clients can bootstrap ACP support by fetching one JSON file and filtering locally. * The ACP website displays the same data for humans, ensuring consistency. * Package-based distribution (`npx`, `uvx`) lowers the barrier for agents that don't need platform-specific binaries. ## Implementation details and plan **Phase 1 – Spec & repo bootstrap** * Finalize JSON Schema and documentation. * Create registry repo with CI (GitHub Actions) that validates on PRs and publishes on merge. * Seed with reference agents. * Implement automated version update workflow (hourly cron via GitHub Actions). * Enforce authentication requirements via CI handshake verification. ## Revision history * 2025-11-28: Initial draft. * 2025-12-16: Minors. * 2026-02-04: Updated to match latest schema — removed `schema_version`, `homepage`, `capabilities`, and `auth` fields; added `icon` field; restructured `distribution` into `binary`, `npx`, and `uvx` types. # Additional Workspace Roots for Session Lifecycle Requests Source: https://agentclientprotocol.com/rfds/additional-directories * Author(s): [egor-baranov](https://github.com/egor-baranov) * Champion: [benbrandt](https://github.com/benbrandt) ## Elevator pitch > What are you proposing to change? ACP currently standardizes `cwd` on session lifecycle requests, but does not define a standard way to declare additional filesystem roots for a session. This RFD proposes an optional field: ```ts theme={null} additionalDirectories?: string[] ``` on: * `NewSessionRequest` * `ResumeSessionRequest` * `ForkSessionRequest` * `LoadSessionRequest` and an optional capability: ```ts theme={null} sessionCapabilities.additionalDirectories?: {} ``` `cwd` remains the primary working directory. `additionalDirectories` expands filesystem scope only. It does not replace `cwd`, define directory contents, or introduce new RPC methods. ## Status quo > How do things work today and what problems does this cause? Why would we change things? ACP currently allows a client to identify one primary workspace root through `cwd`. That is sufficient for single-root projects, but it does not provide a standard way to expose additional roots in multi-root repositories or environments where project code, shared instructions, skills, or mounted workspaces live at separate absolute paths. Without a standardized field, clients and agents must rely on implementation-specific conventions. `_meta` is not appropriate for interoperable workspace-root semantics. This leaves multi-root behavior fragmented and less predictable across implementations. ## What we propose to do about it > What are you proposing to improve the situation? Add `additionalDirectories?: string[]` to the session lifecycle request schemas listed above, and add optional additional-root discovery metadata to `session/list` responses. The proposal is scoped to session lifecycle requests, session discovery requests, and session discovery metadata only: * no new RPC methods are added; * `cwd` remains unchanged as the primary root; and * ACP remains agnostic to directory naming and directory contents. Agents advertise support with `sessionCapabilities.additionalDirectories`. Clients MUST gate usage on that capability. This proposal also allows Agents to include `additionalDirectories` in `SessionInfo` returned by `session/list`. Because Agents are not required to persist additional-root metadata, Clients treat `SessionInfo.additionalDirectories` as optional discovery metadata rather than required session state. `session/load` and `session/resume` remain explicit-list only: omitting `additionalDirectories` or supplying an empty array means no additional roots are activated for the resulting session, while a non-empty array-valued `additionalDirectories` becomes the complete resulting additional-root list for that request. Clients MAY change the additional-root list on `session/load` or `session/resume` from any previously used or reported list, as long as the request `cwd` matches the session's `cwd` and the Agent accepts the requested roots. ## Shiny future > How will things will play out once this feature exists? Clients can declare multi-root workspace scope in a standard way while preserving existing `cwd` behavior. Agents can treat a session as an ordered root set starting with `cwd` and followed by any array-valued `additionalDirectories` entries, and apply the same discovery and resource-handling behavior they already apply under `cwd` to the other declared roots. Security boundaries remain explicit: declared roots communicate intended scope, while sandboxing, approvals, and OS permissions continue to govern actual access. ## Implementation details and plan > Tell me more about your implementation. What is your detailed implementation plan? ### Goals / Non-goals #### Goals * Standardize additional workspace roots on session lifecycle entry points. * Preserve `cwd` as the primary working directory for the session. * Define clear validation and error-handling rules. * Enable generic multi-root discovery and resource handling by agents across all declared roots. * Expand the filesystem scope without changing the existing RPC method structure. #### Non-goals * Defining any required directory names or layouts, such as `.agents/`, `skills/`, or `./`. * Defining a standard instruction, skill, or configuration file format. * Replacing `cwd` with a list-valued field. * Changing relative-path semantics. * Adding new RPC methods. * Defining per-root read/write permissions in the protocol. ### Schema changes The following optional property is added to each request type named above: ```ts theme={null} additionalDirectories?: string[] ``` The following optional property is also added to `SessionInfo`: ```ts theme={null} additionalDirectories?: string[] ``` The following capability is added to `SessionCapabilities`: ```ts theme={null} additionalDirectories?: {} ``` Clients MUST send `additionalDirectories` only when `sessionCapabilities.additionalDirectories` is present. If an Agent advertises `sessionCapabilities.additionalDirectories` and also supports `session/list`, it MAY include `SessionInfo.additionalDirectories` to report the complete ordered additional-root list associated with a listed session. Omitted and empty values are equivalent: the `SessionInfo` response reports no additional roots. Clients MUST NOT merge this field with prior values or infer additional roots from agent-specific state. `ListSessionsRequest` is not extended with `additionalDirectories`. `session/list` filtering remains defined around existing list parameters such as `cwd`; Clients that need to find sessions by additional-root state can filter locally when Agents return `SessionInfo.additionalDirectories`. The session-setup, session-list, and filesystem documentation describe the effective root set rather than `cwd` alone as the session filesystem context or boundary. ### Capability advertisement example ```json theme={null} { "jsonrpc": "2.0", "id": 0, "result": { "protocolVersion": 1, "agentCapabilities": { "sessionCapabilities": { "additionalDirectories": {} } } } } ``` ### `session/list` example When an Agent supports both `sessionCapabilities.additionalDirectories` and `session/list`, Clients may still filter by `cwd`. Agents that track additional-root state may include the complete additional-root list on each returned `SessionInfo`: ```json theme={null} { "jsonrpc": "2.0", "id": 5, "method": "session/list", "params": { "cwd": "/home/user/project" } } ``` ```json theme={null} { "jsonrpc": "2.0", "id": 5, "result": { "sessions": [ { "sessionId": "sess_abc123", "cwd": "/home/user/project", "additionalDirectories": [ "/home/user/shared-lib", "/home/user/product-docs" ], "updatedAt": "2026-03-24T12:00:00Z" } ] } } ``` ### Field definition `additionalDirectories` has the following properties: * The field MUST be an array of strings when present. * Each entry MUST be an absolute path under the same platform path rules used for `cwd`. * Empty strings MUST be rejected. * `null` is not a valid value for the field or for any entry. ACP does not define a wire-level lexical normalization algorithm for `cwd` or `additionalDirectories`. Clients SHOULD remove exact duplicate path strings before sending the request. Clients SHOULD also avoid entries whose path string exactly matches `cwd`. Overlapping or nested roots are not semantically redundant for ACP purposes: because discovery across the effective ordered root list is ordered and implementation-defined, such entries MAY affect behavior even when they do not change the union of accessible paths. Agents MAY remove exact duplicate path strings, including entries identical to `cwd`, provided doing so preserves the first occurrence order of the remaining entries and does not expand scope. When returning `SessionInfo.additionalDirectories`, implementations that normalize or canonicalize path strings SHOULD apply the same platform-appropriate rule consistently to stored session state and the returned `SessionInfo.additionalDirectories`. ### Semantics `cwd` remains the primary working directory for the session. It continues to define: * the default base for relative-path resolution; * the primary working directory semantics already defined by ACP; and * the primary workspace root for clients or agents that distinguish one root as primary. `additionalDirectories` defines zero or more extra filesystem roots that are part of the same session workspace scope. The effective ordered root list for a session is: * `[cwd]` when `additionalDirectories` is omitted or is an empty array * `[cwd, ...additionalDirectories]` when `additionalDirectories` is a non-empty array This ordered root list has the following semantics: * The session workspace is a multi-root workspace. * The roots form a logical union of accessible roots. * The roots are not a virtual overlay filesystem. * ACP does not define shadowing, mount, or merge semantics between roots. * Relative paths MUST continue to resolve only against `cwd`. A file path is in scope for the session if, after platform-appropriate path resolution for access control, it is contained within at least one declared root. This proposal expands workspace scope only. It does not imply that all in-scope paths are writable, nor does it bypass client capability checks, user approvals, sandbox rules, or operating-system permissions. ### Request-specific behavior For `NewSessionRequest`, the resulting additional root list is: * `additionalDirectories` when present with a non-empty array value; * otherwise `[]`. For `LoadSessionRequest` and `ResumeSessionRequest`: * when `additionalDirectories` is present with a non-empty array value, it is the complete resulting list of additional roots for the active session, even if that list differs from the session's previously stored or reported additional roots, provided the request `cwd` matches the session's `cwd`; * when omitted or present as an empty array, the resulting additional root list is `[]`. Agents MUST NOT implicitly reactivate stored additional roots that were not supplied on the `session/load` or `session/resume` request. Supplying `additionalDirectories` on `session/load` or `session/resume` is allowed when the capability is advertised and `cwd` matches the session being loaded or resumed, and doing so may preserve, replace, reduce, or expand the session's previously stored or reported additional-root list for the resulting active session, subject to validation and policy checks. Clients that need to preserve a session's additional roots across restarts or across client instances MUST obtain, persist, or reconstruct the full list and resend it on load or resume. When `session/list` includes `SessionInfo.additionalDirectories`, that value can help with this purpose, but Clients MUST NOT assume it is always returned. For `ForkSessionRequest`: * when `additionalDirectories` is present with a non-empty array value, it is a full replacement list for the forked session; * when omitted or present as an empty array, the resulting additional root list is `[]`. Agents MUST NOT implicitly inherit additional roots from the source session unless the implementation can prove that the active root list is already authoritative to the requesting client on the current connection. Otherwise, the agent MUST fail the request or require the client to provide the full list explicitly. For all lifecycle methods, an agent MAY reject the request if the resulting roots are incompatible with stored session state, fork semantics, sandbox policy, or other implementation constraints. An agent MUST NOT silently drop unsupported or unauthorized roots. This RFD defines lifecycle-time workspace scope only. It does not define mid-session mutation of `additionalDirectories`. ### Agent behavior expectations Agents MUST treat paths under `additionalDirectories` as part of the session's accessible workspace scope in the same sense as paths under `cwd`, subject to capabilities and permissions actually available for that session. For any discovery, indexing, or resource-loading behavior an agent applies under `cwd` such as instructions, skills, configuration, or other agent-specific artifacts, the agent SHOULD apply the same behavior to each declared root in `additionalDirectories`, subject to the same capabilities and permissions. If an implementation resolves conflicts across multiple roots, earlier roots in the effective ordered root list SHOULD take precedence over later roots. Conflict resolution within a single root remains implementation-defined. ACP does not define: * required directory names or layouts; * the contents or file formats of discovered artifacts; * whether an agent MUST perform any discovery at all; or * how discovered artifacts affect prompting or execution. Implementations MAY define such behavior independently, but ACP remains agnostic to directory contents. ### Validation and error handling An agent receiving a session lifecycle request with `additionalDirectories` MUST validate the field before creating, loading, resuming, or forking the session. The agent MUST reject the request if: * `additionalDirectories` is not an array; * any entry is not a string; * any entry is empty; * any entry is not absolute. `invalid_params` is the RECOMMENDED error class for malformed values. If an entry is syntactically valid but cannot be granted under the implementation's policy, sandbox, or user-consent model, the agent MUST fail the request with an appropriate error. The agent MUST NOT silently ignore invalid, unauthorized, or unsupported entries and proceed with a reduced root set. ACP does not require a specific lexical normalization algorithm for validating `cwd` or `additionalDirectories`. Implementations that normalize or canonicalize path strings for comparison, deduplication, or storage SHOULD apply the same platform-appropriate rule consistently to both fields, but authorization and containment checks MUST NOT rely solely on lexical normalization. When enforcing root boundaries for subsequent file access, implementations SHOULD use canonical or real paths where available and MUST prevent escapes through symlinks, junctions, mount points, or equivalent mechanisms. If the implementation cannot safely determine whether a path is within an allowed root, it MUST fail closed. ### Client responsibilities A client that sends `additionalDirectories`: * MUST send only paths it intends to expose to the session; * MUST ensure those paths are absolute; * MUST send the full intended active additional root list on `session/load` and `session/resume`; * SHOULD obtain user consent or otherwise act consistently with the client's trust and permission model; * SHOULD remove exact duplicate path strings and entries identical to `cwd` before sending; and * MUST NOT assume an agent will infer extra roots from project metadata, directory conventions, or prior session state. If a client mediates filesystem access through ACP client capabilities such as `fs/read_text_file` and `fs/write_text_file`, it MUST enforce the effective root set for that session when authorizing path-based access. ACP's filesystem methods are client-mediated and operate on absolute paths, so the client remains responsible for boundary enforcement in those flows. When the client learns about an existing session through `session/list`, it SHOULD use `cwd` together with any `SessionInfo.additionalDirectories` entries returned for that session as the reported root set. Omitted and empty values report no additional roots. Any subsequent lifecycle request establishes the resulting active root set explicitly. If a client launches an agent with direct filesystem access, `additionalDirectories` is not, by itself, a sandbox. Clients that need root boundaries to be enforced in that deployment model SHOULD apply operating-system or runtime sandboxing consistent with the declared root set. ### Examples #### `session/new` ```json theme={null} { "jsonrpc": "2.0", "id": 1, "method": "session/new", "params": { "cwd": "/home/user/project", "additionalDirectories": [ "/home/user/shared-lib", "/home/user/product-docs" ], "mcpServers": [] } } ``` #### `session/load` ```json theme={null} { "jsonrpc": "2.0", "id": 2, "method": "session/load", "params": { "sessionId": "sess_abc123", "cwd": "/home/user/project", "additionalDirectories": [ "/home/user/shared-lib", "/home/user/product-docs" ], "mcpServers": [] } } ``` #### `session/resume` ```json theme={null} { "jsonrpc": "2.0", "id": 3, "method": "session/resume", "params": { "sessionId": "sess_abc123", "cwd": "/home/user/project", "additionalDirectories": [ "/home/user/shared-lib", "/home/user/product-docs" ], "mcpServers": [] } } ``` #### `session/fork` ```json theme={null} { "jsonrpc": "2.0", "id": 4, "method": "session/fork", "params": { "sessionId": "sess_abc123", "cwd": "/home/user/project", "additionalDirectories": [ "/home/user/shared-lib", "/home/user/product-docs" ], "mcpServers": [] } } ``` ### Security considerations `additionalDirectories` increases the set of filesystem paths the agent may be able to inspect or modify. This increases the potential impact of user error, client misconfiguration, or insufficient sandboxing. Clients MUST NOT send directories that the user has not granted to the session under the client's trust model. Clients SHOULD default to explicit selection or other clear workspace-grant semantics for roots outside the primary project directory. Agents and clients MUST treat root enforcement as a security boundary when they claim to enforce it. Boundary checks MUST account for symlink and path-resolution attacks. Ambiguous cases MUST fail closed. This field does not create a new privilege model. All existing permission prompts, approval flows, filesystem capabilities, sandbox controls, and operating-system access rules continue to apply. Because ACP does not define directory contents, agents SHOULD avoid assuming that any discovered file under an additional root is safe, authoritative, or intended for automatic execution. Discovery is not equivalent to trust. ### Backward compatibility This proposal is additive at the schema level. Requests that omit `additionalDirectories` behave exactly as they do today. Existing clients that do not need multi-root support require no changes. Updated agents MUST continue to accept requests that do not include the field. Older implementations may validate request objects strictly against older schemas and MAY reject unknown fields. Clients therefore MUST gate usage on `sessionCapabilities.additionalDirectories`. This proposal intentionally does not extend the responses to `session/new`, `session/load`, `session/resume`, or `session/fork` to surface additional-root state directly. That remains consistent with existing ACP response shapes, which also do not return `cwd`. Instead, when `session/list` is supported, Agents MAY expose the complete ordered additional-root list for listed sessions through `SessionInfo.additionalDirectories`. Omitted and empty values report no additional roots for that `SessionInfo`. `SessionInfoUpdate` remains unchanged because this RFD does not define mid-session mutation of `additionalDirectories`. Clients that need multi-root continuity across `session/load` or `session/resume` MUST still send the full intended list explicitly, and MAY change that list from a previously used or reported list as long as the request `cwd` matches the session's `cwd`. `session/list` filtering remains independent of `additionalDirectories`. Sessions that differ by `additionalDirectories` are distinguishable only when the Agent surfaces that state through `SessionInfo.additionalDirectories`; Clients that need additional-root matching can filter those returned sessions locally. This RFD does not add a new RPC method. It also does not change the meaning of `cwd`, `sessionId`, or `mcpServers`. ### Drawbacks * It expands the workspace surface area and therefore the risk of accidental data exposure. * It introduces another dimension of interoperability for clients and agents to test. * It does not standardize discovery behavior, so implementations may still differ in how they use additional roots. * Older strict validators may reject the new field until they adopt the updated schema. ## Frequently asked questions > What questions have arisen over the course of authoring this document or during subsequent discussions? ### Why not use `_meta`? `_meta` is intended for implementation-specific custom data. A standardized workspace-root concept should be protocol-level, typed, and interoperable across implementations. ### Why not replace `cwd` with `workspaceRoots`? `cwd` already has established semantics as the primary working directory. Replacing it with an array would blur relative-path behavior, reduce clarity for existing implementations, and create a larger migration surface. ### Does ACP define `.agents`, `skills`, or instruction directory conventions? No. ACP does not define `.agents`, `skills`, or other instruction directory conventions. This proposal only defines additional accessible roots. Agents SHOULD handle resources under `additionalDirectories` the same way they handle analogous resources under `cwd`, but the names, layouts, and file formats of those resources remain implementation-defined. ### How should clients handle older agents that may not support this field? Clients MUST gate `additionalDirectories` on `sessionCapabilities.additionalDirectories`. If support is absent, clients SHOULD omit the field and preserve existing behavior. ### Why not restore stored roots on `session/load` or `session/resume` when the field is omitted? Even when `SessionInfo.additionalDirectories` is available through `session/list`, implicit restoration would still let an Agent reactivate filesystem scope that the current request did not state explicitly. Some Agents also do not persist or surface this state at all. That is undesirable for Clients that do not use `session/list`, for Clients resuming a session by ID without first listing it, and for Clients that want request-time control over the active root set. This proposal therefore keeps load and resume explicit-list only for additional roots: omitting the field or supplying an empty array activates none, while supplying a non-empty array is explicitly allowed and sets the complete resulting additional-root list for that request. That list may differ from any previously used or reported list, as long as the request `cwd` matches the session's `cwd` and the Agent accepts the requested roots. ### What alternative approaches did you consider, and why did you settle on this one? Alternatives considered: * putting extra roots in `_meta`; * adding a new workspace-configuration RPC method; and * requiring clients to copy or mount everything under `cwd`. This proposal is preferred because it is additive, keeps `cwd` semantics stable, avoids new methods, and standardizes multi-root scope at session lifecycle boundaries. ## Revision history * 2026-05-21: Moved to Preview. * 2026-03-24: Initial draft. # Authentication Methods Source: https://agentclientprotocol.com/rfds/auth-methods Author(s): [anna239](https://github.com/anna239) ## Elevator pitch > What are you proposing to change? I suggest adding more information about auth methods that agent supports, which will allow clients to draw more appropriate UI. ## Status quo > How do things work today and what problems does this cause? Why would we change things? Agents have different ways of authenticating users: env vars with api keys, running a command like ` login`, some just open a browser and use oauth. [AuthMethod](https://agentclientprotocol.com/protocol/v1/schema#authmethod) does not really tell the client what should be done to authenticate. This means we can't show the user a control for entering key if an agent supports auth through env var. Very few agents can authenticate fully on their own without user input, so agents with ACP auth support are limited in the methods they can offer, or require manual setup before being run as an ACP agent. ## What we propose to do about it > What are you proposing to improve the situation? We can add addition types of AuthMethods, to provide clients with additional information so they can assist in the login process. ## Shiny future > How will things will play out once this feature exists? It will be easier for end-users to start using an agent from inside the IDE as auth process will be more straightforward ## Implementation details and plan > Tell me more about your implementation. What is your detailed implementation plan? I suggest adding following auth types: ### Auth method types 1. Agent auth Same as what there is now – agent handles the auth itself. This is the default type when no `type` is provided, preserving backward compatibility. ```json theme={null} { "id": "123", "name": "Agent", "description": "Authenticate through agent" } ``` An explicit `"type": "agent"` is also accepted but not required. 2. Env variable The user provides credentials that the client passes to the agent as environment variables. Requires `"type": "env_var"`. The `vars` field is an array of `AuthEnvVar` objects, each describing a single environment variable. This supports services that require multiple credentials (e.g. Azure OpenAI needs both an API key and an endpoint URL). Simple single-key example: ```json theme={null} { "id": "openai", "name": "OpenAI API Key", "type": "env_var", "vars": [{ "name": "OPENAI_API_KEY" }], "link": "https://platform.openai.com/api-keys" } ``` Multiple variables with metadata: ```json theme={null} { "id": "azure-openai", "name": "Azure OpenAI", "type": "env_var", "vars": [ { "name": "AZURE_OPENAI_API_KEY", "label": "API Key" }, { "name": "AZURE_OPENAI_ENDPOINT", "label": "Endpoint URL", "secret": false }, { "name": "AZURE_OPENAI_API_VERSION", "label": "API Version", "secret": false, "optional": true } ], "link": "https://portal.azure.com" } ``` Fields on `AuthMethodEnvVar`: * `vars` (required): Array of `AuthEnvVar` objects. * `link` (optional): URL where the user can obtain their credentials. Fields on `AuthEnvVar`: * `name` (required): The environment variable name (e.g. `"OPENAI_API_KEY"`). * `label` (optional): Human-readable label for this variable, displayed in client UI. * `secret` (optional, default `true`): Whether this value is a secret. Clients should use a password-style input for secret vars and a plain text input otherwise. * `optional` (optional, default `false`): Whether this variable is optional. Since environment variables need to be supplied when the agent process starts, the client can check if it already passed such variables to the process, in which case the user can click on the button and the agent will read the already available values. Otherwise, when the user clicks the button, the client could restart the agent process with the desired environment variables, and then automatically send the authenticate message with the correct id to sign in for the user. 3. Terminal Auth This requires the client to be able to run an interactive terminal for the user to login via a TUI. Requires `"type": "terminal"`. ```json theme={null} { "id": "123", "name": "Run in terminal", "description": "Setup Label", "type": "terminal", "args": ["--setup"], "env": { "VAR1": "value1", "VAR2": "value2" } } ``` * `args` (optional, default `[]`): Additional arguments to pass when running the agent binary. * `env` (optional, default `{}`): Additional environment variables to set. The `command` cannot be specified, the client will invoke the exact same binary with the exact same setup. The agent can supply additional arguments and environment variables as necessary. These will be supplied in **addition** to any args/env supplied by default when the server is started. So agents will need to have a way to kickoff their interactive login flow even if normal acp commands/arguments are supplied as well. This is so that the agent doesn't need to know about the environment it is running in. It can't know the absolute path necessarily, and shouldn't be able to supply other commands or programs to minimize security issues. ### Client capabilities Because `terminal` auth methods require specific client-side support, clients must opt in via `AuthCapabilities` on `ClientCapabilities` during initialization: ```json theme={null} { "clientCapabilities": { "auth": { "terminal": true } } } ``` * `auth.terminal` (default `false`): When `true`, the agent may include `terminal` entries in its authentication methods. The `env_var` type does not require a capability opt-in since any client can set environment variables when starting a process, we are just providing additional context for the environment variable. ## Frequently asked questions > What questions have arisen over the course of authoring this document or during subsequent discussions? ### What alternative approaches did you consider, and why did you settle on this one? An alternative approach would be to include this information to an agent's declaration making it more static, see [Registry RFD](https://github.com/agentclientprotocol/agent-client-protocol/pull/289) There is also an alternative to adding a separate `elicitation` capability, which is to create a separate auth type for this. Then the client can decide themselves if they support it or not. ## Revision history There was a part about elicitations [https://github.com/agentclientprotocol/agent-client-protocol/blob/939ef116a1b14016e4e3808b8764237250afa253/docs/rfds/auth.mdx](https://github.com/agentclientprotocol/agent-client-protocol/blob/939ef116a1b14016e4e3808b8764237250afa253/docs/rfds/auth.mdx) removed it for now, will move to a separate rfd * 2026-03-09: Remove auth\_methods from Error type * 2026-03-03: Changed `env_var` from single `varName` to structured `vars` array of `AuthEnvVar` objects; simplified field name from `varName` to `name` * 2026-02-27: Updated to reflect current implementation * 2026-01-14: Updates based on Core Maintainer discussion # Boolean Config Option Type Source: https://agentclientprotocol.com/rfds/boolean-config-option * Author(s): [fscarponi](https://github.com/fscarponi) * Champion: [benbrandt](https://github.com/benbrandt) ## Elevator pitch Add a new `boolean` type to session configuration options, enabling agents to expose simple ON/OFF toggles (e.g., "Brave Mode", "Read Only", "Produce Report") as first-class config options alongside the existing `select` type. ## Status quo Currently, `SessionConfigKind` only supports the `select` type, which allows agents to expose dropdown-style selectors with a list of named values. This works well for choosing models, modes, or reasoning levels. However, there is no native way to represent a simple boolean on/off toggle. To expose a boolean option today, agents must use a `select` with two artificial options (e.g., "on"/"off"), and clients need custom, non-agnostic logic to detect that a particular select is actually a boolean toggle. This defeats the purpose of a standardized protocol. ## What we propose to do about it * Add a `SessionConfigBoolean` struct with a `current_value: bool` field * Add a `Boolean(SessionConfigBoolean)` variant to the `SessionConfigKind` enum, discriminated by `"type": "boolean"` * Add a `SessionConfigOptionValue` internally-tagged enum so that `SetSessionConfigOptionRequest` can carry both string values (for `select`) and boolean values (for `boolean`) with an explicit `type` discriminator * Add a v1 `session.configOptions.boolean` client capability so Agents only send boolean config options to Clients that explicitly opt in * Provide convenience constructors and `From` impls for ergonomic usage * Update documentation and regenerate schema files ## Shiny future Clients can natively render boolean config options as toggle switches or checkboxes, without any custom logic. Agents can expose options like "Brave Mode", "Produce Report", or "Read Only" in a standardized way that any ACP-compliant client understands out of the box. ## Implementation details and plan ### Client capability In v1, Clients opt in by setting `session.configOptions.boolean` in `initialize`: ```json theme={null} { "jsonrpc": "2.0", "id": 0, "method": "initialize", "params": { "protocolVersion": 1, "clientCapabilities": { "session": { "configOptions": { "boolean": {} } } } } } ``` Omitting `session`, `configOptions`, or `boolean` means the Client does not advertise support. `null` at any of those levels is equivalent to omission. Agents **MUST NOT** include `type: "boolean"` config options in v1 `configOptions` payloads unless the Client advertised `session.configOptions.boolean: {}`. This applies to `session/new`, `session/load`, `session/update`, and `session/set_config_option` responses. Agents that need to support older Clients should omit the boolean option or provide a `select` fallback. Protocol v2 does not add an equivalent capability field for this extension. When the unstable boolean config option extension is enabled, v2 peers are boolean-aware by construction; compatibility layers that down-convert v2 client capabilities to v1 advertise `session.configOptions.boolean: {}` by default. ### Wire format: declaring a boolean option In a `session/new` response (or any response containing `configOptions`): ```json theme={null} { "jsonrpc": "2.0", "id": 1, "result": { "sessionId": "sess_abc123", "configOptions": [ { "id": "brave_mode", "name": "Brave Mode", "description": "Skip confirmation prompts and act autonomously", "type": "boolean", "currentValue": true }, { "id": "mode", "name": "Session Mode", "category": "mode", "type": "select", "currentValue": "code", "options": [ { "value": "ask", "name": "Ask" }, { "value": "code", "name": "Code" } ] } ] } } ``` ### Wire format: setting a boolean option The `session/set_config_option` request carries a `type` discriminator alongside the `value`. The `type` field describes the *shape* of the value, not the option kind. For boolean options, Clients send `type: "boolean"` with a boolean value: ```json theme={null} { "jsonrpc": "2.0", "id": 2, "method": "session/set_config_option", "params": { "sessionId": "sess_abc123", "configId": "brave_mode", "type": "boolean", "value": true } } ``` For select and other id-based options, Clients send `type: "id"` with a `SessionConfigValueId` string: ```json theme={null} { "jsonrpc": "2.0", "id": 3, "method": "session/set_config_option", "params": { "sessionId": "sess_abc123", "configId": "mode", "type": "id", "value": "code" } } ``` The response returns the full set of config options with current values, as with `select`: ```json theme={null} { "jsonrpc": "2.0", "id": 2, "result": { "configOptions": [ { "id": "brave_mode", "name": "Brave Mode", "description": "Skip confirmation prompts and act autonomously", "type": "boolean", "currentValue": true }, { "id": "mode", "name": "Session Mode", "category": "mode", "type": "select", "currentValue": "code", "options": [..] } ] } } ``` Key changes: 1. `SessionConfigBoolean` struct with `current_value: bool` 2. `Boolean(SessionConfigBoolean)` variant in `SessionConfigKind` (tagged via `"type": "boolean"`) 3. `SessionConfigOptionValue` enum using `#[serde(tag = "type")]` with an untagged `OtherSessionConfigOptionValue` fallback for future value shapes: * `Id { value: SessionConfigValueId }` — matched when `type` is `"id"` * `Boolean { value: bool }` — matched when `type` is `"boolean"` * `Other(OtherSessionConfigOptionValue)` — preserves unrecognized typed value payloads 4. `SessionConfigOptionValue` is flattened (`#[serde(flatten)]`) onto `SetSessionConfigOptionRequest`, producing top-level `type` and `value` fields on the wire 5. The `value` field type change in `SetSessionConfigOptionRequest` is gated behind `#[cfg(feature = "unstable_boolean_config")]` — without the feature the field remains `SessionConfigValueId` 6. The v1 `ClientCapabilities.session.config_options.boolean` field path serializes as `session.configOptions.boolean` 7. v2 requires a `type` field so future value shapes can be captured without ambiguity ### Client capabilities Clients that receive a config option with an unrecognized `type` should still ignore it. However, boolean config options also change the `session/set_config_option` request shape from the v1 string-only `value` convention to a boolean-valued payload. To avoid breaking Clients or SDKs that do not tolerate that shape, v1 Agents must treat boolean config options as capability-gated and only send them after the Client advertises `session.configOptions.boolean: {}`. ## Frequently asked questions ### What alternative approaches did you consider, and why did you settle on this one? We considered reusing the existing `select` type with a convention (e.g., options named "on"/"off"), but this would require clients to implement non-agnostic detection logic, which contradicts the goal of a standardized protocol. A dedicated `boolean` type is cleaner and lets clients render the appropriate UI control without guessing. ### Is this a breaking change? For v1 peers that do not advertise support, Agents must not send boolean config options, so the existing string-only config option behavior is preserved. v2 requires a `type` field on `session/set_config_option` so id values, boolean values, and future value shapes are distinguishable. For peers that opt in with `session.configOptions.boolean: {}`, boolean options introduce a new `type: "boolean"` setter shape with a boolean `value`. While no one would set a value of type boolean that doesn't support it, there is an issue with possible string deserialization causing the entire session/new or other response to fail deserialization. Most SDKs would process this gracefully, but if popular agents start using it, they could break existing clients. ## Revision history * 2026-06-30: Moved to Preview. * 2026-06-22: Added the v1 `session.configOptions.boolean` client capability gate so boolean config options are only sent to Clients that explicitly opt in, and made v2-to-v1 compatibility conversion advertise the capability by default * 2026-03-05: Updated to reflect final implementation — `flag` renamed to `boolean`, value type changed from untagged `String | Bool` enum to internally-tagged enum with `type` discriminator and untagged `ValueId` fallback, feature-gated behind `unstable_boolean_config` * 2026-02-24: Initial proposal # Configurable LLM Providers Source: https://agentclientprotocol.com/rfds/custom-llm-endpoint * Author(s): [@anna239](https://github.com/anna239), [@xtmq](https://github.com/xtmq) ## Elevator pitch > What are you proposing to change? Add the ability for clients to discover and configure agent LLM providers (identified by `id`) via dedicated provider methods: * `providers/list` * `providers/set` * `providers/disable` This allows clients to route LLM requests through their own infrastructure (proxies, gateways, or self-hosted models) without agents needing to know about this configuration in advance. ## Status quo > How do things work today and what problems does this cause? Why would we change things? ACP does not currently define a standard method for configuring LLM providers. In practice, provider configuration is usually done via environment variables or agent-specific config files. That creates several problems: * No standard way for clients to discover what providers an agent exposes * No standard way to update one specific provider by id * No standard way to disable a specific provider at runtime while preserving provider discoverability * Secret-bearing values in headers are difficult to handle safely when configuration must be round-tripped This particularly affects: * **Client proxies**: clients want to route agent traffic through their own proxies, for example to add headers or logging * **Enterprise deployments**: organizations want to route LLM traffic through internal gateways for compliance, logging, and cost controls * **Self-hosted models**: users running local servers (vLLM, Ollama, etc.) need to redirect agent traffic to local infrastructure * **API gateways**: organizations using multi-provider routing, rate limiting, and caching need standardized endpoint configuration ## Shiny future > How will things play out once this feature exists? Clients will be able to: 1. Understand whether an agent supports client-managed LLM routing 2. See where the agent is currently sending LLM requests (for example in settings UI) 3. Route agent LLM traffic through their own infrastructure (enterprise proxy, gateway, self-hosted stack) 4. Update routing settings from the client instead of relying on agent-specific env vars 5. Disable a provider when needed and later re-enable it explicitly 6. Apply these settings before starting new work in sessions ## Implementation details and plan > Tell me more about your implementation. What is your detailed implementation plan? ### Intended flow ```mermaid theme={null} sequenceDiagram participant Client participant Agent Client->>Agent: initialize Agent-->>Client: initialize response (agentCapabilities.providers = {}) Client->>Agent: providers/list Agent-->>Client: providers/list response Client->>Agent: providers/set (id = "main") Agent-->>Client: providers/set response Client->>Agent: providers/disable (optional) Agent-->>Client: providers/disable response Client->>Agent: session/new ``` 1. Client initializes and checks `agentCapabilities.providers`. 2. Client calls `providers/list` to discover available providers, their current routing targets (or disabled state), supported protocol types, and whether they are required. 3. Client calls `providers/set` to apply new (required) configuration for a specific provider ID. 4. Client may call `providers/disable` when a non-required provider should be disabled. 5. Client creates or loads sessions. ### Capability advertisement The agent advertises support with an empty object capability: ```typescript theme={null} interface AgentCapabilities { // ... existing fields ... /** * Provider configuration support. * If present, the agent supports providers/list, providers/set, and providers/disable. */ providers?: {}; } ``` If `providers` is absent, clients must treat provider methods as unsupported. ### Types ```typescript theme={null} /** * Well-known API protocol identifiers for LLM providers. * * This is an open string type: agents and clients MUST handle unknown * protocol identifiers gracefully. * * Protocol names beginning with `_` are free for custom use, like other * ACP extension methods. Protocol names that do not begin with `_` are * reserved for the ACP spec. */ type LlmProtocol = "anthropic" | "openai" | "azure" | "vertex" | "bedrock" | string; interface ProviderCurrentConfig { /** Protocol currently used by this provider. */ apiType: LlmProtocol; /** Base URL currently used by this provider. */ baseUrl: string; } type ProviderId = string; interface ProviderInfo { /** Provider identifier, for example "main" or "openai". */ providerId: ProviderId; /** Supported protocol types for this provider. */ supported: LlmProtocol[]; /** * Whether this provider is mandatory and cannot be disabled via providers/disable. * If true, clients must not call providers/disable for this provider ID. */ required: boolean; /** * Current effective non-secret routing config. * Null or omitted means provider is disabled. */ current?: ProviderCurrentConfig | null; /** Extension metadata */ _meta?: Record; } ``` ### `providers/list` ```typescript theme={null} interface ProvidersListRequest { /** Extension metadata */ _meta?: Record; } interface ProvidersListResponse { /** Configurable providers with current routing info suitable for UI display. */ providers: ProviderInfo[]; /** Extension metadata */ _meta?: Record; } ``` ### `providers/set` `providers/set` updates the full configuration for one provider ID. ```typescript theme={null} interface SetProviderRequest { /** Provider ID to configure. */ providerId: ProviderId; /** Protocol type for this provider. */ apiType: LlmProtocol; /** Base URL for requests sent through this provider. */ baseUrl: string; /** * Full headers map for this provider. * May include authorization, routing, or other integration-specific headers. * Omitting this field is equivalent to an empty map (no headers). */ headers?: Record; /** Extension metadata */ _meta?: Record; } interface SetProviderResponse { /** Extension metadata */ _meta?: Record; } ``` ### `providers/disable` ```typescript theme={null} interface DisableProviderRequest { /** Provider ID to disable. */ providerId: ProviderId; /** Extension metadata */ _meta?: Record; } interface DisableProviderResponse { /** Extension metadata */ _meta?: Record; } ``` ### Example exchange **initialize Response:** ```json theme={null} { "jsonrpc": "2.0", "id": 0, "result": { "protocolVersion": 1, "agentInfo": { "name": "MyAgent", "version": "2.0.0" }, "agentCapabilities": { "providers": {}, "sessionCapabilities": {} } } } ``` **providers/list Request:** ```json theme={null} { "jsonrpc": "2.0", "id": 1, "method": "providers/list", "params": {} } ``` **providers/list Response:** ```json theme={null} { "jsonrpc": "2.0", "id": 1, "result": { "providers": [ { "providerId": "main", "supported": ["bedrock", "vertex", "azure", "anthropic"], "required": true, "current": { "apiType": "anthropic", "baseUrl": "http://localhost/anthropic" } }, { "providerId": "openai", "supported": ["openai"], "required": false, "current": null } ] } } ``` **providers/set Request:** ```json theme={null} { "jsonrpc": "2.0", "id": 2, "method": "providers/set", "params": { "providerId": "main", "apiType": "anthropic", "baseUrl": "https://llm-gateway.corp.example.com/anthropic/v1", "headers": { "X-Request-Source": "my-ide" } } } ``` **providers/set Response:** ```json theme={null} { "jsonrpc": "2.0", "id": 2, "result": {} } ``` **providers/disable Request:** ```json theme={null} { "jsonrpc": "2.0", "id": 3, "method": "providers/disable", "params": { "providerId": "openai" } } ``` **providers/disable Response:** ```json theme={null} { "jsonrpc": "2.0", "id": 3, "result": {} } ``` ### Behavior 1. **Capability discovery**: agents that support provider methods MUST advertise `agentCapabilities.providers: {}` in `initialize`. Clients SHOULD only call `providers/*` when this capability is present. 2. **Timing and session impact**: provider methods MUST be called after `initialize`. Clients SHOULD configure providers before creating or loading sessions. Agents MAY choose not to apply changes to already running sessions, but SHOULD apply them to sessions created or loaded after the change. 3. **List semantics**: `providers/list` returns configurable providers, their supported protocol types, current effective routing, and `required` flag. Providers SHOULD remain discoverable in list after `providers/disable`. 4. **Client behavior for required providers**: clients SHOULD NOT call `providers/disable` for providers where `required: true`. 5. **Disabled state encoding**: in `providers/list`, `current` omitted or `current: null` means the provider is disabled and MUST NOT be used by the agent for LLM calls. 6. **Set semantics and validation**: `providers/set` replaces the full configuration for the target `id` (`apiType`, `baseUrl`, `headers`); an omitted `headers` field is treated as an empty map. If `id` is unknown, `apiType` is unsupported for that provider, or params are malformed, agents SHOULD return `invalid_params`. 7. **Disable semantics**: `providers/disable` disables the target provider at runtime. A disabled provider MUST appear in `providers/list` with `current` omitted or `current: null`. If target provider has `required: true`, agents MUST return `invalid_params`. Disabling an unknown `id` SHOULD be treated as success (idempotent behavior). 8. **Scope and persistence**: provider configuration is process-scoped and SHOULD NOT be persisted to disk. ## Frequently asked questions > What questions have arisen over the course of authoring this document? ### What does `null` mean in `providers/list`? `current` omitted or `current: null` means the provider is disabled. When disabled, the agent MUST NOT route LLM calls through that provider until the client enables it again with `providers/set`. ### Why is there a `required` flag? Some providers are mandatory for agent operation and must not be disabled. `required` lets clients hide or disable the provider-disable action in UI and avoid calling `providers/disable` for those ids. ### Why not a single `providers/update` method for full list replacement? A full-list update means the client must send complete configuration (including `headers`) for all providers every time. If the client wants to change only one provider, it may not know headers for the others. In that case it cannot safely build a correct full-list payload. Also, `providers/list` does not return headers, so the client cannot simply "take what the agent returned" and send it back with one edit. Per-provider methods (`set` and `disable`) avoid this problem and keep updates explicit. ### Why doesn't `providers/list` return headers? Header values may contain secrets and should not be echoed by the agent. `providers/list` is intentionally limited to non-secret routing information (`current.apiType`, `current.baseUrl`). ### Why are `providers/list` and `providers/set` payloads different? `providers/set` accepts `headers`, including secrets, and is write-oriented. `providers/list` is read-oriented and returns only non-secret routing summary (`current`) for UI and capability discovery. ### Why is this separate from `initialize` params? Clients need capability discovery first, then provider discovery, then configuration. A dedicated method family keeps initialization focused on negotiation and leaves provider mutation to explicit steps. ### Why not use `session-config` with a `provider` category instead? `session-config` is a possible alternative, and we may revisit it as the spec evolves. We did not choose it as the primary approach in this proposal because provider routing here needs dedicated semantics that are difficult to express with today's session config model: * Multiple providers identified by `id`, each with its own lifecycle * Structured payloads (`apiType`, `baseUrl`, full `headers` map) rather than simple scalar values * Explicit discoverable (`providers/list`) and disable (`providers/disable`) semantics Today, `session-config` values are effectively string-oriented and do not define a standard multi-value/structured model for this use case. ## Revision history * 2026-04-19: Made `ProviderInfo.current` optional in `providers/list`; disabled state may be encoded as omitted `current` or `current: null` * 2026-03-22: Finalized provider disable semantics - `providers/remove` renamed to `providers/disable`, required providers are non-disableable, and disabled state is represented as `current: null` * 2026-03-21: Initial draft of provider configuration API (`providers/list`, `providers/set`, `providers/remove`) * 2026-03-07: Rename "provider" to "protocol" to reflect API compatibility level; make `LlmProtocol` an open string type with well-known values; resolve open questions on identifier standardization and model availability * 2026-03-04: Revised to use dedicated `setLlmEndpoints` method with capability advertisement * 2026-02-02: Initial draft - preliminary proposal to start discussion # Represent deleted files in diff Source: https://agentclientprotocol.com/rfds/diff-delete Author(s): [anna239](https://github.com/anna239) ## Elevator pitch > What are you proposing to change? Add flag `deleted` to [Diff](https://agentclientprotocol.com/protocol/v1/tool-calls#diffs) entity type for the case of a deleted file. ## Status quo > How do things work today and what problems does this cause? Why would we change things? Currently, in Diff entity type `newText` is not nullable, so it's not possible to distinguish between a deleted file and empty file. ## What we propose to do about it > What are you proposing to improve the situation? Add flag `deleted` to [Diff](https://agentclientprotocol.com/protocol/v1/tool-calls#diffs) entity type for the case of a deleted file. **Current structure (cannot distinguish deleted file from empty file):** ```json theme={null} { "type": "diff", "path": "/home/user/project/src/config.json", "oldText": "{\n \"debug\": false\n}", "newText": "" } ``` **Proposed structure with `deleted` flag:** ```json theme={null} { "type": "diff", "path": "/home/user/project/src/config.json", "oldText": "{\n \"debug\": false\n}", "newText": "", "deleted": true } ``` Note: we would ideally make newText nullable, but that would break existing clients. ## Shiny future > How will things will play out once this feature exists? It is possible for the agent to distinguish between a deleted file and an empty file. ## Implementation details and plan > Tell me more about your implementation. What is your detailed implementation plan? Adding the new field will be a non-breaking change, and clients that update can better distinguish between deleted and empty files. ## Frequently asked questions > What questions have arisen over the course of authoring this document or during subsequent discussions? **Do we need to represent moved files?** An agent could represent that with a deleted file at the old path and a new file at the new path. We need to rework the entire diff structure to handle more cases, and binary files, but in the meantime this provides a stop-gap until we can implement a more comprehensive solution. ### What alternative approaches did you consider, and why did you settle on this one? We considered making newText nullable, but that would break existing clients. ## Revision history 2026-02-20: Initial draft # Elicitation: Structured User Input Source: https://agentclientprotocol.com/rfds/elicitation * Author(s): [@yordis](https://github.com/yordis) * Champion: [@benbrandt](https://github.com/benbrandt) ## Elevator pitch Add support for agents to request structured information from users through a standardized elicitation mechanism, aligned with [MCP's elicitation feature](https://modelcontextprotocol.io/specification/draft/client/elicitation). This allows agents to ask follow-up questions, collect authentication credentials, gather preferences, and request required information without side-channel communication or ad-hoc client UI implementations. ## Status quo Currently, agents have two limited mechanisms for gathering user input: 1. **Session Config Options** (PR #210): Pre-declared, persistent configuration (model, mode, etc.) with default values required. These are available at session initialization and changes are broadcast to the client. 2. **Unstructured text in turn responses**: Agents can include prompts in their responses, but clients have no standardized way to recognize auth requests, form inputs, or structured selections, leading to inconsistent UX across agents. However, there is no mechanism for agents to: * Request ad-hoc information during a turn (e.g., "Which of these approaches should I proceed with?" from PR #340) * Ask for authentication credentials in a recognized, secure way (pain point from PR #330) * Collect open-ended text input with validation constraints * Handle decision points that weren't anticipated at session initialization * Request sensitive information via out-of-band mechanisms (browser-based OAuth) The community has already identified the need for this: PR #340 explored a `session/select` mechanism but concluded that leveraging an MCP-like elicitation pattern would be more aligned with how clients will already support MCP servers. PR #330 recognized that authentication requests specifically need special handling separate from regular session data. This gap limits the richness of agent-client interaction and forces both agents and clients to implement ad-hoc solutions for structured user input. ## What we propose to do about it We propose introducing an elicitation mechanism for agents to request structured information from users, aligned with [MCP's draft elicitation specification](https://modelcontextprotocol.io/specification/draft/client/elicitation). This addresses discussions from PR #340 about standardizing user selection flows and PR #330 about secure authentication handling. The mechanism would: 1. **Use restricted JSON Schema** (as discussed in PR #210): Like MCP, constrain JSON Schema to a useful subset—flat objects with primitive properties (`string`, `number`, `integer`, `boolean`) plus supported formats and enum values. Clients decide how to render UI based on the schema. 2. **Support two elicitation modes** (following [MCP SEP-1036](https://modelcontextprotocol.io/community/seps/1036-url-mode-elicitation-for-secure-out-of-band-intera)): * **Form mode** (in-band): Structured data collection via JSON Schema forms * **URL mode** (out-of-band): Browser-based flows for sensitive operations like OAuth (addressing PR #330 authentication pain points) 3. **Request/response pattern**: Agents send elicitation requests via an `elicitation/create` method and receive responses. The agent controls when to send requests and whether to wait for responses before proceeding. Unlike Session Config Options (which are persistent), elicitation requests are transient. 4. **Support client capability negotiation**: Clients declare elicitation support via a structured capability object that distinguishes between `form`-based and `url`-based elicitation (following MCP's capability model). This allows clients to support one or both modalities, enables agents to pass capabilities along to MCP servers, and handles graceful degradation when clients have limited elicitation support. 5. **Provide rich context**: Agents can include title, description, detailed constraints, and examples—helping clients render consistent, helpful UI without custom implementations. 6. **Enable out-of-band flows**: Support URL-mode elicitation (like MCP) for sensitive operations like authentication, where credentials bypass the agent entirely (addressing the core pain point in PR #330). ## Shiny future Once implemented, agents can: * Ask users "Which approach would you prefer: A or B?" and receive a structured response * Request text input: "What's the name for this function?" * Collect multiple related pieces of information in a single request * Guide users through decision trees with follow-up questions * Provide rich context (descriptions, examples, constraints) for what they're asking for Clients can: * Present a consistent, standardized UI for elicitation across all agents * Validate user input against constraints before sending to the agent * Cache elicitation history and offer suggestions based on previous responses * Provide keyboard shortcuts and accessibility features for common elicitation types ## Implementation details and plan ### Alignment with MCP This proposal follows MCP's draft elicitation specification. See [MCP Elicitation Specification](https://modelcontextprotocol.io/specification/draft/client/elicitation) for detailed guidance. ACP uses the same JSON Schema constraint approach and capability model, adapted for ACP interactions. Key differences from MCP: * MCP elicitation is tool-call-scoped; ACP elicitation may be tool-call-scoped, session-scoped, or request-scoped * ACP uses `elicitation/create` method (same as MCP) * ACP includes an optional `toolCallId` field to support tool-call-scoped elicitations (e.g., when an agent receives an MCP elicitation during a tool call and needs to redirect it to the user) * ACP must integrate with existing Session Config Options (which also use schema constraints) ### Elicitation Request Structure Agents send elicitation requests when they need information from the user. This is a request/response pattern—the agent sends the request and waits for the client's response. **Example 1: Form Mode - User Selection (from PR #340)** ```json theme={null} { "mode": "form", "message": "How would you like me to approach this refactoring?", "requestedSchema": { "type": "object", "properties": { "strategy": { "type": "string", "title": "Refactoring Strategy", "description": "Choose how aggressively to refactor", "oneOf": [ { "const": "conservative", "title": "Conservative - Minimal changes" }, { "const": "balanced", "title": "Balanced (Recommended)" }, { "const": "aggressive", "title": "Aggressive - Maximum optimization" } ], "default": "balanced" } }, "required": ["strategy"] } } ``` **Example 2: URL Mode - Authentication (from PR #330, out-of-band OAuth)** ```json theme={null} { "mode": "url", "elicitationId": "github-oauth-123", "url": "https://agent.example.com/connect?elicitationId=github-oauth-123", "message": "Please authorize access to your GitHub repositories to continue." } ``` **Example 3: Form Mode - Text Input with Constraints** ```json theme={null} { "mode": "form", "message": "What should this function be named?", "requestedSchema": { "type": "object", "properties": { "name": { "type": "string", "title": "Function Name", "description": "Must be a valid identifier", "minLength": 1, "maxLength": 64, "pattern": "^[a-zA-Z_][a-zA-Z0-9_]*$", "default": "processData" } }, "required": ["name"] } } ``` **Example 4: Form Mode - Multiple Fields** ```json theme={null} { "mode": "form", "message": "Please provide configuration details", "requestedSchema": { "type": "object", "properties": { "name": { "type": "string", "title": "Project Name" }, "port": { "type": "integer", "title": "Port Number", "minimum": 1024, "maximum": 65535, "default": 3000 }, "enableLogging": { "type": "boolean", "title": "Enable Logging", "default": true } }, "required": ["name"] } } ``` ### Elicitation Modes Following MCP's approach (specifically [SEP-1036](https://modelcontextprotocol.io/community/seps/1036-url-mode-elicitation-for-secure-out-of-band-intera)), elicitation supports two modes: **Form mode** (in-band): Servers request structured data from users using restricted JSON Schema. The client decides how to render the form UI based on the schema. **URL mode** (out-of-band): Servers direct users to external URLs for sensitive interactions that must not pass through the agent or client (OAuth flows, payments, credential collection, etc.). This distinction is reflected in the client capabilities model, allowing clients to declare support for one or both modalities. **Normative requirements:** * Clients declaring the `elicitation` capability MUST support at least one mode (`form` or `url`). * Agents MUST NOT send elicitation requests with modes that are not supported by the client. * For URL mode, the `url` parameter MUST contain a valid URL. Unknown elicitation `mode` values are reserved for future ACP variants or implementation-specific extensions. Implementation-specific modes MUST begin with `_`. Clients that do not understand a mode should preserve the raw payload when storing, replaying, proxying, or forwarding elicitation requests, but MUST NOT render it as `form` or `url` or otherwise treat it as supported. ### Restricted JSON Schema Aligning with [MCP's draft elicitation specification](https://modelcontextprotocol.io/specification/draft/client/elicitation), form mode elicitation uses a restricted subset of JSON Schema. Schemas are limited to flat objects with primitive properties only—the client decides how to render appropriate input UI based on the schema. **Supported primitive types:** 1. **String Schema** ```json theme={null} { "type": "string", "title": "Display Name", "description": "Description text", "minLength": 3, "maxLength": 50, "pattern": "^[A-Za-z]+$", "format": "email", "default": "user@example.com" } ``` Supported formats: `email`, `uri`, `date`, `date-time` 2. **Number Schema** ```json theme={null} { "type": "number", "title": "Display Name", "description": "Description text", "minimum": 0, "maximum": 100, "default": 50 } ``` Also supports `"type": "integer"` for whole numbers. 3. **Boolean Schema** ```json theme={null} { "type": "boolean", "title": "Display Name", "description": "Description text", "default": false } ``` 4. **Enum Schema** (for selections) Single-select enum (without titles): ```json theme={null} { "type": "string", "title": "Color Selection", "description": "Choose your favorite color", "enum": ["Red", "Green", "Blue"], "default": "Red" } ``` Single-select enum (with titles): ```json theme={null} { "type": "string", "title": "Color Selection", "description": "Choose your favorite color", "oneOf": [ { "const": "#FF0000", "title": "Red" }, { "const": "#00FF00", "title": "Green" }, { "const": "#0000FF", "title": "Blue" } ], "default": "#FF0000" } ``` Multi-select enum (without titles): ```json theme={null} { "type": "array", "title": "Color Selection", "description": "Choose your favorite colors", "minItems": 1, "maxItems": 2, "items": { "type": "string", "enum": ["Red", "Green", "Blue"] }, "default": ["Red", "Green"] } ``` Multi-select enum (with titles): ```json theme={null} { "type": "array", "title": "Color Selection", "description": "Choose your favorite colors", "minItems": 1, "maxItems": 2, "items": { "anyOf": [ { "const": "#FF0000", "title": "Red" }, { "const": "#00FF00", "title": "Green" }, { "const": "#0000FF", "title": "Blue" } ] }, "default": ["#FF0000", "#00FF00"] } ``` Unknown multi-select `items.type` values are reserved for future ACP variants or implementation-specific extensions. Implementation-specific values MUST begin with `_`. Clients that do not understand a multi-select item type should preserve the raw `items` schema when storing, replaying, proxying, or forwarding elicitation requests, but MUST NOT render it as string multi-select items. **Request schema structure:** ```json theme={null} "requestedSchema": { "type": "object", "properties": { "propertyName": { "type": "string", "title": "Display Name", "description": "Description of the property" }, "anotherProperty": { "type": "number", "minimum": 0, "maximum": 100 } }, "required": ["propertyName"] } ``` **Not supported** (to simplify client implementation): * Complex nested objects/arrays (beyond enum arrays) * Conditional validation * Custom formats beyond the supported list Unknown property schema `type` values are reserved for future ACP variants or implementation-specific extensions. Implementation-specific values MUST begin with `_`. Clients that do not understand a property schema type should preserve the raw schema when storing, replaying, proxying, or forwarding elicitation requests, but MUST NOT render it as a known input control. Clients use this schema to generate appropriate input forms, validate user input before sending, and provide better guidance to users. All primitive types support optional default values; clients SHOULD pre-populate form fields with these values. **Security note:** Following MCP, servers MUST NOT use form mode elicitation to request sensitive information (passwords, API keys, credentials). Sensitive data collection MUST use URL mode elicitation, which bypasses the agent and client entirely. ### Elicitation Request The agent sends an `elicitation/create` request when it needs information from the user: **Form mode example:** ```json theme={null} { "jsonrpc": "2.0", "id": 43, "method": "elicitation/create", "params": { "sessionId": "...", "toolCallId": "tc_123", "mode": "form", "message": "How would you like me to approach this refactoring?", "requestedSchema": { "type": "object", "properties": { "strategy": { "type": "string", "title": "Refactoring Strategy", "oneOf": [ { "const": "conservative", "title": "Conservative" }, { "const": "balanced", "title": "Balanced (Recommended)" }, { "const": "aggressive", "title": "Aggressive" } ], "default": "balanced" } }, "required": ["strategy"] } } } ``` **URL mode example:** ```json theme={null} { "jsonrpc": "2.0", "id": 44, "method": "elicitation/create", "params": { "requestId": 12, "mode": "url", "elicitationId": "github-oauth-001", "url": "https://agent.example.com/connect?elicitationId=github-oauth-001", "message": "Please authorize access to your GitHub repositories." } } ``` The scope fields are flattened at the top level of the request. Elicitation supports two scoping variants: * **Session scope**: `sessionId` is set — tied to a specific session. Optionally includes `toolCallId` when tied to a specific tool call within that session (e.g., when an agent receives an elicitation from an MCP server during a tool call and needs to redirect it to the user). * **Request scope**: `requestId` is set — tied to a specific JSON-RPC request outside of a session (e.g., auth/configuration phases before any session is started). **Request-scoped example:** ```json theme={null} { "jsonrpc": "2.0", "id": 45, "method": "elicitation/create", "params": { "requestId": 12, "mode": "form", "message": "Please provide your workspace name to continue setup.", "requestedSchema": { "type": "object", "properties": { "workspaceName": { "type": "string", "title": "Workspace Name", "description": "The name of your workspace" } }, "required": ["workspaceName"] } } } ``` The client presents the elicitation UI to the user. For form mode, the client generates appropriate input UI based on the JSON Schema. For URL mode, the client opens the URL in a secure browser context. ### User Response Elicitation responses use a three-action model (following MCP) to clearly distinguish between different user actions: **Accept** - User explicitly approved and submitted with data: ```json theme={null} { "jsonrpc": "2.0", "id": 43, "result": { "action": "accept", "content": { "strategy": "balanced" } } } ``` **Decline** - User explicitly declined the request: ```json theme={null} { "jsonrpc": "2.0", "id": 43, "result": { "action": "decline" } } ``` **Cancel** - User dismissed without making an explicit choice (closed dialog, pressed Escape, etc.): ```json theme={null} { "jsonrpc": "2.0", "id": 43, "result": { "action": "cancel" } } ``` Unknown elicitation `action` values are reserved for future ACP variants or implementation-specific extensions. Implementation-specific actions MUST begin with `_`. Agents that do not understand an action should preserve the raw payload when storing, replaying, proxying, or forwarding elicitation responses, but MUST NOT treat it as `accept`, `decline`, or `cancel`. For URL mode elicitation, the response with `action: "accept"` indicates that the user consented to the interaction. It does not mean the interaction is complete—the interaction occurs out-of-band and the client is not aware of the outcome until the agent sends a completion notification. Agents should handle each state appropriately: * **Accept**: Process the submitted data * **Decline**: Handle explicit decline (e.g., use default, offer alternatives) * **Cancel**: Handle dismissal (e.g., use default, prompt again later) ### Message Flow #### Form Mode Flow ```mermaid theme={null} sequenceDiagram participant User participant Client participant Agent Note over Agent: Agent initiates elicitation Agent->>Client: elicitation/create (mode: form) Note over User,Client: Present elicitation UI User-->>Client: Provide requested information Note over Agent,Client: Complete request Client->>Agent: Return user response Note over Agent: Continue processing with new information ``` #### URL Mode Flow ```mermaid theme={null} sequenceDiagram participant UserAgent as User Agent (Browser) participant User participant Client participant Agent Note over Agent: Agent initiates elicitation Agent->>Client: elicitation/create (mode: url) Client->>User: Present consent to open URL User-->>Client: Provide consent Client->>UserAgent: Open URL Client->>Agent: Accept response Note over User,UserAgent: User interaction UserAgent-->>Agent: Interaction complete Agent-->>Client: elicitation/complete (optional) Note over Agent: Continue processing with new information ``` ### Completion Notifications for URL Mode Following MCP, agents MAY send an `elicitation/complete` notification when an out-of-band interaction started by URL mode elicitation is completed: ```json theme={null} { "jsonrpc": "2.0", "method": "elicitation/complete", "params": { "elicitationId": "github-oauth-001" } } ``` Agents sending notifications: * MUST only send the notification to the client that initiated the elicitation request * MUST include the `elicitationId` established in the original request Clients: * MUST ignore notifications referencing unknown or already-completed IDs * MAY use this notification to automatically retry requests, update UI, or continue an interaction * SHOULD provide manual controls for the user to retry or cancel if the notification never arrives ### Error Handling Clients MUST return standard JSON-RPC errors for common failure cases: * When the agent sends an `elicitation/create` request with a mode not declared in client capabilities: `-32602` (Invalid params) ### Client Capabilities Clients declare elicitation support during the `initialize` phase via `ClientCapabilities`, following MCP's capability model pattern. The capability distinguishes between `form`-based and `url`-based elicitation: ```json theme={null} { "jsonrpc": "2.0", "method": "initialize", "params": { "protocolVersion": 1, "clientCapabilities": { "fs": { "readTextFile": true, "writeTextFile": true }, "terminal": true, "elicitation": { "form": {}, "url": {} } }, "clientInfo": { "name": "my-client", "version": "1.0.0" } } } ``` **Capability structure:** * `elicitation.form` - Present if the client can render form UI from restricted JSON Schema (strings, numbers, integers, booleans, enums) * `elicitation.url` - Present if the client can open URLs for out-of-band flows (OAuth, payments, credential collection) **Example: Headless client (no browser access):** ```json theme={null} "elicitation": { "form": {} } ``` **Example: Simple terminal with URL support only:** ```json theme={null} "elicitation": { "url": {} } ``` **Example: Full-featured client:** ```json theme={null} "elicitation": { "form": {}, "url": {} } ``` This structure: 1. Allows clients to declare partial support based on their environment 2. Enables agents to pass capabilities along to MCP servers they connect to 3. Maps cleanly to MCP's elicitation capability model 4. Provides clear semantics for graceful degradation Agents must gracefully handle clients that don't include this field (assumed to have no elicitation support) or that only include one of `form` or `url`. ### Backward Compatibility * If a client doesn't declare `elicitation` capabilities, agents must provide a default value and continue * If a client only declares `elicitation.form`, agents must not send URL-mode elicitation requests (or provide defaults and continue) * If a client only declares `elicitation.url`, agents must not send form-mode elicitation requests (or provide defaults and continue) * Agents should not require elicitation responses to continue operating * Following MCP: an empty capability object (`"elicitation": {}`) is equivalent to declaring support for form mode only ### Statefulness Most practical uses of elicitation require that the agent maintain state about users: * Whether required information has been collected (e.g., the user's display name via form mode elicitation) * Status of resource access (e.g., API keys or a payment flow via URL mode elicitation) Agents implementing elicitation MUST securely associate this state with individual users. Specifically: * State MUST NOT be associated with session IDs alone * State storage MUST be protected against unauthorized access * For remote agents, user identification MUST be derived from credentials acquired during authorization when possible (e.g., `sub` claim) Agents MUST NOT rely on client-provided user identification without agent-side verification, as this can be forged. ## Frequently asked questions ### Can an agent request multiple pieces of information at once? Yes—a single form mode elicitation request can include multiple fields in its `requestedSchema`. The schema is an object with multiple properties, and the client renders a form with all requested fields. For sequential information gathering, agents can send multiple elicitation requests and wait for each response before proceeding. This allows agents to adapt follow-up questions based on previous answers. The request/response model gives agents flexibility: they control when to send elicitation requests and whether to wait for responses or continue with other work. ### How does this differ from session config options? Excellent question from PR #210 discussions. Both use restricted JSON Schema, but serve different purposes: | Aspect | Session Config Options | Elicitation | | -------------------- | -------------------------------------------------- | ---------------------------------------------------------------- | | **Lifecycle** | Persistent, pre-declared at session init | Transient, request/response | | **Scope** | Session-wide configuration | Single decision point or data collection | | **Defaults** | Required (agents must have defaults) | Optional (schema's `required` array determines mandatory fields) | | **State management** | Client maintains full state, broadcast on changes | Agent receives response and decides how to proceed | | **Use cases** | Model selection, session mode, persistent settings | Authentication, clarifying questions, one-time data collection | Session Config Options are great for "how should you run this session?" Elicitation is for "what should I do next?" ### Why align with MCP's elicitation instead of creating something different? As identified in PR #340, clients will already implement MCP elicitation support for MCP servers. Aligning ACP's elicitation with MCP: * Reduces client implementation burden * Creates consistent UX across MCP and ACP agents * Lets code be shared or reused * Follows the protocol design principle of only constraining when necessary PR #340 specifically concluded: "I think we'd rather have an MCP elicitation story in general, and maybe offer the same interface outside of tool calls." ### How does authentication flow work with URL-mode elicitation? From PR #330: URL-mode elicitation allows agents to request authentication without exposing credentials to the protocol. Following [MCP's draft elicitation specification](https://modelcontextprotocol.io/specification/draft/client/elicitation): 1. Agent sends elicitation request with `mode: "url"`, an `elicitationId`, and a URL to the agent's own connect endpoint (not directly to the OAuth provider) 2. Client displays the URL to the user and requests consent to open it 3. Client responds with `action: "accept"` to indicate the user consented 4. User opens URL in their browser (out-of-band process) 5. Agent's connect page verifies the user identity matches the elicitation request 6. Agent redirects user to the OAuth provider's authorization endpoint 7. User authenticates and grants permission 8. OAuth provider redirects back to the agent's redirect\_uri 9. Agent exchanges the authorization code for tokens and stores them bound to the user's identity 10. Agent sends an `elicitation/complete` notification to inform the client **Key guarantees**: * Credentials never flow through the agent LLM or client * The agent is responsible for securely storing third-party tokens * The agent MUST verify user identity to prevent phishing attacks **Security requirements** (from MCP draft spec): Agents requesting URL mode elicitation: * MUST NOT include sensitive information about the end-user (credentials, PII, etc.) in the URL * MUST NOT provide a URL which is pre-authenticated to access a protected resource * SHOULD NOT include URLs intended to be clickable in any field of a form mode elicitation request * SHOULD use HTTPS URLs for non-development environments Clients implementing URL mode elicitation: * MUST NOT automatically pre-fetch the URL or any of its metadata * MUST NOT open the URL without explicit consent from the user * MUST show the full URL to the user for examination before consent * MUST open the URL in a secure manner that does not enable the client or LLM to inspect the content or user inputs (e.g., SFSafariViewController on iOS, not WKWebView) * SHOULD highlight the domain of the URL to mitigate subdomain spoofing * SHOULD have warnings for ambiguous/suspicious URIs (e.g., containing Punycode) * SHOULD NOT render URLs as clickable in any field of an elicitation request, except for the `url` field in a URL mode elicitation request (with the restrictions detailed above) **Phishing prevention**: The agent MUST verify that the user who started the elicitation request is the same user who completes the OAuth flow. This is typically done by checking session cookies against the user identity from the MCP authorization. ### Can agents use elicitation for information required before responding? Yes. By modeling elicitation as a request/response pattern (like MCP's `elicitation/create`), the agent controls its own flow. The agent can: * Send an elicitation request and wait for the response before proceeding * Continue with other work while waiting for user input * Chain multiple elicitations as needed for multi-step workflows This flexibility is why elicitation is modeled as a separate request/response rather than being tightly coupled to turns. ### What if a user doesn't respond to an elicitation request? Elicitation requests require a response. If the user dismisses the elicitation without making an explicit choice (closes the dialog, presses Escape, etc.), the client responds with `action: "cancel"`. The agent then decides how to proceed—it may use a default value, prompt again later, or fail the turn. This ties into the broader request cancellation work: elicitation requests can be cancelled like any other request, and the `cancel` action provides a clear signal that the user chose not to engage rather than explicitly declining. ### Should elicitation support complex nested data structures? We follow MCP's design here. MCP intentionally restricts elicitation schemas to flat objects with primitive properties to simplify client implementation and user experience. Complex nested structures, arrays of objects (beyond enum arrays), and advanced JSON Schema features are explicitly not supported. If MCP expands this in the future, ACP would follow suit. ### How should agents handle clients that don't support elicitation? Agents should always design to gracefully degrade: * Check `elicitation.form` and `elicitation.url` capabilities before sending requests * If the required mode is not supported, provide sensible default values * Describe what they're requesting in turn content (text) as fallback * Proceed with the defaults * For agents connecting to MCP servers: pass the client's elicitation capabilities to the MCP server so it can also make informed decisions ### Can we extend this to replace the existing Permission-Request mechanism? We recommend keeping them separate. Permission requests are fundamentally security decisions—allowing a tool call to proceed is distinct from the model asking for clarification or collecting user preferences. Keeping these separate allows clients to: * Offer a consistent, recognizable UX for security-sensitive decisions (permissions) * Clearly distinguish "the agent needs approval to do something" from "the agent needs information to continue" * Apply different policies (e.g., "always allow file reads" vs. per-request elicitation responses) This is the same reasoning behind keeping authentication flows (URL mode) distinct from data collection (form mode). While we may reuse some types between these mechanisms, conflating the features would blur important security boundaries. ### What about validating user input on the client side? Clients SHOULD validate user input against the provided JSON Schema **before** sending the response to the agent. This prevents invalid data from reaching the agent and provides immediate feedback to the user. Agents SHOULD also validate received data matches the requested schema, as defense-in-depth against malformed or malicious responses. If the agent requires additional validation beyond what's expressible in JSON Schema: 1. Agent validates the received value in the next turn 2. If validation fails, agent can fail the turn with an error 3. Client can then re-prompt the user (or fall back to the original default) For v1, we recommend starting with JSON Schema validation only. If more complex validation patterns emerge from real-world usage, a future RFD can specify additional validation mechanisms. ## Revision history * 2026-07-01: Removed the separate URL-required error flow; URL elicitations use `elicitation/create` and optional `elicitation/complete`. * 2026-02-06: Spec alignment review. Fixed OAuth URL examples to use agent connect endpoints (not direct OAuth provider URLs) per MCP phishing prevention guidance. Added normative requirements section (MUST support at least one mode, MUST NOT send unsupported modes, url MUST be valid). Added Error Handling section for unsupported elicitation modes. Added message flow diagrams (form mode and URL mode). Expanded safe URL handling requirements (pre-fetch prohibition, Punycode warnings, non-clickable URLs in form fields). Added server-side schema validation SHOULD requirement. Added Statefulness subsection with normative requirements for state association and user identification. * 2026-02-05: Major revision to align with MCP draft elicitation specification. Updated enum schema to use `oneOf`/`anyOf` with `const`/`title` instead of `enumNames`. Added multi-select array support. Added `pattern` field for strings. Added completion notifications section. Expanded security considerations including phishing prevention. Updated all examples to match MCP draft spec format. * 2026-02-05: Initial MCP alignment. Removed explicit "input types" in favor of restricted JSON Schema (client decides rendering). Added `mode` field (`form`/`url`). Updated capability model to use `form`/`url` sub-objects per MCP SEP-1036. Added three-action response model (`accept`/`decline`/`cancel`). Removed `password` type (MCP prohibits sensitive data in form mode). * 2026-01-12: Initial draft based on community discussions in PR #340 (user selection), PR #210 (session config alignment), and PR #330 (authentication use cases). Aligned with MCP elicitation patterns. # End-Turn Token Usage Source: https://agentclientprotocol.com/rfds/end-turn-token-usage * Author(s): [@ahmedhesham6](https://github.com/ahmedhesham6) * Champion: [@benbrandt](https://github.com/benbrandt) ## Elevator pitch > What are you proposing to change? Explore a standard way for agents to report token usage when a prompt turn ends. This RFD is intentionally kept in Draft while token accounting semantics are still being refined. Session-level context window size and cumulative cost are covered separately in the [Session Context Size and Cost RFD](/rfds/session-usage). ## Status quo > How do things work today and what problems does this cause? Why would we change things? ACP does not currently define a stable token usage shape for completed prompt turns. Agents may receive token accounting from model providers, but provider responses differ in what they count and when the final numbers become available. This creates several problems: * **No standard turn summary** - Clients cannot show a consistent token breakdown after a turn completes * **Provider mismatch** - Input, output, reasoning, and cache token categories do not map cleanly across all providers * **Ambiguous totals** - It is unclear whether reported values should describe only the completed turn or cumulative session usage * **Prompt lifecycle coupling** - The shape needs clear semantics across stop reasons, streaming updates, and model requests The session-level context and cost proposal no longer depends on resolving these token accounting questions. ## What we propose to do about it > What are you proposing to improve the situation? The current strawman is to add an optional `usage` field to the turn-completion signal. In v1, that signal is `PromptResponse`. In v2, where `session/prompt` returns when the prompt is accepted, the same `usage` object is carried on the idle `state_update` session update that ends the turn. ```json theme={null} { "jsonrpc": "2.0", "id": 1, "result": { "stopReason": "end_turn", "usage": { "totalTokens": 53000, "inputTokens": 35000, "outputTokens": 12000, "thoughtTokens": 5000, "cachedReadTokens": 5000, "cachedWriteTokens": 1000 } } } ``` For v2, the equivalent carrier is: ```json theme={null} { "jsonrpc": "2.0", "method": "session/update", "params": { "sessionId": "sess_abc123def456", "update": { "sessionUpdate": "state_update", "state": "idle", "stopReason": "end_turn", "usage": { "totalTokens": 53000, "inputTokens": 35000, "outputTokens": 12000, "thoughtTokens": 5000, "cachedReadTokens": 5000, "cachedWriteTokens": 1000 } } } } ``` This shape is not ready for Preview. The RFD should resolve the open design questions before standardizing it. ### Strawman Fields * `usage` (object, optional, nullable) - Token usage reported for the completed prompt turn * If `usage` is omitted or `null`, the agent is not reporting token usage for this turn. * `totalTokens` (number, required, non-null) - Total token count according to the final accounting semantics * `inputTokens` (number, required, non-null) - Input token count according to the final accounting semantics * `outputTokens` (number, required, non-null) - Output token count according to the final accounting semantics * `thoughtTokens` (number, optional, nullable) - Thought or reasoning tokens, if the model exposes them * `cachedReadTokens` (number, optional, nullable) - Cache read tokens, if the model exposes them * `cachedWriteTokens` (number, optional, nullable) - Cache write tokens, if the model exposes them For the optional token fields, omission and `null` are equivalent. Both mean the agent is not reporting that token category. ### Open Questions 1. **Per-turn vs cumulative** - Should token values describe only the completed prompt turn, cumulative session totals, or both? 2. **Provider category mapping** - How should ACP map provider-specific categories such as reasoning, cache read, cache write, audio, image, or tool-related tokens? 3. **Streaming semantics** - Should partial usage be allowed during streaming, or should the protocol only report final usage on the turn-completion signal? 4. **Stop reason semantics** - Should usage be included for `end_turn`, `max_tokens`, `max_turn_requests`, `refusal`, and `cancelled` stop reasons? 5. **Cost separation** - Should any per-turn cost estimate exist here, or should cost remain exclusively cumulative session state in `usage_update`? 6. **Naming** - Should field names use `Tokens` suffixes, a nested provider breakdown, or another structure that better matches future extensibility? ## Shiny future > How will things will play out once this feature exists? **For Users:** * Users can see how many tokens a completed turn consumed * Users can understand when expensive reasoning or cache behavior affected a turn * Users can compare turn behavior without confusing it with total context window utilization **For Client Implementations:** * Clients can show consistent post-turn usage summaries * Clients can distinguish context window state from turn-level token accounting * Clients can build analytics without depending on provider-specific response shapes **For Agent Implementations:** * Agents get a standard place to report final token accounting when providers expose it * Agents can omit categories they cannot calculate * Agents can avoid forcing provider-specific accounting into session context updates ## Implementation details and plan > Tell me more about your implementation. What is your detailed implementation plan? 1. Resolve whether token counts are per-turn, cumulative, or represented as separate fields. 2. Decide which token categories are standardized and how provider-specific categories can be represented without losing information. 3. Decide whether streaming usage updates are part of this RFD or a separate extension. 4. Gate this Rust crate surface behind the `unstable_end_turn_token_usage` feature. 5. Update the prompt lifecycle docs with the final semantics. 6. Update the schema and SDK types after the semantics are stable. ## Frequently asked questions > What questions have arisen over the course of authoring this document or during subsequent discussions? ### Why keep this as a separate RFD? The remaining questions are different from context window and cost reporting. Keeping this RFD separate lets context size and cost move to Preview without locking token accounting into a shape before the protocol has a clearer model for completed turns. ### Why keep token usage out of `usage_update`? `usage_update` reports session-level context size and cumulative cost. End-turn token accounting is tied to turn completion and may need different semantics, especially around per-turn and cumulative model usage. ### Why not move this to Preview with context size and cost? Context size and cost have a clear session-level contract. Token usage still needs agreement on accounting semantics, provider category mapping, and how it interacts with turn completion. ### What alternative approaches did you consider, and why did you settle on this one? **Everything in `usage_update`** - Rejected for now because turn-level token accounting should not be conflated with session context state. **Only cumulative totals** - Simple for dashboards, but less useful for understanding the cost of a specific completed prompt turn. **Only per-turn totals** - Useful for post-turn summaries, but may not satisfy clients that want cumulative session analytics. **Provider-specific blobs** - Preserves information, but makes client UI inconsistent and harder to standardize. ## Revision history * 2026-06-02: Split from the original combined session usage and context RFD for separate draft discussion. # Introduce RFD Process Source: https://agentclientprotocol.com/rfds/introduce-rfd-process Author(s): [@benbrandt](https://github.com/benbrandt) ## Elevator pitch > What are you proposing to change? Bullet points welcome. Introduce a "Request for Dialog" (RFD) process to replace ad-hoc design discussions with structured, community-friendly design documents that track features from conception to completion. ## Status quo > How do things work today and what problems does this cause? Why would we change things? Currently all development is being done primarily by the Zed team tracking requests and proposals from multiple teams. The goal is to create a process that helps to keep files organized and which can scale to participation by an emerging community. ## Shiny future > How will things will play out once this feature exists? ### Project licensing All code and RFDs are licensed under an Apache 2.0 license. The project is intended to remain open source and freely available in perpetuity. ### Decision making For the initial phase, the project shall have a "design team" that consists of the Zed team acting in "BDFL" capacity. The expectation is that the project will setup a more structure governance structure as it grows. The design team makes all decisions regarding RFDs and sets overall project direction. ### RFD lifecycle #### RFDs are proposed by opening a PR An RFD begins as a PR adding a new file into the "Draft" section. The RFD can start minimal - just an elevator pitch and status quo are enough to begin dialog. Pull requests become the discussion forum where ideas get refined through collaborative iteration. As discussion proceeds, the FAQ of the RFD should be extended. If discussion has been going long enough, the PR should be closed, feedback summarized, and then re-opened with a link to the original PR. #### The PR is merged into "draft" once a core team member decides to champion it RFD proposals are merged into the "draft" section if a core team member decides to champion them. The champion is then the point-of-contact for that proposal going forward and they will work with the proposal authors and others to make it reality. Core team members do not need to seek consensus to merge a proposal into the draft, but they should listen carefully to concerns from other core team members, as it will be difficult to move the RFD forward if those concerns are not ultimately addressed. Once a proposal is moved to draft, code and implementation may begin to land into the PR. This work needs to be properly feature gated and marked with the name of the RFD. Further discussion on the RFD can take place on [Zulip](https://agentclientprotocol.zulipchat.com/) if needed. #### Moving to the "active" section When core maintainers or a working group are actively spending bandwidth on an RFD, the champion can open a PR to move the file to the active section. Active RFDs are the proposals the project is currently trying to move forward. This is a visibility signal, not a stability commitment or final approval. Active RFDs should have a current owner, a plausible implementation or design plan, and should be updated as the work changes. If work pauses or the owner no longer has bandwidth, the RFD can move back to draft. #### Moving to the "preview" section Once the champion feels the RFD is ready for others to check it out, they can open a PR to move the file to the preview section. This is a signal to the community (and particularly other core team members) to check out the proposal and see what they think. The PR should stay open for "a few days" to give people an opportunity to leave feedback. The champion is empowered to decide whether to land the PR. As ever, all new feedback should be recorded in the FAQ section. #### Deciding to complete an RFD When they feel the RFD is ready to be completed, the champion requests review by the team. The team can raise concerns and notes during discussion. Final decision on an RFD is made by the core team lead. #### Implementation of an RFD Active RFDs are living documents that track implementation progress. Status badges in design documentation link back to the relevant RFD, creating a clear connection between "why we're building this" and "how it works." When building code with an agent, agents should read RFDs during implementation to understand design rationale and update them with implementation progress. ### Moderating and managing RFD discussions Moving RFDs between points in the cycle involve opening PRs. Those PRs will be places to hold dialog and discussion -- but not the only place, we expect more detailed discussions to take place on [Zulip](https://agentclientprotocol.zulipchat.com/) or other communication channels. RFD owners and champions should actively "curate" discussions by collecting questions that come up and ensuring they are covered in the FAQ. Duplicate questions can be directed to the FAQ. If the discussion on the PR gets to the point where Github begins to hide comments, the PR should typically be closed, feedback collected, and then re-opened. ## Implementation plan > What is your implementation plan? * ✅ Create RFD infrastructure (about, TEMPLATE, navigation setup) * ✅ Establish lifecycle: Draft → Active → Preview → Completed * ⏳ Write RFDs for major in-progress features ## Frequently asked questions ### Why "Request for Dialog" and not "Request for Comment"? Well, partly because "dialog" emphasizes conversation and exploration rather than just collecting feedback on a predetermined design. We also shamelessly stole this process from [Niko Matsakis and the Symposium project](https://symposium-dev.github.io/symposium/rfds/index.html) (with permission) so that we could benefit from their experience. ## Revision history * 2026-07-02: Added Active between Draft and Preview to show which RFDs have current maintainer or working-group bandwidth. * 2025-10-28: Initial version, created alongside RFD infrastructure # Logout Method Source: https://agentclientprotocol.com/rfds/logout-method * Author(s): [@anna239](https://github.com/anna239) ## Elevator pitch > What are you proposing to change? Add a `logout` method that allows clients to terminate an authenticated session with an agent. This is the counterpart to the existing `authenticate` method and enables proper session cleanup and credential invalidation. ## Status quo > How do things work today and what problems does this cause? Why would we change things? Currently, ACP provides an `authenticate` method for establishing authenticated sessions, but there is no standardized way to: * Log out of an authenticated session * Invalidate credentials or tokens * Signal to the agent that the user wants to end their authenticated state Users who want to switch accounts, revoke access, or simply log out must rely on: * Manually clearing credentials outside of ACP * Agent-specific workarounds This creates inconsistent user experiences and potential security concerns when credentials should be invalidated but aren't. ## Shiny future > How will things play out once this feature exists? Clients will be able to offer a proper "Log out" button that: 1. Cleanly terminates the authenticated session 2. Allows the agent to invalidate tokens/credentials as needed 3. Returns the connection to an unauthenticated state 4. Enables the user to re-authenticate with different credentials ## Implementation details and plan > Tell me more about your implementation. What is your detailed implementation plan? ### New Method: `logout` A new method that terminates the current authenticated session. #### LogoutRequest ```typescript theme={null} interface LogoutRequest { /** Extension metadata */ _meta?: Record; } ``` #### LogoutResponse ```typescript theme={null} interface LogoutResponse { /** Extension metadata */ _meta?: Record; } ``` ### Capability Advertisement The `logout` capability is advertised within the `auth` object in `AgentCapabilities`: ```typescript theme={null} interface AgentCapabilities { // ... existing fields ... /** Authentication-related capabilities */ auth?: AgentAuthCapabilities; } interface AgentAuthCapabilities { /** Extension metadata */ _meta?: Record; /** Agent supports the logout method. Supply `{}` to indicate support. */ logout?: LogoutCapabilities; } interface LogoutCapabilities { /** Extension metadata */ _meta?: Record; } ``` ### JSON Schema Additions ```json theme={null} { "$defs": { "AgentAuthCapabilities": { "description": "Authentication-related capabilities supported by the agent.", "properties": { "_meta": { "additionalProperties": true, "type": ["object", "null"] }, "logout": { "allOf": [ { "$ref": "#/$defs/LogoutCapabilities" } ], "description": "Whether the agent supports the logout method. Supply `{}` to indicate support." } }, "type": "object" }, "LogoutCapabilities": { "description": "Logout capabilities supported by the agent. Supply `{}` to indicate support.", "properties": { "_meta": { "additionalProperties": true, "type": ["object", "null"] } }, "type": "object" }, "LogoutRequest": { "description": "Request to terminate the current authenticated session.", "properties": { "_meta": { "additionalProperties": true, "type": ["object", "null"] } }, "type": "object", "x-method": "logout", "x-side": "agent" }, "LogoutResponse": { "description": "Response to the logout method.", "properties": { "_meta": { "additionalProperties": true, "type": ["object", "null"] } }, "type": "object", "x-method": "logout", "x-side": "agent" } } } ``` ### Example Exchange **Request:** ```json theme={null} { "jsonrpc": "2.0", "id": 1, "method": "logout", "params": {} } ``` **Response:** ```json theme={null} { "jsonrpc": "2.0", "id": 1, "result": {} } ``` ### Behavior 1. **Pre-condition**: The client should only call `logout` if: * The agent advertises `agentCapabilities.auth.logout: {}` 2. **Agent responsibilities**: * Invalidate any stored tokens or credentials as appropriate * Clean up any session state associated with the authenticated user * Return the connection to an unauthenticated state 3. **Post-condition**: After a successful `logout`: * Subsequent requests that require authentication should return `auth_required` error * The client can call `authenticate` again to establish a new authenticated session 4. **Active sessions**: If there are active sessions when `logout` is called, the agent should either: * Terminate them gracefully * Throw an `auth_required` error ## Frequently asked questions > What questions have arisen over the course of authoring this document? ### Should logout affect active sessions? This is left as implementation-defined. Some agents may want to: * Automatically terminate all sessions (strict security) * Keep sessions running The RFD intentionally does not mandate a specific behavior to allow flexibility. ## Revision history * 2026-05-21: RFD marked as Completed; `logout` is stabilized * 2026-05-17: Moved to Preview. * 2026-02-02: Initial draft # MCP-over-ACP: MCP Transport via ACP Channels Source: https://agentclientprotocol.com/rfds/mcp-over-acp Author(s): [nikomatsakis](https://github.com/nikomatsakis) ## Elevator pitch > What are you proposing to change? Add support for MCP servers that communicate over ACP channels instead of stdio or HTTP. This enables any ACP component to provide MCP tools and handle callbacks through the existing ACP connection, without spawning separate processes or managing additional transports. ## Status quo > How do things work today and what problems does this cause? Why would we change things? ACP and MCP each solve different halves of the problem of interacting with an agent. ACP stands in "front" of the agent, managing sessions, sending prompts, and receiving responses. MCP stands "behind" the agent, providing tools that the agent can use to do its work. Many applications would benefit from being able to be both "in front" of the agent and "behind" it. This would allow a client, for example, to create custom MCP tools that are tailored to a specific request and which live in the client's address space. The only way to combine ACP and MCP today is to use some sort of "backdoor", such as opening an HTTP port for the agent to connect to or providing a binary that communicates with IPC. This is inconvenient to implement but also means that clients cannot be properly abstracted and sandboxed, as some of the communication with the agent is going through side channels. Imagine trying to host an ACP component (client, agent, or [agent extension](./proxy-chains.mdx)) that runs in a WASM sandbox or even on another machine: for that to work, the ACP protocol has to encompass all of the relevant interactions so that messages can be transmitted properly. ## What we propose to do about it > What are you proposing to improve the situation? We propose adding `"acp"` as a new MCP transport type. When an ACP component (client or proxy) adds an MCP server with ACP transport to a session, tool invocations for that server are routed back through the ACP channel to the component that provided it. This enables patterns like: * A **client** that injects project-aware tools into every session and handles callbacks directly * An **[agent extension](./proxy-chains.mdx)** that adds context-aware tools based on the conversation state * A **bridge** that translates ACP-transport MCP servers to stdio for agents that don't support native ACP transport ### How it works When the client connects, the agent advertises MCP-over-ACP support via `mcpCapabilities.acp` in its `InitializeResponse`. If supported, the client can add MCP servers to a `session/new` request with `"type": "acp"` and an `id` that identifies the server: ```json theme={null} { "tools": { "mcpServers": [ { "type": "acp", "name": "project-tools", "id": "550e8400-e29b-41d4-a716-446655440000" } ] } } ``` The `id` is generated by the component providing the MCP server. When the agent connects to the MCP server, an `mcp/connect` message is sent with the MCP server's `id`. This returns a fresh `connectionId`. MCP messages are then sent back and forth using `mcp/message` requests and notifications. Finally, `mcp/disconnect` signals that the connection is closing. `mcp/connect` and `mcp/disconnect` are initiated by the side connecting to the ACP-transport MCP server. In the direct client-provided server case, that means the agent sends them to the client. Once connected, `mcp/message` is bidirectional: the agent can send MCP client-originated requests to the server, and the server can send MCP server-originated requests or notifications back to the agent. ### Bridging and compatibility Existing agents don't support ACP transport for MCP servers. To bridge this gap, a wrapper component can translate between ACP-transport MCP servers and the stdio/HTTP transports that agents already support. The wrapper spawns shim processes or HTTP servers that the agent connects to normally, then relays messages to/from the ACP channel. We've implemented this bridging as part of the conductor described in the [Proxy Chains RFD](./proxy-chains). The conductor always advertises `mcpCapabilities.acp: true` to its clients, handling the translation transparently regardless of whether the downstream agent supports native ACP transport. ### Message flow example ```mermaid theme={null} sequenceDiagram participant Client participant Agent Client->>Agent: session/new (with ACP-transport MCP server) Agent-->>Client: session created Client->>Agent: prompt ("analyze this codebase") Note over Agent: Agent decides to use the tool Agent->>Client: mcp/connect (acpId: "") Client-->>Agent: connectionId: "conn-1" Agent->>Client: mcp/message (list_files tool call) Client-->>Agent: file listing results Client->>Agent: mcp/message (server callback or notification) Agent-->>Client: callback result, if request Agent-->>Client: response using tool results Agent->>Client: mcp/disconnect (connectionId: "conn-1") ``` ## Shiny future > How will things play out once this feature exists? ### Seamless tool injection Components can provide tools without any process management. A Rust development environment could inject cargo-aware tools, a cloud IDE could inject deployment tools, and a security scanner could inject vulnerability checking - all through the same ACP connection they're already using. ### WebAssembly-based tooling Components running in sandboxed environments (like WASM) can provide MCP tools without needing filesystem or process spawning capabilities. The ACP channel is their only interface, and that's sufficient. ### Transparent bridging For agents that don't natively support ACP transport, intermediaries can transparently bridge: accepting MCP-over-ACP from clients and spawning stdio- or HTTP-based MCP servers that the agent can use normally. This provides backwards compatibility while allowing the ecosystem to adopt ACP transport incrementally. ## Implementation details and plan > Tell me more about your implementation. What is your detailed implementation plan? ### Capability advertising Agents advertise MCP-over-ACP support via the [`mcpCapabilities`](/protocol/v1/schema#mcpcapabilities) field in their `InitializeResponse`. We propose adding an `acp` field to this existing structure: ```json theme={null} { "capabilities": { "mcpCapabilities": { "http": false, "sse": false, "acp": true } } } ``` When `mcpCapabilities.acp` is `true`, the agent can handle MCP servers declared with `"type": "acp"` natively. It will initiate `mcp/connect` and `mcp/disconnect` through the ACP channel, and both sides can exchange MCP payloads with `mcp/message`. Clients don't need to advertise anything - they simply check the agent's capabilities to determine whether bridging is needed. **Bridging intermediaries**: An intermediary that provides bridging can present `mcpCapabilities.acp: true` to its clients regardless of whether the downstream agent supports it, handling bridging transparently (see [Bridging](#bridging-for-agents-without-native-support) below). ### MCP transport schema extension We extend the MCP server JSON schema to include ACP as a transport option: ```json theme={null} { "type": "object", "properties": { "type": { "type": "string", "const": "acp" }, "name": { "type": "string" }, "id": { "type": "string" }, "_meta": { "type": ["object", "null"] } }, "required": ["type", "name", "id"] } ``` ### Message reference **Connection lifecycle:** ```json theme={null} // Establish MCP connection { "method": "mcp/connect", "params": { "acpId": "550e8400-e29b-41d4-a716-446655440000", "_meta": { ... } } } // Response result: { "connectionId": "conn-123", "_meta": { ... } } // Close MCP connection { "method": "mcp/disconnect", "params": { "connectionId": "conn-123", "_meta": { ... } } } // Response result: { "_meta": { ... } } ``` **MCP message exchange:** `mcp/message` is bidirectional. Either side can send the following request or notification shape on an established `connectionId`. ```json theme={null} // Send MCP request { "id": 123, "method": "mcp/message", "params": { "connectionId": "conn-123", "method": "", "params": { ... }, "_meta": { ... } } } // Response result: { ... inner MCP result payload ... } // Send MCP notification { "method": "mcp/message", "params": { "connectionId": "conn-123", "method": "", "params": { ... }, "_meta": { ... } } } ``` The inner MCP message fields (`method`, `params`) are flattened into the params object. The `params` field is optional; if omitted or set to `null`, the inner MCP message has no params. Whether the wrapped message is a request or notification is determined by the presence of an `id` field in the outer JSON-RPC envelope, following JSON-RPC conventions. For requests, the ACP response result is the inner MCP result payload, and inner MCP errors are represented with the outer JSON-RPC error response. ### Routing by ID The `acpId` in `mcp/connect` matches the `id` that was provided by the component when it declared the MCP server in `session/new`. The receiving side uses this `id` to route messages to the correct handler. When a component provides multiple MCP servers in a single session, each gets a unique `id`, enabling proper message routing. ### Connection multiplexing Multiple connections to the same MCP server are supported - each `mcp/connect` returns a unique `connectionId`. This allows scenarios where an agent opens multiple concurrent connections to the same tool server. ### Bridging for agents without native support Not all agents will support MCP-over-ACP natively. To maintain compatibility, it is possible to write a bridge that translates ACP-transport MCP servers to transports the agent does support. **Bridging approaches:** * **Stdio shim**: Spawn a small shim process that the agent connects to via stdio. The shim relays MCP messages to/from the ACP channel. This is the most compatible approach since all MCP-capable agents support stdio. * **HTTP bridge**: Run a local HTTP server that the agent connects to. MCP messages are relayed to/from the ACP channel. This works for agents that prefer HTTP transport. **How bridging works:** When a client provides an MCP server with `"type": "acp"`, and the agent doesn't advertise `mcpCapabilities.acp: true`, a bridge can: 1. Rewrite the MCP server declaration in `session/new` to use stdio or HTTP transport 2. Spawn the appropriate shim process or HTTP server 3. Relay messages between the shim and the ACP channel From the agent's perspective, it's talking to a normal stdio/HTTP MCP server. From the client's perspective, it's handling MCP-over-ACP messages. The bridge handles the translation transparently. ```mermaid theme={null} sequenceDiagram participant Client participant Bridge participant Shim as Stdio Shim participant Agent Note over Bridge: Agent doesn't support mcpCapabilities.acp Client->>Bridge: session/new (MCP server with acp transport) Bridge->>Agent: session/new (MCP server with stdio transport) Note over Bridge: Spawns shim for bridging Agent->>Shim: MCP tool call (stdio) Shim->>Bridge: relay Bridge->>Client: mcp/message Client-->>Bridge: tool result Bridge-->>Shim: relay Shim-->>Agent: MCP response (stdio) ``` A first implementation of this bridging exists in the `sacp-conductor` crate, part of the proposed new version of the [ACP Rust SDK](https://github.com/anthropics/rust-sdk). ## Frequently asked questions > What questions have arisen over the course of authoring this document or during subsequent discussions? ### Why use a separate `id` instead of server names? Server names in `mcpServers` are chosen by whoever adds them to the session, and could potentially collide if multiple components add servers. A component-generated `id` provides guaranteed uniqueness and allows the providing component to correlate incoming messages back to the correct session context. This also avoids a potential deadlock: some agents don't return the session ID until after MCP servers have been initialized. Using a component-generated `id` avoids any dependency on agent-provided identifiers. ### How does this relate to proxy chains? MCP-over-ACP is a transport mechanism that works independently of proxy chains. However, proxy chains are a natural use case: a proxy can inject MCP servers into sessions it forwards, handle the tool callbacks, and use the results to enhance its transformations. See the [Proxy Chains RFD](./proxy-chains) for details on how MCP-over-ACP enables context-aware tooling. ### What if the agent doesn't support ACP transport? See the [Bridging for agents without native support](#bridging-for-agents-without-native-support) section above. A bridge can transparently translate ACP-transport MCP servers to stdio or HTTP for agents that don't advertise `mcpCapabilities.acp` support. ### What about security? MCP-over-ACP has the same trust model as regular MCP: you're allowing a component to handle tool invocations. The difference is transport, not trust. Components should only add MCP servers from sources they trust, same as with stdio or HTTP transport. ## Revision history Split from proxy-chains RFD to enable independent use of MCP-over-ACP transport by any ACP component, not just proxies. # Message ID Source: https://agentclientprotocol.com/rfds/message-id * Author(s): [@michelTho](https://github.com/michelTho), [@nemtecl](https://github.com/nemtecl) * Champion: [@benbrandt](https://github.com/benbrandt) ## Elevator pitch Add a `messageId` field to `agent_message_chunk`, `user_message_chunk`, `agent_thought_chunk` session updates to uniquely identify individual messages within a conversation. Only agents generate message IDs, and the IDs are opaque strings. This enables clients to distinguish between different messages beyond changes in update type and lays the groundwork for future capabilities like message editing and session deduplication. ## Status quo Currently, when an Agent sends message chunks via `session/update` notifications, there is no explicit identifier for the message being streamed: ```json theme={null} { "jsonrpc": "2.0", "method": "session/update", "params": { "sessionId": "sess_abc123def456", "update": { "sessionUpdate": "agent_message_chunk", "content": { "type": "text", "text": "Let me analyze your code..." } } } } ``` This creates several limitations: 1. **Ambiguous message boundaries** - When the Agent sends multiple messages in sequence (e.g., alternating between agent and user messages, or multiple agent messages), Clients can only infer message boundaries by detecting a change in the `sessionUpdate` type. If an Agent sends consecutive messages of the same type, Clients cannot distinguish where one message ends and another begins. 2. **Non-standard workarounds** - Currently, implementations rely on the `_meta` field to work around this limitation. While functional, this approach is not standardized and each implementation may use different conventions. 3. **Limited future capabilities** - Without stable message identifiers, it's difficult to build features like: * Message editing or updates * Message-specific metadata or annotations * Message threading or references * Undo/redo functionality As an example, consider this sequence where a Client cannot reliably determine message boundaries: ```json theme={null} // First agent message chunk { "sessionUpdate": "agent_message_chunk", "content": { "type": "text", "text": "Analyzing..." } } // More chunks... but is this still the same message or a new one? { "sessionUpdate": "agent_message_chunk", "content": { "type": "text", "text": "Found issues." } } // Tool call happens { "sessionUpdate": "tool_call", ... } // Another agent message - definitely a new message { "sessionUpdate": "agent_message_chunk", "content": { "type": "text", "text": "Fixed the issues." } } ``` ## What we propose to do about it Add a `messageId` field to `AgentMessageChunk`, `UserMessageChunk`, and `AgentThoughtChunk` session updates. This field would: 1. **Provide stable message identification** - Each message gets a unique identifier that remains constant across all chunks of that message. 2. **Enable reliable message boundary detection** - Clients can definitively determine when a new message starts by observing a change in `messageId`. 3. **Create an extension point for future features** - Message IDs can be referenced in future protocol enhancements. ### Proposed Structure The `session/prompt` request and response do not include a message ID. Clients do not create protocol message IDs. If the Agent emits the accepted user message through `session/update`, it generates and attaches the appropriate message ID to that user message. If the Agent sends `user_message_chunk` updates (e.g., during `session/load`), it uses the user message ID: ```json theme={null} { "jsonrpc": "2.0", "method": "session/update", "params": { "sessionId": "sess_abc123def456", "update": { "sessionUpdate": "user_message_chunk", "messageId": "msg_user_8f7a1", "content": { "type": "text", "text": "Can you analyze this code?" } } } } ``` For agent message chunks, the Agent also generates and includes a `messageId`: ```json theme={null} { "jsonrpc": "2.0", "method": "session/update", "params": { "sessionId": "sess_abc123def456", "update": { "sessionUpdate": "agent_message_chunk", "messageId": "msg_agent_c42b9", "content": { "type": "text", "text": "Let me analyze your code..." } } } } ``` The `messageId` field would be: * **Optional in v1** on `agent_message_chunk`, `user_message_chunk`, and `agent_thought_chunk` updates. An omitted `messageId` and an explicit `null` are equivalent and both mean the Agent did not provide a message ID for that chunk. * **Required in v2** on `agent_message_chunk`, `user_message_chunk`, and `agent_thought_chunk` updates. The field MUST be present and MUST be a non-null string. * **Agent-generated** - the Agent is the only participant that creates protocol message IDs * **Unique per message** within a session * **Stable across chunks** - all chunks belonging to the same message share the same `messageId` * **Opaque** - Implementations treat it as an identifier without parsing its structure In v1, if an Agent supports message IDs and emits an accepted or replayed user message through `session/update`, it SHOULD include `messageId` on that user message update. In v2, every streamed user, agent, and thought message chunk MUST include a `messageId`. ## Shiny future Once this feature exists: 1. **Clear message boundaries** - Clients can reliably render distinct message bubbles in the UI, even when multiple messages of the same type are sent consecutively. 2. **Better streaming UX** - Clients know exactly which message element to append chunks to, enabling smoother visual updates. 3. **Foundation for editing** - With stable message identifiers, future protocol versions could add: * `message/edit` - Agent updates the content of a previously sent message * `message/delete` - Agent removes a message from the conversation * `message/replace` - Agent replaces an entire message with new content 4. **Message metadata** - Future capabilities could reference messages by ID: * Annotations or reactions to specific messages * Citation or cross-reference between messages * Tool calls that reference which message triggered them 5. **Enhanced debugging** - Implementations can trace message flow more easily with explicit IDs in logs and debugging tools. Example future editing capability: ```json theme={null} { "jsonrpc": "2.0", "method": "session/update", "params": { "sessionId": "sess_abc123def456", "update": { "sessionUpdate": "message_update", "messageId": "ea87d0e7-beb8-484a-a404-94a30b78a5a8", "updateType": "replace", "content": { "type": "text", "text": "Actually, let me correct that analysis..." } } } } ``` ## Implementation details and plan ### Phase 1: Core Protocol Changes 1. **Update schema** (`schema/schema.json`): * Add optional `messageId` field (type: `string`) to v1 `ContentChunk` (used by `AgentMessageChunk`, `UserMessageChunk`, `AgentThoughtChunk`) * Add required `messageId` field (type: `string`) to v2 `ContentChunk` 2. **Update Rust SDK** (`rust/client.rs` and `rust/agent.rs`): * Add a dedicated `MessageId` newtype * Add `message_id: Option` field to the v1 `ContentChunk` struct * Add `message_id: MessageId` field to the v2 `ContentChunk` struct * Update serialization to include `messageId` in JSON output when present for v1 and always for v2 3. **Update TypeScript SDK** (if applicable): * Add `messageId` field to corresponding session update types 4. **Update documentation** (`docs/protocol/prompt-turn.mdx`): * Document the `messageId` field and its semantics * Add examples showing message boundaries * Explain that `messageId` changes indicate new messages ### Phase 2: Reference Implementation 5. **Update example agents**: * Modify example agents to generate and include `messageId` in chunks * Demonstrate consistent IDs across chunks of the same message 6. **Update example clients**: * Update clients to consume `messageId` field * Use IDs to properly group chunks into messages * Demonstrate clear message boundary rendering ### Backward Compatibility The `messageId` field is **optional in v1** to ensure this is a non-breaking change. Agents SHOULD include the `messageId` field on message chunks they can identify, but it is not required for v1 compatibility. Features that rely on `messageId` (such as future message editing capabilities) will implicitly require the field to be present - Agents that don't provide it simply won't support those features. The field is **required in v2**, where breaking protocol changes are allowed. v2 agents MUST include `messageId` on every streamed message chunk, and v2 clients can reject chunks where the field is omitted or `null`. ## Frequently asked questions ### What alternative approaches did you consider, and why did you settle on this one? 1. **Continue using `_meta` field** - This is the current workaround but: * Not standardized across implementations * Doesn't signal semantic importance * Easy to overlook or implement inconsistently 2. **Detect message boundaries heuristically** - Clients could infer boundaries from timing, content types, or session state: * Unreliable and fragile * Doesn't work for all scenarios (e.g., consecutive same-type messages) * Creates inconsistent behavior across implementations 3. **Use explicit "message start/end" markers** - Wrap messages with begin/end notifications: * More complex protocol interaction * Requires additional notifications * More state to track on both sides 4. **Client-generated prompt IDs** - Let Clients generate IDs for `session/prompt` requests: * Creates two sources of truth for protocol IDs * Requires agreement on uniqueness across clients and agents * Conflicts with the Agent's role as owner of the session history The proposed approach with `messageId` is: * **Simple** - Just one new field with clear semantics * **Flexible** - Enables future capabilities without further protocol changes * **Practical** - IDs come from the same side that owns and emits session history * **Format-agnostic** - Agents can use identifiers that fit their own storage model ### Who generates message IDs? **Only the Agent generates protocol message IDs.** * **For user messages**: The Client sends a prompt without a message ID. If the Agent emits that accepted or replayed user message as a `session/update`, the Agent generates the message ID and includes it in that notification. * **For agent messages and thoughts**: The Agent generates the ID when creating the message or thought and includes it in session update chunks. This matches other protocol identifiers (`sessionId`, `terminalId`, `toolCallId`) which are agent-generated, and provides practical benefits: * **Single source of truth** - The Agent owns session persistence and message ordering * **No format coordination** - The protocol does not need to standardize UUIDs or another shared ID format * **Adapter-friendly** - Adapters for agents that don't support message IDs can simply omit them ### Should this field be required or optional? The field is **optional** for v1 to ensure backward compatibility. Agents SHOULD include `messageId`, but it is not required. In v1, omitting `messageId` and sending `null` are equivalent and both mean no message ID was provided. The field is **required** for v2. The key MUST be present and its value MUST be a non-null string. ### What format should message IDs use? Message IDs are opaque strings generated by the Agent. Clients MUST compare message IDs as opaque strings and MUST NOT parse or infer meaning from their structure. Because IDs only come from the Agent, collision avoidance is the Agent's responsibility within the session. ### What about message IDs across session loads? When a session is loaded via `session/load`, the Agent may: * Preserve original message IDs if replaying the conversation history * Generate new message IDs if only exposing current state The protocol doesn't require message IDs to be stable across session loads, though Agents MAY choose to make them stable if their implementation supports it. ### Does this apply to other session updates like tool calls or plan updates? This RFD addresses `agent_message_chunk`, `user_message_chunk`, and `agent_thought_chunk` updates. Other session update types (like `tool_call`, `plan`) already have their own identification mechanisms: * Tool calls use `toolCallId` * Plan entries can be tracked by their position in the `entries` array Future RFDs may propose extending `messageId` to other update types if use cases emerge. ## Revision history * **2026-06-05**: Moved to Completed and stabilized optional v1 `messageId` in the stable protocol artifacts * **2026-06-03**: Moved the v1 portion of the RFD to Preview while v2-specific behavior remains part of the v2 draft work * **2026-06-03**: Moved the RFD out of the v2 Draft group while keeping v2-specific behavior; added a dedicated `MessageId` type, and made v2 require message IDs on streamed message chunks while v1 keeps optional message IDs for compatibility * **2026-06-02**: Updated the proposal so message IDs are Agent-generated only, removed client-provided prompt IDs and prompt response acknowledgments, and removed the UUID requirement in favor of opaque strings * **2026-02-17**: Added "Message ID Acknowledgment" section to clarify that presence/absence of `userMessageId` in response indicates whether the Agent recorded the ID; clarified that UUID format is MUST (not SHOULD) since both sides generate IDs; renamed response field to `userMessageId` for clarity (request keeps `messageId`) * **2026-01-29**: Updated to allow both clients and agents to generate message IDs using UUID format * **2025-11-09**: Initial draft # Meta Field Propagation Conventions Source: https://agentclientprotocol.com/rfds/meta-propagation * Author(s): [Adrian Cole](https://github.com/codefromthecrypt) * Champion: [@benbrandt](https://github.com/benbrandt) ## Elevator pitch Document `params._meta` as the convention for propagating metadata from clients to agents, such as trace identifiers or correlation IDs. This aligns with [MCP](https://modelcontextprotocol.io/), enabling shared instrumentation since both protocols use stdio JSON-RPC transports. ## Status quo ACP clients already propagate context to agents via `_meta`. For example, `requestId` is used for request correlation in [AionUi](https://github.com/iOfficeAI/AionUi/blob/main/src/common/codex/types/eventData.ts#L12-L16). However, the [extensibility](/protocol/v1/extensibility) documentation does not specify the `_meta` type or document its use for propagation. Without documentation, parties must coordinate ad hoc, which can lead to portability accidents (such as one side using `_meta.traceparent` and the other `_meta.otel.traceparent`). Documenting that propagated fields are root keys in `_meta` prevents this. ## What we propose to do about it Update the [extensibility](/protocol/v1/extensibility#the-_meta-field) documentation with two changes: 1. Add the type `{ [key: string]: unknown }` to the existing summary sentence. This type is compatible with MCP SDKs. 2. Add a new paragraph after the JSON example about propagation conventions. ## Shiny future * Same instrumentation (OpenInference, etc.) works for both ACP and MCP. * Observability tools can correlate traces across protocols. ## Implementation details **Change 1**: Update the existing summary sentence in [extensibility](/protocol/v1/extensibility#the-_meta-field): ```diff theme={null} -All types in the protocol include a `_meta` field that implementations can use to attach custom information. +All types in the protocol include a `_meta` field with type `{ [key: string]: unknown }` that implementations can use to attach custom information. ``` **Change 2**: After the JSON example, before "Implementations **MUST NOT**", add: > Clients may propagate fields to the agent for correlation purposes, such as `requestId`. The following root-level keys in `_meta` **SHOULD** be reserved for [W3C trace context](https://www.w3.org/TR/trace-context/) to guarantee interop with existing MCP implementations and OpenTelemetry tooling: > > * `traceparent` > * `tracestate` > * `baggage` ## FAQ ### Why document this now? Clients already propagate context via `_meta`. Documenting prevents incompatible drift and enables shared tooling with MCP. ### Why reference MCP? ACP and MCP are the two core agentic protocols, both using stdio JSON-RPC. Where `_meta` types are compatible, instrumentation code can be abstracted and reused for both: Here are several MCP SDKs that propagate W3C trace-context in `_meta`: * [MCP C# SDK](https://github.com/modelcontextprotocol/csharp-sdk) - native W3C trace-context propagation * [OpenInference](https://github.com/Arize-ai/openinference) - Python and TypeScript MCP instrumentation (collaboration between Arize and Elastic) * [curioswitch/mcp-go-sdk-otel](https://github.com/curioswitch/mcp-go-sdk-otel) - Go MCP instrumentation ## Revision history * 2026-06-03: Moved to Completed. * 2025-12-04: Implementation in extensibility docs * 2025-11-28: Initial draft # Model Config Option Category Source: https://agentclientprotocol.com/rfds/model-config-category * Author(s): [anna239](https://github.com/anna239) ## Elevator pitch Add a new `model_config` category to session configuration options, so that agents can expose model-related parameters (context size, speed/quality trade-offs, etc) and clients can group them alongside the main model selector in the UI. ## Status quo The `category` field on `SessionConfigOption` currently supports three values: `mode`, `model`, and `thought_level`. This works well when the model is a single selector, but some agents expose many model configurations — context window size, speed tier, and similar settings that logically belong next to the model picker. ## What we propose to do about it Add a `model_config` variant to `SessionConfigOptionCategory`. Agents tag any model-related parameters with `"category": "model_config"`, and clients render them near the primary `model` selector — for example as secondary controls within a model-picker popover or panel. ### Relationship to `thought_level` Once `model_config` exists, `thought_level` is semantically a special case of a model configuration parameter. We keep `thought_level` as-is for backward compatibility — existing clients already handle it — but new model-related options should use `model_config`. ## Shiny future Agents expose rich, parameterized model configurations over ACP. ## Implementation details and plan ### JSON format An agent declares model-config options in `configOptions`: ```json theme={null} { "configOptions": [ { "id": "model", "name": "Model", "category": "model", "type": "select", "currentValue": "sonnet-4.5", "options": [ { "value": "sonnet-4.5", "name": "Sonnet 4.5" }, { "value": "opus-4.6", "name": "Opus 4.6" } ] }, { "id": "context_size", "name": "Context Size", "category": "model_config", "type": "select", "currentValue": "200k", "options": [ { "value": "200k", "name": "200K" }, { "value": "1m", "name": "1M" } ] }, { "id": "fast_mode", "name": "Fast Mode", "category": "model_config", "type": "boolean", "currentValue": false } ] } ``` ### Client behavior * Clients SHOULD render `model_config` options near the `model` selector (e.g., in the same popover or panel). * Clients that do not recognize the category MUST handle it gracefully per the existing spec — the option still renders, just without special placement. * No new client capability negotiation is needed. ### Should `thought_level` move under `model_config`? Not now. Existing clients already handle `thought_level`, so changing its semantics would be a breaking change. New model-related parameters should use `model_config`; `thought_level` remains for backward compatibility. ## Revision history * 2026-06-24: Moved to Completed and stabilized `model_config` in the protocol artifacts * 2026-06-22: Move to preview * 2026-04-08: Initial proposal # Next Edit Suggestions Source: https://agentclientprotocol.com/rfds/next-edit-suggestions Author(s): [@anna239](https://github.com/anna239) ## Elevator pitch > What are you proposing to change? Add a **Next Edit Suggestion (NES)** capability to ACP, allowing agents to provide predictive code edits. The protocol is designed around **capability negotiation**: agents declare what events and context they can consume, and clients provide only what was requested. ## Status quo > How do things work today and what problems does this cause? Why would we change things? ACP currently has no mechanism for agents to provide inline edit predictions. Each client–agent pair implements NES through proprietary protocols. ## What we propose to do about it > What are you proposing to improve the situation? Introduce a `nes` capability that agents advertise during initialization. The capability declares: * **Events** the agent wants to receive (file lifecycle notifications). * **Context** the agent wants attached to each suggestion request. The client inspects these declarations and provides only what was requested, minimizing overhead for simple agents while allowing rich context for advanced ones. ### Capability advertisement During `initialize`, the agent includes a `nes` field in `agentCapabilities`: ```json theme={null} { "agentCapabilities": { "nes": { "events": { "document": { "didOpen": {}, "didChange": { "syncKind": "incremental" }, "didClose": {}, "didSave": {}, "didFocus": {} } }, "context": { "recentFiles": { "maxCount": 10 }, "relatedSnippets": {}, "editHistory": { "maxCount": 6 }, "userActions": { "maxCount": 16 }, "openFiles": {}, "diagnostics": {} } } } } ``` All fields under `events` and `context` are optional — an agent advertises only what it can use. #### Client capabilities The **client** advertises its own NES-related capabilities in the `initialize` request. The client declares which suggestion kinds it supports beyond the basic `edit` kind. Agents should only suggest kinds that the client has advertised. ```json theme={null} { "clientCapabilities": { "nes": { "jump": {}, "rename": {}, "searchAndReplace": {} } } } ``` Each entry corresponds to a suggestion kind (see [Suggestion kinds](#suggestion-kinds) below). If a kind is absent, the agent must not produce suggestions of that kind. ### Session lifecycle If the `nes` capability is present, the client may call `nes/start` to begin an NES session. An NES session is **separate from and independent of** the ACP chat session — it has its own session ID, its own lifecycle, and its own stream of events and requests. A single ACP connection may have any number of active NES sessions alongside any number of chat sessions. The NES session is started via `nes/start` and closed via `nes/close`; it does not inherit state from, or share context with, chat sessions. The agent can also use the existing `configOptions` mechanism to expose NES-related settings (model selection, debounce preferences, enabled/disabled state, etc.). ## Implementation details and plan > Tell me more about your implementation. What is your detailed implementation plan? ### Position encoding All `Position` objects in NES use zero-based line and zero-based character offsets, following the same conventions as [LSP 3.17](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#position). The meaning of the `character` offset depends on the negotiated **position encoding**. Three encoding kinds are supported: * `"utf-16"` — character offsets count UTF-16 code units. This is the **default** and must be supported by all clients and agents. * `"utf-32"` — character offsets count Unicode code points. * `"utf-8"` — character offsets count UTF-8 code units (bytes). **Negotiation:** The client may declare the position encodings it supports in the `initialize` request via `clientCapabilities.positionEncodings`, listed in order of preference. The agent selects one from the client's list and declares it in its `initialize` response as `agentCapabilities.positionEncoding`. If the client omits `positionEncodings`, or the agent omits `positionEncoding` in its response, both sides default to `"utf-16"`. Client `initialize` request (excerpt): ```json theme={null} { "clientCapabilities": { "positionEncodings": ["utf-32", "utf-16"] } } ``` Agent `initialize` response (excerpt): ```json theme={null} { "agentCapabilities": { "nes": { ... }, "positionEncoding": "utf-32" } } ``` The negotiated encoding applies to all `Position` and `Range` values exchanged within NES — events, suggestion requests, and suggestion responses. ### Events Events are fire-and-forget notifications from client to agent. Every event is scoped to the NES session identified by the `sessionId` returned from `nes/start`. The client sends them only if the corresponding key is present in the agent's advertised `events` capability (e.g. `nes.events.document` for NES). #### `document/didOpen` Sent when a file is opened in the editor. ```json theme={null} { "jsonrpc": "2.0", "method": "document/didOpen", "params": { "sessionId": "session_abc123", "uri": "file:///path/to/file.rs", "languageId": "rust", "version": 1, "text": "fn main() {\n println!(\"hello\");\n}\n" } } ``` #### `document/didChange` Sent when a file is edited. Supports two sync modes declared by the agent: * `"full"` — client sends entire file content each time. * `"incremental"` — client sends only the changed ranges. **Incremental:** ```json theme={null} { "jsonrpc": "2.0", "method": "document/didChange", "params": { "sessionId": "session_abc123", "uri": "file:///path/to/file.rs", "version": 2, "contentChanges": [ { "range": { "start": { "line": 1, "character": 4 }, "end": { "line": 1, "character": 4 } }, "text": "let x = 42;\n " } ] } } ``` **Full:** ```json theme={null} { "jsonrpc": "2.0", "method": "document/didChange", "params": { "sessionId": "session_abc123", "uri": "file:///path/to/file.rs", "version": 2, "contentChanges": [ { "text": "fn main() {\n let x = 42;\n println!(\"hello\");\n}\n" } ] } } ``` #### `document/didClose` Sent when a file is closed. ```json theme={null} { "jsonrpc": "2.0", "method": "document/didClose", "params": { "sessionId": "session_abc123", "uri": "file:///path/to/file.rs" } } ``` #### `document/didSave` Sent when a file is saved. ```json theme={null} { "jsonrpc": "2.0", "method": "document/didSave", "params": { "sessionId": "session_abc123", "uri": "file:///path/to/file.rs" } } ``` #### `document/didFocus` Sent when a file becomes the active editor tab. Unlike `document/didOpen` (which fires once when a file is first opened), `document/didFocus` fires every time the user switches to a file, including files that are already open. This is the primary trigger for agents that need to refresh context on tab switch (e.g. re-indexing relevant code snippets). ```json theme={null} { "jsonrpc": "2.0", "method": "document/didFocus", "params": { "sessionId": "session_abc123", "uri": "file:///path/to/file.rs", "version": 2, "position": { "line": 5, "character": 12 }, "visibleRange": { "start": { "line": 0, "character": 0 }, "end": { "line": 45, "character": 0 } } } } ``` The `position` is the current cursor position. The `visibleRange` is the portion of the file currently visible in the editor viewport. ### Suggestion request The client requests a suggestion by calling `nes/suggest`. Context fields are included only if the agent declared interest in the corresponding `nes.context` key. ```json theme={null} { "jsonrpc": "2.0", "id": 42, "method": "nes/suggest", "params": { "sessionId": "session_abc123", "uri": "file:///path/to/file.rs", "version": 2, "position": { "line": 5, "character": 12 }, "selection": { "start": { "line": 5, "character": 4 }, "end": { "line": 5, "character": 12 } }, "triggerKind": "automatic", "context": { "recentFiles": [ { "uri": "file:///path/to/utils.rs", "languageId": "rust", "text": "pub fn helper() -> i32 { 42 }\n" } ], "relatedSnippets": [ { "uri": "file:///path/to/types.rs", "excerpts": [ { "startLine": 10, "endLine": 25, "text": "pub struct Config {\n pub name: String,\n ...\n}" } ] } ], "editHistory": [ { "uri": "file:///path/to/file.rs", "diff": "--- a/file.rs\n+++ b/file.rs\n@@ -3,0 +3,1 @@\n+ let x = 42;" } ], "userActions": [ { "action": "insertChar", "uri": "file:///path/to/file.rs", "position": { "line": 5, "character": 12 }, "timestampMs": 1719400000000 }, { "action": "cursorMovement", "uri": "file:///path/to/file.rs", "position": { "line": 10, "character": 0 }, "timestampMs": 1719400001200 } ], "openFiles": [ { "uri": "file:///path/to/utils.rs", "languageId": "rust", "visibleRange": { "start": { "line": 0, "character": 0 }, "end": { "line": 30, "character": 0 } }, "lastFocusedMs": 1719399998000 }, { "uri": "file:///path/to/types.rs", "languageId": "rust", "visibleRange": null, "lastFocusedMs": 1719399990000 } ], "diagnostics": [ { "uri": "file:///path/to/file.rs", "range": { "start": { "line": 5, "character": 0 }, "end": { "line": 5, "character": 10 } }, "severity": "error", "message": "cannot find value `foo` in this scope" } ] } } } ``` `selection` is the current text selection range, if any. When the selection is empty (cursor is a point), this field may be omitted or have `start` equal to `end`. Agents can use selection state to predict replacements or transformations of the selected text. `triggerKind` is one of: * `"automatic"` — triggered by user typing or cursor movement * `"diagnostic"` — triggered by a diagnostic (error/warning) appearing at or near the cursor position. The client includes the relevant diagnostics in the `diagnostics` context field so the agent can target a fix. * `"manual"` — triggered by explicit user action (keyboard shortcut) ### Suggestion response A suggestion is one of several kinds, each identified by the `kind` field. The `edit` kind is always supported; other kinds (`jump`, `rename`, `searchAndReplace`) require the client to advertise support in its capabilities. **Edit suggestion:** ```json theme={null} { "jsonrpc": "2.0", "id": 42, "result": { "suggestions": [ { "id": "sugg_001", "kind": "edit", "uri": "file:///path/to/other_file.rs", "edits": [ { "range": { "start": { "line": 5, "character": 0 }, "end": { "line": 5, "character": 10 } }, "newText": "let result = helper();" } ], "cursorPosition": { "line": 5, "character": 22 } } ] } } ``` **Jump suggestion:** ```json theme={null} { "jsonrpc": "2.0", "id": 42, "result": { "suggestions": [ { "id": "sugg_002", "kind": "jump", "uri": "file:///path/to/other_file.rs", "position": { "line": 15, "character": 4 } } ] } } ``` **Rename suggestion:** ```json theme={null} { "jsonrpc": "2.0", "id": 42, "result": { "suggestions": [ { "id": "sugg_003", "kind": "rename", "uri": "file:///path/to/file.rs", "position": { "line": 5, "character": 10 }, "newName": "calculateTotal" } ] } } ``` **Search-and-replace suggestion:** ```json theme={null} { "jsonrpc": "2.0", "id": 42, "result": { "suggestions": [ { "id": "sugg_004", "kind": "searchAndReplace", "uri": "file:///path/to/file.rs", "search": "oldFunction", "replace": "newFunction", "isRegex": false } ] } } ``` A response may contain a mix of suggestion kinds. The client decides how to present them (e.g. inline ghost text for edits, a navigation hint for jumps). Agents must only include suggestion kinds that the client has advertised in its capabilities (except `edit`, which is always supported). Each suggestion contains: * `id` — unique identifier for accept/reject tracking. * `kind` — the suggestion kind (see [Suggestion kinds](#suggestion-kinds) below). Edit suggestions additionally contain: * `uri` — the file to edit. * `edits` — one or more text edits to apply. * `cursorPosition` — optional suggested cursor position after applying edits. Jump suggestions additionally contain: * `uri` — the file to navigate to. * `position` — the target position within that file. Rename suggestions additionally contain: * `uri` — the file URI containing the symbol. * `position` — the position of the symbol to rename. * `newName` — the new name for the symbol. Search-and-replace suggestions additionally contain: * `uri` — the file URI to search within. Can be a folder, then operation is performed in all the files in this folder. * `search` — the text or pattern to find. * `replace` — the replacement text. * `isRegex` (`boolean`, optional) — whether `search` is a regular expression. Defaults to `false`. ### Accept / Reject ```json theme={null} { "jsonrpc": "2.0", "method": "nes/accept", "params": { "sessionId": "session_abc123", "id": "sugg_001" } } ``` ```json theme={null} { "jsonrpc": "2.0", "method": "nes/reject", "params": { "sessionId": "session_abc123", "id": "sugg_001", "reason": "rejected" } } ``` `reason` is one of: * `"rejected"` — the user explicitly dismissed the suggestion (e.g. pressed Escape or typed something incompatible). * `"ignored"` — the suggestion was shown but the user continued editing without interacting with it, and the context changed enough to invalidate it. * `"replaced"` — the suggestion was superseded by a newer suggestion before the user could act on it. * `"cancelled"` — the request was cancelled before the agent returned a response (e.g. the user typed quickly and the previous request became stale). The `reason` field is optional. Providing granular reasons allows agents to improve their models — for example, a `"replaced"` suggestion carries different training signal than an explicit `"rejected"`. ### NES session start The client provides workspace metadata when starting a session. This information is static for the lifetime of the session. ```json theme={null} { "jsonrpc": "2.0", "id": 1, "method": "nes/start", "params": { "workspaceUri": "file:///Users/alice/projects/my-app", "workspaceFolders": [ { "uri": "file:///Users/alice/projects/my-app", "name": "my-app" } ], "repository": { "name": "my-app", "owner": "alice", "remoteUrl": "https://github.com/alice/my-app.git" } } } ``` All fields in `params` are optional. The `repository` field is omitted if the workspace is not a git repository or the info is unavailable. Response: ```json theme={null} { "jsonrpc": "2.0", "id": 1, "result": { "sessionId": "session_abc123" } } ``` The returned `sessionId` scopes all subsequent NES events, requests, and notifications for that session. #### Error handling The agent may reject `nes/start` with an error. In particular, agents that require authentication may return an `auth_required` error: ```json theme={null} { "jsonrpc": "2.0", "id": 1, "error": { "code": -32000, "message": "Authentication required", "data": { "reason": "auth_required" } } } ``` Clients **must** be prepared to handle `auth_required` errors on `nes/start`. The recommended behavior is to prompt the user to authenticate (e.g. sign in or provide credentials) and retry the `nes/start` call after authentication succeeds. Clients should not silently ignore this error or assume NES is unavailable — the agent may be fully functional once the user authenticates. ### NES session close The client closes an NES session by calling `nes/close`. The agent **must** cancel any ongoing work related to the NES session and free up any resources associated with it. ```json theme={null} { "jsonrpc": "2.0", "id": 2, "method": "nes/close", "params": { "sessionId": "session_abc123" } } ``` Response: ```json theme={null} { "jsonrpc": "2.0", "id": 2, "result": {} } ``` ### Suggestion kinds The `kind` field in each suggestion identifies its type. The following kinds are defined: * **`edit`** — A text edit suggestion. Always supported; does not require a client capability. * **`jump`** — Navigate to a different file/position. Requires `jump` in client capabilities. * **`rename`** — Rename a symbol across the workspace. Requires `rename` in client capabilities. * **`searchAndReplace`** — Search and replace text within a file/folder. Requires `searchAndReplace` in client capabilities. Additional suggestion kinds may be added to the protocol in the future. Agents must only produce suggestions whose `kind` the client has advertised (except `edit`, which is always supported). ### Config options The agent can use the existing `configOptions` mechanism from ACP to expose NES-related settings. For example, an agent might return config options like: ```json theme={null} { "configOptions": [ { "id": "nes_model", "name": "Prediction Model", "category": "model", "type": "enum", "currentValue": "fast", "options": [ { "value": "fast", "label": "Fast" }, { "value": "accurate", "label": "Accurate" } ] } ] } ``` ## Frequently asked questions > What questions have arisen over the course of authoring this document or during subsequent discussions? ### Why separate events from context? Events and context serve different purposes and have different delivery models: * **Events** are pushed as they happen — they allow the agent to maintain internal state (like an LSP server tracking open documents). This is the model Copilot uses. * **Context** is attached to each request — it allows stateless agents to receive everything they need in one call. This is the model Zed Predict and Supermaven use. A note about Cursor: Cursor has a separate context-collection phase (`RefreshTabContext`) that involves vector DB lookup and is triggered on file open, tab switch, and significant edits. The event-based approach supports this flow: an NES agent can listen for `document/didOpen`, `document/didFocus`, and accumulated `document/didChange` events to self-trigger its own context refresh. The `document/didFocus` event (with cursor position and visible range) and workspace metadata from `nes/start` provide all the inputs Cursor's `RefreshTabContext` needs. An agent may want both (events for incremental file tracking + context for edit history), or just one. The capability split lets each agent pick the model that fits its architecture. ### Why not reuse LSP's `textDocument/didOpen` etc. directly? LSP's document sync notifications carry the same information, but: 1. ACP is not LSP — reusing method names could cause confusion in implementations that bridge both. 2. We may want to evolve the event payloads independently (e.g. adding metadata fields). 3. Using `document/` as a generic namespace keeps these methods reusable across different ACP capabilities (NES, and potentially others in the future) without tying them to a single feature. ### How does this relate to PR #325? This RFD covers the session lifecycle and also suggests a protocol that would cover a variety of different nes providers ### Why provide workspace info in `nes/start`? Agents that perform server-side indexing (embedding-based retrieval, semantic search) need to know which repository they're working with. This metadata — workspace root, repo name/owner, remote URL — is static for the session lifetime, so it belongs in the session start rather than being repeated on every request or requiring a separate query. ### What alternative approaches did you consider? 1. **Context-only** — Pass all file content, edit history, and metadata as context fields on each `nes/suggest` request, with no event notifications. This is simpler for stateless agents but forces the client to assemble and transmit potentially large payloads on every request, even when nothing changed. It also prevents agents from maintaining their own incremental state (e.g. an internal file mirror or semantic index). 2. **Events-only** — Rely entirely on event notifications (`didOpen`, `didChange`, etc.) and have the agent maintain all state internally, with `nes/suggest` sending only the cursor position. This is efficient on the wire but requires every agent to implement stateful document tracking, which is a high barrier for simple agents that just want the code around the cursor. 3. **Events + context (chosen)** — Allow agents to declare both. An agent that wants to track files incrementally can request events; an agent that prefers stateless request-response can request context fields; a sophisticated agent can use both (events for file sync, context for edit history and definition excerpts). This gives each agent the flexibility to pick the model that fits its architecture without imposing unnecessary complexity. ## Revision history * 2026-02-22: Initial draft # Plan Operations Support Source: https://agentclientprotocol.com/rfds/plan-operations Author(s): [anna239](https://github.com/anna239) ## Elevator pitch > What are you proposing to change? Introduce a new `plan_update` session update type that supports multiple plan formats (item-based, file-based, markdown text), multiple concurrent plans via IDs, and plan removal. The existing `plan` session update type remains unchanged for backward compatibility. ## Status quo > How do things work today and what problems does this cause? Why would we change things? * The `plan` session update contains `entries: Vec` — a flat list of items with priority/status * Plans are one-way notifications (agent → client) via `session/update` * No plan identity — each update replaces the previous plan entirely * No way to remove/dismiss a plan once sent * Only one plan representation (structured items) — no support for free-form markdown or file-based plans ## What we propose to do about it > What are you proposing to improve the situation? Add a new `SessionUpdate` variant — `plan_update` — that carries a `plan` field. The `plan` field is a tagged union (discriminated by `type`) with the following variants: | Type | Description | Specific fields | | ---------- | ----------------------------------------------------- | ---------------------- | | `items` | Structured entries (same semantics as today's `plan`) | `entries: PlanEntry[]` | | `file` | Agent provides a file URI containing the plan | `uri: string` | | `markdown` | Agent provides raw markdown text | `content: string` | All variants carry a required `id: string` that identifies the plan, and an optional `_meta` for extensibility. Additionally, introduce a `plan_removed` session update type that carries only a plan `id` to signal dismissal. This keeps `plan_update` focused purely on content updates and makes removal a distinct, self-describing event. The existing `plan` session update type is **not modified** and continues to work as before. Agents that want multi-plan support, new plan formats, or removal capabilities use `plan_update` and `plan_removed` only when the client advertises `plan`; otherwise they fall back to the existing `plan` session update type. ### Client Capabilities Add to `ClientCapabilities`: ``` plan?: PlanCapabilities ``` This field is optional. When present as `{}`, it signals the client supports the `plan_update` and `plan_removed` session update types. When omitted, the client does not advertise support, and the agent must fall back to the existing `plan` session update type. ### Multiple Plans The `id` field on every plan variant identifies a specific plan. * Each `plan_update` targets the plan with the given `id` * Client tracks plans by ID; updates replace the content of that specific plan * Different plans may use different types (e.g., one `items` plan and one `markdown` plan) ### Plan Removal Agent sends a `plan_removed` session update with the plan's `id` to dismiss it. This is a separate session update type from `plan_update`, keeping content updates and lifecycle events distinct. ## Shiny future > How will things will play out once this feature exists? Agents will be able to present plans in the format most natural to them — structured checklists for step-by-step execution, markdown for free-form design docs, or file URIs for large plans generated externally. Clients that support `plan_update` can show multiple concurrent plans (e.g., a high-level strategy and a detailed implementation checklist), and dismiss plans that are no longer relevant. Clients that don't advertise `plan` continue to receive the existing `plan` updates with no changes. ## Implementation details and plan > Tell me more about your implementation. What is your detailed implementation plan? ### Json Format Examples Existing `plan` (unchanged): ```json theme={null} { "sessionUpdate": "plan", "entries": [{ "content": "Step 1", "priority": "high", "status": "pending" }] } ``` Item-based (`plan_update`): ```json theme={null} { "sessionUpdate": "plan_update", "plan": { "type": "items", "id": "plan-1", "entries": [ { "content": "Step 1", "priority": "high", "status": "pending" } ] } } ``` Markdown: ```json theme={null} { "sessionUpdate": "plan_update", "plan": { "type": "markdown", "id": "plan-1", "content": "## Steps\n- [ ] Refactor module\n- [ ] Add tests" } } ``` File-based: ```json theme={null} { "sessionUpdate": "plan_update", "plan": { "type": "file", "id": "design-doc", "uri": "file:///tmp/plan.md" } } ``` Removal: ```json theme={null} { "sessionUpdate": "plan_removed", "id": "plan-1" } ``` ## Frequently asked questions > What questions have arisen over the course of authoring this document or during subsequent discussions? ### Why a new session update type instead of extending the existing `plan`? The existing `plan` variant flattens `Plan`'s `entries` field directly into the `SessionUpdate` discriminated union object. Adding a nested `plan` field, optional `id`, and polymorphic types to the same variant would require breaking the existing format or complex dual-deserialization logic. A new `plan_update` variant avoids this, but agents must only send it to clients that advertised `plan`; clients that do not advertise support can continue to receive the existing `plan` update. ### Why a separate `plan_removed` session update type instead of a removal variant inside `plan_update`? Keeping removal as a separate session update type means `plan_update`'s `type` discriminator only covers content variants (`items`, `file`, `markdown`), making the schema cleaner. ### Why plan-specific markdown and not a generic markdown content API? A generic "agent shares markdown document" API would be simpler and more reusable. However, keeping it plan-typed enables clients to build plan-aware UX: selecting plan steps for partial execution, tracking plan progress, showing plan history. A generic markdown block wouldn't carry this semantic meaning. If future use cases need generic markdown sharing, that can be a separate feature. ### What alternative approaches did you consider, and why did you settle on this one? 1. **Extending the existing `plan` variant**: Would break backward compatibility or require complex dual-format deserialization. 2. **`"type": "removed"` variant inside `plan_update`**: Instead of a separate `plan_removed` session update type, removal could be a variant in the `plan_update` tagged union (e.g., `"type": "removed"`). This keeps the entire plan lifecycle within a single update type. The tradeoff is that the `type` discriminator mixes content variants with a lifecycle event, making the schema less clean. ## Revision history 2026-04-02: Initial draft # Agent Extensions via ACP Proxies Source: https://agentclientprotocol.com/rfds/proxy-chains Author(s): [nikomatsakis](https://github.com/nikomatsakis) ## Elevator pitch > What are you proposing to change? Enable a universal agent extension mechanism via ACP proxies, components that sit between a client and an agent. Proxies can intercept and transform messages, enabling composable architectures where techniques like context injection, tool coordination, and response filtering can be extracted into reusable components. ## Status quo > How do things work today and what problems does this cause? Why would we change things? The AI agent ecosystem has developed many extension mechanisms: AGENTS.md files, Claude Code plugins, rules files, hooks, MCP servers, etc. Of these, only MCP servers have achieved real standardization across the ecosystem. However, MCP servers are fundamentally limited because they sit "behind" the agent. They can provide tools and respond to function calls, but they cannot: * **Inject or modify prompts** before they reach the agent * **Add global context** that persists across conversations * **Transform responses** before they reach the user * **Coordinate between multiple agents** or manage conversation flow As a result, valuable techniques like context management and response processing remain locked within individual agent implementations, with no way to extract and reuse them across different agents. ## What we propose to do about it > What are you proposing to improve the situation? We propose implementing *agent extensions* via ACP *proxies*, a new kind of component that sits between the client and the agent, forwarding (and potentially altering or introducing) messages. Because proxies can do anything a client could do, they serve as a universal extension mechanism that can subsume AGENTS.md, hooks, MCP servers, etc. Proxies are limited to the customizations exposed by ACP itself, so they would benefit from future ACP extensions like mechanisms to customize system prompts. However, they can already handle the majority of common extension use cases through message interception and transformation. ### Proxying in theory Conceptually, proxies work like a chain where messages flow through each component: ```mermaid theme={null} flowchart LR Client[ACP Client] -->|messages| P1[Context Proxy] P1 -->|enhanced| P2[Tool Filter Proxy] P2 -->|filtered| A[Base Agent] A -->|responses| P2 P2 -->|processed| P1 P1 -->|final| Client ``` ### Proxying in practice: the role of the conductor To allow for proxy isolation, our design does not have proxies communicate directly with their successor in the chain. Instead, there is a central *conductor* component that orchestrates messages moving between components. ```mermaid theme={null} flowchart TB Client[Client] C[Conductor] P1[Context Proxy] P2[Tool Filter Proxy] A[Agent] Client <-->|ACP| C C <-->|ACP| P1 C <-->|ACP| P2 C <-->|ACP| A ``` We add one ACP method for proxy communication: * **`proxy/successor`**: Used bidirectionally - proxies send it to forward messages to their successor, and the conductor sends it to deliver messages from the successor back to the proxy Here's how a single message flows through the system: ```mermaid theme={null} sequenceDiagram participant Client participant Conductor participant P1 as Context Proxy participant P2 as Tool Filter Proxy participant Agent Client->>Conductor: prompt request Conductor->>P1: prompt request P1->>Conductor: proxy/successor (enhanced prompt) Conductor->>P2: enhanced prompt P2->>Conductor: proxy/successor (filtered prompt) Conductor->>Agent: filtered prompt Agent-->>Conductor: response Conductor-->>P2: response P2-->>Conductor: processed response Conductor-->>P1: processed response P1-->>Conductor: final response Conductor-->>Client: final response ``` ## Shiny future > How will things play out once this feature exists? ### User experience and editor integration We expect editors to expose the ability to install proxies in the same way they currently support adding MCP servers - in fact, the distinction probably doesn't matter to users. Both are "extensions" that add capabilities to their AI workflow. When proxies are installed, editors would not start the agent directly, but instead invoke the conductor with the configured proxy chain. From the user's perspective, they're just getting enhanced agent capabilities - the proxy chain architecture remains transparent. ### Language-specific proxy ecosystems The monolithic nature of agent development has meant that most of the "action" happens within agents. We wish to invert this, with agents trending towards simple agentic loops, and the creativity being pushed outwards into the broader ecosystem. The Symposium project is one example exploring this concept, with a focus on Rust. The idea is to give Rust users an automatic set of extensions based on the dependencies they are using. These extensions would be packaged up as SACP proxies using WebAssembly for portability and sandboxing. Symposium aims to become the standard "Rust ACP experience" by providing both core Rust tooling and a framework for Rust libraries to contribute their own proxy components. ```mermaid theme={null} flowchart LR Client[Client] Conductor[Conductor] Agent[Agent] subgraph Symposium["symposium-acp proxy"] RustTools[Rust Language Tools] CrateA[tokio-acp proxy] CrateB[serde-acp proxy] CrateC[bevy-acp proxy] RustTools --> CrateA CrateA --> CrateB CrateB --> CrateC end Client --> Conductor Conductor --> Symposium Symposium --> Agent ``` ### Standardized IDE capabilities Proxy infrastructure could also enable editors to expose standardized IDE capabilities (diagnostics, file system access, terminal APIs) to agents via MCP servers provided by proxies. This keeps the core ACP protocol focused on agent communication while allowing rich IDE integration through the proxy layer. ## Implementation details and plan > Tell me more about your implementation. What is your detailed implementation plan? ### Component roles Each ACP proxy chain forms a sequence of components: ```mermaid theme={null} flowchart LR Client --> Proxy0 --> Proxy1 --> ... --> ProxyN --> Agent ``` The **client** and **agent** are *terminal* roles - the client has only a successor (no predecessor), and the agent has only a predecessor (no successor). Proxies are *non-terminal* - they have both a predecessor and a successor, forwarding messages between them. The **conductor** is a special component that orchestrates proxy chains. It spawns and manages proxy components, routes messages between them, and handles initialization. From the client's perspective, the conductor appears to be an ordinary agent: ```mermaid theme={null} flowchart LR Client -->|ACP| Conductor subgraph Managed["Managed by Conductor"] Proxy0 --> Proxy1 --> ... --> Agent end Conductor -.->|spawns & routes| Managed ``` We provide a canonical conductor implementation in Rust (`sacp-conductor`). Most editors would use this conductor directly to host proxies and agents, though they could also reimplement conductor functionality if needed. ACP defines client and agent as superroles, each with two specializations: ```mermaid theme={null} classDiagram class Client { <> +sends prompts } class Agent { <> +responds to prompts } class TerminalClient { +receives direction from user +connects to terminal agent } class Conductor { +manages proxy chain +sends successor messages } class TerminalAgent { +embodies the LLM +final destination +no successor } class Proxy { +forwards to successor +sends successor messages } Client <|-- TerminalClient Client <|-- Conductor Agent <|-- TerminalAgent Agent <|-- Proxy ``` **Example Architecture:** ```mermaid theme={null} flowchart TB TC[Terminal Client] C[Conductor] P1[Context Proxy] P2[Tool Filter Proxy] TA[Terminal Agent] TC -->|terminal client| C C -->|terminal agent| TC C -->|conductor| P1 P1 -->|proxy| C C -->|conductor| P2 P2 -->|proxy| C C -->|terminal client| TA TA -->|terminal agent| C ``` ### Proxy initialization protocol Components discover their role from the initialization method they receive: * **Proxies** receive `proxy/initialize` - they have a successor and should forward messages * **Agents** receive `initialize` - they are terminal (no successor) and process messages directly The `proxy/initialize` request has the same parameters as `initialize` and expects a standard `InitializeResponse`. The only difference is the method name, which signals to the component that it should operate as a proxy. **Conductor behavior:** * The conductor MUST send `proxy/initialize` to all proxy components * The conductor MUST send `initialize` to the final agent component (if any) * When a proxy forwards an `initialize` via `proxy/successor`, the conductor determines whether the successor is another proxy or the agent, and sends `proxy/initialize` or `initialize` respectively. **Proxy behavior:** * A proxy that receives `proxy/initialize` knows it has a successor * The proxy SHOULD forward requests it does not understand * The proxy SHOULD preserve metadata fields when forwarding messages Note: A conductor can be configured to run in either terminal mode (expecting `initialize`) or proxy mode (expecting `proxy/initialize`), enabling nested proxy chains. ### MCP-over-ACP support Proxies that provide MCP servers use the [MCP-over-ACP transport](./mcp-over-acp) mechanism. The conductor always advertises `mcpCapabilities.acp: true` to proxies and handles bridging for agents that don't support native ACP transport. All proxies MUST respond to `proxy/initialize` with the MCP-over-ACP capability enabled. When the conductor sends `proxy/initialize`, proxies should be prepared to handle `mcp/connect`, `mcp/message`, and `mcp/disconnect` messages for any MCP servers they provide. ### Message reference **Initialization:** ```json theme={null} // Conductor initializes a proxy (proxy knows it has a successor) {"method": "proxy/initialize", "params": } // Conductor initializes the agent (standard ACP) {"method": "initialize", "params": } ``` Both methods use the same parameters as the standard ACP `InitializeRequest` and expect a standard `InitializeResponse`. **Proxy messages:** ```json theme={null} // Proxy sends message to successor, or conductor delivers message from successor // (same method, direction determined by sender) { "method": "proxy/successor", "params": { "method": "", "params": , "meta": { ... } // optional metadata } } ``` The inner message fields (`method`, `params`) are flattened into the params object. Whether the wrapped message is a request or notification is determined by the presence of an `id` field in the outer JSON-RPC envelope, following JSON-RPC conventions. ### Examples (non-normative) The following sequence diagrams illustrate common proxy chain scenarios for implementers. #### Initialization of a 4-component proxy chain This shows the initialization flow for: Terminal Client → Conductor → Context Proxy → Tool Filter Proxy → Terminal Agent ```mermaid theme={null} sequenceDiagram participant TC as Terminal Client participant C as Conductor participant P1 as Context Proxy participant P2 as Tool Filter Proxy participant TA as Terminal Agent Note over TC,TA: === Initialization Phase === TC->>C: initialize Note over C: Conductor spawns proxy components C->>P1: proxy/initialize Note over P1: Proxy forwards to successor P1->>C: proxy/successor (initialize) Note over C: Conductor sends proxy/initialize to next proxy C->>P2: proxy/initialize Note over P2: Proxy forwards to successor P2->>C: proxy/successor (initialize) Note over C: Conductor sends initialize to final agent C->>TA: initialize TA-->>C: InitializeResponse (mcpCapabilities.acp: true/false) C-->>P2: proxy/successor (InitializeResponse) P2-->>C: InitializeResponse C-->>P1: proxy/successor (InitializeResponse) P1-->>C: InitializeResponse Note over C: Conductor acts as terminal agent to client C-->>TC: InitializeResponse Note over TC,TA: Proxy chain initialized and ready ``` #### Context-providing proxy with session notifications This example shows how a proxy can handle initialization and forward session notifications. Sparkle (a collaborative AI framework) runs an embodiment sequence during session creation. ```mermaid theme={null} sequenceDiagram participant Client participant Sparkle as Sparkle Proxy participant Agent Note over Client: Client creates new session Client->>Sparkle: session/new Note over Sparkle: Adds Sparkle MCP server Sparkle->>Agent: session/new + sparkle tools Agent-->>Sparkle: session created (sessionId: S1) Sparkle-->>Client: session/new response (sessionId: S1) Note over Client: Client sends first prompt (during embodiment) Client->>Sparkle: prompt ('Hello, can you help me?') activate Sparkle Note over Sparkle: Delays client prompt, runs embodiment first Sparkle->>Agent: prompt ('you are sparkle...') Note over Agent: Processes embodiment, sends notifications Agent->>Sparkle: session/update (S1: thinking...) Sparkle->>Client: session/update (S1: thinking...) Agent->>Sparkle: session/update (S1: embodiment complete) Sparkle->>Client: session/update (S1: embodiment complete) Agent-->>Sparkle: embodiment response Note over Sparkle: Discards embodiment response, now processes delayed prompt Sparkle->>Agent: prompt ('Hello, can you help me?') deactivate Sparkle Agent-->>Sparkle: response to client Sparkle-->>Client: response to client Note over Client,Agent: Session ready with Sparkle patterns active ``` This demonstrates how proxies can run initialization sequences during session creation while transparently forwarding all session notifications back to the client. ## Frequently asked questions > What questions have arisen over the course of authoring this document or during subsequent discussions? ### Why use a separate `proxy/initialize` method instead of a capability? Earlier designs used a `"proxy": true` capability in the `InitializeRequest` and required proxies to echo it back in the response. This felt awkward because it wasn't really a capability negotiation - it was more of a "you must operate as a proxy" directive. Using a distinct method makes the contract clearer: if you receive `proxy/initialize`, you're a proxy with a successor; if you receive `initialize`, you're the terminal agent. There's no capability dance, no risk of misconfiguration, and components know their role immediately from the method name. ### How do proxies subsume existing agent extension mechanisms? Because proxies sit between the client and agent, they can replicate the functionality of existing extension mechanisms: * **AGENTS.md files**: Proxies can inject context and instructions into prompts before they reach the agent * **Claude Code plugins/skills**: Proxies can add contextual data for available skills and provide MCP resources with detailed skill instructions that are provided on-demand when requested by the agent * **MCP servers**: Proxies can provide tools via [MCP-over-ACP](./mcp-over-acp) and handle tool callbacks * **Subagents**: Proxies can create "subagents" by initiating new sessions and coordinating between multiple agent instances * **Hooks and steering files**: Proxies can modify conversation flow by intercepting requests and responses * **System prompt customization**: Proxies can switch between predefined session modes or prepend system messages to prompts The key advantage is that proxy-based extensions work with any ACP-compatible agent without requiring agent-specific integration or modification. ### How do proxies work with MCP servers? Proxies can provide MCP servers via [MCP-over-ACP transport](./mcp-over-acp), enabling a single proxy to add context, provide tools, and handle callbacks with full awareness of the conversation state. The conductor always advertises `mcpCapabilities.acp: true` to proxies, regardless of whether the downstream agent supports it natively. When the agent doesn't support ACP transport, the conductor handles bridging transparently - spawning stdio shims or HTTP servers that the agent connects to normally, then relaying messages to/from the proxy's ACP channel. This means proxy authors don't need to worry about agent compatibility - they implement MCP-over-ACP, and the conductor handles the rest. ```mermaid theme={null} sequenceDiagram participant Client participant P1 as Context Proxy participant P2 as Filter Proxy participant Agent Note over Client: User asks about project structure Client->>P1: prompt request Note over P1: Analyzes project, adds context + filesystem MCP server P1->>P2: enhanced prompt + filesystem MCP server Note over P2: Forwards enhanced prompt P2->>Agent: prompt with context + tools available Note over Agent: Decides to explore project structure Agent->>P2: mcp/message (list files) Note over P2: Forwards tool call back to Context Proxy P2->>P1: mcp/message (list files) Note over P1: Handles tool call with full project context P1-->>P2: file listing with relevant details P2-->>Agent: file listing with relevant details Agent-->>P2: response using both context and tool results P2-->>P1: response (potentially filtered) P1-->>Client: final response ``` ### Are there any limitations to what proxies can do? Yes, proxies are limited to what is available through the ACP protocol itself. They can intercept and transform any ACP message, but they cannot access capabilities that ACP doesn't expose. For example, proxies cannot directly modify an agent's system prompt or context window - they can only switch between predefined session modes (which may affect system prompts) or prepend additional messages to prompts. Similarly, proxies cannot access internal agent state, model parameters, or other implementation details that aren't exposed through ACP messages. This is actually a feature - it ensures that proxy-based extensions remain portable across different agent implementations and don't rely on agent-specific internals. ### Why not just cascade ACP commands without protocol changes? One alternative is to make proxies be ordinary agents that internally create and manage their successors. This works (HTTP proxies operate this way) but requires each proxy to understand the full chain and know how to start its successors. This couples proxies to transport mechanisms, process management, and chain configuration. Changing transports, reordering the chain, or inserting a new proxy requires modifying predecessor configurations. The conductor design decouples proxies from their successors. Proxies send messages "to successor" and receive messages "from successor" without knowing who that successor is, how it's started, or what transport it uses. This enables: * Changing transport protocols or process management without recompiling proxies * Shipping proxies as low-capability WASM containers that need only a single communication channel * Reordering, adding, or removing proxies through configuration rather than code changes The tradeoff is protocol complexity, but this complexity lives in the conductor (implemented once) rather than being duplicated across proxy implementations. ### Why do all messages go through the conductor instead of direct proxy-to-proxy communication? Even with a conductor, proxies could communicate directly with their successors after the conductor sets up connections. Routing all messages through the conductor further minimizes proxy responsibilities to a single communication channel. This supports running proxies as isolated WebAssembly components with minimal capabilities. It also removes redundant logic: without the conductor routing messages, each proxy would need to manage connections to its successor. The conductor handles process management, capability negotiation, and message routing, allowing proxies to focus on transformation logic. ### How does the standard conductor implementation work? The `sacp-conductor` reference implementation can form trees of proxy chains. It can be configured to run in proxy mode (expecting `proxy/initialize`) or terminal mode (expecting `initialize`). When the last proxy in its managed chain sends a message to its successor, the conductor forwards that message to its own parent conductor (if in proxy mode) or to the final agent (if in terminal mode). This enables hierarchical structures like: ``` client → conductor1 → final-agent ↓ manages proxy-a → conductor2 → proxy-d ↓ manages proxy-b → proxy-c ``` The conductor handles process management, capability negotiation, and message routing, but these are implementation details - the protocol only specifies the message formats and capability requirements. ### What about security concerns with proxy chains? Proxy components can intercept and modify all communication, so trust is essential - similar to installing any software. Users are responsible for the components they choose to run. We plan to explore WebAssembly-based proxies which will offer some measure of sandboxing but such components could still modify prompts in unknown or malicious ways. ### What about performance implications of the proxy chain? Our architecture does introduce additional message passing - each proxy in the chain adds extra hops as messages flow through the conductor. However, these messages are typically small and inexpensive, particularly when compared to the latency of actual LLM inference. For messages that contain significant quantities of data (large file contents, extensive context), we may wish to have the conductor store that data centrally and introduce a "reference" mechanism so that most proxies don't have to inspect or copy large payloads unless they specifically need to transform them. The benefits of composability typically outweigh the minimal latency costs for human-paced development interactions. ### What happens when proxy components crash or misbehave? The conductor manages component lifecycles: * Failed components are restarted automatically where possible * Component crashes don't affect the rest of the chain * Graceful degradation by bypassing failed components * Clear error reporting to help users debug configuration issues ### Can proxy chains be nested or form trees? Yes! The conductor can itself run in proxy mode, enabling hierarchical structures: ``` client → proxy1 → conductor (proxy mode) → final-agent ↓ manages p1 → p2 → p3 ``` This enables complex compositions while maintaining clean interfaces. ### How could proxy chains support multi-agent scenarios in the future? The current design assumes a linear chain where each proxy has a single successor. To support M:N topologies where a proxy communicates with multiple peers (e.g., a research coordinator dispatching to multiple specialized agents), we could extend `proxy/successor` with an optional `peer` field: ```json theme={null} { "method": "proxy/successor", "params": { "method": "prompt", "params": { ... }, "peer": "research-agent" } } ``` When `peer` is omitted, the message goes to the default successor (backwards compatible with the current linear chain model). When present, it specifies which peer the message is intended for. The `proxy/initialize` response could be extended to enumerate available peers, enabling proxies to discover and coordinate between multiple downstream components. ### What's the current implementation status? A prototype version of this proposal has been implemented and is available on crates.io as the crates: * `sacp` -- base ACP protocol SDK * `sacp-tokio` -- adds specific utilities for use with the `tokio` runtime * `sacp-proxy` -- extensions for implementing a proxy * `sacp-rmcp` -- adds specific proxy extension traits for bridging to the rmcp crate * `sacp-conductor` -- reference conductor implementation The canonical sources for those crates is currently the \[symposium-dev/symposium-acp] repository. However, copies have been upstreamed to the [agentclientprotocol/rust-sdk](https://github.com/agentclientprotocol/rust-sdk/tree/main/src/sacp-conductor) repository and, if and when this RFD is accepted, that will become the canonical home. ## Revision history * Initial draft based on working implementation in symposium-acp repository. * Split MCP-over-ACP transport into [separate RFD](./mcp-over-acp) to enable independent use by any ACP component. # Request Cancellation Mechanism Source: https://agentclientprotocol.com/rfds/request-cancellation * Author(s): [Artem Bukhonov](https://github.com/nerzhulart) * Champion: [@benbrandt](https://github.com/benbrandt) ## Elevator pitch Introduce a standardized per-request cancellation mechanism for the Agent Client Protocol, inspired by the [Language Server Protocol (LSP)](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#cancelRequest), to enable a more granular cancellation of requests where individual JSON-RPC requests can be cancelled one by one. ## Status quo The JSON-RPC specification doesn't define any standard mechanism for request cancellation and leaves it up to the implementation. Currently, ACP has some ad-hoc cancellation mechanisms for specific features (like prompt turn cancellation via `session/cancel`), but lacks a general-purpose, per-request cancellation mechanism. This creates the following inconveniences: * cancellation should be handled for each feature separately * some languages that support handy cancellation mechanisms (C#, Kotlin, etc.) can't implement general-purpose request cancellation using ACP low-level machinery, and rather developers should manually call per-feature cancellation methods ## What we propose to do about it Implement an **optional** `$/cancel_request` notification method (inspired by the Language Server Protocol) to both Agent and Client that uses JSON-RPC 2.0 notification format, allowing either party (client or agent) to cancel any outstanding request by its ID. The mechanism will be: * **Optional**: Not all implementations may support this feature, but it is recommended for those that do. * **Flexible**: Provides two response options when cancellation is received: 1. An error response with the standard cancellation error code (-32800) 2. A valid response with partial or cancelled data (when meaningful partial results exist) This approach balances flexibility with standardization, allowing implementations to opt-in to cancellation support while providing predictable behavior when enabled. ## Shiny future Once implemented, this enables: * **SDK integration layer**: Default mechanism that ACP SDKs can automatically wire to native language cancellation (C# CancellationToken, Kotlin Job, Go context.Context, JavaScript AbortController, etc.) * Individual JSON-RPC request cancellation without affecting other concurrent requests * Universal fallback for any request when feature-specific cancellation methods don't exist * Consistent cancellation behavior from both external `$/cancel_request` and internal cancellation triggers * Standard error response (`-32800`) or partial results when requests are cancelled regardless of cancellation source In a future version, we could potentially deprecate the `session/cancel` notification in favor of the more general approach, as it would still provide the same functionality but with more flexibility and consistency. ## Implementation details and plan ### Protocol Changes #### Cancellation Method Add the `$/cancel_request` notification method to the JSON-RPC protocol: ```typescript theme={null} interface CancelNotification { method: "$/cancel_request"; params: { requestId: string | number; // ID of request to cancel }; } ``` ### Cancellation Behavior Either party can send `$/cancel_request` to cancel requests. Notifications whose methods start with '$/' are messages which are protocol implementation dependent and might not be implementable in all clients or agents. For example if the implementation uses a single threaded synchronous programming language then there is little it can do to react to a `$/cancel\_request\` notification. If an agent or client receives notifications starting with '\$/' it is free to ignore the notification. The **receiving party** is **NOT** required to: * Perform special handling for unsupported cancellation requests * Return custom errors for unsupported `$/cancel_request` notifications #### Cancellation Processing When a `$/cancel_request` notification is received by a supporting implementation, it: * **MUST** cancel the corresponding request activity and all nested activities related to that request * **MAY** finish sending any pending notifications before responding * **MUST** send one of these responses for the original request: * A valid response with appropriate data (such as partial results or cancellation marker) * An error response with code [`-32800` (Request Cancelled)](/protocol/v1/draft/schema#errorcode) #### Internal Cancellation Requests can also be cancelled internally by the executing party without receiving `$/cancel_request`: * **Client-side examples**: User closes IDE, switches to different project, file becomes unavailable * **Agent-side examples**: LLM context limit reached, internal timeout, resource constraints When internal cancellation occurs, the executing party **SHOULD**: 1. Send the same `-32800` (Cancelled) error response as if `$/cancel_request` was received 2. Ensure consistent behavior regardless of cancellation source ### Error Code Add standard JSON-RPC error code `-32800` for cancelled requests: * Code: `-32800` * Message: "Request cancelled" * Meaning: Execution of the method was aborted either due to a cancellation request from the caller or because of resource constraints or shutdown. ## Implementations | Repository | Pull request / commit | Status | | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | | `zed-industries/zed` | [zed-industries/zed#59593](https://github.com/zed-industries/zed/pull/59593) | Merged | | `agentclientprotocol/rust-sdk` | [agentclientprotocol/rust-sdk#179](https://github.com/agentclientprotocol/rust-sdk/pull/179) | Merged | | `agentclientprotocol/typescript-sdk` | [agentclientprotocol/typescript-sdk#195](https://github.com/agentclientprotocol/typescript-sdk/pull/195) | Merged | | `agentclientprotocol/claude-agent-acp` | [agentclientprotocol/claude-agent-acp#801](https://github.com/agentclientprotocol/claude-agent-acp/pull/801) | Merged | | `agentclientprotocol/codex-acp` | [agentclientprotocol/codex-acp#221](https://github.com/agentclientprotocol/codex-acp/pull/221) | Merged | | `agentclientprotocol/kotlin-sdk` | [agentclientprotocol/kotlin-sdk@f345883](https://github.com/agentclientprotocol/kotlin-sdk/commit/f3458831da628e26ee29c4a5834d55b443bf5a31) | Needs method name update | ## Frequently asked questions ### What alternative approaches did you consider, and why did you settle on this one? The core need is to add **granular cancellation** as a general mechanism for individual JSON-RPC requests, while **feature-specific cancellation methods** (like `session/cancel`) remain useful for cases requiring additional domain semantics. We selected the **LSP-style `$/cancel_request`** approach because: * Serves as a **default cancellation layer** that SDK implementations can easily map to native language cancellation mechanisms * Proven pattern familiar to developers from LSP ecosystem * Works across all JSON-RPC transports (HTTP, WebSocket, stdio, pipes) * Provides universal fallback when feature-specific cancellation doesn't exist * Complements existing feature-specific methods rather than replacing them ### How does this relate to existing cancellation mechanisms like `session/cancel`? The `$/cancel_request` mechanism is complementary to feature-specific cancellation: * `$/cancel_request`: Generic cancellation for any JSON-RPC request by ID * `session/cancel`: Feature-specific cancellation with additional semantics (e.g., cancels entire prompt turn context, triggers specific cleanup logic) Both mechanisms serve different purposes: **Feature-specific methods** like `session/cancel` provide: * Domain-specific semantics and behavior * Structured cleanup for complex operations * Context-aware cancellation logic **Generic `$/cancel_request`** provides: * Default cancellation layer that bridges programming language cancellation mechanisms (C# CancellationToken, Kotlin Job, Go context.Context, etc.) with ACP * Universal fallback for any request when no feature-specific method exists * Simple ID-based targeting for SDK convenience * Standardized error responses Implementations can use both: feature-specific methods for rich semantics, and `$/cancel_request` for simple per-request cancellation. Note: it is possible that `session/cancel` could be replaced by the more generic `$/cancel_request` in future versions of the protocol. #### Example: Cascading Cancellation Flow ```mermaid theme={null} sequenceDiagram participant Client participant Agent Note over Client,Agent: 1. Session prompt in progress Client->>Agent: session/prompt (id=1, "Analyze file X") Agent-->>Client: session/update (agent started processing) Note over Client,Agent: 2. Agent makes concurrent requests Agent->>Client: terminal/create (id=2, "grep pattern file.txt") Agent->>Client: session/request_permission (id=3, "read sensitive file") Note over Client,Agent: 3. Client cancels the prompt turn Client->>Agent: session/cancel (sessionId) Note over Client,Agent: 4. Agent cascades cancellation internally Agent->>Client: $/cancel_request (id=2) [terminal request] Agent->>Client: $/cancel_request (id=3) [permission request] Note over Client,Agent: 5. Client confirms individual cancellations Client->>Agent: response to id=2 (error -32800 "Cancelled") Client->>Agent: response to id=3 (error -32800 "Cancelled") Note over Client,Agent: 6. Agent completes prompt cancellation Agent->>Client: response to id=1 (stopReason: "cancelled") ``` ### What happens if a request completes before the cancellation is processed? If a request completes normally before the cancellation notification is processed, the implementation should: 1. Send the normal response (not a cancellation error) 2. Ignore the subsequent cancellation notification for that request ID This ensures clients always receive meaningful responses and prevents race conditions. ### How should implementations handle cascading cancellation? When a request is cancelled, implementations should: 1. Cancel the primary request activity 2. Propagate cancellation to any nested/child requests 3. Clean up resources associated with the entire request tree 4. Send cancellation responses for all affected requests This ensures complete cleanup and prevents resource leaks. ## Revision history * 2026-06-29: Moved to Completed and stabilized request cancellation in the protocol artifacts. * 2026-06-24: Move to Preview stage * 2025-12-09: Mirror LSP behavior. * 2025-12-05: Updated with current implementation. * 2025-11-13: Initial version converted from PR #183 # Rust SDK based on SACP Source: https://agentclientprotocol.com/rfds/rust-sdk-v1 Author(s): [nikomatsakis](https://github.com/nikomatsakis) ## Elevator pitch > What are you proposing to change? Replace the current ACP Rust SDK with a new implementation based on SACP (Symposium ACP). The new SDK provides a component-based architecture with builder patterns, explicit message ordering guarantees, and first-class support for [Proxy Chains](./proxy-chains) and [MCP-over-ACP](./mcp-over-acp). ## Status quo > How do things work today and what problems does this cause? Why would we change things? The current `agent-client-protocol` crate has a straightforward design with trait-based callbacks for common ACP methods and well-typed requests and responses. It's convenient for simple purposes but quickly becomes awkward when attempting more complex designs. Two examples that we found pushed the limits of the design are the *conductor* (from the [proxy chains](./proxy-chains) RFD) and the [patchwork-rs](https://patchwork-lang.github.io/patchwork-rs/) project: The Conductor is an orchestrator that routes messages between proxies, agents, and MCP servers. It must adapt messages as they flow through the system and maintain the correct ordering. Patchwork is a programmatic interface for working with agents. It allows Rust programs to run prompts that provide custom tools (implemented using [MCP-over-ACP](./mcp-over-acp)) and messages: ```rust theme={null} let mut results = Vec::new(); let _: () = patchwork.think() .text("Process each item and record it using the `record` tool") .tool( "record", "Record a processed item", async |input: RecordInput, _cx| { results.push(input.item); Ok(RecordOutput { success: true }) }, acp::tool_fn_mut!(), ) .await?; // After the think block, `results` contains all recorded items println!("Recorded: {:?}", results); ``` ### Limitation: Handlers can't send messages The current SDK uses traits like `Agent` where handler methods receive only the request and return only the response: ```rust theme={null} #[async_trait] pub trait Agent { async fn prompt(&self, args: PromptRequest) -> Result; // ... } ``` There's no (easy) way to send messages back to the client from within a handler. If you want to send a `SessionNotification` while processing a prompt, you need to obtain an `AgentSideConnection` through some other means and coordinate access yourself. This is awkward for agents (which want to stream progress during prompt processing) and prohibitive for proxies (which need to forward messages to their successor while handling a request from their predecessor). **Goal:** Handlers should receive a context parameter that provides methods to send requests and notifications back through the connection. ### Limitation: Fixed set of handlers ACP is an extensible protocol where users can add their own method names beginning with `_`. The current SDK uses a trait which means that it cannot offer "first-class" support for user-defined requests/notifications. Instead, these are handled using extension methods (`ext_method`, `ext_notification`). These methods have no static typing and require the user to work with raw JSON. **Goal:** Allow SDK users to define their own request/notification types that are handled in the same fashion as built-in types. ### Limitation: Message handlers must be the same across all session The current API always executes the same handler code for a particular method (e.g., a session/update). If different handling is required for a particular session, that handler must maintain some kind of map from session-id to identify what handling is required, which is non-trivial bookkeeping that can become awkward. As an example of how complex this can get, consider the [elaborate message forwarding scheme](https://nikomatsakis.github.io/threadbare/agent.html#full-trace-nested-think) used by older versions of patchwork. **Goal:** Allow SDK users to add/remove "dynamic" handlers that are specific to a particular session or other part of the protocol. These handlers should be closures so they can capture state. ### Limitation: No ordering guarantees In the current SDK, every incoming request or notification results is handled in a freshly spawned task. This means that it is not possible to guarantee that requests or notifications are handled in the order they arrive. It is also not possible to be sure that a notification is fully handled before the response to another request; this makes it difficult to, for example, be sure that a `session/update` notification is handled before the turn is ended (which is sent as the response to the `prompt` request). This is essential for an application like patchwork, which wishes to fully capture the updates before returning. **Goal:** Handlers should block message processing to allow them to ensure that they fully process a message before other messages are processed. ### Limitation: Confusing naming and 1:1 assumption `AgentSideConnection` is ambiguous - does this represent the agent, or the connection *to* an agent? What's more, the SDK currently assumes that each connection has a single peer, but [proxies](./proxy-chains) may wish to send/receive messages from multiple peers (client or agent). This was a constant source of confusion in early versions of the conductor and frequently required the author to get out a pencil and paper and work things out very carefully. **Goal:** Use directional naming like `ClientToAgent` that makes relationships explicit: "I am the client, the remote peer is an agent." Enable multiple peers. ### Limitation: Awkward to connect components When building tests and other applications, it's convenient to be able to create a client and connect it directly to an agent, leaving the plumbing to the framework. The current SDK only accepts channels and byte-streams which creates unnecessary boilerplate. **Goal:** Provide a `Component` trait that abstracts over anything that can connect to an ACP transport, enabling uniform handling in orchestration scenarios. ### Challenge: Executor independence and starvation freedom This isn't a limitation of the current SDK per se, but a common pitfall in async Rust designs that we want to address. We want the SDK to be independent from specific executors (not tied to tokio) while still supporting richer patterns like spawning background tasks. One specific common issue with Rust async APIs is *starvation*, which can occur with stream-like APIs where it is important to keep awaiting the stream so that items make progress. For example, in a setup like this one, the "connection" is not being "awaited" while each message is handled: ```rust theme={null} // PROBLEMATIC: message handling starves while processing while let Some(message) = connection.next().await { process(message).await; // connection is quiescent during this await! } ``` With careful design, it is possible to avoid these hazards. The most common way is either to spawn tasks (which then ties one to a specific executor) or to use "interior iteration" style APIs like `for_each` or `run_until`: ```rust theme={null} // CORRECT: message handling continues concurrently connection.run_until(async |cx| { // The connection processes messages while this code runs let response = cx.send_request(request).block_task().await?; process(response).await }).await ``` **Goal:** Provide APIs that are starvation-free by design, making it difficult to accidentally block message processing. ## What we propose to do about it > What are you proposing to improve the situation? We propose to adopt the design and implementation from [`sacp`](https://github.com/symposium-dev/symposium-acp/) (developed as part of the [Symposium project](https://symposium.dev)) as the foundation for `agent-client-protocol` v1.0. The `sacp` crates will be imported into this repository and renamed: | Current | New name | | ------------------- | ------------------------------------ | | `sacp` | `agent-client-protocol` (v1.0) | | `sacp-derive` | `agent-client-protocol-derive` | | `sacp-tokio` | `agent-client-protocol-tokio` | | `sacp-rmcp` | `agent-client-protocol-rmcp` | | `sacp-conductor` | `agent-client-protocol-conductor` | | `sacp-test` | `agent-client-protocol-test` | | `sacp-tee` | `agent-client-protocol-tee` | | `sacp-trace-viewer` | `agent-client-protocol-trace-viewer` | The `sacp` crates will then be deprecated in favor of the `agent-client-protocol` family. The new SDK addresses the limitations above through a builder-based API with explicit connection semantics. The following table summarizes the key API concepts and which goals they address: | API Concept | Goals Addressed | | ----------------------------------------------------------------------------------- | ----------------------------------------- | | [Link types](#link-types-and-directional-naming) (`ClientToAgent`, `AgentToClient`) | Confusing naming, 1:1 assumption | | [`Component` trait + `connect_to`](#the-component-trait-and-connect_to) | Awkward to connect components | | [Connection context (`cx`)](#sending-messages) | Handlers can't send messages | | [`on_receive_*` handlers](#handling-messages) with closure types | Fixed set of handlers | | [`serve` / `run_until`](#running-connections-serve-and-run_until) | Executor independence, starvation freedom | | [Session builders + dynamic handlers](#session-builders-and-mcp-servers) | Handlers must be same across sessions | | [Ordering guarantees + `spawn`](#controlling-ordering) | No ordering guarantees | We have validated the design by implementing a number of use cases: * **sacp-conductor** (to be renamed **agent-client-protocol-conductor**) - implementation of the conductor from the [proxy chains](./proxy-chains) RFD * [Patchwork](https://patchwork-lang.github.io/patchwork-rs/) - programmatic agent orchestration * **elizacp** - agent implementing the classic ELIZA program * **agent-client-protocol-tee** - proxy that logs messages before forwarding * **yopo** ("You Only Prompt Once") - CLI tool for single prompts The [Deep dive](#deep-dive) section below walks through each concept in detail. ## Deep dive This section walks through the SDK concepts in detail, organized by what you're trying to do. ### Getting up and going #### Link types and directional naming The SDK is organized around *link types* that describe who you are and who you're talking to. The two most common examples are: * `ClientToAgent` - "I am a client, connecting to an agent" * `AgentToClient` - "I am an agent, serving a client" To build a connection, start with the link type and invoke the `builder` method. Builders use the typical "fluent" style: ```rust theme={null} // As a client connecting to an agent ClientToAgent::builder() .name("my-client") // optional, useful for tracing ``` ```rust theme={null} // As an agent serving clients AgentToClient::builder() .name("my-agent") // optional, useful for tracing ``` Most types in the SDK are parameterized by the link type. This helps document the intent of the connection and also determines default method handling when no event handler is registered. (Both `ClientToAgent` and `AgentToClient` generally error on unhandled messages, but proxies default to forwarding.) #### The `Component` trait and `connect_to` The `connect_to` method connects your builder to the other side. The argument can be anything that implements the `Component` trait, which abstracts over anything that can communicate via JSON-RPC: ```rust theme={null} // Connect to an agent over stdio ClientToAgent::builder() .connect_to(acp::stdio()) ``` The `AcpAgent` type allows connecting to an external ACP agent or agent extension: ```rust theme={null} // Connect to a specific agent by command ClientToAgent::builder() .connect_to(AcpAgent::from_str("some-command --acp")) // Connect to Zed's Claude Code integration ClientToAgent::builder() .connect_to(AcpAgent::zed_claude_code()) ``` For testing, you can connect builders directly to each other - no transport setup required: ```rust theme={null} ClientToAgent::builder() .connect_to(AgentToClient::builder()) ``` Or connect to a struct that implements `Component`: ```rust theme={null} impl Component for MyAgent { async fn serve(self, client: impl Component) -> Result<(), acp::Error> { AgentToClient::builder() .on_receive_request(/* ... */) .serve(client) .await } } // Connect client directly to agent - useful for testing ClientToAgent::builder() .connect_to(MyAgent::new()) ``` #### Running connections: `serve` and `run_until` The `connect_to` method returns a `JrConnection`, but that connection is inert until executed. There are two ways to run it. **`serve()`** runs until the connection is closed. This is for "reactive" components that respond to incoming messages: ```rust theme={null} AgentToClient::builder() .name("my-agent") .on_receive_request(/* ... */) .connect_to(transport)? .serve() .await ``` **`run_until()`** takes an async closure and runs your code concurrently with message handling. The closure receives a *connection context* (conventionally called `cx`) - this is how you interact with the connection, sending messages, spawning tasks, and adding dynamic handlers. When the closure returns, the connection closes: ```rust theme={null} ClientToAgent::builder() .name("my-client") .connect_to(transport)? .run_until(async |cx| { // Your code runs here while messages are handled in the background. // Use `cx` to send requests and notifications. let response = cx.send_request(InitializeRequest::new(ProtocolVersion::LATEST)) .block_task().await?; Ok(response) }) .await ``` The `run_until` pattern directly addresses starvation. Instead of exposing an async stream that users might accidentally block, `run_until` runs your code *concurrently* with message handling. The `cx` type (`JrConnectionCx`) follows the "handle" pattern: cloned values refer to the same connection. It's `'static` so it can be sent across threads or stored in structs. Handlers registered with `on_receive_*` also receive a `cx` parameter. ### Sending messages #### Sending notifications Use `cx.send_notification()` to send a notification. It returns a `Result` that is `Err` if the connection is broken: ```rust theme={null} cx.send_notification(StatusNotification::new("processing"))?; ``` #### Sending requests Use `cx.send_request()` to send a request. This returns a handle for managing the response: ```rust theme={null} let response_handle = cx.send_request(PromptRequest::new(session_id, messages)); ``` The handle is not the response itself - that may not have arrived yet. You have two options for getting it: **`on_response` / `on_ok_response`** registers a handler that runs when the response arrives: ```rust theme={null} cx.send_request(PromptRequest::new(session_id, messages)) .on_ok_response( async move |response: PromptResponse, cx| { println!("Agent finished: {:?}", response.stop_reason); Ok(()) }, acp::on_response!() )?; ``` **`block_task`** returns a future you can await: ```rust theme={null} let response: PromptResponse = cx.send_request(PromptRequest::new(session_id, messages)) .block_task() .await?; ``` The `block_task` approach is convenient but dangerous in handlers (methods that begin with `on_`). See [Controlling ordering](#controlling-ordering) for details. ### Controlling ordering #### Atomic handlers Handler methods (methods whose names begin with `on_`) execute in the order messages arrive. Each handler must complete before the next message is processed: ```rust theme={null} .on_receive_request(async |req: PromptRequest, request_cx, cx| { // No other messages will be processed while this runs cx.send_notification(StatusNotification::new("processing"))?; // The notification is guaranteed to be sent before the response request_cx.respond(PromptResponse::new(StopReason::EndTurn)) }, acp::on_receive_request!()) ``` #### `block_task` and deadlock Using `block_task` inside a handler creates a deadlock: the handler waits for a response, but responses can't be processed until the handler completes. ```rust theme={null} // WRONG - will deadlock .on_receive_request(async |req: PromptRequest, request_cx, cx| { let response = cx.send_request(SomeOtherRequest::new()) .block_task() // Deadlock! Handler blocks waiting for response .await?; // but responses can't be processed until handler returns request_cx.respond(/* ... */) }, acp::on_receive_request!()) ``` Use `on_response` instead, or spawn a task. #### Spawning tasks Use `cx.spawn` to run work concurrently with message handling: ```rust theme={null} .on_receive_request(async |request: PromptRequest, request_cx, cx| { cx.spawn(async move { // Safe to use block_task here - we're in a spawned task let response = cx.send_request(InitializeRequest::new(ProtocolVersion::LATEST)) .block_task() .await?; /* ... */ Ok(()) })?; // Handler returns immediately, spawned work continues request_cx.respond(/* ... */) }, acp::on_receive_request!()) ``` Spawned tasks are tracked in the `JrConnectionCx` and don't require runtime-specific spawning. ### Client sessions When a client sends a `NewSessionRequest`, agents typically need to set up session-specific state: handlers that only apply to this session, MCP servers with tools tailored to the workspace, or initialization logic that runs once the session is confirmed. #### Session builders The session builder API provides a fluent interface for configuring sessions. Start with `cx.build_session()` or `cx.build_session_from()` and chain configuration methods: ```rust theme={null} cx.build_session("/path/to/workspace") .with_mcp_server(my_mcp_server)? // Attach MCP servers (see below) // ... additional configuration ``` MCP servers provide tools that the agent can invoke. We'll show how to define them in the examples below. #### Running sessions with `run_until` The primary way to run a session is with `block_task().run_until()`. This pattern allows your closure to capture borrowed state from the surrounding scope - no `'static` requirement: ```rust theme={null} // Inside a run_until closure (not a handler) let workspace_path = Path::new("/my/workspace"); cx.build_session(workspace_path) .with_mcp_server( McpServer::builder("tools") .tool_fn("get_path", "Returns the path", async move |_: (), _| { // Can capture `workspace_path` by reference! Ok(workspace_path.display().to_string()) }, acp::tool_fn!()) .build() )? .block_task() .run_until(async |mut session| { session.send_prompt("What is the workspace path?")?; let response = session.read_to_string().await?; println!("{response}"); Ok(()) }) .await?; ``` The `run_until` closure receives an `ActiveSession` with methods for interacting with the agent: * **`send_prompt(text)`** - Send a prompt to the agent * **`read_to_string()`** - Read all updates until the turn ends, returning text content * **`read_update()`** - Read individual updates for fine-grained control For more complex MCP servers, you can use the standard rmcp API via the `agent-client-protocol-rmcp` crate: ```rust theme={null} use agent_client_protocol_rmcp::RmcpServer; cx.build_session(workspace_path) .with_mcp_server(RmcpServer::new(my_rmcp_service))? // ... ``` #### Non-blocking sessions with `on_session_start` When you need to start a session from inside an `on_receive_*` handler but can't block, use `on_session_start`. This spawns the session work and returns immediately: ```rust theme={null} .on_receive_request(async |req: NewSessionRequest, request_cx, cx| { cx.build_session_from(req) .with_mcp_server(my_mcp_server)? .on_session_start(async |mut session| { session.send_prompt("Hello")?; let response = session.read_to_string().await?; Ok(()) })?; // Handler returns immediately, session runs in background Ok(()) }, acp::on_receive_request!()) ``` Note that `on_session_start` requires `'static` - closures and MCP servers cannot borrow from the surrounding scope. Use owned data or `Arc` for shared state. #### `start_session` and proxy sessions For cases where you want to avoid the rightward drift of `run_until` but still need blocking behavior, `start_session` returns an `ActiveSession` handle directly: ```rust theme={null} let mut session = cx.build_session(workspace_path) .with_mcp_server(my_mcp_server)? .block_task() .start_session() .await?; session.send_prompt("Hello")?; let response = session.read_to_string().await?; ``` Like `on_session_start`, this requires `'static` for closures and MCP servers. For proxies that want to inject MCP servers but otherwise forward all messages, use `start_session_proxy`: ```rust theme={null} .on_receive_request(async |req: NewSessionRequest, request_cx, cx| { let session_id = cx.build_session_from(req) .with_mcp_server(injected_tools)? .block_task() .start_session_proxy(request_cx) .await?; // Session messages are automatically proxied between client and agent Ok(()) }, acp::on_receive_request!()) ``` ### Handling messages #### Notification handlers Register handlers using `on_receive_notification`. The closure's first argument type determines which notification type it handles: ```rust theme={null} AgentToClient::builder() .on_receive_notification( async |notif: SessionNotification, cx| { // ------------------- // Expected notification type println!("Session update: {:?}", notif.update); Ok(()) }, acp::on_receive_notification!(), // <-- Hacky macro argument required ) ``` Note the 'hacky macro argument'. This is required due to current limitations in async closures. It can be removed once [Return Type Notation](https://github.com/rust-lang/rfcs/pull/3654) is stabilized and [issue #149407](https://github.com/rust-lang/rust/issues/149407) is fixed. #### Request handlers Request handlers receive an additional `request_cx` parameter for sending the response: ```rust theme={null} .on_receive_request( async |req: PromptRequest, request_cx, cx| { // Process the request... cx.send_notification(StatusNotification::new("processing"))?; // Send the response request_cx.respond(PromptResponse::new(StopReason::EndTurn)) }, acp::on_receive_request!(), ) ``` The `request_cx` is `#[must_use]` - the compiler warns if you forget to send a response. It provides three methods: * **`respond(response)`** - Send a successful response * **`respond_with_error(error)`** - Send an error response * **`respond_with_result(result)`** - Send either, based on a `Result` The `request_cx` is `Send`, so you can move it to another task or thread if you need to respond asynchronously: ```rust theme={null} .on_receive_request( async |req: LongRunningRequest, request_cx, cx| { cx.spawn(async move { let result = do_expensive_work(&req).await; request_cx.respond_with_result(result) }); Ok(()) }, acp::on_receive_request!(), ) ``` ### Custom message types Define custom notifications and requests using derive macros: ```rust theme={null} #[derive(Debug, Serialize, Deserialize, JsonSchema, JrNotification)] #[notification(method = "_myapp/progress")] struct ProgressNotification { percent: u32, message: String, } #[derive(Debug, Serialize, Deserialize, JsonSchema, JrRequest)] #[request(method = "_myapp/compute", response = ComputeResponse)] struct ComputeRequest { input: String, } #[derive(Debug, Serialize, Deserialize, JsonSchema)] struct ComputeResponse { result: String, } ``` Custom types work exactly like built-in types - no special `ext_notification` path required: ```rust theme={null} ClientToAgent::builder() .on_receive_notification( async |notif: ProgressNotification, cx| { println!("Progress: {}% - {}", notif.percent, notif.message); Ok(()) }, acp::on_receive_notification!() ) ``` #### Generic message handlers Use `on_receive_message` with `MessageCx` to intercept any incoming message (request or notification) before typed handlers: ```rust theme={null} .on_receive_message( async |message: MessageCx, cx| { // Forward all messages to another thread for processing message_sender.send(message)?; Ok(()) }, acp::on_receive_message!(), ) ``` `MessageCx` is useful for forwarding, logging, or other scenarios where you need to intercept messages before typed dispatch. ### Message handling in depth #### Handler chains A *message handler* takes ownership of a message and either handles it or returns a (possibly modified) copy to be tried by the next handler. Handlers are chained together - each gets a chance to claim the message before it passes to the next. ```mermaid theme={null} flowchart TD MSG[Incoming Message] --> STATIC subgraph STATIC[Static Handlers] S1[Handler 1] --> S2[Handler 2] --> S3[Handler n...] end STATIC -->|not claimed| DYNAMIC subgraph DYNAMIC[Dynamic Handlers] D1[Session handlers, etc.] end DYNAMIC -->|not claimed| DEFAULT[Link Default Handler] DEFAULT -->|not claimed| UNHANDLED{Unhandled} UNHANDLED -->|retry: false| ERROR[Send error response] UNHANDLED -->|retry: true| QUEUE[Queue for retry] QUEUE -->|new handler added| DYNAMIC ``` **Static handlers** are registered at build time via `.on_receive_request()`, etc. They're tried in registration order. **Dynamic handlers** are added at runtime via `cx.add_dynamic_handler()`. They're useful for sub-protocols where groups of related messages are identified by some kind of ID. For example, session messages all share a `session_id` - a dynamic handler can be registered for each session to handle its messages. **Link default handler** provides fallback behavior based on the link type (e.g., proxies forward unhandled messages). #### Message handlers The `JrMessageHandler` trait defines how handlers work: ```rust theme={null} pub trait JrMessageHandler: Send { type Link: JrLink; async fn handle_message( &mut self, message: MessageCx, cx: JrConnectionCx, ) -> Result, Error>; } ``` The handler takes ownership of the message. If it handles the message, it returns `Handled::Yes`. If not, it returns ownership via `Handled::No { message, retry }` so the next handler can try: ```rust theme={null} pub enum Handled { Yes, No { message: T, retry: bool }, } ``` **The `retry` flag**: If any of the static or dynamic handlers returns `retry: true`, and no handler ultimately claims the message, it gets queued and offered to each new dynamic handler as it's added. This solves a race condition with sessions: messages for a session may arrive before the session's dynamic handler is registered. The default handlers for `ClientToAgent` and `AgentToClient` already set `retry: true` for session messages with unrecognized session IDs, so you typically don't need to handle this yourself. For convenience, handlers can return `Ok(())` which is equivalent to `Handled::Yes`. Handlers can also modify the message before passing it along: ```rust theme={null} .on_receive_request(async |mut req: EchoRequest, request_cx, cx| { req.text.push("modified".to_string()); Ok(Handled::No { message: (req, request_cx), retry: false, }) }, acp::on_receive_request!()) ``` #### Registering dynamic handlers Register dynamic handlers at runtime for session-specific or protocol-specific message handling: ```rust theme={null} let registration = cx.add_dynamic_handler(MySessionHandler::new(session_id))?; ``` When `registration` is dropped, the dynamic handler is removed. To keep it alive indefinitely, call `run_indefinitely()`: ```rust theme={null} registration.run_indefinitely(); ``` #### Default handling from link type Each link type defines default handling for unhandled messages. For example: * **`ClientToAgent`** - Errors on unhandled requests, ignores unhandled notifications * **`ProxyToConductor`** - Forwards unhandled messages to the next component You only need to register handlers for messages you want to intercept. #### MatchMessage for implementing handlers When implementing `JrMessageHandler` directly, `MatchMessage` provides ergonomic dispatch: ```rust theme={null} impl JrMessageHandler for MyHandler { type Link = AgentToClient; async fn handle_message( &mut self, message: MessageCx, cx: JrConnectionCx, ) -> Result, Error> { MatchMessage::new(message) .if_request(async |req: InitializeRequest, request_cx| { request_cx.respond(InitializeResponse::new(req.protocol_version)) }) .if_request(async |req: PromptRequest, request_cx| { request_cx.respond(PromptResponse::new(StopReason::EndTurn)) }) .if_notification(async |notif: SessionNotification| { log::info!("Session update: {:?}", notif); Ok(()) }) .await .done() } } ``` For proxies with multiple peers, `MatchMessageFrom` dispatches based on message source: ```rust theme={null} MatchMessageFrom::new(message, &cx) .if_request_from(Client, async |req: PromptRequest, request_cx| { // Handle requests from the client }) .if_notification_from(Agent, async |notif: SessionNotification| { // Handle notifications from the agent }) .await .done() ``` ### Error handling Errors in handlers tear down the connection. If a handler returns an `Err`, the connection closes and all pending operations fail. For request handlers, you can propagate error responses instead of tearing down the connection: ```rust theme={null} .on_receive_request(async |req: ComputeRequest, request_cx, cx| { match process(&req) { Ok(result) => request_cx.respond(ComputeResponse { result }), Err(e) => request_cx.respond_err(JsonRpcError::new( ErrorCode::InvalidParams, format!("Failed to process: {}", e), )), } }, acp::on_receive_request!()) ``` ### Writing proxies #### Multiple peers Simple link types like `ClientToAgent` have one remote peer. Proxy link types like `ProxyToConductor` have two: `Client` (predecessor) and `Agent` (successor). With multiple peers, you must explicitly name which peer you're communicating with: ```rust theme={null} ProxyToConductor::builder() .on_receive_notification_from( acp::Agent, // <-- Receive from the agent async |notif: SessionNotification, cx| { cx.send_notification_to(acp::Client, notif)?; // ----------- // Send to the client Ok(()) }, acp::on_receive_notification!(), ) ``` #### Default forwarding For proxies, the default handling is typically `Forward` - unhandled messages pass through to the next component. You only need to register handlers for messages you want to intercept or modify. #### Session builders for proxies Proxies can add session-scoped handlers: ```rust theme={null} cx.build_session_from(req) .on_receive_notification(async |notif: SessionNotification, cx| { // This handler only runs for this session log_notification(¬if); Ok(()) }, acp::on_receive_notification!()) .on_proxy_session_start(request_cx, async |_| Ok(())) ``` ### Advanced: Defining custom link types Link types define the relationship between peers. The SDK provides built-in types, but you can define your own: ```rust theme={null} use acp::link::{JrLink, LinkDirection}; pub struct MyCustomLink; impl JrLink for MyCustomLink { type ConnectsTo = OtherSideLink; fn direction() -> LinkDirection { LinkDirection::Outbound // We initiate the connection } fn default_request_handling() -> DefaultHandling { DefaultHandling::Error // Unknown requests return an error } fn default_notification_handling() -> DefaultHandling { DefaultHandling::Ignore // Unknown notifications are silently dropped } } ``` ## Shiny future > How will things play out once this feature exists? ### Unified Rust ACP experience The Rust ecosystem will have a single SDK for ACP development. Whether you're building a simple client, a proxy chain, or a programmatic orchestration framework like patchwork-rs, the same SDK handles all cases. ### Smooth transition from the current SDK The new SDK is more ergonomic than the current trait-based approach, so migration should be straightforward for most users. The builder pattern with context parameters (`cx`) replaces the `AgentSideConnection` pattern, and the directional naming (`ClientToAgent` vs `AgentSideConnection`) makes code clearer. We can provide migration guidance and potentially a thin compatibility layer for common patterns, but most users will find the new code simpler than the old. ### Potential crate reorganization Currently, `agent-client-protocol` contains both generic JSON-RPC machinery (builder patterns, message handling, connection management) and ACP-specific types (link types, schema integration). In the future, it might be valuable to split the generic JSON-RPC layer into its own crate. However, this is complicated by the trait implementations: the generic traits need to be implemented for ACP types currently defined in the schema crate. We'd need to carefully consider where type definitions live to avoid orphan rule issues. This reorganization isn't blocking for the initial adoption. ### Cross-language SDK alignment The design principles here - builder patterns, context parameters, directional naming, component abstractions - aren't Rust-specific. They also informed the 1.0 TypeScript SDK, which is available as [`@agentclientprotocol/sdk` v1.0.0](https://www.npmjs.com/package/@agentclientprotocol/sdk/v/1.0.0). This doesn't mean other SDKs should be ports of the Rust SDK. Each language has its own idioms. But the core ideas (explicit message ordering, composable components, context in callbacks) translate across languages. ### Foundation for protocol evolution The builder pattern and component model make it easier to evolve the ACP protocol. New methods can be added without breaking existing code. New component types (beyond client/agent/proxy) can be introduced by implementing the Component trait. ## Implementation details and plan > Tell me more about your implementation. What is your detailed implementation plan? ### Crate structure The new SDK is organized into several crates with clear responsibilities: * **`agent-client-protocol`** - Core SDK with builder patterns, link types, and component abstractions * **`agent-client-protocol-tokio`** - Tokio runtime integration (spawn, timers, I/O) * **`agent-client-protocol-rmcp`** - Bridge to the rmcp crate for MCP integration * **`agent-client-protocol-conductor`** - Reference conductor implementation * **`agent-client-protocol-derive`** - Derive macros for JSON-RPC traits * **`agent-client-protocol-test`** - Test utilities and mock implementations * **`agent-client-protocol-tee`** - Debugging proxy that logs all traffic * **`agent-client-protocol-trace-viewer`** - Interactive sequence diagram viewer for trace files ### Current status This RFD is completed. The SACP-based Rust SDK has been released as [`agent-client-protocol` v1.0.0](https://crates.io/crates/agent-client-protocol/1.0.0), with API documentation on [docs.rs](https://docs.rs/agent-client-protocol/1.0.0). The TypeScript SDK also reached 1.0 as [`@agentclientprotocol/sdk` v1.0.0](https://www.npmjs.com/package/@agentclientprotocol/sdk/v/1.0.0), validating that the SDK direction was not Rust-only. The implementation lives in [agentclientprotocol/rust-sdk](https://github.com/agentclientprotocol/rust-sdk). The original [`sacp`](https://github.com/symposium-dev/symposium-acp) crates remain historical upstream packages and powered: * The conductor (proxy chain orchestration) * patchwork-rs (programmatic agent orchestration) * Symposium (Rust development environment) ### Migration path The transition has been completed through the 1.0 SDK release: 1. **Import `sacp` crates** to `agentclientprotocol/rust-sdk` with the new `agent-client-protocol-*` naming 2. **Preview the upstreamed SDK** to collect feedback on the new builder-based API 3. **Release `agent-client-protocol` v1.0** as the stable Rust SDK 4. **Align other official SDKs** around the same design goals where language idioms allow, starting with the TypeScript SDK's 1.0 release Most users should find migration straightforward - the builder pattern is more ergonomic than the trait-based approach, so the new code is often simpler than the old. Follow-up migration guides, compatibility helpers, or `sacp` deprecation notices can be handled as normal SDK maintenance outside this RFD. ## Frequently asked questions > What questions have arisen over the course of authoring this document or during subsequent discussions? ### What alternative approaches did you consider? We first attempted to build on the existing SDK but due to the limitations decided to try an alternative approach. ### What about other language SDKs? We would like to try and adapt these ideas to other languages. It would be good if the SDKs for all languages took the same general approach. Most of the concerns in this document are not Rust-specific, though as often happens, the limitations become more annoying in Rust because of the limits imposed by the ownership system. ### How does this relate to the Proxy Chains and MCP-over-ACP RFDs? This expanded SDK design is motivated by working through the use cases enabled by [proxy chains](./proxy-chains) and [MCP-over-ACP](./mcp-over-acp). ### How well-tested is this design? The design has been used for a wide range of projects but the majority were written by the SDK author, though Amazon's kiro-cli team and the Goose client adopted sacp for their use case with minimal difficulty. Before we finalize the design, it would be good to have more adopters to help ensure that it meets all common needs. ### Can I derive `JrRequest`/`JrNotification` on enums? Not currently. The derive macros only support structs with a single method name. For enums that group related messages (e.g., all session-related requests), you would need to implement the traits manually. This is a potential future enhancement - enum derives could dispatch to different methods per variant, which would be useful for `MessageCx` typed handlers. For now, use the untyped `MessageCx` with `MatchMessage` for this pattern. ### What changes are needed before stabilizing? The Rust SDK has been stabilized as `agent-client-protocol` v1.0.0, so no RFD-blocking work remains. Future API polish, compatibility helpers, and MCP-over-ACP refinements should be tracked as normal SDK follow-up work. ## Revision history * 2026-06-25: Moved to Completed after the Rust SDK and TypeScript SDK reached v1.0.0. * 2026-05-12: Moved to Preview after the initial crate import was upstreamed to agentclientprotocol/rust-sdk. * Initial draft based on working implementation in symposium-acp repository. # Closing active sessions Source: https://agentclientprotocol.com/rfds/session-close Author(s): [@SteffenDE](https://github.com/SteffenDE) ## Elevator pitch > What are you proposing to change? We propose adding the ability to close active sessions. This allows the agent to free up any memory and threads/subprocesses associated with that session. ## Status quo > How do things work today and what problems does this cause? Why would we change things? Today, if you start a session, it will remain active until the ACP process is terminated. This means that if the agent implements sessions as separate processes, active users with many sessions can end up with a lot of processes unnecessarily using up system memory. Even if the agent does not spawn separate processes, memory used by large tool results or similar could still accumulate over time. The only way to free up that memory is to terminate the whole ACP process, which will stop all sessions and - if the agent does not support resuming sessions (load / resume / fork) - lead to a bad user experience. ## What we propose to do about it > What are you proposing to improve the situation? Add a new `session/close` method. If supported, the agent **must** cancel any ongoing work related to the session (treat it as if `session/cancel` was called) and then free up any resources associated with the session. ## Shiny future > How will things will play out once this feature exists? Clients can track what sessions are actively used by a user and automatically close old sessions. ## Implementation details and plan > Tell me more about your implementation. What is your detailed implementation plan? We propose to add a new `"session/close"` method. Agents must declare this option is available by returning `sessionCapabilities: { close: {} }` in their capabilities. The object is reserved to declare future capabilities. Then the client would be able to close a specific session with: ```json theme={null} { "jsonrpc": "2.0", "id": 1, "method": "session/close", "params": { "sessionId": "sess_789xyz" } } ``` Agents might reply with an error if the session is not active or does not exist. ## Frequently asked questions > What questions have arisen over the course of authoring this document or during subsequent discussions? None so far. ### What alternative approaches did you consider, and why did you settle on this one? It could be an agent specific custom method, since we mainly ran into problems with Claude Code, but even for agents that don't spawn full subprocesses for sessions, cleaning up unneeded sessions still seems like a good idea. ## Revision history * 2026-04-23: RFD marked as Completed; `session/close` is stabilized * 2026-04-14: Move to preview and update capability docs to `sessionCapabilities.close` * 2026-03-09: Rename from session/stop to session/close * 2026-02-24: Initial draft # Session Config Options Source: https://agentclientprotocol.com/rfds/session-config-options Author(s): [@benbrandt](https://github.com/benbrandt) ## Elevator pitch > What are you proposing to change? Allow Agents to provide an arbitrary list of configuration selectors for a given session. Rather than only supporting modes or models, we can allow each Agent to more flexibly specify which configurations to allow the Client to offer the user. ## Status quo > How do things work today and what problems does this cause? Why would we change things? Currently, we allow Agents to [specify a list of modes](https://agentclientprotocol.com/protocol/v1/session-modes) they can run in. The state of the currently selected item is allowed to be modified by both the [Client](https://agentclientprotocol.com/protocol/v1/session-modes#from-the-client) and the [Agent](https://agentclientprotocol.com/protocol/v1/session-modes#from-the-agent). The obvious next selector was a [model selector](https://github.com/agentclientprotocol/agent-client-protocol/pull/182). However, when implementing this, it became clear that even for our current agents, it is not just as simple as "which model do you want?", but also which variant of a given model in terms of thinking parameters that might be better to express as yet another selector. Adding more hard-coded selector options would potentially lead the protocol to need to support many optional ones, or implementors would need to try to find the best existing selector to hack an option into if there wasn't an obvious fit. And if, a few months from now, no agents support something like a mode or reasoning selector, the protocol is left with leftover methods no one really uses, cluttering the interface. Since this space is moving fast, we ideally would find a more flexible option with enough constraints to allow Clients and Agents to both reason about the options provided. ## What we propose to do about it > What are you proposing to improve the situation? Instead, we can allow Agents to provide configuration options in the `session/new` response that not only provide a list of options, but also a `key` of some kind that is a unique identifier for that selector. Additionally, we can optionally allow an Agent to mark each option with a semantic category so that Clients can reliably distinguish broadly common option types (e.g. model selector vs session mode selector vs thought/reasoning level), without needing to infer meaning from the option `id` or `name`. This is intended for UX only (e.g. keyboard shortcuts, icons, preferred placement), and MUST NOT be required for correctness. When the Client receives or sends an update to this selector, it would require both the selector key and the key for the new value. To start, we could continue offering single-value selectors (dropdowns), but allow for the Agent to decide what those are for. ## Shiny future > How will things will play out once this feature exists? The Agent provides a list of available configuration options. The Agent cannot rely on the Client to set or even display these options, as it may not support it. So an Agent MUST always have a default configuration value for every option it provides, and MUST be able to run a turn without these configuration options being set. The Client can render the options provided, send updated values to the Agent, and also display any changes the Agent made during the course of it's execution (for example, if it changes modes or models because of fallbacks or a change in strategy, so that the user can always see the current state). Since we are moving to a world in which there are multiple configuration options, some of which may depend on each other, the Agent MUST provide the complete set of configuration options and their current values whenever a change is made. We would tradeoff some extra data being sent to the Client in order to help minimize the amount of state required to be managed by the Client. The Client would submit a new value, and receive back the full state of all configuration options that it can then replace it's current state with and render. So if changing a model means there are no thinking options, or a new option becomes available, or another value needs to change because the values of an option are different, the Agent will reflect this in its response by providing the entire new state (or an error if it is somehow an invalid selection). ## Implementation details and plan > Tell me more about your implementation. What is your detailed implementation plan? To start, we can implement this based on the [Session Modes](https://agentclientprotocol.com/protocol/v1/session-modes) api. Something like an `InitializeResponse` that looks like: ```json theme={null} { "jsonrpc": "2.0", "id": 1, "result": { "sessionId": "sess_abc123def456", "configOptions": [ { "id": "mode", // this is the unique `key` for communication about which option is being used "name": "Session Mode", // Human-readable label for the option "description": "Optional description for the Client to display to the user." "category": "mode", "type": "select", "currentValue": "ask", "options": [ { "value": "ask", "name": "Ask", "description": "Request permission before making any changes" }, { "value": "code", "name": "Code", "description": "Write and modify code with full tool access" } ] }, { "id": "models", "name": "Model", "category": "model", "type": "select", "currentValue": "ask", "options": [ { "value": "model-1", "name": "Model 1", "description": "The fastest model" }, { "value": "model-2", "name": "Model 2", "description": "The most powerful model" } ] } ] } } ``` ### Option category (optional) Each top-level config option MAY include an optional `category` field. This is intended to help Clients distinguish broadly common selectors and provide a consistent UX (for example, attaching keyboard shortcuts to the first option of a given category). In addition to `category`, Clients SHOULD use the ordering of the `configOptions` array as provided by the Agent as the primary way to establish priority and resolve ties. For example, if multiple options share the same `category`, a Client can prefer the first matching option in the list when assigning keyboard shortcuts or deciding which options to surface most prominently. `category` is semantic metadata and MUST NOT be required for correctness. Clients MUST handle missing or unknown categories gracefully. Category names beginning with `_` are free for custom use. Category names that do not begin with `_` are reserved for the ACP spec. Proposed enum: * `mode` - Session mode selector * `model` - Model selector * `thought_level` - Thought/reasoning level selector * Any string beginning with `_` - Custom category (e.g., `_my_custom_category`) When we introduce this, we could also allow for grouped options, in case there are logical sub-headers and groupings for the options in an individual selector. ```json theme={null} { "id": "models", "name": "Model", "currentValue": "ask", "type": "select", "options": [ { "group": "Provider A", "options": [ { "value": "model-1", "name": "Model 1", "description": "The fastest model" } ] }, { "group": "Provider B", "options": [ { "value": "model-2", "name": "Model 2", "description": "The most powerful model" } ] } ] } ``` We use a list of objects for all of these, to ensure consistent ordering of both the config options and the possible values across languages that may or may not preserve ordering. For grouping options, it needs to be explored whether or not grouped and ungrouped options can be interspersed, or if we need to restrict to one mode or the other (likely the latter). For updating the value from the client and agent, it would follow the same pattern as [session modes](https://agentclientprotocol.com/protocol/v1/session-modes#from-the-client) but have an additional key. ```json theme={null} { "jsonrpc": "2.0", "id": 2, "method": "session/set_config_option", "params": { "sessionId": "sess_abc123def456", "configId": "mode", "value": "code" } } ``` And the response to this request would return the full list of config options with current values. ```json theme={null} { "jsonrpc": "2.0", "id": 2, "result": { "configOptions": [ { "id": "mode", "name": "Session Mode", "type": "select", "currentValue": "ask", "options": [..] }, { "id": "models", "name": "Model", "type": "select", "currentValue": "ask", "options": [..] } ] } } ``` The notification would return the full list of config options with current values as well. ```json theme={null} { "jsonrpc": "2.0", "method": "session/update", "params": { "sessionId": "sess_abc123def456", "update": { "sessionUpdate": "config_option_update", "configOptions": [ { "id": "mode", "name": "Session Mode", "type": "select", "currentValue": "ask", "options": [..] }, { "id": "models", "name": "Model", "type": "select", "currentValue": "ask", "options": [..] } ] } } } ``` We would also likely move session modes to be `@deprecated` in favor of this approach. Until it is removed, we may want Agents to support both fields, and then the Client, if it uses the new config options, should only use the config options supplied and not the `modes` field to avoid duplication. The config options would also take a `type` field to specify different forms of input for the Client to display. If a client receives an option it doesn't recognize, it should ignore it. Since the Agent is required to have a default value, it can gracefully degrade and ignore the option and the Agent should handle it regardless. The Client should also treat the list of options as prioritized by the Agent. So, if for some reason the Agent provides more options than the Client can reasonably display, the Client should show as many as possible, starting at the beginning of the list. We will start with just supporting `select` for now, and expand to other types as needed. ## Frequently asked questions > What questions have arisen over the course of authoring this document or during subsequent discussions? ### What alternative approaches did you consider, and why did you settle on this one? As noted, the Zed team already looked into and implemented experimental support for a model selector. However, this has already diverged from how the Codex CLI is modeling their model selector as of last week, so it seems reasonable to, as per a core design principle of the protocol, only limit the Agent implementations where necessary for the Client to render a faithful UX. Maximizing flexibility for the Agent as they iterate on the best way to model new paradigms seems key here, and it is unclear whether the Client benefits from knowing which type of selector this is. We originally discussed internally having a design closer to this proposal, however walked it back thinking it would be helpful for the Client to know what was being selected. However, as we've now dealt with multiple Agent implementations, it is unclear if this has actually helped the Client, and allowing for more flexibility seems desirable. ### What about connection-level configuration options? This RFD is only concerned with session-level configuration, for which it seems reasonable to require that the Agent can select a default value at all times and not require input from the client before continuing. There seems to be another type of configuration option that is needed when first setting up an Agent (i.e. provider options, plugins, etc.) that are more persistent and may be required by an Agent prior to being able to create a session. These would need to be tackled somewhere closer to the initialization phase, or elsewhere and are out of scope for this RFD. ### What about multi-value selectors? or checkboxes? Or *insert favorite input option here*? This is a question we should discuss of how much complexity we want to introduce for the first version, and how we want to express this to via Client capabilities to allow for more option types in the future. ## Revision history * 2025-10-29: Initial draft * 2026-01-09: Add option categories * 2026-01-15: Allow for category extensions # Session Delete Source: https://agentclientprotocol.com/rfds/session-delete Author(s): [@chazcb](https://github.com/chazcb) Champion: [@benbrandt](https://github.com/benbrandt) ## Elevator pitch > What are you proposing to change? Add a capability-gated `session/delete` method so clients can remove sessions from `session/list`. This complements `session/list` by giving users control over which sessions appear in their session history. ## Status quo > How do things work today and what problems does this cause? Why would we change things? The [`session/list` RFD](/rfds/session-list) introduced the ability for clients to enumerate sessions. However, there's no standard way to remove sessions from this list. Without `session/delete`, users have no control over their session history—old sessions accumulate, and clients must implement non-standard deletion mechanisms or rely on agent-specific cleanup policies. ## What we propose to do about it > What are you proposing to improve the situation? Add a `session/delete` JSON-RPC method that is capability-gated. Agents advertise support via `sessionCapabilities.delete` in their initialization response. ```json theme={null} { "jsonrpc": "2.0", "id": 0, "result": { "protocolVersion": 1, "agentCapabilities": { "sessionCapabilities": { "delete": {} } } } } ``` ### Method ```json theme={null} { "jsonrpc": "2.0", "id": 3, "method": "session/delete", "params": { "sessionId": "sess_abc123def456" } } ``` ### Request Parameters | Field | Type | Required | Description | | ----------- | ----------- | -------- | --------------------- | | `sessionId` | `SessionId` | Yes | The session to delete | ### Response On success, returns an empty result: ```json theme={null} { "jsonrpc": "2.0", "id": 3, "result": {} } ``` ### Semantics * **Removes from list**: The primary effect is that deleted sessions no longer appear in `session/list` results. * **Implementation-defined storage behavior**: Agents may implement soft delete (mark as hidden) or hard delete (remove data). The protocol does not prescribe which. * **Implementation-defined load behavior**: Agents may choose what happens when a client calls `session/load` on a deleted session—return the session anyway, return an error, or any other behavior. The protocol does not prescribe which. * **Idempotent**: Deleting an already-deleted session (or a session that never existed) SHOULD succeed silently rather than error. ## Alternatives considered ### Automatic lifecycle policies only Rely on agents to implement their own session retention policies (e.g., delete sessions older than 30 days) without exposing user control. **Tradeoffs**: Users have no control over which sessions are kept. A session the user wants to keep might be deleted, or a session the user wants gone might persist. ### Add a `hidden` flag to sessions Instead of delete, allow users to mark sessions as hidden. They'd still exist but not appear in `session/list` by default. **Tradeoffs**: More complex—requires filter parameters on `session/list` to show/hide hidden sessions. For most use cases, delete is simpler and matches user expectations. ### Batch deletion Support deleting multiple sessions in one call via a `sessionIds` array. **Tradeoffs**: Could be added later as an extension. Single-session delete covers the common case and keeps the initial implementation simple. ## Shiny future > How will things play out once this feature exists? Users can manage their session history, and all ACP clients can offer this using the same protocol method rather than implementing their own mechanisms. ## Implementation details and plan > Tell me more about your implementation. What is your detailed implementation plan? 1. **Schema**: Add `session/delete` method definition, `DeleteSessionRequest` and `DeleteSessionResponse` types. 2. **Capabilities**: Add `sessionCapabilities.delete` capability flag. 3. **Protocol**: Add `session/delete` to method tables. 4. **Docs**: Update session management docs to include deletion. ## Frequently asked questions > What questions have arisen over the course of authoring this document or during subsequent discussions? ### Why not prescribe soft vs hard delete? Different agents have different storage architectures and compliance requirements. Some may need to retain data for auditing; others may want to free storage immediately. The protocol focuses on the user-facing behavior (removed from list) and leaves storage decisions to implementers. ### Why not prescribe behavior for loading deleted sessions? Similar reasoning—some agents may want to allow "undelete" by loading a soft-deleted session, others may want a clean error. The protocol provides the deletion mechanism; agents decide the semantics that fit their use case. ### Should delete require confirmation? No. Confirmation UX is a client concern. The protocol provides the delete operation; clients can add confirmation dialogs, undo functionality, or other UX patterns as they see fit. ### What if the session is currently active? Agents may reject deletion of active sessions or handle it however they choose. This is implementation-defined. A reasonable approach is to allow deletion—the session simply won't appear in future `session/list` calls. ### Why is this a separate RFD from session/list? The [`session/list` RFD](/rfds/session-list#what-about-session-deletion) explicitly deferred deletion to keep scope focused. Now that `session/list` is established, `session/delete` is a natural complement. ## Revision history * **2026-06-05**: RFD marked as Completed; `session/delete` is stabilized * **2026-06-02**: Moved to Preview * **2025-02-03**: Fixed capability example to use agent capability (initialize response) * **2025-01-24**: Initial draft # Forking of existing sessions Source: https://agentclientprotocol.com/rfds/session-fork * Author(s): [@josevalim](https://github.com/josevalim) * Champion: [@benbrandt](https://github.com/benbrandt) ## Elevator pitch > What are you proposing to change? We propose adding the ability to "fork" a new session based on an existing one. This will allow us to use the current conversation as context to generate pull request descriptions, summaries, etc. without polluting the user history. ## Status quo > How do things work today and what problems does this cause? Why would we change things? Imagine we want to summarize the current conversation to use it in a future chat. If we send a message asking for the summary, it will become part of its context, affecting future user interactions. Therefore we want to be able to fork a session, issue additional messages, and then close the fork. ## What we propose to do about it > What are you proposing to improve the situation? To add a "session/fork" method. ## Shiny future > How will things will play out once this feature exists? We will be able to implement functionality that requires using the current chat without polluting its history, ranging from summaries to potentially subagents. I can also see this feature being extended in the future to support an optional message ID, so the fork happens at a specific message, allowing clients to implement functionality like editing previous messages and similar. ## Implementation details and plan > Tell me more about your implementation. What is your detailed implementation plan? We propose to add a new "session/fork" method. Agents must declare this option is available by returning `session: { fork : {} }` in its capabilities. The object is reserved to declare future capabilities, such as forking from a specific message, a tool call, or similar. Then the client would be able to request a fork of the given session: ```json theme={null} { "jsonrpc": "2.0", "id": 1, "method": "session/fork", "params": { "sessionId": "sess_789xyz", "cwd": "...", "mcpServers": [...] } } ``` The request expects the same options as `session/load`, such as `cwd` and `mcpServers`. Similarly, the agent would respond with optional data such as config options, the same as `session/load`. Agents may reply with an error if forking of that specific session or with the given options is not supported, for example if the agent does not support forking with a different working directory than the initial session. ## Frequently asked questions > What questions have arisen over the course of authoring this document or during subsequent discussions? **Q: Should a new method be introduced or should "session/new" be expanded?** They must be different because they will effectively require different options. For example, "session/new" has options such as capabilities and MCP which are not recommended to be set when forking, as the context being forked was built with other tools, and forking may accept a messageId for checkpoints. **Q: Should fork only accept the `sessionId` or also other options, similar to `session/load`?** Initially, we proposed to only accept the `sessionId`, but this would make it more difficult to allow forking of inactive sessions in agents like `claude-agent-acp`, as Claude does not retain the configured MCP servers of a session. Limiting fork to only already active sessions would limit its usefulness. Moreover, allowing to pass different options also allows features like dynamically adding MCP servers to existing sessions to work by forking them with the new options, if the agent supports it. If it does not, the client can still show an appropriate error message. ### What alternative approaches did you consider, and why did you settle on this one? None. This proposal is inspired by the abilities exposed in Claude Agent SDK. It must be validated against other agents too. ## Revision history * 2025-11-17: Mentioned capabilities format, updated FAQ. * 2025-11-20: Added request format and updated capabilities format. * 2025-12-10: Adjust fork options to align with `session/load`. # Session Info Update Source: https://agentclientprotocol.com/rfds/session-info-update * Author(s): [@ignatov](https://github.com/ignatov) ## Elevator pitch Add a `session_info_update` variant to the existing `SessionUpdate` notification type that allows agents to update session metadata (particularly title/name), enabling dynamic session identification in client UIs without requiring a new endpoint. ## Status quo Currently, the ACP protocol provides session management through `session/new`, `session/load`, and `session/list` (unstable). The `session/list` endpoint returns `SessionInfo` objects that include an optional `title` field for displaying session names in client UIs. However, there are several problems: 1. **No way to communicate title updates** - The `title` field in `SessionInfo` is static in the list response. Agents cannot dynamically update it as the session evolves. 2. **No mechanism for real-time metadata updates** - Unlike commands (`available_commands_update`) or modes (`current_mode_update`), there's no way for agents to: * Auto-generate titles after the first meaningful exchange * Update titles as conversation context shifts * Provide custom metadata that reflects session state 3. **Inconsistent with protocol patterns** - Other dynamic session properties use `session/update` notifications (commands, modes, plans), but metadata has no equivalent mechanism. The current workaround is for clients to: * Maintain their own title mapping (doesn't persist or sync) * Only show static metadata from `session/list` * Have no way to receive agent-generated titles in real-time ## What we propose to do about it Add a new `session_info_update` variant to the existing `SessionUpdate` discriminated union that allows agents to notify clients about metadata changes. This update would: 1. **Follow the existing `SessionUpdate` pattern**: * Uses the same notification mechanism as `available_commands_update`, `current_mode_update`, etc. * Sent via `session/update` method * Agent-initiated, no request/response needed 2. **Align with `SessionInfo` structure**: * Contains the same fields as `SessionInfo` from `session/list` * All fields are optional (partial updates) * Enables incremental metadata updates * **Important**: `SessionInfoUpdate` must stay aligned with `SessionInfo` - when new fields are added to `SessionInfo`, they should also be added to `SessionInfoUpdate` as optional fields 3. **Support common use cases**: * Agent auto-generates title after first prompt * Agent updates title as conversation context shifts * Agent provides custom metadata for client features (tags, status, etc.) * User explicitly requests title change (agent responds with update notification) 4. **Integrate seamlessly**: * No new capability required (uses existing `session/update` mechanism) * Compatible with `session/list` - metadata should persist and be reflected in list responses * Works during active sessions ### Notification Structure The agent sends a `session/update` notification with `sessionUpdate: "session_info_update"`: ```json theme={null} { "jsonrpc": "2.0", "method": "session/update", "params": { "sessionId": "sess_abc123def456", "update": { "sessionUpdate": "session_info_update", "title": "Implement user authentication", "_meta": { "tags": ["feature", "auth"], "priority": "high" } } } } ``` ### SessionInfoUpdate Type The update type mirrors `SessionInfo` but with all fields optional: ```typescript theme={null} { sessionUpdate: "session_info_update", title?: string | null, // Update or clear the title updatedAt?: string | null, // ISO 8601 timestamp (usually agent sets this) _meta?: object | null // Custom metadata update signal } ``` **Note:** `sessionId` and `cwd` are NOT included since: * `sessionId` is already in the notification's `params` * `cwd` is immutable and set during `session/new` ### Examples #### Update title and working directory metadata After the user sends their first meaningful prompt, the agent can generate and send a title along with metadata about the working directory: ```json theme={null} { "jsonrpc": "2.0", "method": "session/update", "params": { "sessionId": "sess_abc123def456", "update": { "sessionUpdate": "session_info_update", "title": "Debug authentication timeout", "_meta": { "projectName": "api-server", "branch": "main" } } } } ``` #### Update title as conversation evolves As the conversation shifts focus: ```json theme={null} { "jsonrpc": "2.0", "method": "session/update", "params": { "sessionId": "sess_abc123def456", "update": { "sessionUpdate": "session_info_update", "title": "Debug authentication timeout → Add retry logic" } } } ``` ## Shiny future Once this feature exists: 1. **Dynamic session identification** - Agents can: * Auto-generate meaningful titles from conversation content * Update titles as conversations evolve * Provide rich metadata for better organization 2. **Improved client UIs** - Clients can: * Show real-time title updates in session lists * Display session status, tags, or other metadata * Update UI immediately without polling `session/list` 3. **Consistent protocol patterns** - Session metadata updates work like other dynamic session properties (commands, modes), creating a unified model 4. **Bidirectional workflows** - Combined with a potential future request method: * User renames session → client sends request → agent acknowledges with `session_info_update` notification * Agent auto-generates title → sends `session_info_update` notification → client displays it 5. **Enhanced use cases**: * Session templates that auto-set titles and tags * Progress indicators via `_meta` * Integration with external tools via metadata * Rich session browsing and filtering ## Implementation details and plan ### Phase 1: Schema Changes 1. **Update `schema.unstable.json`**: * Add `SessionInfoUpdate` type definition * Add new variant to `SessionUpdate` oneOf array * Align fields with `SessionInfo` but make all optional ```json theme={null} { "SessionInfoUpdate": { "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nUpdate to session metadata. All fields are optional to support partial updates.", "properties": { "_meta": { "description": "Extension point for implementations" }, "title": { "description": "Human-readable title for the session", "type": ["string", "null"] }, "updatedAt": { "description": "ISO 8601 timestamp of last activity", "type": ["string", "null"] } }, "type": "object" } } ``` Add to `SessionUpdate` oneOf: ```json theme={null} { "allOf": [ { "$ref": "#/$defs/SessionInfoUpdate" } ], "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nUpdate to session metadata", "properties": { "sessionUpdate": { "const": "session_info_update", "type": "string" } }, "required": ["sessionUpdate"], "type": "object" } ``` ### Phase 2: Protocol Documentation 2. **Create documentation** in `/docs/protocol/session-metadata.mdx`: * Explain the notification mechanism * Provide examples of common patterns * Document omitted, `null`, and object semantics for `_meta` * Clarify relationship with `session/list` 3. **Update existing docs**: * Reference in `/docs/protocol/session-setup.mdx` * Add to `/docs/protocol/prompt-turn.mdx` session update section ### Phase 3: SDK Implementation 4. **Implement in Rust SDK**: * Add `SessionInfoUpdate` struct * Add variant to `SessionUpdate` enum * Update notification handling in agent and client traits * Add helper methods for common patterns 5. **Implement in TypeScript SDK** (if applicable): * Add TypeScript types * Update notification handlers * Add helper methods ### Phase 4: Example Implementation 6. **Update example agents**: * Demonstrate auto-generating title from first prompt * Show updating metadata during session * Example of using `_meta` for custom fields ### Compatibility Considerations * **Fully backward compatible**: This adds a new notification variant to an existing mechanism * **No breaking changes**: Existing agents and clients continue working * **Graceful degradation**: Clients that don't handle this notification simply ignore it * **No new capability needed**: Uses existing `session/update` infrastructure ### Design Decisions **Why notification instead of request/response?** * Consistent with existing `SessionUpdate` patterns (`available_commands_update`, `current_mode_update`) * Agents initiate updates based on conversation state * Simpler than bidirectional request/response * Enables real-time updates without polling **Why make all fields optional?** * Allows partial updates (only update what changed) * More efficient - don't resend unchanged data * Flexible for different use cases * Mirrors partial update patterns in other protocols **Why not include `sessionId` and `cwd` in the update?** * `sessionId` is already in the notification params * `cwd` is immutable (set in `session/new`, never changes) * Keeps update focused on mutable metadata **How do `_meta` updates work?** * Omitted `_meta` means no metadata update was supplied. * `"_meta": null` is an explicit clear signal. * `"_meta": { ... }` supplies a metadata object for clients and extensions to interpret. ### Security Considerations * **No additional security concerns**: Uses existing session authentication * **Input validation**: * Agents should validate title length (recommend 500 chars max) * Sanitize metadata to prevent injection * Validate `_meta` structure based on agent requirements * **Resource limits**: Agents should limit update frequency and metadata size ## Frequently asked questions ### Why not create a new endpoint like `session/update-metadata`? The notification pattern is more appropriate because: 1. **Consistency**: Other dynamic session properties (commands, modes) use notifications 2. **Agent-initiated**: Agents typically generate titles from conversation context 3. **Real-time**: No request/response overhead, updates flow naturally 4. **Simpler**: Reuses existing `session/update` infrastructure ### How does this work with `session/list`? The updated metadata should persist and be reflected in subsequent `session/list` calls. The notification provides real-time updates to connected clients, while `session/list` provides the current state for discovery. ### Can clients trigger title updates? This RFD covers agent-initiated updates. Client-initiated updates could work by: 1. Client sends a prompt asking to rename session 2. Agent updates its internal state 3. Agent sends `session_info_update` notification 4. Client receives and displays the update A future RFD could add explicit request/response for this if needed. ### What if multiple updates are sent in quick succession? Clients should process updates in order and apply each field according to its field semantics. ### Should `updatedAt` be automatically set by the agent? Yes, typically the agent should update this timestamp when any session activity occurs, not just when metadata changes. However, including it in `session_info_update` allows agents to explicitly control it if needed. ### Do agents need a new capability for this? No. All agents that support `session/update` notifications can send this variant. Clients that don't recognize it will ignore it (standard JSON-RPC behavior). ### How does this interact with `session/fork`? When forking, the parent session's metadata could be copied (implementation choice). The forked session would have its own `sessionId` and could receive separate `session_info_update` notifications. ### What happens if title is too long? This is an implementation choice. Agents MAY: * Truncate long titles * Reject updates (though this is a notification, so no error response) * Set a reasonable limit (e.g., 500 characters) Clients SHOULD handle long titles gracefully (truncate in UI, show tooltip, etc.). ### Can `_meta` have nested objects? Yes, `_meta` is an arbitrary object. Agents define its structure. ### What alternative approaches did you consider, and why did you settle on this one? Several alternatives were considered: 1. **Add a new request/response endpoint (`session/update-metadata`)** - This would be inconsistent with how other dynamic session properties (commands, modes) are handled. The notification pattern is more appropriate for agent-initiated updates. 2. **Add title parameter to `session/new`** - Only allows setting title at creation time, doesn't support dynamic updates as the conversation evolves. 3. **Client-side only metadata tracking** - Doesn't work across devices, can get out of sync, and duplicates storage. This is the current workaround and has significant limitations. 4. **Generic `session/update` request for all properties** - Could conflict with immutable properties (sessionId, cwd) and has unclear semantics about what can be updated. The proposed notification-based approach: * **Consistent** with existing protocol patterns * **Flexible** for both agent-initiated and user-initiated updates * **Simple** to implement and understand * **Extensible** via `_meta` field ## Revision history * **2025-11-28**: Initial draft proposal # Session List Source: https://agentclientprotocol.com/rfds/session-list * Author(s): [@ahmedhesham6](https://github.com/ahmedhesham6) * Champion: [@benbrandt](https://github.com/benbrandt) ## Elevator pitch Add a `session/list` endpoint to the ACP protocol that allows clients to query and enumerate existing sessions from an agent, enabling session management features like session history, session switching, and session cleanup. ## Status quo Currently, the ACP protocol provides session management through `session/new` and `session/load` endpoints. However, there is no way for clients to: 1. **Discover existing sessions** - Clients cannot query what sessions exist on an agent 2. **Display session history** - Users cannot see a list of their past conversations 3. **Manage multiple sessions** - Switching between sessions requires clients to track session IDs themselves 4. **Clean up old sessions** - No way to discover stale or abandoned sessions for cleanup This creates several problems: * **Poor user experience** - Users cannot browse their conversation history or resume previous sessions easily * **Client-side complexity** - Each client must implement its own session tracking and persistence * **Inconsistent behavior** - Different clients handle session management differently, leading to fragmented experiences The current workaround is for clients to maintain their own session registry, but this: * Requires persistent storage on the client side * Can get out of sync if sessions are created/destroyed outside the client * Doesn't work across different client instances or devices * Cannot leverage agent-side session metadata or state ## What we propose to do about it Add a new `session/list` JSON-RPC method to the protocol that returns metadata about sessions known to the agent. This endpoint would: 1. **Return a list of sessions** with essential metadata: * `sessionId` - Unique identifier * `cwd` - Working directory for the session * `title` - Optional human-readable title (could be auto-generated from first prompt) * `updatedAt` - Timestamp of last update to the session * `_meta` - Optional agent-specific metadata 2. **Support filtering and pagination**: * Filter by working directory * Agent provides an optional cursor for retrieving the next page of results 3. **Be an optional capability**: * Agents advertise `sessionCapabilities: { list: {} }` in initialization if they support this feature * Clients check for this capability before attempting to call `session/list` * Agents without persistent session storage don't need to implement this ### JSON-RPC Request The client calls `session/list` with optional filtering and pagination parameters: ```json theme={null} { "jsonrpc": "2.0", "id": 2, "method": "session/list", "params": { "cwd": "/home/user/project", "cursor": "eyJwYWdlIjogMn0=" } } ``` #### Request Parameters All parameters are optional: * `cwd` (string) - Filter sessions by working directory * `cursor` (string) - Opaque cursor token from a previous response's `nextCursor` field for cursor-based pagination #### Minimal Request Example A request with no filters returns all sessions with default sorting: ```json theme={null} { "jsonrpc": "2.0", "id": 2, "method": "session/list", "params": {} } ``` ### JSON-RPC Response The agent responds with a list of sessions and cursor pagination metadata: ```json theme={null} { "jsonrpc": "2.0", "id": 2, "result": { "sessions": [ { "sessionId": "sess_abc123def456", "updatedAt": "2025-10-29T14:22:15Z", "cwd": "/home/user/project", "title": "Implement session list API", "_meta": { "messageCount": 12, "hasErrors": false } }, { "sessionId": "sess_xyz789ghi012", "updatedAt": "2025-10-28T16:45:30Z", "cwd": "/home/user/another-project", "title": "Debug authentication flow" }, { "sessionId": "sess_uvw345rst678", "updatedAt": "2025-10-27T15:30:00Z", "cwd": "/home/user/project" } ], "nextCursor": "eyJwYWdlIjogM30=" } } ``` #### Response Fields **Response object:** * `sessions` (array) - Array of session information objects * `nextCursor` (string, optional) - Opaque cursor token. If present, pass this in the next request's `cursor` parameter to fetch the next page. If absent, there are no more results. **SessionInfo object:** * `sessionId` (string, required) - Unique identifier for the session * `cwd` (string, required) - Working directory for the session * `title` (string, optional) - Human-readable title (may be auto-generated from first prompt) * `updatedAt` (string, optional) - ISO 8601 timestamp of last activity * `_meta` (object, optional) - Agent-specific metadata (e.g., message count, error status, tags) #### Empty Result Example When no sessions match the criteria: ```json theme={null} { "jsonrpc": "2.0", "id": 2, "result": { "sessions": [] } } ``` ## Shiny future Once this feature exists: 1. **Clients can build session browsers** - Users can view a list of all their conversations, sorted by recency or relevance 2. **Session switching becomes seamless** - Users can easily switch between ongoing conversations 3. **Better resource management** - Clients can identify and clean up old or inactive sessions 4. **Cross-device continuity** - Users could potentially access their sessions from different devices (if agent supports it) 5. **Improved UX patterns**: * "Recent conversations" sidebar * Search through past sessions * Archive/delete old sessions * Resume interrupted work easily Agents that implement this feature gain: * Better visibility into active sessions * Opportunity to implement session lifecycle policies * Foundation for future features like session sharing or collaboration ## Implementation details and plan ### Phase 1: Core Protocol Changes 1. **Update schema.json** to add: * `session/list` method definition * `ListSessionsRequest` and `ListSessionsResponse` types * `SessionInfo` type * `sessionCapabilities/list` capability flag 2. **Update protocol documentation** in `/docs/protocol/session-setup.mdx`: * Document the new endpoint * Explain when to use it vs. maintaining client-side session tracking * Provide examples of common use cases ### Phase 2: Reference Implementation 3. **Implement in Rust SDK** (`src/agent.rs` and `src/client.rs`): * Add `list_sessions` method to agent trait * Provide default implementation (empty list) for agents without persistence * Add client method to call `session/list` 4. **Add to TypeScript SDKs** (if applicable): * Update TypeScript types * Add client methods ### Phase 3: Example Implementation 5. **Create example agent** that demonstrates: * In-memory session registry * Automatic title generation from first prompt * Session lifecycle management (cleanup after N days) * Pagination and filtering ### Compatibility Considerations * **Backward compatible**: Existing agents continue working without implementing this * **Capability-based**: Clients check for `sessionCapabilities.list` capability before using * **No breaking changes**: No modifications to existing endpoints ### Security Considerations * **Session isolation**: Agents must ensure sessions are only listed for the authenticated client * **Resource limits**: Agents should enforce reasonable page sizes internally to prevent abuse ## Frequently asked questions ### What alternative approaches did you consider, and why did you settle on this one? Several alternatives were considered: 1. **Client-side session tracking only** - This is the current approach, but it has limitations: * Doesn't work across devices * Can get out of sync * Adds complexity to every client implementation 2. **Session events/notifications** - Push notifications when sessions are created/destroyed: * More complex to implement * Requires long-lived connections * Still requires client-side state management * Better suited as a future enhancement, not a replacement 3. **File-based session manifest** - Agent writes session list to a file that clients read: * Couples agent and client file system access * Doesn't work for remote agents * No standard format The proposed RPC approach is: * **Consistent with existing protocol design** - Uses same RPC patterns as other endpoints * **Flexible** - Supports filtering, pagination, and agent-specific metadata * **Optional** - Agents can opt-in based on their architecture * **Simple** - Single request/response pattern, easy to implement and use ### Why not make this mandatory for all agents? Many agents may not have persistent storage or multi-session capabilities. Making this optional: * Allows simple, stateless agents to remain compliant * Reduces implementation burden * Lets agents evolve session management over time ### How does this interact with `session/load`? `session/load` remains the mechanism to actually restore a session. `session/list` is for discovery only: 1. Client calls `session/list` to get available sessions 2. User selects a session 3. Client calls `session/load` with the chosen `sessionId` Agents may support `session/list` without supporting `session/load` (e.g., for read-only session browsing). ### Should we include session content in the list response? No, for several reasons: * **Performance** - Full conversation history could be large * **Privacy** - Listing sessions might be less sensitive than exposing full content * **Separation of concerns** - Use `session/load` to get full session content ### What about session deletion? Session deletion is intentionally left out of this RFD to keep scope focused. It is covered separately by the [Session Delete RFD](/rfds/session-delete) and the [`session/delete`](/protocol/v1/session-delete) method. ### How should pagination work for large session lists? We use cursor-based pagination: * Request includes an optional `cursor` * Response includes `nextCursor` when more results are available * Clients should treat a missing `nextCursor` as the end of results * Clients MUST treat cursors as opaque tokens: don't parse, modify, or persist them across sessions * The cursor MUST be a string; never send a raw JSON object as the cursor * Servers SHOULD provide stable cursors and handle invalid cursors gracefully Good request example: ```json theme={null} { "jsonrpc": "2.0", "id": 2, "method": "session/list", "params": { "cwd": "/home/user/project", "createdAfter": "2025-10-20T00:00:00Z", "cursor": "eyJwYWdlIjogMn0=", "search": "auth" } } ``` Corresponding response example: ```json theme={null} { "jsonrpc": "2.0", "id": 2, "result": { "sessions": [/* ... */], "nextCursor": "eyJwYWdlIjogM30=" } } ``` ## Revision history * **2025-10-29**: Initial draft proposal * **2025-10-30**: Update to use `_meta` field for agent-specific metadata * **2025-10-30**: Switch from offset-based to cursor-based pagination using continuation tokens * **2025-10-30**: Rename `lastAccessedAt` to `updatedAt` for consistency * **2025-10-30**: Remove `preview` field from SessionInfo (out of scope) * **2025-10-30**: Remove session orphaning from problem statement * **2025-10-30**: Replace `sortBy`/`sortOrder` with `search` parameter; remove `total` count from response * **2025-10-31**: Update pagination: `continuationToken` → `cursor`, `nextContinuationToken` → `nextCursor`, remove `hasMore` * **2025-11-11**: Remove `createdAt`, `updatedAt`, and `search` filters from the request parameters * **2025-11-23**: Remove `limit` parameter from request; make `createdAt` and `updatedAt` optional in SessionInfo * **2025-11-24**: Update capabilities schema, consolidate to single `updatedAt` timestamp # Resuming of existing sessions Source: https://agentclientprotocol.com/rfds/session-resume * Author(s): [@josevalim](https://github.com/josevalim) * Champion: [@benbrandt](https://github.com/benbrandt) ## Elevator pitch > What are you proposing to change? We propose adding the ability to resume existing sessions. This is similar to "session/load", except it does not return previous messages. ## Status quo > How do things work today and what problems does this cause? Why would we change things? While the spec provides a "session/load" command, not all coding agents implement it. This means that, once you close your editor, browser, etc, you can't resume the conversation. This is particularly a problem for agents that do not directly implement ACP and the functionality is implemented via a wrapper. In such cases, they may provide the ability to resume (without history), which we would like to hook into. Not only that, resuming could be used as a mechanism for proxies and adapter libraries to emulate "session/load". ## What we propose to do about it > What are you proposing to improve the situation? Add a "session/resume" command and a capability `{ sessionCapabilities: { resume: {} } }`. ## Shiny future > How will things will play out once this feature exists? We will be able to resume existing conversations, providing a better user experience. Not only that, if an agent does not implement "session/load" but it does implement "session/resume", it is now possible to implement a proxy/adapter that intercepts the agents messages and writes them to disk. Now when the client issues a "session/load", the proxy/adapter converts it to a "session/resume", and then returns the stored messages. ## Implementation details and plan > Tell me more about your implementation. What is your detailed implementation plan? ## Frequently asked questions > What questions have arisen over the course of authoring this document or during subsequent discussions? ### Should we introduce a new operation (session/resume) or add an option to (session/load)? A separate method provides a few benefits: * for clients that for whatever reason don't want the history, they can just resume * for agents that can only supply resume, a proxy on top could provide load on top of it, but the agent is still clear on what it supports * for agents who support both, it should be trivial to use the same resume functionality, they just either replay events or do not ### What alternative approaches did you consider, and why did you settle on this one? The biggest question is if it makes sense to support both "session/load" and "session/resume". When we start a new session over ACP, we introduce custom MCP tools and configuration. This means that, while we could use "session/load" to load our own chats, loading third-party chats would likely lead to a flawed user experience, as our tools would not be available. And depending on the ACP implementation, not even the capabilities would be respected (as loading a third-party session would not include our capabilities in its history, misleading the agent). Therefore, if we assume "session/load" is for loading conversations started by the client itself, "session/resume" is effectively a subset of "session/load", decoupled from storage mechanics. If an agent implements "session/load", then it can be used directly, but if it doesn't, a proxy or adapter can provide a reasonable fallback on top of "session/resume". This argues "session/resume" is the basic primitive which "session/load" builds on top of. ## Revision history * 2026-04-22: RFD marked as Completed; `session/resume` is stabilized * 2026-04-14: Update capability shape to `sessionCapabilities.resume` * 2025-11-24: Update FAQ to mention session/resume vs session/load # Session Context Size and Cost Source: https://agentclientprotocol.com/rfds/session-usage * Author(s): [@ahmedhesham6](https://github.com/ahmedhesham6) * Champion: [@benbrandt](https://github.com/benbrandt) ## Elevator pitch > What are you proposing to change? Add a standardized `usage_update` session notification that lets agents report the current context window utilization and optional cumulative session cost. This RFD is intentionally scoped to session-level context size and cost. End-turn token accounting is covered separately in the [End-Turn Token Usage RFD](/rfds/end-turn-token-usage). ## Status quo > How do things work today and what problems does this cause? Why would we change things? ACP currently has no standardized way for agents to communicate: 1. **Context window status** - How much of the model's context window is being used 2. **Cost information** - Estimated cumulative cost for API usage This creates several problems: * **No context management** - Clients cannot warn users when a session is approaching context limits or suggest compaction * **No cost transparency** - Users cannot track spending or estimate whether a long-running session should continue * **Inconsistent implementations** - Each agent reports context and cost differently, if it reports them at all Common AI coding tool behavior suggests users benefit from: * Context percentage indicators such as "19%" * Absolute token details such as "31.4K of 200K tokens" * Warning thresholds around high context utilization * Auto-compaction or handoff suggestions near context limits * Cumulative session cost tracking rather than only per-turn cost breakdowns ## What we propose to do about it > What are you proposing to improve the situation? Agents send context window and cost information via `session/update` notifications with `sessionUpdate: "usage_update"`: ```json theme={null} { "jsonrpc": "2.0", "method": "session/update", "params": { "sessionId": "sess_abc123", "update": { "sessionUpdate": "usage_update", "used": 53000, "size": 200000 } } } ``` ### Context Window Fields * `used` (number, required) - Tokens currently in context * `size` (number, required) - Total context window size in tokens Clients can compute `remaining` as `size - used` and `percentage` as `used / size * 100`. If a model has a dynamic context window, agents should report the current effective limit and send another `usage_update` if that limit changes. If an agent cannot provide a meaningful context window size, it should not send `usage_update`; this RFD does not define a `null` or omitted `size` state. ### Cost Field * `cost` (object, optional, nullable) - Cumulative session cost * `amount` (number, required) - Total cumulative cost for the session * `currency` (string, required) - ISO 4217 currency code, such as `"USD"` or `"EUR"` Example with optional cost: ```json theme={null} { "jsonrpc": "2.0", "method": "session/update", "params": { "sessionId": "sess_abc123", "update": { "sessionUpdate": "usage_update", "used": 53000, "size": 200000, "cost": { "amount": 0.045, "currency": "USD" } } } } ``` Agents send updates when they have current data available: * After `session/new`, once the session ID exists * After `session/load`, `session/resume`, or `session/fork` for restored sessions * After `session/prompt`, when usage data becomes available * Anytime context window state or cumulative cost changes significantly This approach provides flexibility for different agent implementations: * Agents that can query current usage without a prompt can send updates immediately after session setup * Agents that only receive usage data while prompting can send updates after prompt completion * Agents that cannot calculate cost can still report context window status ### Design Principles 1. **Session state** - Context size and cumulative cost describe the session, not one prompt response 2. **Agent-pushed notifications** - Agents proactively send updates when data becomes available, following the same pattern as dynamic session properties such as `available_commands_update`, `current_mode_update`, and `session_info_update` 3. **Agent calculates, client can verify** - Agents know their model and tokenizer best, but expose raw values so clients can compute derived display values 4. **Flexible cost reporting** - Cost is optional because not all agents track it, and the currency is explicit because agents may bill in different currencies 5. **Optional but recommended** - Context and cost reporting remains optional to preserve compatibility ## Shiny future > How will things will play out once this feature exists? **For Users:** * **Visibility**: Users see current context window usage with percentage indicators * **Cost awareness**: Users can track cumulative spending at any time * **Better planning**: Users know when to start a new session, compact context, or hand off work * **Transparency**: Users get a consistent view of resource consumption across agents **For Client Implementations:** * **Consistent UI**: Clients can show usage through standard progress indicators, detail hovers, and warning states * **Smart warnings**: Clients can warn users as sessions approach high context utilization * **Cost controls**: Clients can implement budget alerts and session-level spend displays * **Reactive updates**: Clients update UI immediately when agents push new data * **No polling needed**: Clients do not need a separate status request to keep context displays current **For Agent Implementations:** * **Standard reporting**: Agents get a clear contract for reporting context window and cost state * **Flexibility**: Agents can report only the cost information they can calculate * **Model diversity**: The shape works across providers and local models ## Implementation details and plan > Tell me more about your implementation. What is your detailed implementation plan? 1. Add a `UsageUpdate` variant to `SessionUpdate` with discriminator `sessionUpdate: "usage_update"`. 2. Add a `Cost` type with `amount` and `currency` fields. 3. Gate this Rust crate surface behind the `unstable_session_usage` feature. 4. Document the `session/update` notification variant in the protocol docs. 5. Add examples showing when agents should send context window and cost updates. This RFD does not add end-turn token usage to the turn-completion signal. That proposal remains in draft in the [End-Turn Token Usage RFD](/rfds/end-turn-token-usage). ## Frequently asked questions > What questions have arisen over the course of authoring this document or during subsequent discussions? ### Why split this from end-turn token usage? Context window size and cumulative cost are session-level state that clients may need before, during, or after a prompt. Token accounting for a completed turn has different open questions, including whether values should be per-turn or cumulative and how cache metrics should map across providers. ### How do users know when to hand off or compact context? The `usage_update` notification provides the raw values clients need: * `used` and `size` give absolute numbers for precise tracking * Clients can compute `remaining` as `size - used` * Clients can compute `percentage` as `used / size * 100` **Recommended client behavior:** | Percentage | Action | | ---------- | ---------------------------------------------------------------- | | \< 75% | Normal operation | | 75-90% | Yellow indicator, suggest "Context filling up" | | 90-95% | Orange indicator, recommend "Start new session or summarize" | | > 95% | Red indicator, warn "Next prompt may fail - handoff recommended" | ### Why is cost in `session/update` instead of the turn-completion signal? Cost is cumulative session state, similar to context window utilization: * Users usually want total spending, not only per-turn costs * Both cost and context window are session-level metrics that can change when agents compact, switch models, or restore sessions * Cost is optional because not all agents track it ### Why not assume USD for cost? Agents may bill in different currencies: * European agents might bill in EUR * Asian agents might bill in JPY or CNY * Currency conversion rates change Agents should report the actual billing currency and let clients convert if needed. ### How does this work with streaming responses? Agents may send `usage_update` notifications during streaming if context state changes and they have reliable current data. They should also send an update after prompt completion when final context size or cost data becomes available. ### What about models without fixed context windows? Agents should report the current effective context window size. For models with dynamic windows, agents should report the current limit and send another update if it changes. If there is no meaningful context window size, agents should not send `usage_update`; `size` is required and non-null. ### What about rate limits and quotas? This RFD focuses on context window utilization and cumulative cost. Rate limits and quotas are separate concerns that could be addressed in a future RFD. ### Should cached tokens count toward context window usage? Yes. Cached tokens still occupy context window space even when they are cheaper to process. Context window usage should include all tokens currently in context. ### Why a notification instead of a request? Using `session/update` notifications instead of a `session/status` request provides several benefits: 1. **Consistency**: Follows the same pattern as other dynamic session properties 2. **Agent flexibility**: Agents can send updates when they have data available 3. **No polling**: Clients receive updates reactively 4. **Real-time updates**: Updates flow naturally as part of the session lifecycle ### What if the client connects mid-session? When a client connects to an existing session through `session/load` or `session/resume`, agents should send a `usage_update` notification if they have current context window data available. This lets the client UI immediately display accurate context and cost information. For agents that only provide usage during active prompting, the client UI may not show usage until after the first prompt is sent. ### What alternative approaches did you consider, and why did you settle on this one? **Everything in the turn-completion signal** - Simpler, but context window and cost are session state that users may want independently of prompt turns. **Request/response with `session/status`** - Requires clients to poll, and some agents do not have APIs to query current status without a prompt. **Client calculates everything** - Rejected because clients do not know the agent's tokenizer, exact context window size, model routing, or pricing. **Only percentage, no raw tokens** - Rejected because users want absolute numbers, clients cannot verify calculations, and it is less transparent. ## Revision history * 2025-12-07: Initial draft * 2025-12-13: Changed from `session/status` request method to `session/update` notification with `sessionUpdate: "context_update"`. Made `cost` optional and removed `remaining` field (clients can compute as `size - used`). This approach provides more flexibility for agents and follows the same pattern as other dynamic session properties. * 2025-12-17: Renamed `reasoning_tokens` to `thought_tokens` for consistency with ACP terminology. Removed `percentage` field (clients can compute as `used / size * 100`). * 2025-12-19: Renamed `sessionUpdate: "context_update"` to `sessionUpdate: "usage_update"` to better reflect the payload semantics (includes both context window info and cumulative cost). * 2026-06-02: Split end-turn token accounting into a separate draft RFD. * 2026-06-03: Moved to Preview. * 2026-06-05: Moved to Completed. # Streamable HTTP & WebSocket Transport Source: https://agentclientprotocol.com/rfds/streamable-http-websocket-transport * Author(s): [alexhancock](https://github.com/alexhancock), [jh-block](https://github.com/jh-block) * Champion: [anna239](https://github.com/anna239) ## Elevator pitch > What are you proposing to change? ACP needs a standard remote transport. We propose **long-lived GET streams** for server→client messages (one connection-scoped plus one per session), with **POST** for client→server messages, and **WebSocket upgrade** as an alternative on the same endpoint. A single `/acp` endpoint supports two connectivity profiles: * **Streamable HTTP (POST/GET/DELETE)** — Long-lived SSE streams per connection: one connection-scoped stream for connection-level server→client messages, plus one session-scoped stream per session for session-level messages. POST requests return immediately (202 Accepted, except `initialize`). Requires HTTP/2. * **WebSocket upgrade (GET with `Upgrade: websocket`)** — persistent, full-duplex, low-latency bidirectional messaging. Clients that support remote ACP over HTTP MUST support both Streamable HTTP and WebSocket. This allows servers to support only WebSocket if they choose, simplifying deployment. Both profiles share the same JSON-RPC message format and ACP lifecycle as the existing **stdio** local subprocess transport. ## Status quo > How do things work today and what problems does this cause? Why would we change things? ACP only has stdio. There is no standard remote transport, which causes fragmentation as implementers invent their own HTTP layers, leading to incompatible SDKs and deployments. ## What we propose to do about it > What are you proposing to improve the situation? ### 1. Adds an HTTP Transport ACP adopts a streamable HTTP transport with three key characteristics: 1. **Long-lived GET streams (one connection-scoped, one per session)** — All server→client messages (responses to requests and unsolicited notifications) are delivered via SSE streams opened with GET. The **connection-scoped stream** (scoped to `Acp-Connection-Id`) carries connection-level messages: responses to `session/new` and `session/load` (which the client cannot receive on a session-scoped stream because it does not yet have a `sessionId`), and any server-initiated messages not tied to a specific session. The **session-scoped stream** (scoped to `Acp-Connection-Id` + `Acp-Session-Id`) carries all messages for a single session: session update notifications, server-to-client requests like `request_permission`, and responses to session-scoped POSTs like `session/prompt` and `session/cancel`. Responses are correlated to the POST that originated them by JSON-RPC `id`. 2. **POST requests return immediately (except initialize)** — Client→server messages are sent via POST. Most POST requests return `202 Accepted` immediately with an empty body. The actual response comes later on the appropriate GET stream, correlated by JSON-RPC `id`. The `initialize` request is special: it returns `200 OK` with a JSON response body containing capabilities and the `Acp-Connection-Id`. The `Acp-Connection-Id` is also included in the response header. 3. **Requires HTTP/2** — Streamable HTTP transport MUST use HTTP/2. This provides multiplexing for concurrent POST requests while maintaining long-lived GET streams (one connection-scoped plus one per session), and improves efficiency for high-frequency message exchanges. ### 4. Adds WebSocket as a first-class upgrade on the same endpoint A GET with `Upgrade: websocket` upgrades to a persistent bidirectional channel — same endpoint, same lifecycle model. This is important for ACP, as it is more bidirectional in its nature as a protocol. ### 5. Requires cookie support on HTTP transports Clients MUST accept, store, and return cookies set by the server on all HTTP-based transports (Streamable HTTP and WebSocket). Cookies MUST be sent on subsequent requests to the server for the duration of the connection. Clients MAY discard all cookies when a connection is terminated. This allows servers to rely on cookies for session affinity (e.g., sticky sessions behind a load balancer) and other small amounts of per-connection state. ### 6. Defines a unified routing model | Method | Upgrade Header? | Behavior | | -------- | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | | `POST` | — | Send JSON-RPC message. `initialize` returns 200 with JSON body. All others return 202 Accepted immediately. | | `GET` | No | Open SSE stream. `Acp-Connection-Id` alone → connection-scoped stream. `Acp-Connection-Id` + `Acp-Session-Id` → session-scoped stream. | | `GET` | `Upgrade: websocket` | Upgrade to WebSocket for full-duplex messaging | | `DELETE` | — | Terminate the connection | ### 7. Preserves the full ACP lifecycle The `initialize` → `initialized` → messages → close lifecycle is identical regardless of transport. The `Acp-Connection-Id` binds requests to the initialized connection and its negotiated capabilities. Session identity is carried in JSON-RPC message bodies via the `sessionId` field. ## Durability and reliability expectations > What guarantees does this transport make, and what is deferred? This RFD is targeted for inclusion in **v1** as an additive feature, with more robust durability and reliability primitives coming in **v2**. The lists below clarify what implementers can expect from using the HTTP/WS transport in different versions of ACP ### v1 In v1, durability and reliability are the implementer's responsibility — the protocol provides the building blocks, not the guarantees. Specifically, you can expect: * **Sessions survive disconnects.** A session persists on the server independently of any one connection, so after a dropped connection a client can reconnect and resume it via `session/load`. * **Session affinity is preserved across reconnects.** Required client cookie support lets a load balancer route a reconnecting client back to the same backend; deployments without native sticky sessions can supply affinity themselves using an external store such as Redis keyed by connection/session ID. * **Reconnect and retry are up to the implementer.** Detecting a dropped connection and re-establishing it is handled at the SDK/host layer, optionally via a local proxy. * **Liveness detection is up to the implementer.** Keeping intermediaries from timing out and detecting half-open or unresponsive connections is done with SDK/host-level transport and application ping/pong, not by the protocol. * **In-flight messages are not replayed.** There is no message sequencing or stream resumption, so server→client messages emitted while a client was disconnected are not redelivered on reconnect. ### v2 * **Message IDs on streamed messages.** Streamed message chunks carry IDs (a "last replay ID"), enabling reliable retry and resumption after a reconnect. * **Stream resumability.** SSE `Last-Event-ID`-style resumption lets a reconnecting client replay messages missed while disconnected. * **Defined reconnection semantics.** Reconnection scenarios are addressed by the protocol/SDKs rather than left entirely to each implementer. * **More reliable notification update cycles.** v2 tightens the update/notification lifecycle to reduce lost or out-of-order updates. * **Standardized keepalive.** Both transport-level and application-level ping/pong become part of the protocol's reliability story, enabling more sophisticated awareness by both client and server of when the other end has crashed. ## Shiny future > How will things play out once this feature exists? * **SDK implementers** get a clear, testable transport spec — Rust, TypeScript, and Python SDKs can all interoperate. * **Desktop clients** use WebSocket for low-latency streaming; all clients support it as a baseline. * **Cloud deployments** expose agents behind standard HTTP load balancers using the stateless-friendly HTTP mode, with cookie-based sticky sessions guaranteed by client support. * **Proxy chains** can route ACP traffic over HTTP for multi-hop agent topologies. ## Implementation details and plan > Tell me more about your implementation. What is your detailed implementation plan? ### Transport Architecture ``` ┌─────────────────────────────────┐ │ /acp endpoint │ └──────┬──────────┬───────────────┘ │ │ ┌───────────▼──┐ ┌────▼──────────────┐ │ HTTP State │ │ WebSocket State │ │(connections) │ │ (connections) │ └───────┬──────┘ └────┬──────────────┘ │ │ ┌───────▼──────────────▼───────────────┐ │ ACP Agent (JSON-RPC handler) │ │ serve(agent, read, write) │ └─────────────────────────────────────┘ ``` ### Identity Model ACP over Streamable HTTP uses two HTTP headers for connection and session identity, plus JSON-RPC message fields: * **`Acp-Connection-Id`** (HTTP header) — Transport-level identifier returned by the server in the `initialize` response. Required on all HTTP requests after `initialize` and on every GET stream (both connection-scoped and session-scoped). Binds requests to an initialized connection and its negotiated capabilities. * **`Acp-Session-Id`** (HTTP header) — Session-level identifier returned in the `session/new` response body. Required on all session-scoped POST requests (`session/prompt`, `session/cancel`, permission responses, etc.) and on the session-scoped GET stream. * **`sessionId`** (JSON-RPC field) — Session-level identifier also included in JSON-RPC `params` for session-scoped methods and in responses on the GET streams. A single connection may host multiple sessions, each with its own `sessionId` and its own session-scoped GET stream. ### Streamable HTTP Message Flow ``` Client Server │ │ │ ═══ Connection Initialization ═══ │ │ │ │─── POST /acp ─────────────────────>│ { method: "initialize", id: 1 } │ Content-Type: application/json │ (no Acp-Connection-Id header) │ │ │<────── 200 OK ─────────────────────│ { id: 1, result: { capabilities, connectionId } } │ Acp-Connection-Id: │ Response includes Acp-Connection-Id header │ Content-Type: application/json │ │ │ │ ═══ Open Connection-Scoped GET ═══│ │ │ │─── GET /acp ──────────────────────>│ Open long-lived connection-scoped SSE stream │ Acp-Connection-Id: │ for connection-level server→client messages │ Accept: text/event-stream │ (no Acp-Session-Id header) │ ┌─────────────────────│ (SSE stream open) │ │ │ │ │ │ │ ═══ Session Creation ═══ │ │ │ │─── POST /acp ─────────────────────>│ { method: "session/new", id: 2, │ Acp-Connection-Id: │ params: { cwd, mcpServers } } │ │ │<────── 202 Accepted ───────────────│ (returns immediately) │ │ │ │<─────────────│─ SSE event ─────────│ { id: 2, result: { sessionId: "sess_abc123" } } │ │ │ (response on connection-scoped stream) │ │ │ │ ═══ Open Session-Scoped GET ═══ │ │ │ │─── GET /acp ──────────────────────>│ Open long-lived session-scoped SSE stream │ Acp-Connection-Id: │ for sess_abc123 │ Acp-Session-Id: sess_abc123 │ │ Accept: text/event-stream │ │ ┌─────────────────────│ (SSE stream open) │ │ │ │ ═══ Prompt Flow ═══ │ (all events below arrive on the session-scoped stream) │ │ │─── POST /acp ─────────────────────>│ { method: "session/prompt", id: 3, │ Acp-Connection-Id: │ params: { sessionId: "sess_abc123", prompt } } │ Acp-Session-Id: sess_abc123 │ │ │ │<────── 202 Accepted ───────────────│ (returns immediately) │ │ │ │<─────────────│─ SSE event ─────────│ notification: AgentMessageChunk (sessionId: "sess_abc123") │<─────────────│─ SSE event ─────────│ notification: AgentThoughtChunk (sessionId: "sess_abc123") │<─────────────│─ SSE event ─────────│ notification: ToolCall (sessionId: "sess_abc123") │<─────────────│─ SSE event ─────────│ notification: ToolCallUpdate (sessionId: "sess_abc123") │<─────────────│─ SSE event ─────────│ notification: AgentMessageChunk (sessionId: "sess_abc123") │<─────────────│─ SSE event ─────────│ { id: 3, result: { sessionId: "sess_abc123", ... } } │ │ │ (response comes on GET stream) │ │ │ │ ═══ Permission Flow ═══ │ │ (when tool requires confirmation) │ │ │ │─── POST /acp ─────────────────────>│ { method: "session/prompt", id: 4, ... } │ Acp-Connection-Id: │ │ Acp-Session-Id: sess_abc123 │ │ │ │<────── 202 Accepted ───────────────│ │ │ │ │<─────────────│─ SSE event ─────────│ notification: ToolCall (sessionId: "sess_abc123") │<─────────────│─ SSE event ─────────│ { method: "request_permission", id: 99, │ │ │ params: { sessionId: "sess_abc123", ... } } │ │ │ (server-to-client request) │─── POST /acp ────────────────────>│ { id: 99, result: { outcome: "allow_once" } } │ Acp-Connection-Id: │ (client response) │ Acp-Session-Id: sess_abc123 │ │ │ │<────── 202 Accepted ───────────────│ │ │ │ │<─────────────│─ SSE event ─────────│ notification: ToolCallUpdate (sessionId: "sess_abc123") │<─────────────│─ SSE event ─────────│ { id: 4, result: { sessionId: "sess_abc123", ... } } │ │ │ (response comes on GET stream) │ │ │ │ ═══ Cancel Flow ═══ │ │ │ │─── POST /acp ─────────────────────>│ { method: "session/prompt", id: 5, ... } │ Acp-Connection-Id: │ │ Acp-Session-Id: sess_abc123 │ │ │ │<────── 202 Accepted ───────────────│ │ │ │ │<─────────────│─ SSE event ─────────│ notification: AgentMessageChunk (sessionId: "sess_abc123") │ │ │ │─── POST /acp ─────────────────────>│ { method: "session/cancel", │ Acp-Connection-Id: │ params: { sessionId: "sess_abc123" } } │ Acp-Session-Id: sess_abc123 │ │ │ │<────── 202 Accepted ───────────────│ (notification, no id) │ │ │ │<─────────────│─ SSE event ─────────│ { id: 5, result: { sessionId: "sess_abc123", ... } } │ │ │ (response comes on GET stream) │ │ │ │ ═══ Resume Session Flow ═══ │ │ (new connection, existing session)│ │ │ │─── POST /acp ─────────────────────>│ { method: "initialize", id: 1 } │ (no Acp-Connection-Id) │ New connection │<────── 200 OK ─────────────────────│ { id: 1, result: { capabilities, connectionId } } │ Acp-Connection-Id: │ │ │ │─── GET /acp ──────────────────────>│ Open new connection-scoped GET stream │ Acp-Connection-Id: │ │ ┌─────────────────────│ (SSE stream open) │ │ │ │─── GET /acp ──────────────────────>│ Open session-scoped GET stream for sess_abc123 │ Acp-Connection-Id: │ │ Acp-Session-Id: sess_abc123 │ │ ┌─────────────────────│ (SSE stream open) │ │ │ │─── POST /acp ─────────────────────>│ { method: "session/load", id: 2, │ Acp-Connection-Id: │ params: { sessionId: "sess_abc123", cwd } } │ Acp-Session-Id: sess_abc123 │ │ │ │<────── 202 Accepted ───────────────│ │ │ │ │<─────────────│─ SSE event ─────────│ notification: UserMessageChunk (on session-scoped stream) │<─────────────│─ SSE event ─────────│ notification: AgentMessageChunk (on session-scoped stream) │<─────────────│─ SSE event ─────────│ notification: ToolCall (on session-scoped stream) │<─────────────│─ SSE event ─────────│ notification: ToolCallUpdate (on session-scoped stream) │<─────────────│─ SSE event ─────────│ { id: 2, result: { sessionId: "sess_abc123" } } │ │ │ (response on connection-scoped stream) │ │ │ │ ═══ Connection Termination ═══ │ │ │ │─── DELETE /acp ───────────────────>│ Terminate connection │ Acp-Connection-Id: │ │<────────── 202 Accepted ───────────│ │ ▼ │ (GET stream closes) ``` #### Content Negotiation and Validation * POST `Content-Type` **MUST** be `application/json` (415 otherwise). * GET `Accept` **MUST** include `text/event-stream` (406 otherwise). * POST requests for session-scoped operations **MUST** include both `Acp-Connection-Id` and `Acp-Session-Id` headers. * GET requests without `Upgrade: websocket` **MUST** include `Acp-Connection-Id`. If `Acp-Session-Id` is also present, the stream is session-scoped; otherwise it is connection-scoped. An unknown `Acp-Session-Id` for the given connection returns 404. * Batch JSON-RPC requests return 501. * HTTP/2 is **REQUIRED** for Streamable HTTP transport. ### WebSocket Request Flow #### Connection Establishment (GET with Upgrade) ``` Client Server │ GET /acp │ │ Upgrade: websocket │ │────────────────────────────────────────►│ │ HTTP 101 Switching Protocols │ │ Acp-Connection-Id: │ │◄────────────────────────────────────────│ │ ══════ WebSocket Channel ══════════════│ ``` A new connection is created on upgrade. The `Acp-Connection-Id` is returned in the upgrade response headers. The client must still send `initialize` as the first JSON-RPC message over the WebSocket to negotiate capabilities before creating sessions. #### Bidirectional Messaging All messages are WebSocket text frames containing JSON-RPC. Binary frames are ignored. On disconnect, the server cleans up the connection and any associated sessions. ### Unified Endpoint Routing ``` GET /acp ├── Has Upgrade: websocket? → WebSocket handler └── No → SSE stream handler ├── Missing Acp-Connection-Id? → 400 Bad Request ├── Unknown Acp-Connection-Id? → 404 Not Found ├── Has Acp-Session-Id unknown for this connection? → 404 Not Found ├── Has Acp-Session-Id → Open session-scoped SSE stream └── No Acp-Session-Id → Open connection-scoped SSE stream POST /acp ├── Initialize request (no Acp-Connection-Id)? → Create connection, return 200 with JSON ├── No Acp-Connection-Id? → 400 Bad Request ├── Unknown Acp-Connection-Id? → 404 Not Found ├── Session-scoped request missing Acp-Session-Id? → 400 Bad Request └── Has valid Acp-Connection-Id (and Acp-Session-Id if required) → Forward to agent, return 202 Accepted DELETE /acp ├── Has Acp-Connection-Id? → Terminate connection and all associated sessions, return 202 └── No Acp-Connection-Id? → 400 Bad Request ``` ### Connection and Session Model ``` Connection { connection_id: String, // Acp-Connection-Id capabilities: NegotiatedCapabilities, sessions: HashMap, // keyed by sessionId (JSON-RPC field) get_stream: Option, // Connection-scoped GET stream to_agent_tx: mpsc::Sender, from_agent_rx: Arc>>, handle: JoinHandle<()>, } Session { session_id: String, // sessionId (JSON-RPC field) get_stream: Option, // Session-scoped GET stream // session-specific state } ``` The agent task is spawned once per connection. Server→client messages are routed to either the connection-scoped GET stream or the appropriate session-scoped GET stream based on whether the message is tied to a specific session. Sessions are identified by the `sessionId` field in JSON-RPC messages. The transport layer adapts channels to the wire format (SSE events for HTTP, text frames for WebSocket). ### Comparing to MCP Streamable HTTP | MCP Requirement | ACP Implementation | Status | | ------------------------------------------ | ------------------------------------------ | -------------------- | | POST for all client→server messages | ✅ | Compliant | | Accept header validation (406) | ✅ | Compliant | | Notifications/responses return 202 | ✅ (except `initialize` returns 200) | Mostly compliant | | Requests return SSE stream | ❌ (long-lived GET streams instead) | Documented deviation | | Session ID on initialize response | ✅ (`Acp-Connection-Id`) | Compliant (renamed) | | Session ID required on subsequent requests | ✅ (`Acp-Connection-Id` + `Acp-Session-Id`) | Compliant (extended) | | GET opens SSE stream | ✅ (connection-scoped + session-scoped) | Compliant (extended) | | DELETE terminates session | ✅ (terminates connection) | Compliant | | 404 for unknown sessions | ✅ (unknown connection IDs) | Compliant | | Batch requests | ❌ (returns 501) | Documented deviation | | Resumability (Last-Event-ID) | ❌ | Deferred to v2 | | Protocol version header | ❌ | Future work | ### Deviations from MCP Streamable HTTP 1. **Long-lived GET streams (connection-scoped + per-session)**: MCP opens a new SSE stream for each request response. ACP uses long-lived GET streams per connection — one connection-scoped stream plus one session-scoped stream per session. POST requests (except `initialize`) return 202 Accepted immediately, and responses arrive on the appropriate GET stream correlated by JSON-RPC `id`. 2. **Initialize returns JSON directly**: MCP's `initialize` returns an SSE stream. ACP's `initialize` returns `200 OK` with a JSON response body containing capabilities and `connectionId`. The `Acp-Connection-Id` is also included in the response header. 3. **HTTP/2 required**: ACP requires HTTP/2 for multiplexing concurrent POST requests alongside the long-lived GET stream. 4. **Two-header model**: ACP uses both `Acp-Connection-Id` (for connection identity) and `Acp-Session-Id` (for session identity on POST requests and on the session-scoped GET stream). MCP only uses `Mcp-Session-Id`. This allows ACP to distinguish connection-level state from session-level operations while supporting multiple concurrent sessions on one connection. 5. **WebSocket extension**: MCP doesn't define WebSocket. ACP adds it as a required client capability. Clients MUST support WebSocket, and servers MAY choose to only support WebSocket connections. 6. **Cookie support required**: Clients MUST handle cookies on HTTP transports for the duration of the connection, enabling sticky sessions and per-connection server state. 7. **No batch requests**: Returns 501. May be added later. 8. **No resumability yet in reference implementation**: SSE event IDs and `Last-Event-ID` resumption are deferred to v2 (see [Durability and reliability expectations](#durability-and-reliability-expectations)). ### Implementation Plan 1. **Phase 1 — Specification** (this RFD): Define the transport spec and align terminology. 2. **Phase 2 — Reference Implementation** (in progress): Working implementation in Goose (`block/goose`). 3. **Phase 3 — SDK Support**: Add Streamable HTTP and WebSocket client support to Rust SDK (`sacp`), then TypeScript SDK. 4. **Phase 4 — Hardening**: Origin validation, `Acp-Protocol-Version`, SSE resumability, batch requests, security audit. ## Frequently asked questions > What questions have arisen over the course of authoring this document or during subsequent discussions? ### Why not just use MCP Streamable HTTP as-is? MCP opens a new SSE stream for each request response, which creates many short-lived connections and complicates load balancing. ACP uses long-lived GET streams per connection (one connection-scoped plus one per session), dramatically reducing connection count and simplifying sticky session routing. This is better suited for ACP's bidirectional, multi-session nature. ### How are sessions identified? ACP uses `Acp-Connection-Id` in HTTP headers to identify the connection, and `Acp-Session-Id` (plus the `sessionId` JSON-RPC field) to identify sessions. A single connection may host multiple sessions. The connection-scoped GET stream delivers connection-level messages; each session-scoped GET stream delivers messages for exactly one session. ### Why add WebSocket support? ACP is highly bidirectional with frequent streaming updates. WebSocket provides true bidirectional messaging with lower per-message overhead than HTTP. Clients MUST support WebSocket so that servers can choose to only support WebSocket connections, simplifying deployment. Streamable HTTP remains available as an additional option for environments where WebSocket is not viable on the server side (e.g., serverless). ### How does the server distinguish WebSocket from SSE on GET? By inspecting the `Upgrade: websocket` header. This is standard HTTP behavior. ### Can a client have multiple sessions on one connection? Yes. A client may call `session/new` multiple times within a single `Acp-Connection-Id`. Each returns a distinct `sessionId` in the response body (delivered on the connection-scoped GET stream). For each session, the client opens a separate session-scoped GET stream using `Acp-Connection-Id` + `Acp-Session-Id`. ### What alternative approaches did you consider, and why did you settle on this one? * **Per-request SSE streams (like MCP)**: Rejected — creates too many long-lived connections, complicates load balancing, and wastes resources. * **Separate endpoints** (`/acp/http`, `/acp/ws`): Rejected — single endpoint is simpler; WebSocket upgrade is natural HTTP. * **WebSocket only**: Rejected — doesn't work through all proxies. * **Single connection-scoped GET stream with JSON-RPC demuxing**: Rejected — forces both server and client to parse JSON-RPC bodies to route by session, couples all sessions' backpressure together, and makes per-session resume/reconnect awkward. Splitting into a connection-scoped stream plus per-session streams keeps all session-level routing on HTTP headers. ### How does this interact with authentication? Authentication (see auth-methods RFD) is orthogonal and layered on top via HTTP headers, query parameters, or WebSocket subprotocols. `Acp-Connection-Id` and `Acp-Session-Id` are transport-level identifiers, not auth tokens. ### What about the `Acp-Protocol-Version` header? Clients SHOULD include it on all requests after initialization. Not yet implemented; part of Phase 4 hardening. ### Why require HTTP/2? HTTP/2 provides multiplexing, allowing many concurrent POST requests alongside the long-lived GET streams (one connection-scoped plus one per active session) on a single TCP connection. This is essential for efficient operation with the long-lived-stream model. HTTP/1.1 would require separate TCP connections for each concurrent POST and each GET stream, defeating the efficiency gains. ## Revision history * **2026-07-02**: Moved to Active to reflect current Transports Working Group focus. * **2026-06-05**: Added a "Durability and reliability expectations" section splitting out what implementers can expect in v1 (sessions survive disconnects, session affinity is preserved across reconnects, and reconnect/retry/liveness are the implementer's responsibility with no in-flight message replay) versus v2 (message IDs, stream resumability, defined reconnection semantics, more reliable notification cycles, and standardized keepalive). Marked Last-Event-ID resumability as deferred to v2. * **2026-05-04**: Split the single GET stream into two: a connection-scoped stream (GET with `Acp-Connection-Id`) for connection-level messages such as responses to `session/new` and `session/load`, and session-scoped streams (GET with `Acp-Connection-Id` + `Acp-Session-Id`) for session updates, server-to-client requests like `request_permission`, and responses to session-scoped POSTs. Routing happens on HTTP headers rather than JSON-RPC body inspection; per-session streams have independent lifetimes. * **2026-04-23**: Major revision to single long-lived GET stream model. Changed from per-request SSE streams to a single connection-scoped GET stream for all server→client messages. POST requests (except `initialize`) now return 202 Accepted immediately. `initialize` returns 200 OK with JSON response body. Required HTTP/2 for multiplexing. This change makes the HTTP usage more similar to WebSocket and supports better the bidirectional nature of ACP. * **2026-04-15**: Minor edits * **2026-04-01**: Introduced a two-header identity model: `Acp-Connection-Id` (returned at `initialize`, binds to the connection) and `Acp-Session-Id` (returned at `session/new`, scopes to a session). This addresses feedback that the original single `Acp-Session-Id` conflated transport binding with ACP session identity, and enables session-scoped GET listener streams for targeted server-to-client event delivery. Removed connection-scoped GET streams — all GET SSE listeners now require both `Acp-Connection-Id` and `Acp-Session-Id`. * **2025-03-10**: Initial draft based on the RFC template and goose reference implementation. # RFD Updates Source: https://agentclientprotocol.com/rfds/updates Lifecycle updates for ACP Requests for Dialog This page tracks lifecycle changes for ACP Requests for Dialog. For broader ACP announcements, see [Updates](/updates). ## Agent Telemetry Export RFD removed The Agent Telemetry Export RFD has been removed. Its design relied on clients injecting OpenTelemetry environment variables when launching agent subprocesses, which doesn't translate to the new remote transports where agents aren't launched by the client. Since the approach required no changes to the protocol itself, clients and agents that want this setup can adopt standard [OpenTelemetry SDK environment variable configuration](https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/) directly. ## Active RFD stage added The RFD lifecycle now includes an Active stage between Draft and Preview. Active RFDs are the proposals that core maintainers or a working group are currently spending bandwidth on. ## Boolean Config Option RFD moves to Preview stage The RFD for adding boolean session configuration options has been moved to Preview stage. Please review the [RFD](/rfds/boolean-config-option) for more information on the current proposal and provide feedback before the feature is stabilized. ## Request Cancellation RFD moves to Completed The RFD for `$/cancel_request` protocol-level request cancellation has been stabilized and is now part of the protocol. Either side can send this notification to request cancellation of an outstanding JSON-RPC request by ID. Please review the [documentation](/protocol/v1/cancellation) for more information. ## Rust SDK based on SACP RFD moves to Completed The RFD for basing the Rust SDK on SACP has been completed with the 1.0 SDK releases. The Rust SDK is available as [`agent-client-protocol` v1.0.0](https://crates.io/crates/agent-client-protocol/1.0.0), and the TypeScript SDK reached the same milestone with [`@agentclientprotocol/sdk` v1.0.0](https://www.npmjs.com/package/@agentclientprotocol/sdk/v/1.0.0), showing that the SDK design goals apply beyond Rust. ## model\_config Category RFD moves to Completed The RFD for the `model_config` session configuration option category has been stabilized and is now a part of the protocol. Agents can use `model_config` for model-related parameters such as context size or speed/quality trade-offs. Please review the [documentation](/protocol/v1/session-config-options#option-categories) for more information. ## Request Cancellation RFD moves to Preview stage The RFD for adding `$/cancel_request` protocol-level request cancellation has been moved to Preview stage. Please review the [RFD](/rfds/request-cancellation) for more information on the current proposal and provide feedback before the feature is stabilized. ## model\_config Category RFD moves to Preview stage The RFD for the `model_config` session configuration option category has been moved to Preview stage. Please review the [RFD](/rfds/model-config-category) for more information on the current proposal and provide feedback before the feature is stabilized. ## Message ID RFD moves to Completed The RFD for adding agent-generated `messageId` fields to streamed message chunks has been stabilized and is now part of the protocol. In v1, message IDs are optional on message chunks for compatibility; in v2, they are required. Please review the [documentation](/protocol/v1/prompt-turn#message-ids) for more information. ## Session Context Size and Cost RFD moves to Completed The RFD for `usage_update` session notifications has been stabilized and is now a part of the protocol. Agents can report session-level context window size and optional cumulative cost through `session/update`. Please review the [documentation](/protocol/v1/prompt-turn#session-usage-updates) for more information. ## session/delete RFD moves to Completed The RFD for allowing clients to delete a given session has been stabilized and is now a part of the protocol. Please review the [documentation](/protocol/v1/session-delete) for more information. ## \_meta Propagation RFD moves to Completed The RFD for documenting `_meta` propagation conventions for correlation metadata has been completed and is now part of the protocol documentation. Please review the [documentation](/protocol/v1/extensibility#the-_meta-field) for more information. ## Message ID RFD moves to Preview stage for v1 The v1 portion of the RFD for adding message IDs to streamed message chunks has been moved to Preview stage. Please review the [RFD](/rfds/message-id) for more information on the current proposal and provide feedback before the v1 behavior is stabilized. ## Session Usage RFD moves to Preview stage The RFD for adding `usage_update` session notifications for context window size and cumulative session cost has been moved to Preview stage. Please review the [RFD](/rfds/session-usage) for more information on the current proposal and provide feedback before the feature is stabilized. ## v2 Plan Variants RFD moves to Draft stage The RFD for making item-based `plan_update` the default v2 plan shape has been moved to Draft stage. Please review the [RFD](/rfds/v2/plan-variants) for more information on replacing the old v1 `plan` update in v2 while the broader Plan Operations RFD continues. ## End-Turn Token Usage RFD moves to Draft stage The end-turn token usage portions of the original Session Usage RFD have been split into a separate Draft RFD for further design work. Please review the [RFD](/rfds/end-turn-token-usage) for more information on the current proposal and provide feedback. ## session/delete RFD moves to Preview stage The RFD for allowing clients to delete a given session has been moved to Preview stage. Please review the [RFD](/rfds/session-delete) for more information on the current proposal and provide feedback before the feature is stabilized. ## Unstable Session Model API removed The never-stabilized `session/set_model` API and related session model response fields have been removed from the protocol artifacts. Agents should continue to expose model selection through [Session Config Options](/rfds/session-config-options). ## Additional Directories RFD moves to Completed The RFD for allowing clients to specify additional workspace roots for session lifecycle requests has been stabilized and is now a part of the protocol. Please review the [documentation](/protocol/v1/session-setup#additional-workspace-roots) for more information. ## v2 Enum Variant Extension RFD moves to Draft stage The RFD for reserving `_`-prefixed enum variants for extensions while preserving non-underscore variants for future ACP versions has been moved to Draft stage. Please review the [RFD](/rfds/v2/enum-variant-extension) for more information on the current proposal and provide feedback as work on v2 continues. ## Plan Operations Support RFD moves to Draft stage The RFD for adding `plan_update` and `plan_removed` session update types has been moved to Draft stage. Please review the [RFD](/rfds/plan-operations) for more information on the current proposal and provide feedback as work on the implementation begins. ## Logout Method RFD moves to Completed The RFD for the `logout` method has been stabilized and is now a part of the protocol. Please review the [documentation](/protocol/v1/authentication#logging-out) for more information. ## Additional Directories RFD moves to Preview stage The RFD for allowing clients to specify additional workspace roots for session lifecycle requests has been moved to Preview stage. Please review the [RFD](/rfds/additional-directories) for more information on the current proposal and provide feedback before the feature is stabilized. ## Logout Method RFD moves to Preview stage The RFD for adding a `logout` method to the protocol has been moved to Preview stage. Please review the [RFD](/rfds/logout-method) for more information on the current proposal and provide feedback before the feature is stabilized. ## Rust SDK based on SACP RFD moves to Preview stage The RFD for basing the Rust SDK on SACP has been moved to Preview stage now that the initial crate import has been upstreamed to [agentclientprotocol/rust-sdk](https://github.com/agentclientprotocol/rust-sdk). Please review the [RFD](/rfds/rust-sdk-v1) for more information on the current proposal and provide feedback before the SDK is stabilized as 1.0. ## model\_config Category RFD moves to Draft The RFD for the `model_config` category been moved to Draft stage. Please review the [RFD](/rfds/model-config-category) for more information on the current proposal and provide feedback before the feature is stabilized. ## v2 Prompting RFD moves to Draft The RFD for how the prompt lifecycle will work in v2 of the protocol been moved to Draft stage. Please review the [RFD](/rfds/v2/prompt) for more information on the current proposal and provide feedback before the feature is stabilized. ## session/close RFD moves to Completed The RFD for the `session/close` method has been stabilized and is now a part of the protocol. Please review the [documentation](/protocol/v1/session-setup#closing-active-sessions) for more information. ## session/resume RFD moves to Completed The RFD for the `session/resume` method has been stabilized and is now a part of the protocol. Please review the [documentation](/protocol/v1/session-setup#resuming-sessions) for more information. ## Streamable HTTP & WebSocket Transport RFD moves to Draft stage The RFD for standardizing a remote ACP transport based on MCP Streamable HTTP with a WebSocket upgrade has been moved to Draft stage. Please review the [RFD](/rfds/streamable-http-websocket-transport) for more information on the current proposal and provide feedback as work on the implementation begins. ## session/resume RFD moves to Preview stage The RFD for adding a `session/resume` method to the protocol has been moved to Preview stage. Please review the [RFD](/rfds/session-resume) for more information on the current proposal and provide feedback before the feature is stabilized. ## session/close RFD moves to Preview stage The RFD for allowing agents to close a given session has been moved to Preview stage. Please review the [RFD](/rfds/session-close) for more information on the current proposal and provide feedback before the feature is stabilized. ## Custom LLM Endpoint RFD moves to Draft stage The RFD for allowing clients to specify custom provider configuration has been moved to Draft stage. Please review the [RFD](/rfds/custom-llm-endpoint) for more information on the current proposal and provide feedback as work on the implementation begins. ## Additional Directories RFD moves to Draft stage The RFD for allowing clients to specify additional directories for the agent to access has been moved to Draft stage. Please review the [RFD](/rfds/additional-directories) for more information on the current proposal and provide feedback as work on the implementation begins. ## ACP Registry RFD moves to Completed The RFD for the initial version of the ACP Registry has been completed. Please review the [documentation](/get-started/registry) for more information. ## session\_info\_update Notification RFD moves to Completed The RFD for the session\_info\_update notification has been stabilized and is now a part of the protocol. Please review the [documentation](/protocol/v1/session-list#updating-session-metadata) for more information. ## session/list RFD moves to Completed The RFD for the session/list method has been stabilized and is now a part of the protocol. Please review the [documentation](/protocol/v1/session-list) for more information. ## Next Edit Suggestions RFD moves to Draft stage The RFD for allowing agents to provide next edit suggestions has been moved to Draft stage. Please review the [RFD](/rfds/next-edit-suggestions) for more information on the current proposal and provide feedback as work on the implementation begins. ## session/close RFD moves to Draft stage The RFD for allowing agents to close a given session has been moved to Draft stage. Please review the [RFD](/rfds/session-close) for more information on the current proposal and provide feedback as work on the implementation begins. ## Elicitation RFD moves to Draft stage The RFD for allowing agents to elicit user input has been moved to Draft stage. Please review the [RFD](/rfds/elicitation) for more information on the current proposal and provide feedback as work on the implementation begins. ## Boolean Config Option RFD moves to Draft stage The RFD for adding boolean config options to the protocol has been moved to Draft stage. Please review the [RFD](/rfds/boolean-config-option) for more information on the current proposal and provide feedback as work on the implementation begins. ## Delete in Diff RFD moves to Draft stage The RFD for indicating whether a diff resulted in a file deletion has been moved to Draft stage. Please review the [RFD](/rfds/diff-delete) for more information on the current proposal and provide feedback as work on the implementation begins. ## Message ID RFD moves to Draft stage The RFD for adding message ids to the protocol has been moved to Draft stage. Please review the [RFD](/rfds/message-id) for more information on the current proposal and provide feedback as work on the implementation begins. ## session/list RFD moves to Preview stage The RFD for the session/list method has been moved to Preview stage. Please review the [RFD](/rfds/session-list) for more information on the current proposal and provide feedback before the feature is stabilized. ## session\_info\_update RFD moves to Preview stage The RFD for the session\_info\_update notification has been moved to Preview stage. Please review the [RFD](/rfds/session-info-update) for more information on the current proposal and provide feedback before the feature is stabilized. ## ACP Registry RFD moves to Preview stage The RFD for the ACP Registry has been moved to Preview stage. Please review the [RFD](/rfds/acp-agent-registry) for more information on the current proposal and provide feedback before the feature is stabilized. ## Session Config Options RFD moves to Completed The RFD for adding more generic Session Config Options to the protocol has been stabilized and is now a part of the protocol. Please review the [documentation](/protocol/v1/session-config-options) for more information. ## session/delete moves to Draft stage The RFD for allowing clients to delete a given session has been moved to Draft stage. Please review the [RFD](/rfds/session-delete) for more information on the current proposal and provide feedback as work on the implementation begins. ## Logout method moves to Draft stage The RFD for allowing clients to logout from an agent connection has been moved to Draft stage. Please review the [RFD](/rfds/logout-method) for more information on the current proposal and provide feedback as work on the implementation begins. ## Rust SDK based on SACP RFD moves to Draft stage The RFD for basing the Rust SDK on SACP has been moved to Draft stage. Please review the [RFD](/rfds/rust-sdk-v1) for more information on the current proposal and provide feedback as work on the implementation begins. ## Session Config Options RFD moves to Preview stage The RFD for adding more generic Session Config Options to the protocol has been moved to Preview stage. Please review the [RFD](/rfds/session-config-options) for more information on the current proposal and provide feedback before the feature is stabilized. ## Authentication Methods RFD moves to Draft stage The RFD for creating additional types of authentication methods has been moved to Draft stage. Please review the [RFD](/rfds/auth-methods) for more information on the current proposal and provide feedback as work on the implementation begins. ## Agent Registry RFD moves to Draft stage The RFD for creating an Agent Registry has been moved to Draft stage. Please review the [RFD](/rfds/acp-agent-registry) for more information on the current proposal and provide feedback as work on the implementation begins. ## Session Usage RFD moves to Draft stage The RFD for adding a new usage\_update variant on the session/update notification and usage field on prompt responses in the protocol has been moved to Draft stage. Please review the [RFD](/rfds/session-usage) for more information on the current proposal and provide feedback as work on the implementation begins. ## Proxy Chains RFD moves to Draft stage The RFD for adding proxy chain functionality in the protocol has been moved to Draft stage. Please review the [RFD](/rfds/proxy-chains) for more information on the current proposal and provide feedback as work on the implementation begins. ## Agent Telemetry Export RFD moves to Draft stage The RFD for providing more guidance on how agents should export telemetry has been moved to Draft stage. (This RFD was later removed — see the July 2, 2026 update.) ## session\_info\_update notification RFD moves to Draft stage The RFD for adding a new session\_info\_update variant on the session/update notification in the protocol has been moved to Draft stage. Please review the [RFD](/rfds/session-info-update) for more information on the current proposal and provide feedback as work on the implementation begins. ## \_meta Propagation RFD moves to Draft stage The RFD for providing more guidance on how the \_meta parameter should be used within the protocol has been moved to Draft stage. Please review the [RFD](/rfds/meta-propagation) for more information on the current proposal and provide feedback as work on the implementation begins. ## session/resume RFD moves to Draft stage The RFD for adding a session/resume method to the protocol has been moved to Draft stage. Please review the [RFD](/rfds/session-resume) for more information on the current proposal and provide feedback as work on the implementation begins. ## \$/cancelRequest RFD moves to Draft stage The RFD for adding a \$/cancelRequest method to the protocol has been moved to Draft stage. Please review the [RFD](/rfds/request-cancellation) for more information on the current proposal and provide feedback as work on the implementation begins. ## session/fork RFD moves to Draft stage The RFD for adding a session/fork method to the protocol has been moved to Draft stage. Please review the [RFD](/rfds/session-fork) for more information on the current proposal and provide feedback as work on the implementation begins. ## Session Config Options RFD moves to Draft stage The RFD for adding more generic Session Config Options to the protocol has been moved to Draft stage. Please review the [RFD](/rfds/session-config-options) for more information on the current proposal and provide feedback as work on the implementation begins. ## session/list RFD moves to Draft stage The RFD for adding a session/list method to the protocol has been moved to Draft stage. Please review the [RFD](/rfds/session-list) for more information on the current proposal and provide feedback as work on the implementation begins. # v2 Client Filesystem and Terminal Surface Source: https://agentclientprotocol.com/rfds/v2/client-filesystem-terminal-capabilities Author(s): [@benbrandt](https://github.com/benbrandt) ## Elevator pitch > What are you proposing to change? ACP v2 will remove the standard Client filesystem and terminal execution surface. Specifically, v2 removes `clientCapabilities.fs`, the top-level `clientCapabilities.terminal` field, the Client filesystem methods, the Client terminal execution methods, and terminal tool-call content. This surface was intended to let Agents ask Clients to read or write files and run commands in a Client-managed terminal. In practice, it has not been widely adopted by both Clients and Agents, and many Agents are moving toward their own sandboxing and execution configuration instead. ## Status quo > How do things work today and what problems does this cause? Why would we change things? In v1, Clients can advertise filesystem access with: ```json theme={null} { "clientCapabilities": { "fs": { "readTextFile": true, "writeTextFile": true }, "terminal": true } } ``` Agents then use those fields to decide whether `fs/read_text_file`, `fs/write_text_file`, and `terminal/*` methods are available. Agents can also embed terminal output in tool calls using `ToolCallContent` with `type: "terminal"`. However, this hasn't been widely adopted. Both because not many clients outside of IDEs could even offer these, and even then, IDE support has been mixed. Also, this requires the Agent tools to handle both methods, and in practice, most stuck to their standard implementations. ## What we propose to do about it > What are you proposing to improve the situation? Remove the following standard v2 Client capability fields: * `clientCapabilities.fs` * `clientCapabilities.terminal` Remove the following standard v2 Client methods: * `fs/read_text_file` * `fs/write_text_file` * `terminal/create` * `terminal/output` * `terminal/release` * `terminal/wait_for_exit` * `terminal/kill` Remove terminal tool-call content from v2 as well. Without standard Client terminal methods, a standard terminal content type would no longer have a standard producer or lifetime model. This will be replaced by a richer way for agents to report their own terminal output. As this was always opt-in anyway, it should require minimal changes on the agent side, as they just no longer have to check these capabilities. This does not remove `clientCapabilities.auth.terminal`. That field belongs to the authentication methods proposal and only indicates whether a Client can run an Agent's terminal authentication flow for the user. ## Shiny future > How will things play out once this feature exists? ACP v2 has less Client-side surface for behavior that is not seeing broad interoperable use. If clients want to offer specialized tooling in these areas, they can already expose a special MCP server to the agent to provide access. We will look at exploring configuration for Agent sandboxing and filesystem access in the future. ## Implementation details and plan > Tell me more about your implementation. What is your detailed implementation plan? * Remove `fs` and top-level `terminal` from the v2 `ClientCapabilities` schema. * Remove the v2 `FileSystemCapabilities` schema type because it is no longer referenced. * Remove the v2 filesystem and terminal request/response structs, method-name constants, RPC enum variants, and generated method metadata. * Remove v2 terminal tool-call content and its terminal ID dependency. * Keep v1 unchanged. * Cross-version conversion from v1 to v2 drops `fs` and top-level `terminal`. * Cross-version conversion from v2 to v1 maps filesystem and terminal execution support to unsupported. * Cross-version conversion from v1 to v2 returns an error for removed filesystem and terminal method variants or terminal tool-call content. * Update v2 initialization, overview, tool-call, and schema docs so Agents do not check these v1 capability fields or call these v1 methods for v2 connections. ## Frequently asked questions > What questions have arisen over the course of authoring this document or during subsequent discussions? ### Does this remove terminal authentication? No. Terminal authentication is represented by `clientCapabilities.auth.terminal`, not by the top-level `clientCapabilities.terminal` execution capability. This RFD only removes the top-level terminal execution capability. ## Revision history 2026-06-02: Initial draft # v2 Diff File States Source: https://agentclientprotocol.com/rfds/v2/diff-file-states Author(s): [@benbrandt](https://github.com/benbrandt) ## Elevator pitch > What are you proposing to change? ACP v2 should replace the v1 `Diff` shape's ambiguous `oldText` / `newText` fields with a structured diff content shape that can represent text patches, non-text file changes, and file-level operations such as add, delete, modify, move, and copy. Every diff should carry structured file-change metadata. Text changes can also carry renderable git patch text in an optional top-level `patch` field. ## Status quo > How do things work today and what problems does this cause? Why would we change things? ACP v1 represents a diff as a path plus old and new text: ```json theme={null} { "type": "diff", "path": "/home/user/project/src/config.json", "oldText": "{\n \"debug\": false\n}", "newText": "{\n \"debug\": true\n}" } ``` That shape has several problems: * `newText: ""` cannot distinguish a deleted file from an empty file without an extra flag. * Moves and copies have no direct representation. * Binary files, symlinks, and directory entries do not have useful `oldText` and `newText` values. * Clients have to reconstruct or parse a diff to render changes. * The protocol cannot express whether a file path was added, deleted, modified, moved, or copied. The v1 [Represent deleted files in diff](/rfds/diff-delete) RFD is a useful stop-gap for deletes, but v2 can use a cleaner breaking shape instead of adding more flags to the v1 structure. ## What we propose to do about it > What are you proposing to improve the situation? ACP v2 should make `ToolCallContent` diffs carry a required `changes` array and an optional `patch` object. Use `patch` for changes that have useful renderable patch text: ```json theme={null} { "type": "diff", "changes": [ { "operation": "modify", "path": "/home/user/project/src/config.json", "fileType": "text", "mimeType": "application/json" } ], "patch": { "format": "git_patch", "diff": "diff --git /home/user/project/src/config.json /home/user/project/src/config.json\n--- /home/user/project/src/config.json\n+++ /home/user/project/src/config.json\n@@ -1,3 +1,3 @@\n {\n- \"debug\": false\n+ \"debug\": true\n }\n" } } ``` The patch format should be `git_patch`. A single git patch can describe several files, so the patch is top-level context for the whole `changes` array. Clients can render the patch text directly for baseline compatibility, and can use `changes` to identify affected paths and operations without parsing the patch. Agents SHOULD provide `patch` whenever feasible. Clients MUST handle diffs where `patch` is omitted or `null`. Omit `patch` when patch text is not useful: ```json theme={null} { "type": "diff", "changes": [ { "operation": "modify", "path": "/home/user/project/assets/logo.png", "fileType": "binary", "mimeType": "image/png" } ] } ``` Omitted and `null` `patch` values are equivalent and mean no renderable patch text was provided. The initial operation set should be: * `add` * `delete` * `modify` * `move` * `copy` `add`, `delete`, and `modify` use `path`. `move` and `copy` use `oldPath` and `path`. All paths in protocol payloads are absolute. The optional `fileType` field should describe the file entry when known: * `text` * `binary` * `directory` * `symlink` The optional `mimeType` field should carry a MIME type when known. Omitted and `null` values for `fileType` and `mimeType` mean the value is unknown. ## Shiny future > How will things play out once this feature exists? Clients can render text diffs without needing to synthesize patch text, and can show file-level summaries without parsing patch syntax. Agents can report binary updates, symlink changes, deletes, moves, and copies without forcing those cases through text-only fields. The v2 diff surface is also easier for SDKs to model because operations are explicit enum variants rather than inferred states from optional text fields. ## Implementation details and plan > Tell me more about your implementation. What is your detailed implementation plan? 1. Replace the v2 `Diff` struct's old text fields with a required `changes` array. 2. Add an optional `patch` field with `format` and `diff`. 3. Use `git_patch` as the ACP-defined patch format, and document that Agents SHOULD provide it whenever feasible. 4. Add structured `DiffChange` operation variants for add, delete, modify, move, and copy, with path fields owned by each operation variant. 5. Add optional `fileType` and `mimeType` fields for non-text and unknown file content. 6. Preserve custom or future operations where the receiver can safely store, replay, forward, or display a generic summary. 7. Update generated schema and v2 protocol docs. ## Frequently asked questions > What questions have arisen over the course of authoring this document or during subsequent discussions? ### Why use git patch instead of unified diff? Git patch is broadly supported, renderable as text, and can carry file-level headers for multiple files. ACP can still add future patch formats if another format becomes necessary, but v2 should define one default format first. ### Why is `patch` top-level instead of attached to individual changes? Git patch text can describe several files and several operation kinds at once. Keeping `patch` top-level makes it renderable context for the whole diff content item, while `changes` remains the structured source for paths and operations. ### Should patch text be restricted to some operation variants? No. Git patches can represent adds, deletes, modifies, moves/renames, copies, and some binary changes. If an Agent has useful patch text for a set of changes, it can include `patch`; otherwise it can omit it. ### Why is `patch` optional if Agents SHOULD provide it? Some changes have no useful patch text, especially same-path binary updates and symlink target changes. Patch generation can also fail or be unavailable in a given Agent implementation. Keeping `patch` optional lets clients rely on structured `changes`, while the SHOULD encourages better rendering when patch text is practical. ### What kind of `modify` does not have a patch? Mostly in-place non-text updates, such as a binary file update at the same path or a symlink target change. Text modifications should normally use `patch` so clients have renderable text. ### Why include both patch text and structured changes? Patch text gives clients an immediate baseline rendering path. Structured changes give clients operation and path metadata without requiring patch parsing, which matters for summaries, file trees, permissions, and non-text indicators. ## Revision history * 2026-07-02: Initial draft # v2 Enum Variant Extension Source: https://agentclientprotocol.com/rfds/v2/enum-variant-extension Author(s): [@benbrandt](https://github.com/benbrandt) ## Elevator pitch > What are you proposing to change? ACP v2 should make enum-like protocol values forward-compatible when a receiver has a safe fallback. Values beginning with `_` are reserved for implementation-specific extensions. Values that do not begin with `_` are reserved for ACP itself, including future ACP variants. This gives extensions a dedicated namespace without making older clients and agents reject additive informational protocol changes. ## Status quo > How do things work today and what problems does this cause? Why would we change things? ACP already uses `_` as the extension namespace for custom methods and session config categories. Session config categories also preserve unknown category strings, so older clients can handle newer categories gracefully. Most other string enum protocol fields are closed in the generated schema. Examples include roles, status values, stop reasons, and permission option kinds. SDKs and validators generated from those schemas can reject a message only because a newer ACP version added a variant the older implementation does not understand. That strictness is useful when the value controls whether a request can be fulfilled correctly. It is unnecessarily disruptive for notifications, display metadata, and other informational fields. A client can often ignore, preserve, forward, or display a generic fallback for an unknown session update, tool status, plan entry status, or UI hint. Without a shared namespace rule, extension authors also have no safe place to experiment with custom values without risking collisions with future ACP variants. ## What we propose to do about it > What are you proposing to improve the situation? ACP v2 should define one enum extension rule for ACP-owned values when the receiving side has an acceptable fallback: * Values beginning with `_` are reserved for implementation-specific extensions. * Values that do not begin with `_` are reserved for ACP. * Future ACP versions may add non-underscore values without treating that as an extension collision. * Extensions MUST NOT define custom non-underscore values. * Implementations MUST NOT treat an unknown non-underscore value as a custom extension. * Implementations SHOULD preserve unknown values when storing, replaying, proxying, or forwarding protocol data. * Implementations that cannot act on an unknown value SHOULD degrade gracefully according to the field's semantics. This applies to ACP-owned enum-like values where fallback behavior is acceptable. It does not apply to JSON-RPC fixed constants such as `"2.0"`, numeric code spaces such as JSON-RPC error codes, user-supplied elicitation option values such as `enum` or `oneOf` constants, or discriminators that are core to fulfilling a request. Elicitation schema annotations such as string `format` values can still be open because an implementation can safely treat an unknown format as an annotation. Tagged unions use string discriminator fields too. When ACP v2 opens one of these unions, it must preserve both the discriminator and the unknown object payload. The same `_` namespace rule applies. Raw fallbacks are appropriate for notification-style and display-data payloads that can be stored, replayed, proxied, forwarded, ignored, or rendered with a generic UI. Tagged unions that are core to request handling, such as elicitation actions, schema-shape discriminators, and transport selectors, may remain closed and produce a deserialization error for unknown variants. Permission outcomes can be opened when unknown outcomes are handled conservatively and never treated as approval. ## Shiny future > How will things play out once this feature exists? Adding a new ACP variant for an informational or notification-like field will not automatically make older v2 validators reject otherwise well-formed messages. Older implementations can preserve and forward data they do not understand, or fall back to generic display behavior when that is enough. Extensions get a clear rule: use `_vendor_or_feature`-style values for custom variants, and leave every non-underscore spelling available for ACP. Capabilities are still available when a sender needs to know whether the receiver can act on a value. This proposal makes the wire format tolerant first; capabilities can still gate behavior when behavior matters. ## Implementation details and plan > Tell me more about your implementation. What is your detailed implementation plan? The v2 Rust schema types should encode this convention directly where fallback behavior is safe: * String enum definitions list known values explicitly and include a fallback variant that captures the unknown string. * Notification-style and display-data tagged unions can add a raw fallback variant that captures the discriminator string and unknown payload object. * Raw tagged-union fallbacks must not hide malformed known variants. If a known discriminator is present but its payload is invalid, deserialization must still fail. * Untagged shape unions can add raw fallbacks only when there is a reliable discriminator field to distinguish a future/custom variant from a malformed known shape. * Core control-flow discriminators may remain closed when there is no safe fallback behavior. * SDKs should expose fallback variants that carry unknown values rather than silently discarding them. * Conversion from v2 to v1 should fail when an unknown v2 value cannot be represented in v1 without data loss. * v1 schemas remain unchanged. The fallback variant's Rust doc comment should document the `_` extension rule and the future-ACP reservation rule, so generated docs and schemas follow the type source of truth. This proposal does not require identical runtime behavior for every unknown value. Some fields can be ignored, some can be displayed generically, some can be preserved only for replay or proxying, and some should still fail because the receiver cannot safely continue. The cross-cutting rule is that `_` means extension-owned, non-underscore means ACP-owned, and parsers and validators should not fail solely because a value is unknown when that field has a defined fallback path. ## Frequently asked questions > What questions have arisen over the course of authoring this document or during subsequent discussions? ### Why not reserve all unknown values for custom extensions? That would block ACP from adding future standard variants without risking collisions with implementations that already claimed those names. Reserving non-underscore values for ACP gives the protocol room to grow while still giving extensions a predictable namespace. ### Does this make every enum extensible? No. This proposal opens values only when the receiver has a safe fallback. If understanding the variant is required for correctness, the enum should remain closed or be gated by capability negotiation. ### Why not use capabilities for every new variant? Capabilities are still useful when a sender needs to know whether the receiver can act on a variant. They do not solve parsing, validation, storage, replay, or proxying problems. The enum extension rule makes the wire format tolerant first; capabilities can still gate behavior when needed. ### Does this mean implementations must understand every unknown variant? No. Implementations should preserve unknown values when practical and degrade gracefully according to the field's semantics. This proposal is about forward compatibility and namespace ownership, not requiring old software to implement future behavior. ## Revision history 2026-05-27: Initial draft # v2 Message Updates and Chunks Source: https://agentclientprotocol.com/rfds/v2/message-updates Author(s): [@benbrandt](https://github.com/benbrandt) ## Elevator pitch > What are you proposing to change? ACP v2 should support both whole-message updates and streamed message chunks for user messages, agent messages, and agent thoughts. Whole-message updates are upserts keyed by `messageId`, while chunks append content to the message with the matching `messageId`. ## Status quo > How do things work today and what problems does this cause? Why would we change things? ACP v1 primarily reports user, agent, and thought messages as chunks: ```json theme={null} { "sessionUpdate": "agent_message_chunk", "messageId": "msg_agent_c42b9", "content": { "type": "text", "text": "Analyzing..." } } ``` Chunks are a natural fit for streaming model output, but they are awkward for session replay, accepted user-message acknowledgments, and cases where the Agent already has the whole message available. Also, there is no way for an agent to *redact* content from a message if needed due to guardrail APIs. The v2 prompt lifecycle also needs a clear way for Agents to acknowledge or replay accepted user messages. The [Message ID RFD](../message-id.mdx) keeps message IDs agent-owned, so the Agent needs a session update that can provide both the accepted user-message content and its `messageId`. ## What we propose to do about it > What are you proposing to improve the situation? ACP v2 should define whole-message session updates: * `user_message` * `agent_message` * `agent_thought` Each whole-message update is an upsert keyed by `messageId`: * `messageId` is required. * `content` and `_meta` are patch fields. * An omitted patch field leaves the previous value unchanged. * `null` explicitly clears or unsets the field. * Any concrete value replaces the previous value. * `content` is replaced as a whole array, not appended. * `[]` and `null` both clear `content`. * For a new `messageId`, omitted fields use Client defaults. The matching chunk updates remain: * `user_message_chunk` * `agent_message_chunk` * `agent_thought_chunk` Chunks append their single `content` item to the current content of the message with the matching `messageId`. ### Interaction between updates and chunks Clients apply message updates and chunks in the order they are received for each `messageId`. If a message update includes `content`, that content array replaces all content currently stored for the message, including content accumulated from earlier chunks: ```json theme={null} [ { "sessionUpdate": "agent_message_chunk", "messageId": "m1", "content": { "type": "text", "text": "A" } }, { "sessionUpdate": "agent_message_chunk", "messageId": "m1", "content": { "type": "text", "text": "B" } }, { "sessionUpdate": "agent_message", "messageId": "m1", "content": [{ "type": "text", "text": "C" }] } ] ``` After this sequence, the message content is `[C]`. If chunks arrive after a message update, they append to the update's current content: ```json theme={null} [ { "sessionUpdate": "agent_message", "messageId": "m1", "content": [{ "type": "text", "text": "A" }] }, { "sessionUpdate": "agent_message_chunk", "messageId": "m1", "content": { "type": "text", "text": "B" } } ] ``` After this sequence, the message content is `[A, B]`. If a message update omits `content`, it does not change the current content. This lets Agents update `_meta` or future optional fields without resending the whole content array: ```json theme={null} { "sessionUpdate": "agent_message", "messageId": "m1", "_meta": { "source": "replay" } } ``` ## Shiny future > How will things play out once this feature exists? Agents can choose the reporting style that matches the data they have: * use whole-message updates for replay, accepted user messages, and already-complete model output; * use chunks for streaming output; * combine both when an Agent needs to seed content and then stream more, or correct/replace content after streaming. The upsert shape also leaves room for future message-level fields, such as annotations or provenance, without forcing Agents to resend content for metadata-only updates. ## Implementation details and plan > Tell me more about your implementation. What is your detailed implementation plan? 1. Add the `user_message`, `agent_message`, and `agent_thought` session update variants to v2. 2. Require `messageId` on whole-message updates and chunks. 3. Represent `content` and `_meta` as three-state patch fields so omitted, `null`, and concrete values can be distinguished. 4. Document ordering semantics between whole-message updates and chunks in the protocol docs. 5. Convert content-bearing whole-message updates to v1 by fanning out one v1 chunk per content block. This is equivalent only when the v1 side has not already received content for that `messageId`; v1 has no way to replace earlier chunks. Patch-only updates, `content: null`, `content: []`, and `_meta: null` cannot be represented by v1 chunks and should fail conversion rather than silently dropping the update. ## Frequently asked questions > What questions have arisen over the course of authoring this document or during subsequent discussions? ### Why are message updates upserts instead of required full snapshots? Upserts let Agents update `_meta` or future optional message fields without resending the whole content array. ### Why does `content` replace instead of append? Appending is already represented by chunks. A whole-message update needs to be able to replay, correct, or replace the current content for a message. Making `content` a replacement array gives each update type a distinct behavior: message updates replace fields, chunks append content. ### What happens when a message update arrives before any chunks? The Client creates or updates the message for that `messageId`. Later chunks with the same `messageId` append to the current content. ### What happens when chunks arrive before a message update? The Client appends those chunks to the message for that `messageId`. A later message update with `content` replaces the accumulated content. ## Revision history * 2026-06-09: Initial draft. # ACP v2 Proposal Source: https://agentclientprotocol.com/rfds/v2/overview Author(s): [@benbrandt](https://github.com/benbrandt) This is a tracking RFD for the collection of RFDs that require breaking changes to the protocol and should make up ACP v2. ## Elevator pitch > What are you proposing to change? With ACP, we aim to move fast while keeping breaking changes to a minimum. However, we've gotten to a point where there are enough changes we would like to do that would benefit from some core redesigns that will allow for extending the protocol with new features more easily. We've also managed to add new features that have led to learnings that would benefit from consolidation and alignment in other areas of the protocol to smooth things out and make things more consistent. ## Status quo > How do things work today and what problems does this cause? Why would we change things? We have had a fairly successful time adding new features via new capabilities and adding in new features in a non-breaking way. But some of the learnings we have made will require breaking changes, and it feels like there are enough of these built up, or RFDs we are stuck due to required changes that now is a good time to do so. ## What we propose to do about it > What are you proposing to improve the situation? ### Current Active RFDs Current RFDs with active maintainer focus that are targeting the v2 release * [New Prompt Lifecycle](./prompt.mdx) * [Enum Variant Extension](./enum-variant-extension.mdx) * [Required Session Methods](./required-session-methods.mdx) * [Session Resume Replay](./session-resume-replay.mdx) * [Client Filesystem and Terminal Surface](./client-filesystem-terminal-capabilities.mdx) * [Plan Variants](./plan-variants.mdx) * [Tool Call Updates](./tool-call-updates.mdx) * [Diff File States](./diff-file-states.mdx) * [Permission Requests](./permission-requests.mdx) * [Message Updates and Chunks](./message-updates.mdx) * [Remote Transports](../streamable-http-websocket-transport.mdx) Other RFDs will progress separately and are not dependent on breaking changes (specifically the new prompt lifecycle) and can land in either or both v1 and v2. ### v2 Changes * Remove the dedicated session modes API from v2. This includes the `modes` session response fields, `session/set_mode`, `current_mode_update`, and the `SessionMode*` types. * Agents should expose mode-like and model-related state through [Session Config Options](../session-config-options.mdx) instead of dedicated mode or model selector APIs. * Remove the v1 Client filesystem and terminal execution surface from v2. This includes `clientCapabilities.fs`, the top-level `clientCapabilities.terminal` field, `fs/*` methods, `terminal/*` methods, and terminal tool-call content. Terminal authentication remains separate under `clientCapabilities.auth.terminal`. * Make [plan variants](./plan-variants.mdx) the default v2 plan shape by replacing the old `plan` session update with item-based `plan_update`. * Replace the v1 split between `tool_call` and `tool_call_update` with a single [tool-call update](./tool-call-updates.mdx) upsert shape keyed by `toolCallId`. * Add [tool-call content chunks](./tool-call-updates.mdx) so Agents can stream individual `ToolCallContent` items that append to a tool call. * Replace the v1 `Diff` `oldText` / `newText` shape with [diff file states](./diff-file-states.mdx): renderable `git_patch` text for text changes plus structured file operations for add, delete, modify, move, copy, and non-text changes. * Make [permission requests](./permission-requests.mdx) carry a required prompt `title` and optional extensible `subject` tagged union. Tool-call permissions use `subject.type: "tool_call"` with the same `ToolCallUpdate` payload shape as session updates, while subject-less permissions rely on the common prompt fields. * Add [whole-message updates](./message-updates.mdx) for `user_message`, `agent_message`, and `agent_thought` alongside streamed chunks. Message updates are upserts keyed by `messageId`; their `content` arrays replace current message content, while chunks append to the current content. * Require [message IDs](../message-id.mdx) on streamed message chunks. * Follow JSON-RPC 2.0 batch request and notification behavior. * Clean up capability naming and organization: * Use a single `capabilities` field in both `initialize` params and results, replacing the v1-style `clientCapabilities` and `agentCapabilities` fields. * Require implementation metadata in both `initialize` params and results with a single role-agnostic `info` field, replacing the v1-style `clientInfo` and `agentInfo` fields so agent-to-agent and other symmetric ACP connections do not need role-specific field names. * Group authentication methods under `auth/*`: v2 uses `auth/login` and `auth/logout` instead of v1's top-level `authenticate` and `logout` method names. `auth/logout` is required for v2 Agents and no longer uses a `capabilities.auth.logout` support marker. The generated request and response type names follow the grouped method naming as `LoginAuthRequest` / `LoginAuthResponse` and `LogoutAuthRequest` / `LogoutAuthResponse`. * Use concise capability group names such as `session` and `auth`, replacing names like `sessionCapabilities`. * Make `session` optional so non-session agents, such as NES-only agents, can omit it. * Require the baseline session lifecycle methods when `session` is present: `session/new`, `session/list`, `session/resume`, `session/close`, `session/prompt`, `session/cancel`, and `session/update`. * Remove `session/load` from v2. `session/resume` handles both no-replay resume and full replay by using the optional `replayFrom` cursor from the [Session Resume Replay](./session-resume-replay.mdx) RFD. * Move optional session-scoped capability groups under `session`, including `prompt` and `mcp` as `session.prompt` and `session.mcp`. * Represent support markers as capability objects instead of booleans. Supplying `{}` means supported, while omission or `null` means unsupported. Booleans remain appropriate for actual data or configuration inside an already-advertised capability. * Align MCP server transports with the current MCP transport model: * Remove the deprecated HTTP+SSE MCP transport from v2. * Make stdio an explicit `session.mcp.stdio` capability so Agents that cannot launch local subprocesses can opt out. * Require MCP server configurations to include a `type` discriminator, including `type: "stdio"`, so unknown future transports can be preserved as extension/future variants. * Keep HTTP as the remote MCP server transport capability. * Unify ID naming and typing across the v2 schema. Protocol fields should use domain-specific ID names such as `messageId`, `toolCallId`, `planId`, `providerId`, and `serverId` rather than a generic `id` whenever the field identifies a protocol entity or references another resource. ### RFDs to be Written Changes under consideration that still need to be drafted or moved to draft: * Streaming/Non-streaming consistency follow-ups: * Terminal Output type for streaming terminal output from an agent * MCP: tool timeouts, more lifecycle methods ## Shiny future > How will things play out once this feature exists? There is a lot of work to do, especially on the SDK side, to support both versions, but it is likely that we should be able to allow Agents specifically to target v2 APIs and gracefully fall back to v1 messages for v1 clients, to avoid huge support issues. However, once all of this work is in place, it should be much easier to make additional breaking changes in the future when necessary, we've been kind of letting this build up given the effort required for the entire ecosystem, but the ACP maintainers will be charting a course forward to make this as smooth as possible! ## Implementation details and plan > Tell me more about your implementation. What is your detailed implementation plan? ### v2 + v1 Schema publishing I have created a [draft of the v2 schema](https://github.com/agentclientprotocol/agent-client-protocol/pull/1099), which is currently a direct duplicate of v1. This also has the necessary conversion types that are needed for Rust at least to convert between the two. But this has a nice side-effect of a clear diff of how the schema will change and also what conversion is necessary. So the plan is to start proposing draft RFDs with the relevant schema changes where possible for approval. Once we have more pieces in place, we can start publishing both schemas to assist SDK developers to start figuring out how to support this. **This should be done in an opt-in, off by default, clearly labeled unstable way for SDK consumers**. There will likely be bumps as we figure out the necessary plumbing and we shouldn't be shipping v2 in production without feature flags prior to a more stable release as we align all of the necessary pieces. ### SDK Support With the needed breaking changes, as much as possible I am targeting having a consistent API surface for Agents, since they will want to target v2 APIs but still support v1 clients. Because of how the version negotiation works, if we can achieve the same thing for clients that will be great, but if not, they will at least be provided clear version entry points. ## Frequently asked questions > What questions have arisen over the course of authoring this document or during subsequent discussions? ## Revision history * 2026-07-02: Added the v2 Diff File States RFD for renderable git patches, structured file operations, and non-text file changes. * 2026-07-02: Moved the v2 RFD collection to Active. * 2026-07-02: Added the v2 Permission Requests RFD for required permission titles, optional structured subjects, and tool-call permission subjects. * 2026-07-02: Added the v2 Session Resume Replay RFD and recorded that `session/resume` replaces `session/load` in v2 by using optional `replayFrom` cursors. * 2026-07-02: Added the v2 Required Session Methods RFD and recorded that `session/list`, `session/resume`, and `session/close` are baseline when `session` is present. * 2026-06-30: Recorded the v2 ID unification principle: prefer domain-specific ID field names. * 2026-06-25: Recorded the v2 initialize information cleanup: required role-agnostic `info` in both params and results, replacing `clientInfo` and `agentInfo`. * 2026-06-25: Recorded the v2 authentication method cleanup: grouped `auth/login` and required `auth/logout` method names replace v1's top-level `authenticate` and capability-gated `logout`, with matching generated type names for login and logout auth requests and responses. * 2026-06-09: Added tool-call content chunks for streaming individual `ToolCallContent` items. * 2026-06-09: Added the v2 Message Updates and Chunks RFD to define whole-message upserts alongside streamed message chunks. * 2026-06-08: Added the v2 Tool Call Updates RFD to make `tool_call_update` the single upsert-style tool-call session update. * 2026-06-05: Recorded the v2 capability cleanup: unified initialize capability fields, optional `session` support for NES-only agents, session-scoped `prompt` and `mcp` capabilities, object-shaped support markers, explicit `session.mcp.stdio`, removal of deprecated MCP SSE transport, and tagged MCP server transport configs. * 2026-06-02: Recorded the v2 decision to follow JSON-RPC 2.0 batch request and notification behavior. * 2026-06-02: Recorded the v2 decision to remove Client filesystem and terminal execution capabilities, methods, and terminal tool-call content. * 2026-06-02: Added the v2 Plan Variants RFD to make item-based `plan_update` the default v2 plan shape. * 2026-06-01: Recorded that model selection should remain represented by session config options instead of a dedicated selector API. * 2026-05-28: Recorded the v2 decision to remove session modes in favor of session config options * 2026-05-06: Initial draft # v2 Permission Requests Source: https://agentclientprotocol.com/rfds/v2/permission-requests Author(s): [@benbrandt](https://github.com/benbrandt) ## Elevator pitch > What are you proposing to change? ACP v2 should make `session/request_permission` extensible beyond tool calls by giving every permission prompt a required `title` and optional structured `subject` context. Tool-call permissions use `subject.type: "tool_call"`, while permission requests that are not tied to a structured subject omit `subject`. Permission prompt copy should live in common `title` and `description` fields rather than in subject-specific data. ## Status quo > How do things work today and what problems does this cause? Why would we change things? In v1, permission requests are tied directly to a `toolCall` field. This works for tool execution approvals, but it makes the method harder to extend to other approval-like decisions. It also tempts agents to put permission prompt text into `ToolCallUpdate.title` or `ToolCallUpdate.content`. In v2, those fields are patch state for the displayed tool call. Using them for permission-only UI copy can accidentally change the tool call shown elsewhere. ## What we propose to do about it > What are you proposing to improve the situation? `RequestPermissionRequest` should have these common fields: * `sessionId` * `title` * `description` * `options` * `subject` * `_meta` `title` is required and is the minimum user-visible prompt text. `description` is optional explanatory text. Omitted and `null` descriptions are equivalent and mean no extra detail was provided. `subject` is optional structured context and uses a tagged union when present: ```json theme={null} { "title": "Approve file edit?", "description": "Allow the agent to edit src/main.rs?", "subject": { "type": "tool_call", "toolCall": { "toolCallId": "call_001" } } } ``` ```json theme={null} { "title": "Continue with elevated permissions?" } ``` Omitted and `null` subjects are equivalent and mean no structured subject was provided. A missing subject is not a separate subject variant. Unknown future subject types should be preserved by clients that proxy, store, replay, or forward permission requests. Clients that cannot understand the subject should show a generic permission prompt or decline according to policy. ## Shiny future > How will things play out once this feature exists? Agents can request permission for tool calls without conflating permission UI text with tool-call state. They can also request permission for operations that have no structured subject, and future structured subjects can be added without changing the common permission request fields. ## Implementation details and plan > Tell me more about your implementation. What is your detailed implementation plan? 1. Replace the v1-style top-level `toolCall` request field in v2 with an optional `subject` tagged union. 2. Add the `tool_call` subject variant. 3. Add common required `title` and optional `description` fields. 4. Keep `options` as the required list of choices the user can select. 5. Preserve unknown subject variants while keeping known subject payloads distinct from unknown fallback variants. ## Frequently asked questions > What questions have arisen over the course of authoring this document or during subsequent discussions? ### Why require `title`? The title is the minimum text a client can display when asking the user to choose an option. Requiring it keeps subject-less permission requests usable without requiring clients to synthesize prompt copy. ### Why make `subject` optional? Some permission requests are valid without a structured target. In those cases, `title`, `description`, and `options` are the permission request, while `subject` is additional context for clients that can render a richer prompt. ### Why not flatten subject fields into the request? `title`, `description`, and `options` describe the permission prompt. `subject` describes what the permission is about. Keeping the subject nested avoids collisions between common request fields and future subject-specific fields. ### Why not use `ToolCallUpdate.title` or `ToolCallUpdate.content` for permission copy? Those fields patch the displayed tool call. Permission-only prompt copy belongs in the common `title` and `description` fields so a permission request does not accidentally replace tool-call title or content. It's quite common to have a reason you want to display for the permission request itself without updating the persisted tool call for the rest of the session. ## Revision history * 2026-07-02: Initial draft # v2 Plan Variants Source: https://agentclientprotocol.com/rfds/v2/plan-variants Author(s): [@benbrandt](https://github.com/benbrandt) ## Elevator pitch > What are you proposing to change? ACP v2 should make the extensible `plan_update` shape the default plan notification, remove the old v1 `sessionUpdate: "plan"` shape from the v2 schema, and keep the rest of the plan operations work in the unstable plan-operations surface while that RFD continues. For now, v2 should stabilize only the item-based plan content variant: ```json theme={null} { "sessionUpdate": "plan_update", "plan": { "type": "items", "id": "plan-1", "entries": [ { "content": "Step 1", "priority": "high", "status": "pending" } ] } } ``` The markdown, file, and removal pieces from [Plan Operations Support](/rfds/plan-operations) remain behind the unstable plan-operations feature while that RFD continues. V2 does not need a separate `plan` runtime handshake for those unstable operations. ## Status quo > How do things work today and what problems does this cause? Why would we change things? The v1 plan update is a flattened session update: ```json theme={null} { "sessionUpdate": "plan", "entries": [{ "content": "Step 1", "priority": "high", "status": "pending" }] } ``` This shape has no stable place for a plan ID or a content discriminator. Adding either field to the same `plan` variant would create a mixed old/new shape and make future plan formats harder to add cleanly. The Plan Operations RFD already introduced a more extensible tagged shape through `plan_update`, plus markdown, file, and removal operations. Those pieces are still useful, but v2 does not need to wait on them to adopt the extensible baseline. ## What we propose to do about it > What are you proposing to improve the situation? ACP v2 should: * Remove the old `sessionUpdate: "plan"` variant from the v2 schema. * Add `sessionUpdate: "plan_update"` to the default v2 schema. * Stabilize only the `plan.type: "items"` content variant for now. * Keep unknown plan content variants parseable and preservable using the [v2 enum variant extension](/rfds/v2/enum-variant-extension) rules. * Require every plan content variant, including unknown future/custom variants, to carry a plan `id`. * Remove `ClientCapabilities.plan` from v2. * Keep `markdown`, `file`, and `plan_removed` behind `unstable_plan_operations` until the Plan Operations RFD settles. The stable v2 `plan_update` shape does not require a capability. The unstable plan operations remain gated by the draft unstable protocol surface, not by a v2 `plan` field. ### Compatibility When converting a v1 single-plan update to v2, implementations should use the item-based `plan_update` shape with a stable synthetic plan ID of `main`. When converting v2 back to a v1 surface that does not support unstable plan operations, an item-based `plan_update` can be represented as the old v1 `plan` update by dropping the plan ID. Other plan content variants cannot be represented without the unstable v1 plan operations surface. ## Shiny future > How will things play out once this feature exists? The v2 schema has a single extensible place for plan content from the start. ACP can later promote markdown, file-backed, removal, or other plan operations without introducing another incompatible plan update shape. Clients that only understand item-based plans can still preserve or display unknown plan content generically, while extensions get the `_`-prefixed namespace defined by the enum variant extension RFD. ## Implementation details and plan > Tell me more about your implementation. What is your detailed implementation plan? 1. Make `PlanUpdate`, `PlanUpdateContent`, `PlanItems`, and `PlanId` part of the default v2 Rust schema source. 2. Remove `PlanCapabilities` from the v2 Rust schema source. 3. Leave `PlanFile`, `PlanMarkdown`, and `PlanRemoved` behind `unstable_plan_operations`. 4. Remove `SessionUpdate::Plan` from the v2 schema and make `SessionUpdate::PlanUpdate` the default plan variant. 5. Keep the removed `sessionUpdate: "plan"` discriminator rejected so it cannot be accepted as an unknown v2 update. 6. Require `id` on unknown `PlanUpdateContent` fallback payloads. 7. Update v1/v2 conversion helpers so v1 `plan` maps to v2 `plan_update` with `id: "main"`. 8. Regenerate v2 schema docs so the default v2 surface shows only the item-based tagged plan shape. ## Frequently asked questions > What questions have arisen over the course of authoring this document or during subsequent discussions? ### Why not wait for the Plan Operations RFD to complete? The item-based tagged shape is enough to make v2 extensible and remove the old flattened plan variant. The rest of the Plan Operations RFD can continue behind the unstable feature flag. V2 does not need `plan` because those operations are either part of the draft unstable protocol surface or absent from the stable schema. ### Why use `main` for v1 conversion? The v1 shape represents exactly one plan and has no plan ID. A stable synthetic ID lets conversion preserve that single-plan meaning in v2 while keeping the v2 schema explicit about plan identity. ## Revision history 2026-06-02: Initial draft # v2 Prompt Lifecycle Source: https://agentclientprotocol.com/rfds/v2/prompt Author(s): [@benbrandt](https://github.com/benbrandt) ## Elevator pitch > What are you proposing to change? For v2 of the protocol wire format, I am proposing a change in the lifecycle of the prompt request, allowing for more dynamic session updates from the agent, and unlocking new capabilities in the process. Once a session is created, the agent will be able to send session updates at any point in time, and prompt requests will last until the prompt is accepted, not until the end of the turn. As I'll go into later, this not only removes some current awkwardness around the prompt request lifecycle, but also provides a more flexible foundation to add features like queued messages and multi-client replay. This can even allow the agent to initiate an interaction in a session rather than requiring it to wait for a user prompt, which is becoming increasingly important for background tasks and agents which may send updates before or after a "turn" is over since its runtime might be different than the main conversation. ## Status quo > How do things work today and what problems does this cause? Why would we change things? Currently, the protocol kind of assumes that all turns will be initiated by a client and ended by an agent, with a series of session update notifications in-between. While in many cases this is enough, it is becoming clear that this model is not flexible enough. It is not clear how to model queued messages for instance: would these create a new turn request lifecycle? Or fit into the existing one? What if the agent wants to submit some text at the start of a session *before* the user prompts? Or a status update? Also, if an agent finishes its turn, wants to wait for the next user action, but has a background subagent or task running, can it only submit updates about that status after the user prompts again? When replaying a session, the prompt request can be turned into a user message notification, but what about the end of turn response? If you call load during a currently running session, how do you know that the turn is done? Some clients handle these out-of-turn updates more gracefully than others. But it is a constant point of confusion in discussions and issues. In the spirit of allowing as much flexibility in the protocol for new paradigms and designs to emerge in the prompt lifecycle, I think imposing fewer restrictions in the protocol, whether explicitly described or just implicitly inferred because of vague wording, on when participants can make session updates will allow for more dynamic sessions, as well as make it easier to extend to new use cases in the future. ## What we propose to do about it > What are you proposing to improve the situation? ### Change the `session/prompt` response `session/prompt` is still a request, but its response lifecycle will change. The agent will respond once the prompt has been *accepted*, not when the turn is over. Message IDs do not need to be returned from the prompt response; per the [message id RFD](/rfds/message-id), the agent remains the source of truth for message IDs and provides them through the session update stream when it emits the accepted user message. ```json theme={null} { "jsonrpc": "2.0", "id": "req_12345", "result": {} } ``` The completed Message ID RFD keeps message IDs agent-owned. The accepted user message notification becomes the natural point to provide the required v2 message ID. Queueing messages (still an ongoing discussion) would also fit much nicer in this pattern. Either by adding additional parameters or a separate method, we can allow a client to submit queued messages and either cancel or edit them before they get accepted without having to mess with the turn semantics of the previous prompt request approach. Again, queueing is decidedly not part of this RFD, but it seems to fall more naturally into this semantic change in the prompt request. ### Additional Agent `session/update` notification types Because `session/update`s can more freely flow from the agent, and we lost the ability to pass end\_turn and other information from the prompt response, we need to provide the agent with the affordance for a few more notification types. #### User message accepted/acknowledged In order to have a consistent understanding between agent and client on where the user message appears within the session history in relation to other messages, it is important to see when and where the agent has accepted the user message into the feed. This will also be important for queueing messages, depending on how we implement that, so that the client can know if it is still allowed to edit the queued message, or where in the turn order it got inserted. Even without a new queue, which may allow for editing the queued message, it means that the client doesn't necessarily have to send a `session/cancel` before prompting. This would need some exploration, but potentially the agent could decide whether it cancels the current turn and inserts it immediately, or inserts it at the next convenient break point. This should probably still be defined as "as soon as possible" and queueing would enable some later points, but it could still be more graceful than needing to cancel all current tool calls for example, as is required at the moment. The question then turns to what makes up this notification. Which brings us to: **Who owns the user message id?** The [message id RFD](/rfds/message-id) defines that the agent owns message IDs. This means the client cannot eagerly create a protocol message ID for a prompt, but it also means we avoid a shared uniqueness or UUID requirement between clients and agents. By allowing the agent to replay or acknowledge the user message as a session update, we have a natural place for the agent to provide both the content and the ID once it is inserted in the session. Ultimately the agent is responsible for session persistence. If there is only one source for IDs, we can continue to treat them as opaque strings that fit well into each agent's implementation. My current proposal is that this would look like the client sending the following message: ```json theme={null} { "jsonrpc": "2.0", "id": "req_12345", "method": "session/prompt", "params": { "sessionId": "sess_789xyz", "prompt": [ { "type": "text", "text": "What's the capital of France?" } ] } } ``` And the Agent responds with: ```json theme={null} { "jsonrpc": "2.0", "id": "req_12345", "result": {} } ``` And also send the notification: ```json theme={null} { "jsonrpc": "2.0", "method": "session/update", "params": { "sessionId": "sess_789xyz", "update": { "sessionUpdate": "user_message", "messageId": "mess_456def", "content": [ { "type": "text", "text": "What's the capital of France?" } ] } } } ``` The client can then make sure the prompt sits nicely in the feed at the right place, and also allows for multiple clients to be attached to the same session because they would receive a user message that they didn't prompt. The agent-provided `messageId` in that notification gives the client the ID for future message-specific operations. This is a new message type as well. Not a `user_message_chunk` but just a `user_message` that allows for sending the entire message at once. The [Message Updates and Chunks](./message-updates.mdx) RFD defines the corresponding whole-message update and streamed-chunk patterns for user messages, agent messages, and agent thoughts. #### `state_update` notification This would be a notification from the agent to indicate that it's current status has changed, such as the "turn" has ended, carrying information like `stopReason` and `usage` data for that turn. **Running**, to indicate that a turn has begun. Important now that turns aren't tied necessarily to prompts: ```json theme={null} { "jsonrpc": "2.0", "method": "session/update", "params": { "sessionId": "sess_789xyz", "update": { "sessionUpdate": "state_update", "state": "running" } } } ``` **Idle**, whenever the agent is done, with optional data on why: ```json theme={null} { "jsonrpc": "2.0", "method": "session/update", "params": { "sessionId": "sess_789xyz", "update": { "sessionUpdate": "state_update", "state": "idle", "stopReason": "end_turn" } } } ``` **Requires Action**, for when the agent is trying to run, but needs to wait on user input to continue, it isn't just idle: ```json theme={null} { "jsonrpc": "2.0", "method": "session/update", "params": { "sessionId": "sess_789xyz", "update": { "sessionUpdate": "state_update", "state": "requires_action" } } } ``` We could explore adding which permission or elicitation it is waiting on if we wanted to make it clearer if we wanted. ## Shiny future > How will things play out once this feature exists? This isn't a huge schema change, but it is a fundamental behavior change in the protocol that I believe: * Provides agents with much more flexibility in how they want to update a client about a given session * Solves some concrete pain points in the current model (i.e. how to integrate prompts into session replay and multi-client replays, message IDs, etc) Ultimately, after months of using the current model, I am quite excited for what these small tweaks can unlock, and also how we can build on top of this in the future! ## Implementation details and plan > Tell me more about your implementation. What is your detailed implementation plan? Overall, this isn't a huge lift on schema definition, but it is a large, **breaking** change in behavior which means we can only stabilize in protocol version 2. Depending on how the rest of v2 testing goes, we can either: 1. Make this an opt-in "future-flag" capability on v1 so people can experiment, but it would be an unstable feature regardless. 2. We establish a preview/beta flow for v2 We definitely need 2 regardless, and can likely handle this in a similar "unstable" manner as we do for current unstabilized features. It's likely timing will work out that we can just do it that way. If for some reason the timing doesn't work out, we can start experimenting with an unstable capability or some `_meta` flag. ## Frequently asked questions > What questions have arisen over the course of authoring this document or during subsequent discussions? I've hopefully addressed all of the questions and concerns for what motivated this above, but happy to engage with others on this. ### What alternative approaches did you consider, and why did you settle on this one? #### Prompt as a notification Early discussions revolved around having this be a bidirectional stream of notifications on the session. While this felt very symmetrical and appealing, it ran into several problems in practice: 1. Clients only really had one type of notification that made sense to emit on the session: user messages 2. The Agent would still need to replay that message to show where it got accepted within the message history 3. We would then need a notification-based way of emitting errors for invalid prompts that would need to be tied to fire-and-forget notifications. Given that the agent is ultimately the owner of the session history, it makes sense that it is the sole notifier of the session, because it is the source of truth. By having all client interactions on the session remain requests, albeit much shorter-lived ones in theory, we still have a semantically meaningful way to communicate errors and when and how a given request was incorporated into the session. ## Revision history 2026-04-13: Initial draft 2026-04-22: Move from bidirectional notification approach to a change in the prompt request lifecycle # v2 Required Session Methods Source: https://agentclientprotocol.com/rfds/v2/required-session-methods Author(s): [@benbrandt](https://github.com/benbrandt) ## Elevator pitch > What are you proposing to change? For ACP v2, Agents that advertise the `session` capability should be required to support the core session lifecycle methods: `session/list`, `session/resume`, and `session/close`. These methods should no longer have individual v2 capability markers. The presence of `capabilities.session` should be enough for Clients to rely on the baseline session surface. ## Status quo > How do things work today and what problems does this cause? Why would we change things? In v1, several session lifecycle methods were added as optional capabilities: `loadSession`, `sessionCapabilities.list`, `sessionCapabilities.resume`, and `sessionCapabilities.close`. For v2, we can set a cleaner baseline. If an Agent supports sessions, it should support the lifecycle operations needed to create, discover, reconnect, replay, prompt, cancel, update, and close them. In practice, almost all agents, especially the most popular ones, already support these, and we know from users that they don't want to interact without these capabilities. ## What we propose to do about it > What are you proposing to improve the situation? When an Agent includes `capabilities.session` in its v2 `initialize` response, the Agent **MUST** support these baseline session methods: * `session/new` * `session/list` * `session/resume` * `session/close` * `session/prompt` * `session/cancel` * `session/update` The v2 schema should remove the optional `session.list`, `session.resume`, and `session.close` capability fields. The [Session Resume Replay](./session-resume-replay.mdx) RFD removes `session/load` from v2 and folds history replay into `session/resume`. The remaining optional session capabilities continue to represent optional features: * `session.prompt` advertises prompt content extensions beyond the baseline. * `session.mcp` advertises supported MCP server transports. * `session.delete` advertises support for deleting sessions from future `session/list` results. * `session.additionalDirectories` advertises support for extra workspace roots on supported session lifecycle requests. * `session.fork` remains an unstable optional capability while the Session Fork RFD is in progress. ## Shiny future > How will things play out once this feature exists? Clients targeting v2 can treat `capabilities.session` as the single signal for the baseline session surface. This makes session history, reconnection, replay, and cleanup flows simpler to implement and less surprising for users. Agents that do not support sessions can still omit `capabilities.session` entirely, preserving the option for non-session surfaces such as next edit suggestions. ## Implementation details and plan > Tell me more about your implementation. What is your detailed implementation plan? 1. Remove the v2 `session.load`, `session.list`, `session.resume`, and `session.close` capability fields and their generated support-marker types. 2. Update the v2 docs to describe these methods as baseline whenever `capabilities.session` is present. 3. Update the v2 overview RFD and initialization docs to call out the required session lifecycle methods. ## Frequently asked questions > What questions have arisen over the course of authoring this document or during subsequent discussions? ### Does this make sessions required for every v2 Agent? No. `capabilities.session` remains optional. This RFD only defines the baseline methods required once an Agent chooses to advertise the session surface. ### Why is `session/delete` still optional? `session/delete` removes a persisted session from future list results. That is a separate storage-management operation, not part of the minimal lifecycle needed. ## Revision history * 2026-07-02: Initial draft. # v2 Session Resume Replay Source: https://agentclientprotocol.com/rfds/v2/session-resume-replay Author(s): [@benbrandt](https://github.com/benbrandt) ## Elevator pitch > What are you proposing to change? ACP v2 should unify `session/load` and `session/resume` into a single `session/resume` method. Clients that need replay can set a `replayFrom` cursor on `session/resume`; clients that omit `replayFrom` get the current resume behavior without history replay. ## Status quo > How do things work today and what problems does this cause? Why would we change things? In v1, `session/load` and `session/resume` are separate methods. They restore the same existing session context, reconnect MCP servers, and accept the same workspace parameters. The main behavioral difference is replay: * `session/load` replays previous conversation history before responding. * `session/resume` restores the session without replaying previous history. Keeping both methods in v2 preserves a distinction that is better represented as an option on one restore operation. It also forces capability and transport docs to explain two methods when the client is choosing between replay modes. ## What we propose to do about it > What are you proposing to improve the situation? Remove `session/load` from the v2 method surface and keep `session/resume`. `ResumeSessionRequest` gains an optional `replayFrom` field. Omitted `replayFrom` and `replayFrom: null` are equivalent: the Agent resumes without replaying previous conversation history. The initial replay cursor is: ```json theme={null} { "type": "start" } ``` `replayFrom: { "type": "start" }` means the Agent replays the whole conversation through `session/update` notifications before responding to the `session/resume` request. Replay cursors are inclusive: replay includes the position identified by the cursor. Future cursors that identify a message would replay that message before continuing with later history. ## Shiny future > How will things will play out once this feature exists? Clients always reconnect to existing sessions through `session/resume`. They choose replay behavior by setting or omitting `replayFrom`, rather than choosing between two different restore methods. This leaves room for future replay cursors, such as replaying from a specific message, checkpoint, or server-provided cursor, without adding more session restore methods. ## Implementation details and plan > Tell me more about your implementation. What is your detailed implementation plan? 1. Remove `session/load`, `LoadSessionRequest`, and `LoadSessionResponse` from the v2 schema. 2. Add `replayFrom` to `ResumeSessionRequest` as an optional tagged union. 3. Add the initial `replayFrom` variant `{ "type": "start" }`. ## Frequently asked questions > What questions have arisen over the course of authoring this document or during subsequent discussions? ### Is `replayFrom` required? No. The field is optional. Omitted and `null` both mean no history replay. ### Why use an object instead of a string? An object leaves room for future cursor forms that need fields. The initial `start` form has no fields beyond `type`. ## Revision history * 2026-07-02: Initial draft. # v2 Tool Call Updates Source: https://agentclientprotocol.com/rfds/v2/tool-call-updates Author(s): [@benbrandt](https://github.com/benbrandt) ## Elevator pitch > What are you proposing to change? ACP v2 should use a single `sessionUpdate: "tool_call_update"` notification for both creating and updating tool calls. The payload is an upsert keyed by `toolCallId`: if the Client has not seen the ID before, it creates a new displayed tool call; otherwise, it patches the existing tool call. ACP v2 should also add `sessionUpdate: "tool_call_content_chunk"` for streaming tool-call content. Chunks are keyed by `toolCallId` and append one `ToolCallContent` item to the current content for that tool call. ## Status quo > How do things work today and what problems does this cause? Why would we change things? ACP v1 has two related tool-call session updates: ```json theme={null} { "sessionUpdate": "tool_call", "toolCallId": "call_001", "title": "Reading configuration file", "kind": "read", "status": "pending" } ``` ```json theme={null} { "sessionUpdate": "tool_call_update", "toolCallId": "call_001", "status": "completed" } ``` In practice, many agents end up sending just the updates anyway, because the notifications are functionally equivalent. So, many clients already support handling just `tool_call_update` notifications if that is all they receive. There also wasn't a way to handle unsetting values, which we established with `session_info_update`. ## What we propose to do about it > What are you proposing to improve the situation? ACP v2 should remove `sessionUpdate: "tool_call"` and make `sessionUpdate: "tool_call_update"` the only tool-call session update. The v2 `ToolCallUpdate` payload has the following semantics: * `toolCallId` is required. * `title`, `kind`, `status`, `content`, `locations`, `rawInput`, and `rawOutput` are patch fields. * An omitted patch field leaves the previous value unchanged. * `null` explicitly clears or unsets the field. * Any concrete value replaces the previous value. * `content` and `locations` are replaced as whole arrays, not appended. * `[]` and `null` both clear a collection field. * `_meta` preserves omitted, `null`, and object values as distinct update signals. * For a new `toolCallId`, omitted fields use Client defaults. * Agents **SHOULD** include `title` the first time they report a `toolCallId`. The `session/request_permission` method should also carry this same `ToolCallUpdate` shape for tool-call permissions. In v2, [permission requests](./permission-requests.mdx) use an optional `subject` tagged union; tool-call permissions set `subject.type: "tool_call"` and carry the update in the subject's `toolCall` field, so the permission UI receives the same patch/upsert payload shape as session updates while leaving room for future permission subjects. Permission-specific prompt text should be carried by the request's common required `title` and optional `description` fields instead of `ToolCallUpdate.title` or `ToolCallUpdate.content`. That keeps a permission prompt from accidentally replacing displayed tool-call state. ACP v2 should define a matching streaming update: * `tool_call_content_chunk` Each content chunk has the following semantics: * `toolCallId` is required. * `content` is required and contains one `ToolCallContent` item. * Clients append the chunk's `content` item to the current content for that `toolCallId`. * Clients apply `tool_call_update` and `tool_call_content_chunk` notifications in the order they are received for each `toolCallId`. * If a `tool_call_update` includes `content`, that array replaces all content currently stored for the tool call, including content accumulated from earlier chunks. * Later `tool_call_content_chunk` notifications append to the replacement content. * `content: []` or `content: null` on `tool_call_update` clears tool-call content. ## Shiny future > How will things play out once this feature exists? There is a single way to update a tool call, removing lots of confusion from how v1 operated. Agents can stream long-running tool output without repeatedly resending the whole content array, while still retaining a replacement update for replay, correction, and redaction. ## Implementation details and plan > Tell me more about your implementation. What is your detailed implementation plan? 1. Remove the v2 `sessionUpdate: "tool_call"` variant from the schema. 2. Represent patch fields with a three-state type so implementations can distinguish omitted, `null`, and concrete values. 3. Add the v2 `sessionUpdate: "tool_call_content_chunk"` variant with required `toolCallId` and required single `content` item. 4. Document ordering between full replacement content on `tool_call_update` and appended content chunks. ### Compatibility When converting v1 to v2, both v1 `tool_call` and v1 `tool_call_update` should map to v2 `tool_call_update`. When converting v2 back to v1, adapters can represent a v2 update as a v1 `ToolCallUpdate`. This is lossy for some explicit clear operations because v1 update fields cannot represent every distinction between omitted and `null`. Collection clears can be represented as empty arrays, but scalar/raw-field clears may have to be omitted when targeting v1. This will be a best-effort conversion. `tool_call_content_chunk` cannot be represented by the stateless v2-to-v1 conversion helper because v1 tool-call content updates replace the whole content array rather than appending one item. A stateful bridge can choose to accumulate chunks and emit replacement arrays to v1 clients. ## Frequently asked questions > What questions have arisen over the course of authoring this document or during subsequent discussions? ### Why keep the `tool_call_update` name instead of `tool_call`? Most of the other notification names end in "update", and this was by far the most used, so it should help with the migration hopefully. ### Why make `null` different from omission? Tool-call updates need both operations. Omission means "I am not changing this field"; `null` means "clear the value." Without the distinction, Agents cannot unset a previously supplied field without resending a full replacement object. ### Why add chunks instead of making `tool_call_update.content` append? `tool_call_update.content` needs replacement semantics for replay, correction, and redaction. A separate chunk variant gives streaming a distinct append operation without making replacement updates ambiguous. ## Revision history * 2026-07-02: Updated permission requests to carry tool-call updates through an optional `subject` tagged union and to use separate `title` and `description` fields for permission-specific prompt text. * 2026-06-09: Added `tool_call_content_chunk` for streaming tool-call content. * 2026-06-08: Initial draft # Updates Source: https://agentclientprotocol.com/updates Updates and announcements about the Agent Client Protocol This page is for larger ACP announcements and project updates. For lifecycle changes to Requests for Dialog, see [RFD Updates](/rfds/updates). ## Logout Method is Stabilized The Logout Method RFD has moved to Completed and the `logout` method is stabilized. This gives clients a standard way to end an authenticated state and let users authenticate again without restarting the ACP connection. [Read the full announcement](/announcements/logout-method-stabilized). ## Session Close is Stabilized The Session Close RFD has moved to Completed and the `session/close` method is stabilized. This gives clients a way to tell agents to cancel in-flight work for a session and free the resources tied to it without tearing down the whole ACP process. [Read the full announcement](/announcements/session-close-stabilized). ## Session Resume is Stabilized The Session Resume RFD has moved to Completed and the `session/resume` method is stabilized. This gives clients a way to reconnect to an existing session without replaying conversation history, and lets proxies and adapters build `session/load` semantics on top of the simpler primitive. [Read the full announcement](/announcements/session-resume-stabilized). ## Transports Working Group A new Transports Working Group has been formed to standardize remote agent transports like WebSockets and HTTP. Anna Zhdan (JetBrains / Core Maintainer) and Alex Hancock (Block / Goose) are leading the effort, with a Draft RFD already underway. [Read the full announcement](/announcements/transports-working-group). ## ACP Registry is Released The ACP Registry RFD has moved to Completed and the initial version of the registry is released. The registry gives ACP clients a standard way to discover, install, and configure compatible agents without inventing custom integration metadata. [Read the full announcement](/announcements/acp-agent-registry-stabilized). ## Session Info Update is Stabilized The Session Info Update RFD has moved to Completed and the session\_info\_update notification is stabilized. This lets agents push session metadata updates to clients in real time so titles and related metadata stay current without polling. [Read the full announcement](/announcements/session-info-update-stabilized). ## Session List is Stabilized The Session List RFD has moved to Completed and the session/list method is stabilized. This gives clients a standard way to discover existing sessions from an agent for features like history, switching, and cleanup. [Read the full announcement](/announcements/session-list-stabilized). ## Sergey Ignatov joins ACP as Lead Maintainer Sergey Ignatov from JetBrains is now a Lead Maintainer for ACP. This reflects the growing collaboration between Zed and JetBrains around ACP and recognizes Sergey’s significant contributions to the protocol. [Read the full announcement](/announcements/sergey-ignatov-lead-maintainer). ## Session Config Options are now Stabilized The Session Config Options RFD has moved to Completed and is stabilized. This gives agents a flexible way to expose session-level configuration such as models, modes, reasoning levels, and other selectors. [Read the full announcement](/announcements/session-config-options-stabilized). ## Implementation information for agents and clients ACP now allows agents and clients to share implementation details during initialization using the optional clientInfo and agentInfo fields. This makes it easier to identify which implementation is running, improve compatibility diagnostics, and understand adoption across the ecosystem. [Read the full announcement](/announcements/implementation-information).