Skip to content

Adds first-class notification subscription support via a notifications/ directory convention#526

Open
0xKoller wants to merge 7 commits into
canaryfrom
first-class-notification-subscription-support-via-a-xmcp-411
Open

Adds first-class notification subscription support via a notifications/ directory convention#526
0xKoller wants to merge 7 commits into
canaryfrom
first-class-notification-subscription-support-via-a-xmcp-411

Conversation

@0xKoller

@0xKoller 0xKoller commented Mar 19, 2026

Copy link
Copy Markdown
Contributor
  • Adds first-class notification subscription support via a notifications/ directory convention, as proposed in [Feature] First-class notification subscription support via a notifications/ directory convention #525
  • Introduces subscribe(method, handler) exported from xmcp for handling MCP client→server notifications (roots/list_changed, cancelled, progress, initialized, and custom methods)
  • Follows the same file-based discovery pattern as tools, prompts, and resources
  • Adds documentation page and updates project structure, custom directories docs
  • Adds dedicated examples/notifications/ example with test script, readme available.

Update 04/20

Added example for custom notification
Screenshot 2026-04-20 at 10 07 46

@linear

linear Bot commented Mar 19, 2026

Copy link
Copy Markdown

@vercel

vercel Bot commented Mar 19, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
xmcp-website Ready Ready Preview, Comment Apr 20, 2026 1:08pm

@github-actions

github-actions Bot commented Mar 19, 2026

Copy link
Copy Markdown

Rspack Bundle Analysis

Build Assets Total Size (MB) Build Time
Main Compiler 4 5.28 5.44s
Runtime Compiler 6 4.34 5.83s

Main Compiler

Source: stats-main.json

Asset Chunk Size (KB) Size (MB)
cli.js cli 4942.14 4.83
index.js index 369.37 0.36
cloudflare.js cloudflare 81.70 0.08
detached-flush.js detached-flush 15.18 0.01

Total emitted JS: 5.28 MB

Runtime Compiler

Source: stats-runtime.json

Asset Chunk Size (KB) Size (MB)
http.js http 1042.77 1.02
adapter-nestjs.js adapter-nestjs 1018.99 1.00
adapter-nextjs.js adapter-nextjs 1013.54 0.99
adapter-express.js adapter-express 1011.30 0.99
stdio.js stdio 353.73 0.35
headers.js headers 1.73 0.00

Total emitted JS: 4.34 MB

Package Footprint (npm pack + npm install)

Item Size (KB) Size (MB)
Tarball (.tgz) 3383.96 3.30
dist/ 9926.77 9.69
node_modules/ 101349.03 98.97
dist + node_modules 111275.80 108.67

@0xKoller 0xKoller marked this pull request as ready for review March 23, 2026 13:59
@0xKoller 0xKoller requested a review from valebearzotti as a code owner March 23, 2026 13:59
@greptile-apps

greptile-apps Bot commented Mar 23, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds first-class notification subscription support to xmcp via a src/notifications.ts single-file convention (mirroring the existing src/middleware.ts pattern). It introduces defineNotifications() with fully typed handlers for all standard MCP client-to-server notifications (initialized, cancelled, progress, rootsListChanged, taskStatus) plus arbitrary custom methods, wires them through the compiler's file-watcher/code-generation pipeline, dispatches them in the stateless HTTP transport, and ships documentation and a working example.

Key changes:

  • defineNotifications() maps user-facing shorthand keys to full MCP method strings and returns a NotificationsConfig object consumed at runtime.
  • addNotificationsToServer() registers handlers on the low-level SDK server; for cancelled and progress it wraps the SDK's internal _notificationHandlers with a hard throw if the private field is missing — addressing the prior review concern about silent handler replacement.
  • stateless-streamable-http.ts now dispatches pure-notification POST batches through onmessage before returning 202, so handlers fire correctly in stateless mode.
  • All three previous review thread concerns (private API safety, AbortSignal lifecycle, project-structure tree rendering) appear to be addressed in this revision.
  • One minor inconsistency remains: the expected console output documented in the example's test script doesn't match the string the handler actually produces ("cancelled" vs "was cancelled").

Confidence Score: 5/5

Safe to merge; all prior P1 concerns are addressed and only a minor documentation string mismatch remains.

