mindgraph-mcp 0.9.0 → 0.10.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Shuruheel Ahamed
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -62,6 +62,8 @@ Or drop this into your Claude Desktop config by hand (`~/Library/Application Sup
62
62
  | `mindgraph_synthesize` | Project-scoped cross-document synthesis — mine signals, spawn Article-generation jobs |
63
63
  | `mindgraph_ontology` | Operational Ontology (Layer 7) — typed domain objects, NL queries with provenance, extraction proposal review (approve/reject), object linking |
64
64
 
65
+ Plus **dynamically generated read tools**: when the tenant's active schema declares object types, the server renders per-type `search_<objs>` / `get_<obj>` / `summarize_<obj>` tools (from the cloud's `GET /v1/ontology/tools` manifest) at session start. `summarize_<obj>` returns the object — whether mapped from a connected SQL database or extracted from documents — *plus* its cognitive context (claims, risks, decisions, evidence).
66
+
65
67
  **7 prompt templates** for common workflows:
66
68
 
67
69
  `graph-summary`, `active-goals`, `open-questions`, `review-contradictions`, `knowledge-about`, `daily-briefing`, `capture-conversation`.
package/dist/cli.js CHANGED
@@ -1011,7 +1011,7 @@ var init_tools = __esm({
1011
1011
  },
1012
1012
  {
1013
1013
  name: "mindgraph_retrieve",
1014
- description: "Search and explore the knowledge graph. Use 'context' (default) for BM25 keyword retrieval \u2014 returns graph nodes only by default (labels, summaries, types, confidence); set include_chunks=true to also get full source text. Pass 1-3 discriminating keywords, not sentences. GOOD: 'Kissinger NATO'. BAD: 'What is Kissinger\\'s view on NATO?'. Use 'document_index' to list all ingested documents (titles, dates, UIDs) for orientation. Use 'semantic' when keywords return nothing \u2014 conceptual/fuzzy queries. Use 'hybrid' for keyword + semantic. Use 'text' for fast keyword-only. Use 'neighborhood'/'chain'/'path'/'subgraph' for graph traversal. Use 'preferences' for advice or recommendation requests ('suggest a hotel', 'what should I read?') \u2014 it returns the user's stated/learned preferences relevant to the query (pass the topic as 'query'), so your answer reflects what they actually like instead of being generic. Other structured actions ('active_goals', 'open_questions', etc.) only when the user explicitly asks.",
1014
+ description: "Search and explore the knowledge graph. Use 'context' (default) for BM25 keyword retrieval \u2014 returns graph nodes only by default (labels, summaries, types, confidence); set include_chunks=true to also get full source text. Pass 1-3 discriminating keywords, not sentences. GOOD: 'Kissinger NATO'. BAD: 'What is Kissinger\\'s view on NATO?'. Use 'document_index' to list all ingested documents (titles, dates, UIDs) for orientation. Use 'semantic' when keywords return nothing \u2014 conceptual/fuzzy queries. Use 'hybrid' for keyword + semantic. Use 'text' for fast keyword-only. Use 'neighborhood'/'chain'/'path'/'subgraph' for graph traversal \u2014 traversal steps carry path_cost (lower = stronger connection; sum of -ln(edge weight)) and path_confidence (product of edge confidences; a ranking signal, not a calibrated probability) for the returned path, useful for ranking results; they score the path returned, not the best possible path. Use 'preferences' for advice or recommendation requests ('suggest a hotel', 'what should I read?') \u2014 it returns the user's stated/learned preferences relevant to the query (pass the topic as 'query'), so your answer reflects what they actually like instead of being generic. Other structured actions ('active_goals', 'open_questions', etc.) only when the user explicitly asks.",
1015
1015
  inputSchema: {
1016
1016
  type: "object",
1017
1017
  properties: {
@@ -1311,6 +1311,84 @@ var init_tools = __esm({
1311
1311
  }
1312
1312
  });
1313
1313
 
1314
+ // src/generated-tools.ts
1315
+ async function getGeneratedTools(client2) {
1316
+ if (cache && Date.now() - cache.fetchedAt < CACHE_TTL_MS) {
1317
+ return { tools: cache.tools, byName: cache.byName };
1318
+ }
1319
+ try {
1320
+ const c = client2;
1321
+ if (typeof c.listOntologyTools !== "function") {
1322
+ cache = { tools: [], byName: /* @__PURE__ */ new Map(), fetchedAt: Date.now() };
1323
+ return { tools: cache.tools, byName: cache.byName };
1324
+ }
1325
+ const resp = await c.listOntologyTools();
1326
+ const byName = /* @__PURE__ */ new Map();
1327
+ const tools = [];
1328
+ for (const d of resp.tools ?? []) {
1329
+ byName.set(d.name, d);
1330
+ tools.push({
1331
+ name: d.name,
1332
+ description: d.description,
1333
+ inputSchema: d.input_schema
1334
+ });
1335
+ }
1336
+ cache = { tools, byName, fetchedAt: Date.now() };
1337
+ return { tools, byName };
1338
+ } catch {
1339
+ cache = { tools: [], byName: /* @__PURE__ */ new Map(), fetchedAt: Date.now() };
1340
+ return { tools: cache.tools, byName: cache.byName };
1341
+ }
1342
+ }
1343
+ async function handleGeneratedTool(client2, desc, args) {
1344
+ try {
1345
+ switch (desc.maps_to) {
1346
+ case "search": {
1347
+ const query = args.query;
1348
+ if (!query) return err2(`query is required for ${desc.name}`);
1349
+ const limit = typeof args.limit === "number" ? args.limit : void 0;
1350
+ return ok2(
1351
+ await client2.searchDomainObjects(query, {
1352
+ schema_id: desc.schema_id,
1353
+ object_types: [desc.object_type],
1354
+ limit
1355
+ })
1356
+ );
1357
+ }
1358
+ case "object": {
1359
+ const uid = args.uid;
1360
+ if (!uid) return err2(`uid is required for ${desc.name}`);
1361
+ return ok2(await client2.getDomainObject(uid));
1362
+ }
1363
+ case "object_context": {
1364
+ const uid = args.uid;
1365
+ if (!uid) return err2(`uid is required for ${desc.name}`);
1366
+ const depth = typeof args.depth === "number" ? args.depth : void 0;
1367
+ return ok2(await client2.getDomainObjectContext(uid, depth));
1368
+ }
1369
+ default:
1370
+ return err2(`unknown generated tool mapping: ${desc.maps_to}`);
1371
+ }
1372
+ } catch (e) {
1373
+ return err2(e instanceof Error ? e.message : String(e));
1374
+ }
1375
+ }
1376
+ var ok2, err2, CACHE_TTL_MS, cache;
1377
+ var init_generated_tools = __esm({
1378
+ "src/generated-tools.ts"() {
1379
+ "use strict";
1380
+ ok2 = (data) => ({
1381
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
1382
+ });
1383
+ err2 = (message) => ({
1384
+ content: [{ type: "text", text: JSON.stringify({ error: message }) }],
1385
+ isError: true
1386
+ });
1387
+ CACHE_TTL_MS = 5 * 60 * 1e3;
1388
+ cache = null;
1389
+ }
1390
+ });
1391
+
1314
1392
  // src/index.ts
1315
1393
  var src_exports = {};
1316
1394
  async function main() {
@@ -1329,6 +1407,7 @@ var init_src = __esm({
1329
1407
  import_types = require("@modelcontextprotocol/sdk/types.js");
1330
1408
  import_mindgraph = require("mindgraph");
1331
1409
  init_tools();
1410
+ init_generated_tools();
1332
1411
  API_KEY = process.env.MINDGRAPH_API_KEY;
1333
1412
  BASE_URL = process.env.MINDGRAPH_BASE_URL || "https://api.mindgraph.cloud";
1334
1413
  AGENT_ID = process.env.MINDGRAPH_AGENT_ID || "mcp";
@@ -1422,7 +1501,7 @@ Capture knowledge when the user shares something worth remembering:
1422
1501
  server = new import_server.Server(
1423
1502
  {
1424
1503
  name: "mindgraph",
1425
- version: "0.7.1"
1504
+ version: "0.9.1"
1426
1505
  },
1427
1506
  {
1428
1507
  capabilities: {
@@ -1434,7 +1513,8 @@ Capture knowledge when the user shares something worth remembering:
1434
1513
  }
1435
1514
  );
1436
1515
  server.setRequestHandler(import_types.ListToolsRequestSchema, async () => {
1437
- return { tools: TOOLS };
1516
+ const generated = await getGeneratedTools(client);
1517
+ return { tools: [...TOOLS, ...generated.tools] };
1438
1518
  });
1439
1519
  server.setRequestHandler(import_types.CallToolRequestSchema, async (request) => {
1440
1520
  const { name, arguments: args } = request.params;
@@ -1442,6 +1522,11 @@ Capture knowledge when the user shares something worth remembering:
1442
1522
  if (!toolArgs.agent_id) {
1443
1523
  toolArgs.agent_id = AGENT_ID;
1444
1524
  }
1525
+ const generated = await getGeneratedTools(client);
1526
+ const desc = generated.byName.get(name);
1527
+ if (desc) {
1528
+ return handleGeneratedTool(client, desc, toolArgs);
1529
+ }
1445
1530
  return handleTool(client, name, toolArgs);
1446
1531
  });
1447
1532
  PROMPTS = [
package/dist/index.js CHANGED
@@ -297,7 +297,7 @@ var TOOLS = [
297
297
  },
298
298
  {
299
299
  name: "mindgraph_retrieve",
300
- description: "Search and explore the knowledge graph. Use 'context' (default) for BM25 keyword retrieval \u2014 returns graph nodes only by default (labels, summaries, types, confidence); set include_chunks=true to also get full source text. Pass 1-3 discriminating keywords, not sentences. GOOD: 'Kissinger NATO'. BAD: 'What is Kissinger\\'s view on NATO?'. Use 'document_index' to list all ingested documents (titles, dates, UIDs) for orientation. Use 'semantic' when keywords return nothing \u2014 conceptual/fuzzy queries. Use 'hybrid' for keyword + semantic. Use 'text' for fast keyword-only. Use 'neighborhood'/'chain'/'path'/'subgraph' for graph traversal. Use 'preferences' for advice or recommendation requests ('suggest a hotel', 'what should I read?') \u2014 it returns the user's stated/learned preferences relevant to the query (pass the topic as 'query'), so your answer reflects what they actually like instead of being generic. Other structured actions ('active_goals', 'open_questions', etc.) only when the user explicitly asks.",
300
+ description: "Search and explore the knowledge graph. Use 'context' (default) for BM25 keyword retrieval \u2014 returns graph nodes only by default (labels, summaries, types, confidence); set include_chunks=true to also get full source text. Pass 1-3 discriminating keywords, not sentences. GOOD: 'Kissinger NATO'. BAD: 'What is Kissinger\\'s view on NATO?'. Use 'document_index' to list all ingested documents (titles, dates, UIDs) for orientation. Use 'semantic' when keywords return nothing \u2014 conceptual/fuzzy queries. Use 'hybrid' for keyword + semantic. Use 'text' for fast keyword-only. Use 'neighborhood'/'chain'/'path'/'subgraph' for graph traversal \u2014 traversal steps carry path_cost (lower = stronger connection; sum of -ln(edge weight)) and path_confidence (product of edge confidences; a ranking signal, not a calibrated probability) for the returned path, useful for ranking results; they score the path returned, not the best possible path. Use 'preferences' for advice or recommendation requests ('suggest a hotel', 'what should I read?') \u2014 it returns the user's stated/learned preferences relevant to the query (pass the topic as 'query'), so your answer reflects what they actually like instead of being generic. Other structured actions ('active_goals', 'open_questions', etc.) only when the user explicitly asks.",
301
301
  inputSchema: {
302
302
  type: "object",
303
303
  properties: {
@@ -1285,6 +1285,78 @@ async function handleOntology(client2, args) {
1285
1285
  }
1286
1286
  }
1287
1287
 
1288
+ // src/generated-tools.ts
1289
+ var ok2 = (data) => ({
1290
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
1291
+ });
1292
+ var err2 = (message) => ({
1293
+ content: [{ type: "text", text: JSON.stringify({ error: message }) }],
1294
+ isError: true
1295
+ });
1296
+ var CACHE_TTL_MS = 5 * 60 * 1e3;
1297
+ var cache = null;
1298
+ async function getGeneratedTools(client2) {
1299
+ if (cache && Date.now() - cache.fetchedAt < CACHE_TTL_MS) {
1300
+ return { tools: cache.tools, byName: cache.byName };
1301
+ }
1302
+ try {
1303
+ const c = client2;
1304
+ if (typeof c.listOntologyTools !== "function") {
1305
+ cache = { tools: [], byName: /* @__PURE__ */ new Map(), fetchedAt: Date.now() };
1306
+ return { tools: cache.tools, byName: cache.byName };
1307
+ }
1308
+ const resp = await c.listOntologyTools();
1309
+ const byName = /* @__PURE__ */ new Map();
1310
+ const tools = [];
1311
+ for (const d of resp.tools ?? []) {
1312
+ byName.set(d.name, d);
1313
+ tools.push({
1314
+ name: d.name,
1315
+ description: d.description,
1316
+ inputSchema: d.input_schema
1317
+ });
1318
+ }
1319
+ cache = { tools, byName, fetchedAt: Date.now() };
1320
+ return { tools, byName };
1321
+ } catch {
1322
+ cache = { tools: [], byName: /* @__PURE__ */ new Map(), fetchedAt: Date.now() };
1323
+ return { tools: cache.tools, byName: cache.byName };
1324
+ }
1325
+ }
1326
+ async function handleGeneratedTool(client2, desc, args) {
1327
+ try {
1328
+ switch (desc.maps_to) {
1329
+ case "search": {
1330
+ const query = args.query;
1331
+ if (!query) return err2(`query is required for ${desc.name}`);
1332
+ const limit = typeof args.limit === "number" ? args.limit : void 0;
1333
+ return ok2(
1334
+ await client2.searchDomainObjects(query, {
1335
+ schema_id: desc.schema_id,
1336
+ object_types: [desc.object_type],
1337
+ limit
1338
+ })
1339
+ );
1340
+ }
1341
+ case "object": {
1342
+ const uid = args.uid;
1343
+ if (!uid) return err2(`uid is required for ${desc.name}`);
1344
+ return ok2(await client2.getDomainObject(uid));
1345
+ }
1346
+ case "object_context": {
1347
+ const uid = args.uid;
1348
+ if (!uid) return err2(`uid is required for ${desc.name}`);
1349
+ const depth = typeof args.depth === "number" ? args.depth : void 0;
1350
+ return ok2(await client2.getDomainObjectContext(uid, depth));
1351
+ }
1352
+ default:
1353
+ return err2(`unknown generated tool mapping: ${desc.maps_to}`);
1354
+ }
1355
+ } catch (e) {
1356
+ return err2(e instanceof Error ? e.message : String(e));
1357
+ }
1358
+ }
1359
+
1288
1360
  // src/index.ts
1289
1361
  var API_KEY = process.env.MINDGRAPH_API_KEY;
1290
1362
  var BASE_URL = process.env.MINDGRAPH_BASE_URL || "https://api.mindgraph.cloud";
@@ -1379,7 +1451,7 @@ Capture knowledge when the user shares something worth remembering:
1379
1451
  var server = new import_server.Server(
1380
1452
  {
1381
1453
  name: "mindgraph",
1382
- version: "0.7.1"
1454
+ version: "0.9.1"
1383
1455
  },
1384
1456
  {
1385
1457
  capabilities: {
@@ -1391,7 +1463,8 @@ var server = new import_server.Server(
1391
1463
  }
1392
1464
  );
1393
1465
  server.setRequestHandler(import_types.ListToolsRequestSchema, async () => {
1394
- return { tools: TOOLS };
1466
+ const generated = await getGeneratedTools(client);
1467
+ return { tools: [...TOOLS, ...generated.tools] };
1395
1468
  });
1396
1469
  server.setRequestHandler(import_types.CallToolRequestSchema, async (request) => {
1397
1470
  const { name, arguments: args } = request.params;
@@ -1399,6 +1472,11 @@ server.setRequestHandler(import_types.CallToolRequestSchema, async (request) =>
1399
1472
  if (!toolArgs.agent_id) {
1400
1473
  toolArgs.agent_id = AGENT_ID;
1401
1474
  }
1475
+ const generated = await getGeneratedTools(client);
1476
+ const desc = generated.byName.get(name);
1477
+ if (desc) {
1478
+ return handleGeneratedTool(client, desc, toolArgs);
1479
+ }
1402
1480
  return handleTool(client, name, toolArgs);
1403
1481
  });
1404
1482
  var PROMPTS = [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mindgraph-mcp",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "MCP server for MindGraph — connect Claude Desktop, Claude Code, and any MCP client to your knowledge graph",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -13,6 +13,7 @@
13
13
  "scripts": {
14
14
  "build": "tsup",
15
15
  "lint": "tsc --noEmit",
16
+ "test": "vitest run",
16
17
  "dev": "tsup --watch"
17
18
  },
18
19
  "keywords": [
@@ -27,11 +28,12 @@
27
28
  "license": "MIT",
28
29
  "dependencies": {
29
30
  "@modelcontextprotocol/sdk": "^1.12.1",
30
- "mindgraph": "^0.5.0"
31
+ "mindgraph": "^0.11.0"
31
32
  },
32
33
  "devDependencies": {
33
34
  "@types/node": "^22.0.0",
34
35
  "tsup": "^8.0.0",
35
- "typescript": "^5.7.0"
36
+ "typescript": "^5.7.0",
37
+ "vitest": "^3.0.0"
36
38
  }
37
39
  }