Skip to content

Resource Events: keep copies of resources current - #19

Open
lmaccherone wants to merge 15 commits into
modelcontextprotocol:mainfrom
lumenize:resource-webhooks-on-events
Open

lmaccherone wants to merge 15 commits into
modelcontextprotocol:mainfrom
lumenize:resource-webhooks-on-events

Conversation

@lmaccherone

Copy link
Copy Markdown

Motivation and Context

This adds proposals/XXXX-resource-events.md, an Extensions Track proposal that lets an MCP client keep copies of resources current. For each change, a server sends an event of the extension's own, mcp.resource.collection.updated, through SEP-3415, Events. The event names the collection a client watches and each resource in it that changed, with its new revision, and carries none of the contents. A client that lacks a revision catches up on the whole collection with one call, resources/collection/catchup, from the moment it was previously caught up to. So a lost, repeated or late event does no harm, and a client back from a night away catches up on just what changed, instead of reading every resource again.

It builds on SEP-3415 at b08e4f9, and on the Filesystems working group's strawman of resource operations, experimental-ext-filesystems#2, for writes and revisions. As Peter (@pja-ant) suggested on Discord on 2026-10-20, it starts as a PR here for the group to comment on, like Luca Chang's Task Event Sources draft, #2.

Feedback on the core design would help most. A server records each change at a moment on a timeline, and a subscriber catches up from the moment it was previously caught up to, much as a database follower reads its leader's op-log, but using each changed resource as it now stands, instead of every op on the way.

Reference implementation

lumenize/resource-webhooks-on-events builds the extension on Cloudflare Workers, without an MCP SDK, with the spec's schema.ts for 2026-07-28 as its only schema. Its client speaks MCP to the server over HTTP, with JSON responses and no SSE stream, and the server delivers webhooks over https to a gateway, implementing SEP-3415's forward-proxy deployment, which passes each event to the client over a WebSocket. 457 tests pass, and a deployed tier runs the proposal's happy path on Cloudflare. MCP's conformance suite passes against the server and the client, apart from a listed baseline of features the prototype has no use for, such as prompts. Only the prototype's own client and server have run the extension so far, and the proposal's § Reference Implementation lists what is designed but not built.

A note on the Rationale

Each decision in the Rationale opens by restating its rule, so it reads without scrolling back to the Specification. That makes the section repetitive on purpose. If the group prefers, it can be cut significantly before this merges.

🤖 Generated with help from Claude Code

lmaccherone and others added 14 commits November 4, 2026 04:25
Split out of lumenize/resource-webhooks-on-events' master plan: its proposal
half becomes the Specification, its Decisions the Rationale, without the
rows about the prototype alone, and its open questions Q6 and Q7. New are
the Abstract, Backward Compatibility, Security Implications, the Reference
Implementation and the build's two findings. SEP-3415 is pinned at b08e4f9
and the strawman at 8d0fa88. The first flavor's text stays in 2b5276a.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Larry's edits split the Abstract, name the extension's event in the
SEP-3415 reference, and ask for the skills work in References and for
D1's reason. The Rationale is now a bold-lead paragraph per decision under
five topics. D3, D9, D11, D16, D18, D21, D22, D23 and D24 go, since the
rules already carry their answers: R22 now says its read carries Mcp-Name,
and R23 that a start-over replaces copies with earlier revisions after a
restore. D1 says why a second lifecycle loses, D6 gives this design's
reason for ordered moments, and D7 covers a client's own writes. SEP-2640
joins the References.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every rule now says what binds with MUST, SHOULD or MAY, and the
Specification says it reads them as BCP 14 does. Where a keyword landed on
something that doesn't act, such as a change or a reply, the sentence names
the server or the client instead. R10's eventId is a SHOULD, as in
SEP-3415. R8 folds into the catch-up definition, which now says a client
is caught up on each uri-specification. D19 no longer says a merge leaves
truncated false, since SEP-3415 settles that for a subscription with no
replay.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The outline, the four problems and the use cases stay. Explanations the
group shares, such as why long streams and polling hurt and what webhook
delivery is for, become shorthand. Problem 4 now leads with the missing
collection subscription, as Larry asked, and the closing map's answer to
it matches.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A read that comes in pages and a catch-up that stops short open the less common cases. A
  read's paging moves out of R22 into R26, R14 moves beside it, and D14 says why the two page
  differently.
- R25: catch-ups and collection reads MUST cover only what the principal may read, events SHOULD,
  and a server MAY start a principal over when its access changes. Q6 asks whether partial
  access gets an error or a partial answer.
- Findings goes. Its Cloudflare egress finding becomes a note in Security, and the catch-up cost
  it reported was the prototype's own.
- Q7's SDK helper folds into (a), events are coalesced rather than merged, R13 defers to hasMore,
  and the happy path says where A lists its template.
- Wording that read as machine-drafted is plainer: the Abstract's last line, the Specification's
  opener, and three long sentences split.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The identifier becomes io.modelcontextprotocol/resource-events, beside the
strawman's resource-operations and SEP-3415's events, since a webhook is one
of two ways a client hears of a change. The file follows, as
proposals/XXXX-resource-events.md.

The strawman's head, db67f78, requires ifMatch on resources/delete and drops
uri from a write's contents. Nothing the proposal says of the strawman
changes with it. The prototype now has 457 tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… in the next

A webhook is how a server must offer the event, but a client can also poll
the catch-up, so the first sentence no longer names it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ing also works

The prototype and the Motivation speak of webhooks, but nothing here stops
the event from going over SEP-3415's push delivery, and a client can poll
with no stream at all.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SEP-3415 makes no delivery mode mandatory, and nothing in the Rationale
argued for requiring webhooks, so a server that offers only push, such as a
stdio server, can now declare the extension. The References entry and the
Security section say the same.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A patch is {mimeType, text} or {mimeType, blob}, as a strawman write's contents is, in place of
a patchMimeType beside a patch carried as JSON for JSON-based formats.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nc, and add previousMoment

One method reads a collection, subscribes and catches up, so the gap between
reading and subscribing is gone (D28). Each event names the moment of the one
before it on the same subscription, so a lost event is found at the next one
and a writer moves forward with no call (D29). Q8 asks whether a method other
than events/subscribe may create an Events subscription. SEP-3415 is cited as
Accepted on 2026-11-11. The examples' code blocks are set one field to a line.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant