-
Notifications
You must be signed in to change notification settings - Fork 123
add proxy chains RFD #243
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add proxy chains RFD #243
Conversation
nikomatsakis
commented
Nov 15, 2025
- Proposes extending ACP with proxy chain capabilities for composable agent architectures
- Defines conductor/proxy roles and initialization protocols
- Includes MCP-over-ACP transport for seamless tool integration
- Provides complete protocol specification with examples and FAQ
- Enables universal extension mechanism that subsumes existing approaches
|
One thing I am noticing as I work with this system: It would be really nice if the MCP-over-ACP protocol gave you the ability to correlate an MCP request with an ACP session-id that it originates from. I'm working on some research agent proxies that would benefit from this because they want to remember some details from the originating session to know how to react. |
benbrandt
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright a few questions, but overall I'm quite excited about what this unlocks!
docs/rfds/proxy-chains.mdx
Outdated
|
|
||
| ### Capability Reference | ||
|
|
||
| **Proxy Capability** (`"proxy": true`) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to be clear:
- a Client offers this as a capability to indicate to the agent it should run in proxy mode
- an Agent offers this as a capability if it can work as a proxy and agrees to do so?
You cover this later, and if I understand correctly, the only ones who use this capability are components of the conductor<->proxy connection, it shouldn't be used elsewhere? Like basically a registration with a conductor at some level in the chain?
I guess I am trying to better understand which components would set this to "true" in the chain and which would not need to or should not?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it's a bit tricky. The way I did it is like this: the conductor (and only the conductor) offers this capability to the "proxy" components (those that have a successor). It indicates to them that they can send messages to their successor. Then they respond with this capability to indicate that they are capable of being used in proxy mode.
This way, if a "terminal agent" like claude-code-acp is used in proxy position, the conductor will return an error, since that makes no sense.
On the other hand, if an agent is designed to be used as a proxy, it can error if it does NOT receive the proxy capability.
Some agents can operate either as a terminal agent OR a proxy (including the conductor itself), so they check for whether they've been offered the proxy capability to decide how they are operating.
docs/rfds/proxy-chains.mdx
Outdated
|
|
||
| - **Conductor ↔ proxy initialization**: `InitializeRequest` contains `"proxy": true`. The server MUST respond with `"proxy": true` in the `InitializeResponse` to acknowledge it will act as a proxy. The conductor MUST error and terminate if the server responds without `"proxy": true`. | ||
|
|
||
| - **Proxy ↔ successor agent**: When a proxy receives an `InitializeRequest`, it MUST remove the `"proxy"` capability before forwarding via `proxy/successor/request`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note for later down the line: it would be great at the SDK level to have helpers for this at some level to make it easier not to forget this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I think I baked this into the SDK actually so that agents don't have to do anything in particular.
| - It is only legal to include in a server's initialization response if it was offered by the client. In that case, it indicates that the server agrees to act as a proxy, not as an agent. | ||
| - When a component responds with proxy capability, it SHOULD forward requests it does not understand and it SHOULD preserve metadata fields when forwarding messages. | ||
|
|
||
| **MCP-over-ACP Transport** (`"mcp_acp_transport": true`) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor thing, but i wonder if there is a way to merge this with the existing MCP capabilities? https://agentclientprotocol.com/protocol/initialization#mcp-capabilities
We currently only have it on the server side. I could see that if a client supplies an mcp server of type "acp" transport it is an implicit indication that it would support it? And then the client should only send one of the agent responds withe an acp capability here?
It might be too implicit, but could work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could work, yes. It'd be nice not to have to separate out these things to be honest. I used to have just one (proxy + acp-over-mcp), but I separate them to allow for agents that can natively use mcp-over-acp.
docs/rfds/proxy-chains.mdx
Outdated
|
|
||
| ### Why do proxies remove the proxy/MCP capabilities when forwarding to their successor? | ||
|
|
||
| The conductor would always re-add these capabilities anyway, which shows that they are really specific to conductor↔proxy communication and so we decided to simply omit them. This makes it clear that these capabilities are part of the conductor's orchestration protocol rather than something proxies need to understand or manage. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One thought I had: is the relationship between conductor and proxy distinct enough to require it's own initialize method?
That being said, it is possible these things want to modify the initialize call to the eventual agent, so we should probably leave as is.
The next question is: should this be a capability? Or a top-level param?
Kind of thinking out loud, I guess there's just something about this that isn't just "I can act as a proxy" and more of a flag of "we must have a proxy relationship" that is hanging me up currently.
I actually think what you have proposed is ultimately fine, but there's just something here that makes me want to reflect this differently than a capability... but I am not sure if that instinct is correct or if I am overthinking it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. I agree it doesn't quite feel right to me. Maybe a method like initialize/proxy makes sense instead? It's not really a capability, as you say, and it winds up requiring some "finnicky" stuff to act like one.
|
|
||
| ### 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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this actually helps me figure out a better home for some unexplored design space of how best to handle IDE->agent functionality.
Like if we wanted to have a "standardized" way to offer IDE capabilities to an agent, like surfacing diagnostics, or even the current file system + terminal APIs, these could potentially move to "proxy" space potentially (would need to sort out how the specific proxy would talk back to what is essentially the "client")
But it would allow for a lot of experimentation on the best way to expose this without muddying the main protocol (especially since there is more demand for non-IDE clients) while still providing a lot more control and power than just offering an MCP server from the client
|
I wonder if, instead of baking proxies into the protocol, we should instead augment the protocol to make proxies possible (in case it isn't already). For example, HTTP does not have proxy specific messages, instead the proxy is a natural extension of the protocol. FWIW, we have already implemented a websockets-to-io ACP proxy, and besides MCP-over-ACP (which we also had to emulate), the proxy works just fine. So I wonder if the benefits here could be implemented by cascading ACP commands: The reason I bring this up is because extensions to ACP will push complexity to either client or agents (or both), and given ACP is a M:N protocol, any extension we push needs to be implemented either M, N, or M+N times. Unless I am misunderstanding the proposal. :)
Yes, this is a must have, as you can have multiple ACP sessions over the same channel. |
gkgoat1
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My idea from the discussion
|
|
||
| When proxing a `session/new` request, proxies can add MCP servers using a new transport type, `"acp"`. When agents invoke an MCP server that uses `"acp"` transport, the MCP requests are sent through the ACP channel. (To accommodate existing agents, our conductor will automatically bridge MCP servers using `"acp"` transport to use `"stdio"` transport.) | ||
|
|
||
| Leveraging `"acp"` transport allows a single ACP proxy to do all of the following: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO MCP proxying should be separate from the whole proxy infrastructure as it could be used by editors like VSCode with natively-integrated MCP servers and agents to supply tools in a sandbox.
ACP cascades seem like a simpler option IMO as there would be no controller (what even would it bring to the table?) imo.
+1, this seems like a lot of complexity everywhere
+1, MCP-over-ACP can reduce the needed permissions of agents as well. |
|
wonder if this can be used to also create re-usable components for downloading, installing, updating, enabling out-of-protocol start up options / workarounds (add extra root folders for example). A commander that supported this would probably need its own API for such pre-ACP operations... probably too much stuff to proxy behind a basic Initializaiton request. But there is probably a lot of redundant efforts around managing these setup activities. |
|
Hi all, an update: I've been iterating on the prototype implementation. I have a few updates to make to the specification but you can see it embodied in the sacp Rust crate. It is working very well and the Rust SDK now supports a number of interesting use cases, including ephermal MCP servers that have access to program state (see patchwork-rs). I've made a few changes relative to what is written in this RFD at the moment:
To respond to some of the points raised on this thread: Do we really need the conductor?
I initially had a design where an ACP proxy was an ACP agent that internally created its delegatee. However, I found that to have a critical flaw, which was that it required each proxy to understand the full chain and know how to start them, which shouldn't really be their concern: you want to be able to centrally upgrade the kinds of ACP proxies and agents and/or swap out the transport protocols without the proxy having to be recompiled. Under the current design, a proxy is blissfully ignorant of all those details. This in turn allows for the end-goal I am working towards, which is that proxies can be shipped as "low-capability" WASM containers that run in sandboxes. I could imagine a variant of this design in which the proxy "creates" its successor via a message to the conductor, but honestly, that happens already when the proxy sends the "initialize" method up to the conductor. How would we scale to more agents and things?The proposal as written just has a notion of a single successor, but it would be quite simple I think to scale this up to have more than just two. We would simply extend the Perhaps a good first step would be to have the response from I would rather not specify how that works without working from concerete use cases; my expectation is that we would actually want to do something where, as part of initialization, we can send follow-up requests to the conductor requesting that it "create a peer". I could imagine doing that now to "create the successor". |
|
Hi all-- I pushed some updates from the work I've done. First, I updated to use a distinct In terms of larger feedback, I added a FAQ to explain why I think proxies deserve to be first-class in the protocol, "Why do all messages go through the conductor instead of direct proxy-to-proxy communication?. The TL;DR is that I want to be able to centralize and consolidate the lifecycle work of spawning successors and so forth; I don't want proxies to be responsible for knowing how to do that. I also wrote up a FAQ explaining how we could support this work to cover multi-agent scenarios in the future, rather than just a single proxy. But writing that last FAQ raised up a question that has been bugging me for some time, which is: Why are we distinguishing MCP-over-ACP and proxies anyway? On the one hand, they are conceptually distinct features, and I was pondering splitting up the RFD into two RFDs, one for MCP-over-ACP and one for proxies. On the other hand, the MCP-over-ACP protocl and implementation is basically the same as ACP proxying, but more general, because it's already covering the "multi-agent" scenario. So I wrote up a FAQ explaining why I have specialized MCP-over-ACP and proxies into distinct things. The TL;DR is that if proxies could "start" their successor, that gives them a lot of generality (i.e., they can also disconnect, presumably, or start multiple successors) and I'm not sure we want to do that yet. I have to say though I am not 100% convinced by this argument. It wouldn't be all that hard to modify the conductor to handle the more general case, and it would be interesting. Basically you'd just have a I'm curious what people think about that question. |
- Proposes extending ACP with proxy chain capabilities for composable agent architectures - Defines conductor/proxy roles and initialization protocols - Includes MCP-over-ACP transport for seamless tool integration - Provides complete protocol specification with examples and FAQ - Enables universal extension mechanism that subsumes existing approaches
Addresses feedback from PR discussion about whether protocol-level proxy support is needed vs simple ACP command cascading. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Changes initialization protocol from capability negotiation to distinct methods: proxies receive `proxy/initialize`, agents receive `initialize`. This makes component roles explicit from the method name rather than requiring a capability handshake. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replaces separate request/notification methods with unified methods where the message type is determined by presence of id field: - proxy/successor/message: proxy sends to successor - proxy/successor/receive: conductor delivers from successor 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Explains how proxies correlate MCP requests with sessions by using fresh ACP-IDs instead of session-ids, avoiding a deadlock where agents don't return session-id until after MCP initialization. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Consolidate proxy/successor/message and proxy/successor/receive to single bidirectional proxy/successor method - Update MCP message format to match implementation (mcp/connect, mcp/message, mcp/disconnect with connectionId) - Add IDE capabilities section to Shiny Future - Add FAQ on M:N multi-agent topologies with optional peer field - Add FAQ explaining MCP vs proxy message separation (lifecycle semantics) - Fix message format documentation to show flattened inner message structure 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
So, in accordance with the ACP process, I'm going to merge this as a draft RFD with myself as champion. I appreciate the feedback from folks on the thread so far -- I'd love to continue the conversation in the Zulip thread. |