πŸ“šguideintermediate

PayMCP: Accept Payments in AI Agents with MCP (2025)

Add payment processing to MCP tools and AI agents. Set up PayMCP with Stripe, PayPal, Coinbase. Learn pay-per-request billing and subscription gating patterns.

ByMCP Directory Team
Published
⏱️20 minutes
paymcppaymentsmcpstripepaypalmonetizationai-agentsbillingsubscriptionsfintech

PayMCP: Accept Payments in AI Agents with MCP

What you'll learn: How to add payment processing to your MCP tools and AI agents using PayMCP. Includes setup for Stripe, PayPal, Coinbase, and patterns for pay-per-request billing and subscriptions.

What is PayMCP?

PayMCP is a lightweight Python SDK that adds a provider-agnostic payment layer to MCP (Model Context Protocol) tools and agents. It enables you to:

  • Charge per API/tool call
  • Gate features behind subscriptions
  • Support multiple payment providers
  • Build monetized AI services

Use Cases

| Use Case | Description | |----------|-------------| | AI Tool Marketplace | Charge for premium AI capabilities | | Subscription Services | Gate advanced features behind plans | | API Monetization | Bill per request to your MCP servers | | Agent Services | Let AI agents pay for and charge for services |

Prerequisites

Before starting, ensure you have:

  • Python 3.9+ installed
  • Payment provider account (Stripe, PayPal, etc.)
  • API keys from your chosen provider
  • Basic understanding of MCP servers

Installation

Install PayMCP and MCP SDK:

pip install mcp paymcp

For Redis-backed production deployments:

pip install mcp paymcp redis

Quick Start

Step 1: Set Up Environment Variables

# Stripe (recommended for getting started)
export STRIPE_API_KEY="sk_test_..."

# Or PayPal
export PAYPAL_CLIENT_ID="..."
export PAYPAL_CLIENT_SECRET="..."

Step 2: Create Your First Paid Tool

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

# Create MCP server
mcp = FastMCP("my-paid-tools")

# Initialize PayMCP with Stripe
PayMCP(
    mcp,
    providers=[
        StripeProvider(api_key=os.getenv("STRIPE_API_KEY")),
    ],
    mode=Mode.TWO_STEP
)

# Create a paid tool
@mcp.tool()
@price(amount=0.10, currency="USD")
async def summarize_text(text: str, ctx: Context) -> str:
    """Summarize text for $0.10 per request"""
    # Your summarization logic
    return f"Summary: {text[:200]}..."

if __name__ == "__main__":
    mcp.run()

Step 3: Run Your Server

python server.py

Supported Payment Providers

PayMCP supports multiple payment providers out of the box:

| Provider | Pay-Per-Request | Subscriptions | Setup Complexity | |----------|-----------------|---------------|------------------| | Stripe | Yes | Yes | Easy | | PayPal | Yes | No | Medium | | Coinbase Commerce | Yes | No | Easy | | Adyen | Yes | No | Medium | | Square | Yes | No | Medium | | Walleot | Yes | No | Easy |

Stripe Configuration

from paymcp.providers import StripeProvider

provider = StripeProvider(
    api_key=os.getenv("STRIPE_API_KEY")
)

PayPal Configuration

from paymcp.providers import PayPalProvider

provider = PayPalProvider(
    client_id=os.getenv("PAYPAL_CLIENT_ID"),
    client_secret=os.getenv("PAYPAL_CLIENT_SECRET"),
    sandbox=True  # Set to False for production
)

Coinbase Commerce Configuration

from paymcp.providers import CoinbaseCommerceProvider

provider = CoinbaseCommerceProvider(
    api_key=os.getenv("COINBASE_COMMERCE_API_KEY")
)

Payment Patterns

Pattern 1: Pay-Per-Request

Charge users each time they call a tool:

@mcp.tool()
@price(amount=0.05, currency="USD")
async def analyze_image(image_url: str, ctx: Context) -> str:
    """Analyze an image - $0.05 per analysis"""
    # Analysis logic
    return "Analysis results..."

Pattern 2: Subscription Gating

Require an active subscription to use premium tools:

from paymcp import subscription

@mcp.tool()
@subscription(plan="price_pro_monthly")
async def generate_report(data: dict, ctx: Context) -> str:
    """Generate detailed report (Pro subscribers only)"""
    return "Detailed report..."

When using subscription tools, PayMCP automatically provides:

  • list_subscriptions β€” Show available plans
  • start_subscription β€” Create or resume subscription
  • cancel_subscription β€” Cancel at period end

Pattern 3: Tiered Pricing

Different prices for different tiers:

@mcp.tool()
@price(amount=0.01, currency="USD")
async def basic_search(query: str, ctx: Context) -> str:
    """Basic search - $0.01"""
    return "Basic results..."

@mcp.tool()
@price(amount=0.10, currency="USD")
async def advanced_search(query: str, ctx: Context) -> str:
    """Advanced search with AI - $0.10"""
    return "Advanced results..."

Pattern 4: Dynamic Pricing

Calculate price based on input:

@mcp.tool()
async def process_document(document: str, ctx: Context) -> str:
    """Process document - price varies by length"""
    word_count = len(document.split())
    price_usd = max(0.01, word_count * 0.001)  # $0.001 per word, min $0.01

    # Custom billing logic
    return f"Processed {word_count} words"

Payment Modes

PayMCP supports different payment flow patterns:

| Mode | Description | Best For | |------|-------------|----------| | TWO_STEP | First call returns payment URL, second confirms | Most use cases | | RESUBMIT | Adds payment_id parameter to tool | Simple flows | | ELICITATION | Requests payment within same call | Streaming | | PROGRESS | Keeps connection open while polling | Real-time feedback | | DYNAMIC_TOOLS | Exposes temporary helper tools | Complex flows |

Example: TWO_STEP Mode (Default)

PayMCP(
    mcp,
    providers=[StripeProvider(api_key=os.getenv("STRIPE_API_KEY"))],
    mode=Mode.TWO_STEP
)

User flow:

  1. User calls summarize_text("Hello world")
  2. Returns: "Please complete payment at: https://checkout.stripe.com/..."
  3. User completes payment
  4. User calls summarize_text("Hello world") again
  5. Returns: "Summary: Hello world..."

Production Setup

Redis State Storage

For horizontal scaling and persistent state:

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

Multiple Providers

Support multiple payment methods:

PayMCP(
    mcp,
    providers=[
        StripeProvider(api_key=os.getenv("STRIPE_API_KEY")),
        PayPalProvider(
            client_id=os.getenv("PAYPAL_CLIENT_ID"),
            client_secret=os.getenv("PAYPAL_CLIENT_SECRET")
        ),
        CoinbaseCommerceProvider(api_key=os.getenv("COINBASE_API_KEY"))
    ],
    mode=Mode.TWO_STEP
)

Authentication & Identity

PayMCP extracts user identity from:

  1. ctx.authInfo from MCP context
  2. Bearer token in Authorization header

Required JWT claims:

  • sub β€” Used as userId
  • email β€” Recommended for provider matching
# PayMCP automatically extracts identity
# No additional code needed if your MCP client sends auth

Security Considerations

Critical: Deployment Mode

Warning: PayMCP is NOT compatible with STDIO mode deployments where users run servers locally. This would expose payment provider API keys.

Safe deployment patterns:

  • Remote MCP servers (HTTP/SSE)
  • Hosted services
  • Serverless functions

Best Practices

  1. Never expose API keys in client-side code
  2. Use environment variables for all secrets
  3. Enable webhook verification for payment confirmation
  4. Implement rate limiting to prevent abuse
  5. Log all transactions for audit purposes

Creating Custom Payment Providers

Extend BasePaymentProvider to add new providers:

from paymcp.providers import BasePaymentProvider

class MyCustomProvider(BasePaymentProvider):
    def __init__(self, api_key: str):
        self.api_key = api_key

    async def create_payment(
        self,
        amount: float,
        currency: str,
        description: str
    ) -> tuple[str, str]:
        """Create a payment and return (payment_id, payment_url)"""
        # Integration with your payment system
        payment_id = "pay_123"
        payment_url = "https://payment.example.com/pay/123"
        return payment_id, payment_url

    async def get_payment_status(self, payment_id: str) -> str:
        """Check payment status: 'pending', 'completed', 'failed'"""
        # Check your payment system
        return "completed"

Real-World Example: AI Writing Tool

Complete example of a monetized AI writing service:

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

mcp = FastMCP("ai-writer")

PayMCP(
    mcp,
    providers=[StripeProvider(api_key=os.getenv("STRIPE_API_KEY"))],
    mode=Mode.TWO_STEP
)

# Free tool
@mcp.tool()
async def check_grammar(text: str, ctx: Context) -> str:
    """Free grammar check"""
    return f"Grammar checked: {text}"

# Pay-per-request
@mcp.tool()
@price(amount=0.05, currency="USD")
async def rewrite_paragraph(text: str, style: str, ctx: Context) -> str:
    """Rewrite paragraph in specified style - $0.05"""
    return f"Rewritten ({style}): {text}"

# Premium subscription
@mcp.tool()
@subscription(plan="price_unlimited_monthly")
async def write_article(topic: str, length: int, ctx: Context) -> str:
    """Generate full article - Pro subscribers only"""
    return f"Article about {topic}..."

if __name__ == "__main__":
    mcp.run()

Troubleshooting

Payment Not Processing

Symptoms: Payment URL generated but payment fails

Solutions:

  1. Verify API keys are correct
  2. Check provider dashboard for errors
  3. Ensure webhook endpoints are configured
  4. Verify user has valid payment method

State Not Persisting

Symptoms: Users have to repay on server restart

Solutions:

  1. Use Redis state storage for production
  2. Configure Redis connection properly
  3. Check Redis connectivity

Identity Not Resolving

Symptoms: Users can't be matched to payments

Solutions:

  1. Ensure JWT contains sub claim
  2. Add email claim for provider matching
  3. Verify auth flow is sending tokens

Related Payment MCP Projects

The MCP payments ecosystem is growing:

Next Steps

Resources


Last updated: December 2025 | Have questions? Browse more MCP guides