@noelclaw/mcp 2.4.0 → 3.1.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 +216 -116
- package/dist/agent-loop.js +141 -0
- package/dist/cli.js +170 -0
- package/dist/server.js +10 -10
- package/dist/tools/coder.js +0 -105
- package/dist/tools/defi.js +0 -38
- package/dist/tools/framework.js +0 -108
- package/dist/tools/swarm.js +0 -355
- package/dist/tools/vault.js +0 -106
- package/package.json +3 -2
package/dist/tools/swarm.js
CHANGED
|
@@ -38,34 +38,6 @@ exports.SWARM_TOOLS = [
|
|
|
38
38
|
description: "Get the current status of the swarm: active agents, shared memory snapshot, execution scores, and recent runs.",
|
|
39
39
|
inputSchema: { type: "object", properties: {}, required: [] },
|
|
40
40
|
},
|
|
41
|
-
{
|
|
42
|
-
name: "write_swarm_memory",
|
|
43
|
-
description: "Write a key-value pair to the swarm's shared memory.",
|
|
44
|
-
inputSchema: {
|
|
45
|
-
type: "object",
|
|
46
|
-
properties: {
|
|
47
|
-
agentId: { type: "string", description: "ID of the agent writing this memory entry" },
|
|
48
|
-
key: { type: "string", description: "Memory key" },
|
|
49
|
-
value: { type: "string", description: "Value to store" },
|
|
50
|
-
ttlSeconds: { type: "number", description: "Optional TTL in seconds" },
|
|
51
|
-
},
|
|
52
|
-
required: ["agentId", "key", "value"],
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
name: "get_swarm_memory",
|
|
57
|
-
description: "Read a value from the swarm's shared memory by key.",
|
|
58
|
-
inputSchema: {
|
|
59
|
-
type: "object",
|
|
60
|
-
properties: { key: { type: "string", description: "Memory key to read" } },
|
|
61
|
-
required: ["key"],
|
|
62
|
-
},
|
|
63
|
-
},
|
|
64
|
-
{
|
|
65
|
-
name: "get_execution_scores",
|
|
66
|
-
description: "Get the self-improvement scores for all skills.",
|
|
67
|
-
inputSchema: { type: "object", properties: {}, required: [] },
|
|
68
|
-
},
|
|
69
41
|
{
|
|
70
42
|
name: "swarm_research",
|
|
71
43
|
description: "Research any topic using the multi-agent swarm — automatically starts the swarm if needed, " +
|
|
@@ -117,71 +89,6 @@ exports.SWARM_TOOLS = [
|
|
|
117
89
|
required: [],
|
|
118
90
|
},
|
|
119
91
|
},
|
|
120
|
-
{
|
|
121
|
-
name: "swarm_broadcast",
|
|
122
|
-
description: "Broadcast a message or signal to all active swarm agents simultaneously. " +
|
|
123
|
-
"All agents will receive and act on the message in their next cycle. " +
|
|
124
|
-
"Use to coordinate the swarm: change focus, alert about market conditions, or inject a directive.",
|
|
125
|
-
inputSchema: {
|
|
126
|
-
type: "object",
|
|
127
|
-
properties: {
|
|
128
|
-
message: { type: "string", description: "Message to broadcast to all agents (max 500 chars)" },
|
|
129
|
-
priority: { type: "string", enum: ["low", "normal", "high", "urgent"], description: "Message priority (default: normal)" },
|
|
130
|
-
targetAgents: { type: "array", items: { type: "string" }, description: "Optional: target specific agent IDs. Omit to broadcast to all." },
|
|
131
|
-
},
|
|
132
|
-
required: ["message"],
|
|
133
|
-
},
|
|
134
|
-
},
|
|
135
|
-
{
|
|
136
|
-
name: "swarm_pulse",
|
|
137
|
-
description: "Get an instant snapshot from all swarm agents — market prices, sentiment, on-chain activity, and agent health. " +
|
|
138
|
-
"Unlike get_swarm_status, swarm_pulse triggers all agents to report their latest readings right now. " +
|
|
139
|
-
"Best for a quick market briefing or sanity check before making decisions.",
|
|
140
|
-
inputSchema: {
|
|
141
|
-
type: "object",
|
|
142
|
-
properties: {
|
|
143
|
-
token: { type: "string", description: "Optional: focus the pulse on a specific token (e.g. 'BTC', 'ETH')" },
|
|
144
|
-
},
|
|
145
|
-
required: [],
|
|
146
|
-
},
|
|
147
|
-
},
|
|
148
|
-
{
|
|
149
|
-
name: "swarm_reflect",
|
|
150
|
-
description: "Consolidate everything the swarm has learned into a single coherent intelligence summary. " +
|
|
151
|
-
"Reads all recent research vault entries from swarm agents, groups by agent, extracts key signals, " +
|
|
152
|
-
"and saves a unified reflection to vault. " +
|
|
153
|
-
"Run this after swarm_research or swarm_pulse to crystallize findings into long-term memory. " +
|
|
154
|
-
"Best used once per research session or daily for ongoing monitoring.",
|
|
155
|
-
inputSchema: {
|
|
156
|
-
type: "object",
|
|
157
|
-
properties: {
|
|
158
|
-
hoursBack: { type: "number", description: "How many hours of swarm activity to include (default: 24)" },
|
|
159
|
-
focus: { type: "string", description: "Optional: focus reflection on a specific topic or token" },
|
|
160
|
-
},
|
|
161
|
-
required: [],
|
|
162
|
-
},
|
|
163
|
-
},
|
|
164
|
-
{
|
|
165
|
-
name: "swarm_watch",
|
|
166
|
-
description: "Register a topic or token for continuous swarm monitoring. " +
|
|
167
|
-
"Swarm agents will prioritize this topic in every cycle — price changes, sentiment shifts, news, on-chain flows. " +
|
|
168
|
-
"Findings are saved to vault and semantic memory automatically. " +
|
|
169
|
-
"Use memory_insight or swarm_brief to check accumulated findings. " +
|
|
170
|
-
"Alert conditions: price_spike | sentiment_shift | news | whale_move | all.",
|
|
171
|
-
inputSchema: {
|
|
172
|
-
type: "object",
|
|
173
|
-
properties: {
|
|
174
|
-
topic: { type: "string", description: "What to watch — token symbol, protocol name, or any topic (e.g. 'ETH', 'Lido', 'Base ecosystem')" },
|
|
175
|
-
priority: { type: "string", enum: ["low", "normal", "high"], description: "Monitoring priority (default: normal)" },
|
|
176
|
-
alertOn: {
|
|
177
|
-
type: "array",
|
|
178
|
-
items: { type: "string", enum: ["price_spike", "sentiment_shift", "news", "whale_move", "all"] },
|
|
179
|
-
description: "Which signals to watch for (default: price_spike, sentiment_shift, news)",
|
|
180
|
-
},
|
|
181
|
-
},
|
|
182
|
-
required: ["topic"],
|
|
183
|
-
},
|
|
184
|
-
},
|
|
185
92
|
];
|
|
186
93
|
const StartSwarmSchema = zod_1.z.object({
|
|
187
94
|
config: zod_1.z.object({
|
|
@@ -189,26 +96,12 @@ const StartSwarmSchema = zod_1.z.object({
|
|
|
189
96
|
byok: zod_1.z.boolean().optional(),
|
|
190
97
|
}).optional(),
|
|
191
98
|
});
|
|
192
|
-
const WriteMemorySchema = zod_1.z.object({ agentId: zod_1.z.string().min(1), key: zod_1.z.string().min(1), value: zod_1.z.string(), ttlSeconds: zod_1.z.number().optional() });
|
|
193
|
-
const GetMemorySchema = zod_1.z.object({ key: zod_1.z.string().min(1) });
|
|
194
99
|
const ResearchSchema = zod_1.z.object({ topic: zod_1.z.string().min(1), depth: zod_1.z.enum(["quick", "standard", "deep"]).optional() });
|
|
195
100
|
const BriefSchema = zod_1.z.object({ limit: zod_1.z.number().optional() });
|
|
196
101
|
const TriggerAgentSchema = zod_1.z.object({
|
|
197
102
|
agentId: zod_1.z.enum(["market-monitor", "sentiment-tracker", "memory-manager", "risk-verifier", "workflow-executor", "onchain-analyst", "news-aggregator"]),
|
|
198
103
|
params: zod_1.z.record(zod_1.z.string(), zod_1.z.any()).optional(),
|
|
199
104
|
});
|
|
200
|
-
const BroadcastSchema = zod_1.z.object({
|
|
201
|
-
message: zod_1.z.string().min(1).max(500),
|
|
202
|
-
priority: zod_1.z.enum(["low", "normal", "high", "urgent"]).optional(),
|
|
203
|
-
targetAgents: zod_1.z.array(zod_1.z.string()).optional(),
|
|
204
|
-
});
|
|
205
|
-
const PulseSchema = zod_1.z.object({ token: zod_1.z.string().optional() });
|
|
206
|
-
const ReflectSchema = zod_1.z.object({ hoursBack: zod_1.z.number().positive().optional(), focus: zod_1.z.string().optional() });
|
|
207
|
-
const WatchSchema = zod_1.z.object({
|
|
208
|
-
topic: zod_1.z.string().min(1),
|
|
209
|
-
priority: zod_1.z.enum(["low", "normal", "high"]).optional(),
|
|
210
|
-
alertOn: zod_1.z.array(zod_1.z.enum(["price_spike", "sentiment_shift", "news", "whale_move", "all"])).optional(),
|
|
211
|
-
});
|
|
212
105
|
const NO_KEY_MSG = `🔑 Swarm tools require a NoelClaw API key.\n\n` +
|
|
213
106
|
`→ Get yours free at: https://noelclaw.com\n\n` +
|
|
214
107
|
`Then add it to your MCP config:\n` +
|
|
@@ -311,41 +204,6 @@ async function handleSwarmTool(name, args) {
|
|
|
311
204
|
}
|
|
312
205
|
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
313
206
|
}
|
|
314
|
-
case "write_swarm_memory": {
|
|
315
|
-
const parsed = WriteMemorySchema.safeParse(args);
|
|
316
|
-
if (!parsed.success)
|
|
317
|
-
return { content: [{ type: "text", text: `Invalid input: ${String(parsed.error.issues[0].path[0])} ${parsed.error.issues[0].message}` }], isError: true };
|
|
318
|
-
const { agentId, key, value, ttlSeconds } = parsed.data;
|
|
319
|
-
await (0, convex_js_1.callConvex)("/swarm/memory/write", "POST", { agentId, key, value, ttlSeconds }, "write_swarm_memory");
|
|
320
|
-
return { content: [{ type: "text", text: `✅ Memory written: [${agentId}] ${key}${ttlSeconds ? ` (expires in ${ttlSeconds}s)` : ""}` }] };
|
|
321
|
-
}
|
|
322
|
-
case "get_swarm_memory": {
|
|
323
|
-
const parsed = GetMemorySchema.safeParse(args);
|
|
324
|
-
if (!parsed.success)
|
|
325
|
-
return { content: [{ type: "text", text: `Invalid input: key ${parsed.error.issues[0].message}` }], isError: true };
|
|
326
|
-
const data = await (0, convex_js_1.callConvex)(`/swarm/memory/read?key=${encodeURIComponent(parsed.data.key)}`, "GET", undefined, "get_swarm_memory");
|
|
327
|
-
if (data.error)
|
|
328
|
-
return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
|
|
329
|
-
if (data.value === null || data.value === undefined)
|
|
330
|
-
return { content: [{ type: "text", text: `No value found for key: ${parsed.data.key}` }] };
|
|
331
|
-
return { content: [{ type: "text", text: `**${parsed.data.key}**: ${data.value}` }] };
|
|
332
|
-
}
|
|
333
|
-
case "get_execution_scores": {
|
|
334
|
-
const data = await (0, convex_js_1.callConvex)("/swarm/scores", "GET", undefined, "get_execution_scores");
|
|
335
|
-
if (data.error)
|
|
336
|
-
return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
|
|
337
|
-
const scores = data.scores ?? [];
|
|
338
|
-
if (!scores.length)
|
|
339
|
-
return { content: [{ type: "text", text: "No execution scores yet. Run some swarm agents to build a history." }] };
|
|
340
|
-
const sorted = scores.sort((a, b) => b.lastScore - a.lastScore);
|
|
341
|
-
const lines = [
|
|
342
|
-
`**Execution Scores**`, ``,
|
|
343
|
-
`| Skill | Score | W | L | Avg Duration | Last Adapted |`,
|
|
344
|
-
`|-------|-------|---|---|--------------|--------------|`,
|
|
345
|
-
...sorted.map((s) => `| ${s.skillName} | ${(s.lastScore * 100).toFixed(0)}% | ${s.successCount} | ${s.failCount} | ${Math.round(s.avgDurationMs / 1000)}s | ${new Date(s.lastAdaptedAt).toUTCString()} |`),
|
|
346
|
-
];
|
|
347
|
-
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
348
|
-
}
|
|
349
207
|
case "swarm_research": {
|
|
350
208
|
const parsed = ResearchSchema.safeParse(args);
|
|
351
209
|
if (!parsed.success)
|
|
@@ -453,219 +311,6 @@ async function handleSwarmTool(name, args) {
|
|
|
453
311
|
lines.push(`\nUse \`memory_context topic: "<topic>"\` to load full content for any research area.`);
|
|
454
312
|
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
455
313
|
}
|
|
456
|
-
case "swarm_broadcast": {
|
|
457
|
-
const parsed = BroadcastSchema.safeParse(args);
|
|
458
|
-
if (!parsed.success)
|
|
459
|
-
return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
|
|
460
|
-
const { message, priority = "normal", targetAgents } = parsed.data;
|
|
461
|
-
let data;
|
|
462
|
-
try {
|
|
463
|
-
data = await (0, convex_js_1.callConvex)("/swarm/broadcast", "POST", { message, priority, targetAgents }, "swarm_broadcast");
|
|
464
|
-
}
|
|
465
|
-
catch (err) {
|
|
466
|
-
return swarmAuthError(err);
|
|
467
|
-
}
|
|
468
|
-
if (data.error)
|
|
469
|
-
return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
|
|
470
|
-
const targets = targetAgents?.join(", ") ?? "all agents";
|
|
471
|
-
return {
|
|
472
|
-
content: [{
|
|
473
|
-
type: "text",
|
|
474
|
-
text: [
|
|
475
|
-
`📡 **Broadcast sent** [${priority}]`,
|
|
476
|
-
`To: ${targets}`,
|
|
477
|
-
`Message: "${message}"`,
|
|
478
|
-
data.deliveredTo ? `Delivered to ${data.deliveredTo} agent(s)` : "",
|
|
479
|
-
].filter(Boolean).join("\n"),
|
|
480
|
-
}],
|
|
481
|
-
};
|
|
482
|
-
}
|
|
483
|
-
case "swarm_pulse": {
|
|
484
|
-
const parsed = PulseSchema.safeParse(args ?? {});
|
|
485
|
-
if (!parsed.success)
|
|
486
|
-
return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
|
|
487
|
-
const { token } = parsed.data;
|
|
488
|
-
await (0, convex_js_1.callConvex)("/swarm/start", "POST", {}, "start_swarm").catch(() => { });
|
|
489
|
-
let data;
|
|
490
|
-
try {
|
|
491
|
-
data = await (0, convex_js_1.callConvex)("/swarm/pulse", "POST", { token }, "swarm_pulse");
|
|
492
|
-
}
|
|
493
|
-
catch (err) {
|
|
494
|
-
return swarmAuthError(err);
|
|
495
|
-
}
|
|
496
|
-
if (data.error)
|
|
497
|
-
return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
|
|
498
|
-
const readings = data.readings ?? [];
|
|
499
|
-
const lines = [
|
|
500
|
-
`💓 **Swarm Pulse**${token ? ` — ${token}` : ""}`,
|
|
501
|
-
`Agents reporting: ${readings.length}`,
|
|
502
|
-
``,
|
|
503
|
-
];
|
|
504
|
-
for (const r of readings) {
|
|
505
|
-
lines.push(`**[${r.agentId}]** ${r.summary ?? "No data"}`);
|
|
506
|
-
if (r.data) {
|
|
507
|
-
const preview = JSON.stringify(r.data).slice(0, 120);
|
|
508
|
-
lines.push(` ${preview}${preview.length === 120 ? "…" : ""}`);
|
|
509
|
-
}
|
|
510
|
-
lines.push("");
|
|
511
|
-
}
|
|
512
|
-
if (!readings.length)
|
|
513
|
-
lines.push("No agents responding. Use `start_swarm` first.");
|
|
514
|
-
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
515
|
-
}
|
|
516
|
-
case "swarm_reflect": {
|
|
517
|
-
const parsed = ReflectSchema.safeParse(args ?? {});
|
|
518
|
-
if (!parsed.success)
|
|
519
|
-
return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
|
|
520
|
-
const { hoursBack = 24, focus } = parsed.data;
|
|
521
|
-
const params = new URLSearchParams({ type: "research", limit: "30" });
|
|
522
|
-
let data;
|
|
523
|
-
try {
|
|
524
|
-
data = await (0, convex_js_1.callConvex)(`/vault/list?${params}`, "GET", undefined, "swarm_reflect");
|
|
525
|
-
}
|
|
526
|
-
catch (err) {
|
|
527
|
-
return swarmAuthError(err);
|
|
528
|
-
}
|
|
529
|
-
if (data.error)
|
|
530
|
-
return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
|
|
531
|
-
const SWARM_AGENTS = new Set(["market-monitor", "sentiment-tracker", "onchain-analyst", "news-aggregator", "risk-verifier", "memory-manager"]);
|
|
532
|
-
const cutoff = Date.now() - hoursBack * 3600000;
|
|
533
|
-
const entries = (data.entries ?? []).filter((e) => {
|
|
534
|
-
const isSwarm = SWARM_AGENTS.has(e.agentId);
|
|
535
|
-
const isRecent = e.updatedAt >= cutoff;
|
|
536
|
-
const matchesFocus = !focus || e.title.toLowerCase().includes(focus.toLowerCase());
|
|
537
|
-
return isSwarm && isRecent && matchesFocus;
|
|
538
|
-
});
|
|
539
|
-
if (!entries.length) {
|
|
540
|
-
return {
|
|
541
|
-
content: [{
|
|
542
|
-
type: "text",
|
|
543
|
-
text: [
|
|
544
|
-
`📋 **Swarm Reflection** — Nothing to consolidate`,
|
|
545
|
-
``,
|
|
546
|
-
`No swarm research found in the last ${hoursBack}h${focus ? ` about "${focus}"` : ""}.`,
|
|
547
|
-
``,
|
|
548
|
-
`Start one: \`swarm_research topic: "${focus ?? "market overview"}"\``,
|
|
549
|
-
].join("\n"),
|
|
550
|
-
}],
|
|
551
|
-
};
|
|
552
|
-
}
|
|
553
|
-
// Group by agent
|
|
554
|
-
const byAgent = {};
|
|
555
|
-
for (const e of entries) {
|
|
556
|
-
const a = e.agentId ?? "unknown";
|
|
557
|
-
(byAgent[a] ?? (byAgent[a] = [])).push(e);
|
|
558
|
-
}
|
|
559
|
-
const agentSummaries = Object.entries(byAgent).map(([agent, es]) => {
|
|
560
|
-
const items = es.map((e) => ` • ${e.title}`).join("\n");
|
|
561
|
-
return `**[${agent}]** — ${es.length} finding(s)\n${items}`;
|
|
562
|
-
});
|
|
563
|
-
const signals = entries
|
|
564
|
-
.sort((a, b) => b.updatedAt - a.updatedAt)
|
|
565
|
-
.slice(0, 6)
|
|
566
|
-
.map((e) => `• [${e.agentId}] ${e.title}`);
|
|
567
|
-
// Write synthesis to vault for long-term memory
|
|
568
|
-
const now = new Date();
|
|
569
|
-
const reflectionKey = `swarm/reflection-${now.toISOString().slice(0, 10)}-${now.getHours()}h`;
|
|
570
|
-
const reflectionContent = [
|
|
571
|
-
`# Swarm Reflection — ${now.toUTCString()}`,
|
|
572
|
-
`Period: last ${hoursBack}h${focus ? ` · Focus: ${focus}` : ""} · ${entries.length} entries from ${Object.keys(byAgent).length} agent(s)`,
|
|
573
|
-
``,
|
|
574
|
-
`## Agent Findings`,
|
|
575
|
-
agentSummaries.join("\n\n"),
|
|
576
|
-
``,
|
|
577
|
-
`## Key Signals`,
|
|
578
|
-
signals.join("\n"),
|
|
579
|
-
].join("\n");
|
|
580
|
-
const saved = await (0, convex_js_1.callConvex)("/vault/save", "POST", {
|
|
581
|
-
type: "research",
|
|
582
|
-
title: `Swarm Reflection — ${now.toLocaleDateString("en-US")}${focus ? ` — ${focus}` : ""}`,
|
|
583
|
-
content: reflectionContent,
|
|
584
|
-
key: reflectionKey,
|
|
585
|
-
agentId: "swarm-coordinator",
|
|
586
|
-
tags: ["reflection", "swarm", ...(focus ? [focus] : [])],
|
|
587
|
-
commitMsg: "swarm_reflect auto-consolidation",
|
|
588
|
-
}, "swarm_reflect").catch(() => null);
|
|
589
|
-
// Also sync to semantic memory so it's retrievable by topic
|
|
590
|
-
if (saved) {
|
|
591
|
-
(0, memory_js_1.syncToSupermemory)(reflectionContent, {
|
|
592
|
-
vaultKey: reflectionKey, title: `Swarm Reflection ${now.toLocaleDateString("en-US")}`,
|
|
593
|
-
type: "research", tags: ["reflection"], source: "swarm_reflect",
|
|
594
|
-
});
|
|
595
|
-
}
|
|
596
|
-
return {
|
|
597
|
-
content: [{
|
|
598
|
-
type: "text",
|
|
599
|
-
text: [
|
|
600
|
-
`📋 **Swarm Reflection** — ${entries.length} findings from ${Object.keys(byAgent).length} agent(s)`,
|
|
601
|
-
`Period: last ${hoursBack}h${focus ? ` · Focus: ${focus}` : ""}`,
|
|
602
|
-
``,
|
|
603
|
-
agentSummaries.join("\n\n"),
|
|
604
|
-
``,
|
|
605
|
-
`**Key signals:**`,
|
|
606
|
-
...signals,
|
|
607
|
-
``,
|
|
608
|
-
saved ? `✅ Synthesis saved to vault: \`${reflectionKey}\`` : "⚠️ Could not save synthesis",
|
|
609
|
-
``,
|
|
610
|
-
`Use \`memory_insight topic: "${focus ?? "swarm research"}"\` for the full intelligence picture.`,
|
|
611
|
-
].filter(Boolean).join("\n"),
|
|
612
|
-
}],
|
|
613
|
-
};
|
|
614
|
-
}
|
|
615
|
-
case "swarm_watch": {
|
|
616
|
-
const parsed = WatchSchema.safeParse(args);
|
|
617
|
-
if (!parsed.success)
|
|
618
|
-
return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
|
|
619
|
-
const { topic, priority = "normal", alertOn = ["price_spike", "sentiment_shift", "news"] } = parsed.data;
|
|
620
|
-
const watchKey = `watch:${topic.toLowerCase().replace(/\s+/g, "-")}`;
|
|
621
|
-
const watchConfig = JSON.stringify({
|
|
622
|
-
topic, priority,
|
|
623
|
-
alertOn: alertOn.includes("all") ? ["price_spike", "sentiment_shift", "news", "whale_move"] : alertOn,
|
|
624
|
-
registeredAt: Date.now(),
|
|
625
|
-
});
|
|
626
|
-
// Write to swarm shared memory (picked up by agents in next cycle)
|
|
627
|
-
await (0, convex_js_1.callConvex)("/swarm/memory/write", "POST", {
|
|
628
|
-
agentId: "swarm-coordinator",
|
|
629
|
-
key: watchKey,
|
|
630
|
-
value: watchConfig,
|
|
631
|
-
}, "swarm_watch").catch(() => { });
|
|
632
|
-
// Persist to vault so it survives swarm restarts
|
|
633
|
-
await (0, convex_js_1.callConvex)("/vault/save", "POST", {
|
|
634
|
-
type: "memory",
|
|
635
|
-
title: `Watch: ${topic}`,
|
|
636
|
-
content: watchConfig,
|
|
637
|
-
key: `watch/${topic.toLowerCase().replace(/\s+/g, "-")}`,
|
|
638
|
-
agentId: "swarm-coordinator",
|
|
639
|
-
tags: ["watch", "monitor", topic.toLowerCase()],
|
|
640
|
-
commitMsg: "swarm_watch registered",
|
|
641
|
-
}, "swarm_watch").catch(() => { });
|
|
642
|
-
const alertList = (alertOn.includes("all")
|
|
643
|
-
? ["price_spike", "sentiment_shift", "news", "whale_move"]
|
|
644
|
-
: alertOn).join(", ");
|
|
645
|
-
return {
|
|
646
|
-
content: [{
|
|
647
|
-
type: "text",
|
|
648
|
-
text: [
|
|
649
|
-
`👁️ **Watch Registered: ${topic}**`,
|
|
650
|
-
`Priority: ${priority} · Alerts: ${alertList}`,
|
|
651
|
-
``,
|
|
652
|
-
`Swarm agents will now prioritize "${topic}" in every monitoring cycle.`,
|
|
653
|
-
`Watch key: \`${watchKey}\``,
|
|
654
|
-
``,
|
|
655
|
-
`**What happens next:**`,
|
|
656
|
-
`• market-monitor will track price & volume anomalies`,
|
|
657
|
-
`• sentiment-tracker will watch social signal shifts`,
|
|
658
|
-
`• news-aggregator will flag relevant news & narratives`,
|
|
659
|
-
alertOn.includes("whale_move") || alertOn.includes("all") ? `• onchain-analyst will detect large wallet movements` : "",
|
|
660
|
-
``,
|
|
661
|
-
`**Check findings:**`,
|
|
662
|
-
`• \`swarm_brief\` — latest research entries`,
|
|
663
|
-
`• \`memory_insight topic: "${topic}"\` — full intelligence report`,
|
|
664
|
-
`• \`swarm_reflect focus: "${topic}"\` — consolidated summary`,
|
|
665
|
-
].filter(Boolean).join("\n"),
|
|
666
|
-
}],
|
|
667
|
-
};
|
|
668
|
-
}
|
|
669
314
|
default:
|
|
670
315
|
return null;
|
|
671
316
|
}
|
package/dist/tools/vault.js
CHANGED
|
@@ -6,7 +6,6 @@ const zod_1 = require("zod");
|
|
|
6
6
|
const convex_js_1 = require("../convex.js");
|
|
7
7
|
const memory_js_1 = require("./memory.js");
|
|
8
8
|
const VAULT_TYPES = ["research", "execution", "workflow", "prompt", "file", "memory", "credential"];
|
|
9
|
-
const LINK_RELATIONS = ["references", "derived_from", "supersedes", "related", "continues"];
|
|
10
9
|
exports.VAULT_TOOLS = [
|
|
11
10
|
{
|
|
12
11
|
name: "vault_save",
|
|
@@ -137,36 +136,6 @@ exports.VAULT_TOOLS = [
|
|
|
137
136
|
required: ["name"],
|
|
138
137
|
},
|
|
139
138
|
},
|
|
140
|
-
{
|
|
141
|
-
name: "vault_publish",
|
|
142
|
-
description: "Publish a vault entry to the community vault so other Noelclaw users can discover and fork it. " +
|
|
143
|
-
"Credentials are never published — only research, prompts, workflows, memory, and execution entries. " +
|
|
144
|
-
"You can also unpublish by setting isPublic to false.",
|
|
145
|
-
inputSchema: {
|
|
146
|
-
type: "object",
|
|
147
|
-
properties: {
|
|
148
|
-
key: { type: "string", description: "Entry key to publish or unpublish" },
|
|
149
|
-
isPublic: { type: "boolean", description: "true to publish, false to unpublish (default true)" },
|
|
150
|
-
authorName: { type: "string", description: "Display name shown to the community (default: Anonymous)" },
|
|
151
|
-
},
|
|
152
|
-
required: ["key"],
|
|
153
|
-
},
|
|
154
|
-
},
|
|
155
|
-
{
|
|
156
|
-
name: "vault_explore",
|
|
157
|
-
description: "Browse the community vault — public entries shared by all Noelclaw users. " +
|
|
158
|
-
"Discover research, prompts, and workflows published by the community. " +
|
|
159
|
-
"Use vault_save to fork any entry into your own vault.",
|
|
160
|
-
inputSchema: {
|
|
161
|
-
type: "object",
|
|
162
|
-
properties: {
|
|
163
|
-
type: { type: "string", enum: ["research", "execution", "workflow", "prompt", "file", "memory"], description: "Filter by entry type" },
|
|
164
|
-
search: { type: "string", description: "Search query to filter community entries" },
|
|
165
|
-
limit: { type: "number", description: "Max entries to return (default 20)" },
|
|
166
|
-
},
|
|
167
|
-
required: [],
|
|
168
|
-
},
|
|
169
|
-
},
|
|
170
139
|
{
|
|
171
140
|
name: "vault_pin",
|
|
172
141
|
description: "Pin or unpin a Noel-Vault entry. Pinned entries always appear first in vault_list and are " +
|
|
@@ -192,20 +161,6 @@ exports.VAULT_TOOLS = [
|
|
|
192
161
|
required: ["key"],
|
|
193
162
|
},
|
|
194
163
|
},
|
|
195
|
-
{
|
|
196
|
-
name: "vault_link",
|
|
197
|
-
description: "Create a semantic link between two Noel-Vault entries. Links appear in vault_read output and " +
|
|
198
|
-
"help agents navigate related knowledge. Relations: references | derived_from | supersedes | related | continues.",
|
|
199
|
-
inputSchema: {
|
|
200
|
-
type: "object",
|
|
201
|
-
properties: {
|
|
202
|
-
fromKey: { type: "string", description: "Source entry key" },
|
|
203
|
-
toKey: { type: "string", description: "Target entry key" },
|
|
204
|
-
relation: { type: "string", enum: [...LINK_RELATIONS], description: "Relationship type (default: related)" },
|
|
205
|
-
},
|
|
206
|
-
required: ["fromKey", "toKey"],
|
|
207
|
-
},
|
|
208
|
-
},
|
|
209
164
|
{
|
|
210
165
|
name: "vault_tag",
|
|
211
166
|
description: "Add or replace tags on an existing Noel-Vault entry without modifying its content. " +
|
|
@@ -250,11 +205,8 @@ const DiffSchema = zod_1.z.object({ key: zod_1.z.string().min(1), fromVersion: z
|
|
|
250
205
|
const ExportSchema = zod_1.z.object({ type: zod_1.z.enum(VAULT_TYPES).optional() });
|
|
251
206
|
const StoreCredentialSchema = zod_1.z.object({ name: zod_1.z.string().min(1), value: zod_1.z.string().min(1), description: zod_1.z.string().optional() });
|
|
252
207
|
const GetCredentialSchema = zod_1.z.object({ name: zod_1.z.string().min(1) });
|
|
253
|
-
const PublishSchema = zod_1.z.object({ key: zod_1.z.string().min(1), isPublic: zod_1.z.boolean().optional(), authorName: zod_1.z.string().optional() });
|
|
254
|
-
const ExploreSchema = zod_1.z.object({ type: zod_1.z.enum(["research", "execution", "workflow", "prompt", "file", "memory"]).optional(), search: zod_1.z.string().optional(), limit: zod_1.z.number().optional() });
|
|
255
208
|
const PinSchema = zod_1.z.object({ key: zod_1.z.string().min(1), pinned: zod_1.z.boolean().optional() });
|
|
256
209
|
const DeleteSchema = zod_1.z.object({ key: zod_1.z.string().min(1) });
|
|
257
|
-
const LinkSchema = zod_1.z.object({ fromKey: zod_1.z.string().min(1), toKey: zod_1.z.string().min(1), relation: zod_1.z.enum(LINK_RELATIONS).optional() });
|
|
258
210
|
const TagSchema = zod_1.z.object({ key: zod_1.z.string().min(1), tags: zod_1.z.array(zod_1.z.string()).min(1), replace: zod_1.z.boolean().optional() });
|
|
259
211
|
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
260
212
|
function formatBytes(n) {
|
|
@@ -467,54 +419,6 @@ async function handleVaultTool(name, args) {
|
|
|
467
419
|
lines.push(`Stored: ${data.storedAt}`);
|
|
468
420
|
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
469
421
|
}
|
|
470
|
-
case "vault_publish": {
|
|
471
|
-
const parsed = PublishSchema.safeParse(args);
|
|
472
|
-
if (!parsed.success)
|
|
473
|
-
return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
|
|
474
|
-
const { key, isPublic = true, authorName } = parsed.data;
|
|
475
|
-
const endpoint = isPublic ? "/vault/publish" : "/vault/unpublish";
|
|
476
|
-
const data = await (0, convex_js_1.callConvex)(endpoint, "POST", { key, authorName }, "vault_publish");
|
|
477
|
-
if (data.error)
|
|
478
|
-
return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
|
|
479
|
-
return {
|
|
480
|
-
content: [{
|
|
481
|
-
type: "text",
|
|
482
|
-
text: isPublic
|
|
483
|
-
? `🌐 **Published to community vault**\nKey: \`${key}\`\nOther Noelclaw users can now discover this entry.\nUse \`vault_publish key: "${key}" isPublic: false\` to unpublish.`
|
|
484
|
-
: `🔒 **Unpublished**\nKey: \`${key}\` is now private.`,
|
|
485
|
-
}],
|
|
486
|
-
};
|
|
487
|
-
}
|
|
488
|
-
case "vault_explore": {
|
|
489
|
-
const parsed = ExploreSchema.safeParse(args ?? {});
|
|
490
|
-
if (!parsed.success)
|
|
491
|
-
return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
|
|
492
|
-
const params = new URLSearchParams();
|
|
493
|
-
if (parsed.data.type)
|
|
494
|
-
params.set("type", parsed.data.type);
|
|
495
|
-
if (parsed.data.search)
|
|
496
|
-
params.set("search", parsed.data.search);
|
|
497
|
-
if (parsed.data.limit)
|
|
498
|
-
params.set("limit", String(parsed.data.limit));
|
|
499
|
-
const data = await (0, convex_js_1.callConvex)(`/vault/community?${params}`, "GET", undefined, "vault_explore");
|
|
500
|
-
if (data.error)
|
|
501
|
-
return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
|
|
502
|
-
const entries = data.entries ?? [];
|
|
503
|
-
if (!entries.length)
|
|
504
|
-
return { content: [{ type: "text", text: `No community vault entries found${parsed.data.type ? ` of type '${parsed.data.type}'` : ""}.${parsed.data.search ? ` Try a different search term.` : "\nBe the first to publish with vault_publish!"}` }] };
|
|
505
|
-
const header = `🌐 **Community Vault** — ${entries.length} public entries`;
|
|
506
|
-
const rows = entries.map((e) => [
|
|
507
|
-
`**[${e.type}]** ${e.title} · by ${e.authorName ?? "Anonymous"}`,
|
|
508
|
-
` \`${e.key}\` · v${e.version} · ${formatDate(e.updatedAt)}`,
|
|
509
|
-
e.content ? ` ${e.content.slice(0, 100)}${e.content.length > 100 ? "…" : ""}` : "",
|
|
510
|
-
].filter(Boolean).join("\n"));
|
|
511
|
-
return {
|
|
512
|
-
content: [{
|
|
513
|
-
type: "text",
|
|
514
|
-
text: [header, "", ...rows, "", "To fork an entry: `vault_save type: <type> title: <title> content: <content>`"].join("\n"),
|
|
515
|
-
}],
|
|
516
|
-
};
|
|
517
|
-
}
|
|
518
422
|
case "vault_pin": {
|
|
519
423
|
const parsed = PinSchema.safeParse(args);
|
|
520
424
|
if (!parsed.success)
|
|
@@ -534,16 +438,6 @@ async function handleVaultTool(name, args) {
|
|
|
534
438
|
return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
|
|
535
439
|
return { content: [{ type: "text", text: `🗑️ Deleted: \`${parsed.data.key}\` (${data.versionsRemoved ?? 0} versions removed)` }] };
|
|
536
440
|
}
|
|
537
|
-
case "vault_link": {
|
|
538
|
-
const parsed = LinkSchema.safeParse(args);
|
|
539
|
-
if (!parsed.success)
|
|
540
|
-
return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
|
|
541
|
-
const { fromKey, toKey, relation = "related" } = parsed.data;
|
|
542
|
-
const data = await (0, convex_js_1.callConvex)("/vault/link", "POST", { fromKey, toKey, relation }, "vault_link");
|
|
543
|
-
if (data.error)
|
|
544
|
-
return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
|
|
545
|
-
return { content: [{ type: "text", text: `🔗 Linked: \`${fromKey}\` → [${relation}] → \`${toKey}\`` }] };
|
|
546
|
-
}
|
|
547
441
|
case "vault_tag": {
|
|
548
442
|
const parsed = TagSchema.safeParse(args);
|
|
549
443
|
if (!parsed.success)
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@noelclaw/mcp",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "Noelclaw AI Operating System — persistent memory, multi-agent swarm, DeFi execution, market intelligence, and Sentinel-gated playbooks.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"noelclaw-mcp": "dist/index.js"
|
|
7
|
+
"noelclaw-mcp": "dist/index.js",
|
|
8
|
+
"noelclaw": "dist/cli.js"
|
|
8
9
|
},
|
|
9
10
|
"scripts": {
|
|
10
11
|
"build": "tsc",
|