npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@stagewise/mcp-extension-push-notifications

v0.1.1

Published

Push Notifications extension for the Model Context Protocol

Readme

MCP Push Notifications Extension

Typed schemas and client/server facades for identity-scoped durable Push Notifications in the Model Context Protocol.

Extension identifier: io.stagewise/push-notifications

Delivery model

  • io.stagewise/push-notifications/get retrieves pending unacknowledged events for the authenticated consumer.
  • io.stagewise/push-notifications/ack records durable client acceptance and removes events from the pending view.
  • io.stagewise/push-notifications/event provides optional low-latency delivery.

Delivery is at least once. Servers persist before publishing. Clients accept and deduplicate by eventId before acknowledging. Live delivery is a latency optimization; pending retrieval is recovery. This extension is a durable queue, not a replayable historical event stream.

Every event carries ordered MCP ContentBlock values in content and may carry producer-owned JSON in data. The runtime schema is the canonical ContentBlockSchema from this package's pinned MCP SDK revision, so text, images, audio, embedded resources, and resource links use standard MCP representations. Transport support does not imply that every client or model can consume every content type.

const event = {
  eventId: crypto.randomUUID(),
  sourceId: 'chat:local',
  type: 'chat.message.received',
  createdAt: new Date().toISOString(),
  content: [{ type: 'text', text: 'Hello' }],
  data: { messageId: '42' },
};

Small media may be carried inline as base64 MCP image/audio blocks. Larger or long-lived media should use MCP resource links whose authorization and retention match the event feed.

Client

Register before connecting:

const events = registerPushNotificationsClient(mcpClient, {
  async onEvent({ params }) {
    const accepted = await inbox.commit([params.event]);
    if (accepted) await events.acknowledgeEvents({ eventIds: [params.event.eventId] });
  },
});

Subscribe first, then drain pending pages:

const subscription = await events.listen();

while (true) {
  const page = await events.getEvents({ limit: 100 });
  await inbox.commit(page.events);
  if (page.events.length > 0) {
    await events.acknowledgeEvents({
      eventIds: page.events.map((event) => event.eventId),
    });
  }
  if (!page.hasMore) break;
}

subscription.closed.catch(() => reconnect());

A lost acknowledgement can cause the same events to return again. eventId is the idempotency key. listen() resolves after subscription acknowledgement; closed settles when the long-lived request completes or fails.

Clients must treat content, MIME declarations, linked resources, and event data as untrusted input.

Server facade

Storage and consumer resolution remain application concerns:

const events = registerPushNotificationsServer(mcpServer, {
  getEvents: ({ limit }, context) => store.pending(consumerFrom(context), limit),
  acknowledgeEvents: ({ eventIds }, context) =>
    store.acknowledge(consumerFrom(context), eventIds),
});

await store.append(consumerKey, event);
await events.sendEvent({ event }, { metadata: requestMeta });

Acknowledgement is idempotent. Acknowledged IDs disappear immediately from pending retrieval. Retention of payloads and acknowledgement tombstones is server policy. Applications that expose several MCP extensions can pass registerDiscoveryHandler: false and register one composed server/discover handler.

HTTP subscriptions

The HTTP manager requires a trusted consumer-key resolver and targeted publication:

const subscriptions = createPushNotificationsHttpSubscriptionManager(mcp.fetch, {
  resolveConsumerKey: (request) => authenticatedConsumerKey(request),
  onSubscriptionStateChanged: (consumerKey, active) => {
    updateConsumerActivity(consumerKey, active);
  },
});

app.all('/mcp', (context) => subscriptions.fetch(context.req.raw));

await store.append(consumerKey, event);
subscriptions.publish(consumerKey, { event });

Only one active live subscription is retained per consumer key; a new subscription replaces the previous stream. The key is derived from authentication context and never accepted from a Push Notifications payload. The optional lifecycle callback reports installation and removal of the active stream; observer failures are isolated from protocol handling.

Capabilities and schemas

Every facade request declares the client capability. Server support is discovered lazily and cached. src/spec.types.ts is the source of truth; generated Zod schemas and schema.json must remain fresh.

pnpm generate:schemas
pnpm check:schema
pnpm typecheck
pnpm test
pnpm build

See specification/draft/events.md for the normative contract.