Skip to main content
Glama
aspiring-100x

mpp-mcp-gateway

mpp-mcp-gateway

MCPサーバーを、Tempoブロックチェーン上のMachine Payments Protocol(MPP)によるステーブルコインのマイクロペイメントで収益化します。

AIエージェントに対して、呼び出しごと、セッションごと、またはアクセスキーごとに課金するMCPツールサーバーを構築できます。決済はpathUSDおよびその他のTIP-20ステーブルコインで行われます。また、設定可能な支出上限に基づいて、これらのツールに自動的に支払うAIエージェントクライアントを構築できます。

License: MIT

目次

Related MCP server: MCP Server TypeScript

概要

mpp-mcp-gateway は、MCP(Model Context Protocol)サーバーにステーブルコインのマイクロペイメントゲーティングを追加するTypeScriptライブラリです。AIエージェントが有料ツールを呼び出すと、サーバーは402 Payment Requiredチャレンジを発行します。エージェントのクライアントはTempoブロックチェーン上で支払いトランザクションに署名し、資格情報を付けて呼び出しを再試行します。サーバーはハンドラーを実行してレシート付きで結果を返す前に、決済を検証します。

主な機能:

  • 4つの価格モデル — 従量課金(per-call)、段階的(tiered)、セッション(支払いチャネル)、アクセスキー(サブスクリプション)

  • マルチカレンシー対応 — ツールごとに複数のTIP-20ステーブルコインを受け付け

  • 正確な収益追跡 — BigInt演算により、数百万件のサブセント支払いでも浮動小数点のずれを防止

  • プラグ可能なストレージ — インメモリ、Upstash Redis(アトミックCAS)、Cloudflare KV、または独自実装

  • レート制限 — トークンバケット(インメモリまたはRedisバックエンド)とツールごとの上書き設定

  • 認証ミドルウェア — ベアラートークン、APIキー、HTTP Basic、署名付きURL、CORS — すべてタイミングセーフ

  • Prometheusメトリクス/metrics エンドポイント、依存関係ゼロ

  • OpenTelemetryトレーシング — 有料呼び出しごとのオプトインのスパンツリー、無効時はコストゼロ

  • ウェブフック — HMAC署名付きイベントプッシュ、リトライ、バックオフ、デッドレターフック付き

  • サービスディスカバリーx-payment-info 拡張機能を備えたOpenAPI 3.1(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. サーバーがMPPチャレンジを含むMCPエラーコード -32042 で応答する

  3. クライアントが支出上限を適用し、支払いに署名し、資格情報を付けて再試行する

  4. サーバーが mppx 経由でオンチェーン決済を検証する

  5. ハンドラーが実行され、支払いレシート(txハッシュ、タイムスタンプ)とともに結果が返される

インストール

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

価格モデル

従量課金(Per-Call)

呼び出しごとの固定価格です。1回の呼び出しにつき1つのオンチェーントランザクションが必要です。

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

段階的(Tiered)

累計呼び出し回数に応じて価格が下がる(または上がる)方式です。

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回分の呼び出しを購入」といったUXに最適です。

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

アトミック(プロミスチェーン)

テスト、ローカル開発、単一インスタンス

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

カスタムストア

4つのメソッドからなるインターフェースを実装します:

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

認証ミドルウェア

ダッシュボード、メトリクス、ディスカバリーの各エンドポイントを保護するための5つの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アプリから静的ファイルとして配信します。

サービスディスカバリー

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

ウェブフック

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.receivedaccess-key.issuedaccess-key.expiredsession.openedsession.closedcall.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.namemppmcp.pricing.typemppmcp.amountmppmcp.payment.modemppmcp.payment.tx-hashmppmcp.session.actionmppmcp.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

required

クライアント名

version

string

required

クライアントバージョン

privateKey

0x${string}

required

エージェントウォレットの秘密鍵

maxPerCall

string

'1.00'

1回の呼び出しあたりの最大支出(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 (legacy)

後方互換の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桁)を使用します。数百万回の操作後も浮動小数点のずれは発生しません。

  • ゼロコストのオプトイン — トレーシング、ウェブフック、レート制限は、設定しない限りノーオペレーションです。トレースしないデプロイメントではスパンは割り当てられません。

  • すべてプラグ可能 — ストア、ロガー、レートリミッター、認証はインターフェースベースです。ゲートウェイコードに触れることなく実装を交換できます。

  • フェイルファスト — 設定エラーはリクエスト時ではなく構築時にスローされます。

  • エラーは値 — 安定したコードを持つ型付きエラークラス。プログラムによる処理にはinstanceofまたはerr.codeを使用します。

  • リングバッファコールログ — O(1)で事前割り当てされ、決して増大しません。高スループット下でもGCプレッシャーがありません。

  • グレースフルライフサイクル — シャットダウンゲートは新しい呼び出しを拒否し、ドレインは進行中の呼び出しを待機し、ウェブフックをフラッシュしてから切断します。

開発

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

  • セッションライフサイクル(オープン → バウチャー → クローズ → 決済)

  • 支出上限(1回あたり、合計、セッション預入)

  • OpenTelemetryトレーシング(スパン属性、エラー記録)

  • ウェブフック(配信、リトライ、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インターフェースが同梱されています。デフォルト:秘密情報(秘密鍵、クレデンシャル、署名済みトランザクション)を自動的にリダクションしてstderrにJSONで出力します。

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

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