@noelclaw/mcp 2.3.1 → 3.0.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/index.js +10 -9
- package/dist/server.js +39 -29
- package/dist/tools/automation.js +38 -0
- package/dist/tools/coder.js +62 -105
- package/dist/tools/defi.js +113 -24
- package/dist/tools/framework.js +0 -108
- package/dist/tools/humanizer.js +143 -2
- package/dist/tools/insight.js +197 -19
- package/dist/tools/market.js +182 -7
- package/dist/tools/memory.js +159 -43
- package/dist/tools/miroshark.js +15 -4
- package/dist/tools/os.js +223 -0
- package/dist/tools/scanner.js +183 -52
- package/dist/tools/swarm.js +37 -79
- package/dist/tools/vault.js +39 -200
- package/package.json +5 -2
- package/dist/tools/news.js +0 -6
- package/dist/tools/research.js +0 -8
- package/dist/tools/twitter.js +0 -67
package/dist/tools/coder.js
CHANGED
|
@@ -27,53 +27,6 @@ function err(text) {
|
|
|
27
27
|
}
|
|
28
28
|
// ── Tool definitions ──────────────────────────────────────────────────────────
|
|
29
29
|
exports.CODER_TOOLS = [
|
|
30
|
-
{
|
|
31
|
-
name: "scaffold_project",
|
|
32
|
-
description: "Generate a complete project scaffold for a described idea — file tree + key file contents. " +
|
|
33
|
-
"Supports: Solidity contract projects, MCP skill packages, React/Next.js dApps, Convex backends, CLI tools.",
|
|
34
|
-
inputSchema: {
|
|
35
|
-
type: "object",
|
|
36
|
-
properties: {
|
|
37
|
-
description: {
|
|
38
|
-
type: "string",
|
|
39
|
-
description: "What to build — be specific: tech stack, purpose, target chain, key features",
|
|
40
|
-
},
|
|
41
|
-
stack: {
|
|
42
|
-
type: "string",
|
|
43
|
-
enum: ["solidity", "mcp-skill", "react-dapp", "convex-backend", "node-cli", "auto"],
|
|
44
|
-
description: "Tech stack. Use 'auto' to let Noel decide based on description.",
|
|
45
|
-
},
|
|
46
|
-
extras: {
|
|
47
|
-
type: "string",
|
|
48
|
-
description: "Optional: extra requirements, constraints, or preferred libraries",
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
|
-
required: ["description"],
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
name: "generate_component",
|
|
56
|
-
description: "Generate a production-ready React/TypeScript component. Returns the full .tsx file content. " +
|
|
57
|
-
"Includes props typing, Tailwind styling, and any hooks needed.",
|
|
58
|
-
inputSchema: {
|
|
59
|
-
type: "object",
|
|
60
|
-
properties: {
|
|
61
|
-
description: {
|
|
62
|
-
type: "string",
|
|
63
|
-
description: "What the component does, what it shows, and how it should behave",
|
|
64
|
-
},
|
|
65
|
-
name: {
|
|
66
|
-
type: "string",
|
|
67
|
-
description: "Component name in PascalCase, e.g. 'TokenPriceCard'",
|
|
68
|
-
},
|
|
69
|
-
context: {
|
|
70
|
-
type: "string",
|
|
71
|
-
description: "Optional: existing imports, store usage, or API calls the component should use",
|
|
72
|
-
},
|
|
73
|
-
},
|
|
74
|
-
required: ["description", "name"],
|
|
75
|
-
},
|
|
76
|
-
},
|
|
77
30
|
{
|
|
78
31
|
name: "generate_contract",
|
|
79
32
|
description: "Generate a Solidity smart contract from a description. Returns the full .sol file. " +
|
|
@@ -138,6 +91,33 @@ exports.CODER_TOOLS = [
|
|
|
138
91
|
required: ["code"],
|
|
139
92
|
},
|
|
140
93
|
},
|
|
94
|
+
{
|
|
95
|
+
name: "generate_mcp_skill",
|
|
96
|
+
description: "Generate a complete Claude Code skill (.md file) from a description. " +
|
|
97
|
+
"Skills are slash-command workflows that run inside Claude Code — they can call tools, " +
|
|
98
|
+
"loop, delegate to subagents, and have persistent behavior. " +
|
|
99
|
+
"Returns a ready-to-use .md file you can drop into your .claude/skills/ directory. " +
|
|
100
|
+
"Use this to automate repetitive Claude Code workflows without writing TypeScript.",
|
|
101
|
+
inputSchema: {
|
|
102
|
+
type: "object",
|
|
103
|
+
properties: {
|
|
104
|
+
description: {
|
|
105
|
+
type: "string",
|
|
106
|
+
description: "What the skill should do — be specific about inputs, outputs, and any tools it should use",
|
|
107
|
+
},
|
|
108
|
+
name: {
|
|
109
|
+
type: "string",
|
|
110
|
+
description: "Skill name in kebab-case, e.g. 'daily-standup', 'code-review', 'deploy-check'",
|
|
111
|
+
},
|
|
112
|
+
tools: {
|
|
113
|
+
type: "array",
|
|
114
|
+
items: { type: "string" },
|
|
115
|
+
description: "Optional: list of Claude Code tools or MCP tools the skill should use, e.g. ['Bash', 'Read', 'memory_search']",
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
required: ["description", "name"],
|
|
119
|
+
},
|
|
120
|
+
},
|
|
141
121
|
{
|
|
142
122
|
name: "review_code",
|
|
143
123
|
description: "Review and improve a piece of code. Returns the improved version with a summary of changes. " +
|
|
@@ -163,16 +143,6 @@ exports.CODER_TOOLS = [
|
|
|
163
143
|
},
|
|
164
144
|
];
|
|
165
145
|
// ── Handlers ──────────────────────────────────────────────────────────────────
|
|
166
|
-
const ScaffoldSchema = zod_1.z.object({
|
|
167
|
-
description: zod_1.z.string().min(10),
|
|
168
|
-
stack: zod_1.z.enum(["solidity", "mcp-skill", "react-dapp", "convex-backend", "node-cli", "auto"]).optional(),
|
|
169
|
-
extras: zod_1.z.string().optional(),
|
|
170
|
-
});
|
|
171
|
-
const ComponentSchema = zod_1.z.object({
|
|
172
|
-
description: zod_1.z.string().min(5),
|
|
173
|
-
name: zod_1.z.string().min(1),
|
|
174
|
-
context: zod_1.z.string().optional(),
|
|
175
|
-
});
|
|
176
146
|
const ContractSchema = zod_1.z.object({
|
|
177
147
|
description: zod_1.z.string().min(5),
|
|
178
148
|
name: zod_1.z.string().min(1),
|
|
@@ -191,56 +161,13 @@ const ReviewSchema = zod_1.z.object({
|
|
|
191
161
|
language: zod_1.z.string().optional(),
|
|
192
162
|
goals: zod_1.z.string().optional(),
|
|
193
163
|
});
|
|
164
|
+
const McpSkillSchema = zod_1.z.object({
|
|
165
|
+
description: zod_1.z.string().min(10),
|
|
166
|
+
name: zod_1.z.string().min(1).regex(/^[a-z0-9-]+$/, "must be kebab-case"),
|
|
167
|
+
tools: zod_1.z.array(zod_1.z.string()).optional(),
|
|
168
|
+
});
|
|
194
169
|
async function handleCoderTool(name, args) {
|
|
195
170
|
switch (name) {
|
|
196
|
-
case "scaffold_project": {
|
|
197
|
-
const p = ScaffoldSchema.safeParse(args);
|
|
198
|
-
if (!p.success)
|
|
199
|
-
return err(`Invalid input: ${p.error.message}`);
|
|
200
|
-
const { description, stack = "auto", extras } = p.data;
|
|
201
|
-
const prompt = `Generate a complete project scaffold for the following:\n\n` +
|
|
202
|
-
`Description: ${description}\n` +
|
|
203
|
-
`Stack: ${stack}\n` +
|
|
204
|
-
(extras ? `Extra requirements: ${extras}\n` : "") +
|
|
205
|
-
`\nOutput format:\n` +
|
|
206
|
-
`1. Brief overview (2-3 sentences)\n` +
|
|
207
|
-
`2. Full file tree with all files listed\n` +
|
|
208
|
-
`3. Full content of each key file (package.json, main entry, config, one core module)\n` +
|
|
209
|
-
`4. Setup instructions (3-5 steps)\n\n` +
|
|
210
|
-
`Use real, runnable code. No TODO placeholders.`;
|
|
211
|
-
try {
|
|
212
|
-
const response = await (0, llm_js_1.callLLM)(CODER_SYSTEM, prompt, 4096);
|
|
213
|
-
return ok(response);
|
|
214
|
-
}
|
|
215
|
-
catch (e) {
|
|
216
|
-
return err(`scaffold_project failed: ${e.message}`);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
case "generate_component": {
|
|
220
|
-
const p = ComponentSchema.safeParse(args);
|
|
221
|
-
if (!p.success)
|
|
222
|
-
return err(`Invalid input: ${p.error.message}`);
|
|
223
|
-
const { description, name: componentName, context } = p.data;
|
|
224
|
-
const prompt = `Generate a production-ready React TypeScript component.\n\n` +
|
|
225
|
-
`Component name: ${componentName}\n` +
|
|
226
|
-
`Description: ${description}\n` +
|
|
227
|
-
(context ? `Context / existing code to integrate with:\n${context}\n` : "") +
|
|
228
|
-
`\nRequirements:\n` +
|
|
229
|
-
`- Full .tsx file, no omissions\n` +
|
|
230
|
-
`- TypeScript props interface\n` +
|
|
231
|
-
`- Tailwind CSS for styling\n` +
|
|
232
|
-
`- Use lucide-react for icons if needed\n` +
|
|
233
|
-
`- Framer Motion for animations if the component is interactive\n` +
|
|
234
|
-
`- Named export (not default)\n` +
|
|
235
|
-
`Output only the .tsx file content, no prose before or after.`;
|
|
236
|
-
try {
|
|
237
|
-
const response = await (0, llm_js_1.callLLM)(CODER_SYSTEM, prompt, 3000);
|
|
238
|
-
return ok(response);
|
|
239
|
-
}
|
|
240
|
-
catch (e) {
|
|
241
|
-
return err(`generate_component failed: ${e.message}`);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
171
|
case "generate_contract": {
|
|
245
172
|
const p = ContractSchema.safeParse(args);
|
|
246
173
|
if (!p.success)
|
|
@@ -341,6 +268,36 @@ async function handleCoderTool(name, args) {
|
|
|
341
268
|
return err(`review_code failed: ${e.message}`);
|
|
342
269
|
}
|
|
343
270
|
}
|
|
271
|
+
case "generate_mcp_skill": {
|
|
272
|
+
const p = McpSkillSchema.safeParse(args);
|
|
273
|
+
if (!p.success)
|
|
274
|
+
return err(`Invalid input: ${p.error.message}`);
|
|
275
|
+
const { description, name: skillName, tools = [] } = p.data;
|
|
276
|
+
const prompt = `Generate a complete Claude Code skill (.md file) for the following workflow:\n\n` +
|
|
277
|
+
`Skill name: /${skillName}\n` +
|
|
278
|
+
`Description: ${description}\n` +
|
|
279
|
+
(tools.length ? `Tools to use: ${tools.join(", ")}\n` : "") +
|
|
280
|
+
`\n` +
|
|
281
|
+
`Claude Code skill format rules:\n` +
|
|
282
|
+
`- The file is a markdown document that serves as a system prompt for a Claude Code slash command\n` +
|
|
283
|
+
`- It should start with a brief description of what the skill does\n` +
|
|
284
|
+
`- Include an "## Input" section explaining what arguments the skill accepts (if any)\n` +
|
|
285
|
+
`- Include a "## Steps" section with numbered, concrete steps\n` +
|
|
286
|
+
`- Include a "## Output" section describing what the skill produces\n` +
|
|
287
|
+
`- Steps can reference tool calls like "Use the Bash tool to run X" or "Use memory_search to find Y"\n` +
|
|
288
|
+
`- Steps can reference conditional logic and loops\n` +
|
|
289
|
+
`- Keep it under 200 lines — skills should be focused, not monolithic\n` +
|
|
290
|
+
`- Write in imperative second person ("Run...", "Check...", "If X, then...")\n` +
|
|
291
|
+
`- Do NOT include markdown fences around the output — output the raw .md content directly\n\n` +
|
|
292
|
+
`Output only the .md file content, ready to save as .claude/skills/${skillName}.md`;
|
|
293
|
+
try {
|
|
294
|
+
const response = await (0, llm_js_1.callLLM)(CODER_SYSTEM, prompt, 2000);
|
|
295
|
+
return ok(`# /${skillName} skill — save to .claude/skills/${skillName}.md\n\n---\n\n${response}`);
|
|
296
|
+
}
|
|
297
|
+
catch (e) {
|
|
298
|
+
return err(`generate_mcp_skill failed: ${e.message}`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
344
301
|
default:
|
|
345
302
|
return null;
|
|
346
303
|
}
|
package/dist/tools/defi.js
CHANGED
|
@@ -51,13 +51,44 @@ exports.DEFI_TOOLS = [
|
|
|
51
51
|
},
|
|
52
52
|
},
|
|
53
53
|
{
|
|
54
|
-
name: "
|
|
55
|
-
description: "AI-powered
|
|
56
|
-
|
|
54
|
+
name: "analyze_wallet",
|
|
55
|
+
description: "AI-powered analysis of any public wallet on Base — not just your own. " +
|
|
56
|
+
"Enter any 0x address: see token holdings, portfolio value, concentration risk, " +
|
|
57
|
+
"DeFi positions, and a behavioral profile (whale, degen, LP provider, etc.). " +
|
|
58
|
+
"Use to track smart money, research whales, or audit any wallet before copying trades.",
|
|
59
|
+
inputSchema: {
|
|
60
|
+
type: "object",
|
|
61
|
+
properties: {
|
|
62
|
+
address: { type: "string", description: "Wallet address to analyze (0x...)" },
|
|
63
|
+
label: { type: "string", description: "Optional label for this wallet (e.g. 'whale from Twitter')" },
|
|
64
|
+
},
|
|
65
|
+
required: ["address"],
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: "get_defi_yields",
|
|
70
|
+
description: "Fetch top DeFi yield opportunities on Base — Morpho, Moonwell, Aerodrome, Uniswap, and more. " +
|
|
71
|
+
"Returns live APY, TVL, and pool info from DeFiLlama (no API key required). " +
|
|
72
|
+
"Filter by token or minimum APY. Use before depositing to find the best rates.",
|
|
73
|
+
inputSchema: {
|
|
74
|
+
type: "object",
|
|
75
|
+
properties: {
|
|
76
|
+
token: { type: "string", description: "Optional: filter by token symbol, e.g. 'USDC', 'ETH', 'WETH'" },
|
|
77
|
+
minApy: { type: "number", description: "Optional: minimum APY % to show (default 1)" },
|
|
78
|
+
limit: { type: "number", description: "Max results to return (default 20)" },
|
|
79
|
+
},
|
|
80
|
+
required: [],
|
|
81
|
+
},
|
|
57
82
|
},
|
|
58
83
|
];
|
|
59
84
|
const SwapSchema = zod_1.z.object({ fromToken: zod_1.z.string().min(1), toToken: zod_1.z.string().min(1), amount: zod_1.z.string().min(1) });
|
|
60
85
|
const SendSchema = zod_1.z.object({ token: zod_1.z.string().min(1), toAddress: zod_1.z.string().regex(/^0x[0-9a-fA-F]{40}$/, "must be a valid 0x address"), amount: zod_1.z.string().min(1) });
|
|
86
|
+
const AnalyzeWalletSchema = zod_1.z.object({ address: zod_1.z.string().regex(/^0x[0-9a-fA-F]{40}$/, "must be a valid 0x address"), label: zod_1.z.string().optional() });
|
|
87
|
+
const DefiYieldsSchema = zod_1.z.object({
|
|
88
|
+
token: zod_1.z.string().optional(),
|
|
89
|
+
minApy: zod_1.z.number().optional(),
|
|
90
|
+
limit: zod_1.z.number().int().min(1).max(100).optional(),
|
|
91
|
+
}).default({});
|
|
61
92
|
const BUY_DECIMALS = { USDC: 6, USDT: 6, DAI: 18, ETH: 18, WETH: 18 };
|
|
62
93
|
function formatTokenAmount(raw, token) {
|
|
63
94
|
const dec = BUY_DECIMALS[token.toUpperCase()] ?? 18;
|
|
@@ -150,38 +181,96 @@ async function handleDefiTool(name, args) {
|
|
|
150
181
|
}],
|
|
151
182
|
};
|
|
152
183
|
}
|
|
153
|
-
case "
|
|
154
|
-
const
|
|
155
|
-
if (
|
|
156
|
-
return { content: [{ type: "text", text: `
|
|
157
|
-
}
|
|
184
|
+
case "analyze_wallet": {
|
|
185
|
+
const parsed = AnalyzeWalletSchema.safeParse(args);
|
|
186
|
+
if (!parsed.success)
|
|
187
|
+
return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
|
|
188
|
+
const { address, label } = parsed.data;
|
|
189
|
+
const data = await (0, convex_js_1.callConvex)("/wallet/analyze", "POST", { address, label }, "analyze_wallet");
|
|
190
|
+
if (data.error)
|
|
191
|
+
return { content: [{ type: "text", text: `Wallet analysis failed: ${data.error}` }], isError: true };
|
|
158
192
|
const total = (data.totalUsd ?? 0).toFixed(2);
|
|
193
|
+
const walletLabel = label ? ` — ${label}` : "";
|
|
159
194
|
const topHoldings = (data.holdings ?? [])
|
|
160
|
-
.slice(0,
|
|
161
|
-
.map(
|
|
195
|
+
.slice(0, 8)
|
|
196
|
+
.map(h => `• **${h.token}**: $${(h.valueUsd ?? 0).toFixed(2)}${h.pct != null ? ` (${h.pct}%)` : ""}`)
|
|
162
197
|
.join("\n");
|
|
198
|
+
const profileLine = data.profile ? `**Profile:** ${data.profile}\n` : "";
|
|
163
199
|
const header = [
|
|
164
|
-
`**
|
|
165
|
-
|
|
200
|
+
`**Wallet Analysis**${walletLabel}`,
|
|
201
|
+
`\`${address}\``,
|
|
202
|
+
`**Portfolio value:** $${total}`,
|
|
166
203
|
``,
|
|
204
|
+
profileLine,
|
|
167
205
|
`**Holdings:**`,
|
|
168
|
-
topHoldings,
|
|
206
|
+
topHoldings || "No token holdings found.",
|
|
169
207
|
``,
|
|
170
208
|
].join("\n");
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
209
|
+
const body = data.analysis ?? (data.analysisError ? `*AI analysis unavailable: ${data.analysisError}*` : "*AI analysis not available*");
|
|
210
|
+
return { content: [{ type: "text", text: header + body }] };
|
|
211
|
+
}
|
|
212
|
+
case "get_defi_yields": {
|
|
213
|
+
const parsed = DefiYieldsSchema.safeParse(args ?? {});
|
|
214
|
+
if (!parsed.success)
|
|
215
|
+
return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
|
|
216
|
+
const { token, minApy = 1, limit = 20 } = parsed.data;
|
|
217
|
+
let pools;
|
|
218
|
+
try {
|
|
219
|
+
const res = await fetch("https://yields.llama.fi/pools", { signal: AbortSignal.timeout(15000) });
|
|
220
|
+
if (!res.ok)
|
|
221
|
+
throw new Error(`HTTP ${res.status}`);
|
|
222
|
+
const data = await res.json();
|
|
223
|
+
pools = data.data ?? [];
|
|
224
|
+
}
|
|
225
|
+
catch (e) {
|
|
226
|
+
return { content: [{ type: "text", text: `DeFiLlama fetch failed: ${e.message}` }], isError: true };
|
|
174
227
|
}
|
|
175
|
-
|
|
176
|
-
|
|
228
|
+
// Filter to Base chain
|
|
229
|
+
let filtered = pools.filter((p) => p.chain === "Base");
|
|
230
|
+
// Filter by token if specified
|
|
231
|
+
if (token) {
|
|
232
|
+
const upper = token.toUpperCase();
|
|
233
|
+
filtered = filtered.filter((p) => (p.symbol ?? "").toUpperCase().includes(upper) ||
|
|
234
|
+
(p.underlyingTokens ?? []).some((t) => t.toUpperCase().includes(upper)));
|
|
177
235
|
}
|
|
178
|
-
|
|
179
|
-
|
|
236
|
+
// Filter by minimum APY and remove outliers (>10000% are usually broken)
|
|
237
|
+
filtered = filtered
|
|
238
|
+
.filter((p) => (p.apy ?? 0) >= minApy && (p.apy ?? 0) <= 10000)
|
|
239
|
+
.sort((a, b) => (b.apy ?? 0) - (a.apy ?? 0))
|
|
240
|
+
.slice(0, limit);
|
|
241
|
+
if (!filtered.length) {
|
|
242
|
+
return {
|
|
243
|
+
content: [{
|
|
244
|
+
type: "text",
|
|
245
|
+
text: [
|
|
246
|
+
`## DeFi Yields on Base`,
|
|
247
|
+
`No pools found${token ? ` for ${token.toUpperCase()}` : ""} with APY ≥ ${minApy}%.`,
|
|
248
|
+
``,
|
|
249
|
+
`Try lowering \`minApy\` or removing the token filter.`,
|
|
250
|
+
].join("\n"),
|
|
251
|
+
}],
|
|
252
|
+
};
|
|
180
253
|
}
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
254
|
+
const fmt = (n) => n >= 1000000000 ? `$${(n / 1000000000).toFixed(1)}B`
|
|
255
|
+
: n >= 1000000 ? `$${(n / 1000000).toFixed(1)}M`
|
|
256
|
+
: n >= 1000 ? `$${(n / 1000).toFixed(0)}K`
|
|
257
|
+
: `$${n.toFixed(0)}`;
|
|
258
|
+
const lines = [
|
|
259
|
+
`## DeFi Yields on Base${token ? ` — ${token.toUpperCase()}` : ""}`,
|
|
260
|
+
`Top ${filtered.length} pools · APY ≥ ${minApy}% · Source: DeFiLlama`,
|
|
261
|
+
``,
|
|
262
|
+
`| # | Pool | Protocol | APY | TVL |`,
|
|
263
|
+
`|---|------|----------|-----|-----|`,
|
|
264
|
+
];
|
|
265
|
+
filtered.forEach((p, i) => {
|
|
266
|
+
const apy = (p.apy ?? 0).toFixed(1);
|
|
267
|
+
const tvl = fmt(p.tvlUsd ?? 0);
|
|
268
|
+
const name = (p.symbol ?? p.pool ?? "—").replace(/-/g, " ");
|
|
269
|
+
const proj = p.project ?? "—";
|
|
270
|
+
lines.push(`| ${i + 1} | ${name} | ${proj} | **${apy}%** | ${tvl} |`);
|
|
271
|
+
});
|
|
272
|
+
lines.push(``, `Use \`swap_tokens\` to position, then deposit via the protocol's UI. Always check smart contract risk before depositing.`);
|
|
273
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
185
274
|
}
|
|
186
275
|
default:
|
|
187
276
|
return null;
|
package/dist/tools/framework.js
CHANGED
|
@@ -4,32 +4,6 @@ exports.FRAMEWORK_TOOLS = void 0;
|
|
|
4
4
|
exports.handleFrameworkTool = handleFrameworkTool;
|
|
5
5
|
const convex_js_1 = require("../convex.js");
|
|
6
6
|
exports.FRAMEWORK_TOOLS = [
|
|
7
|
-
{
|
|
8
|
-
name: "create_task_packet",
|
|
9
|
-
description: "Define a scoped task for Noel Framework. Converts plain-English intent into a " +
|
|
10
|
-
"structured Task Packet (territory, permissions, doNotDo constraints). " +
|
|
11
|
-
"Sentinel validates before any agent action. " +
|
|
12
|
-
"Example: 'buy ETH when it drops 5%, max $20, don't touch my USDC'.",
|
|
13
|
-
inputSchema: {
|
|
14
|
-
type: "object",
|
|
15
|
-
properties: {
|
|
16
|
-
task: {
|
|
17
|
-
type: "string",
|
|
18
|
-
description: "What you want the agents to do, in plain English.",
|
|
19
|
-
},
|
|
20
|
-
name: {
|
|
21
|
-
type: "string",
|
|
22
|
-
description: "Optional short name for this task (max 5 words).",
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
required: ["task"],
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
name: "list_task_packets",
|
|
30
|
-
description: "List all your Task Packets — draft, active, completed, and blocked.",
|
|
31
|
-
inputSchema: { type: "object", properties: {}, required: [] },
|
|
32
|
-
},
|
|
33
7
|
{
|
|
34
8
|
name: "list_playbooks",
|
|
35
9
|
description: "List available Noel Framework playbooks — predefined multi-step agent workflows. " +
|
|
@@ -65,62 +39,10 @@ exports.FRAMEWORK_TOOLS = [
|
|
|
65
39
|
"Full transparency on what agents are and aren't allowed to do.",
|
|
66
40
|
inputSchema: { type: "object", properties: {}, required: [] },
|
|
67
41
|
},
|
|
68
|
-
{
|
|
69
|
-
name: "get_sentinel_rules",
|
|
70
|
-
description: "Get Sentinel rules for each swarm agent and each playbook role — " +
|
|
71
|
-
"territory, permissions, blocked actions, and value caps. " +
|
|
72
|
-
"Shows exactly what each agent is and isn't allowed to do.",
|
|
73
|
-
inputSchema: { type: "object", properties: {}, required: [] },
|
|
74
|
-
},
|
|
75
42
|
];
|
|
76
43
|
async function handleFrameworkTool(name, args) {
|
|
77
44
|
const a = (args ?? {});
|
|
78
45
|
switch (name) {
|
|
79
|
-
// ── create_task_packet ──────────────────────────────────────────────────
|
|
80
|
-
case "create_task_packet": {
|
|
81
|
-
if (!a.task) {
|
|
82
|
-
return { content: [{ type: "text", text: "task is required" }], isError: true };
|
|
83
|
-
}
|
|
84
|
-
const result = await (0, convex_js_1.callConvex)("/framework/task", "POST", {
|
|
85
|
-
naturalLanguage: a.task,
|
|
86
|
-
name: a.name,
|
|
87
|
-
}, "create_task_packet");
|
|
88
|
-
if (result.error) {
|
|
89
|
-
return { content: [{ type: "text", text: `Failed: ${result.error}` }], isError: true };
|
|
90
|
-
}
|
|
91
|
-
const p = result.packet ?? {};
|
|
92
|
-
const lines = [
|
|
93
|
-
`✅ **Task Packet created**`,
|
|
94
|
-
``,
|
|
95
|
-
`**Name:** ${p.name ?? "—"}`,
|
|
96
|
-
`**Task:** ${p.task ?? a.task}`,
|
|
97
|
-
`**Territory:** ${(p.territory ?? []).join(", ") || "—"}`,
|
|
98
|
-
`**Permissions:** ${(p.permissions ?? []).join(", ") || "—"}`,
|
|
99
|
-
`**Blocked:** ${(p.doNotDo ?? []).join(", ") || "none"}`,
|
|
100
|
-
`**Max value:** ${p.maxValueUsd != null ? `$${p.maxValueUsd}` : "no limit"}`,
|
|
101
|
-
``,
|
|
102
|
-
`ID: \`${result.id}\``,
|
|
103
|
-
`Ready to run a playbook with this task scope.`,
|
|
104
|
-
];
|
|
105
|
-
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
106
|
-
}
|
|
107
|
-
// ── list_task_packets ───────────────────────────────────────────────────
|
|
108
|
-
case "list_task_packets": {
|
|
109
|
-
const result = await (0, convex_js_1.callConvex)("/framework/tasks", "GET", undefined, "list_task_packets");
|
|
110
|
-
const tasks = result.tasks ?? [];
|
|
111
|
-
if (tasks.length === 0) {
|
|
112
|
-
return {
|
|
113
|
-
content: [{
|
|
114
|
-
type: "text",
|
|
115
|
-
text: "No task packets yet. Use create_task_packet to define your first task.",
|
|
116
|
-
}],
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
const list = tasks
|
|
120
|
-
.map((t) => `• **${t.name}** [${t.status}]\n ${t.task}`)
|
|
121
|
-
.join("\n\n");
|
|
122
|
-
return { content: [{ type: "text", text: `**Your Task Packets**\n\n${list}` }] };
|
|
123
|
-
}
|
|
124
46
|
// ── list_playbooks ──────────────────────────────────────────────────────
|
|
125
47
|
case "list_playbooks": {
|
|
126
48
|
const result = await (0, convex_js_1.callConvex)("/framework/playbooks", "GET", undefined, "list_playbooks");
|
|
@@ -223,36 +145,6 @@ async function handleFrameworkTool(name, args) {
|
|
|
223
145
|
}],
|
|
224
146
|
};
|
|
225
147
|
}
|
|
226
|
-
// ── get_sentinel_rules ──────────────────────────────────────────────────
|
|
227
|
-
case "get_sentinel_rules": {
|
|
228
|
-
const rules = {
|
|
229
|
-
"market-monitor": { territory: ["market_data", "price_check", "market"], permissions: ["read:market", "write:memory"], doNotDo: ["swap", "send", "transfer", "buy", "sell", "drain"], maxValueUsd: 0 },
|
|
230
|
-
"sentiment-tracker": { territory: ["sentiment", "news_analysis", "news"], permissions: ["read:market", "read:social", "write:memory"], doNotDo: ["swap", "send", "transfer", "buy", "sell"], maxValueUsd: 0 },
|
|
231
|
-
"workflow-executor": { territory: ["swap", "send", "automation"], permissions: ["read:market", "write:tx", "write:memory"], doNotDo: ["delete_wallet", "change_keys", "drain_wallet"], maxValueUsd: 500 },
|
|
232
|
-
"memory-manager": { territory: ["memory_compress", "memory_prune", "memory"], permissions: ["read:memory", "write:memory", "delete:memory"], doNotDo: ["swap", "send", "transfer"], maxValueUsd: 0 },
|
|
233
|
-
"risk-verifier": { territory: ["risk_check", "verify", "risk"], permissions: ["read:market", "read:memory"], doNotDo: ["swap", "send", "transfer", "modify_rules"], maxValueUsd: 0 },
|
|
234
|
-
"playbook:scout": { territory: ["get", "read", "list", "market", "signal", "portfolio", "insight", "research", "data", "recap", "whale", "score", "execution", "memory", "noel", "swarm", "smart"], permissions: ["read:market", "read:signals", "read:portfolio", "read:memory", "write:memory"], doNotDo: ["send_token", "deploy_token", "mint_nft", "claim_fees", "swap_tokens", "delete_automation", "stop_swarm"], maxValueUsd: 0, note: "Playbook read-only scout role" },
|
|
235
|
-
"playbook:tinker": { territory: ["create", "write", "start", "run", "swap", "send", "deploy", "mint", "claim", "automation", "memory", "swarm"], permissions: ["write:tx", "write:memory", "execute:automation", "swap:token", "deploy:token"], doNotDo: ["delete_wallet", "drain_wallet"], maxValueUsd: 100, note: "Playbook execution role" },
|
|
236
|
-
"playbook:skeptic": { territory: ["get", "read", "ask", "analyze", "verify", "research", "check", "market", "signal", "execution", "score", "portfolio", "memory", "noel", "insight"], permissions: ["read:market", "read:signals", "read:portfolio", "read:memory"], doNotDo: ["send_token", "deploy_token", "mint_nft", "claim_fees", "swap_tokens", "delete"], maxValueUsd: 0, note: "Playbook analysis/verification role" },
|
|
237
|
-
"playbook:memory": { territory: ["memory", "write", "read", "compress", "prune", "summarize"], permissions: ["read:memory", "write:memory", "delete:memory"], doNotDo: ["send_token", "swap_tokens", "deploy_token", "mint_nft", "claim_fees"], maxValueUsd: 0, note: "Playbook memory management role" },
|
|
238
|
-
};
|
|
239
|
-
const sections = [
|
|
240
|
-
"**Sentinel Rules**",
|
|
241
|
-
"",
|
|
242
|
-
"**Swarm Agents (DEFAULT_RULES)**",
|
|
243
|
-
...["market-monitor", "sentiment-tracker", "workflow-executor", "memory-manager", "risk-verifier"].map(agent => {
|
|
244
|
-
const r = rules[agent];
|
|
245
|
-
return `\n**${agent}**\n Territory: ${r.territory.join(", ")}\n Permissions: ${r.permissions.join(", ")}\n Blocked: ${r.doNotDo.join(", ")}\n Max value: $${r.maxValueUsd}`;
|
|
246
|
-
}),
|
|
247
|
-
"",
|
|
248
|
-
"**Playbook Roles (DB rules — seeded at deploy)**",
|
|
249
|
-
...["playbook:scout", "playbook:tinker", "playbook:skeptic", "playbook:memory"].map(agent => {
|
|
250
|
-
const r = rules[agent];
|
|
251
|
-
return `\n**${agent}** _(${r.note})_\n Territory: ${r.territory.slice(0, 6).join(", ")}…\n Blocked: ${r.doNotDo.join(", ")}\n Max value: $${r.maxValueUsd}`;
|
|
252
|
-
}),
|
|
253
|
-
];
|
|
254
|
-
return { content: [{ type: "text", text: sections.join("\n") }] };
|
|
255
|
-
}
|
|
256
148
|
default:
|
|
257
149
|
return null;
|
|
258
150
|
}
|