mindgraph-mcp 0.15.0 → 0.16.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 +1 -1
- package/dist/cli.js +489 -37
- package/dist/index.js +482 -37
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2412,6 +2412,361 @@ var init_sync_tool = __esm({
|
|
|
2412
2412
|
}
|
|
2413
2413
|
});
|
|
2414
2414
|
|
|
2415
|
+
// src/render.ts
|
|
2416
|
+
function obj(value) {
|
|
2417
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
2418
|
+
}
|
|
2419
|
+
function str(value) {
|
|
2420
|
+
return typeof value === "string" && value ? value : void 0;
|
|
2421
|
+
}
|
|
2422
|
+
function num(value) {
|
|
2423
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
2424
|
+
}
|
|
2425
|
+
function clip(value, max, state) {
|
|
2426
|
+
const text = str(value);
|
|
2427
|
+
if (!text) return void 0;
|
|
2428
|
+
if (text.length <= max) return text;
|
|
2429
|
+
state.bounded = true;
|
|
2430
|
+
return `${text.slice(0, max - 1)}\u2026`;
|
|
2431
|
+
}
|
|
2432
|
+
function isNodeShaped(value) {
|
|
2433
|
+
const node = obj(value);
|
|
2434
|
+
return Boolean(node && str(node.uid) && (str(node.label) || str(node.node_type)));
|
|
2435
|
+
}
|
|
2436
|
+
function unwrapSearchResult(value) {
|
|
2437
|
+
if (isNodeShaped(value)) return value;
|
|
2438
|
+
const wrapper = obj(value);
|
|
2439
|
+
const inner = obj(wrapper?.node);
|
|
2440
|
+
if (inner && isNodeShaped(inner)) {
|
|
2441
|
+
const score = num(wrapper.score);
|
|
2442
|
+
return score !== void 0 ? { ...inner, retrieval_score: score } : inner;
|
|
2443
|
+
}
|
|
2444
|
+
return void 0;
|
|
2445
|
+
}
|
|
2446
|
+
function statusTag(node) {
|
|
2447
|
+
const tags = [];
|
|
2448
|
+
if (node.superseded === true) {
|
|
2449
|
+
tags.push(
|
|
2450
|
+
node.superseded_by ? `SUPERSEDED by ${str(node.superseded_by)}` : "SUPERSEDED"
|
|
2451
|
+
);
|
|
2452
|
+
}
|
|
2453
|
+
const truth = str(node.truth_status) ?? str(obj(node.props)?.truth_status);
|
|
2454
|
+
if (truth && truth !== "accepted") tags.push(`truth: ${truth}`);
|
|
2455
|
+
if (node.currently_valid === false) tags.push("OUTSIDE ITS VALIDITY WINDOW");
|
|
2456
|
+
if (node.valid_at_time === false) tags.push("NOT VALID AT THE REQUESTED TIME");
|
|
2457
|
+
return tags.length > 0 ? ` [${tags.join("; ")}]` : "";
|
|
2458
|
+
}
|
|
2459
|
+
function firstQuote(node, state) {
|
|
2460
|
+
const chunks = Array.isArray(node.source_chunks) ? node.source_chunks : [];
|
|
2461
|
+
for (const raw of chunks) {
|
|
2462
|
+
const chunk = obj(raw);
|
|
2463
|
+
const quote = str(chunk?.quote) ?? str(obj(chunk?.anchor)?.exact);
|
|
2464
|
+
if (quote) return clip(quote, QUOTE_MAX, state);
|
|
2465
|
+
}
|
|
2466
|
+
return void 0;
|
|
2467
|
+
}
|
|
2468
|
+
function sourceTag(node) {
|
|
2469
|
+
const docs = Array.isArray(node.source_documents) ? node.source_documents : [];
|
|
2470
|
+
const first = obj(docs[0]);
|
|
2471
|
+
const title = str(first?.title);
|
|
2472
|
+
if (!title) return void 0;
|
|
2473
|
+
const extra = docs.length > 1 ? ` (+${docs.length - 1} more)` : "";
|
|
2474
|
+
return `from: ${title}${extra}`;
|
|
2475
|
+
}
|
|
2476
|
+
function layer7Fields(node, state) {
|
|
2477
|
+
const fields = obj(obj(node.props)?.fields);
|
|
2478
|
+
if (!fields) return void 0;
|
|
2479
|
+
const pairs = Object.entries(fields).filter(([, value]) => value !== null && value !== void 0 && value !== "").map(([key, value]) => `${key}: ${Array.isArray(value) ? value.join(", ") : String(value)}`);
|
|
2480
|
+
if (pairs.length === 0) return void 0;
|
|
2481
|
+
return clip(pairs.join("; "), SUMMARY_MAX, state);
|
|
2482
|
+
}
|
|
2483
|
+
function nodeSummaryText(node, state) {
|
|
2484
|
+
const props = obj(node.props);
|
|
2485
|
+
return clip(node.summary, SUMMARY_MAX, state) ?? clip(props?.content, SUMMARY_MAX, state) ?? clip(props?.description, SUMMARY_MAX, state) ?? clip(props?.definition, SUMMARY_MAX, state);
|
|
2486
|
+
}
|
|
2487
|
+
function nodeLines(node, state) {
|
|
2488
|
+
const label = clip(node.label, LABEL_MAX, state) || "(unlabeled)";
|
|
2489
|
+
const type = str(node.node_type) || "Node";
|
|
2490
|
+
const confidence = num(node.confidence);
|
|
2491
|
+
const score = num(node.retrieval_score);
|
|
2492
|
+
const parenthetical = [
|
|
2493
|
+
type,
|
|
2494
|
+
confidence !== void 0 ? `confidence ${confidence}` : void 0,
|
|
2495
|
+
score !== void 0 ? `score ${Math.round(score * 100) / 100}` : void 0
|
|
2496
|
+
].filter(Boolean).join(", ");
|
|
2497
|
+
const head = `- **${label}** [${str(node.uid) || "?"}] (${parenthetical})${statusTag(node)}`;
|
|
2498
|
+
const summary = nodeSummaryText(node, state);
|
|
2499
|
+
const lines = [summary && summary !== label ? `${head}: ${summary}` : head];
|
|
2500
|
+
const fields = layer7Fields(node, state);
|
|
2501
|
+
if (fields) lines.push(` - fields: ${fields}`);
|
|
2502
|
+
const quote = firstQuote(node, state);
|
|
2503
|
+
if (quote) lines.push(` - source quote: "${quote}"`);
|
|
2504
|
+
const source = sourceTag(node);
|
|
2505
|
+
if (source) lines.push(` - ${source}`);
|
|
2506
|
+
const believers = Array.isArray(node.believed_by) ? node.believed_by : [];
|
|
2507
|
+
if (believers.length > 0) {
|
|
2508
|
+
const rendered = believers.slice(0, 3).map((raw) => {
|
|
2509
|
+
const believer = obj(raw);
|
|
2510
|
+
const conf = num(believer?.confidence);
|
|
2511
|
+
return `${str(believer?.agent_label) || "?"}${conf !== void 0 ? ` (${conf})` : ""}`;
|
|
2512
|
+
}).join(", ");
|
|
2513
|
+
lines.push(` - believed by: ${rendered}`);
|
|
2514
|
+
}
|
|
2515
|
+
return lines.join("\n");
|
|
2516
|
+
}
|
|
2517
|
+
function groupedNodeSections(nodes, state, headingPrefix = "") {
|
|
2518
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2519
|
+
for (const node of nodes) {
|
|
2520
|
+
const type = str(node.node_type) || "Node";
|
|
2521
|
+
const group = groups.get(type) ?? [];
|
|
2522
|
+
group.push(node);
|
|
2523
|
+
groups.set(type, group);
|
|
2524
|
+
}
|
|
2525
|
+
const sections = [];
|
|
2526
|
+
let first = true;
|
|
2527
|
+
for (const [type, group] of groups) {
|
|
2528
|
+
sections.push({
|
|
2529
|
+
heading: `${first ? headingPrefix : ""}### ${type}`,
|
|
2530
|
+
items: group.map((node) => nodeLines(node, state))
|
|
2531
|
+
});
|
|
2532
|
+
first = false;
|
|
2533
|
+
}
|
|
2534
|
+
return sections;
|
|
2535
|
+
}
|
|
2536
|
+
function relationshipItems(edges, labelByUid, state) {
|
|
2537
|
+
const lines = [];
|
|
2538
|
+
for (const raw of edges) {
|
|
2539
|
+
const edge = obj(raw);
|
|
2540
|
+
if (!edge) continue;
|
|
2541
|
+
const type = str(edge.edge_type) || "?";
|
|
2542
|
+
const from = str(edge.from_uid);
|
|
2543
|
+
const to = str(edge.to_uid);
|
|
2544
|
+
if (CURATION_EDGE_TYPES.has(type)) continue;
|
|
2545
|
+
if (!from || !to || from === to) continue;
|
|
2546
|
+
const fromLabel = labelByUid.get(from);
|
|
2547
|
+
const toLabel = labelByUid.get(to);
|
|
2548
|
+
if (!fromLabel || !toLabel) continue;
|
|
2549
|
+
lines.push(`- ${fromLabel} \u2014${type}\u2192 ${toLabel}`);
|
|
2550
|
+
}
|
|
2551
|
+
if (lines.length > RELATIONSHIP_MAX) {
|
|
2552
|
+
state.bounded = true;
|
|
2553
|
+
const extra = lines.length - RELATIONSHIP_MAX;
|
|
2554
|
+
return [...lines.slice(0, RELATIONSHIP_MAX), `- (+${extra} more relationships)`];
|
|
2555
|
+
}
|
|
2556
|
+
return lines;
|
|
2557
|
+
}
|
|
2558
|
+
function assemble(header, sections, state) {
|
|
2559
|
+
const lines = [header];
|
|
2560
|
+
let used = header.length;
|
|
2561
|
+
for (const section of sections) {
|
|
2562
|
+
if (section.items.length === 0 && !section.heading) continue;
|
|
2563
|
+
const headingCost = section.heading.length + 2;
|
|
2564
|
+
if (used + headingCost > RENDER_CHAR_BUDGET) {
|
|
2565
|
+
state.bounded = true;
|
|
2566
|
+
continue;
|
|
2567
|
+
}
|
|
2568
|
+
let sectionUsed = 0;
|
|
2569
|
+
const kept = [];
|
|
2570
|
+
let dropped = 0;
|
|
2571
|
+
for (const item of section.items) {
|
|
2572
|
+
if (used + headingCost + sectionUsed + item.length + 1 > RENDER_CHAR_BUDGET) {
|
|
2573
|
+
dropped += 1;
|
|
2574
|
+
continue;
|
|
2575
|
+
}
|
|
2576
|
+
kept.push(item);
|
|
2577
|
+
sectionUsed += item.length + 1;
|
|
2578
|
+
}
|
|
2579
|
+
if (kept.length === 0 && section.items.length > 0) {
|
|
2580
|
+
state.bounded = true;
|
|
2581
|
+
continue;
|
|
2582
|
+
}
|
|
2583
|
+
if (dropped > 0) {
|
|
2584
|
+
state.bounded = true;
|
|
2585
|
+
const marker = `(+${dropped} more not shown)`;
|
|
2586
|
+
kept.push(marker);
|
|
2587
|
+
sectionUsed += marker.length + 1;
|
|
2588
|
+
}
|
|
2589
|
+
lines.push(
|
|
2590
|
+
section.items.length === 0 ? section.heading : `${section.heading}
|
|
2591
|
+
${kept.join("\n")}`
|
|
2592
|
+
);
|
|
2593
|
+
used += headingCost + sectionUsed;
|
|
2594
|
+
}
|
|
2595
|
+
if (state.bounded) {
|
|
2596
|
+
lines.push(
|
|
2597
|
+
'\u2026[rendered context bounded \u2014 fetch full node content by uid, or pass format: "json" for the raw response]'
|
|
2598
|
+
);
|
|
2599
|
+
}
|
|
2600
|
+
return lines.join("\n\n");
|
|
2601
|
+
}
|
|
2602
|
+
function policySections(payload, state) {
|
|
2603
|
+
const policies = Array.isArray(payload.applicable_policies) ? payload.applicable_policies : [];
|
|
2604
|
+
if (policies.length === 0) return [];
|
|
2605
|
+
const items = policies.map((raw) => obj(raw)).filter((policy) => Boolean(policy)).map((policy) => {
|
|
2606
|
+
const rules = Array.isArray(policy.prose_rules) ? policy.prose_rules.map(String).join("; ") : policy.prose_rules;
|
|
2607
|
+
return `- ${str(policy.name) || "(unnamed policy)"}: ${clip(rules, SUMMARY_MAX, state) || "(structured rules apply)"}`;
|
|
2608
|
+
});
|
|
2609
|
+
return [{ heading: "## Applicable policies", items }];
|
|
2610
|
+
}
|
|
2611
|
+
function hiddenSections(payload) {
|
|
2612
|
+
const hidden = payload.hidden_item_count;
|
|
2613
|
+
if (hidden === void 0 || hidden === null) return [];
|
|
2614
|
+
return [
|
|
2615
|
+
{
|
|
2616
|
+
heading: `Note: ${hidden} item(s) were hidden by access scoping.`,
|
|
2617
|
+
items: []
|
|
2618
|
+
}
|
|
2619
|
+
];
|
|
2620
|
+
}
|
|
2621
|
+
function renderContext(response) {
|
|
2622
|
+
const payload = obj(response);
|
|
2623
|
+
if (!payload) return void 0;
|
|
2624
|
+
const graph = obj(payload.graph);
|
|
2625
|
+
const rawNodes = Array.isArray(graph?.nodes) ? graph.nodes : [];
|
|
2626
|
+
const shaped = rawNodes.filter(isNodeShaped);
|
|
2627
|
+
const nodes = shaped.filter((node) => !INFRA_NODE_TYPES.has(str(node.node_type) || ""));
|
|
2628
|
+
const infraOnly = shaped.length - nodes.length;
|
|
2629
|
+
const articles = Array.isArray(payload.articles) ? payload.articles : [];
|
|
2630
|
+
const chunks = Array.isArray(payload.chunks) ? payload.chunks : [];
|
|
2631
|
+
const state = { bounded: false };
|
|
2632
|
+
const sections = [];
|
|
2633
|
+
if (articles.length > 0) {
|
|
2634
|
+
sections.push({
|
|
2635
|
+
heading: "## Knowledge articles",
|
|
2636
|
+
items: articles.map((raw) => obj(raw)).filter((article) => Boolean(article)).map(
|
|
2637
|
+
(article) => `### ${str(article.label) || "(untitled)"} [${str(article.uid) || "?"}]
|
|
2638
|
+
${clip(article.content, ARTICLE_MAX, state) || ""}`
|
|
2639
|
+
)
|
|
2640
|
+
});
|
|
2641
|
+
}
|
|
2642
|
+
if (nodes.length > 0) {
|
|
2643
|
+
sections.push(...groupedNodeSections(nodes, state, "## Knowledge graph\n\n"));
|
|
2644
|
+
const labelByUid = /* @__PURE__ */ new Map();
|
|
2645
|
+
for (const node of nodes) labelByUid.set(str(node.uid), str(node.label) || "?");
|
|
2646
|
+
const relationships = relationshipItems(
|
|
2647
|
+
Array.isArray(graph?.edges) ? graph.edges : [],
|
|
2648
|
+
labelByUid,
|
|
2649
|
+
state
|
|
2650
|
+
);
|
|
2651
|
+
if (relationships.length > 0) {
|
|
2652
|
+
sections.push({ heading: "## Relationships", items: relationships });
|
|
2653
|
+
}
|
|
2654
|
+
} else if (infraOnly > 0) {
|
|
2655
|
+
sections.push({
|
|
2656
|
+
heading: `Note: ${infraOnly} document/chunk node(s) matched \u2014 use action "document_index" or include_chunks for their content.`,
|
|
2657
|
+
items: []
|
|
2658
|
+
});
|
|
2659
|
+
}
|
|
2660
|
+
if (chunks.length > 0) {
|
|
2661
|
+
sections.push({
|
|
2662
|
+
heading: "## Source excerpts",
|
|
2663
|
+
items: chunks.map((raw) => obj(raw)).filter((chunk) => Boolean(chunk)).map(
|
|
2664
|
+
(chunk) => `- [${str(chunk.chunk_uid) || "?"}] from "${str(chunk.document_title) || "unknown document"}": "${clip(chunk.content, CHUNK_MAX, state) || ""}"`
|
|
2665
|
+
)
|
|
2666
|
+
});
|
|
2667
|
+
}
|
|
2668
|
+
sections.push(...policySections(payload, state));
|
|
2669
|
+
sections.push(...hiddenSections(payload));
|
|
2670
|
+
if (sections.length === 0) {
|
|
2671
|
+
return "No results found for this search.";
|
|
2672
|
+
}
|
|
2673
|
+
if (nodes.length === 0 && articles.length === 0 && chunks.length === 0) {
|
|
2674
|
+
return assemble("# Retrieved context\n\nNo graph results matched this search.", sections, state);
|
|
2675
|
+
}
|
|
2676
|
+
return assemble("# Retrieved context", sections, state);
|
|
2677
|
+
}
|
|
2678
|
+
function renderNodeList(response, title, limit) {
|
|
2679
|
+
let candidates = [];
|
|
2680
|
+
if (Array.isArray(response)) {
|
|
2681
|
+
candidates = response;
|
|
2682
|
+
} else {
|
|
2683
|
+
const payload = obj(response);
|
|
2684
|
+
if (!payload) return void 0;
|
|
2685
|
+
for (const key of ["nodes", "results", "items", "goals", "questions", "claims", "preferences", "documents"]) {
|
|
2686
|
+
const candidate = payload[key];
|
|
2687
|
+
if (Array.isArray(candidate) && candidate.some((entry) => unwrapSearchResult(entry))) {
|
|
2688
|
+
candidates = candidate;
|
|
2689
|
+
break;
|
|
2690
|
+
}
|
|
2691
|
+
}
|
|
2692
|
+
}
|
|
2693
|
+
let nodes = candidates.map(unwrapSearchResult).filter((node) => Boolean(node));
|
|
2694
|
+
if (nodes.length === 0) return void 0;
|
|
2695
|
+
const total = nodes.length;
|
|
2696
|
+
const state = { bounded: false };
|
|
2697
|
+
if (limit !== void 0 && limit > 0 && nodes.length > limit) {
|
|
2698
|
+
nodes = nodes.slice(0, limit);
|
|
2699
|
+
state.bounded = true;
|
|
2700
|
+
}
|
|
2701
|
+
const sections = groupedNodeSections(nodes, state);
|
|
2702
|
+
const shown = nodes.length;
|
|
2703
|
+
return assemble(
|
|
2704
|
+
`# ${title} (${shown === total ? total : `${shown} of ${total}`})`,
|
|
2705
|
+
sections,
|
|
2706
|
+
state
|
|
2707
|
+
);
|
|
2708
|
+
}
|
|
2709
|
+
function renderTraversal(response) {
|
|
2710
|
+
const payload = obj(response);
|
|
2711
|
+
if (!payload) return void 0;
|
|
2712
|
+
const container = obj(payload.graph) ?? payload;
|
|
2713
|
+
const rawNodes = Array.isArray(container.nodes) ? container.nodes : [];
|
|
2714
|
+
const nodes = rawNodes.filter(isNodeShaped);
|
|
2715
|
+
const paths = Array.isArray(payload.paths) ? payload.paths : [];
|
|
2716
|
+
if (nodes.length === 0 && paths.length === 0) return void 0;
|
|
2717
|
+
const state = { bounded: false };
|
|
2718
|
+
const sections = [];
|
|
2719
|
+
const labelByUid = /* @__PURE__ */ new Map();
|
|
2720
|
+
for (const node of nodes) labelByUid.set(str(node.uid), str(node.label) || "?");
|
|
2721
|
+
if (paths.length > 0) {
|
|
2722
|
+
const items = paths.map((raw) => obj(raw)).filter((path6) => Boolean(path6)).map((path6, index) => {
|
|
2723
|
+
const uids = Array.isArray(path6.node_uids) ? path6.node_uids.map(String) : [];
|
|
2724
|
+
const labels = Array.isArray(path6.labels) ? path6.labels.map(String) : Array.isArray(path6.nodes) ? path6.nodes.map((hop) => {
|
|
2725
|
+
const hopNode = obj(hop);
|
|
2726
|
+
return str(hopNode?.label) || labelByUid.get(str(hopNode?.uid) || "") || str(hop) || "?";
|
|
2727
|
+
}) : [];
|
|
2728
|
+
const route = labels.length > 0 ? labels.join(" \u2192 ") : uids.join(" \u2192 ") || "(path)";
|
|
2729
|
+
const cost = num(path6.cost) ?? num(path6.path_cost);
|
|
2730
|
+
const confidence = num(path6.path_confidence);
|
|
2731
|
+
const meta = [
|
|
2732
|
+
cost !== void 0 ? `cost ${cost}` : void 0,
|
|
2733
|
+
confidence !== void 0 ? `confidence ${confidence}` : void 0
|
|
2734
|
+
].filter(Boolean);
|
|
2735
|
+
const head = `${index + 1}. ${route}${meta.length > 0 ? ` (${meta.join(", ")})` : ""}`;
|
|
2736
|
+
return labels.length > 0 && uids.length > 0 ? `${head}
|
|
2737
|
+
uids: ${uids.join(" \u2192 ")}` : head;
|
|
2738
|
+
});
|
|
2739
|
+
sections.push({ heading: "## Paths", items });
|
|
2740
|
+
}
|
|
2741
|
+
if (nodes.length > 0) {
|
|
2742
|
+
sections.push(...groupedNodeSections(nodes, state, "## Nodes\n\n"));
|
|
2743
|
+
const relationships = relationshipItems(
|
|
2744
|
+
Array.isArray(container.edges) ? container.edges : [],
|
|
2745
|
+
labelByUid,
|
|
2746
|
+
state
|
|
2747
|
+
);
|
|
2748
|
+
if (relationships.length > 0) {
|
|
2749
|
+
sections.push({ heading: "## Relationships", items: relationships });
|
|
2750
|
+
}
|
|
2751
|
+
}
|
|
2752
|
+
return assemble("# Graph traversal", sections, state);
|
|
2753
|
+
}
|
|
2754
|
+
var LABEL_MAX, SUMMARY_MAX, QUOTE_MAX, ARTICLE_MAX, CHUNK_MAX, RELATIONSHIP_MAX, RENDER_CHAR_BUDGET, CURATION_EDGE_TYPES, INFRA_NODE_TYPES;
|
|
2755
|
+
var init_render = __esm({
|
|
2756
|
+
"src/render.ts"() {
|
|
2757
|
+
"use strict";
|
|
2758
|
+
LABEL_MAX = 200;
|
|
2759
|
+
SUMMARY_MAX = 400;
|
|
2760
|
+
QUOTE_MAX = 180;
|
|
2761
|
+
ARTICLE_MAX = 6e3;
|
|
2762
|
+
CHUNK_MAX = 6e3;
|
|
2763
|
+
RELATIONSHIP_MAX = 40;
|
|
2764
|
+
RENDER_CHAR_BUDGET = 24e3;
|
|
2765
|
+
CURATION_EDGE_TYPES = /* @__PURE__ */ new Set(["POSSIBLE_DUPLICATE", "PossibleDuplicate"]);
|
|
2766
|
+
INFRA_NODE_TYPES = /* @__PURE__ */ new Set(["Chunk", "Document", "Article"]);
|
|
2767
|
+
}
|
|
2768
|
+
});
|
|
2769
|
+
|
|
2415
2770
|
// src/tools.ts
|
|
2416
2771
|
function toolsForProfile(profile = process.env.MINDGRAPH_PROFILE || "general") {
|
|
2417
2772
|
if (profile === "coding") return TOOLS;
|
|
@@ -3005,6 +3360,7 @@ async function handlePlan(client2, args) {
|
|
|
3005
3360
|
async function handleRetrieve(client2, args) {
|
|
3006
3361
|
const {
|
|
3007
3362
|
action,
|
|
3363
|
+
format,
|
|
3008
3364
|
query,
|
|
3009
3365
|
start_uid,
|
|
3010
3366
|
end_uid,
|
|
@@ -3025,12 +3381,24 @@ async function handleRetrieve(client2, args) {
|
|
|
3025
3381
|
graph_max_depth,
|
|
3026
3382
|
confidence_min,
|
|
3027
3383
|
salience_min,
|
|
3384
|
+
article_limit,
|
|
3385
|
+
valid_at,
|
|
3386
|
+
created_after,
|
|
3387
|
+
created_before,
|
|
3388
|
+
explain,
|
|
3389
|
+
k,
|
|
3390
|
+
max_cost,
|
|
3028
3391
|
agent_id
|
|
3029
3392
|
} = args;
|
|
3393
|
+
const rendered = (raw, render) => {
|
|
3394
|
+
if (format === "json") return ok3(raw);
|
|
3395
|
+
const text = render(raw);
|
|
3396
|
+
return text !== void 0 ? { content: [{ type: "text", text }] } : ok3(raw);
|
|
3397
|
+
};
|
|
3030
3398
|
switch (action) {
|
|
3031
3399
|
case "context":
|
|
3032
3400
|
if (!query) return err3("query is required for context retrieval");
|
|
3033
|
-
return
|
|
3401
|
+
return rendered(
|
|
3034
3402
|
await client2.retrieveContext({
|
|
3035
3403
|
query,
|
|
3036
3404
|
node_limit: limit,
|
|
@@ -3039,110 +3407,139 @@ async function handleRetrieve(client2, args) {
|
|
|
3039
3407
|
chunk_limit: include_chunks ? limit ?? 5 : 0,
|
|
3040
3408
|
// Scoped by default, never walled: the coding profile drops the
|
|
3041
3409
|
// document/article leg (the "Bob Work" class of noise) unless the
|
|
3042
|
-
// model opts in for a genuinely document-shaped question.
|
|
3043
|
-
|
|
3410
|
+
// model opts in for a genuinely document-shaped question. Any
|
|
3411
|
+
// profile can drop it with include_documents: false (previously a
|
|
3412
|
+
// silent no-op outside coding). An EXPLICIT article_limit is at
|
|
3413
|
+
// least as clear an opt-in as include_documents:true — it always
|
|
3414
|
+
// wins over profile defaults (a coding-profile model reaching for
|
|
3415
|
+
// the advertised knob must not get a silent no-op).
|
|
3416
|
+
...article_limit !== void 0 ? { article_limit } : include_documents === false || process.env.MINDGRAPH_PROFILE === "coding" && !include_documents ? { article_limit: 0 } : {},
|
|
3417
|
+
valid_at,
|
|
3044
3418
|
include_graph,
|
|
3045
3419
|
graph_expansion_limit: graph_expansion_limit ?? 3,
|
|
3046
3420
|
graph_max_depth: graph_max_depth ?? 2
|
|
3047
|
-
})
|
|
3421
|
+
}),
|
|
3422
|
+
renderContext
|
|
3048
3423
|
);
|
|
3049
3424
|
case "text":
|
|
3050
3425
|
if (!query) return err3("query is required for text search");
|
|
3051
|
-
return
|
|
3426
|
+
return rendered(
|
|
3052
3427
|
await client2.retrieve({
|
|
3053
3428
|
action: "text",
|
|
3054
3429
|
query,
|
|
3055
3430
|
limit,
|
|
3056
3431
|
node_types,
|
|
3057
3432
|
layer,
|
|
3433
|
+
explain,
|
|
3058
3434
|
agent_id
|
|
3059
|
-
})
|
|
3435
|
+
}),
|
|
3436
|
+
(raw) => renderNodeList(raw, `Text search: ${query}`)
|
|
3060
3437
|
);
|
|
3061
3438
|
case "semantic":
|
|
3062
3439
|
if (!query) return err3("query is required for semantic search");
|
|
3063
|
-
return
|
|
3440
|
+
return rendered(
|
|
3064
3441
|
await client2.retrieve({
|
|
3065
3442
|
action: "semantic",
|
|
3066
3443
|
query,
|
|
3067
3444
|
k: limit,
|
|
3068
3445
|
node_types,
|
|
3069
3446
|
layer,
|
|
3447
|
+
explain,
|
|
3070
3448
|
agent_id
|
|
3071
|
-
})
|
|
3449
|
+
}),
|
|
3450
|
+
(raw) => renderNodeList(raw, `Semantic search: ${query}`)
|
|
3072
3451
|
);
|
|
3073
3452
|
case "hybrid":
|
|
3074
3453
|
if (!query) return err3("query is required for hybrid search");
|
|
3075
|
-
return
|
|
3454
|
+
return rendered(
|
|
3076
3455
|
await client2.retrieve({
|
|
3077
3456
|
action: "hybrid",
|
|
3078
3457
|
query,
|
|
3079
3458
|
k: limit,
|
|
3080
3459
|
node_types,
|
|
3081
3460
|
layer,
|
|
3461
|
+
explain,
|
|
3082
3462
|
agent_id
|
|
3083
|
-
})
|
|
3463
|
+
}),
|
|
3464
|
+
// With explain, per-leg scoring must survive — raw JSON only.
|
|
3465
|
+
explain ? () => void 0 : (raw) => renderNodeList(raw, `Hybrid search: ${query}`)
|
|
3084
3466
|
);
|
|
3085
|
-
// Structured queries
|
|
3467
|
+
// Structured queries. These go through the generic /retrieve action
|
|
3468
|
+
// dispatch rather than the SDK's no-argument convenience methods — the
|
|
3469
|
+
// conveniences silently dropped `limit`/`offset` (the tool advertised
|
|
3470
|
+
// them; five actions ignored them).
|
|
3086
3471
|
case "active_goals":
|
|
3087
|
-
return ok3(await client2.getGoals());
|
|
3088
3472
|
case "open_questions":
|
|
3089
|
-
return ok3(await client2.getOpenQuestions());
|
|
3090
3473
|
case "weak_claims":
|
|
3091
|
-
return ok3(await client2.getWeakClaims());
|
|
3092
3474
|
case "pending_approvals":
|
|
3093
|
-
return ok3(await client2.getPendingApprovals());
|
|
3094
3475
|
case "unresolved_contradictions":
|
|
3095
|
-
return
|
|
3476
|
+
return rendered(
|
|
3477
|
+
await client2.retrieve({
|
|
3478
|
+
action,
|
|
3479
|
+
limit,
|
|
3480
|
+
offset,
|
|
3481
|
+
agent_id
|
|
3482
|
+
}),
|
|
3483
|
+
(raw) => renderNodeList(raw, action.replace(/_/g, " "), limit)
|
|
3484
|
+
);
|
|
3096
3485
|
case "stale_derivations":
|
|
3097
|
-
return
|
|
3486
|
+
return rendered(
|
|
3098
3487
|
await client2.retrieve({
|
|
3099
3488
|
action: "stale_derivations",
|
|
3100
3489
|
limit,
|
|
3101
3490
|
offset
|
|
3102
|
-
})
|
|
3491
|
+
}),
|
|
3492
|
+
(raw) => renderNodeList(raw, "stale derivations", limit)
|
|
3103
3493
|
);
|
|
3104
3494
|
case "preferences":
|
|
3105
|
-
return
|
|
3495
|
+
return rendered(
|
|
3106
3496
|
await client2.retrieve({
|
|
3107
3497
|
action: "preferences",
|
|
3108
3498
|
...query ? { query, k: limit } : {},
|
|
3109
3499
|
limit,
|
|
3110
3500
|
agent_id
|
|
3111
|
-
})
|
|
3501
|
+
}),
|
|
3502
|
+
(raw) => renderNodeList(raw, "Preferences", limit)
|
|
3112
3503
|
);
|
|
3113
3504
|
case "layer":
|
|
3114
3505
|
if (!layer) return err3("layer is required for layer retrieval");
|
|
3115
|
-
return
|
|
3506
|
+
return rendered(
|
|
3116
3507
|
await client2.retrieve({
|
|
3117
3508
|
action: "layer",
|
|
3118
3509
|
layer,
|
|
3119
3510
|
limit,
|
|
3511
|
+
node_types,
|
|
3120
3512
|
agent_id
|
|
3121
|
-
})
|
|
3513
|
+
}),
|
|
3514
|
+
(raw) => renderNodeList(raw, `Layer: ${layer}`, limit)
|
|
3122
3515
|
);
|
|
3123
3516
|
case "recent":
|
|
3124
|
-
return
|
|
3517
|
+
return rendered(
|
|
3125
3518
|
await client2.retrieve({
|
|
3126
3519
|
action: "recent",
|
|
3127
3520
|
limit,
|
|
3128
3521
|
node_types,
|
|
3129
3522
|
confidence_min,
|
|
3130
3523
|
salience_min,
|
|
3524
|
+
created_after,
|
|
3525
|
+
created_before,
|
|
3131
3526
|
agent_id
|
|
3132
|
-
})
|
|
3527
|
+
}),
|
|
3528
|
+
(raw) => renderNodeList(raw, "Recently ingested", limit)
|
|
3133
3529
|
);
|
|
3134
3530
|
// Document inventory
|
|
3135
3531
|
case "document_index":
|
|
3136
|
-
return
|
|
3532
|
+
return rendered(
|
|
3137
3533
|
await client2.getNodes({
|
|
3138
3534
|
node_type: "Document",
|
|
3139
3535
|
limit: limit ?? 100
|
|
3140
|
-
})
|
|
3536
|
+
}),
|
|
3537
|
+
(raw) => renderNodeList(raw, "Ingested documents")
|
|
3141
3538
|
);
|
|
3142
3539
|
// Graph traversal
|
|
3143
3540
|
case "chain":
|
|
3144
3541
|
if (!start_uid) return err3("start_uid is required for chain traversal");
|
|
3145
|
-
return
|
|
3542
|
+
return rendered(
|
|
3146
3543
|
await client2.traverse({
|
|
3147
3544
|
action: "chain",
|
|
3148
3545
|
start_uid,
|
|
@@ -3150,11 +3547,12 @@ async function handleRetrieve(client2, args) {
|
|
|
3150
3547
|
exclude_edge_types,
|
|
3151
3548
|
include_provenance,
|
|
3152
3549
|
max_nodes
|
|
3153
|
-
})
|
|
3550
|
+
}),
|
|
3551
|
+
renderTraversal
|
|
3154
3552
|
);
|
|
3155
3553
|
case "neighborhood":
|
|
3156
3554
|
if (!start_uid) return err3("start_uid is required for neighborhood traversal");
|
|
3157
|
-
return
|
|
3555
|
+
return rendered(
|
|
3158
3556
|
await client2.traverse({
|
|
3159
3557
|
action: "neighborhood",
|
|
3160
3558
|
start_uid,
|
|
@@ -3164,12 +3562,13 @@ async function handleRetrieve(client2, args) {
|
|
|
3164
3562
|
exclude_edge_types,
|
|
3165
3563
|
include_provenance,
|
|
3166
3564
|
max_nodes
|
|
3167
|
-
})
|
|
3565
|
+
}),
|
|
3566
|
+
renderTraversal
|
|
3168
3567
|
);
|
|
3169
3568
|
case "path":
|
|
3170
3569
|
if (!start_uid) return err3("start_uid is required for path traversal");
|
|
3171
3570
|
if (!end_uid) return err3("end_uid is required for path traversal");
|
|
3172
|
-
return
|
|
3571
|
+
return rendered(
|
|
3173
3572
|
await client2.traverse({
|
|
3174
3573
|
action: "path",
|
|
3175
3574
|
start_uid,
|
|
@@ -3180,11 +3579,12 @@ async function handleRetrieve(client2, args) {
|
|
|
3180
3579
|
exclude_edge_types,
|
|
3181
3580
|
include_provenance,
|
|
3182
3581
|
max_nodes
|
|
3183
|
-
})
|
|
3582
|
+
}),
|
|
3583
|
+
renderTraversal
|
|
3184
3584
|
);
|
|
3185
3585
|
case "subgraph":
|
|
3186
3586
|
if (!start_uid) return err3("start_uid is required for subgraph traversal");
|
|
3187
|
-
return
|
|
3587
|
+
return rendered(
|
|
3188
3588
|
await client2.traverse({
|
|
3189
3589
|
action: "subgraph",
|
|
3190
3590
|
start_uid,
|
|
@@ -3194,7 +3594,24 @@ async function handleRetrieve(client2, args) {
|
|
|
3194
3594
|
exclude_edge_types,
|
|
3195
3595
|
include_provenance,
|
|
3196
3596
|
max_nodes
|
|
3197
|
-
})
|
|
3597
|
+
}),
|
|
3598
|
+
renderTraversal
|
|
3599
|
+
);
|
|
3600
|
+
case "top_k_paths":
|
|
3601
|
+
if (!start_uid) return err3("start_uid is required for top_k_paths");
|
|
3602
|
+
if (!end_uid) return err3("end_uid is required for top_k_paths");
|
|
3603
|
+
return rendered(
|
|
3604
|
+
await client2.traverse({
|
|
3605
|
+
action: "top_k_paths",
|
|
3606
|
+
start_uid,
|
|
3607
|
+
end_uid,
|
|
3608
|
+
k,
|
|
3609
|
+
max_hops: max_depth,
|
|
3610
|
+
max_cost,
|
|
3611
|
+
edge_types,
|
|
3612
|
+
exclude_edge_types
|
|
3613
|
+
}),
|
|
3614
|
+
renderTraversal
|
|
3198
3615
|
);
|
|
3199
3616
|
default:
|
|
3200
3617
|
return err3(`Unknown retrieve action: ${action}`);
|
|
@@ -3440,6 +3857,7 @@ var init_tools = __esm({
|
|
|
3440
3857
|
init_code_tool();
|
|
3441
3858
|
init_codegraph();
|
|
3442
3859
|
init_sync_tool();
|
|
3860
|
+
init_render();
|
|
3443
3861
|
TOOLS = [
|
|
3444
3862
|
{
|
|
3445
3863
|
name: "mindgraph_capture",
|
|
@@ -4013,10 +4431,16 @@ var init_tools = __esm({
|
|
|
4013
4431
|
"neighborhood",
|
|
4014
4432
|
"path",
|
|
4015
4433
|
"subgraph",
|
|
4434
|
+
"top_k_paths",
|
|
4016
4435
|
"document_index"
|
|
4017
4436
|
],
|
|
4018
4437
|
description: "Retrieval or traversal strategy"
|
|
4019
4438
|
},
|
|
4439
|
+
format: {
|
|
4440
|
+
type: "string",
|
|
4441
|
+
enum: ["text", "json"],
|
|
4442
|
+
description: "Output format (default 'text'): a compact rendered context block \u2014 labels, uids, summaries, status tags, relationships. Pass 'json' for the raw server response (full props, source-chunk offsets, retrieval metadata)."
|
|
4443
|
+
},
|
|
4020
4444
|
query: {
|
|
4021
4445
|
type: "string",
|
|
4022
4446
|
description: "Search query \u2014 for 'text'/'context': 1-3 BM25 keywords, drop filler words. For 'semantic'/'hybrid': natural language is OK since it uses vector similarity."
|
|
@@ -4076,7 +4500,35 @@ var init_tools = __esm({
|
|
|
4076
4500
|
},
|
|
4077
4501
|
include_documents: {
|
|
4078
4502
|
type: "boolean",
|
|
4079
|
-
description: "Include ingested documents and wiki articles in context results. The coding profile defaults these OFF so engineering questions aren't answered from unrelated ingested content; pass true when the question is about a document, spec, or article."
|
|
4503
|
+
description: "Include ingested documents and wiki articles in context results. The coding profile defaults these OFF so engineering questions aren't answered from unrelated ingested content; pass true when the question is about a document, spec, or article \u2014 or false in any profile to drop the article leg."
|
|
4504
|
+
},
|
|
4505
|
+
article_limit: {
|
|
4506
|
+
type: "number",
|
|
4507
|
+
description: "Max wiki articles in context results (default 3; 0 disables the article leg). An explicit value overrides profile defaults, including the coding profile's articles-off default."
|
|
4508
|
+
},
|
|
4509
|
+
valid_at: {
|
|
4510
|
+
type: "string",
|
|
4511
|
+
description: "ISO-8601 date (e.g. '2021-06') for context: nodes with validity windows are annotated as of this date instead of today"
|
|
4512
|
+
},
|
|
4513
|
+
created_after: {
|
|
4514
|
+
type: "number",
|
|
4515
|
+
description: "Unix seconds \u2014 'recent' action: only nodes ingested after this time"
|
|
4516
|
+
},
|
|
4517
|
+
created_before: {
|
|
4518
|
+
type: "number",
|
|
4519
|
+
description: "Unix seconds \u2014 'recent' action: only nodes ingested before this time"
|
|
4520
|
+
},
|
|
4521
|
+
explain: {
|
|
4522
|
+
type: "boolean",
|
|
4523
|
+
description: "hybrid action: include per-leg retrieval scoring (legs) in the response"
|
|
4524
|
+
},
|
|
4525
|
+
k: {
|
|
4526
|
+
type: "number",
|
|
4527
|
+
description: "top_k_paths action: number of cheapest paths (default 3, cap 25)"
|
|
4528
|
+
},
|
|
4529
|
+
max_cost: {
|
|
4530
|
+
type: "number",
|
|
4531
|
+
description: "top_k_paths action: optional cost ceiling for returned paths"
|
|
4080
4532
|
},
|
|
4081
4533
|
include_chunks: {
|
|
4082
4534
|
type: "boolean",
|
|
@@ -4907,7 +5359,7 @@ async function main() {
|
|
|
4907
5359
|
const transport = new import_stdio.StdioServerTransport();
|
|
4908
5360
|
await server.connect(transport);
|
|
4909
5361
|
console.error(
|
|
4910
|
-
`MindGraph MCP server v${"0.
|
|
5362
|
+
`MindGraph MCP server v${"0.16.0"} running on stdio (${BASE_URL})`
|
|
4911
5363
|
);
|
|
4912
5364
|
}
|
|
4913
5365
|
var import_server, import_stdio, import_types, API_KEY, BASE_URL, AGENT_ID, ORG_ID, PROFILE, HARNESS, client, INSTRUCTIONS, server, GOVERNANCE_CONFIG, PROMPTS;
|
|
@@ -5047,7 +5499,7 @@ Capture knowledge when the user shares something worth remembering:
|
|
|
5047
5499
|
server = new import_server.Server(
|
|
5048
5500
|
{
|
|
5049
5501
|
name: "mindgraph",
|
|
5050
|
-
version: "0.
|
|
5502
|
+
version: "0.16.0"
|
|
5051
5503
|
},
|
|
5052
5504
|
{
|
|
5053
5505
|
capabilities: {
|