@finchagentic/mcp 4.7.2 → 4.7.3
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/README.md +74 -39
- package/dist/_zod-helpers.js +10 -0
- package/dist/cli-doctor.js +300 -0
- package/dist/cli-env.js +91 -0
- package/dist/cli-install.js +236 -0
- package/dist/cli-login.js +101 -0
- package/dist/cli-orders.js +175 -0
- package/dist/cli-setup.js +270 -0
- package/dist/cli-ui.js +168 -0
- package/dist/cli-vault.js +75 -0
- package/dist/cli.js +61 -1152
- package/dist/config.js +3 -2
- package/dist/enrichment-router.js +5 -18
- package/dist/finch-output.js +30 -29
- package/dist/finch-status.js +105 -40
- package/dist/local-vault.js +5 -12
- package/dist/output-schemas.js +3 -12
- package/dist/project.js +4 -13
- package/dist/server.js +13 -35
- package/dist/tool-filter.js +4 -13
- package/dist/tools/_solidity-scan.js +5 -14
- package/dist/tools/agents.js +14 -26
- package/dist/tools/deep-research-constants.js +33 -0
- package/dist/tools/deep-research-firecrawl.js +88 -0
- package/dist/tools/deep-research-planning.js +249 -0
- package/dist/tools/deep-research-synthesis.js +343 -0
- package/dist/tools/deep-research-text.js +158 -0
- package/dist/tools/deep-research-tools.js +90 -0
- package/dist/tools/deep-research.js +47 -938
- package/dist/tools/defi.js +4 -3
- package/dist/tools/insider.js +4 -14
- package/dist/tools/insight.js +7 -6
- package/dist/tools/market.js +16 -15
- package/dist/tools/memory.js +43 -61
- package/dist/tools/monitor.js +7 -6
- package/dist/tools/os.js +7 -85
- package/dist/tools/research.js +7 -6
- package/dist/tools/rh-mcp-constants.js +65 -0
- package/dist/tools/rh-mcp-dex.js +107 -0
- package/dist/tools/rh-mcp-provider.js +127 -0
- package/dist/tools/rh-mcp-resolve.js +137 -0
- package/dist/tools/rh-mcp-risk.js +230 -0
- package/dist/tools/rh-mcp-safety.js +234 -0
- package/dist/tools/rh-mcp-swap.js +181 -0
- package/dist/tools/rh-mcp-tools.js +137 -0
- package/dist/tools/rh-mcp.js +75 -1191
- package/dist/tools/scanner.js +10 -9
- package/dist/tools/stake.js +7 -20
- package/dist/tools/vault.js +52 -59
- package/package.json +12 -9
package/dist/tools/defi.js
CHANGED
|
@@ -7,6 +7,7 @@ const zod_1 = require("zod");
|
|
|
7
7
|
const convex_js_1 = require("../convex.js");
|
|
8
8
|
const wallet_js_1 = require("../wallet.js");
|
|
9
9
|
const token_decimals_js_1 = require("../token-decimals.js");
|
|
10
|
+
const _zod_helpers_js_1 = require("../_zod-helpers.js");
|
|
10
11
|
exports.DEFI_TOOLS = [
|
|
11
12
|
{
|
|
12
13
|
name: "get_defi_yields",
|
|
@@ -223,9 +224,9 @@ async function handleDefiTool(name, args) {
|
|
|
223
224
|
// list either, so this case was already unreachable dead code - no Tool
|
|
224
225
|
// registers the name "analyze_wallet" for the MCP dispatcher to route to.
|
|
225
226
|
case "get_defi_yields": {
|
|
226
|
-
const parsed =
|
|
227
|
-
if (!parsed.
|
|
228
|
-
return
|
|
227
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(DefiYieldsSchema, args ?? {});
|
|
228
|
+
if (!parsed.ok)
|
|
229
|
+
return parsed.error;
|
|
229
230
|
const { token, minApy = 1, limit = 20 } = parsed.data;
|
|
230
231
|
let pools;
|
|
231
232
|
try {
|
package/dist/tools/insider.js
CHANGED
|
@@ -1,18 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// Insider transactions from SEC Form 4
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// 1. A company's filing feed contains Form 4s where that company is the
|
|
8
|
-
// REPORTING OWNER of a stake in some other issuer. Those are not insider
|
|
9
|
-
// trades in the ticker you asked about, so every filing is checked against
|
|
10
|
-
// `issuerTradingSymbol` before it counts.
|
|
11
|
-
//
|
|
12
|
-
// 2. Most "insider selling" is transaction code F — shares withheld to cover
|
|
13
|
-
// tax on vesting RSUs. It is automatic, not a decision, and carries no
|
|
14
|
-
// signal. Only P (open-market purchase) and S (open-market sale) reflect a
|
|
15
|
-
// choice, so they are reported separately from everything else.
|
|
2
|
+
// Insider transactions from SEC Form 4. Two traps handled: (1) filings
|
|
3
|
+
// where the company is REPORTING OWNER of another issuer are filtered via
|
|
4
|
+
// `issuerTradingSymbol`; (2) code F (tax withholding on RSU vests, no real
|
|
5
|
+
// signal) is separated from P/S (actual buy/sell decisions).
|
|
16
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
7
|
exports.INSIDER_TOOLS = void 0;
|
|
18
8
|
exports.buildInsiderSummary = buildInsiderSummary;
|
package/dist/tools/insight.js
CHANGED
|
@@ -8,6 +8,7 @@ const llm_js_1 = require("../llm.js");
|
|
|
8
8
|
const memory_js_1 = require("./memory.js");
|
|
9
9
|
const enrichment_router_js_1 = require("../enrichment-router.js");
|
|
10
10
|
const signal_gate_js_1 = require("../signal-gate.js");
|
|
11
|
+
const _zod_helpers_js_1 = require("../_zod-helpers.js");
|
|
11
12
|
exports.INSIGHT_TOOLS = [
|
|
12
13
|
{
|
|
13
14
|
name: "ask_finch",
|
|
@@ -449,9 +450,9 @@ async function handleInsightTool(name, args) {
|
|
|
449
450
|
return { content: [{ type: "text", text: contextHeader + finalAnswer }] };
|
|
450
451
|
}
|
|
451
452
|
if (name === "market_thesis") {
|
|
452
|
-
const parsed =
|
|
453
|
-
if (!parsed.
|
|
454
|
-
return
|
|
453
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(MarketThesisSchema, args);
|
|
454
|
+
if (!parsed.ok)
|
|
455
|
+
return parsed.error;
|
|
455
456
|
const { token, context } = parsed.data;
|
|
456
457
|
const priceData = await fetchVerifiedPrice(token);
|
|
457
458
|
// Hard guard: without a verified live price, refuse to generate. Better
|
|
@@ -517,9 +518,9 @@ async function handleInsightTool(name, args) {
|
|
|
517
518
|
};
|
|
518
519
|
}
|
|
519
520
|
if (name === "trade_plan") {
|
|
520
|
-
const parsed =
|
|
521
|
-
if (!parsed.
|
|
522
|
-
return
|
|
521
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(TradePlanSchema, args);
|
|
522
|
+
if (!parsed.ok)
|
|
523
|
+
return parsed.error;
|
|
523
524
|
const { token, side = "long", portfolioSize, riskTolerance = "moderate", timeframe } = parsed.data;
|
|
524
525
|
const priceData = await fetchVerifiedPrice(token);
|
|
525
526
|
// Hard guard: trade plans without verified live price = entry/SL/TP
|
package/dist/tools/market.js
CHANGED
|
@@ -11,6 +11,7 @@ exports.handleMarketTool = handleMarketTool;
|
|
|
11
11
|
const zod_1 = require("zod");
|
|
12
12
|
const _http_cache_js_1 = require("../_http-cache.js");
|
|
13
13
|
const dex_pair_js_1 = require("../dex-pair.js");
|
|
14
|
+
const _zod_helpers_js_1 = require("../_zod-helpers.js");
|
|
14
15
|
const COINGECKO = "https://api.coingecko.com/api/v3";
|
|
15
16
|
const SYMBOL_TO_ID = {
|
|
16
17
|
BTC: "bitcoin", ETH: "ethereum", SOL: "solana", BNB: "binancecoin",
|
|
@@ -291,9 +292,9 @@ async function fetchMarketSnapshot() {
|
|
|
291
292
|
async function handleMarketTool(name, args) {
|
|
292
293
|
switch (name) {
|
|
293
294
|
case "get_market_data": {
|
|
294
|
-
const parsed =
|
|
295
|
-
if (!parsed.
|
|
296
|
-
return
|
|
295
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(GetMarketDataSchema, args ?? {});
|
|
296
|
+
if (!parsed.ok)
|
|
297
|
+
return parsed.error;
|
|
297
298
|
const { token } = parsed.data;
|
|
298
299
|
if (token) {
|
|
299
300
|
const resolved = await resolveTokenId(token);
|
|
@@ -347,9 +348,9 @@ async function handleMarketTool(name, args) {
|
|
|
347
348
|
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
348
349
|
}
|
|
349
350
|
case "get_token_data": {
|
|
350
|
-
const parsed =
|
|
351
|
-
if (!parsed.
|
|
352
|
-
return
|
|
351
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(GetTokenDataSchema, args);
|
|
352
|
+
if (!parsed.ok)
|
|
353
|
+
return parsed.error;
|
|
353
354
|
const q = parsed.data.question;
|
|
354
355
|
// Try to extract a known symbol first, then fall back to search
|
|
355
356
|
const upperQ = q.toUpperCase();
|
|
@@ -379,9 +380,9 @@ async function handleMarketTool(name, args) {
|
|
|
379
380
|
};
|
|
380
381
|
}
|
|
381
382
|
case "compare_tokens": {
|
|
382
|
-
const parsed =
|
|
383
|
-
if (!parsed.
|
|
384
|
-
return
|
|
383
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(CompareTokensSchema, args);
|
|
384
|
+
if (!parsed.ok)
|
|
385
|
+
return parsed.error;
|
|
385
386
|
const syms = parsed.data.tokens.map(t => t.toUpperCase());
|
|
386
387
|
const ids = syms.map(s => SYMBOL_TO_ID[s]).filter(Boolean);
|
|
387
388
|
const unknown = syms.filter(s => !SYMBOL_TO_ID[s]);
|
|
@@ -447,9 +448,9 @@ async function handleMarketTool(name, args) {
|
|
|
447
448
|
return { content: [{ type: "text", text: lines.join("\n") }], structuredContent: buildMarketOverview(global, fg, trendCoins) };
|
|
448
449
|
}
|
|
449
450
|
case "token_history": {
|
|
450
|
-
const parsed =
|
|
451
|
-
if (!parsed.
|
|
452
|
-
return
|
|
451
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(TokenHistorySchema, args);
|
|
452
|
+
if (!parsed.ok)
|
|
453
|
+
return parsed.error;
|
|
453
454
|
const days = parsed.data.days ?? 7;
|
|
454
455
|
const resolved = await resolveTokenId(parsed.data.token);
|
|
455
456
|
if (!resolved)
|
|
@@ -496,9 +497,9 @@ async function handleMarketTool(name, args) {
|
|
|
496
497
|
};
|
|
497
498
|
}
|
|
498
499
|
case "get_base_token_data": {
|
|
499
|
-
const parsed =
|
|
500
|
-
if (!parsed.
|
|
501
|
-
return
|
|
500
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(GetBaseTokenDataSchema, args);
|
|
501
|
+
if (!parsed.ok)
|
|
502
|
+
return parsed.error;
|
|
502
503
|
const address = parsed.data.tokenAddress.toLowerCase();
|
|
503
504
|
const [pair, coingeckoId] = await Promise.all([
|
|
504
505
|
fetchDexscreenerBaseToken(address),
|
package/dist/tools/memory.js
CHANGED
|
@@ -50,6 +50,7 @@ const public_url_js_1 = require("../public-url.js");
|
|
|
50
50
|
const local_vault_js_1 = require("../local-vault.js");
|
|
51
51
|
const local_memory_js_1 = require("../local-memory.js");
|
|
52
52
|
const _text_search_js_1 = require("../_text-search.js");
|
|
53
|
+
const _zod_helpers_js_1 = require("../_zod-helpers.js");
|
|
53
54
|
// memory_extract and memory_consolidate used to run their own LLM calls here
|
|
54
55
|
// (and a matching pair of Convex routes did the same server-side). Both are now
|
|
55
56
|
// two-pass: the tool fetches and stores, the caller decides what the facts are
|
|
@@ -197,7 +198,9 @@ async function syncToSupermemory(content, metadata, sourceUrl) {
|
|
|
197
198
|
// problem is the token/key was rejected. Only genuinely transient errors
|
|
198
199
|
// (network hiccup, the other RRF side still succeeding) degrade silently
|
|
199
200
|
// to an empty contribution - an auth failure is rethrown so it reaches the
|
|
200
|
-
// user instead of being swallowed.
|
|
201
|
+
// user instead of being swallowed. Found live: memory_search/memory_context/
|
|
202
|
+
// memory_insight/memory_consolidate all share this retrieval path, so a
|
|
203
|
+
// single fix here closes the same bug across all four tools.
|
|
201
204
|
function isAuthFailure(err) {
|
|
202
205
|
return err instanceof Error && /Authentication required/i.test(err.message);
|
|
203
206
|
}
|
|
@@ -315,7 +318,7 @@ exports.MEMORY_TOOLS = [
|
|
|
315
318
|
"Unlike vault_save, memory_add is instant: no versioning, no type required. " +
|
|
316
319
|
"Use for notes, decisions, preferences, or anything you want to find later. " +
|
|
317
320
|
"Pass sourceUrl to fetch and index any web page, GitHub repo, or Notion page automatically - " +
|
|
318
|
-
"searchable in ~30s. Retrieval is
|
|
321
|
+
"searchable in ~30s. Retrieval is full-text (keyword) search, not embeddings - " +
|
|
319
322
|
"'what did I say about ETH yield?' finds notes containing those words or close variants, " +
|
|
320
323
|
"not unrelated phrasing with the same meaning. " +
|
|
321
324
|
"Auto-deduplicates: identical content in your recent 50 memories is skipped (override with force:true). " +
|
|
@@ -337,11 +340,11 @@ exports.MEMORY_TOOLS = [
|
|
|
337
340
|
},
|
|
338
341
|
{
|
|
339
342
|
name: "memory_search",
|
|
340
|
-
description: "
|
|
341
|
-
"
|
|
342
|
-
"
|
|
343
|
-
"
|
|
344
|
-
"
|
|
343
|
+
description: "Full-text (keyword) search over your stored memories, with 90-day time-decay weighting so " +
|
|
344
|
+
"recent notes outrank stale ones with similar wording. Good for exact-token lookups (env var " +
|
|
345
|
+
"names, contract addresses, IDs, specific phrases) - it does not understand meaning, so " +
|
|
346
|
+
"'low risk crypto yield' will not match a note phrased as 'conservative DeFi strategies' " +
|
|
347
|
+
"unless the words themselves overlap.",
|
|
345
348
|
inputSchema: {
|
|
346
349
|
type: "object",
|
|
347
350
|
properties: {
|
|
@@ -355,7 +358,7 @@ exports.MEMORY_TOOLS = [
|
|
|
355
358
|
name: "memory_context",
|
|
356
359
|
description: "Retrieve the most relevant memories for a topic, formatted as AI-ready context. " +
|
|
357
360
|
"Use at the start of research tasks to prime with everything stored about a topic. " +
|
|
358
|
-
"
|
|
361
|
+
"Uses full-text (keyword) search, not embeddings - phrase your topic with the words " +
|
|
359
362
|
"you expect were actually used when the memory was saved.",
|
|
360
363
|
inputSchema: {
|
|
361
364
|
type: "object",
|
|
@@ -535,9 +538,9 @@ function buildMemoryList(tag, results) {
|
|
|
535
538
|
async function handleMemoryTool(name, args) {
|
|
536
539
|
switch (name) {
|
|
537
540
|
case "memory_add": {
|
|
538
|
-
const parsed =
|
|
539
|
-
if (!parsed.
|
|
540
|
-
return
|
|
541
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(AddSchema, args);
|
|
542
|
+
if (!parsed.ok)
|
|
543
|
+
return parsed.error;
|
|
541
544
|
const { content, title, tags, sourceUrl, force } = parsed.data;
|
|
542
545
|
// `sourceUrl` is not stored, it is fetched and indexed — by the local
|
|
543
546
|
// memory server on the user's own machine when local mode is on. An
|
|
@@ -581,18 +584,9 @@ async function handleMemoryTool(name, args) {
|
|
|
581
584
|
// dedupes even before supermemory has indexed the first one.
|
|
582
585
|
if (!sourceUrl)
|
|
583
586
|
rememberRecentHash(hash, data?.id ?? "saved", title);
|
|
584
|
-
//
|
|
585
|
-
//
|
|
586
|
-
//
|
|
587
|
-
// autonomous contradiction call would break that. Instead this just
|
|
588
|
-
// resurfaces whatever memory_search's own retrieval already ranks as
|
|
589
|
-
// related to the content just saved, so the CALLING model - already
|
|
590
|
-
// reasoning about this exact save, in the same turn, no extra API
|
|
591
|
-
// cost - can judge for itself whether the two actually conflict and
|
|
592
|
-
// decide what to do (mark the old one superseded, fold both into
|
|
593
|
-
// memory_consolidate, or just note the new one is the current one).
|
|
594
|
-
// Best-effort: a search hiccup here must never fail or block the save
|
|
595
|
-
// that already succeeded above.
|
|
587
|
+
// Conflict hint, not a proof - no LLM call server-side (two-pass, no
|
|
588
|
+
// API key). Resurfaces related existing memories so the calling
|
|
589
|
+
// model decides. Best-effort, must never block the save above.
|
|
596
590
|
let conflictNote = "";
|
|
597
591
|
try {
|
|
598
592
|
const related = await hybridMemorySearch(content, 8);
|
|
@@ -636,9 +630,9 @@ async function handleMemoryTool(name, args) {
|
|
|
636
630
|
};
|
|
637
631
|
}
|
|
638
632
|
case "memory_search": {
|
|
639
|
-
const parsed =
|
|
640
|
-
if (!parsed.
|
|
641
|
-
return
|
|
633
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(SearchSchema, args);
|
|
634
|
+
if (!parsed.ok)
|
|
635
|
+
return parsed.error;
|
|
642
636
|
const { query, limit = 10 } = parsed.data;
|
|
643
637
|
// Over-fetch so post-decay ranking still has enough material.
|
|
644
638
|
const overfetch = Math.min(50, Math.max(limit * 2, 20));
|
|
@@ -718,9 +712,9 @@ async function handleMemoryTool(name, args) {
|
|
|
718
712
|
return { content: [{ type: "text", text: [header, "", ...rows, promotionHint].filter(Boolean).join("\n") }], structuredContent: buildMemorySearch(query, decayed) };
|
|
719
713
|
}
|
|
720
714
|
case "memory_context": {
|
|
721
|
-
const parsed =
|
|
722
|
-
if (!parsed.
|
|
723
|
-
return
|
|
715
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(ContextSchema, args);
|
|
716
|
+
if (!parsed.ok)
|
|
717
|
+
return parsed.error;
|
|
724
718
|
const { topic, limit = 8 } = parsed.data;
|
|
725
719
|
// v3.25.1: use hybrid retrieval so context loading picks up exact-token
|
|
726
720
|
// matches (env var names, model IDs, contract addresses) that semantic-
|
|
@@ -771,16 +765,16 @@ async function handleMemoryTool(name, args) {
|
|
|
771
765
|
`• memory_add (URL indexing) - ✅`,
|
|
772
766
|
`• Google Drive / Gmail / Notion - connect at finchagentic.com`,
|
|
773
767
|
``,
|
|
774
|
-
`**Capabilities:**
|
|
768
|
+
`**Capabilities:** Full-text (keyword) search with 90-day time-decay ranking - not embeddings, no vector search.`,
|
|
775
769
|
].join("\n"),
|
|
776
770
|
}],
|
|
777
771
|
structuredContent: buildMemoryProfile(data),
|
|
778
772
|
};
|
|
779
773
|
}
|
|
780
774
|
case "memory_list": {
|
|
781
|
-
const parsed =
|
|
782
|
-
if (!parsed.
|
|
783
|
-
return
|
|
775
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(ListSchema, args ?? {});
|
|
776
|
+
if (!parsed.ok)
|
|
777
|
+
return parsed.error;
|
|
784
778
|
const { limit = 20, tag } = parsed.data;
|
|
785
779
|
const localList = (0, local_memory_js_1.getLocalMemoryConfig)();
|
|
786
780
|
let results;
|
|
@@ -809,9 +803,9 @@ async function handleMemoryTool(name, args) {
|
|
|
809
803
|
return { content: [{ type: "text", text: [header, "", ...rows].join("\n") }], structuredContent: buildMemoryList(tag, results) };
|
|
810
804
|
}
|
|
811
805
|
case "memory_delete": {
|
|
812
|
-
const parsed =
|
|
813
|
-
if (!parsed.
|
|
814
|
-
return
|
|
806
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(DeleteMemSchema, args);
|
|
807
|
+
if (!parsed.ok)
|
|
808
|
+
return parsed.error;
|
|
815
809
|
if (args?.confirm !== true) {
|
|
816
810
|
return {
|
|
817
811
|
content: [{
|
|
@@ -840,9 +834,9 @@ async function handleMemoryTool(name, args) {
|
|
|
840
834
|
return { content: [{ type: "text", text: `🗑️ Memory deleted: \`${parsed.data.id}\`` }] };
|
|
841
835
|
}
|
|
842
836
|
case "memory_insight": {
|
|
843
|
-
const parsed =
|
|
844
|
-
if (!parsed.
|
|
845
|
-
return
|
|
837
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(InsightSchema, args);
|
|
838
|
+
if (!parsed.ok)
|
|
839
|
+
return parsed.error;
|
|
846
840
|
const { topic, depth = "standard" } = parsed.data;
|
|
847
841
|
const memLimit = depth === "deep" ? 15 : depth === "quick" ? 5 : 8;
|
|
848
842
|
// v3.25.1: hybrid retrieval for memory side (was semantic-only) so the
|
|
@@ -928,9 +922,9 @@ async function handleMemoryTool(name, args) {
|
|
|
928
922
|
return { content: [{ type: "text", text: lines.filter(l => l !== undefined).join("\n") }] };
|
|
929
923
|
}
|
|
930
924
|
case "memory_extract": {
|
|
931
|
-
const parsed =
|
|
932
|
-
if (!parsed.
|
|
933
|
-
return
|
|
925
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(ExtractSchema, args);
|
|
926
|
+
if (!parsed.ok)
|
|
927
|
+
return parsed.error;
|
|
934
928
|
const { text, facts, source = "extract" } = parsed.data;
|
|
935
929
|
// ── PASS 1: hand the text back with the extraction rubric ───────────
|
|
936
930
|
// Deciding what counts as a fact is judgement about *this user's*
|
|
@@ -985,9 +979,9 @@ async function handleMemoryTool(name, args) {
|
|
|
985
979
|
};
|
|
986
980
|
}
|
|
987
981
|
case "memory_consolidate": {
|
|
988
|
-
const parsed =
|
|
989
|
-
if (!parsed.
|
|
990
|
-
return
|
|
982
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(ConsolidateSchema, args);
|
|
983
|
+
if (!parsed.ok)
|
|
984
|
+
return parsed.error;
|
|
991
985
|
const { topic, limit = 12, summary } = parsed.data;
|
|
992
986
|
const localConsolidateCfg = (0, local_memory_js_1.getLocalMemoryConfig)();
|
|
993
987
|
// ── PASS 2: save the caller's merged version ────────────────────────
|
|
@@ -1050,22 +1044,10 @@ async function handleMemoryTool(name, args) {
|
|
|
1050
1044
|
}],
|
|
1051
1045
|
};
|
|
1052
1046
|
}
|
|
1053
|
-
// memory_publish
|
|
1054
|
-
//
|
|
1055
|
-
//
|
|
1056
|
-
//
|
|
1057
|
-
// app/convex/http.ts), so `published` was never actually set true; the
|
|
1058
|
-
// real publishEntry mutation (POST /vault/publish) existed but this tool
|
|
1059
|
-
// never called it. And even a correctly-wired publish would have done
|
|
1060
|
-
// nothing observable: nothing anywhere reads the `published` field for
|
|
1061
|
-
// cross-user browsing - no /vault/community route (dangling comment
|
|
1062
|
-
// only, same pattern as the other removed routes), no backend query for
|
|
1063
|
-
// it, no "Memory Marketplace" page in app/src. The tool asked users to
|
|
1064
|
-
// accept an "IRREVERSIBLE, PUBLIC" risk for a marketplace that doesn't
|
|
1065
|
-
// exist at any layer. Re-add only alongside building the actual
|
|
1066
|
-
// discovery path: a community-browse query + route + UI that reads
|
|
1067
|
-
// `published: true` entries. vault_unpublish is left in place - it's a
|
|
1068
|
-
// correct, harmless no-op until then.
|
|
1047
|
+
// memory_publish removed - broken (silently dropped isPublic/authorName,
|
|
1048
|
+
// never actually set `published`) AND pointless even if fixed - no
|
|
1049
|
+
// Memory Marketplace page/query ever reads that field. vault_unpublish
|
|
1050
|
+
// stays, it's a harmless no-op. Re-add only alongside building discovery.
|
|
1069
1051
|
default:
|
|
1070
1052
|
return null;
|
|
1071
1053
|
}
|
package/dist/tools/monitor.js
CHANGED
|
@@ -5,6 +5,7 @@ exports.buildMonitorList = buildMonitorList;
|
|
|
5
5
|
exports.handleMonitorTool = handleMonitorTool;
|
|
6
6
|
const zod_1 = require("zod");
|
|
7
7
|
const convex_js_1 = require("../convex.js");
|
|
8
|
+
const _zod_helpers_js_1 = require("../_zod-helpers.js");
|
|
8
9
|
const TRIGGER_BASE = "https://api.trigger.dev/api/v1";
|
|
9
10
|
const MONITOR_TASK_ID = "finch-monitor";
|
|
10
11
|
const MONITOR_INPUT_SCHEMA = {
|
|
@@ -104,9 +105,9 @@ function buildMonitorList(schedules, configs = {}) {
|
|
|
104
105
|
}
|
|
105
106
|
async function handleMonitorTool(name, args) {
|
|
106
107
|
if (name === "schedule_research") {
|
|
107
|
-
const parsed =
|
|
108
|
-
if (!parsed.
|
|
109
|
-
return
|
|
108
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(CreateSchema, args);
|
|
109
|
+
if (!parsed.ok)
|
|
110
|
+
return parsed.error;
|
|
110
111
|
const key = getKey();
|
|
111
112
|
if (!key)
|
|
112
113
|
return noKeyMsg();
|
|
@@ -282,9 +283,9 @@ async function handleMonitorTool(name, args) {
|
|
|
282
283
|
}
|
|
283
284
|
}
|
|
284
285
|
if (name === "cancel_monitor") {
|
|
285
|
-
const parsed =
|
|
286
|
-
if (!parsed.
|
|
287
|
-
return
|
|
286
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(CancelSchema, args);
|
|
287
|
+
if (!parsed.ok)
|
|
288
|
+
return parsed.error;
|
|
288
289
|
if (args?.confirm !== true) {
|
|
289
290
|
return {
|
|
290
291
|
content: [{
|
package/dist/tools/os.js
CHANGED
|
@@ -2,20 +2,17 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.OS_TOOLS = void 0;
|
|
4
4
|
exports.handleOsTool = handleOsTool;
|
|
5
|
-
const convex_js_1 = require("../convex.js");
|
|
6
|
-
const token_gate_js_1 = require("../token-gate.js");
|
|
7
|
-
const wallet_js_1 = require("../wallet.js");
|
|
8
5
|
const local_memory_js_1 = require("../local-memory.js");
|
|
9
6
|
const config_js_1 = require("../config.js");
|
|
10
7
|
const CONVEX_SITE = process.env.FINCH_CONVEX_URL ?? "https://valuable-fish-533.convex.site";
|
|
8
|
+
// finch_status moved to ../finch-status.ts (Sep 2026) - the old version here
|
|
9
|
+
// made 6 Promise.allSettled backend calls and silently defaulted every field
|
|
10
|
+
// to zero/basic on ANY failure (including a 401), which read as "empty but
|
|
11
|
+
// healthy" instead of surfacing a real auth problem. The new one makes one
|
|
12
|
+
// cheap authed call and reports the real state. Kept out of OS_TOOLS/this
|
|
13
|
+
// switch entirely (not just filtered at list time) so there's exactly one
|
|
14
|
+
// place this tool is defined - server.ts wires it in directly.
|
|
11
15
|
exports.OS_TOOLS = [
|
|
12
|
-
{
|
|
13
|
-
name: "finch_status",
|
|
14
|
-
description: "Full runtime dashboard - memory size, persistent agents, active automations, recent vault research, " +
|
|
15
|
-
"execution scores, and your tier. Like `htop` for your AI runtime. " +
|
|
16
|
-
"Run this to see what's running and what state your runtime is currently holding.",
|
|
17
|
-
inputSchema: { type: "object", properties: {}, required: [] },
|
|
18
|
-
},
|
|
19
16
|
{
|
|
20
17
|
name: "finch_diagnostics",
|
|
21
18
|
description: "Health check for all Finch services - Convex backend, Firecrawl, Supermemory, and configured API keys. " +
|
|
@@ -38,81 +35,6 @@ exports.OS_TOOLS = [
|
|
|
38
35
|
];
|
|
39
36
|
async function handleOsTool(name, args) {
|
|
40
37
|
switch (name) {
|
|
41
|
-
case "finch_status": {
|
|
42
|
-
// Respect local memory mode here too - querying Convex's /memory/profile
|
|
43
|
-
// when writes have been going to a local supermemory server instead
|
|
44
|
-
// would report a stale/zero count (the exact bug memory_profile's own
|
|
45
|
-
// handler was fixed to avoid).
|
|
46
|
-
const localStatusCfg = (0, local_memory_js_1.getLocalMemoryConfig)();
|
|
47
|
-
const memoryProfileCall = localStatusCfg
|
|
48
|
-
? (0, local_memory_js_1.localMemoryProfile)(localStatusCfg)
|
|
49
|
-
: (0, convex_js_1.callConvex)("/memory/profile", "GET", undefined, "memory_profile");
|
|
50
|
-
const [tierResult, walletResult, memRes, autoRes, vaultRes, agentsRes] = await Promise.allSettled([
|
|
51
|
-
(0, token_gate_js_1.getTier)(),
|
|
52
|
-
(0, wallet_js_1.getOrCreateWallet)(),
|
|
53
|
-
memoryProfileCall,
|
|
54
|
-
(0, convex_js_1.callConvex)("/automations/list", "GET", undefined, "list_automations"),
|
|
55
|
-
(0, convex_js_1.callConvex)("/vault/list?type=research&limit=5", "GET", undefined, "vault_list"),
|
|
56
|
-
(0, convex_js_1.callConvex)("/vault/list?type=memory&limit=20", "GET", undefined, "vault_list"),
|
|
57
|
-
]);
|
|
58
|
-
const tier = tierResult.status === "fulfilled" ? tierResult.value : "basic";
|
|
59
|
-
const wallet = walletResult.status === "fulfilled" ? walletResult.value : null;
|
|
60
|
-
const mem = memRes.status === "fulfilled" ? memRes.value : null;
|
|
61
|
-
const autos = autoRes.status === "fulfilled" ? autoRes.value : null;
|
|
62
|
-
const vault = vaultRes.status === "fulfilled" ? vaultRes.value : null;
|
|
63
|
-
const agents = agentsRes.status === "fulfilled" ? agentsRes.value : null;
|
|
64
|
-
const automations = autos?.automations ?? [];
|
|
65
|
-
const activeAutos = automations.filter((a) => a.status === "active");
|
|
66
|
-
const vaultEntries = vault?.entries ?? [];
|
|
67
|
-
// Persistent agents live in vault as type=memory with key prefix "agent/"
|
|
68
|
-
const persistentAgents = (agents?.entries ?? []).filter((e) => typeof e.key === "string" && e.key.startsWith("agent/"));
|
|
69
|
-
const memTotal = mem?.total ?? 0;
|
|
70
|
-
const memStatus = mem?.status ?? "unknown";
|
|
71
|
-
const tierLabel = tier === "holder"
|
|
72
|
-
? "\u{1F7E2} **Holder** - premium tools unlocked"
|
|
73
|
-
: "⚪ **Basic** - hold FINCH on Base to unlock premium tools";
|
|
74
|
-
const walletShort = wallet
|
|
75
|
-
? `${wallet.address.slice(0, 6)}...${wallet.address.slice(-4)}`
|
|
76
|
-
: "not configured";
|
|
77
|
-
const lines = [
|
|
78
|
-
`**Finch Runtime - System Status**`,
|
|
79
|
-
`────────────────────────────────`,
|
|
80
|
-
``,
|
|
81
|
-
`🔑 **Tier** ${tierLabel}`,
|
|
82
|
-
`👛 **Wallet** ${walletShort}`,
|
|
83
|
-
``,
|
|
84
|
-
`🧠 **Memory** ${memStatus === "ok" ? "✅" : "⚠️"} ${memTotal} entries · Space: ${mem?.space ?? "-"}`,
|
|
85
|
-
`🤖 **Agents** ${persistentAgents.length} persistent agent${persistentAgents.length === 1 ? "" : "s"} in vault`,
|
|
86
|
-
`⚡ **Automations** ${activeAutos.length} active of ${automations.length} total`,
|
|
87
|
-
`📚 **Vault** ${vaultEntries.length} recent research entries`,
|
|
88
|
-
``,
|
|
89
|
-
];
|
|
90
|
-
if (activeAutos.length > 0) {
|
|
91
|
-
lines.push(`**Active Automations:**`);
|
|
92
|
-
for (const a of activeAutos.slice(0, 5)) {
|
|
93
|
-
const next = a.nextRunAt ? ` · next ${new Date(a.nextRunAt).toUTCString()}` : "";
|
|
94
|
-
lines.push(` • ${a.name} - ${a.triggerType}${next}`);
|
|
95
|
-
}
|
|
96
|
-
lines.push("");
|
|
97
|
-
}
|
|
98
|
-
if (persistentAgents.length > 0) {
|
|
99
|
-
lines.push(`**Persistent Agents:**`);
|
|
100
|
-
for (const a of persistentAgents.slice(0, 5)) {
|
|
101
|
-
const name = a.key.replace(/^agent\//, "");
|
|
102
|
-
lines.push(` • ${name} - v${a.version ?? 1}`);
|
|
103
|
-
}
|
|
104
|
-
lines.push("");
|
|
105
|
-
}
|
|
106
|
-
if (vaultEntries.length > 0) {
|
|
107
|
-
lines.push(`**Recent Research:**`);
|
|
108
|
-
for (const e of vaultEntries) {
|
|
109
|
-
lines.push(` • [${e.agentId ?? "vault"}] ${e.title}`);
|
|
110
|
-
}
|
|
111
|
-
lines.push("");
|
|
112
|
-
}
|
|
113
|
-
lines.push(`💡 Run \`deep_research query: "..."\` to launch multi-agent research · \`agent_spawn\` to start a persistent agent`);
|
|
114
|
-
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
115
|
-
}
|
|
116
38
|
case "finch_diagnostics": {
|
|
117
39
|
const CONVEX_URL = process.env.FINCH_CONVEX_URL ?? "https://valuable-fish-533.convex.site";
|
|
118
40
|
// Ping root of each service — any non-5xx means the host is up
|
package/dist/tools/research.js
CHANGED
|
@@ -5,6 +5,7 @@ exports.handleResearchTool = handleResearchTool;
|
|
|
5
5
|
const zod_1 = require("zod");
|
|
6
6
|
const convex_js_1 = require("../convex.js");
|
|
7
7
|
const public_url_js_1 = require("../public-url.js");
|
|
8
|
+
const _zod_helpers_js_1 = require("../_zod-helpers.js");
|
|
8
9
|
const FC_BASE = "https://api.firecrawl.dev/v1";
|
|
9
10
|
exports.RESEARCH_TOOLS = [
|
|
10
11
|
{
|
|
@@ -98,9 +99,9 @@ async function basicFetch(url) {
|
|
|
98
99
|
}
|
|
99
100
|
async function handleResearchTool(name, args) {
|
|
100
101
|
if (name === "web_scrape") {
|
|
101
|
-
const parsed =
|
|
102
|
-
if (!parsed.
|
|
103
|
-
return
|
|
102
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(ScrapeSchema, args);
|
|
103
|
+
if (!parsed.ok)
|
|
104
|
+
return parsed.error;
|
|
104
105
|
const { url, focus } = parsed.data;
|
|
105
106
|
const unsafe = await (0, public_url_js_1.assertPublicUrl)(url);
|
|
106
107
|
if (unsafe) {
|
|
@@ -118,9 +119,9 @@ async function handleResearchTool(name, args) {
|
|
|
118
119
|
return { content: [{ type: "text", text: `**${url}**${focusNote}${body}\n\n_Source: ${source}_` }] };
|
|
119
120
|
}
|
|
120
121
|
if (name === "web_search") {
|
|
121
|
-
const parsed =
|
|
122
|
-
if (!parsed.
|
|
123
|
-
return
|
|
122
|
+
const parsed = (0, _zod_helpers_js_1.parseOrError)(SearchSchema, args);
|
|
123
|
+
if (!parsed.ok)
|
|
124
|
+
return parsed.error;
|
|
124
125
|
const { query, limit = 5 } = parsed.data;
|
|
125
126
|
// Priority 1: user BYOK direct path. Priority 2: Finch proxy.
|
|
126
127
|
// Same data shape so the formatting code below is identical for both.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Robinhood Chain (chainId 4663) constants shared across the rh-mcp-* modules.
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.RH_DEX_CHAIN = exports.DEXSCREENER = exports.RH_BLOCKSCOUT_V2 = exports.RH_STOCKS = exports.USDG_DECIMALS = exports.USDG_ADDRESS = exports.RH_UNIVERSAL_ROUTER = exports.RH_PERMIT2 = exports.NATIVE_ETH = exports.RH_EXPLORER = exports.RH_RPC = exports.RH_CHAIN_ID = void 0;
|
|
5
|
+
exports.rhRpcDisplay = rhRpcDisplay;
|
|
6
|
+
exports.RH_CHAIN_ID = 4663;
|
|
7
|
+
exports.RH_RPC = process.env.ROBINHOOD_RPC_URL ??
|
|
8
|
+
process.env.RH_RPC_URL ??
|
|
9
|
+
process.env.FINCH_RH_RPC_URL ??
|
|
10
|
+
"https://rpc.mainnet.chain.robinhood.com";
|
|
11
|
+
/** Host only, for display in tool output - never the full RH_RPC. An
|
|
12
|
+
* operator can point ROBINHOOD_RPC_URL/RH_RPC_URL at a keyed provider
|
|
13
|
+
* (same pattern app/convex just fixed for its own Alchemy-key leak risk -
|
|
14
|
+
* a `.../v2/<key>`-style URL echoed verbatim into a tool response lands the
|
|
15
|
+
* key in chat transcripts/client logs). Falls back to the raw value only
|
|
16
|
+
* if it isn't a parseable URL, which should never happen in practice. */
|
|
17
|
+
function rhRpcDisplay() {
|
|
18
|
+
try {
|
|
19
|
+
return new URL(exports.RH_RPC).host;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return "(configured)";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.RH_EXPLORER = "https://robinhoodchain.blockscout.com";
|
|
26
|
+
exports.NATIVE_ETH = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
|
|
27
|
+
exports.RH_PERMIT2 = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
28
|
+
exports.RH_UNIVERSAL_ROUTER = "0x8876789976decbfcbbbe364623c63652db8c0904";
|
|
29
|
+
/**
|
|
30
|
+
* Canonical Global Dollar (USDG) — RH chain's settlement stablecoin (same
|
|
31
|
+
* address as app/convex/_settlement.ts). Hardcoded rather than resolved via
|
|
32
|
+
* DexScreener ticker search: "USDG" is the exact symbol an imposter contract
|
|
33
|
+
* would spoof, and dexSearchByTicker ranks by pool liquidity, not authenticity
|
|
34
|
+
* — a fake pool with inflated liquidity could otherwise outrank the real token.
|
|
35
|
+
*/
|
|
36
|
+
exports.USDG_ADDRESS = "0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168";
|
|
37
|
+
exports.USDG_DECIMALS = 6;
|
|
38
|
+
/** ClawHood / Finch catalog (22 tokenized stocks — official "Robinhood Token" contracts). */
|
|
39
|
+
exports.RH_STOCKS = [
|
|
40
|
+
{ address: "0xaf3d76f1834a1d425780943c99ea8a608f8a93f9", symbol: "AAPL", name: "Apple" },
|
|
41
|
+
{ address: "0x86923f96303d656e4aa86d9d42d1e57ad2023fdc", symbol: "AMD", name: "AMD" },
|
|
42
|
+
{ address: "0x12f190a9f9d7d37a250758b26824b97ce941bf54", symbol: "AMZN", name: "Amazon" },
|
|
43
|
+
{ address: "0x822cc93ffd030293e9842c30bbd678f530701867", symbol: "BE", name: "Bloom Energy" },
|
|
44
|
+
{ address: "0x6330d8c3178a418788df01a47479c0ce7ccf450b", symbol: "COIN", name: "Coinbase" },
|
|
45
|
+
{ address: "0x5f10a1c971b69e47e059e1dc91901b59b3fb49c3", symbol: "CRWV", name: "CoreWeave" },
|
|
46
|
+
{ address: "0x1b0e319c6a659f002271b69db8a7df2f911c153e", symbol: "GME", name: "GameStop" },
|
|
47
|
+
{ address: "0x2e0847e8910a9732eb3fb1bb4b70a580adad4fe3", symbol: "GOOGL", name: "Alphabet" },
|
|
48
|
+
{ address: "0xc72b96e0e48ecd4dc75e1e45396e26300bc39681", symbol: "INTC", name: "Intel" },
|
|
49
|
+
{ address: "0xc0d6457c16cc70d6790dd43521c899c87ce02f35", symbol: "META", name: "Meta" },
|
|
50
|
+
{ address: "0xe93237c50d904957cf27e7b1133b510c669c2e74", symbol: "MSFT", name: "Microsoft" },
|
|
51
|
+
{ address: "0xff080c8ce2e5feadaca0da81314ae59d232d4afd", symbol: "MU", name: "Micron" },
|
|
52
|
+
{ address: "0xe0444ef8bf4ed74f74fd73686e2ddf4c1c5591e8", symbol: "NFLX", name: "Netflix" },
|
|
53
|
+
{ address: "0xd0601ce157db5bdc3162bbac2a2c8af5320d9eec", symbol: "NVDA", name: "NVIDIA" },
|
|
54
|
+
{ address: "0xb0992820e760d836549ba69bc7598b4af75dee03", symbol: "ORCL", name: "Oracle" },
|
|
55
|
+
{ address: "0x894e1ec2d74ffe5aef8dc8a9e84686accb964f2a", symbol: "PLTR", name: "Palantir" },
|
|
56
|
+
{ address: "0xd5f3879160bc7c32ebb4dc785f8a4f505888de68", symbol: "QQQ", name: "Invesco QQQ" },
|
|
57
|
+
{ address: "0xb90a19ff0af67f7779aff50a882a9cff42446400", symbol: "SNDK", name: "Sandisk" },
|
|
58
|
+
{ address: "0x4a0e65a3eccec6dbe60ae065f2e7bb85fae35eea", symbol: "SPCX", name: "SPACEX" },
|
|
59
|
+
{ address: "0x117cc2133c37b721f49de2a7a74833232b3b4c0c", symbol: "SPY", name: "SPDR S&P 500" },
|
|
60
|
+
{ address: "0x322f0929c4625ed5bad873c95208d54e1c003b2d", symbol: "TSLA", name: "Tesla" },
|
|
61
|
+
{ address: "0xd917b029c761d264c6a312bbbcda868658ef86a6", symbol: "USAR", name: "USA Rare Earth" },
|
|
62
|
+
];
|
|
63
|
+
exports.RH_BLOCKSCOUT_V2 = "https://robinhoodchain.blockscout.com/api/v2";
|
|
64
|
+
exports.DEXSCREENER = "https://api.dexscreener.com";
|
|
65
|
+
exports.RH_DEX_CHAIN = "robinhood";
|