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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ without coupling later session events to that response.
[Protocol V2](./md/protocol-v2.md).

`Client.builder()`, `Agent.builder()`, and `Proxy.builder()` remain stable-v1
entry points; their `.v2()` counterparts select the draft-v2 API. Raw proxy
routing infrastructure that selects and validates a version itself can use
`without_acp_version_guard`, but ordinary v2 proxy implementations should use
`Proxy.v2()`.
entry points; their `.v2()` counterparts select the draft-v2 API. With
`unstable_protocol_v2`, use `Proxy.protocol_router()` to expose separate strict
v1 and v2 proxy implementations as one component. Custom raw routing
infrastructure can use `without_acp_version_guard`.

## Integrations

Expand Down
7 changes: 5 additions & 2 deletions md/conductor.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,12 @@ extension fields from being interpreted as a permissive v1 request and dropped.
An exact-version request whose typed value is unchanged keeps its original raw
parameters, including unknown extensions. A request for a later compatible
protocol version selects v2 and is canonicalized through the selected v2
schema, matching the core protocol router.
schema.
The command-line component provider, `AgentOnly`, `ProxiesAndAgent`, and static
proxy vectors accept both versions. Custom instantiators can implement the
proxy vectors can carry either selected schema, but each supplied component
must support that version. Use `Agent.protocol_router()` or
`Proxy.protocol_router()` when a static component has separate implementations.
Custom instantiators can implement the
feature-gated `instantiate_v2_proxies_and_agent` or `instantiate_v2_proxies`
method; their default implementation rejects v2 with a JSON-RPC response and
leaves the connection in a failed state that rejects later traffic. A modified
Expand Down
15 changes: 9 additions & 6 deletions md/mcp-bridge.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ agent-client-protocol-polyfill = { version = "...", features = ["unstable_protoc

The feature makes this concrete compatibility proxy recognize v2
initialization, capability, session setup, and `mcp/*` wire types. It does not
add high-level v2 global MCP attachment or proxy-session helpers to the core
SDK; those remain v1-only.
change the core attachment API. `Proxy.v2().with_mcp_server(...)` provides
connection-global attachment, and `V2SessionBuilder::with_mcp_server(...)`
provides per-new-session attachment. The polyfill adapts their native
declarations when the final agent supports only HTTP MCP.

## Placement

Expand All @@ -51,12 +53,13 @@ ConductorImpl::new_agent("conductor", components)
.await?;
```

For v1, the application proxy can attach a high-level
The application proxy can attach a high-level
`agent_client_protocol::mcp_server::McpServer`. The SDK advertises it in session
setup requests as `McpServer::Acp`; callers do not need to construct a transport
placeholder themselves. In a v2 chain, a version-aware proxy currently supplies
the `schema::v2::McpServer::Acp` declaration directly because the high-level
global proxy attachment helpers remain v1-only.
placeholder themselves. In v2, `Proxy.v2().with_mcp_server(...)` provides
connection-global attachment and `V2SessionBuilder::with_mcp_server(...)`
provides per-new-session attachment. The polyfill translates those native
declarations at the final compatibility boundary.

During initialization, the polyfill forwards the request to its successor. When
the successor advertises HTTP MCP support, the polyfill advertises native ACP
Expand Down
5 changes: 3 additions & 2 deletions md/migration_v2.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -339,8 +339,9 @@ Construct `Lines` and `ByteStreams` with `Lines::new(outgoing, incoming)` and
## Draft v2 schema updates

The optional `unstable_protocol_v2` surface now tracks
`agent-client-protocol-schema` 1.5. Because this API is explicitly unstable, its source changes
are included in the SDK 2.0 migration rather than treated as stable-v1 wire changes.
`agent-client-protocol-schema` 1.6. The changes accumulated across schema 1.5
and 1.6 are included in the SDK 2.0 migration because this API is explicitly
unstable, rather than treated as stable-v1 wire changes.

- Many values that were plain `String` or `PathBuf` fields are semantic newtypes, including
`AbsolutePath`, `MediaType`, session/message/tool/terminal IDs, and list cursors. Construct them
Expand Down
39 changes: 28 additions & 11 deletions md/protocol-v2.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,8 +265,15 @@ the stable v1 builder, while `Proxy.v2()` is v2-only and requires
`_proxy/initialize` to select protocol v2. A proxy built for one version rejects
the other version instead of parsing it through a permissive schema.

Raw routing infrastructure is the exception. If a component deliberately
selects and validates the version itself, it can use
When one component must expose independently authored v1 and v2 proxies,
compose them with
`Proxy.protocol_router().with_v1(v1_proxy).with_v2(v2_proxy)`. The conductor
has already selected and canonicalized the protocol before
`_proxy/initialize`, so the proxy router requires an exact v1 or v2 match,
preserves the complete initial transport frame, and does not downgrade or
convert later traffic.

Components implementing their own raw version selector can use
`Proxy.builder().without_acp_version_guard()` and keep protocol-neutral
`ConnectionTo` callbacks. This disables the SDK's automatic version guard and
is not a substitute for selecting `Proxy.v2()` in an ordinary v2 proxy
Expand All @@ -281,14 +288,17 @@ request, `info`, `capabilities`, metadata, and unknown extension fields
therefore retain their wire shape across conductor-controlled rewrites. A proxy
implementation can still deliberately replace the request it forwards.

As with the core protocol router, an exact v2 request can retain unknown raw
fields, while a request for a later compatible version is canonicalized through
the selected v2 schema before component instantiation.
An exact v2 request can retain unknown raw fields, while a request for a later
compatible version is canonicalized through the selected v2 schema before
component instantiation.

Proxy implementations use
`agent_client_protocol::schema::v2::InitializeProxyRequest`; its response is the
v2 `InitializeResponse`. The flat `schema::InitializeProxyRequest` remains the
stable v1 type. Static conductor component providers support both versions.
stable v1 type. Static conductor component providers can carry either selected
schema, but each supplied component must support that version. Use
`Agent.protocol_router()` or `Proxy.protocol_router()` when a static component
has separate implementations.
Custom `InstantiateProxiesAndAgent` and `InstantiateProxies` implementations
opt into v2 by implementing their feature-gated v2 method; the default rejects
v2 rather than interpreting it as v1. Returning the initialize request
Expand Down Expand Up @@ -331,8 +341,9 @@ The SDK handles the `initialize` negotiation at the JSON-RPC boundary:
match. The SDK does not convert traffic between v1 and v2.

That means v1 and v2 implementations still need separate handlers.
`Agent.v2()` and `Client.v2()` are v2-only. While protocol v2 stabilizes, the
`unstable_protocol_v2` crate feature also exposes `Agent.protocol_router()` and
`Agent.v2()`, `Client.v2()`, and `Proxy.v2()` are v2-only. While protocol v2
stabilizes, the `unstable_protocol_v2` crate feature also exposes
`Agent.protocol_router()`, `Proxy.protocol_router()`, and
`Client.protocol_connector()` for composing version-specific implementations.

Agents can add protocol implementations independently, which makes it easy for
Expand Down Expand Up @@ -380,7 +391,7 @@ agent
# }
```

The protocol router reads the initial `initialize` request, selects the
The agent protocol router reads the initial `initialize` request, selects the
highest configured protocol version that is compatible with the requested
version, and then hands the connection to that implementation. If only v2 is
configured, v1 clients are rejected without changing the fluent API. The router
Expand All @@ -390,6 +401,12 @@ initial frame may be a batch whose first call-shaped entry is `initialize`; the
router preserves the complete frame when handing it to the selected
implementation. Response-only frames before initialization are ignored.

The proxy protocol router reads `_proxy/initialize` after the conductor has
selected the chain's wire version. It therefore requires an exact configured
v1 or v2 implementation instead of negotiating or downgrading. It validates
the selected schema, then hands the complete, unchanged initial frame to that
strict implementation.

Clients use a connector because fallback may require opening a new transport.
Both client implementations and the agent transport are factories:

Expand Down Expand Up @@ -428,8 +445,8 @@ connection. That does not turn an otherwise valid v2 request into an error.
The `unstable_protocol_v2` API follows the moving draft schema. Schema 1.5 added
semantic newtypes for paths, media types, IDs, and cursors; renamed
`DiffPatch.diff` to `DiffPatch.text`; and added terminal state and output update
types. The next schema dependency update removes the former schema-wide v1/v2
conversion API: versioned implementations should remain separate, with
types. Schema 1.6 removed the former schema-wide v1/v2 conversion API:
versioned implementations should remain separate, with
purpose-specific adapters at runtime boundaries where the required state and
policy are available. These are draft API changes rather than stable v1 wire
changes. See [Migrating to
Expand Down
5 changes: 4 additions & 1 deletion md/transport-architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,10 @@ Lifecycle-sensitive calls should normally be sent individually. As a
compatibility measure, `AgentProtocolRouter` can select a v1 or v2 agent when
the first call-shaped entry is `initialize`, while preserving the original
frame for the selected implementation. Response-only frames received before
initialization are ignored. `ClientProtocolConnector` starts each attempted
initialization are ignored. `ProxyProtocolRouter` provides the analogous
boundary after conductor selection: it requires an exact v1 or v2
`_proxy/initialize` match and preserves the complete initial frame without
cross-version conversion. `ClientProtocolConnector` starts each attempted
client implementation with an individual `initialize` request.

## Message Flow
Expand Down
Loading