Skip to content

Commit 75e00be

Browse files
benbrandtclaude
andauthored
refactor(acp)!: reach each protocol version's schema only through v1::schema / v2::schema (#481)
Each protocol version's message types were reachable twice: as `agent_client_protocol::schema::v1::X` and as `agent_client_protocol::v1::schema::X` (and the same for v2). Make the version modules the only path. - `v1::schema` and `v2::schema` now re-export `agent_client_protocol_schema::v1` and `::v2` directly. - `schema::v1` and `schema::v2` are removed. The root `schema` module keeps only the version-neutral items: `ProtocolVersion`, `MaybeUndefined`, `IntoOption`, `IntoMaybeUndefined` and `SuccessorMessage`. - All internal code, tests, examples, the cookbook, doctests and the book use the new paths. Public doc examples import `vN::schema` and write `schema::X`; internal code uses a local `vN::schema as vN` alias where that keeps the diff readable. - The migration guide gains a rename row and drops the text that presented both paths. The stability guidance in lib.rs, the README and the book, and the "Adding a protocol version" checklist, point at `vN::schema`. BREAKING CHANGE: `agent_client_protocol::schema::v1` and `agent_client_protocol::schema::v2` are removed. Use `agent_client_protocol::v1::schema` and `agent_client_protocol::v2::schema` instead. `schema::ProtocolVersion`, `schema::MaybeUndefined`, `schema::IntoOption`, `schema::IntoMaybeUndefined` and `schema::SuccessorMessage` are unchanged. Co-authored-by: Claude <noreply@anthropic.com>
1 parent ccd26a8 commit 75e00be

105 files changed

Lines changed: 528 additions & 523 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎md/design.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ The core SDK. Provides:
1414
- **Versioned connection builders** (`Client.v1()`, `Agent.v2()`, ..., `connect_to()`, `connect_with()`)
1515
- **Per-version modules** (`agent_client_protocol::v1`, and `agent_client_protocol::v2` with `unstable_protocol_v2`) - the same names (`ConnectionTo`, `Builder`, `Responder`, `SessionBuilder`, ...) for each protocol version
1616
- **Message handling** (`on_receive_request`, `on_receive_notification`, `on_receive_dispatch`)
17-
- **Protocol types** (`agent_client_protocol::schema::*`) - all ACP message types
17+
- **Protocol types** (`agent_client_protocol::v1::schema`, and `agent_client_protocol::v2::schema` with `unstable_protocol_v2`) - each protocol version's ACP message types, with the version-neutral items such as `ProtocolVersion` in `agent_client_protocol::schema`
1818
- **Transports and process launching** (`Channel`, `Lines`, `ByteStreams`, `Stdio`, `AcpAgent`)
1919
- **MCP server attachment** - runtime-agnostic interfaces for wiring MCP servers into ACP sessions through the opt-in `unstable_mcp_over_acp` transport
2020

‎md/migration-v5.md‎

Lines changed: 62 additions & 48 deletions
Large diffs are not rendered by default.

