Skip to main content
Glama
aspiring-100x

mpp-mcp-gateway

mpp-mcp-gateway

Монетизируйте любой MCP-сервер с помощью микроплатежей в стейблкоинах через протокол Machine Payments Protocol (MPP) в блокчейне Tempo.

Создавайте MCP-серверы инструментов, которые взимают с AI-агентов плату за вызов, за сессию или через ключи доступа — с расчётом в pathUSD и других TIP-20 стейблкоинах. Создавайте клиенты AI-агентов, которые автоматически платят за эти инструменты с настраиваемыми лимитами расходов.

License: MIT

Table of Contents

Related MCP server: easy-mcp

Обзор

mpp-mcp-gateway — это библиотека TypeScript, которая добавляет шлюз микроплатежей в стейблкоинах к MCP-серверам (Model Context Protocol). Когда AI-агент вызывает платный инструмент, сервер отвечает ошибкой 402 Payment Required. Клиент агента подписывает платёжную транзакцию в блокчейне Tempo, повторяет вызов с учётными данными, а сервер проверяет расчёт перед запуском обработчика и возвращает результат с чеком.

Ключевые возможности:

  • Четыре модели ценообразования — за вызов, многоуровневая, сессионная (платёжные каналы) и по ключам доступа (подписки)

  • Поддержка нескольких валют — приём нескольких TIP-20 стейблкоинов для каждого инструмента

  • Точный учёт выручки — арифметика BigInt предотвращает дрейф чисел с плавающей запятой при миллионах платежей меньше цента

  • Подключаемое хранилище — in-memory, Upstash Redis (атомарный CAS), Cloudflare KV или своё собственное

  • Ограничение частоты запросов — token bucket (в памяти или на базе Redis) с переопределениями для отдельных инструментов

  • Промежуточное ПО аутентификации — bearer-токен, API-ключ, HTTP Basic, подписанные URL, CORS — всё с защитой от timing-атак

  • Метрики Prometheus — эндпоинт /metrics, без зависимостей

  • Трассировка OpenTelemetry — опциональное дерево спанов для каждого платного вызова, нулевая стоимость при отключении

  • Вебхуки — отправка событий с подписью HMAC, повторами, экспоненциальной задержкой и dead-letter хуками

  • Обнаружение сервисов — OpenAPI 3.1 с расширениями x-payment-info (сканируется mpp.land)

  • Панель мониторинга — React UI + JSON API для мониторинга выручки и вызовов в реальном времени

  • Плавное завершение работы — ожидание завершения выполняющихся вызовов, запуск хуков, обработка вебхуков

  • Переносимость между средами — работает на Node.js 20+, Cloudflare Workers, Vercel Edge, Deno, Bun

Как это работает

┌─────────────┐         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. Агент вызывает платный инструмент через MCP

  2. Сервер отвечает кодом ошибки MCP -32042, содержащим MPP-челлендж

  3. Клиент соблюдает лимиты расходов, подписывает платёж и повторяет запрос с учётными данными

  4. Сервер проверяет расчёт в блокчейне через mppx

  5. Выполняется обработчик, результат возвращается с платёжным чеком (хэш транзакции, метка времени)

Установка

npm install mpp-mcp-gateway

Пировые зависимости (устанавливайте только то, что используете):

# 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

Быстрый старт

Сервер (поставщик инструментов)

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()

Клиент (AI-агент)

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()

Модели ценообразования

За вызов

Фиксированная цена за вызов. Одна транзакция в блокчейне за вызов.

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

Многоуровневая

Цена уменьшается (или увеличивается) в зависимости от накопленного количества вызовов.

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

Сессия (платёжные каналы)

Агент один раз открывает эскроу-канал в блокчейне. Последующие вызовы отправляют подписанные ваучеры вне блокчейна. Сервер закрывает канал, забирая наибольший ваучер. Лучше всего подходит для потоковых инструментов или инструментов с высокой частотой вызовов.

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

Управление сессией на стороне клиента:

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

Ключ доступа (подписки)

Агент платит один раз заранее и получает непрозрачный токен. Последующие вызовы предъявляют токен — дальнейших платежей не требуется, пока ключ не истечёт или не будет исчерпан. Лучше всего подходит для UX «купить дневной пропуск» или «купить N вызовов».

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)
}

