Skip to content

Add a showcase config that broadcasts resource updates for --load-subscribe - #2972

Merged
cliffhall merged 2 commits into
v2/mainfrom
v2/feat/2964-broadcast-resource-updates
Nov 12, 2026
Merged

cliffhall merged 2 commits into
v2/mainfrom
v2/feat/2964-broadcast-resource-updates

Conversation

@cliffhall

Copy link
Copy Markdown
Member

Closes #2964

What

A new showcase config, test-servers/configs/subscriptions-broadcast-http.json (legacy era, port 3225), whose resource updates reach every subscribed session, so --load-subscribe can be tried by hand without writing a server.

It is the stock subscriptions-legacy-http.json (three numbered_resources + update_resource, subscriptions: true) plus one new composable option, broadcastResourceUpdates: true:

  • test-servers/src/resource-update-broadcast.ts — createResourceUpdateBroadcast(), a hub shared by every session of one server. Each session joins it in createMcpServer (after its resources are registered) and leaves on server.onclose (chained, not replaced). publish(uri, text) applies the text to every session's copy of the resource and sends notifications/resources/updated to each session subscribed; a session whose send fails is skipped rather than failing the call. The latest text per URI is replayed to a session that joins later, so a fresh session reads the updated content. setResourceText is the one place a resource's text is replaced, now shared with the non-broadcast path.
  • resolveConfig turns broadcastResourceUpdates: true into one hub on the resolved ServerConfig.resourceUpdateBroadcast; every session is built from that one object, so they all share it. In-process callers can pass a hub directly.
  • update_resource publishes through the hub when there is one (and reports how many subscribed sessions it notified); without one its behavior is unchanged.

Docs: a row and a section in docs/test-servers.md, a "Common starting points" row in the test-servers skill, and a pointer from the CLI README's --load-subscribe section.

Evidence — the issue's run, by hand

Built server + launcher from this branch, same command against each config:

$ node clients/launcher/build/index.js --cli <url> --transport http --method tools/call \
    --tool-name update_resource --tool-arg uri=test://resource_1 --tool-arg 'text=v{{seq}}' \
    --load --load-requests 10 --load-subscribe test://resource_1 --load-subscribers 5 --format text

== subscriptions-legacy-http.json (stock, port 3221)
  Fan-out:    `test://resource_1`, triggered; 5 of 5 sessions subscribed, 0 failed
  Delivered:  10 of 50 notifications (10 updates × 5), 40 missed across 4 sessions, 0 unexpected
-- resources/read from a fresh session: "Content for resource 1"

== subscriptions-broadcast-http.json (new, port 3225)
  Fan-out:    `test://resource_1`, triggered; 5 of 5 sessions subscribed, 0 failed
  Delivered:  50 of 50 notifications (10 updates × 5), 0 missed across 0 sessions, 0 unexpected
-- resources/read from a fresh session: "v10"

== subscriptions-broadcast-http.json, --load-requests 100 --load-subscribers 50
  Fan-out:    `test://resource_1`, triggered; 50 of 50 sessions subscribed, 0 failed
  Delivered:  5000 of 5000 notifications (100 updates × 50), 0 missed across 0 sessions, 0 unexpected
  Delivery:   min 1.09 / mean 4.07 / p50 3.56 / p90 6.18 / p95 7.7 / p99 15.09 / p99.9 18.44 / max 19.45 ms
-- resources/read from a fresh session: "v100"

Tests

clients/cli/__tests__/subscriptions-broadcast.test.ts:

  • the showcase config, resolved from the JSON file, under a real --load-subscribe CLI run: 4 updates × 3 subscribers = 12 delivered, 0 missed; a fresh session reads the last update; and the hub is empty afterward (every session left on close);
  • the stock legacy config under the same run: only the caller is notified (4 delivered, 8 missed) and no hub is created — so the opt-in stays an opt-in;
  • the hub itself: applies to every member, counts only subscribed sessions, skips a failing send, replays the latest update to a late joiner, stops after leave.

test-servers/ is test infrastructure outside every coverage include, so no per-file gate applies to it.

npm run local:gate green.

🤖 Generated with Claude Code

…scribe (#2964)

subscriptions-broadcast-http.json sets the new broadcastResourceUpdates
option: every session of the composable server joins one shared hub, and
update_resource publishes through it, so one call updates the resource in
every session and notifies each one subscribed. A --load-subscribe fan-out
run against it delivers every notification instead of reporting all but
the calling session as missed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

A rejected sessionless HTTP POST can leave an unregistered server retained in the hub, leaking resources and escaping shutdown cleanup.

1 open issue
What changed in this PR

Adds a legacy HTTP showcase server for manually exercising --load-subscribe fan-out, addressing #2964 within the existing composable test-server infrastructure.

Changes:

  • Adds an opt-in broadcast hub that updates resource content across sessions, notifies subscribers, and replays the latest content to new sessions.
  • Adds a showcase configuration and tests for broadcast delivery, stock behavior, replay, and cleanup.
  • Links the fixture from the test-server guide, CLI README, and test-server skill.
File Description
test-servers/​src/​test-server-fixtures.ts Routes resource updates through the optional hub.
test-servers/​src/​resource-update-broadcast.ts Implements broadcasting, replay, and shared text replacement.
test-servers/​src/​resolve-config.ts Creates one hub per opted-in configuration.
test-servers/​src/​load-config.ts Declares the JSON broadcast option.
test-servers/​src/​index.ts Exports the broadcast API.
test-servers/​src/​composable-test-server.ts Connects resource state and server lifecycle to the hub.
test-servers/​configs/​subscriptions-broadcast-http.json Adds the legacy HTTP broadcast showcase.
docs/​test-servers.md Explains broadcast behavior and manual fan-out usage.
clients/​cli/​README.md Points --load-subscribe users to the fixture.
clients/​cli/​__tests__/​subscriptions-broadcast.test.ts Tests CLI fan-out and hub behavior.
.claude/​skills/​test-servers/​SKILL.md Adds the fixture to common starting points.

Review effort: Balanced

A sessionless HTTP POST the transport rejects builds a server that is never
registered as a session nor closed; joined at construction, the hub held it
forever. Nothing can subscribe before initialized and the hub replays the
latest updates on join, so waiting loses nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
@cliffhall

Copy link
Copy Markdown
Member Author

Copilot round 1 — 1 finding, fixed in 16bc7a4:

  • Unregistered server from a rejected sessionless POST retained in the hub → the session now joins on oninitialized rather than at construction; regression test added.

No suppressed comments. Requesting round 2.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

No active review issues block approval of the opt-in broadcast showcase server change.

No open issues 🎉

1 resolved since last review

Review effort: Balanced

@cliffhall

Copy link
Copy Markdown
Member Author

Copilot review loop closed — round 2 was clean (no inline comments, no suppressed comments; round 1's finding resolved). Exit: clean round.

@cliffhall
cliffhall merged commit ddf3f41 into v2/main Nov 12, 2026
12 checks passed
@cliffhall
cliffhall deleted the v2/feat/2964-broadcast-resource-updates branch November 12, 2026 06:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

v2 Issues and PRs for v2

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Test servers: a showcase config that broadcasts resource updates, for manual --load-subscribe fan-out runs

2 participants