@astramindapp/mcp-server 0.12.0 → 0.13.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.
package/dist/server.js CHANGED
@@ -1163,18 +1163,22 @@ function createMindMcpServer(client) {
1163
1163
  // ─── mind_admin ─────────────────────────────────────────
1164
1164
  // Admin-only operations. Requires an admin API key.
1165
1165
  // Create users, manage featured minds, list/update users.
1166
- server.tool("mind_admin", "Admin-only tool for managing MIND users and featured minds. Requires an admin API key. Use to create new user accounts, provision featured minds, list users, and manage the featured minds catalog.", {
1166
+ server.tool("mind_admin", "Admin-only tool for managing MIND users and the Featured Minds Portal. Requires an admin API key. Full CRUD over featured minds (create / list / get_full / update / update_owner_profile / reorder / delete), plus user provisioning and tier/credit management. The Featured Minds Portal at /admin/featuredmindsportal uses these same endpoints — get_featured_mind_full + update_featured_mind_owner_profile let you edit the linked user's LLM model, public chat prompt, temperature, reasoning effort, brand fields, and avatar/banner from any MCP client.", {
1167
1167
  action: zod_1.z
1168
1168
  .enum([
1169
1169
  "create_user",
1170
1170
  "create_featured_mind",
1171
1171
  "list_featured_minds",
1172
+ "get_featured_mind_full",
1172
1173
  "update_featured_mind",
1174
+ "update_featured_mind_owner_profile",
1175
+ "reorder_featured_minds",
1176
+ "delete_featured_mind",
1173
1177
  "list_users",
1174
1178
  "update_user_tier",
1175
1179
  "adjust_user_credits",
1176
1180
  ])
1177
- .describe("Admin action: create_user, create_featured_mind, list_featured_minds, update_featured_mind, list_users, update_user_tier, adjust_user_credits"),
1181
+ .describe("Admin action. Featured Minds Portal actions: create_featured_mind, list_featured_minds, get_featured_mind_full (bundled view + owner profile + model catalog), update_featured_mind (catalog fields: title/description/tags/featured/display_order/is_public/avatar_url/banner_url/subtitle/price), update_featured_mind_owner_profile (write-through to linked user_profiles: preferred_llm_model, public_mind_prompt, chat_temperature, chat_reasoning_effort, public_mind_enabled, public_mind_tagline/greeting/persona, bio, avatar_url, banner_url), reorder_featured_minds (bulk display_order via ordered_mind_ids list), delete_featured_mind. User actions: create_user, list_users, update_user_tier, adjust_user_credits."),
1178
1182
  // create_user fields
1179
1183
  username: zod_1.z.string().optional().describe("Username for new user (3-30 chars, letters/numbers/underscores)"),
1180
1184
  email: zod_1.z.string().optional().describe("Email for new user"),
@@ -1182,23 +1186,53 @@ function createMindMcpServer(client) {
1182
1186
  source: zod_1.z.string().optional().describe("Source app/partner that created this user (e.g. 'myapp')"),
1183
1187
  generate_api_key: zod_1.z.boolean().optional().describe("If true, also generate a developer API key (mind_...) for the new user"),
1184
1188
  api_key_name: zod_1.z.string().optional().describe("Name for the generated API key (defaults to '{source} integration')"),
1185
- // featured mind fields
1186
- mind_id: zod_1.z.string().optional().describe("Featured mind ID for update operations"),
1189
+ // featured mind catalog fields
1190
+ mind_id: zod_1.z.string().optional().describe("Featured mind ID — required for get_featured_mind_full / update_featured_mind / update_featured_mind_owner_profile / delete_featured_mind"),
1187
1191
  title: zod_1.z.string().optional().describe("Display title for the featured mind"),
1188
- description: zod_1.z.string().optional().describe("Description of the featured mind"),
1192
+ subtitle: zod_1.z.string().optional().describe("One-line hook shown under the title on portal cards (≤140 chars)"),
1193
+ description: zod_1.z.string().optional().describe("Long-form description shown on the public profile"),
1189
1194
  tags: zod_1.z.array(zod_1.z.string()).optional().describe("Tags for the featured mind"),
1190
1195
  price: zod_1.z.number().optional().describe("Subscription price (0 = free)"),
1191
- featured: zod_1.z.boolean().optional().describe("Whether to show in featured section"),
1192
- display_order: zod_1.z.number().optional().describe("Sort order in featured list"),
1193
- is_public: zod_1.z.boolean().optional().describe("If true, anyone can query this mind"),
1194
- avatar_url: zod_1.z.string().optional().describe("Avatar image URL"),
1196
+ featured: zod_1.z.boolean().optional().describe("Whether to show in the featured grid on /discover"),
1197
+ display_order: zod_1.z.number().optional().describe("Sort order in featured list (lower = earlier)"),
1198
+ is_public: zod_1.z.boolean().optional().describe("If true, anyone can query this mind at normal credit rate (marketplace public)"),
1199
+ avatar_url: zod_1.z.string().optional().describe("Avatar image URL (S3-hosted). Writes-through to user_profiles when used with update_featured_mind_owner_profile."),
1200
+ banner_url: zod_1.z.string().optional().describe("Banner image URL (S3-hosted). Writes-through to user_profiles when used with update_featured_mind_owner_profile."),
1201
+ // owner_profile fields (write-through to user_profiles)
1202
+ preferred_llm_model: zod_1.z
1203
+ .union([zod_1.z.string(), zod_1.z.null()])
1204
+ .optional()
1205
+ .describe("LLM model id (e.g. 'openai/gpt-4o', 'anthropic/claude-sonnet-4.6'). Used by the public chat at /m/{username}. Pass null to clear and fall back to platform default. Use mind_admin with action=list_featured_minds then get_featured_mind_full to see available_models."),
1206
+ public_mind_enabled: zod_1.z.boolean().optional().describe("Whether anonymous visitors can chat with this MIND at /m/{username}"),
1207
+ public_mind_prompt: zod_1.z
1208
+ .string()
1209
+ .max(3000)
1210
+ .optional()
1211
+ .describe("System prompt used by the public chat (≤3000 chars). Empty string clears it and the auto-built default is used."),
1212
+ public_mind_tagline: zod_1.z.string().max(160).optional().describe("Public landing tagline (≤160 chars)"),
1213
+ public_mind_greeting: zod_1.z.string().max(500).optional().describe("Public landing greeting message (≤500 chars)"),
1214
+ public_mind_persona: zod_1.z.string().max(800).optional().describe("Persona description used inside the auto-built default prompt (≤800 chars)"),
1215
+ chat_temperature: zod_1.z
1216
+ .union([zod_1.z.number().min(0).max(2), zod_1.z.null()])
1217
+ .optional()
1218
+ .describe("Per-MIND temperature override (0.0–2.0). Null clears to provider default."),
1219
+ chat_reasoning_effort: zod_1.z
1220
+ .union([zod_1.z.enum(["minimal", "low", "medium", "high"]), zod_1.z.null()])
1221
+ .optional()
1222
+ .describe("Per-MIND reasoning_effort override. Only honored for thinking-capable models (supports_reasoning_effort=true)."),
1223
+ bio: zod_1.z.string().max(500).optional().describe("Owner bio (shown across MIND profile surfaces, ≤500 chars)"),
1224
+ // reorder
1225
+ ordered_mind_ids: zod_1.z
1226
+ .array(zod_1.z.string())
1227
+ .optional()
1228
+ .describe("For reorder_featured_minds: full list of mind_ids in the order you want. Index in the list becomes display_order."),
1195
1229
  // user management fields
1196
1230
  tier: zod_1.z.string().optional().describe("Subscription tier: free, pro, enterprise"),
1197
1231
  credits: zod_1.z.number().optional().describe("Credits to add (positive) or deduct (negative)"),
1198
1232
  // list fields
1199
1233
  query: zod_1.z.string().optional().describe("Search query for list_users"),
1200
1234
  page: zod_1.z.number().optional().describe("Page number for paginated results"),
1201
- }, async ({ action, username, email, password, source, generate_api_key, api_key_name, mind_id, title, description, tags, price, featured, display_order, is_public, avatar_url, tier, credits, query, page }) => {
1235
+ }, async ({ action, username, email, password, source, generate_api_key, api_key_name, mind_id, title, subtitle, description, tags, price, featured, display_order, is_public, avatar_url, banner_url, preferred_llm_model, public_mind_enabled, public_mind_prompt, public_mind_tagline, public_mind_greeting, public_mind_persona, chat_temperature, chat_reasoning_effort, bio, ordered_mind_ids, tier, credits, query, page }) => {
1202
1236
  try {
1203
1237
  switch (action) {
1204
1238
  case "create_user": {
@@ -1262,6 +1296,8 @@ function createMindMcpServer(client) {
1262
1296
  const patch = {};
1263
1297
  if (title !== undefined)
1264
1298
  patch.title = title;
1299
+ if (subtitle !== undefined)
1300
+ patch.subtitle = subtitle;
1265
1301
  if (description !== undefined)
1266
1302
  patch.description = description;
1267
1303
  if (tags !== undefined)
@@ -1276,11 +1312,131 @@ function createMindMcpServer(client) {
1276
1312
  patch.is_public = is_public;
1277
1313
  if (avatar_url !== undefined)
1278
1314
  patch.avatar_url = avatar_url;
1315
+ if (banner_url !== undefined)
1316
+ patch.banner_url = banner_url;
1279
1317
  const result = await client.adminUpdateFeaturedMind(mind_id, patch);
1280
1318
  return {
1281
1319
  content: [{ type: "text", text: `✅ Updated featured mind: "${result.title}"` }],
1282
1320
  };
1283
1321
  }
1322
+ case "get_featured_mind_full": {
1323
+ if (!mind_id) {
1324
+ return {
1325
+ content: [{ type: "text", text: "Error: mind_id is required for get_featured_mind_full" }],
1326
+ isError: true,
1327
+ };
1328
+ }
1329
+ const full = await client.adminGetFeaturedMindFull(mind_id);
1330
+ const op = full.owner_profile;
1331
+ const fm = full.featured_mind;
1332
+ const modelLines = full.available_models
1333
+ .slice(0, 8)
1334
+ .map((m) => ` • ${m.id} — ${m.name} (${m.provider})`)
1335
+ .join("\n");
1336
+ const more = full.available_models.length > 8 ? ` …and ${full.available_models.length - 8} more` : "";
1337
+ const ownerLines = [
1338
+ `@${op.username}`,
1339
+ ` preferred_llm_model: ${op.preferred_llm_model ?? "(platform default)"}`,
1340
+ ` public_mind_enabled: ${op.public_mind_enabled}`,
1341
+ ` public_mind_prompt: ${op.public_mind_prompt ? `${op.public_mind_prompt.slice(0, 80)}${op.public_mind_prompt.length > 80 ? "…" : ""}` : "(default)"}`,
1342
+ ` tagline: ${op.public_mind_tagline || "—"}`,
1343
+ ` greeting: ${op.public_mind_greeting ? op.public_mind_greeting.slice(0, 60) + (op.public_mind_greeting.length > 60 ? "…" : "") : "—"}`,
1344
+ ` persona: ${op.public_mind_persona ? op.public_mind_persona.slice(0, 60) + (op.public_mind_persona.length > 60 ? "…" : "") : "—"}`,
1345
+ ` temperature: ${op.chat_temperature ?? "(default)"}`,
1346
+ ` reasoning_effort: ${op.chat_reasoning_effort ?? "(default)"}`,
1347
+ ` avatar_url: ${op.avatar_url ? "✓ set" : "—"}`,
1348
+ ` banner_url: ${op.banner_url ? "✓ set" : "—"}`,
1349
+ ].join("\n");
1350
+ return {
1351
+ content: [
1352
+ {
1353
+ type: "text",
1354
+ text: `Featured mind: "${fm.title}" (id: ${fm.mind_id}, order: ${fm.display_order}, featured: ${fm.featured}, public: ${fm.is_public ?? false})\n\nOwner profile (write-through target):\n${ownerLines}\n\nAvailable models (${full.available_models.length} total):\n${modelLines}${more ? "\n" + more : ""}`,
1355
+ },
1356
+ ],
1357
+ };
1358
+ }
1359
+ case "update_featured_mind_owner_profile": {
1360
+ if (!mind_id) {
1361
+ return {
1362
+ content: [{ type: "text", text: "Error: mind_id is required for update_featured_mind_owner_profile" }],
1363
+ isError: true,
1364
+ };
1365
+ }
1366
+ const patch = {};
1367
+ if (preferred_llm_model !== undefined)
1368
+ patch.preferred_llm_model = preferred_llm_model;
1369
+ if (public_mind_enabled !== undefined)
1370
+ patch.public_mind_enabled = public_mind_enabled;
1371
+ if (public_mind_prompt !== undefined)
1372
+ patch.public_mind_prompt = public_mind_prompt;
1373
+ if (public_mind_tagline !== undefined)
1374
+ patch.public_mind_tagline = public_mind_tagline;
1375
+ if (public_mind_greeting !== undefined)
1376
+ patch.public_mind_greeting = public_mind_greeting;
1377
+ if (public_mind_persona !== undefined)
1378
+ patch.public_mind_persona = public_mind_persona;
1379
+ if (chat_temperature !== undefined)
1380
+ patch.chat_temperature = chat_temperature;
1381
+ if (chat_reasoning_effort !== undefined)
1382
+ patch.chat_reasoning_effort = chat_reasoning_effort;
1383
+ if (bio !== undefined)
1384
+ patch.bio = bio;
1385
+ if (avatar_url !== undefined)
1386
+ patch.avatar_url = avatar_url;
1387
+ if (banner_url !== undefined)
1388
+ patch.banner_url = banner_url;
1389
+ if (Object.keys(patch).length === 0) {
1390
+ return {
1391
+ content: [{ type: "text", text: "Nothing to update — pass at least one owner_profile field." }],
1392
+ isError: true,
1393
+ };
1394
+ }
1395
+ const result = await client.adminUpdateFeaturedMindOwnerProfile(mind_id, patch);
1396
+ const changed = Object.keys(patch).join(", ");
1397
+ return {
1398
+ content: [
1399
+ {
1400
+ type: "text",
1401
+ text: `✅ Owner-profile write-through for @${result.username}: ${changed}`,
1402
+ },
1403
+ ],
1404
+ };
1405
+ }
1406
+ case "reorder_featured_minds": {
1407
+ if (!ordered_mind_ids || ordered_mind_ids.length === 0) {
1408
+ return {
1409
+ content: [{ type: "text", text: "Error: ordered_mind_ids (full ordered list) is required" }],
1410
+ isError: true,
1411
+ };
1412
+ }
1413
+ const result = await client.adminReorderFeaturedMinds(ordered_mind_ids);
1414
+ return {
1415
+ content: [
1416
+ {
1417
+ type: "text",
1418
+ text: `✅ Reordered ${result.total} featured mind(s) — ${result.updated} display_order value(s) changed`,
1419
+ },
1420
+ ],
1421
+ };
1422
+ }
1423
+ case "delete_featured_mind": {
1424
+ if (!mind_id) {
1425
+ return {
1426
+ content: [{ type: "text", text: "Error: mind_id is required for delete_featured_mind" }],
1427
+ isError: true,
1428
+ };
1429
+ }
1430
+ const result = await client.adminDeleteFeaturedMind(mind_id);
1431
+ return {
1432
+ content: [
1433
+ {
1434
+ type: "text",
1435
+ text: `✅ Featured mind removed (${result.status}). The user's underlying MIND is not deleted.`,
1436
+ },
1437
+ ],
1438
+ };
1439
+ }
1284
1440
  case "list_users": {
1285
1441
  const result = await client.adminListUsers({ q: query, page: page ?? 1 });
1286
1442
  const users = result.users ?? [];
@@ -2364,8 +2520,12 @@ function createMindMcpServer(client) {
2364
2520
  "link_invoice",
2365
2521
  "unlink_invoice",
2366
2522
  "create_invoice",
2523
+ "list_workflows",
2524
+ "link_workflow",
2525
+ "unlink_workflow",
2526
+ "used_by",
2367
2527
  ])
2368
- .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), transfer_owner (move the agent to another account's board — needs owner_username), share (grant another account access — needs grantee_username + share_role), list_shares (who the agent is shared with), revoke_share (drop a share grant — needs share_id), 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)"),
2528
+ .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), transfer_owner (move the agent to another account's board — needs owner_username), share (grant another account access — needs grantee_username + share_role), list_shares (who the agent is shared with), revoke_share (drop a share grant — needs share_id), 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), list_workflows (workflows linked to an agent), link_workflow (attach an existing workflow slug — needs workflow_slug), unlink_workflow (detach a workflow — needs workflow_slug), used_by (inverse view: which agents call this workflow)"),
2369
2529
  slug: zod_1.z.string().optional().describe("Agent slug (lowercase, dashes). Required for get/update/delete/heartbeat/probe/activities/set_*/transfer_owner/share/list_shares/revoke_share."),
2370
2530
  // ownership / sharing
2371
2531
  owner_username: zod_1.z.string().optional().describe("MIND account username to transfer the agent to (action=transfer_owner). The account must already exist."),
@@ -2432,8 +2592,29 @@ function createMindMcpServer(client) {
2432
2592
  unit_price: zod_1.z.number().optional().describe("Line-item unit price for create_invoice (default 0)."),
2433
2593
  currency: zod_1.z.string().optional().describe("Invoice currency for create_invoice (default USD)."),
2434
2594
  invoice_notes: zod_1.z.string().optional().describe("Notes shown on the invoice for create_invoice."),
2595
+ // workflow linking + kind discriminator
2596
+ kind: zod_1.z
2597
+ .enum(["agent", "workflow"])
2598
+ .optional()
2599
+ .describe("Registry kind. Pass on create to scaffold a Workflow card instead of an Agent. Pass on action=list (via list_kind) to filter the board."),
2600
+ list_kind: zod_1.z
2601
+ .enum(["agent", "workflow"])
2602
+ .optional()
2603
+ .describe("Filter the board by kind when listing — 'agent' or 'workflow'. Omit to return both."),
2604
+ workflow_slug: zod_1.z.string().optional().describe("Workflow slug — required for link_workflow / unlink_workflow."),
2605
+ trigger_summary: zod_1.z.string().optional().describe("One-line description of what fires this workflow (kind='workflow' only)."),
2606
+ inputs_summary: zod_1.z.string().optional().describe("One-line description of the data this workflow consumes."),
2607
+ outputs_summary: zod_1.z.string().optional().describe("One-line description of the artifacts this workflow produces."),
2608
+ credentials_required: zod_1.z
2609
+ .array(zod_1.z.string())
2610
+ .optional()
2611
+ .describe("Named credentials this workflow touches (e.g. ['OpenAI API key', 'Resend API key', 'MIND tenant key'])."),
2612
+ steps_json: zod_1.z
2613
+ .string()
2614
+ .optional()
2615
+ .describe("JSON-encoded array of WorkflowStep dicts (kind='workflow' only). Each step: {order, name, description, kind, credentials[], inputs[], outputs[], notes}. Step kind ∈ {trigger, action, ai, branch, loop, wait, notify, output}."),
2435
2616
  }, async (args) => {
2436
- 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, owner_username, grantee_username, share_role, share_id, 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, list_query, 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;
2617
+ 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, owner_username, grantee_username, share_role, share_id, 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, list_query, include_archived, limit, overwrite, invoice_id, bill_to_name, bill_to_company, bill_to_email, line_item_description, quantity, unit_price, currency, invoice_notes, kind, list_kind, workflow_slug, trigger_summary, inputs_summary, outputs_summary, credentials_required, steps_json, } = args;
2437
2618
  const requireSlug = (label) => {
2438
2619
  if (!slug) {
2439
2620
  return `Error: 'slug' is required for action=${label}.`;
@@ -2482,6 +2663,29 @@ function createMindMcpServer(client) {
2482
2663
  requires_approval: authority_requires_approval ?? [],
2483
2664
  };
2484
2665
  }
2666
+ // Workflow-specific create/update fields. `kind` defaults server-side
2667
+ // to "agent" so it's only sent when the caller passes it explicitly.
2668
+ if (kind !== undefined)
2669
+ payload.kind = kind;
2670
+ if (trigger_summary !== undefined)
2671
+ payload.trigger_summary = trigger_summary;
2672
+ if (inputs_summary !== undefined)
2673
+ payload.inputs_summary = inputs_summary;
2674
+ if (outputs_summary !== undefined)
2675
+ payload.outputs_summary = outputs_summary;
2676
+ if (credentials_required !== undefined)
2677
+ payload.credentials_required = credentials_required;
2678
+ if (steps_json !== undefined) {
2679
+ try {
2680
+ const parsed = JSON.parse(steps_json);
2681
+ if (Array.isArray(parsed))
2682
+ payload.steps = parsed;
2683
+ }
2684
+ catch {
2685
+ // Surface a clear error rather than silently sending invalid JSON.
2686
+ throw new Error("steps_json must be a JSON-encoded array of WorkflowStep dicts.");
2687
+ }
2688
+ }
2485
2689
  return payload;
2486
2690
  };
2487
2691
  try {
@@ -2491,17 +2695,23 @@ function createMindMcpServer(client) {
2491
2695
  status: list_status,
2492
2696
  host: list_host,
2493
2697
  tag: list_tag,
2698
+ kind: list_kind,
2494
2699
  q: list_query,
2495
2700
  include_archived,
2496
2701
  });
2497
2702
  const lines = [];
2498
2703
  const s = result.stats;
2499
- 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`, "");
2704
+ const get = (k) => s[k] ?? 0;
2705
+ const kindLine = s.agents !== undefined || s.workflows !== undefined
2706
+ ? `Kind: ${get("agents")} agents · ${get("workflows")} workflows`
2707
+ : "";
2708
+ lines.push(`Registry: ${get("total")} total · ${get("running")} running · ${get("planned")} planned · ${get("paused")} paused · ${get("error")} errored`, `Live: ${get("online")} online · ${get("stale")} stale · ${get("offline")} offline`, kindLine, "");
2500
2709
  for (const a of result.agents) {
2501
2710
  const host = a.host ?? "—";
2502
- lines.push(`• [${a.status.padEnd(8)}] ${a.slug.padEnd(28)} ${a.live_status.padEnd(7)} host=${host} owner=${a.owner_username ?? "—"} — ${a.name}`);
2711
+ const kindIcon = a.kind === "workflow" ? "⚙" : "🧠";
2712
+ lines.push(`• ${kindIcon} [${a.status.padEnd(8)}] ${a.slug.padEnd(28)} ${a.live_status.padEnd(7)} host=${host} owner=${a.owner_username ?? "—"} — ${a.name}`);
2503
2713
  }
2504
- return { content: [{ type: "text", text: lines.join("\n") }] };
2714
+ return { content: [{ type: "text", text: lines.filter(Boolean).join("\n") }] };
2505
2715
  }
2506
2716
  case "get": {
2507
2717
  const err = requireSlug("get");
@@ -2873,6 +3083,86 @@ function createMindMcpServer(client) {
2873
3083
  ],
2874
3084
  };
2875
3085
  }
3086
+ case "list_workflows": {
3087
+ const err = requireSlug("list_workflows");
3088
+ if (err)
3089
+ return { content: [{ type: "text", text: err }], isError: true };
3090
+ const result = await client.listAgentWorkflows(slug);
3091
+ if (!result.workflows.length) {
3092
+ return { content: [{ type: "text", text: `No workflows linked to ${slug}.` }] };
3093
+ }
3094
+ const lines = result.workflows.map((w) => ` ⚙ ${w.slug.padEnd(28)} steps=${String(w.step_count).padStart(2)} · ${w.trigger_summary ?? "(no trigger summary)"} — ${w.name}`);
3095
+ return {
3096
+ content: [
3097
+ { type: "text", text: `Workflows linked to ${slug}:\n${lines.join("\n")}` },
3098
+ ],
3099
+ };
3100
+ }
3101
+ case "link_workflow": {
3102
+ const err = requireSlug("link_workflow");
3103
+ if (err)
3104
+ return { content: [{ type: "text", text: err }], isError: true };
3105
+ if (!workflow_slug) {
3106
+ return {
3107
+ content: [{ type: "text", text: "Error: 'workflow_slug' is required for link_workflow." }],
3108
+ isError: true,
3109
+ };
3110
+ }
3111
+ const updated = await client.linkAgentWorkflow(slug, workflow_slug);
3112
+ return {
3113
+ content: [
3114
+ {
3115
+ type: "text",
3116
+ text: `🔗 Linked workflow ${workflow_slug} to ${updated.slug} (${updated.linked_workflows.length} total).`,
3117
+ },
3118
+ ],
3119
+ };
3120
+ }
3121
+ case "unlink_workflow": {
3122
+ const err = requireSlug("unlink_workflow");
3123
+ if (err)
3124
+ return { content: [{ type: "text", text: err }], isError: true };
3125
+ if (!workflow_slug) {
3126
+ return {
3127
+ content: [{ type: "text", text: "Error: 'workflow_slug' is required for unlink_workflow." }],
3128
+ isError: true,
3129
+ };
3130
+ }
3131
+ const updated = await client.unlinkAgentWorkflow(slug, workflow_slug);
3132
+ return {
3133
+ content: [
3134
+ {
3135
+ type: "text",
3136
+ text: `✂️ Unlinked workflow ${workflow_slug} from ${updated.slug} (${updated.linked_workflows.length} remain).`,
3137
+ },
3138
+ ],
3139
+ };
3140
+ }
3141
+ case "used_by": {
3142
+ const err = requireSlug("used_by");
3143
+ if (err)
3144
+ return { content: [{ type: "text", text: err }], isError: true };
3145
+ const result = await client.workflowUsedBy(slug);
3146
+ if (!result.agents.length) {
3147
+ return {
3148
+ content: [
3149
+ {
3150
+ type: "text",
3151
+ text: `${result.kind === "workflow" ? "Workflow" : "Agent"} ${result.slug} is not linked from any agent yet.`,
3152
+ },
3153
+ ],
3154
+ };
3155
+ }
3156
+ const lines = result.agents.map((a) => ` 🧠 ${a.slug.padEnd(28)} host=${a.host ?? "—"} owner=${a.owner_username ?? "—"} — ${a.name}`);
3157
+ return {
3158
+ content: [
3159
+ {
3160
+ type: "text",
3161
+ text: `${result.name} is linked from ${result.agents.length} agent(s):\n${lines.join("\n")}`,
3162
+ },
3163
+ ],
3164
+ };
3165
+ }
2876
3166
  }
2877
3167
  }
2878
3168
  catch (err) {