mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-09-10 15:05:40 +00:00
a357b9f609
* fix(mcp): restrict the remote transport to the era pad can serve (TASK-2977)
mcp-go 1.0 implements the stateless protocol core from 2026-07-28 — no
handshake, no sessions, per-request identity in _meta — and its
Streamable HTTP transport advertises EVERY revision it implements by
default, serving both eras concurrently on one endpoint and deciding the
era per request. pad's construction site passed no version restriction,
so the library bump alone had main answering modern-era traffic through
server/discover while pad://_meta/version still published 2025-11-25 as
the maximum revision this server can negotiate.
NOT A PRODUCTION DEFECT, checked rather than assumed: app.getpad.dev and
mcp.getpad.dev both report commit 0e2cb06a built 2026-08-31, nine days
before the mcp-go 1.0 merge, and 0.58 has no 2026-07-28 constant at all.
The gap is in main, ahead of a hand deploy, so this lands before it can
become real.
Beyond the mismatch: pad_set_workspace pins a session default workspace
and the stateless era has no session for that pin to live in. So the
modern era is not something pad happens not to advertise, it is
something pad is not known to be able to serve. Establishing what it
would take is step 2 of this item; the honest advertisement meanwhile is
the era pad was built and tested against.
The set is DERIVED from mcp.LegacyProtocolVersions(), the SDK's own
answer to "which revisions use the handshake", so a future SDK adding a
legacy revision includes it and one adding a modern revision excludes
it, with no edit here. A hand-written list would silently mean the wrong
thing after either bump — the same shape as the defect being closed.
The option set moved into mcpserver.NewRemoteTransport so a test can
drive what cmd/pad actually constructs. An advertised set is only
correct if the option is PASSED, and a test building its own transport
would vouch for the option and not for the binding (CONVE-19).
Four tests, and the second is what makes the first mean anything:
- a well-formed 2026-07-28 server/discover against pad's transport is
refused with code -32022, data.requested naming the version and
data.supported carrying exactly the legacy four. Asserting "an error
came back" would also pass on a transport that had simply broken.
- the identical bytes against an UNRESTRICTED mcp-go transport are
SERVED, with 2026-07-28 among supportedVersions. Negative control: it
is what says the refusal comes from pad's option rather than from a
malformed request or a changed library default. Both requests carry
the Mcp-Method header the modern era requires, so a refusal cannot be
about headers.
- initialize still negotiates 2025-11-25 — the restriction must not
break the era pad actually serves.
- AdvertisedMCPProtocolVersion equals the newest served revision, which
is the half TestAdvertisedProtocolVersion structurally cannot see: it
pins the literal to what the HANDSHAKE answers, and the modern era has
no handshake.
Sweep: meta.go's two comments described the handshake cap as the whole
story. One construction site only — pad-cloud is an OAuth/billing layer
and builds no transport, so the pad binary in cloud mode is the single
place this is decided.
Claude-Session: https://claude.ai/code/session_01GqaEDuCtRiSJfa7eppWecn
* test(mcp): the handshake test measured less than its comment claimed (TASK-2977)
A mutation found this in my own instrument. Restricting the advertised
list to a version that EXCLUDES 2025-11-25 leaves the legacy-handshake
test green, so that test cannot be evidence that the restriction
preserved the era pad serves — which is exactly what its comment said it
was.
The mechanism, read rather than inferred from the green: initialize is
answered by MCPServer through mcp.NegotiateLegacyVersion, which consults
LATEST_LEGACY_PROTOCOL_VERSION and never the transport's list. The two
are independent.
So the comment now says what the test measures (the legacy path works)
and what it does not (that the restriction preserved it), and the
independence is pinned as its own subtest: a transport advertising only
2025-06-18 still answers initialize with 2025-11-25. If that ever fails,
the handshake has become coupled to the advertised list and the
restriction has become able to refuse legacy clients — the moment this
file needs a different test.
Nothing about the fix changes; the claim about the evidence does.
Claude-Session: https://claude.ai/code/session_01GqaEDuCtRiSJfa7eppWecn