Commit graph

20 commits

Author SHA1 Message Date
Peter Alexander
905ade36a2
Bound webhook retries and add a per-delivery no-retry signal
Webhook retries were open-ended exponential backoff. Servers should now cap
both attempt count and elapsed retry window (illustrative: 3-5 attempts within
10-15 minutes); exhausted events are abandoned for watermark purposes and
recoverable via cursor replay when the upstream is durable. A receiver can
respond 410 Gone to suppress retries for a delivery it deliberately rejects,
treated like 413 as non-retryable without affecting the subscription.
2026-06-15 12:39:12 +01:00
Peter Alexander
2d798bfeab
Clarify that an absent cursor is equivalent to cursor: null
Cursor Lifecycle now states that omitting the cursor field is identical to
sending an explicit null in requests, responses, and delivered payloads, and
that neither side may fail on omission. This lets emit-only servers never
emit the field and lets clients with nothing persisted omit it.
2026-06-15 12:38:01 +01:00
Peter Alexander
082166f0ba
Rename inner subscription params field to arguments
The subscribe/poll/stream requests nested a params object inside JSON-RPC
params, which read as params.params in code and docs. The inner field is now
arguments, matching the inputSchema/arguments pairing on tools/call. Applies
to the wire examples, the subscription identity tuple, and the SDK guidance
snippets; the outer JSON-RPC params envelope is unchanged.
2026-06-15 12:37:28 +01:00
Peter Alexander
2f47edf7e2
Allow prior out-of-band verification as a third endpoint verification path 2026-06-12 19:41:27 +01:00
Peter Alexander
99f3589c83
Make webhook TTL client-suggested with explicit no-expiry option
The client now suggests a subscription lifetime via ttlMs on
events/subscribe; the server's refreshBefore grant is authoritative and
SHOULD only shorten (clamping up to a server minimum TTL is the one
sanctioned exception). An explicit ttlMs: null requests no expiry, and
refreshBefore: null grants it.

This reframes server-side durability as the server's choice, coupled to
the TTLs it grants: short grants keep the existing in-memory soft-state
model, while long or no-expiry grants shift durability and cleanup onto
the server (persist across restarts, persist verification status,
failure-based GC instead of TTL expiry, no expectation of unsubscribe).
Rationale: long-lived tenants with many subscriptions shouldn't be
forced into a server-invented refresh cadence, and a one-year TTL is
practically unlimited anyway — better to offer no expiry explicitly and
attach the real obligations to it.
2026-06-11 16:57:32 +01:00
Peter Alexander
1034fbbc52
Merge delivery-modes rationale from origin into design-sketch 2026-05-22 13:47:55 +01:00
Peter Alexander
567be29eb9
Consolidate event error codes into reusable general-purpose set
Replace the seven event-specific error codes (-32011..-32017) with five
general-purpose codes that carry their specifics in a typed `data` payload:

  NotFound, Forbidden, ResourceExhausted, Unsupported, CallbackEndpointError

Bad or non-https callback URLs and malformed secrets now fold into the
standard InvalidParams (-32602). The remaining codes are contiguous
(-32011..-32015) and named for reuse across the protocol rather than scoped
to events; clients still distinguish every case they act on by code, by
method, or by a typed `data` discriminator.

Also corrects the stale -32002 ResourceNotFound reference (no longer present
in the schema) and records the trade-off in Key Design Decisions.
2026-05-22 13:45:50 +01:00
Peter Alexander
9f9cdce8b1
Resolve endpoint verification and add Security threat model
- Replace Open Question 6 with normative endpoint-verification rules:
  a mandatory intent challenge (or allowlist) before activating webhook
  delivery, cached per (principal, url); add -32014 EndpointVerificationFailed
  and fill the verification control-envelope row.
- Add optional asymmetric server signing (Standard Webhooks v1a,) for
  known-server allowlists. The webhook-signing key is discovered from an
  origin the client already authenticates: inline webhooks.signingKeys on
  the SEP-2127 server card if it lands first, otherwise a standalone
  /.well-known/mcp-webhook-jwks.json document (one chosen at SEP-review time).
- Correct the webhook-secret rationale (HMAC stops deception, not flooding)
  and add Key Design Decisions rows for endpoint verification and server identity.
