@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.
Files changed (50) hide show
  1. package/README.md +74 -39
  2. package/dist/_zod-helpers.js +10 -0
  3. package/dist/cli-doctor.js +300 -0
  4. package/dist/cli-env.js +91 -0
  5. package/dist/cli-install.js +236 -0
  6. package/dist/cli-login.js +101 -0
  7. package/dist/cli-orders.js +175 -0
  8. package/dist/cli-setup.js +270 -0
  9. package/dist/cli-ui.js +168 -0
  10. package/dist/cli-vault.js +75 -0
  11. package/dist/cli.js +61 -1152
  12. package/dist/config.js +3 -2
  13. package/dist/enrichment-router.js +5 -18
  14. package/dist/finch-output.js +30 -29
  15. package/dist/finch-status.js +105 -40
  16. package/dist/local-vault.js +5 -12
  17. package/dist/output-schemas.js +3 -12
  18. package/dist/project.js +4 -13
  19. package/dist/server.js +13 -35
  20. package/dist/tool-filter.js +4 -13
  21. package/dist/tools/_solidity-scan.js +5 -14
  22. package/dist/tools/agents.js +14 -26
  23. package/dist/tools/deep-research-constants.js +33 -0
  24. package/dist/tools/deep-research-firecrawl.js +88 -0
  25. package/dist/tools/deep-research-planning.js +249 -0
  26. package/dist/tools/deep-research-synthesis.js +343 -0
  27. package/dist/tools/deep-research-text.js +158 -0
  28. package/dist/tools/deep-research-tools.js +90 -0
  29. package/dist/tools/deep-research.js +47 -938
  30. package/dist/tools/defi.js +4 -3
  31. package/dist/tools/insider.js +4 -14
  32. package/dist/tools/insight.js +7 -6
  33. package/dist/tools/market.js +16 -15
  34. package/dist/tools/memory.js +43 -61
  35. package/dist/tools/monitor.js +7 -6
  36. package/dist/tools/os.js +7 -85
  37. package/dist/tools/research.js +7 -6
  38. package/dist/tools/rh-mcp-constants.js +65 -0
  39. package/dist/tools/rh-mcp-dex.js +107 -0
  40. package/dist/tools/rh-mcp-provider.js +127 -0
  41. package/dist/tools/rh-mcp-resolve.js +137 -0
  42. package/dist/tools/rh-mcp-risk.js +230 -0
  43. package/dist/tools/rh-mcp-safety.js +234 -0
  44. package/dist/tools/rh-mcp-swap.js +181 -0
  45. package/dist/tools/rh-mcp-tools.js +137 -0
  46. package/dist/tools/rh-mcp.js +75 -1191
  47. package/dist/tools/scanner.js +10 -9
  48. package/dist/tools/stake.js +7 -20
  49. package/dist/tools/vault.js +52 -59
  50. package/package.json +12 -9
@@ -7,6 +7,7 @@ exports.assessTokenSecurity = assessTokenSecurity;
7
7
  exports.handleScannerTool = handleScannerTool;
8
8
  const zod_1 = require("zod");
9
9
  const _http_cache_js_1 = require("../_http-cache.js");
10
+ const _zod_helpers_js_1 = require("../_zod-helpers.js");
10
11
  // ── Constants ─────────────────────────────────────────────────────────────────
11
12
  const DEFAULT_MIN_LIQ = 50000;
12
13
  const DEFAULT_MIN_SCORE = 50;