The three issues flagged in earlier review rounds — silent handler replacement for SDK-internal methods, unaborted AbortSignal in NotificationExtra, and the notifications directory appearing as a child of resources in the project-structure tree — are all resolved in this revision. The implementation follows established patterns in the codebase, the private-API access now fails loudly at startup rather than silently, and the type system correctly narrows handler signatures. The only remaining finding is a P2 documentation inconsistency in the example test script.

examples/notifications/test-notifications.ts (minor expected-output string mismatch)

Important Files Changed

Filename Overview
packages/xmcp/src/runtime/utils/notifications.ts New utility that registers user notification handlers on the MCP server, wrapping SDK-internal handlers for cancelled/progress via a hard-fail guard on _notificationHandlers; implementation is solid.
packages/xmcp/src/runtime/transports/http/stateless-streamable-http.ts Adds onmessage dispatch for pure-notification POST batches so handlers fire before the 202 response is sent; logic is correct for both pure-notification and mixed batches.
packages/xmcp/src/define-notifications.ts Maps user-facing shorthand keys to full MCP method strings and returns a typed NotificationsConfig; TypeScript generics correctly handle known keys vs custom string keys.
packages/xmcp/src/types/notification.ts Defines NotificationParams, NotificationHandler, and NotificationsConfig; types are accurate and the paramless-handler distinction via conditional type is clean.
packages/xmcp/src/runtime/utils/server.ts Integrates notifications loading into createServer and configureServer alongside existing tools/prompts/resources; pattern is consistent with the rest of the codebase.
packages/xmcp/src/compiler/index.ts Watches ./src/notifications.ts with onAdd/onUnlink hooks following the same pattern as middleware; hasNotifications is correctly included in telemetry.
examples/notifications/test-notifications.ts Test client for the notifications example; contains a minor documentation inconsistency in the expected output for the cancelled notification.
apps/website/content/docs/core-concepts/notifications.mdx New documentation page covering client-to-server notifications, handler signatures, custom methods, SDK handler preservation, and error handling; content is accurate and well-structured.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: examples/notifications/test-notifications.ts
Line: 481-483

Comment:
**Expected output comment doesn't match handler output**

The comment on line 483 says the server will print `"Request req-42 was cancelled: User clicked cancel"`, but the actual handler in `src/notifications.ts` produces `"Request req-42 cancelled: User clicked cancel"` (no `"was"`). A developer running the example would see a different string than advertised.

```suggestion
  console.log(
    "   Expected: [notification] Request req-42 cancelled: User clicked cancel\n"
  );
```

How can I resolve this? If you propose a fix, please make it concise.

Reviews (2): Last reviewed commit: "Asks and fixes" | Re-trigger Greptile

Comment thread packages/xmcp/src/runtime/utils/notifications.ts
Comment thread packages/xmcp/src/runtime/utils/notifications.ts Outdated
Comment thread apps/website/content/docs/getting-started/project-structure.mdx Outdated

@valebearzotti valebearzotti left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

notifications are not another element/concept of mcp servers, rather than interceptors. we shouldn't add them in another folder. it breaks the foundational DX entirely. should be handled separately if any under the middleware as interceptors. we're only console logging stuff there's no reason to make a syntax for all this.
if any we should export methods like notification.cancelled() and then add whatever we want to track inside it. but should be treated as interceptors. this should be significantly simpler.

@0xKoller

Copy link
Copy Markdown
Contributor Author

@greptileai review

@0xKoller

Copy link
Copy Markdown
Contributor Author

Architecture

Notifications no longer use a src/notifications/ directory with per-file auto-detection. Instead, they follow the same single-file pattern as src/middleware.ts. Users create a single src/notifications.ts file with all handlers defined via defineNotifications(). The core directories remain tools/resources/prompts only.

DX improvements

  • Short camelCase keys instead of full MCP method strings (cancelled instead of notifications/cancelled, rootsListChanged instead of notifications/roots/list_changed). xmcp maps them internally.

  • Only valid keys are allowed with full autocomplete.

  • Custom notification methods are supported by using the full method string as the key.

  • Added taskStatus notification (notifications/tasks/status), completing all 5 client-to-server notifications from the ClientNotification union in the SDK.

@valebearzotti

Copy link
Copy Markdown
Collaborator

gotta update to match the newer config style + can you add a screenshot of a custom notification and how it's tracked?

@0xKoller

Copy link
Copy Markdown
Contributor Author

Just pushed latest changes with the requested screenshot example of custom notification

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.

[Feature] First-class notification subscription support via a notifications/ directory convention

2 participants