@noelclaw/mcp 1.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.
@@ -0,0 +1,162 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SWARM_TOOLS = void 0;
4
+ exports.handleSwarmTool = handleSwarmTool;
5
+ const zod_1 = require("zod");
6
+ const convex_js_1 = require("../convex.js");
7
+ exports.SWARM_TOOLS = [
8
+ {
9
+ name: "start_swarm",
10
+ description: "Start the multi-agent swarm for autonomous market monitoring, sentiment tracking, and workflow execution.",
11
+ inputSchema: {
12
+ type: "object",
13
+ properties: {
14
+ config: {
15
+ type: "object",
16
+ description: "Optional swarm config",
17
+ properties: {
18
+ enabledAgents: { type: "array", items: { type: "string" }, description: "Agent IDs to enable" },
19
+ byok: { type: "boolean", description: "Use your own Bankr API key" },
20
+ },
21
+ },
22
+ },
23
+ required: [],
24
+ },
25
+ },
26
+ {
27
+ name: "stop_swarm",
28
+ description: "Stop the active swarm session for a user.",
29
+ inputSchema: { type: "object", properties: {}, required: [] },
30
+ },
31
+ {
32
+ name: "get_swarm_status",
33
+ description: "Get the current status of the swarm: active agents, shared memory snapshot, execution scores, and recent runs.",
34
+ inputSchema: { type: "object", properties: {}, required: [] },
35
+ },
36
+ {
37
+ name: "write_swarm_memory",
38
+ description: "Write a key-value pair to the swarm's shared memory.",
39
+ inputSchema: {
40
+ type: "object",
41
+ properties: {
42
+ agentId: { type: "string", description: "ID of the agent writing this memory entry" },
43
+ key: { type: "string", description: "Memory key" },
44
+ value: { type: "string", description: "Value to store" },
45
+ ttlSeconds: { type: "number", description: "Optional TTL in seconds" },
46
+ },
47
+ required: ["agentId", "key", "value"],
48
+ },
49
+ },
50
+ {
51
+ name: "get_swarm_memory",
52
+ description: "Read a value from the swarm's shared memory by key.",
53
+ inputSchema: {
54
+ type: "object",
55
+ properties: { key: { type: "string", description: "Memory key to read" } },
56
+ required: ["key"],
57
+ },
58
+ },
59
+ {
60
+ name: "get_execution_scores",
61
+ description: "Get the self-improvement scores for all skills.",
62
+ inputSchema: { type: "object", properties: {}, required: [] },
63
+ },
64
+ ];
65
+ const StartSwarmSchema = zod_1.z.object({
66
+ config: zod_1.z.object({
67
+ enabledAgents: zod_1.z.array(zod_1.z.string()).optional(),
68
+ byok: zod_1.z.boolean().optional(),
69
+ }).optional(),
70
+ });
71
+ 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() });
72
+ const GetMemorySchema = zod_1.z.object({ key: zod_1.z.string().min(1) });
73
+ async function handleSwarmTool(name, args) {
74
+ switch (name) {
75
+ case "start_swarm": {
76
+ const parsed = StartSwarmSchema.safeParse(args ?? {});
77
+ if (!parsed.success)
78
+ return { content: [{ type: "text", text: `Invalid input: config ${parsed.error.issues[0].message}` }], isError: true };
79
+ const data = await (0, convex_js_1.callConvex)("/swarm/start", "POST", { config: parsed.data.config }, "start_swarm");
80
+ if (!data.success)
81
+ return { content: [{ type: "text", text: `Failed: ${data.error}` }], isError: true };
82
+ const agents = data.activeAgents ?? [];
83
+ return {
84
+ content: [{
85
+ type: "text",
86
+ text: [`šŸ¤– **Swarm Started**`, `Status: ${data.status}`, `Active agents (${agents.length}): ${agents.join(", ")}`, ``, `Use \`get_swarm_status\` to monitor, \`stop_swarm\` to stop.`].join("\n"),
87
+ }],
88
+ };
89
+ }
90
+ case "stop_swarm": {
91
+ const data = await (0, convex_js_1.callConvex)("/swarm/stop", "POST", {}, "stop_swarm");
92
+ if (!data.success)
93
+ return { content: [{ type: "text", text: `Failed: ${data.error}` }], isError: true };
94
+ return { content: [{ type: "text", text: `ā¹ļø Swarm stopped.` }] };
95
+ }
96
+ case "get_swarm_status": {
97
+ const data = await (0, convex_js_1.callConvex)("/swarm/status", "GET", undefined, "get_swarm_status");
98
+ if (data.error)
99
+ return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
100
+ const job = data.job;
101
+ const memory = data.memory ?? [];
102
+ const scores = data.scores ?? [];
103
+ const lines = [
104
+ `šŸ¤– **Swarm Status**`,
105
+ job ? `Status: ${job.status} | Agents: ${(job.activeAgents ?? []).join(", ")}` : `No active swarm.`,
106
+ ``,
107
+ ];
108
+ if (memory.length > 0) {
109
+ lines.push(`**Shared Memory** (${memory.length} entries)`);
110
+ for (const m of memory.slice(0, 5))
111
+ lines.push(`• [${m.agentId}] ${m.key}: ${m.value.slice(0, 80)}`);
112
+ if (memory.length > 5)
113
+ lines.push(` …and ${memory.length - 5} more`);
114
+ lines.push("");
115
+ }
116
+ if (scores.length > 0) {
117
+ lines.push(`**Execution Scores** (top skills)`);
118
+ const sorted = scores.sort((a, b) => b.lastScore - a.lastScore).slice(0, 5);
119
+ for (const s of sorted)
120
+ lines.push(`• ${s.skillName}: ${(s.lastScore * 100).toFixed(0)}% | ${s.successCount}W/${s.failCount}L | avg ${Math.round(s.avgDurationMs / 1000)}s`);
121
+ }
122
+ return { content: [{ type: "text", text: lines.join("\n") }] };
123
+ }
124
+ case "write_swarm_memory": {
125
+ const parsed = WriteMemorySchema.safeParse(args);
126
+ if (!parsed.success)
127
+ return { content: [{ type: "text", text: `Invalid input: ${String(parsed.error.issues[0].path[0])} ${parsed.error.issues[0].message}` }], isError: true };
128
+ const { agentId, key, value, ttlSeconds } = parsed.data;
129
+ await (0, convex_js_1.callConvex)("/swarm/memory/write", "POST", { agentId, key, value, ttlSeconds }, "write_swarm_memory");
130
+ return { content: [{ type: "text", text: `āœ… Memory written: [${agentId}] ${key}${ttlSeconds ? ` (expires in ${ttlSeconds}s)` : ""}` }] };
131
+ }
132
+ case "get_swarm_memory": {
133
+ const parsed = GetMemorySchema.safeParse(args);
134
+ if (!parsed.success)
135
+ return { content: [{ type: "text", text: `Invalid input: key ${parsed.error.issues[0].message}` }], isError: true };
136
+ const data = await (0, convex_js_1.callConvex)(`/swarm/memory?key=${encodeURIComponent(parsed.data.key)}`, "GET", undefined, "get_swarm_memory");
137
+ if (data.error)
138
+ return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
139
+ if (data.value === null || data.value === undefined)
140
+ return { content: [{ type: "text", text: `No value found for key: ${parsed.data.key}` }] };
141
+ return { content: [{ type: "text", text: `**${parsed.data.key}**: ${data.value}` }] };
142
+ }
143
+ case "get_execution_scores": {
144
+ const data = await (0, convex_js_1.callConvex)("/swarm/scores", "GET", undefined, "get_execution_scores");
145
+ if (data.error)
146
+ return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
147
+ const scores = data.scores ?? [];
148
+ if (!scores.length)
149
+ return { content: [{ type: "text", text: "No execution scores yet. Run some swarm agents to build a history." }] };
150
+ const sorted = scores.sort((a, b) => b.lastScore - a.lastScore);
151
+ const lines = [
152
+ `**Execution Scores**`, ``,
153
+ `| Skill | Score | W | L | Avg Duration | Last Adapted |`,
154
+ `|-------|-------|---|---|--------------|--------------|`,
155
+ ...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()} |`),
156
+ ];
157
+ return { content: [{ type: "text", text: lines.join("\n") }] };
158
+ }
159
+ default:
160
+ return null;
161
+ }
162
+ }
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TWITTER_TOOLS = void 0;
4
+ exports.handleTwitterTool = handleTwitterTool;
5
+ const zod_1 = require("zod");
6
+ exports.TWITTER_TOOLS = [
7
+ {
8
+ name: "post_tweet",
9
+ description: "Post a tweet on X (Twitter) via Ayrshare. Requires AYRSHARE_API_KEY env var.",
10
+ inputSchema: {
11
+ type: "object",
12
+ properties: {
13
+ text: { type: "string", description: "Tweet content (max 280 characters)" },
14
+ reply_to: { type: "string", description: "Optional: tweet ID to reply to" },
15
+ },
16
+ required: ["text"],
17
+ },
18
+ },
19
+ ];
20
+ const PostTweetSchema = zod_1.z.object({
21
+ text: zod_1.z.string().min(1).max(280),
22
+ reply_to: zod_1.z.string().optional(),
23
+ });
24
+ async function handleTwitterTool(name, args) {
25
+ if (name !== "post_tweet")
26
+ return null;
27
+ const parsed = PostTweetSchema.safeParse(args);
28
+ if (!parsed.success)
29
+ return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
30
+ const apiKey = process.env.AYRSHARE_API_KEY;
31
+ if (!apiKey) {
32
+ return {
33
+ content: [{ type: "text", text: "AYRSHARE_API_KEY not set.\n\nGet your key at ayrshare.com → Profile → API Key" }],
34
+ isError: true,
35
+ };
36
+ }
37
+ const body = {
38
+ post: parsed.data.text,
39
+ platforms: ["twitter"],
40
+ };
41
+ if (parsed.data.reply_to)
42
+ body.twitterOptions = { inReplyToStatusId: parsed.data.reply_to };
43
+ try {
44
+ const res = await fetch("https://app.ayrshare.com/api/post", {
45
+ method: "POST",
46
+ headers: {
47
+ "Content-Type": "application/json",
48
+ Authorization: `Bearer ${apiKey}`,
49
+ },
50
+ body: JSON.stringify(body),
51
+ signal: AbortSignal.timeout(15000),
52
+ });
53
+ const data = await res.json();
54
+ if (!res.ok || data.status === "error") {
55
+ const msg = data?.message ?? data?.errors?.[0] ?? JSON.stringify(data);
56
+ return { content: [{ type: "text", text: `Ayrshare error: ${msg}` }], isError: true };
57
+ }
58
+ const tweetId = data?.postIds?.find((p) => p.platform === "twitter")?.id;
59
+ const tweetUrl = tweetId ? `https://x.com/i/web/status/${tweetId}` : "";
60
+ return {
61
+ content: [{ type: "text", text: `āœ… Tweet posted!\n\n"${parsed.data.text}"${tweetUrl ? `\n\n${tweetUrl}` : ""}` }],
62
+ };
63
+ }
64
+ catch (err) {
65
+ return { content: [{ type: "text", text: `Failed to post tweet: ${err.message}` }], isError: true };
66
+ }
67
+ }
@@ -0,0 +1,293 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VAULT_TOOLS = void 0;
4
+ exports.handleVaultTool = handleVaultTool;
5
+ const zod_1 = require("zod");
6
+ const convex_js_1 = require("../convex.js");
7
+ const VAULT_TYPES = ["research", "execution", "workflow", "prompt", "file", "memory"];
8
+ const LINK_RELATIONS = ["references", "derived_from", "supersedes", "related", "continues"];
9
+ exports.VAULT_TOOLS = [
10
+ {
11
+ name: "vault_save",
12
+ description: "Save or update an artifact in Noel-Vault — the persistent memory layer for agents. " +
13
+ "Use this to store research outputs, execution logs, workflows, versioned prompts, " +
14
+ "generated files, or long-term memory. Each save creates a new version automatically. " +
15
+ "Same key = update existing (git-style). Types: research | execution | workflow | prompt | file | memory.",
16
+ inputSchema: {
17
+ type: "object",
18
+ properties: {
19
+ type: { type: "string", enum: [...VAULT_TYPES], description: "Entry type" },
20
+ title: { type: "string", description: "Human-readable title" },
21
+ content: { type: "string", description: "Main content — markdown, JSON, code, or plain text" },
22
+ key: { type: "string", description: "Optional slug key e.g. 'research/btc-dominance-analysis'. Auto-generated if omitted." },
23
+ contentType: { type: "string", enum: ["markdown", "json", "text", "code"], description: "Content format hint" },
24
+ agentId: { type: "string", description: "Agent ID writing this entry" },
25
+ tags: { type: "array", items: { type: "string" }, description: "Tags for filtering and search" },
26
+ commitMsg: { type: "string", description: "Commit message for this version, e.g. 'initial research', 'refined with on-chain data'" },
27
+ metadata: { type: "string", description: "Optional JSON string for extra structured fields" },
28
+ },
29
+ required: ["type", "title", "content"],
30
+ },
31
+ },
32
+ {
33
+ name: "vault_read",
34
+ description: "Read a Noel-Vault entry by its key. Returns full content, version, tags, and any linked entries.",
35
+ inputSchema: {
36
+ type: "object",
37
+ properties: {
38
+ key: { type: "string", description: "Entry key e.g. 'research/btc-dominance-analysis'" },
39
+ },
40
+ required: ["key"],
41
+ },
42
+ },
43
+ {
44
+ name: "vault_list",
45
+ description: "List Noel-Vault entries. Filter by type, agent, or pinned status. Returns previews, not full content.",
46
+ inputSchema: {
47
+ type: "object",
48
+ properties: {
49
+ type: { type: "string", enum: [...VAULT_TYPES], description: "Filter by type" },
50
+ agentId: { type: "string", description: "Filter by agent that wrote the entries" },
51
+ pinned: { type: "boolean", description: "Show only pinned entries" },
52
+ limit: { type: "number", description: "Max entries to return (default 50)" },
53
+ },
54
+ required: [],
55
+ },
56
+ },
57
+ {
58
+ name: "vault_search",
59
+ description: "Full-text search across Noel-Vault. Searches content, titles, and tags. " +
60
+ "Optionally filter by type. Returns ranked results with previews.",
61
+ inputSchema: {
62
+ type: "object",
63
+ properties: {
64
+ query: { type: "string", description: "Search query" },
65
+ type: { type: "string", enum: [...VAULT_TYPES], description: "Narrow search to a specific type" },
66
+ limit: { type: "number", description: "Max results (default 20)" },
67
+ },
68
+ required: ["query"],
69
+ },
70
+ },
71
+ {
72
+ name: "vault_history",
73
+ description: "Get the full version history of a Noel-Vault entry — like git log. " +
74
+ "Shows each version with its commit message, author agent, size, and timestamp.",
75
+ inputSchema: {
76
+ type: "object",
77
+ properties: {
78
+ key: { type: "string", description: "Entry key" },
79
+ },
80
+ required: ["key"],
81
+ },
82
+ },
83
+ {
84
+ name: "vault_diff",
85
+ description: "Compare two versions of a Noel-Vault entry — like git diff. " +
86
+ "Shows lines added (+) and removed (-) between fromVersion and toVersion.",
87
+ inputSchema: {
88
+ type: "object",
89
+ properties: {
90
+ key: { type: "string", description: "Entry key" },
91
+ fromVersion: { type: "number", description: "Older version number" },
92
+ toVersion: { type: "number", description: "Newer version number" },
93
+ },
94
+ required: ["key", "fromVersion", "toVersion"],
95
+ },
96
+ },
97
+ {
98
+ name: "vault_export",
99
+ description: "Export your entire Noel-Vault or a specific type as a structured bundle. " +
100
+ "Useful for archiving, syncing to Gitlawb, or passing context to another agent.",
101
+ inputSchema: {
102
+ type: "object",
103
+ properties: {
104
+ type: { type: "string", enum: [...VAULT_TYPES], description: "Export only this type (omit for full export)" },
105
+ },
106
+ required: [],
107
+ },
108
+ },
109
+ ];
110
+ // ─── Zod schemas ─────────────────────────────────────────────────────────────
111
+ const SaveSchema = zod_1.z.object({
112
+ type: zod_1.z.enum(VAULT_TYPES),
113
+ title: zod_1.z.string().min(1),
114
+ content: zod_1.z.string().min(1),
115
+ key: zod_1.z.string().optional(),
116
+ contentType: zod_1.z.enum(["markdown", "json", "text", "code"]).optional(),
117
+ agentId: zod_1.z.string().optional(),
118
+ tags: zod_1.z.array(zod_1.z.string()).optional(),
119
+ commitMsg: zod_1.z.string().optional(),
120
+ metadata: zod_1.z.string().optional(),
121
+ });
122
+ const ReadSchema = zod_1.z.object({ key: zod_1.z.string().min(1) });
123
+ const ListSchema = zod_1.z.object({
124
+ type: zod_1.z.enum(VAULT_TYPES).optional(),
125
+ agentId: zod_1.z.string().optional(),
126
+ pinned: zod_1.z.boolean().optional(),
127
+ limit: zod_1.z.number().optional(),
128
+ });
129
+ const SearchSchema = zod_1.z.object({
130
+ query: zod_1.z.string().min(1),
131
+ type: zod_1.z.enum(VAULT_TYPES).optional(),
132
+ limit: zod_1.z.number().optional(),
133
+ });
134
+ const HistorySchema = zod_1.z.object({ key: zod_1.z.string().min(1) });
135
+ const DiffSchema = zod_1.z.object({ key: zod_1.z.string().min(1), fromVersion: zod_1.z.number(), toVersion: zod_1.z.number() });
136
+ const ExportSchema = zod_1.z.object({ type: zod_1.z.enum(VAULT_TYPES).optional() });
137
+ // ─── Helpers ─────────────────────────────────────────────────────────────────
138
+ function formatBytes(n) {
139
+ if (!n)
140
+ return "—";
141
+ if (n < 1024)
142
+ return `${n}B`;
143
+ if (n < 1024 * 1024)
144
+ return `${(n / 1024).toFixed(1)}KB`;
145
+ return `${(n / 1024 / 1024).toFixed(2)}MB`;
146
+ }
147
+ function formatDate(ts) {
148
+ return new Date(ts).toUTCString();
149
+ }
150
+ // ─── Handler ─────────────────────────────────────────────────────────────────
151
+ async function handleVaultTool(name, args) {
152
+ switch (name) {
153
+ case "vault_save": {
154
+ const parsed = SaveSchema.safeParse(args);
155
+ if (!parsed.success)
156
+ return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
157
+ const data = await (0, convex_js_1.callConvex)("/vault/save", "POST", parsed.data, "vault_save");
158
+ if (data.error)
159
+ return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
160
+ const { key, version, changed } = data;
161
+ const lines = [
162
+ `šŸ“¦ **Vault ${changed ? (version === 1 ? "Created" : "Updated") : "Unchanged"}**`,
163
+ `Key: \`${key}\``,
164
+ `Version: v${version}`,
165
+ changed && version > 1 ? `Previous version auto-snapshotted.` : "",
166
+ ``,
167
+ `Use \`vault_read\` to retrieve, \`vault_history\` to see all versions.`,
168
+ ].filter(Boolean);
169
+ return { content: [{ type: "text", text: lines.join("\n") }] };
170
+ }
171
+ case "vault_read": {
172
+ const parsed = ReadSchema.safeParse(args);
173
+ if (!parsed.success)
174
+ return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
175
+ const data = await (0, convex_js_1.callConvex)(`/vault/entry?key=${encodeURIComponent(parsed.data.key)}`, "GET", undefined, "vault_read");
176
+ if (data.error)
177
+ return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
178
+ const lines = [
179
+ `šŸ“‚ **${data.title}**`,
180
+ `Key: \`${data.key}\` Ā· Type: ${data.type} Ā· v${data.version} Ā· ${formatBytes(data.size)}`,
181
+ data.tags?.length ? `Tags: ${data.tags.join(", ")}` : "",
182
+ data.isPinned ? "šŸ“Œ Pinned" : "",
183
+ data.agentId ? `Agent: ${data.agentId}` : "",
184
+ `Updated: ${formatDate(data.updatedAt)}`,
185
+ data.linkedKeys?.length ? `\nLinks:\n${data.linkedKeys.map((l) => ` → ${l}`).join("\n")}` : "",
186
+ ``,
187
+ `---`,
188
+ ``,
189
+ data.content,
190
+ ].filter((l) => l !== "");
191
+ return { content: [{ type: "text", text: lines.join("\n") }] };
192
+ }
193
+ case "vault_list": {
194
+ const parsed = ListSchema.safeParse(args ?? {});
195
+ if (!parsed.success)
196
+ return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
197
+ const params = new URLSearchParams();
198
+ if (parsed.data.type)
199
+ params.set("type", parsed.data.type);
200
+ if (parsed.data.agentId)
201
+ params.set("agentId", parsed.data.agentId);
202
+ if (parsed.data.pinned !== undefined)
203
+ params.set("pinned", String(parsed.data.pinned));
204
+ if (parsed.data.limit)
205
+ params.set("limit", String(parsed.data.limit));
206
+ const data = await (0, convex_js_1.callConvex)(`/vault/list?${params}`, "GET", undefined, "vault_list");
207
+ if (data.error)
208
+ return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
209
+ const entries = data.entries ?? [];
210
+ if (!entries.length)
211
+ return { content: [{ type: "text", text: `No vault entries found${parsed.data.type ? ` of type '${parsed.data.type}'` : ""}.` }] };
212
+ const header = `šŸ“š **Noel-Vault** (${entries.length} entries)`;
213
+ const rows = entries.map((e) => `${e.isPinned ? "šŸ“Œ " : ""}[\`${e.key}\`] ${e.title} — v${e.version} Ā· ${e.type} Ā· ${formatBytes(e.size)} Ā· ${formatDate(e.updatedAt)}`);
214
+ return { content: [{ type: "text", text: [header, "", ...rows].join("\n") }] };
215
+ }
216
+ case "vault_search": {
217
+ const parsed = SearchSchema.safeParse(args);
218
+ if (!parsed.success)
219
+ return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
220
+ const params = new URLSearchParams({ q: parsed.data.query });
221
+ if (parsed.data.type)
222
+ params.set("type", parsed.data.type);
223
+ if (parsed.data.limit)
224
+ params.set("limit", String(parsed.data.limit));
225
+ const data = await (0, convex_js_1.callConvex)(`/vault/search?${params}`, "GET", undefined, "vault_search");
226
+ if (data.error)
227
+ return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
228
+ const results = data.results ?? [];
229
+ if (!results.length)
230
+ return { content: [{ type: "text", text: `No vault entries found for: "${parsed.data.query}"` }] };
231
+ const header = `šŸ” **Vault Search**: "${parsed.data.query}" — ${results.length} result(s)`;
232
+ const rows = results.map((r, i) => [
233
+ `${i + 1}. [\`${r.key}\`] **${r.title}** (${r.type} Ā· v${r.version})`,
234
+ ` ${r.preview}`,
235
+ ].join("\n"));
236
+ return { content: [{ type: "text", text: [header, "", ...rows].join("\n") }] };
237
+ }
238
+ case "vault_history": {
239
+ const parsed = HistorySchema.safeParse(args);
240
+ if (!parsed.success)
241
+ return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
242
+ const data = await (0, convex_js_1.callConvex)(`/vault/history?key=${encodeURIComponent(parsed.data.key)}`, "GET", undefined, "vault_history");
243
+ if (data.error)
244
+ return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
245
+ const { key, title, currentVersion, history } = data;
246
+ const header = [
247
+ `šŸ“œ **History**: ${title}`,
248
+ `Key: \`${key}\` Ā· Current: v${currentVersion}`,
249
+ ``,
250
+ `| Version | Commit | Agent | Size | Date |`,
251
+ `|---------|--------|-------|------|------|`,
252
+ ];
253
+ const rows = history.map((v) => `| v${v.version} | ${v.commitMsg ?? "—"} | ${v.agentId ?? "—"} | ${formatBytes(v.size)} | ${formatDate(v.createdAt)} |`);
254
+ return { content: [{ type: "text", text: [...header, ...rows].join("\n") }] };
255
+ }
256
+ case "vault_diff": {
257
+ const parsed = DiffSchema.safeParse(args);
258
+ if (!parsed.success)
259
+ return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
260
+ const { key, fromVersion, toVersion } = parsed.data;
261
+ const data = await (0, convex_js_1.callConvex)(`/vault/diff?key=${encodeURIComponent(key)}&from=${fromVersion}&to=${toVersion}`, "GET", undefined, "vault_diff");
262
+ if (data.error)
263
+ return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
264
+ const lines = [
265
+ `šŸ“ **Diff**: \`${data.key}\` — v${fromVersion} → v${toVersion}`,
266
+ ``,
267
+ "```diff",
268
+ data.diff,
269
+ "```",
270
+ ];
271
+ return { content: [{ type: "text", text: lines.join("\n") }] };
272
+ }
273
+ case "vault_export": {
274
+ const parsed = ExportSchema.safeParse(args ?? {});
275
+ if (!parsed.success)
276
+ return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
277
+ const params = parsed.data.type ? `?type=${parsed.data.type}` : "";
278
+ const data = await (0, convex_js_1.callConvex)(`/vault/export${params}`, "GET", undefined, "vault_export");
279
+ if (data.error)
280
+ return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
281
+ const { exportedAt, totalEntries, entries } = data;
282
+ const header = [
283
+ `šŸ“¤ **Vault Export**`,
284
+ `Exported: ${formatDate(exportedAt)} Ā· ${totalEntries} entries${parsed.data.type ? ` (type: ${parsed.data.type})` : ""}`,
285
+ ``,
286
+ ];
287
+ const rows = entries.map((e) => `**[\`${e.key}\`]** ${e.title} (${e.type} Ā· v${e.version})\n${e.content.slice(0, 500)}${e.content.length > 500 ? "\n…" : ""}`);
288
+ return { content: [{ type: "text", text: [...header, ...rows.join("\n\n---\n\n").split("\n")].join("\n") }] };
289
+ }
290
+ default:
291
+ return null;
292
+ }
293
+ }
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WALLET_TOOLS = void 0;
4
+ exports.handleWalletTool = handleWalletTool;
5
+ const zod_1 = require("zod");
6
+ const convex_js_1 = require("../convex.js");
7
+ const wallet_js_1 = require("../wallet.js");
8
+ exports.WALLET_TOOLS = [
9
+ {
10
+ name: "get_wallet_address",
11
+ description: "Get your Noelclaw wallet address. This is the local MCP wallet used to sign " +
12
+ "requests and receive on-chain assets. Keys never leave your machine.",
13
+ inputSchema: { type: "object", properties: {}, required: [] },
14
+ },
15
+ {
16
+ name: "set_telegram",
17
+ description: "Connect your Telegram account for push notifications — trading signals, " +
18
+ "whale alerts, and daily recaps sent directly to your Telegram.",
19
+ inputSchema: {
20
+ type: "object",
21
+ properties: {
22
+ chat_id: {
23
+ type: "string",
24
+ description: "Your Telegram chat ID. Get it by messaging @userinfobot on Telegram.",
25
+ },
26
+ bot_token: {
27
+ type: "string",
28
+ description: "Optional: your own Telegram bot token. Leave empty to use the Noelclaw bot.",
29
+ },
30
+ },
31
+ required: ["chat_id"],
32
+ },
33
+ },
34
+ ];
35
+ const SetTelegramSchema = zod_1.z.object({
36
+ chat_id: zod_1.z.string().min(1),
37
+ bot_token: zod_1.z.string().optional(),
38
+ });
39
+ async function handleWalletTool(name, args) {
40
+ switch (name) {
41
+ case "get_wallet_address": {
42
+ try {
43
+ const wallet = await (0, wallet_js_1.getOrCreateWallet)();
44
+ return {
45
+ content: [{
46
+ type: "text",
47
+ text: [
48
+ `**Your Noelclaw Wallet**`,
49
+ ``,
50
+ `Address: \`${wallet.address}\``,
51
+ `Network: Base mainnet (chainId 8453)`,
52
+ ``,
53
+ `This wallet is stored locally at \`~/.noelclaw/wallet.json\`.`,
54
+ `Private keys never leave your machine — all signing happens locally.`,
55
+ ``,
56
+ `Use this address to receive ETH, USDC, or any ERC-20 token on Base.`,
57
+ ].join("\n"),
58
+ }],
59
+ };
60
+ }
61
+ catch (err) {
62
+ return { content: [{ type: "text", text: `Failed to load wallet: ${err.message}` }], isError: true };
63
+ }
64
+ }
65
+ case "set_telegram": {
66
+ const parsed = SetTelegramSchema.safeParse(args);
67
+ if (!parsed.success) {
68
+ return { content: [{ type: "text", text: `Invalid input: chat_id ${parsed.error.issues[0].message}` }], isError: true };
69
+ }
70
+ const { chat_id, bot_token } = parsed.data;
71
+ const result = await (0, convex_js_1.callConvex)("/telegram/connect", "POST", {
72
+ chatId: chat_id,
73
+ botToken: bot_token,
74
+ }, "set_telegram");
75
+ if (result.error) {
76
+ return { content: [{ type: "text", text: `Failed: ${result.error}` }], isError: true };
77
+ }
78
+ return {
79
+ content: [{
80
+ type: "text",
81
+ text: [
82
+ `āœ… **Telegram connected**`,
83
+ ``,
84
+ `Chat ID: \`${chat_id}\``,
85
+ `You'll now receive:`,
86
+ `• Trading signals (BTC/ETH, 08:00 UTC daily)`,
87
+ `• Whale alerts (every 6 hours)`,
88
+ `• Daily recap`,
89
+ `• Research reports`,
90
+ ``,
91
+ `To stop: message the bot or use \`set_telegram\` with an empty chat_id.`,
92
+ ].join("\n"),
93
+ }],
94
+ };
95
+ }
96
+ default:
97
+ return null;
98
+ }
99
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });