@quackai/q402-mcp 0.6.6 → 0.7.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/dist/index.js +267 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -124,7 +124,7 @@ function loadConfig() {
|
|
|
124
124
|
const walletIdRaw = ENV.Q402_AGENT_WALLET_ADDRESS ?? ENV.Q402_WALLET_ID;
|
|
125
125
|
if (!ENV.Q402_AGENT_WALLET_ADDRESS && ENV.Q402_WALLET_ID) {
|
|
126
126
|
process.stderr.write(
|
|
127
|
-
"[q402-mcp] Q402_WALLET_ID is deprecated \u2014 rename to Q402_AGENT_WALLET_ADDRESS. Old name will be removed in v0.
|
|
127
|
+
"[q402-mcp] Q402_WALLET_ID is deprecated \u2014 rename to Q402_AGENT_WALLET_ADDRESS. Old name will be removed in v0.8.0.\n"
|
|
128
128
|
);
|
|
129
129
|
}
|
|
130
130
|
const walletId = typeof walletIdRaw === "string" && walletIdRaw.length > 0 ? walletIdRaw.toLowerCase() : null;
|
|
@@ -211,7 +211,7 @@ var isValidPrivateKey = (s) => typeof s === "string" && PRIVATE_KEY_RE.test(s);
|
|
|
211
211
|
|
|
212
212
|
// src/version.ts
|
|
213
213
|
var PACKAGE_NAME = "@quackai/q402-mcp";
|
|
214
|
-
var PACKAGE_VERSION = "0.
|
|
214
|
+
var PACKAGE_VERSION = "0.7.1";
|
|
215
215
|
|
|
216
216
|
// src/tools/quote.ts
|
|
217
217
|
import { z } from "zod";
|
|
@@ -2674,7 +2674,7 @@ var RecurringCreateInputSchema = z11.object({
|
|
|
2674
2674
|
});
|
|
2675
2675
|
var RECURRING_CREATE_TOOL = {
|
|
2676
2676
|
name: "q402_recurring_create",
|
|
2677
|
-
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.
|
|
2677
|
+
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 (configured on the dashboard) \u2014 the dashboard's dailyLimit cap currently applies to manual sends only, NOT recurring fires, so an attacker with the apiKey could schedule N rules at perTxMax and drain the wallet's USDC balance over time. The user can stop a rule any time via q402_recurring_cancel.",
|
|
2678
2678
|
inputSchema: {
|
|
2679
2679
|
type: "object",
|
|
2680
2680
|
properties: {
|
|
@@ -2971,6 +2971,255 @@ async function runRecurringFires(input) {
|
|
|
2971
2971
|
}
|
|
2972
2972
|
}
|
|
2973
2973
|
|
|
2974
|
+
// src/tools/recurring-pause.ts
|
|
2975
|
+
import { z as z14 } from "zod";
|
|
2976
|
+
var RecurringPauseInputSchema = z14.object({
|
|
2977
|
+
ruleId: z14.string().min(1).describe(
|
|
2978
|
+
"Rule id to pause. Obtain from q402_recurring_list \u2014 each entry's `ruleId` field. Pausing is immediate and reversible."
|
|
2979
|
+
),
|
|
2980
|
+
walletId: z14.string().optional().describe(
|
|
2981
|
+
"Optional lowercased Agent Wallet address when the user holds multiple wallets. Defaults to Q402_AGENT_WALLET_ADDRESS env, then the owner's default wallet."
|
|
2982
|
+
)
|
|
2983
|
+
});
|
|
2984
|
+
var RECURRING_PAUSE_TOOL = {
|
|
2985
|
+
name: "q402_recurring_pause",
|
|
2986
|
+
description: `Pause an active recurring-payment rule. Takes a ruleId (from q402_recurring_list). The rule transitions to status "paused" \u2014 the cron skips it on every tick until you resume. Fully reversible via q402_recurring_resume. Use this when the user says 'pause my Friday payout' or 'hold on, stop my recurring rule for now' \u2014 gentler than cancel, no re-authoring required. Authenticated by the paid Multichain API key (same gate as create/cancel). Read q402_recurring_list first to find the matching ruleId.`,
|
|
2987
|
+
inputSchema: {
|
|
2988
|
+
type: "object",
|
|
2989
|
+
properties: {
|
|
2990
|
+
ruleId: {
|
|
2991
|
+
type: "string",
|
|
2992
|
+
description: "Rule id from q402_recurring_list. Required."
|
|
2993
|
+
},
|
|
2994
|
+
walletId: {
|
|
2995
|
+
type: "string",
|
|
2996
|
+
description: "Optional. Lowercased Agent Wallet address when the user holds multiple wallets. Defaults to Q402_AGENT_WALLET_ADDRESS env, then the owner's default wallet on the server."
|
|
2997
|
+
}
|
|
2998
|
+
},
|
|
2999
|
+
required: ["ruleId"],
|
|
3000
|
+
additionalProperties: false
|
|
3001
|
+
}
|
|
3002
|
+
};
|
|
3003
|
+
async function runRecurringPause(input) {
|
|
3004
|
+
const base = CONFIG.relayBaseUrl;
|
|
3005
|
+
const dashboardUrl = base.replace(/\/api$/, "") + "/dashboard?tab=agent";
|
|
3006
|
+
if (!CONFIG.apiKey || !CONFIG.apiKey.startsWith("q402_live_")) {
|
|
3007
|
+
return {
|
|
3008
|
+
ok: false,
|
|
3009
|
+
walletId: null,
|
|
3010
|
+
rule: null,
|
|
3011
|
+
error: "API_KEY_REQUIRED",
|
|
3012
|
+
message: "No live Q402 API key configured. Run q402_doctor to set one up.",
|
|
3013
|
+
dashboardUrl
|
|
3014
|
+
};
|
|
3015
|
+
}
|
|
3016
|
+
const explicitWalletId = typeof input.walletId === "string" && input.walletId.length > 0 ? input.walletId.toLowerCase() : CONFIG.walletId;
|
|
3017
|
+
try {
|
|
3018
|
+
const res = await fetch(`${base}/wallet/agentic/recurring-by-key`, {
|
|
3019
|
+
method: "POST",
|
|
3020
|
+
headers: { "Content-Type": "application/json" },
|
|
3021
|
+
body: JSON.stringify({
|
|
3022
|
+
apiKey: CONFIG.apiKey,
|
|
3023
|
+
action: "pause",
|
|
3024
|
+
ruleId: input.ruleId,
|
|
3025
|
+
...explicitWalletId ? { walletId: explicitWalletId } : {}
|
|
3026
|
+
})
|
|
3027
|
+
});
|
|
3028
|
+
const data = await res.json().catch(() => ({}));
|
|
3029
|
+
if (!res.ok) {
|
|
3030
|
+
return {
|
|
3031
|
+
ok: false,
|
|
3032
|
+
walletId: explicitWalletId,
|
|
3033
|
+
rule: null,
|
|
3034
|
+
error: data.error ?? `HTTP_${res.status}`,
|
|
3035
|
+
message: data.message ?? `Pause failed with HTTP ${res.status}.`,
|
|
3036
|
+
dashboardUrl
|
|
3037
|
+
};
|
|
3038
|
+
}
|
|
3039
|
+
return {
|
|
3040
|
+
ok: true,
|
|
3041
|
+
walletId: data.walletId ?? explicitWalletId,
|
|
3042
|
+
rule: data.rule ?? null,
|
|
3043
|
+
dashboardUrl
|
|
3044
|
+
};
|
|
3045
|
+
} catch (e) {
|
|
3046
|
+
return {
|
|
3047
|
+
ok: false,
|
|
3048
|
+
walletId: explicitWalletId,
|
|
3049
|
+
rule: null,
|
|
3050
|
+
error: "NETWORK_ERROR",
|
|
3051
|
+
message: e instanceof Error ? e.message : String(e),
|
|
3052
|
+
dashboardUrl
|
|
3053
|
+
};
|
|
3054
|
+
}
|
|
3055
|
+
}
|
|
3056
|
+
|
|
3057
|
+
// src/tools/recurring-resume.ts
|
|
3058
|
+
import { z as z15 } from "zod";
|
|
3059
|
+
var RecurringResumeInputSchema = z15.object({
|
|
3060
|
+
ruleId: z15.string().min(1).describe(
|
|
3061
|
+
"Rule id to resume. Obtain from q402_recurring_list \u2014 each entry's `ruleId` field. Resume is immediate; nextRunAt advances to the next valid slot."
|
|
3062
|
+
),
|
|
3063
|
+
walletId: z15.string().optional().describe(
|
|
3064
|
+
"Optional lowercased Agent Wallet address when the user holds multiple wallets. Defaults to Q402_AGENT_WALLET_ADDRESS env, then the owner's default wallet."
|
|
3065
|
+
)
|
|
3066
|
+
});
|
|
3067
|
+
var RECURRING_RESUME_TOOL = {
|
|
3068
|
+
name: "q402_recurring_resume",
|
|
3069
|
+
description: "Resume a paused or stopped recurring-payment rule. Takes a ruleId (from q402_recurring_list). Supported transitions: paused \u2192 active, paused-by-archive \u2192 active (after restoring the wallet), and fired-cap-exceeded \u2192 active (after raising the per-tx cap or re-subscribing). nextRunAt is advanced to the next valid slot so the rule doesn't immediately fire on a stale schedule. Cancelled rules cannot be resumed \u2014 re-author via q402_recurring_create. Authenticated by the paid Multichain API key.",
|
|
3070
|
+
inputSchema: {
|
|
3071
|
+
type: "object",
|
|
3072
|
+
properties: {
|
|
3073
|
+
ruleId: {
|
|
3074
|
+
type: "string",
|
|
3075
|
+
description: "Rule id from q402_recurring_list. Required."
|
|
3076
|
+
},
|
|
3077
|
+
walletId: {
|
|
3078
|
+
type: "string",
|
|
3079
|
+
description: "Optional. Lowercased Agent Wallet address when the user holds multiple wallets. Defaults to Q402_AGENT_WALLET_ADDRESS env, then the owner's default wallet on the server."
|
|
3080
|
+
}
|
|
3081
|
+
},
|
|
3082
|
+
required: ["ruleId"],
|
|
3083
|
+
additionalProperties: false
|
|
3084
|
+
}
|
|
3085
|
+
};
|
|
3086
|
+
async function runRecurringResume(input) {
|
|
3087
|
+
const base = CONFIG.relayBaseUrl;
|
|
3088
|
+
const dashboardUrl = base.replace(/\/api$/, "") + "/dashboard?tab=agent";
|
|
3089
|
+
if (!CONFIG.apiKey || !CONFIG.apiKey.startsWith("q402_live_")) {
|
|
3090
|
+
return {
|
|
3091
|
+
ok: false,
|
|
3092
|
+
walletId: null,
|
|
3093
|
+
rule: null,
|
|
3094
|
+
error: "API_KEY_REQUIRED",
|
|
3095
|
+
message: "No live Q402 API key configured. Run q402_doctor to set one up.",
|
|
3096
|
+
dashboardUrl
|
|
3097
|
+
};
|
|
3098
|
+
}
|
|
3099
|
+
const explicitWalletId = typeof input.walletId === "string" && input.walletId.length > 0 ? input.walletId.toLowerCase() : CONFIG.walletId;
|
|
3100
|
+
try {
|
|
3101
|
+
const res = await fetch(`${base}/wallet/agentic/recurring-by-key`, {
|
|
3102
|
+
method: "POST",
|
|
3103
|
+
headers: { "Content-Type": "application/json" },
|
|
3104
|
+
body: JSON.stringify({
|
|
3105
|
+
apiKey: CONFIG.apiKey,
|
|
3106
|
+
action: "resume",
|
|
3107
|
+
ruleId: input.ruleId,
|
|
3108
|
+
...explicitWalletId ? { walletId: explicitWalletId } : {}
|
|
3109
|
+
})
|
|
3110
|
+
});
|
|
3111
|
+
const data = await res.json().catch(() => ({}));
|
|
3112
|
+
if (!res.ok) {
|
|
3113
|
+
return {
|
|
3114
|
+
ok: false,
|
|
3115
|
+
walletId: explicitWalletId,
|
|
3116
|
+
rule: null,
|
|
3117
|
+
error: data.error ?? `HTTP_${res.status}`,
|
|
3118
|
+
message: data.message ?? `Resume failed with HTTP ${res.status}.`,
|
|
3119
|
+
dashboardUrl
|
|
3120
|
+
};
|
|
3121
|
+
}
|
|
3122
|
+
return {
|
|
3123
|
+
ok: true,
|
|
3124
|
+
walletId: data.walletId ?? explicitWalletId,
|
|
3125
|
+
rule: data.rule ?? null,
|
|
3126
|
+
dashboardUrl
|
|
3127
|
+
};
|
|
3128
|
+
} catch (e) {
|
|
3129
|
+
return {
|
|
3130
|
+
ok: false,
|
|
3131
|
+
walletId: explicitWalletId,
|
|
3132
|
+
rule: null,
|
|
3133
|
+
error: "NETWORK_ERROR",
|
|
3134
|
+
message: e instanceof Error ? e.message : String(e),
|
|
3135
|
+
dashboardUrl
|
|
3136
|
+
};
|
|
3137
|
+
}
|
|
3138
|
+
}
|
|
3139
|
+
|
|
3140
|
+
// src/tools/recurring-skip-next.ts
|
|
3141
|
+
import { z as z16 } from "zod";
|
|
3142
|
+
var RecurringSkipNextInputSchema = z16.object({
|
|
3143
|
+
ruleId: z16.string().min(1).describe(
|
|
3144
|
+
"Rule id whose next scheduled fire to skip. Obtain from q402_recurring_list \u2014 each entry's `ruleId` field."
|
|
3145
|
+
),
|
|
3146
|
+
walletId: z16.string().optional().describe(
|
|
3147
|
+
"Optional lowercased Agent Wallet address when the user holds multiple wallets. Defaults to Q402_AGENT_WALLET_ADDRESS env, then the owner's default wallet."
|
|
3148
|
+
)
|
|
3149
|
+
});
|
|
3150
|
+
var RECURRING_SKIP_NEXT_TOOL = {
|
|
3151
|
+
name: "q402_recurring_skip_next",
|
|
3152
|
+
description: "Skip ONLY the next scheduled fire of a recurring-payment rule. Cadence is preserved \u2014 the fire after the skipped one runs normally. Use this when the user says 'skip the next Friday payout, Alice is on holiday' or 'don't fire this month's subscription, charge it next month'. The rule must be in active status; paused / cancelled rules must be resumed first. Authenticated by the paid Multichain API key. Call q402_recurring_list first to confirm the ruleId and current schedule.",
|
|
3153
|
+
inputSchema: {
|
|
3154
|
+
type: "object",
|
|
3155
|
+
properties: {
|
|
3156
|
+
ruleId: {
|
|
3157
|
+
type: "string",
|
|
3158
|
+
description: "Rule id from q402_recurring_list. Required."
|
|
3159
|
+
},
|
|
3160
|
+
walletId: {
|
|
3161
|
+
type: "string",
|
|
3162
|
+
description: "Optional. Lowercased Agent Wallet address when the user holds multiple wallets. Defaults to Q402_AGENT_WALLET_ADDRESS env, then the owner's default wallet on the server."
|
|
3163
|
+
}
|
|
3164
|
+
},
|
|
3165
|
+
required: ["ruleId"],
|
|
3166
|
+
additionalProperties: false
|
|
3167
|
+
}
|
|
3168
|
+
};
|
|
3169
|
+
async function runRecurringSkipNext(input) {
|
|
3170
|
+
const base = CONFIG.relayBaseUrl;
|
|
3171
|
+
const dashboardUrl = base.replace(/\/api$/, "") + "/dashboard?tab=agent";
|
|
3172
|
+
if (!CONFIG.apiKey || !CONFIG.apiKey.startsWith("q402_live_")) {
|
|
3173
|
+
return {
|
|
3174
|
+
ok: false,
|
|
3175
|
+
walletId: null,
|
|
3176
|
+
rule: null,
|
|
3177
|
+
error: "API_KEY_REQUIRED",
|
|
3178
|
+
message: "No live Q402 API key configured. Run q402_doctor to set one up.",
|
|
3179
|
+
dashboardUrl
|
|
3180
|
+
};
|
|
3181
|
+
}
|
|
3182
|
+
const explicitWalletId = typeof input.walletId === "string" && input.walletId.length > 0 ? input.walletId.toLowerCase() : CONFIG.walletId;
|
|
3183
|
+
try {
|
|
3184
|
+
const res = await fetch(`${base}/wallet/agentic/recurring-by-key`, {
|
|
3185
|
+
method: "POST",
|
|
3186
|
+
headers: { "Content-Type": "application/json" },
|
|
3187
|
+
body: JSON.stringify({
|
|
3188
|
+
apiKey: CONFIG.apiKey,
|
|
3189
|
+
action: "skip-next",
|
|
3190
|
+
ruleId: input.ruleId,
|
|
3191
|
+
...explicitWalletId ? { walletId: explicitWalletId } : {}
|
|
3192
|
+
})
|
|
3193
|
+
});
|
|
3194
|
+
const data = await res.json().catch(() => ({}));
|
|
3195
|
+
if (!res.ok) {
|
|
3196
|
+
return {
|
|
3197
|
+
ok: false,
|
|
3198
|
+
walletId: explicitWalletId,
|
|
3199
|
+
rule: null,
|
|
3200
|
+
error: data.error ?? `HTTP_${res.status}`,
|
|
3201
|
+
message: data.message ?? `Skip-next failed with HTTP ${res.status}.`,
|
|
3202
|
+
dashboardUrl
|
|
3203
|
+
};
|
|
3204
|
+
}
|
|
3205
|
+
return {
|
|
3206
|
+
ok: true,
|
|
3207
|
+
walletId: data.walletId ?? explicitWalletId,
|
|
3208
|
+
rule: data.rule ?? null,
|
|
3209
|
+
dashboardUrl
|
|
3210
|
+
};
|
|
3211
|
+
} catch (e) {
|
|
3212
|
+
return {
|
|
3213
|
+
ok: false,
|
|
3214
|
+
walletId: explicitWalletId,
|
|
3215
|
+
rule: null,
|
|
3216
|
+
error: "NETWORK_ERROR",
|
|
3217
|
+
message: e instanceof Error ? e.message : String(e),
|
|
3218
|
+
dashboardUrl
|
|
3219
|
+
};
|
|
3220
|
+
}
|
|
3221
|
+
}
|
|
3222
|
+
|
|
2974
3223
|
// src/index.ts
|
|
2975
3224
|
function jsonText(value) {
|
|
2976
3225
|
return { type: "text", text: JSON.stringify(value, null, 2) };
|
|
@@ -2995,6 +3244,9 @@ async function main() {
|
|
|
2995
3244
|
RECURRING_LIST_TOOL,
|
|
2996
3245
|
RECURRING_CREATE_TOOL,
|
|
2997
3246
|
RECURRING_FIRES_TOOL,
|
|
3247
|
+
RECURRING_PAUSE_TOOL,
|
|
3248
|
+
RECURRING_RESUME_TOOL,
|
|
3249
|
+
RECURRING_SKIP_NEXT_TOOL,
|
|
2998
3250
|
RECURRING_CANCEL_TOOL,
|
|
2999
3251
|
CLEAR_DELEGATION_TOOL
|
|
3000
3252
|
]
|
|
@@ -3055,6 +3307,18 @@ async function main() {
|
|
|
3055
3307
|
const parsed = RecurringFiresInputSchema.parse(args ?? {});
|
|
3056
3308
|
return { content: [jsonText(await runRecurringFires(parsed))] };
|
|
3057
3309
|
}
|
|
3310
|
+
case "q402_recurring_pause": {
|
|
3311
|
+
const parsed = RecurringPauseInputSchema.parse(args ?? {});
|
|
3312
|
+
return { content: [jsonText(await runRecurringPause(parsed))] };
|
|
3313
|
+
}
|
|
3314
|
+
case "q402_recurring_resume": {
|
|
3315
|
+
const parsed = RecurringResumeInputSchema.parse(args ?? {});
|
|
3316
|
+
return { content: [jsonText(await runRecurringResume(parsed))] };
|
|
3317
|
+
}
|
|
3318
|
+
case "q402_recurring_skip_next": {
|
|
3319
|
+
const parsed = RecurringSkipNextInputSchema.parse(args ?? {});
|
|
3320
|
+
return { content: [jsonText(await runRecurringSkipNext(parsed))] };
|
|
3321
|
+
}
|
|
3058
3322
|
default:
|
|
3059
3323
|
return {
|
|
3060
3324
|
isError: true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quackai/q402-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "MCP server for Q402 — gasless USDC, USDT, and RLUSD payments across 9 EVM chains, callable from Claude (Desktop / Code), OpenAI Codex CLI, and any other Model Context Protocol client.",
|
|
5
5
|
"mcpName": "io.github.bitgett/q402-mcp",
|
|
6
6
|
"keywords": [
|