@true402.dev/mcp-server 0.6.0 → 0.7.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/dist/index.js CHANGED
@@ -22,7 +22,7 @@ const SERVER_URL = process.env.SERVER_URL ?? "https://true402.dev/api";
22
22
  const WALLET_PRIVATE_KEY = process.env.WALLET_PRIVATE_KEY;
23
23
  const server = new McpServer({
24
24
  name: "true402",
25
- version: "0.6.0",
25
+ version: "0.7.0",
26
26
  });
27
27
  // Start server with stdio transport
28
28
  async function main() {
@@ -54,6 +54,12 @@ export interface EVMPaymentRequirement {
54
54
  * - asset must be the canonical USDC on a supported chain (no arbitrary token/verifyingContract).
55
55
  */
56
56
  export declare function assessRequirement(req: EVMPaymentRequirement, maxUsdc: number): string | null;
57
+ /**
58
+ * Parse the MAX_PAYMENT_USDC ceiling FAIL-CLOSED: unset → the 0.10 default; a non-numeric or negative
59
+ * value (e.g. the European comma-decimal "0,10" → NaN) clamps to 0 = refuse ALL auto-pay, never
60
+ * disables the only wallet-drain guard.
61
+ */
62
+ export declare function parseMaxUsdc(raw: string | undefined): number;
57
63
  /** Refuse to send a signed payment over cleartext (the X-PAYMENT header would be interceptable).
58
64
  * Returns a refusal string, or null if the URL is https (or localhost for dev). */
59
65
  export declare function requireSecureUrl(url: string): string | null;
package/dist/x402-pay.js CHANGED
@@ -91,7 +91,9 @@ export function assessRequirement(req, maxUsdc) {
91
91
  }
92
92
  if (!Number.isFinite(amountUsdc) || amountUsdc < 0)
93
93
  return "the 402 amount is invalid; refusing to sign.";
94
- if (Number.isFinite(maxUsdc) && amountUsdc > maxUsdc) {
94
+ // `maxUsdc` is a finite, non-negative invariant (see parseMaxUsdc) — the comparison ALWAYS runs, so
95
+ // a misconfigured ceiling can never silently disable the cap (maxUsdc=0 refuses everything).
96
+ if (amountUsdc > maxUsdc) {
95
97
  return `refusing to auto-pay $${amountUsdc} USDC — it exceeds the MAX_PAYMENT_USDC ceiling of $${maxUsdc}. Raise MAX_PAYMENT_USDC if this is intended.`;
96
98
  }
97
99
  const usdc = USDC_BY_CHAIN[resolveChainId(req.network)];
@@ -100,6 +102,20 @@ export function assessRequirement(req, maxUsdc) {
100
102
  }
101
103
  return null;
102
104
  }
105
+ /**
106
+ * Parse the MAX_PAYMENT_USDC ceiling FAIL-CLOSED: unset → the 0.10 default; a non-numeric or negative
107
+ * value (e.g. the European comma-decimal "0,10" → NaN) clamps to 0 = refuse ALL auto-pay, never
108
+ * disables the only wallet-drain guard.
109
+ */
110
+ export function parseMaxUsdc(raw) {
111
+ if (raw === undefined)
112
+ return 0.1;
113
+ const n = Number(raw);
114
+ if (Number.isFinite(n) && n >= 0)
115
+ return n;
116
+ console.error(`true402 MCP: MAX_PAYMENT_USDC='${raw}' is not a valid number — refusing ALL auto-pay. Set a numeric value like 0.10.`);
117
+ return 0;
118
+ }
103
119
  /** Refuse to send a signed payment over cleartext (the X-PAYMENT header would be interceptable).
104
120
  * Returns a refusal string, or null if the URL is https (or localhost for dev). */
105
121
  export function requireSecureUrl(url) {
@@ -129,7 +145,14 @@ export async function signEIP3009Payment(privateKey, requirement) {
129
145
  const account = privateKeyToAccount(key);
130
146
  const now = Math.floor(Date.now() / 1000);
131
147
  const validAfter = BigInt(now - 60); // valid 60s in the past (clock skew tolerance)
132
- const validBefore = BigInt(now + requirement.maxTimeoutSeconds);
148
+ // Validate + bound the signed window: maxTimeoutSeconds arrives via JSON and may be a string
149
+ // (`now + "999"` would string-concat); a hostile 402 must not keep the authorization broadcastable
150
+ // far into the future. Coerce, reject garbage, cap at 10 minutes.
151
+ const timeout = Number(requirement.maxTimeoutSeconds);
152
+ if (!Number.isInteger(timeout) || timeout <= 0) {
153
+ throw new Error("Payment requirement has an invalid maxTimeoutSeconds");
154
+ }
155
+ const validBefore = BigInt(now + Math.min(timeout, 600));
133
156
  // Generate a random nonce (bytes32)
134
157
  const randomBytes = new Uint8Array(32);
135
158
  crypto.getRandomValues(randomBytes);
@@ -267,7 +290,7 @@ export async function payAndFetch(baseUrl, path, body, walletPrivateKey) {
267
290
  }
268
291
  // Spend ceiling + asset/network pin: a hostile server must not make our wallet sign a draining
269
292
  // amount or a payment to an arbitrary token/chain. Refuse BEFORE signing.
270
- const maxUsdc = Number(process.env.MAX_PAYMENT_USDC ?? "0.10");
293
+ const maxUsdc = parseMaxUsdc(process.env.MAX_PAYMENT_USDC);
271
294
  const refusal = assessRequirement(evmRequirement, maxUsdc);
272
295
  if (refusal) {
273
296
  return { ok: false, paymentRequired: true, message: refusal };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@true402.dev/mcp-server",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "mcpName": "dev.true402/mcp-server",
5
5
  "description": "MCP server for the true402 machine-native marketplace — pay-per-call AI + web + Base on-chain tools over x402 (USDC on Base): LLM inference, SEO/GEO audit, web extract, link preview, robots/AI-crawler check, security headers, and on-chain DeFi trading signals (token rug/honeypot safety, new token pairs, liquidity-pull/rug alerts, whale swaps).",
6
6
  "type": "module",