@astramindapp/mcp-server 0.7.0 → 0.8.3

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/dist/server.js CHANGED
@@ -5,34 +5,66 @@
5
5
  * Exposes the MIND knowledge graph as MCP tools that any LLM agent can use.
6
6
  * This is the universal AI memory layer.
7
7
  *
8
- * Tools (16):
9
- * mind_query — Semantic search across the knowledge graph
10
- * mind_remember — Store/manage content in the graph (auto-categorized)
11
- * mind_context — Get structured always-loaded context (soul/user/rules)
12
- * mind_life — Read/write goals, projects, tasks, calendar events, stats
13
- * mind_crm — Read/write contacts, interactions, and activities
14
- * mind_graph — Get graph statistics, diagnostics, and labels
15
- * mind_admin — Admin-only user and featured mind management
16
- * mind_sense — MINDsense emotional intelligence layer
17
- * mind_research — Deep research agent jobs
18
- * mind_train — Self-training and chat-to-KG sessions
19
- * mind_social — Thoughts, communities, and feed
20
- * mind_profile — Profile, prompts, and model preferences
21
- * mind_insights — ALE insights and analytics
22
- * mind_automate — Automations and triggers
23
- * mind_notify — Notifications management
24
- * mind_agents — Admin-only Agent Command Center (registry, heartbeat,
25
- * probe, status — for Anthony's agent fleet)
8
+ * The server is self-describing: every connecting client receives
9
+ * SERVER_INSTRUCTIONS (from ./integration-guide) in the MCP `initialize`
10
+ * response, and can read the full playbook from the `mind://integration-guide`
11
+ * resource. Any agent that connects therefore knows what MIND is, which
12
+ * integration path to use for its runtime, how to authenticate, and the
13
+ * session protocol — without having to be told.
14
+ *
15
+ * Tools (22):
16
+ * mind_query — Semantic search across the knowledge graph
17
+ * mind_remember — Store/manage content in the graph (auto-categorized)
18
+ * mind_context — Get structured always-loaded context (soul/user/rules)
19
+ * mind_life — Goals, projects, tasks, calendar (incl. bulk_delete)
20
+ * mind_tasks — Site-wide assignable, reportable work items
21
+ * mind_crm — Read/write contacts, interactions, and activities
22
+ * mind_graph — Get graph statistics, diagnostics, and labels
23
+ * mind_admin — Admin-only user and featured mind management
24
+ * mind_sense — MINDsense emotional intelligence layer
25
+ * mind_research — Deep research agent jobs
26
+ * mind_train — Self-training and chat-to-KG sessions
27
+ * mind_social — Thoughts, communities, and feed
28
+ * mind_profile — Profile, prompts, and model preferences
29
+ * mind_insights — ALE insights and analytics
30
+ * mind_automate — Automations and triggers
31
+ * mind_notify — Notifications management
32
+ * mind_agents — Admin-only Agent Command Center (registry, heartbeat,
33
+ * probe, status — for the agent fleet)
34
+ * mind_accounts — Multi-MIND accounts: discover/create MINDs, manage
35
+ * owners and viewers, send invitations
36
+ * mind_list_templates / mind_get_template / mind_save_typed /
37
+ * mind_bootstrap_templates — the 16 Front Layer typed-document templates
26
38
  */
27
39
  Object.defineProperty(exports, "__esModule", { value: true });
28
40
  exports.createMindMcpServer = createMindMcpServer;
29
41
  const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
30
42
  const zod_1 = require("zod");
43
+ const integration_guide_js_1 = require("./integration-guide.js");
31
44
  function createMindMcpServer(client) {
32
45
  const server = new mcp_js_1.McpServer({
33
46
  name: "mind",
34
47
  version: "0.2.0",
48
+ }, {
49
+ // Returned to every client in the MCP `initialize` response — the first
50
+ // thing any connecting agent sees about how to integrate with MIND.
51
+ instructions: integration_guide_js_1.SERVER_INSTRUCTIONS,
35
52
  });
53
+ // ─── mind://integration-guide ───────────────────────────
54
+ // The full integration playbook, readable on demand by any MCP client.
55
+ server.registerResource("integration-guide", "mind://integration-guide", {
56
+ title: "MIND Integration Guide",
57
+ description: "Complete playbook for integrating any AI agent with MIND — the three integration paths (MCP server, OpenClaw plugin, REST API), authentication, the session protocol, all 22 tools, REST endpoint mapping, and common recipes.",
58
+ mimeType: "text/markdown",
59
+ }, async (uri) => ({
60
+ contents: [
61
+ {
62
+ uri: uri.href,
63
+ mimeType: "text/markdown",
64
+ text: integration_guide_js_1.INTEGRATION_GUIDE,
65
+ },
66
+ ],
67
+ }));
36
68
  // ─── mind_query ─────────────────────────────────────────
37
69
  // Semantic search across the user's knowledge graph.
38
70
  // Replaces flat MEMORY.md — returns only relevant memories.
@@ -292,11 +324,11 @@ function createMindMcpServer(client) {
292
324
  server.tool("mind_life", "Manage goals, projects, tasks, and calendar events in MIND Life. Use for tracking work, creating action items, updating progress, completing tasks, managing calendar, and viewing stats.", {
293
325
  action: zod_1.z
294
326
  .enum([
295
- "list", "create", "update", "complete", "delete", "move", "get",
327
+ "list", "create", "update", "complete", "delete", "bulk_delete", "move", "get",
296
328
  "calendar_list", "calendar_create", "calendar_update", "calendar_delete",
297
329
  "stats",
298
330
  ])
299
- .describe("What to do: list/create/update/complete/delete/move/get items, calendar_list/calendar_create/calendar_update/calendar_delete events, stats (productivity overview)"),
331
+ .describe("What to do: list/create/update/complete/delete/bulk_delete/move/get items, calendar_list/calendar_create/calendar_update/calendar_delete events, stats (productivity overview)"),
300
332
  title: zod_1.z.string().optional().describe("Title for new items or calendar events"),
301
333
  description: zod_1.z.string().optional().describe("Description for new items"),
302
334
  status: zod_1.z
@@ -309,11 +341,12 @@ function createMindMcpServer(client) {
309
341
  .describe("Priority: low, medium, high, urgent"),
310
342
  due_date: zod_1.z.string().optional().describe("Due date in YYYY-MM-DD format"),
311
343
  item_id: zod_1.z.string().optional().describe("Item ID for update/complete/delete/move/get actions"),
344
+ item_ids: zod_1.z.array(zod_1.z.string()).optional().describe("Item IDs for bulk_delete (1–200 IDs deleted in one call)"),
312
345
  start_time: zod_1.z.string().optional().describe("Start time for calendar events (ISO 8601)"),
313
346
  end_time: zod_1.z.string().optional().describe("End time for calendar events (ISO 8601)"),
314
347
  event_id: zod_1.z.string().optional().describe("Event ID for calendar_update/calendar_delete"),
315
348
  all_day: zod_1.z.boolean().optional().describe("Whether the calendar event is all-day"),
316
- }, async ({ action, title, description, status, priority, due_date, item_id, start_time, end_time, event_id, all_day }) => {
349
+ }, async ({ action, title, description, status, priority, due_date, item_id, item_ids, start_time, end_time, event_id, all_day }) => {
317
350
  try {
318
351
  switch (action) {
319
352
  case "list": {
@@ -395,6 +428,22 @@ function createMindMcpServer(client) {
395
428
  content: [{ type: "text", text: `✅ Deleted life item ${item_id}` }],
396
429
  };
397
430
  }
431
+ case "bulk_delete": {
432
+ if (!item_ids || item_ids.length === 0) {
433
+ return {
434
+ content: [{ type: "text", text: "Error: item_ids (a non-empty array of IDs) is required for bulk_delete" }],
435
+ isError: true,
436
+ };
437
+ }
438
+ const result = await client.bulkDeleteLifeItems(item_ids);
439
+ const lines = [
440
+ `✅ Bulk delete complete — ${result.deleted_count} life item(s) deleted.`,
441
+ ];
442
+ if (result.not_found.length > 0) {
443
+ lines.push(`⚠️ ${result.not_found.length} id(s) skipped (not found / not yours): ${result.not_found.join(", ")}`);
444
+ }
445
+ return { content: [{ type: "text", text: lines.join("\n") }] };
446
+ }
398
447
  case "move": {
399
448
  if (!item_id || !status) {
400
449
  return {
@@ -512,6 +561,212 @@ function createMindMcpServer(client) {
512
561
  };
513
562
  }
514
563
  });
564
+ // ─── mind_tasks ─────────────────────────────────────────
565
+ // Site-wide tasks: assignable, completable work items that attach to a
566
+ // Life project, a CRM contact, an agent, or stand alone.
567
+ server.tool("mind_tasks", "Manage site-wide tasks in MIND — assignable, completable, reportable work items. A task can attach to a Life project (parent_type=life_item), a CRM contact (parent_type=contact), an agent (parent_type=agent), or stand alone. Assign tasks to a MIND member, an agent, or an external email. Use action=reports for completion analytics.", {
568
+ action: zod_1.z
569
+ .enum([
570
+ "list", "create", "get", "update", "complete", "reopen",
571
+ "assign", "delete", "reports",
572
+ ])
573
+ .describe("What to do: list/create/get/update/complete/reopen/assign/delete tasks, or reports (completion analytics)"),
574
+ task_id: zod_1.z.string().optional().describe("Task ID — required for get/update/complete/reopen/assign/delete"),
575
+ title: zod_1.z.string().optional().describe("Task title (required for create)"),
576
+ description: zod_1.z.string().optional().describe("Task description"),
577
+ status: zod_1.z.string().optional().describe("Filter (list) or new status: open, in_progress, blocked, done"),
578
+ priority: zod_1.z.string().optional().describe("Priority: none, low, medium, high, urgent"),
579
+ due_date: zod_1.z.string().optional().describe("Due date YYYY-MM-DD"),
580
+ parent_type: zod_1.z.string().optional().describe("Attach to: life_item, contact, agent, or none"),
581
+ parent_id: zod_1.z.string().optional().describe("ID of the parent project / contact / agent"),
582
+ assignee_type: zod_1.z.string().optional().describe("Assignee: user, agent, external, or unassigned"),
583
+ assignee_id: zod_1.z.string().optional().describe("Username, agent slug, or email of the assignee"),
584
+ dispatch_agent: zod_1.z.boolean().optional().describe("When assigning to an agent, also fire it now"),
585
+ note: zod_1.z.string().optional().describe("Completion note (for complete)"),
586
+ overdue: zod_1.z.boolean().optional().describe("List only overdue tasks"),
587
+ }, async ({ action, task_id, title, description, status, priority, due_date, parent_type, parent_id, assignee_type, assignee_id, dispatch_agent, note, overdue }) => {
588
+ try {
589
+ const fmt = (t) => `• [${t.status}] ${t.title}${t.priority && t.priority !== "none" ? ` (${t.priority})` : ""}` +
590
+ `${t.due_date ? `, due ${t.due_date}` : ""}${t.is_overdue ? " ⚠️ OVERDUE" : ""}` +
591
+ `${t.assignee_label ? `, → ${t.assignee_label}` : ""}` +
592
+ `${t.parent_label ? `, on ${t.parent_label}` : ""} — id: ${t.task_id}`;
593
+ switch (action) {
594
+ case "list": {
595
+ const params = {};
596
+ if (status)
597
+ params.status = status;
598
+ if (priority)
599
+ params.priority = priority;
600
+ if (parent_type)
601
+ params.parent_type = parent_type;
602
+ if (parent_id)
603
+ params.parent_id = parent_id;
604
+ if (assignee_type)
605
+ params.assignee_type = assignee_type;
606
+ if (assignee_id)
607
+ params.assignee_id = assignee_id;
608
+ if (overdue)
609
+ params.overdue = "true";
610
+ const result = await client.listTasks(params);
611
+ const tasks = result.tasks ?? [];
612
+ if (tasks.length === 0) {
613
+ return { content: [{ type: "text", text: "No tasks found." }] };
614
+ }
615
+ return {
616
+ content: [{
617
+ type: "text",
618
+ text: `${result.total} task(s):\n` + tasks.map(fmt).join("\n"),
619
+ }],
620
+ };
621
+ }
622
+ case "create": {
623
+ if (!title) {
624
+ return {
625
+ content: [{ type: "text", text: "Error: title is required to create a task" }],
626
+ isError: true,
627
+ };
628
+ }
629
+ const t = await client.createTask({
630
+ title, description, status, priority, due_date,
631
+ parent_type, parent_id, assignee_type, assignee_id,
632
+ dispatch_agent,
633
+ });
634
+ return {
635
+ content: [{ type: "text", text: `✅ Created task: ${fmt(t)}` }],
636
+ };
637
+ }
638
+ case "get": {
639
+ if (!task_id) {
640
+ return {
641
+ content: [{ type: "text", text: "Error: task_id is required for get" }],
642
+ isError: true,
643
+ };
644
+ }
645
+ const t = await client.getTask(task_id);
646
+ const extra = t.agent_run_status ? `\nAgent run: ${t.agent_run_status}` : "";
647
+ return {
648
+ content: [{
649
+ type: "text",
650
+ text: `${fmt(t)}${t.description ? `\n${t.description}` : ""}${extra}`,
651
+ }],
652
+ };
653
+ }
654
+ case "update": {
655
+ if (!task_id) {
656
+ return {
657
+ content: [{ type: "text", text: "Error: task_id is required for update" }],
658
+ isError: true,
659
+ };
660
+ }
661
+ const patch = {};
662
+ if (title !== undefined)
663
+ patch.title = title;
664
+ if (description !== undefined)
665
+ patch.description = description;
666
+ if (status !== undefined)
667
+ patch.status = status;
668
+ if (priority !== undefined)
669
+ patch.priority = priority;
670
+ if (due_date !== undefined)
671
+ patch.due_date = due_date;
672
+ if (parent_type !== undefined)
673
+ patch.parent_type = parent_type;
674
+ if (parent_id !== undefined)
675
+ patch.parent_id = parent_id;
676
+ const t = await client.updateTask(task_id, patch);
677
+ return {
678
+ content: [{ type: "text", text: `✅ Updated task: ${fmt(t)}` }],
679
+ };
680
+ }
681
+ case "complete": {
682
+ if (!task_id) {
683
+ return {
684
+ content: [{ type: "text", text: "Error: task_id is required for complete" }],
685
+ isError: true,
686
+ };
687
+ }
688
+ const t = await client.completeTask(task_id, true, note);
689
+ return {
690
+ content: [{ type: "text", text: `✅ Completed task: "${t.title}"` }],
691
+ };
692
+ }
693
+ case "reopen": {
694
+ if (!task_id) {
695
+ return {
696
+ content: [{ type: "text", text: "Error: task_id is required for reopen" }],
697
+ isError: true,
698
+ };
699
+ }
700
+ const t = await client.completeTask(task_id, false);
701
+ return {
702
+ content: [{ type: "text", text: `↩️ Reopened task: "${t.title}"` }],
703
+ };
704
+ }
705
+ case "assign": {
706
+ if (!task_id || !assignee_type) {
707
+ return {
708
+ content: [{ type: "text", text: "Error: task_id and assignee_type are required for assign" }],
709
+ isError: true,
710
+ };
711
+ }
712
+ const t = await client.assignTask(task_id, {
713
+ assignee_type, assignee_id, dispatch_agent: dispatch_agent ?? false,
714
+ });
715
+ return {
716
+ content: [{
717
+ type: "text",
718
+ text: `✅ Assigned "${t.title}" → ${t.assignee_label ?? t.assignee_id ?? "unassigned"}`,
719
+ }],
720
+ };
721
+ }
722
+ case "delete": {
723
+ if (!task_id) {
724
+ return {
725
+ content: [{ type: "text", text: "Error: task_id is required for delete" }],
726
+ isError: true,
727
+ };
728
+ }
729
+ await client.deleteTask(task_id);
730
+ return {
731
+ content: [{ type: "text", text: `🗑️ Deleted task ${task_id}` }],
732
+ };
733
+ }
734
+ case "reports": {
735
+ const params = {};
736
+ if (parent_type)
737
+ params.parent_type = parent_type;
738
+ if (parent_id)
739
+ params.parent_id = parent_id;
740
+ const r = await client.taskReports(params);
741
+ const lines = [
742
+ `📊 Task report (${r.total} total)`,
743
+ ` open ${r.open} · in progress ${r.in_progress} · blocked ${r.blocked} · done ${r.done}`,
744
+ ` completion rate: ${r.completion_rate}%`,
745
+ ` overdue: ${r.overdue} · due soon: ${r.due_soon} · unassigned: ${r.unassigned}`,
746
+ ` avg completion: ${r.avg_completion_days} days`,
747
+ ];
748
+ if (r.by_assignee && r.by_assignee.length) {
749
+ lines.push(" By assignee:");
750
+ for (const a of r.by_assignee.slice(0, 10)) {
751
+ lines.push(` ${a.assignee_label ?? a.assignee_id ?? "unassigned"}: ${a.done}/${a.total} done (${a.completion_rate}%)`);
752
+ }
753
+ }
754
+ return { content: [{ type: "text", text: lines.join("\n") }] };
755
+ }
756
+ default:
757
+ return {
758
+ content: [{ type: "text", text: `Unknown action: ${action}` }],
759
+ isError: true,
760
+ };
761
+ }
762
+ }
763
+ catch (err) {
764
+ return {
765
+ content: [{ type: "text", text: `mind_tasks error: ${err instanceof Error ? err.message : String(err)}` }],
766
+ isError: true,
767
+ };
768
+ }
769
+ });
515
770
  // ─── mind_crm ───────────────────────────────────────────
516
771
  // Read/write contacts, interactions, and activities.
517
772
  // Enhanced: delete, get, log_activity, list_activities.
@@ -1936,8 +2191,12 @@ function createMindMcpServer(client) {
1936
2191
  "seed_known",
1937
2192
  "set_status",
1938
2193
  "set_current_job",
2194
+ "list_invoices",
2195
+ "link_invoice",
2196
+ "unlink_invoice",
2197
+ "create_invoice",
1939
2198
  ])
1940
- .describe("Action: list (filter+stats), get (detail+recent activities), create, update (partial), delete (soft archive; pass hard=true to remove), heartbeat (push from agent runtime), probe (HTTP/OpenClaw liveness probe), log_activity (manual note), list_activities, import_from_mind (enrich from agent-identity docs), seed_known (idempotent upsert of canonical seed), set_status (shortcut update), set_current_job (shortcut update)"),
2199
+ .describe("Action: list (filter+stats), get (detail+recent activities), create, update (partial), delete (soft archive; pass hard=true to remove), heartbeat (push from agent runtime), probe (HTTP/OpenClaw liveness probe), log_activity (manual note), list_activities, import_from_mind (enrich from agent-identity docs), seed_known (idempotent upsert of canonical seed), set_status (shortcut update), set_current_job (shortcut update), list_invoices (invoices linked to an agent), link_invoice (attach an existing invoice id), unlink_invoice (detach an invoice id), create_invoice (create a new Invoice Agent draft + link it)"),
1941
2200
  slug: zod_1.z.string().optional().describe("Agent slug (lowercase, dashes). Required for get/update/delete/heartbeat/probe/activities/set_*."),
1942
2201
  // create / update fields
1943
2202
  name: zod_1.z.string().optional().describe("Display name."),
@@ -1988,8 +2247,18 @@ function createMindMcpServer(client) {
1988
2247
  limit: zod_1.z.number().optional().describe("Max activities to return for list_activities (default 50)."),
1989
2248
  // seed_known
1990
2249
  overwrite: zod_1.z.boolean().optional().describe("If true with action=seed_known, reset existing records to seed defaults."),
2250
+ // invoice linking
2251
+ invoice_id: zod_1.z.string().optional().describe("Invoice id — required for link_invoice / unlink_invoice."),
2252
+ bill_to_name: zod_1.z.string().optional().describe("Recipient name for create_invoice (required)."),
2253
+ bill_to_company: zod_1.z.string().optional().describe("Recipient company for create_invoice."),
2254
+ bill_to_email: zod_1.z.string().optional().describe("Recipient email for create_invoice."),
2255
+ line_item_description: zod_1.z.string().optional().describe("Single line-item description for create_invoice."),
2256
+ quantity: zod_1.z.number().optional().describe("Line-item quantity for create_invoice (default 1)."),
2257
+ unit_price: zod_1.z.number().optional().describe("Line-item unit price for create_invoice (default 0)."),
2258
+ currency: zod_1.z.string().optional().describe("Invoice currency for create_invoice (default USD)."),
2259
+ invoice_notes: zod_1.z.string().optional().describe("Notes shown on the invoice for create_invoice."),
1991
2260
  }, async (args) => {
1992
- const { action, slug, name, description, status, cadence, host, host_address, port, health_url, source_path, source_repo, mind_identity_doc_id, responsibilities, triggers, tags, owner_email, expected_interval_seconds, authority_can_autonomous, authority_requires_approval, hard, current_job, metrics, note, source, activity_type, activity_payload, list_status, list_host, list_tag, include_archived, limit, overwrite, } = args;
2261
+ const { action, slug, name, description, status, cadence, host, host_address, port, health_url, source_path, source_repo, mind_identity_doc_id, responsibilities, triggers, tags, owner_email, expected_interval_seconds, authority_can_autonomous, authority_requires_approval, hard, current_job, metrics, note, source, activity_type, activity_payload, list_status, list_host, list_tag, include_archived, limit, overwrite, invoice_id, bill_to_name, bill_to_company, bill_to_email, line_item_description, quantity, unit_price, currency, invoice_notes, } = args;
1993
2262
  const requireSlug = (label) => {
1994
2263
  if (!slug) {
1995
2264
  return `Error: 'slug' is required for action=${label}.`;
@@ -2250,6 +2519,109 @@ function createMindMcpServer(client) {
2250
2519
  ],
2251
2520
  };
2252
2521
  }
2522
+ case "list_invoices": {
2523
+ const err = requireSlug("list_invoices");
2524
+ if (err)
2525
+ return { content: [{ type: "text", text: err }], isError: true };
2526
+ const result = await client.listAgentInvoices(slug);
2527
+ if (!result.invoices.length) {
2528
+ return { content: [{ type: "text", text: `No invoices linked to ${slug}.` }] };
2529
+ }
2530
+ const lines = result.invoices.map((i) => ` ${i.invoice_number.padEnd(10)} ${i.status.padEnd(8)} ${i.currency} ${i.total
2531
+ .toFixed(2)
2532
+ .padStart(10)} — ${i.bill_to_company ?? i.bill_to_name ?? "—"}`);
2533
+ return {
2534
+ content: [
2535
+ { type: "text", text: `Invoices linked to ${slug}:\n${lines.join("\n")}` },
2536
+ ],
2537
+ };
2538
+ }
2539
+ case "link_invoice": {
2540
+ const err = requireSlug("link_invoice");
2541
+ if (err)
2542
+ return { content: [{ type: "text", text: err }], isError: true };
2543
+ if (!invoice_id) {
2544
+ return {
2545
+ content: [{ type: "text", text: "Error: 'invoice_id' is required for link_invoice." }],
2546
+ isError: true,
2547
+ };
2548
+ }
2549
+ const updated = await client.linkAgentInvoice(slug, invoice_id);
2550
+ return {
2551
+ content: [
2552
+ {
2553
+ type: "text",
2554
+ text: `🔗 Linked invoice ${invoice_id} to ${updated.slug} (${updated.linked_invoices.length} total).`,
2555
+ },
2556
+ ],
2557
+ };
2558
+ }
2559
+ case "unlink_invoice": {
2560
+ const err = requireSlug("unlink_invoice");
2561
+ if (err)
2562
+ return { content: [{ type: "text", text: err }], isError: true };
2563
+ if (!invoice_id) {
2564
+ return {
2565
+ content: [{ type: "text", text: "Error: 'invoice_id' is required for unlink_invoice." }],
2566
+ isError: true,
2567
+ };
2568
+ }
2569
+ const updated = await client.unlinkAgentInvoice(slug, invoice_id);
2570
+ return {
2571
+ content: [
2572
+ {
2573
+ type: "text",
2574
+ text: `✂️ Unlinked invoice ${invoice_id} from ${updated.slug} (${updated.linked_invoices.length} remain).`,
2575
+ },
2576
+ ],
2577
+ };
2578
+ }
2579
+ case "create_invoice": {
2580
+ const err = requireSlug("create_invoice");
2581
+ if (err)
2582
+ return { content: [{ type: "text", text: err }], isError: true };
2583
+ if (!bill_to_name) {
2584
+ return {
2585
+ content: [
2586
+ { type: "text", text: "Error: 'bill_to_name' is required for create_invoice." },
2587
+ ],
2588
+ isError: true,
2589
+ };
2590
+ }
2591
+ const invoicePayload = {
2592
+ bill_from: {
2593
+ name: "Anthony Conti",
2594
+ company: "Astra AI, Inc.",
2595
+ email: "anthony@theastraway.com",
2596
+ },
2597
+ bill_to: {
2598
+ name: bill_to_name,
2599
+ company: bill_to_company ?? null,
2600
+ email: bill_to_email ?? null,
2601
+ },
2602
+ line_items: line_item_description
2603
+ ? [
2604
+ {
2605
+ description: line_item_description,
2606
+ quantity: quantity ?? 1,
2607
+ unit_price: unit_price ?? 0,
2608
+ },
2609
+ ]
2610
+ : [],
2611
+ currency: currency ?? "USD",
2612
+ };
2613
+ if (invoice_notes)
2614
+ invoicePayload.notes = invoice_notes;
2615
+ const res = await client.createAgentInvoice(slug, invoicePayload);
2616
+ return {
2617
+ content: [
2618
+ {
2619
+ type: "text",
2620
+ text: `🧾 Created invoice ${res.invoice.invoice_number} (${res.invoice.currency} ${res.invoice.total.toFixed(2)}) and linked it to ${slug}.`,
2621
+ },
2622
+ ],
2623
+ };
2624
+ }
2253
2625
  }
2254
2626
  }
2255
2627
  catch (err) {
@@ -2259,6 +2631,171 @@ function createMindMcpServer(client) {
2259
2631
  };
2260
2632
  }
2261
2633
  });
2634
+ // ─── mind_accounts ──────────────────────────────────────
2635
+ // Multi-MIND accounts: discover the MINDs this key's owner can access,
2636
+ // create new ones, and manage owners / viewers. Requires the server to
2637
+ // have MULTI_MIND_ACCOUNTS_ENABLED set.
2638
+ server.tool("mind_accounts", "Manage multi-MIND accounts. A 'MIND' is a knowledge-graph account; one person can own or be granted access to many. Use to discover every MIND you can access (list), spin up a new one (create), permanently delete one (delete), see who can access a MIND (members), grant an existing user access (grant), or email an invitation (invite).", {
2639
+ action: zod_1.z
2640
+ .enum(["list", "create", "delete", "members", "grant", "invite"])
2641
+ .describe("list (every MIND you can access), create (a new MIND you own), delete (permanently delete a MIND you own), members (owners/viewers of a MIND), grant (give an existing user access), invite (email an invitation)"),
2642
+ label: zod_1.z
2643
+ .string()
2644
+ .optional()
2645
+ .describe("Display name for the new MIND (required for create)"),
2646
+ mind_username: zod_1.z
2647
+ .string()
2648
+ .optional()
2649
+ .describe("Username of the MIND to manage (required for delete/members/grant/invite)"),
2650
+ grantee_username: zod_1.z
2651
+ .string()
2652
+ .optional()
2653
+ .describe("Existing MINDapp username to grant access to (required for grant)"),
2654
+ email: zod_1.z
2655
+ .string()
2656
+ .optional()
2657
+ .describe("Email address to send an invitation to (required for invite)"),
2658
+ role: zod_1.z
2659
+ .enum(["owner", "viewer"])
2660
+ .optional()
2661
+ .default("owner")
2662
+ .describe("Access level: owner (full access) or viewer (read-only)"),
2663
+ }, async ({ action, label, mind_username, grantee_username, email, role }) => {
2664
+ try {
2665
+ switch (action) {
2666
+ case "list": {
2667
+ const res = await client.listMinds();
2668
+ if (!res.enabled) {
2669
+ return {
2670
+ content: [
2671
+ {
2672
+ type: "text",
2673
+ text: "Multi-MIND accounts are not enabled for this MIND.",
2674
+ },
2675
+ ],
2676
+ };
2677
+ }
2678
+ const lines = res.accounts.map((a) => `${a.is_active ? "▸" : " "} ${a.label} (@${a.username}) — ${a.role}${a.is_self ? " · you" : ""}`);
2679
+ return {
2680
+ content: [
2681
+ {
2682
+ type: "text",
2683
+ text: `MINDs you can access (${res.accounts.length}):\n${lines.join("\n")}`,
2684
+ },
2685
+ ],
2686
+ };
2687
+ }
2688
+ case "create": {
2689
+ if (!label) {
2690
+ return {
2691
+ content: [{ type: "text", text: "Error: label is required for create" }],
2692
+ isError: true,
2693
+ };
2694
+ }
2695
+ const res = await client.createMind(label);
2696
+ return {
2697
+ content: [
2698
+ {
2699
+ type: "text",
2700
+ text: `✅ Created MIND "${res.label}" (@${res.username}).`,
2701
+ },
2702
+ ],
2703
+ };
2704
+ }
2705
+ case "delete": {
2706
+ if (!mind_username) {
2707
+ return {
2708
+ content: [{ type: "text", text: "Error: mind_username is required for delete" }],
2709
+ isError: true,
2710
+ };
2711
+ }
2712
+ await client.deleteMind(mind_username);
2713
+ return {
2714
+ content: [
2715
+ {
2716
+ type: "text",
2717
+ text: `✅ Permanently deleted MIND @${mind_username}.`,
2718
+ },
2719
+ ],
2720
+ };
2721
+ }
2722
+ case "members": {
2723
+ if (!mind_username) {
2724
+ return {
2725
+ content: [{ type: "text", text: "Error: mind_username is required for members" }],
2726
+ isError: true,
2727
+ };
2728
+ }
2729
+ const res = await client.listMindMembers(mind_username);
2730
+ const members = res.members
2731
+ .map((m) => `• @${m.username} — ${m.role}${m.is_primary ? " (primary)" : ""}`)
2732
+ .join("\n");
2733
+ const invites = res.pending_invites.length
2734
+ ? "\nPending invites:\n" +
2735
+ res.pending_invites.map((i) => `• ${i.email} — ${i.role}`).join("\n")
2736
+ : "";
2737
+ return {
2738
+ content: [
2739
+ {
2740
+ type: "text",
2741
+ text: `Members of @${res.mind_username}:\n${members}${invites}`,
2742
+ },
2743
+ ],
2744
+ };
2745
+ }
2746
+ case "grant": {
2747
+ if (!mind_username || !grantee_username) {
2748
+ return {
2749
+ content: [
2750
+ {
2751
+ type: "text",
2752
+ text: "Error: mind_username and grantee_username are required for grant",
2753
+ },
2754
+ ],
2755
+ isError: true,
2756
+ };
2757
+ }
2758
+ await client.grantMindMember(mind_username, grantee_username, role);
2759
+ return {
2760
+ content: [
2761
+ {
2762
+ type: "text",
2763
+ text: `✅ Granted @${grantee_username} ${role} access to @${mind_username}.`,
2764
+ },
2765
+ ],
2766
+ };
2767
+ }
2768
+ case "invite": {
2769
+ if (!mind_username || !email) {
2770
+ return {
2771
+ content: [
2772
+ {
2773
+ type: "text",
2774
+ text: "Error: mind_username and email are required for invite",
2775
+ },
2776
+ ],
2777
+ isError: true,
2778
+ };
2779
+ }
2780
+ const res = await client.createMindInvite(mind_username, email, role);
2781
+ return {
2782
+ content: [
2783
+ {
2784
+ type: "text",
2785
+ text: `✅ Invitation sent to ${res.email} (${res.role}) for @${mind_username}.`,
2786
+ },
2787
+ ],
2788
+ };
2789
+ }
2790
+ }
2791
+ }
2792
+ catch (err) {
2793
+ return {
2794
+ content: [{ type: "text", text: `mind_accounts error: ${err}` }],
2795
+ isError: true,
2796
+ };
2797
+ }
2798
+ });
2262
2799
  return server;
2263
2800
  }
2264
2801
  //# sourceMappingURL=server.js.map