Back to Codex
APIs·
advanced
·15 min read·Apr 4, 2026

How to Accept Stripe's Machine Payments Protocol (MPP) in Your MCP Server

Monetize your MCP server by accepting per-tool-call payments using Stripe's Machine Payments Protocol. This guide covers pricing schemas, payment verification, and webhook handling for MPP-enabled MCP servers.

StripeMPPMachine Payments ProtocolpaymentsMCP servermonetizationbillingTypeScript

Accept Stripe's Machine Payments Protocol in Your MCP Server

Stripe's Machine Payments Protocol (MPP) lets you charge AI agents per tool call on your MCP server. Instead of billing human users, you bill the machine wallets held by AI agent frameworks — fully automated, with Stripe handling fraud, settlement, and compliance.

This guide walks through adding MPP to an existing MCP server.

What You're Building

code
AI Agent (Client) → MPP-enabled MCP Server → Your Business Logic
                           ↓
              Stripe verifies machine wallet
              Charges settled per tool call
              Funds deposited to your Stripe account

Prerequisites

  • Stripe account with MPP enabled (request access at stripe.com/mpp)
  • An existing MCP server (TypeScript or Python)
  • Stripe CLI installed
  • Node.js 18+

Step 1: Enable MPP on Your Stripe Account

MPP is currently in private beta. Once approved:

  1. Go to Stripe Dashboard → Settings → Machine Payments
  2. Enable MPP for your account
  3. Create a Merchant MPP Profile — this establishes your server identity in the Stripe MPP registry
  4. Note your
    code
    mpp_merchant_id

Step 2: Install the MPP Server SDK

bash
npm install @stripe/mcp-mpp-server stripe

Step 3: Add Pricing Metadata to Your Tools

The MPP protocol extends the standard MCP tool schema with an

code
x-mpp-pricing
field. When clients connect, they read this to understand costs before calling tools.

typescript
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
import { MPPMiddleware } from '@stripe/mcp-mpp-server';

const server = new Server(
  { name: 'my-paid-mcp-server', version: '1.0.0' },
  { capabilities: { tools: {} } }
);

// Define tools with MPP pricing
server.setRequestHandler(ListToolsRequestSchema, async () => {
  return {
    tools: [
      {
        name: 'search_records',
        description: 'Search our proprietary database of records',
        inputSchema: {
          type: 'object',
          properties: {
            query: { type: 'string', description: 'Search query' },
            limit: { type: 'number', description: 'Max results' }
          },
          required: ['query']
        },
        // MPP pricing extension
        'x-mpp-pricing': {
          model: 'per_call',
          amount: 5,        // 5 cents per call
          currency: 'usd',
          unit: 'cent'
        }
      },
      {
        name: 'generate_report',
        description: 'Generate a full analytical report',
        inputSchema: { ... },
        'x-mpp-pricing': {
          model: 'per_call',
          amount: 50,       // 50 cents per call
          currency: 'usd',
          unit: 'cent'
        }
      },
      {
        name: 'get_status',
        description: 'Check server status — free tool',
        inputSchema: { type: 'object', properties: {} }
        // No x-mpp-pricing = free tool
      }
    ]
  };
});

Step 4: Add the MPP Middleware

The MPP middleware intercepts

code
call_tool
requests, verifies the client's machine wallet with Stripe, charges the wallet, and only proceeds to your tool logic if payment succeeds.

typescript
import Stripe from 'stripe';
import { MPPMiddleware, MPPConfig } from '@stripe/mcp-mpp-server';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

const mppConfig: MPPConfig = {
  stripe,
  merchantId: process.env.MPP_MERCHANT_ID!,
  // Tool name → pricing (must match your ListTools response)
  pricing: {
    search_records:  { amount: 5,  currency: 'usd', unit: 'cent' },
    generate_report: { amount: 50, currency: 'usd', unit: 'cent' }
  },
  // Called when payment succeeds — proceed with tool logic
  onPaymentSuccess: async (toolName, walletId, chargeId) => {
    console.log(`Charged ${walletId} for ${toolName}: charge ${chargeId}`);
  },
  // Called when payment fails — return error to client
  onPaymentFailure: async (toolName, walletId, error) => {
    console.error(`Payment failed for ${toolName}: ${error.message}`);
  }
};

const mpp = new MPPMiddleware(mppConfig);

// Wrap your tool handler with MPP middleware
server.setRequestHandler(CallToolRequestSchema, mpp.wrap(async (request) => {
  const { name, arguments: args } = request.params;

  if (name === 'search_records') {
    const results = await myDatabase.search(args.query, args.limit ?? 10);
    return { content: [{ type: 'text', text: JSON.stringify(results) }] };
  }

  if (name === 'generate_report') {
    const report = await myReportEngine.generate(args);
    return { content: [{ type: 'text', text: report }] };
  }

  if (name === 'get_status') {
    return { content: [{ type: 'text', text: 'OK' }] };
  }

  throw new Error(`Unknown tool: ${name}`);
}));

Step 5: Handle Webhooks for Settlement Events

Stripe sends webhook events for each machine payment. Set up a webhook endpoint to track revenue and handle edge cases:

typescript
import express from 'express';

const app = express();

app.post('/stripe/webhooks', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.headers['stripe-signature']!;
  let event;

  try {
    event = stripe.webhooks.constructEvent(
      req.body,
      sig,
      process.env.STRIPE_WEBHOOK_SECRET!
    );
  } catch (err) {
    return res.status(400).send(`Webhook Error: ${err.message}`);
  }

  switch (event.type) {
    case 'machine_payment.succeeded':
      const payment = event.data.object;
      // Update your usage records
      await db.logPayment({
        toolName: payment.metadata.tool_name,
        walletId: payment.machine_wallet,
        amount:   payment.amount,
        chargeId: payment.id
      });
      break;

    case 'machine_payment.failed':
      // Log failures for debugging
      console.error('Payment failed:', event.data.object);
      break;

    case 'machine_wallet.balance_low':
      // Optionally: proactively notify the client
      break;
  }

  res.json({ received: true });
});

app.listen(3001);

Register your webhook endpoint:

bash
stripe listen --forward-to /stripe/webhooks
# In production:
stripe webhooks create --url https://yourserver.com/stripe/webhooks \
  --events machine_payment.succeeded,machine_payment.failed,machine_wallet.balance_low

Step 6: Environment Variables

bash
STRIPE_SECRET_KEY=sk_live_your_key
STRIPE_WEBHOOK_SECRET=whsec_your_secret
MPP_MERCHANT_ID=mpp_merchant_abc123

Step 7: Register in the Stripe MPP Registry

For clients to discover and trust your server, register it:

bash
curl https://api.stripe.com/v1/mpp/registry/servers \
  -u sk_live_your_key: \
  -d "merchant_id=mpp_merchant_abc123" \
  -d "server_url=https://yourserver.com/mcp" \
  -d "display_name=My Data Server" \
  -d "description=Proprietary business records search" \
  -d "category=data"

Once approved (usually within 24 hours), your server appears in the Stripe MPP registry and clients can discover it with verified trust badges.

Pricing Models

MPP supports several pricing models in

code
x-mpp-pricing
:

ModelDescriptionExample
code
per_call
Flat fee per tool invocation
code
{ model: 'per_call', amount: 10, unit: 'cent' }
code
per_token
Charge based on input/output tokens
code
{ model: 'per_token', amount: 1, unit: 'cent', per: 1000 }
code
per_result
Charge per result returned
code
{ model: 'per_result', amount: 2, unit: 'cent' }
code
subscription
Free for wallets with active subscription
code
{ model: 'subscription', plan_id: 'plan_xyz' }

Security Considerations

  1. Always verify payment server-side: Never trust the client's claim that payment succeeded — let the MPP middleware verify with Stripe
  2. Idempotency keys: The middleware handles idempotency automatically, preventing double-charges on retries
  3. Webhook signature verification: Always verify
    code
    stripe-signature
    headers — never skip this step
  4. Rate limiting: Apply per-wallet rate limits independently of payment to prevent abuse
  5. Audit logging: Log every tool call with its
    code
    chargeId
    for dispute resolution

Testing

Use Stripe test mode with test machine wallets:

bash
# Create a test machine wallet
curl https://api.stripe.com/v1/machine_wallets \
  -u sk_test_your_test_key: \
  -d "display_name=Test Agent" \
  -d "budget[amount]=10000" \
  -d "budget[currency]=usd"

Test cards and wallets don't trigger real charges — perfect for development and CI.

Monitoring Revenue

View MPP revenue in Stripe Dashboard → Machine Payments → Revenue, or pull reports via API:

bash
curl https://api.stripe.com/v1/machine_payments \
  -u sk_live_your_key: \
  -d "merchant=mpp_merchant_abc123" \
  -d "created[gte]=1700000000"