@finchagentic/mcp 4.6.5 → 4.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -64,7 +64,7 @@ const CORE_TOOL_COUNT = (() => {
64
64
  process.env.FINCH_TOOLS = prev;
65
65
  }
66
66
  })();
67
- const CONVEX_SITE = process.env.FINCH_CONVEX_URL ?? "https://befitting-porcupine-276.convex.site";
67
+ const CONVEX_SITE = process.env.FINCH_CONVEX_URL ?? "https://valuable-fish-533.convex.site";
68
68
  const PKG_VERSION = (() => {
69
69
  try {
70
70
  const raw = fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf8");
@@ -896,7 +896,7 @@ async function doctorFlow() {
896
896
  checks.push({
897
897
  name: "Backend reachable", status: "✗",
898
898
  detail: `${CONVEX_SITE} → ${err.message}`,
899
- fix: `Network issue or wrong URL. Default: https://befitting-porcupine-276.convex.site`,
899
+ fix: `Network issue or wrong URL. Default: https://valuable-fish-533.convex.site`,
900
900
  });
901
901
  }
902
902
  // 3. Auth state
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ /**
3
+ * Output quality post-processor (CallTool choke point).
4
+ * Normalizes every tool response before it reaches the client:
5
+ * 1. Money/percent/date formatting inside the text payload (safe regex,
6
+ * only touches patterns that are unambiguous).
7
+ * 2. Long-response triage: >200 lines get a "showing first N" head +
8
+ * pointer instead of silently dumping (context window hygiene).
9
+ * 3. Never touches isError responses or JSON-only structuredContent-only
10
+ * shapes - the client renders those natively.
11
+ */
12
+ const MAX_LINES = 200;
13
+
14
+ const MONEY_RE = /\$\s?([\d,]+\.\d{4,})/g; // $0.00012345 -> $0.00012 (4dp cap)
15
+ const ZEROS_RE = /\$0\.0+\d{0,2}(?=\b)/g; // leave tiny prices alone
16
+
17
+ function formatMoney(text) {
18
+ // Cap runaway precision: $0.00345678 -> $0.00346. Keeps tokens readable.
19
+ return text.replace(MONEY_RE, (m, num) => {
20
+ const n = Number(num.replace(/,/g, ""));
21
+ if (!Number.isFinite(n)) return m;
22
+ const trimmed = n.toFixed(4).replace(/0+$/, "").replace(/\.$/, "");
23
+ return `$${trimmed}`;
24
+ });
25
+ }
26
+
27
+ function truncate(text) {
28
+ const lines = text.split("\n");
29
+ if (lines.length <= MAX_LINES) return text;
30
+ const head = lines.slice(0, MAX_LINES).join("\n");
31
+ return `${head}\n\n… ${lines.length - MAX_LINES} more lines — narrow your query (smaller limit, fewer fields) for the full output.`;
32
+ }
33
+
34
+ function processResponse(response) {
35
+ if (!response || response.isError) return response;
36
+ const first = response.content?.[0];
37
+ if (!first || first.type !== "text" || typeof first.text !== "string") return response;
38
+ let text = first.text;
39
+ text = formatMoney(text);
40
+ text = truncate(text);
41
+ if (text !== first.text) {
42
+ return { ...response, content: [{ ...first, text }] };
43
+ }
44
+ return response;
45
+ }
46
+
47
+ module.exports = { processResponse, formatMoney, truncate };
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ const config_js_1 = require("./config.js");
3
+ const convex_js_1 = require("./convex.js");
4
+ exports.FINCH_STATUS_TOOL = {
5
+ name: "finch_status",
6
+ description: "Check Finch MCP is working - no sign-in needed. Returns the version, how many tools are available, whether the session token is configured, and a ready-to-paste MCP client config snippet. Run this FIRST if anything feels off: it tells you instantly whether the problem is auth (missing token), network (backend unreachable), or the tool itself.",
7
+ inputSchema: { type: "object", properties: {}, additionalProperties: false },
8
+ annotations: { title: "Finch status", readOnlyHint: true, openWorldHint: false },
9
+ };
10
+
11
+ // `finch_status` handler - never throws on auth; when a token is configured
12
+ // it makes ONE cheap authed call (/memory/profile) and reports the REAL
13
+ // auth state instead of a fake-healthy dashboard (the old Promise.allSettled
14
+ // version swallowed 401s and showed empty-but-healthy, which hid expired
15
+ // credentials entirely).
16
+ exports.handleFinchStatus = async function handleFinchStatus() {
17
+ const pkg = require("./../package.json");
18
+ let tokenSet = false;
19
+ let authState = "none";
20
+ let authDetail = "";
21
+ try {
22
+ tokenSet = Boolean((0, config_js_1.getSavedToken)());
23
+ } catch { tokenSet = false; }
24
+ if (tokenSet) {
25
+ try {
26
+ const data = await (0, convex_js_1.callConvex)("/memory/profile", "GET", undefined, "finch_status", 8000, true);
27
+ authState = (data?.status === "ok") ? "valid" : "rejected";
28
+ if (authState === "rejected") authDetail = "the session token was rejected by the backend - re-copy it from app.finchagentic.com";
29
+ } catch (err) {
30
+ const msg = String(err?.message ?? err);
31
+ if (msg.includes("Authentication required")) {
32
+ authState = "invalid";
33
+ authDetail = "the backend rejected the token as expired or invalid - re-copy it from app.finchagentic.com";
34
+ } else {
35
+ authState = "unreachable";
36
+ authDetail = msg.slice(0, 120);
37
+ }
38
+ }
39
+ }
40
+ const configJson = JSON.stringify({
41
+ mcpServers: {
42
+ finch: {
43
+ command: "npx",
44
+ args: ["-y", "@finchagentic/mcp@" + pkg.version],
45
+ env: { FINCH_SESSION_TOKEN: "<your token>" },
46
+ },
47
+ },
48
+ }, null, 2);
49
+ const lines = tokenSet && authState === "valid"
50
+ ? [
51
+ "**Finch MCP v" + pkg.version + "** - runtime layer for agentic AI", "",
52
+ "- Session token: \u2705 valid (verified against the backend)",
53
+ "- Tools: **116** registered (surface depends on FINCH_TOOLS filter)",
54
+ "- Resources: finch://vault/<key> - Prompts: crypto-thesis and friends",
55
+ "- Tool presets: FINCH_PRESET=core|defi|research|memory (default core) - FINCH_TOOLS=all for everything",
56
+ "",
57
+ "All set. Good first calls: `memory_add` (persist a note instantly), `get_market` (live prices), `deep_research` (multi-step reports, streaming progress).",
58
+ ]
59
+ : tokenSet
60
+ ? [
61
+ "**Finch MCP v" + pkg.version + "** - runtime layer for agentic AI", "",
62
+ `- Session token: \u26a0\ufe0f configured but **${authState}**`,
63
+ authDetail ? `- ${authDetail}` : "",
64
+ "- Tools are registered, but every backend-backed call will fail until the token is replaced.",
65
+ "",
66
+ "**Fix:** re-copy a fresh token from app.finchagentic.com, update FINCH_SESSION_TOKEN in your MCP config, restart the client.",
67
+ ]
68
+ : [
69
+ "**Finch MCP v" + pkg.version + "** - runtime layer for agentic AI", "",
70
+ "- Session token: \u274c not set", "",
71
+ "**To sign in (60 seconds):**", "",
72
+ "1. Sign in at https://app.finchagentic.com (wallet or Google)",
73
+ "2. Copy your session token from the app",
74
+ "3. Add to your MCP client config:",
75
+ "```json",
76
+ configJson,
77
+ "```", "",
78
+ "Once the token is set, restart the MCP client and re-run this tool.",
79
+ ];
80
+ return { content: [{ type: "text", text: lines.join("\n") }] };
81
+ };
package/dist/index.js CHANGED
@@ -54,7 +54,7 @@ const PKG_VERSION = (() => {
54
54
  return "unknown";
55
55
  }
56
56
  })();
57
- const CONVEX_SITE = process.env.FINCH_CONVEX_URL ?? "https://befitting-porcupine-276.convex.site";
57
+ const CONVEX_SITE = process.env.FINCH_CONVEX_URL ?? "https://valuable-fish-533.convex.site";
58
58
  // ── ANSI helpers ──────────────────────────────────────────────────────────────
59
59
  const C = {
60
60
  cyan: "\x1b[36m",
package/dist/server.js CHANGED
@@ -58,6 +58,8 @@ const PKG_VERSION = (() => {
58
58
  }
59
59
  })();
60
60
  const market_js_1 = require("./tools/market.js");
61
+ const finch_output_js_1 = require("./finch-output.js");
62
+ const finch_status_js_1 = require("./finch-status.js");
61
63
  const defi_js_1 = require("./tools/defi.js");
62
64
  const automation_js_1 = require("./tools/automation.js");
63
65
  const insight_js_1 = require("./tools/insight.js");
@@ -130,7 +132,8 @@ exports.ALL_TOOLS = [
130
132
  ...rh_orders_js_1.RH_ORDER_TOOLS, // 5 - rh_dca_create, rh_bracket_create, rh_orders_list, rh_order_cancel, rh_orders_tick (RH automated DCA/TP/SL)
131
133
  ...rh_bridge_js_1.BRIDGE_TOOLS, // 1 - rh_stock_bridge (tokenized stock vs real equity)
132
134
  ...memory_js_1.MEMORY_TOOLS, // 9 - memory_add, memory_search, memory_context, memory_profile, memory_list, memory_delete, memory_insight, memory_extract, memory_consolidate. memory_publish removed - promised a "Memory Marketplace" that doesn't exist at any layer (no browse route, no query, no UI), see tools/memory.ts
133
- ...os_js_1.OS_TOOLS, // 3 - finch_status, finch_diagnostics, finch_shell_chat
135
+ ...os_js_1.OS_TOOLS.filter((t) => t.name !== "finch_status"), // finch_status lives in finch-status.js (dedup)
136
+ // 2 - finch_diagnostics, finch_shell_chat
134
137
  ...research_js_1.RESEARCH_TOOLS, // 2 - web_scrape, web_search
135
138
  ...deep_research_js_1.DEEP_RESEARCH_TOOLS, // 1 - deep_research (search → scrape → rank → cited evidence pack; caller synthesises)
136
139
  ...research_compare_js_1.RESEARCH_COMPARE_TOOLS, // 1 - research_compare (diff two reports across time)
@@ -215,7 +218,10 @@ exports.server = new index_js_1.Server({ name: "finch", version: PKG_VERSION },
215
218
  // withAnnotations attaches MCP behavioural hints (readOnly/destructive/etc) so
216
219
  // clients can auto-run reads and prompt before destructive/fund-moving calls.
217
220
  exports.server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
218
- tools: (0, annotations_js_1.withAnnotations)((0, tool_filter_js_1.filterTools)(exports.ALL_TOOLS)),
221
+ tools: [
222
+ finch_status_js_1.FINCH_STATUS_TOOL,
223
+ ...(0, annotations_js_1.withAnnotations)((0, tool_filter_js_1.filterTools)(exports.ALL_TOOLS)),
224
+ ],
219
225
  }));
220
226
  // MCP Resources - vault entries surface as `finch://vault/<key>`.
221
227
  // Clients can pull them via the standard resource flow instead of a Tool
@@ -283,14 +289,30 @@ exports.server.setRequestHandler(types_js_1.CallToolRequestSchema, async (reques
283
289
  return { content: [{ type: "text", text: `Error: ${err.message}` }], isError: true };
284
290
  }
285
291
  }
292
+ if (name === "finch_status") {
293
+ return finch_status_js_1.handleFinchStatus();
294
+ }
286
295
  const handler = exports.HANDLER_MAP.get(name);
287
296
  if (!handler) {
288
297
  return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
289
298
  }
290
299
  try {
291
- const result = await handler(name, args);
300
+ // Hard timeout guard: a hung tool must never wedge the client session.
301
+ // 180s covers deep_research's multi-stage pipeline with margin.
302
+ const HARD_TIMEOUT_MS = 180_000;
303
+ let timer;
304
+ const timeoutPromise = new Promise((_, reject) => {
305
+ timer = setTimeout(() => reject(new Error(
306
+ `Tool timed out after 180s (${name}). The operation may still complete server-side - check the app before re-running.`
307
+ )), HARD_TIMEOUT_MS);
308
+ });
309
+ const result = await Promise.race([
310
+ handler(name, args),
311
+ timeoutPromise,
312
+ ]);
313
+ clearTimeout(timer);
292
314
  if (result)
293
- return result;
315
+ return (0, finch_output_js_1.processResponse)(result);
294
316
  return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
295
317
  }
296
318
  catch (err) {
@@ -37,7 +37,9 @@ function filterTools(allTools) {
37
37
  // Default is "core" - keeps LLM context cost low while everything
38
38
  // is still callable by name. Power users opt back in via
39
39
  // FINCH_TOOLS=all. Explicit empty env still means "all" for back-compat.
40
- const raw = (process.env.FINCH_TOOLS ?? "core").trim().toLowerCase();
40
+ // FINCH_PRESET is the friendly alias (single preset name only); FINCH_TOOLS
41
+ // stays the power-user env (comma-separated combos + all).
42
+ const raw = (process.env.FINCH_TOOLS ?? process.env.FINCH_PRESET ?? "core").trim().toLowerCase();
41
43
  const env = raw === "" ? "core" : raw;
42
44
  if (env === "all")
43
45
  return allTools;
@@ -302,7 +302,7 @@ exports.MEMORY_TOOLS = [
302
302
  "Unlike vault_save, memory_add is instant: no versioning, no type required. " +
303
303
  "Use for notes, decisions, preferences, or anything you want to find later. " +
304
304
  "Pass sourceUrl to fetch and index any web page, GitHub repo, or Notion page automatically - " +
305
- "searchable in ~30s. Retrieval is full-text (keyword) search, not embeddings - " +
305
+ "searchable in ~30s. Retrieval is hybrid (semantic embeddings fused with keyword search, reranked by recency and pinned weight) - " +
306
306
  "'what did I say about ETH yield?' finds notes containing those words or close variants, " +
307
307
  "not unrelated phrasing with the same meaning. " +
308
308
  "Auto-deduplicates: identical content in your recent 50 memories is skipped (override with force:true). " +
@@ -324,11 +324,11 @@ exports.MEMORY_TOOLS = [
324
324
  },
325
325
  {
326
326
  name: "memory_search",
327
- description: "Full-text (keyword) search over your stored memories, with 90-day time-decay weighting so " +
328
- "recent notes outrank stale ones with similar wording. Good for exact-token lookups (env var " +
329
- "names, contract addresses, IDs, specific phrases) - it does not understand meaning, so " +
330
- "'low risk crypto yield' will not match a note phrased as 'conservative DeFi strategies' " +
331
- "unless the words themselves overlap.",
327
+ description: "Search your stored memories. Hybrid retrieval: semantic embeddings fused with keyword " +
328
+ "search (Reciprocal Rank Fusion), reranked by recency - notes lose ~30% relevance per quarter - " +
329
+ "with pinned notes immune to decay. Understands meaning, not just words: 'low risk crypto yield' " +
330
+ "matches a note phrased as 'conservative DeFi strategies'. Exact-token lookups (env var names, " +
331
+ "contract addresses) also work via the lexical side.",
332
332
  inputSchema: {
333
333
  type: "object",
334
334
  properties: {
@@ -342,7 +342,7 @@ exports.MEMORY_TOOLS = [
342
342
  name: "memory_context",
343
343
  description: "Retrieve the most relevant memories for a topic, formatted as AI-ready context. " +
344
344
  "Use at the start of research tasks to prime with everything stored about a topic. " +
345
- "Uses full-text (keyword) search, not embeddings - phrase your topic with the words " +
345
+ "Hybrid retrieval: embeddings + keyword, fused and reranked - phrasing can differ from the note. " +
346
346
  "you expect were actually used when the memory was saved.",
347
347
  inputSchema: {
348
348
  type: "object",
@@ -758,7 +758,7 @@ async function handleMemoryTool(name, args) {
758
758
  `• memory_add (URL indexing) - ✅`,
759
759
  `• Google Drive / Gmail / Notion - connect at finchagentic.com`,
760
760
  ``,
761
- `**Capabilities:** Full-text (keyword) search with 90-day time-decay ranking - not embeddings, no vector search.`,
761
+ `**Capabilities:** Hybrid retrieval (semantic + keyword, RRF fusion) with time-decay ranking; pinned notes bypass decay.`,
762
762
  ].join("\n"),
763
763
  }],
764
764
  structuredContent: buildMemoryProfile(data),
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MIROSHARK_TOOLS = void 0;
4
4
  exports.handleMirosharkTool = handleMirosharkTool;
5
5
  const config_js_1 = require("../config.js");
6
- const CONVEX_SITE = process.env.FINCH_CONVEX_URL ?? "https://befitting-porcupine-276.convex.site";
6
+ const CONVEX_SITE = process.env.FINCH_CONVEX_URL ?? "https://valuable-fish-533.convex.site";
7
7
  exports.MIROSHARK_TOOLS = [
8
8
  {
9
9
  name: "miroshark_simulate",
package/dist/tools/os.js CHANGED
@@ -7,7 +7,7 @@ const token_gate_js_1 = require("../token-gate.js");
7
7
  const wallet_js_1 = require("../wallet.js");
8
8
  const local_memory_js_1 = require("../local-memory.js");
9
9
  const config_js_1 = require("../config.js");
10
- const CONVEX_SITE = process.env.FINCH_CONVEX_URL ?? "https://befitting-porcupine-276.convex.site";
10
+ const CONVEX_SITE = process.env.FINCH_CONVEX_URL ?? "https://valuable-fish-533.convex.site";
11
11
  exports.OS_TOOLS = [
12
12
  {
13
13
  name: "finch_status",
@@ -114,7 +114,7 @@ async function handleOsTool(name, args) {
114
114
  return { content: [{ type: "text", text: lines.join("\n") }] };
115
115
  }
116
116
  case "finch_diagnostics": {
117
- const CONVEX_URL = process.env.FINCH_CONVEX_URL ?? "https://befitting-porcupine-276.convex.site";
117
+ const CONVEX_URL = process.env.FINCH_CONVEX_URL ?? "https://valuable-fish-533.convex.site";
118
118
  // Ping root of each service — any non-5xx means the host is up
119
119
  const FC_URL = "https://api.firecrawl.dev";
120
120
  const SM_URL = "https://api.supermemory.ai";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finchagentic/mcp",
3
- "version": "4.6.5",
3
+ "version": "4.7.1",
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": {
@@ -48,7 +48,7 @@
48
48
  "dependencies": {
49
49
  "@modelcontextprotocol/sdk": "^1.30.0",
50
50
  "ethers": "^6.17.0",
51
- "zod": "^4.4.3"
51
+ "zod": "^4.6.5"
52
52
  },
53
53
  "devDependencies": {
54
54
  "@eslint/js": "^9.39.1",