Skip to content

fix(acp)!: version-neutral wire envelopes and extensible proxy types - #478

Merged
benbrandt merged 7 commits into
mainfrom
cleanup-wire-types
Nov 1, 2026
Merged

benbrandt merged 7 commits into
mainfrom
cleanup-wire-types

Conversation

@benbrandt

@benbrandt benbrandt commented Nov 1, 2026 •

Copy link
Copy Markdown
Member

Part of the pre-5.0.0 public API cleanup. This PR covers the wire envelopes, the schema dispatch enums, the proxy protocol types, and the derive-to-core contract.

Addressed

  • F08: RawJsonRpcMessage::Notification now holds an SDK-owned RawJsonRpcNotification instead of schema::v1::Notification<RawJsonRpcParams>. The wire format does not change.
  • F10 / F42: SuccessorMessage is #[non_exhaustive] and gains new(message) and a .meta(..) builder. _meta is typed as Option<serde_json::Map<String, Value>>, and the non-spec meta alias is no longer read. schema::v1::InitializeProxyRequest and schema::v2::InitializeProxyRequest are also #[non_exhaustive].
  • F25: Proxies now unwrap incoming proxy/successor params with json_cast_params, so malformed params get -32602 instead of -32700. The duplicate util::internal_error and util::parse_error are removed. The util::json_cast docs now say it is not for inbound params.
  • F06: The v1 dispatch enums keep the leading _ on extension methods, as v2 already does. A v1 regression test is added.
  • F31: The v1 and v2 dispatch enum macros now delegate method() to the schema enum, so variants the macro doesn't list are sent with their real method name instead of "_unknown".
  • F41: MCP_SERVER_UNAVAILABLE and MCP_BACKEND_FAILURE are ErrorCode constants. Adds MCP_RESOURCE_LIMIT_EXCEEDED (-33000).
  • G93: Derive output now calls agent_client_protocol::__private::json_cast* instead of public util helpers. The workspace pins the derive crate exactly (=3.6.0). I checked this with release-plz 0.4.2: release-plz set-version rewrites the pin to =5.0.0 and keeps the = operator. Both crates are in the acp version_group, so they always get the same version and the pin stays in sync.
  • G94 (partial): The raw envelope types gain Eq. RawJsonRpcMessage gains PartialEq/Eq and From conversions from each envelope.
  • G95: The rustdoc now says that unknown request and notification envelope members are dropped.
  • F63: Removes util::TypeNotification. Use MatchDispatch::if_notification instead.

Every breaking change has a section in md/migration-v5.md.

Skipped

Checks

These were run locally on this head:

  • cargo fmt --check
  • both clippy runs (-D warnings)
  • cargo hack check over the feature powerset at depth 2
  • cargo test --workspace --all-features --locked
  • cargo test -p agent-client-protocol --no-default-features
  • cargo doc with -D warnings for the core, derive, and conductor crates
  • mdbook build

All passed except one target. agent-client-protocol-derive --lib aborts at load time (dyld) under the local build-cache runner. The same thing happens on main, so it is not caused by this change.

Review follow-up (3c913a5)

  • A proxy/successor whose envelope _meta is not an object now has that _meta ignored, matching how the schema crate treats _meta, instead of failing the forwarded message with -32602.
  • Conductor error conversions use Error::into_internal_error.
  • The migration guide now says how to compare MCP error codes: error.code == MCP_BACKEND_FAILURE, with .value() where an i32 is needed.
  • An independent review also ran a full release-plz update on a scratch clone. It rewrote the derive pin from =3.6.0 to =5.0.0 and moved core and derive to 5.0.0 together.

benbrandt and others added 7 commits October 31, 2026 10:55
RawJsonRpcMessage::Notification wrapped the schema crate's
v1::Notification<RawJsonRpcParams>, unlike its Request and Response
variants. It now holds the SDK's own RawJsonRpcNotification with the same
method and params fields. The wire format is unchanged.

The raw envelope types also gain Eq, RawJsonRpcMessage gains PartialEq/Eq
and From conversions from each envelope, and the rustdoc states that
unknown request and notification envelope members are dropped.

