@quackai/q402-mcp 0.8.22 → 0.8.23
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 +126 -12
- package/package.json +73 -75
package/dist/index.js
CHANGED
|
@@ -211,7 +211,7 @@ var isValidPrivateKey = (s) => typeof s === "string" && PRIVATE_KEY_RE.test(s);
|
|
|
211
211
|
// package.json
|
|
212
212
|
var package_default = {
|
|
213
213
|
name: "@quackai/q402-mcp",
|
|
214
|
-
version: "0.8.
|
|
214
|
+
version: "0.8.23",
|
|
215
215
|
description: "MCP server for Q402 \u2014 gasless USDC/USDT/RLUSD payments on 10 EVM chains + Chainlink CCIP USDC bridge on the eth/avax/arbitrum triangle, callable from Claude (Desktop / Code), OpenAI Codex CLI, and any other Model Context Protocol client.",
|
|
216
216
|
mcpName: "io.github.bitgett/q402-mcp",
|
|
217
217
|
keywords: [
|
|
@@ -262,7 +262,7 @@ var package_default = {
|
|
|
262
262
|
},
|
|
263
263
|
devDependencies: {
|
|
264
264
|
"@types/node": "^20.11.0",
|
|
265
|
-
tsup: "^8.
|
|
265
|
+
tsup: "^8.5.1",
|
|
266
266
|
typescript: "^5.5.0"
|
|
267
267
|
},
|
|
268
268
|
repository: {
|
|
@@ -279,9 +279,7 @@ var package_default = {
|
|
|
279
279
|
access: "public"
|
|
280
280
|
},
|
|
281
281
|
overrides: {
|
|
282
|
-
|
|
283
|
-
qs: "^6.15.2",
|
|
284
|
-
hono: "^4.12.21"
|
|
282
|
+
esbuild: "^0.28.1"
|
|
285
283
|
}
|
|
286
284
|
};
|
|
287
285
|
|
|
@@ -927,6 +925,29 @@ function sandboxPay(chain, input) {
|
|
|
927
925
|
};
|
|
928
926
|
}
|
|
929
927
|
|
|
928
|
+
// src/consent.ts
|
|
929
|
+
import { sha256, toUtf8Bytes } from "ethers";
|
|
930
|
+
function canonicalIntent(intent) {
|
|
931
|
+
const sortValue = (v) => {
|
|
932
|
+
if (Array.isArray(v)) return v.map(sortValue);
|
|
933
|
+
if (v && typeof v === "object") {
|
|
934
|
+
const src = v;
|
|
935
|
+
const out = {};
|
|
936
|
+
for (const k of Object.keys(src).sort()) out[k] = sortValue(src[k]);
|
|
937
|
+
return out;
|
|
938
|
+
}
|
|
939
|
+
return v;
|
|
940
|
+
};
|
|
941
|
+
return JSON.stringify(sortValue(intent));
|
|
942
|
+
}
|
|
943
|
+
function consentTokenFor(intent) {
|
|
944
|
+
return "ct_" + sha256(toUtf8Bytes(canonicalIntent(intent))).slice(2, 18);
|
|
945
|
+
}
|
|
946
|
+
function checkConsent(intent, provided) {
|
|
947
|
+
const expected = consentTokenFor(intent);
|
|
948
|
+
return { ok: provided === expected, expected };
|
|
949
|
+
}
|
|
950
|
+
|
|
930
951
|
// src/tools/pay.ts
|
|
931
952
|
var PayInputSchema = z2.object({
|
|
932
953
|
chain: z2.enum(["avax", "bnb", "eth", "xlayer", "stable", "mantle", "injective", "monad", "scroll", "arbitrum"]),
|
|
@@ -951,6 +972,9 @@ When MORE THAN ONE wallet is configured in the user's environment, you MUST ask
|
|
|
951
972
|
confirm: z2.literal(true).describe(
|
|
952
973
|
"MUST be true. Prove the user explicitly approved this exact payment in the conversation right before this tool was called. When hookParams is set you MUST confirm what it actually does to the money: the split RECIPIENTS and their shares (funds go to those addresses, not `to`), and any oracle condition gating the settlement \u2014 not just the top-level recipient and amount. Setting this to true on behalf of the user without that confirmation is a violation of the tool contract."
|
|
953
974
|
),
|
|
975
|
+
consentToken: z2.string().optional().describe(
|
|
976
|
+
'Two-phase consent. LEAVE THIS UNSET on the first call: the tool will NOT send \u2014 it returns status="needs_confirmation" with a human-readable `preview` of the exact payment and a `consentToken`. Relay that preview to the user verbatim, get their explicit yes, then call again with the SAME args plus this `consentToken`. The tool re-derives the token from the params it is about to execute and refuses on mismatch, so you cannot preview one payment and execute another. Never fabricate a token.'
|
|
977
|
+
),
|
|
954
978
|
hookParams: z2.object({
|
|
955
979
|
recipientAgentId: z2.string().optional().describe("ReputationGate: the recipient's ERC-8004 agent id."),
|
|
956
980
|
condition: z2.object({
|
|
@@ -1086,6 +1110,29 @@ async function runPay(input) {
|
|
|
1086
1110
|
if (CONFIG.allowedRecipients.length > 0) {
|
|
1087
1111
|
guardsApplied.push(`recipient_allowlist[${CONFIG.allowedRecipients.length}]`);
|
|
1088
1112
|
}
|
|
1113
|
+
const consentIntent = {
|
|
1114
|
+
t: "pay",
|
|
1115
|
+
chain: input.chain,
|
|
1116
|
+
to: input.to.toLowerCase(),
|
|
1117
|
+
amount: input.amount,
|
|
1118
|
+
token: input.token,
|
|
1119
|
+
...input.hookParams?.splits ? { splits: input.hookParams.splits.map((s) => ({ r: s.recipient.toLowerCase(), bps: s.bps })) } : {}
|
|
1120
|
+
};
|
|
1121
|
+
const consent = checkConsent(consentIntent, input.consentToken);
|
|
1122
|
+
if (!consent.ok) {
|
|
1123
|
+
const splitNote = input.hookParams?.splits ? ` \u2014 split ${input.hookParams.splits.length} ways; funds go to the split recipients, not ${input.to}` : "";
|
|
1124
|
+
const fromNote = senderWallet ? ` from ${senderWallet.addressShort}` : "";
|
|
1125
|
+
return {
|
|
1126
|
+
result: failureResult("consent"),
|
|
1127
|
+
guardsApplied: [...guardsApplied, "two_phase_consent"],
|
|
1128
|
+
senderWallet,
|
|
1129
|
+
needsConsent: {
|
|
1130
|
+
status: "needs_confirmation",
|
|
1131
|
+
preview: `Send ${input.amount} ${input.token} to ${input.to} on ${chain.key}${fromNote}${splitNote}. Confirm with the user, then re-call q402_pay with the same args plus consentToken="${consent.expected}".`,
|
|
1132
|
+
consentToken: consent.expected
|
|
1133
|
+
}
|
|
1134
|
+
};
|
|
1135
|
+
}
|
|
1089
1136
|
const scopeRequest = input.keyScope ?? "auto";
|
|
1090
1137
|
const resolved = resolveApiKey(input.chain, scopeRequest);
|
|
1091
1138
|
guardsApplied.push(`scope=${resolved.scope}${resolved.fromLegacyFallback ? "(legacy)" : ""}`);
|
|
@@ -1427,9 +1474,13 @@ var BatchPayInputSchema = z3.object({
|
|
|
1427
1474
|
),
|
|
1428
1475
|
confirm: z3.literal(true).describe(
|
|
1429
1476
|
"MUST be true. The user must have explicitly approved this exact set of recipients, amounts, chain, and token in the conversation right before this tool was called. Setting confirm=true on behalf of the user without that approval is a violation of the tool contract."
|
|
1477
|
+
),
|
|
1478
|
+
consentToken: z3.string().optional().describe(
|
|
1479
|
+
'Two-phase consent. LEAVE UNSET on the first call: the tool will NOT send \u2014 it returns status="needs_confirmation" with a `setupHint` preview of every recipient + amount and a `consentToken`. Relay that preview to the user, get an explicit yes, then re-call with the SAME args plus this `consentToken`. The tool re-derives it from the batch it is about to send and refuses on mismatch, so you cannot preview one batch and execute another. Never fabricate a token.'
|
|
1430
1480
|
)
|
|
1431
1481
|
});
|
|
1432
1482
|
function maxAmountGuardBatch(recipients, cap) {
|
|
1483
|
+
let total = 0;
|
|
1433
1484
|
for (let i = 0; i < recipients.length; i++) {
|
|
1434
1485
|
const r = recipients[i];
|
|
1435
1486
|
const numeric = Number(r.amount);
|
|
@@ -1441,6 +1492,12 @@ function maxAmountGuardBatch(recipients, cap) {
|
|
|
1441
1492
|
`recipients[${i}]: amount $${r.amount} exceeds the per-call cap of $${cap}. Set Q402_MAX_AMOUNT_PER_CALL to a higher value if intentional.`
|
|
1442
1493
|
);
|
|
1443
1494
|
}
|
|
1495
|
+
total += numeric;
|
|
1496
|
+
}
|
|
1497
|
+
if (total > cap) {
|
|
1498
|
+
throw new Error(
|
|
1499
|
+
`batch total $${total.toFixed(2)} across ${recipients.length} recipients exceeds the per-call cap of $${cap}. Q402_MAX_AMOUNT_PER_CALL bounds the WHOLE batch, not each row. Raise the cap if this batch is intentional, or split it into smaller batches.`
|
|
1500
|
+
);
|
|
1444
1501
|
}
|
|
1445
1502
|
}
|
|
1446
1503
|
function recipientAllowlistGuardBatch(recipients, allow) {
|
|
@@ -1464,11 +1521,31 @@ async function runBatchPay(input) {
|
|
|
1464
1521
|
}
|
|
1465
1522
|
const guardsApplied = [];
|
|
1466
1523
|
maxAmountGuardBatch(input.recipients, CONFIG.maxAmountPerCallUsd);
|
|
1467
|
-
guardsApplied.push(`max_amount<=${CONFIG.maxAmountPerCallUsd} (per
|
|
1524
|
+
guardsApplied.push(`max_amount<=${CONFIG.maxAmountPerCallUsd} (per row AND batch total)`);
|
|
1468
1525
|
recipientAllowlistGuardBatch(input.recipients, CONFIG.allowedRecipients);
|
|
1469
1526
|
if (CONFIG.allowedRecipients.length > 0) {
|
|
1470
1527
|
guardsApplied.push(`recipient_allowlist[${CONFIG.allowedRecipients.length}]`);
|
|
1471
1528
|
}
|
|
1529
|
+
const consentIntent = {
|
|
1530
|
+
t: "batch",
|
|
1531
|
+
chain: input.chain,
|
|
1532
|
+
token: input.token,
|
|
1533
|
+
recipients: input.recipients.map((r) => ({ to: r.to.toLowerCase(), amount: r.amount }))
|
|
1534
|
+
};
|
|
1535
|
+
const consent = checkConsent(consentIntent, input.consentToken);
|
|
1536
|
+
if (!consent.ok) {
|
|
1537
|
+
const total = input.recipients.reduce((s, r) => s + Number(r.amount), 0);
|
|
1538
|
+
const lines = input.recipients.map((r, i) => ` ${i + 1}. ${r.amount} ${input.token} -> ${r.to}`).join("\n");
|
|
1539
|
+
return {
|
|
1540
|
+
mode: "none",
|
|
1541
|
+
status: "needs_confirmation",
|
|
1542
|
+
guardsApplied: [...guardsApplied, "two_phase_consent"],
|
|
1543
|
+
consentToken: consent.expected,
|
|
1544
|
+
setupHint: `Batch on ${input.chain}: ${input.recipients.length} recipients, total ${total} ${input.token}.
|
|
1545
|
+
${lines}
|
|
1546
|
+
Confirm the full list with the user, then re-call q402_batch_pay with the same args plus consentToken="${consent.expected}".`
|
|
1547
|
+
};
|
|
1548
|
+
}
|
|
1472
1549
|
const modes = detectAgenticModes(CONFIG);
|
|
1473
1550
|
const available = [];
|
|
1474
1551
|
if (modes.modeA && CONFIG.privateKey && isValidPrivateKey(CONFIG.privateKey)) {
|
|
@@ -1953,7 +2030,7 @@ var BALANCE_TOOL = {
|
|
|
1953
2030
|
|
|
1954
2031
|
// src/tools/receipt.ts
|
|
1955
2032
|
import { z as z5 } from "zod";
|
|
1956
|
-
import { keccak256, toUtf8Bytes, getBytes, verifyMessage } from "ethers";
|
|
2033
|
+
import { keccak256, toUtf8Bytes as toUtf8Bytes2, getBytes, verifyMessage } from "ethers";
|
|
1957
2034
|
var ReceiptShape = z5.object({
|
|
1958
2035
|
receiptId: z5.string(),
|
|
1959
2036
|
createdAt: z5.string(),
|
|
@@ -1994,8 +2071,9 @@ function canonicalize(fields) {
|
|
|
1994
2071
|
return JSON.stringify(sorted);
|
|
1995
2072
|
}
|
|
1996
2073
|
function digest(canonical) {
|
|
1997
|
-
return keccak256(
|
|
2074
|
+
return keccak256(toUtf8Bytes2(canonical));
|
|
1998
2075
|
}
|
|
2076
|
+
var RELAYER_SIGNER = "0xfc77ff29178b7286a8ba703d7a70895ca74ff466";
|
|
1999
2077
|
function verifyReceiptSignature(r) {
|
|
2000
2078
|
try {
|
|
2001
2079
|
const fields = {
|
|
@@ -2012,7 +2090,7 @@ function verifyReceiptSignature(r) {
|
|
|
2012
2090
|
sandbox: r.sandbox
|
|
2013
2091
|
};
|
|
2014
2092
|
const recovered = verifyMessage(getBytes(digest(canonicalize(fields))), r.signature).toLowerCase();
|
|
2015
|
-
return recovered === r.signedBy.toLowerCase();
|
|
2093
|
+
return recovered === RELAYER_SIGNER && r.signedBy.toLowerCase() === RELAYER_SIGNER;
|
|
2016
2094
|
} catch {
|
|
2017
2095
|
return false;
|
|
2018
2096
|
}
|
|
@@ -3091,6 +3169,9 @@ var BridgeSendInputSchema = z11.object({
|
|
|
3091
3169
|
maxFeeRaw: z11.string().regex(/^\d+$/).optional().describe("Optional client-side fee cap in raw 18-dec wei. Server still clamps to its 10% slippage ceiling; clients may LOWER but not RAISE."),
|
|
3092
3170
|
confirm: z11.boolean().optional().describe(
|
|
3093
3171
|
"MUST be true to fire a LIVE bridge (ignored in sandbox). Set this only after the user has explicitly approved this exact bridge (src, dst, amount, feeToken) in the conversation. When omitted or false on a live call the tool previews the action and does NOT move any funds. Never set confirm:true on the user's behalf without approval."
|
|
3172
|
+
),
|
|
3173
|
+
consentToken: z11.string().optional().describe(
|
|
3174
|
+
"Two-phase consent. LEAVE UNSET on the first live call: the tool previews the bridge (without moving funds) and returns a `consentToken`. Relay the preview to the user, get an explicit yes, then re-call with sandbox:false, confirm:true, AND this consentToken. The tool re-derives it from the bridge it is about to execute (src, dst, amount, feeToken) and refuses on mismatch."
|
|
3094
3175
|
)
|
|
3095
3176
|
}).refine((d) => d.src !== d.dst, {
|
|
3096
3177
|
// Local Zod rejection saves a network round-trip + a Q402 backend log
|
|
@@ -3141,6 +3222,10 @@ var BRIDGE_SEND_TOOL = {
|
|
|
3141
3222
|
confirm: {
|
|
3142
3223
|
type: "boolean",
|
|
3143
3224
|
description: "MUST be true to fire a LIVE bridge (ignored in sandbox) \u2014 set only after the user explicitly approved this exact bridge in chat. Omit (or false) on a live call to preview without moving funds."
|
|
3225
|
+
},
|
|
3226
|
+
consentToken: {
|
|
3227
|
+
type: "string",
|
|
3228
|
+
description: "Two-phase consent token. Leave unset on the first live call to get a preview + token; re-call with confirm:true AND this token after the user approves. Bound to (src, dst, amount, feeToken) \u2014 re-derived server-side-of-the-tool and refused on mismatch."
|
|
3144
3229
|
}
|
|
3145
3230
|
},
|
|
3146
3231
|
required: ["src", "dst", "amount"]
|
|
@@ -3170,13 +3255,21 @@ async function runBridgeSend(input) {
|
|
|
3170
3255
|
}]
|
|
3171
3256
|
};
|
|
3172
3257
|
}
|
|
3173
|
-
|
|
3258
|
+
const consentIntent = {
|
|
3259
|
+
t: "bridge",
|
|
3260
|
+
src: input.src,
|
|
3261
|
+
dst: input.dst,
|
|
3262
|
+
amount: input.amount,
|
|
3263
|
+
feeToken: input.feeToken === "native" ? "native" : "LINK"
|
|
3264
|
+
};
|
|
3265
|
+
const consent = checkConsent(consentIntent, input.consentToken);
|
|
3266
|
+
if (input.confirm !== true || !consent.ok) {
|
|
3174
3267
|
const walletDesc = typeof input.walletId === "string" && input.walletId.length > 0 ? `wallet ${input.walletId.toLowerCase()}` : "your default Agent Wallet";
|
|
3175
3268
|
const fee = input.feeToken === "native" ? "native" : "LINK";
|
|
3176
3269
|
return {
|
|
3177
3270
|
content: [{
|
|
3178
3271
|
type: "text",
|
|
3179
|
-
text: `Will bridge ${input.amount} raw USDC units from ${input.src}
|
|
3272
|
+
text: `Will bridge ${input.amount} raw USDC units from ${input.src} -> ${input.dst} via Chainlink CCIP from ${walletDesc} (fee paid in ${fee}). This MOVES FUNDS on-chain. Confirm with the user, then re-call with sandbox:false, confirm:true, AND consentToken="${consent.expected}".`
|
|
3180
3273
|
}]
|
|
3181
3274
|
};
|
|
3182
3275
|
}
|
|
@@ -4007,7 +4100,7 @@ var RecurringCreateInputSchema = z19.object({
|
|
|
4007
4100
|
});
|
|
4008
4101
|
var RECURRING_CREATE_TOOL = {
|
|
4009
4102
|
name: "q402_recurring_create",
|
|
4010
|
-
description: "Author a new recurring-payment rule on the user's Agent Wallet. Single-recipient (use the dashboard for multi-recipient payroll). Pick a cadence \u2014 hourly:N, daily, weekly:{day}, monthly:N, or monthly:last \u2014 and a recipient + amount + chain + token. Authenticated by the configured Multichain API key; no private key required. Recurring requires the paid Multichain subscription on EVERY chain including bnb \u2014 trial keys are rejected at create time with MULTICHAIN_REQUIRED and should keep using q402_pay for one-shot Trial sends. Each fire is bounded by the wallet's perTxMax
|
|
4103
|
+
description: "Author a new recurring-payment rule on the user's Agent Wallet. Single-recipient (use the dashboard for multi-recipient payroll). Pick a cadence \u2014 hourly:N, daily, weekly:{day}, monthly:N, or monthly:last \u2014 and a recipient + amount + chain + token. Authenticated by the configured Multichain API key; no private key required. Recurring requires the paid Multichain subscription on EVERY chain including bnb \u2014 trial keys are rejected at create time with MULTICHAIN_REQUIRED and should keep using q402_pay for one-shot Trial sends. Each fire is bounded server-side by BOTH the wallet's perTxMax AND its dailyLimit \u2014 a rule's daily total reserves against the same daily bucket as manual sends (the scheduler skips the fire if the bucket can't cover it), so scheduled rules can't outrun the dashboard caps. This tool also enforces your local Q402_MAX_AMOUNT_PER_CALL + Q402_ALLOWED_RECIPIENTS rails at create time. The user can stop a rule any time via q402_recurring_cancel.",
|
|
4011
4104
|
inputSchema: {
|
|
4012
4105
|
type: "object",
|
|
4013
4106
|
properties: {
|
|
@@ -4082,6 +4175,27 @@ async function runRecurringCreate(input) {
|
|
|
4082
4175
|
dashboardUrl
|
|
4083
4176
|
};
|
|
4084
4177
|
}
|
|
4178
|
+
const amountNum = Number(input.amount);
|
|
4179
|
+
if (Number.isFinite(amountNum) && amountNum > CONFIG.maxAmountPerCallUsd) {
|
|
4180
|
+
return {
|
|
4181
|
+
ok: false,
|
|
4182
|
+
walletId: null,
|
|
4183
|
+
rule: null,
|
|
4184
|
+
error: "AMOUNT_EXCEEDS_CAP",
|
|
4185
|
+
message: `Per-fire amount $${input.amount} exceeds your Q402_MAX_AMOUNT_PER_CALL cap of $${CONFIG.maxAmountPerCallUsd}. Each recurring fire is bounded by the same per-call cap as a one-shot q402_pay \u2014 raise the cap if this schedule is intentional.`,
|
|
4186
|
+
dashboardUrl
|
|
4187
|
+
};
|
|
4188
|
+
}
|
|
4189
|
+
if (CONFIG.allowedRecipients.length > 0 && !CONFIG.allowedRecipients.includes(input.recipient.toLowerCase())) {
|
|
4190
|
+
return {
|
|
4191
|
+
ok: false,
|
|
4192
|
+
walletId: null,
|
|
4193
|
+
rule: null,
|
|
4194
|
+
error: "RECIPIENT_NOT_ALLOWED",
|
|
4195
|
+
message: `Recipient ${input.recipient} is not in Q402_ALLOWED_RECIPIENTS. A recurring rule would send to it on every fire \u2014 add it to the allowlist or unset the env var to disable the guard.`,
|
|
4196
|
+
dashboardUrl
|
|
4197
|
+
};
|
|
4198
|
+
}
|
|
4085
4199
|
const explicitWalletId = typeof input.walletId === "string" && input.walletId.length > 0 ? input.walletId.toLowerCase() : CONFIG.walletId;
|
|
4086
4200
|
try {
|
|
4087
4201
|
const res = await fetch(`${base}/wallet/agentic/recurring-by-key`, {
|
package/package.json
CHANGED
|
@@ -1,75 +1,73 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@quackai/q402-mcp",
|
|
3
|
-
"version": "0.8.
|
|
4
|
-
"description": "MCP server for Q402 — gasless USDC/USDT/RLUSD payments on 10 EVM chains + Chainlink CCIP USDC bridge on the eth/avax/arbitrum triangle, callable from Claude (Desktop / Code), OpenAI Codex CLI, and any other Model Context Protocol client.",
|
|
5
|
-
"mcpName": "io.github.bitgett/q402-mcp",
|
|
6
|
-
"keywords": [
|
|
7
|
-
"mcp",
|
|
8
|
-
"model-context-protocol",
|
|
9
|
-
"claude",
|
|
10
|
-
"claude-desktop",
|
|
11
|
-
"claude-code",
|
|
12
|
-
"codex",
|
|
13
|
-
"openai-codex",
|
|
14
|
-
"cline",
|
|
15
|
-
"q402",
|
|
16
|
-
"x402",
|
|
17
|
-
"stablecoin",
|
|
18
|
-
"usdc",
|
|
19
|
-
"usdt",
|
|
20
|
-
"rlusd",
|
|
21
|
-
"ripple",
|
|
22
|
-
"gasless",
|
|
23
|
-
"eip-7702",
|
|
24
|
-
"payments",
|
|
25
|
-
"ai-agents"
|
|
26
|
-
],
|
|
27
|
-
"type": "module",
|
|
28
|
-
"main": "dist/index.js",
|
|
29
|
-
"bin": {
|
|
30
|
-
"q402-mcp": "dist/index.js"
|
|
31
|
-
},
|
|
32
|
-
"files": [
|
|
33
|
-
"dist",
|
|
34
|
-
"README.md",
|
|
35
|
-
"LICENSE"
|
|
36
|
-
],
|
|
37
|
-
"engines": {
|
|
38
|
-
"node": ">=18.18"
|
|
39
|
-
},
|
|
40
|
-
"scripts": {
|
|
41
|
-
"build": "tsup",
|
|
42
|
-
"dev": "tsup --watch",
|
|
43
|
-
"lint": "tsc --noEmit",
|
|
44
|
-
"prepublishOnly": "npm run lint && npm run build",
|
|
45
|
-
"start": "node dist/index.js"
|
|
46
|
-
},
|
|
47
|
-
"dependencies": {
|
|
48
|
-
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
49
|
-
"ethers": "^6.16.0",
|
|
50
|
-
"zod": "^3.23.8"
|
|
51
|
-
},
|
|
52
|
-
"devDependencies": {
|
|
53
|
-
"@types/node": "^20.11.0",
|
|
54
|
-
"tsup": "^8.
|
|
55
|
-
"typescript": "^5.5.0"
|
|
56
|
-
},
|
|
57
|
-
"repository": {
|
|
58
|
-
"type": "git",
|
|
59
|
-
"url": "git+https://github.com/bitgett/q402-mcp.git"
|
|
60
|
-
},
|
|
61
|
-
"homepage": "https://q402.quackai.ai/claude",
|
|
62
|
-
"bugs": {
|
|
63
|
-
"url": "https://github.com/bitgett/q402-mcp/issues"
|
|
64
|
-
},
|
|
65
|
-
"license": "Apache-2.0",
|
|
66
|
-
"author": "David Lee <davidlee@quackai.ai>",
|
|
67
|
-
"publishConfig": {
|
|
68
|
-
"access": "public"
|
|
69
|
-
},
|
|
70
|
-
"overrides": {
|
|
71
|
-
"
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@quackai/q402-mcp",
|
|
3
|
+
"version": "0.8.23",
|
|
4
|
+
"description": "MCP server for Q402 — gasless USDC/USDT/RLUSD payments on 10 EVM chains + Chainlink CCIP USDC bridge on the eth/avax/arbitrum triangle, callable from Claude (Desktop / Code), OpenAI Codex CLI, and any other Model Context Protocol client.",
|
|
5
|
+
"mcpName": "io.github.bitgett/q402-mcp",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"mcp",
|
|
8
|
+
"model-context-protocol",
|
|
9
|
+
"claude",
|
|
10
|
+
"claude-desktop",
|
|
11
|
+
"claude-code",
|
|
12
|
+
"codex",
|
|
13
|
+
"openai-codex",
|
|
14
|
+
"cline",
|
|
15
|
+
"q402",
|
|
16
|
+
"x402",
|
|
17
|
+
"stablecoin",
|
|
18
|
+
"usdc",
|
|
19
|
+
"usdt",
|
|
20
|
+
"rlusd",
|
|
21
|
+
"ripple",
|
|
22
|
+
"gasless",
|
|
23
|
+
"eip-7702",
|
|
24
|
+
"payments",
|
|
25
|
+
"ai-agents"
|
|
26
|
+
],
|
|
27
|
+
"type": "module",
|
|
28
|
+
"main": "dist/index.js",
|
|
29
|
+
"bin": {
|
|
30
|
+
"q402-mcp": "dist/index.js"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist",
|
|
34
|
+
"README.md",
|
|
35
|
+
"LICENSE"
|
|
36
|
+
],
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=18.18"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsup",
|
|
42
|
+
"dev": "tsup --watch",
|
|
43
|
+
"lint": "tsc --noEmit",
|
|
44
|
+
"prepublishOnly": "npm run lint && npm run build",
|
|
45
|
+
"start": "node dist/index.js"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
49
|
+
"ethers": "^6.16.0",
|
|
50
|
+
"zod": "^3.23.8"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@types/node": "^20.11.0",
|
|
54
|
+
"tsup": "^8.5.1",
|
|
55
|
+
"typescript": "^5.5.0"
|
|
56
|
+
},
|
|
57
|
+
"repository": {
|
|
58
|
+
"type": "git",
|
|
59
|
+
"url": "git+https://github.com/bitgett/q402-mcp.git"
|
|
60
|
+
},
|
|
61
|
+
"homepage": "https://q402.quackai.ai/claude",
|
|
62
|
+
"bugs": {
|
|
63
|
+
"url": "https://github.com/bitgett/q402-mcp/issues"
|
|
64
|
+
},
|
|
65
|
+
"license": "Apache-2.0",
|
|
66
|
+
"author": "David Lee <davidlee@quackai.ai>",
|
|
67
|
+
"publishConfig": {
|
|
68
|
+
"access": "public"
|
|
69
|
+
},
|
|
70
|
+
"overrides": {
|
|
71
|
+
"esbuild": "^0.28.1"
|
|
72
|
+
}
|
|
73
|
+
}
|