@finchagentic/mcp 4.6.1 β†’ 4.6.2

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.
Files changed (58) hide show
  1. package/package.json +5 -6
  2. package/dist/_http-cache.js +0 -96
  3. package/dist/_text-search.js +0 -39
  4. package/dist/agent-loop.js +0 -301
  5. package/dist/annotations.js +0 -122
  6. package/dist/cli.js +0 -1391
  7. package/dist/clink-input.js +0 -15
  8. package/dist/config.js +0 -132
  9. package/dist/convex.js +0 -175
  10. package/dist/dex-pair.js +0 -54
  11. package/dist/enrichment-router.js +0 -315
  12. package/dist/index.js +0 -258
  13. package/dist/llm.js +0 -298
  14. package/dist/local-memory-file.js +0 -150
  15. package/dist/local-memory.js +0 -135
  16. package/dist/local-vault.js +0 -456
  17. package/dist/output-schemas.js +0 -605
  18. package/dist/project.js +0 -36
  19. package/dist/prompts.js +0 -111
  20. package/dist/public-url.js +0 -107
  21. package/dist/resources.js +0 -111
  22. package/dist/server.js +0 -322
  23. package/dist/signal-gate.js +0 -57
  24. package/dist/token-decimals.js +0 -26
  25. package/dist/token-gate.js +0 -88
  26. package/dist/tool-filter.js +0 -53
  27. package/dist/tools/_solidity-scan.js +0 -313
  28. package/dist/tools/agents.js +0 -441
  29. package/dist/tools/automation.js +0 -354
  30. package/dist/tools/base-mcp.js +0 -466
  31. package/dist/tools/base.js +0 -283
  32. package/dist/tools/chronicle.js +0 -268
  33. package/dist/tools/coder.js +0 -94
  34. package/dist/tools/deep-research.js +0 -1421
  35. package/dist/tools/defi.js +0 -292
  36. package/dist/tools/equity.js +0 -372
  37. package/dist/tools/events.js +0 -182
  38. package/dist/tools/github.js +0 -564
  39. package/dist/tools/insider.js +0 -264
  40. package/dist/tools/insight.js +0 -630
  41. package/dist/tools/market.js +0 -555
  42. package/dist/tools/memory.js +0 -1059
  43. package/dist/tools/miroshark.js +0 -350
  44. package/dist/tools/monitor.js +0 -319
  45. package/dist/tools/os.js +0 -236
  46. package/dist/tools/packets.js +0 -296
  47. package/dist/tools/research-chain.js +0 -226
  48. package/dist/tools/research-compare.js +0 -280
  49. package/dist/tools/research.js +0 -188
  50. package/dist/tools/rh-bridge.js +0 -148
  51. package/dist/tools/rh-mcp.js +0 -1448
  52. package/dist/tools/rh-orders.js +0 -556
  53. package/dist/tools/scanner.js +0 -564
  54. package/dist/tools/stake.js +0 -369
  55. package/dist/tools/vault.js +0 -1020
  56. package/dist/tools/wallet.js +0 -200
  57. package/dist/types.js +0 -2
  58. package/dist/wallet.js +0 -372
@@ -1,369 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.STAKE_TOOLS = void 0;
4
- exports.parseStakeAmount = parseStakeAmount;
5
- exports.handleStakeTool = handleStakeTool;
6
- const ethers_1 = require("ethers");
7
- const convex_js_1 = require("../convex.js");
8
- const config_js_1 = require("../config.js");
9
- const rh_mcp_js_1 = require("./rh-mcp.js");
10
- // FINCH staking - MCP wrapper around the EXISTING app/convex staking actions
11
- // (stake.ts's stakeFINCH/unstakeFINCH, stakeInternal.ts's myStakingStats/
12
- // globalStats). No staking logic lives here - this only parses a
13
- // human-friendly amount ("all", "max", "50%", "1m", "100k", a raw number)
14
- // into wei and calls the backend, exactly like the webapp's Stake page does.
15
- //
16
- // IMPORTANT - custodial wallet, not the local signing wallet: unlike
17
- // base_mcp_swap/rh_mcp_swap (which sign and broadcast with the LOCAL
18
- // ~/.finch/wallet.json key and need no login), staking moves FINCH from the
19
- // account's CUSTODIAL wallet (the one shown on the webapp's Wallet page) -
20
- // stake.ts's actions resolve identity via a real logged-in session, not a
21
- // wallet signature. Requires `finch login` first; there is no way around
22
- // this without changing stake.ts's own session-based design, which is out
23
- // of scope here. The local wallet and the custodial wallet are DIFFERENT
24
- // addresses with different balances - never conflate them in output.
25
- //
26
- // SECURITY: no tool here ever returns a private key or seed phrase, and
27
- // none ever will - the custodial key stays server-side (Turnkey-enclave /
28
- // encrypted-at-rest), exactly as it already does for the webapp. "Log in,
29
- // then stake" is the ceiling of what this integration does; it does not,
30
- // and must not, add an export path.
31
- const FINCH_CA = "0xce1981b0431fb495912cab057d2877a290199824";
32
- const FINCH_DECIMALS = 18;
33
- const ERC20_BALANCE_ABI = ["function balanceOf(address) view returns (uint256)"];
34
- // Mirrors app/convex/stake.ts's STAKING_TIERS - fixed constants, not worth a
35
- // round-trip to the backend to fetch. Keep in sync if the backend tiers change.
36
- const STAKING_TIERS = {
37
- 7: { multiplier: 1.0, label: "Flexible" },
38
- 30: { multiplier: 1.5, label: "Committed" },
39
- 90: { multiplier: 2.0, label: "Diamond Hands" },
40
- };
41
- const VALID_LOCK_TIERS = Object.keys(STAKING_TIERS).map(Number);
42
- function requireLogin() {
43
- const token = (0, config_js_1.getSavedToken)();
44
- return token ? { token } : null;
45
- }
46
- const NOT_LOGGED_IN_MSG = "Staking needs a logged-in session (not just the local wallet). Run `finch login` first, then retry.";
47
- async function finchBalanceOf(address) {
48
- const provider = await (0, rh_mcp_js_1.rhProviderAsync)();
49
- const finch = new ethers_1.ethers.Contract(FINCH_CA, ERC20_BALANCE_ABI, provider);
50
- return await finch.balanceOf(address);
51
- }
52
- // Parses "all" | "max" | "50%" | "1m" | "100k" | "100000" | "value 100000"
53
- // against a live balance. Returns wei as a string, or throws with a message
54
- // the caller can show verbatim.
55
- function parseStakeAmount(input, balanceWei) {
56
- const raw = input.trim().toLowerCase().replace(/^value\s+/, "");
57
- if (raw === "all" || raw === "max") {
58
- if (balanceWei <= 0n)
59
- throw new Error("Wallet has 0 FINCH - nothing to stake.");
60
- return balanceWei;
61
- }
62
- const pctMatch = raw.match(/^(\d+(?:\.\d+)?)\s*%$/);
63
- if (pctMatch) {
64
- const pct = parseFloat(pctMatch[1]);
65
- if (!(pct > 0) || pct > 100)
66
- throw new Error("Percentage must be between 0 and 100.");
67
- return (balanceWei * BigInt(Math.round(pct * 100))) / 10000n;
68
- }
69
- const suffixMatch = raw.match(/^(\d+(?:\.\d+)?)\s*([km])$/);
70
- if (suffixMatch) {
71
- const n = parseFloat(suffixMatch[1]);
72
- const mult = suffixMatch[2] === "m" ? 1000000 : 1000;
73
- return ethers_1.ethers.parseUnits((n * mult).toString(), FINCH_DECIMALS);
74
- }
75
- const plainMatch = raw.match(/^(\d+(?:\.\d+)?)$/);
76
- if (plainMatch) {
77
- return ethers_1.ethers.parseUnits(plainMatch[1], FINCH_DECIMALS);
78
- }
79
- throw new Error(`Could not parse amount "${input}". Use "all", "max", a percentage like "50%", ` +
80
- `a shorthand like "1m" or "100k", or a plain number like "100000".`);
81
- }
82
- function fmtFinch(wei) {
83
- return `${Number(ethers_1.ethers.formatUnits(wei, FINCH_DECIMALS)).toLocaleString("en-US", { maximumFractionDigits: 4 })} FINCH`;
84
- }
85
- exports.STAKE_TOOLS = [
86
- {
87
- name: "stake_finch_status",
88
- description: "View your FINCH staking status - custodial wallet balance available to stake, your active stakes " +
89
- "(amount, lock-up remaining, accrued rewards), and pool-wide stats (total staked, daily USDG reward). " +
90
- "Requires `finch login` (stakes live on your account's custodial wallet, not the local MCP signing wallet).",
91
- inputSchema: { type: "object", properties: {}, required: [] },
92
- },
93
- {
94
- name: "stake_finch",
95
- description: "Stake FINCH from your custodial wallet (the one shown on the webapp's Wallet page) to earn USDG " +
96
- "rewards. Locks for a fixed period chosen at stake time - unstake is blocked until then, and the " +
97
- "tier cannot be changed mid-stake. Longer locks earn a higher reward multiplier: " +
98
- "7 days = 1.0x (default) Β· 30 days = 1.5x Β· 90 days = 2.0x. " +
99
- "Requires `finch login` first and `confirm: true`. Amount accepts \"all\"/\"max\" (entire " +
100
- "available balance), a percentage (\"50%\"), a shorthand (\"1m\", \"100k\"), or a plain number " +
101
- "(\"100000\").",
102
- inputSchema: {
103
- type: "object",
104
- properties: {
105
- amount: {
106
- type: "string",
107
- description: "\"all\" | \"max\" | \"50%\" | \"1m\" | \"100k\" | \"100000\"",
108
- },
109
- lockTier: {
110
- type: "number",
111
- enum: [7, 30, 90],
112
- description: "Lock period in days: 7 (1.0x, default), 30 (1.5x), or 90 (2.0x). Omit for 7-day/1.0x.",
113
- },
114
- confirm: {
115
- type: "boolean",
116
- description: "Must be true to stake - this moves real FINCH out of your custodial wallet.",
117
- },
118
- },
119
- required: ["amount", "confirm"],
120
- },
121
- },
122
- {
123
- name: "unstake_finch",
124
- description: "Unstake FINCH back to your custodial wallet, plus any pending USDG rewards. Only works once the " +
125
- "lock-up period has passed (check stake_finch_status for which stakes are unlockable). Requires " +
126
- "`finch login` and `confirm: true`. Pass a specific stakeId, or \"all\" to unstake every currently " +
127
- "unlockable stake in one call (each processed and reported individually).",
128
- inputSchema: {
129
- type: "object",
130
- properties: {
131
- stakeId: { type: "string", description: "A stake ID from stake_finch_status, or \"all\"" },
132
- confirm: { type: "boolean", description: "Must be true to unstake." },
133
- },
134
- required: ["stakeId", "confirm"],
135
- },
136
- },
137
- {
138
- name: "claim_vested_rewards",
139
- description: "Claim all your fully-vested USDG staking rewards to your custodial wallet. Daily rewards split " +
140
- "50% instant / 50% vested over 3 days - this claims whatever portion has finished vesting across " +
141
- "ALL your stakes in one call (check stake_finch_status for accrued totals; you'll also get a " +
142
- "notification when rewards become claimable). Does nothing to the stake itself - principal stays " +
143
- "staked and earning. Requires `finch login` and `confirm: true`.",
144
- inputSchema: {
145
- type: "object",
146
- properties: {
147
- confirm: { type: "boolean", description: "Must be true to claim - this moves real USDG into your custodial wallet." },
148
- },
149
- required: ["confirm"],
150
- },
151
- },
152
- {
153
- name: "stake_auto_restake",
154
- description: "Turn auto-restake on/off for one of your stakes. When ON, the moment the lock-up ends the stake " +
155
- "is automatically renewed for another full period at the SAME tier - no FINCH ever leaves the " +
156
- "treasury, no gas, no confirmation needed each cycle (a background job on the Finch backend does " +
157
- "this, roughly every 15 minutes). When OFF (default), you instead get a one-time notification that " +
158
- "the stake is unlockable and it just sits there earning nothing until you run unstake_finch. " +
159
- "Does NOT auto-compound rewards into the stake - that would require signing from your custodial " +
160
- "wallet unattended, which this intentionally does not do; claim rewards yourself with " +
161
- "claim_vested_rewards (you'll get a notification when they're ready). Requires `finch login`.",
162
- inputSchema: {
163
- type: "object",
164
- properties: {
165
- stakeId: { type: "string", description: "A stake ID from stake_finch_status" },
166
- enabled: { type: "boolean", description: "true to turn auto-restake on, false to turn it off" },
167
- },
168
- required: ["stakeId", "enabled"],
169
- },
170
- },
171
- ];
172
- async function handleStakeTool(name, args) {
173
- if (name === "stake_finch_status") {
174
- const login = requireLogin();
175
- if (!login)
176
- return { content: [{ type: "text", text: NOT_LOGGED_IN_MSG }], isError: true };
177
- const data = await (0, convex_js_1.callConvex)("/mcp/stake/status", "GET", undefined, "stake_finch_status");
178
- if (data.error)
179
- return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
180
- const address = data.walletAddress ?? null;
181
- let balanceWei = null;
182
- if (address) {
183
- try {
184
- balanceWei = await finchBalanceOf(address);
185
- }
186
- catch { /* balance is a nice-to-have, don't fail the whole status */ }
187
- }
188
- const my = data.myStats;
189
- const global = data.global;
190
- const lines = [
191
- `## FINCH Staking Status`,
192
- ``,
193
- address ? `**Custodial wallet:** \`${address}\` ${balanceWei != null ? `(${fmtFinch(balanceWei)} available to stake)` : ""}` : `_Could not resolve custodial wallet address._`,
194
- ``,
195
- global ? `**Pool:** ${global.totalStakedHuman.toLocaleString()} FINCH staked by ${global.totalStakers} stakers Β· ${global.dailyRewardUsdg} USDG/day pool` : "",
196
- `**Lock tiers:** ${VALID_LOCK_TIERS.map((t) => `${t}d/${STAKING_TIERS[t].multiplier}x (${STAKING_TIERS[t].label})`).join(" Β· ")} β€” pick with \`stake_finch lockTier\`, default 7d/1.0x`,
197
- ``,
198
- my ? `**Your stakes:** ${my.activeStakes} active Β· ${my.totalStaked.toLocaleString()} FINCH staked Β· ${my.totalRewards.toFixed(4)} USDG accrued Β· ${my.canUnstakeCount} unlockable now` : "",
199
- ];
200
- if (my?.stakes?.length) {
201
- lines.push(``, `**Detail:**`);
202
- for (const s of my.stakes) {
203
- const unlocked = Date.now() >= s.unlockAt;
204
- const tier = STAKING_TIERS[s.lockTier ?? 7] ?? STAKING_TIERS[7];
205
- const autoNote = s.status === "staked" ? (s.autoRestake ? " Β· πŸ”„ auto-restake ON" : "") : "";
206
- lines.push(`- \`${s._id}\` β€” ${s.amountHuman} FINCH Β· ${s.lockTier ?? 7}d/${tier.multiplier}x Β· ${s.status}${s.status === "staked" ? (unlocked ? " Β· unlockable now" : ` Β· unlocks ${new Date(s.unlockAt).toISOString().slice(0, 10)}`) : ""}${autoNote}`);
207
- }
208
- }
209
- return { content: [{ type: "text", text: lines.filter(Boolean).join("\n") }] };
210
- }
211
- if (name === "stake_finch") {
212
- const raw = (args ?? {});
213
- const { amount, confirm } = raw;
214
- // Coerce defensively - MCP clients commonly send numeric args as JSON
215
- // strings, and unlike `amount` (a string by design, parsed by
216
- // parseStakeAmount) or `confirm` (unambiguous boolean), a stray
217
- // "30" here must not silently mismatch the numeric VALID_LOCK_TIERS
218
- // check and fall through to the 7-day default with no error.
219
- const lockTier = raw.lockTier === undefined || raw.lockTier === null || raw.lockTier === ""
220
- ? undefined
221
- : Number(raw.lockTier);
222
- if (!amount)
223
- return { content: [{ type: "text", text: "amount is required" }], isError: true };
224
- if (lockTier !== undefined && (!Number.isFinite(lockTier) || !VALID_LOCK_TIERS.includes(lockTier))) {
225
- return {
226
- content: [{ type: "text", text: `Invalid lockTier ${JSON.stringify(raw.lockTier)} - must be one of ${VALID_LOCK_TIERS.join(", ")} (days).` }],
227
- isError: true,
228
- };
229
- }
230
- if (confirm !== true) {
231
- return {
232
- content: [{ type: "text", text: "Refusing to stake without confirmation - pass `confirm: true` after reviewing the amount." }],
233
- isError: true,
234
- };
235
- }
236
- const login = requireLogin();
237
- if (!login)
238
- return { content: [{ type: "text", text: NOT_LOGGED_IN_MSG }], isError: true };
239
- const status = await (0, convex_js_1.callConvex)("/mcp/stake/status", "GET", undefined, "stake_finch_status");
240
- if (status.error)
241
- return { content: [{ type: "text", text: `Error: ${status.error}` }], isError: true };
242
- if (!status.walletAddress)
243
- return { content: [{ type: "text", text: "Could not resolve your custodial wallet address." }], isError: true };
244
- let balanceWei;
245
- try {
246
- balanceWei = await finchBalanceOf(status.walletAddress);
247
- }
248
- catch (e) {
249
- return { content: [{ type: "text", text: `Could not read FINCH balance: ${e?.message ?? e}` }], isError: true };
250
- }
251
- let amountWei;
252
- try {
253
- amountWei = parseStakeAmount(amount, balanceWei);
254
- }
255
- catch (e) {
256
- return { content: [{ type: "text", text: e.message }], isError: true };
257
- }
258
- if (amountWei > balanceWei) {
259
- return {
260
- content: [{ type: "text", text: `Insufficient FINCH. Have ${fmtFinch(balanceWei)}, tried to stake ${fmtFinch(amountWei)}.` }],
261
- isError: true,
262
- };
263
- }
264
- const result = await (0, convex_js_1.callConvex)("/mcp/stake/stake", "POST", { amountWei: amountWei.toString(), lockTier }, "stake_finch", 30000, true);
265
- if (result.error)
266
- return { content: [{ type: "text", text: `Stake failed: ${result.error}` }], isError: true };
267
- const tier = STAKING_TIERS[lockTier ?? 7];
268
- return {
269
- content: [{
270
- type: "text",
271
- text: [
272
- `βœ… Staked ${fmtFinch(amountWei)}`,
273
- `Lock: ${lockTier ?? 7} days (${tier.label}, ${tier.multiplier}x rewards)`,
274
- `Stake ID: \`${result.stakeId}\``,
275
- `Tx: \`${result.txHash}\``,
276
- `${rh_mcp_js_1.RH_EXPLORER}/tx/${result.txHash}`,
277
- ].join("\n"),
278
- }],
279
- };
280
- }
281
- if (name === "unstake_finch") {
282
- const { stakeId, confirm } = (args ?? {});
283
- if (!stakeId)
284
- return { content: [{ type: "text", text: "stakeId is required (or \"all\")" }], isError: true };
285
- if (confirm !== true) {
286
- return {
287
- content: [{ type: "text", text: "Refusing to unstake without confirmation - pass `confirm: true`." }],
288
- isError: true,
289
- };
290
- }
291
- const login = requireLogin();
292
- if (!login)
293
- return { content: [{ type: "text", text: NOT_LOGGED_IN_MSG }], isError: true };
294
- let targetIds;
295
- if (stakeId.toLowerCase() === "all") {
296
- const status = await (0, convex_js_1.callConvex)("/mcp/stake/status", "GET", undefined, "stake_finch_status");
297
- if (status.error)
298
- return { content: [{ type: "text", text: `Error: ${status.error}` }], isError: true };
299
- const now = Date.now();
300
- targetIds = (status.myStats?.stakes ?? [])
301
- .filter((s) => s.status === "staked" && now >= s.unlockAt)
302
- .map((s) => s._id);
303
- if (!targetIds.length) {
304
- return { content: [{ type: "text", text: "No stakes are currently unlockable." }] };
305
- }
306
- }
307
- else {
308
- targetIds = [stakeId];
309
- }
310
- const lines = [];
311
- for (const id of targetIds) {
312
- const result = await (0, convex_js_1.callConvex)("/mcp/stake/unstake", "POST", { stakeId: id }, "unstake_finch", 30000, true);
313
- if (result.error) {
314
- lines.push(`πŸ”΄ \`${id}\`: ${result.error}`);
315
- }
316
- else {
317
- lines.push(`βœ… \`${id}\` unstaked β€” tx \`${result.txHash}\`${result.rewardsTxHash ? ` Β· rewards tx \`${result.rewardsTxHash}\`` : ""}`);
318
- }
319
- }
320
- return { content: [{ type: "text", text: lines.join("\n") }] };
321
- }
322
- if (name === "claim_vested_rewards") {
323
- const { confirm } = (args ?? {});
324
- if (confirm !== true) {
325
- return {
326
- content: [{ type: "text", text: "Refusing to claim without confirmation - pass `confirm: true`." }],
327
- isError: true,
328
- };
329
- }
330
- const login = requireLogin();
331
- if (!login)
332
- return { content: [{ type: "text", text: NOT_LOGGED_IN_MSG }], isError: true };
333
- const result = await (0, convex_js_1.callConvex)("/mcp/stake/claim-rewards", "POST", {}, "claim_vested_rewards", 30000, true);
334
- if (result.error)
335
- return { content: [{ type: "text", text: `Claim failed: ${result.error}` }], isError: true };
336
- return {
337
- content: [{
338
- type: "text",
339
- text: [
340
- `βœ… Claimed ${(result.claimedAmount ?? 0).toFixed(4)} USDG vested rewards`,
341
- `Tx: \`${result.txHash}\``,
342
- `${rh_mcp_js_1.RH_EXPLORER}/tx/${result.txHash}`,
343
- ].join("\n"),
344
- }],
345
- };
346
- }
347
- if (name === "stake_auto_restake") {
348
- const { stakeId, enabled } = (args ?? {});
349
- if (!stakeId)
350
- return { content: [{ type: "text", text: "stakeId is required" }], isError: true };
351
- if (typeof enabled !== "boolean")
352
- return { content: [{ type: "text", text: "enabled (true/false) is required" }], isError: true };
353
- const login = requireLogin();
354
- if (!login)
355
- return { content: [{ type: "text", text: NOT_LOGGED_IN_MSG }], isError: true };
356
- const result = await (0, convex_js_1.callConvex)("/mcp/stake/auto-restake", "POST", { stakeId, enabled }, "stake_auto_restake");
357
- if (result.error)
358
- return { content: [{ type: "text", text: `Failed: ${result.error}` }], isError: true };
359
- return {
360
- content: [{
361
- type: "text",
362
- text: enabled
363
- ? `πŸ”„ Auto-restake ON for \`${stakeId}\`. It'll renew itself for another full period at the same tier every time it unlocks - no action needed from you.`
364
- : `⏸️ Auto-restake OFF for \`${stakeId}\`. You'll get a notification when it unlocks instead, and it'll just sit there until you run \`unstake_finch\`.`,
365
- }],
366
- };
367
- }
368
- return null;
369
- }