@noelclaw/mcp 1.5.7 → 2.2.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.
@@ -4,7 +4,7 @@ exports.VAULT_TOOLS = void 0;
4
4
  exports.handleVaultTool = handleVaultTool;
5
5
  const zod_1 = require("zod");
6
6
  const convex_js_1 = require("../convex.js");
7
- const VAULT_TYPES = ["research", "execution", "workflow", "prompt", "file", "memory"];
7
+ const VAULT_TYPES = ["research", "execution", "workflow", "prompt", "file", "memory", "credential"];
8
8
  const LINK_RELATIONS = ["references", "derived_from", "supersedes", "related", "continues"];
9
9
  exports.VAULT_TOOLS = [
10
10
  {
@@ -106,6 +106,93 @@ exports.VAULT_TOOLS = [
106
106
  required: [],
107
107
  },
108
108
  },
109
+ {
110
+ name: "vault_remember",
111
+ description: "One-liner: save anything to Noel-Vault without specifying type or title. " +
112
+ "Just pass content — Noel infers the title and saves as a memory entry. " +
113
+ "Use this for quick notes, preferences, decisions, and things to remember across sessions. " +
114
+ "Example: vault_remember content: 'User prefers low-risk DeFi, confirmed multiple times'",
115
+ inputSchema: {
116
+ type: "object",
117
+ properties: {
118
+ content: { type: "string", description: "What to remember — plain text, markdown, or a short note" },
119
+ title: { type: "string", description: "Optional title. Auto-inferred from content if omitted." },
120
+ tags: { type: "array", items: { type: "string" }, description: "Optional tags for later retrieval" },
121
+ },
122
+ required: ["content"],
123
+ },
124
+ },
125
+ {
126
+ name: "vault_context",
127
+ description: "Load relevant vault entries for a topic and return them as formatted context ready to inject into a prompt. " +
128
+ "Call this at the start of any research or analysis task to prime your AI with all past knowledge on the topic. " +
129
+ "Returns full content of matching entries, filtered by relevance. Credentials are never included.",
130
+ inputSchema: {
131
+ type: "object",
132
+ properties: {
133
+ topic: { type: "string", description: "Topic or query to load context for, e.g. 'ETH DeFi strategy' or 'BTC market thesis'" },
134
+ limit: { type: "number", description: "Max entries to return (default 8, max 20)" },
135
+ },
136
+ required: ["topic"],
137
+ },
138
+ },
139
+ {
140
+ name: "vault_store_credential",
141
+ description: "Securely store an API key, token, or secret in your vault. " +
142
+ "Credentials are stored under type=credential and are excluded from normal search and export. " +
143
+ "Use this to keep API keys organized and accessible across agent sessions.",
144
+ inputSchema: {
145
+ type: "object",
146
+ properties: {
147
+ name: { type: "string", description: "Credential name, e.g. 'ALCHEMY_API_KEY', 'TELEGRAM_BOT_TOKEN'" },
148
+ value: { type: "string", description: "The secret value to store" },
149
+ description: { type: "string", description: "Optional note about this credential — what it's for, expiry, etc." },
150
+ },
151
+ required: ["name", "value"],
152
+ },
153
+ },
154
+ {
155
+ name: "vault_get_credential",
156
+ description: "Retrieve a stored credential from the vault by name. " +
157
+ "Only returns credentials owned by the authenticated user.",
158
+ inputSchema: {
159
+ type: "object",
160
+ properties: {
161
+ name: { type: "string", description: "Credential name as used in vault_store_credential" },
162
+ },
163
+ required: ["name"],
164
+ },
165
+ },
166
+ {
167
+ name: "vault_publish",
168
+ description: "Publish a vault entry to the community vault so other Noelclaw users can discover and fork it. " +
169
+ "Credentials are never published — only research, prompts, workflows, memory, and execution entries. " +
170
+ "You can also unpublish by setting isPublic to false.",
171
+ inputSchema: {
172
+ type: "object",
173
+ properties: {
174
+ key: { type: "string", description: "Entry key to publish or unpublish" },
175
+ isPublic: { type: "boolean", description: "true to publish, false to unpublish (default true)" },
176
+ authorName: { type: "string", description: "Display name shown to the community (default: Anonymous)" },
177
+ },
178
+ required: ["key"],
179
+ },
180
+ },
181
+ {
182
+ name: "vault_explore",
183
+ description: "Browse the community vault — public entries shared by all Noelclaw users. " +
184
+ "Discover research, prompts, and workflows published by the community. " +
185
+ "Use vault_save to fork any entry into your own vault.",
186
+ inputSchema: {
187
+ type: "object",
188
+ properties: {
189
+ type: { type: "string", enum: ["research", "execution", "workflow", "prompt", "file", "memory"], description: "Filter by entry type" },
190
+ search: { type: "string", description: "Search query to filter community entries" },
191
+ limit: { type: "number", description: "Max entries to return (default 20)" },
192
+ },
193
+ required: [],
194
+ },
195
+ },
109
196
  ];
