Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
323 changes: 305 additions & 18 deletions examples/mcp-paid-tool/README.md
Original file line number Diff line number Diff line change
@@ -1,33 +1,320 @@
# paid MCP tool pattern
# Sell a paid MCP tool with x402 and Pyrimid

Best fit: MCP servers with expensive data, scraping, enrichment, analytics, compliance checks, search, or model calls.
This guide shows the smallest reproducible pattern for turning an MCP tool or
API call into a paid product that agents can discover through Pyrimid and buy
with Base USDC.

## Tool design
Use this pattern when your MCP server has an expensive tool: search,
enrichment, scraping, analytics, compliance checks, dataset export, model
calls, or any result where a free preview helps the buyer decide before paying.

- Free tool: `preview_*` returns schema, price, sample output, and payment requirement.
- Paid tool: `buy_*` returns HTTP 402/x402 requirement until paid.
- Discovery: publish server card, `llms.txt`, `agents.txt`, and Pyrimid catalog entry.
## Flow

## Minimal product metadata
1. Publish free discovery metadata so agents can see the tool, price, output
shape, and payment route.
2. Return HTTP `402` with x402 metadata from the paid endpoint until the buyer
supplies `X-PAYMENT` or `X-PAYMENT-TX`.
3. Verify the Base USDC payment and return the paid result.
4. List the product in Pyrimid so affiliate agents can route buyers to it.

```
MCP client -> preview_tool -> free schema, sample, price
MCP client -> buy_tool -> 402 x402 requirement
Buyer pays Base USDC through PyrimidRouter
MCP client retries buy_tool with X-PAYMENT-TX
Endpoint verifies payment and returns paid JSON
```

## Working paid endpoint

Drop this into a Next.js App Router project as
`app/api/x402/mcp-search/route.ts`. It is intentionally self-contained; replace
`runPaidSearch` with your actual expensive tool call.

```ts
import { NextRequest, NextResponse } from 'next/server';
import { verifyPyrimidPaymentTx } from '@/lib/payment-verification';

const PRODUCT = {
vendorId: 'acme-mcp',
productId: 'paid_mcp_search',
priceAtomic: 250_000, // 0.25 USDC, 6 decimals
priceDisplay: '0.25',
endpoint: 'https://acme.example/api/x402/mcp-search',
router: '0xc949AEa380D7b7984806143ddbfE519B03ABd68B',
};

export async function GET(req: NextRequest) {
const paymentProof =
req.headers.get('x-payment') || req.headers.get('x-payment-tx');
const query = req.nextUrl.searchParams.get('q') || 'mcp monetization';

if (!paymentProof) {
return NextResponse.json(
{
error: 'payment_required',
message: `Pay ${PRODUCT.priceDisplay} USDC on Base, then retry with X-PAYMENT-TX.`,
accepts: [x402Requirement(req.url)],
},
{
status: 402,
headers: {
'Cache-Control': 'private, no-store',
'X-Payment-Required': JSON.stringify(x402Requirement(req.url)),
'WWW-Authenticate': 'x402',
},
}
);
}

const verification = await verifyPyrimidPaymentTx(paymentProof, {
requiredAmountAtomic: PRODUCT.priceAtomic,
// Add expectedVendorId / expectedProductId here after onchain registration.
});

if (!verification.valid) {
return NextResponse.json(
{ error: 'invalid_payment', reason: verification.reason },
{ status: 402, headers: { 'Cache-Control': 'private, no-store' } }
);
}

return NextResponse.json({
ok: true,
paid: true,
product_id: PRODUCT.productId,
query,
result: runPaidSearch(query),
payment: verification,
});
}

function x402Requirement(resource: string) {
return {
x402Version: 2,
scheme: 'exact',
network: 'base',
asset: 'USDC',
maxAmountRequired: PRODUCT.priceDisplay,
payTo: PRODUCT.router,
resource,
description: 'Paid MCP search result with enriched citations',
mimeType: 'application/json',
vendorId: PRODUCT.vendorId,
productId: PRODUCT.productId,
affiliateBps: 3000,
protocol: 'pyrimid',
settlement: 'PyrimidRouter on Base',
};
}

function runPaidSearch(query: string) {
return {
summary: `Paid search package for ${query}`,
citations: [
'https://pyrimid.ai/quickstart',
'https://pyrimid.ai/api/v1/catalog',
],
next_actions: [
'Expose this route from the MCP buy_* tool.',
'Publish the catalog metadata below.',
'Test the no-payment 402 response before taking real payments.',
],
};
}
```

If your stack is not Next.js, keep the same behavior: no payment proof returns
`402`, valid payment returns the paid JSON, invalid payment returns a structured
`402` error.

## Expected 402 response

Agents should be able to call the paid URL without payment and parse a complete
x402 requirement:

```bash
curl -i 'https://acme.example/api/x402/mcp-search?q=wallet-risk'
```

```http
HTTP/1.1 402 Payment Required
Cache-Control: private, no-store
WWW-Authenticate: x402
X-Payment-Required: {"x402Version":2,"scheme":"exact","network":"base","asset":"USDC","maxAmountRequired":"0.25","payTo":"0xc949AEa380D7b7984806143ddbfE519B03ABd68B","resource":"https://acme.example/api/x402/mcp-search?q=wallet-risk","description":"Paid MCP search result with enriched citations","mimeType":"application/json","vendorId":"acme-mcp","productId":"paid_mcp_search","affiliateBps":3000,"protocol":"pyrimid","settlement":"PyrimidRouter on Base"}
```

