@x402/mcp 2.3.0-alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +351 -0
- package/dist/cjs/index.d.ts +971 -0
- package/dist/cjs/index.js +826 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/esm/index.d.mts +971 -0
- package/dist/esm/index.mjs +782 -0
- package/dist/esm/index.mjs.map +1 -0
- package/package.json +64 -0
package/README.md
ADDED
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
# @x402/mcp
|
|
2
|
+
|
|
3
|
+
MCP (Model Context Protocol) integration for the x402 payment protocol. This package enables paid tool calls in MCP servers and automatic payment handling in MCP clients.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @x402/mcp @x402/core @modelcontextprotocol/sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start (Recommended)
|
|
12
|
+
|
|
13
|
+
### Server - Using Payment Wrapper
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
17
|
+
import { createPaymentWrapper, x402ResourceServer } from "@x402/mcp";
|
|
18
|
+
import { HTTPFacilitatorClient } from "@x402/core/server";
|
|
19
|
+
import { ExactEvmScheme } from "@x402/evm/exact/server";
|
|
20
|
+
import { z } from "zod";
|
|
21
|
+
|
|
22
|
+
// Create standard MCP server
|
|
23
|
+
const mcpServer = new McpServer({ name: "premium-api", version: "1.0.0" });
|
|
24
|
+
|
|
25
|
+
// Set up x402 for payment handling
|
|
26
|
+
const facilitatorClient = new HTTPFacilitatorClient({ url: "https://x402.org/facilitator" });
|
|
27
|
+
const resourceServer = new x402ResourceServer(facilitatorClient);
|
|
28
|
+
resourceServer.register("eip155:84532", new ExactEvmScheme());
|
|
29
|
+
await resourceServer.initialize();
|
|
30
|
+
|
|
31
|
+
// Build payment requirements
|
|
32
|
+
const accepts = await resourceServer.buildPaymentRequirements({
|
|
33
|
+
scheme: "exact",
|
|
34
|
+
network: "eip155:84532",
|
|
35
|
+
payTo: "0x...", // Your wallet address
|
|
36
|
+
price: "$0.10",
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Create payment wrapper with accepts array
|
|
40
|
+
const paid = createPaymentWrapper(resourceServer, {
|
|
41
|
+
accepts,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Register paid tools - wrap handler
|
|
45
|
+
mcpServer.tool(
|
|
46
|
+
"financial_analysis",
|
|
47
|
+
"Advanced AI-powered financial analysis. Costs $0.10.",
|
|
48
|
+
{ ticker: z.string() },
|
|
49
|
+
paid(async (args) => {
|
|
50
|
+
const analysis = await performAnalysis(args.ticker);
|
|
51
|
+
return { content: [{ type: "text", text: analysis }] };
|
|
52
|
+
})
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
// Register free tools - no wrapper needed
|
|
56
|
+
mcpServer.tool("ping", "Health check", {}, async () => ({
|
|
57
|
+
content: [{ type: "text", text: "pong" }],
|
|
58
|
+
}));
|
|
59
|
+
|
|
60
|
+
// Connect to transport
|
|
61
|
+
await mcpServer.connect(transport);
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Client - Using Factory Function
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
import { createX402MCPClient } from "@x402/mcp";
|
|
68
|
+
import { ExactEvmScheme } from "@x402/evm/exact/client";
|
|
69
|
+
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
70
|
+
|
|
71
|
+
// Create client with factory (simplest approach)
|
|
72
|
+
const client = createX402MCPClient({
|
|
73
|
+
name: "my-agent",
|
|
74
|
+
version: "1.0.0",
|
|
75
|
+
schemes: [{ network: "eip155:84532", client: new ExactEvmScheme(walletAccount) }],
|
|
76
|
+
autoPayment: true,
|
|
77
|
+
onPaymentRequested: async ({ paymentRequired }) => {
|
|
78
|
+
console.log(`Tool requires payment: ${paymentRequired.accepts[0].amount}`);
|
|
79
|
+
return true; // Return false to deny payment
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// Connect and use
|
|
84
|
+
const transport = new SSEClientTransport(new URL("http://localhost:4022/sse"));
|
|
85
|
+
await client.connect(transport);
|
|
86
|
+
|
|
87
|
+
const result = await client.callTool("financial_analysis", { ticker: "AAPL" });
|
|
88
|
+
console.log(result.content);
|
|
89
|
+
|
|
90
|
+
if (result.paymentMade) {
|
|
91
|
+
console.log("Payment settled:", result.paymentResponse?.transaction);
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Advanced Features
|
|
96
|
+
|
|
97
|
+
### Production Hooks
|
|
98
|
+
|
|
99
|
+
Add hooks for logging, rate limiting, receipts, and more:
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
// Build payment requirements
|
|
103
|
+
const accepts = await resourceServer.buildPaymentRequirements({
|
|
104
|
+
scheme: "exact",
|
|
105
|
+
network: "eip155:84532",
|
|
106
|
+
payTo: "0x...",
|
|
107
|
+
price: "$0.10",
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const paid = createPaymentWrapper(resourceServer, {
|
|
111
|
+
accepts,
|
|
112
|
+
hooks: {
|
|
113
|
+
// Called after payment verification, before tool execution
|
|
114
|
+
// Return false to abort execution
|
|
115
|
+
onBeforeExecution: async ({ toolName, paymentPayload, paymentRequirements }) => {
|
|
116
|
+
console.log(`Executing ${toolName} for ${paymentPayload.payer}`);
|
|
117
|
+
|
|
118
|
+
// Rate limiting example
|
|
119
|
+
if (await isRateLimited(paymentPayload.payer)) {
|
|
120
|
+
console.log("Rate limit exceeded");
|
|
121
|
+
return false; // Abort execution, don't charge
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return true; // Continue
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
// Called after tool execution, before settlement
|
|
128
|
+
onAfterExecution: async ({ toolName, result, paymentPayload }) => {
|
|
129
|
+
// Log metrics
|
|
130
|
+
await metrics.record(toolName, result.isError);
|
|
131
|
+
},
|
|
132
|
+
|
|
133
|
+
// Called after successful settlement
|
|
134
|
+
onAfterSettlement: async ({ toolName, settlement, paymentPayload }) => {
|
|
135
|
+
// Send receipt to user
|
|
136
|
+
await sendReceipt(paymentPayload.payer, {
|
|
137
|
+
tool: toolName,
|
|
138
|
+
transaction: settlement.transaction,
|
|
139
|
+
network: settlement.network,
|
|
140
|
+
});
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// All tools using this wrapper inherit the hooks
|
|
146
|
+
mcpServer.tool("search", "Premium search", { query: z.string() },
|
|
147
|
+
paid(async (args) => ({ content: [...] }))
|
|
148
|
+
);
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Multiple Wrappers with Different Prices
|
|
152
|
+
|
|
153
|
+
Create separate wrappers for different payment tiers:
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
// Build requirements for different price points
|
|
157
|
+
const basicAccepts = await resourceServer.buildPaymentRequirements({
|
|
158
|
+
scheme: "exact",
|
|
159
|
+
network: "eip155:84532",
|
|
160
|
+
payTo: "0x...",
|
|
161
|
+
price: "$0.05",
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
const premiumAccepts = await resourceServer.buildPaymentRequirements({
|
|
165
|
+
scheme: "exact",
|
|
166
|
+
network: "eip155:84532",
|
|
167
|
+
payTo: "0x...",
|
|
168
|
+
price: "$0.50",
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// Create wrappers with different prices
|
|
172
|
+
const paidBasic = createPaymentWrapper(resourceServer, { accepts: basicAccepts });
|
|
173
|
+
const paidPremium = createPaymentWrapper(resourceServer, { accepts: premiumAccepts });
|
|
174
|
+
|
|
175
|
+
// Register tools with appropriate pricing
|
|
176
|
+
mcpServer.tool("basic_search", "...", {}, paidBasic(async (args) => ({ content: [...] })));
|
|
177
|
+
mcpServer.tool("premium_search", "...", {}, paidPremium(async (args) => ({ content: [...] })));
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Multiple Payment Options
|
|
181
|
+
|
|
182
|
+
Support multiple payment methods by including multiple requirements:
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
// Build requirements for different payment schemes
|
|
186
|
+
const exactPayment = await resourceServer.buildPaymentRequirements({
|
|
187
|
+
scheme: "exact",
|
|
188
|
+
network: "eip155:84532",
|
|
189
|
+
payTo: "0x...",
|
|
190
|
+
price: "$0.10",
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
const subscriptionPayment = await resourceServer.buildPaymentRequirements({
|
|
194
|
+
scheme: "subscription",
|
|
195
|
+
network: "eip155:1",
|
|
196
|
+
payTo: "0x...",
|
|
197
|
+
price: "$50", // Monthly subscription
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// Client can choose either payment method
|
|
201
|
+
const paid = createPaymentWrapper(resourceServer, {
|
|
202
|
+
accepts: [exactPayment[0], subscriptionPayment[0]],
|
|
203
|
+
});
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Client - Wrapper Functions
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
210
|
+
import { wrapMCPClientWithPayment, wrapMCPClientWithPaymentFromConfig } from "@x402/mcp";
|
|
211
|
+
import { x402Client } from "@x402/core/client";
|
|
212
|
+
import { ExactEvmScheme } from "@x402/evm/exact/client";
|
|
213
|
+
|
|
214
|
+
// Option 1: Wrap existing client with existing payment client
|
|
215
|
+
const mcpClient = new Client({ name: "my-agent", version: "1.0.0" });
|
|
216
|
+
const paymentClient = new x402Client()
|
|
217
|
+
.register("eip155:84532", new ExactEvmScheme(walletAccount));
|
|
218
|
+
|
|
219
|
+
const x402Mcp = wrapMCPClientWithPayment(mcpClient, paymentClient, {
|
|
220
|
+
autoPayment: true,
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// Option 2: Wrap existing client with config
|
|
224
|
+
const x402Mcp2 = wrapMCPClientWithPaymentFromConfig(mcpClient, {
|
|
225
|
+
schemes: [{ network: "eip155:84532", client: new ExactEvmScheme(walletAccount) }],
|
|
226
|
+
});
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Payment Flow
|
|
230
|
+
|
|
231
|
+
1. **Client calls tool** → No payment attached
|
|
232
|
+
2. **Server returns 402** → PaymentRequired in structured result (see SDK Limitation below)
|
|
233
|
+
3. **Client creates payment** → Using x402Client
|
|
234
|
+
4. **Client retries with payment** → PaymentPayload in `_meta["x402/payment"]`
|
|
235
|
+
5. **Server verifies & executes** → Tool runs if payment valid
|
|
236
|
+
6. **Server settles payment** → Transaction submitted
|
|
237
|
+
7. **Server returns result** → SettleResponse in `_meta["x402/payment-response"]`
|
|
238
|
+
|
|
239
|
+
## MCP SDK Limitation
|
|
240
|
+
|
|
241
|
+
The x402 MCP transport spec defines payment errors using JSON-RPC's native error format:
|
|
242
|
+
```json
|
|
243
|
+
{ "error": { "code": 402, "data": { /* PaymentRequired */ } } }
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
However, the MCP SDK converts `McpError` exceptions to tool results with `isError: true`, losing the `error.data` field. To work around this, we embed the error structure in the result content:
|
|
247
|
+
|
|
248
|
+
```json
|
|
249
|
+
{
|
|
250
|
+
"content": [{ "type": "text", "text": "{\"x402/error\": {\"code\": 402, \"data\": {...}}}" }],
|
|
251
|
+
"isError": true
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
The client parses this structure to extract PaymentRequired data. This is a pragmatic workaround that maintains compatibility while we track upstream SDK improvements.
|
|
256
|
+
|
|
257
|
+
## Configuration Options
|
|
258
|
+
|
|
259
|
+
### x402MCPClientOptions
|
|
260
|
+
|
|
261
|
+
| Option | Type | Default | Description |
|
|
262
|
+
|--------|------|---------|-------------|
|
|
263
|
+
| `autoPayment` | `boolean` | `true` | Automatically retry with payment on 402 |
|
|
264
|
+
| `onPaymentRequested` | `function` | `() => true` | Hook for human-in-the-loop approval when payment is requested |
|
|
265
|
+
|
|
266
|
+
### X402MCPServerConfig (Factory)
|
|
267
|
+
|
|
268
|
+
| Option | Type | Default | Description |
|
|
269
|
+
|--------|------|---------|-------------|
|
|
270
|
+
| `name` | `string` | Required | MCP server name |
|
|
271
|
+
| `version` | `string` | Required | MCP server version |
|
|
272
|
+
| `facilitator` | `string \| FacilitatorClient` | Default facilitator | Facilitator for payment processing |
|
|
273
|
+
| `schemes` | `SchemeRegistration[]` | `[]` | Payment scheme registrations |
|
|
274
|
+
| `syncFacilitatorOnStart` | `boolean` | `true` | Initialize facilitator immediately |
|
|
275
|
+
|
|
276
|
+
### MCPToolPaymentConfig
|
|
277
|
+
|
|
278
|
+
| Option | Type | Required | Description |
|
|
279
|
+
|--------|------|----------|-------------|
|
|
280
|
+
| `scheme` | `string` | Yes | Payment scheme (e.g., "exact") |
|
|
281
|
+
| `network` | `Network` | Yes | CAIP-2 network ID (e.g., "eip155:84532") |
|
|
282
|
+
| `price` | `Price` | Yes | Price (e.g., "$0.10" or "1000000") |
|
|
283
|
+
| `payTo` | `string` | Yes | Recipient wallet address |
|
|
284
|
+
| `maxTimeoutSeconds` | `number` | No | Payment timeout (default: 60) |
|
|
285
|
+
| `extra` | `object` | No | Scheme-specific parameters (e.g., EIP-712 domain) |
|
|
286
|
+
| `resource` | `object` | No | Resource metadata |
|
|
287
|
+
|
|
288
|
+
### PaymentWrapperConfig (for createPaymentWrapper)
|
|
289
|
+
|
|
290
|
+
| Option | Type | Required | Description |
|
|
291
|
+
|--------|------|----------|-------------|
|
|
292
|
+
| `scheme` | `string` | Yes | Payment scheme (e.g., "exact") |
|
|
293
|
+
| `network` | `Network` | Yes | CAIP-2 network ID (e.g., "eip155:84532") |
|
|
294
|
+
| `payTo` | `string` | Yes | Recipient wallet address |
|
|
295
|
+
| `price` | `Price` | No | Price - omit to specify per-tool |
|
|
296
|
+
| `maxTimeoutSeconds` | `number` | No | Payment timeout (default: 60) |
|
|
297
|
+
| `extra` | `object` | No | Scheme-specific parameters |
|
|
298
|
+
| `resource` | `object` | No | Resource metadata |
|
|
299
|
+
|
|
300
|
+
## Hooks
|
|
301
|
+
|
|
302
|
+
### Client Hooks
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
const client = createX402MCPClient({...});
|
|
306
|
+
|
|
307
|
+
// Called when a 402 is received (before payment)
|
|
308
|
+
// Return { payment } to use custom payment, { abort: true } to stop
|
|
309
|
+
client.onPaymentRequired(async ({ toolName, paymentRequired }) => {
|
|
310
|
+
const cached = await cache.get(toolName);
|
|
311
|
+
if (cached) return { payment: cached };
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
// Called before payment is created
|
|
315
|
+
client.onBeforePayment(async ({ paymentRequired }) => {
|
|
316
|
+
await logPaymentAttempt(paymentRequired);
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
// Called after payment is submitted
|
|
320
|
+
client.onAfterPayment(async ({ paymentPayload, settleResponse }) => {
|
|
321
|
+
await saveReceipt(settleResponse.transaction);
|
|
322
|
+
});
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### Server Hooks
|
|
326
|
+
|
|
327
|
+
```typescript
|
|
328
|
+
const server = createX402MCPServer({...});
|
|
329
|
+
|
|
330
|
+
// Called after verification, before tool execution
|
|
331
|
+
// Return false to abort and return 402
|
|
332
|
+
server.onBeforeExecution(async ({ toolName, paymentPayload }) => {
|
|
333
|
+
if (isBlocked(paymentPayload.signer)) {
|
|
334
|
+
return false; // Aborts execution
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
// Called after tool execution, before settlement
|
|
339
|
+
server.onAfterExecution(async ({ toolName, result }) => {
|
|
340
|
+
metrics.recordExecution(toolName, result.isError);
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
// Called after successful settlement
|
|
344
|
+
server.onAfterSettlement(async ({ toolName, settlement }) => {
|
|
345
|
+
await logTransaction(toolName, settlement.transaction);
|
|
346
|
+
});
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
## License
|
|
350
|
+
|
|
351
|
+
Apache-2.0
|