MCPay
High-performance Go gateway and reverse proxy for Model Context Protocol servers with rate-limiting, authorization, and pay-per-result metering.
README
<p align="center"> <img src="docs/assets/mcpay-mark.svg" alt="MCPay" width="132" height="132"> </p>
<h1 align="center">MCPay</h1>
<p align="center"> <strong>Stripe for AI agents.</strong> The spend authorization layer for paid AI tools.<br> Give agents a hard budget. Charge per execution. Keep payment code out of the tool handler. </p>
<p align="center"> <a href="https://github.com/yiaany/MCPay/actions/workflows/ci.yml"><img alt="CI" src="https://img.shields.io/github/actions/workflow/status/yiaany/MCPay/ci.yml?branch=main&style=flat-square&label=CI"></a> <img alt="Go 1.25+" src="https://img.shields.io/badge/Go-1.25%2B-00ADD8?style=flat-square&logo=go&logoColor=white"> <img alt="Node.js 24+" src="https://img.shields.io/badge/Node.js-24%2B-5FA04E?style=flat-square&logo=nodedotjs&logoColor=white"> <img alt="Python 3.11+" src="https://img.shields.io/badge/Python-3.11%2B-3776AB?style=flat-square&logo=python&logoColor=white"> <img alt="PostgreSQL 16+" src="https://img.shields.io/badge/PostgreSQL-16%2B-4169E1?style=flat-square&logo=postgresql&logoColor=white"> <a href="https://github.com/yiaany/MCPay/releases/tag/v0.1.0-beta.1"><img alt="MCPay beta release" src="https://img.shields.io/badge/release-v0.1.0--beta.1-B7FF64?style=flat-square&labelColor=111827"></a> <a href="#license"><img alt="Business Source License 1.1" src="https://img.shields.io/badge/license-BSL%201.1-111827?style=flat-square"></a> </p>
<p align="center"> <a href="#run-it-locally"><strong>Run the beta</strong></a> · <a href="#request-path"><strong>See the protocol</strong></a> · <a href="#security-model"><strong>Inspect security</strong></a> · <a href="#connect-an-mcp-server"><strong>Connect a tool</strong></a> </p>
<p align="center"> <code>signed spend sessions</code> <code>atomic nonce claims</code> <code>immutable prices</code> <code>durable settlement</code> </p>
MCPay sits between an AI agent and an MCP server. The agent receives a short-lived spend token tied to one server, a fixed action list, exact prices, a budget, an expiry time, and a nonce range. The gateway verifies that policy, atomically claims the nonce, calls the upstream tool, then records successfully delivered 2xx usage in a durable outbox.
No subscription per tool. No creator API key inside the agent. No floating-point money.
Beta status: the repository runs a closed, no-real-money beta. Wallet top-ups are test ledger credits; Stripe deposits, creator payouts, KYC/AML, disputes, and tax workflows aren't included yet.
<details> <summary><strong>What happens on one paid call?</strong></summary>
- The agent creates a spend session backed by a reserved wallet balance.
- MCPay signs the exact server, action prices, budget, expiry, and nonce range.
- The gateway verifies the token and claims the nonce in PostgreSQL before dispatch.
- A delivered
2xxresponse enters the local bbolt outbox and settles asynchronously.
</details>
<details> <summary><strong>What can I run today?</strong></summary>
The repository includes a Go API, gateway, settlement worker, PostgreSQL migrations, a creator dashboard, JavaScript and Python SDKs, browser tests, and a Docker Compose beta stack. Real deposits and payouts remain intentionally disabled.
</details>
Product tour
<p align="center"> <img src="docs/assets/control-plane.png" alt="MCPay control plane in dark mode" width="100%"> </p>
The control plane shows reserved agent budget, creator revenue, paid-call volume, local authorization latency, and recent settlement activity.
<table> <tr> <td width="50%"><img src="docs/assets/creator-studio.png" alt="MCPay Creator Studio in dark mode"></td> <td width="50%"><img src="docs/assets/settled-ledger.png" alt="MCPay settled ledger in dark mode"></td> </tr> <tr> <td><strong>Creator Studio</strong><br>Map each MCP action to an integer price and copy a configured gateway command.</td> <td><strong>Settled Ledger</strong><br>Trace every accepted call by receipt, nonce, action, price, status, and measured authorization time.</td> </tr> </table>
Screenshots use a fixed documentation dataset. The figures aren't production metrics.
Why MCPay exists
An agent that calls ten paid tools shouldn't need ten subscriptions, ten billing SDKs, and ten long-lived secrets. A creator shouldn't have to write reservation logic, replay protection, price checks, receipt storage, and settlement workers before charging five cents for web_search.
MCPay makes the spend contract explicit before execution:
| Policy | Bound into each spend session |
|---|---|
| Where the money can go | One server_id and environment |
| What the agent can call | An allowlist of action names |
| What each call costs | Immutable action -> price_minor snapshots |
| Maximum exposure | Reserved budget and maximum price per call |
| Replay boundary | Nonce start/end range with a distributed atomic claim |
| Lifetime | Signed expiry timestamp and online revocation check |
If the claim doesn't match the active session, the gateway stops before the upstream sees the request.
How it compares
| MCPay | API keys plus custom billing | Central billing proxy | Per-tool subscriptions | |
|---|---|---|---|---|
| Agent gets a bounded spend grant | Yes | You build it | Sometimes | No |
| Exact price signed into the session | Yes | You build it | Provider-specific | No |
| Cross-gateway replay protection | PostgreSQL atomic claim | Usually missing | Depends on provider | Not applicable |
| Tool handler owns payment code | No | Yes | Partly | Yes |
Delivered 2xx usage survives gateway restart |
bbolt outbox | You build it | Provider-owned | Provider-owned |
| Self-hosted control plane | Yes | Yes | Usually no | No |
| One wallet across creators | Designed for it | No common contract | Platform-specific | No |
| Source can be audited | Yes | Your code only | Usually no | Usually no |
Checkout is the easy part. MCPay handles bounded authorization, duplicate suppression across hosts, immutable prices, revocation ordering, durable delivery of accepted usage, and retry-safe settlement.
Request path
sequenceDiagram
participant A as Agent
participant C as MCPay API
participant G as MCPay Gateway
participant P as PostgreSQL
participant T as MCP Tool
participant W as Settlement Worker
A->>C: Create spend session
C->>P: Reserve budget and store price snapshot
C-->>A: Ed25519-signed spend token
A->>G: tools/call + token + nonce
G->>G: Verify issuer, server, action, price, expiry
G->>P: Atomic nonce claim
P-->>G: Active and unique
G->>P: Mark nonce dispatched
G->>T: Execute tool
T-->>G: Result
G-->>A: Result
G->>G: Persist delivered 2xx usage in bbolt outbox
G->>C: Upload usage batch
C->>P: Insert idempotent usage record
W->>P: Settle creator credit
The control-plane call happens before dispatch because offline-only replay protection can't coordinate two gateway machines or stop a revoked session. Signature checks still happen locally, so malformed or out-of-scope tokens never reach the database claim.
Security model
MCPay doesn't call a signed JWT "encrypted." It isn't. Spend tokens carry readable claims and use Ed25519 signatures so a gateway can detect any modification without holding the signing key.
| Boundary | What MCPay does |
|---|---|
| Spend tokens | Ed25519 signatures; the API keeps the private key, gateways receive only the public key |
| Browser authentication | HttpOnly, SameSite=Strict cookie; Secure is set on HTTPS requests |
| Password storage | bcrypt hashes, never plaintext passwords |
| Invite codes and session tokens | SHA-256 hashes stored in PostgreSQL |
| Gateway credentials | HMAC-SHA-256 signed, versioned, and scoped to one server |
| Public transport | HTTPS required outside explicit local-development mode |
| Internal Docker traffic | Private CA and TLS between Caddy and the API |
| Money state | Integer minor units, append-only ledger events, serializable transactions, deterministic idempotency keys |
| Replay control | Two-phase PostgreSQL nonce claim with a short pre-dispatch lease, then a non-reusable dispatched state |
| Usage delivery | Delivered 2xx calls enter a bounded bbolt outbox with at-least-once upload, idempotent ingestion, and idempotent settlement |
| HTTP forwarding | Redirects blocked, hop-by-hop headers removed, forwarding headers stripped, request size and time limits applied |
| Agent token injection | HTTPS plus an explicit gateway-origin allowlist; spend headers aren't added to arbitrary URLs |
What encryption at rest means here
MCPay doesn't add application-level field encryption to ledger rows. PostgreSQL credentials, signing keys, gateway secrets, and backups belong in your secret manager and encrypted storage; managed PostgreSQL or encrypted host volumes should protect database files at rest. That distinction matters because claiming "everything is encrypted" would hide the actual trust boundary.
For a beta deployment, keep PostgreSQL and the worker on a private network, terminate public TLS at Caddy, mount secrets at runtime, back up before migrations, and never commit deploy/.env.beta.
Run it locally
Requirements: Docker Engine with Compose v2, Go 1.25+ for key generation, and 4 GB of available memory. Node.js 24 runs inside the dashboard build container.
cp deploy/.env.beta.example deploy/.env.beta
go run ./cmd/mcpay-keygen
Paste the generated Ed25519 values into deploy/.env.beta, replace every replace-* value, then start the stack:
docker compose --env-file deploy/.env.beta -f deploy/compose.beta.yml config
docker compose --env-file deploy/.env.beta -f deploy/compose.beta.yml build
docker compose --env-file deploy/.env.beta -f deploy/compose.beta.yml up -d
docker compose --env-file deploy/.env.beta -f deploy/compose.beta.yml ps
Open http://localhost:8080. The compose stack starts PostgreSQL, migrations, API, worker, dashboard, Caddy, and internal TLS. Test top-ups remain virtual credits.
Check the running stack:
MCPAY_BETA_URL=http://localhost:8080 ./scripts/verify-central-beta.sh
PowerShell:
./scripts/verify-central-beta.ps1 -BaseUrl http://localhost:8080
Deployment notes, backups, DNS, and the Vercel/Supabase option live in docs/central-beta-runbook.md and docs/beta-deployment.md.
Connect an MCP server
Create a server and action in Creator Studio, issue its server-scoped gateway credential, then run the gateway from the repository beside the MCP process:
go run ./cmd/mcpay-gateway \
--target https://your-mcp-server.example \
--mcp-path /mcp \
--server-id srv_example \
--environment beta \
--token-issuer mcpay.beta \
--public-key "$MCPAY_PUBLIC_KEY" \
--control-plane-api https://api.example/v1/gateway/servers/srv_example \
--nonce-claim-api https://api.example/v1/gateway/nonces/claim \
--usage-api https://api.example/v1/usage-records \
--usage-api-token "$MCPAY_GATEWAY_API_TOKEN" \
--state-file ./mcpay-gateway.db
Paid requests carry two headers:
Authorization: Bearer <spend-token>
X-MCPay-Nonce: <nonce-within-the-signed-range>
The gateway removes both headers before forwarding the request upstream.
Connect an agent
MCPayAgentClient creates short-lived sessions and injects payment headers only into allowlisted HTTPS gateway origins:
import {
FileAgentSessionCreationStore,
MCPayAgentClient,
} from "@mcpay/sdk-js";
const paid = new MCPayAgentClient({
apiBaseUrl: "https://pay.example.com/api/mcpay",
accessToken: process.env.MCPAY_ACCESS_TOKEN!,
walletId: process.env.MCPAY_WALLET_ID!,
serverId: process.env.MCPAY_SERVER_ID!,
reservedBudget: 50,
gatewayOrigins: ["https://tools.example.com"],
sessionCreationStore: new FileAgentSessionCreationStore(
"./mcpay-agent-sessions.json",
),
});
const response = await paid.fetch("https://tools.example.com/mcp", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "tools/call",
params: {
name: "web_search",
arguments: { query: "payment rails for MCP" },
},
}),
});
Initialization and tool discovery pass through without spend headers. Session creation uses an idempotency key, so a lost response doesn't reserve the budget twice.
Direct SDK wrappers
JavaScript and Python wrappers exist for development and private integrations. Production mode rejects volatile usage; the durable gateway remains the recommended deployment path because it keeps nonce and usage state across process restarts.
import { MCPayClient } from "@mcpay/sdk-js";
const mcpay = new MCPayClient({
publicKey: process.env.MCPAY_PUBLIC_KEY!,
issuer: process.env.MCPAY_TOKEN_ISSUER!,
serverId: process.env.MCPAY_SERVER_ID!,
environment: "development",
allowVolatileUsage: true,
});
export const paidSearch = mcpay.tool({
name: "web_search",
priceMinor: 5,
handler: async (query: string) => search(query),
});
import os
from mcpay import MCPayClient
mcpay = MCPayClient(
public_key=os.environ["MCPAY_PUBLIC_KEY"],
issuer=os.environ["MCPAY_TOKEN_ISSUER"],
server_id=os.environ["MCPAY_SERVER_ID"],
environment="development",
allow_volatile_usage=True,
)
@mcpay.tool(name="web_search", price_minor=5)
async def search(query: str):
return await run_search(query)
Verification
The main branch checks Go, PostgreSQL integration, migrations, both SDKs, browser tests, package builds, dependency audits, and the Docker Compose configuration.
go test ./...
go test -race ./...
go vet ./...
go build ./cmd/...
npm ci
npm audit --audit-level=high
npm run build
npm run test
python -m pip install build
python -m build packages/sdk-python
python -m unittest discover -s packages/sdk-python/tests
For PostgreSQL tests, point MCPAY_TEST_DATABASE_URL at a disposable migrated database. The tests truncate application tables; never use a production database.
Run the loopback authorization benchmark with:
go run ./cmd/mcpay-benchmark --samples 1000 --warmup 100
It reports raw, local SDK, and gateway p50/p95/p99 on your machine. The benchmark excludes session setup, central ledger writes, and asynchronous usage upload, so it isn't a production SLA.
What ships today
| Available in the beta | Still required before real-money launch |
|---|---|
| Invite-only accounts and virtual USD wallets | Stripe or bank deposit integration |
| Server/action catalog with immutable price snapshots | Creator payouts and payout reconciliation |
| Signed spend sessions and online revocation | KYC/AML, sanctions, disputes, and tax handling |
| Distributed nonce claims across gateway hosts | Signing-key rotation with kid rollout procedures |
Durable outbox for successfully delivered 2xx usage and retry-safe settlement |
Production monitoring, paging, backup drills, and incident runbooks |
| Creator receipts, analytics, and test top-ups | Legal review for every launch jurisdiction |
MCPay is ready for a controlled beta with test credits. It isn't ready to hold customer funds.
Repository map
| Path | Purpose |
|---|---|
apps/api |
HTTP control-plane handlers and authentication |
apps/dashboard |
Creator and agent-budget dashboard |
cmd/mcpay-api |
Durable Go API process |
cmd/mcpay-gateway |
Paid MCP and HTTP reverse proxy |
cmd/mcpay-worker |
Settlement, retry, reconciliation, and expiry loop |
internal/controlplane |
PostgreSQL money and usage transactions |
internal/gateway |
Authorization proxy and bbolt state |
internal/sessions |
Spend claims and Ed25519 token code |
packages/sdk-js |
Agent client and JavaScript paid-tool wrapper |
packages/sdk-python |
Python async paid-tool wrapper |
migrations |
Ordered PostgreSQL schema changes |
License
MCPay uses the Business Source License 1.1. BSL isn't an OSI-approved open-source license, but it gives everyone access to the source and permits copying, modification, redistribution, and non-production use.
The MCPay Additional Use Grant permits internal production use when the user or organization has no more than $100,000 USD in aggregate gross revenue during the previous 12 months. It doesn't permit a hosted, managed, embedded, or white-label MCPay service for third parties. Production use outside that grant needs a commercial license; open an issue in the official repository to request commercial terms.
On August 13, 2030, this version changes to the Apache License 2.0. BSL also applies its open-source Change License on the fourth anniversary of a version's first public BSL distribution if that date arrives earlier.
Read LICENSE for the binding terms. The dashboard is distributed separately under the MIT license in apps/dashboard/LICENSE, including attribution for its upstream author and MCPay modifications. Have counsel review the BSL parameters before a real-money launch or fundraising diligence.
Security research is welcome. Source access makes review possible; it doesn't prove the absence of vulnerabilities, and BSL supplies the software without a security warranty.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。