@astramindapp/mcp-server 0.1.0 → 0.3.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/README.md +70 -3
- package/dist/mind-client.d.ts +66 -1
- package/dist/mind-client.d.ts.map +1 -1
- package/dist/mind-client.js +27 -0
- package/dist/mind-client.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +189 -26
- package/dist/server.js.map +1 -1
- package/package.json +5 -1
- package/src/cli.ts +0 -47
- package/src/index.ts +0 -15
- package/src/mind-client.ts +0 -324
- package/src/server.ts +0 -457
- package/src/setup.ts +0 -372
- package/tsconfig.json +0 -20
package/src/server.ts
DELETED
|
@@ -1,457 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MIND MCP Server
|
|
3
|
-
*
|
|
4
|
-
* Exposes the MIND knowledge graph as MCP tools that any LLM agent can use.
|
|
5
|
-
* This is the universal AI memory layer.
|
|
6
|
-
*
|
|
7
|
-
* Tools:
|
|
8
|
-
* mind_query — Semantic search across the knowledge graph
|
|
9
|
-
* mind_remember — Store content to the graph (auto-categorized)
|
|
10
|
-
* mind_context — Get structured always-loaded context (soul/user/rules)
|
|
11
|
-
* mind_life — Read/write goals, projects, tasks
|
|
12
|
-
* mind_crm — Read/write contacts and interactions
|
|
13
|
-
* mind_graph — Get graph statistics and health
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
17
|
-
import { z } from "zod";
|
|
18
|
-
import { MindClient } from "./mind-client.js";
|
|
19
|
-
|
|
20
|
-
export function createMindMcpServer(client: MindClient): McpServer {
|
|
21
|
-
const server = new McpServer({
|
|
22
|
-
name: "mind",
|
|
23
|
-
version: "0.1.0",
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
// ─── mind_query ─────────────────────────────────────────
|
|
27
|
-
// Semantic search across the user's knowledge graph.
|
|
28
|
-
// Replaces flat MEMORY.md — returns only relevant memories.
|
|
29
|
-
|
|
30
|
-
server.tool(
|
|
31
|
-
"mind_query",
|
|
32
|
-
"Search your MIND knowledge graph. Returns AI-synthesized answer from your stored documents, entries, and thoughts. Use this BEFORE making decisions — MIND is your memory.",
|
|
33
|
-
{
|
|
34
|
-
query: z.string().describe("What to search for in your knowledge graph"),
|
|
35
|
-
mode: z
|
|
36
|
-
.enum(["mix", "graph", "vector"])
|
|
37
|
-
.optional()
|
|
38
|
-
.default("mix")
|
|
39
|
-
.describe("Search mode: mix (default, best results), graph (entity relationships), vector (semantic similarity)"),
|
|
40
|
-
},
|
|
41
|
-
async ({ query, mode }) => {
|
|
42
|
-
try {
|
|
43
|
-
const result = await client.query({ query, mode });
|
|
44
|
-
const text = [
|
|
45
|
-
result.response,
|
|
46
|
-
"",
|
|
47
|
-
`--- Sources: ${result.sources?.length ?? 0} | Credits used: ${result.credits_used} | Remaining: ${result.credits_remaining} ---`,
|
|
48
|
-
];
|
|
49
|
-
|
|
50
|
-
if (result.sources?.length) {
|
|
51
|
-
text.push("");
|
|
52
|
-
for (const s of result.sources.slice(0, 5)) {
|
|
53
|
-
text.push(
|
|
54
|
-
`• ${s.title ?? "Untitled"} (score: ${s.relevance_score?.toFixed(2) ?? "N/A"})`
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
return { content: [{ type: "text" as const, text: text.join("\n") }] };
|
|
60
|
-
} catch (err) {
|
|
61
|
-
return {
|
|
62
|
-
content: [{ type: "text" as const, text: `Error querying MIND: ${err}` }],
|
|
63
|
-
isError: true,
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
// ─── mind_remember ──────────────────────────────────────
|
|
70
|
-
// Store content to the knowledge graph. Auto-categorized by type.
|
|
71
|
-
// Replaces manual memory file writes.
|
|
72
|
-
|
|
73
|
-
server.tool(
|
|
74
|
-
"mind_remember",
|
|
75
|
-
"Store something in your MIND knowledge graph. Use for facts, decisions, learnings, research, notes — anything worth remembering. Auto-categorized. Always log outcomes here after completing tasks.",
|
|
76
|
-
{
|
|
77
|
-
content: z.string().describe("What to remember — the actual content to store"),
|
|
78
|
-
type: z
|
|
79
|
-
.enum(["document", "entry", "thought"])
|
|
80
|
-
.optional()
|
|
81
|
-
.default("entry")
|
|
82
|
-
.describe(
|
|
83
|
-
"Storage type: document (long-form, structured — research, specs, strategies), entry (medium — observations, findings, action logs), thought (quick — fleeting ideas, one-liners)"
|
|
84
|
-
),
|
|
85
|
-
title: z
|
|
86
|
-
.string()
|
|
87
|
-
.optional()
|
|
88
|
-
.describe("Title for documents and entries (auto-generated if omitted for thoughts)"),
|
|
89
|
-
tags: z
|
|
90
|
-
.array(z.string())
|
|
91
|
-
.optional()
|
|
92
|
-
.describe("Tags for categorization (e.g. ['strategy', 'competitor', 'learning'])"),
|
|
93
|
-
source: z
|
|
94
|
-
.string()
|
|
95
|
-
.optional()
|
|
96
|
-
.default("mcp-agent")
|
|
97
|
-
.describe("Source identifier (e.g. 'claude-code', 'openclaw', 'cursor')"),
|
|
98
|
-
},
|
|
99
|
-
async ({ content, type, title, tags, source }) => {
|
|
100
|
-
try {
|
|
101
|
-
let result: { id?: string; entry_id?: string; thought_id?: string; item_id?: string };
|
|
102
|
-
let storedAs: string;
|
|
103
|
-
|
|
104
|
-
switch (type) {
|
|
105
|
-
case "document":
|
|
106
|
-
result = await client.createDocument({
|
|
107
|
-
title: title ?? `Agent Document — ${new Date().toISOString().slice(0, 10)}`,
|
|
108
|
-
content,
|
|
109
|
-
source: source ?? "mcp-agent",
|
|
110
|
-
tags,
|
|
111
|
-
});
|
|
112
|
-
storedAs = "document";
|
|
113
|
-
break;
|
|
114
|
-
|
|
115
|
-
case "thought":
|
|
116
|
-
result = await client.createThought({ content });
|
|
117
|
-
storedAs = "thought";
|
|
118
|
-
break;
|
|
119
|
-
|
|
120
|
-
case "entry":
|
|
121
|
-
default:
|
|
122
|
-
result = await client.createEntry({
|
|
123
|
-
title,
|
|
124
|
-
content,
|
|
125
|
-
entry_type: "text",
|
|
126
|
-
tags,
|
|
127
|
-
});
|
|
128
|
-
storedAs = "entry";
|
|
129
|
-
break;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const id = result.id ?? result.entry_id ?? result.thought_id ?? "unknown";
|
|
133
|
-
return {
|
|
134
|
-
content: [
|
|
135
|
-
{
|
|
136
|
-
type: "text" as const,
|
|
137
|
-
text: `✅ Stored to MIND as ${storedAs} (id: ${id})${title ? ` — "${title}"` : ""}`,
|
|
138
|
-
},
|
|
139
|
-
],
|
|
140
|
-
};
|
|
141
|
-
} catch (err) {
|
|
142
|
-
return {
|
|
143
|
-
content: [{ type: "text" as const, text: `Error storing to MIND: ${err}` }],
|
|
144
|
-
isError: true,
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
);
|
|
149
|
-
|
|
150
|
-
// ─── mind_context ───────────────────────────────────────
|
|
151
|
-
// Get structured always-loaded context.
|
|
152
|
-
// Equivalent to OpenClaw's SOUL.md + USER.md + AGENTS.md bootstrap injection,
|
|
153
|
-
// but backed by the knowledge graph.
|
|
154
|
-
|
|
155
|
-
server.tool(
|
|
156
|
-
"mind_context",
|
|
157
|
-
"Get your persistent context from MIND — identity, preferences, rules, and recent activity. Call this at the start of every session to know who you are, who the user is, and what matters right now.",
|
|
158
|
-
{
|
|
159
|
-
sections: z
|
|
160
|
-
.array(z.enum(["soul", "user", "rules", "priorities", "recent"]))
|
|
161
|
-
.optional()
|
|
162
|
-
.default(["soul", "user", "rules", "priorities", "recent"])
|
|
163
|
-
.describe(
|
|
164
|
-
"Which context sections to load: soul (identity/personality), user (who the user is), rules (operating constraints), priorities (current goals/tasks), recent (latest activity)"
|
|
165
|
-
),
|
|
166
|
-
},
|
|
167
|
-
async ({ sections }) => {
|
|
168
|
-
try {
|
|
169
|
-
const results: string[] = [];
|
|
170
|
-
|
|
171
|
-
for (const section of sections) {
|
|
172
|
-
let query: string;
|
|
173
|
-
switch (section) {
|
|
174
|
-
case "soul":
|
|
175
|
-
query = "my identity, mission, personality, who I am, my role and purpose";
|
|
176
|
-
break;
|
|
177
|
-
case "user":
|
|
178
|
-
query = "user profile, preferences, timezone, communication style, who is the user";
|
|
179
|
-
break;
|
|
180
|
-
case "rules":
|
|
181
|
-
query = "operating rules, constraints, hard limits, things I must never do, safety rules";
|
|
182
|
-
break;
|
|
183
|
-
case "priorities":
|
|
184
|
-
query = "current priorities, active goals, top tasks, what to focus on right now";
|
|
185
|
-
break;
|
|
186
|
-
case "recent":
|
|
187
|
-
query = "most recent activity, what happened today, latest outcomes and decisions";
|
|
188
|
-
break;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
const result = await client.query({ query, mode: "mix" });
|
|
192
|
-
results.push(`## ${section.toUpperCase()}\n\n${result.response}`);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
return {
|
|
196
|
-
content: [{ type: "text" as const, text: results.join("\n\n---\n\n") }],
|
|
197
|
-
};
|
|
198
|
-
} catch (err) {
|
|
199
|
-
return {
|
|
200
|
-
content: [{ type: "text" as const, text: `Error loading context from MIND: ${err}` }],
|
|
201
|
-
isError: true,
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
);
|
|
206
|
-
|
|
207
|
-
// ─── mind_life ──────────────────────────────────────────
|
|
208
|
-
// Read/write goals, projects, and tasks.
|
|
209
|
-
|
|
210
|
-
server.tool(
|
|
211
|
-
"mind_life",
|
|
212
|
-
"Manage goals, projects, and tasks in MIND Life. Use for tracking work, creating action items, updating progress, and completing tasks.",
|
|
213
|
-
{
|
|
214
|
-
action: z
|
|
215
|
-
.enum(["list", "create", "update", "complete", "delete"])
|
|
216
|
-
.describe("What to do: list (view items), create (new item), update (modify), complete (mark done), delete (remove)"),
|
|
217
|
-
title: z.string().optional().describe("Title for new items"),
|
|
218
|
-
description: z.string().optional().describe("Description for new items"),
|
|
219
|
-
status: z
|
|
220
|
-
.string()
|
|
221
|
-
.optional()
|
|
222
|
-
.describe("Status filter for list, or new status for update (e.g. action, someday, waiting, completed)"),
|
|
223
|
-
priority: z
|
|
224
|
-
.string()
|
|
225
|
-
.optional()
|
|
226
|
-
.describe("Priority: low, medium, high, urgent"),
|
|
227
|
-
due_date: z.string().optional().describe("Due date in YYYY-MM-DD format"),
|
|
228
|
-
item_id: z.string().optional().describe("Item ID for update/complete/delete actions"),
|
|
229
|
-
},
|
|
230
|
-
async ({ action, title, description, status, priority, due_date, item_id }) => {
|
|
231
|
-
try {
|
|
232
|
-
switch (action) {
|
|
233
|
-
case "list": {
|
|
234
|
-
const result = await client.listLifeItems(status);
|
|
235
|
-
const items = result.items ?? [];
|
|
236
|
-
if (items.length === 0) {
|
|
237
|
-
return { content: [{ type: "text" as const, text: "No life items found." }] };
|
|
238
|
-
}
|
|
239
|
-
const lines = items.map(
|
|
240
|
-
(i) =>
|
|
241
|
-
`• [${i.priority ?? "—"}] ${i.title} (status: ${i.status ?? "—"}${i.due_date ? `, due: ${i.due_date}` : ""}) — id: ${i.item_id}`
|
|
242
|
-
);
|
|
243
|
-
return { content: [{ type: "text" as const, text: lines.join("\n") }] };
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
case "create": {
|
|
247
|
-
if (!title) {
|
|
248
|
-
return {
|
|
249
|
-
content: [{ type: "text" as const, text: "Error: title is required to create a life item" }],
|
|
250
|
-
isError: true,
|
|
251
|
-
};
|
|
252
|
-
}
|
|
253
|
-
const result = await client.createLifeItem({
|
|
254
|
-
title,
|
|
255
|
-
description,
|
|
256
|
-
status: status ?? "action",
|
|
257
|
-
priority: priority ?? "medium",
|
|
258
|
-
due_date,
|
|
259
|
-
});
|
|
260
|
-
return {
|
|
261
|
-
content: [
|
|
262
|
-
{
|
|
263
|
-
type: "text" as const,
|
|
264
|
-
text: `✅ Created life item: "${result.title}" (id: ${result.item_id}, priority: ${result.priority}, due: ${result.due_date ?? "none"})`,
|
|
265
|
-
},
|
|
266
|
-
],
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
case "update": {
|
|
271
|
-
if (!item_id) {
|
|
272
|
-
return {
|
|
273
|
-
content: [{ type: "text" as const, text: "Error: item_id is required for update" }],
|
|
274
|
-
isError: true,
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
const patch: Record<string, unknown> = {};
|
|
278
|
-
if (title) patch.title = title;
|
|
279
|
-
if (description) patch.description = description;
|
|
280
|
-
if (status) patch.status = status;
|
|
281
|
-
if (priority) patch.priority = priority;
|
|
282
|
-
if (due_date) patch.due_date = due_date;
|
|
283
|
-
const result = await client.updateLifeItem(item_id, patch);
|
|
284
|
-
return {
|
|
285
|
-
content: [{ type: "text" as const, text: `✅ Updated: "${result.title}" (status: ${result.status})` }],
|
|
286
|
-
};
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
case "complete": {
|
|
290
|
-
if (!item_id) {
|
|
291
|
-
return {
|
|
292
|
-
content: [{ type: "text" as const, text: "Error: item_id is required for complete" }],
|
|
293
|
-
isError: true,
|
|
294
|
-
};
|
|
295
|
-
}
|
|
296
|
-
const result = await client.updateLifeItem(item_id, { status: "completed" });
|
|
297
|
-
return {
|
|
298
|
-
content: [{ type: "text" as const, text: `✅ Completed: "${result.title}"` }],
|
|
299
|
-
};
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
case "delete": {
|
|
303
|
-
if (!item_id) {
|
|
304
|
-
return {
|
|
305
|
-
content: [{ type: "text" as const, text: "Error: item_id is required for delete" }],
|
|
306
|
-
isError: true,
|
|
307
|
-
};
|
|
308
|
-
}
|
|
309
|
-
await client.deleteLifeItem(item_id);
|
|
310
|
-
return {
|
|
311
|
-
content: [{ type: "text" as const, text: `✅ Deleted life item ${item_id}` }],
|
|
312
|
-
};
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
} catch (err) {
|
|
316
|
-
return {
|
|
317
|
-
content: [{ type: "text" as const, text: `Error in mind_life: ${err}` }],
|
|
318
|
-
isError: true,
|
|
319
|
-
};
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
);
|
|
323
|
-
|
|
324
|
-
// ─── mind_crm ───────────────────────────────────────────
|
|
325
|
-
// Read/write contacts and interactions.
|
|
326
|
-
|
|
327
|
-
server.tool(
|
|
328
|
-
"mind_crm",
|
|
329
|
-
"Manage contacts and relationships in MIND CRM. Use for tracking people, companies, leads, and interaction history.",
|
|
330
|
-
{
|
|
331
|
-
action: z
|
|
332
|
-
.enum(["list", "create", "update"])
|
|
333
|
-
.describe("What to do: list contacts, create new contact, update existing"),
|
|
334
|
-
name: z.string().optional().describe("Contact name (required for create)"),
|
|
335
|
-
email: z.string().optional().describe("Contact email"),
|
|
336
|
-
company: z.string().optional().describe("Company/organization"),
|
|
337
|
-
type: z.string().optional().describe("Contact type: lead, prospect, partner, customer, personal"),
|
|
338
|
-
stage: z.string().optional().describe("Pipeline stage: new, qualified, proposal, closed, lost"),
|
|
339
|
-
source: z.string().optional().describe("How you found them"),
|
|
340
|
-
notes: z.string().optional().describe("Notes about this contact"),
|
|
341
|
-
contact_id: z.string().optional().describe("Contact ID for updates"),
|
|
342
|
-
},
|
|
343
|
-
async ({ action, name, email, company, type, stage, source, notes, contact_id }) => {
|
|
344
|
-
try {
|
|
345
|
-
switch (action) {
|
|
346
|
-
case "list": {
|
|
347
|
-
const result = await client.listContacts();
|
|
348
|
-
const contacts = result.contacts ?? [];
|
|
349
|
-
if (contacts.length === 0) {
|
|
350
|
-
return { content: [{ type: "text" as const, text: "No contacts found." }] };
|
|
351
|
-
}
|
|
352
|
-
const lines = contacts.map(
|
|
353
|
-
(c) =>
|
|
354
|
-
`• ${c.name}${c.company ? ` @ ${c.company}` : ""}${c.email ? ` (${c.email})` : ""} — ${c.type ?? "—"} / ${c.stage ?? "—"} — id: ${c.id ?? c.contact_id}`
|
|
355
|
-
);
|
|
356
|
-
return { content: [{ type: "text" as const, text: lines.join("\n") }] };
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
case "create": {
|
|
360
|
-
if (!name) {
|
|
361
|
-
return {
|
|
362
|
-
content: [{ type: "text" as const, text: "Error: name is required to create a contact" }],
|
|
363
|
-
isError: true,
|
|
364
|
-
};
|
|
365
|
-
}
|
|
366
|
-
const result = await client.createContact({
|
|
367
|
-
name,
|
|
368
|
-
email,
|
|
369
|
-
company,
|
|
370
|
-
type,
|
|
371
|
-
stage,
|
|
372
|
-
source,
|
|
373
|
-
notes,
|
|
374
|
-
});
|
|
375
|
-
return {
|
|
376
|
-
content: [
|
|
377
|
-
{
|
|
378
|
-
type: "text" as const,
|
|
379
|
-
text: `✅ Created contact: "${result.name}" (id: ${result.id ?? result.contact_id})`,
|
|
380
|
-
},
|
|
381
|
-
],
|
|
382
|
-
};
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
case "update": {
|
|
386
|
-
if (!contact_id) {
|
|
387
|
-
return {
|
|
388
|
-
content: [{ type: "text" as const, text: "Error: contact_id is required for update" }],
|
|
389
|
-
isError: true,
|
|
390
|
-
};
|
|
391
|
-
}
|
|
392
|
-
const patch: Record<string, unknown> = {};
|
|
393
|
-
if (name) patch.name = name;
|
|
394
|
-
if (email) patch.email = email;
|
|
395
|
-
if (company) patch.company = company;
|
|
396
|
-
if (type) patch.type = type;
|
|
397
|
-
if (stage) patch.stage = stage;
|
|
398
|
-
if (notes) patch.notes = notes;
|
|
399
|
-
const result = await client.updateContact(contact_id, patch);
|
|
400
|
-
return {
|
|
401
|
-
content: [{ type: "text" as const, text: `✅ Updated contact: "${result.name}"` }],
|
|
402
|
-
};
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
} catch (err) {
|
|
406
|
-
return {
|
|
407
|
-
content: [{ type: "text" as const, text: `Error in mind_crm: ${err}` }],
|
|
408
|
-
isError: true,
|
|
409
|
-
};
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
);
|
|
413
|
-
|
|
414
|
-
// ─── mind_graph ─────────────────────────────────────────
|
|
415
|
-
// Get graph statistics and health.
|
|
416
|
-
|
|
417
|
-
server.tool(
|
|
418
|
-
"mind_graph",
|
|
419
|
-
"Get MIND knowledge graph statistics — how many entities, relationships, documents, and entries exist. Use to check graph health and growth.",
|
|
420
|
-
{},
|
|
421
|
-
async () => {
|
|
422
|
-
try {
|
|
423
|
-
const [graph, profile] = await Promise.all([
|
|
424
|
-
client.graphInfo(),
|
|
425
|
-
client.credits().catch(() => null),
|
|
426
|
-
]);
|
|
427
|
-
|
|
428
|
-
const lines = [
|
|
429
|
-
"📊 MIND Graph Status",
|
|
430
|
-
`• Entities: ${graph.total_entities}`,
|
|
431
|
-
`• Relationships: ${graph.total_relationships}`,
|
|
432
|
-
`• Storage: ${graph.storage_status?.status ?? "unknown"}`,
|
|
433
|
-
];
|
|
434
|
-
|
|
435
|
-
if (graph.popular_labels?.length) {
|
|
436
|
-
const docCount = graph.popular_labels.reduce((sum, l) => sum + l.count, 0);
|
|
437
|
-
lines.push(`• Items in graph: ${docCount}`);
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
if (profile) {
|
|
441
|
-
lines.push(`• Credits: ${profile.credits_balance}/${profile.credits_limit}`);
|
|
442
|
-
lines.push(`• Tier: ${profile.tier}`);
|
|
443
|
-
lines.push(`• Documents: ${profile.documents_used}/${profile.documents_limit}`);
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
return { content: [{ type: "text" as const, text: lines.join("\n") }] };
|
|
447
|
-
} catch (err) {
|
|
448
|
-
return {
|
|
449
|
-
content: [{ type: "text" as const, text: `Error getting graph info: ${err}` }],
|
|
450
|
-
isError: true,
|
|
451
|
-
};
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
);
|
|
455
|
-
|
|
456
|
-
return server;
|
|
457
|
-
}
|