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-reverse

v1.1.0

Published

Reverse transports for MCP (Model Context Protocol) — SSE engine. Allows internal MCP servers behind NAT/firewall to connect out to public MCP clients.

Readme

mcp-reverse

Reverse transports for MCP — SSE engine.
Let internal MCP servers behind NAT/firewall connect OUT to your public client.

The Problem

Standard MCP transports are client-initiated: the Client must reach the Server's address.

Client (public) ───connect───> Server (public)   ✅ works
Client (public) ───connect───X Server (NAT)       ❌ unreachable

mcp-reverse flips the direction at the transport layer. The internal MCP Server initiates the connection; the public MCP Client accepts it. The MCP protocol then runs normally over the established channel.

Public Client (chat-ai) <───incoming──── Internal Server (behind NAT)       ✅ works

Features

| Feature | Support | |---------|:---:| | NAT traversal | ✅ | | Authentication (token + custom handler) | ✅ | | Keepalive / heartbeat | ✅ SSE comments | | Auto-reconnect (exponential backoff + jitter) | ✅ | | TLS / HTTPS | ✅ | | Tools / Resources / Prompts | ✅ | | Notifications (both directions) | ✅ | | Next.js / Vercel / serverless | ✅ |

Install

npm install mcp-reverse

Requires @modelcontextprotocol/sdk as peer dependency.


Quick Start — SSE

Public Side (chat-ai / Next.js App Router)

import { SSEAcceptor } from 'mcp-reverse';
import { Client } from '@modelcontextprotocol/sdk/client/index.js';

const acceptor = new SSEAcceptor({
  authTokens: { 'office-server': 'secret123' },
});

acceptor.onConnection(async ({ transport, metadata }) => {
  console.log(`Server connected: ${metadata.serverName}`);

  const client = new Client(
    { name: 'chat-ai', version: '1.0.0' },
    { capabilities: {} }
  );
  await client.connect(transport);

  // Use client normally
  const tools = await client.listTools();
  const result = await client.callTool({ name: 'exec', arguments: { cmd: 'ls' } });
});

acceptor.onDisconnection((serverName) => console.log(`Disconnected: ${serverName}`));

// ─── Next.js App Router integration ───
export async function GET(req: NextRequest) {
  return acceptor.handleSSE(req);
}
export async function POST(req: NextRequest) {
  return acceptor.handleMessage(req);
}

Or run standalone (creates its own HTTP server):

const acceptor = new SSEAcceptor({ port: 3400, authTokens: { ... } });
await acceptor.start();  // listens on http://0.0.0.0:3400/mcp-reverse

Internal Side (behind NAT)

import { SSEReverseClientTransport } from 'mcp-reverse';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';

const transport = new SSEReverseClientTransport({
  url: 'https://public-chatai.example.com:3000/mcp-reverse',
  serverName: 'office-server',
  authToken: 'secret123',
  reconnect: { enabled: true },
});

const server = new McpServer({ name: 'office-server', version: '1.0.0' });
server.tool('greet', 'Greet someone', { name: z.string() },
  async ({ name }) => ({ content: [{ type: 'text', text: `Hello, ${name}!` }] })
);

await server.connect(transport);

High-Level Client (recommended)

import { ReverseMCPClient } from 'mcp-reverse/connector';

const client = await ReverseMCPClient.createSSE(server, {
  url: 'https://public-host.example.com:3000/mcp-reverse',
  serverName: 'office-server',
  authToken: 'secret123',
  reconnect: { enabled: true, maxDelay: 30000 },
});

client.on('connected', () => console.log('Connected'));
client.on('disconnected', () => console.log('Disconnected'));
client.on('reconnecting', (attempt) => console.log(`Reconnecting (${attempt})`));

API Reference

SSEAcceptor (Public Side)

new SSEAcceptor(options: SSEAcceptorOptions | SSEAcceptorStandaloneOptions)

| Option | Type | Default | Notes | |--------|------|---------|-------| | authTokens | Record<string, string> | — | serverName → token map | | authHandler | async (meta) => boolean | — | Custom auth logic | | heartbeat | SSEHeartbeatOptions | {enabled:true} | Keepalive | | maxMessageSize | number | 4MB | POST body limit | | sessionTimeout | number | 60000 | Inactivity timeout (ms) | | pathPrefix | string | /mcp-reverse | URL prefix | | port | number | — | Standalone only: listen port |

SSEReverseClientTransport (Internal Side)

new SSEReverseClientTransport(options: SSEReverseClientTransportOptions)

| Option | Type | Default | Notes | |--------|------|---------|-------| | url | string | required | Base URL | | serverName | string | required | Server identifier | | authToken | string | — | Bearer token | | reconnect | ReconnectOptions | {enabled:true} | Auto-reconnect | | heartbeat | SSEHeartbeatOptions | {enabled:true} | Keepalive | | headers | Record<string, string> | — | Extra headers | | insecureTls | boolean | false | Skip TLS verify | | queryParams | Record<string, string> | — | Extra query params |

Reconnect Options

{
  enabled?: boolean;       // default: true
  initialDelay?: number;   // default: 1000ms
  maxDelay?: number;       // default: 30000ms
  multiplier?: number;     // default: 2
  jitter?: boolean;        // default: true
  maxRetries?: number;     // default: 0 (infinite)
}

Project Structure

src/
├── protocol/                # Protocol-level abstractions
│   ├── types.ts             # All type definitions
│   └── reconnect.ts         # Exponential backoff reconnection
├── acceptor/
│   └── sse-acceptor.ts      # SSEAcceptor (public side)
├── connector/
│   ├── mcp-connector.ts     # ReverseMCPClient (high-level)
│   └── sse-connector.ts     # SSEReverseClientTransport (internal side)
├── transport/
│   ├── sse-transport.ts     # SSEConnectionTransport
│   └── sse-util.ts          # SSE parsing/formatting utilities
├── proxy/
│   └── reverse-proxy.ts     # ReverseProxy / gateway
└── index.ts                 # Main entry point

Sub-path exports

| Export path | Description | |-------------|-------------| | mcp-reverse | Main entry — all types | | mcp-reverse/connector | High-level ReverseMCPClient | | mcp-reverse/acceptor | SSEAcceptor | | mcp-reverse/sse | Backward compat — all SSE types | | mcp-reverse/transport | SSEConnectionTransport, SSE utilities | | mcp-reverse/proxy | ReverseProxy |


Testing

npm test                   # All 43 tests (unit + integration)
npm run build              # TypeScript compilation

License

MIT