@noelclaw/mcp 2.3.0 → 2.3.1

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.
@@ -1,82 +1,53 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MEMORY_TOOLS = exports.SM_SPACE = void 0;
4
- exports.smFetch = smFetch;
3
+ exports.MEMORY_TOOLS = void 0;
5
4
  exports.syncToSupermemory = syncToSupermemory;
6
5
  exports.searchSupermemory = searchSupermemory;
7
6
  exports.handleMemoryTool = handleMemoryTool;
8
7
  const zod_1 = require("zod");
9
- const SM_BASE = "https://api.supermemory.ai/v3";
10
- exports.SM_SPACE = "noelclaw-vault";
11
- // ─── Supermemory HTTP helpers ────────────────────────────────────────────────
12
- async function smFetch(path, method, body) {
13
- const apiKey = process.env.SUPERMEMORY_API_KEY;
14
- if (!apiKey)
15
- return { ok: false, data: null, error: "SUPERMEMORY_API_KEY not set" };
16
- try {
17
- const res = await fetch(`${SM_BASE}${path}`, {
18
- method,
19
- headers: {
20
- Authorization: `Bearer ${apiKey}`,
21
- "Content-Type": "application/json",
22
- },
23
- body: body ? JSON.stringify(body) : undefined,
24
- signal: AbortSignal.timeout(15000),
25
- });
26
- if (!res.ok) {
27
- const err = await res.text().catch(() => "");
28
- return { ok: false, data: null, error: `${res.status}: ${err.slice(0, 120)}` };
29
- }
30
- return { ok: true, data: await res.json() };
31
- }
32
- catch (err) {
33
- return { ok: false, data: null, error: err.message };
34
- }
35
- }
8
+ const convex_js_1 = require("../convex.js");
9
+ // ─── Helpers (proxied through Convex — server-side Supermemory key) ──────────
36
10
  async function syncToSupermemory(content, metadata, sourceUrl) {
37
- await smFetch("/memories", "POST", {
11
+ await (0, convex_js_1.callConvex)("/memory/add", "POST", {
38
12
  content,
39
13
  metadata,
40
- spaces: [exports.SM_SPACE],
41
14
  ...(sourceUrl ? { sourceUrl } : {}),
42
- });
15
+ }).catch(() => { });
43
16
  }
44
17
  async function searchSupermemory(query, limit = 10) {
45
- const { ok, data } = await smFetch("/search", "POST", {
46
- q: query,
47
- n: limit,
48
- spaces: [exports.SM_SPACE],
49
- returnContext: true,
50
- });
51
- if (!ok || !data?.results)
18
+ try {
19
+ const data = await (0, convex_js_1.callConvex)("/memory/search", "POST", { q: query, n: limit });
20
+ return data?.results ?? [];
21
+ }
22
+ catch {
52
23
  return [];
53
- return data.results;
24
+ }
54
25
  }
55
26
  // ─── Tool definitions ────────────────────────────────────────────────────────
