mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-09-10 23:15:40 +00:00
docs(mcp): the reason given for the protocol restriction was false — replace it with the true one (TASK-2977) (#1312)
TASK-2977 step 1 restricted the remote transport to the handshake era and gave this reason: pad_set_workspace pins a session default workspace that the stateless era has nowhere to keep, so pad "is not known to be able to serve" that era. It is plausible and it is false for THIS transport, and I wrote it reasoning from the tool's purpose rather than from its remote behaviour. cmd/pad builds the cloud dispatcher with a SHARED workspace state whose ResolveDefault() returns "" by construction — BUG-1865, the cross-user workspace bleed — so the pin is recorded and never consulted here. Resolution on /mcp is the explicit workspace argument, else a default derived per request from the caller's own OAuth identity and token allow-list. Every input comes from the request. This transport has been stateless with respect to workspace resolution since that bug was fixed, and the fix for a cross-user bug turns out to be most of the work a stateless era would need. THE TRUE REASON IS BETTER AND WAS ONE FILE AWAY. The mcp-active-sessions gauge is keyed on the Mcp-Session-Id header, and the generate-only session-id manager at this transport's call site exists so that header is always minted and the gauge stays observable (PR #400 round 1). SEP-2567 REMOVES session IDs in 2026-07-28 — a server serving that revision never mints or echoes one — so in that era nothing pad mints is available to key on. Ruled day 62: that is an accepted cost, not a blocker, and it is recorded where the key is CHOSEN rather than only where the era is refused — middleware_mcp_session.go now carries the obligation on whoever opens that era to re-key the gauge first, and says why the era's arrival is exactly the moment a silently-flat gauge gets read as "no MCP traffic" instead of "no measurement". The superseded reason is kept in the comment as superseded, four lines of it, because the false reason is the PLAUSIBLE one: the next person to reason about the stateless era from pad_set_workspace's name will reach for it, and the comment now meets them with the shared-state mechanism instead. TWO CODEX ROUNDS, TWO FINDINGS, both about this change's own prose and both verified in the code before accepting: - The server-package comment named mcp.ServedProtocolVersions as a symbol. It is not reachable from there — internal/mcp imports internal/server and not the reverse, which is also why the transport reaches the router as a plain http.Handler. Named by path now, with the direction stated. - "Modern-era traffic would be invisible to the gauge" was OVERSTATED, which is this unit's own defect class arriving inside the fix for it. trackMCPSession resolves the id from the response header and FALLS BACK to the request header, so a modern-era client that volunteers an Mcp-Session-Id is still tracked. The accurate claim, now in both files: the gauge stops depending on anything pad mints and starts depending on whether clients keep sending a header the spec removed — under-counting by a margin nobody controls, rather than a flat zero. Comment-only; no behaviour changes. The restriction, its four tests and the derived version set are untouched. Claude-Session: https://claude.ai/code/session_01GqaEDuCtRiSJfa7eppWecn
This commit is contained in:
@@ -20,12 +20,34 @@ import (
|
||||
// the advertisement is the promise a client acts on, and nothing here has
|
||||
// been read against the modern revision, let alone tested.
|
||||
//
|
||||
// It is also not merely a documentation gap. pad_set_workspace pins a session
|
||||
// default workspace, and the stateless era has no sessions 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 a separate unit; until then the honest advertisement is the
|
||||
// era pad was built against and is tested against.
|
||||
// It is also not merely a documentation gap: something pad relies on is deleted
|
||||
// by that era. internal/server/middleware_mcp_session.go keys the
|
||||
// mcp-active-sessions gauge on the Mcp-Session-Id header, and the generate-only
|
||||
// session-id manager at this transport's call site exists SO THAT the header is
|
||||
// always minted and the gauge stays observable. SEP-2567 removes session IDs in
|
||||
// 2026-07-28 — a server serving that version never mints or echoes one — so in
|
||||
// that era nothing pad mints is available to key on. The tracker does fall back
|
||||
// to a client-supplied REQUEST header, so the honest claim is under-counting by
|
||||
// a margin nobody controls rather than a flat zero; either way it is missing
|
||||
// numbers rather than wrong ones, in the direction that reads as quiet. The
|
||||
// full statement is at that header's declaration. Whoever opens that era
|
||||
// re-keys the gauge first; the cost is recorded at the metric's definition.
|
||||
//
|
||||
// AN EARLIER VERSION OF THIS COMMENT GAVE A DIFFERENT AND FALSE REASON, and it
|
||||
// is worth the four lines because the false one is the plausible one. It said
|
||||
// pad_set_workspace pins a session default workspace that the stateless era has
|
||||
// nowhere to keep. That is true of the LOCAL stdio transport and false of this
|
||||
// one: cmd/pad builds the cloud dispatcher with a SHARED workspace state whose
|
||||
// ResolveDefault() returns "" by construction (BUG-1865, the cross-user
|
||||
// workspace bleed), so the pin is recorded and never consulted here, and
|
||||
// resolution is already per-request — the explicit workspace argument, else a
|
||||
// default derived from the caller's own OAuth identity and token allow-list.
|
||||
// This transport has therefore been stateless with respect to workspace
|
||||
// resolution since that bug was fixed, and the fix for a cross-user bug turns
|
||||
// out to be most of the work a stateless era would need.
|
||||
//
|
||||
// Until the gauge is re-keyed, the honest advertisement is the era pad was
|
||||
// built against and is tested against.
|
||||
//
|
||||
// DERIVED, NOT LISTED, and that is load-bearing. mcp.LegacyProtocolVersions()
|
||||
// is the SDK's own answer to "which revisions use the handshake", so a future
|
||||
|
||||
@@ -269,6 +269,32 @@ func (s *Server) stopMCPSessionTracker() {
|
||||
// the audit row's status, so this matches the audit row's view.
|
||||
//
|
||||
// No-op when the tracker isn't wired (selfhost / tests).
|
||||
//
|
||||
// ACCEPTED COST, RECORDED HERE BECAUSE THIS IS WHERE THE KEY IS CHOSEN
|
||||
// (TASK-2977, ruled day 62). This gauge is keyed on a header the MCP protocol
|
||||
// DELETES: SEP-2567 removes session IDs in revision 2026-07-28, and a server
|
||||
// serving that revision never mints or echoes one. So in that era there is no
|
||||
// PROTOCOL-PROVIDED id for this to key on.
|
||||
//
|
||||
// Read trackMCPSession below before believing the stronger claim, which an
|
||||
// earlier draft of this comment made: the id falls back to the REQUEST header,
|
||||
// so a modern-era client that volunteers an Mcp-Session-Id anyway is still
|
||||
// tracked. The accurate statement is therefore not "that era is invisible" but
|
||||
// "the gauge stops depending on anything pad mints and starts depending on
|
||||
// whether clients keep sending a header the spec removed" — under-counting by a
|
||||
// margin nobody controls, in the direction that reads as quiet rather than as
|
||||
// breakage.
|
||||
//
|
||||
// It costs nothing today because pad's remote transport does not advertise that
|
||||
// revision: ServedProtocolVersions in internal/mcp restricts it to the
|
||||
// handshake era, and the gauge's observability is one of the two reasons stated
|
||||
// there. Named by path rather than as a symbol because it is not callable from
|
||||
// here — internal/mcp imports this package, not the other way round, which is
|
||||
// also why the transport is handed to the router as a plain http.Handler. The
|
||||
// obligation is on whoever opens that era — RE-KEY THIS GAUGE FIRST, on
|
||||
// something the modern era carries (the caller's identity plus a per-connection
|
||||
// value), because the era's arrival is exactly the moment a silently-flat gauge
|
||||
// would be read as "no MCP traffic" instead of "no measurement".
|
||||
const mcpSessionIDHeader = "Mcp-Session-Id"
|
||||
|
||||
func (s *Server) trackMCPSession(reqHeader, respHeader func(string) string, method string, httpStatus int) {
|
||||
|
||||
Reference in New Issue
Block a user