- Add a Threat Model summary table to Security Considerations.
2026-05-22 13:07:23 +01:00
Peter Alexander
754be6ce47
Add rationale for three delivery modes to Key Design Decisions
Records why poll, push, and webhook all exist as protocol-level modes
rather than picking one: the deployment topologies are disjoint and no
mode subsumes another. Acknowledges the departure from the usual
one-way-to-do-it stance and the spec/SDK cost it carries, and notes the
rejected alternative (one mode + proxy synthesis) and why.
2026-05-14 18:36:25 +01:00
Peter Alexander
197c32b4c5
Align push correlation with SEP-2575 and rename duration fields to Ms suffix
- Push notifications now carry the parent events/stream request id in params._meta["io.modelcontextprotocol/subscriptionId"] (per SEP-2575) instead of params.requestId
- Rename nextPollSeconds -> nextPollMs and maxAge -> maxAgeMs (integer milliseconds) to match the MCP duration-field convention; example values and prose updated
2026-05-10 16:45:42 +01:00
Peter Alexander
d4faef9758
Add nextCursor to events/list and _meta to event types and occurrences
events/list response now shows nextCursor for consistency with base MCP list pagination. Event-type definitions and EventOccurrence both gain optional _meta, matching Tool/Resource/Prompt.
2026-05-01 20:15:53 +01:00
Peter Alexander
8c6a1343cc
Redact example secret to avoid triggering secret scanners 2026-04-30 23:11:37 +01:00
Peter Alexander
58df0af65b
Revise Events design sketch: gap model, replay bounding, identity hardening, decisions table
- Unify cursor expiry into in-band `truncated: true` (drop -32014 CursorExpired): poll result body, push re-sends notifications/events/active mid-stream, webhook refresh response + {"type":"gap"} control envelope; client never re-subscribes for a gap
- Add `maxAge` (seconds) to bound replay across all modes; servers may decline by truncating to now
- Heartbeat carries cursor (push) and refresh response carries the safe-watermark cursor (webhook) so the client's position advances during quiet periods
- Webhook now requires an authenticated principal (key is (principal, url, name, params)); drop body `id` (route via X-MCP-Subscription-Id); add Non-event webhook bodies subsection (gap/terminated/verification with `type` discriminator)
- Drop subscription `id` from poll/stream (one sub per request; correlate via JSON-RPC id / requestId)
- Rewrite Summary as a how-it-works overview; add Key Design Decisions table; add OQ5 (task state changes as events) and OQ6 (endpoint verification + asymmetric server signing)
- Consistency pass across diagrams, examples, error lists, and appendix step references
2026-04-30 23:02:10 +01:00
Peter Alexander
26ba242ff0
Revise Events design sketch: Standard Webhooks, client-supplied secret, tuple-based identity
- Adopt Standard Webhooks as the delivery signature scheme (webhook-id/timestamp/signature, v1 base64, multi-sig rotation); keep X-MCP-Subscription-Id as the one MCP-specific header
- Webhook secret is now client-supplied and REQUIRED (whsec_ + 24-64B base64, server MUST reject otherwise); server never generates one
- Subscription identity is (principal?, delivery.url, name, params); the response returns a server-derived id used only for routing, never as input
- Revert to no-mode-mandatory for delivery modes
- TLS is now MUST for delivery.url
- Allow cursor: null in any delivery for event types without replay; clarify CursorExpired always implies a possible gap
2026-04-30 15:02:32 +01:00
Peter Alexander
249efdcc63
Revise Events design sketch: poll mandatory, webhook delivery profile, SEP review fixes
Make poll a mandatory delivery mode so every events-capable server is reachable by every client (push/webhook remain optional optimizations). Add a Webhook Delivery Profile subsection with WAF-relevant invariants (POST-only, required headers, 256 KiB body SHOULD, TLS termination) and defer User-Agent to SEP-1336 and egress-IP discovery to SEP-2127. Incorporate further fixes from adversarial review.
2026-04-30 14:30:53 +01:00
Peter Alexander
3dfc90566d
Remove revision deltas doc 2026-04-17 11:53:51 +01:00
Peter Alexander
641eca02a7
Revise Events design sketch: webhook cursor model, subscription identity, security hardening
Resolve internal contradictions and design gaps found in review:
- Webhook cursors are now client-owned (per-event retry, no server watermark); name/params/delivery.url are immutable identity fields; unsubscribe requires delivery.url
- Unify error/terminated shape across modes; define heartbeat and terminated notifications; renumber error codes to -32011..-32016 to avoid base-spec collision
- SSRF: validate at delivery time, block fe80::/10, MUST NOT follow redirects; HMAC: X-MCP-Subscription-Id header, hex-lowercase encoding, mandatory timestamp, retry regenerates
- events/stream scoped to event notifications only (existing GET SSE retained); StreamEventsResult send rules clarified per transport
- Broadcast emit now uses author-supplied match/transform hooks with ctx; poll lease keyed on (principal, eventName, hash(params)); eventId SHOULD be upstream's stable identifier
- Qualify at-least-once for emit-only types; refresh reactivates suspended delivery; capability uses listChanged
- Add Open Questions on multi-event-name subscriptions and ownership-verification handshake

Add design-sketch-revision-deltas.md documenting all changes for implementations built against the prior revision.
2026-04-17 11:52:12 +01:00
Peter Alexander
4f8a1129d8
Add delivery-mode diagrams, e2e webhook appendix, and clarifications
- Mermaid sequence diagrams for poll/push/webhook delivery
- Appendix: end-to-end GitHub example with Redis sub store
- Push: clarify multiple concurrent streams allowed; note HTTP/2 dependency
- Webhook: specify 2xx ACK requires durability or end-consumer processing
- Use nextPollSeconds consistently
2026-04-10 12:41:21 +01:00
Peter Alexander
603f5de12a
Add Events design sketch proposal 2026-04-09 21:30:11 +01:00
Peter Alexander
3314cd8dba
Initial commit: WG scaffolding (README, CONTRIBUTING, LICENSE, CODEOWNERS) 2026-04-09 16:53:28 +01:00