x402-proxy 0.8.6 → 0.9.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/CHANGELOG.md CHANGED
@@ -7,6 +7,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.9.1] - 2026-03-26
11
+
12
+ ### Added
13
+ - `setup --non-interactive` flag - auto-generates wallet and outputs addresses as JSON to stdout (`{"base":"0x...","tempo":"0x...","solana":"..."}`)
14
+ - `setup --import-mnemonic` flag - import existing BIP-39 mnemonic non-interactively
15
+ - MCP proxy auto-generates wallet on first run when no wallet exists (no more "No wallet configured" error)
16
+ - OpenClaw integration example in README and SKILL.md: `openclaw mcp set surf '{"command":"npx","args":["-y","x402-proxy","mcp","https://surf.cascade.fyi/mcp"]}'`
17
+
18
+ ### Fixed
19
+ - `npx -y` flag added to all generated MCP configs (`mcp add` command and docs) - prevents npx install prompt from corrupting MCP stdio protocol
20
+ - MCP config examples no longer show `X402_PROXY_WALLET_MNEMONIC` env var as default - wallet file is the primary path, env vars are documented as fallback only
21
+ - All example MCP URLs updated to `https://surf.cascade.fyi/mcp`
22
+ - Non-interactive JSON output uses network names (`base`, `tempo`, `solana`) instead of generic `evm`
23
+
24
+ ## [0.9.0] - 2026-03-25
25
+
26
+ ### Added
27
+ - `mcp add` command - onboarding wizard to install MCP servers into Claude Code, Cursor, VS Code, and 16+ other AI clients via `@getmcp/generators`
28
+ - Auto-detects installed AI clients and highlights them in the selection list
29
+ - Shows config diff preview with green markers before writing
30
+ - Prompts to overwrite if server name already exists (shows current config)
31
+ - Wallet setup runs automatically if not yet configured
32
+ - Balance check and funding hints shown after successful install
33
+ - `-c` / `--config-dir` global flag to override config directory for all commands
34
+ - Custom config directory injected as `XDG_CONFIG_HOME` env var into generated MCP server configs
35
+ - Tempo address shown alongside Base address in setup wizard
36
+
37
+ ### Fixed
38
+ - Solana USDC balance shows `0` instead of `?` for fresh wallets (non-existent ATA means zero balance, not unknown)
39
+ - MPP payment protocol description corrected from "streaming micropayments" to "machine payments over HTTP 402"
40
+
10
41
  ## [0.8.6] - 2026-03-25
11
42
 
12
43
  ### Fixed
@@ -246,7 +277,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
246
277
  - `appendHistory` / `readHistory` / `calcSpend` - JSONL transaction history
247
278
  - Re-exports from `@x402/fetch`, `@x402/svm`, `@x402/evm`
248
279
 
249
- [Unreleased]: https://github.com/cascade-protocol/x402-proxy/compare/v0.8.4...HEAD
280
+ [Unreleased]: https://github.com/cascade-protocol/x402-proxy/compare/v0.9.1...HEAD
281
+ [0.9.1]: https://github.com/cascade-protocol/x402-proxy/compare/v0.9.0...v0.9.1
282
+ [0.9.0]: https://github.com/cascade-protocol/x402-proxy/compare/v0.8.6...v0.9.0
250
283
  [0.8.6]: https://github.com/cascade-protocol/x402-proxy/compare/v0.8.5...v0.8.6
251
284
  [0.8.5]: https://github.com/cascade-protocol/x402-proxy/compare/v0.8.4...v0.8.5
252
285
  [0.8.4]: https://github.com/cascade-protocol/x402-proxy/compare/v0.8.3...v0.8.4
package/README.md CHANGED
@@ -14,23 +14,38 @@ No wallet? It'll walk you through setup automatically. One mnemonic derives both
14
14
 
15
15
  ## MCP Proxy
16
16
 
17
- Let your AI agent consume any paid MCP server. Configure in Claude, Cursor, or any MCP client:
17
+ Let your AI agent consume any paid MCP server.
18
+
19
+ ### Quick setup
20
+
21
+ ```bash
22
+ npx x402-proxy mcp add surf https://surf.cascade.fyi/mcp
23
+ ```
24
+
25
+ Auto-detects installed AI clients (Claude Code, Cursor, VS Code, and 16+ others), shows a config preview, and writes it for you. Runs wallet setup if needed.
26
+
27
+ ### Manual config
28
+
29
+ Or add to your client config directly:
18
30
 
19
31
  ```json
20
32
  {
21
33
  "mcpServers": {
22
- "paid-service": {
34
+ "surf": {
23
35
  "command": "npx",
24
- "args": ["x402-proxy", "mcp", "https://mcp.example.com/sse"],
25
- "env": {
26
- "X402_PROXY_WALLET_MNEMONIC": "your 24 words here"
27
- }
36
+ "args": ["-y", "x402-proxy", "mcp", "https://surf.cascade.fyi/mcp"]
28
37
  }
29
38
  }
30
39
  }
31
40
  ```
32
41
 
33
- The proxy sits between your agent and the remote server, intercepting 402 responses, paying automatically, and forwarding the result. Supports both x402 and MPP protocols. Your agent never touches crypto.
42
+ The proxy auto-generates a wallet on first run and uses `~/.config/x402-proxy/wallet.json`. No env vars needed. Your agent never touches crypto.
43
+
44
+ For OpenClaw:
45
+
46
+ ```bash
47
+ openclaw mcp set surf '{"command":"npx","args":["-y","x402-proxy","mcp","https://surf.cascade.fyi/mcp"]}'
48
+ ```
34
49
 
