Skip to main content
Glama
aspiring-100x

mpp-mcp-gateway

mpp-mcp-gateway

Monetiza cualquier servidor MCP con micropagos en stablecoins a través del Machine Payments Protocol (MPP) en la cadena de bloques Tempo.

Crea servidores de herramientas MCP que cobren a los agentes de IA por llamada, por sesión o mediante claves de acceso, con liquidación en pathUSD y otros stablecoins TIP-20. Crea clientes de agente de IA que paguen por esas herramientas automáticamente con límites de gasto configurables.

License: MIT

Tabla de contenidos

Related MCP server: MCP Server TypeScript

Descripción general

mpp-mcp-gateway es una biblioteca de TypeScript que añade control de micropagos en stablecoins a los servidores MCP (Model Context Protocol). Cuando un agente de IA llama a una herramienta de pago, el servidor emite un desafío 402 Payment Required. El cliente del agente firma una transacción de pago en la cadena de bloques Tempo, reintenta la llamada con una credencial, y el servidor verifica la liquidación antes de ejecutar el handler y devolver el resultado con un recibo.

Características principales:

  • Cuatro modelos de precios — por llamada, por niveles, por sesión (canales de pago) y por clave de acceso (suscripciones)

  • Soporte multimoneda — acepta múltiples stablecoins TIP-20 por herramienta

  • Seguimiento exacto de ingresos — la aritmética con BigInt evita la deriva de coma flotante en millones de pagos de menos de un céntimo

  • Almacenamiento conectable — en memoria, Upstash Redis (CAS atómico), Cloudflare KV, o trae el tuyo propio

  • Limitación de velocidad — token bucket (en memoria o respaldado por Redis) con anulaciones por herramienta

  • Middleware de autenticación — token bearer, clave de API, HTTP Basic, URLs firmadas, CORS — todo seguro frente a ataques de temporización

  • Métricas de Prometheus — endpoint /metrics, cero dependencias

  • Trazado con OpenTelemetry — árbol de spans opcional por llamada de pago, coste cero cuando está desactivado

  • Webhooks — envío de eventos firmados con HMAC con reintentos, backoff y hooks de dead-letter

  • Descubrimiento de servicios — OpenAPI 3.1 con extensiones x-payment-info (rastreadas por mpp.land)

  • Panel de control — interfaz React + API JSON para la monitorización en vivo de ingresos y llamadas

  • Apagado ordenado — drena las llamadas en curso, dispara hooks, liquida webhooks

  • Portátil entre entornos de ejecución — funciona en Node.js 20+, Cloudflare Workers, Vercel Edge, Deno, Bun

Cómo funciona

┌─────────────┐         402 Challenge         ┌──────────────────┐
│  AI Agent   │ ────────────────────────────── │  Paid MCP Server │
│  (Client)   │                                │  (Gateway)       │
│             │ ◄── Payment Required (-32042)  │                  │
│             │                                │                  │
│  Signs tx   │ ── Credential (signed payment) │  Verifies on     │
│  via mppx   │                            ──► │  Tempo chain     │
│             │                                │                  │
│             │ ◄── Tool Result + Receipt      │  Runs handler    │
└─────────────┘                                └──────────────────┘
  1. El agente llama a una herramienta de pago a través de MCP

  2. El servidor responde con el código de error MCP -32042 que contiene un desafío MPP

  3. El cliente aplica los límites de gasto, firma el pago y reintenta con una credencial

  4. El servidor verifica la liquidación en cadena mediante mppx

  5. Se ejecuta el handler y se devuelve el resultado con un recibo de pago (hash de la transacción, marca de tiempo)

Instalación

npm install mpp-mcp-gateway

Dependencias entre pares (instala solo lo que uses):

# For HTTP/Express transports and dashboard
npm install express

# For Upstash Redis stores / rate limiting
npm install @upstash/redis

# For OpenTelemetry tracing
npm install @opentelemetry/api

# For Cloudflare Workers KV store
npm install @cloudflare/workers-types

Inicio rápido

Servidor (proveedor de herramientas)

import { createPaidMcpServer } from 'mpp-mcp-gateway/server'
import { z } from 'zod'

const server = createPaidMcpServer({
  name: 'my-api',
  version: '1.0.0',
  recipient: '0xYourWalletAddress',
  secretKey: process.env.PAYMENT_SECRET_KEY!,
  network: 'testnet',
  tools: [
    {
      name: 'get_weather',
      description: 'Get weather for a city. $0.001 per call.',
      inputSchema: { city: z.string() },
      pricing: { type: 'per-call', amount: '0.001' },
      handler: async ({ city }) => ({
        content: [{ type: 'text', text: `Weather in ${city}: 72°F, sunny` }],
      }),
    },
    {
      name: 'ping',
      description: 'Free liveness check.',
      inputSchema: {},
      // No pricing = free tool
      handler: async () => ({
        content: [{ type: 'text', text: 'pong' }],
      }),
    },
  ],
})

await server.startStdio()

Cliente (agente de IA)

import { createPaidMcpClient } from 'mpp-mcp-gateway/client'
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'

const client = createPaidMcpClient({
  name: 'my-agent',
  version: '1.0.0',
  privateKey: process.env.AGENT_PRIVATE_KEY! as `0x${string}`,
  maxPerCall: '0.10',    // safety cap: max $0.10 per single call
  maxTotal: '10.00',     // safety cap: max $10.00 total spend
  network: 'testnet',
})

const transport = new StdioClientTransport({
  command: 'node',
  args: ['server.js'],
})
await client.connect(transport)

// Free call — no payment required
const ping = await client.callTool('ping')
console.log(ping.content[0].text) // "pong"
console.log(ping.paid)            // false

// Paid call — automatic 402 → sign → retry
const weather = await client.callTool('get_weather', { city: 'Tokyo' })
console.log(weather.content[0].text) // "Weather in Tokyo: 72°F, sunny"
console.log(weather.paid)            // true
console.log(weather.receipt?.reference) // "0xabc...def" (tx hash)

await client.close()

Modelos de precios

Por llamada

Precio fijo por invocación. Una transacción en cadena por llamada.

pricing: { type: 'per-call', amount: '0.001' }

Por niveles

El precio disminuye (o aumenta) según el número acumulado de llamadas.

pricing: {
  type: 'tiered',
  tiers: [
    { upTo: 100, amount: '0.01' },
    { upTo: 1000, amount: '0.005' },
    { upTo: 'unlimited', amount: '0.001' },
  ],
}

Sesión (canales de pago)

El agente abre un canal de depósito en garantía (escrow) en cadena una sola vez. Las llamadas posteriores envían vales firmados fuera de cadena. El servidor liquida el vale más alto cuando el canal se cierra. Ideal para herramientas de streaming o de alta frecuencia.

pricing: {
  type: 'session',
  amount: '0.0005',          // per-unit price
  unitType: 'request',       // informational label
  suggestedDeposit: '0.50',  // hint for initial channel funding
}

Gestión de sesión en el cliente:

// Make multiple calls against the same channel
await client.callTool('think', { topic: 'AI alignment' })
await client.callTool('think', { topic: 'quantum computing' })

// Cooperatively close and settle on-chain
const result = await client.closeSession('think')
console.log(result.receipt.reference) // settlement tx hash

Clave de acceso (suscripciones)

El agente paga una vez por adelantado y recibe un token opaco. Las llamadas posteriores presentan el token; no hay más pagos hasta que la clave caduque o se agote. Ideal para experiencias de «comprar un pase de un día» o «comprar N llamadas».

pricing: {
  type: 'access-key',
  amount: '0.01',       // upfront cost
  validFor: '1d',       // time limit (supports: 60s, 30m, 4h, 7d)
  maxCalls: 100,        // call limit (at least one of validFor/maxCalls required)
}

El cliente gestiona el caché automáticamente:

// First call: pays $0.01, receives access key
const r1 = await client.callTool('premium_data', { query: 'foo' })
console.log(r1.paid)                    // true
console.log(r1.accessKey?.justIssued)   // true
console.log(r1.accessKey?.remainingCalls) // 99

// Subsequent calls: free (key presented in _meta)
const r2 = await client.callTool('premium_data', { query: 'bar' })
console.log(r2.paid)  // false

Multimoneda

Cualquier modelo de precios puede aceptar múltiples stablecoins TIP-20:

pricing: {
  type: 'per-call',
  amount: '0.001',
  accept: [
    { currency: '0x20c0...0000', amount: '0.001' }, // pathUSD
    { currency: '0x20c0...0001', amount: '0.001' }, // alphaUSD
  ],
}

API del servidor

import { createPaidMcpServer, PaidMcpServer } from 'mpp-mcp-gateway/server'

const server = createPaidMcpServer(config)

// Start on stdio (for CLI / subprocess use)
await server.startStdio()

// Or access the underlying McpServer for custom transports
const mcpServer = server.server
await mcpServer.connect(someTransport)

// Runtime inspection
server.getStats()           // GatewayStats (calls, revenue, sessions, keys)
server.listTools()          // tool names, descriptions, current prices
server.getRecentCalls(100)  // last N calls from the ring buffer
server.getInFlightCount()   // currently active handlers
server.isShuttingDown()     // true after close() begins
server.describe()           // full descriptor for discovery/OpenAPI

// Access-key management
await server.listAccessKeys()          // live keys issued by this instance
await server.revokeAccessKey(token)    // { revoked: boolean }

// Graceful shutdown
await server.close({ timeoutMs: 25_000 })

API del cliente

import { createPaidMcpClient, PaidMcpClient } from 'mpp-mcp-gateway/client'

const client = createPaidMcpClient(config)

await client.connect(transport)
await client.listTools()
const result = await client.callTool('tool_name', { arg: 'value' })

// Spending state
client.getSpending()    // { totalSpent, remaining, maxTotal, maxPerCall, ... }
client.resetSpending()  // reset cumulative counter (for tests)

// Access key management
client.getAccessKeys()          // cached keys by tool name
client.clearAccessKey('tool')   // force re-payment on next call
client.clearAccessKeys()        // drop all cached keys

// Session management
client.getOpenSessions()        // open channels by tool name
await client.closeSession('tool') // settle channel on-chain

await client.close()

Transportes

La pasarela funciona con cualquier transporte MCP. Se incluyen ejemplos:

Transporte

Caso de uso

Ejemplo

stdio

Herramientas CLI, lanzamiento de subprocesos

examples/paid-weather-mcp/

Streamable HTTP

Servidores de red (modernos)

examples/paid-weather-http/

SSE (heredado)

Clientes MCP antiguos

examples/paid-weather-sse/

In-Memory

Pruebas, mismo proceso

examples/in-memory-demo/

Adaptadores de almacenamiento

La pasarela utiliza una interfaz conectable MppMcpStore para persistir los registros de claves de acceso y el estado de los canales de sesión.

import { Store } from 'mpp-mcp-gateway/stores'

Adaptador

Atomicidad

Caso de uso

Store.memory()

Atómico (cadena de promesas)

Pruebas, desarrollo local, una sola instancia

Store.upstash(redis)

Atómico (CAS con Lua)

Producción, múltiples instancias

Store.cloudflareKv(ns)

Mejor esfuerzo

Claves de acceso en el edge (no para sesiones)

Store.bridge(legacy)

Mejor esfuerzo

Compatibilidad hacia atrás con los stores de mppx

Ejemplo con Upstash

import { Redis } from '@upstash/redis'
import { createUpstashStore } from 'mpp-mcp-gateway/stores'

const store = createUpstashStore(
  new Redis({ url: process.env.UPSTASH_URL!, token: process.env.UPSTASH_TOKEN! }),
  { keyPrefix: 'mppmcp:', ttlSeconds: 30 * 24 * 3600 }
)

const server = createPaidMcpServer({
  // ...
  accessKeyStore: store,
  sessionStore: store,
})

Store personalizado

Implementa la interfaz de cuatro métodos:

interface MppMcpStore {
  get<T>(key: string): Promise<T | null>
  put(key: string, value: unknown): Promise<void>
  delete(key: string): Promise<void>
  update<T>(key: string, transform: (current: T | null) => T | null): Promise<T | null>
}

El método update debe garantizar una lectura-modificación-escritura atómica. La devolución de llamada transform puede invocarse varias veces en condiciones de contención (backends de estilo CAS).

Limitación de velocidad

La limitación de velocidad se aplica antes de la lógica de pago y del handler; las llamadas denegadas nunca emiten un 402 ni ejecutan tu handler.

const server = createPaidMcpServer({
  // ...
  rateLimit: {
    refillPerMinute: 60,   // sustained rate
    capacity: 10,          // burst capacity
    perTool: {
      expensive_ai: { refillPerMinute: 5, capacity: 2 },
      cheap_lookup:  { refillPerMinute: 600, capacity: 100 },
    },
    // Custom bucketing (e.g. per-session on HTTP transports)
    keyExtractor: (toolName, extra) => `${toolName}:${extra.sessionId ?? 'default'}`,
  },
})

Para despliegues de múltiples instancias, usa el limitador respaldado por Upstash:

import { upstashTokenBucketLimiter } from 'mpp-mcp-gateway/rate-limit'

const limiter = upstashTokenBucketLimiter(redis, {
  keyPrefix: 'mppmcp:rl:',
  refillPerMinute: 120,
  capacity: 20,
})

const server = createPaidMcpServer({
  // ...
  rateLimit: { limiter },
})

Middleware de autenticación

Cinco fábricas de middleware de Express para proteger los endpoints del panel, las métricas y el descubrimiento:

import { auth } from 'mpp-mcp-gateway'

// Bearer token (constant-time comparison)
mountDashboard(server, app, {
  middleware: auth.bearerToken(process.env.DASHBOARD_TOKEN!, { realm: 'admin' }),
})

// API key in custom header
mountMetrics(server, app, {
  middleware: auth.apiKey({ header: 'x-api-key', value: process.env.METRICS_KEY! }),
})

// HTTP Basic Auth (multi-user)
mountDashboard(server, app, {
  middleware: auth.basicAuth({ users: { admin: 'secret' }, realm: 'gateway' }),
})

// HMAC-signed URLs with TTL
mountDashboard(server, app, {
  middleware: auth.signedQuery({ secret: process.env.URL_SECRET!, ttlSeconds: 300 }),
})

// Public CORS for registry crawlers
mountDiscovery(server, app, {
  middleware: auth.publicCors(),
})

Panel y monitorización

API JSON

import { mountDashboard } from 'mpp-mcp-gateway'

mountDashboard(server, app, { prefix: '/api' })

Expone:

Endpoint

Respuesta

GET /api/stats

{ stats: GatewayStats } — llamadas, ingresos, sesiones, claves, tiempo de actividad

GET /api/tools

{ tools: [{ name, description, price }] }

GET /api/calls?limit=N

{ calls: CallLogEntry[] } — las más recientes primero

GET /api/keys

{ keys: AccessKeyListEntry[] } — claves de acceso activas

DELETE /api/keys/:token

{ revoked: boolean } — revoca una clave (mutación; protégelo con middleware)

Métricas de Prometheus

import { mountMetrics } from 'mpp-mcp-gateway'

mountMetrics(server, app, {
  middleware: auth.bearerToken(process.env.METRICS_TOKEN!),
})

