Skip to content

Commit 851f3a7

Browse files
authored
feat(unstable-v2): add v2 resume session builder (#329)
1 parent 5368c28 commit 851f3a7

21 files changed

Lines changed: 1380 additions & 229 deletions

File tree

‎README.md‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,11 @@ proxy attachment. Draft protocol v2 supports the same two attachment scopes
2727
when both `unstable_protocol_v2` and `unstable_mcp_over_acp` are enabled:
2828
`Proxy.v2().with_mcp_server(...)` injects a global declaration into each
2929
supported session setup request, while
30-
`V2SessionBuilder::with_mcp_server(...)` attaches a server to one new session.
31-
Successful v2 attachments remain active for the connection lifetime, and
32-
`V2SessionBuilder::on_proxy_session_start` forwards a proxied setup response
33-
without coupling later session events to that response.
30+
`V2SessionBuilder::with_mcp_server(...)` attaches a server to one new session
31+
and `V2ResumeSessionBuilder::with_mcp_server(...)` attaches one while resuming.
32+
Successful v2 attachments remain active for the connection lifetime, and both
33+
builders expose `on_proxy_session_start` to forward proxied setup without
34+
coupling later session events to that response.
3435

3536
**Proxy orchestration**
3637

‎md/mcp-bridge.md‎

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@ agent-client-protocol-polyfill = { version = "...", features = ["unstable_protoc
3131
The feature makes this concrete compatibility proxy recognize v2
3232
initialization, capability, session setup, and `mcp/*` wire types. It does not
3333
change the core attachment API. `Proxy.v2().with_mcp_server(...)` provides
34-
connection-global attachment, and `V2SessionBuilder::with_mcp_server(...)`
35-
provides per-new-session attachment. The polyfill adapts their native
34+
connection-global attachment. `V2SessionBuilder::with_mcp_server(...)` and
35+
`V2ResumeSessionBuilder::with_mcp_server(...)` provide per-session attachment
36+
for new and resumed sessions respectively. The polyfill adapts their native
3637
declarations when the final agent supports only HTTP MCP.
3738

3839
## Placement
@@ -57,9 +58,10 @@ The application proxy can attach a high-level
5758
`agent_client_protocol::mcp_server::McpServer`. The SDK advertises it in session
5859
setup requests as `McpServer::Acp`; callers do not need to construct a transport
5960
placeholder themselves. In v2, `Proxy.v2().with_mcp_server(...)` provides
60-
connection-global attachment and `V2SessionBuilder::with_mcp_server(...)`
61-
provides per-new-session attachment. The polyfill translates those native
62-
declarations at the final compatibility boundary.
61+
connection-global attachment. `V2SessionBuilder::with_mcp_server(...)` and
62+
`V2ResumeSessionBuilder::with_mcp_server(...)` provide per-session attachment
63+
for new and resumed sessions respectively. The polyfill translates those
64+
native declarations at the final compatibility boundary.
6365

6466
During initialization, the polyfill forwards the request to its successor. When
6567
the successor advertises HTTP MCP support, the polyfill advertises native ACP

‎md/migration_v2.0.md‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -273,10 +273,12 @@ tables:
273273
Code using `Builder::with_mcp_server` or `SessionBuilder::with_mcp_server` continues to attach the
274274
high-level server in the same place; the emitted declaration and wire methods change. Global
275275
builder attachment advertises the same server ID on `session/new`, `session/load`,
276-
`session/resume`, and feature-gated `session/fork`. Per-session attachment remains specific to
277-
`session/new`. Do not construct an HTTP server with an `acp:` URL. If the final agent accepts HTTP
278-
but not native ACP MCP servers, insert `McpOverAcpPolyfill` immediately before it. The polyfill now
279-
consumes native `McpServer::Acp` declarations and adapts only its final-agent-facing side.
276+
`session/resume`, and feature-gated `session/fork`. Stable v1 per-session attachment remains
277+
specific to `session/new`; draft v2 additionally supports per-session resume attachment through
278+
`V2ResumeSessionBuilder::with_mcp_server`. Do not construct an HTTP server with an `acp:` URL. If
279+
the final agent accepts HTTP but not native ACP MCP servers, insert `McpOverAcpPolyfill`
280+
immediately before it. The polyfill now consumes native `McpServer::Acp` declarations and adapts
281+
only its final-agent-facing side.
280282

281283
The polyfill's public `BridgeMode` enum and `McpOverAcpPolyfill::stdio` were removed because the
282284
required conductor `mcp` helper subcommand no longer exists. The polyfill has one supported mode;

‎md/protocol-v2.md‎

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,10 @@ Stable callbacks receive `ConnectionTo<_>` and expose the protocol v1
8383
Callbacks installed through `Client.v2()` receive `V2ConnectionTo<_>` and expose
8484
the v2 `build_session*` and `resume_session*` helpers. The shared names describe
8585
the same lifecycle operations while the connection type selects their schema
86-
and return types at compile time.
86+
and return types at compile time. The `resume_session*` helpers return a
87+
`V2ResumeSessionBuilder`; call `start_session` to publish the request and obtain
88+
an `OpenedV2Session` containing the command handle and complete
89+
`ResumeSessionResponse`.
8790

8891
Low-level custom `with_handler` and `with_runner` implementations continue to
8992
receive the protocol-neutral `ConnectionTo<_>`, and generic `send_request`
@@ -173,6 +176,10 @@ V2 deliberately separates prompt submission from session observation:
173176
Its `OpenedV2Session` result keeps the command handle separate from the
174177
complete `NewSessionResponse` represented by the linked schema, rather than
175178
reconstructing a selected subset of its fields.
179+
- `V2ConnectionTo::resume_session` and `resume_session_from` return a
180+
`V2ResumeSessionBuilder`. Its `start_session` method publishes
181+
`session/resume` and returns an `OpenedV2Session` containing the complete
182+
`ResumeSessionResponse` without reconstructing it.
176183
- `V2Session` is a cloneable command handle containing only the session ID and
177184
connection. It does not own, buffer, or unregister inbound messages.
178185
- Register typed `UpdateSessionNotification` and `RequestPermissionRequest`
@@ -201,20 +208,23 @@ V2 deliberately separates prompt submission from session observation:
201208
cached on the command handle.
202209

203210
Install connection handlers before `session/new` and `session/resume` requests.
204-
This is especially important for `resume_session_from`: replay updates
205-
precede the resume response on the wire, so preinstalled typed handlers observe
206-
them in order. If a handler forwards updates to another task, the application
207-
is responsible for any additional projection-drained barrier it needs before
208-
treating replay as locally applied.
211+
This is especially important before calling `start_session` on a
212+
`V2ResumeSessionBuilder`: replay updates precede the resume response on the
213+
wire, so preinstalled typed handlers observe them in order. If a handler
214+
forwards updates to another task, the application is responsible for any
215+
additional projection-drained barrier it needs before treating replay as
216+
locally applied.
209217

210218
Dropping command handles has no network or inbound-routing side effect. For a
211-
session created with `V2SessionBuilder::with_mcp_server`, the SDK installs the
212-
MCP routes and initially polls their runner tasks before publishing
213-
`session/new`, so the agent can connect to those servers during session setup.
214-
Runners may continue asynchronous initialization; custom connectors must be
215-
able to queue connections and messages once constructed. A successful setup
216-
promotes the attachment to the connection lifetime; any setup failure cleans it
217-
up. This attachment requires both `unstable_protocol_v2` and
219+
session configured with `V2SessionBuilder::with_mcp_server` or
220+
`V2ResumeSessionBuilder::with_mcp_server`, the SDK installs the MCP routes and
221+
initially polls their runner tasks before publishing `session/new` or
222+
`session/resume`, so the agent can connect to those servers during setup or
223+
resume replay. Runners may continue asynchronous initialization; custom
224+
connectors must be able to queue connections and messages once constructed. A
225+
successful setup promotes the attachment to the connection lifetime; a setup
226+
failure, including an error response after cancellation, cleans up the pending
227+
attachment. This attachment requires both `unstable_protocol_v2` and
218228
`unstable_mcp_over_acp`.
219229

220230
A v2 proxy can instead attach one server globally with
@@ -223,8 +233,9 @@ server ID and adds its declaration to v2 `session/new`, `session/resume`, and
223233
feature-gated `session/fork` requests. It modifies only the `mcpServers` field,
224234
preserving unrelated setup fields and extensions for downstream handlers.
225235

226-
`V2SessionBuilder::on_proxy_session_start` is the non-blocking setup helper for
227-
a v2 proxy:
236+
`V2SessionBuilder::on_proxy_session_start` and
237+
`V2ResumeSessionBuilder::on_proxy_session_start` are the non-blocking setup
238+
helpers for a v2 proxy:
228239

229240
```rust,ignore
230241
use agent_client_protocol::schema::v2;
@@ -247,13 +258,21 @@ Proxy
247258
);
248259
```
249260

250-
The helper forwards request cancellation, sends an ordered downstream
251-
`session/new`, installs session routing before later inbound traffic is
252-
dispatched, and forwards the complete `NewSessionResponse`. It then spawns the
253-
callback outside the ordering barrier with an `OpenedV2Session` containing the
254-
command-only session handle and complete setup response. Updates and
255-
interactive requests remain independent connection traffic and should still
256-
be handled by typed callbacks on `Proxy.v2()`.
261+
Both helpers forward request cancellation, send an ordered downstream setup
262+
request, and forward the complete operation-specific response without
263+
reconstruction. For `session/new`, routing is installed when its response makes
264+
the new session ID available and before later inbound traffic is dispatched.
265+
For `session/resume`, the ID is already known, so routing and any per-session
266+
MCP attachment are ready before the downstream request is published. Replay
267+
updates can therefore be forwarded upstream before the complete
268+
`ResumeSessionResponse`, as required by the protocol. A failed or cancelled
269+
downstream response drops pending routing and MCP attachment; successful setup
270+
keeps them for the connection lifetime. A cancellation signal itself remains
271+
advisory: it is forwarded downstream while the helper awaits that response.
272+
The helper then spawns the callback outside the ordering barrier with an
273+
`OpenedV2Session` containing the command-only session handle and complete setup
274+
response. Updates and interactive requests remain independent connection
275+
traffic and should still be handled by typed callbacks on `Proxy.v2()`.
257276

258277
If an application wants stream ergonomics, it can fan typed updates out from
259278
the connection handler with an explicit buffering and subscriber policy.

0 commit comments

Comments
 (0)