@finchagentic/mcp 4.0.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.
Files changed (57) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +345 -0
  3. package/dist/_http-cache.js +96 -0
  4. package/dist/agent-loop.js +231 -0
  5. package/dist/annotations.js +113 -0
  6. package/dist/cli.js +1195 -0
  7. package/dist/clink-input.js +15 -0
  8. package/dist/config.js +132 -0
  9. package/dist/convex.js +151 -0
  10. package/dist/dex-pair.js +54 -0
  11. package/dist/enrichment-router.js +315 -0
  12. package/dist/index.js +256 -0
  13. package/dist/llm.js +323 -0
  14. package/dist/local-memory.js +102 -0
  15. package/dist/local-vault.js +454 -0
  16. package/dist/output-schemas.js +551 -0
  17. package/dist/prompts.js +111 -0
  18. package/dist/public-url.js +107 -0
  19. package/dist/resources.js +116 -0
  20. package/dist/server.js +300 -0
  21. package/dist/signal-gate.js +57 -0
  22. package/dist/token-decimals.js +26 -0
  23. package/dist/token-gate.js +88 -0
  24. package/dist/tool-filter.js +44 -0
  25. package/dist/tools/_solidity-scan.js +313 -0
  26. package/dist/tools/agents.js +729 -0
  27. package/dist/tools/automation.js +314 -0
  28. package/dist/tools/base-mcp.js +478 -0
  29. package/dist/tools/base.js +269 -0
  30. package/dist/tools/chronicle.js +268 -0
  31. package/dist/tools/coder.js +94 -0
  32. package/dist/tools/deep-research.js +1416 -0
  33. package/dist/tools/defi.js +291 -0
  34. package/dist/tools/equity.js +364 -0
  35. package/dist/tools/events.js +182 -0
  36. package/dist/tools/framework.js +150 -0
  37. package/dist/tools/github.js +514 -0
  38. package/dist/tools/insider.js +264 -0
  39. package/dist/tools/insight.js +634 -0
  40. package/dist/tools/market.js +555 -0
  41. package/dist/tools/memory.js +1046 -0
  42. package/dist/tools/miroshark.js +343 -0
  43. package/dist/tools/monitor.js +319 -0
  44. package/dist/tools/os.js +226 -0
  45. package/dist/tools/packets.js +296 -0
  46. package/dist/tools/research-chain.js +226 -0
  47. package/dist/tools/research-compare.js +280 -0
  48. package/dist/tools/research.js +188 -0
  49. package/dist/tools/rh-bridge.js +148 -0
  50. package/dist/tools/rh-mcp.js +1411 -0
  51. package/dist/tools/rh-orders.js +471 -0
  52. package/dist/tools/scanner.js +534 -0
  53. package/dist/tools/vault.js +764 -0
  54. package/dist/tools/wallet.js +200 -0
  55. package/dist/types.js +2 -0
  56. package/dist/wallet.js +184 -0
  57. package/package.json +87 -0
@@ -0,0 +1,314 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AUTOMATION_TOOLS = void 0;
4
+ exports.buildAutomationList = buildAutomationList;
5
+ exports.buildAutomationRuns = buildAutomationRuns;
6
+ exports.handleAutomationTool = handleAutomationTool;
7
+ const zod_1 = require("zod");
8
+ const convex_js_1 = require("../convex.js");
9
+ const CATEGORY_PATTERNS = [
10
+ ["insufficient_balance", /insufficient|not enough|balance|exceeds|nsf/i],
11
+ ["quote_failed", /quote|0x quote|price impact|liquidity|no route|aggregator/i],
12
+ ["tx_reverted", /revert|transaction failed|out of gas|execution reverted|nonce/i],
13
+ ["rpc_error", /rpc|timeout|network|fetch failed|econnrefused|enotfound|gateway/i],
14
+ ["auth_error", /auth|unauthorized|session|token|forbidden|403|401/i],
15
+ ["config_missing", /not set|missing|required.*env|env.*required|api_key|api key|wallet not/i],
16
+ ];
17
+ const CATEGORY_BADGE = {
18
+ insufficient_balance: "πŸ’Έ INSUFFICIENT_BALANCE",
19
+ quote_failed: "πŸ“‰ QUOTE_FAILED",
20
+ tx_reverted: "πŸ›‘ TX_REVERTED",
21
+ rpc_error: "🌐 RPC_ERROR",
22
+ auth_error: "πŸ” AUTH_ERROR",
23
+ config_missing: "βš™οΈ CONFIG_MISSING",
24
+ unknown: "❓ UNKNOWN",
25
+ };
26
+ const CATEGORY_FIX = {
27
+ insufficient_balance: "Top up the wallet, lower the per-run amount, or check token allowance.",
28
+ quote_failed: "Token may have low liquidity. Try a different route, smaller size, or wait and retry.",
29
+ tx_reverted: "Check token approvals, slippage, and that you have ETH for gas.",
30
+ rpc_error: "Transient. Re-run shortly. Set FINCH_RPC_URL to a faster provider if it persists.",
31
+ auth_error: "Run `finch login` to refresh session, or re-issue the API key.",
32
+ config_missing: "Backend env var likely missing - check finch doctor for the specific config.",
33
+ unknown: "Unrecognized failure. Open get_automation_runs for the raw error and the chronicle log.",
34
+ };
35
+ function categorizeError(err) {
36
+ if (!err)
37
+ return "unknown";
38
+ for (const [cat, pat] of CATEGORY_PATTERNS) {
39
+ if (pat.test(err))
40
+ return cat;
41
+ }
42
+ return "unknown";
43
+ }
44
+ exports.AUTOMATION_TOOLS = [
45
+ {
46
+ name: "create_automation",
47
+ description: "Create an automation in plain English. Supports DCA, price alerts, conditional buys/sells, and recurring market updates.",
48
+ inputSchema: {
49
+ type: "object",
50
+ properties: { rawInput: { type: "string", description: "Plain English description of the automation" } },
51
+ required: ["rawInput"],
52
+ },
53
+ },
54
+ {
55
+ name: "list_automations",
56
+ description: "List all your automations - active, paused, and completed - with status, run counts, and next scheduled run.",
57
+ inputSchema: { type: "object", properties: {}, required: [] },
58
+ },
59
+ {
60
+ name: "pause_automation",
61
+ description: "Pause or resume an automation by ID.",
62
+ inputSchema: {
63
+ type: "object",
64
+ properties: { automationId: { type: "string", description: "Automation ID (from list_automations)" } },
65
+ required: ["automationId"],
66
+ },
67
+ },
68
+ {
69
+ name: "delete_automation",
70
+ description: "PERMANENT. Delete an automation β€” this cannot be undone. Requires confirm: true. " +
71
+ "Run list_automations first and show the user which automation (id + name) you are about to " +
72
+ "remove. If they only want it to stop running, pause_automation is reversible.",
73
+ inputSchema: {
74
+ type: "object",
75
+ properties: {
76
+ automationId: { type: "string", description: "Automation ID (from list_automations)" },
77
+ confirm: { type: "boolean", description: "Must be true to delete. Guards against irreversible loss." },
78
+ },
79
+ required: ["automationId", "confirm"],
80
+ },
81
+ },
82
+ {
83
+ name: "get_automation_runs",
84
+ description: "Get the execution history for an automation - each run's status (success/failed/skipped), amount spent, tx hash, and error message if any. Useful for debugging why an automation isn't working.",
85
+ inputSchema: {
86
+ type: "object",
87
+ properties: {
88
+ automationId: { type: "string", description: "Automation ID (from list_automations)" },
89
+ limit: { type: "number", description: "Max runs to return (default 20)" },
90
+ },
91
+ required: ["automationId"],
92
+ },
93
+ },
94
+ {
95
+ name: "run_automation",
96
+ description: "Trigger an automation immediately - regardless of its schedule or trigger condition. " +
97
+ "Use to test an automation after creating it, or to run a one-off DCA/swap/alert right now. " +
98
+ "Pass dryRun:true to simulate (no tx broadcast, no funds moved) - useful for verifying config before real money. " +
99
+ "The automation must be active (not paused or deleted). " +
100
+ "Get the ID from list_automations.",
101
+ inputSchema: {
102
+ type: "object",
103
+ properties: {
104
+ automationId: { type: "string", description: "Automation ID to run now (from list_automations)" },
105
+ dryRun: { type: "boolean", description: "Simulate without broadcasting tx. No funds moved. Use to verify config." },
106
+ },
107
+ required: ["automationId"],
108
+ },
109
+ },
110
+ ];
111
+ const CreateAutomationSchema = zod_1.z.object({ rawInput: zod_1.z.string().min(1) });
112
+ const AutomationIdSchema = zod_1.z.object({ automationId: zod_1.z.string().min(1) });
113
+ const RunAutomationSchema = zod_1.z.object({ automationId: zod_1.z.string().min(1), dryRun: zod_1.z.boolean().optional() });
114
+ const RunsSchema = zod_1.z.object({ automationId: zod_1.z.string().min(1), limit: zod_1.z.number().int().min(1).max(100).optional() });
115
+ // ── Structured output builders (schemas in output-schemas.ts) ───────────────
116
+ function buildAutomationList(automations) {
117
+ return {
118
+ count: automations.length,
119
+ automations: automations.map((a) => ({
120
+ id: a._id,
121
+ name: a.name,
122
+ status: a.status ?? null,
123
+ triggerType: a.triggerType ?? null,
124
+ actionType: a.actionType ?? null,
125
+ totalRuns: a.totalRuns ?? 0,
126
+ totalSpentUsd: a.totalSpentUsd ?? 0,
127
+ nextRunAt: a.nextRunAt ?? null,
128
+ lastError: a.lastError ?? null,
129
+ })),
130
+ };
131
+ }
132
+ function buildAutomationRuns(automationId, runs) {
133
+ return {
134
+ automationId,
135
+ count: runs.length,
136
+ runs: runs.map((r) => ({
137
+ status: r.status ?? null,
138
+ triggeredAt: r.triggeredAt ?? null,
139
+ amountUsd: r.amountUsd ?? null,
140
+ txHash: r.txHash ?? null,
141
+ error: r.error ?? null,
142
+ })),
143
+ };
144
+ }
145
+ async function handleAutomationTool(name, args) {
146
+ switch (name) {
147
+ case "create_automation": {
148
+ const parsed = CreateAutomationSchema.safeParse(args);
149
+ if (!parsed.success)
150
+ return { content: [{ type: "text", text: `Invalid input: rawInput ${parsed.error.issues[0].message}` }], isError: true };
151
+ const data = await (0, convex_js_1.callConvex)("/automations/create", "POST", { rawInput: parsed.data.rawInput }, "create_automation");
152
+ if (!data.success)
153
+ return { content: [{ type: "text", text: `Failed: ${data.error}` }], isError: true };
154
+ const triggerLabel = {
155
+ schedule: "⏰ Schedule", price_drop_pct: "πŸ“‰ Price Drop %", price_rise_pct: "πŸ“ˆ Price Rise %",
156
+ price_below: "⬇️ Price Below", price_above: "⬆️ Price Above",
157
+ dominance_below: "πŸ“Š Dominance Below", dominance_above: "πŸ“Š Dominance Above",
158
+ };
159
+ const actionLabel = { swap: "πŸ’± Swap", send: "πŸ“€ Send", alert: "πŸ”” Alert" };
160
+ return {
161
+ content: [{
162
+ type: "text",
163
+ text: [
164
+ `βœ… **Automation Created**`, ``,
165
+ `**Name:** ${data.name}`, `**ID:** \`${data.automationId}\``,
166
+ `**Trigger:** ${triggerLabel[data.triggerType] ?? data.triggerType}`,
167
+ `**Action:** ${actionLabel[data.actionType] ?? data.actionType}`,
168
+ data.priceBaselineUsd ? `**Baseline price:** $${Number(data.priceBaselineUsd).toLocaleString()}` : ``,
169
+ ``, `Use \`list_automations\` to see all your automations.`,
170
+ ].filter(Boolean).join("\n"),
171
+ }],
172
+ };
173
+ }
174
+ case "list_automations": {
175
+ const data = await (0, convex_js_1.callConvex)("/automations/list", "GET", undefined, "list_automations");
176
+ if (data.error)
177
+ return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
178
+ const automations = data.automations ?? [];
179
+ if (!automations.length)
180
+ return { content: [{ type: "text", text: "No automations yet. Use `create_automation` to create one." }], structuredContent: buildAutomationList([]) };
181
+ const statusIcon = { active: "🟒", paused: "⏸️", completed: "βœ…", error: "❌" };
182
+ const lines = [`**Your Automations** (${automations.length})`, ""];
183
+ for (const auto of automations) {
184
+ lines.push(`${statusIcon[auto.status] ?? "β€’"} **${auto.name}** - \`${auto._id}\``);
185
+ lines.push(` Trigger: ${auto.triggerType} | Action: ${auto.actionType} | Runs: ${auto.totalRuns}`);
186
+ if (auto.totalSpentUsd > 0)
187
+ lines.push(` Total spent: $${Number(auto.totalSpentUsd).toFixed(2)}`);
188
+ if (auto.nextRunAt && auto.status === "active")
189
+ lines.push(` Next run: ${new Date(auto.nextRunAt).toUTCString()}`);
190
+ if (auto.lastError) {
191
+ const cat = categorizeError(auto.lastError);
192
+ lines.push(` ⚠️ Last error [${CATEGORY_BADGE[cat]}]: ${auto.lastError}`);
193
+ }
194
+ lines.push("");
195
+ }
196
+ return { content: [{ type: "text", text: lines.join("\n") }], structuredContent: buildAutomationList(automations) };
197
+ }
198
+ case "pause_automation": {
199
+ const parsed = AutomationIdSchema.safeParse(args);
200
+ if (!parsed.success)
201
+ return { content: [{ type: "text", text: `Invalid input: automationId ${parsed.error.issues[0].message}` }], isError: true };
202
+ const data = await (0, convex_js_1.callConvex)("/automations/pause", "POST", { automationId: parsed.data.automationId }, "pause_automation");
203
+ if (data.error)
204
+ return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
205
+ const icon = data.status === "active" ? "▢️ Resumed" : "⏸️ Paused";
206
+ return { content: [{ type: "text", text: `${icon} successfully.` }] };
207
+ }
208
+ case "delete_automation": {
209
+ const parsed = AutomationIdSchema.safeParse(args);
210
+ if (!parsed.success)
211
+ return { content: [{ type: "text", text: `Invalid input: automationId ${parsed.error.issues[0].message}` }], isError: true };
212
+ if (args?.confirm !== true) {
213
+ return {
214
+ content: [{
215
+ type: "text",
216
+ text: "Refusing to delete: this permanently removes the automation and cannot be undone. " +
217
+ "Show the user which automation this is, then pass `confirm: true`. To stop it " +
218
+ "without losing it, use `pause_automation`.",
219
+ }],
220
+ isError: true,
221
+ };
222
+ }
223
+ const data = await (0, convex_js_1.callConvex)("/automations/delete", "POST", { automationId: parsed.data.automationId }, "delete_automation");
224
+ if (data.error)
225
+ return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
226
+ return { content: [{ type: "text", text: "πŸ—‘οΈ Automation deleted." }] };
227
+ }
228
+ case "get_automation_runs": {
229
+ const parsed = RunsSchema.safeParse(args);
230
+ if (!parsed.success)
231
+ return { content: [{ type: "text", text: `Invalid input: automationId ${parsed.error.issues[0].message}` }], isError: true };
232
+ const { automationId, limit = 20 } = parsed.data;
233
+ const qs = `automationId=${encodeURIComponent(automationId)}&limit=${limit}`;
234
+ const data = await (0, convex_js_1.callConvex)(`/automations/runs?${qs}`, "GET", undefined, "get_automation_runs");
235
+ if (data.error)
236
+ return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
237
+ const runs = data.runs ?? [];
238
+ if (!runs.length)
239
+ return { content: [{ type: "text", text: "No runs yet for this automation." }], structuredContent: buildAutomationRuns(automationId, []) };
240
+ const statusIcon = { success: "βœ…", failed: "❌", skipped: "⏭️" };
241
+ const lines = [`**Run History** (${runs.length} shown)`, ""];
242
+ // Track failure breakdown so we can show a "top failure cause" line at the bottom.
243
+ const failureCounts = new Map();
244
+ for (const r of runs) {
245
+ const icon = statusIcon[r.status] ?? "β€’";
246
+ const time = new Date(r.triggeredAt).toUTCString();
247
+ const spent = r.amountUsd != null ? ` Β· $${Number(r.amountUsd).toFixed(2)}` : "";
248
+ const tx = r.txHash ? ` Β· [tx](https://basescan.org/tx/${r.txHash})` : "";
249
+ lines.push(`${icon} **${r.status}**${spent}${tx} - ${time}`);
250
+ if (r.error) {
251
+ const cat = categorizeError(r.error);
252
+ failureCounts.set(cat, (failureCounts.get(cat) ?? 0) + 1);
253
+ lines.push(` ${CATEGORY_BADGE[cat]} ${r.error}`);
254
+ }
255
+ }
256
+ if (failureCounts.size > 0) {
257
+ const top = [...failureCounts.entries()].sort((a, b) => b[1] - a[1])[0];
258
+ lines.push("", `**Top failure cause:** ${CATEGORY_BADGE[top[0]]} (${top[1]} run${top[1] !== 1 ? "s" : ""})`);
259
+ lines.push(`β†’ ${CATEGORY_FIX[top[0]]}`);
260
+ }
261
+ return { content: [{ type: "text", text: lines.join("\n") }], structuredContent: buildAutomationRuns(automationId, runs) };
262
+ }
263
+ case "run_automation": {
264
+ const parsed = RunAutomationSchema.safeParse(args);
265
+ if (!parsed.success)
266
+ return { content: [{ type: "text", text: `Invalid input: automationId ${parsed.error.issues[0].message}` }], isError: true };
267
+ const { automationId, dryRun } = parsed.data;
268
+ const data = await (0, convex_js_1.callConvex)("/automations/run", "POST", { automationId, dryRun: !!dryRun }, "run_automation");
269
+ if (data.error) {
270
+ const cat = categorizeError(data.error);
271
+ return {
272
+ content: [{
273
+ type: "text",
274
+ text: [
275
+ `❌ **Automation run failed** - ${CATEGORY_BADGE[cat]}`,
276
+ ``,
277
+ `Error: ${data.error}`,
278
+ ``,
279
+ `β†’ ${CATEGORY_FIX[cat]}`,
280
+ ].join("\n"),
281
+ }],
282
+ isError: true,
283
+ };
284
+ }
285
+ const statusIcon = { success: "βœ…", failed: "❌", skipped: "⏭️" };
286
+ const icon = dryRun ? "πŸ§ͺ" : (statusIcon[data.status] ?? "⚑");
287
+ const spent = data.amountUsd != null ? ` Β· $${Number(data.amountUsd).toFixed(2)}${dryRun ? " simulated" : " spent"}` : "";
288
+ const txLine = data.txHash ? `\nTx: https://basescan.org/tx/${data.txHash}` : "";
289
+ const dryRunNote = dryRun ? `\n${`πŸ§ͺ **DRY RUN** - no transaction broadcast, no funds moved.`}` : "";
290
+ // If backend signaled failure inside the payload, surface the category too.
291
+ let failureLine = "";
292
+ if (data.status === "failed" && data.error) {
293
+ const cat = categorizeError(data.error);
294
+ failureLine = `\n\n${CATEGORY_BADGE[cat]} β†’ ${CATEGORY_FIX[cat]}`;
295
+ }
296
+ return {
297
+ content: [{
298
+ type: "text",
299
+ text: [
300
+ `${icon} **Automation triggered: ${data.status ?? (dryRun ? "simulated" : "executed")}**${spent}`,
301
+ dryRunNote,
302
+ data.message ?? "",
303
+ txLine,
304
+ failureLine,
305
+ ``,
306
+ `Use \`get_automation_runs automationId: "${automationId}"\` to see full history.`,
307
+ ].filter(Boolean).join("\n"),
308
+ }],
309
+ };
310
+ }
311
+ default:
312
+ return null;
313
+ }
314
+ }