56
27
  exports.MEMORY_TOOLS = [
57
28
  {
58
29
  name: "memory_add",
59
- description: "Add content to Noelclaw's semantic memory layer (powered by Supermemory). " +
60
- "Unlike vault_save, memory_add is quick no versioning, no type required. " +
61
- "Use this for notes, decisions, preferences, URLs, or anything you want to " +
62
- "find later with natural language search. Memory is indexed semantically — " +
63
- "'what did I say about ETH yield?' will find it even without exact keywords.",
30
+ description: "Add content to your Noelclaw semantic memory no setup needed, no extra API keys. " +
31
+ "Unlike vault_save, memory_add is instant: no versioning, no type required. " +
32
+ "Use for notes, decisions, preferences, URLs, or anything you want to find later " +
33
+ "with natural language. Memory is indexed semantically — 'what did I say about ETH yield?' " +
34
+ "will find it even without exact keywords.",
64
35
  inputSchema: {
65
36
  type: "object",
66
37
  properties: {
67
38
  content: { type: "string", description: "Content to remember — text, markdown, or a note" },
68
39
  title: { type: "string", description: "Optional title for this memory" },
69
40
  tags: { type: "array", items: { type: "string" }, description: "Tags for grouping" },
70
- sourceUrl: { type: "string", description: "Optional URL — if provided, Supermemory also fetches and indexes the page content" },
41
+ sourceUrl: { type: "string", description: "Optional URL — Noelclaw will fetch and index the page content automatically" },
71
42
  },
72
43
  required: ["content"],
73
44
  },
74
45
  },
75
46
  {
76
47
  name: "memory_search",
77
- description: "Semantic search across all Noelclaw memories. Unlike keyword search, this " +
78
- "understands meaning — 'low risk crypto yield' will match 'conservative DeFi strategies'. " +
79
- "Also searches memories synced from vault_save and vault_remember automatically.",
48
+ description: "Semantic search across all your Noelclaw memories. Understands meaning, not just keywords — " +
49
+ "'low risk crypto yield' matches 'conservative DeFi strategies'. " +
50
+ "Also searches memories auto-synced from vault_save and vault_remember.",
80
51
  inputSchema: {
81
52
  type: "object",
82
53
  properties: {
@@ -88,10 +59,9 @@ exports.MEMORY_TOOLS = [
88
59
  },
89
60
  {
90
61
  name: "memory_context",
91
- description: "Retrieve the most semantically relevant memories for a topic and format them " +
92
- "as ready-to-inject context. Use at the start of research tasks to prime with " +
93
- "everything stored about a topic. Smarter than vault_context — uses vector search, " +
94
- "not just keyword matching.",
62
+ description: "Retrieve the most semantically relevant memories for a topic, formatted as AI-ready context. " +
63
+ "Use at the start of research tasks to prime with everything stored about a topic. " +
64
+ "Smarter than vault_context — uses vector search, not just keyword matching.",
95
65
  inputSchema: {
96
66
  type: "object",
97
67
  properties: {
@@ -103,8 +73,8 @@ exports.MEMORY_TOOLS = [
103
73
  },
104
74
  {
105
75
  name: "memory_profile",
106
- description: "Show your semantic memory stats — how many memories, recent additions, and " +
107
- "connected data sources. Useful for auditing what Noelclaw knows about you.",
76
+ description: "Show your semantic memory stats — total memories stored, your memory space, and connected sources. " +
77
+ "Useful for auditing what Noelclaw knows about you.",
108
78
  inputSchema: {
109
79
  type: "object",
110
80
  properties: {},
@@ -113,32 +83,18 @@ exports.MEMORY_TOOLS = [
113
83
  },
114
84
  {
115
85
  name: "memory_connect",
116
- description: "Connect an external data source to Noelclaw's semantic memory, or add content " +
117
- "from a specific URL. Supported sources:\n" +
118
- "- url: fetch and index any web page or GitHub file\n" +
119
- "- github: index a GitHub repository README/docs\n" +
120
- "- notion: add content from a public Notion page URL\n" +
121
- "- manual: paste content directly (same as memory_add with sourceUrl)\n" +
122
- "For Google Drive, Gmail, and full Notion sync, connect via your Noelclaw dashboard.",
86
+ description: "Fetch and index content from an external URL into your semantic memory. " +
87
+ "Works with any web page, GitHub repo/file, or Notion page URL. " +
88
+ "Noelclaw crawls the URL and indexes the full content searchable in ~30s. " +
89
+ "For full Google Drive / Gmail / Notion workspace sync, connect via the Noelclaw dashboard.",
123
90
  inputSchema: {
124
91
  type: "object",
125
92
  properties: {
126
- source: {
127
- type: "string",
128
- enum: ["url", "github", "notion", "manual"],
129
- description: "Source type",
130
- },
131
- url: {
132
- type: "string",
133
- description: "URL to fetch and index (required for url/github/notion sources)",
134
- },
135
- content: {
136
- type: "string",
137
- description: "Direct content (for source: manual)",
138
- },
139
- title: { type: "string", description: "Title for this memory" },
93
+ url: { type: "string", description: "URL to fetch and index (GitHub, Notion, web page, etc.)" },
94
+ title: { type: "string", description: "Optional title for this memory" },
95
+ tags: { type: "array", items: { type: "string" }, description: "Optional tags" },
140
96
  },
141
- required: ["source"],
97
+ required: ["url"],
142
98
  },
143
99
  },
144
100
  ];
@@ -158,41 +114,35 @@ const ContextSchema = zod_1.z.object({
158
114
  limit: zod_1.z.number().optional(),
159
115
  });
160
116
  const ConnectSchema = zod_1.z.object({
161
- source: zod_1.z.enum(["url", "github", "notion", "manual"]),
162
- url: zod_1.z.string().url().optional(),
163
- content: zod_1.z.string().optional(),
117
+ url: zod_1.z.string().url(),
164
118
  title: zod_1.z.string().optional(),
119
+ tags: zod_1.z.array(zod_1.z.string()).optional(),
165
120
  });
166
121
  // ─── Handler ─────────────────────────────────────────────────────────────────
167
122
  async function handleMemoryTool(name, args) {
168
- const apiKey = process.env.SUPERMEMORY_API_KEY;
169
123
  switch (name) {
170
124
  case "memory_add": {
171
125
  const parsed = AddSchema.safeParse(args);
172
126
  if (!parsed.success)
173
127
  return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
174
- if (!apiKey)
175
- return { content: [{ type: "text", text: "⚠️ SUPERMEMORY_API_KEY not configured.\nSet it in your MCP env to enable semantic memory.\nGet a free key at supermemory.ai" }], isError: true };
176
128
  const { content, title, tags, sourceUrl } = parsed.data;
177
- const { ok, data, error } = await smFetch("/memories", "POST", {
129
+ const data = await (0, convex_js_1.callConvex)("/memory/add", "POST", {
178
130
  content,
179
- metadata: { title, tags, source: "noelclaw-mcp", addedAt: Date.now() },
180
- spaces: [exports.SM_SPACE],
131
+ metadata: { title, tags, source: "memory_add", addedAt: Date.now() },
181
132
  ...(sourceUrl ? { sourceUrl } : {}),
182
- });
183
- if (!ok)
184
- return { content: [{ type: "text", text: `Error adding to memory: ${error}` }], isError: true };
185
- const memId = data?.id ?? data?.memoryId ?? "saved";
133
+ }).catch((err) => ({ error: err.message }));
134
+ if (data?.error)
135
+ return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
186
136
  return {
187
137
  content: [{
188
138
  type: "text",
189
139
  text: [
190
- `🧠 **Memory added** — ID: \`${memId}\``,
140
+ `🧠 **Memory added** — ID: \`${data?.id ?? "saved"}\``,
191
141
  title ? `Title: ${title}` : "",
192
- sourceUrl ? `Source: ${sourceUrl}` : "",
142
+ sourceUrl ? `Source: ${sourceUrl} (indexing in background…)` : "",
193
143
  tags?.length ? `Tags: ${tags.join(", ")}` : "",
194
144
  ``,
195
- `Find it later with: \`memory_search query: "${(title ?? content).slice(0, 40)}"\``,
145
+ `Find it with: \`memory_search query: "${(title ?? content).slice(0, 40)}"\``,
196
146
  ].filter(Boolean).join("\n"),
197
147
  }],
198
148
  };
@@ -201,8 +151,6 @@ async function handleMemoryTool(name, args) {
201
151
  const parsed = SearchSchema.safeParse(args);
202
152
  if (!parsed.success)
203
153
  return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
204
- if (!apiKey)
205
- return { content: [{ type: "text", text: "⚠️ SUPERMEMORY_API_KEY not configured. Set it in your MCP env to enable semantic memory." }], isError: true };
206
154
  const { query, limit = 10 } = parsed.data;
207
155
  const results = await searchSupermemory(query, limit);
208
156
  if (!results.length)
@@ -223,8 +171,6 @@ async function handleMemoryTool(name, args) {
223
171
  const parsed = ContextSchema.safeParse(args);
224
172
  if (!parsed.success)
225
173
  return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
226
- if (!apiKey)
227
- return { content: [{ type: "text", text: "⚠️ SUPERMEMORY_API_KEY not configured. Set it in your MCP env to enable semantic memory." }], isError: true };
228
174
  const { topic, limit = 8 } = parsed.data;
229
175
  const results = await searchSupermemory(topic, limit);
230
176
  if (!results.length)
@@ -249,32 +195,26 @@ async function handleMemoryTool(name, args) {
249
195
  };
250
196
  }
251
197
  case "memory_profile": {
252
- if (!apiKey)
253
- return { content: [{ type: "text", text: "⚠️ SUPERMEMORY_API_KEY not configured.\n\nSet `SUPERMEMORY_API_KEY` in your MCP env vars.\nGet a free key at supermemory.ai" }], isError: true };
254
- const { ok, data, error } = await smFetch(`/memories?spaces=${exports.SM_SPACE}&limit=1`, "GET");
255
- if (!ok)
256
- return { content: [{ type: "text", text: `Error fetching profile: ${error}` }], isError: true };
257
- const count = data?.total ?? data?.count ?? "unknown";
198
+ const data = await (0, convex_js_1.callConvex)("/memory/profile", "GET").catch(() => null);
199
+ const total = data?.total ?? 0;
200
+ const status = data?.status ?? "unknown";
201
+ const space = data?.space ?? "—";
258
202
  return {
259
203
  content: [{
260
204
  type: "text",
261
205
  text: [
262
- `🧠 **Noelclaw Semantic Memory Profile**`,
206
+ `🧠 **Noelclaw Semantic Memory**`,
263
207
  ``,
264
- `Space: \`${exports.SM_SPACE}\``,
265
- `Total memories: **${count}**`,
266
- `Status: ✅ Connected (Supermemory)`,
208
+ `Space: \`${space}\``,
209
+ `Total memories: **${total}**`,
210
+ `Status: ${status === "ok" ? "Active" : status === "not_configured" ? "⏳ Setting up" : "⚠️ " + status}`,
267
211
  ``,
268
- `**Data sources:**`,
269
- `• vault_save / vault_remember — auto-synced ✅`,
270
- `• memory_add direct input ✅`,
271
- `• memory_connectURL ingestion ✅`,
272
- `• Google Drive / Gmail / Notion — connect via noelclaw.com dashboard`,
212
+ `**Auto-synced sources:**`,
213
+ `• vault_save + vault_remember — ✅`,
214
+ `• memory_add + memory_connect ✅`,
215
+ `• Google Drive / Gmail / Notion connect at noelclaw.com`,
273
216
  ``,
274
- `**Capabilities:**`,
275
- `• Semantic search (vector similarity)`,
276
- `• 81.6% LongMemEval accuracy`,
277
- `• Context injection for AI tasks`,
217
+ `**Capabilities:** Semantic search · Vector context · 81.6% LongMemEval`,
278
218
  ].join("\n"),
279
219
  }],
280
220
  };
@@ -283,53 +223,25 @@ async function handleMemoryTool(name, args) {
283
223
  const parsed = ConnectSchema.safeParse(args);
284
224
  if (!parsed.success)
285
225
  return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
286
- if (!apiKey)
287
- return { content: [{ type: "text", text: "⚠️ SUPERMEMORY_API_KEY not configured. Set it in your MCP env to enable semantic memory." }], isError: true };
288
- const { source, url, content, title } = parsed.data;
289
- if (source === "manual") {
290
- if (!content)
291
- return { content: [{ type: "text", text: "content is required for source: manual" }], isError: true };
292
- const { ok, data, error } = await smFetch("/memories", "POST", {
293
- content,
294
- metadata: { title, source: "manual", addedAt: Date.now() },
295
- spaces: [exports.SM_SPACE],
296
- });
297
- if (!ok)
298
- return { content: [{ type: "text", text: `Error: ${error}` }], isError: true };
299
- return { content: [{ type: "text", text: `✅ Memory connected — ID: \`${data?.id ?? "saved"}\`` }] };
300
- }
301
- if (!url)
302
- return { content: [{ type: "text", text: `url is required for source: ${source}` }], isError: true };
303
- const { ok, data, error } = await smFetch("/memories", "POST", {
304
- content: title ?? "",
226
+ const { url, title, tags } = parsed.data;
227
+ const inferredTitle = title ?? url.split("/").filter(Boolean).pop() ?? url;
228
+ const data = await (0, convex_js_1.callConvex)("/memory/add", "POST", {
229
+ content: inferredTitle,
305
230
  sourceUrl: url,
306
- metadata: { title, source, connectedAt: Date.now() },
307
- spaces: [exports.SM_SPACE],
308
- });
309
- if (!ok)
310
- return { content: [{ type: "text", text: `Error connecting source: ${error}` }], isError: true };
311
- const sourceLabels = {
312
- url: "Web page",
313
- github: "GitHub repo",
314
- notion: "Notion page",
315
- };
231
+ metadata: { title: inferredTitle, tags, source: "memory_connect", connectedAt: Date.now() },
232
+ }).catch((err) => ({ error: err.message }));
233
+ if (data?.error)
234
+ return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
316
235
  return {
317
236
  content: [{
318
237
  type: "text",
319
238
  text: [
320
- `✅ **${sourceLabels[source] ?? source} connected**`,
321
- `URL: ${url}`,
239
+ `✅ **Connected**: ${url}`,
322
240
  `Memory ID: \`${data?.id ?? "queued"}\``,
323
241
  ``,
324
- `Supermemory is fetching and indexing the content in the background.`,
325
- `Search it in ~30s with: \`memory_search query: "${title ?? url.split("/").pop()}"\``,
326
- ``,
327
- source === "github"
328
- ? `💡 Tip: For full repo sync (all files), connect via the Noelclaw dashboard at noelclaw.com`
329
- : source === "notion"
330
- ? `💡 Tip: For full Notion workspace sync, connect via the Noelclaw dashboard at noelclaw.com`
331
- : "",
332
- ].filter(Boolean).join("\n"),
242
+ `Noelclaw is fetching and indexing the content in the background.`,
243
+ `Search it in ~30s: \`memory_search query: "${inferredTitle.slice(0, 40)}"\``,
244
+ ].join("\n"),
333
245
  }],
334
246
  };
335
247
  }
@@ -274,19 +274,19 @@ async function handleVaultTool(name, args) {
274
274
  if (data.error)
275
275
  return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
276
276
  const { key, version, changed } = data;
277
- // Mirror to semantic memory (fire-and-forget, don't fail the save if SM is unavailable)
277
+ // Mirror to semantic memory (fire-and-forget)
278
278
  if (parsed.data.type !== "credential") {
279
279
  (0, memory_js_1.syncToSupermemory)(parsed.data.content, {
280
280
  vaultKey: key, title: parsed.data.title, type: parsed.data.type,
281
281
  tags: parsed.data.tags, version, source: "vault_save",
282
- }).catch(() => { });
282
+ });
283
283
  }
284
284
  const lines = [
285
285
  `📦 **Vault ${changed ? (version === 1 ? "Created" : "Updated") : "Unchanged"}**`,
286
286
  `Key: \`${key}\``,
287
287
  `Version: v${version}`,
288
288
  changed && version > 1 ? `Previous version auto-snapshotted.` : "",
289
- process.env.SUPERMEMORY_API_KEY ? `🧠 Synced to semantic memory` : "",
289
+ `🧠 Synced to semantic memory`,
290
290
  ``,
291
291
  `Use \`vault_read\` to retrieve, \`vault_history\` to see all versions.`,
292
292
  ].filter(Boolean);
@@ -341,8 +341,8 @@ async function handleVaultTool(name, args) {
341
341
  const parsed = SearchSchema.safeParse(args);
342
342
  if (!parsed.success)
343
343
  return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
344
- // Try Supermemory semantic search first
345
- if (process.env.SUPERMEMORY_API_KEY) {
344
+ // Try semantic search first (proxied through Convex)
345
+ {
346
346
  const smResults = await (0, memory_js_1.searchSupermemory)(parsed.data.query, parsed.data.limit ?? 20);
347
347
  if (smResults.length > 0) {
348
348
  // Filter by type if requested
@@ -456,8 +456,8 @@ async function handleVaultTool(name, args) {
456
456
  (0, memory_js_1.syncToSupermemory)(content, {
457
457
  vaultKey: data.key, title: inferredTitle, type: "memory",
458
458
  tags, version: data.version, source: "vault_remember",
459
- }).catch(() => { });
460
- const smNote = process.env.SUPERMEMORY_API_KEY ? " 🧠 Synced to semantic memory" : "";
459
+ });
460
+ const smNote = " 🧠 Synced to semantic memory";
461
461
  return { content: [{ type: "text", text: `✅ Remembered — key: \`${data.key}\` (v${data.version})${smNote}\nRetrieve later with: \`vault_context topic: "${inferredTitle.slice(0, 40)}"\`` }] };
462
462
  }
463
463
  case "vault_context": {
@@ -465,8 +465,8 @@ async function handleVaultTool(name, args) {
465
465
  if (!parsed.success)
466
466
  return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
467
467
  const limit = parsed.data.limit ?? 8;
468
- // Semantic context via Supermemory
469
- if (process.env.SUPERMEMORY_API_KEY) {
468
+ // Semantic context (proxied through Convex)
469
+ {
470
470
  const smResults = await (0, memory_js_1.searchSupermemory)(parsed.data.topic, limit);
471
471
  if (smResults.length > 0) {
472
472
  const contextParts = smResults.map((r, i) => {
@@ -589,11 +589,9 @@ async function handleVaultTool(name, args) {
589
589
  metadata: JSON.stringify({ sourceUrl: url }),
590
590
  }, "vault_connect");
591
591
  // Mirror full content to Supermemory with source URL for ingestion
592
- if (process.env.SUPERMEMORY_API_KEY) {
593
- (0, memory_js_1.syncToSupermemory)("", {
594
- vaultKey: vaultData.key, title: inferredTitle, type, tags, source: "vault_connect",
595
- }, url).catch(() => { });
596
- }
592
+ (0, memory_js_1.syncToSupermemory)("", {
593
+ vaultKey: vaultData.key, title: inferredTitle, type, tags, source: "vault_connect",
594
+ }, url);
597
595
  if (vaultData.error)
598
596
  return { content: [{ type: "text", text: `Error: ${vaultData.error}` }], isError: true };
599
597
  return {
@@ -603,9 +601,7 @@ async function handleVaultTool(name, args) {
603
601
  `🔗 **Vault Connected**`,
604
602
  `URL: ${url}`,
605
603
  `Vault key: \`${vaultData.key}\``,
606
- process.env.SUPERMEMORY_API_KEY
607
- ? `🧠 Indexing to semantic memory… searchable in ~30s`
608
- : `⚠️ Set SUPERMEMORY_API_KEY for full semantic indexing`,
604
+ `🧠 Indexing to semantic memory… searchable in ~30s`,
609
605
  ``,
610
606
  `Search it with: \`vault_search query: "${inferredTitle}"\``,
611
607
  `Or load as context: \`vault_context topic: "${inferredTitle}"\``,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noelclaw/mcp",
3
- "version": "2.3.0",
3
+ "version": "2.3.1",
4
4
  "description": "Noelclaw as an MCP skill — persistent memory, multi-agent coordination, scenario simulation, DeFi execution, and Sentinel-gated playbooks.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {