@finchagentic/mcp 4.2.0 → 4.5.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 +17 -12
- package/dist/annotations.js +9 -1
- package/dist/cli.js +139 -0
- package/dist/convex.js +31 -4
- package/dist/index.js +11 -8
- package/dist/project.js +36 -0
- package/dist/server.js +14 -6
- package/dist/tool-filter.js +5 -5
- package/dist/tools/agents.js +46 -6
- package/dist/tools/automation.js +47 -7
- package/dist/tools/coder.js +1 -1
- package/dist/tools/deep-research.js +1 -1
- package/dist/tools/defi.js +4 -4
- package/dist/tools/equity.js +1 -1
- package/dist/tools/events.js +1 -1
- package/dist/tools/github.js +51 -1
- package/dist/tools/insider.js +1 -1
- package/dist/tools/insight.js +2 -2
- package/dist/tools/market.js +5 -5
- package/dist/tools/memory.js +8 -8
- package/dist/tools/miroshark.js +8 -1
- package/dist/tools/monitor.js +2 -2
- package/dist/tools/os.js +9 -3
- package/dist/tools/research-chain.js +1 -1
- package/dist/tools/research-compare.js +1 -1
- package/dist/tools/research.js +2 -2
- package/dist/tools/rh-bridge.js +1 -1
- package/dist/tools/rh-mcp.js +16 -2
- package/dist/tools/rh-orders.js +13 -6
- package/dist/tools/scanner.js +3 -3
- package/dist/tools/stake.js +43 -3
- package/dist/tools/vault.js +195 -19
- package/package.json +3 -4
package/dist/tools/stake.js
CHANGED
|
@@ -134,6 +134,21 @@ exports.STAKE_TOOLS = [
|
|
|
134
134
|
required: ["stakeId", "confirm"],
|
|
135
135
|
},
|
|
136
136
|
},
|
|
137
|
+
{
|
|
138
|
+
name: "claim_vested_rewards",
|
|
139
|
+
description: "Claim all your fully-vested USDG staking rewards to your custodial wallet. Daily rewards split " +
|
|
140
|
+
"50% instant / 50% vested over 3 days - this claims whatever portion has finished vesting across " +
|
|
141
|
+
"ALL your stakes in one call (check stake_finch_status for accrued totals; you'll also get a " +
|
|
142
|
+
"notification when rewards become claimable). Does nothing to the stake itself - principal stays " +
|
|
143
|
+
"staked and earning. Requires `finch login` and `confirm: true`.",
|
|
144
|
+
inputSchema: {
|
|
145
|
+
type: "object",
|
|
146
|
+
properties: {
|
|
147
|
+
confirm: { type: "boolean", description: "Must be true to claim - this moves real USDG into your custodial wallet." },
|
|
148
|
+
},
|
|
149
|
+
required: ["confirm"],
|
|
150
|
+
},
|
|
151
|
+
},
|
|
137
152
|
{
|
|
138
153
|
name: "stake_auto_restake",
|
|
139
154
|
description: "Turn auto-restake on/off for one of your stakes. When ON, the moment the lock-up ends the stake " +
|
|
@@ -143,7 +158,7 @@ exports.STAKE_TOOLS = [
|
|
|
143
158
|
"the stake is unlockable and it just sits there earning nothing until you run unstake_finch. " +
|
|
144
159
|
"Does NOT auto-compound rewards into the stake - that would require signing from your custodial " +
|
|
145
160
|
"wallet unattended, which this intentionally does not do; claim rewards yourself with " +
|
|
146
|
-
"
|
|
161
|
+
"claim_vested_rewards (you'll get a notification when they're ready). Requires `finch login`.",
|
|
147
162
|
inputSchema: {
|
|
148
163
|
type: "object",
|
|
149
164
|
properties: {
|
|
@@ -246,7 +261,7 @@ async function handleStakeTool(name, args) {
|
|
|
246
261
|
isError: true,
|
|
247
262
|
};
|
|
248
263
|
}
|
|
249
|
-
const result = await (0, convex_js_1.callConvex)("/mcp/stake/stake", "POST", { amountWei: amountWei.toString(), lockTier }, "stake_finch");
|
|
264
|
+
const result = await (0, convex_js_1.callConvex)("/mcp/stake/stake", "POST", { amountWei: amountWei.toString(), lockTier }, "stake_finch", 30000, true);
|
|
250
265
|
if (result.error)
|
|
251
266
|
return { content: [{ type: "text", text: `Stake failed: ${result.error}` }], isError: true };
|
|
252
267
|
const tier = STAKING_TIERS[lockTier ?? 7];
|
|
@@ -294,7 +309,7 @@ async function handleStakeTool(name, args) {
|
|
|
294
309
|
}
|
|
295
310
|
const lines = [];
|
|
296
311
|
for (const id of targetIds) {
|
|
297
|
-
const result = await (0, convex_js_1.callConvex)("/mcp/stake/unstake", "POST", { stakeId: id }, "unstake_finch");
|
|
312
|
+
const result = await (0, convex_js_1.callConvex)("/mcp/stake/unstake", "POST", { stakeId: id }, "unstake_finch", 30000, true);
|
|
298
313
|
if (result.error) {
|
|
299
314
|
lines.push(`🔴 \`${id}\`: ${result.error}`);
|
|
300
315
|
}
|
|
@@ -304,6 +319,31 @@ async function handleStakeTool(name, args) {
|
|
|
304
319
|
}
|
|
305
320
|
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
306
321
|
}
|
|
322
|
+
if (name === "claim_vested_rewards") {
|
|
323
|
+
const { confirm } = (args ?? {});
|
|
324
|
+
if (confirm !== true) {
|
|
325
|
+
return {
|
|
326
|
+
content: [{ type: "text", text: "Refusing to claim without confirmation - pass `confirm: true`." }],
|
|
327
|
+
isError: true,
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
const login = requireLogin();
|
|
331
|
+
if (!login)
|
|
332
|
+
return { content: [{ type: "text", text: NOT_LOGGED_IN_MSG }], isError: true };
|
|
333
|
+
const result = await (0, convex_js_1.callConvex)("/mcp/stake/claim-rewards", "POST", {}, "claim_vested_rewards", 30000, true);
|
|
334
|
+
if (result.error)
|
|
335
|
+
return { content: [{ type: "text", text: `Claim failed: ${result.error}` }], isError: true };
|
|
336
|
+
return {
|
|
337
|
+
content: [{
|
|
338
|
+
type: "text",
|
|
339
|
+
text: [
|
|
340
|
+
`✅ Claimed ${(result.claimedAmount ?? 0).toFixed(4)} USDG vested rewards`,
|
|
341
|
+
`Tx: \`${result.txHash}\``,
|
|
342
|
+
`${rh_mcp_js_1.RH_EXPLORER}/tx/${result.txHash}`,
|
|
343
|
+
].join("\n"),
|
|
344
|
+
}],
|
|
345
|
+
};
|
|
346
|
+
}
|
|
307
347
|
if (name === "stake_auto_restake") {
|
|
308
348
|
const { stakeId, enabled } = (args ?? {});
|
|
309
349
|
if (!stakeId)
|
package/dist/tools/vault.js
CHANGED
|
@@ -9,14 +9,16 @@ const convex_js_1 = require("../convex.js");
|
|
|
9
9
|
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
|
-
const
|
|
12
|
+
const project_js_1 = require("../project.js");
|
|
13
|
+
const VAULT_TYPES = ["research", "execution", "workflow", "prompt", "file", "memory", "code", "credential"];
|
|
13
14
|
exports.VAULT_TOOLS = [
|
|
14
15
|
{
|
|
15
16
|
name: "vault_save",
|
|
16
17
|
description: "Save or update a versioned artifact in Finch Vault. Same key = update (git-style: prior version snapshotted, patched to v+1). " +
|
|
17
|
-
"Types: research | execution | workflow | prompt | file | memory. " +
|
|
18
|
+
"Types: research | execution | workflow | prompt | file | memory | code. " +
|
|
18
19
|
"Entries up to 10MB - content over 600KB auto-offloads to blob storage. " +
|
|
19
|
-
"For quick unstructured notes, use memory_add instead."
|
|
20
|
+
"For quick unstructured notes, use memory_add instead. For coding sessions specifically, " +
|
|
21
|
+
"prefer code_session_save - same versioning, but a structured template and auto-linking built in.",
|
|
20
22
|
inputSchema: {
|
|
21
23
|
type: "object",
|
|
22
24
|
properties: {
|
|
@@ -29,10 +31,37 @@ exports.VAULT_TOOLS = [
|
|
|
29
31
|
tags: { type: "array", items: { type: "string" }, description: "Tags for filtering and search" },
|
|
30
32
|
commitMsg: { type: "string", description: "Commit message for this version, e.g. 'initial research', 'refined with on-chain data'" },
|
|
31
33
|
metadata: { type: "string", description: "Optional JSON string for extra structured fields" },
|
|
34
|
+
workspaceProject: {
|
|
35
|
+
type: "string",
|
|
36
|
+
description: "Optional: file this entry into a named Finch workspace project (the same Projects a user " +
|
|
37
|
+
"organizes their Agents/vault into on the Agents page). Matched case-insensitively by name; " +
|
|
38
|
+
"created automatically if it doesn't exist yet. Not the same thing as a `key` path segment - " +
|
|
39
|
+
"this tags the entry in Finch's own project system. Hosted vault only (no effect in local-vault mode).",
|
|
40
|
+
},
|
|
32
41
|
},
|
|
33
42
|
required: ["type", "content"],
|
|
34
43
|
},
|
|
35
44
|
},
|
|
45
|
+
{
|
|
46
|
+
name: "code_session_save",
|
|
47
|
+
description: "Persist a coding/debugging session as a versioned Markdown snapshot in Finch Vault, keyed by " +
|
|
48
|
+
"project (`code/<project>`) - so the next session (yours, or another agent's) has real context " +
|
|
49
|
+
"instead of starting cold. Same project = new version, full history kept (git-style, like vault_save). " +
|
|
50
|
+
"Auto-links to related past code and research entries. " +
|
|
51
|
+
"Call this at the end of a substantive coding task - not for every single file read or trivial edit.",
|
|
52
|
+
inputSchema: {
|
|
53
|
+
type: "object",
|
|
54
|
+
properties: {
|
|
55
|
+
project: { type: "string", description: "Project or repo slug, e.g. 'finch-webapp', 'mcp-server'. Becomes the vault key: code/<project>." },
|
|
56
|
+
summary: { type: "string", description: "What was done this session - the task, the approach, the outcome." },
|
|
57
|
+
filesChanged: { type: "array", items: { type: "string" }, description: "Files touched, e.g. ['app/convex/vault.ts', 'app/src/App.tsx']" },
|
|
58
|
+
decisions: { type: "string", description: "Notable decisions or tradeoffs made and why - the part a future session can't re-derive from a diff alone." },
|
|
59
|
+
nextSteps: { type: "string", description: "What's left, or what to pick up next session." },
|
|
60
|
+
tags: { type: "array", items: { type: "string" }, description: "Extra tags for search, e.g. ['bugfix', 'refactor']" },
|
|
61
|
+
},
|
|
62
|
+
required: ["project", "summary"],
|
|
63
|
+
},
|
|
64
|
+
},
|
|
36
65
|
{
|
|
37
66
|
name: "vault_read",
|
|
38
67
|
description: "Read a Finch Vault entry by its key. Returns full content, version, tags, and any linked entries.",
|
|
@@ -232,6 +261,14 @@ exports.VAULT_TOOLS = [
|
|
|
232
261
|
required: ["key"],
|
|
233
262
|
},
|
|
234
263
|
},
|
|
264
|
+
{
|
|
265
|
+
name: "list_projects",
|
|
266
|
+
description: "List your Finch workspace projects - the same Projects used to organize Agents and vault content on the " +
|
|
267
|
+
"webapp Agents page. Read-only. Check this before passing `workspaceProject` to vault_save/agent_spawn if " +
|
|
268
|
+
"you want to reuse an existing project rather than relying on the automatic case-insensitive name match. " +
|
|
269
|
+
"No effect / nothing to list in local-vault mode.",
|
|
270
|
+
inputSchema: { type: "object", properties: {}, required: [] },
|
|
271
|
+
},
|
|
235
272
|
];
|
|
236
273
|
// ─── Zod schemas ─────────────────────────────────────────────────────────────
|
|
237
274
|
const SaveSchema = zod_1.z.object({
|
|
@@ -244,6 +281,15 @@ const SaveSchema = zod_1.z.object({
|
|
|
244
281
|
tags: zod_1.z.array(zod_1.z.string()).optional(),
|
|
245
282
|
commitMsg: zod_1.z.string().optional(),
|
|
246
283
|
metadata: zod_1.z.string().optional(),
|
|
284
|
+
workspaceProject: zod_1.z.string().optional(),
|
|
285
|
+
});
|
|
286
|
+
const CodeSessionSchema = zod_1.z.object({
|
|
287
|
+
project: zod_1.z.string().min(1).max(80),
|
|
288
|
+
summary: zod_1.z.string().min(1),
|
|
289
|
+
filesChanged: zod_1.z.array(zod_1.z.string()).max(100).optional(),
|
|
290
|
+
decisions: zod_1.z.string().optional(),
|
|
291
|
+
nextSteps: zod_1.z.string().optional(),
|
|
292
|
+
tags: zod_1.z.array(zod_1.z.string()).optional(),
|
|
247
293
|
});
|
|
248
294
|
const ReadSchema = zod_1.z.object({ key: zod_1.z.string().min(1) });
|
|
249
295
|
const ListSchema = zod_1.z.object({
|
|
@@ -322,7 +368,7 @@ async function handleVaultTool(name, args) {
|
|
|
322
368
|
case "vault_save": {
|
|
323
369
|
const parsed = SaveSchema.safeParse(args);
|
|
324
370
|
if (!parsed.success)
|
|
325
|
-
return { content: [{ type: "text", text:
|
|
371
|
+
return { content: [{ type: "text", text: `${parsed.error.issues[0].message}` }], isError: true };
|
|
326
372
|
if (parsed.data.type === "credential") {
|
|
327
373
|
// vault_save writes plaintext to disk/DB - "credential" is only a
|
|
328
374
|
// valid FILTER value for vault_list/search/export (which correctly
|
|
@@ -340,7 +386,19 @@ async function handleVaultTool(name, args) {
|
|
|
340
386
|
// Auto-generate title from content if not provided
|
|
341
387
|
const firstLine = parsed.data.content.split("\n")[0].replace(/^#+\s*/, "").slice(0, 80);
|
|
342
388
|
const autoTitle = parsed.data.title ?? (firstLine || `${parsed.data.type} - ${new Date().toISOString().slice(0, 10)}`);
|
|
343
|
-
const
|
|
389
|
+
const { workspaceProject, ...rest } = parsed.data;
|
|
390
|
+
const savePayload = { ...rest, title: autoTitle };
|
|
391
|
+
// Resolve the project name -> id server-side (auto-creates on first use).
|
|
392
|
+
// Local vault has no project concept at all - workspaceProject is
|
|
393
|
+
// silently a no-op there rather than a confusing network error.
|
|
394
|
+
let resolvedProjectName = null;
|
|
395
|
+
if (workspaceProject && !localVault) {
|
|
396
|
+
const resolved = await (0, project_js_1.resolveProjectId)(workspaceProject);
|
|
397
|
+
if (resolved) {
|
|
398
|
+
savePayload.projectId = resolved.projectId;
|
|
399
|
+
resolvedProjectName = resolved.name;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
344
402
|
const data = localVault
|
|
345
403
|
? (0, local_vault_js_1.localVaultSave)(localVault, savePayload)
|
|
346
404
|
: await (0, convex_js_1.callConvex)("/vault/save", "POST", savePayload, "vault_save");
|
|
@@ -380,6 +438,7 @@ async function handleVaultTool(name, args) {
|
|
|
380
438
|
`Key: \`${key}\``,
|
|
381
439
|
`Version: v${version}`,
|
|
382
440
|
changed && version > 1 ? `Previous version auto-snapshotted.` : "",
|
|
441
|
+
resolvedProjectName ? `📁 Project: ${resolvedProjectName}` : "",
|
|
383
442
|
mirrorToMemory ? `🧠 Synced to searchable memory` : (localVault ? `💾 Stored locally at ~/.finch/vault` : ""),
|
|
384
443
|
...linkSummary,
|
|
385
444
|
``,
|
|
@@ -387,10 +446,99 @@ async function handleVaultTool(name, args) {
|
|
|
387
446
|
].filter(Boolean);
|
|
388
447
|
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
389
448
|
}
|
|
449
|
+
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 };
|
|
453
|
+
const { project, summary, filesChanged, decisions, nextSteps, tags } = parsed.data;
|
|
454
|
+
const projectSlug = project.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60) || "session";
|
|
455
|
+
const key = `code/${projectSlug}`;
|
|
456
|
+
const content = [
|
|
457
|
+
`# Code session: ${project}`,
|
|
458
|
+
``,
|
|
459
|
+
`_${new Date().toISOString()}_`,
|
|
460
|
+
``,
|
|
461
|
+
`## Summary`,
|
|
462
|
+
summary,
|
|
463
|
+
filesChanged?.length ? `\n## Files changed\n${filesChanged.map((f) => `- \`${f}\``).join("\n")}` : "",
|
|
464
|
+
decisions ? `\n## Decisions\n${decisions}` : "",
|
|
465
|
+
nextSteps ? `\n## Next steps\n${nextSteps}` : "",
|
|
466
|
+
].filter(Boolean).join("\n");
|
|
467
|
+
const savePayload = {
|
|
468
|
+
type: "code",
|
|
469
|
+
key,
|
|
470
|
+
title: `Code: ${project}`,
|
|
471
|
+
content,
|
|
472
|
+
contentType: "markdown",
|
|
473
|
+
agentId: "code-session",
|
|
474
|
+
tags: ["code-session", ...(tags ?? [])],
|
|
475
|
+
commitMsg: summary.slice(0, 80),
|
|
476
|
+
};
|
|
477
|
+
const data = localVault
|
|
478
|
+
? (0, local_vault_js_1.localVaultSave)(localVault, savePayload)
|
|
479
|
+
: await (0, convex_js_1.callConvex)("/vault/save", "POST", savePayload, "vault_save");
|
|
480
|
+
if (data.error)
|
|
481
|
+
return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
|
|
482
|
+
const { version, changed } = data;
|
|
483
|
+
// Same mirror-to-memory rule as vault_save: skip only when local vault
|
|
484
|
+
// is on WITHOUT local memory, so query terms never phone home for a
|
|
485
|
+
// fully offline setup.
|
|
486
|
+
const mirrorToMemory = !localVault || !!(0, local_memory_js_1.getLocalMemoryConfig)();
|
|
487
|
+
if (mirrorToMemory) {
|
|
488
|
+
(0, memory_js_1.syncToSupermemory)(content, {
|
|
489
|
+
vaultKey: key, title: savePayload.title, type: "code",
|
|
490
|
+
tags: savePayload.tags, version, source: "code_session_save",
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
// Auto-link to related past code/research entries - same idea as
|
|
494
|
+
// deep_research's auto-linking, so a project's session history and any
|
|
495
|
+
// research that informed it stay connected instead of sitting as
|
|
496
|
+
// disconnected entries. Purely additive - never blocks the save.
|
|
497
|
+
const linked = [];
|
|
498
|
+
try {
|
|
499
|
+
const searchQuery = `${project} ${summary}`.slice(0, 200);
|
|
500
|
+
let hits = [];
|
|
501
|
+
if (localVault) {
|
|
502
|
+
const searchResult = (0, local_vault_js_1.localVaultSearch)(localVault, searchQuery, { limit: 6 });
|
|
503
|
+
hits = (searchResult.results ?? [])
|
|
504
|
+
.filter((r) => !!r.key && r.key !== key)
|
|
505
|
+
.map((r) => ({ key: r.key, title: r.title ?? "(untitled)" }));
|
|
506
|
+
}
|
|
507
|
+
else {
|
|
508
|
+
const searchResult = (await (0, convex_js_1.callConvex)("/vault/search", "POST", { q: searchQuery, n: 8 }, "vault_search"));
|
|
509
|
+
hits = (searchResult?.results ?? [])
|
|
510
|
+
.map((r) => {
|
|
511
|
+
const hitKey = r.metadata?.vaultKey ?? r.metadata?.key ?? null;
|
|
512
|
+
return hitKey ? { key: hitKey, title: r.metadata?.title ?? "(untitled)" } : null;
|
|
513
|
+
})
|
|
514
|
+
.filter((h) => !!h && h.key !== key);
|
|
515
|
+
}
|
|
516
|
+
for (const hit of hits.slice(0, 3)) {
|
|
517
|
+
try {
|
|
518
|
+
if (localVault)
|
|
519
|
+
(0, local_vault_js_1.localVaultLink)(localVault, key, hit.key, "related");
|
|
520
|
+
else
|
|
521
|
+
await (0, convex_js_1.callConvex)("/vault/link", "POST", { fromKey: key, toKey: hit.key, relation: "related" }, "vault_link");
|
|
522
|
+
linked.push(hit.key);
|
|
523
|
+
}
|
|
524
|
+
catch { /* skip individual link failures */ }
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
catch { /* auto-link is purely additive */ }
|
|
528
|
+
const lines = [
|
|
529
|
+
`📦 **Code session ${changed ? (version === 1 ? "saved" : "updated") : "unchanged"}** - \`${key}\` (v${version})`,
|
|
530
|
+
filesChanged?.length ? `Files: ${filesChanged.length}` : "",
|
|
531
|
+
linked.length ? `🔗 Linked to ${linked.length} related entr${linked.length === 1 ? "y" : "ies"}: ${linked.map((k) => `\`${k}\``).join(", ")}` : "",
|
|
532
|
+
mirrorToMemory ? `🧠 Synced to searchable memory` : "",
|
|
533
|
+
``,
|
|
534
|
+
`Next session: \`vault_read key="${key}"\` for the latest state, or \`vault_history key="${key}"\` for the full timeline.`,
|
|
535
|
+
].filter(Boolean);
|
|
536
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
537
|
+
}
|
|
390
538
|
case "vault_read": {
|
|
391
539
|
const parsed = ReadSchema.safeParse(args);
|
|
392
540
|
if (!parsed.success)
|
|
393
|
-
return { content: [{ type: "text", text:
|
|
541
|
+
return { content: [{ type: "text", text: `${parsed.error.issues[0].message}` }], isError: true };
|
|
394
542
|
const vaultReadKey = parsed.data.key;
|
|
395
543
|
let data;
|
|
396
544
|
try {
|
|
@@ -440,12 +588,26 @@ async function handleVaultTool(name, args) {
|
|
|
440
588
|
const backlinksBlock = Array.isArray(data.backlinks) && data.backlinks.length > 0
|
|
441
589
|
? `\n🔙 Linked from (${data.backlinks.length}):\n${data.backlinks.map((b) => ` ← \`${b.key}\`${b.title ? ` - ${b.title}` : ""}`).join("\n")}`
|
|
442
590
|
: "";
|
|
591
|
+
// A raw projectId means nothing to a reader - resolve it to a name.
|
|
592
|
+
// One extra call only when the entry is actually tagged; skipped
|
|
593
|
+
// entirely for the common case (unassigned) and in local-vault mode.
|
|
594
|
+
let projectLabel = "";
|
|
595
|
+
if (data.projectId && !localVault) {
|
|
596
|
+
try {
|
|
597
|
+
const projData = await (0, convex_js_1.callConvex)("/projects/list", "GET", undefined, "list_projects");
|
|
598
|
+
const match = projData?.projects?.find((p) => p.id === data.projectId);
|
|
599
|
+
if (match)
|
|
600
|
+
projectLabel = `📁 Project: ${match.name}`;
|
|
601
|
+
}
|
|
602
|
+
catch { /* best-effort - never block the read over this */ }
|
|
603
|
+
}
|
|
443
604
|
const lines = [
|
|
444
605
|
`📂 **${data.title}**`,
|
|
445
606
|
`Key: \`${data.key}\` · Type: ${data.type} · v${data.version} · ${sizeLabel}${data.contentFileId ? " · blob" : ""}`,
|
|
446
607
|
data.tags?.length ? `Tags: ${data.tags.join(", ")}` : "",
|
|
447
608
|
data.isPinned ? "📌 Pinned" : "",
|
|
448
609
|
data.agentId ? `Agent: ${data.agentId}` : "",
|
|
610
|
+
projectLabel,
|
|
449
611
|
`Updated: ${formatDate(data.updatedAt)}`,
|
|
450
612
|
data.linkedKeys?.length ? `\nLinks out:\n${data.linkedKeys.map((l) => ` → ${l}`).join("\n")}` : "",
|
|
451
613
|
backlinksBlock,
|
|
@@ -459,7 +621,7 @@ async function handleVaultTool(name, args) {
|
|
|
459
621
|
case "vault_list": {
|
|
460
622
|
const parsed = ListSchema.safeParse(args ?? {});
|
|
461
623
|
if (!parsed.success)
|
|
462
|
-
return { content: [{ type: "text", text:
|
|
624
|
+
return { content: [{ type: "text", text: `${parsed.error.issues[0].message}` }], isError: true };
|
|
463
625
|
const params = new URLSearchParams();
|
|
464
626
|
if (parsed.data.type)
|
|
465
627
|
params.set("type", parsed.data.type);
|
|
@@ -491,7 +653,7 @@ async function handleVaultTool(name, args) {
|
|
|
491
653
|
case "vault_search": {
|
|
492
654
|
const parsed = SearchSchema.safeParse(args);
|
|
493
655
|
if (!parsed.success)
|
|
494
|
-
return { content: [{ type: "text", text:
|
|
656
|
+
return { content: [{ type: "text", text: `${parsed.error.issues[0].message}` }], isError: true };
|
|
495
657
|
// Full-text search, proxied through Convex (searchSupermemory is a
|
|
496
658
|
// legacy name - it calls Finch's own /memory/search endpoint, not a
|
|
497
659
|
// third-party semantic service; there is no embedding step here). Large vault
|
|
@@ -592,7 +754,7 @@ async function handleVaultTool(name, args) {
|
|
|
592
754
|
case "vault_history": {
|
|
593
755
|
const parsed = HistorySchema.safeParse(args);
|
|
594
756
|
if (!parsed.success)
|
|
595
|
-
return { content: [{ type: "text", text:
|
|
757
|
+
return { content: [{ type: "text", text: `${parsed.error.issues[0].message}` }], isError: true };
|
|
596
758
|
const histKey = parsed.data.key;
|
|
597
759
|
let data;
|
|
598
760
|
try {
|
|
@@ -632,7 +794,7 @@ async function handleVaultTool(name, args) {
|
|
|
632
794
|
case "vault_diff": {
|
|
633
795
|
const parsed = DiffSchema.safeParse(args);
|
|
634
796
|
if (!parsed.success)
|
|
635
|
-
return { content: [{ type: "text", text:
|
|
797
|
+
return { content: [{ type: "text", text: `${parsed.error.issues[0].message}` }], isError: true };
|
|
636
798
|
const { key, fromVersion, toVersion } = parsed.data;
|
|
637
799
|
let data;
|
|
638
800
|
try {
|
|
@@ -676,7 +838,7 @@ async function handleVaultTool(name, args) {
|
|
|
676
838
|
case "vault_export": {
|
|
677
839
|
const parsed = ExportSchema.safeParse(args ?? {});
|
|
678
840
|
if (!parsed.success)
|
|
679
|
-
return { content: [{ type: "text", text:
|
|
841
|
+
return { content: [{ type: "text", text: `${parsed.error.issues[0].message}` }], isError: true };
|
|
680
842
|
const params = parsed.data.type ? `?type=${parsed.data.type}` : "";
|
|
681
843
|
const data = localVault
|
|
682
844
|
? (0, local_vault_js_1.localVaultExport)(localVault, parsed.data.type)
|
|
@@ -695,7 +857,7 @@ async function handleVaultTool(name, args) {
|
|
|
695
857
|
case "vault_store_credential": {
|
|
696
858
|
const parsed = StoreCredentialSchema.safeParse(args);
|
|
697
859
|
if (!parsed.success)
|
|
698
|
-
return { content: [{ type: "text", text:
|
|
860
|
+
return { content: [{ type: "text", text: `${parsed.error.issues[0].message}` }], isError: true };
|
|
699
861
|
const data = localVault
|
|
700
862
|
? (0, local_vault_js_1.localVaultStoreCredential)(localVault, parsed.data.name, parsed.data.value, parsed.data.description)
|
|
701
863
|
: await (0, convex_js_1.callConvex)("/vault/credential/store", "POST", parsed.data, "vault_store_credential");
|
|
@@ -706,7 +868,7 @@ async function handleVaultTool(name, args) {
|
|
|
706
868
|
case "vault_get_credential": {
|
|
707
869
|
const parsed = GetCredentialSchema.safeParse(args);
|
|
708
870
|
if (!parsed.success)
|
|
709
|
-
return { content: [{ type: "text", text:
|
|
871
|
+
return { content: [{ type: "text", text: `${parsed.error.issues[0].message}` }], isError: true };
|
|
710
872
|
const params = new URLSearchParams({ name: parsed.data.name });
|
|
711
873
|
let data;
|
|
712
874
|
try {
|
|
@@ -729,7 +891,7 @@ async function handleVaultTool(name, args) {
|
|
|
729
891
|
case "vault_pin": {
|
|
730
892
|
const parsed = PinSchema.safeParse(args);
|
|
731
893
|
if (!parsed.success)
|
|
732
|
-
return { content: [{ type: "text", text:
|
|
894
|
+
return { content: [{ type: "text", text: `${parsed.error.issues[0].message}` }], isError: true };
|
|
733
895
|
const { key, pinned = true } = parsed.data;
|
|
734
896
|
const data = localVault
|
|
735
897
|
? (0, local_vault_js_1.localVaultPin)(localVault, key, pinned)
|
|
@@ -741,7 +903,7 @@ async function handleVaultTool(name, args) {
|
|
|
741
903
|
case "vault_unpublish": {
|
|
742
904
|
const parsed = UnpublishSchema.safeParse(args);
|
|
743
905
|
if (!parsed.success)
|
|
744
|
-
return { content: [{ type: "text", text:
|
|
906
|
+
return { content: [{ type: "text", text: `${parsed.error.issues[0].message}` }], isError: true };
|
|
745
907
|
// Publishing is a hosted/marketplace concept - a local vault is private
|
|
746
908
|
// by construction, so there is nothing to retract.
|
|
747
909
|
if (localVault) {
|
|
@@ -762,7 +924,7 @@ async function handleVaultTool(name, args) {
|
|
|
762
924
|
case "vault_delete": {
|
|
763
925
|
const parsed = DeleteSchema.safeParse(args);
|
|
764
926
|
if (!parsed.success)
|
|
765
|
-
return { content: [{ type: "text", text:
|
|
927
|
+
return { content: [{ type: "text", text: `${parsed.error.issues[0].message}` }], isError: true };
|
|
766
928
|
if (args?.confirm !== true) {
|
|
767
929
|
return {
|
|
768
930
|
content: [{
|
|
@@ -797,7 +959,7 @@ async function handleVaultTool(name, args) {
|
|
|
797
959
|
case "vault_tag": {
|
|
798
960
|
const parsed = TagSchema.safeParse(args);
|
|
799
961
|
if (!parsed.success)
|
|
800
|
-
return { content: [{ type: "text", text:
|
|
962
|
+
return { content: [{ type: "text", text: `${parsed.error.issues[0].message}` }], isError: true };
|
|
801
963
|
const { key, tags, replace = false } = parsed.data;
|
|
802
964
|
const data = localVault
|
|
803
965
|
? (0, local_vault_js_1.localVaultTag)(localVault, key, tags, replace)
|
|
@@ -809,7 +971,7 @@ async function handleVaultTool(name, args) {
|
|
|
809
971
|
case "vault_link": {
|
|
810
972
|
const parsed = LinkSchema.safeParse(args);
|
|
811
973
|
if (!parsed.success)
|
|
812
|
-
return { content: [{ type: "text", text:
|
|
974
|
+
return { content: [{ type: "text", text: `${parsed.error.issues[0].message}` }], isError: true };
|
|
813
975
|
const { fromKey, toKey, relation } = parsed.data;
|
|
814
976
|
const data = localVault
|
|
815
977
|
? (0, local_vault_js_1.localVaultLink)(localVault, fromKey, toKey, relation)
|
|
@@ -822,7 +984,7 @@ async function handleVaultTool(name, args) {
|
|
|
822
984
|
case "vault_related": {
|
|
823
985
|
const parsed = RelatedSchema.safeParse(args);
|
|
824
986
|
if (!parsed.success)
|
|
825
|
-
return { content: [{ type: "text", text:
|
|
987
|
+
return { content: [{ type: "text", text: `${parsed.error.issues[0].message}` }], isError: true };
|
|
826
988
|
const { key, relation } = parsed.data;
|
|
827
989
|
const params = new URLSearchParams({ key });
|
|
828
990
|
if (relation)
|
|
@@ -838,6 +1000,20 @@ async function handleVaultTool(name, args) {
|
|
|
838
1000
|
const lines = items.map(r => `- **${r.title}** (\`${r.key}\`) [${r.type}] - ${r.direction} \`${r.relation}\``);
|
|
839
1001
|
return { content: [{ type: "text", text: `## Related entries for \`${key}\` (${items.length})\n\n${lines.join("\n")}` }] };
|
|
840
1002
|
}
|
|
1003
|
+
case "list_projects": {
|
|
1004
|
+
if (localVault) {
|
|
1005
|
+
return { content: [{ type: "text", text: "Local vault mode has no project concept - `workspaceProject` has no effect, and there's nothing to list here." }] };
|
|
1006
|
+
}
|
|
1007
|
+
const data = await (0, convex_js_1.callConvex)("/projects/list", "GET", undefined, "list_projects");
|
|
1008
|
+
if (data.error)
|
|
1009
|
+
return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
|
|
1010
|
+
const projects = data.projects ?? [];
|
|
1011
|
+
if (!projects.length) {
|
|
1012
|
+
return { content: [{ type: "text", text: "No projects yet. Pass `workspaceProject: \"name\"` to `vault_save` or `agent_spawn` and one is created automatically." }] };
|
|
1013
|
+
}
|
|
1014
|
+
const lines = projects.map((p) => `- **${p.name}** (\`${p.slug}\`)${p.description ? ` - ${p.description}` : ""}`);
|
|
1015
|
+
return { content: [{ type: "text", text: `📁 **Your projects** (${projects.length})\n\n${lines.join("\n")}` }] };
|
|
1016
|
+
}
|
|
841
1017
|
default:
|
|
842
1018
|
return null;
|
|
843
1019
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@finchagentic/mcp",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.5.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": {
|
|
@@ -50,9 +50,8 @@
|
|
|
50
50
|
"LICENSE"
|
|
51
51
|
],
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
54
|
-
"ethers": "^6.
|
|
55
|
-
"node-fetch": "^3.3.2",
|
|
53
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
54
|
+
"ethers": "^6.17.0",
|
|
56
55
|
"zod": "^4.4.3"
|
|
57
56
|
},
|
|
58
57
|
"devDependencies": {
|