agentwallet-mcp 1.2.0 → 1.4.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 HiFriendbot
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,198 @@
1
+ # AgentWallet MCP Server
2
+
3
+ Permissionless EVM wallet infrastructure for AI agents. Create wallets, sign transactions, and broadcast on-chain — on any EVM chain.
4
+
5
+ **No KYC. No KYT. No transaction monitoring. No one can block your wallet.**
6
+
7
+ <p align="center">
8
+ <img src="assets/demo.svg" alt="AgentWallet demo — AI agent pays x402 invoice automatically" width="800">
9
+ </p>
10
+
11
+ ## Features
12
+
13
+ - **28 MCP tools** — create wallets, send transactions, approve tokens, wrap ETH, pay and accept x402 payments, and more
14
+ - **Any EVM chain** — Ethereum, Base, Polygon, BSC, Arbitrum, Optimism, Avalanche, Zora, PulseChain, and any other EVM-compatible chain
15
+ - **x402 payments** — pay for x402-enabled APIs automatically, or accept x402 payments on your own endpoints
16
+ - **Secure** — Private keys encrypted at rest, decrypted only during signing, zeroed from memory immediately after
17
+ - **Permissionless** — No identity verification, no compliance gatekeeping. Create a wallet and transact immediately.
18
+
19
+ ## Pricing
20
+
21
+ - **$0.0069 per transaction** (31% less expensive than Coinbase CDP)
22
+ - **6,000 free transactions/month**
23
+ - No monthly fee, no tiers — just pay as you go
24
+
25
+ ## Quick Start
26
+
27
+ Get your free API key at [hifriendbot.com/wallet](https://hifriendbot.com/wallet) (no credit card required).
28
+
29
+ ### Claude Desktop / OpenClaw
30
+
31
+ Add to your config:
32
+
33
+ ```json
34
+ {
35
+ "mcpServers": {
36
+ "agentwallet": {
37
+ "command": "npx",
38
+ "args": ["-y", "agentwallet-mcp"],
39
+ "env": {
40
+ "AGENTWALLET_USER": "your_username",
41
+ "AGENTWALLET_PASS": "your_api_key"
42
+ }
43
+ }
44
+ }
45
+ }
46
+ ```
47
+
48
+ ### Claude Code
49
+
50
+ ```bash
51
+ claude mcp add agentwallet \
52
+ -e AGENTWALLET_USER=your_username \
53
+ -e AGENTWALLET_PASS=your_api_key \
54
+ -- npx -y agentwallet-mcp
55
+ ```
56
+
57
+ ### VS Code
58
+
59
+ Add to your settings:
60
+
61
+ ```json
62
+ {
63
+ "mcp": {
64
+ "servers": {
65
+ "agentwallet": {
66
+ "command": "npx",
67
+ "args": ["-y", "agentwallet-mcp"],
68
+ "env": {
69
+ "AGENTWALLET_USER": "your_username",
70
+ "AGENTWALLET_PASS": "your_api_key"
71
+ }
72
+ }
73
+ }
74
+ }
75
+ }
76
+ ```
77
+
78
+ ## Tools
79
+
80
+ | Tool | Description |
81
+ |------|-------------|
82
+ | `create_wallet` | Create a new wallet on any EVM chain |
83
+ | `list_wallets` | List all your wallets |
84
+ | `get_wallet` | Get wallet details by ID |
85
+ | `get_balance` | Check native token balance on any chain |
86
+ | `get_token_balance` | Check ERC-20 token balance |
87
+ | `get_token_info` | Get ERC-20 token name, symbol, and decimals |
88
+ | `transfer` | Send native tokens (ETH, POL, BNB, etc.) |
89
+ | `transfer_token` | Send ERC-20 tokens (USDC, USDT, etc.) |
90
+ | `send_transaction` | Sign and broadcast a raw transaction |
91
+ | `sign_transaction` | Sign a transaction without broadcasting |
92
+ | `call_contract` | Read-only contract call (eth_call) |
93
+ | `approve_token` | Approve ERC-20 token spending for DeFi |
94
+ | `get_allowance` | Check ERC-20 token allowance |
95
+ | `wrap_eth` | Wrap native tokens to WETH/WAVAX/etc. |
96
+ | `unwrap_eth` | Unwrap WETH back to native tokens |
97
+ | `pay_x402` | Pay x402 invoices automatically (fetch, pay, retry) |
98
+ | `create_paywall` | Create an x402 paywall to charge for a resource |
99
+ | `list_paywalls` | List all your x402 paywalls |
100
+ | `get_paywall` | Get paywall details by ID |
101
+ | `update_paywall` | Update paywall pricing, resource, or status |
102
+ | `delete_paywall` | Delete a paywall |
103
+ | `get_paywall_payments` | View payment history for a paywall |
104
+ | `get_x402_revenue` | Aggregate revenue stats across all paywalls |
105
+ | `get_usage` | Check your monthly usage and billing |
106
+ | `get_chains` | List all supported chains |
107
+ | `pause_wallet` | Emergency pause a wallet |
108
+ | `unpause_wallet` | Resume a paused wallet |
109
+ | `delete_wallet` | Delete a wallet |
110
+
111
+ ## Supported Chains
112
+
113
+ | Chain | ID | Native Token | Stablecoin |
114
+ |-------|-----|-------------|------------|
115
+ | Ethereum | 1 | ETH | USDC |
116
+ | Base | 8453 | ETH | USDC |
117
+ | Polygon | 137 | POL | USDC |
118
+ | BSC | 56 | BNB | USDT |
119
+ | Arbitrum | 42161 | ETH | USDC |
120
+ | Optimism | 10 | ETH | USDC |
121
+ | Avalanche | 43114 | AVAX | USDC |
122
+ | Zora | 7777777 | ETH | USDC |
123
+ | PulseChain | 369 | PLS | USDC |
124
+
125
+ ## Use Case: GuessMarket
126
+
127
+ Pair with [guessmarket-mcp](https://www.npmjs.com/package/guessmarket-mcp) to let your AI agent trade prediction markets:
128
+
129
+ 1. Create a wallet on Base
130
+ 2. Approve USDC spending
131
+ 3. Buy YES/NO shares on prediction markets
132
+ 4. Provide liquidity and earn trading fees
133
+ 5. Claim winnings
134
+
135
+ All on-chain. All through MCP. No frontend needed.
136
+
137
+ ## x402 Payments
138
+
139
+ AgentWallet natively supports the [x402 open payment standard](https://x402.org). When your Ai agent encounters an API that returns HTTP 402 Payment Required, the `pay_x402` tool handles the entire flow automatically:
140
+
141
+ 1. Fetches the URL and detects the 402 response
142
+ 2. Parses the payment requirements (amount, token, chain)
143
+ 3. Executes the on-chain payment from your wallet
144
+ 4. Retries the request with proof of payment
145
+ 5. Returns the final response
146
+
147
+ **Always set `max_payment` to control spending:**
148
+
149
+ ```
150
+ pay_x402(
151
+ url="https://api.example.com/premium-data",
152
+ wallet_id=1,
153
+ max_payment="1.00"
154
+ )
155
+ ```
156
+
157
+ Supports ERC-20 tokens (USDC, USDT) and native tokens on any chain. Compatible with x402 V1 and V2 (CAIP-2 chain identifiers).
158
+
159
+ ## x402 Acceptance
160
+
161
+ AgentWallet also lets you **accept** x402 payments. Create a paywall, point it at any resource, and get a public URL that charges agents automatically:
162
+
163
+ ```
164
+ create_paywall(
165
+ wallet_id=1,
166
+ name="Premium API",
167
+ amount="0.01",
168
+ token_name="USDC",
169
+ token_address="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
170
+ chain_id=8453,
171
+ resource_url="https://your-api.com/data"
172
+ )
173
+ ```
174
+
175
+ When an agent hits the paywall URL:
176
+ 1. Gets back HTTP 402 with payment requirements
177
+ 2. Pays on-chain using `pay_x402` (or any x402-compatible client)
178
+ 3. Retries with proof of payment
179
+ 4. Receives the protected content
180
+
181
+ On-chain verification ensures every payment is real. Replay protection prevents double-spending. Revenue tracking shows you who paid, how much, and when.
182
+
183
+ ## Security
184
+
185
+ - Private keys are generated server-side and encrypted at rest
186
+ - Keys are decrypted only during transaction signing and zeroed from memory immediately after
187
+ - EIP-1559 transactions only with gas safety caps
188
+ - Bug bounty program: $50–$500 for responsible disclosure ([details](https://hifriendbot.com/wallet))
189
+
190
+ ## Links
191
+
192
+ - **Website:** [hifriendbot.com/wallet](https://hifriendbot.com/wallet)
193
+ - **npm:** [agentwallet-mcp](https://www.npmjs.com/package/agentwallet-mcp)
194
+ - **Security:** [security@hifriendbot.com](mailto:security@hifriendbot.com)
195
+
196
+ ## License
197
+
198
+ MIT
package/build/index.js CHANGED
@@ -80,9 +80,38 @@ function encodeUint256(value) {
80
80
  return hex.padStart(64, '0');
81
81
  }
82
82
  // ─── Server ──────────────────────────────────────────────────────
83
+ // ─── x402 Network Mapping ──────────────────────────────────────
84
+ const X402_NETWORKS = {
85
+ 'ethereum': 1,
86
+ 'base': 8453,
87
+ 'base-sepolia': 84532,
88
+ 'polygon': 137,
89
+ 'arbitrum': 42161,
90
+ 'optimism': 10,
91
+ 'bsc': 56,
92
+ 'avalanche': 43114,
93
+ 'zora': 7777777,
94
+ 'pulsechain': 369,
95
+ };
96
+ /**
97
+ * Resolve an x402 network identifier to a chain ID.
98
+ * Supports plain names ("base"), CAIP-2 format ("eip155:8453"), and raw chain IDs ("8453").
99
+ */
100
+ function resolveChainId(network) {
101
+ if (X402_NETWORKS[network])
102
+ return X402_NETWORKS[network];
103
+ const caipMatch = network.match(/^eip155:(\d+)$/);
104
+ if (caipMatch)
105
+ return parseInt(caipMatch[1], 10);
106
+ const num = parseInt(network, 10);
107
+ if (!isNaN(num) && num > 0)
108
+ return num;
109
+ return null;
110
+ }
111
+ // ─── Server ──────────────────────────────────────────────────────
83
112
  const server = new McpServer({
84
113
  name: 'agentwallet',
85
- version: '1.2.0',
114
+ version: '1.4.0',
86
115
  });
87
116
  // ─── Tool: create_wallet ─────────────────────────────────────────
88
117
  server.tool('create_wallet', 'Create a new EVM wallet. Returns the wallet ID and address. ' +
@@ -425,6 +454,181 @@ server.tool('get_token_info', 'Get the name, symbol, and decimals of any ERC-20
425
454
  decimals,
426
455
  });
427
456
  });
457
+ // ─── Tool: pay_x402 ─────────────────────────────────────────────
458
+ server.tool('pay_x402', 'Handle an x402 payment flow. Fetches a URL, and if the server returns HTTP 402 Payment Required, ' +
459
+ 'parses the payment requirements, executes the on-chain payment, and retries the request with ' +
460
+ 'proof of payment. Returns the final response. Supports the x402 open payment standard ' +
461
+ '(https://x402.org). Set max_payment to prevent overspending.', {
462
+ url: z.string().url().describe('The URL to access (will handle 402 payment if required)'),
463
+ wallet_id: z.number().int().describe('Wallet ID to pay from'),
464
+ method: z.string().default('GET').describe('HTTP method (GET, POST, PUT, DELETE)'),
465
+ headers: z.string().optional().describe('Optional JSON string of additional request headers'),
466
+ body: z.string().optional().describe('Optional request body for POST/PUT requests'),
467
+ max_payment: z.string().optional().describe('Maximum payment in human-readable format (e.g. "1.00" for 1 USDC). ' +
468
+ 'Rejects payments above this amount. Strongly recommended to prevent overspending.'),
469
+ prefer_chain: z.number().int().optional().describe('Preferred chain ID if the server accepts payment on multiple chains ' +
470
+ '(e.g. 8453 for Base, 1 for Ethereum)'),
471
+ }, async ({ url, wallet_id, method, headers: headersJson, body: reqBody, max_payment, prefer_chain }) => {
472
+ // Build request headers
473
+ const reqHeaders = { Accept: 'application/json' };
474
+ if (headersJson) {
475
+ try {
476
+ Object.assign(reqHeaders, JSON.parse(headersJson));
477
+ }
478
+ catch {
479
+ throw new Error('Invalid headers JSON. Must be a JSON object (e.g. {"Authorization": "Bearer ..."}).');
480
+ }
481
+ }
482
+ // Validate URL — block private IPs and cloud metadata endpoints
483
+ const urlObj = new URL(url);
484
+ const hostname = urlObj.hostname.toLowerCase();
485
+ const blockedHosts = ['localhost', '127.0.0.1', '0.0.0.0', '[::1]', '169.254.169.254', 'metadata.google.internal'];
486
+ const blockedPrefixes = ['10.', '172.16.', '172.17.', '172.18.', '172.19.', '172.20.', '172.21.',
487
+ '172.22.', '172.23.', '172.24.', '172.25.', '172.26.', '172.27.', '172.28.', '172.29.',
488
+ '172.30.', '172.31.', '192.168.'];
489
+ if (blockedHosts.includes(hostname) || blockedPrefixes.some(p => hostname.startsWith(p))) {
490
+ throw new Error('URL points to a private/internal address. Only public URLs are allowed.');
491
+ }
492
+ if (!['http:', 'https:'].includes(urlObj.protocol)) {
493
+ throw new Error('Only http: and https: URLs are supported.');
494
+ }
495
+ // Step 1: Make initial request
496
+ const reqOptions = {
497
+ method,
498
+ headers: reqHeaders,
499
+ signal: AbortSignal.timeout(30_000), // 30s timeout
500
+ };
501
+ if (reqBody && method !== 'GET') {
502
+ reqOptions.body = reqBody;
503
+ if (!reqHeaders['Content-Type'])
504
+ reqHeaders['Content-Type'] = 'application/json';
505
+ }
506
+ const initialRes = await fetch(url, reqOptions);
507
+ // If not 402, return the response as-is (no payment needed)
508
+ if (initialRes.status !== 402) {
509
+ const text = await initialRes.text();
510
+ let parsed;
511
+ try {
512
+ parsed = JSON.parse(text);
513
+ }
514
+ catch {
515
+ parsed = text;
516
+ }
517
+ return jsonResponse({
518
+ status: initialRes.status,
519
+ payment_required: false,
520
+ response: parsed,
521
+ });
522
+ }
523
+ // Step 2: Parse x402 payment requirements from 402 response
524
+ let paymentInfo;
525
+ try {
526
+ paymentInfo = await initialRes.json();
527
+ }
528
+ catch {
529
+ throw new Error('402 response body is not valid JSON. This server may not support x402.');
530
+ }
531
+ if (!paymentInfo.accepts || paymentInfo.accepts.length === 0) {
532
+ throw new Error('402 response has no payment options in "accepts" array.');
533
+ }
534
+ const options = paymentInfo.accepts;
535
+ let option;
536
+ if (prefer_chain) {
537
+ option = options.find(a => resolveChainId(a.network) === prefer_chain) || options[0];
538
+ }
539
+ else {
540
+ option = options[0];
541
+ }
542
+ // Resolve the chain
543
+ const chainId = resolveChainId(option.network);
544
+ if (!chainId) {
545
+ throw new Error(`Unsupported x402 network: "${option.network}". ` +
546
+ `Supported: ${Object.keys(X402_NETWORKS).join(', ')}, or any CAIP-2 / numeric chain ID.`);
547
+ }
548
+ // Calculate human-readable amount
549
+ const amount = formatUnits(option.maxAmountRequired, option.requiredDecimals);
550
+ // Step 4: Check spending limit
551
+ if (max_payment) {
552
+ const maxRaw = parseUnits(max_payment, option.requiredDecimals);
553
+ if (BigInt(option.maxAmountRequired) > BigInt(maxRaw)) {
554
+ return jsonResponse({
555
+ status: 402,
556
+ payment_required: true,
557
+ payment_made: false,
558
+ error: `Payment of ${amount} ${option.extra?.name || 'tokens'} exceeds your max_payment limit of ${max_payment}`,
559
+ required_amount: amount,
560
+ max_allowed: max_payment,
561
+ token: option.extra?.name || 'native',
562
+ network: option.network,
563
+ chain_id: chainId,
564
+ pay_to: option.payTo,
565
+ description: option.description,
566
+ });
567
+ }
568
+ }
569
+ // Step 5: Execute payment
570
+ let txResult;
571
+ if (option.extra?.token) {
572
+ // ERC-20 token payment (e.g. USDC)
573
+ const calldata = '0xa9059cbb' + padAddress(option.payTo) + encodeUint256(option.maxAmountRequired);
574
+ txResult = (await api(`/wallets/${wallet_id}/send`, 'POST', {
575
+ to: option.extra.token,
576
+ value: '0',
577
+ data: calldata,
578
+ chain_id: chainId,
579
+ }));
580
+ }
581
+ else {
582
+ // Native token payment (ETH, etc.)
583
+ txResult = (await api(`/wallets/${wallet_id}/send`, 'POST', {
584
+ to: option.payTo,
585
+ value: option.maxAmountRequired,
586
+ data: '',
587
+ chain_id: chainId,
588
+ }));
589
+ }
590
+ const txHash = txResult.tx_hash;
591
+ // Step 6: Build x402 payment proof and retry
592
+ const paymentProof = {
593
+ x402Version: paymentInfo.x402Version || 1,
594
+ scheme: option.scheme,
595
+ network: option.network,
596
+ payload: { txHash },
597
+ };
598
+ const paymentHeader = Buffer.from(JSON.stringify(paymentProof)).toString('base64');
599
+ const retryHeaders = { ...reqHeaders, 'X-PAYMENT': paymentHeader };
600
+ const retryOptions = {
601
+ method,
602
+ headers: retryHeaders,
603
+ signal: AbortSignal.timeout(30_000),
604
+ };
605
+ if (reqBody && method !== 'GET') {
606
+ retryOptions.body = reqBody;
607
+ }
608
+ const retryRes = await fetch(url, retryOptions);
609
+ const retryText = await retryRes.text();
610
+ let retryParsed;
611
+ try {
612
+ retryParsed = JSON.parse(retryText);
613
+ }
614
+ catch {
615
+ retryParsed = retryText;
616
+ }
617
+ return jsonResponse({
618
+ status: retryRes.status,
619
+ payment_required: true,
620
+ payment_made: true,
621
+ amount,
622
+ token: option.extra?.name || 'native',
623
+ token_address: option.extra?.token || null,
624
+ network: option.network,
625
+ chain_id: chainId,
626
+ pay_to: option.payTo,
627
+ tx_hash: txHash,
628
+ description: option.description,
629
+ response: retryParsed,
630
+ });
631
+ });
428
632
  // ─── Tool: get_usage ─────────────────────────────────────────────
429
633
  server.tool('get_usage', 'Get the current month\'s usage statistics. ' +
430
634
  'Returns operations count, tier info, remaining quota, and fees.', {}, async () => {
@@ -459,6 +663,114 @@ server.tool('delete_wallet', 'Delete (soft-delete) a wallet. The wallet will no
459
663
  const data = await api(`/wallets/${wallet_id}`, 'DELETE');
460
664
  return jsonResponse(data);
461
665
  });
666
+ // ─── Tool: create_paywall ────────────────────────────────────────
667
+ server.tool('create_paywall', 'Create an x402 paywall that charges agents/clients for accessing a resource. ' +
668
+ 'Returns a public access URL that returns HTTP 402 until paid. ' +
669
+ 'Agents pay on-chain, then retry with proof to get the content.', {
670
+ wallet_id: z.number().int().describe('Wallet ID to receive payments'),
671
+ name: z.string().describe('Human-readable paywall name (e.g. "Premium API Access")'),
672
+ description: z.string().default('').describe('Description shown in the 402 response'),
673
+ amount: z.string().describe('Price in human-readable format (e.g. "0.01" for 0.01 USDC)'),
674
+ token_type: z.enum(['erc20', 'native']).default('erc20').describe('"erc20" for stablecoin payments, "native" for ETH/POL/etc.'),
675
+ token_address: z.string().default('').describe('ERC-20 token contract address (required if token_type is "erc20"). Use get_chains to find stablecoin addresses.'),
676
+ token_decimals: z.number().int().default(6).describe('Token decimals (6 for USDC, 18 for ETH/most tokens)'),
677
+ token_name: z.string().default('USDC').describe('Token display name (e.g. "USDC", "ETH")'),
678
+ chain_id: z.number().int().default(8453).describe('Chain ID for payments (8453=Base, 1=Ethereum, etc.)'),
679
+ resource_url: z.string().url().describe('URL of the protected resource to serve after payment verification'),
680
+ resource_mime: z.string().default('application/json').describe('MIME type of the resource (e.g. "application/json", "text/plain")'),
681
+ }, async ({ wallet_id, name, description, amount, token_type, token_address, token_decimals, token_name, chain_id, resource_url, resource_mime }) => {
682
+ // Convert human-readable amount to raw token units
683
+ const rawAmount = parseUnits(amount, token_decimals);
684
+ const data = await api('/x402/paywalls', 'POST', {
685
+ wallet_id,
686
+ name,
687
+ description,
688
+ amount: rawAmount,
689
+ token_type,
690
+ token_address,
691
+ token_decimals,
692
+ token_name,
693
+ chain_id,
694
+ resource_url,
695
+ resource_mime,
696
+ });
697
+ return jsonResponse({
698
+ ...data,
699
+ price: `${amount} ${token_name}`,
700
+ chain_id,
701
+ });
702
+ });
703
+ // ─── Tool: list_paywalls ────────────────────────────────────────
704
+ server.tool('list_paywalls', 'List all your x402 paywalls. Returns paywall IDs, names, pricing, ' +
705
+ 'access URLs, payment counts, and revenue totals.', {
706
+ page: z.number().int().default(1).describe('Page number'),
707
+ per_page: z.number().int().default(50).describe('Results per page (max 100)'),
708
+ }, async ({ page, per_page }) => {
709
+ const data = await api(`/x402/paywalls?page=${page}&per_page=${per_page}`);
710
+ return jsonResponse(data);
711
+ });
712
+ // ─── Tool: get_paywall ──────────────────────────────────────────
713
+ server.tool('get_paywall', 'Get details for a specific x402 paywall by ID. ' +
714
+ 'Returns pricing, access URL, payment stats, and configuration.', {
715
+ paywall_id: z.number().int().describe('Paywall ID'),
716
+ }, async ({ paywall_id }) => {
717
+ const data = await api(`/x402/paywalls/${paywall_id}`);
718
+ return jsonResponse(data);
719
+ });
720
+ // ─── Tool: update_paywall ───────────────────────────────────────
721
+ server.tool('update_paywall', 'Update an x402 paywall configuration. ' +
722
+ 'Can change price, resource URL, active status, or any other field.', {
723
+ paywall_id: z.number().int().describe('Paywall ID to update'),
724
+ name: z.string().optional().describe('New paywall name'),
725
+ description: z.string().optional().describe('New description'),
726
+ amount: z.string().optional().describe('New price in human-readable format (e.g. "0.05")'),
727
+ token_decimals: z.number().int().optional().describe('Token decimals (needed if changing amount)'),
728
+ resource_url: z.string().url().optional().describe('New resource URL'),
729
+ resource_mime: z.string().optional().describe('New MIME type'),
730
+ is_active: z.boolean().optional().describe('Enable (true) or disable (false) the paywall'),
731
+ }, async ({ paywall_id, name, description, amount, token_decimals, resource_url, resource_mime, is_active }) => {
732
+ const body = {};
733
+ if (name !== undefined)
734
+ body.name = name;
735
+ if (description !== undefined)
736
+ body.description = description;
737
+ if (resource_url !== undefined)
738
+ body.resource_url = resource_url;
739
+ if (resource_mime !== undefined)
740
+ body.resource_mime = resource_mime;
741
+ if (is_active !== undefined)
742
+ body.is_active = is_active;
743
+ // Convert human-readable amount to raw if provided
744
+ if (amount !== undefined) {
745
+ const decimals = token_decimals ?? 6; // Default to USDC decimals
746
+ body.amount = parseUnits(amount, decimals);
747
+ }
748
+ const data = await api(`/x402/paywalls/${paywall_id}`, 'PUT', body);
749
+ return jsonResponse(data);
750
+ });
751
+ // ─── Tool: delete_paywall ───────────────────────────────────────
752
+ server.tool('delete_paywall', 'Delete an x402 paywall. The access URL will return 404 after deletion.', {
753
+ paywall_id: z.number().int().describe('Paywall ID to delete'),
754
+ }, async ({ paywall_id }) => {
755
+ const data = await api(`/x402/paywalls/${paywall_id}`, 'DELETE');
756
+ return jsonResponse(data);
757
+ });
758
+ // ─── Tool: get_paywall_payments ─────────────────────────────────
759
+ server.tool('get_paywall_payments', 'Get payment history for a specific x402 paywall. ' +
760
+ 'Returns verified payments with TX hashes, payer addresses, amounts, and timestamps.', {
761
+ paywall_id: z.number().int().describe('Paywall ID'),
762
+ page: z.number().int().default(1).describe('Page number'),
763
+ per_page: z.number().int().default(20).describe('Results per page (max 100)'),
764
+ }, async ({ paywall_id, page, per_page }) => {
765
+ const data = await api(`/x402/paywalls/${paywall_id}/payments?page=${page}&per_page=${per_page}`);
766
+ return jsonResponse(data);
767
+ });
768
+ // ─── Tool: get_x402_revenue ─────────────────────────────────────
769
+ server.tool('get_x402_revenue', 'Get aggregate x402 revenue statistics across all your paywalls. ' +
770
+ 'Returns total payments and revenue broken down by chain and token.', {}, async () => {
771
+ const data = await api('/x402/revenue');
772
+ return jsonResponse(data);
773
+ });
462
774
  // ─── Start ──────────────────────────────────────────────────────
463
775
  async function main() {
464
776
  const transport = new StdioServerTransport();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "agentwallet-mcp",
3
- "version": "1.2.0",
4
- "description": "MCP server for AgentWallet — lets AI agents create wallets, sign transactions, and broadcast on any EVM chain",
3
+ "version": "1.4.0",
4
+ "description": "MCP server for AgentWallet — lets AI agents create wallets, sign transactions, broadcast on any EVM chain, pay x402 invoices, and accept x402 payments",
5
5
  "type": "module",
6
6
  "main": "build/index.js",
7
7
  "bin": {
@@ -36,6 +36,9 @@
36
36
  "erc20",
37
37
  "token-transfer",
38
38
  "transaction-signing",
39
+ "x402",
40
+ "http-402",
41
+ "agent-payments",
39
42
  "agentwallet"
40
43
  ],
41
44
  "author": "HiFriendbot (https://hifriendbot.com)",