@astramindapp/mcp-server 0.6.0 → 0.8.2

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,7 +5,7 @@
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 (15):
8
+ * Tools (17):
9
9
  * mind_query — Semantic search across the knowledge graph
10
10
  * mind_remember — Store/manage content in the graph (auto-categorized)
11
11
  * mind_context — Get structured always-loaded context (soul/user/rules)
@@ -21,6 +21,10 @@
21
21
  * mind_insights — ALE insights and analytics
22
22
  * mind_automate — Automations and triggers
23
23
  * mind_notify — Notifications management
24
+ * mind_agents — Admin-only Agent Command Center (registry, heartbeat,
25
+ * probe, status — for Anthony's agent fleet)
26
+ * mind_accounts — Multi-MIND accounts: discover/create MINDs, manage
27
+ * owners and viewers, send invitations
24
28
  */
25
29
  Object.defineProperty(exports, "__esModule", { value: true });
26
30
  exports.createMindMcpServer = createMindMcpServer;
@@ -290,11 +294,11 @@ function createMindMcpServer(client) {
290
294
  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.", {
291
295
  action: zod_1.z
292
296
  .enum([
293
- "list", "create", "update", "complete", "delete", "move", "get",
297
+ "list", "create", "update", "complete", "delete", "bulk_delete", "move", "get",
294
298
  "calendar_list", "calendar_create", "calendar_update", "calendar_delete",
295
299
  "stats",
296
300
  ])
297
- .describe("What to do: list/create/update/complete/delete/move/get items, calendar_list/calendar_create/calendar_update/calendar_delete events, stats (productivity overview)"),
301
+ .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)"),
298
302
  title: zod_1.z.string().optional().describe("Title for new items or calendar events"),
299
303
  description: zod_1.z.string().optional().describe("Description for new items"),
300
304
  status: zod_1.z
@@ -307,11 +311,12 @@ function createMindMcpServer(client) {
307
311
  .describe("Priority: low, medium, high, urgent"),
308
312
  due_date: zod_1.z.string().optional().describe("Due date in YYYY-MM-DD format"),
309
313
  item_id: zod_1.z.string().optional().describe("Item ID for update/complete/delete/move/get actions"),
314
+ item_ids: zod_1.z.array(zod_1.z.string()).optional().describe("Item IDs for bulk_delete (1–200 IDs deleted in one call)"),
310
315
  start_time: zod_1.z.string().optional().describe("Start time for calendar events (ISO 8601)"),
311
316
  end_time: zod_1.z.string().optional().describe("End time for calendar events (ISO 8601)"),
312
317
  event_id: zod_1.z.string().optional().describe("Event ID for calendar_update/calendar_delete"),
313
318
  all_day: zod_1.z.boolean().optional().describe("Whether the calendar event is all-day"),
314
- }, async ({ action, title, description, status, priority, due_date, item_id, start_time, end_time, event_id, all_day }) => {
319
+ }, async ({ action, title, description, status, priority, due_date, item_id, item_ids, start_time, end_time, event_id, all_day }) => {
315
320
  try {
316
321
  switch (action) {
317
322
  case "list": {
@@ -393,6 +398,22 @@ function createMindMcpServer(client) {
393
398
  content: [{ type: "text", text: `✅ Deleted life item ${item_id}` }],
394
399
  };
395
400
  }
401
+ case "bulk_delete": {
402
+ if (!item_ids || item_ids.length === 0) {
403
+ return {
404
+ content: [{ type: "text", text: "Error: item_ids (a non-empty array of IDs) is required for bulk_delete" }],
405
+ isError: true,
406
+ };
407
+ }
408
+ const result = await client.bulkDeleteLifeItems(item_ids);
409
+ const lines = [
410
+ `✅ Bulk delete complete — ${result.deleted_count} life item(s) deleted.`,
411
+ ];
412
+ if (result.not_found.length > 0) {
413
+ lines.push(`⚠️ ${result.not_found.length} id(s) skipped (not found / not yours): ${result.not_found.join(", ")}`);
414
+ }
415
+ return { content: [{ type: "text", text: lines.join("\n") }] };
416
+ }
396
417
  case "move": {
397
418
  if (!item_id || !status) {
398
419
  return {
@@ -510,6 +531,212 @@ function createMindMcpServer(client) {
510
531
  };
511
532
  }
512
533
  });
534
+ // ─── mind_tasks ─────────────────────────────────────────
535
+ // Site-wide tasks: assignable, completable work items that attach to a
536
+ // Life project, a CRM contact, an agent, or stand alone.
537
+ 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.", {
538
+ action: zod_1.z
539
+ .enum([
540
+ "list", "create", "get", "update", "complete", "reopen",
541
+ "assign", "delete", "reports",
542
+ ])
543
+ .describe("What to do: list/create/get/update/complete/reopen/assign/delete tasks, or reports (completion analytics)"),
544
+ task_id: zod_1.z.string().optional().describe("Task ID — required for get/update/complete/reopen/assign/delete"),
545
+ title: zod_1.z.string().optional().describe("Task title (required for create)"),
546
+ description: zod_1.z.string().optional().describe("Task description"),
547
+ status: zod_1.z.string().optional().describe("Filter (list) or new status: open, in_progress, blocked, done"),
548
+ priority: zod_1.z.string().optional().describe("Priority: none, low, medium, high, urgent"),
549
+ due_date: zod_1.z.string().optional().describe("Due date YYYY-MM-DD"),
550
+ parent_type: zod_1.z.string().optional().describe("Attach to: life_item, contact, agent, or none"),
551
+ parent_id: zod_1.z.string().optional().describe("ID of the parent project / contact / agent"),
552
+ assignee_type: zod_1.z.string().optional().describe("Assignee: user, agent, external, or unassigned"),
553
+ assignee_id: zod_1.z.string().optional().describe("Username, agent slug, or email of the assignee"),
554
+ dispatch_agent: zod_1.z.boolean().optional().describe("When assigning to an agent, also fire it now"),
555
+ note: zod_1.z.string().optional().describe("Completion note (for complete)"),
556
+ overdue: zod_1.z.boolean().optional().describe("List only overdue tasks"),
557
+ }, async ({ action, task_id, title, description, status, priority, due_date, parent_type, parent_id, assignee_type, assignee_id, dispatch_agent, note, overdue }) => {
558
+ try {
559
+ const fmt = (t) => `• [${t.status}] ${t.title}${t.priority && t.priority !== "none" ? ` (${t.priority})` : ""}` +
560
+ `${t.due_date ? `, due ${t.due_date}` : ""}${t.is_overdue ? " ⚠️ OVERDUE" : ""}` +
561
+ `${t.assignee_label ? `, → ${t.assignee_label}` : ""}` +
562
+ `${t.parent_label ? `, on ${t.parent_label}` : ""} — id: ${t.task_id}`;
563
+ switch (action) {
564
+ case "list": {
565
+ const params = {};
566
+ if (status)
567
+ params.status = status;
568
+ if (priority)
569
+ params.priority = priority;
570
+ if (parent_type)
571
+ params.parent_type = parent_type;
572
+ if (parent_id)
573
+ params.parent_id = parent_id;
574
+ if (assignee_type)
575
+ params.assignee_type = assignee_type;
576
+ if (assignee_id)
577
+ params.assignee_id = assignee_id;
578
+ if (overdue)
579
+ params.overdue = "true";
580
+ const result = await client.listTasks(params);
581
+ const tasks = result.tasks ?? [];
582
+ if (tasks.length === 0) {
583
+ return { content: [{ type: "text", text: "No tasks found." }] };
584
+ }
585
+ return {
586
+ content: [{
587
+ type: "text",
588
+ text: `${result.total} task(s):\n` + tasks.map(fmt).join("\n"),
589
+ }],
590
+ };
591
+ }
592
+ case "create": {
593
+ if (!title) {
594
+ return {
595
+ content: [{ type: "text", text: "Error: title is required to create a task" }],
596
+ isError: true,
597
+ };
598
+ }
599
+ const t = await client.createTask({
600
+ title, description, status, priority, due_date,
601
+ parent_type, parent_id, assignee_type, assignee_id,
602
+ dispatch_agent,
603
+ });
604
+ return {
605
+ content: [{ type: "text", text: `✅ Created task: ${fmt(t)}` }],
606
+ };
607
+ }
608
+ case "get": {
609
+ if (!task_id) {
610
+ return {
611
+ content: [{ type: "text", text: "Error: task_id is required for get" }],
612
+ isError: true,
613
+ };
614
+ }
615
+ const t = await client.getTask(task_id);
616
+ const extra = t.agent_run_status ? `\nAgent run: ${t.agent_run_status}` : "";
617
+ return {
618
+ content: [{
619
+ type: "text",
620
+ text: `${fmt(t)}${t.description ? `\n${t.description}` : ""}${extra}`,
621
+ }],
622
+ };
623
+ }
624
+ case "update": {
625
+ if (!task_id) {
626
+ return {
627
+ content: [{ type: "text", text: "Error: task_id is required for update" }],
628
+ isError: true,
629
+ };
630
+ }
631
+ const patch = {};
632
+ if (title !== undefined)
633
+ patch.title = title;
634
+ if (description !== undefined)
635
+ patch.description = description;
636
+ if (status !== undefined)
637
+ patch.status = status;
638
+ if (priority !== undefined)
639
+ patch.priority = priority;
640
+ if (due_date !== undefined)
641
+ patch.due_date = due_date;
642
+ if (parent_type !== undefined)
643
+ patch.parent_type = parent_type;
644
+ if (parent_id !== undefined)
645
+ patch.parent_id = parent_id;
646
+ const t = await client.updateTask(task_id, patch);
647
+ return {
648
+ content: [{ type: "text", text: `✅ Updated task: ${fmt(t)}` }],
649
+ };
650
+ }
651
+ case "complete": {
652
+ if (!task_id) {
653
+ return {
654
+ content: [{ type: "text", text: "Error: task_id is required for complete" }],
655
+ isError: true,
656
+ };
657
+ }
658
+ const t = await client.completeTask(task_id, true, note);
659
+ return {
660
+ content: [{ type: "text", text: `✅ Completed task: "${t.title}"` }],
661
+ };
662
+ }
663
+ case "reopen": {
664
+ if (!task_id) {
665
+ return {
666
+ content: [{ type: "text", text: "Error: task_id is required for reopen" }],
667
+ isError: true,
668
+ };
669
+ }
670
+ const t = await client.completeTask(task_id, false);
671
+ return {
672
+ content: [{ type: "text", text: `↩️ Reopened task: "${t.title}"` }],
673
+ };
674
+ }
675
+ case "assign": {
676
+ if (!task_id || !assignee_type) {
677
+ return {
678
+ content: [{ type: "text", text: "Error: task_id and assignee_type are required for assign" }],
679
+ isError: true,
680
+ };
681
+ }
682
+ const t = await client.assignTask(task_id, {
683
+ assignee_type, assignee_id, dispatch_agent: dispatch_agent ?? false,
684
+ });
685
+ return {
686
+ content: [{
687
+ type: "text",
688
+ text: `✅ Assigned "${t.title}" → ${t.assignee_label ?? t.assignee_id ?? "unassigned"}`,
689
+ }],
690
+ };
691
+ }
692
+ case "delete": {
693
+ if (!task_id) {
694
+ return {
695
+ content: [{ type: "text", text: "Error: task_id is required for delete" }],
696
+ isError: true,
697
+ };
698
+ }
699
+ await client.deleteTask(task_id);
700
+ return {
701
+ content: [{ type: "text", text: `🗑️ Deleted task ${task_id}` }],
702
+ };
703
+ }
704
+ case "reports": {
705
+ const params = {};
706
+ if (parent_type)
707
+ params.parent_type = parent_type;
708
+ if (parent_id)
709
+ params.parent_id = parent_id;
710
+ const r = await client.taskReports(params);
711
+ const lines = [
712
+ `📊 Task report (${r.total} total)`,
713
+ ` open ${r.open} · in progress ${r.in_progress} · blocked ${r.blocked} · done ${r.done}`,
714
+ ` completion rate: ${r.completion_rate}%`,
715
+ ` overdue: ${r.overdue} · due soon: ${r.due_soon} · unassigned: ${r.unassigned}`,
716
+ ` avg completion: ${r.avg_completion_days} days`,
717
+ ];
718
+ if (r.by_assignee && r.by_assignee.length) {
719
+ lines.push(" By assignee:");
720
+ for (const a of r.by_assignee.slice(0, 10)) {
721
+ lines.push(` ${a.assignee_label ?? a.assignee_id ?? "unassigned"}: ${a.done}/${a.total} done (${a.completion_rate}%)`);
722
+ }
723
+ }
724
+ return { content: [{ type: "text", text: lines.join("\n") }] };
725
+ }
726
+ default:
727
+ return {
728
+ content: [{ type: "text", text: `Unknown action: ${action}` }],
729
+ isError: true,
730
+ };
731
+ }
732
+ }
733
+ catch (err) {
734
+ return {
735
+ content: [{ type: "text", text: `mind_tasks error: ${err instanceof Error ? err.message : String(err)}` }],
736
+ isError: true,
737
+ };
738
+ }
739
+ });
513
740
  // ─── mind_crm ───────────────────────────────────────────
514
741
  // Read/write contacts, interactions, and activities.
515
742
  // Enhanced: delete, get, log_activity, list_activities.
@@ -1912,6 +2139,633 @@ function createMindMcpServer(client) {
1912
2139
  };
1913
2140
  }
1914
2141
  });