@@ -343,9 +344,9 @@ exports.SCANNER_TOOLS = [
343
344
  async function handleScannerTool(name, args) {
344
345
  // ── score_token ────────────────────────────────────────────────────────────
345
346
  if (name === "score_token") {
346
- const parsed = ScoreTokenSchema.safeParse(args);
347
- if (!parsed.success)
348
- return { content: [{ type: "text", text: `${parsed.error.issues[0].message}` }], isError: true };
347
+ const parsed = (0, _zod_helpers_js_1.parseOrError)(ScoreTokenSchema, args);
348
+ if (!parsed.ok)
349
+ return parsed.error;
349
350
  const { address, minLiquidity = DEFAULT_MIN_LIQ } = parsed.data;
350
351
  const data = await fetchJson(`https://api.dexscreener.com/latest/dex/tokens/${address}`);
351
352
  const pair = (data.pairs ?? [])
@@ -412,9 +413,9 @@ async function handleScannerTool(name, args) {
412
413
  }
413
414
  // ── check_token ────────────────────────────────────────────────────────────
414
415
  if (name === "check_token") {
415
- const parsed = CheckTokenSchema.safeParse(args);
416
- if (!parsed.success)
417
- return { content: [{ type: "text", text: `${parsed.error.issues[0].message}` }], isError: true };
416
+ const parsed = (0, _zod_helpers_js_1.parseOrError)(CheckTokenSchema, args);
417
+ if (!parsed.ok)
418
+ return parsed.error;
418
419
  const { address } = parsed.data;
419
420
  // GoPlusLabs - chain 8453 = Base mainnet
420
421
  const data = await fetchJson(`https://api.gopluslabs.io/api/v1/token_security/8453?contract_addresses=${address}`);
@@ -474,9 +475,9 @@ async function handleScannerTool(name, args) {
474
475
  }
475
476
  // ── scan_market ────────────────────────────────────────────────────────────
476
477
  if (name === "scan_market") {
477
- const parsed = ScanDipsSchema.safeParse(args ?? {});
478
- if (!parsed.success)
479
- return { content: [{ type: "text", text: `${parsed.error.issues[0].message}` }], isError: true };
478
+ const parsed = (0, _zod_helpers_js_1.parseOrError)(ScanDipsSchema, args ?? {});
479
+ if (!parsed.ok)
480
+ return parsed.error;
480
481
  const input = args;
481
482
  const mode = input?.mode === "momentum" ? "momentum" : "dips";
482
483
  const { minScore = DEFAULT_MIN_SCORE, minLiquidity = DEFAULT_MIN_LIQ, limit = 40 } = parsed.data;
@@ -7,27 +7,14 @@ const ethers_1 = require("ethers");
7
7
  const convex_js_1 = require("../convex.js");
8
8
  const config_js_1 = require("../config.js");
9
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.
10
+ // FINCH staking - MCP wrapper around the existing app/convex staking
11
+ // actions. Parses a human amount ("all", "50%", "1m") into wei and calls
12
+ // the backend.
15
13
  //
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.
14
+ // Uses the CUSTODIAL wallet (session-based, requires `finch login`), not
15
+ // the local signing wallet base_mcp_swap/rh_mcp_swap use - different
16
+ // address, different balance, never conflate. No tool here ever returns a
17
+ // private key or seed phrase; the custodial key stays server-side.
31
18
  const FINCH_CA = "0xce1981b0431fb495912cab057d2877a290199824";
32
19
  const FINCH_DECIMALS = 18;
33
20
  const ERC20_BALANCE_ABI = ["function balanceOf(address) view returns (uint256)"];
@@ -10,6 +10,7 @@ const memory_js_1 = require("./memory.js");
10
10
  const local_vault_js_1 = require("../local-vault.js");
11
11
  const local_memory_js_1 = require("../local-memory.js");
12
12
  const project_js_1 = require("../project.js");
13
+ const _zod_helpers_js_1 = require("../_zod-helpers.js");
13
14
  const VAULT_TYPES = ["research", "execution", "workflow", "prompt", "file", "memory", "code", "credential"];
14
15
  exports.VAULT_TOOLS = [
15
16
  {
@@ -366,9 +367,9 @@ async function handleVaultTool(name, args) {
366
367
  const localVault = (0, local_vault_js_1.getLocalVaultConfig)();
367
368
  switch (name) {
368
369
  case "vault_save": {
369
- const parsed = SaveSchema.safeParse(args);
370
- if (!parsed.success)
371
- return { content: [{ type: "text", text: `${parsed.error.issues[0].message}` }], isError: true };
370
+ const parsed = (0, _zod_helpers_js_1.parseOrError)(SaveSchema, args);
371
+ if (!parsed.ok)
372
+ return parsed.error;
372
373
  if (parsed.data.type === "credential") {
373
374
  // vault_save writes plaintext to disk/DB - "credential" is only a
374
375
  // valid FILTER value for vault_list/search/export (which correctly
@@ -447,9 +448,9 @@ async function handleVaultTool(name, args) {
447
448
  return { content: [{ type: "text", text: lines.join("\n") }] };
448
449
  }
449
450
  case "code_session_save": {
450
- const parsed = CodeSessionSchema.safeParse(args);
451
- if (!parsed.success)
452
- return { content: [{ type: "text", text: `${parsed.error.issues[0].message}` }], isError: true };
451
+ const parsed = (0, _zod_helpers_js_1.parseOrError)(CodeSessionSchema, args);
452
+ if (!parsed.ok)
453
+ return parsed.error;
453
454
  const { project, summary, filesChanged, decisions, nextSteps, tags } = parsed.data;
454
455
  const projectSlug = project.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60) || "session";
455
456
  const key = `code/${projectSlug}`;
@@ -536,9 +537,9 @@ async function handleVaultTool(name, args) {
536
537
  return { content: [{ type: "text", text: lines.join("\n") }] };
537
538
  }
538
539
  case "vault_read": {
539
- const parsed = ReadSchema.safeParse(args);
540
- if (!parsed.success)
541
- return { content: [{ type: "text", text: `${parsed.error.issues[0].message}` }], isError: true };
540
+ const parsed = (0, _zod_helpers_js_1.parseOrError)(ReadSchema, args);
541
+ if (!parsed.ok)
542
+ return parsed.error;
542
543
  const vaultReadKey = parsed.data.key;
543
544
  let data;
544
545
  try {
@@ -619,9 +620,9 @@ async function handleVaultTool(name, args) {
619
620
  return { content: [{ type: "text", text: lines.join("\n") }] };
620
621
  }
621
622
  case "vault_list": {
622
- const parsed = ListSchema.safeParse(args ?? {});
623
- if (!parsed.success)
624
- return { content: [{ type: "text", text: `${parsed.error.issues[0].message}` }], isError: true };
623
+ const parsed = (0, _zod_helpers_js_1.parseOrError)(ListSchema, args ?? {});
624
+ if (!parsed.ok)
625
+ return parsed.error;
625
626
  const params = new URLSearchParams();
626
627
  if (parsed.data.type)
627
628
  params.set("type", parsed.data.type);
@@ -651,20 +652,12 @@ async function handleVaultTool(name, args) {
651
652
  };
652
653
  }
653
654
  case "vault_search": {
654
- const parsed = SearchSchema.safeParse(args);
655
- if (!parsed.success)
656
- return { content: [{ type: "text", text: `${parsed.error.issues[0].message}` }], isError: true };
657
- // Full-text search, proxied through Convex (searchSupermemory is a
658
- // legacy name - it calls Finch's own /memory/search endpoint, not a
659
- // third-party semantic service; there is no embedding step here). Large vault
660
- // entries are indexed as multiple chunks tagged with isVaultChunk +
661
- // vaultKey - group chunks back to their parent entry so the result
662
- // list shows one row per entry, not one row per chunk.
663
- //
664
- // Skipped entirely when vaultBackend is local - a local vault's whole
665
- // point is "no network," so the query string must never leave the
666
- // machine, not even to check for results before falling back to the
667
- // (always-local) full-text branch below.
655
+ const parsed = (0, _zod_helpers_js_1.parseOrError)(SearchSchema, args);
656
+ if (!parsed.ok)
657
+ return parsed.error;
658
+ // Full-text search via Convex (no embeddings). Large entries are
659
+ // chunked (isVaultChunk/vaultKey) - grouped back to one row per entry.
660
+ // Skipped entirely in local-vault mode - query must never leave the machine.
668
661
  if (!localVault) {
669
662
  const limit = parsed.data.limit ?? 20;
670
663
  // Over-fetch so that after chunk dedup we still have ~limit rows.
@@ -752,9 +745,9 @@ async function handleVaultTool(name, args) {
752
745
  };
753
746
  }
754
747
  case "vault_history": {
755
- const parsed = HistorySchema.safeParse(args);
756
- if (!parsed.success)
757
- return { content: [{ type: "text", text: `${parsed.error.issues[0].message}` }], isError: true };
748
+ const parsed = (0, _zod_helpers_js_1.parseOrError)(HistorySchema, args);
749
+ if (!parsed.ok)
750
+ return parsed.error;
758
751
  const histKey = parsed.data.key;
759
752
  let data;
760
753
  try {
@@ -792,9 +785,9 @@ async function handleVaultTool(name, args) {
792
785
  return { content: [{ type: "text", text: [...header, ...rows].join("\n") }] };
793
786
  }
794
787
  case "vault_diff": {
795
- const parsed = DiffSchema.safeParse(args);
796
- if (!parsed.success)
797
- return { content: [{ type: "text", text: `${parsed.error.issues[0].message}` }], isError: true };
788
+ const parsed = (0, _zod_helpers_js_1.parseOrError)(DiffSchema, args);
789
+ if (!parsed.ok)
790
+ return parsed.error;
798
791
  const { key, fromVersion, toVersion } = parsed.data;
799
792
  let data;
800
793
  try {
@@ -836,9 +829,9 @@ async function handleVaultTool(name, args) {
836
829
  return { content: [{ type: "text", text: lines.join("\n") }] };
837
830
  }
838
831
  case "vault_export": {
839
- const parsed = ExportSchema.safeParse(args ?? {});
840
- if (!parsed.success)
841
- return { content: [{ type: "text", text: `${parsed.error.issues[0].message}` }], isError: true };
832
+ const parsed = (0, _zod_helpers_js_1.parseOrError)(ExportSchema, args ?? {});
833
+ if (!parsed.ok)
834
+ return parsed.error;
842
835
  const params = parsed.data.type ? `?type=${parsed.data.type}` : "";
843
836
  const data = localVault
844
837
  ? (0, local_vault_js_1.localVaultExport)(localVault, parsed.data.type)
@@ -855,9 +848,9 @@ async function handleVaultTool(name, args) {
855
848
  return { content: [{ type: "text", text: [...header, ...rows.join("\n\n---\n\n").split("\n")].join("\n") }] };
856
849
  }
857
850
  case "vault_store_credential": {
858
- const parsed = StoreCredentialSchema.safeParse(args);
859
- if (!parsed.success)
860
- return { content: [{ type: "text", text: `${parsed.error.issues[0].message}` }], isError: true };
851
+ const parsed = (0, _zod_helpers_js_1.parseOrError)(StoreCredentialSchema, args);
852
+ if (!parsed.ok)
853
+ return parsed.error;
861
854
  const data = localVault
862
855
  ? (0, local_vault_js_1.localVaultStoreCredential)(localVault, parsed.data.name, parsed.data.value, parsed.data.description)
863
856
  : await (0, convex_js_1.callConvex)("/vault/credential/store", "POST", parsed.data, "vault_store_credential");
@@ -866,9 +859,9 @@ async function handleVaultTool(name, args) {
866
859
  return { content: [{ type: "text", text: `🔐 Credential stored: \`${data.name}\`\nKey: \`${data.key}\`\nRetrieve with: \`vault_get_credential name: "${data.name}"\`` }] };
867
860
  }
868
861
  case "vault_get_credential": {
869
- const parsed = GetCredentialSchema.safeParse(args);
870
- if (!parsed.success)
871
- return { content: [{ type: "text", text: `${parsed.error.issues[0].message}` }], isError: true };
862
+ const parsed = (0, _zod_helpers_js_1.parseOrError)(GetCredentialSchema, args);
863
+ if (!parsed.ok)
864
+ return parsed.error;
872
865
  const params = new URLSearchParams({ name: parsed.data.name });
873
866
  let data;
874
867
  try {
@@ -889,9 +882,9 @@ async function handleVaultTool(name, args) {
889
882
  return { content: [{ type: "text", text: lines.join("\n") }] };
890
883
  }
891
884
  case "vault_pin": {
892
- const parsed = PinSchema.safeParse(args);
893
- if (!parsed.success)
894
- return { content: [{ type: "text", text: `${parsed.error.issues[0].message}` }], isError: true };
885
+ const parsed = (0, _zod_helpers_js_1.parseOrError)(PinSchema, args);
886
+ if (!parsed.ok)
887
+ return parsed.error;
895
888
  const { key, pinned = true } = parsed.data;
896
889
  const data = localVault
897
890
  ? (0, local_vault_js_1.localVaultPin)(localVault, key, pinned)
@@ -901,9 +894,9 @@ async function handleVaultTool(name, args) {
901
894
  return { content: [{ type: "text", text: pinned ? `📌 Pinned: \`${key}\`` : `📌 Unpinned: \`${key}\`` }] };
902
895
  }
903
896
  case "vault_unpublish": {
904
- const parsed = UnpublishSchema.safeParse(args);
905
- if (!parsed.success)
906
- return { content: [{ type: "text", text: `${parsed.error.issues[0].message}` }], isError: true };
897
+ const parsed = (0, _zod_helpers_js_1.parseOrError)(UnpublishSchema, args);
898
+ if (!parsed.ok)
899
+ return parsed.error;
907
900
  // Publishing is a hosted/marketplace concept - a local vault is private
908
901
  // by construction, so there is nothing to retract.
909
902
  if (localVault) {
@@ -922,9 +915,9 @@ async function handleVaultTool(name, args) {
922
915
  };
923
916
  }
924
917
  case "vault_delete": {
925
- const parsed = DeleteSchema.safeParse(args);
926
- if (!parsed.success)
927
- return { content: [{ type: "text", text: `${parsed.error.issues[0].message}` }], isError: true };
918
+ const parsed = (0, _zod_helpers_js_1.parseOrError)(DeleteSchema, args);
919
+ if (!parsed.ok)
920
+ return parsed.error;
928
921
  if (args?.confirm !== true) {
929
922
  return {
930
923
  content: [{
@@ -957,9 +950,9 @@ async function handleVaultTool(name, args) {
957
950
  return { content: [{ type: "text", text: `🗑️ Deleted: \`${parsed.data.key}\` (${data.versionsRemoved ?? 0} versions removed${memoryNote})` }] };
958
951
  }
959
952
  case "vault_tag": {
960
- const parsed = TagSchema.safeParse(args);
961
- if (!parsed.success)
962
- return { content: [{ type: "text", text: `${parsed.error.issues[0].message}` }], isError: true };
953
+ const parsed = (0, _zod_helpers_js_1.parseOrError)(TagSchema, args);
954
+ if (!parsed.ok)
955
+ return parsed.error;
963
956
  const { key, tags, replace = false } = parsed.data;
964
957
  const data = localVault
965
958
  ? (0, local_vault_js_1.localVaultTag)(localVault, key, tags, replace)
@@ -969,9 +962,9 @@ async function handleVaultTool(name, args) {
969
962
  return { content: [{ type: "text", text: `🏷️ Tags ${replace ? "set" : "updated"} on \`${key}\`: ${(data.tags ?? tags).join(", ")}` }] };
970
963
  }
971
964
  case "vault_link": {
972
- const parsed = LinkSchema.safeParse(args);
973
- if (!parsed.success)
974
- return { content: [{ type: "text", text: `${parsed.error.issues[0].message}` }], isError: true };
965
+ const parsed = (0, _zod_helpers_js_1.parseOrError)(LinkSchema, args);
966
+ if (!parsed.ok)
967
+ return parsed.error;
975
968
  const { fromKey, toKey, relation } = parsed.data;
976
969
  const data = localVault
977
970
  ? (0, local_vault_js_1.localVaultLink)(localVault, fromKey, toKey, relation)
@@ -982,9 +975,9 @@ async function handleVaultTool(name, args) {
982
975
  return { content: [{ type: "text", text: `🔗 ${action}: \`${fromKey}\` -[${relation}]→ \`${toKey}\`` }] };
983
976
  }
984
977
  case "vault_related": {
985
- const parsed = RelatedSchema.safeParse(args);
986
- if (!parsed.success)
987
- return { content: [{ type: "text", text: `${parsed.error.issues[0].message}` }], isError: true };
978
+ const parsed = (0, _zod_helpers_js_1.parseOrError)(RelatedSchema, args);
979
+ if (!parsed.ok)
980
+ return parsed.error;
988
981
  const { key, relation } = parsed.data;
989
982
  const params = new URLSearchParams({ key });
990
983
  if (relation)
package/package.json CHANGED
@@ -1,18 +1,22 @@
1
1
  {
2
2
  "name": "@finchagentic/mcp",
3
- "version": "4.7.2",
3
+ "version": "4.7.3",
4
4
  "description": "The runtime layer for Agentic AI. Persistent memory, autonomous agents, and workflows that survive every session.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
7
7
  "finch": "dist/cli.js",
8
- "finch-mcp": "dist/index.js",
9
- "@finchagentic/mcp": "dist/index.js"
8
+ "finch-mcp": "dist/index.js"
10
9
  },
11
10
  "preferGlobal": true,
12
11
  "scripts": {
13
- "smoke": "node scripts/smoke.mjs",
14
- "check:sync": "node scripts/check-registry-sync.mjs",
15
- "test": "npm run smoke && npm run check:sync"
12
+ "build": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && tsc",
13
+ "dev": "ts-node src/index.ts",
14
+ "start": "node dist/index.js",
15
+ "test": "vitest run",
16
+ "test:watch": "vitest",
17
+ "test:mutation": "node scripts/mutation-check.js",
18
+ "prepare": "husky",
19
+ "prepublishOnly": "npm run build && node scripts/version-readme.js"
16
20
  },
17
21
  "lint-staged": {
18
22
  "*.{ts,tsx}": "eslint --fix"
@@ -48,7 +52,7 @@
48
52
  "dependencies": {
49
53
  "@modelcontextprotocol/sdk": "^1.30.0",
50
54
  "ethers": "^6.17.0",
51
- "zod": "^4.6.5"
55
+ "zod": "^4.4.3"
52
56
  },
53
57
  "devDependencies": {
54
58
  "@eslint/js": "^9.39.1",
@@ -66,6 +70,5 @@
66
70
  },
67
71
  "publishConfig": {
68
72
  "access": "public"
69
- },
70
- "mcpName": "io.github.finchagentic/mcp"
73
+ }
71
74
  }