Skip to main content
Glama
mpp-mcp-gateway

mpp-mcp-gateway

mpp-mcp-gateway

通过 Tempo 区块链上的机器支付协议(MPP),使用稳定币微支付为任何 MCP 服务器变现。

构建按次调用、按会话或通过访问密钥向 AI 代理收费的 MCP 工具服务器 — 以 pathUSD 和其他 TIP-20 稳定币结算。构建能自动为这些工具付费并带有可配置支出上限的 AI 代理客户端。

License: MIT

目录

Related MCP server: easy-mcp

概述

mpp-mcp-gateway 是一个 TypeScript 库,为 MCP(Model Context Protocol)服务器添加稳定币微支付门控。当 AI 代理调用付费工具时,服务器会发出 402 Payment Required 质询。代理的客户端在 Tempo 区块链上签署支付交易,使用凭证重试调用,服务器在运行处理器并返回带收据的结果之前验证结算。

主要能力:

  • 四种定价模型 — 按次调用、分级定价、会话(支付通道)和访问密钥(订阅)

  • 多币种支持 — 每个工具可接受多种 TIP-20 稳定币

  • 精确收入跟踪 — BigInt 运算可防止数百万笔亚美分支付中的浮点漂移

  • 可插拔存储 — 内存、Upstash Redis(原子 CAS)、Cloudflare KV,或自带实现

  • 速率限制 — 令牌桶(内存或 Redis 支持),支持按工具覆盖

  • 认证中间件 — 不记名令牌、API 密钥、HTTP Basic、签名 URL、CORS — 全部为时序安全

  • Prometheus 指标 — /metrics 端点,零依赖

  • OpenTelemetry 追踪 — 每次付费调用可选的 span 树,禁用时零开销

  • Webhooks — HMAC 签名的事件推送,支持重试、退避和死信钩子

  • 服务发现 — 带 x-payment-info 扩展的 OpenAPI 3.1(由 mpp.land 爬取)

  • 仪表盘 — React UI + JSON API,用于实时收入和调用监控

  • 优雅关闭 — 排空进行中的调用、触发钩子、结算 webhooks

  • 运行时可移植 — 支持 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

访问密钥(订阅)

代理预先支付一次并获得一个不透明令牌。后续调用出示该令牌 — 在密钥过期或用尽之前无需再次支付。最适合“购买日票”或“购买 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(旧版)

较旧的 MCP 客户端

examples/paid-weather-sse/

In-Memory

测试、同进程

examples/in-memory-demo/

存储适配器

网关使用可插拔的 MppMcpStore 接口来持久化访问密钥记录和会话通道状态。

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

适配器

原子性

使用场景

Store.memory()

原子(promise 链)

测试、本地开发、单实例

Store.upstash(redis)

原子(Lua CAS)

生产环境、多实例

Store.cloudflareKv(ns)

尽力而为

边缘访问密钥(不用于会话)

Store.bridge(legacy)

尽力而为

与 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 方法必须保证原子读-改-写。在竞争条件下(CAS 风格后端),transform 回调可能被多次调用。

速率限制

速率限制在支付和处理器逻辑之前触发 — 被拒绝的调用永远不会发出 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} — 付费、免费、会话、access_key、总计

  • mppmcp_revenue_micro_usd_total{tool} — 以微美元计的累计收入

  • 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/。它每 2 秒轮询一次 JSON API,并显示:

  • 按收入排序的收入计数器和工具表

  • 按支付模式着色的实时调用日志

  • 访问密钥和会话统计

cd dashboard
npm install
npm run build

从你的 Express 应用中将 dashboard/dist/ 作为静态文件提供。

服务发现

根据 MPP 服务发现 IETF 草案,生成并提供带有 x-payment-info 扩展的 OpenAPI 3.1 文档。像 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

Webhooks

使用 HMAC-SHA-256 签名将事件推送到 URL。投递是即发即弃(非阻塞),支持重试和指数退避。

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 追踪

可选启用。传入 tracer 即可为每次付费调用获取 span 树。禁用时零开销。

import { trace } from '@opentelemetry/api'

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

Span 树:

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}

—

服务器赞助的 gas(付费方私钥)

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 tracer(可选启用)

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

控制台+脱敏

结构化日志记录器

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 + 仪表盘 + 发现

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 主网,因为收益路由仅限线上环境使用。

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。边缘部署则使用其平台原生的路由器和认证原语。

架构

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 位小数)。数百万次操作后也不会出现浮点漂移。

  • 零成本可选 — 跟踪、webhook 和速率限制在未配置时均为空操作。未启用跟踪的部署不会分配 span。

  • 一切可插拔 — 存储、日志记录器、速率限制器和认证均基于接口。无需改动网关代码即可替换实现。

  • 快速失败 — 配置错误在构造时抛出,而不是在请求时。

  • 错误即值 — 带有稳定错误码的类型化错误类。使用 instanceof 或 err.code 进行编程式处理。

  • 环形缓冲区调用日志 — O(1) 预分配,永不增长。高吞吐下无 GC 压力。

  • 优雅生命周期 — 关闭门控拒绝新调用,排空等待进行中的调用,刷新 webhook,然后断开连接。

开发

# 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 个工厂)

  • 调用日志环形缓冲区(回绕、容量限制)

  • 优雅关闭与排空

  • 仪表盘 API 响应

  • 发现 / OpenAPI 生成

  • 错误分类

  • 免费工具(免支付路径)

  • 日志记录器(结构化输出、脱敏、子日志记录器)

  • Prometheus 指标格式化

  • 多币种发现

  • 付费流程(402 → 凭证 → 收据)

  • 定价计算(分层、按次调用)

  • 速率限制(令牌桶、拒绝、retry-after)

  • 收益精确性(多次调用间的 BigInt 累加)

  • 运行时辅助函数(randomHex、hmacSha256Hex)

  • 会话生命周期(开启 → 凭证 → 关闭 → 结算)

  • 支出上限(单次调用、总计、会话存款)

  • OpenTelemetry 跟踪(span 属性、错误记录)

  • Webhook(投递、重试、HMAC 签名、死信)

  • 类型测试(通过 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.
    517 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.
    18 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