1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
[]
= "protocol-droid"
= "A Model Context Protocol toolkit that speaks every published spec revision, with one canonical IR behind many per-revision codecs."
= true
= true
= true
= true
= true
= true
= true
= true
# Both HTTP halves are first-class here — every transport ships a server and a
# client. `web-programming::http-server` and `::http-client` are the slugs
# crates.io actually carries; there is no bare `web-programming::http` leaf.
= [
"network-programming",
"parser-implementations",
"web-programming::http-server",
"web-programming::http-client",
]
# docs.rs builds with the crate's DEFAULT features, which are serde + stdio —
# so the HTTP server and client, the OAuth client and resource server, and the
# A2A and ACP modules are all absent from the published documentation.
# `all-features` is what makes the crate's actual API visible, and it is what
# the `rustdoc` CI job checks so the two cannot disagree.
[]
= true
# No `[features]` table yet. The feature gates for each transport, the legacy
# SSE path, and OAuth are introduced by the tasks that implement them (T13-T16),
# not declared empty in advance.
[]
# The envelope decoder defaults to `serde_json`, which is portable and has no
# target-specific code. `simd` swaps in a vectorised parser behind a flag so the
# two can be benchmarked against each other rather than argued about; it is off
# by default because a `default-features = false` build has to stay lean and
# because a decoder that is only fast on some targets is a portability trap.
= ["serde", "stdio"]
# serde_json is the portable default and is always compiled — the generated
# wire types depend on it, so it is data rather than an optional integration.
= []
# T13. The stdio transport is the only adapter in the crate that names a
# runtime, so it is the one adapter that is gated. `default-features = false`
# drops the executor, the `AsyncRead`/`AsyncWrite` plumbing and the diagnostic
# channel with it, leaving the domain, the IR and every codec — which is the
# shape a server that speaks only HTTP, or a tool that only speaks the IR, wants.
= ["dep:tokio"]
# T14. The Streamable HTTP server adapter, which is the other half of the same
# story: `stdio` covers the process-spawning client and this covers the
# network server, and the two disagree about nearly everything. `http` is
# **not** a default because the two transports are alternatives, not a set —
# a `stdio` server has no use for a `Router`, and a `default-features = false`
# build is how a consumer says "I am linking this as a library, not running a
# server". Turning it on adds axum, the `net` half of tokio, and the SSE
# responder; everything protocol-shaped — the session store, the event ring,
# the idempotency port, the routing headers, negotiation — is already compiled
# without it, so a consumer that only wants the IR and the codecs pays for none
# of this.
= ["dep:axum", "dep:base64", "dep:futures-core", "dep:futures-util", "dep:reqwest", "dep:tower", "dep:tokio", "tokio/net", "tokio/rt", "tokio/sync", "tokio/macros"]
# T15. The deprecated 2024-11-05 HTTP+SSE transport, which `2025-03-26`
# replaced and which is the only transport that revision speaks. It needs the
# HTTP stack, so it implies it rather than listing it again — and it is a
# separate gate from `http` because the two are alternatives, not a set: a
# server that speaks Streamable HTTP has no use for a second endpoint and an
# `endpoint` event. `TransportKind::LegacySse` stays ungated in the domain,
# because describing a transport costs nothing and only speaking it does.
= ["http"]
# T16. OAuth 2.1 authorization. It implies `http` because both token sources
# reach an authorization server that is somewhere else, and it adds the one
# dependency the rest of the crate has no use for: a JWS signature check.
#
# Only the *adapters* are gated. `domain/mcp/authorization` and
# `application/oauth/token` stay compiled in a lean build for the same reason
# the session store and the routing headers do — they are protocol-shaped and
# pure, and gating them would cost a consumer the authorization model to save
# nothing.
= ["http", "dep:jsonwebtoken", "dep:base64", "dep:url"]
# Session ids that do not enumerate. `IdSource` is a port every server must
# satisfy and this crate shipped no implementation of it, so the only code a
# consumer could copy was a `fetch_add` counter.
= ["dep:getrandom"]
# T27. The A2A (Agent2Agent) protocol module — per-revision method table,
# per-revision wire codec, and the application handlers behind them. Gated
# on its own feature because (a) the dependency footprint is independent of
# MCP and a consumer that never speaks A2A should not pay it, and (b) the
# feature gate is the seam that lets a future `a2a-grpc` / `a2a-rest` build
# on top of this without re-touching MCP. `domain/a2a` and `application/a2a`
# stay compiled in a lean build for the same reason the MCP domain does —
# they are pure value objects, so gating them would cost the version model to
# save nothing. The `url` dep is here (not in the unconditional list) because
# only the SSRF filter needs it, and the SSRF filter is A2A-specific.
= ["http", "dep:url", "url/serde"]
# T29. The ACP (Agent Client Protocol) module — Zed's editor↔agent
# JSON-RPC binding. The feature is `acp-experimental` (not `acp`)
# because the spec is pre-1.0 (`schema-v1.25.0` is the pin) and the
# task explicitly excludes it from semver guarantees. The gate is
# what keeps the experimental module out of a consumer's lean
# build; the schema is still compiled because the type names live
# in the protocol-neutral IR.
#
# Pulls in `tokio` (with `process`, `io-std`, `io-util`, `rt`,
# `sync`, `time`) because the stdio transport is the only ACP
# adapter that runs an executor. The `acp-stdio` sub-module is
# gated on the same flag so a consumer that pulls in
# `acp-experimental` for the typed `initialize` shape but never
# spawns a child process pays for the typed envelope and not the
# runtime. The feature interaction with the crate-wide `stdio`
# flag is `acp-experimental` *adds* the deps; turning the crate
# `stdio` on does not turn `acp-experimental` on. A consumer that
# wants both the typed envelope and the stdio peer enables both
# flags.
= [
"dep:futures-core",
"dep:tokio",
"tokio/io-std",
"tokio/io-util",
"tokio/process",
"tokio/rt",
"tokio/sync",
"tokio/time",
"tokio/macros",
]
[]
# The registry publishes a new immutable snapshot by swapping the `Arc`. Not
# optional: the capability's requirement is that reads are lock-free, and an
# `RwLock` would make `tools/list` contend with a publish.
= { = true }
# `version` inherits from [workspace.package] like every other sibling dep. It
# used to be hardcoded at "0.1.0", which is how a workspace version bump
# becomes a build failure here instead of a version bump — the pin has to be
# the one place that changes, or it is the one place that stops you changing it.
= { = true }
= { = true }
= "1.2.2"
= { = true }
= { = true }
= { = true }
# Only the `stdio` adapter needs an executor. `io-std` for the `AsyncRead`/
# `AsyncWrite` helpers and `tokio::io::Stderr`, `io-util` for the `AsyncReadExt`
# and `AsyncWriteExt` traits, and `process` for the child a client spawns.
#
# `process` does NOT need the public `signal` feature. On a Unix target tokio
# compiles its `SIGCHLD` reaper for `process` regardless, because
# `cfg_signal_internal!` in tokio's `macros/cfg.rs` is
# `any(feature = "signal", all(unix, feature = "process"))` — so enabling
# `signal` would add the public signal *API* and buy nothing, and a client that
# never registers a signal handler has no use for it. `rt` and `sync` come from
# the workspace declaration above and are not repeated.
= { = true, = true, = [
"io-std",
"io-util",
"process",
] }
# T14. The Streamable HTTP server. `http` turns these on; nothing else does,
# and a `--no-default-features` build resolves without them at all.
= { = true, = true }
# T17. `limit` for the concurrency ceiling and `timeout` for the request
# deadline. Both are service wrappers rather than logic — re-implementing a
# semaphore-guarded service or a `tokio::time::timeout` wrapper by hand is the
# kind of "obviously simple" that is subtly wrong about cancellation, and
# `tower` is already in the workspace so this adds no new resolution.
= { = true, = true, = ["limit", "timeout", "util"] }
= { = true, = true }
= { = true, = true }
# T14. The Streamable HTTP client, gated by the same `http` feature as the
# server above: a consumer that does not run an HTTP server has no use for an
# HTTP client, and the two are alternatives rather than a set.
= { = true, = true }
# T16. JWS signature verification for the JWKS token source. See the workspace
# declaration for why `use_pem` and `aws_lc_rs` are both off.
= { = true, = true }
# T16. The OS CSPRNG behind `RandomIds`, so a server can mint session ids that
# do not enumerate. `getrandom` rather than `rand`: this is one `u64` per
# session opened, not a stream of draws, and it wants no seeding, no thread-local
# state and no `Default`.
= { = true, = true }
# T16 stage 7. PKCE. `sha2` for the S256 derivation and `base64` for its
# base64url encoding — both are pulled by `jsonwebtoken` already, and both are
# declared here because the client half needs them directly rather than through
# somebody else's re-export. The `base64` line repeats the dev-dependency below
# rather than hoisting it to `[workspace.dependencies]`: one crate uses it, and
# a workspace table entry would invite a second one.
= { = true }
# SEP-2322. A server hands the client an opaque `requestState` and must be able
# to tell a tampered one from its own: HMAC-SHA256 under a per-server key. Not
# feature-gated, because the multi round-trip pattern is core protocol at
# `2026-07-28` rather than an integration a consumer opts in to.
= { = true }
= { = "0.22.1", = false, = [
"alloc",
], = true }
# T16 stage 7. Building the authorization redirect. `url` is already in the
# graph through `reqwest`, and appending query parameters to an endpoint that
# may already carry some is the case hand-rolled percent-encoding gets wrong:
# the endpoint comes from a discovery document this crate does not control.
= { = "2.5.8", = true }
[]
# The envelope path is the project's one perf-critical path, so its decode,
# route, and params-decode costs are measured rather than estimated. CI gates a
# >10% regression against the committed baseline.
= { = true }
= { = true }
# The schema-validation property tests in `tests/acp_schema_validation.rs`
# validate the typed `InitializeRequest` shape against the vendored ACP
# v1.25.0 schema. `jsonschema` is a dev-only dep; the runtime is too
# expensive to ship in a library that already has its own typed
# validation.
= { = true }
# The golden feature profiles are snapshots a human reviews, so a matrix change
# shows up as a diff to read rather than as a failure to debug.
= { = true }
# The lowering properties are the crate's central claim — that a value which
# fits survives a round trip, and that the gate is exact — so they are generated
# rather than hand-picked. A hand-picked value proves one case was thought of.
= { = true }
# `StreamExt::next` over a response body. Dev-only: nothing in the adapter
# consumes a `Stream` through these helpers, but a test reading the replayed
# frames out of an open SSE response has no other way to stop before the
# stream parks forever waiting for a push that is not coming.
= { = "0.3.31", = false, = [
"std",
] }
# The capability table is checked against the vendored `schema.json` files, so
# the domain's model is verified against the bytes it describes rather than
# against a second copy of the same assumptions.
= { = true }
# The cancellation race test needs a real scheduler: a single-poll harness can
# prove the waker fires, but not that a handler awaiting the signal is resumed
# and then suppressed from answering. `time::pause` makes the deadline part of
# that test instantaneous and deterministic.
= { = true, = ["rt", "time"] }
= "1.0.121"
# RFC 7515 base64url, for the one JWT no encoder will produce: an unsecured
# `alg: none` token. `jsonwebtoken` depends on it too, so this is a dev-only
# path to a crate already in the graph rather than a new one.
= { = "0.22.1", = false, = ["alloc"] }
# Drives a `Router` to a response without binding a socket, which is what lets
# the security-header test read headers off a real response rather than
# asserting that a middleware value is `Some`.
= { = true, = ["util"] }
[]
= true
# divan is its own harness; libtest would only add a wrapper around a binary
# that is meant to be run directly.
[[]]
= "jsonrpc_hot_path"
= false
[[]]
= "registry"
= false
[[]]
= "a2a_serving"
= false
# Same reason as the `examples/` below: this bench imports
# `adapters::secondary::a2a`, which is gated on `a2a`. Without this the default
# build — `cargo check --all-targets` with no features — fails to compile it.
= ["a2a"]
# Cargo builds every file in `examples/` and `benches/` for every feature
# configuration, so one that speaks a gated transport must declare what it
# needs or `cargo check --all-targets` with no features at all — the plain
# default build — fails to compile it. The target is skipped rather than built
# lean; a stripped-down stdio server would demonstrate nothing, and a bench
# measuring the A2A table without the A2A table would measure nothing.
[[]]
= "stdio_server"
= ["stdio"]
[[]]
= "mcp_client"
= ["stdio"]
[[]]
= "server"
= ["http"]
[[]]
= "http_server"
= ["http"]
[[]]
= "http_client"
= ["http"]
[[]]
= "conformance_reference_server"
= ["http", "random-ids"]
[[]]
= "conformance_reference_client"
= ["http", "oauth", "random-ids"]
# The single source for the resource-server assembly in the security guide.
# `oauth` for the authenticator, `http` for the router and `ServerConfig`.
[[]]
= "oauth_resource_server"
= ["http", "oauth"]
[[]]
= "a2a_client"
= ["a2a", "http"]
[[]]
= "a2a_minimal_server"
= ["a2a", "http"]
[[]]
= "a2a_server"
= ["a2a", "http"]
[[]]
= "a2a_typed_client"
= ["a2a", "http"]
# The RFC 9728 discovery document. `http` only — the document is served over
# any axum app and needs neither an OAuth token source nor the A2A module.
[[]]
= "protected_resource_server"
= ["http"]