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

mcp-backplane

v0.1.0

Published

Redis backplane for MCP TypeScript servers: a ServerEventBus that fans subscriptions/listen notifications out across nodes, and an EventStore for resumable sessionful transports.

Readme

mcp-backplane

Redis backplane for MCP TypeScript servers that run on more than one process.

The official SDK ships in-memory implementations for the two places where server state has to cross node boundaries, and its docs tell you to bring your own for real deployments. This package is that implementation:

  • RedisServerEventBus implements the v2 SDK's ServerEventBus over Redis pub/sub. Without it, subscriptions/listen breaks behind a load balancer: handler.notify.toolsChanged() on one node never reaches a subscriber whose stream another node holds.
  • RedisEventStore implements EventStore over Redis Streams, for sessionful Streamable HTTP deployments (the v1 SDK, or v2's legacy transport). Dropped SSE streams resume from any node via Last-Event-ID, and survive restarts.

No runtime dependencies. Bring your own Redis client — both node-redis (v4+) and ioredis (v5+) work and are detected automatically.

Install

npm install mcp-backplane

Fan notifications out across nodes (v2 SDK)

import { createMcpHandler } from "@modelcontextprotocol/server";
import { RedisServerEventBus } from "mcp-backplane";
import { createClient } from "redis";

const pubClient = createClient({ url: process.env.REDIS_URL });
const subClient = pubClient.duplicate();
await Promise.all([pubClient.connect(), subClient.connect()]);

const bus = new RedisServerEventBus({ pubClient, subClient });

const handler = createMcpHandler(buildServer, { bus });

Give every node the same construction and handler.notify.resourceUpdated(uri) on any node reaches the open subscriptions/listen streams on all of them.

Two clients because Redis dedicates a subscribed connection to pub/sub; duplicate() in either library gives you the second one. Both libraries re-subscribe on their own after a reconnect.

Options

| option | default | | | ----------- | ------------------- | -------------------------------------------------------- | | pubClient | required | client used for PUBLISH | | subClient | required | client used for SUBSCRIBE (separate connection) | | channel | mcp:server-events | must match on every node | | onError | console.error | publish failures, bad payloads, throwing listeners | | library | auto-detected | 'node-redis' | 'ioredis' if detection guesses wrong |

bus.ready() resolves once the subscription is confirmed — worth awaiting during startup, since events published elsewhere before that can be missed. bus.close() unsubscribes; closing the clients stays your job.

Events with kinds this package doesn't know about pass through unchanged, so SDK releases that add new ServerEvent variants don't need a lockstep upgrade here.

Resumable sessions (v1 SDK / v2 legacy transport)

import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { RedisEventStore } from "mcp-backplane";
import { randomUUID } from "node:crypto";

const transport = new StreamableHTTPServerTransport({
  sessionIdGenerator: () => randomUUID(),
  eventStore: new RedisEventStore({ client, ttlSeconds: 60 * 60 }),
});

Same option on v2's NodeStreamableHTTPServerTransport for 2025-era sessionful serving. When a client reconnects with Last-Event-ID, any node that shares the Redis instance can replay what it missed — the event ids encode the stream they belong to.

Options

| option | default | | | -------------------- | ------------- | ------------------------------------------------ | | client | required | node-redis or ioredis client | | keyPrefix | mcp:stream: | Redis key is keyPrefix + streamId | | maxEventsPerStream | 1024 | approximate XADD MAXLEN ~ cap; null for none | | ttlSeconds | off | refresh EXPIRE on every write | | replayBatchSize | 100 | events per XRANGE during replay | | library | auto-detected | as above |

Size maxEventsPerStream to your notification volume — a client further behind than the cap loses the trimmed messages.

An InMemoryEventStore with identical behavior ships too, for tests and single-process dev.

Important behavior

  • Redis pub/sub is fire-and-forget. If a node is disconnected when an event is published, that node misses it — same trade-off @socket.io/redis-adapter makes. List-changed notifications are hints by design, so a missed one costs a client a stale cache until the next change, not correctness.
  • publish() never throws and never blocks on Redis: local listeners are invoked synchronously, the PUBLISH is fire-and-forget, and failures go to onError.
  • storeEvent propagates Redis errors; the SDK transport handles a rejected write by tearing down the stream rather than silently dropping resumability.

Roadmap

  • Postgres backends (LISTEN/NOTIFY bus, single-table event store)
  • NATS bus
  • A durable task store, once the SDK's tasks-extension runtime lands (typescript-sdk#2189)

License

MIT