```json
{
"error": "payment_required",
"message": "Pay 0.25 USDC on Base, then retry with X-PAYMENT-TX.",
"accepts": [
{
"x402Version": 2,
"scheme": "exact",
"network": "base",
"asset": "USDC",
"maxAmountRequired": "0.25",
"payTo": "0xc949AEa380D7b7984806143ddbfE519B03ABd68B",
"resource": "https://acme.example/api/x402/mcp-search?q=wallet-risk",
"description": "Paid MCP search result with enriched citations",
"mimeType": "application/json",
"vendorId": "acme-mcp",
"productId": "paid_mcp_search",
"affiliateBps": 3000,
"protocol": "pyrimid",
"settlement": "PyrimidRouter on Base"
}
]
}
```

## MCP tool shape

Expose a free preview tool and a paid buy tool. The preview tool makes buyer
agents comfortable before they spend.

```json
{
"tools": [
{
"name": "preview_mcp_search",
"description": "Preview price, inputs, sample output, and payment route for paid MCP search.",
"inputSchema": {
"type": "object",
"properties": {
"q": { "type": "string", "description": "Search query" }
},
"required": ["q"]
}
},
{
"name": "buy_mcp_search",
"description": "Buy the full MCP search result after x402 payment.",
"inputSchema": {
"type": "object",
"properties": {
"q": { "type": "string" },
"x_payment_tx": { "type": "string", "description": "Base tx hash or x402 payment proof" }
},
"required": ["q", "x_payment_tx"]
}
}
]
}
```

## Pyrimid catalog metadata

Publish product metadata that matches the paid endpoint and MCP tool names.
Agents can then discover the product through the Pyrimid catalog before making
a paid call.

```json
{
"vendor_id": "your-mcp-server",
"product_id": "paid_search",
"description": "Paid MCP search result with enriched citations",
"vendor_id": "acme-mcp",
"vendor_name": "Acme MCP",
"product_id": "paid_mcp_search",
"name": "Paid MCP Search",
"description": "Paid MCP search result with enriched citations and next-action recommendations.",
"category": "search-scraping",
"tags": ["mcp", "search", "x402", "paid-tools"],
"price_usdc": 50000,
"tags": ["mcp", "search", "x402", "base", "paid-tools"],
"price_usdc": 250000,
"price_display": "0.25",
"affiliate_bps": 3000,
"endpoint": "https://your-service.com/api/paid/search",
"endpoint": "https://acme.example/api/x402/mcp-search",
"network": "base",
"asset": "USDC"
"asset": "USDC",
"payment_protocol": "x402",
"settlement": "PyrimidRouter",
"mcp": {
"server_url": "https://acme.example/mcp",
"preview_tool": "preview_mcp_search",
"paid_tool": "buy_mcp_search"
},
"output_schema": {
"type": "object",
"properties": {
"summary": { "type": "string" },
"citations": { "type": "array", "items": { "type": "string" } },
"next_actions": { "type": "array", "items": { "type": "string" } }
},
"required": ["summary", "citations", "next_actions"]
},
"links": {
"pyrimid_catalog": "https://pyrimid.ai/api/v1/catalog",
"pyrimid_quickstart": "https://pyrimid.ai/quickstart",
"pyrimid_proof": "https://pyrimid.ai/proof"
}
}
```

## Discovery files

Add a short `llms.txt` or `agents.txt` entry so crawlers can find the paid
route even before the catalog indexes it.

```txt
# Acme MCP

Paid MCP search endpoint:
https://acme.example/api/x402/mcp-search

Payment:
- Protocol: x402
- Network: Base
- Asset: USDC
- Router: PyrimidRouter
- Catalog: https://pyrimid.ai/api/v1/catalog
- Quickstart: https://pyrimid.ai/quickstart

Free preview tool: preview_mcp_search
Paid tool: buy_mcp_search
```

## Repro checklist

Run these checks before asking agents to buy:

```bash
# Public discovery works.
curl -fsSL https://acme.example/llms.txt
curl -fsSL https://acme.example/agents.txt

# The paid endpoint returns a 402 without executing the paid tool.
curl -i 'https://acme.example/api/x402/mcp-search?q=wallet-risk'

# The 402 response includes Base USDC and Pyrimid product metadata.
curl -fsSL 'https://acme.example/api/x402/mcp-search?q=wallet-risk' \
| jq '.accepts[0] | {network, asset, payTo, productId, affiliateBps}'

# Agents can find the product in Pyrimid after indexing.
curl -fsSL 'https://pyrimid.ai/api/v1/catalog?query=paid_mcp_search&limit=5'
```

Do not test with a real payment until the no-payment path is correct. The
unpaid response should be cache-safe, browser-readable, and complete enough for
an x402 client to pay and retry.

## Common mistakes

- Returning `400` validation errors before `402`; agents cannot learn how to
pay.
- Hiding price, network, or asset outside the machine-readable `accepts[]`
object.
- Using a different `product_id` in the route, MCP tool, and catalog entry.
- Forgetting `affiliate_bps`; distribution agents need to know the commission
before recommending the product.
- Listing a paid tool without a free preview, forcing buyers to spend before
seeing the schema or sample output.

## Why route through Pyrimid?

- Agents can find your tool in one catalog.
- Buyer agents get a standard x402 payment flow.
- Affiliates can route demand to your tool.
- Vendor, affiliate, and protocol fees are visible onchain.
- Agents can discover your tool in one catalog:
<https://pyrimid.ai/api/v1/catalog>.
- Buyer agents get a standard x402 payment flow on Base.
- Affiliate agents can route demand to your tool and earn the commission you
choose.
- Vendor, affiliate, and protocol fees are visible onchain:
<https://pyrimid.ai/proof>.
- The integration path is documented at <https://pyrimid.ai/quickstart>.