BREAKING CHANGE: RawJsonRpcMessage::Notification now holds
RawJsonRpcNotification instead of schema::v1::Notification<RawJsonRpcParams>.

Co-Authored-By: Claude <noreply@anthropic.com>
SuccessorMessage was an exhaustive struct with public fields and no
constructor, so the proxy protocol could not add a field (the proxy-chains
RFD already mentions a possible `peer`) without a major release. It is now
`#[non_exhaustive]`, with `SuccessorMessage::new(message)` and a `.meta(..)`
builder. `meta` is typed as a JSON object, matching ACP `_meta`, and the
non-spec `meta` alias is no longer read. `schema::v1::InitializeProxyRequest`
and `schema::v2::InitializeProxyRequest` are `#[non_exhaustive]` too; they
already have `new()` and `From<InitializeRequest>`.

Proxies now unwrap incoming `proxy/successor` envelopes with
`json_cast_params`, so malformed envelope params are answered with Invalid
params (-32602) instead of Parse error (-32700).

BREAKING CHANGE: SuccessorMessage, schema::v1::InitializeProxyRequest and
schema::v2::InitializeProxyRequest are #[non_exhaustive]; build them with
`new()`. SuccessorMessage::meta is Option<serde_json::Map<String, Value>>
and the `meta` alias for `_meta` is no longer accepted.

Co-Authored-By: Claude <noreply@anthropic.com>
The v1 dispatch enum macros stripped the leading `_` from extension methods,
so a parsed `_vendor/x` reported and re-sent `vendor/x` and the peer
answered -32601. They now keep the wire method name, as the v2 macros do.

The v1 and v2 dispatch enum macros also delegate `JsonRpcMessage::method`
to the schema enum's inherent `method()`, so variants the macro does not
list are sent with their real method name instead of `"_unknown"`.

BREAKING CHANGE: `ExtMethodRequest` / `ExtNotification` values parsed by the
v1 dispatch enums now carry the full `_`-prefixed method name.

Co-Authored-By: Claude <noreply@anthropic.com>
MCP_SERVER_UNAVAILABLE and MCP_BACKEND_FAILURE were bare i32 constants,
unlike every other SDK error code. They are now ErrorCode constants, and
MCP_RESOURCE_LIMIT_EXCEEDED adds the RFD's -33000 code.

BREAKING CHANGE: mcp_server::MCP_SERVER_UNAVAILABLE and
mcp_server::MCP_BACKEND_FAILURE are ErrorCode instead of i32.

Co-Authored-By: Claude <noreply@anthropic.com>
…rate

The derive macros called the public `util::json_cast` and
`util::json_cast_params`, and the core crate required the derive crate with
a caret requirement. A newer derive could emit calls the resolved core
version lacks, and the util helpers could not change without breaking
derived impls. The derive output now calls `__private::json_cast*`, and
the core crate depends on the derive crate released with it exactly.

Co-Authored-By: Claude <noreply@anthropic.com>
`util::internal_error` and `util::parse_error` duplicated the `Error`
constructors; the SDK keeps a crate-private `internal_error` for its own
use. `util::TypeNotification` duplicated `MatchDispatch::if_notification`.
`util::json_cast` now documents that it is not for incoming params, which
take `json_cast_params`.

BREAKING CHANGE: `util::internal_error`, `util::parse_error` and
`util::TypeNotification` are removed. Use `Error::internal_error().data(..)`,
`Error::into_internal_error`, `Error::parse_error()` and
`MatchDispatch::new(Dispatch::Notification(message))`.

Co-Authored-By: Claude <noreply@anthropic.com>
…g the message

A proxy/successor whose envelope _meta is not an object now has that _meta
ignored, as the schema crate does for _meta, instead of failing the whole
forwarded message with -32602. The conductor uses Error::into_internal_error
in place of hand-written closures, and the migration guide describes how to
compare MCP error codes with ErrorCode.

Co-Authored-By: Claude <noreply@anthropic.com>
@benbrandt
benbrandt merged commit 7a44332 into main Nov 1, 2026
14 checks passed
@benbrandt
benbrandt deleted the cleanup-wire-types branch November 1, 2026 20:05
@acp-release-bot acp-release-bot Bot mentioned this pull request Nov 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant