Skip to content

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

Open
0xKoller wants to merge 5 commits intocanaryfrom
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 5 commits intocanaryfrom
first-class-notification-subscription-support-via-a-xmcp-411

Conversation

@0xKoller
Copy link
Copy Markdown
Collaborator

  • 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.

@linear
Copy link
Copy Markdown

linear bot commented Mar 19, 2026

@vercel
Copy link
Copy Markdown
Contributor

vercel bot commented Mar 19, 2026

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

Project Deployment Actions Updated (UTC)
xmcp-website Ready Ready Preview, Comment Mar 30, 2026 4:32pm

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 19, 2026

Rspack Bundle Analysis

Build Assets Total Size (MB) Build Time
Main Compiler 4 6.34 5.05s
Runtime Compiler 6 5.37 5.62s

Main Compiler

Source: stats-main.json

Asset Chunk Size (KB) Size (MB)
cli.js cli 6021.05 5.88
index.js index 369.16 0.36
cloudflare.js cloudflare 81.70 0.08
detached-flush.js detached-flush 15.18 0.01

Total emitted JS: 6.34 MB

Runtime Compiler

Source: stats-runtime.json

Asset Chunk Size (KB) Size (MB)
http.js http 1308.00 1.28
adapter-nestjs.js adapter-nestjs 1284.28 1.25
adapter-nextjs.js adapter-nextjs 1278.35 1.25
adapter-express.js adapter-express 1276.59 1.25
stdio.js stdio 345.57 0.34
headers.js headers 1.33 0.00

Total emitted JS: 5.37 MB

Package Footprint (npm pack + npm install)

Item Size (KB) Size (MB)
Tarball (.tgz) 4635.66 4.53
dist/ 12053.24 11.77
node_modules/ 101232.81 98.86
dist + node_modules 113286.04 110.63

@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
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 23, 2026

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
Copy link
Copy Markdown
Collaborator

@valebearzotti valebearzotti left a comment

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
Collaborator Author

@greptileai review

@0xKoller
Copy link
Copy Markdown
Collaborator 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?

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