‎md/protocol-v2.md‎

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,7 @@ Agent
8383
```
8484

8585
When v2 mode is enabled, application code should use SDK types from
86-
`agent_client_protocol::v2` and message types from `v2::schema`, the same
87-
module as `agent_client_protocol::schema::v2`. Importing
86+
`agent_client_protocol::v2` and message types from `v2::schema`. Importing
8887
`agent_client_protocol::v2::{self, schema}` brings both into scope without a
8988
name clash. The preceding agent fragment demonstrates version negotiation only.
9089
An agent that advertises session support must also implement the complete
@@ -107,7 +106,7 @@ handle and complete operation-specific response.
107106
Low-level custom `with_handler` and `with_runner` implementations continue to
108107
receive the protocol-neutral `ConnectionTo<_>`, and generic `send_request`
109108
remains schema-agnostic. These generic APIs do not infer a protocol version from
110-
the Rust payload type: callers on a v2 connection must use `schema::v2` types,
109+
the Rust payload type: callers on a v2 connection must use `v2::schema` types,
111110
or deliberately send extension or untyped messages. The connection guard
112111
enforces negotiation and initialization lifecycle, not Rust-type provenance.
113112
Dynamic handlers registered through `v2::ConnectionTo::add_dynamic_handler` use
@@ -251,15 +250,14 @@ the agent must reject. It does not rename or drop either server.
251250
non-blocking setup helpers for a v2 proxy:
252251

253252
```rust,ignore
254-
use agent_client_protocol::schema::v2;
255-
use agent_client_protocol::v2::SessionSetup;
253+
use agent_client_protocol::v2::{SessionSetup, schema};
256254
use agent_client_protocol::{Client, Proxy};
257255
258256
Proxy
259257
.v2()
260258
.on_receive_request_from(
261259
Client,
262-
async |request: v2::NewSessionRequest, responder, cx| {
260+
async |request: schema::NewSessionRequest, responder, cx| {
263261
cx.build_session_from(request)
264262
.with_mcp_server(session_server)?
265263
.on_proxy_session_start(responder, async |setup| {
@@ -356,9 +354,9 @@ it, such as a v2-only agent behind a v1-only proxy, rejects the
356354
initialization, and the client sees the error.
357355

358356
Proxy implementations use the schema crate's
359-
`agent_client_protocol::schema::v2::InitializeProxyRequest`. Its response is
360-
`schema::v2::InitializeProxyResponse`, a transparent wrapper around the v2
361-
`InitializeResponse`. `schema::v1::InitializeProxyRequest` is the v1
357+
`agent_client_protocol::v2::schema::InitializeProxyRequest`. Its response is
358+
`v2::schema::InitializeProxyResponse`, a transparent wrapper around the v2
359+
`InitializeResponse`. `v1::schema::InitializeProxyRequest` is the v1
362360
type. Static conductor component providers can carry either selected
363361
schema, but each supplied component must support that version. Use
364362
`Agent.protocol_router()` or `Proxy.protocol_router()` when a static component
@@ -416,15 +414,15 @@ The SDK handles the `initialize` negotiation at the JSON-RPC boundary:
416414
- V2 request ids must be strings or integers. A v2 connection answers a request
417415
with a `null` id with `-32600` and `id: null`. V1 connections still accept
418416
`null` ids.
419-
- Since schema 1.13, v2 has its own `schema::v2::RequestId` without a `Null`
417+
- Since schema 1.13, v2 has its own `v2::schema::RequestId` without a `Null`
420418
variant. Request handles carry a protocol marker as their last type
421419
parameter: `Responder<T, V2>`, `ResponseRouter<T, V2>`,
422420
`SentRequest<T, V2>`, `PreparedRequest<T, V2>` and
423421
`Dispatch<Req, Notif, V2>`, named `v2::Responder<T>`,
424422
`v2::ResponseRouter<T>`, `v2::SentRequest<T>`, `v2::PreparedRequest<T>` and
425423
`v2::Dispatch<Req, Notif>`. Handles from `Client.v2()`, `Agent.v2()`,
426424
`Proxy.v2()` and `v2::ConnectionTo` use `V2`, so their `id()` returns a
427-
`schema::v2::RequestId`, for example to match an elicitation's `requestId`.
425+
`v2::schema::RequestId`, for example to match an elicitation's `requestId`.
428426
It never fails, because the SDK rejects `null` IDs on v2 connections and
429427
never assigns `null` itself. v1 builders use `V1`. Low-level
430428
`HandleDispatchFrom` and `MatchDispatch` handlers are not tied to a version:
@@ -502,17 +500,17 @@ applications built with v2 support to control v2 rollout with a runtime feature
502500
flag:
503501

504502
```rust
505-
use agent_client_protocol::schema::{v1, v2};
503+
use agent_client_protocol::{v1, v2};
506504
use agent_client_protocol::{Agent, ConnectTo};
507505

508-
# fn implementation() -> v2::Implementation {
509-
# v2::Implementation::new("example", "0.1.0")
506+
# fn implementation() -> v2::schema::Implementation {
507+
# v2::schema::Implementation::new("example", "0.1.0")
510508
# }
511509
# async fn serve(client_transport: impl agent_client_protocol::ConnectTo<Agent>) -> agent_client_protocol::Result<()> {
512510
# let enable_protocol_v2 = true;
513511
let v1_agent = Agent.v1().on_receive_request(
514-
async |initialize: v1::InitializeRequest, responder, _cx| {
515-
responder.respond(v1::InitializeResponse::new(initialize.protocol_version))
512+
async |initialize: v1::schema::InitializeRequest, responder, _cx| {
513+
responder.respond(v1::schema::InitializeResponse::new(initialize.protocol_version))
516514
},
517515
agent_client_protocol::on_receive_request!(),
518516
);
@@ -521,8 +519,8 @@ let agent = Agent.protocol_router().with_v1(v1_agent);
521519

522520
let agent = if enable_protocol_v2 {
523521
let v2_agent = Agent.v2().on_receive_request(
524-
async |initialize: v2::InitializeRequest, responder, _cx| {
525-
responder.respond(v2::InitializeResponse::new(
522+
async |initialize: v2::schema::InitializeRequest, responder, _cx| {
523+
responder.respond(v2::schema::InitializeResponse::new(
526524
initialize.protocol_version,
527525
implementation(),
528526
))
@@ -576,7 +574,7 @@ arrive before initialization are ignored. The proxy protocol router, whose
576574
with `-32022` but closes after a rejection.
577575

578576
Custom endpoints can construct the same wire error with
579-
`schema::v2::Error::unsupported_protocol_version(requested, supported).into()`.
577+
`v2::schema::Error::unsupported_protocol_version(requested, supported).into()`.
580578
The code is defined by v2 negotiation, not by a change to the v1 protocol.
581579
The SDK's version-neutral `Error` carries any numeric code, so a v1 client
582580
receives the initialization error as an unknown code without new v1 schema
@@ -739,7 +737,7 @@ removes keys whose values are `null`, and replaces each other value whole.
739737
Nested objects are not merged; nested nulls are ordinary data. An empty object
740738
does not clear metadata. Replay uses the same rules and never implicitly resets
741739
metadata. `ToolCallUpdate::apply_update` and `TerminalUpdate::apply_update`
742-
apply this rule; use `schema::v2::apply_meta_update` for application-owned
740+
apply this rule; use `v2::schema::apply_meta_update` for application-owned
743741
message, session, compaction, or subagent projections. Metadata on chunks,
744742
content, snapshots, and `plan_update` is not an entity patch.
745743

@@ -836,7 +834,7 @@ Client projections should also follow these rules:
836834

837835
Draft v2 type changes:
838836

839-
- JSON-RPC batching is removed from v2, and `schema::v2::JsonRpcBatch` no
837+
- JSON-RPC batching is removed from v2, and `v2::schema::JsonRpcBatch` no
840838
longer exists. The SDK dropped batch support in both protocol versions:
841839
every connection answers a JSON array with a single `-32600` error with
842840
`id: null` (see [JSON-RPC batches](#json-rpc-batches)).

‎md/protocol.md‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,20 @@ Use the version's `SystemPrompt::new` builder and pass the complete request
2222
through the SDK's `_from` session helpers:
2323

2424
```rust
25-
use agent_client_protocol::schema::v1;
25+
use agent_client_protocol::v1::schema;
2626

27-
let instructions = v1::SystemPrompt::new(vec![
27+
let instructions = schema::SystemPrompt::new(vec![
2828
"Review changes without modifying files.".into(),
2929
]);
30-
let request = v1::NewSessionRequest::new("/workspace/project")
30+
let request = schema::NewSessionRequest::new("/workspace/project")
3131
.system_prompt(instructions);
3232
// After checking the agent's advertised capability:
3333
// connection.build_session_from(request).block_task().start_session().await?;
3434
# let _ = request;
3535
```
3636

3737
This snippet requires `unstable_session_system_prompt`; v2 additionally
38-
requires `unstable_protocol_v2` and uses the equivalent `v2` types.
38+
requires `unstable_protocol_v2` and uses the equivalent `v2::schema` types.
3939
`SystemPrompt::new` defaults to `append` in Rust, but both `mode` and `content`
4040
are required on the wire. Omitted or `null` `systemPrompt` supplies no client
4141
instructions. Malformed values fail typed request decoding with `-32602`;
@@ -87,8 +87,8 @@ component that it is running as a proxy and may forward messages with
8787

8888
The response is the matching version's normal `InitializeResponse` result. The
8989
SDK uses the schema crate's types, one per version:
90-
`schema::v1::InitializeProxyRequest` and, with `unstable_protocol_v2`,
91-
`schema::v2::InitializeProxyRequest`. Each is answered with that version's
90+
`v1::schema::InitializeProxyRequest` and, with `unstable_protocol_v2`,
91+
`v2::schema::InitializeProxyRequest`. Each is answered with that version's
9292
`InitializeProxyResponse`, a transparent wrapper around its
9393
`InitializeResponse`. Convert a forwarded `initialize` response with `.into()`.
9494
The final agent receives the ordinary `initialize` method and does not need to
@@ -123,8 +123,8 @@ next to `method` and `params`; the inner message keeps its own `_meta` inside
123123
its `params`.
124124

125125
The schema types for the envelope are `ProxySuccessorRequest`,
126-
`ProxySuccessorNotification` and `ProxySuccessorResponse` in `schema::v1` and
127-
`schema::v2`, with the `ProxyRequest`, `ProxyResponse` and `ProxyNotification`
126+
`ProxySuccessorNotification` and `ProxySuccessorResponse` in `v1::schema` and
127+
`v2::schema`, with the `ProxyRequest`, `ProxyResponse` and `ProxyNotification`
128128
unions and the method names in `PROXY_METHOD_NAMES`. They hold the inner
129129
message as raw JSON. `schema::SuccessorMessage<M>` is the SDK's typed view of
130130
the same envelope: it holds the inner message as an SDK message type, works for

‎md/versioning.md‎

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,11 @@ are private. Work through this list, using v2 as the template. Search for
5858
`ProtocolVersion::V2` and `unstable_protocol_v2` to find each place.
5959

6060
1. **Schema.** Bump the `agent-client-protocol-schema` pin to a release with
61-
the new version's types. Add `schema::v3` in
62-
`src/agent-client-protocol/src/schema/mod.rs`, with the JSON-RPC trait
63-
implementations for its messages (see `v2_impls.rs`) and its
64-
`InitializeProxyRequest`.
61+
the new version's types. Implement the JSON-RPC traits for its messages
62+
in `src/agent-client-protocol/src/schema/` (see `v2_impls.rs`), including
63+
its proxy-chain types (`impl_proxy_jsonrpc!` in `proxy_protocol.rs`). The
64+
messages are re-exported as `v3::schema` in step 4, the only public path
65+
to them.
6566
2. **Feature gate.** Add `unstable_protocol_v3` to
6667
`src/agent-client-protocol/Cargo.toml`, forwarding to the schema crate's
6768
feature, and to the crates that need a passthrough (the polyfill and the
@@ -72,7 +73,8 @@ are private. Work through this list, using v2 as the template. Search for
7273
conversions.
7374
4. **Connection and module.** Add `jsonrpc/v3_connection.rs` and
7475
`src/agent-client-protocol/src/v3.rs`, re-exporting the shared names
75-
(`ConnectionTo`, `Builder`, the request handle aliases, `schema`) and the
76+
(`ConnectionTo`, `Builder`, the request handle aliases, and
77+
`pub use agent_client_protocol_schema::v3 as schema;`) and the
7678
version's session API. Add `Client.v3()`, `Agent.v3()` and `Proxy.v3()`.
7779
5. **Negotiation.** Teach the wire guard in `jsonrpc/protocol_compat.rs` the
7880
new version: selecting it from `initialize` and `proxy/initialize`,

‎src/agent-client-protocol-conductor/src/conductor.rs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,7 @@
137137
use std::sync::Arc;
138138

139139
#[cfg(feature = "unstable_protocol_v2")]
140-
use agent_client_protocol::schema::v2;
141-
use agent_client_protocol::schema::{ProtocolVersion, v1};
140+
use agent_client_protocol::v2::schema as v2;
142141
use agent_client_protocol::{
143142
Agent, BoxFuture, Client, Conductor, ConnectTo, Dispatch, DynConnectTo, Error, JsonRpcMessage,
144143
Proxy, Responder, Role, RunWithConnectionTo, UntypedMessage, role::HasPeer,
@@ -149,6 +148,7 @@ use agent_client_protocol::{
149148
};
150149
use agent_client_protocol::{HandleDispatchFrom, util::MatchDispatchFrom};
151150
use agent_client_protocol::{Handled, schema::SuccessorMessage};
151+
use agent_client_protocol::{schema::ProtocolVersion, v1::schema as v1};
152152
use futures::{
153153
SinkExt, StreamExt,
154154
channel::mpsc::{self},

‎src/agent-client-protocol-conductor/src/trace.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use std::path::Path;
1010
use std::time::Instant;
1111

1212
use agent_client_protocol::schema::SuccessorMessage;
13-
use agent_client_protocol::schema::v1::{
13+
use agent_client_protocol::v1::schema::{
1414
MessageMcpNotification, MessageMcpRequest, MessageMcpResponse,
1515
};
1616
use agent_client_protocol::{

‎src/agent-client-protocol-conductor/tests/initialization_sequence.rs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
//! 2. Multi-component chains: proxies receive `v1::InitializeProxyRequest`
66
//! 3. Last component (agent) receives `InitializeRequest`
77
8-
use agent_client_protocol::schema::v1::{AgentCapabilities, InitializeRequest, InitializeResponse};
9-
use agent_client_protocol::schema::{ProtocolVersion, v1};
8+
use agent_client_protocol::v1::schema::{AgentCapabilities, InitializeRequest, InitializeResponse};
109
use agent_client_protocol::{Agent, Client, Conductor, ConnectTo, DynConnectTo, Proxy};
10+
use agent_client_protocol::{schema::ProtocolVersion, v1::schema as v1};
1111
use agent_client_protocol_conductor::{ConductorImpl, ProxiesAndAgent};
1212
use agent_client_protocol_test::testy::v1::Testy;
1313
use std::sync::Arc;

‎src/agent-client-protocol-conductor/tests/initialization_v2.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ use std::sync::{
55
atomic::{AtomicUsize, Ordering},
66
};
77

8-
use agent_client_protocol::schema::{ProtocolVersion, v1, v2};
98
use agent_client_protocol::{
109
Agent, Builder, ByteStreams, Client, Conductor, ConnectTo, ConnectionTo, DynConnectTo, Error,
1110
JsonRpcRequest, JsonRpcResponse, Proxy, UntypedMessage,
1211
};
12+
use agent_client_protocol::{schema::ProtocolVersion, v1::schema as v1, v2::schema as v2};
1313
use agent_client_protocol_conductor::{
1414
ConductorImpl, InstantiateProxies, InstantiateProxiesAndAgent, InstantiatedChain,
1515
InstantiatedProxies, ProxiesAndAgent, SelectedInitializeRequest,

‎src/agent-client-protocol-conductor/tests/mcp-integration.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ mod mcp_integration;
99

1010
use agent_client_protocol::Agent;
1111
use agent_client_protocol::schema::ProtocolVersion;
12-
use agent_client_protocol::schema::v1::{
12+
use agent_client_protocol::v1::schema::{
1313
ContentBlock, InitializeRequest, NewSessionRequest, PromptRequest, SessionNotification,
1414
TextContent,
1515
};

0 commit comments

Comments
 (0)