Skip to main content

PayMCP

Provider-agnostic payment layer for MCP (Model Context Protocol) tools and agents.

🆕 x402 protocol is now fully supported. PayMCP includes native support for the x402 payment protocol and a dedicated Mode.X402 for clients capable of automatic on-chain payments.

paymcp is a lightweight SDK that helps you add monetization to your MCP‑based tools, servers, or agents. It supports multiple payment providers and integrates seamlessly with MCP's tool/resource interface.

Paper: https://zenodo.org/records/18158720

See the full documentation.


🔧 Features

  • ✅ Add @price(...) decorators to your MCP tools to enable pay‑per‑request billing.
  • ✅ Gate tools behind active subscriptions (where supported) with the @subscription(...) decorator; helper tools included.
  • 🔁 Pay‑per‑request flows support multiple modes (AUTO / X402 / TWO_STEP / RESUBMIT / ELICITATION / PROGRESS / DYNAMIC_TOOLS).
  • 🔌 Built-in support for major providers (see list) — plus a pluggable interface for custom providers.
  • ⚙️ Easy integration with FastMCP or other MCP servers

🚀 Quickstart

Install the SDK from PyPI:

pip install mcp paymcp

Initialize PayMCP:

import os
from mcp.server.fastmcp import FastMCP, Context
from paymcp import Mode, price
from paymcp.providers import StripeProvider

mcp = FastMCP("AI agent name")

PayMCP(
    mcp,
    providers=[
        StripeProvider(api_key=os.getenv("STRIPE_API_KEY")),
    ],
    mode=Mode.AUTO # optional, AUTO (default) / X402 / TWO_STEP / RESUBMIT / ELICITATION / PROGRESS / DYNAMIC_TOOLS
)

💡 Tip: In Mode.AUTO, you can configure both a traditional provider (e.g. Stripe) and an X402 provider. If the client has an X402 wallet, PayMCP will automatically use the x402 protocol; otherwise, it falls back to the traditional provider.

Use the @price decorator on any tool:

@mcp.tool()
@price(amount=0.99, currency="USD")
def add(a: int, b: int, ctx: Context) -> int: # `ctx` is required by the PayMCP tool signature — include it even if unused
    """Adds two numbers and returns the result."""
    return a + b

Demo MCP servers:

💰 Choose How to Charge (per tool)

Use either @price or @subscription on a tool (they are mutually exclusive).

Option A — Pay‑per‑request

@mcp.tool()
@price(amount=0.19, currency="USD")
def summarize(text: str, ctx: Context) -> str:
    return text[:200]

Option B — Subscription‑gated (providers with subscription support, e.g., Stripe)

User authentication is your responsibility. PayMCP will resolve identity from ctx.authInfo or a Bearer token (Authorization header). Make sure your token carries:

  • sub (treated as userId), and ideally
  • email (highly recommended for provider matching, e.g., Stripe).

PayMCP does not validate or verify the token; it only parses it to extract userId/email.

from paymcp import subscription

@mcp.tool()
@subscription(plan="price_pro_monthly")  # or a list of accepted plan IDs from your provider
async def generate_report(ctx: Context) -> str:
    return "Your report"

When you register the first subscription‑protected tool, PayMCP auto‑registers helper tools:

  • list_subscriptions — current subscriptions + available plans for the user.
  • start_subscription — accepts planId to create (or resume) a subscription.
  • cancel_subscription — accepts subscriptionId to cancel at period end.

🧩 Supported Providers

Built-in support is available for the following providers. You can also write a custom provider.

  • ✅ Stripe — pay‑per‑request + subscriptions

  • ✅ Adyen — pay‑per‑request

  • ✅ Coinbase Commerce — pay‑per‑request

  • ✅ PayPal — pay‑per‑request

  • ✅ Square — pay‑per‑request

  • ✅ Walleot — pay‑per‑request

  • ✅ USDC‑x402 (Base) — pay‑per‑request (x402 protocol)

  • ✅ USDC‑SPL‑x402 (Solana) — pay‑per‑request (x402 protocol)

  • 🔜 More providers welcome! Open an issue or PR.

🔌 Writing a Custom Provider

Any provider must subclass BasePaymentProvider and implement create_payment(...) and get_payment_status(...).

from paymcp.providers import BasePaymentProvider

class MyProvider(BasePaymentProvider):

    def create_payment(self, amount: float, currency: str, description: str):
        # Return (payment_id, payment_url)
        return "unique-payment-id", "https://example.com/pay"

    def get_payment_status(self, payment_id: str) -> str:
        return "paid"

PayMCP(mcp, providers=[MyProvider(api_key="...")])

🗄️ State Storage

By default, PayMCP stores payment_id and pending tool arguments in memory using a process-local Map. This is not durable and will not work across server restarts or multiple server instances (no horizontal scaling).

To enable durable and scalable state storage, you can provide a custom StateStore implementation. PayMCP includes a built-in RedisStateStore, which works with any Redis-compatible client.

from redis.asyncio import from_url
from paymcp import PayMCP, RedisStateStore

redis = await from_url("redis://localhost:6379")
PayMCP(
    mcp,
    providers=[
        StripeProvider(api_key=os.getenv("STRIPE_API_KEY")),
    ],
    state_store=RedisStateStore(redis)
)

🧭 Modes (pay‑per‑request only)

In version 0.4.2, paymentFlow was renamed to mode (old name still works).

The mode parameter controls how the user is guided through the pay‑per‑request payment process. Pick what fits your client:

  • Mode.AUTO (default) — Detects client capabilities and automatically selects the payment provider. If both a traditional provider and an X402 provider are configured, PayMCP uses x402 when the client supports it, and falls back to the traditional provider otherwise.
  • Mode.TWO_STEP — Splits the tool into two MCP methods. First call returns payment_url + next_step; the confirm method verifies and runs the original logic. Works in most clients.
  • Mode.RESUBMIT — Adds optional payment_id to the tool signature. First call returns payment_url + payment_id; second call with payment_id verifies then runs the tool. Similar compatibility to TWO_STEP.
  • Mode.ELICITATION — Sends a payment link via MCP elicitation (if supported). After payment, the tool completes in the same call.
  • Mode.PROGRESS — Keeps the call open, streams progress while polling the provider, and returns automatically once paid.
  • Mode.DYNAMIC_TOOLS — Temporarily exposes additional tools (e.g., confirm_payment_*) to steer the client/LLM through the flow.
  • Mode.X402 — Uses the x402 protocol for automatic on‑chain payments. Clients receive an MCP error with HTTP status 402 Payment Required formatted per x402, and can auto‑pay and retry without user interaction.

⚠️ Important limitations:

  • Mode.X402 can be used only if you are certain the MCP client supports automatic payments via x402.
  • Most major MCP clients do NOT currently support x402.
  • If client support is uncertain, use Mode.AUTO instead — it will safely fall back to other compatible flows.

Supported assets (current x402 protocol):

  • USDC on Base
  • USDC on Solana (often referred to as USDC‑SPL)

To accept payments in Mode.X402, you must use the X402Provider.

X402 Provider Configuration

Minimal setup for accepting USDC payments using x402:

import os
from paymcp.providers import X402Provider

provider = X402Provider(
    pay_to=[{"address": "0xYourAddress"}]
)

For development and testing, use Base Sepolia testnet:

provider = X402Provider(
    pay_to=[{
        "address": "0xYourAddress",
        "network": "eip155:84532",  # Base Sepolia testnet
    }]
)

eip155:84532 is the CAIP‑2 network identifier for the Base Sepolia testnet.

You can configure multiple pay_to entries to enable multi‑network or multi‑asset acceptance within the same provider instance.

⚠️ Note: Mode.X402 works only with MCP clients that explicitly support the x402 payment protocol. Since most existing clients do not, it is strongly recommended to use Mode.AUTO unless you fully control the client environment.


🔒 Security Notice

PayMCP is NOT compatible with STDIO mode deployments where end users download and run MCP servers locally. This would expose your payment provider API keys to end users, creating serious security vulnerabilities.


📄 License

MIT License

Release files for paymcp 0.9.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for paymcp 0.9.0
File Size Uploaded
paymcp-0.9.0.tar.gz 111.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for paymcp 0.9.0
File Interpreter ABI Platform
paymcp-0.9.0-py3-none-any.whl Python 3 none any Details

Total release size: 176.6 kB

Release files / paymcp-0.9.0.tar.gz

Download URL paymcp-0.9.0.tar.gz
Size 111.0 kB
Tags Source
SHA-256 checksum
How to use checksums
21d791b9769a881877ba339d4d340b4341a9492111c107486c2275f92e70831f
BLAKE2b-256 checksum
How to use checksums
56941b3365409a13ad77c6eb4eb34f97e96ff1fdab36f670ef918f770bfe4803
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 12, 2026.

Transparency log

Release files / paymcp-0.9.0-py3-none-any.whl

Download URL paymcp-0.9.0-py3-none-any.whl
Size 65.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1fc997f3191bac724ff8a218249a13ba0e13470ae7a57dcb5e277bd3be84f8a4
BLAKE2b-256 checksum
How to use checksums
8677e707e1d68da9b63140fdad82b9393d7eb3c5b9b312fceb613bfcfce48cd7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 12, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.9.0 This release

2 release files

0.8.4

2 release files

0.8.3

2 release files

0.8.2

2 release files

0.8.1

2 release files

0.7.0

2 release files

0.6.1

2 release files

0.5.3

2 release files

0.5.1

2 release files

0.4.3

2 release files

0.4.2

2 release files

0.3.1

2 release files

0.2.0

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

0.0.5

2 release files

0.0.4

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page