Métricas expuestas:

  • mppmcp_calls_total{tool} — contador por herramienta

  • mppmcp_calls_by_mode_total{mode} — de pago, gratuitas, sesión, access_key, total

  • mppmcp_revenue_micro_usd_total{tool} — ingresos acumulados en micro-USD

  • mppmcp_in_flight_calls — medidor de handlers activos

  • mppmcp_access_keys_issued_total / expired_total

  • mppmcp_sessions_opened_total / closed_total

  • mppmcp_rate_limited_total — llamadas rechazadas por el limitador de velocidad

  • mppmcp_rejected_shutting_down_total — llamadas rechazadas durante el apagado

  • mppmcp_uptime_seconds

  • mppmcp_shutting_down

Panel de React

Hay un panel prefabricado de React + Vite en dashboard/. Consulta la API JSON cada 2 segundos y muestra:

  • Contadores de ingresos y tabla de herramientas ordenada por ingresos

  • Registro de llamadas en vivo codificado por colores según el modo de pago

  • Estadísticas de claves de acceso y sesiones

cd dashboard
npm install
npm run build

Sirve dashboard/dist/ como archivos estáticos desde tu aplicación Express.

Descubrimiento de servicios

Genera y sirve un documento OpenAPI 3.1 con extensiones x-payment-info según el borrador IETF de descubrimiento de servicios de MPP. Los registros públicos como mpp.land lo rastrean automáticamente.

import { mountDiscovery } from 'mpp-mcp-gateway'

mountDiscovery(server, app, {
  baseUrl: 'https://api.example.com',
  categories: ['data', 'search'],
  docs: { homepage: 'https://example.com/docs' },
})
// GET /openapi.json → OpenAPI 3.1 with x-payment-info per tool

Webhooks

Envía eventos a una URL con firmas HMAC-SHA-256. La entrega es de tipo fire-and-forget (no bloqueante), con reintentos y backoff exponencial.

const server = createPaidMcpServer({
  // ...
  webhooks: {
    url: 'https://example.com/webhook',
    secret: process.env.WEBHOOK_SECRET!,
    events: ['payment.received', 'session.closed'], // or omit for all
    maxAttempts: 3,
    onDrop: async (event, lastError) => {
      // Dead-letter: persist to DB for replay
      await db.insert('webhook_dlq', { event, error: lastError })
    },
  },
})

Tipos de evento: payment.received, access-key.issued, access-key.expired, session.opened, session.closed, call.failed

Verificación por parte del receptor:

import { createHmac } from 'node:crypto'

function verify(req) {
  const expected = 'sha256=' + createHmac('sha256', WEBHOOK_SECRET)
    .update(`${req.headers['x-mppmcp-timestamp']}.${req.body}`)
    .digest('hex')
  return timingSafeEqual(Buffer.from(expected), Buffer.from(req.headers['x-mppmcp-signature']))
}

Trazado con OpenTelemetry

Opcional. Pasa un tracer para obtener un árbol de spans por llamada de pago. Cero sobrecarga cuando está desactivado.

import { trace } from '@opentelemetry/api'

const server = createPaidMcpServer({
  // ...
  tracer: trace.getTracer('mpp-mcp-gateway', '1.0.0'),
})

Árbol de spans:

mppmcp.tool.call (root)
├── mppmcp.payment.charge   (or mppmcp.session.advance, mppmcp.access-key.redeem)
└── mppmcp.handler.run

Atributos: mppmcp.tool.name, mppmcp.pricing.type, mppmcp.amount, mppmcp.payment.mode, mppmcp.payment.tx-hash, mppmcp.session.action, mppmcp.error.code

CLI del operador

Inspecciona y gestiona pasarelas desplegadas desde la línea de comandos:

npx mpp-mcp inspect https://my-gateway.fly.dev --token=secret123
npx mpp-mcp stats https://api.example.com
npx mpp-mcp tools https://api.example.com
npx mpp-mcp calls https://api.example.com --limit=50
npx mpp-mcp keys list https://api.example.com --token=admin
npx mpp-mcp keys revoke mppmcp_abc123... https://api.example.com --token=admin

Referencia de configuración

Servidor (PaidMcpServerConfig)

Campo

Tipo

Predeterminado

Descripción

name

string

obligatorio

Nombre del servidor anunciado a los clientes

version

