x402-proxy 0.4.2 → 0.5.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,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.5.1] - 2026-03-19
11
+
12
+ ### Fixed
13
+ - Solana USDC balance showing 0.0000 instead of actual balance due to `getTokenAccountsByOwner` being rate-limited (429) on public Solana RPC
14
+ - Replaced with offline ATA derivation via `@solana/kit` + lightweight `getTokenAccountBalance` call that works reliably on any public RPC
15
+ - RPC errors now show `?` instead of silently displaying `0.0000`
16
+
17
+ ## [0.5.0] - 2026-03-16
18
+
19
+ ### Changed
20
+ - MCP proxy graduated from alpha to stable - removed alpha warning and label
21
+ - MCP proxy uses low-level `Server` class instead of `McpServer` to proxy raw JSON schemas verbatim without Zod conversion
22
+ - MCP proxy now forwards blob resource contents (previously only text was proxied)
23
+ - MCP content type widened to pass through all MCP content types (image, audio, resource_link) not just text
24
+
25
+ ### Added
26
+ - 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
27
+
10
28
  ## [0.4.2] - 2026-03-16
11
29
 
12
30
  ### Fixed
@@ -105,7 +123,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
105
123
  ### Added
106
124
  - CLI binary accessible via `npx x402-proxy`
107
125
  - `fetch` command (default) - curl-like HTTP client with automatic x402 payment
108
- - `mcp` command (alpha) - MCP stdio proxy with auto-payment for AI agents
126
+ - `mcp` command - MCP stdio proxy with auto-payment for AI agents
109
127
  - `setup` command - interactive onboarding wizard with @clack/prompts
110
128
  - `status` command - config, wallet, and spend summary
111
129
  - `wallet` subcommand with `info`, `history`, `fund`, `export-key`
@@ -129,7 +147,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
129
147
  - `appendHistory` / `readHistory` / `calcSpend` - JSONL transaction history
130
148
  - Re-exports from `@x402/fetch`, `@x402/svm`, `@x402/evm`
131
149
 
132
- [Unreleased]: https://github.com/cascade-protocol/x402-proxy/compare/v0.4.2...HEAD
150
+ [Unreleased]: https://github.com/cascade-protocol/x402-proxy/compare/v0.5.1...HEAD
151
+ [0.5.1]: https://github.com/cascade-protocol/x402-proxy/compare/v0.5.0...v0.5.1
152
+ [0.5.0]: https://github.com/cascade-protocol/x402-proxy/compare/v0.4.2...v0.5.0
133
153
  [0.4.2]: https://github.com/cascade-protocol/x402-proxy/compare/v0.4.1...v0.4.2
134
154
  [0.4.1]: https://github.com/cascade-protocol/x402-proxy/compare/v0.4.0...v0.4.1
135
155
  [0.4.0]: https://github.com/cascade-protocol/x402-proxy/compare/v0.3.2...v0.4.0
package/dist/bin/cli.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { a as buildX402Client, c as error, d as warn, f as appendHistory, g as readHistory, h as formatTxLine, i as walletInfoCommand, l as info, m as displayNetwork, o as resolveWallet, p as calcSpend, s as dim, u as isTTY } from "../wallet-BHywTzdN.js";
2
+ import { a as buildX402Client, c as error, d as warn, f as appendHistory, g as readHistory, h as formatTxLine, i as walletInfoCommand, l as info, m as displayNetwork, o as resolveWallet, p as calcSpend, s as dim, u as isTTY } from "../wallet-BMYYtAP6.js";
3
3
  import { c as isConfigured, i as ensureConfigDir, l as loadConfig, o as getHistoryPath, u as loadWalletFile } from "../derive-CISr_ond.js";
4
4
  import { n as setupCommand } from "../setup-gla-Qyqi.js";
5
- import { n as statusCommand } from "../status-DxW72Hx6.js";
5
+ import { n as statusCommand } from "../status-BoH_1kIH.js";
6
6
  import { buildApplication, buildCommand, buildRouteMap, run } from "@stricli/core";
7
7
  import pc from "picocolors";
8
8
  import { decodePaymentResponseHeader, wrapFetchWithPayment } from "@x402/fetch";
