~bigbes/sr-ht-spec · parade

main · last commit 18 hours ago · 8tmadfpi

← Back to the parade

spec-taf Set Stateless: true on specs' MCP transport to stop the session id acting as a bearer credential Lined Up

status: open P0 bug
bd update spec-taf --claim bd close spec-taf
Created byEugene Blikh
Ownerbigbes@gmail.com
Created2026-08-17T20:40:04Z
Updated2026-08-17T20:40:04Z
Description
What is wrong: `mcpsrv/mcpsrv.go::Handler` builds `&mcp.StreamableHTTPOptions{DisableLocalhostProtection: true}` and never sets `Stateless`, so it defaults to `false` — the MCP transport runs in stateful mode.

Why it matters: in the SDK's stateful mode, a tool handler runs under the context of the request that *opened* the session, not the one carrying the call. The credential presented at handshake authorises every later call on that session, so `Mcp-Session-Id` becomes a bearer credential in its own right. A token revoked mid-session keeps working until the client reconnects, and a token presented once and dropped keeps authorising. spec.sr.ht has write tools (`spec_propose`, `spec_comment`) that resolve the acting agent from the request context, and the package's own doc promises "one resolution per request, never a second one here" — a promise only stateless mode keeps.

Sibling that does it correctly: `Stateless: true` is a literal in `sourcehut-bench`, `sourcehut-artifacts` and `sourcehut-curator`'s `mcpsrv` packages, a package constant in `sourcehut-coverage`/`sourcehut-dolt`, and hardcoded in `sr-ht-ecore/mcphttp/mcphttp.go::StreamableOptions`. `sourcehut-dolt/mcpsrv::TestIdentityIsPerCallAndNotPerSession` measured the difference against the same SDK version: stateful with "handshake anonymous, call with the OWNER's token" returns 4 results (the call's token is not consulted); stateless returns 6.

Fix: add `Stateless: true` to the options literal in `mcpsrv.go::Handler`; port dolt's `TestIdentityIsPerCallAndNotPerSession` (or bench's `TestEachCallIsAuthenticatedOnItsOwn`) to pin it. Better yet, do this as part of adopting `mcphttp.StreamableOptions()` (see the mcphttp-adoption issue) which already hardcodes it.

Note: verified that `Stateless` is absent (`grep -rn Stateless sourcehut-specs/mcpsrv/` returns zero matches); the exploitability claim is inferred from dolt's measurement, not run against specs itself.

Audit: D01 (cross-repo audit, 2026-08-17)

Depends on

No outgoing dependencies.

Depended on by

Nothing depends on this issue.

No comments.

  • Eugene Blikh created the issue · 2026-08-17T23:40:03Z
Stored rows — what this pane was built from, as read
issues 1 row
id spec-taf
content_hash 5b9d3f0d53e5d628a06b2ebe9cb063612e49da724da9f9e81acd88500522c0d6
title Set Stateless: true on specs' MCP transport to stop the session id acting as a bearer credential
description What is wrong: `mcpsrv/mcpsrv.go::Handler` builds `&mcp.StreamableHTTPOptions{DisableLocalhostProtection: true}` and never sets `Stateless`, so it defaults to `false` — the MCP transport runs in stateful mode. Why it matters: in the SDK's stateful mode, a tool handler runs under the context of the request that *opened* the session, not the one carrying the call. The credential presented at handshake authorises every later call on that session, so `Mcp-Session-Id` becomes a bearer credential in its own right. A token revoked mid-session keeps working until the client reconnects, and a token presented once and dropped keeps authorising. spec.sr.ht has write tools (`spec_propose`, `spec_comment`) that resolve the acting agent from the request context, and the package's own doc promises "one resolution per request, never a second one here" — a promise only stateless mode keeps. Sibling that does it correctly: `Stateless: true` is a literal in `sourcehut-bench`, `sourcehut-artifacts` and `sourcehut-curator`'s `mcpsrv` packages, a package constant in `sourcehut-coverage`/`sourcehut-dolt`, and hardcoded in `sr-ht-ecore/mcphttp/mcphttp.go::StreamableOptions`. `sourcehut-dolt/mcpsrv::TestIdentityIsPerCallAndNotPerSession` measured the difference against the same SDK version: stateful with "handshake anonymous, call with the OWNER's token" returns 4 results (the call's token is not consulted); stateless returns 6. Fix: add `Stateless: true` to the options literal in `mcpsrv.go::Handler`; port dolt's `TestIdentityIsPerCallAndNotPerSession` (or bench's `TestEachCallIsAuthenticatedOnItsOwn`) to pin it. Better yet, do this as part of adopting `mcphttp.StreamableOptions()` (see the mcphttp-adoption issue) which already hardcodes it. Note: verified that `Stateless` is absent (`grep -rn Stateless sourcehut-specs/mcpsrv/` returns zero matches); the exploitability claim is inferred from dolt's measurement, not run against specs itself. Audit: D01 (cross-repo audit, 2026-08-17)
design
acceptance_criteria
notes
status open
priority 0
issue_type bug
assignee NULL
estimated_minutes NULL
created_at 2026-08-17T20:40:04Z
created_by Eugene Blikh
owner bigbes@gmail.com
updated_at 2026-08-17T20:40:04Z
closed_at NULL
closed_by_session
external_ref NULL
spec_id
compaction_level 0
compacted_at NULL
compacted_at_commit NULL
original_size NULL
sender
ephemeral 0
wisp_type
pinned 0
is_template 0
mol_type
work_type
source_system
metadata �{}
source_repo
close_reason
event_kind
actor
target
payload
await_type
await_id
timeout_ns 0
waiters
hook_bead
role_bead
agent_state
last_activity NULL
role_type
rig
due_at NULL
defer_until NULL
no_history 0
started_at NULL
is_blocked 0
events 1 row
id 01a01173-e6b9-71bd-a36d-b08c986805dc
issue_id spec-taf
event_type created
actor Eugene Blikh
old_value
new_value
comment NULL
created_at 2026-08-17T23:40:03Z