string

obligatorio

Versión del servidor

recipient

0x${string}

obligatorio

Dirección de wallet que recibe los pagos

secretKey

string

obligatorio

Clave HMAC para vincular los desafíos de pago

tools

PaidToolDefinition[]

obligatorio

Definiciones de herramientas con sus handlers

currency

0x${string}

pathUSD

Dirección del contrato del stablecoin TIP-20

network

'mainnet' | 'testnet'

'testnet'

Red de Tempo

feePayerKey

0x${string}

Gas patrocinado por el servidor (clave privada del pagador de comisiones)

sessionAccountKey

0x${string}

Clave de operador necesaria para la liquidación de sesiones

escrowContract

0x${string}

predeterminado por red

Contrato de depósito en garantía de sesión

accessKeyStore

MppMcpStore

en memoria

Persistencia para las claves de acceso

sessionStore

MppMcpStore

en memoria

Persistencia para los canales de sesión

accessKeyBinding

'none' | 'wallet'

'none'

Vincular las claves a la wallet que paga

callLogSize

number

1000

Capacidad del búfer circular (0 = desactivado)

logger

Logger

console+redaction

Registrador estructurado

drainTimeoutMs

number

30000

Tiempo de espera de apagado ordenado

onShutdown

() => void

Hook que se dispara cuando comienza el drenaje

rateLimit

object

60/min por herramienta

Configuración del límite de velocidad

tracer

Tracer

Tracer de OpenTelemetry (opcional)

webhooks

WebhookConfig

Configuración del envío de eventos

Cliente (PaidMcpClientConfig)

Campo

Tipo

Por defecto

Descripción

name

string

obligatorio

Nombre del cliente

version

string

obligatorio

Versión del cliente

privateKey

0x${string}

obligatorio

Clave privada de la cartera del agente

maxPerCall

string

'1.00'

Gasto máximo por llamada individual (USD)

maxTotal

string

'100.00'

Gasto máximo acumulado (USD)

maxSessionDeposit

string

'1.00'

Depósito máximo del canal (USD)

network

'mainnet' | 'testnet'

'testnet'

Red Tempo

logger

Logger

console+redaction

Registrador estructurado

verifySettlement

boolean

false

Verificar la transacción de liquidación de la sesión en la cadena

Ejemplos

Ejemplo

Precio

Transporte

Qué demuestra

in-memory-demo

por llamada

InMemory

Recorrido completo 402 en un solo proceso

paid-weather-mcp

por llamada

stdio

El agente lanza el servidor como subproceso

paid-weather-http

por llamada

Streamable HTTP

Servidor de red en Express

paid-weather-sse

por llamada

SSE (legacy)

Transporte SSE compatible con versiones anteriores

paid-weather-dashboard

por llamada + access-key

Streamable HTTP

MCP combinado + panel + descubrimiento

paid-streaming-mcp

sesión

stdio

Canales de pago, vales, cierre

paid-subscription-mcp

access-key

stdio

Pase diario, solo tiempo, paquetes de llamadas

paid-peer-cash-mcp

por llamada

stdio

Controla el acceso a las herramientas de Peer Cash y luego retira los ingresos de MPP

Ejecuta cualquier ejemplo:

# In-memory demo (no wallet needed)
npm run example:demo

# Server + client pairs
npm run example:server      # then in another terminal:
npm run example:client

npm run example:http:server
npm run example:http:client

npm run example:streaming:server
npm run example:streaming:client

npm run example:subscription:server
npm run example:subscription:client

# Node.js 22+, Tempo mainnet
npm run example:peer-cash:server

# Dashboard (with all endpoints)
npm run example:dashboard:server

Financiación de una cartera de prueba

Los ejemplos de pago requieren una cartera financiada en la red de prueba de Tempo. El ejemplo de Peer Cash es la excepción: usa la red principal de Tempo porque la ruta de ingresos solo está disponible en vivo.

cast rpc tempo_fundAddress 0xYourAddress --rpc-url https://rpc.moderato.tempo.xyz

Compatibilidad en tiempo de ejecución

La biblioteca principal (servidor, cliente, almacenes, límite de tasa, importes, claves de acceso) es portable entre entornos de ejecución mediante Web Crypto:

Runtime

Soporte

Node.js 20+

Completo

Cloudflare Workers

Completo

Vercel Edge

Completo

Deno

Completo

Bun

Completo

El módulo de middleware auth.ts usa node:crypto y requiere Node.js. Los despliegues Edge usan en su lugar el enrutador nativo y las primitivas de autenticación de su plataforma.

Arquitectura

src/
├── server.ts          PaidMcpServer — payment gating, stats, shutdown, webhooks
├── client.ts          PaidMcpClient — auto-payment, caps, key caching, sessions
├── types.ts           Core interfaces (PricingModel, configs, stats, results)
├── index.ts           Barrel exports (11 subpath entry points)
├── access-keys.ts     Issue, redeem (atomic), validate, duration parsing
├── amounts.ts         BigInt <-> USD string conversion (exact arithmetic)
├── auth.ts            5 Express middleware factories (timing-safe)
├── cli.ts             Operator CLI (inspect, stats, tools, calls, keys)
├── constants.ts       Tempo networks, token addresses, escrow contracts
├── dashboard.ts       JSON API: /api/stats, /api/tools, /api/calls
├── discovery.ts       OpenAPI 3.1 generation with x-payment-info
├── errors.ts          9 typed error classes with stable codes
├── logger.ts          Logger interface + 4 implementations + redaction
├── metrics.ts         Prometheus /metrics (hand-formatted, zero deps)
├── rate-limit.ts      RateLimiter interface + 3 implementations
├── runtime.ts         Cross-runtime: randomHex, writeLogLine, hmacSha256Hex
├── tracing.ts         OTel span helpers (no-op when disabled)
├── webhooks.ts        HMAC-signed event push with retry + dead-letter
└── stores/
    ├── types.ts       MppMcpStore interface
    ├── index.ts       Store namespace + re-exports
    ├── memory.ts      In-memory (atomic via promise chains)
    ├── upstash.ts     Upstash Redis (atomic via Lua CAS)
    ├── cloudflare-kv.ts  Cloudflare KV (best-effort)
    └── bridge.ts      Legacy 3-method store adapter

Exportaciones del paquete

{
  ".": "Main barrel (everything)",
  "./server": "PaidMcpServer",
  "./client": "PaidMcpClient",
  "./dashboard": "mountDashboard",
  "./discovery": "mountDiscovery, buildOpenApi",
  "./stores": "Store adapters",
  "./rate-limit": "Rate limiter implementations",
  "./auth": "Auth middleware factories",
  "./metrics": "mountMetrics, formatMetrics",
  "./tracing": "startSpan, withSpan, TRACE_ATTRS",
  "./webhooks": "WebhookDispatcher, event types"
}

Principios de diseño

  • Exactitud de ingresos — todas las operaciones monetarias usan unidades base bigint (6 decimales). Sin desviación de coma flotante después de millones de operaciones.

  • Adopción opcional sin coste — el trazado, los webhooks y el límite de tasa son operaciones nulas salvo que se configuren. Los despliegues sin trazado no asignan spans.

  • Todo es conectable — los almacenes, registradores, limitadores de tasa y la autenticación se basan en interfaces. Intercambia implementaciones sin tocar el código de la puerta de enlace.

  • Fallar rápido — los errores de configuración se lanzan en el momento de la construcción, no en el de la petición.

  • Los errores son valores — clases de error tipadas con códigos estables. Usa instanceof o err.code para el manejo programático.

  • Registro de llamadas en búfer circular — preasignado O(1), nunca crece. Sin presión del recolector de basura bajo alto rendimiento.

  • Ciclo de vida elegante — la compuerta de apagado rechaza nuevas llamadas, el drenaje espera a las que están en curso, vacía los webhooks y luego se desconecta.

Desarrollo

# Install dependencies
npm install

# Build
npm run build

# Type check
npm run typecheck

# Run tests
npm run test

# Run tests in watch mode
npm run test:watch

# Type tests (tsd)
npm run test:types

# Benchmarks
npm run bench

# Generate docs
npm run docs

Suite de pruebas

27+ archivos de prueba que cubren:

  • Atomicidad de las claves de acceso (canjes concurrentes de claves de N llamadas)

  • Flujos de claves de acceso (emisión → canje → agotamiento → nuevo pago)

  • Matemática de importes (conversiones BigInt, casos límite)

  • Middleware de autenticación (las 5 fábricas)

  • Búfer circular de registro de llamadas (envoltura, límites de capacidad)

  • Apagado elegante y drenaje

  • Respuestas de la API del panel

  • Descubrimiento / generación de OpenAPI

  • Taxonomía de errores

  • Herramientas gratuitas (ruta sin pago)

  • Registrador (salida estructurada, redacción, registradores secundarios)

  • Formato de métricas de Prometheus

  • Descubrimiento multidivisa

  • Flujo de pago (402 → credencial → recibo)

  • Cálculos de precios (por niveles, por llamada)

  • Límite de tasa (cubo de tokens, denegación, retry-after)

  • Exactitud de ingresos (acumulación BigInt en muchas llamadas)

  • Utilidades de tiempo de ejecución (randomHex, hmacSha256Hex)

  • Ciclo de vida de la sesión (abrir → vale → cerrar → liquidar)

  • Límites de gasto (por llamada, total, depósito de sesión)

  • Trazado OpenTelemetry (atributos de span, registro de errores)

  • Webhooks (entrega, reintento, firma HMAC, mensajes fallidos)

  • Pruebas de tipos (mediante tsd)

  • Pruebas de rendimiento (mediante vitest bench)

Apagado elegante

Conecta close() a la señal de apagado de tu contenedor:

process.on('SIGTERM', async () => {
  try {
    await server.close({ timeoutMs: 25_000 })
    process.exit(0)
  } catch {
    process.exit(1) // drain timed out
  }
})

Registro estructurado

La biblioteca incluye una interfaz Logger conectable. Por defecto: JSON a stderr con redacción automática de secretos (claves privadas, credenciales, transacciones firmadas).

import { consoleLogger, silentLogger, withRedaction } from 'mpp-mcp-gateway'

// Custom logger
const server = createPaidMcpServer({
  // ...
  logger: withRedaction(consoleLogger({ level: 'debug', pretty: true })),
})

// Silence for tests
const server = createPaidMcpServer({
  // ...
  logger: silentLogger(),
})

Adáptalo a pino, winston o cualquier biblioteca de registro:

const adapter: Logger = {
  debug: (m, c) => pino.debug(c, m),
  info: (m, c) => pino.info(c, m),
  warn: (m, c) => pino.warn(c, m),
  error: (m, c) => pino.error(c, m),
  child: (bindings) => /* wrap pino.child(bindings) */,
}

Licencia

MIT — Gaurav Pant

A
license - permissive license
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • -
    license
    Not graded
    quality
    Not graded
    maintenance
    A production-ready TypeScript MCP server providing basic tools (add, echo, timestamp), resources (server info, greetings, data access), and prompt templates (analyze, code-review, summarize). Serves as a foundation for building custom MCP servers with extensible architecture.
    225
  • A
    license
    Not graded
    quality
    C
    maintenance
    MCP server for AgentPay — the payment gateway for autonomous AI agents. Fund a wallet once, give your agent the key, and it discovers, provisions, and pays for tool APIs on its own. One key, every tool.
    112
    1
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Simplifies creating MCP servers in TypeScript with an Express-like API and experimental decorators, enabling quick definition of tools, resources, and prompts.
    26
    196
    MIT

View all related MCP servers

Related MCP Connectors

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/aspiring-100x/mpp-mcp-gateway'

If you have feedback or need assistance with the MCP directory API, please join our Discord server