Skip to content

Commit 56738da

Browse files
committed
feat: add payments-mcp
1 parent 49737da commit 56738da

File tree

5 files changed

+70
-135
lines changed

5 files changed

+70
-135
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,13 @@ pnpm dev
3232
npx @modelcontextprotocol/inspector
3333
```
3434

35+
## Development
36+
Run the following command in the `packages/payments-mcp` directory:
37+
```bash
38+
pnpm link
39+
```
40+
You'll now be able to run `payments-mcp` anywhere (e.g. in Claude Desktop MCP).
41+
3542
## Deploy
3643
```bash
3744
fly secrets set RPC_URL=$RPC_URL

packages/payments-mcp/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
"type": "module",
66
"main": "dist/index.js",
77
"private": true,
8+
"bin": {
9+
"payments-mcp": "./dist/index.js"
10+
},
811
"scripts": {
912
"build": "tsc",
1013
"dev": "tsx watch src/index.ts",

packages/payments-mcp/src/index.ts

Lines changed: 57 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,100 +1,98 @@
1+
#!/usr/bin/env node
2+
13
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
24
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
35
import { z } from 'zod';
46
import {
5-
createWalletClient,
6-
http,
7-
isAddress,
8-
type Account,
9-
type Hex,
7+
createWalletClient,
8+
http,
9+
isAddress,
10+
type Hex,
1011
} from 'viem';
1112
import { privateKeyToAccount } from 'viem/accounts';
1213
import {
13-
createPaymentHeader,
14-
selectPaymentRequirements,
15-
} from 'x402/dist/esm/client/index.mjs';
14+
createPaymentHeader,
15+
} from 'x402/client';
1616
import debug from 'debug';
1717

1818
import pkg from '../package.json' with { type: 'json' };
1919
import { base } from 'viem/chains';
2020

2121
const X402_VERSION = 1;
22+
const BASE_USDC_ADDRESS = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
2223

2324
// Load the environment variables
2425
const PRIVATE_KEY = process.env.PRIVATE_KEY as Hex;
2526
const MAX_PAYMENT_AMOUNT_USDC = process.env.MAX_PAYMENT_AMOUNT_USDC;
2627

2728
if (!PRIVATE_KEY) {
28-
throw new Error('PRIVATE_KEY is not set');
29+
throw new Error('PRIVATE_KEY is not set');
2930
}
3031

3132
if (!MAX_PAYMENT_AMOUNT_USDC) {
32-
throw new Error('MAX_PAYMENT_AMOUNT_USDC is not set');
33+
throw new Error('MAX_PAYMENT_AMOUNT_USDC is not set');
3334
}
3435

3536
const log = debug('payments-mcp');
3637

3738
const walletClient = createWalletClient({
38-
chain: base,
39-
transport: http(),
40-
account: privateKeyToAccount(PRIVATE_KEY),
39+
chain: base,
40+
transport: http(),
41+
account: privateKeyToAccount(PRIVATE_KEY),
4142
});
4243

4344
const server = new McpServer({
44-
name: pkg.name,
45-
version: pkg.version,
45+
name: pkg.name,
46+
version: pkg.version,
4647
});
4748

4849
server.tool(
49-
'create_payment',
50-
'Create a payment to use with paid MCP servers.',
51-
{
52-
tool: z.string().describe('The MCP tool to pay for'),
53-
amount: z
54-
.number()
55-
.max(Number(MAX_PAYMENT_AMOUNT_USDC))
56-
.describe('The payment amount in USDC'),
57-
asset: z
58-
.string()
59-
.refine(isAddress, { message: 'Invalid address' })
60-
.describe('The asset to pay with'),
61-
recipient: z
62-
.string()
63-
.refine(isAddress, { message: 'Invalid address' })
64-
.describe('The recipient of the payment'),
65-
},
66-
async ({ amount, asset, recipient, tool }) => {
67-
log(`Creating payment of ${amount} ${asset} to ${recipient}`);
50+
'create_payment',
51+
'Create a payment to use with paid MCP servers.',
52+
{
53+
tool: z.string().describe('The MCP tool to pay for').optional(),
54+
amount: z
55+
.number()
56+
.max(Number(MAX_PAYMENT_AMOUNT_USDC))
57+
.describe('The payment amount in USDC'),
58+
recipient: z
59+
.string()
60+
.refine(isAddress, { message: 'Invalid address' })
61+
.describe('The recipient of the payment'),
62+
},
63+
async ({ amount, recipient, tool }) => {
64+
log(`Creating payment of ${amount} to ${recipient}`);
6865

69-
const header = await createPaymentHeader(
70-
walletClient as any,
71-
X402_VERSION,
72-
{
73-
scheme: 'exact',
74-
description: 'Payment for MCP server',
75-
network: 'base',
76-
maxAmountRequired: amount.toString(),
77-
resource: tool,
78-
mimeType: 'application/json',
79-
payTo: recipient,
80-
maxTimeoutSeconds: 3600,
81-
asset,
82-
}
83-
);
66+
const header = await createPaymentHeader(
67+
walletClient as any,
68+
X402_VERSION,
69+
{
70+
scheme: 'exact',
71+
description: 'Payment for MCP server',
72+
network: 'base',
73+
maxAmountRequired: amount.toString(),
74+
resource: tool ?? 'unknown',
75+
mimeType: 'application/json',
76+
payTo: recipient,
77+
maxTimeoutSeconds: 3600,
78+
asset: BASE_USDC_ADDRESS,
79+
}
80+
);
8481

85-
log(`Payment header: ${header}`);
82+
log(`Payment header: ${header}`);
8683

87-
return {
88-
content: [
89-
{
90-
type: 'text',
91-
text: header,
92-
},
93-
],
94-
};
95-
}
84+
return {
85+
content: [
86+
{
87+
type: 'text',
88+
text: header,
89+
},
90+
],
91+
};
92+
}
9693
);
9794

9895
const transport = new StdioServerTransport();
9996

97+
log('Starting MCP server transport=stdio');
10098
await server.connect(transport);

packages/payments-mcp/tsconfig.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
"extends": "../../tsconfig.base.json",
33
"compilerOptions": {
44
"outDir": "./dist",
5-
"rootDir": "./src"
5+
"rootDir": "./src",
6+
"module": "nodenext",
7+
"moduleResolution": "nodenext"
68
},
79
"include": [
810
"src/**/*"

pnpm-lock.yaml

Lines changed: 0 additions & 75 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)