35
50
  ## HTTP Requests
36
51
 
@@ -67,6 +82,7 @@ $ npx x402-proxy https://api.example.com/data | jq '.results'
67
82
  ```bash
68
83
  $ npx x402-proxy <url> # paid HTTP request (default command)
69
84
  $ npx x402-proxy mcp <url> # MCP stdio proxy for agents
85
+ $ npx x402-proxy mcp add <name> <url> # install MCP server into your AI client
70
86
  $ npx x402-proxy setup # onboarding wizard
71
87
  $ npx x402-proxy status # config + wallet + spend summary
72
88
  $ npx x402-proxy config # show current configuration
@@ -77,7 +93,7 @@ $ npx x402-proxy wallet history # payment history
77
93
  $ npx x402-proxy wallet export-key <target> # bare key/mnemonic to stdout (evm|solana|mnemonic)
78
94
  ```
79
95
 
80
- All commands support `--help` for details.
96
+ All commands support `--help` for details. Use `-c <dir>` to override the config directory.
81
97
 
82
98
  ## Wallet
83
99
 
@@ -97,7 +113,7 @@ $ MY_MNEMONIC=$(npx x402-proxy wallet export-key mnemonic)
97
113
 
98
114
  ## Env Vars
99
115
 
100
- Override wallet per-instance (useful for MCP configs):
116
+ Override wallet per-instance (fallback for environments where the wallet file isn't accessible):
101
117
 
102
118
  ```
103
119
  X402_PROXY_WALLET_MNEMONIC # BIP-39 mnemonic (derives both chains)
package/dist/bin/cli.js CHANGED
@@ -1,10 +1,13 @@
1
1
  #!/usr/bin/env node
2
- import { a as getHistoryPath, c as loadConfig, i as getConfigDirShort, l as loadWalletFile, s as isConfigured, u as saveConfig } from "../derive-BR6N1ZjI.js";
3
- import { _ as error, b as warn, c as resolveWallet, d as displayNetwork, f as formatAmount, g as dim, h as readHistory, l as appendHistory, m as formatUsdcValue, o as walletInfoCommand, p as formatTxLine, s as buildX402Client, u as calcSpend, v as info, y as isTTY } from "../wallet-BiX5_XFY.js";
4
- import { n as setupCommand } from "../setup-B1r-Opou.js";
5
- import { n as statusCommand } from "../status-DvSu8reM.js";
2
+ import { a as getHistoryPath, c as loadConfig, i as getConfigDirShort, l as loadWalletFile, s as isConfigured, u as saveConfig } from "../derive-CAYmX-Yz.js";
3
+ import { _ as error, b as warn, c as resolveWallet, d as displayNetwork, f as formatAmount, g as dim, h as readHistory, l as appendHistory, m as formatUsdcValue, n as fetchAllBalances, o as walletInfoCommand, p as formatTxLine, s as buildX402Client, u as calcSpend, v as info, y as isTTY } from "../wallet-DZsXptY7.js";
4
+ import { n as setupCommand, t as runSetup } from "../setup-B7YJa7s6.js";
5
+ import { n as statusCommand } from "../status-D3f5IVf6.js";
6
+ import { dirname, join, normalize, resolve } from "node:path";
6
7
  import { buildApplication, buildCommand, buildRouteMap, run } from "@stricli/core";
7
8
  import pc from "picocolors";
9
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
10
+ import { homedir } from "node:os";
8
11
  import { decodePaymentResponseHeader, wrapFetchWithPayment } from "@x402/fetch";
9
12
  import { base58 } from "@scure/base";
10
13
  import * as prompts from "@clack/prompts";
@@ -398,7 +401,7 @@ Examples:
398
401
  };
399
402
  if (!url) {
400
403
  if (isConfigured()) {
401
- const { displayStatus } = await import("../status-DjudJ6fD.js");
404
+ const { displayStatus } = await import("../status-BkURZYDA.js");
402
405
  await displayStatus();
403
406
  console.log();
404
407
  console.log(pc.dim(" Commands:"));
@@ -450,7 +453,7 @@ Examples:
450
453
  process.exit(1);
451
454
  }
452
455
  dim(" No wallet found. Let's set one up first.\n");
453
- const { runSetup } = await import("../setup-CH4woul8.js");
456
+ const { runSetup } = await import("../setup-xhtYsp7D.js");
454
457
  await runSetup();
455
458
  console.log();
456
459
  wallet = resolveWallet();
@@ -463,7 +466,7 @@ Examples:
463
466
  verbose(`protocol: ${resolvedProtocol ?? "auto-detect"}, maxDeposit: ${maxDeposit}`);
464
467
  let preferredNetwork = config?.defaultNetwork;
465
468
  if (!preferredNetwork && wallet.evmAddress && wallet.solanaAddress) {
466
- const { fetchAllBalances } = await import("../wallet-BkZ0DOJL.js");
469
+ const { fetchAllBalances } = await import("../wallet-BM0ngyqk.js");
467
470
  const balances = await fetchAllBalances(wallet.evmAddress, wallet.solanaAddress);
468
471
  const evmUsdc = balances.evm ? Number(balances.evm.usdc) : 0;
469
472
  const solUsdc = balances.sol ? Number(balances.sol.usdc) : 0;
@@ -630,7 +633,7 @@ Examples:
630
633
  const hasSolana = accepts.some((a) => a.network.startsWith("solana:"));
631
634
  const hasMpp = detected.mpp;
632
635
  const hasOther = accepts.some((a) => !a.network.startsWith("eip155:") && !a.network.startsWith("solana:"));
633
- const { fetchAllBalances } = await import("../wallet-BkZ0DOJL.js");
636
+ const { fetchAllBalances } = await import("../wallet-BM0ngyqk.js");
634
637
  const balances = await fetchAllBalances(wallet.evmAddress, wallet.solanaAddress);
635
638
  const evmUsdc = hasEvm && balances.evm ? Number(balances.evm.usdc) : 0;
636
639
  const solUsdc = hasSolana && balances.sol ? Number(balances.sol.usdc) : 0;
@@ -761,8 +764,9 @@ const mcpCommand = buildCommand({
761
764
 
762
765
  Add to your MCP client config (Claude, Cursor, etc.):
763
766
  "command": "npx",
764
- "args": ["x402-proxy", "mcp", "https://mcp.example.com/sse"],
765
- "env": { "X402_PROXY_WALLET_MNEMONIC": "your 24 words" }`
767
+ "args": ["-y", "x402-proxy", "mcp", "https://surf.cascade.fyi/mcp"]
768
+
769
+ Wallet is auto-generated on first run. No env vars needed.`
766
770
  },
767
771
  parameters: {
768
772
  flags: {
@@ -805,8 +809,18 @@ Add to your MCP client config (Claude, Cursor, etc.):
805
809
  solanaKey: flags.solanaKey
806
810
  });
807
811
  if (wallet.source === "none") {
808
- error("No wallet configured.\nRun:\n $ npx x402-proxy setup\n\nOr set X402_PROXY_WALLET_MNEMONIC");
809
- process.exit(1);
812
+ dim("No wallet found. Auto-generating...");
813
+ const { runSetup } = await import("../setup-xhtYsp7D.js");
814
+ await runSetup({ nonInteractive: true });
815
+ const fresh = resolveWallet({
816
+ evmKey: flags.evmKey,
817
+ solanaKey: flags.solanaKey
818
+ });
819
+ if (fresh.source === "none") {
820
+ error("Wallet auto-setup failed. Run: $ npx x402-proxy setup");
821
+ process.exit(1);
822
+ }
823
+ Object.assign(wallet, fresh);
810
824
  }
811
825
  dim(`x402-proxy MCP proxy -> ${remoteUrl}`);
812
826
  if (wallet.evmAddress) dim(` EVM: ${wallet.evmAddress}`);
@@ -840,7 +854,7 @@ Add to your MCP client config (Claude, Cursor, etc.):
840
854
  async function startX402Proxy() {
841
855
  let preferredNetwork = config?.defaultNetwork;
842
856
  if (!preferredNetwork && wallet.evmAddress && wallet.solanaAddress) {
843
- const { fetchAllBalances } = await import("../wallet-BkZ0DOJL.js");
857
+ const { fetchAllBalances } = await import("../wallet-BM0ngyqk.js");
844
858
  const balances = await fetchAllBalances(wallet.evmAddress, wallet.solanaAddress);
845
859
  const evmUsdc = balances.evm ? Number(balances.evm.usdc) : 0;
846
860
  const solUsdc = balances.sol ? Number(balances.sol.usdc) : 0;
@@ -860,7 +874,7 @@ Add to your MCP client config (Claude, Cursor, etc.):
860
874
  }
861
875
  const remoteClient = new Client({
862
876
  name: "x402-proxy",
863
- version: "0.8.6"
877
+ version: "0.9.1"
864
878
  });
865
879
  const x402Mcp = new x402MCPClient(remoteClient, x402PaymentClient, {
866
880
  autoPayment: true,
@@ -898,7 +912,7 @@ Add to your MCP client config (Claude, Cursor, etc.):
898
912
  }
899
913
  const localServer = new Server({
900
914
  name: "x402-proxy",
901
- version: "0.8.6"
915
+ version: "0.9.1"
902
916
  }, { capabilities: {
903
917
  tools: tools.length > 0 ? {} : void 0,
904
918
  resources: remoteResources.length > 0 ? {} : void 0
@@ -993,7 +1007,7 @@ Add to your MCP client config (Claude, Cursor, etc.):
993
1007
  }));
994
1008
  const remoteClient = new Client({
995
1009
  name: "x402-proxy",
996
- version: "0.8.6"
1010
+ version: "0.9.1"
997
1011
  });
998
1012
  await connectTransport(remoteClient);
999
1013
  const mppClient = McpClient.wrap(remoteClient, { methods: wrappedMethods });
@@ -1008,7 +1022,7 @@ Add to your MCP client config (Claude, Cursor, etc.):
1008
1022
  }
1009
1023
  const localServer = new Server({
1010
1024
  name: "x402-proxy",
1011
- version: "0.8.6"
1025
+ version: "0.9.1"
1012
1026
  }, { capabilities: {
1013
1027
  tools: tools.length > 0 ? {} : void 0,
1014
1028
  resources: remoteResources.length > 0 ? {} : void 0
@@ -1085,6 +1099,190 @@ Add to your MCP client config (Claude, Cursor, etc.):
1085
1099
  }
1086
1100
  });
1087
1101
 
1102
+ //#endregion
1103
+ //#region src/commands/mcp-add.ts
1104
+ function resolvePlatformPath(raw) {
1105
+ let p = raw;
1106
+ if (p.startsWith("~/")) p = join(homedir(), p.slice(2));
1107
+ p = p.replace(/%UserProfile%/gi, homedir());
1108
+ p = p.replace(/%AppData%/gi, process.env.APPDATA ?? join(homedir(), "AppData", "Roaming"));
1109
+ p = p.replace(/%LocalAppData%/gi, process.env.LOCALAPPDATA ?? join(homedir(), "AppData", "Local"));
1110
+ return normalize(p);
1111
+ }
1112
+ function parseConfigFile(path, format) {
1113
+ if (!existsSync(path)) return {};
1114
+ const raw = readFileSync(path, "utf-8").trim();
1115
+ if (!raw) return {};
1116
+ if (format === "json" || format === "jsonc") {
1117
+ const stripped = format === "jsonc" ? raw.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "") : raw;
1118
+ return JSON.parse(stripped);
1119
+ }
1120
+ return JSON.parse(raw);
1121
+ }
1122
+ const mcpAddCommand = buildCommand({
1123
+ docs: {
1124
+ brief: "Add an MCP server to your AI client",
1125
+ fullDescription: "Add a remote MCP server to Claude Code, Cursor, VS Code, or other AI clients with automatic x402 payment proxy."
1126
+ },
1127
+ parameters: {
1128
+ flags: {
1129
+ client: {
1130
+ kind: "parsed",
1131
+ brief: "Target client (claude-code, cursor, vscode, etc.)",
1132
+ parse: String,
1133
+ optional: true
1134
+ },
1135
+ yes: {
1136
+ kind: "boolean",
1137
+ brief: "Skip confirmation prompt",
1138
+ default: false
1139
+ }
1140
+ },
1141
+ positional: {
1142
+ kind: "tuple",
1143
+ parameters: [{
1144
+ brief: "Server name",
1145
+ parse: String
1146
+ }, {
1147
+ brief: "Remote MCP server URL",
1148
+ parse: String
1149
+ }]
1150
+ }
1151
+ },
1152
+ async func(flags, name, url) {
1153
+ prompts.intro(pc.cyan("Add MCP server"));
1154
+ try {
1155
+ new URL(url);
1156
+ } catch {
1157
+ prompts.log.error(`Invalid URL: ${url}`);
1158
+ prompts.cancel("Aborted.");
1159
+ process.exit(1);
1160
+ }
1161
+ const serverName = name;
1162
+ if (!isConfigured()) {
1163
+ prompts.log.warn("No wallet configured. Let's set one up first.\n");
1164
+ await runSetup();
1165
+ console.log();
1166
+ prompts.log.step(pc.cyan("Continuing MCP setup..."));
1167
+ }
1168
+ const { generators, getAppIds, generateConfig, deepMerge } = await import("@getmcp/generators");
1169
+ let clientId;
1170
+ if (flags.client) {
1171
+ const appIds = getAppIds();
1172
+ if (!appIds.includes(flags.client)) {
1173
+ prompts.log.error(`Unknown client: ${flags.client}`);
1174
+ prompts.log.info(`Supported: ${appIds.join(", ")}`);
1175
+ prompts.cancel("Aborted.");
1176
+ process.exit(1);
1177
+ }
1178
+ clientId = flags.client;
1179
+ } else {
1180
+ const appIds = getAppIds();
1181
+ const detected = appIds.filter((id) => generators[id].detectInstalled());
1182
+ const selected = await prompts.select({
1183
+ message: "Where would you like to install the MCP server?",
1184
+ options: appIds.map((id) => ({
1185
+ value: id,
1186
+ label: `${generators[id].app.name}${detected.includes(id) ? pc.dim(" (detected)") : ""}`
1187
+ })),
1188
+ initialValue: detected.includes("claude-code") ? "claude-code" : detected[0] ?? "claude-code"
1189
+ });
1190
+ if (prompts.isCancel(selected)) {
1191
+ prompts.cancel("Cancelled.");
1192
+ process.exit(0);
1193
+ }
1194
+ clientId = selected;
1195
+ }
1196
+ const generator = generators[clientId];
1197
+ const globalPaths = generator.app.globalConfigPaths;
1198
+ const platform = process.platform;
1199
+ const rawPath = globalPaths?.[platform];
1200
+ if (!rawPath) {
1201
+ prompts.log.error(`No global config path for ${generator.app.name} on ${platform}`);
1202
+ prompts.cancel("Aborted.");
1203
+ process.exit(1);
1204
+ }
1205
+ const configPath = resolvePlatformPath(rawPath);
1206
+ const configFormat = generator.app.configFormat;
1207
+ const configDirOverride = process.env.X402_PROXY_CONFIG_DIR_OVERRIDE;
1208
+ const generated = generateConfig(clientId, serverName, {
1209
+ command: "npx",
1210
+ args: [
1211
+ "-y",
1212
+ "x402-proxy",
1213
+ "mcp",
1214
+ url
1215
+ ],
1216
+ env: configDirOverride ? { XDG_CONFIG_HOME: configDirOverride } : {},
1217
+ transport: "stdio"
1218
+ });
1219
+ let existing;
1220
+ if (configFormat === "yaml") {
1221
+ const { default: YAML } = await import("yaml");
1222
+ if (existsSync(configPath)) {
1223
+ const raw = readFileSync(configPath, "utf-8").trim();
1224
+ existing = raw ? YAML.parse(raw) ?? {} : {};
1225
+ } else existing = {};
1226
+ } else existing = parseConfigFile(configPath, configFormat);
1227
+ const rootKey = Object.keys(generated)[0] ?? "mcpServers";
1228
+ const existingServers = existing[rootKey] ?? {};
1229
+ if (existingServers[serverName]) {
1230
+ prompts.log.warn(`Server ${pc.bold(serverName)} already exists in config`);
1231
+ prompts.log.message(pc.dim(JSON.stringify({ [serverName]: existingServers[serverName] }, null, 2)));
1232
+ const overwrite = await prompts.confirm({ message: "Overwrite?" });
1233
+ if (prompts.isCancel(overwrite) || !overwrite) {
1234
+ prompts.cancel("Cancelled.");
1235
+ process.exit(0);
1236
+ }
1237
+ }
1238
+ prompts.log.info(`Config will be added to ${pc.dim(configPath)}`);
1239
+ const previewLines = generator.serialize(generated).split("\n");
1240
+ const formatted = previewLines.map((line, i) => {
1241
+ if (i === 0 || i === previewLines.length - 1) return line;
1242
+ const trimmed = line.trimStart();
1243
+ if (trimmed.startsWith(`"${rootKey}"`) || trimmed.startsWith(`${rootKey}:`)) return line;
1244
+ return `${pc.green("+")} ${line}`;
1245
+ }).join("\n");
1246
+ prompts.log.message(formatted);
1247
+ if (!flags.yes) {
1248
+ const proceed = await prompts.confirm({ message: "Would you like to proceed?" });
1249
+ if (prompts.isCancel(proceed) || !proceed) {
1250
+ prompts.cancel("Cancelled.");
1251
+ process.exit(0);
1252
+ }
1253
+ }
1254
+ const merged = deepMerge(existing, generated);
1255
+ const dir = dirname(configPath);
1256
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
1257
+ const serialized = generator.serialize(merged);
1258
+ writeFileSync(configPath, serialized.endsWith("\n") ? serialized : `${serialized}\n`);
1259
+ prompts.log.success(`Added ${pc.bold(serverName)} MCP to ${pc.bold(generator.app.name)}`);
1260
+ const wallet = resolveWallet();
1261
+ if (wallet.source !== "none") {
1262
+ const balances = await fetchAllBalances(wallet.evmAddress, wallet.solanaAddress);
1263
+ const evmUsdc = balances.evm ? Number(balances.evm.usdc) : 0;
1264
+ const solUsdc = balances.sol ? Number(balances.sol.usdc) : 0;
1265
+ const tempoUsdc = balances.tempo ? Number(balances.tempo.usdc) : 0;
1266
+ if (evmUsdc === 0 && solUsdc === 0 && tempoUsdc === 0) {
1267
+ prompts.log.warn("Balance: 0 USDC");
1268
+ prompts.log.info("To use paid MCP tools, send USDC to your wallet:");
1269
+ if (wallet.evmAddress) prompts.log.message(` Base: ${pc.cyan(wallet.evmAddress)}`);
1270
+ if (wallet.solanaAddress) prompts.log.message(` Solana: ${pc.cyan(wallet.solanaAddress)}`);
1271
+ } else {
1272
+ const parts = [];
1273
+ if (balances.evm) parts.push(`Base: ${balances.evm.usdc} USDC`);
1274
+ if (balances.sol) parts.push(`Solana: ${balances.sol.usdc} USDC`);
1275
+ if (balances.tempo) parts.push(`Tempo: ${balances.tempo.usdc} USDC`);
1276
+ prompts.log.success(`Balance: ${parts.join(" | ")}`);
1277
+ }
1278
+ }
1279
+ prompts.log.step("Try your first request:");
1280
+ prompts.log.message(` ${pc.cyan("$ npx x402-proxy https://twitter.surf.cascade.fyi/users/cascade_fyi")}`);
1281
+ prompts.log.message(` ${pc.dim("Run")} ${pc.cyan("npx x402-proxy")} ${pc.dim("to see your wallet and balance")}`);
1282
+ prompts.outro(pc.green(`MCP server ${pc.bold(serverName)} is ready to use!`));
1283
+ }
1284
+ });
1285
+
1088
1286
  //#endregion
1089
1287
  //#region src/commands/wallet-export.ts
1090
1288
  const walletExportCommand = buildCommand({
@@ -1190,28 +1388,37 @@ const walletHistoryCommand = buildCommand({
1190
1388
 
1191
1389
  //#endregion
1192
1390
  //#region src/app.ts
1391
+ const walletRoutes = buildRouteMap({
1392
+ routes: {
1393
+ info: walletInfoCommand,
1394
+ history: walletHistoryCommand,
1395
+ "export-key": walletExportCommand
1396
+ },
1397
+ defaultCommand: "info",
1398
+ docs: { brief: "Wallet management" }
1399
+ });
1400
+ const configRoutes = buildRouteMap({
1401
+ routes: {
1402
+ show: configShowCommand,
1403
+ set: configSetCommand,
1404
+ unset: configUnsetCommand
1405
+ },
1406
+ defaultCommand: "show",
1407
+ docs: { brief: "Manage configuration" }
1408
+ });
1193
1409
  const routes = buildRouteMap({
1194
1410
  routes: {
1195
1411
  fetch: fetchCommand,
1196
- mcp: mcpCommand,
1197
- wallet: buildRouteMap({
1412
+ mcp: buildRouteMap({
1198
1413
  routes: {
1199
- info: walletInfoCommand,
1200
- history: walletHistoryCommand,
1201
- "export-key": walletExportCommand
1414
+ proxy: mcpCommand,
1415
+ add: mcpAddCommand
1202
1416
  },
1203
- defaultCommand: "info",
1204
- docs: { brief: "Wallet management" }
1205
- }),
1206
- config: buildRouteMap({
1207
- routes: {
1208
- show: configShowCommand,
1209
- set: configSetCommand,
1210
- unset: configUnsetCommand
1211
- },
1212
- defaultCommand: "show",
1213
- docs: { brief: "Manage configuration" }
1417
+ defaultCommand: "proxy",
1418
+ docs: { brief: "MCP proxy and management" }
1214
1419
  }),
1420
+ wallet: walletRoutes,
1421
+ config: configRoutes,
1215
1422
  setup: setupCommand,
1216
1423
  status: statusCommand
1217
1424
  },
@@ -1220,7 +1427,7 @@ const routes = buildRouteMap({
1220
1427
  });
1221
1428
  const app = buildApplication(routes, {
1222
1429
  name: "x402-proxy",
1223
- versionInfo: { currentVersion: "0.8.6" },
1430
+ versionInfo: { currentVersion: "0.9.1" },
1224
1431
  scanner: { caseStyle: "allow-kebab-for-camel" }
1225
1432
  });
1226
1433
 
@@ -1232,7 +1439,23 @@ function buildContext(process) {
1232
1439
 
1233
1440
  //#endregion
1234
1441
  //#region src/bin/cli.ts
1235
- await run(app, process.argv.slice(2).map((a) => a === "-H" ? "--header" : a), buildContext(process));
1442
+ process.on("SIGINT", () => process.exit(130));
1443
+ const rawArgs = process.argv.slice(2);
1444
+ const args = [];
1445
+ for (let i = 0; i < rawArgs.length; i++) {
1446
+ const a = rawArgs[i];
1447
+ if (a === "-H") args.push("--header");
1448
+ else if ((a === "--config-dir" || a === "-c") && i + 1 < rawArgs.length) {
1449
+ const dir = resolve(rawArgs[++i]);
1450
+ process.env.XDG_CONFIG_HOME = dir;
1451
+ process.env.X402_PROXY_CONFIG_DIR_OVERRIDE = dir;
1452
+ } else if (a.startsWith("--config-dir=")) {
1453
+ const dir = resolve(a.slice(13));
1454
+ process.env.XDG_CONFIG_HOME = dir;
1455
+ process.env.X402_PROXY_CONFIG_DIR_OVERRIDE = dir;
1456
+ } else args.push(a);
1457
+ }
1458
+ await run(app, args, buildContext(process));
1236
1459
 
1237
1460
  //#endregion
1238
1461
  export { };
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
+ import path from "node:path";
2
3
  import fs from "node:fs";
3
4
  import os from "node:os";
4
- import path from "node:path";
5
5
  import { parse, stringify } from "yaml";
6
6
  import { ed25519 } from "@noble/curves/ed25519.js";
7
7
  import { base58 } from "@scure/base";
@@ -726,7 +726,7 @@ function createWalletCommand(ctx) {
726
726
  try {
727
727
  const snap = await getWalletSnapshot(ctx.rpcUrl, walletAddress, ctx.historyPath);
728
728
  const solscanUrl = `https://solscan.io/account/${walletAddress}`;
729
- const lines = [`x402-proxy v0.8.6`];
729
+ const lines = [`x402-proxy v0.9.1`];
730
730
  const defaultModel = ctx.allModels[0];
731
731
  if (defaultModel) lines.push("", `**Model** - ${defaultModel.name} (${defaultModel.provider})`);
732
732
  lines.push("", `**[Wallet](${solscanUrl})**`, `\`${walletAddress}\``);
@@ -1,15 +1,55 @@
1
1
  #!/usr/bin/env node
2
- import { d as saveWalletFile, i as getConfigDirShort, n as deriveSolanaKeypair, o as getWalletPath, r as generateMnemonic, s as isConfigured, t as deriveEvmKeypair, u as saveConfig } from "./derive-BR6N1ZjI.js";
2
+ import { d as saveWalletFile, i as getConfigDirShort, l as loadWalletFile, n as deriveSolanaKeypair, o as getWalletPath, r as generateMnemonic, s as isConfigured, t as deriveEvmKeypair, u as saveConfig } from "./derive-CAYmX-Yz.js";
3
3
  import { buildCommand } from "@stricli/core";
4
4
  import pc from "picocolors";
5
5
  import * as prompts from "@clack/prompts";
6
6
 
7
7
  //#region src/commands/setup.ts
8
8
  async function runSetup(opts) {
9
+ const nonInteractive = opts?.nonInteractive ?? false;
10
+ if (nonInteractive && isConfigured() && !opts?.force) {
11
+ const walletFile = loadWalletFile();
12
+ if (walletFile) {
13
+ process.stdout.write(`${JSON.stringify({
14
+ base: walletFile.addresses.evm,
15
+ tempo: walletFile.addresses.evm,
16
+ solana: walletFile.addresses.solana
17
+ })}\n`);
18
+ return;
19
+ }
20
+ }
9
21
  if (isConfigured() && !opts?.force) {
10
22
  prompts.log.warn(`Already configured. Wallet at ${pc.dim(getWalletPath())}\nTo reconfigure, run:\n ${pc.cyan("$ npx x402-proxy setup --force")}`);
11
23
  return;
12
24
  }
25
+ let mnemonic;
26
+ if (nonInteractive) {
27
+ if (opts?.importMnemonic) {
28
+ const words = opts.importMnemonic.trim().split(/\s+/);
29
+ if (words.length !== 12 && words.length !== 24) {
30
+ process.stderr.write("Error: mnemonic must be 12 or 24 words\n");
31
+ process.exit(1);
32
+ }
33
+ mnemonic = opts.importMnemonic.trim();
34
+ } else mnemonic = generateMnemonic();
35
+ const evm = deriveEvmKeypair(mnemonic);
36
+ const sol = deriveSolanaKeypair(mnemonic);
37
+ saveWalletFile({
38
+ version: 1,
39
+ mnemonic,
40
+ addresses: {
41
+ evm: evm.address,
42
+ solana: sol.address
43
+ }
44
+ });
45
+ saveConfig({ preferredProtocol: "x402" });
46
+ process.stdout.write(`${JSON.stringify({
47
+ base: evm.address,
48
+ tempo: evm.address,
49
+ solana: sol.address
50
+ })}\n`);
51
+ return;
52
+ }
13
53
  prompts.intro(pc.cyan("x402-proxy setup"));
14
54
  prompts.log.info("This will generate a single BIP-39 mnemonic that derives wallets for both Solana and EVM chains.");
15
55
  const action = await prompts.select({
@@ -26,7 +66,6 @@ async function runSetup(opts) {
26
66
  prompts.cancel("Setup cancelled.");
27
67
  process.exit(0);
28
68
  }
29
- let mnemonic;
30
69
  if (action === "generate") {
31
70
  mnemonic = generateMnemonic();
32
71
  prompts.log.warn("Write down your mnemonic and store it safely. It will NOT be shown again.");
@@ -48,6 +87,7 @@ async function runSetup(opts) {
48
87
  const evm = deriveEvmKeypair(mnemonic);
49
88
  const sol = deriveSolanaKeypair(mnemonic);
50
89
  prompts.log.success(`Base address: ${pc.green(evm.address)}`);
90
+ prompts.log.success(`Tempo address: ${pc.green(evm.address)}`);
51
91
  prompts.log.success(`Solana address: ${pc.green(sol.address)}`);
52
92
  saveWalletFile({
53
93
  version: 1,
@@ -64,7 +104,7 @@ async function runSetup(opts) {
64
104
  label: "x402 - on-chain payments (Base, Solana)"
65
105
  }, {
66
106
  value: "mpp",
67
- label: "MPP - streaming micropayments (Tempo)"
107
+ label: "MPP - machine payments over HTTP 402 (Tempo)"
68
108
  }]
69
109
  });
70
110
  if (prompts.isCancel(protocol)) {
@@ -108,20 +148,49 @@ async function runSetup(opts) {
108
148
  prompts.outro(pc.green("Setup complete!"));
109
149
  }
110
150
  const setupCommand = buildCommand({
111
- docs: { brief: "Set up x402-proxy with a new wallet" },
151
+ docs: {
152
+ brief: "Set up x402-proxy wallet (generate new or import existing mnemonic)",
153
+ fullDescription: `Set up x402-proxy wallet interactively, or use --non-interactive for automated environments.
154
+
155
+ Non-interactive mode auto-generates a wallet and outputs JSON to stdout:
156
+ $ npx x402-proxy setup --non-interactive
157
+ {"base":"0x...","tempo":"0x...","solana":"..."}
158
+
159
+ Import an existing mnemonic non-interactively:
160
+ $ npx x402-proxy setup --non-interactive --import-mnemonic "word1 word2 ... word24"
161
+
162
+ If a wallet already exists, --non-interactive outputs the existing addresses.`
163
+ },
112
164
  parameters: {
113
- flags: { force: {
114
- kind: "boolean",
115
- brief: "Overwrite existing configuration",
116
- default: false
117
- } },
165
+ flags: {
166
+ force: {
167
+ kind: "boolean",
168
+ brief: "Overwrite existing configuration",
169
+ default: false
170
+ },
171
+ nonInteractive: {
172
+ kind: "boolean",
173
+ brief: "Auto-generate wallet and output addresses as JSON (no prompts)",
174
+ default: false
175
+ },
176
+ importMnemonic: {
177
+ kind: "parsed",
178
+ brief: "Import a BIP-39 mnemonic (use with --non-interactive)",
179
+ parse: String,
180
+ optional: true
181
+ }
182
+ },
118
183
  positional: {
119
184
  kind: "tuple",
120
185
  parameters: []
121
186
  }
122
187
  },
123
188
  async func(flags) {
124
- await runSetup({ force: flags.force });
189
+ await runSetup({
190
+ force: flags.force,
191
+ nonInteractive: flags.nonInteractive,
192
+ importMnemonic: flags.importMnemonic
193
+ });
125
194
  }
126
195
  });
127
196
 
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { n as setupCommand, t as runSetup } from "./setup-B7YJa7s6.js";
3
+
4
+ export { runSetup };
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import { n as statusCommand, t as displayStatus } from "./status-DvSu8reM.js";
2
+ import { n as statusCommand, t as displayStatus } from "./status-D3f5IVf6.js";
3
3
 
4
4
  export { displayStatus };
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { a as getHistoryPath, c as loadConfig, i as getConfigDirShort } from "./derive-BR6N1ZjI.js";
3
- import { c as resolveWallet, f as formatAmount, g as dim, h as readHistory, m as formatUsdcValue, n as fetchAllBalances, p as formatTxLine, t as balanceLine, u as calcSpend } from "./wallet-BiX5_XFY.js";
2
+ import { a as getHistoryPath, c as loadConfig, i as getConfigDirShort } from "./derive-CAYmX-Yz.js";
3
+ import { c as resolveWallet, f as formatAmount, g as dim, h as readHistory, m as formatUsdcValue, n as fetchAllBalances, p as formatTxLine, t as balanceLine, u as calcSpend } from "./wallet-DZsXptY7.js";
4
4
  import { buildCommand } from "@stricli/core";
5
5
  import pc from "picocolors";
6
6
 
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import { a as fetchTempoBalances, i as fetchSolanaBalances, n as fetchAllBalances, o as walletInfoCommand, r as fetchEvmBalances, t as balanceLine } from "./wallet-BiX5_XFY.js";
2
+ import { a as fetchTempoBalances, i as fetchSolanaBalances, n as fetchAllBalances, o as walletInfoCommand, r as fetchEvmBalances, t as balanceLine } from "./wallet-DZsXptY7.js";
3
3
 
4
4
  export { fetchAllBalances };
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import { a as getHistoryPath, l as loadWalletFile, n as deriveSolanaKeypair, t as deriveEvmKeypair } from "./derive-BR6N1ZjI.js";
2
+ import { a as getHistoryPath, l as loadWalletFile, n as deriveSolanaKeypair, t as deriveEvmKeypair } from "./derive-CAYmX-Yz.js";
3
+ import { dirname } from "node:path";
3
4
  import { buildCommand } from "@stricli/core";
4
5
  import pc from "picocolors";
5
6
  import { appendFileSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
6
- import { dirname } from "node:path";
7
7
  import { x402Client } from "@x402/fetch";
8
8
  import { ed25519 } from "@noble/curves/ed25519.js";
9
9
  import { base58 } from "@scure/base";
@@ -374,7 +374,7 @@ async function fetchSolanaBalances(ownerAddress) {
374
374
  const usdcVal = usdcRes.result?.value;
375
375
  return {
376
376
  sol,
377
- usdc: usdcVal ? formatUsdcValue(Number(usdcVal.uiAmountString)) : usdcVal === void 0 ? "?" : "0"
377
+ usdc: usdcVal ? formatUsdcValue(Number(usdcVal.uiAmountString)) : "0"
378
378
  };
379
379
  }
380
380
  function balanceLine(usdc, native, nativeSymbol) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "x402-proxy",
3
- "version": "0.8.6",
3
+ "version": "0.9.1",
4
4
  "description": "curl for x402 paid APIs. Auto-pays any endpoint on Base, Solana, and Tempo. Also works as an OpenClaw plugin.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -27,21 +27,22 @@
27
27
  },
28
28
  "dependencies": {
29
29
  "@clack/prompts": "^1.1.0",
30
+ "@getmcp/generators": "^0.10.1",
30
31
  "@modelcontextprotocol/sdk": "^1.27.1",
31
32
  "@noble/curves": "^2.0.1",
32
33
  "@noble/hashes": "^2.0.1",
33
34
  "@scure/base": "^2.0.0",
34
35
  "@scure/bip32": "^2.0.1",
35
36
  "@scure/bip39": "^2.0.1",
36
- "@solana/kit": "^6.0.0",
37
37
  "@sinclair/typebox": "^0.34.48",
38
+ "@solana/kit": "^6.0.0",
38
39
  "@stricli/core": "^1.2.6",
39
40
  "@x402/evm": "^2.6.0",
40
41
  "@x402/fetch": "^2.6.0",
41
42
  "@x402/mcp": "^2.6.0",
42
- "mppx": "^0.4.9",
43
43
  "@x402/svm": "^2.6.0",
44
44
  "ethers": "^6.0.0",
45
+ "mppx": "^0.4.9",
45
46
  "picocolors": "^1.1.1",
46
47
  "viem": "^2.0.0",
47
48
  "yaml": "^2.8.2"
package/skills/SKILL.md CHANGED
@@ -42,6 +42,7 @@ npx x402-proxy https://api.example.com/data > response.json
42
42
  ```
43
43
  x402-proxy <url> # paid HTTP request (default)
44
44
  x402-proxy mcp <url> # MCP stdio proxy for AI agents
45
+ x402-proxy mcp add <name> <url> # install MCP server into AI client
45
46
  x402-proxy setup # wallet onboarding wizard
46
47
  x402-proxy setup --force # re-run setup (overwrite existing wallet)
47
48
  x402-proxy status # config + wallet + daily spend summary
@@ -70,23 +71,39 @@ x402-proxy wallet export-key mnemonic # bare mnemonic to stdout
70
71
 
71
72
  ## MCP proxy for AI agents
72
73
 
73
- Drop into Claude, Cursor, or any MCP client config:
74
+ Quick setup (auto-detects installed AI clients):
75
+
76
+ ```bash
77
+ x402-proxy mcp add surf https://surf.cascade.fyi/mcp
78
+ ```
79
+
80
+ Or drop into your client config manually:
74
81
 
75
82
  ```json
76
83
  {
77
84
  "mcpServers": {
78
- "paid-service": {
85
+ "surf": {
79
86
  "command": "npx",
80
- "args": ["x402-proxy", "mcp", "https://mcp.example.com/sse"],
81
- "env": {
82
- "X402_PROXY_WALLET_MNEMONIC": "your 24 words here"
83
- }
87
+ "args": ["-y", "x402-proxy", "mcp", "https://surf.cascade.fyi/mcp"]
84
88
  }
85
89
  }
86
90
  }
87
91
  ```
88
92
 
89
- The proxy intercepts 402 responses, pays automatically, forwards the result. Supports StreamableHTTP and SSE.
93
+ For OpenClaw:
94
+
95
+ ```bash
96
+ openclaw mcp set surf '{"command":"npx","args":["-y","x402-proxy","mcp","https://surf.cascade.fyi/mcp"]}'
97
+ ```
98
+
99
+ The wallet is auto-generated on first run and stored at `~/.config/x402-proxy/wallet.json`. No env vars needed. The proxy intercepts 402 responses, pays automatically, forwards the result. Supports StreamableHTTP and SSE.
100
+
101
+ For non-interactive setup (e.g. automated provisioning):
102
+
103
+ ```bash
104
+ npx x402-proxy setup --non-interactive
105
+ # outputs: {"evm":"0x...","solana":"..."}
106
+ ```
90
107
 
91
108
  ## Wallet & env vars
92
109
 
@@ -1,4 +0,0 @@
1
- #!/usr/bin/env node
2
- import { n as setupCommand, t as runSetup } from "./setup-B1r-Opou.js";
3
-
4
- export { runSetup };