2142
+ // ─── mind_agents ────────────────────────────────────────
2143
+ // Admin-only registry of every agent across MINDapp + the VPS fleet.
2144
+ // Surface UI: https://m-i-n-d.ai/agents · Backend: /admin/agents/*
2145
+ // Use this BEFORE creating a new agent so you don't collide with an
2146
+ // existing slug, and AFTER any non-trivial work to update status,
2147
+ // current_job, or responsibilities.
2148
+ server.tool("mind_agents", "Admin-only Agent Command Center — canonical registry of every Astra AI agent (running on VPS, planned, archived). Query before scaffolding a new agent. Update status / current_job / responsibilities after non-trivial work. Backed by /admin/agents on the MIND backend; admin API key required.", {
2149
+ action: zod_1.z
2150
+ .enum([
2151
+ "list",
2152
+ "get",
2153
+ "create",
2154
+ "update",
2155
+ "delete",
2156
+ "heartbeat",
2157
+ "probe",
2158
+ "log_activity",
2159
+ "list_activities",
2160
+ "import_from_mind",
2161
+ "seed_known",
2162
+ "set_status",
2163
+ "set_current_job",
2164
+ "list_invoices",
2165
+ "link_invoice",
2166
+ "unlink_invoice",
2167
+ "create_invoice",
2168
+ ])
2169
+ .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)"),
2170
+ slug: zod_1.z.string().optional().describe("Agent slug (lowercase, dashes). Required for get/update/delete/heartbeat/probe/activities/set_*."),
2171
+ // create / update fields
2172
+ name: zod_1.z.string().optional().describe("Display name."),
2173
+ description: zod_1.z.string().optional().describe("One-paragraph description of the agent."),
2174
+ status: zod_1.z
2175
+ .enum(["running", "paused", "planned", "archived", "error"])
2176
+ .optional()
2177
+ .describe("Lifecycle status."),
2178
+ cadence: zod_1.z
2179
+ .enum(["continuous", "scheduled", "on-demand"])
2180
+ .optional()
2181
+ .describe("How the agent runs."),
2182
+ host: zod_1.z.string().optional().describe("Host: DO1 / DO2-LEO / local / render / vercel / atlas-droplet / other."),
2183
+ host_address: zod_1.z.string().optional().describe("IP or hostname (e.g. 45.55.233.204)."),
2184
+ port: zod_1.z.number().optional().describe("TCP port if applicable."),
2185
+ health_url: zod_1.z.string().optional().describe("Optional HTTP health endpoint for the probe action."),
2186
+ source_path: zod_1.z.string().optional().describe("Local path to the agent directory (e.g. /Users/anthonyjconti/Documents/Agents/Foo)."),
2187
+ source_repo: zod_1.z.string().optional().describe("Git repo URL when applicable."),
2188
+ mind_identity_doc_id: zod_1.z.string().optional().describe("MIND document id for the agent-identity record."),
2189
+ responsibilities: zod_1.z.array(zod_1.z.string()).optional().describe("Bullet list of what the agent does."),
2190
+ triggers: zod_1.z.array(zod_1.z.string()).optional().describe("Canonical phrases that should invoke this agent."),
2191
+ tags: zod_1.z.array(zod_1.z.string()).optional().describe("Domain tags for filtering."),
2192
+ owner_email: zod_1.z.string().optional().describe("Owner contact (defaults to anthony@theastraway.com)."),
2193
+ expected_interval_seconds: zod_1.z
2194
+ .number()
2195
+ .optional()
2196
+ .describe("Expected heartbeat cadence in seconds; live_status thresholds compute against 2× / 4× this value."),
2197
+ authority_can_autonomous: zod_1.z.array(zod_1.z.string()).optional().describe("Things this agent can do without asking."),
2198
+ authority_requires_approval: zod_1.z.array(zod_1.z.string()).optional().describe("Things that need Anthony's sign-off."),
2199
+ // delete options
2200
+ hard: zod_1.z.boolean().optional().describe("If true with action=delete, permanently remove. Default soft-archives."),
2201
+ // heartbeat fields
2202
+ current_job: zod_1.z.string().optional().describe("What the agent is doing right now (set on heartbeat or set_current_job)."),
2203
+ metrics: zod_1.z.record(zod_1.z.string(), zod_1.z.any()).optional().describe("Arbitrary metrics blob attached to a heartbeat."),
2204
+ note: zod_1.z.string().optional().describe("Free-form note attached to a heartbeat."),
2205
+ source: zod_1.z.string().optional().describe("Source attribution for heartbeats / activities."),
2206
+ // log_activity fields
2207
+ activity_type: zod_1.z.string().optional().describe("Activity type for log_activity (e.g. job_done, job_start, error, note)."),
2208
+ activity_payload: zod_1.z.record(zod_1.z.string(), zod_1.z.any()).optional().describe("Activity payload for log_activity."),
2209
+ // list / list_activities filters
2210
+ list_status: zod_1.z
2211
+ .enum(["running", "paused", "planned", "archived", "error"])
2212
+ .optional()
2213
+ .describe("Filter by status when listing."),
2214
+ list_host: zod_1.z.string().optional().describe("Filter by host when listing."),
2215
+ list_tag: zod_1.z.string().optional().describe("Filter by tag when listing."),
2216
+ include_archived: zod_1.z.boolean().optional().describe("Include archived records when listing (default false)."),
2217
+ limit: zod_1.z.number().optional().describe("Max activities to return for list_activities (default 50)."),
2218
+ // seed_known
2219
+ overwrite: zod_1.z.boolean().optional().describe("If true with action=seed_known, reset existing records to seed defaults."),
2220
+ // invoice linking
2221
+ invoice_id: zod_1.z.string().optional().describe("Invoice id — required for link_invoice / unlink_invoice."),
2222
+ bill_to_name: zod_1.z.string().optional().describe("Recipient name for create_invoice (required)."),
2223
+ bill_to_company: zod_1.z.string().optional().describe("Recipient company for create_invoice."),
2224
+ bill_to_email: zod_1.z.string().optional().describe("Recipient email for create_invoice."),
2225
+ line_item_description: zod_1.z.string().optional().describe("Single line-item description for create_invoice."),
2226
+ quantity: zod_1.z.number().optional().describe("Line-item quantity for create_invoice (default 1)."),
2227
+ unit_price: zod_1.z.number().optional().describe("Line-item unit price for create_invoice (default 0)."),
2228
+ currency: zod_1.z.string().optional().describe("Invoice currency for create_invoice (default USD)."),
2229
+ invoice_notes: zod_1.z.string().optional().describe("Notes shown on the invoice for create_invoice."),
2230
+ }, async (args) => {
2231
+ 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;
2232
+ const requireSlug = (label) => {
2233
+ if (!slug) {
2234
+ return `Error: 'slug' is required for action=${label}.`;
2235
+ }
2236
+ return null;
2237
+ };
2238
+ const buildPayload = () => {
2239
+ const payload = {};
2240
+ if (name !== undefined)
2241
+ payload.name = name;
2242
+ if (description !== undefined)
2243
+ payload.description = description;
2244
+ if (status !== undefined)
2245
+ payload.status = status;
2246
+ if (cadence !== undefined)
2247
+ payload.cadence = cadence;
2248
+ if (host !== undefined)
2249
+ payload.host = host;
2250
+ if (host_address !== undefined)
2251
+ payload.host_address = host_address;
2252
+ if (port !== undefined)
2253
+ payload.port = port;
2254
+ if (health_url !== undefined)
2255
+ payload.health_url = health_url;
2256
+ if (source_path !== undefined)
2257
+ payload.source_path = source_path;
2258
+ if (source_repo !== undefined)
2259
+ payload.source_repo = source_repo;
2260
+ if (mind_identity_doc_id !== undefined)
2261
+ payload.mind_identity_doc_id = mind_identity_doc_id;
2262
+ if (responsibilities !== undefined)
2263
+ payload.responsibilities = responsibilities;
2264
+ if (triggers !== undefined)
2265
+ payload.triggers = triggers;
2266
+ if (tags !== undefined)
2267
+ payload.tags = tags;
2268
+ if (owner_email !== undefined)
2269
+ payload.owner_email = owner_email;
2270
+ if (expected_interval_seconds !== undefined)
2271
+ payload.expected_interval_seconds = expected_interval_seconds;
2272
+ if (current_job !== undefined)
2273
+ payload.current_job = current_job;
2274
+ if (authority_can_autonomous !== undefined || authority_requires_approval !== undefined) {
2275
+ payload.authority = {
2276
+ can_autonomous: authority_can_autonomous ?? [],
2277
+ requires_approval: authority_requires_approval ?? [],
2278
+ };
2279
+ }
2280
+ return payload;
2281
+ };
2282
+ try {
2283
+ switch (action) {
2284
+ case "list": {
2285
+ const result = await client.listAgents({
2286
+ status: list_status,
2287
+ host: list_host,
2288
+ tag: list_tag,
2289
+ include_archived,
2290
+ });
2291
+ const lines = [];
2292
+ const s = result.stats;
2293
+ lines.push(`Agent fleet: ${s.total} total · ${s.running} running · ${s.planned} planned · ${s.paused} paused · ${s.error} errored`, `Live: ${s.online} online · ${s.stale} stale · ${s.offline} offline`, "");
2294
+ for (const a of result.agents) {
2295
+ const host = a.host ?? "—";
2296
+ lines.push(`• [${a.status.padEnd(8)}] ${a.slug.padEnd(28)} ${a.live_status.padEnd(7)} host=${host} — ${a.name}`);
2297
+ }
2298
+ return { content: [{ type: "text", text: lines.join("\n") }] };
2299
+ }
2300
+ case "get": {
2301
+ const err = requireSlug("get");
2302
+ if (err)
2303
+ return { content: [{ type: "text", text: err }], isError: true };
2304
+ const detail = await client.getAgent(slug);
2305
+ const lines = [
2306
+ `${detail.name} (${detail.slug})`,
2307
+ ` status=${detail.status} · cadence=${detail.cadence} · live=${detail.live_status}`,
2308
+ ` host=${detail.host ?? "—"}${detail.host_address ? ` (${detail.host_address})` : ""}`,
2309
+ ` last_heartbeat=${detail.last_heartbeat ?? "never"} · current_job=${detail.current_job ?? "—"}`,
2310
+ detail.description ? ` ${detail.description}` : "",
2311
+ detail.responsibilities.length
2312
+ ? ` Responsibilities:\n - ${detail.responsibilities.join("\n - ")}`
2313
+ : "",
2314
+ detail.tags.length ? ` Tags: ${detail.tags.join(", ")}` : "",
2315
+ detail.recent_activities?.length
2316
+ ? ` Recent activity: ${detail.recent_activities
2317
+ .slice(0, 5)
2318
+ .map((e) => `${e.type}@${e.ts.slice(0, 19)}`)
2319
+ .join(" · ")}`
2320
+ : "",
2321
+ ].filter(Boolean);
2322
+ return { content: [{ type: "text", text: lines.join("\n") }] };
2323
+ }
2324
+ case "create": {
2325
+ if (!slug || !name) {
2326
+ return {
2327
+ content: [
2328
+ { type: "text", text: "Error: 'slug' and 'name' are required for create." },
2329
+ ],
2330
+ isError: true,
2331
+ };
2332
+ }
2333
+ const payload = { slug, name, ...buildPayload() };
2334
+ const created = await client.createAgent(payload);
2335
+ return {
2336
+ content: [
2337
+ { type: "text", text: `✅ Created agent: ${created.slug} (status=${created.status})` },
2338
+ ],
2339
+ };
2340
+ }
2341
+ case "update": {
2342
+ const err = requireSlug("update");
2343
+ if (err)
2344
+ return { content: [{ type: "text", text: err }], isError: true };
2345
+ const payload = buildPayload();
2346
+ if (Object.keys(payload).length === 0) {
2347
+ return {
2348
+ content: [
2349
+ { type: "text", text: "Error: provide at least one field to update." },
2350
+ ],
2351
+ isError: true,
2352
+ };
2353
+ }
2354
+ const updated = await client.updateAgent(slug, payload);
2355
+ return {
2356
+ content: [
2357
+ { type: "text", text: `✅ Updated ${updated.slug} (status=${updated.status}, live=${updated.live_status})` },
2358
+ ],
2359
+ };
2360
+ }
2361
+ case "delete": {
2362
+ const err = requireSlug("delete");
2363
+ if (err)
2364
+ return { content: [{ type: "text", text: err }], isError: true };
2365
+ const result = await client.deleteAgent(slug, !!hard);
2366
+ return {
2367
+ content: [
2368
+ { type: "text", text: `✅ ${result.hard ? "Deleted" : "Archived"} ${result.deleted}` },
2369
+ ],
2370
+ };
2371
+ }
2372
+ case "heartbeat": {
2373
+ const err = requireSlug("heartbeat");
2374
+ if (err)
2375
+ return { content: [{ type: "text", text: err }], isError: true };
2376
+ const updated = await client.agentHeartbeat(slug, {
2377
+ current_job,
2378
+ metrics,
2379
+ note,
2380
+ source: source ?? "mcp-agent",
2381
+ });
2382
+ return {
2383
+ content: [
2384
+ {
2385
+ type: "text",
2386
+ text: `💓 heartbeat ${updated.slug} → live=${updated.live_status}${updated.current_job ? ` · job="${updated.current_job}"` : ""}`,
2387
+ },
2388
+ ],
2389
+ };
2390
+ }
2391
+ case "probe": {
2392
+ const err = requireSlug("probe");
2393
+ if (err)
2394
+ return { content: [{ type: "text", text: err }], isError: true };
2395
+ const result = await client.agentProbe(slug);
2396
+ const p = result.probe;
2397
+ const summary = p.ok
2398
+ ? `✅ ${p.probe_kind ?? "probe"} OK · ${p.status_code ?? ""} · ${p.latency_ms ?? "?"}ms${p.endpoint ? ` · ${p.endpoint}` : ""}`
2399
+ : `❌ ${p.probe_kind ?? "probe"} failed: ${p.error ?? "no signal"}`;
2400
+ return { content: [{ type: "text", text: summary }] };
2401
+ }
2402
+ case "log_activity": {
2403
+ const err = requireSlug("log_activity");
2404
+ if (err)
2405
+ return { content: [{ type: "text", text: err }], isError: true };
2406
+ const activity = await client.logAgentActivity(slug, {
2407
+ type: activity_type ?? "note",
2408
+ payload: activity_payload,
2409
+ source: source ?? "mcp-agent",
2410
+ });
2411
+ return {
2412
+ content: [
2413
+ { type: "text", text: `📝 ${activity.type} logged for ${slug} (${activity.activity_id})` },
2414
+ ],
2415
+ };
2416
+ }
2417
+ case "list_activities": {
2418
+ const err = requireSlug("list_activities");
2419
+ if (err)
2420
+ return { content: [{ type: "text", text: err }], isError: true };
2421
+ const result = await client.listAgentActivities(slug, limit ?? 50);
2422
+ if (!result.activities.length) {
2423
+ return { content: [{ type: "text", text: `No activity for ${slug}.` }] };
2424
+ }
2425
+ const lines = result.activities.map((e) => ` ${e.ts} · ${e.type.padEnd(15)} · src=${e.source}`);
2426
+ return { content: [{ type: "text", text: lines.join("\n") }] };
2427
+ }
2428
+ case "import_from_mind": {
2429
+ const result = await client.importAgentsFromMind();
2430
+ if (result.error) {
2431
+ return {
2432
+ content: [{ type: "text", text: `⚠️ import-from-mind: ${result.error}` }],
2433
+ isError: true,
2434
+ };
2435
+ }
2436
+ return {
2437
+ content: [
2438
+ {
2439
+ type: "text",
2440
+ text: `✅ Enriched ${result.enriched} agent(s) from MIND identity docs (${result.skipped} skipped). Slugs: ${result.matched_slugs.join(", ") || "—"}`,
2441
+ },
2442
+ ],
2443
+ };
2444
+ }
2445
+ case "seed_known": {
2446
+ const result = await client.seedKnownAgents(!!overwrite);
2447
+ return {
2448
+ content: [
2449
+ {
2450
+ type: "text",
2451
+ text: `✅ Seed: +${result.inserted} new · ${result.updated} updated · ${result.skipped} unchanged · ${result.total_known} canonical`,
2452
+ },
2453
+ ],
2454
+ };
2455
+ }
2456
+ case "set_status": {
2457
+ const err = requireSlug("set_status");
2458
+ if (err)
2459
+ return { content: [{ type: "text", text: err }], isError: true };
2460
+ if (!status) {
2461
+ return {
2462
+ content: [{ type: "text", text: "Error: 'status' is required for set_status." }],
2463
+ isError: true,
2464
+ };
2465
+ }
2466
+ const updated = await client.updateAgent(slug, { status });
2467
+ return {
2468
+ content: [
2469
+ { type: "text", text: `✅ ${updated.slug} → status=${updated.status}` },
2470
+ ],
2471
+ };
2472
+ }
2473
+ case "set_current_job": {
2474
+ const err = requireSlug("set_current_job");
2475
+ if (err)
2476
+ return { content: [{ type: "text", text: err }], isError: true };
2477
+ if (current_job === undefined) {
2478
+ return {
2479
+ content: [
2480
+ { type: "text", text: "Error: 'current_job' is required for set_current_job." },
2481
+ ],
2482
+ isError: true,
2483
+ };
2484
+ }
2485
+ const updated = await client.updateAgent(slug, { current_job });
2486
+ return {
2487
+ content: [
2488
+ { type: "text", text: `✅ ${updated.slug} → current_job="${updated.current_job ?? ""}"` },
2489
+ ],
2490
+ };
2491
+ }
2492
+ case "list_invoices": {
2493
+ const err = requireSlug("list_invoices");
2494
+ if (err)
2495
+ return { content: [{ type: "text", text: err }], isError: true };
2496
+ const result = await client.listAgentInvoices(slug);
2497
+ if (!result.invoices.length) {
2498
+ return { content: [{ type: "text", text: `No invoices linked to ${slug}.` }] };
2499
+ }
2500
+ const lines = result.invoices.map((i) => ` ${i.invoice_number.padEnd(10)} ${i.status.padEnd(8)} ${i.currency} ${i.total
2501
+ .toFixed(2)
2502
+ .padStart(10)} — ${i.bill_to_company ?? i.bill_to_name ?? "—"}`);
2503
+ return {
2504
+ content: [
2505
+ { type: "text", text: `Invoices linked to ${slug}:\n${lines.join("\n")}` },
2506
+ ],
2507
+ };
2508
+ }
2509
+ case "link_invoice": {
2510
+ const err = requireSlug("link_invoice");
2511
+ if (err)
2512
+ return { content: [{ type: "text", text: err }], isError: true };
2513
+ if (!invoice_id) {
2514
+ return {
2515
+ content: [{ type: "text", text: "Error: 'invoice_id' is required for link_invoice." }],
2516
+ isError: true,
2517
+ };
2518
+ }
2519
+ const updated = await client.linkAgentInvoice(slug, invoice_id);
2520
+ return {
2521
+ content: [
2522
+ {
2523
+ type: "text",
2524
+ text: `🔗 Linked invoice ${invoice_id} to ${updated.slug} (${updated.linked_invoices.length} total).`,
2525
+ },
2526
+ ],
2527
+ };
2528
+ }
2529
+ case "unlink_invoice": {
2530
+ const err = requireSlug("unlink_invoice");
2531
+ if (err)
2532
+ return { content: [{ type: "text", text: err }], isError: true };
2533
+ if (!invoice_id) {
2534
+ return {
2535
+ content: [{ type: "text", text: "Error: 'invoice_id' is required for unlink_invoice." }],
2536
+ isError: true,
2537
+ };
2538
+ }
2539
+ const updated = await client.unlinkAgentInvoice(slug, invoice_id);
2540
+ return {
2541
+ content: [
2542
+ {
2543
+ type: "text",
2544
+ text: `✂️ Unlinked invoice ${invoice_id} from ${updated.slug} (${updated.linked_invoices.length} remain).`,
2545
+ },
2546
+ ],
2547
+ };
2548
+ }
2549
+ case "create_invoice": {
2550
+ const err = requireSlug("create_invoice");
2551
+ if (err)
2552
+ return { content: [{ type: "text", text: err }], isError: true };
2553
+ if (!bill_to_name) {
2554
+ return {
2555
+ content: [
2556
+ { type: "text", text: "Error: 'bill_to_name' is required for create_invoice." },
2557
+ ],
2558
+ isError: true,
2559
+ };
2560
+ }
2561
+ const invoicePayload = {
2562
+ bill_from: {
2563
+ name: "Anthony Conti",
2564
+ company: "Astra AI, Inc.",
2565
+ email: "anthony@theastraway.com",
2566
+ },
2567
+ bill_to: {
2568
+ name: bill_to_name,
2569
+ company: bill_to_company ?? null,
2570
+ email: bill_to_email ?? null,
2571
+ },
2572
+ line_items: line_item_description
2573
+ ? [
2574
+ {
2575
+ description: line_item_description,
2576
+ quantity: quantity ?? 1,
2577
+ unit_price: unit_price ?? 0,
2578
+ },
2579
+ ]
2580
+ : [],
2581
+ currency: currency ?? "USD",
2582
+ };
2583
+ if (invoice_notes)
2584
+ invoicePayload.notes = invoice_notes;
2585
+ const res = await client.createAgentInvoice(slug, invoicePayload);
2586
+ return {
2587
+ content: [
2588
+ {
2589
+ type: "text",
2590
+ text: `🧾 Created invoice ${res.invoice.invoice_number} (${res.invoice.currency} ${res.invoice.total.toFixed(2)}) and linked it to ${slug}.`,
2591
+ },
2592
+ ],
2593
+ };
2594
+ }
2595
+ }
2596
+ }
2597
+ catch (err) {
2598
+ return {
2599
+ content: [{ type: "text", text: `mind_agents error: ${err}` }],
2600
+ isError: true,
2601
+ };
2602
+ }
2603
+ });
2604
+ // ─── mind_accounts ──────────────────────────────────────
2605
+ // Multi-MIND accounts: discover the MINDs this key's owner can access,
2606
+ // create new ones, and manage owners / viewers. Requires the server to
2607
+ // have MULTI_MIND_ACCOUNTS_ENABLED set.
2608
+ 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).", {
2609
+ action: zod_1.z
2610
+ .enum(["list", "create", "delete", "members", "grant", "invite"])
2611
+ .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)"),
2612
+ label: zod_1.z
2613
+ .string()
2614
+ .optional()
2615
+ .describe("Display name for the new MIND (required for create)"),
2616
+ mind_username: zod_1.z
2617
+ .string()
2618
+ .optional()
2619
+ .describe("Username of the MIND to manage (required for delete/members/grant/invite)"),
2620
+ grantee_username: zod_1.z
2621
+ .string()
2622
+ .optional()
2623
+ .describe("Existing MINDapp username to grant access to (required for grant)"),
2624
+ email: zod_1.z
2625
+ .string()
2626
+ .optional()
2627
+ .describe("Email address to send an invitation to (required for invite)"),
2628
+ role: zod_1.z
2629
+ .enum(["owner", "viewer"])
2630
+ .optional()
2631
+ .default("owner")
2632
+ .describe("Access level: owner (full access) or viewer (read-only)"),
2633
+ }, async ({ action, label, mind_username, grantee_username, email, role }) => {
2634
+ try {
2635
+ switch (action) {
2636
+ case "list": {
2637
+ const res = await client.listMinds();
2638
+ if (!res.enabled) {
2639
+ return {
2640
+ content: [
2641
+ {
2642
+ type: "text",
2643
+ text: "Multi-MIND accounts are not enabled for this MIND.",
2644
+ },
2645
+ ],
2646
+ };
2647
+ }
2648
+ const lines = res.accounts.map((a) => `${a.is_active ? "▸" : " "} ${a.label} (@${a.username}) — ${a.role}${a.is_self ? " · you" : ""}`);
2649
+ return {
2650
+ content: [
2651
+ {
2652
+ type: "text",
2653
+ text: `MINDs you can access (${res.accounts.length}):\n${lines.join("\n")}`,
2654
+ },
2655
+ ],
2656
+ };
2657
+ }
2658
+ case "create": {
2659
+ if (!label) {
2660
+ return {
2661
+ content: [{ type: "text", text: "Error: label is required for create" }],
2662
+ isError: true,
2663
+ };
2664
+ }
2665
+ const res = await client.createMind(label);
2666
+ return {
2667
+ content: [
2668
+ {
2669
+ type: "text",
2670
+ text: `✅ Created MIND "${res.label}" (@${res.username}).`,
2671
+ },
2672
+ ],
2673
+ };
2674
+ }
2675
+ case "delete": {
2676
+ if (!mind_username) {
2677
+ return {
2678
+ content: [{ type: "text", text: "Error: mind_username is required for delete" }],
2679
+ isError: true,
2680
+ };
2681
+ }
2682
+ await client.deleteMind(mind_username);
2683
+ return {
2684
+ content: [
2685
+ {
2686
+ type: "text",
2687
+ text: `✅ Permanently deleted MIND @${mind_username}.`,
2688
+ },
2689
+ ],
2690
+ };
2691
+ }
2692
+ case "members": {
2693
+ if (!mind_username) {
2694
+ return {
2695
+ content: [{ type: "text", text: "Error: mind_username is required for members" }],
2696
+ isError: true,
2697
+ };
2698
+ }
2699
+ const res = await client.listMindMembers(mind_username);
2700
+ const members = res.members
2701
+ .map((m) => `• @${m.username} — ${m.role}${m.is_primary ? " (primary)" : ""}`)
2702
+ .join("\n");
2703
+ const invites = res.pending_invites.length
2704
+ ? "\nPending invites:\n" +
2705
+ res.pending_invites.map((i) => `• ${i.email} — ${i.role}`).join("\n")
2706
+ : "";
2707
+ return {
2708
+ content: [
2709
+ {
2710
+ type: "text",
2711
+ text: `Members of @${res.mind_username}:\n${members}${invites}`,
2712
+ },
2713
+ ],
2714
+ };
2715
+ }
2716
+ case "grant": {
2717
+ if (!mind_username || !grantee_username) {
2718
+ return {
2719
+ content: [
2720
+ {
2721
+ type: "text",
2722
+ text: "Error: mind_username and grantee_username are required for grant",
2723
+ },
2724
+ ],
2725
+ isError: true,
2726
+ };
2727
+ }
2728
+ await client.grantMindMember(mind_username, grantee_username, role);
2729
+ return {
2730
+ content: [
2731
+ {
2732
+ type: "text",
2733
+ text: `✅ Granted @${grantee_username} ${role} access to @${mind_username}.`,
2734
+ },
2735
+ ],
2736
+ };
2737
+ }
2738
+ case "invite": {
2739
+ if (!mind_username || !email) {
2740
+ return {
2741
+ content: [
2742
+ {
2743
+ type: "text",
2744
+ text: "Error: mind_username and email are required for invite",
2745
+ },
2746
+ ],
2747
+ isError: true,
2748
+ };
2749
+ }
2750
+ const res = await client.createMindInvite(mind_username, email, role);
2751
+ return {
2752
+ content: [
2753
+ {
2754
+ type: "text",
2755
+ text: `✅ Invitation sent to ${res.email} (${res.role}) for @${mind_username}.`,
2756
+ },
2757
+ ],
2758
+ };
2759
+ }
2760
+ }
2761
+ }
2762
+ catch (err) {
2763
+ return {
2764
+ content: [{ type: "text", text: `mind_accounts error: ${err}` }],
2765
+ isError: true,
2766
+ };
2767
+ }
2768
+ });
1915
2769
  return server;
1916
2770
  }
1917
2771
  //# sourceMappingURL=server.js.map