Examples
Basic MCP Server with Payment
A minimal MCP server that charges $0.01 per search call:
ts
import Fastify from 'fastify';
import { PayrePay } from '@payre/pay';
const app = Fastify();
const payre = new PayrePay({ secretKey: process.env.PAYRE_SECRET_KEY });
const sessions = new Map();
// MCP initialize
app.post('/mcp', async (req, reply) => {
const { method, params, id } = req.body;
if (method === 'initialize') {
const sessionId = crypto.randomUUID();
sessions.set(sessionId, { initialized: true });
reply.header('mcp-session-id', sessionId);
return {
jsonrpc: '2.0', id,
result: {
protocolVersion: '2025-03-26',
capabilities: { tools: { listChanged: false } },
serverInfo: { name: 'my-paid-server', version: '1.0.0' },
},
};
}
if (method === 'tools/list') {
return {
jsonrpc: '2.0', id,
result: {
tools: [{
name: 'search',
description: 'Search documents ($0.01/call)',
inputSchema: {
type: 'object',
properties: { query: { type: 'string' } },
required: ['query'],
},
}],
},
};
}
if (method === 'tools/call') {
const apiKey = req.headers['x-payre-key'];
if (!apiKey) {
return {
jsonrpc: '2.0', id,
result: {
content: [{ type: 'text', text: 'Payment required. Set X-Payre-Key header.' }],
isError: true,
},
};
}
try {
const receipt = await payre.charge({ apiKey, tool: 'search', amount: 0.01 });
const results = await doSearch(params.arguments.query);
return {
jsonrpc: '2.0', id,
result: {
content: [{ type: 'text', text: JSON.stringify(results) }],
_meta: { receipt },
},
};
} catch (err) {
return {
jsonrpc: '2.0', id,
result: {
content: [{ type: 'text', text: `Payment failed: ${err.message}` }],
isError: true,
},
};
}
}
});
app.listen({ port: 3001 });Multi-Tool Pricing
A server with three tools at different price points:
ts
import { PayrePay } from '@payre/pay';
const payre = new PayrePay({ secretKey: process.env.PAYRE_SECRET_KEY });
const pricing = payre.pricing({
'search': 0.005, // $0.005 -- cheap, high volume
'summarize': 0.01, // $0.01 -- moderate
'generate': 0.05, // $0.05 -- expensive, uses LLM
});
async function handleToolCall(toolName, args, request) {
const receipt = await pricing.charge({
apiKey: request.headers['x-payre-key'],
tool: toolName,
});
switch (toolName) {
case 'search': return doSearch(args.query);
case 'summarize': return doSummarize(args.text);
case 'generate': return doGenerate(args.prompt);
}
}Freemium Model
Give users 5 free calls, then require payment:
ts
const freeTierUsage = new Map(); // apiKey -> count
async function handleToolCall(params, request) {
const apiKey = request.headers['x-payre-key'];
// Track free tier
const used = freeTierUsage.get(apiKey) ?? 0;
if (used < 5) {
// Free tier
freeTierUsage.set(apiKey, used + 1);
return doSearch(params.query);
}
// Paid tier
const receipt = await payre.charge({
apiKey,
tool: 'search',
amount: 0.005,
});
return doSearch(params.query);
}Consumer: Calling a Paid MCP Server
From the consumer side, using Claude Desktop or any MCP client:
json
{
"mcpServers": {
"paid-search": {
"url": "https://search-server.example.com/mcp",
"headers": {
"X-Payre-Key": "pyr_ck_live_your_key_here"
}
}
}
}Or with curl:
bash
# Initialize
curl -X POST https://search-server.example.com/mcp \
-H "Content-Type: application/json" \
-H "X-Payre-Key: pyr_ck_live_xxx" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","clientInfo":{"name":"test","version":"1.0"}}}'
# Call tool (charges $0.01)
curl -X POST https://search-server.example.com/mcp \
-H "Content-Type: application/json" \
-H "X-Payre-Key: pyr_ck_live_xxx" \
-H "mcp-session-id: <session-id-from-init>" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"search","arguments":{"query":"hello world"}}}'