agentwallet-mcp 1.10.0 → 1.10.1

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 CHANGED
@@ -1,316 +1,326 @@
1
- # AgentWallet MCP Server
2
-
3
- Permissionless wallet infrastructure for AI agents. Create wallets, sign transactions, and broadcast on-chain, on any EVM chain and Solana. Built-in guards. No KYC. The only AI agent wallet that accepts crypto for its own API fees.
4
-
5
- **Your keys can stay on your machine.** Set one environment variable and every signature happens in your own process. No server sees the key, no company can freeze the wallet, and you can verify that claim by reading `src/local-wallet.ts` or by running the server with the API pointed at a closed port.
6
-
7
- **No KYC. No KYT. No approval process. No transaction monitoring. No one can block your wallet. Pay with USDC on-chain, no credit card required.**
8
-
9
- ## Two modes
10
-
11
- | | Local (self-custody) | Hosted (custodial) |
12
- |---|---|---|
13
- | Who holds the key | You, in your own process | Encrypted on AgentWallet servers |
14
- | Set up | One env var, no account needed | API key |
15
- | Chains | EVM + Solana | EVM + Solana |
16
- | Can anyone freeze it | No | Yes, that is what pause is for |
17
- | Spend guards | `AGENTWALLET_MAX_TX_NATIVE`, `AGENTWALLET_MAX_TX_SOL`, `AGENTWALLET_MAX_AUTOPAY` | Server-side limits, pause, rate limits |
18
- | Paywalls, usage, billing | Needs an API key too | Included |
19
-
20
- Run `wallet_mode` at any time and the server will tell you which one you are in, and which address it controls.
21
-
22
- ### Local signing in one variable
23
-
24
- ```json
25
- {
26
- "mcpServers": {
27
- "agentwallet": {
28
- "command": "npx",
29
- "args": ["-y", "agentwallet-mcp"],
30
- "env": {
31
- "AGENTWALLET_PRIVATE_KEY": "0xyour_key",
32
- "AGENTWALLET_RPC_8453": "https://your-own-rpc",
33
- "AGENTWALLET_MAX_TX_NATIVE": "0.05"
34
- }
35
- }
36
- }
37
- }
38
- ```
39
-
40
- Use `AGENTWALLET_KEYFILE=/path/to/key` instead if you would rather keep the key out of your shell config. The key is read once, never written to disk, never logged, and never included in an error message.
41
-
42
- `AGENTWALLET_RPC_<chainId>` (or `AGENTWALLET_RPC_URL` for all chains) points at an endpoint you trust. Without it a public RPC is used, and a public RPC can see which addresses you ask about.
43
-
44
- `AGENTWALLET_MAX_TX_NATIVE` is a per-transaction ceiling in native units. In hosted mode the server enforces limits; in local mode there is no server, so this guard is the only one there is. Set it.
45
-
46
- ### Solana local signing
47
-
48
- ```json
49
- "env": {
50
- "AGENTWALLET_SOLANA_KEY": "[12,34...]",
51
- "AGENTWALLET_SOLANA_RPC": "https://your-own-rpc",
52
- "AGENTWALLET_MAX_TX_SOL": "1"
53
- }
54
- ```
55
-
56
- Accepts whichever format you already have: a `solana-keygen` id.json array, a base58 secret key as exported by Phantom, or base64. Use `AGENTWALLET_SOLANA_KEYFILE` to point at a file instead. Native SOL and SPL token transfers are both signed locally, and a missing associated token account is created for the recipient automatically.
57
-
58
- Set either key, or both. They are independent: run EVM locally and Solana hosted, or the reverse.
59
-
60
- **An operation with no matching local key is refused, never silently routed to the hosted signer.** If you have an EVM key configured and ask for a Solana transfer with no Solana key, the server stops and tells you which variable is missing. Quietly moving funds onto a key you do not hold, while you believe you are in self-custody, is the worst thing this server could do.
61
-
62
- ### About the dependency tree
63
-
64
- Local signing uses [viem](https://viem.sh) for EVM and `@solana/web3.js` for Solana, plus `bs58` for key parsing. SPL instructions are built by hand rather than with `@solana/spl-token`, because that package pulls in `bigint-buffer`, which carries a high severity buffer overflow advisory. A wallet has no business shipping that to save a dozen lines of instruction encoding.
65
-
66
- `npm audit` currently reports issues inside `@modelcontextprotocol/sdk`'s HTTP transport dependencies. This server speaks stdio, so that code never loads, and the SDK is not something this package can patch. Run the audit yourself. Publishing a tree you can inspect is the point.
67
-
68
- <p align="center">
69
- <img src="assets/demo.svg" alt="AgentWallet demo, AI agent pays x402 invoice automatically" width="800">
70
- </p>
71
-
72
- ## Features
73
-
74
- - **31 MCP tools**: create wallets, send transactions, approve tokens, wrap ETH, transfer SPL tokens, pay and accept x402 payments, verify custody mode, and more
75
- - **EVM + Solana**: Ethereum, Base, Polygon, BSC, Arbitrum, Optimism, Avalanche, Zora, PulseChain, Solana, and any other EVM-compatible chain
76
- - **SOL + SPL tokens**: native SOL transfers and SPL token transfers (USDC, USDT, etc.) with automatic account creation
77
- - **Built-in guards**: daily spending limits, gas price protection, emergency pause, rate limiting, replay protection, and on-chain verification, all active by default
78
- - **x402 payments**: pay for x402-enabled APIs automatically, or accept x402 payments on your own endpoints (EVM and Solana)
79
- - **Self-custody option**: run local mode and the key never leaves your machine. In hosted mode, keys are encrypted at rest, decrypted only during signing, and zeroed from memory immediately after. Either way you can export and walk away.
80
- - **Permissionless**: No KYC. No KYT. No identity verification. No approval process. No compliance gatekeeping. Sign up, get an API key, and start transacting immediately.
81
- - **30-second setup**: three lines of config. No SDK to install. No dependencies to manage.
82
-
83
- ## Pricing
84
-
85
- - **$0.00345 per operation**
86
- - **6,000 free operations/month**
87
- - **$0.0005 per x402 verification**
88
- - **1,000 free x402 verifications/month**
89
- - **Pay with USDC on-chain** via x402, no credit card required
90
- - No monthly fee, no tiers, just pay as you go
91
-
92
- Competitor comparisons are kept at [hifriendbot.com/wallet/#pricing](https://hifriendbot.com/wallet/#pricing) with the date they were last verified. They live there rather than here because a published npm README cannot be corrected when someone else changes their prices.
93
-
94
- ## Quick Start
95
-
96
- Get your free API key at [hifriendbot.com/wallet](https://hifriendbot.com/wallet), no credit card required, no KYC, no approval wait.
97
-
98
- ### Claude Desktop / OpenClaw
99
-
100
- Add to your config:
101
-
102
- ```json
103
- {
104
- "mcpServers": {
105
- "agentwallet": {
106
- "command": "npx",
107
- "args": ["-y", "agentwallet-mcp"],
108
- "env": {
109
- "AGENTWALLET_USER": "your_username",
110
- "AGENTWALLET_PASS": "your_api_key",
111
- "AGENTWALLET_WALLET_ID": "1"
112
- }
113
- }
114
- }
115
- }
116
- ```
117
-
118
- > `AGENTWALLET_WALLET_ID` is optional. Set it to enable x402 auto-pay: when you exceed the free tier without a credit card, the MCP server automatically pays for operations with USDC from this wallet.
119
- >
120
- > **Auto-pay safety cap.** `AGENTWALLET_MAX_AUTOPAY` (optional, default `1`) is the maximum amount, in human-readable units of the asset, that x402 auto-pay will authorize for a single payment. Any 402 requirement above this cap is rejected instead of paid, so a malformed or tampered payment requirement cannot drain the wallet. Raise it only if you genuinely need larger automatic payments (for example `"5"` to allow up to 5 USDC per call).
121
-
122
- ### Claude Code
123
-
124
- ```bash
125
- claude mcp add agentwallet \
126
- -e AGENTWALLET_USER=your_username \
127
- -e AGENTWALLET_PASS=your_api_key \
128
- -e AGENTWALLET_WALLET_ID=1 \
129
- -- npx -y agentwallet-mcp
130
- ```
131
-
132
- ### VS Code
133
-
134
- Add to your settings:
135
-
136
- ```json
137
- {
138
- "mcp": {
139
- "servers": {
140
- "agentwallet": {
141
- "command": "npx",
142
- "args": ["-y", "agentwallet-mcp"],
143
- "env": {
144
- "AGENTWALLET_USER": "your_username",
145
- "AGENTWALLET_PASS": "your_api_key",
146
- "AGENTWALLET_WALLET_ID": "1"
147
- }
148
- }
149
- }
150
- }
151
- }
152
- ```
153
-
154
- ## Tools
155
-
156
- | Tool | Description |
157
- |------|-------------|
158
- | `create_wallet` | Create a new EVM or Solana wallet |
159
- | `list_wallets` | List all your wallets |
160
- | `get_wallet` | Get wallet details by ID |
161
- | `get_balance` | Check native token balance on any chain |
162
- | `get_token_balance` | Check ERC-20 or SPL token balance |
163
- | `get_token_info` | Get ERC-20 token name, symbol, and decimals |
164
- | `transfer` | Send native tokens (ETH, SOL, POL, BNB, etc.) |
165
- | `transfer_token` | Send ERC-20 or SPL tokens (USDC, USDT, etc.) |
166
- | `send_transaction` | Sign and broadcast a raw transaction |
167
- | `sign_transaction` | Sign a transaction without broadcasting |
168
- | `call_contract` | Read-only contract call (eth_call) |
169
- | `approve_token` | Approve ERC-20 token spending for DeFi |
170
- | `get_allowance` | Check ERC-20 token allowance |
171
- | `wrap_eth` | Wrap native tokens to WETH/WAVAX/etc. |
172
- | `unwrap_eth` | Unwrap WETH back to native tokens |
173
- | `pay_x402` | Pay x402 invoices automatically (fetch, pay, retry) |
174
- | `create_paywall` | Create an x402 paywall to charge for a resource |
175
- | `list_paywalls` | List all your x402 paywalls |
176
- | `get_paywall` | Get paywall details by ID |
177
- | `update_paywall` | Update paywall pricing, resource, or status |
178
- | `delete_paywall` | Delete a paywall |
179
- | `get_paywall_payments` | View payment history for a paywall |
180
- | `get_x402_revenue` | Aggregate revenue stats across all paywalls |
181
- | `wallet_mode` | Report whether signing is local (self-custody) or hosted, and which address is in use |
182
- | `export_wallet_key` | How to export a hosted wallet key and move to self-custody |
183
- | `buy_verification_credits` | Buy x402 verification credits with USDC on-chain |
184
- | `get_usage` | Check your monthly usage and billing |
185
- | `get_chains` | List all supported chains |
186
- | `pause_wallet` | Emergency pause a wallet |
187
- | `unpause_wallet` | Resume a paused wallet |
188
- | `delete_wallet` | Delete a wallet |
189
-
190
- ## Supported Chains
191
-
192
- | Chain | ID | Native Token | Stablecoin |
193
- |-------|-----|-------------|------------|
194
- | Ethereum | 1 | ETH | USDC |
195
- | Base | 8453 | ETH | USDC |
196
- | Polygon | 137 | POL | USDC |
197
- | BSC | 56 | BNB | USDT |
198
- | Arbitrum | 42161 | ETH | USDC |
199
- | Optimism | 10 | ETH | USDC |
200
- | Avalanche | 43114 | AVAX | USDC |
201
- | Zora | 7777777 | ETH | USDC |
202
- | PulseChain | 369 | PLS | USDC |
203
- | Solana | 900 | SOL | USDC |
204
- | Solana Devnet | 901 | SOL | USDC |
205
-
206
- ## Use Case: GuessMarket
207
-
208
- Pair with [guessmarket-mcp](https://www.npmjs.com/package/guessmarket-mcp) to let your AI agent trade prediction markets:
209
-
210
- 1. Create a wallet on Base
211
- 2. Approve USDC spending
212
- 3. Buy YES/NO shares on prediction markets
213
- 4. Provide liquidity and earn trading fees
214
- 5. Claim winnings
215
-
216
- All on-chain. All through MCP. No frontend needed.
217
-
218
- ## x402 Payments
219
-
220
- 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:
221
-
222
- 1. Fetches the URL and detects the 402 response
223
- 2. Parses the payment requirements (amount, token, chain)
224
- 3. Executes the on-chain payment from your wallet
225
- 4. Retries the request with proof of payment
226
- 5. Returns the final response
227
-
228
- **Set `max_payment` to control spending:**
229
-
230
- ```
231
- pay_x402(
232
- url="https://api.example.com/premium-data",
233
- wallet_id=1,
234
- max_payment="1.00"
235
- )
236
- ```
237
-
238
- `max_payment` is enforced as a hard per-payment cap. If you omit it, `pay_x402` falls back to `AGENTWALLET_MAX_AUTOPAY` (default `1`), the same cap the auto-pay path uses, so a malicious or compromised 402 endpoint can never authorize an unbounded payment. Set `max_payment` explicitly (or raise `AGENTWALLET_MAX_AUTOPAY`) to allow a larger single payment.
239
-
240
- Supports ERC-20 tokens, SPL tokens, and native tokens on EVM and Solana. Compatible with x402 V1 and V2 (CAIP-2 chain identifiers), and reads the token address from the standard x402 `asset` field (falling back to `extra.token`).
241
-
242
- ## x402 Acceptance
243
-
244
- AgentWallet also lets you **accept** x402 payments. Create a paywall, point it at any resource, and get a public URL that charges agents automatically:
245
-
246
- ```
247
- create_paywall(
248
- wallet_id=1,
249
- name="Premium API",
250
- amount="0.01",
251
- token_name="USDC",
252
- token_address="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
253
- chain_id=8453,
254
- resource_url="https://your-api.com/data"
255
- )
256
- ```
257
-
258
- When an agent hits the paywall URL:
259
- 1. Gets back HTTP 402 with payment requirements
260
- 2. Pays on-chain using `pay_x402` (or any x402-compatible client)
261
- 3. Retries with proof of payment
262
- 4. Receives the protected content
263
-
264
- On-chain verification ensures every payment is real. Replay protection prevents double-spending. Revenue tracking shows you who paid, how much, and when. 1,000 free verifications/month, then $0.0005 each. See the [pricing comparison](https://hifriendbot.com/wallet/#pricing) for how that stacks up.
265
-
266
- ## How We Compare
267
-
268
- | Feature | Coinbase CDP | AgentWallet |
269
- |---------|-------------|-------------|
270
- | Setup Time | Install SDK + configure | **3 lines of config** |
271
- | Approval Process | Identity verification required | **None, instant access** |
272
- | KYC Required | Yes | **No** |
273
- | KYT / Transaction Monitoring | Yes | **No** |
274
- | Can Block Your Wallet | Yes | **No** |
275
- | Built-in Guards | Yes (requires setup) | **Yes (active by default)** |
276
- | Free Operations / Month | 5,000 | **6,000** |
277
- | Cost Per Operation | $0.005 | **$0.00345** |
278
- | x402 Verification Cost | $0.001 | **$0.0005** |
279
- | Free x402 Verifications / Month | 1,000 | **1,000** |
280
- | x402 Acceptance (Paywalls) | No | **Yes** |
281
- | Pay for API Fees with Crypto | No (credit card only) | **Yes (USDC via x402)** |
282
- | Supported Chains | 8 EVM + Solana | **Any EVM + Solana** |
283
- | Token Tools | Yes | **ERC-20 + SPL (29 tools)** |
284
- | MCP Server | Yes | **Yes** |
285
-
286
- ## Pay with Crypto, No Credit Card Required
287
-
288
- AgentWallet is the only AI agent wallet infrastructure that accepts crypto for its own API fees. Every competitor, Coinbase CDP, Circle, MoonPay, Crossmint, Turnkey, requires a credit card or monthly invoice. With AgentWallet, your agent can pay for operations with USDC on-chain via the x402 protocol. No credit card, no invoice, no billing portal. Just on-chain payments.
289
-
290
- When your agent exceeds the free tier (6,000 ops/month) without a credit card configured, the API returns HTTP 402 with USDC payment instructions. Your agent pays on-chain, retries with proof of payment, and the operation executes. Fully automated via the MCP server.
291
-
292
- You can also pre-purchase x402 verification credits with USDC using the `buy_verification_credits` tool, keeping your paywalls running beyond the free 1,000 verifications/month without needing a credit card.
293
-
294
- ## Built-in Guards
295
-
296
- All guards are active by default, no configuration required.
297
-
298
- - **Encrypted at rest**: private keys encrypted before storage and never leave the server
299
- - **Memory zeroing**: keys wiped from memory immediately after every signing operation
300
- - **Daily spending limits**: set a per-wallet daily cap in USD, enforced automatically on every transaction
301
- - **Gas price protection**: transactions blocked when gas prices spike above safe thresholds
302
- - **Emergency pause**: instantly freeze any wallet or all wallets with one click
303
- - **Rate limiting**: API requests capped per minute to prevent abuse and brute force attacks
304
- - **Replay protection**: every x402 payment verified on-chain with unique transaction tracking
305
- - **On-chain verification**: x402 payments verified directly on the blockchain with finalized commitment
306
- - Bug bounty program: $50,$500 for responsible disclosure ([details](https://hifriendbot.com/wallet))
307
-
308
- ## Links
309
-
310
- - **Website:** [hifriendbot.com/wallet](https://hifriendbot.com/wallet)
311
- - **npm:** [agentwallet-mcp](https://www.npmjs.com/package/agentwallet-mcp)
312
- - **Security:** [security@hifriendbot.com](mailto:security@hifriendbot.com)
313
-
314
- ## License
315
-
316
- MIT
1
+ # AgentWallet MCP Server
2
+
3
+ Permissionless wallet infrastructure for AI agents. Create wallets, sign transactions, and broadcast on-chain, on any EVM chain and Solana. Built-in guards. No KYC. The only AI agent wallet that accepts crypto for its own API fees.
4
+
5
+ **Your keys can stay on your machine.** Set one environment variable and every signature happens in your own process. No server sees the key, no company can freeze the wallet, and you can verify that claim by reading `src/local-wallet.ts` or by running the server with the API pointed at a closed port.
6
+
7
+ **No KYC. No KYT. No approval process. No transaction monitoring. No one can block your wallet. Pay with USDC on-chain, no credit card required.**
8
+
9
+ ## Two modes
10
+
11
+ | | Local (self-custody) | Hosted (custodial) |
12
+ |---|---|---|
13
+ | Who holds the key | You, in your own process | Encrypted on AgentWallet servers |
14
+ | Set up | One env var, no account needed | API key |
15
+ | Chains | EVM + Solana | EVM + Solana |
16
+ | Can anyone freeze it | No | Yes, that is what pause is for |
17
+ | Spend guards | `AGENTWALLET_MAX_TX_NATIVE`, `AGENTWALLET_MAX_TX_SOL`, `AGENTWALLET_MAX_AUTOPAY` | Server-side limits, pause, rate limits |
18
+ | Paywalls, usage, billing | Needs an API key too | Included |
19
+
20
+ Run `wallet_mode` at any time and the server will tell you which one you are in, and which address it controls.
21
+
22
+ ### Local signing in one variable
23
+
24
+ ```json
25
+ {
26
+ "mcpServers": {
27
+ "agentwallet": {
28
+ "command": "npx",
29
+ "args": ["-y", "agentwallet-mcp"],
30
+ "env": {
31
+ "AGENTWALLET_PRIVATE_KEY": "0xyour_key",
32
+ "AGENTWALLET_RPC_8453": "https://your-own-rpc",
33
+ "AGENTWALLET_MAX_TX_NATIVE": "0.05"
34
+ }
35
+ }
36
+ }
37
+ }
38
+ ```
39
+
40
+ Use `AGENTWALLET_KEYFILE=/path/to/key` instead if you would rather keep the key out of your shell config. The key is read once, never written to disk, never logged, and never included in an error message.
41
+
42
+ `AGENTWALLET_RPC_<chainId>` (or `AGENTWALLET_RPC_URL` for all chains) points at an endpoint you trust. Without it a public RPC is used, and a public RPC can see which addresses you ask about.
43
+
44
+ `AGENTWALLET_MAX_TX_NATIVE` is a per-transaction ceiling in native units. In hosted mode the server enforces limits; in local mode there is no server, so this guard is the only one there is. Set it.
45
+
46
+ ### Solana local signing
47
+
48
+ ```json
49
+ "env": {
50
+ "AGENTWALLET_SOLANA_KEY": "[12,34...]",
51
+ "AGENTWALLET_SOLANA_RPC": "https://your-own-rpc",
52
+ "AGENTWALLET_MAX_TX_SOL": "1"
53
+ }
54
+ ```
55
+
56
+ Accepts whichever format you already have: a `solana-keygen` id.json array, a base58 secret key as exported by Phantom, or base64. Use `AGENTWALLET_SOLANA_KEYFILE` to point at a file instead. Native SOL and SPL token transfers are both signed locally, and a missing associated token account is created for the recipient automatically.
57
+
58
+ Set either key, or both. They are independent: run EVM locally and Solana hosted, or the reverse.
59
+
60
+ **An operation with no matching local key is refused, never silently routed to the hosted signer.** If you have an EVM key configured and ask for a Solana transfer with no Solana key, the server stops and tells you which variable is missing. Quietly moving funds onto a key you do not hold, while you believe you are in self-custody, is the worst thing this server could do.
61
+
62
+ ### About the dependency tree
63
+
64
+ Local signing uses [viem](https://viem.sh) for EVM and `@solana/web3.js` for Solana, plus `bs58` for key parsing. SPL instructions are built by hand rather than with `@solana/spl-token`, because that package pulls in `bigint-buffer`, which carries a high severity buffer overflow advisory. A wallet has no business shipping that to save a dozen lines of instruction encoding.
65
+
66
+ `npm audit` currently reports issues inside `@modelcontextprotocol/sdk`'s HTTP transport dependencies. This server speaks stdio, so that code never loads, and the SDK is not something this package can patch. Run the audit yourself. Publishing a tree you can inspect is the point.
67
+
68
+ <p align="center">
69
+ <img src="assets/demo.svg" alt="AgentWallet demo, AI agent pays x402 invoice automatically" width="800">
70
+ </p>
71
+
72
+ ## Features
73
+
74
+ - **31 MCP tools**: create wallets, send transactions, approve tokens, wrap ETH, transfer SPL tokens, pay and accept x402 payments, verify custody mode, and more
75
+ - **EVM + Solana**: Ethereum, Base, Polygon, BSC, Arbitrum, Optimism, Avalanche, Zora, PulseChain, Solana, and any other EVM-compatible chain
76
+ - **SOL + SPL tokens**: native SOL transfers and SPL token transfers (USDC, USDT, etc.) with automatic account creation
77
+ - **Built-in guards**: daily spending limits, gas price protection, emergency pause, rate limiting, replay protection, and on-chain verification, all active by default
78
+ - **x402 payments**: pay for x402-enabled APIs automatically, or accept x402 payments on your own endpoints (EVM and Solana)
79
+ - **Self-custody option**: run local mode and the key never leaves your machine. In hosted mode, keys are encrypted at rest, decrypted only during signing, and zeroed from memory immediately after. Either way you can export and walk away.
80
+ - **Permissionless**: No KYC. No KYT. No identity verification. No approval process. No compliance gatekeeping. Sign up, get an API key, and start transacting immediately.
81
+ - **30-second setup**: three lines of config. No SDK to install. No dependencies to manage.
82
+
83
+ ## Pricing
84
+
85
+ - **$0.00345 per operation**
86
+ - **6,000 free operations/month**
87
+ - **$0.0005 per x402 verification**
88
+ - **1,000 free x402 verifications/month**
89
+ - **Pay with USDC on-chain** via x402, no credit card required
90
+ - No monthly fee, no tiers, just pay as you go
91
+
92
+ Competitor comparisons are kept at [hifriendbot.com/wallet/#pricing](https://hifriendbot.com/wallet/#pricing) with the date they were last verified. They live there rather than here because a published npm README cannot be corrected when someone else changes their prices.
93
+
94
+ ## Quick Start
95
+
96
+ Get your free API key at [hifriendbot.com/wallet](https://hifriendbot.com/wallet), no credit card required, no KYC, no approval wait.
97
+
98
+ ### Claude Desktop / OpenClaw
99
+
100
+ Add to your config:
101
+
102
+ ```json
103
+ {
104
+ "mcpServers": {
105
+ "agentwallet": {
106
+ "command": "npx",
107
+ "args": ["-y", "agentwallet-mcp"],
108
+ "env": {
109
+ "AGENTWALLET_USER": "your_username",
110
+ "AGENTWALLET_PASS": "your_api_key",
111
+ "AGENTWALLET_WALLET_ID": "1"
112
+ }
113
+ }
114
+ }
115
+ }
116
+ ```
117
+
118
+ > `AGENTWALLET_WALLET_ID` is optional. Set it to enable x402 auto-pay: when you exceed the free tier without a credit card, the MCP server automatically pays for operations with USDC from this wallet.
119
+ >
120
+ > **Auto-pay safety cap.** `AGENTWALLET_MAX_AUTOPAY` (optional, default `1`) is the maximum amount, in human-readable units of the asset, that x402 auto-pay will authorize for a single payment. Any 402 requirement above this cap is rejected instead of paid, so a malformed or tampered payment requirement cannot drain the wallet. Raise it only if you genuinely need larger automatic payments (for example `"5"` to allow up to 5 USDC per call).
121
+
122
+ ### Claude Code
123
+
124
+ ```bash
125
+ claude mcp add agentwallet \
126
+ -e AGENTWALLET_USER=your_username \
127
+ -e AGENTWALLET_PASS=your_api_key \
128
+ -e AGENTWALLET_WALLET_ID=1 \
129
+ -- npx -y agentwallet-mcp
130
+ ```
131
+
132
+ ### VS Code
133
+
134
+ Add to your settings:
135
+
136
+ ```json
137
+ {
138
+ "mcp": {
139
+ "servers": {
140
+ "agentwallet": {
141
+ "command": "npx",
142
+ "args": ["-y", "agentwallet-mcp"],
143
+ "env": {
144
+ "AGENTWALLET_USER": "your_username",
145
+ "AGENTWALLET_PASS": "your_api_key",
146
+ "AGENTWALLET_WALLET_ID": "1"
147
+ }
148
+ }
149
+ }
150
+ }
151
+ }
152
+ ```
153
+
154
+ ## Tools
155
+
156
+ | Tool | Description |
157
+ |------|-------------|
158
+ | `create_wallet` | Create a new EVM or Solana wallet |
159
+ | `list_wallets` | List all your wallets |
160
+ | `get_wallet` | Get wallet details by ID |
161
+ | `get_balance` | Check native token balance on any chain |
162
+ | `get_token_balance` | Check ERC-20 or SPL token balance |
163
+ | `get_token_info` | Get ERC-20 token name, symbol, and decimals |
164
+ | `transfer` | Send native tokens (ETH, SOL, POL, BNB, etc.) |
165
+ | `transfer_token` | Send ERC-20 or SPL tokens (USDC, USDT, etc.) |
166
+ | `send_transaction` | Sign and broadcast a raw transaction |
167
+ | `sign_transaction` | Sign a transaction without broadcasting |
168
+ | `call_contract` | Read-only contract call (eth_call) |
169
+ | `approve_token` | Approve ERC-20 token spending for DeFi |
170
+ | `get_allowance` | Check ERC-20 token allowance |
171
+ | `wrap_eth` | Wrap native tokens to WETH/WAVAX/etc. |
172
+ | `unwrap_eth` | Unwrap WETH back to native tokens |
173
+ | `pay_x402` | Pay x402 invoices automatically (fetch, pay, retry) |
174
+ | `create_paywall` | Create an x402 paywall to charge for a resource |
175
+ | `list_paywalls` | List all your x402 paywalls |
176
+ | `get_paywall` | Get paywall details by ID |
177
+ | `update_paywall` | Update paywall pricing, resource, or status |
178
+ | `delete_paywall` | Delete a paywall |
179
+ | `get_paywall_payments` | View payment history for a paywall |
180
+ | `get_x402_revenue` | Aggregate revenue stats across all paywalls |
181
+ | `wallet_mode` | Report whether signing is local (self-custody) or hosted, and which address is in use |
182
+ | `export_wallet_key` | How to export a hosted wallet key and move to self-custody |
183
+ | `buy_verification_credits` | Buy x402 verification credits with USDC on-chain |
184
+ | `get_usage` | Check your monthly usage and billing |
185
+ | `get_chains` | List all supported chains |
186
+ | `pause_wallet` | Emergency pause a wallet |
187
+ | `unpause_wallet` | Resume a paused wallet |
188
+ | `delete_wallet` | Delete a wallet |
189
+
190
+ ## Supported Chains
191
+
192
+ | Chain | ID | Native Token | Stablecoin |
193
+ |-------|-----|-------------|------------|
194
+ | Ethereum | 1 | ETH | USDC |
195
+ | Base | 8453 | ETH | USDC |
196
+ | Polygon | 137 | POL | USDC |
197
+ | BSC | 56 | BNB | USDT |
198
+ | Arbitrum | 42161 | ETH | USDC |
199
+ | Optimism | 10 | ETH | USDC |
200
+ | Avalanche | 43114 | AVAX | USDC |
201
+ | Zora | 7777777 | ETH | USDC |
202
+ | PulseChain | 369 | PLS | USDC |
203
+ | Solana | 900 | SOL | USDC |
204
+ | Solana Devnet | 901 | SOL | USDC |
205
+
206
+ ## Use Case: GuessMarket
207
+
208
+ Pair with [guessmarket-mcp](https://www.npmjs.com/package/guessmarket-mcp) to let your AI agent trade prediction markets:
209
+
210
+ 1. Create a wallet on Base
211
+ 2. Approve USDC spending
212
+ 3. Buy YES/NO shares on prediction markets
213
+ 4. Provide liquidity and earn trading fees
214
+ 5. Claim winnings
215
+
216
+ All on-chain. All through MCP. No frontend needed.
217
+
218
+ ## x402 Payments
219
+
220
+ 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:
221
+
222
+ 1. Fetches the URL and detects the 402 response
223
+ 2. Parses the payment requirements (amount, token, chain)
224
+ 3. Executes the on-chain payment from your wallet
225
+ 4. Retries the request with proof of payment
226
+ 5. Returns the final response
227
+
228
+ **Set `max_payment` to control spending:**
229
+
230
+ ```
231
+ pay_x402(
232
+ url="https://api.example.com/premium-data",
233
+ wallet_id=1,
234
+ max_payment="1.00"
235
+ )
236
+ ```
237
+
238
+ `max_payment` is enforced as a hard per-payment cap. If you omit it, `pay_x402` falls back to `AGENTWALLET_MAX_AUTOPAY` (default `1`), the same cap the auto-pay path uses, so a malicious or compromised 402 endpoint can never authorize an unbounded payment. Set `max_payment` explicitly (or raise `AGENTWALLET_MAX_AUTOPAY`) to allow a larger single payment.
239
+
240
+ Supports ERC-20 tokens, SPL tokens, and native tokens on EVM and Solana. Compatible with x402 V1 and V2 (CAIP-2 chain identifiers), and reads the token address from the standard x402 `asset` field (falling back to `extra.token`).
241
+
242
+ ## x402 Acceptance
243
+
244
+ AgentWallet also lets you **accept** x402 payments. Create a paywall, point it at any resource, and get a public URL that charges agents automatically:
245
+
246
+ ```
247
+ create_paywall(
248
+ wallet_id=1,
249
+ name="Premium API",
250
+ amount="0.01",
251
+ token_name="USDC",
252
+ token_address="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
253
+ chain_id=8453,
254
+ resource_url="https://your-api.com/data"
255
+ )
256
+ ```
257
+
258
+ When an agent hits the paywall URL:
259
+ 1. Gets back HTTP 402 with payment requirements
260
+ 2. Pays on-chain using `pay_x402` (or any x402-compatible client)
261
+ 3. Retries with proof of payment
262
+ 4. Receives the protected content
263
+
264
+ On-chain verification ensures every payment is real. Replay protection prevents double-spending. Revenue tracking shows you who paid, how much, and when. 1,000 free verifications/month, then $0.0005 each. See the [pricing comparison](https://hifriendbot.com/wallet/#pricing) for how that stacks up.
265
+
266
+ ## How We Compare
267
+
268
+ | Feature | Coinbase CDP | AgentWallet |
269
+ |---------|-------------|-------------|
270
+ | Setup Time | Install SDK + configure | **3 lines of config** |
271
+ | Approval Process | Identity verification required | **None, instant access** |
272
+ | KYC Required | Yes | **No** |
273
+ | KYT / Transaction Monitoring | Yes | **No** |
274
+ | Can Block Your Wallet | Yes | **No** |
275
+ | Built-in Guards | Yes (requires setup) | **Yes (active by default)** |
276
+ | Free Operations / Month | 5,000 | **6,000** |
277
+ | Cost Per Operation | $0.005 | **$0.00345** |
278
+ | x402 Verification Cost | $0.001 | **$0.0005** |
279
+ | Free x402 Verifications / Month | 1,000 | **1,000** |
280
+ | x402 Acceptance (Paywalls) | No | **Yes** |
281
+ | Pay for API Fees with Crypto | No (credit card only) | **Yes (USDC via x402)** |
282
+ | Supported Chains | 8 EVM + Solana | **Any EVM + Solana** |
283
+ | Token Tools | Yes | **ERC-20 + SPL (29 tools)** |
284
+ | MCP Server | Yes | **Yes** |
285
+
286
+ ## Pay with Crypto, No Credit Card Required
287
+
288
+ AgentWallet is the only AI agent wallet infrastructure that accepts crypto for its own API fees. Every competitor, Coinbase CDP, Circle, MoonPay, Crossmint, Turnkey, requires a credit card or monthly invoice. With AgentWallet, your agent can pay for operations with USDC on-chain via the x402 protocol. No credit card, no invoice, no billing portal. Just on-chain payments.
289
+
290
+ When your agent exceeds the free tier (6,000 ops/month) without a credit card configured, the API returns HTTP 402 with USDC payment instructions. Your agent pays on-chain, retries with proof of payment, and the operation executes. Fully automated via the MCP server.
291
+
292
+ You can also pre-purchase x402 verification credits with USDC using the `buy_verification_credits` tool, keeping your paywalls running beyond the free 1,000 verifications/month without needing a credit card.
293
+
294
+ ## Built-in Guards
295
+
296
+ All guards are active by default, no configuration required.
297
+
298
+ - **Encrypted at rest**: private keys encrypted before storage and never leave the server
299
+ - **Memory zeroing**: keys wiped from memory immediately after every signing operation
300
+ - **Daily spending limits**: set a per-wallet daily cap in USD, enforced automatically on every transaction
301
+ - **Gas price protection**: transactions blocked when gas prices spike above safe thresholds
302
+ - **Emergency pause**: instantly freeze any wallet or all wallets with one click
303
+ - **Rate limiting**: API requests capped per minute to prevent abuse and brute force attacks
304
+ - **Replay protection**: every x402 payment verified on-chain with unique transaction tracking
305
+ - **On-chain verification**: x402 payments verified directly on the blockchain with finalized commitment
306
+ - Bug bounty program: $50,$500 for responsible disclosure ([details](https://hifriendbot.com/wallet))
307
+
308
+ ## Links
309
+
310
+ - **Website:** [hifriendbot.com/wallet](https://hifriendbot.com/wallet)
311
+ - **npm:** [agentwallet-mcp](https://www.npmjs.com/package/agentwallet-mcp)
312
+ - **Security:** [security@hifriendbot.com](mailto:security@hifriendbot.com)
313
+
314
+
315
+ ## Security
316
+
317
+ `pay_x402` validates the target URL before every outbound request and again on
318
+ each redirect hop. IP literals are canonicalized (including IPv4-mapped IPv6
319
+ such as `[::ffff:127.0.0.1]`) and hostnames are resolved, with loopback,
320
+ private, link-local, carrier-grade NAT, multicast and cloud-metadata
321
+ destinations refused. Report security issues privately to
322
+ security@hifriendbot.com.
323
+
324
+ ## License
325
+
326
+ MIT
package/build/index.js CHANGED
@@ -12,6 +12,7 @@
12
12
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
13
13
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
14
14
  import { z } from 'zod';
15
+ import { assertPublicUrl, safeFetch } from './ssrf-guard.js';
15
16
  import { deriveX402Payment, isWithinCap } from './x402-payment.js';
16
17
  import { isLocalMode, getLocalAddress, localWalletRecord, localSend, localNativeBalance, localTokenBalance, localSignMessage, resolveRpcUrl, } from './local-wallet.js';
17
18
  import { isSolanaLocalMode, getSolanaAddress, solanaWalletRecord, localSolBalance, localSplBalance, localSolTransfer, localSplTransfer, resolveSolanaRpc, } from './local-solana.js';
@@ -360,82 +361,82 @@ function resolveChainId(network) {
360
361
  // ─── Server ──────────────────────────────────────────────────────
361
362
  const server = new McpServer({
362
363
  name: 'agentwallet',
363
- version: '1.8.0',
364
+ version: '1.10.1',
364
365
  }, {
365
- instructions: `AgentWallet gives AI agents their own blockchain wallets. Private keys are encrypted server-side and never exposed — agents sign and broadcast transactions without ever touching raw keys.
366
-
367
- ## Getting Started
368
- 1. Call list_wallets to see existing wallets, or create_wallet to make a new one.
369
- 2. Fund the wallet by sending tokens to its address from an external source.
370
- 3. Use transfer (native tokens) or transfer_token (ERC-20/SPL tokens) to send funds.
371
-
372
- ## Supported Chains
373
- - **EVM chains:** Ethereum (1), Base (8453), Arbitrum (42161), Optimism (10), Polygon (137), Avalanche (43114), BSC (56), Zora (7777777), PulseChain (369)
374
- - **Solana:** Mainnet (900), Devnet (901)
375
- - Call get_chains for the full list with native tokens and stablecoin contract addresses.
376
-
377
- ## EVM vs Solana
378
- - **Addresses:** EVM uses 0x-prefixed hex (42 chars). Solana uses Base58 (32-44 chars).
379
- - **Native tokens:** EVM = ETH/POL/BNB/etc (18 decimals, wei). Solana = SOL (9 decimals, lamports).
380
- - **Token standards:** EVM uses ERC-20. Solana uses SPL tokens.
381
- - **EVM-only tools:** approve_token, get_allowance, wrap_eth, unwrap_eth, call_contract, get_token_info. These do not work on Solana.
382
- - **Works on both:** create_wallet, transfer, transfer_token, send_transaction, sign_transaction, get_balance, get_token_balance.
383
-
384
- ## Common Workflows
385
-
386
- ### Send native tokens (ETH, SOL, etc.)
387
- Use the transfer tool with a human-readable amount (e.g. "0.1" ETH). It auto-converts to wei/lamports.
388
-
389
- ### Send ERC-20 or SPL tokens
390
- Use transfer_token. You need: wallet_id, token contract address, recipient, amount, chain_id, and decimals (6 for USDC, 18 for most ERC-20s). Call get_chains to find stablecoin addresses.
391
-
392
- ### Interact with DeFi (EVM only)
393
- 1. Check allowance with get_allowance.
394
- 2. If needed, call approve_token to let the DeFi contract spend your tokens.
395
- 3. Use send_transaction with the contract's calldata to execute the interaction.
396
-
397
- ### Wrap/unwrap native tokens (EVM only)
398
- Use wrap_eth to convert ETH → WETH (required by many DeFi protocols). Use unwrap_eth to convert back.
399
-
400
- ### Read on-chain data (EVM only)
401
- Use call_contract for gas-free read-only calls (eth_call). Provide ABI-encoded calldata.
402
-
403
- ## x402 Payments — Paying for Resources
404
- The pay_x402 tool handles the full x402 payment flow automatically:
405
- 1. Fetches the URL.
406
- 2. If the server returns HTTP 402, parses the payment requirements.
407
- 3. Pays on-chain using your wallet.
408
- 4. Retries the request with proof of payment.
409
- Always set max_payment to prevent overspending (e.g. "1.00" for max 1 USDC).
410
-
411
- ## x402 Paywalls — Accepting Payments
412
- Create paywalls to charge other agents for accessing your resources:
413
- 1. create_paywall — set price, token, chain, and the resource URL to protect.
414
- 2. Share the access URL. Clients that pay on-chain get the resource; others get HTTP 402.
415
- 3. list_paywalls and get_paywall_payments to track revenue.
416
- 4. get_x402_revenue for aggregate stats across all paywalls.
417
-
418
- ## Wallet Security
419
- - Use pause_wallet to immediately freeze a wallet if compromised. No transactions can be signed while paused.
420
- - Use unpause_wallet to resume operations.
421
- - Use delete_wallet to permanently disable a wallet.
422
- - Wallets have server-enforced spending limits.
423
-
424
- ## Tool Selection Guide
425
- | Goal | Tool |
426
- |------|------|
427
- | Send ETH/SOL/native tokens | transfer |
428
- | Send USDC/ERC-20/SPL tokens | transfer_token |
429
- | Check native balance | get_balance |
430
- | Check token balance | get_token_balance |
431
- | Look up token details | get_token_info (EVM only) |
432
- | Approve DeFi spending | approve_token (EVM only) |
433
- | Check approval amount | get_allowance (EVM only) |
434
- | Read smart contract | call_contract (EVM only) |
435
- | Custom transaction | send_transaction |
436
- | Sign without broadcasting | sign_transaction |
437
- | Pay for x402 resource | pay_x402 |
438
- | Charge for your resource | create_paywall |
366
+ instructions: `AgentWallet gives AI agents their own blockchain wallets. Private keys are encrypted server-side and never exposed — agents sign and broadcast transactions without ever touching raw keys.
367
+
368
+ ## Getting Started
369
+ 1. Call list_wallets to see existing wallets, or create_wallet to make a new one.
370
+ 2. Fund the wallet by sending tokens to its address from an external source.
371
+ 3. Use transfer (native tokens) or transfer_token (ERC-20/SPL tokens) to send funds.
372
+
373
+ ## Supported Chains
374
+ - **EVM chains:** Ethereum (1), Base (8453), Arbitrum (42161), Optimism (10), Polygon (137), Avalanche (43114), BSC (56), Zora (7777777), PulseChain (369)
375
+ - **Solana:** Mainnet (900), Devnet (901)
376
+ - Call get_chains for the full list with native tokens and stablecoin contract addresses.
377
+
378
+ ## EVM vs Solana
379
+ - **Addresses:** EVM uses 0x-prefixed hex (42 chars). Solana uses Base58 (32-44 chars).
380
+ - **Native tokens:** EVM = ETH/POL/BNB/etc (18 decimals, wei). Solana = SOL (9 decimals, lamports).
381
+ - **Token standards:** EVM uses ERC-20. Solana uses SPL tokens.
382
+ - **EVM-only tools:** approve_token, get_allowance, wrap_eth, unwrap_eth, call_contract, get_token_info. These do not work on Solana.
383
+ - **Works on both:** create_wallet, transfer, transfer_token, send_transaction, sign_transaction, get_balance, get_token_balance.
384
+
385
+ ## Common Workflows
386
+
387
+ ### Send native tokens (ETH, SOL, etc.)
388
+ Use the transfer tool with a human-readable amount (e.g. "0.1" ETH). It auto-converts to wei/lamports.
389
+
390
+ ### Send ERC-20 or SPL tokens
391
+ Use transfer_token. You need: wallet_id, token contract address, recipient, amount, chain_id, and decimals (6 for USDC, 18 for most ERC-20s). Call get_chains to find stablecoin addresses.
392
+
393
+ ### Interact with DeFi (EVM only)
394
+ 1. Check allowance with get_allowance.
395
+ 2. If needed, call approve_token to let the DeFi contract spend your tokens.
396
+ 3. Use send_transaction with the contract's calldata to execute the interaction.
397
+
398
+ ### Wrap/unwrap native tokens (EVM only)
399
+ Use wrap_eth to convert ETH → WETH (required by many DeFi protocols). Use unwrap_eth to convert back.
400
+
401
+ ### Read on-chain data (EVM only)
402
+ Use call_contract for gas-free read-only calls (eth_call). Provide ABI-encoded calldata.
403
+
404
+ ## x402 Payments — Paying for Resources
405
+ The pay_x402 tool handles the full x402 payment flow automatically:
406
+ 1. Fetches the URL.
407
+ 2. If the server returns HTTP 402, parses the payment requirements.
408
+ 3. Pays on-chain using your wallet.
409
+ 4. Retries the request with proof of payment.
410
+ Always set max_payment to prevent overspending (e.g. "1.00" for max 1 USDC).
411
+
412
+ ## x402 Paywalls — Accepting Payments
413
+ Create paywalls to charge other agents for accessing your resources:
414
+ 1. create_paywall — set price, token, chain, and the resource URL to protect.
415
+ 2. Share the access URL. Clients that pay on-chain get the resource; others get HTTP 402.
416
+ 3. list_paywalls and get_paywall_payments to track revenue.
417
+ 4. get_x402_revenue for aggregate stats across all paywalls.
418
+
419
+ ## Wallet Security
420
+ - Use pause_wallet to immediately freeze a wallet if compromised. No transactions can be signed while paused.
421
+ - Use unpause_wallet to resume operations.
422
+ - Use delete_wallet to permanently disable a wallet.
423
+ - Wallets have server-enforced spending limits.
424
+
425
+ ## Tool Selection Guide
426
+ | Goal | Tool |
427
+ |------|------|
428
+ | Send ETH/SOL/native tokens | transfer |
429
+ | Send USDC/ERC-20/SPL tokens | transfer_token |
430
+ | Check native balance | get_balance |
431
+ | Check token balance | get_token_balance |
432
+ | Look up token details | get_token_info (EVM only) |
433
+ | Approve DeFi spending | approve_token (EVM only) |
434
+ | Check approval amount | get_allowance (EVM only) |
435
+ | Read smart contract | call_contract (EVM only) |
436
+ | Custom transaction | send_transaction |
437
+ | Sign without broadcasting | sign_transaction |
438
+ | Pay for x402 resource | pay_x402 |
439
+ | Charge for your resource | create_paywall |
439
440
  | Check usage/billing | get_usage |`,
440
441
  });
441
442
  // ─── Tool: create_wallet ─────────────────────────────────────────
@@ -900,19 +901,10 @@ server.tool('pay_x402', 'Handle an x402 payment flow. Fetches a URL, and if the
900
901
  throw new Error('Invalid headers JSON. Must be a JSON object (e.g. {"Authorization": "Bearer ..."}).');
901
902
  }
902
903
  }
903
- // Validate URL — block private IPs and cloud metadata endpoints
904
- const urlObj = new URL(url);
905
- const hostname = urlObj.hostname.toLowerCase();
906
- const blockedHosts = ['localhost', '127.0.0.1', '0.0.0.0', '[::1]', '169.254.169.254', 'metadata.google.internal'];
907
- const blockedPrefixes = ['10.', '172.16.', '172.17.', '172.18.', '172.19.', '172.20.', '172.21.',
908
- '172.22.', '172.23.', '172.24.', '172.25.', '172.26.', '172.27.', '172.28.', '172.29.',
909
- '172.30.', '172.31.', '192.168.'];
910
- if (blockedHosts.includes(hostname) || blockedPrefixes.some(p => hostname.startsWith(p))) {
911
- throw new Error('URL points to a private/internal address. Only public URLs are allowed.');
912
- }
913
- if (urlObj.protocol !== 'https:') {
914
- throw new Error('Only HTTPS URLs are supported for x402 payments.');
915
- }
904
+ // Validate URL: canonicalizes IP literals (including IPv4-mapped IPv6) and
905
+ // resolves DNS names, rejecting any private/internal destination. Redirect
906
+ // hops are re-validated by safeFetch below.
907
+ await assertPublicUrl(url);
916
908
  // Step 1: Make initial request
917
909
  const reqOptions = {
918
910
  method,
@@ -924,7 +916,7 @@ server.tool('pay_x402', 'Handle an x402 payment flow. Fetches a URL, and if the
924
916
  if (!reqHeaders['Content-Type'])
925
917
  reqHeaders['Content-Type'] = 'application/json';
926
918
  }
927
- const initialRes = await fetch(url, reqOptions);
919
+ const initialRes = await safeFetch(url, reqOptions);
928
920
  // If not 402, return the response as-is (no payment needed)
929
921
  if (initialRes.status !== 402) {
930
922
  const text = await initialRes.text();
@@ -1058,7 +1050,7 @@ server.tool('pay_x402', 'Handle an x402 payment flow. Fetches a URL, and if the
1058
1050
  if (reqBody && method !== 'GET') {
1059
1051
  retryOptions.body = reqBody;
1060
1052
  }
1061
- const retryRes = await fetch(url, retryOptions);
1053
+ const retryRes = await safeFetch(url, retryOptions);
1062
1054
  const retryText = await retryRes.text();
1063
1055
  let retryParsed;
1064
1056
  try {
@@ -0,0 +1,38 @@
1
+ /**
2
+ * SSRF guard for outbound x402 requests.
3
+ *
4
+ * The previous guard compared `new URL(url).hostname` against literal strings
5
+ * and dotted-decimal prefixes. That misses every alternate spelling of a
6
+ * private destination: IPv4-mapped IPv6 (`[::ffff:127.0.0.1]` parses to
7
+ * `[::ffff:7f00:1]`), the rest of 127.0.0.0/8, IPv6 unique-local and
8
+ * link-local, the whole 169.254.0.0/16 link-local range rather than only the
9
+ * metadata address, and 100.64.0.0/10.
10
+ *
11
+ * This module canonicalizes the host into actual IP addresses and tests them
12
+ * against numeric ranges, then resolves DNS names and applies the same test to
13
+ * every answer, so a public name pointing at a private address is refused too.
14
+ *
15
+ * Reported privately by ARC Security Research, 2026-07-31.
16
+ */
17
+ /**
18
+ * Reduce a host to the IP address it actually denotes.
19
+ * Strips IPv6 brackets and unwraps IPv4-mapped and IPv4-compatible IPv6.
20
+ */
21
+ export declare function canonicalizeHost(hostRaw: string): {
22
+ ip: string | null;
23
+ family: 0 | 4 | 6;
24
+ };
25
+ /** True when the address is loopback, private, link-local, multicast or reserved. */
26
+ export declare function isPrivateAddress(ipRaw: string): boolean;
27
+ /**
28
+ * Throw unless `url` is an https URL pointing at a public destination.
29
+ * DNS names are resolved and every answer is checked, so a public name that
30
+ * maps to a private address is rejected.
31
+ */
32
+ export declare function assertPublicUrl(url: string): Promise<void>;
33
+ /**
34
+ * fetch() that validates the target, and every redirect hop, against
35
+ * assertPublicUrl. Redirects are followed manually because the automatic
36
+ * follower would happily land on a private address after a public first hop.
37
+ */
38
+ export declare function safeFetch(url: string, options?: RequestInit, maxHops?: number): Promise<Response>;
@@ -0,0 +1,178 @@
1
+ /**
2
+ * SSRF guard for outbound x402 requests.
3
+ *
4
+ * The previous guard compared `new URL(url).hostname` against literal strings
5
+ * and dotted-decimal prefixes. That misses every alternate spelling of a
6
+ * private destination: IPv4-mapped IPv6 (`[::ffff:127.0.0.1]` parses to
7
+ * `[::ffff:7f00:1]`), the rest of 127.0.0.0/8, IPv6 unique-local and
8
+ * link-local, the whole 169.254.0.0/16 link-local range rather than only the
9
+ * metadata address, and 100.64.0.0/10.
10
+ *
11
+ * This module canonicalizes the host into actual IP addresses and tests them
12
+ * against numeric ranges, then resolves DNS names and applies the same test to
13
+ * every answer, so a public name pointing at a private address is refused too.
14
+ *
15
+ * Reported privately by ARC Security Research, 2026-07-31.
16
+ */
17
+ import { isIP } from 'node:net';
18
+ import { lookup } from 'node:dns/promises';
19
+ /** Hostnames that must never be reached regardless of what they resolve to. */
20
+ const BLOCKED_NAMES = new Set([
21
+ 'localhost',
22
+ 'metadata.google.internal',
23
+ 'metadata.goog',
24
+ 'instance-data',
25
+ ]);
26
+ function ipv4ToInt(ip) {
27
+ const parts = ip.split('.');
28
+ if (parts.length !== 4)
29
+ return null;
30
+ let n = 0;
31
+ for (const p of parts) {
32
+ const v = Number(p);
33
+ if (!Number.isInteger(v) || v < 0 || v > 255)
34
+ return null;
35
+ n = (n << 8 >>> 0) + v;
36
+ }
37
+ return n >>> 0;
38
+ }
39
+ function inV4Range(ip, cidr) {
40
+ const [base, bitsRaw] = cidr.split('/');
41
+ const bits = Number(bitsRaw);
42
+ const a = ipv4ToInt(ip);
43
+ const b = ipv4ToInt(base);
44
+ if (a === null || b === null)
45
+ return false;
46
+ if (bits === 0)
47
+ return true;
48
+ const mask = (0xffffffff << (32 - bits)) >>> 0;
49
+ return (a & mask) === (b & mask);
50
+ }
51
+ /** Ranges that are never a legitimate x402 payment endpoint. */
52
+ const V4_BLOCKED = [
53
+ '0.0.0.0/8', // this network / unspecified
54
+ '10.0.0.0/8', // private
55
+ '100.64.0.0/10', // carrier-grade NAT
56
+ '127.0.0.0/8', // loopback, all of it
57
+ '169.254.0.0/16', // link-local, includes cloud metadata
58
+ '172.16.0.0/12', // private
59
+ '192.0.0.0/24', // IETF protocol assignments
60
+ '192.168.0.0/16', // private
61
+ '198.18.0.0/15', // benchmarking
62
+ '224.0.0.0/4', // multicast
63
+ '240.0.0.0/4', // reserved, includes 255.255.255.255
64
+ ];
65
+ /**
66
+ * Reduce a host to the IP address it actually denotes.
67
+ * Strips IPv6 brackets and unwraps IPv4-mapped and IPv4-compatible IPv6.
68
+ */
69
+ export function canonicalizeHost(hostRaw) {
70
+ let host = hostRaw.trim().toLowerCase();
71
+ if (host.startsWith('[') && host.endsWith(']'))
72
+ host = host.slice(1, -1);
73
+ const zone = host.indexOf('%'); // fe80::1%eth0
74
+ if (zone !== -1)
75
+ host = host.slice(0, zone);
76
+ const fam = isIP(host);
77
+ if (fam === 0)
78
+ return { ip: null, family: 0 };
79
+ if (fam === 4)
80
+ return { ip: host, family: 4 };
81
+ // IPv6: unwrap ::ffff:a.b.c.d and its hex spelling ::ffff:7f00:1
82
+ const mapped = host.match(/^::ffff:(\d+\.\d+\.\d+\.\d+)$/);
83
+ if (mapped)
84
+ return { ip: mapped[1], family: 4 };
85
+ const hexMapped = host.match(/^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/);
86
+ if (hexMapped) {
87
+ const hi = parseInt(hexMapped[1], 16);
88
+ const lo = parseInt(hexMapped[2], 16);
89
+ return { ip: [(hi >> 8) & 255, hi & 255, (lo >> 8) & 255, lo & 255].join('.'), family: 4 };
90
+ }
91
+ return { ip: host, family: 6 };
92
+ }
93
+ /** True when the address is loopback, private, link-local, multicast or reserved. */
94
+ export function isPrivateAddress(ipRaw) {
95
+ const { ip, family } = canonicalizeHost(ipRaw);
96
+ if (!ip)
97
+ return false;
98
+ if (family === 4)
99
+ return V4_BLOCKED.some(c => inV4Range(ip, c));
100
+ const v6 = ip;
101
+ if (v6 === '::' || v6 === '::1')
102
+ return true; // unspecified, loopback
103
+ if (/^f[cd][0-9a-f]{2}:/.test(v6))
104
+ return true; // fc00::/7 unique-local
105
+ if (/^fe[89ab][0-9a-f]:/.test(v6))
106
+ return true; // fe80::/10 link-local
107
+ if (/^ff[0-9a-f]{2}:/.test(v6))
108
+ return true; // ff00::/8 multicast
109
+ if (/^(64:ff9b|2002):/.test(v6))
110
+ return true; // NAT64 / 6to4 translation
111
+ return false;
112
+ }
113
+ /**
114
+ * Throw unless `url` is an https URL pointing at a public destination.
115
+ * DNS names are resolved and every answer is checked, so a public name that
116
+ * maps to a private address is rejected.
117
+ */
118
+ export async function assertPublicUrl(url) {
119
+ let u;
120
+ try {
121
+ u = new URL(url);
122
+ }
123
+ catch {
124
+ throw new Error('Invalid URL.');
125
+ }
126
+ if (u.protocol !== 'https:') {
127
+ throw new Error('Only HTTPS URLs are supported for x402 payments.');
128
+ }
129
+ const hostRaw = u.hostname.toLowerCase();
130
+ const bare = hostRaw.startsWith('[') && hostRaw.endsWith(']') ? hostRaw.slice(1, -1) : hostRaw;
131
+ if (BLOCKED_NAMES.has(bare)) {
132
+ throw new Error('URL points to a private/internal address. Only public URLs are allowed.');
133
+ }
134
+ const { ip, family } = canonicalizeHost(hostRaw);
135
+ if (family !== 0) {
136
+ if (isPrivateAddress(ip)) {
137
+ throw new Error('URL points to a private/internal address. Only public URLs are allowed.');
138
+ }
139
+ return;
140
+ }
141
+ // A name: every address it resolves to must be public.
142
+ let answers;
143
+ try {
144
+ answers = await lookup(bare, { all: true });
145
+ }
146
+ catch {
147
+ throw new Error(`Could not resolve host "${bare}".`);
148
+ }
149
+ if (!answers.length)
150
+ throw new Error(`Could not resolve host "${bare}".`);
151
+ for (const a of answers) {
152
+ if (isPrivateAddress(a.address)) {
153
+ throw new Error('URL resolves to a private/internal address. Only public URLs are allowed.');
154
+ }
155
+ }
156
+ }
157
+ /**
158
+ * fetch() that validates the target, and every redirect hop, against
159
+ * assertPublicUrl. Redirects are followed manually because the automatic
160
+ * follower would happily land on a private address after a public first hop.
161
+ */
162
+ export async function safeFetch(url, options = {}, maxHops = 3) {
163
+ let current = url;
164
+ for (let hop = 0; hop <= maxHops; hop++) {
165
+ await assertPublicUrl(current);
166
+ const res = await fetch(current, { ...options, redirect: 'manual' });
167
+ const isRedirect = res.status >= 300 && res.status < 400 && res.headers.has('location');
168
+ if (!isRedirect)
169
+ return res;
170
+ const next = new URL(res.headers.get('location'), current).toString();
171
+ // A redirected request must not replay the body or method blindly.
172
+ if (res.status === 303 || ((res.status === 301 || res.status === 302) && options.method && options.method !== 'GET')) {
173
+ options = { ...options, method: 'GET', body: undefined };
174
+ }
175
+ current = next;
176
+ }
177
+ throw new Error('Too many redirects while contacting the x402 endpoint.');
178
+ }
package/package.json CHANGED
@@ -1,68 +1,68 @@
1
- {
2
- "name": "agentwallet-mcp",
3
- "mcpName": "io.github.hifriendbot/agentwallet",
4
- "version": "1.10.0",
5
- "description": "MCP server for AgentWallet: self-custody local signing on EVM and Solana, or hosted wallets. Sign, broadcast, pay x402 invoices, and accept x402 payments.",
6
- "type": "module",
7
- "main": "build/index.js",
8
- "bin": {
9
- "agentwallet-mcp": "build/index.js"
10
- },
11
- "files": [
12
- "build",
13
- "README.md",
14
- "LICENSE"
15
- ],
16
- "scripts": {
17
- "build": "tsc",
18
- "start": "node build/index.js",
19
- "test": "node test/x402-payment.test.mjs && node test/local-mode.test.mjs",
20
- "prepublishOnly": "npm run build"
21
- },
22
- "keywords": [
23
- "mcp",
24
- "model-context-protocol",
25
- "wallet",
26
- "ai-agent",
27
- "blockchain",
28
- "evm",
29
- "ethereum",
30
- "base",
31
- "arbitrum",
32
- "optimism",
33
- "polygon",
34
- "avalanche",
35
- "bsc",
36
- "zora",
37
- "pulsechain",
38
- "solana",
39
- "spl-token",
40
- "erc20",
41
- "token-transfer",
42
- "transaction-signing",
43
- "x402",
44
- "http-402",
45
- "agent-payments",
46
- "agentwallet",
47
- "self-custody",
48
- "non-custodial",
49
- "local-signing"
50
- ],
51
- "author": "HiFriendbot (https://hifriendbot.com)",
52
- "license": "MIT",
53
- "homepage": "https://hifriendbot.com/wallet/",
54
- "engines": {
55
- "node": ">=18.0.0"
56
- },
57
- "dependencies": {
58
- "@modelcontextprotocol/sdk": "^1.11.4",
59
- "@solana/web3.js": "^1.98.4",
60
- "bs58": "^6.0.0",
61
- "viem": "^2.55.10",
62
- "zod": "^3.24.0"
63
- },
64
- "devDependencies": {
65
- "@types/node": "^20.0.0",
66
- "typescript": "^5.3.0"
67
- }
68
- }
1
+ {
2
+ "name": "agentwallet-mcp",
3
+ "mcpName": "io.github.hifriendbot/agentwallet",
4
+ "version": "1.10.1",
5
+ "description": "MCP server for AgentWallet: self-custody local signing on EVM and Solana, or hosted wallets. Sign, broadcast, pay x402 invoices, and accept x402 payments.",
6
+ "type": "module",
7
+ "main": "build/index.js",
8
+ "bin": {
9
+ "agentwallet-mcp": "build/index.js"
10
+ },
11
+ "files": [
12
+ "build",
13
+ "README.md",
14
+ "LICENSE"
15
+ ],
16
+ "scripts": {
17
+ "build": "tsc",
18
+ "start": "node build/index.js",
19
+ "test": "node test/x402-payment.test.mjs && node test/local-mode.test.mjs && node test/ssrf-guard.test.mjs",
20
+ "prepublishOnly": "npm run build"
21
+ },
22
+ "keywords": [
23
+ "mcp",
24
+ "model-context-protocol",
25
+ "wallet",
26
+ "ai-agent",
27
+ "blockchain",
28
+ "evm",
29
+ "ethereum",
30
+ "base",
31
+ "arbitrum",
32
+ "optimism",
33
+ "polygon",
34
+ "avalanche",
35
+ "bsc",
36
+ "zora",
37
+ "pulsechain",
38
+ "solana",
39
+ "spl-token",
40
+ "erc20",
41
+ "token-transfer",
42
+ "transaction-signing",
43
+ "x402",
44
+ "http-402",
45
+ "agent-payments",
46
+ "agentwallet",
47
+ "self-custody",
48
+ "non-custodial",
49
+ "local-signing"
50
+ ],
51
+ "author": "HiFriendbot (https://hifriendbot.com)",
52
+ "license": "MIT",
53
+ "homepage": "https://hifriendbot.com/wallet/",
54
+ "engines": {
55
+ "node": ">=18.0.0"
56
+ },
57
+ "dependencies": {
58
+ "@modelcontextprotocol/sdk": "^1.11.4",
59
+ "@solana/web3.js": "^1.98.4",
60
+ "bs58": "^6.0.0",
61
+ "viem": "^2.55.10",
62
+ "zod": "^3.24.0"
63
+ },
64
+ "devDependencies": {
65
+ "@types/node": "^20.0.0",
66
+ "typescript": "^5.3.0"
67
+ }
68
+ }