stacklok / stacklok/toolhive

Support server-initiated requests (elicitation/sampling) over the streamable proxy

Open
#5,939 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

chore
Dominant language
Go
Stars
2.2k
Forks
300
Avg merge
1d 15h
Merged PRs (30d)
184

Description

Part of #5743. Follow-up surfaced by #5744 (PR #5934).

Gap

The thv run streamable HTTP proxy multiplexes N downstream client sessions onto one shared backend MCP connection (a single stdio pipe to the container). #5934 added correct, per-session routing for every server→client message that carries an attribution key the proxy can track:

  • */list_changed → all sessions (global)
  • notifications/progress → the originating request's stream (correlated by the client-chosen, proxy-rewritten progressToken)
  • notifications/resources/updated → only sessions subscribed to that URI

But server-INITIATED requestselicitation/create and sampling/createMessage — arrive from the backend with a server-generated JSON-RPC id and no downstream-session attribution. Over a shared backend the proxy cannot know which client should answer, so #5934 securely refuses them (-32601 back to the backend so it unblocks) rather than mis-delivering. This is recorded as the sole accepted entry in test/conformance/expected-failures.yaml (tools-call-elicitation).

notifications/message (logging) is dropped for the same reason (unattributable content over a shared backend); its upstream level is still reconciled to the max verbosity across sessions.

Why a heuristic is not acceptable

Inferring the target from "only one tool-call is in flight" is racy (TOCTOU with concurrent sessions) and a wrong guess is a cross-session leak: one client could read or answer another client's elicitation/sampling prompt (content injection / prompt exfiltration). Forwarding must be attributable, not guessed.

Options (both require other work to land first)

  1. Per-session backend connections (the vMCP model). Give each downstream session its own connection to the backend so a server-initiated request is unambiguously attributable and the SDK correlates ids both ways — this is exactly why vMCP already forwards elicitation/sampling (#5875). For the streamable thv run proxy this is a transport re-architecture (backend lifecycle per session, resource cost, teardown), not a small change. Covers the 2025-11-25 (Legacy) revision.
  2. 2026-07-28 (Modern) / MRTR. The Modern revision removes server-initiated requests entirely (client pulls via MRTR), so the correlation problem disappears. Gated on the go-sdk v1.7 / Modern work (#5754, #5757). This is the cleaner long-term answer.

Acceptance

  • Elicitation/sampling round-trip correctly through the streamable proxy without any cross-session delivery, under whichever model is chosen.
  • Remove tools-call-elicitation from test/conformance/expected-failures.yaml (it should then pass); wire per-session logging delivery at the same time (notifications/message).

Related

  • #5744 / PR #5934 (the routing this builds on; baseline entry)
  • #5759 (MRTR-based elicitation/sampling passthrough in vMCP — vMCP-specific; this issue is the shared-backend streamable-proxy counterpart)
  • #5754, #5757 (go-sdk v1.7 / Modern-revision vocabulary)

Generated with Claude Code

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading PR #5934 and the streamable proxy routing it introduced, then inspect test/conformance/expected-failures.yaml and the tools-call-elicitation case. Compare the per-session vMCP work in #5759 with the Modern/MRTR dependencies in #5754 and #5757 before choosing an architecture. Done means elicitation and sampling round-trip without cross-session delivery, logging is delivered per session, and tools-call-elicitation is removed from the expected failures.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
api, backend, security, testing
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.