@finchagentic/mcp 4.6.4 → 4.7.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.
package/README.md CHANGED
@@ -37,13 +37,13 @@ Always pin the version. Never use `@latest`.
37
37
 
38
38
  ```bash
39
39
  # One-command installer (detects common MCP clients)
40
- npx -y -p @finchagentic/mcp@4.6.2 finch install
40
+ npx -y @finchagentic/mcp@4.6.5 finch install
41
41
  ```
42
42
 
43
43
  ### Claude Code
44
44
 
45
45
  ```bash
46
- claude mcp add finch -s user -- npx -y -p @finchagentic/mcp@4.6.2 finch-mcp
46
+ claude mcp add finch -s user -- npx -y @finchagentic/mcp@4.6.5
47
47
  ```
48
48
 
49
49
  ### Cursor / Windsurf / Claude Desktop
@@ -62,7 +62,7 @@ claude mcp add finch -s user -- npx -y -p @finchagentic/mcp@4.6.2 finch-mcp
62
62
  ### Hermes
63
63
 
64
64
  ```bash
65
- hermes mcp add finch -- npx -y -p @finchagentic/mcp@4.6.2 finch-mcp
65
+ hermes mcp add finch -- npx -y @finchagentic/mcp@4.6.5
66
66
  ```
67
67
 
68
68
  <details>
@@ -122,7 +122,7 @@ Default palette is `core` (lighter context). Full set:
122
122
  Finch is the runtime. **Your LLM is the brain. Your data stays yours.**
123
123
 
124
124
  ```bash
125
- npx -y -p @finchagentic/mcp@4.6.2 finch setup
125
+ npx -y @finchagentic/mcp@4.6.5 finch setup
126
126
  ```
127
127
 
128
128
  | Piece | Location |
package/dist/cli.js CHANGED
File without changes
@@ -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,49 @@
1
+ "use strict";
2
+ const config_js_1 = require("./config.js");
3
+ exports.FINCH_STATUS_TOOL = {
4
+ name: "finch_status",
5
+ 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.",
6
+ inputSchema: { type: "object", properties: {}, additionalProperties: false },
7
+ annotations: { title: "Finch status", readOnlyHint: true, openWorldHint: false },
8
+ };
9
+
10
+ // `finch_status` handler - fully local, never touches the backend, never auths.
11
+ exports.handleFinchStatus = async function handleFinchStatus() {
12
+ const pkg = require("./../package.json");
13
+ let tokenSet = false;
14
+ try {
15
+ tokenSet = Boolean((0, config_js_1.getSavedToken)());
16
+ } catch { tokenSet = false; }
17
+ const configJson = JSON.stringify({
18
+ mcpServers: {
19
+ finch: {
20
+ command: "npx",
21
+ args: ["-y", "@finchagentic/mcp@" + pkg.version],
22
+ env: { FINCH_SESSION_TOKEN: "<your token>" },
23
+ },
24
+ },
25
+ }, null, 2);
26
+ const lines = tokenSet
27
+ ? [
28
+ "**Finch MCP v" + pkg.version + "** - runtime layer for agentic AI", "",
29
+ "- Session token: \u2705 configured",
30
+ "- Tools: **116** registered (surface depends on FINCH_TOOLS filter)",
31
+ "- Resources: finch://vault/<key> - Prompts: crypto-thesis and friends",
32
+ "- Tool presets: FINCH_PRESET=core|defi|research|memory (default core) - FINCH_TOOLS=all for everything",
33
+ "",
34
+ "All set. Good first calls: `memory_add` (persist a note instantly), `get_market` (live prices), `deep_research` (multi-step reports, streaming progress).",
35
+ ]
36
+ : [
37
+ "**Finch MCP v" + pkg.version + "** - runtime layer for agentic AI", "",
38
+ "- Session token: \u274c not set", "",
39
+ "**To sign in (60 seconds):**", "",
40
+ "1. Sign in at https://app.finchagentic.com (wallet or Google)",
41
+ "2. Copy your session token from the app",
42
+ "3. Add to your MCP client config:",
43
+ "```json",
44
+ configJson,
45
+ "```", "",
46
+ "Once the token is set, restart the MCP client and re-run this tool.",
47
+ ];
48
+ return { content: [{ type: "text", text: lines.join("\n") }] };
49
+ };
package/dist/index.js CHANGED
File without changes
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");
@@ -215,7 +217,10 @@ exports.server = new index_js_1.Server({ name: "finch", version: PKG_VERSION },
215
217
  // withAnnotations attaches MCP behavioural hints (readOnly/destructive/etc) so
216
218
  // clients can auto-run reads and prompt before destructive/fund-moving calls.
217
219
  exports.server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
218
- tools: (0, annotations_js_1.withAnnotations)((0, tool_filter_js_1.filterTools)(exports.ALL_TOOLS)),
220
+ tools: [
221
+ finch_status_js_1.FINCH_STATUS_TOOL,
222
+ ...(0, annotations_js_1.withAnnotations)((0, tool_filter_js_1.filterTools)(exports.ALL_TOOLS)),
223
+ ],
219
224
  }));
220
225
  // MCP Resources - vault entries surface as `finch://vault/<key>`.
221
226
  // Clients can pull them via the standard resource flow instead of a Tool
@@ -283,14 +288,30 @@ exports.server.setRequestHandler(types_js_1.CallToolRequestSchema, async (reques
283
288
  return { content: [{ type: "text", text: `Error: ${err.message}` }], isError: true };
284
289
  }
285
290
  }
291
+ if (name === "finch_status") {
292
+ return finch_status_js_1.handleFinchStatus();
293
+ }
286
294
  const handler = exports.HANDLER_MAP.get(name);
287
295
  if (!handler) {
288
296
  return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
289
297
  }
290
298
  try {
291
- const result = await handler(name, args);
299
+ // Hard timeout guard: a hung tool must never wedge the client session.
300
+ // 180s covers deep_research's multi-stage pipeline with margin.
301
+ const HARD_TIMEOUT_MS = 180_000;
302
+ let timer;
303
+ const timeoutPromise = new Promise((_, reject) => {
304
+ timer = setTimeout(() => reject(new Error(
305
+ `Tool timed out after 180s (${name}). The operation may still complete server-side - check the app before re-running.`
306
+ )), HARD_TIMEOUT_MS);
307
+ });
308
+ const result = await Promise.race([
309
+ handler(name, args),
310
+ timeoutPromise,
311
+ ]);
312
+ clearTimeout(timer);
292
313
  if (result)
293
- return result;
314
+ return (0, finch_output_js_1.processResponse)(result);
294
315
  return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
295
316
  }
296
317
  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),
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@finchagentic/mcp",
3
- "version": "4.6.4",
3
+ "version": "4.7.0",
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"
8
+ "finch-mcp": "dist/index.js",
9
+ "@finchagentic/mcp": "dist/index.js"
9
10
  },
10
11
  "preferGlobal": true,
11
12
  "scripts": {
@@ -47,7 +48,7 @@
47
48
  "dependencies": {
48
49
  "@modelcontextprotocol/sdk": "^1.30.0",
49
50
  "ethers": "^6.17.0",
50
- "zod": "^4.4.3"
51
+ "zod": "^4.6.5"
51
52
  },
52
53
  "devDependencies": {
53
54
  "@eslint/js": "^9.39.1",