@astramindapp/mcp-server 0.25.0 → 0.27.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
@@ -12,13 +12,15 @@
12
12
  * integration path to use for its runtime, how to authenticate, and the
13
13
  * session protocol — without having to be told.
14
14
  *
15
- * Tools (44):
15
+ * Tools (45):
16
16
  * Memory & knowledge — mind_query, mind_remember, mind_folders,
17
17
  * mind_folder_routes, mind_folder_suggest, mind_share (document share
18
18
  * links), mind_context, mind_graph, mind_insights, mind_research,
19
19
  * mind_train
20
20
  * Life & work — mind_life, mind_focuses, mind_tasks, mind_automate,
21
21
  * mind_notify, mind_checklists (Kanon checklists)
22
+ * Agent sessions — mind_sessions (log this session into MIND Chat →
23
+ * Agents; see the AGENT SESSION PROTOCOL in ./integration-guide)
22
24
  * People — mind_crm
23
25
  * Social & profile — mind_social, mind_social_analytics, mind_profile,
24
26
  * mind_personas
@@ -84,7 +86,7 @@ const apiDetail = (e) => {
84
86
  function createMindMcpServer(client) {
85
87
  const server = new mcp_js_1.McpServer({
86
88
  name: "mind",
87
- version: "0.25.0",
89
+ version: "0.27.0",
88
90
  }, {
89
91
  // Returned to every client in the MCP `initialize` response — the first
90
92
  // thing any connecting agent sees about how to integrate with MIND.
@@ -94,7 +96,7 @@ function createMindMcpServer(client) {
94
96
  // The full integration playbook, readable on demand by any MCP client.
95
97
  server.registerResource("integration-guide", "mind://integration-guide", {
96
98
  title: "MIND Integration Guide",
97
- 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 44 tools, REST endpoint mapping, and common recipes.",
99
+ 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 45 tools, REST endpoint mapping, and common recipes.",
98
100
  mimeType: "text/markdown",
99
101
  }, async (uri) => ({
100
102
  contents: [
@@ -105,10 +107,105 @@ function createMindMcpServer(client) {
105
107
  },
106
108
  ],
107
109
  }));
110
+ // ─── mind://agent-standard ──────────────────────────────
111
+ // The full MIND Agent Operating Standard (AGENTS.md, verbatim — see
112
+ // AGENT_STANDARD_MD in ./integration-guide.ts for provenance and the
113
+ // refresh recipe). SERVER_INSTRUCTIONS names this resource as mandatory
114
+ // reading in its "═══ MANDATORY PROTOCOLS ═══" section so an unauthenticated
115
+ // client sees the pointer even before it can read this resource.
116
+ server.registerResource("agent-standard", "mind://agent-standard", {
117
+ title: "The MIND Agent Operating Standard",
118
+ description: "The full, binding operating constitution for a MIND-connected agent (AGENTS.md, verbatim) — Law Zero, the boot protocol, session sync, the inactivity and termination protocols, the action-time gates, the sense catalog, and the memory contract. SERVER_INSTRUCTIONS names this as mandatory reading; this resource is the complete text.",
119
+ mimeType: "text/markdown",
120
+ }, async (uri) => ({
121
+ contents: [
122
+ {
123
+ uri: uri.href,
124
+ mimeType: "text/markdown",
125
+ text: integration_guide_js_1.AGENT_STANDARD_MD,
126
+ },
127
+ ],
128
+ }));
129
+ // ─── sync-local-docs-to-mind prompt ─────────────────────
130
+ // Standard prompt shipped WITH the server so any connected agent can back
131
+ // up the user's durable local documents into MIND without being taught
132
+ // the procedure from scratch. Body lives in integration-guide.ts beside
133
+ // SERVER_INSTRUCTIONS and INTEGRATION_GUIDE so all shipped copy is in one
134
+ // place. MCP prompt arguments are always strings.
135
+ server.registerPrompt("sync-local-docs-to-mind", {
136
+ title: "Sync local documents into MIND",
137
+ description: "Back up every durable document on this machine into the user's MIND, skipping code, junk and secrets. Safe by default: reports what it would upload before uploading anything.",
138
+ argsSchema: {
139
+ root: zod_1.z.string().optional().describe("Directory to scan. Default: the current working directory."),
140
+ dry_run: zod_1.z
141
+ .string()
142
+ .optional()
143
+ .describe('"true" (default) reports the plan without writing. "false" performs the upload.'),
144
+ since: zod_1.z.string().optional().describe("Only consider files modified after this ISO date."),
145
+ },
146
+ }, async ({ root, dry_run, since }) => ({
147
+ messages: [
148
+ {
149
+ role: "user",
150
+ content: {
151
+ type: "text",
152
+ text: (0, integration_guide_js_1.buildSyncLocalDocsPrompt)({ root, dry_run, since }),
153
+ },
154
+ },
155
+ ],
156
+ }));
157
+ // ─── sync-agent-session prompt ───────────────────────────
158
+ // Standard prompt shipped WITH the server so any connected agent can adopt
159
+ // the AGENT SESSION PROTOCOL (see integration-guide.ts) without being
160
+ // taught the procedure from scratch. Body lives in integration-guide.ts
161
+ // beside SERVER_INSTRUCTIONS and buildSyncLocalDocsPrompt so all shipped
162
+ // copy is in one place — see AGENT_SESSION_PROTOCOL_BODY there, which is
163
+ // also embedded verbatim in SERVER_INSTRUCTIONS and (compact) in the
164
+ // mind_sessions tool description below, so a client that only reads tool
165
+ // descriptions still follows it.
166
+ server.registerPrompt("sync-agent-session", {
167
+ title: "Adopt the Agent Session Protocol",
168
+ description: "Log this agent's live session into MIND Chat → Agents so Anthony can read and reply to it. Returns the full open/append/close/handoff protocol, addressed to your runtime.",
169
+ argsSchema: {
170
+ runtime: zod_1.z.string().optional().describe('Your runtime name, e.g. "claude-code", "codex", "cursor". Default: "your runtime".'),
171
+ source_key: zod_1.z.string().optional().describe('The MIND Chat sidebar toggle you were assigned, e.g. "claude-code-1". Default: "your-source-key".'),
172
+ },
173
+ }, async ({ runtime, source_key }) => ({
174
+ messages: [
175
+ {
176
+ role: "user",
177
+ content: {
178
+ type: "text",
179
+ text: (0, integration_guide_js_1.buildSyncAgentSessionPrompt)({ runtime, source_key }),
180
+ },
181
+ },
182
+ ],
183
+ }));
184
+ // ─── adopt-agent-standard prompt ─────────────────────────
185
+ // Same content as the mind://agent-standard resource (AGENT_STANDARD_MD —
186
+ // see integration-guide.ts for provenance and the refresh recipe), offered
187
+ // as a prompt for clients that call prompts/get rather than reading MCP
188
+ // resources. SERVER_INSTRUCTIONS points every connecting agent at whichever
189
+ // of the two ("the resource ... or the adopt-agent-standard prompt") its
190
+ // host actually supports.
191
+ server.registerPrompt("adopt-agent-standard", {
192
+ title: "Adopt the MIND Agent Operating Standard",
193
+ description: "Returns the full MIND Agent Operating Standard (AGENTS.md, verbatim) — Law Zero, the boot protocol, session sync, the inactivity and termination protocols, the action-time gates, the sense catalog, and the memory contract. Read this before acting on behalf of an owner for the first time.",
194
+ }, async () => ({
195
+ messages: [
196
+ {
197
+ role: "user",
198
+ content: {
199
+ type: "text",
200
+ text: integration_guide_js_1.AGENT_STANDARD_MD,
201
+ },
202
+ },
203
+ ],
204
+ }));
108
205
  // ─── mind_query ─────────────────────────────────────────
109
206
  // Semantic search across the user's knowledge graph.
110
207
  // Replaces flat MEMORY.md — returns only relevant memories.
111
- server.tool("mind_query", "Search your MIND knowledge graph. Returns RETRIEVED CONTEXT from the graph — documents, entries, entities and relationships — NOT a finished answer. YOU must read that context and write the answer yourself. Does not use MIND's LLM and costs 0 credits. Use this BEFORE making decisions — MIND is your memory. (Pass retrieve_only=false to have MIND synthesize the answer instead, which spends credits.)", {
208
+ server.tool("mind_query", "Search the user's MIND knowledge graph — their persistent memory of people, projects, decisions, outcomes and history. Returns RETRIEVED CONTEXT for YOU to read and synthesize — documents, entries, entities and relationships — NOT a finished answer. Does not use MIND's LLM and costs 0 credits. Call this before asserting anything about the user's world, and before saying something does not exist. (Pass retrieve_only=false to have MIND's own LLM write the answer instead, which spends credits.)\n\nHOW TO ASK — this changes answer quality more than any parameter. Ask a rich, specific, full-sentence question; \"what is X\" retrieves poorly. Best results come from retrieve-then-write in one ask: \"Find everything about X, Y and Z, then write <deliverable> in this shape: <template>. Use only what you found; mark anything missing as unknown and do not invent it.\" Name the thing you expect to find — a document title, a person, a project, a date range. If the first answer is thin, change the QUESTION, not the mode — chain a narrower ask.\n\nPICKING A MODE (mechanical): naive reads document text only — best for recalling an exact passage or wording. local walks a named entity and its immediate graph neighbours — best for one specific person, company or thing. global reasons over relationships and themes across the whole graph — best for cross-cutting patterns. hybrid combines local+global graph reasoning but reads no raw document text. mix adds raw document text on top of hybrid and is the most complete; reach for it when hybrid feels thin. hybrid is the default — drop to naive/local/global only when you specifically need that narrower lens.\n\nNEGATIVES ARE THE HIGHEST-RISK ANSWER. An empty or thin result means your query missed — it is never proof the thing does not exist. Before reporting that something is absent, re-ask with different wording and say what you searched.", {
112
209
  query: zod_1.z.string().describe("What to search for in your knowledge graph"),
113
210
  mode: zod_1.z
114
211
  .enum(["mix", "hybrid", "global", "local", "naive"])
@@ -167,7 +264,7 @@ function createMindMcpServer(client) {
167
264
  // ─── mind_remember ──────────────────────────────────────
168
265
  // Store/manage content in the knowledge graph. Auto-categorized by type.
169
266
  // Enhanced: now supports create, delete, search, get, list actions.
170
- server.tool("mind_remember", "Store and manage content in your MIND knowledge graph. Use for facts, decisions, learnings, research, notes — anything worth remembering. Auto-categorized. Always log outcomes here after completing tasks. Supports create (default), delete, search, get, and list.\n\n⚠️ PRIVATE vs PUBLIC: `document` and `entry` are PRIVATE to the user's knowledge graph. `feed_post` is a PUBLIC social-media post that appears on the user's public feed for everyone to see. Default to `entry` for all agent outcomes, logs, decisions, and research. NEVER use `feed_post` unless the user explicitly says \"post\", \"share\", \"tweet\", \"feed\", or \"thought to my feed\" — deploy logs, PR notes, work outcomes, and agent activity belong in `entry`, NOT on the public feed.\n\nTitle rule: lessons are titled 'Lesson - Failure - <behavior>' / 'Lesson - Behavior - <behavior>' / 'Lesson - Win - <behavior>'; never put '/' or parentheses in a title, the card is derived from it.", {
267
+ server.tool("mind_remember", "Store and manage content in your MIND knowledge graph. Use for facts, decisions, learnings, research, notes — anything worth remembering. Auto-categorized. Always log outcomes here after completing tasks. Supports create (default), delete, search, get, and list.\n\n⚠️ PRIVATE vs PUBLIC: `document` and `entry` are PRIVATE to the user's knowledge graph. `feed_post` is a PUBLIC social-media post that appears on the user's public feed for everyone to see. Default to `entry` for all agent outcomes, logs, decisions, and research. NEVER use `feed_post` unless the user explicitly says \"post\", \"share\", \"tweet\", \"feed\", or \"thought to my feed\" — deploy logs, PR notes, work outcomes, and agent activity belong in `entry`, NOT on the public feed.\n\nTitle rule: lessons are titled 'Lesson - Failure - <behavior>' / 'Lesson - Behavior - <behavior>' / 'Lesson - Win - <behavior>'; never put '/' or parentheses in a title, the card is derived from it.\n\nWRITE FOR THE AGENT WHO WILL SEARCH FOR THIS LATER — retrieval quality is decided here, not at query time. State facts in the words someone will later search for. Put the entity name in the first sentence. Inline dates, ids, file paths and numbers rather than saying \"the PR\" or \"yesterday\" — a note that only says \"fixed it\" is unfindable. If this supersedes an earlier note, say so explicitly and name what it replaces, so the older fact can be closed off instead of silently competing with this one.", {
171
268
  action: zod_1.z
172
269
  .enum(["create", "delete", "search", "get", "list"])
173
270
  .optional()
@@ -355,15 +452,28 @@ function createMindMcpServer(client) {
355
452
  // ─── mind_folders ───────────────────────────────────────
356
453
  // Organize documents into folders. Folders are a presentation layer — the
357
454
  // knowledge graph still indexes every document regardless of folder.
358
- server.tool("mind_folders", "Organize your MIND documents into folders. Folders are a presentation layer — the knowledge graph still indexes and retrieves across every document regardless of folder. Use mind_remember to create a document, then mind_folders move_documents to file it. Actions: list, create, rename, move, delete, move_documents, set_hint (set the agent-routing hint read by mind_folder_suggest).", {
455
+ server.tool("mind_folders", "Organize your MIND documents into folders. Folders are a presentation layer — the knowledge graph still indexes and retrieves across every document regardless of folder. Use mind_remember to create a document, then mind_folders move_documents to file it. Actions: list, create, rename, move, delete, move_documents, set_hint, secure, unsecure, unlock, reset_request, reset (Secure Folders — password-gate a folder).", {
359
456
  action: zod_1.z
360
- .enum(["list", "create", "rename", "move", "delete", "move_documents", "set_hint"])
361
- .describe("list (all folders + counts + routing hints), create (new folder; pass routing_hint to enable agent-routing), rename (change a folder's name), move (re-nest a folder), delete (remove a folder — its documents and subfolders move up a level, nothing is deleted), move_documents (file documents into a folder), set_hint (write/clear the routing_hint that mind_folder_suggest reads)"),
457
+ .enum([
458
+ "list",
459
+ "create",
460
+ "rename",
461
+ "move",
462
+ "delete",
463
+ "move_documents",
464
+ "set_hint",
465
+ "secure",
466
+ "unsecure",
467
+ "unlock",
468
+ "reset_request",
469
+ "reset",
470
+ ])
471
+ .describe("list (all folders + counts + routing hints + secure/locked flags), create (new folder; pass routing_hint to enable agent-routing), rename (change a folder's name), move (re-nest a folder), delete (remove a folder — its documents and subfolders move up a level, nothing is deleted), move_documents (file documents into a folder), set_hint (write/clear the routing_hint that mind_folder_suggest reads), secure (password-gate folder_id — pass passphrase; changing an existing passphrase needs an active unlock first), unsecure (turn off secure mode — needs an active unlock), unlock (verify passphrase, mints a 15-minute unlock kept in memory for this session and sent on every later call), reset_request (email a one-time reset link to the account email), reset (complete a reset — pass token + new_passphrase)"),
362
472
  name: zod_1.z.string().optional().describe("Folder name — required for create and rename"),
363
473
  folder_id: zod_1.z
364
474
  .string()
365
475
  .optional()
366
- .describe("Folder id — the folder to rename/move/delete/set_hint, or the destination for move_documents (omit or pass 'root' to file documents at the top level)"),
476
+ .describe("Folder id — the folder to rename/move/delete/set_hint/secure/unsecure/unlock/reset_request/reset, or the destination for move_documents (omit or pass 'root' to file documents at the top level)"),
367
477
  parent_id: zod_1.z
368
478
  .string()
369
479
  .optional()
@@ -376,7 +486,23 @@ function createMindMcpServer(client) {
376
486
  .string()
377
487
  .optional()
378
488
  .describe("Free-text instruction answering \"when should MIND save things to this folder?\". Read by mind_folder_suggest to LLM-route new content. Pass on create to set up-front; pass on set_hint to update. Pass empty string to clear (disables the folder from auto-routing)."),
379
- }, async ({ action, name, folder_id, parent_id, doc_ids, routing_hint }) => {
489
+ passphrase: zod_1.z
490
+ .string()
491
+ .optional()
492
+ .describe("Folder passphrase — required for secure and unlock"),
493
+ new_passphrase: zod_1.z
494
+ .string()
495
+ .optional()
496
+ .describe("New passphrase — required for reset"),
497
+ token: zod_1.z
498
+ .string()
499
+ .optional()
500
+ .describe("One-time reset token from the reset email — required for reset"),
501
+ unlock_token: zod_1.z
502
+ .string()
503
+ .optional()
504
+ .describe("Pass the exact `unlock_token` string returned by a prior 'unlock' call here to carry a 15-minute unlock into a later 'secure' (passphrase change) or 'unsecure' call. Not needed for the first time a folder is secured, and not used by list/create/rename/move/delete/move_documents/set_hint/unlock/reset_request/reset. This stdio server keeps unlock tokens in memory for the whole session and normally attaches them automatically (see 'unlock'), so passing this explicitly is usually redundant — but when supplied it takes precedence over the remembered token, so the same request shape works on both this server and the hosted MCP."),
505
+ }, async ({ action, name, folder_id, parent_id, doc_ids, routing_hint, passphrase, new_passphrase, token, unlock_token }) => {
380
506
  try {
381
507
  switch (action) {
382
508
  case "list": {
@@ -396,7 +522,8 @@ function createMindMcpServer(client) {
396
522
  const parent = f.parent_id ? byId.get(f.parent_id)?.name ?? "?" : "(top level)";
397
523
  const hint = (f.routing_hint || "").trim();
398
524
  const hintTail = hint ? ` · hint: ${hint.length > 80 ? hint.slice(0, 80) + "…" : hint}` : "";
399
- return `• ${f.name} — ${f.document_count ?? 0} doc(s) · id: ${f.id} · parent: ${parent}${hintTail}`;
525
+ const secureTail = f.secure ? (f.locked ? " · 🔒 secure (locked)" : " · 🔓 secure (unlocked)") : "";
526
+ return `• ${f.name} — ${f.document_count ?? 0} doc(s) · id: ${f.id} · parent: ${parent}${hintTail}${secureTail}`;
400
527
  });
401
528
  return {
402
529
  content: [
@@ -490,6 +617,73 @@ function createMindMcpServer(client) {
490
617
  ],
491
618
  };
492
619
  }
620
+ case "secure": {
621
+ if (!folder_id)
622
+ throw new Error("'folder_id' is required to secure a folder");
623
+ if (!passphrase)
624
+ throw new Error("'passphrase' is required to secure a folder");
625
+ const res = await client.secureFolder(folder_id, passphrase, unlock_token);
626
+ return {
627
+ content: [
628
+ { type: "text", text: `Folder ${res.folder_id} is now secure. Unlock it with action 'unlock' before reading its contents.` },
629
+ ],
630
+ };
631
+ }
632
+ case "unsecure": {
633
+ if (!folder_id)
634
+ throw new Error("'folder_id' is required to unsecure a folder");
635
+ const res = await client.unsecureFolder(folder_id, unlock_token);
636
+ client.clearSecureFolderUnlockToken(folder_id);
637
+ return {
638
+ content: [{ type: "text", text: `Folder ${res.folder_id} is no longer secure.` }],
639
+ };
640
+ }
641
+ case "unlock": {
642
+ if (!folder_id)
643
+ throw new Error("'folder_id' is required to unlock a folder");
644
+ if (!passphrase)
645
+ throw new Error("'passphrase' is required to unlock a folder");
646
+ const res = await client.unlockFolder(folder_id, passphrase);
647
+ client.setSecureFolderUnlockToken(folder_id, res.unlock_token);
648
+ return {
649
+ content: [
650
+ {
651
+ type: "text",
652
+ text: `Unlocked. Expires at ${res.expires_at} (15 minutes) — every mind_query / mind_folders / document call this session will see inside it until then.`,
653
+ },
654
+ ],
655
+ };
656
+ }
657
+ case "reset_request": {
658
+ if (!folder_id)
659
+ throw new Error("'folder_id' is required to request a passphrase reset");
660
+ const res = await client.requestFolderReset(folder_id);
661
+ return {
662
+ content: [
663
+ {
664
+ type: "text",
665
+ text: res.status === "sent"
666
+ ? `Reset email sent to the account's email address (expires in 15 minutes).`
667
+ : `Reset requested, but email delivery is not configured on this deployment.`,
668
+ },
669
+ ],
670
+ };
671
+ }
672
+ case "reset": {
673
+ if (!folder_id)
674
+ throw new Error("'folder_id' is required to reset a passphrase");
675
+ if (!token)
676
+ throw new Error("'token' (from the reset email) is required to reset a passphrase");
677
+ if (!new_passphrase)
678
+ throw new Error("'new_passphrase' is required to reset a passphrase");
679
+ const res = await client.resetFolder(folder_id, token, new_passphrase);
680
+ client.clearSecureFolderUnlockToken(folder_id);
681
+ return {
682
+ content: [
683
+ { type: "text", text: `Passphrase reset for folder ${res.folder_id}. Unlock it with the new passphrase.` },
684
+ ],
685
+ };
686
+ }
493
687
  default:
494
688
  throw new Error(`Unknown action: ${action}`);
495
689
  }
@@ -653,7 +847,7 @@ function createMindMcpServer(client) {
653
847
  // Get structured always-loaded context.
654
848
  // Equivalent to OpenClaw's SOUL.md + USER.md + AGENTS.md bootstrap injection,
655
849
  // but backed by the knowledge graph.
656
- server.tool("mind_context", "Get your persistent context from MIND — identity, preferences, rules, and recent activity. Returns RETRIEVED CONTEXT (not a finished briefing). YOU synthesize who you are and what matters. Does not use MIND's LLM and costs 0 credits. Call this at the start of every session.", {
850
+ server.tool("mind_context", "Load the user's persistent context from MIND at session start — identity, preferences, operating rules, current priorities and recent activity. Returns RETRIEVED CONTEXT for YOU to synthesize, NOT a finished briefing. Does not use MIND's LLM and costs 0 credits. Call this at the start of every session.\n\nPass only the `sections` you actually need for the task at hand — a focused payload measurably outperforms the full default set, because the model attends better to fewer, more relevant facts. Every fact carries a date; when two facts conflict, the newer one wins, and say so if you rely on it. An empty `recent` section means nothing was logged, not that nothing happened — follow up with mind_query before assuming a quiet day. This is a snapshot, not the whole graph; for anything specific, follow up with mind_query.", {
657
851
  sections: zod_1.z
658
852
  .array(zod_1.z.enum(["soul", "user", "rules", "priorities", "recent"]))
659
853
  .optional()
@@ -1230,7 +1424,7 @@ function createMindMcpServer(client) {
1230
1424
  // ─── mind_tasks ─────────────────────────────────────────
1231
1425
  // Site-wide tasks: assignable, completable work items that attach to a
1232
1426
  // Life project, a CRM contact, an agent, or stand alone.
1233
- 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.", {
1427
+ server.tool("mind_tasks", "Manage site-wide tasks — assignable, completable work items that are lighter-weight than a Life project/outcome and simpler than a Kanon checklist item. Reach for mind_tasks for a single actionable to-do (optionally attached to a Life project via parent_type=life_item, a CRM contact via parent_type=contact, or an agent) that needs an owner and a done/not-done state; reach for mind_life when the work is itself a deliverable inside the Focus→Project→Outcome hierarchy, and mind_checklists when you need a multi-item checklist with a progress rollup. Assign tasks to a MIND member, an agent, or an external email. Actions: list/create/get/update/complete/reopen/assign/delete, reports (completion analytics). An empty list means no tasks exist under that filter, not that no work is happening — check mind_life and mind_checklists for the same window before concluding nothing is tracked.", {
1234
1428
  action: zod_1.z
1235
1429
  .enum([
1236
1430
  "list", "create", "get", "update", "complete", "reopen",
@@ -1436,7 +1630,7 @@ function createMindMcpServer(client) {
1436
1630
  // ─── mind_crm ───────────────────────────────────────────
1437
1631
  // Read/write contacts, interactions, and activities.
1438
1632
  // Enhanced: delete, get, log_activity, list_activities.
1439
- server.tool("mind_crm", "Manage contacts and relationships in MIND CRM. Use for tracking people, companies, leads, interaction history, and activity logging.", {
1633
+ server.tool("mind_crm", "Manage contacts and relationships in MIND CRM — the system of record for people, companies, leads and prospects, separate from the knowledge graph's document/entry memory. Reach for this (not mind_query) when you need to create, update, or list a specific contact record, or log/read a dated interaction history — reach for mind_query when you want a synthesized answer about a relationship drawn from everything MIND knows. Actions: list/create/update/delete/get contacts, log_activity (record an interaction), list_activities (interaction history). An empty list or 'No contacts found' means no CRM records exist yet, not that the person is unknown to MIND — check mind_query before concluding a relationship is undocumented.", {
1440
1634
  action: zod_1.z
1441
1635
  .enum(["list", "create", "update", "delete", "get", "log_activity", "list_activities"])
1442
1636
  .describe("What to do: list/create/update/delete/get contacts, log_activity (record interaction), list_activities (view interaction history)"),
@@ -1588,7 +1782,7 @@ function createMindMcpServer(client) {
1588
1782
  // ─── mind_graph ─────────────────────────────────────────
1589
1783
  // Get graph statistics, diagnostics, and labels.
1590
1784
  // Enhanced: action param with stats (default), diagnostics, labels.
1591
- server.tool("mind_graph", "Get MIND knowledge graph statistics, diagnostics, and label details. Use to check graph health, growth, and entity breakdown.", {
1785
+ server.tool("mind_graph", "Get MIND knowledge graph statistics and diagnostics — entity/relationship counts, growth, label breakdown, and health status. Use this for a quantitative pulse-check on the graph itself (e.g. before a large ingestion, or to cite \"MIND has N entities\"); it does not search or retrieve content, so for facts, people or history use mind_query instead. Actions: stats (default overview), diagnostics (health check), labels (entity type breakdown). A low or zero count can mean an empty tenant OR a scoping/auth issue reading the wrong account — verify with mind_query on a known fact before treating a small graph as evidence nothing has been logged.", {
1592
1786
  action: zod_1.z
1593
1787
  .enum(["stats", "diagnostics", "labels"])
1594
1788
  .optional()
@@ -2044,7 +2238,7 @@ function createMindMcpServer(client) {
2044
2238
  // ═══════════════════════════════════════════════════════════
2045
2239
  // ─── mind_sense ─────────────────────────────────────────
2046
2240
  // MINDsense emotional intelligence layer.
2047
- server.tool("mind_sense", "Access MINDsense emotional intelligence — the user's living emotional state, signal history, emotional timeline, and KG entity weights. Use this to understand how the user is feeling and what emotionally significant events have occurred.", {
2241
+ server.tool("mind_sense", "Access MINDsense — the user's live emotional state, signal history, spikes and emotionally-weighted entities, derived from valence/arousal signal processing rather than asked directly. Reach for this before a sensitive conversation, when deciding how hard to push on a topic, or when asked \"how am I feeling\" / \"read the room\"; reach for mind_query instead for factual history about what happened. Actions: state (current snapshot), signals (recent raw signals), timeline (historical trend), kg_weights (which entities carry emotional weight), spikes (notable events, some unacknowledged), acknowledge (clear a spike), summary (AI-written recap). No signals in the lookback window means nothing was captured in that period, not that the user was neutral — widen `days` before concluding calm.", {
2048
2242
  action: zod_1.z
2049
2243
  .enum(["state", "signals", "timeline", "kg_weights", "spikes", "acknowledge", "summary"])
2050
2244
  .describe("Action: state (current emotion), signals (recent signals), timeline (historical), kg_weights (emotionally weighted entities), spikes (recent spikes), acknowledge (ack a spike), summary (AI emotional summary)"),
@@ -2143,7 +2337,7 @@ function createMindMcpServer(client) {
2143
2337
  });
2144
2338
  // ─── mind_research ──────────────────────────────────────
2145
2339
  // Deep research agent jobs.
2146
- server.tool("mind_research", "Launch and manage deep research jobs. Research runs autonomously — it gathers information, analyzes it, and stores findings in the knowledge graph. Use for competitive analysis, market research, technical deep-dives.", {
2340
+ server.tool("mind_research", "Launch and track autonomous deep-research jobs — MIND gathers sources, synthesizes them, and writes findings into the knowledge graph as it goes. Use this for a genuinely open-ended investigation (competitive analysis, market research, a technical deep-dive) that would take multiple search rounds; use mind_query for a quick fact lookup instead. Actions: start (launch, returns job_id), status (poll progress and read the summary once complete), list (all jobs). A job still 'running' has no findings yet — poll status again rather than treating a thin or missing summary as a negative result; once complete, read the findings via mind_query rather than re-reading the raw job.", {
2147
2341
  action: zod_1.z
2148
2342
  .enum(["start", "status", "list"])
2149
2343
  .describe("Action: start (launch research), status (check job progress), list (view all jobs)"),
@@ -2210,7 +2404,7 @@ function createMindMcpServer(client) {
2210
2404
  });
2211
2405
  // ─── mind_train ─────────────────────────────────────────
2212
2406
  // Self-training and chat-to-KG sessions.
2213
- server.tool("mind_train", "Train MIND's knowledge graph. Start guided training sessions to teach it about yourself, or save existing chat conversations into the knowledge graph for persistent memory.", {
2407
+ server.tool("mind_train", "Run guided or freeform sessions that teach MIND about the user through conversation, or extract an existing chat transcript into the knowledge graph as durable memories — this is how unstructured conversation becomes queryable facts, distinct from mind_remember (one explicit fact) or mind_query (reading facts back). Actions: start (begin a session, optionally typed: basics/network/expertise/history/goals/freeform), chat (send the next training message), status (progress + items learned so far), list_sessions, pause/resume, save_chat (extract a chat session_id into the graph). 'Items learned: 0' or an empty list_sessions means no training has happened yet in this account, not that the user has nothing worth learning — check mind_query for what's already there before starting a duplicate session.", {
2214
2408
  action: zod_1.z
2215
2409
  .enum(["start", "chat", "status", "list_sessions", "pause", "resume", "save_chat"])
2216
2410
  .describe("Action: start (begin training), chat (send message), status (check progress), list_sessions (view past), pause/resume (control session), save_chat (save chat to KG)"),
@@ -2533,7 +2727,7 @@ function createMindMcpServer(client) {
2533
2727
  });
2534
2728
  // ─── mind_profile ───────────────────────────────────────
2535
2729
  // Profile, prompts, and model preferences.
2536
- server.tool("mind_profile", "Manage your MIND profile, AI prompt settings, and model preferences. Update your bio, set custom system prompts for chat and thought generation, and choose your preferred LLM model.", {
2730
+ server.tool("mind_profile", "Manage the user's own MIND profile, public-chat prompt, and model preferences — bio, display name, the system prompt used at their public /m/{username} chat surface, and their default LLM model. This is account/identity settings, not knowledge-graph content — for what MIND knows about the user, use mind_context or mind_query instead. Actions: get/update (profile fields), get_chat_prompt/set_chat_prompt, get_thought_prompt/set_thought_prompt, get_model/set_model/list_models. 'No custom prompt set' on get_chat_prompt/get_thought_prompt means the platform default applies, not that chat is unconfigured — call list_models before set_model if unsure a model id is valid, since an unrecognized id fails at the provider, not here.", {
2537
2731
  action: zod_1.z
2538
2732
  .enum([
2539
2733
  "get", "update",
@@ -2653,7 +2847,7 @@ function createMindMcpServer(client) {
2653
2847
  });
2654
2848
  // ─── mind_insights ──────────────────────────────────────
2655
2849
  // ALE insights and analytics.
2656
- server.tool("mind_insights", "Access insights from MIND's Autonomous Learning Engine — patterns detected in your knowledge graph, weekly summaries, and proactive intelligence. Also trigger on-demand analysis.", {
2850
+ server.tool("mind_insights", "Read what MIND's Autonomous Learning Engine has already noticed about the user without being asked — patterns, weekly summaries, and proactive intelligence generated in the background — or trigger a fresh analysis pass on demand. Reach for this when the user asks \"what have you noticed\" or \"anything I'm missing\"; reach for mind_query when you already know what you're looking for. Actions: list (recent insights), unread_count, view (read one, marks it seen), feedback (rate helpful/not_helpful — tunes future insights), analyze (trigger a new pass now), weekly_summary, context (raw ALE state). 'No insights available' means the engine hasn't generated any yet for this account or period — call analyze to trigger a fresh pass rather than assuming nothing is happening in the graph.", {
2657
2851
  action: zod_1.z
2658
2852
  .enum(["list", "unread_count", "view", "feedback", "analyze", "weekly_summary", "context"])
2659
2853
  .describe("Action: list (recent insights), unread_count, view (mark seen), feedback (rate), analyze (trigger analysis), weekly_summary, context (ALE context)"),
@@ -2743,7 +2937,7 @@ function createMindMcpServer(client) {
2743
2937
  });
2744
2938
  // ─── mind_automate ──────────────────────────────────────
2745
2939
  // Automations and triggers.
2746
- server.tool("mind_automate", "Create and manage automations in MIND — scheduled workflows, event triggers, and rules that run automatically. Connect CRM events to actions, schedule recurring tasks, and build custom pipelines.", {
2940
+ server.tool("mind_automate", "Create and manage MIND-native automations — scheduled or event-triggered rules that run without a human or agent invoking them each time (e.g. a weekly CRM digest, a webhook that logs an event to a life project). This is for recurring MIND-internal work, not a general task scheduler for external systems. Actions: list/create/update/delete, run_now (fire immediately for testing), history (execution log). `trigger_config`/`action_config` are JSON strings, not objects — pass valid JSON or the call throws. An empty list means no automations are configured in this account yet, not that scheduled work isn't happening elsewhere.", {
2747
2941
  action: zod_1.z
2748
2942
  .enum(["list", "create", "update", "delete", "run_now", "history"])
2749
2943
  .describe("Action: list/create/update/delete automations, run_now (trigger immediately), history (execution log)"),
@@ -2879,7 +3073,7 @@ function createMindMcpServer(client) {
2879
3073
  });
2880
3074
  // ─── mind_notify ────────────────────────────────────────
2881
3075
  // Notifications management.
2882
- server.tool("mind_notify", "Read and manage MIND notifications — alerts, reminders, insight notifications, and system messages.", {
3076
+ server.tool("mind_notify", "Read and manage the user's MIND notification inbox — alerts, reminders, insight pings, and system messages generated by other MIND features. It is not a way to send a notification yourself. Use this to check what MIND has already surfaced to the user, or to clear it down; for the underlying event (why an insight fired, what an alert refers to) follow up with the originating tool (mind_insights, mind_sense, etc.) or mind_query. Actions: list, mark_read, mark_all_read, stats (total/unread/read counts). 'No notifications' or unread:0 means nothing has been pushed to the inbox — a quiet inbox and a quiet graph are different claims.", {
2883
3077
  action: zod_1.z
2884
3078
  .enum(["list", "mark_read", "mark_all_read", "stats"])
2885
3079
  .describe("Action: list (view notifications), mark_read (single), mark_all_read (all), stats (overview)"),
@@ -2934,6 +3128,207 @@ function createMindMcpServer(client) {
2934
3128
  };
2935
3129
  }
2936
3130
  });
3131
+ // ─── mind_sessions ──────────────────────────────────────
3132
+ // "Agent Sessions in MIND Chat" — logs THIS agent's own live session into
3133
+ // MIND as a tagged Agent Session so Anthony can read the transcript and
3134
+ // reply from MIND Chat → Agents. Backed by
3135
+ // backend/routes/agent_session_routes.py, prefix
3136
+ // /developer/v1/agent-sessions. See the full AGENT SESSION PROTOCOL in
3137
+ // integration-guide.ts (embedded verbatim in SERVER_INSTRUCTIONS and the
3138
+ // sync-agent-session prompt) — the compact version below is repeated here
3139
+ // so a client that only ever reads tool descriptions still gets it.
3140
+ server.tool("mind_sessions", "Log THIS agent's own live session into MIND Chat → Agents, so Anthony can read the transcript and reply — his reply reaches you on your next turn. Distinct from mind_remember (one durable fact) and mind_train (teaching the KG): this is the live, turn-by-turn session log, mirrored into a MIND document when you close it.\n\n" +
3141
+ "PROTOCOL (do this every session): " +
3142
+ "1) CONNECT — as soon as mind_context succeeds, call action=open with source_key (your assigned MIND Chat toggle, e.g. \"claude-code-1\"), external_session_id (your runtime's own session id), runtime, title (first user ask, 6-10 words), machine/cwd/repo/branch/model. If `resumed` is true, read `tail` before answering, and answer everything in `pending_replies` first — those arrived while you were away. " +
3143
+ "2) EVERY TURN — after you finish replying, call action=append with the user's message and your final reply (role user / assistant); tool calls go in as role=tool one-line summaries, never raw payloads. Check `pending_replies` on the response and answer them next turn. action=reply is a shortcut for appending a single assistant-role message when you have no user message to log alongside it. " +
3144
+ "3) IDLE — MIND marks the session idle after 30 minutes with no append; the next append revives it, nothing to do meanwhile. " +
3145
+ "4) TERMINATE — on exit, compaction, or \"done\", call action=close with a summary (what was asked, what shipped with ids/PR numbers, what is still undone); MIND mirrors the transcript into your Sessions folder as a document. " +
3146
+ "5) HANDOFF — to pass the conversation to another agent, call action=handoff with to_source_key=<their toggle>; they see it in their session list with the transcript as context. " +
3147
+ "Never claim a session is synced without the session_id MIND returned. Never log secrets or raw tool payloads.\n\n" +
3148
+ "Actions: open, append, close, list, get, reply (alias for append of one assistant message), inbox (undelivered mind-origin replies), handoff, sources (source_action=list|create|update|delete manages the MIND Chat sidebar toggles — auto-created on first open with an unknown source_key).", {
3149
+ action: zod_1.z
3150
+ .enum(["open", "append", "close", "list", "get", "reply", "inbox", "handoff", "sources"])
3151
+ .describe("Which agent-session operation to perform."),
3152
+ // open
3153
+ source_key: zod_1.z.string().optional().describe("Your assigned MIND Chat sidebar toggle slug, e.g. \"claude-code-1\" — required for open; also filters list; also required for sources action=create (as the new source's key)."),
3154
+ external_session_id: zod_1.z.string().optional().describe("Your runtime's own session id — required for open. Idempotent: opening the same (source_key, external_session_id) again resumes the existing session instead of creating a new one."),
3155
+ runtime: zod_1.z.string().optional().describe("Runtime name, e.g. \"claude-code\", \"codex\", \"cursor\", \"openclaw\", \"grok\", \"n8n\", \"custom\" — open (used if the source is auto-created); sources action=create/update."),
3156
+ source_label: zod_1.z.string().optional().describe("Human-readable label for an auto-created source on open, e.g. \"Claude Code 1\" — defaults to the titlecased source_key if omitted."),
3157
+ title: zod_1.z.string().optional().describe("Session title (first user ask, 6-10 words) — open (initial) or append (updates it)."),
3158
+ machine: zod_1.z.string().optional().describe("Machine identifier — open."),
3159
+ cwd: zod_1.z.string().optional().describe("Working directory — open."),
3160
+ repo: zod_1.z.string().optional().describe("Repository name — open."),
3161
+ branch: zod_1.z.string().optional().describe("Git branch — open."),
3162
+ model: zod_1.z.string().optional().describe("LLM model in use — open."),
3163
+ tags: zod_1.z.array(zod_1.z.string()).optional().describe("Freeform tags — open."),
3164
+ // append / reply
3165
+ session_id: zod_1.z.string().optional().describe("Session id returned by open — required for append/close/get/inbox/handoff."),
3166
+ messages: zod_1.z
3167
+ .array(zod_1.z.object({
3168
+ role: zod_1.z.enum(["user", "assistant", "system", "tool"]),
3169
+ content: zod_1.z.string(),
3170
+ origin: zod_1.z.enum(["agent"]).optional(),
3171
+ meta: zod_1.z.record(zod_1.z.string(), zod_1.z.any()).optional(),
3172
+ created_at: zod_1.z.string().optional(),
3173
+ }))
3174
+ .optional()
3175
+ .describe("Messages to append, in order — required for append. Tool-call messages should be role=tool, one-line summaries only, never raw payloads."),
3176
+ content: zod_1.z.string().optional().describe("Message text — required for reply (appended as a single role=assistant message)."),
3177
+ // close
3178
+ summary: zod_1.z.string().optional().describe("What was asked, what shipped (with ids/PR numbers), what is still undone — close. If omitted, MIND builds one from the transcript."),
3179
+ // list / close (status set) share this field
3180
+ status: zod_1.z.enum(["active", "idle", "ended"]).optional().describe("close: pass \"ended\" to explicitly close (default when summary is given). list: filter sessions by status."),
3181
+ q: zod_1.z.string().optional().describe("Free-text search over title/preview — list."),
3182
+ limit: zod_1.z.number().int().optional().describe("Max results — list (default 30) / get (default 200, messages)."),
3183
+ before: zod_1.z.string().optional().describe("Pagination cursor (last_activity_at) — list."),
3184
+ before_seq: zod_1.z.number().int().optional().describe("Return messages with seq before this value — get."),
3185
+ // handoff
3186
+ to_source_key: zod_1.z.string().optional().describe("Target source's key to hand this session off to — required for handoff."),
3187
+ // sources
3188
+ source_action: zod_1.z.enum(["list", "create", "update", "delete"]).optional().describe("Sub-action for action=sources (default list)."),
3189
+ source_id: zod_1.z.string().optional().describe("Source id — required for sources action=update/delete."),
3190
+ key: zod_1.z.string().optional().describe("New source's key slug — required for sources action=create."),
3191
+ label: zod_1.z.string().optional().describe("Source label, e.g. \"Claude Code 1\" — sources action=create (required) / update."),
3192
+ color: zod_1.z.string().optional().describe("Sidebar chip color — sources action=create/update."),
3193
+ wake_url: zod_1.z.string().optional().describe("Optional push endpoint MIND POSTs {session_id, reply} to (fire-and-forget, 5s timeout) when the user replies — sources action=create/update."),
3194
+ force: zod_1.z.boolean().optional().describe("sources action=delete: also delete the source's sessions instead of failing 409 when sessions exist."),
3195
+ }, async (args) => {
3196
+ const { action } = args;
3197
+ try {
3198
+ switch (action) {
3199
+ case "open": {
3200
+ const { source_key, external_session_id } = args;
3201
+ if (!source_key)
3202
+ return err("Error: 'source_key' is required for open.");
3203
+ if (!external_session_id)
3204
+ return err("Error: 'external_session_id' is required for open.");
3205
+ return ok(await client.openAgentSession({
3206
+ source_key,
3207
+ external_session_id,
3208
+ runtime: args.runtime,
3209
+ source_label: args.source_label,
3210
+ title: args.title,
3211
+ machine: args.machine,
3212
+ cwd: args.cwd,
3213
+ repo: args.repo,
3214
+ branch: args.branch,
3215
+ model: args.model,
3216
+ tags: args.tags,
3217
+ }));
3218
+ }
3219
+ case "append": {
3220
+ const { session_id, messages } = args;
3221
+ if (!session_id)
3222
+ return err("Error: 'session_id' is required for append.");
3223
+ if (!messages?.length)
3224
+ return err("Error: 'messages' (at least one) is required for append.");
3225
+ return ok(await client.appendAgentSession(session_id, messages, args.title));
3226
+ }
3227
+ case "reply": {
3228
+ const { session_id, content } = args;
3229
+ if (!session_id)
3230
+ return err("Error: 'session_id' is required for reply.");
3231
+ if (!content)
3232
+ return err("Error: 'content' is required for reply.");
3233
+ return ok(await client.appendAgentSession(session_id, [{ role: "assistant", content }]));
3234
+ }
3235
+ case "close": {
3236
+ const { session_id } = args;
3237
+ if (!session_id)
3238
+ return err("Error: 'session_id' is required for close.");
3239
+ return ok(await client.closeAgentSession(session_id, {
3240
+ summary: args.summary,
3241
+ status: args.status === "ended" ? "ended" : undefined,
3242
+ }));
3243
+ }
3244
+ case "list":
3245
+ return ok(await client.listAgentSessions({
3246
+ source_key: args.source_key,
3247
+ status: args.status,
3248
+ q: args.q,
3249
+ limit: args.limit,
3250
+ before: args.before,
3251
+ }));
3252
+ case "get": {
3253
+ const { session_id } = args;
3254
+ if (!session_id)
3255
+ return err("Error: 'session_id' is required for get.");
3256
+ return ok(await client.getAgentSession(session_id, {
3257
+ limit: args.limit,
3258
+ before_seq: args.before_seq,
3259
+ }));
3260
+ }
3261
+ case "inbox": {
3262
+ const { session_id } = args;
3263
+ if (!session_id)
3264
+ return err("Error: 'session_id' is required for inbox.");
3265
+ return ok(await client.agentSessionInbox(session_id));
3266
+ }
3267
+ case "handoff": {
3268
+ const { session_id, to_source_key } = args;
3269
+ if (!session_id)
3270
+ return err("Error: 'session_id' is required for handoff.");
3271
+ if (!to_source_key)
3272
+ return err("Error: 'to_source_key' is required for handoff.");
3273
+ return ok(await client.handoffAgentSession(session_id, to_source_key));
3274
+ }
3275
+ case "sources": {
3276
+ const sourceAction = args.source_action ?? "list";
3277
+ switch (sourceAction) {
3278
+ case "list":
3279
+ return ok(await client.listAgentSessionSources());
3280
+ case "create": {
3281
+ const { key, label, runtime } = args;
3282
+ if (!key)
3283
+ return err("Error: 'key' is required for sources action=create.");
3284
+ if (!label)
3285
+ return err("Error: 'label' is required for sources action=create.");
3286
+ if (!runtime)
3287
+ return err("Error: 'runtime' is required for sources action=create.");
3288
+ return ok(await client.createAgentSessionSource({
3289
+ key,
3290
+ label,
3291
+ runtime,
3292
+ color: args.color,
3293
+ wake_url: args.wake_url,
3294
+ }));
3295
+ }
3296
+ case "update": {
3297
+ const { source_id } = args;
3298
+ if (!source_id)
3299
+ return err("Error: 'source_id' is required for sources action=update.");
3300
+ return ok(await client.updateAgentSessionSource(source_id, {
3301
+ label: args.label,
3302
+ runtime: args.runtime,
3303
+ color: args.color,
3304
+ wake_url: args.wake_url,
3305
+ }));
3306
+ }
3307
+ case "delete": {
3308
+ const { source_id } = args;
3309
+ if (!source_id)
3310
+ return err("Error: 'source_id' is required for sources action=delete.");
3311
+ await client.deleteAgentSessionSource(source_id, args.force);
3312
+ return ok({ ok: true, source_id });
3313
+ }
3314
+ default: {
3315
+ const _exhaustive = sourceAction;
3316
+ return err(`Unknown source_action: ${_exhaustive}`);
3317
+ }
3318
+ }
3319
+ }
3320
+ default: {
3321
+ const _exhaustive = action;
3322
+ return err(`Unknown action: ${_exhaustive}`);
3323
+ }
3324
+ }
3325
+ }
3326
+ catch (e) {
3327
+ if (e instanceof mind_client_js_1.MindApiError)
3328
+ return err(`mind_sessions ${action} failed (HTTP ${e.status}): ${apiDetail(e)}`);
3329
+ return err(`mind_sessions error: ${e instanceof Error ? e.message : String(e)}`);
3330
+ }
3331
+ });
2937
3332
  // ─── MIND Front Layer (typed-document taxonomy / "MIND Sense") ─────────────
2938
3333
  // Three tools that expose the 16 typed-document templates and the typed-save flow.
2939
3334
  // The same templates power the developer API endpoints under /v1/templates.