110
197
  // ─── Zod schemas ─────────────────────────────────────────────────────────────
111
198
  const SaveSchema = zod_1.z.object({
@@ -134,6 +221,12 @@ const SearchSchema = zod_1.z.object({
134
221
  const HistorySchema = zod_1.z.object({ key: zod_1.z.string().min(1) });
135
222
  const DiffSchema = zod_1.z.object({ key: zod_1.z.string().min(1), fromVersion: zod_1.z.number(), toVersion: zod_1.z.number() });
136
223
  const ExportSchema = zod_1.z.object({ type: zod_1.z.enum(VAULT_TYPES).optional() });
224
+ const RememberSchema = zod_1.z.object({ content: zod_1.z.string().min(1), title: zod_1.z.string().optional(), tags: zod_1.z.array(zod_1.z.string()).optional() });
225
+ const ContextSchema = zod_1.z.object({ topic: zod_1.z.string().min(1), limit: zod_1.z.number().optional() });
226
+ 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() });
227
+ const GetCredentialSchema = zod_1.z.object({ name: zod_1.z.string().min(1) });
228
+ 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() });
229
+ 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() });
137
230
  // ─── Helpers ─────────────────────────────────────────────────────────────────
138
231
  function formatBytes(n) {
139
232
  if (!n)
@@ -287,6 +380,110 @@ async function handleVaultTool(name, args) {
287
380
  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
381
  return { content: [{ type: "text", text: [...header, ...rows.join("\n\n---\n\n").split("\n")].join("\n") }] };
289
382
  }
383
+ case "vault_remember": {
384
+ const parsed = RememberSchema.safeParse(args);
385
+ if (!parsed.success)
386
+ return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
387
+ const { content, title, tags } = parsed.data;
388
+ const inferredTitle = title ?? content.slice(0, 60).replace(/\n/g, " ").trim() + (content.length > 60 ? "…" : "");
389
+ const data = await (0, convex_js_1.callConvex)("/vault/save", "POST", {
390
+ type: "memory",
391
+ title: inferredTitle,
392
+ content,
393
+ tags,
394
+ commitMsg: "vault_remember",
395
+ }, "vault_remember");
396
+ if (data.error)
397
+ return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
398
+ return { content: [{ type: "text", text: `✅ Remembered — key: \`${data.key}\` (v${data.version})\nRetrieve later with: \`vault_context topic: "${inferredTitle.slice(0, 40)}"\`` }] };
399
+ }
400
+ case "vault_context": {
401
+ const parsed = ContextSchema.safeParse(args);
402
+ if (!parsed.success)
403
+ return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
404
+ const params = new URLSearchParams({ q: parsed.data.topic });
405
+ if (parsed.data.limit)
406
+ params.set("limit", String(parsed.data.limit));
407
+ const data = await (0, convex_js_1.callConvex)(`/vault/context?${params}`, "GET", undefined, "vault_context");
408
+ if (data.error)
409
+ return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
410
+ if (!data.context)
411
+ return { content: [{ type: "text", text: `No vault context found for: "${parsed.data.topic}"\nStart building your knowledge base with vault_remember or vault_save.` }] };
412
+ const summary = data.entries.map((e) => `[${e.type}] ${e.title}`).join(", ");
413
+ return { content: [{ type: "text", text: `📚 **Vault Context** loaded for: "${parsed.data.topic}"\nEntries: ${summary}\n\n---\n\n${data.context}` }] };
414
+ }
415
+ case "vault_store_credential": {
416
+ const parsed = StoreCredentialSchema.safeParse(args);
417
+ if (!parsed.success)
418
+ return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
419
+ const data = await (0, convex_js_1.callConvex)("/vault/credential/store", "POST", parsed.data, "vault_store_credential");
420
+ if (data.error)
421
+ return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
422
+ return { content: [{ type: "text", text: `🔐 Credential stored: \`${data.name}\`\nKey: \`${data.key}\`\nRetrieve with: \`vault_get_credential name: "${data.name}"\`` }] };
423
+ }
424
+ case "vault_get_credential": {
425
+ const parsed = GetCredentialSchema.safeParse(args);
426
+ if (!parsed.success)
427
+ return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
428
+ const params = new URLSearchParams({ name: parsed.data.name });
429
+ const data = await (0, convex_js_1.callConvex)(`/vault/credential?${params}`, "GET", undefined, "vault_get_credential");
430
+ if (data.error)
431
+ return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
432
+ const lines = [`🔐 **${data.name}**`, `Value: \`${data.value}\``];
433
+ if (data.description)
434
+ lines.push(`Note: ${data.description}`);
435
+ if (data.storedAt)
436
+ lines.push(`Stored: ${data.storedAt}`);
437
+ return { content: [{ type: "text", text: lines.join("\n") }] };
438
+ }
439
+ case "vault_publish": {
440
+ const parsed = PublishSchema.safeParse(args);
441
+ if (!parsed.success)
442
+ return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
443
+ const { key, isPublic = true, authorName } = parsed.data;
444
+ const endpoint = isPublic ? "/vault/publish" : "/vault/unpublish";
445
+ const data = await (0, convex_js_1.callConvex)(endpoint, "POST", { key, authorName }, "vault_publish");
446
+ if (data.error)
447
+ return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
448
+ return {
449
+ content: [{
450
+ type: "text",
451
+ text: isPublic
452
+ ? `🌐 **Published to community vault**\nKey: \`${key}\`\nOther Noelclaw users can now discover this entry.\nUse \`vault_publish key: "${key}" isPublic: false\` to unpublish.`
453
+ : `🔒 **Unpublished**\nKey: \`${key}\` is now private.`,
454
+ }],
455
+ };
456
+ }
457
+ case "vault_explore": {
458
+ const parsed = ExploreSchema.safeParse(args ?? {});
459
+ if (!parsed.success)
460
+ return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
461
+ const params = new URLSearchParams();
462
+ if (parsed.data.type)
463
+ params.set("type", parsed.data.type);
464
+ if (parsed.data.search)
465
+ params.set("search", parsed.data.search);
466
+ if (parsed.data.limit)
467
+ params.set("limit", String(parsed.data.limit));
468
+ const data = await (0, convex_js_1.callConvex)(`/vault/community?${params}`, "GET", undefined, "vault_explore");
469
+ if (data.error)
470
+ return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
471
+ const entries = data.entries ?? [];
472
+ if (!entries.length)
473
+ 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!"}` }] };
474
+ const header = `🌐 **Community Vault** — ${entries.length} public entries`;
475
+ const rows = entries.map((e) => [
476
+ `**[${e.type}]** ${e.title} · by ${e.authorName ?? "Anonymous"}`,
477
+ ` \`${e.key}\` · v${e.version} · ${formatDate(e.updatedAt)}`,
478
+ e.content ? ` ${e.content.slice(0, 100)}${e.content.length > 100 ? "…" : ""}` : "",
479
+ ].filter(Boolean).join("\n"));
480
+ return {
481
+ content: [{
482
+ type: "text",
483
+ text: [header, "", ...rows, "", "To fork an entry: `vault_save type: <type> title: <title> content: <content>`"].join("\n"),
484
+ }],
485
+ };
486
+ }
290
487
  default:
291
488
  return null;
292
489
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noelclaw/mcp",
3
- "version": "1.5.7",
3
+ "version": "2.2.0",
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": {