Клиент автоматически обрабатывает кэширование:

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

Несколько валют

Любая модель ценообразования может принимать несколько 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 сервера

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 клиента

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()

Транспорты

Шлюз работает с любым MCP-транспортом. Включены примеры:

Транспорт

Вариант использования

Пример

stdio

CLI-инструменты, запуск подпроцессов

examples/paid-weather-mcp/

Streamable HTTP

Сетевые серверы (современные)

examples/paid-weather-http/

SSE (legacy)

Старые MCP-клиенты

examples/paid-weather-sse/

In-Memory

Тестирование, в рамках одного процесса

examples/in-memory-demo/

Адаптеры хранилища

Шлюз использует подключаемый интерфейс MppMcpStore для сохранения записей ключей доступа и состояния сессионных каналов.

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

Адаптер

Атомарность

Вариант использования

Store.memory()

Атомарная (цепочка промисов)

Тесты, локальная разработка, один инстанс

Store.upstash(redis)

Атомарная (Lua CAS)

Продакшен, несколько инстансов

Store.cloudflareKv(ns)

Best-effort

Ключи доступа на периферии (не для сессий)

Store.bridge(legacy)

Best-effort

Обратная совместимость с хранилищами mppx

Пример с 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,
})

Собственное хранилище

Реализуйте интерфейс из четырёх методов:

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

Метод update должен гарантировать атомарное чтение-изменение-запись. Колбэк transform может вызываться несколько раз при конкуренции (CAS-подобные бэкенды).

Ограничение частоты запросов

Ограничение частоты запросов срабатывает до логики платежей и обработчиков — отклонённые вызовы никогда не выдают 402 и не запускают ваш обработчик.

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'}`,
  },
})

Для развёртываний с несколькими инстансами используйте ограничитель на базе 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 },
})

Промежуточное ПО аутентификации

Пять фабрик промежуточного ПО Express для защиты эндпоинтов панели мониторинга, метрик и обнаружения:

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(),
})

Панель мониторинга и наблюдение

JSON API

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

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

Предоставляет:

Эндпоинт

Ответ

GET /api/stats

{ stats: GatewayStats } — вызовы, выручка, сессии, ключи, время работы

GET /api/tools

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

GET /api/calls?limit=N

{ calls: CallLogEntry[] } — сначала новые

GET /api/keys

{ keys: AccessKeyListEntry[] } — активные ключи доступа

DELETE /api/keys/:token

{ revoked: boolean } — отозвать ключ (изменяющий; защитите с помощью промежуточного ПО)

Метрики Prometheus

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

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

Предоставляемые метрики:

  • mppmcp_calls_total{tool} — счётчик по инструментам

  • mppmcp_calls_by_mode_total{mode} — paid, free, session, access_key, total

  • mppmcp_revenue_micro_usd_total{tool} — суммарная выручка в микро-USD

  • mppmcp_in_flight_calls — датчик активных обработчиков

  • mppmcp_access_keys_issued_total / expired_total

  • mppmcp_sessions_opened_total / closed_total

  • mppmcp_rate_limited_total — вызовы, отклонённые ограничителем частоты

  • mppmcp_rejected_shutting_down_total — вызовы, отклонённые при завершении работы

  • mppmcp_uptime_seconds

  • mppmcp_shutting_down

Панель на React

Готовая панель на React + Vite находится в dashboard/. Она опрашивает JSON API каждые 2 секунды и отображает:

  • Счётчики выручки и таблицу инструментов, отсортированную по выручке

  • Живой журнал вызовов с цветовой кодировкой по режиму оплаты

  • Статистику по ключам доступа и сессиям

cd dashboard
npm install
npm run build

Отдавайте dashboard/dist/ как статические файлы из вашего Express-приложения.

Обнаружение сервисов

Создавайте и отдавайте документ OpenAPI 3.1 с расширениями x-payment-info в соответствии с черновиком IETF по обнаружению сервисов MPP. Публичные реестры, такие как mpp.land, автоматически сканируют его.

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

Вебхуки

Отправляйте события на URL с подписями HMAC-SHA-256. Доставка выполняется по принципу fire-and-forget (неблокирующая), с повторами и экспоненциальной задержкой.

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 })
    },
  },
})

Типы событий: payment.received, access-key.issued, access-key.expired, session.opened, session.closed, call.failed

Проверка получателем:

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']))
}

Трассировка OpenTelemetry

Опционально. Передайте трассировщик, чтобы получать дерево спанов для каждого платного вызова. Нулевые накладные расходы при отключении.

import { trace } from '@opentelemetry/api'

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

Дерево спанов:

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

Атрибуты: mppmcp.tool.name, mppmcp.pricing.type, mppmcp.amount, mppmcp.payment.mode, mppmcp.payment.tx-hash, mppmcp.session.action, mppmcp.error.code

CLI оператора

Просматривайте и управляйте развёрнутыми шлюзами из командной строки:

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

Справочник по конфигурации

Сервер (PaidMcpServerConfig)

Поле

Тип

По умолчанию

Описание

name

string

обязательно

Имя сервера, сообщаемое клиентам

version

string

обязательно

Версия сервера

recipient

0x${string}

обязательно

Адрес кошелька для получения платежей

secretKey

string

обязательно

HMAC-ключ для привязки платёжных челленджей

tools

PaidToolDefinition[]

обязательно

Определения инструментов с обработчиками

currency

0x${string}

pathUSD

Адрес контракта TIP-20 стейблкоина

network

'mainnet' | 'testnet'

'testnet'

Сеть Tempo

feePayerKey

0x${string}

—

Газ за счёт сервера (приватный ключ плательщика комиссии)

sessionAccountKey

0x${string}

—

Ключ оператора, необходимый для расчёта по сессиям

escrowContract

0x${string}

по умолчанию для сети

Эскроу-контракт сессии

accessKeyStore

MppMcpStore

в памяти

Хранилище для ключей доступа

sessionStore

MppMcpStore

в памяти

Хранилище для сессионных каналов

accessKeyBinding

'none' | 'wallet'

'none'

Привязка ключей к платящему кошельку

callLogSize

number

1000

Ёмкость кольцевого буфера (0 = отключено)

logger

Logger

console+redaction

Структурированный логгер

drainTimeoutMs

number

30000

Тайм-аут плавного завершения работы

onShutdown

() => void

—

Хук, вызываемый при начале завершения

rateLimit

object

60/min per tool

Конфигурация ограничения частоты

tracer

Tracer

—

Трассировщик OpenTelemetry (опционально)

webhooks

WebhookConfig

—

Конфигурация отправки событий

Клиент (PaidMcpClientConfig)

Поле

Тип

По умолчанию

Описание

name

string

обязательно

Имя клиента

version

string

обязательно

Версия клиента

privateKey

0x${string}

обязательно

Приватный ключ кошелька агента

maxPerCall

string

'1.00'

Максимальный расход за один вызов (USD)

maxTotal

string

'100.00'

Максимальный суммарный расход (USD)

maxSessionDeposit

string

'1.00'

Максимальный депозит канала (USD)

network

'mainnet' | 'testnet'

'testnet'

Сеть Tempo

logger

Logger

console+redaction

Структурированный логгер

verifySettlement

boolean

false

Проверять транзакцию расчёта сессии в блокчейне

Примеры

Пример

Ценообразование

Транспорт

Что демонстрирует

in-memory-demo

за вызов

InMemory

Полный цикл 402 в одном процессе

paid-weather-mcp

за вызов

stdio

Агент запускает сервер как подпроцесс

paid-weather-http

за вызов

Streamable HTTP

Сетевой сервер на Express

paid-weather-sse

за вызов

SSE (устаревший)

Обратно совместимый SSE-транспорт

paid-weather-dashboard

за вызов + ключ доступа

Streamable HTTP

Комбинированный MCP + дашборд + discovery

paid-streaming-mcp

сессия

stdio

Платёжные каналы, ваучеры, закрытие

paid-subscription-mcp

ключ доступа

stdio

Дневной пропуск, только по времени, пакеты вызовов

paid-peer-cash-mcp

за вызов

stdio

Доступ к инструментам Peer Cash, затем вывод дохода MPP

Запустите любой пример:

# 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

Пополнение тестового кошелька

Платные примеры требуют пополненного кошелька в тестовой сети Tempo. Пример Peer Cash — исключение: он использует основную сеть Tempo, поскольку маршрут дохода доступен только в live-режиме.

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

Совместимость сред выполнения

Базовая библиотека (сервер, клиент, хранилища, ограничение скорости, суммы, ключи доступа) переносима между средами выполнения через Web Crypto:

Среда выполнения

Поддержка

Node.js 20+

Полная

Cloudflare Workers

Полная

Vercel Edge

Полная

Deno

Полная

Bun

Полная

Модуль промежуточного слоя auth.ts использует node:crypto и требует Node.js. Edge-развёртывания вместо этого используют нативный роутер и примитивы аутентификации своей платформы.

Архитектура

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

Экспорт пакета

{
  ".": "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"
}

Принципы проектирования

  • Точность расчётов — все денежные вычисления используют базовые единицы bigint (6 десятичных знаков). Никакого дрейфа чисел с плавающей точкой после миллионов операций.

  • Подключение без затрат — трассировка, вебхуки и ограничение скорости являются no-op, если не настроены. Развёртывания без трассировки не выделяют спаны.

  • Всё подключаемо — хранилища, логгеры, ограничители скорости и аутентификация основаны на интерфейсах. Меняйте реализации, не трогая код шлюза.

  • Быстрый отказ — ошибки конфигурации выбрасываются в момент создания, а не в момент запроса.

  • Ошибки — это значения — типизированные классы ошибок со стабильными кодами. Используйте instanceof или err.code для программной обработки.

  • Кольцевой буфер журнала вызовов — O(1) с предварительным выделением памяти, никогда не растёт. Нет нагрузки на GC при высокой пропускной способности.

  • Плавный жизненный цикл — шлюз завершения отклоняет новые вызовы, drain ожидает выполняющиеся, сброс вебхуков, затем отключение.

Разработка

# 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

Набор тестов

27+ тестовых файлов, покрывающих:

  • Атомарность ключей доступа (одновременные погашения ключей на N вызовов)

  • Потоки ключей доступа (выпуск → погашение → исчерпание → повторная оплата)

  • Математика сумм (конвертации BigInt, крайние случаи)

  • Промежуточный слой аутентификации (все 5 фабрик)

  • Кольцевой буфер журнала вызовов (переполнение, ограничения ёмкости)

  • Плавное завершение и drain

  • Ответы API дашборда

  • Генерация Discovery / OpenAPI

  • Таксономия ошибок

  • Бесплатные инструменты (путь без оплаты)

  • Логгер (структурированный вывод, редактирование, дочерние логгеры)

  • Форматирование метрик Prometheus

  • Мультивалютный discovery

  • Платный поток (402 → учётные данные → квитанция)

  • Расчёт цен (тарифы, за вызов)

  • Ограничение скорости (token bucket, отказ, retry-after)

  • Точность расчётов (накопление BigInt при множестве вызовов)

  • Хелперы среды выполнения (randomHex, hmacSha256Hex)

  • Жизненный цикл сессии (открытие → ваучер → закрытие → расчёт)

  • Лимиты расходов (за вызов, суммарный, депозит сессии)

  • Трассировка OpenTelemetry (атрибуты спанов, запись ошибок)

  • Вебхуки (доставка, повтор, подпись HMAC, dead-letter)

  • Тесты типов (через tsd)

  • Бенчмарки пропускной способности (через vitest bench)

Плавное завершение

Подключите close() к сигналу завершения вашего контейнера:

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

Структурированное логирование

Библиотека поставляется с подключаемым интерфейсом Logger. По умолчанию: JSON в stderr с автоматическим редактированием секретов (приватные ключи, учётные данные, подписанные транзакции).

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(),
})

Адаптируйте под pino, winston или любую библиотеку логирования:

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) */,
}

Лицензия

MIT — Gaurav Pant

Related MCP Connectors

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.
    430 npm
    -
  • A
    license
    Not graded
    quality
    D
    maintenance
    Simplifies creating MCP servers in TypeScript with an Express-like API and experimental decorators, enabling quick definition of tools, resources, and prompts.
    5 npm
    197
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Turn your typed TypeScript functions into an MCP server — tool, resource, and prompt schemas inferred from your types and JSDoc. No schema library, no decorators, no boilerplate.
    7 npm
    MIT