x402-proxy 0.4.1 → 0.5.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/CHANGELOG.md +153 -0
- package/README.md +2 -2
- package/dist/bin/cli.js +52 -25
- package/package.json +3 -1
- package/skills/SKILL.md +144 -0
- package/skills/references/library.md +85 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.5.0] - 2026-03-16
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- MCP proxy graduated from alpha to stable - removed alpha warning and label
|
|
14
|
+
- MCP proxy uses low-level `Server` class instead of `McpServer` to proxy raw JSON schemas verbatim without Zod conversion
|
|
15
|
+
- MCP proxy now forwards blob resource contents (previously only text was proxied)
|
|
16
|
+
- MCP content type widened to pass through all MCP content types (image, audio, resource_link) not just text
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
- MCP proxy forwards `notifications/tools/list_changed` and `notifications/resources/list_changed` from remote servers so local clients stay in sync with dynamic tool/resource updates
|
|
20
|
+
|
|
21
|
+
## [0.4.2] - 2026-03-16
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
- Twitter endpoint URLs updated from `/user/` to `/users/` to match spec change
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
- CHANGELOG.md included in npm package metadata
|
|
28
|
+
- `skills/` directory with SKILL.md and library reference included in npm package
|
|
29
|
+
|
|
30
|
+
## [0.4.1] - 2026-03-13
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
- Extracted `createNetworkFilter`, `createNetworkPreference`, `networkToCaipPrefix` as exported functions for testability
|
|
34
|
+
- Publish workflow auto-creates GitHub releases from CHANGELOG.md (no more manual `gh release create`)
|
|
35
|
+
- Release docs updated in CLAUDE.md with `act` dry-run instructions for CI workflows
|
|
36
|
+
|
|
37
|
+
### Added
|
|
38
|
+
- Tests for network filter, network preference selector, Solana address derivation, and wallet resolution (14 new tests)
|
|
39
|
+
|
|
40
|
+
## [0.4.0] - 2026-03-13
|
|
41
|
+
|
|
42
|
+
### Added
|
|
43
|
+
- `--network` flag for `fetch` and `mcp` commands - hard filter that requires a specific network (base, solana, or CAIP-2 ID), fails with clear error if unavailable
|
|
44
|
+
- Human-readable network names in payment output ("Base", "Solana" instead of "eip155:8453")
|
|
45
|
+
- `displayNetwork()` exported from library for mapping CAIP-2 IDs to display names
|
|
46
|
+
|
|
47
|
+
### Fixed
|
|
48
|
+
- Wildcard scheme registration (`eip155:*`, `solana:*`) via SDK helpers - payment signing now works for any EVM chain a server requests, not just Base
|
|
49
|
+
- Solana address derivation for `--solana-key` flag and `X402_PROXY_WALLET_SOLANA_KEY` env var - balance detection, wallet display, and history recording were broken without it
|
|
50
|
+
- MCP command now auto-detects preferred network based on USDC balance (same fix previously applied to `fetch`)
|
|
51
|
+
- MCP payment history records now include `amount`, `to`, and correct `network` (removed fragile type cast)
|
|
52
|
+
- Removed debug prefix stripping from payment amounts in handler
|
|
53
|
+
- USDC balance display now shows 4 decimal places (was 2)
|
|
54
|
+
|
|
55
|
+
## [0.3.2] - 2026-03-13
|
|
56
|
+
|
|
57
|
+
### Added
|
|
58
|
+
- Auto-setup: running `npx x402-proxy <url>` without a wallet launches the setup wizard, then continues with the request
|
|
59
|
+
- 402 error handling parses the endpoint's `PAYMENT-REQUIRED` header to show actual accepted networks and costs
|
|
60
|
+
- CI pipeline (GitHub Actions: check, build, test on push/PR)
|
|
61
|
+
- Automated npm publishing with OIDC provenance on tag push
|
|
62
|
+
- Tests for wallet derivation and transaction history (25 tests)
|
|
63
|
+
- Funding hint in `wallet` when USDC balance is zero
|
|
64
|
+
|
|
65
|
+
### Changed
|
|
66
|
+
- Version injected at build time from package.json (no more stale hardcoded strings)
|
|
67
|
+
- `wallet fund` command removed (addresses and hint shown in `wallet` directly)
|
|
68
|
+
- All command references use `$ npx x402-proxy` format
|
|
69
|
+
|
|
70
|
+
## [0.3.1] - 2026-03-12
|
|
71
|
+
|
|
72
|
+
### Added
|
|
73
|
+
- `wallet export-key mnemonic` - export BIP-39 mnemonic to stdout (pipe-safe, with confirmation prompt)
|
|
74
|
+
|
|
75
|
+
## [0.3.0] - 2026-03-12
|
|
76
|
+
|
|
77
|
+
### Added
|
|
78
|
+
- Live wallet balances in `status` and `wallet` commands (USDC + ETH/SOL via RPC)
|
|
79
|
+
- Recent transactions shown in `status` (last 5) and `wallet` (last 10)
|
|
80
|
+
- Network preference (`defaultNetwork` config) - prefers configured chain when endpoint accepts multiple
|
|
81
|
+
- Spend limits (`spendLimitDaily`, `spendLimitPerTx` config) enforced via x402Client policy
|
|
82
|
+
- `--verbose` flag on `wallet` command to show transaction IDs
|
|
83
|
+
- Confirmation prompt on `wallet export-key` when stdout is a terminal
|
|
84
|
+
- Help text with usage examples for `fetch --help` and `mcp --help`
|
|
85
|
+
- Full command reference, "try:" suggestion, and repo link in no-args output
|
|
86
|
+
- Network indicator on transaction lines (base/sol)
|
|
87
|
+
- Setup outro with "try your first request" using real endpoint
|
|
88
|
+
|
|
89
|
+
### Changed
|
|
90
|
+
- "EVM" label replaced with "Base" throughout (wallet, status, setup)
|
|
91
|
+
- Config directory displayed as `~/.config/...` instead of absolute path
|
|
92
|
+
- Error messages prefixed with `✗`, success with `✓` for accessibility
|
|
93
|
+
- No-args output redesigned: identity header, wallet summary, commands, try suggestion
|
|
94
|
+
- Example URLs use path-based format (`/user/cascade_fyi`) to avoid zsh glob issues
|
|
95
|
+
- `@solana/kit`, `ethers`, `viem` moved from peerDependencies to dependencies (fixes npx ERESOLVE warnings)
|
|
96
|
+
- Wallet subcommand hints shown at bottom of `wallet` output
|
|
97
|
+
|
|
98
|
+
### Fixed
|
|
99
|
+
- RPC balance failures show "(network error)" instead of silent omission
|
|
100
|
+
|
|
101
|
+
## [0.2.1] - 2026-03-12
|
|
102
|
+
|
|
103
|
+
### Changed
|
|
104
|
+
- Package description and keywords aligned with "curl for x402 paid APIs" positioning
|
|
105
|
+
- README rewritten: real endpoint in Quick Start, MCP Proxy elevated above HTTP Requests
|
|
106
|
+
- Stricli commands use explicit generic types (fixes TS 5.9 type inference)
|
|
107
|
+
- `displayStatus()` extracted as callable function from status command
|
|
108
|
+
- `PaymentRequirements.amount` used instead of removed `maxAmountRequired`
|
|
109
|
+
|
|
110
|
+
### Fixed
|
|
111
|
+
- All `tsc --noEmit` type errors resolved (previously passing only at build time)
|
|
112
|
+
- Biome schema version bumped to match CLI 2.4.6
|
|
113
|
+
|
|
114
|
+
## [0.2.0] - 2026-03-12
|
|
115
|
+
|
|
116
|
+
### Added
|
|
117
|
+
- CLI binary accessible via `npx x402-proxy`
|
|
118
|
+
- `fetch` command (default) - curl-like HTTP client with automatic x402 payment
|
|
119
|
+
- `mcp` command - MCP stdio proxy with auto-payment for AI agents
|
|
120
|
+
- `setup` command - interactive onboarding wizard with @clack/prompts
|
|
121
|
+
- `status` command - config, wallet, and spend summary
|
|
122
|
+
- `wallet` subcommand with `info`, `history`, `fund`, `export-key`
|
|
123
|
+
- BIP-39 mnemonic wallet derivation (Solana SLIP-10 + EVM BIP-32 from single seed)
|
|
124
|
+
- XDG-compliant config storage (`~/.config/x402-proxy/`)
|
|
125
|
+
- Wallet resolution cascade: flags > env vars > mnemonic env > wallet.json
|
|
126
|
+
- JSONL payment history with auto-truncation
|
|
127
|
+
- Env var overrides with `X402_PROXY_WALLET_*` prefix
|
|
128
|
+
|
|
129
|
+
### Changed
|
|
130
|
+
- Package now ships both CLI binary and library
|
|
131
|
+
- Dual tsdown build entries (bin/cli with shebang + index with dts)
|
|
132
|
+
|
|
133
|
+
## [0.1.0] - 2026-03-10
|
|
134
|
+
|
|
135
|
+
### Added
|
|
136
|
+
- Initial release (library only)
|
|
137
|
+
- `createX402ProxyHandler` - wraps fetch with automatic x402 payment
|
|
138
|
+
- `extractTxSignature` - extracts TX signature from payment response headers
|
|
139
|
+
- `loadSvmWallet` / `loadEvmWallet` - wallet loading utilities
|
|
140
|
+
- `appendHistory` / `readHistory` / `calcSpend` - JSONL transaction history
|
|
141
|
+
- Re-exports from `@x402/fetch`, `@x402/svm`, `@x402/evm`
|
|
142
|
+
|
|
143
|
+
[Unreleased]: https://github.com/cascade-protocol/x402-proxy/compare/v0.5.0...HEAD
|
|
144
|
+
[0.5.0]: https://github.com/cascade-protocol/x402-proxy/compare/v0.4.2...v0.5.0
|
|
145
|
+
[0.4.2]: https://github.com/cascade-protocol/x402-proxy/compare/v0.4.1...v0.4.2
|
|
146
|
+
[0.4.1]: https://github.com/cascade-protocol/x402-proxy/compare/v0.4.0...v0.4.1
|
|
147
|
+
[0.4.0]: https://github.com/cascade-protocol/x402-proxy/compare/v0.3.2...v0.4.0
|
|
148
|
+
[0.3.2]: https://github.com/cascade-protocol/x402-proxy/compare/v0.3.1...v0.3.2
|
|
149
|
+
[0.3.1]: https://github.com/cascade-protocol/x402-proxy/compare/v0.3.0...v0.3.1
|
|
150
|
+
[0.3.0]: https://github.com/cascade-protocol/x402-proxy/compare/v0.2.1...v0.3.0
|
|
151
|
+
[0.2.1]: https://github.com/cascade-protocol/x402-proxy/compare/v0.2.0...v0.2.1
|
|
152
|
+
[0.2.0]: https://github.com/cascade-protocol/x402-proxy/compare/v0.1.0...v0.2.0
|
|
153
|
+
[0.1.0]: https://github.com/cascade-protocol/x402-proxy/releases/tag/v0.1.0
|
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
## Quick Start
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npx x402-proxy https://twitter.surf.cascade.fyi/
|
|
8
|
+
npx x402-proxy https://twitter.surf.cascade.fyi/users/cascade_fyi
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
That's it. The endpoint returns 402, x402-proxy pays and streams the response.
|
|
@@ -38,7 +38,7 @@ Works like curl. Response body streams to stdout, payment info goes to stderr.
|
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
40
|
# GET request
|
|
41
|
-
$ npx x402-proxy https://twitter.surf.cascade.fyi/
|
|
41
|
+
$ npx x402-proxy https://twitter.surf.cascade.fyi/users/cascade_fyi
|
|
42
42
|
|
|
43
43
|
# POST with body and headers
|
|
44
44
|
$ npx x402-proxy --method POST \
|
package/dist/bin/cli.js
CHANGED
|
@@ -330,7 +330,7 @@ Examples:
|
|
|
330
330
|
//#region src/commands/mcp.ts
|
|
331
331
|
const mcpCommand = buildCommand({
|
|
332
332
|
docs: {
|
|
333
|
-
brief: "Start MCP stdio proxy with x402 payment
|
|
333
|
+
brief: "Start MCP stdio proxy with x402 payment",
|
|
334
334
|
fullDescription: `Start an MCP stdio proxy with automatic x402 payment for AI agents.
|
|
335
335
|
|
|
336
336
|
Add to your MCP client config (Claude, Cursor, etc.):
|
|
@@ -376,7 +376,6 @@ Add to your MCP client config (Claude, Cursor, etc.):
|
|
|
376
376
|
error("No wallet configured.\nRun:\n $ npx x402-proxy setup\n\nOr set X402_PROXY_WALLET_MNEMONIC");
|
|
377
377
|
process.exit(1);
|
|
378
378
|
}
|
|
379
|
-
warn("Note: MCP proxy is alpha - please report issues.");
|
|
380
379
|
dim(`x402-proxy MCP proxy -> ${remoteUrl}`);
|
|
381
380
|
if (wallet.evmAddress) dim(` EVM: ${wallet.evmAddress}`);
|
|
382
381
|
if (wallet.solanaAddress) dim(` Solana: ${wallet.solanaAddress}`);
|
|
@@ -399,13 +398,14 @@ Add to your MCP client config (Claude, Cursor, etc.):
|
|
|
399
398
|
const { Client } = await import("@modelcontextprotocol/sdk/client/index.js");
|
|
400
399
|
const { SSEClientTransport } = await import("@modelcontextprotocol/sdk/client/sse.js");
|
|
401
400
|
const { StreamableHTTPClientTransport } = await import("@modelcontextprotocol/sdk/client/streamableHttp.js");
|
|
402
|
-
const {
|
|
401
|
+
const { Server } = await import("@modelcontextprotocol/sdk/server/index.js");
|
|
403
402
|
const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
|
|
404
403
|
const { x402MCPClient } = await import("@x402/mcp");
|
|
405
|
-
const
|
|
404
|
+
const remoteClient = new Client({
|
|
406
405
|
name: "x402-proxy",
|
|
407
|
-
version: "0.
|
|
408
|
-
})
|
|
406
|
+
version: "0.5.0"
|
|
407
|
+
});
|
|
408
|
+
const x402Mcp = new x402MCPClient(remoteClient, x402PaymentClient, {
|
|
409
409
|
autoPayment: true,
|
|
410
410
|
onPaymentRequested: (ctx) => {
|
|
411
411
|
const accept = ctx.paymentRequired.accepts?.[0];
|
|
@@ -447,31 +447,58 @@ Add to your MCP client config (Claude, Cursor, etc.):
|
|
|
447
447
|
error(`Failed to connect to ${remoteUrl}: ${err instanceof Error ? err.message : String(err)}`);
|
|
448
448
|
process.exit(1);
|
|
449
449
|
}
|
|
450
|
-
|
|
450
|
+
let { tools } = await x402Mcp.listTools();
|
|
451
451
|
dim(` ${tools.length} tools available`);
|
|
452
|
-
|
|
452
|
+
let remoteResources = [];
|
|
453
|
+
try {
|
|
454
|
+
remoteResources = (await x402Mcp.listResources()).resources;
|
|
455
|
+
if (remoteResources.length > 0) dim(` ${remoteResources.length} resources available`);
|
|
456
|
+
} catch {
|
|
457
|
+
dim(" Resources not available from remote");
|
|
458
|
+
}
|
|
459
|
+
const localServer = new Server({
|
|
453
460
|
name: "x402-proxy",
|
|
454
|
-
version: "0.
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
|
|
461
|
+
version: "0.5.0"
|
|
462
|
+
}, { capabilities: {
|
|
463
|
+
tools: tools.length > 0 ? {} : void 0,
|
|
464
|
+
resources: remoteResources.length > 0 ? {} : void 0
|
|
465
|
+
} });
|
|
466
|
+
const { ListToolsRequestSchema, CallToolRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ToolListChangedNotificationSchema, ResourceListChangedNotificationSchema } = await import("@modelcontextprotocol/sdk/types.js");
|
|
467
|
+
localServer.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: tools.map((t) => ({
|
|
468
|
+
name: t.name,
|
|
469
|
+
description: t.description,
|
|
470
|
+
inputSchema: t.inputSchema,
|
|
471
|
+
annotations: t.annotations
|
|
472
|
+
})) }));
|
|
473
|
+
localServer.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
474
|
+
const { name, arguments: args } = request.params;
|
|
475
|
+
const result = await x402Mcp.callTool(name, args ?? {});
|
|
458
476
|
return {
|
|
459
477
|
content: result.content,
|
|
460
478
|
isError: result.isError
|
|
461
479
|
};
|
|
462
480
|
});
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
481
|
+
if (remoteResources.length > 0) {
|
|
482
|
+
localServer.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: remoteResources.map((r) => ({
|
|
483
|
+
name: r.name,
|
|
484
|
+
uri: r.uri,
|
|
485
|
+
description: r.description,
|
|
486
|
+
mimeType: r.mimeType
|
|
487
|
+
})) }));
|
|
488
|
+
localServer.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
489
|
+
return { contents: (await x402Mcp.readResource({ uri: request.params.uri })).contents.map((c) => ({ ...c })) };
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
remoteClient.setNotificationHandler(ToolListChangedNotificationSchema, async () => {
|
|
493
|
+
tools = (await x402Mcp.listTools()).tools;
|
|
494
|
+
dim(` Tools updated: ${tools.length} available`);
|
|
495
|
+
await localServer.notification({ method: "notifications/tools/list_changed" });
|
|
496
|
+
});
|
|
497
|
+
if (remoteResources.length > 0) remoteClient.setNotificationHandler(ResourceListChangedNotificationSchema, async () => {
|
|
498
|
+
remoteResources = (await x402Mcp.listResources()).resources;
|
|
499
|
+
dim(` Resources updated: ${remoteResources.length} available`);
|
|
500
|
+
await localServer.notification({ method: "notifications/resources/list_changed" });
|
|
501
|
+
});
|
|
475
502
|
const stdioTransport = new StdioServerTransport();
|
|
476
503
|
await localServer.connect(stdioTransport);
|
|
477
504
|
dim(" MCP proxy running (stdio)");
|
|
@@ -608,7 +635,7 @@ const routes = buildRouteMap({
|
|
|
608
635
|
});
|
|
609
636
|
const app = buildApplication(routes, {
|
|
610
637
|
name: "x402-proxy",
|
|
611
|
-
versionInfo: { currentVersion: "0.
|
|
638
|
+
versionInfo: { currentVersion: "0.5.0" },
|
|
612
639
|
scanner: { caseStyle: "allow-kebab-for-camel" }
|
|
613
640
|
});
|
|
614
641
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "x402-proxy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "curl for x402 paid APIs. Auto-pays any endpoint on Base and Solana.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -47,6 +47,8 @@
|
|
|
47
47
|
"files": [
|
|
48
48
|
"dist/**",
|
|
49
49
|
"README.md",
|
|
50
|
+
"CHANGELOG.md",
|
|
51
|
+
"skills/**",
|
|
50
52
|
"LICENSE"
|
|
51
53
|
],
|
|
52
54
|
"keywords": [
|
package/skills/SKILL.md
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: x402-proxy
|
|
3
|
+
description: Use x402-proxy CLI for consuming and debugging x402 paid APIs. Use this skill when testing x402 endpoints, configuring MCP payment proxies for AI agents, managing x402 wallets, or scripting paid HTTP requests. Triggers on x402-proxy, npx x402-proxy, x402 endpoint testing, paid API debugging, MCP payment proxy, x402 wallet management, or any mention of auto-paying HTTP 402 responses.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# x402-proxy
|
|
7
|
+
|
|
8
|
+
`curl` for x402 paid APIs. Auto-pays HTTP 402 responses with USDC on Base and Solana.
|
|
9
|
+
|
|
10
|
+
## Quick start
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npx x402-proxy https://twitter.surf.cascade.fyi/users/cascade_fyi
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
First run auto-creates a wallet. No setup needed.
|
|
17
|
+
|
|
18
|
+
## HTTP requests
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# GET - body to stdout, payment info to stderr
|
|
22
|
+
npx x402-proxy https://api.example.com/resource
|
|
23
|
+
|
|
24
|
+
# POST with body and headers
|
|
25
|
+
npx x402-proxy --method POST \
|
|
26
|
+
--header "Content-Type: application/json" \
|
|
27
|
+
--body '{"query":"example"}' \
|
|
28
|
+
https://api.example.com/search
|
|
29
|
+
|
|
30
|
+
# Force a specific chain
|
|
31
|
+
npx x402-proxy --network solana https://api.example.com/data
|
|
32
|
+
|
|
33
|
+
# Pipe-safe - only response body on stdout
|
|
34
|
+
npx x402-proxy https://api.example.com/data | jq '.results'
|
|
35
|
+
|
|
36
|
+
# Save response to file
|
|
37
|
+
npx x402-proxy https://api.example.com/data > response.json
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Commands
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
x402-proxy <url> # paid HTTP request (default)
|
|
44
|
+
x402-proxy mcp <url> # MCP stdio proxy for AI agents
|
|
45
|
+
x402-proxy setup # wallet onboarding wizard
|
|
46
|
+
x402-proxy setup --force # re-run setup (overwrite existing wallet)
|
|
47
|
+
x402-proxy status # config + wallet + daily spend summary
|
|
48
|
+
x402-proxy wallet # show addresses and USDC balances
|
|
49
|
+
x402-proxy wallet history # payment log
|
|
50
|
+
x402-proxy wallet history --limit 5 # last 5 payments
|
|
51
|
+
x402-proxy wallet history --json # machine-readable output
|
|
52
|
+
x402-proxy wallet export-key evm # bare EVM private key to stdout
|
|
53
|
+
x402-proxy wallet export-key solana # bare Solana private key to stdout
|
|
54
|
+
x402-proxy wallet export-key mnemonic # bare mnemonic to stdout
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Fetch flags
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
--method, -X <METHOD> HTTP method (default: GET)
|
|
61
|
+
--header, -H <KEY:VALUE> Add request header (repeatable)
|
|
62
|
+
--body, -d <DATA> Request body (string or @file)
|
|
63
|
+
--network <base|solana> Force payment on this chain
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## MCP proxy for AI agents
|
|
67
|
+
|
|
68
|
+
Drop into Claude, Cursor, or any MCP client config:
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"mcpServers": {
|
|
73
|
+
"paid-service": {
|
|
74
|
+
"command": "npx",
|
|
75
|
+
"args": ["x402-proxy", "mcp", "https://mcp.example.com/sse"],
|
|
76
|
+
"env": {
|
|
77
|
+
"X402_PROXY_WALLET_MNEMONIC": "your 24 words here"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The proxy intercepts 402 responses, pays automatically, forwards the result. Supports StreamableHTTP and SSE.
|
|
85
|
+
|
|
86
|
+
## Wallet & env vars
|
|
87
|
+
|
|
88
|
+
One BIP-39 mnemonic derives both Solana and EVM keypairs. Auto-detects which chain based on USDC balance.
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
X402_PROXY_WALLET_MNEMONIC # BIP-39 mnemonic (derives both chains)
|
|
92
|
+
X402_PROXY_WALLET_EVM_KEY # EVM private key (hex, 0x optional)
|
|
93
|
+
X402_PROXY_WALLET_SOLANA_KEY # Solana private key (base58 or JSON array)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Resolution: flags > env vars > mnemonic env > `~/.config/x402-proxy/wallet.json`
|
|
97
|
+
|
|
98
|
+
Pipe-safe export for scripting:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
MY_KEY=$(npx x402-proxy wallet export-key evm)
|
|
102
|
+
MY_MNEMONIC=$(npx x402-proxy wallet export-key mnemonic)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Config
|
|
106
|
+
|
|
107
|
+
Lives at `~/.config/x402-proxy/` (or `$XDG_CONFIG_HOME/x402-proxy/`):
|
|
108
|
+
|
|
109
|
+
```yaml
|
|
110
|
+
# config.yaml
|
|
111
|
+
defaultNetwork: base # or "solana"
|
|
112
|
+
spendLimitDaily: 10 # USDC daily cap
|
|
113
|
+
spendLimitPerTx: 1 # USDC per-request cap
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Also supports JSONC and JSON config files. Wallet stored in `wallet.json` (mode 0600), payments logged to `history.jsonl`.
|
|
117
|
+
|
|
118
|
+
## Testing & debugging x402 services
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
# Smoke test an endpoint
|
|
122
|
+
npx x402-proxy https://your-service.com/paid-route
|
|
123
|
+
|
|
124
|
+
# Test both chains
|
|
125
|
+
npx x402-proxy --network base https://your-service.com/route
|
|
126
|
+
npx x402-proxy --network solana https://your-service.com/route
|
|
127
|
+
|
|
128
|
+
# Batch test
|
|
129
|
+
for route in /users/test /tweets/search /v1/crawl; do
|
|
130
|
+
echo "--- $route ---"
|
|
131
|
+
npx x402-proxy "https://your-service.com$route" 2>/dev/null | head -c 200
|
|
132
|
+
echo
|
|
133
|
+
done
|
|
134
|
+
|
|
135
|
+
# Check what you spent
|
|
136
|
+
npx x402-proxy wallet history --limit 5
|
|
137
|
+
npx x402-proxy status
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
stdout = response body, stderr = payment info. Pipes, redirects, and `jq` all work cleanly.
|
|
141
|
+
|
|
142
|
+
## Library API
|
|
143
|
+
|
|
144
|
+
For programmatic use in Node.js apps, read `references/library.md`.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# x402-proxy Library API
|
|
2
|
+
|
|
3
|
+
```bash
|
|
4
|
+
npm install x402-proxy
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
## Exports
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
// Core
|
|
11
|
+
import { createX402ProxyHandler, extractTxSignature } from "x402-proxy";
|
|
12
|
+
import { x402Client } from "x402-proxy";
|
|
13
|
+
|
|
14
|
+
// Chain schemes
|
|
15
|
+
import { ExactEvmScheme, toClientEvmSigner } from "x402-proxy";
|
|
16
|
+
import { ExactSvmScheme } from "x402-proxy";
|
|
17
|
+
|
|
18
|
+
// Wallet loaders
|
|
19
|
+
import { loadEvmWallet, loadSvmWallet } from "x402-proxy";
|
|
20
|
+
|
|
21
|
+
// History
|
|
22
|
+
import { appendHistory, readHistory, calcSpend, explorerUrl, formatTxLine } from "x402-proxy";
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## EVM (Base) setup
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { createX402ProxyHandler, x402Client, ExactEvmScheme, toClientEvmSigner } from "x402-proxy";
|
|
29
|
+
import { createWalletClient, http } from "viem";
|
|
30
|
+
import { privateKeyToAccount } from "viem/accounts";
|
|
31
|
+
import { base } from "viem/chains";
|
|
32
|
+
|
|
33
|
+
const account = privateKeyToAccount(process.env.EVM_KEY as `0x${string}`);
|
|
34
|
+
const wallet = createWalletClient({ account, chain: base, transport: http() });
|
|
35
|
+
|
|
36
|
+
const client = x402Client(fetch)
|
|
37
|
+
.register(new ExactEvmScheme(toClientEvmSigner(wallet)));
|
|
38
|
+
|
|
39
|
+
const { x402Fetch, shiftPayment } = createX402ProxyHandler({ x402Client: client });
|
|
40
|
+
|
|
41
|
+
const res = await x402Fetch("https://api.example.com/data");
|
|
42
|
+
const body = await res.json();
|
|
43
|
+
const payment = shiftPayment(); // { network, payTo, amount, asset }
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Solana setup
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
import { createX402ProxyHandler, x402Client, ExactSvmScheme, loadSvmWallet } from "x402-proxy";
|
|
50
|
+
|
|
51
|
+
const signer = loadSvmWallet(process.env.MNEMONIC!);
|
|
52
|
+
const client = x402Client(fetch).register(new ExactSvmScheme(signer));
|
|
53
|
+
const { x402Fetch } = createX402ProxyHandler({ x402Client: client });
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Payment history
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
import { appendHistory, readHistory, calcSpend } from "x402-proxy";
|
|
60
|
+
|
|
61
|
+
// Read and summarize
|
|
62
|
+
const records = await readHistory("./history.jsonl");
|
|
63
|
+
const { dailyUsd, totalUsd, txCount } = calcSpend(records);
|
|
64
|
+
|
|
65
|
+
// Append a record
|
|
66
|
+
await appendHistory("./history.jsonl", {
|
|
67
|
+
ts: Date.now(),
|
|
68
|
+
status: 200,
|
|
69
|
+
network: "eip155:8453",
|
|
70
|
+
payTo: "0x...",
|
|
71
|
+
amount: "0.001",
|
|
72
|
+
asset: "USDC",
|
|
73
|
+
txId: "0xabc...",
|
|
74
|
+
host: "api.example.com",
|
|
75
|
+
});
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Types
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
type PaymentInfo = { network: string; payTo: string; amount: string; asset: string };
|
|
82
|
+
type X402ProxyOptions = { x402Client: ReturnType<typeof x402Client> };
|
|
83
|
+
type X402ProxyHandler = { x402Fetch: typeof fetch; shiftPayment: () => PaymentInfo | undefined };
|
|
84
|
+
type TxRecord = { ts: number; status: number; network: string; payTo: string; amount: string; asset: string; txId?: string; host: string };
|
|
85
|
+
```
|