@@ -118,7 +118,7 @@ Examples:
118
118
  async func(flags, url) {
119
119
  if (!url) {
120
120
  if (isConfigured()) {
121
- const { displayStatus } = await import("../status-DrC0uxCS.js");
121
+ const { displayStatus } = await import("../status-BVIU3-b6.js");
122
122
  await displayStatus();
123
123
  console.log();
124
124
  console.log(pc.dim(" Commands:"));
@@ -179,7 +179,7 @@ Examples:
179
179
  const config = loadConfig();
180
180
  let preferredNetwork = config?.defaultNetwork;
181
181
  if (!preferredNetwork && wallet.evmAddress && wallet.solanaAddress) {
182
- const { fetchEvmBalances, fetchSolanaBalances } = await import("../wallet-DFhJWn3o.js");
182
+ const { fetchEvmBalances, fetchSolanaBalances } = await import("../wallet-BoY0fgX7.js");
183
183
  const [evmBal, solBal] = await Promise.allSettled([fetchEvmBalances(wallet.evmAddress), fetchSolanaBalances(wallet.solanaAddress)]);
184
184
  const evmUsdc = evmBal.status === "fulfilled" ? Number(evmBal.value?.usdc ?? 0) : 0;
185
185
  const solUsdc = solBal.status === "fulfilled" ? Number(solBal.value?.usdc ?? 0) : 0;
@@ -231,7 +231,7 @@ Examples:
231
231
  const hasEvm = accepts.some((a) => a.network.startsWith("eip155:"));
232
232
  const hasSolana = accepts.some((a) => a.network.startsWith("solana:"));
233
233
  const hasOther = accepts.some((a) => !a.network.startsWith("eip155:") && !a.network.startsWith("solana:"));
234
- const { fetchEvmBalances, fetchSolanaBalances } = await import("../wallet-DFhJWn3o.js");
234
+ const { fetchEvmBalances, fetchSolanaBalances } = await import("../wallet-BoY0fgX7.js");
235
235
  let evmUsdc = 0;
236
236
  let solUsdc = 0;
237
237
  if (hasEvm && wallet.evmAddress) try {
@@ -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 (alpha)",
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,14 +376,13 @@ 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}`);
383
382
  const config = loadConfig();
384
383
  let preferredNetwork = config?.defaultNetwork;
385
384
  if (!preferredNetwork && wallet.evmAddress && wallet.solanaAddress) {
386
- const { fetchEvmBalances, fetchSolanaBalances } = await import("../wallet-DFhJWn3o.js");
385
+ const { fetchEvmBalances, fetchSolanaBalances } = await import("../wallet-BoY0fgX7.js");
387
386
  const [evmBal, solBal] = await Promise.allSettled([fetchEvmBalances(wallet.evmAddress), fetchSolanaBalances(wallet.solanaAddress)]);
388
387
  const evmUsdc = evmBal.status === "fulfilled" ? Number(evmBal.value?.usdc ?? 0) : 0;
389
388
  const solUsdc = solBal.status === "fulfilled" ? Number(solBal.value?.usdc ?? 0) : 0;
@@ -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 { McpServer } = await import("@modelcontextprotocol/sdk/server/mcp.js");
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 x402Mcp = new x402MCPClient(new Client({
404
+ const remoteClient = new Client({
406
405
  name: "x402-proxy",
407
- version: "0.4.2"
408
- }), x402PaymentClient, {
406
+ version: "0.5.1"
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
- const { tools } = await x402Mcp.listTools();
450
+ let { tools } = await x402Mcp.listTools();
451
451
  dim(` ${tools.length} tools available`);
452
- const localServer = new McpServer({
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.4.2"
455
- });
456
- for (const tool of tools) localServer.tool(tool.name, tool.description ?? "", tool.inputSchema?.properties ? Object.fromEntries(Object.entries(tool.inputSchema.properties).map(([k, v]) => [k, v])) : {}, async (args) => {
457
- const result = await x402Mcp.callTool(tool.name, args);
461
+ version: "0.5.1"
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
- try {
464
- const { resources } = await x402Mcp.listResources();
465
- if (resources.length > 0) {
466
- dim(` ${resources.length} resources available`);
467
- for (const resource of resources) localServer.resource(resource.name, resource.uri, resource.description ? { description: resource.description } : {}, async (uri) => {
468
- return { contents: (await x402Mcp.readResource({ uri: uri.href })).contents.map((c) => ({
469
- uri: c.uri,
470
- text: "text" in c ? c.text : ""
471
- })) };
472
- });
473
- }
474
- } catch {}
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.4.2" },
638
+ versionInfo: { currentVersion: "0.5.1" },
612
639
  scanner: { caseStyle: "allow-kebab-for-camel" }
613
640
  });
614
641
 
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import { n as statusCommand, t as displayStatus } from "./status-DxW72Hx6.js";
2
+ import { n as statusCommand, t as displayStatus } from "./status-BoH_1kIH.js";
3
3
 
4
4
  export { displayStatus };
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { g as readHistory, h as formatTxLine, n as fetchEvmBalances, o as resolveWallet, p as calcSpend, r as fetchSolanaBalances, s as dim, t as balanceLine } from "./wallet-BHywTzdN.js";
2
+ import { g as readHistory, h as formatTxLine, n as fetchEvmBalances, o as resolveWallet, p as calcSpend, r as fetchSolanaBalances, s as dim, t as balanceLine } from "./wallet-BMYYtAP6.js";
3
3
  import { a as getConfigDirShort, l as loadConfig, o as getHistoryPath } from "./derive-CISr_ond.js";
4
4
  import { buildCommand } from "@stricli/core";
5
5
  import pc from "picocolors";
@@ -12,6 +12,7 @@ import { registerExactSvmScheme } from "@x402/svm/exact/client";
12
12
  import { createPublicClient, http } from "viem";
13
13
  import { privateKeyToAccount } from "viem/accounts";
14
14
  import { base } from "viem/chains";
15
+ import { address, getAddressEncoder, getProgramDerivedAddress } from "@solana/kit";
15
16
 
16
17
  //#region src/history.ts
17
18
  const HISTORY_MAX_LINES = 1e3;
@@ -285,6 +286,8 @@ const BASE_RPC = "https://mainnet.base.org";
285
286
  const SOLANA_RPC = "https://api.mainnet-beta.solana.com";
286
287
  const USDC_BASE = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
287
288
  const USDC_SOLANA_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
289
+ const TOKEN_PROGRAM = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
290
+ const ATA_PROGRAM = "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL";
288
291
  async function rpcCall(url, method, params) {
289
292
  return (await fetch(url, {
290
293
  method: "POST",
@@ -308,17 +311,26 @@ async function fetchEvmBalances(address) {
308
311
  usdc: usdcRes.result ? (Number(BigInt(usdcRes.result)) / 1e6).toFixed(4) : "?"
309
312
  };
310
313
  }
311
- async function fetchSolanaBalances(address) {
312
- const [solRes, usdcRes] = await Promise.all([rpcCall(SOLANA_RPC, "getBalance", [address]), rpcCall(SOLANA_RPC, "getTokenAccountsByOwner", [
313
- address,
314
- { mint: USDC_SOLANA_MINT },
315
- { encoding: "jsonParsed" }
316
- ])]);
314
+ async function getUsdcAta(owner) {
315
+ const encoder = getAddressEncoder();
316
+ const [ata] = await getProgramDerivedAddress({
317
+ programAddress: address(ATA_PROGRAM),
318
+ seeds: [
319
+ encoder.encode(address(owner)),
320
+ encoder.encode(address(TOKEN_PROGRAM)),
321
+ encoder.encode(address(USDC_SOLANA_MINT))
322
+ ]
323
+ });
324
+ return ata;
325
+ }
326
+ async function fetchSolanaBalances(ownerAddress) {
327
+ const ata = await getUsdcAta(ownerAddress);
328
+ const [solRes, usdcRes] = await Promise.all([rpcCall(SOLANA_RPC, "getBalance", [ownerAddress]), rpcCall(SOLANA_RPC, "getTokenAccountBalance", [ata])]);
317
329
  const sol = solRes.result?.value != null ? (solRes.result.value / 1e9).toFixed(6) : "?";
318
- const accounts = usdcRes.result?.value;
330
+ const usdcVal = usdcRes.result?.value;
319
331
  return {
320
332
  sol,
321
- usdc: accounts?.length ? Number(accounts[0].account.data.parsed.info.tokenAmount.uiAmountString).toFixed(4) : "0.0000"
333
+ usdc: usdcVal ? Number(usdcVal.uiAmountString).toFixed(4) : usdcVal === void 0 ? "?" : "0.0000"
322
334
  };
323
335
  }
324
336
  function balanceLine(usdc, native, nativeSymbol) {
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { i as walletInfoCommand, n as fetchEvmBalances, r as fetchSolanaBalances, t as balanceLine } from "./wallet-BMYYtAP6.js";
3
+
4
+ export { fetchEvmBalances, fetchSolanaBalances };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "x402-proxy",
3
- "version": "0.4.2",
3
+ "version": "0.5.1",
4
4
  "description": "curl for x402 paid APIs. Auto-pays any endpoint on Base and Solana.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -1,4 +0,0 @@
1
- #!/usr/bin/env node
2
- import { i as walletInfoCommand, n as fetchEvmBalances, r as fetchSolanaBalances, t as balanceLine } from "./wallet-BHywTzdN.js";
3
-
4
- export { fetchEvmBalances, fetchSolanaBalances };