mindgraph-mcp 0.15.0 → 0.17.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/cli.js CHANGED
@@ -2123,13 +2123,13 @@ var init_code_tool = __esm({
2123
2123
  init_error_detail();
2124
2124
  init_codegraph();
2125
2125
  ok = (data) => ({
2126
- content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
2126
+ content: [{ type: "text", text: JSON.stringify(data) }]
2127
2127
  });
2128
2128
  err = (code, message, details) => ({
2129
2129
  content: [
2130
2130
  {
2131
2131
  type: "text",
2132
- text: JSON.stringify({ error: message, code, details }, null, 2)
2132
+ text: JSON.stringify({ error: message, code, details })
2133
2133
  }
2134
2134
  ],
2135
2135
  isError: true
@@ -2335,7 +2335,7 @@ var init_sync_tool = __esm({
2335
2335
  init_codegraph();
2336
2336
  init_error_detail();
2337
2337
  ok2 = (data) => ({
2338
- content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
2338
+ content: [{ type: "text", text: JSON.stringify(data) }]
2339
2339
  });
2340
2340
  err2 = (code, message) => ({
2341
2341
  content: [{ type: "text", text: JSON.stringify({ code, error: message }) }],
@@ -2412,6 +2412,523 @@ 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(
2435
+ node && str(node.uid) && (str(node.label) || str(node.node_type) || obj(node.node_type))
2436
+ );
2437
+ }
2438
+ function nodeDisplayType(node) {
2439
+ const props = obj(node.props);
2440
+ const plain = str(node.node_type);
2441
+ return (plain && plain !== "Custom" ? plain : void 0) ?? str(obj(node.node_type)?.Custom) ?? str(props?.object_type) ?? str(props?.type_name) ?? str(obj(props?.data)?.domain_type) ?? plain ?? "Node";
2442
+ }
2443
+ function nodeFields(node) {
2444
+ const props = obj(node.props);
2445
+ return obj(props?.fields) ?? obj(obj(props?.data)?.fields);
2446
+ }
2447
+ function unwrapSearchResult(value) {
2448
+ if (isNodeShaped(value)) return value;
2449
+ const wrapper = obj(value);
2450
+ const inner = obj(wrapper?.node) ?? obj(wrapper?.object);
2451
+ if (inner && isNodeShaped(inner)) {
2452
+ const score = num(wrapper.score);
2453
+ return score !== void 0 ? { ...inner, retrieval_score: score } : inner;
2454
+ }
2455
+ return void 0;
2456
+ }
2457
+ function statusTag(node) {
2458
+ const tags = [];
2459
+ if (node.superseded === true) {
2460
+ tags.push(
2461
+ node.superseded_by ? `SUPERSEDED by ${str(node.superseded_by)}` : "SUPERSEDED"
2462
+ );
2463
+ }
2464
+ const truth = str(node.truth_status) ?? str(obj(node.props)?.truth_status);
2465
+ if (truth && truth !== "accepted") tags.push(`truth: ${truth}`);
2466
+ if (node.currently_valid === false) tags.push("OUTSIDE ITS VALIDITY WINDOW");
2467
+ if (node.valid_at_time === false) tags.push("NOT VALID AT THE REQUESTED TIME");
2468
+ return tags.length > 0 ? ` [${tags.join("; ")}]` : "";
2469
+ }
2470
+ function firstQuote(node, state) {
2471
+ const chunks = Array.isArray(node.source_chunks) ? node.source_chunks : [];
2472
+ for (const raw of chunks) {
2473
+ const chunk = obj(raw);
2474
+ const quote = str(chunk?.quote) ?? str(obj(chunk?.anchor)?.exact);
2475
+ if (quote) return clip(quote, QUOTE_MAX, state);
2476
+ }
2477
+ return void 0;
2478
+ }
2479
+ function sourceTag(node) {
2480
+ const docs = Array.isArray(node.source_documents) ? node.source_documents : [];
2481
+ const first = obj(docs[0]);
2482
+ const title = str(first?.title);
2483
+ if (!title) return void 0;
2484
+ const extra = docs.length > 1 ? ` (+${docs.length - 1} more)` : "";
2485
+ return `from: ${title}${extra}`;
2486
+ }
2487
+ function layer7Fields(node, state) {
2488
+ const fields = nodeFields(node);
2489
+ if (!fields) return void 0;
2490
+ const pairs = Object.entries(fields).filter(([, value]) => value !== null && value !== void 0 && value !== "").map(([key, value]) => `${key}: ${Array.isArray(value) ? value.join(", ") : String(value)}`);
2491
+ if (pairs.length === 0) return void 0;
2492
+ return clip(pairs.join("; "), SUMMARY_MAX, state);
2493
+ }
2494
+ function nodeSummaryText(node, state) {
2495
+ const props = obj(node.props);
2496
+ 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);
2497
+ }
2498
+ function nodeLines(node, state) {
2499
+ const label = clip(node.label, LABEL_MAX, state) || "(unlabeled)";
2500
+ const type = nodeDisplayType(node);
2501
+ const confidence = num(node.confidence);
2502
+ const score = num(node.retrieval_score);
2503
+ const parenthetical = [
2504
+ type,
2505
+ confidence !== void 0 ? `confidence ${confidence}` : void 0,
2506
+ score !== void 0 ? `score ${Math.round(score * 100) / 100}` : void 0
2507
+ ].filter(Boolean).join(", ");
2508
+ const head = `- **${label}** [${str(node.uid) || "?"}] (${parenthetical})${statusTag(node)}`;
2509
+ const summary = nodeSummaryText(node, state);
2510
+ const lines = [summary && summary !== label ? `${head}: ${summary}` : head];
2511
+ const fields = layer7Fields(node, state);
2512
+ if (fields) lines.push(` - fields: ${fields}`);
2513
+ const quote = firstQuote(node, state);
2514
+ if (quote) lines.push(` - source quote: "${quote}"`);
2515
+ const source = sourceTag(node);
2516
+ if (source) lines.push(` - ${source}`);
2517
+ const believers = Array.isArray(node.believed_by) ? node.believed_by : [];
2518
+ if (believers.length > 0) {
2519
+ const rendered2 = believers.slice(0, 3).map((raw) => {
2520
+ const believer = obj(raw);
2521
+ const conf = num(believer?.confidence);
2522
+ return `${str(believer?.agent_label) || "?"}${conf !== void 0 ? ` (${conf})` : ""}`;
2523
+ }).join(", ");
2524
+ lines.push(` - believed by: ${rendered2}`);
2525
+ }
2526
+ return lines.join("\n");
2527
+ }
2528
+ function groupedNodeSections(nodes, state, headingPrefix = "") {
2529
+ const groups = /* @__PURE__ */ new Map();
2530
+ for (const node of nodes) {
2531
+ const type = nodeDisplayType(node);
2532
+ const group = groups.get(type) ?? [];
2533
+ group.push(node);
2534
+ groups.set(type, group);
2535
+ }
2536
+ const sections = [];
2537
+ let first = true;
2538
+ for (const [type, group] of groups) {
2539
+ sections.push({
2540
+ heading: `${first ? headingPrefix : ""}### ${type}`,
2541
+ items: group.map((node) => nodeLines(node, state))
2542
+ });
2543
+ first = false;
2544
+ }
2545
+ return sections;
2546
+ }
2547
+ function relationshipItems(edges, labelByUid, state, options) {
2548
+ const lines = [];
2549
+ for (const raw of edges) {
2550
+ const edge = obj(raw);
2551
+ if (!edge) continue;
2552
+ const type = str(edge.edge_type) || "?";
2553
+ const from = str(edge.from_uid);
2554
+ const to = str(edge.to_uid);
2555
+ if (CURATION_EDGE_TYPES.has(type)) continue;
2556
+ if (!from || !to || from === to) continue;
2557
+ const fromLabel = labelByUid.get(from) ?? (options?.fallbackToUid ? `[${from}]` : void 0);
2558
+ const toLabel = labelByUid.get(to) ?? (options?.fallbackToUid ? `[${to}]` : void 0);
2559
+ if (!fromLabel || !toLabel) continue;
2560
+ lines.push(`- ${fromLabel} \u2014${type}\u2192 ${toLabel}`);
2561
+ }
2562
+ if (lines.length > RELATIONSHIP_MAX) {
2563
+ state.bounded = true;
2564
+ const extra = lines.length - RELATIONSHIP_MAX;
2565
+ return [...lines.slice(0, RELATIONSHIP_MAX), `- (+${extra} more relationships)`];
2566
+ }
2567
+ return lines;
2568
+ }
2569
+ function assemble(header, sections, state) {
2570
+ const lines = [header];
2571
+ let used = header.length;
2572
+ for (const section of sections) {
2573
+ if (section.items.length === 0 && !section.heading) continue;
2574
+ const headingCost = section.heading.length + 2;
2575
+ if (used + headingCost > RENDER_CHAR_BUDGET) {
2576
+ state.bounded = true;
2577
+ continue;
2578
+ }
2579
+ let sectionUsed = 0;
2580
+ const kept = [];
2581
+ let dropped = 0;
2582
+ for (const item of section.items) {
2583
+ if (used + headingCost + sectionUsed + item.length + 1 > RENDER_CHAR_BUDGET) {
2584
+ dropped += 1;
2585
+ continue;
2586
+ }
2587
+ kept.push(item);
2588
+ sectionUsed += item.length + 1;
2589
+ }
2590
+ if (kept.length === 0 && section.items.length > 0) {
2591
+ state.bounded = true;
2592
+ continue;
2593
+ }
2594
+ if (dropped > 0) {
2595
+ state.bounded = true;
2596
+ const marker = `(+${dropped} more not shown)`;
2597
+ kept.push(marker);
2598
+ sectionUsed += marker.length + 1;
2599
+ }
2600
+ lines.push(
2601
+ section.items.length === 0 ? section.heading : section.heading ? `${section.heading}
2602
+ ${kept.join("\n")}` : kept.join("\n")
2603
+ );
2604
+ used += headingCost + sectionUsed;
2605
+ }
2606
+ if (state.bounded) {
2607
+ lines.push(
2608
+ '\u2026[rendered context bounded \u2014 fetch full node content by uid, or pass format: "json" for the raw response]'
2609
+ );
2610
+ }
2611
+ return lines.join("\n\n");
2612
+ }
2613
+ function policySections(payload, state) {
2614
+ const policies = Array.isArray(payload.applicable_policies) ? payload.applicable_policies : [];
2615
+ if (policies.length === 0) return [];
2616
+ const items = policies.map((raw) => obj(raw)).filter((policy) => Boolean(policy)).map((policy) => {
2617
+ const rules = Array.isArray(policy.prose_rules) ? policy.prose_rules.map(String).join("; ") : policy.prose_rules;
2618
+ return `- ${str(policy.name) || "(unnamed policy)"}: ${clip(rules, SUMMARY_MAX, state) || "(structured rules apply)"}`;
2619
+ });
2620
+ return [{ heading: "## Applicable policies", items }];
2621
+ }
2622
+ function hiddenSections(payload) {
2623
+ const hidden = payload.hidden_item_count;
2624
+ if (hidden === void 0 || hidden === null) return [];
2625
+ return [
2626
+ {
2627
+ heading: `Note: ${hidden} item(s) were hidden by access scoping.`,
2628
+ items: []
2629
+ }
2630
+ ];
2631
+ }
2632
+ function renderContext(response) {
2633
+ const payload = obj(response);
2634
+ if (!payload) return void 0;
2635
+ const graph = obj(payload.graph);
2636
+ const rawNodes = Array.isArray(graph?.nodes) ? graph.nodes : [];
2637
+ const shaped = rawNodes.filter(isNodeShaped);
2638
+ const nodes = shaped.filter((node) => !INFRA_NODE_TYPES.has(str(node.node_type) || ""));
2639
+ const infraOnly = shaped.length - nodes.length;
2640
+ const articles = Array.isArray(payload.articles) ? payload.articles : [];
2641
+ const chunks = Array.isArray(payload.chunks) ? payload.chunks : [];
2642
+ const state = { bounded: false };
2643
+ const sections = [];
2644
+ if (articles.length > 0) {
2645
+ sections.push({
2646
+ heading: "## Knowledge articles",
2647
+ items: articles.map((raw) => obj(raw)).filter((article) => Boolean(article)).map(
2648
+ (article) => `### ${str(article.label) || "(untitled)"} [${str(article.uid) || "?"}]
2649
+ ${clip(article.content, ARTICLE_MAX, state) || ""}`
2650
+ )
2651
+ });
2652
+ }
2653
+ if (nodes.length > 0) {
2654
+ sections.push(...groupedNodeSections(nodes, state, "## Knowledge graph\n\n"));
2655
+ const labelByUid = /* @__PURE__ */ new Map();
2656
+ for (const node of nodes) labelByUid.set(str(node.uid), str(node.label) || "?");
2657
+ const relationships = relationshipItems(
2658
+ Array.isArray(graph?.edges) ? graph.edges : [],
2659
+ labelByUid,
2660
+ state
2661
+ );
2662
+ if (relationships.length > 0) {
2663
+ sections.push({ heading: "## Relationships", items: relationships });
2664
+ }
2665
+ } else if (infraOnly > 0) {
2666
+ sections.push({
2667
+ heading: `Note: ${infraOnly} document/chunk node(s) matched \u2014 use action "document_index" or include_chunks for their content.`,
2668
+ items: []
2669
+ });
2670
+ }
2671
+ if (chunks.length > 0) {
2672
+ sections.push({
2673
+ heading: "## Source excerpts",
2674
+ items: chunks.map((raw) => obj(raw)).filter((chunk) => Boolean(chunk)).map(
2675
+ (chunk) => `- [${str(chunk.chunk_uid) || "?"}] from "${str(chunk.document_title) || "unknown document"}": "${clip(chunk.content, CHUNK_MAX, state) || ""}"`
2676
+ )
2677
+ });
2678
+ }
2679
+ sections.push(...policySections(payload, state));
2680
+ sections.push(...hiddenSections(payload));
2681
+ if (sections.length === 0) {
2682
+ return "No results found for this search.";
2683
+ }
2684
+ if (nodes.length === 0 && articles.length === 0 && chunks.length === 0) {
2685
+ return assemble("# Retrieved context\n\nNo graph results matched this search.", sections, state);
2686
+ }
2687
+ return assemble("# Retrieved context", sections, state);
2688
+ }
2689
+ function renderNodeList(response, title, limit) {
2690
+ let candidates = [];
2691
+ if (Array.isArray(response)) {
2692
+ candidates = response;
2693
+ } else {
2694
+ const payload = obj(response);
2695
+ if (!payload) return void 0;
2696
+ for (const key of ["nodes", "results", "items", "goals", "questions", "claims", "preferences", "documents"]) {
2697
+ const candidate = payload[key];
2698
+ if (Array.isArray(candidate) && candidate.some((entry) => unwrapSearchResult(entry))) {
2699
+ candidates = candidate;
2700
+ break;
2701
+ }
2702
+ }
2703
+ }
2704
+ let nodes = candidates.map(unwrapSearchResult).filter((node) => Boolean(node));
2705
+ if (nodes.length === 0) return void 0;
2706
+ const total = nodes.length;
2707
+ const state = { bounded: false };
2708
+ if (limit !== void 0 && limit > 0 && nodes.length > limit) {
2709
+ nodes = nodes.slice(0, limit);
2710
+ state.bounded = true;
2711
+ }
2712
+ const sections = groupedNodeSections(nodes, state);
2713
+ const shown = nodes.length;
2714
+ return assemble(
2715
+ `# ${title} (${shown === total ? total : `${shown} of ${total}`})`,
2716
+ sections,
2717
+ state
2718
+ );
2719
+ }
2720
+ function renderTraversal(response) {
2721
+ const payload = obj(response);
2722
+ if (!payload) return void 0;
2723
+ const container = obj(payload.graph) ?? payload;
2724
+ const rawNodes = Array.isArray(container.nodes) ? container.nodes : [];
2725
+ const nodes = rawNodes.filter(isNodeShaped);
2726
+ const paths = Array.isArray(payload.paths) ? payload.paths : [];
2727
+ if (nodes.length === 0 && paths.length === 0) return void 0;
2728
+ const state = { bounded: false };
2729
+ const sections = [];
2730
+ const labelByUid = /* @__PURE__ */ new Map();
2731
+ for (const node of nodes) labelByUid.set(str(node.uid), str(node.label) || "?");
2732
+ if (paths.length > 0) {
2733
+ const items = paths.map((raw) => obj(raw)).filter((path6) => Boolean(path6)).map((path6, index) => {
2734
+ const uids = Array.isArray(path6.node_uids) ? path6.node_uids.map(String) : [];
2735
+ const labels = Array.isArray(path6.labels) ? path6.labels.map(String) : Array.isArray(path6.nodes) ? path6.nodes.map((hop) => {
2736
+ const hopNode = obj(hop);
2737
+ return str(hopNode?.label) || labelByUid.get(str(hopNode?.uid) || "") || str(hop) || "?";
2738
+ }) : [];
2739
+ const route = labels.length > 0 ? labels.join(" \u2192 ") : uids.join(" \u2192 ") || "(path)";
2740
+ const cost = num(path6.cost) ?? num(path6.path_cost);
2741
+ const confidence = num(path6.path_confidence);
2742
+ const meta = [
2743
+ cost !== void 0 ? `cost ${cost}` : void 0,
2744
+ confidence !== void 0 ? `confidence ${confidence}` : void 0
2745
+ ].filter(Boolean);
2746
+ const head = `${index + 1}. ${route}${meta.length > 0 ? ` (${meta.join(", ")})` : ""}`;
2747
+ return labels.length > 0 && uids.length > 0 ? `${head}
2748
+ uids: ${uids.join(" \u2192 ")}` : head;
2749
+ });
2750
+ sections.push({ heading: "## Paths", items });
2751
+ }
2752
+ if (nodes.length > 0) {
2753
+ sections.push(...groupedNodeSections(nodes, state, "## Nodes\n\n"));
2754
+ const relationships = relationshipItems(
2755
+ Array.isArray(container.edges) ? container.edges : [],
2756
+ labelByUid,
2757
+ state
2758
+ );
2759
+ if (relationships.length > 0) {
2760
+ sections.push({ heading: "## Relationships", items: relationships });
2761
+ }
2762
+ }
2763
+ return assemble("# Graph traversal", sections, state);
2764
+ }
2765
+ function neighborLine(value) {
2766
+ const node = obj(value);
2767
+ if (!node) return void 0;
2768
+ const label = str(node.label) || "(unlabeled)";
2769
+ const uid = str(node.uid);
2770
+ const type = str(node.node_type);
2771
+ return `- ${label}${uid ? ` [${uid}]` : ""}${type ? ` (${type})` : ""}`;
2772
+ }
2773
+ function renderOntologyAnswer(response) {
2774
+ const payload = obj(response);
2775
+ if (!payload) return void 0;
2776
+ const objects = Array.isArray(payload.objects) ? payload.objects.filter(isNodeShaped) : isNodeShaped(payload.object) ? [payload.object] : [];
2777
+ const cognitive = obj(payload.cognitive_context);
2778
+ const provenance = Array.isArray(payload.provenance) ? payload.provenance : [];
2779
+ const hasQuerySignature = Array.isArray(payload.objects) || obj(payload.confidence) !== void 0;
2780
+ const cognitiveEmpty = !cognitive || Object.values(cognitive).every((v) => !Array.isArray(v) || v.length === 0);
2781
+ if (objects.length === 0 && cognitiveEmpty && provenance.length === 0) {
2782
+ return hasQuerySignature ? "No matching domain objects." : void 0;
2783
+ }
2784
+ const state = { bounded: false };
2785
+ const sections = [];
2786
+ if (objects.length > 0) {
2787
+ sections.push(...groupedNodeSections(objects, state));
2788
+ }
2789
+ const labelByUid = /* @__PURE__ */ new Map();
2790
+ for (const node of objects) labelByUid.set(str(node.uid), str(node.label) || "?");
2791
+ const graph = obj(payload.graph);
2792
+ for (const raw of Array.isArray(graph?.nodes) ? graph.nodes : []) {
2793
+ const node = obj(raw);
2794
+ const uid = str(node?.uid);
2795
+ if (uid && !labelByUid.has(uid)) labelByUid.set(uid, str(node?.label) || "?");
2796
+ }
2797
+ if (cognitive) {
2798
+ for (const rawNodes of Object.values(cognitive)) {
2799
+ if (!Array.isArray(rawNodes)) continue;
2800
+ for (const raw of rawNodes) {
2801
+ const node = obj(raw);
2802
+ const uid = str(node?.uid);
2803
+ if (uid && !labelByUid.has(uid)) labelByUid.set(uid, str(node?.label) || "?");
2804
+ }
2805
+ }
2806
+ }
2807
+ const relations = Array.isArray(payload.relations) ? payload.relations : [];
2808
+ const relationLines = relationshipItems(relations, labelByUid, state, { fallbackToUid: true });
2809
+ if (relationLines.length > 0) {
2810
+ sections.push({ heading: "## Relations", items: relationLines });
2811
+ }
2812
+ if (cognitive) {
2813
+ for (const [category, rawNodes] of Object.entries(cognitive)) {
2814
+ if (!Array.isArray(rawNodes) || rawNodes.length === 0) continue;
2815
+ const items = rawNodes.map(neighborLine).filter((line) => Boolean(line));
2816
+ if (items.length > 0) {
2817
+ sections.push({ heading: `### Cognitive context \u2014 ${category}`, items });
2818
+ }
2819
+ }
2820
+ }
2821
+ if (provenance.length > 0) {
2822
+ const items = provenance.map((raw) => obj(raw)).filter((entry) => Boolean(entry)).map((entry) => {
2823
+ const span = clip(entry.text_span, QUOTE_MAX, state) || "";
2824
+ const by = str(entry.ingested_by_name);
2825
+ const suffix = `${by ? ` (ingested by ${by})` : ""} [source ${str(entry.source_uid) || "?"}]`;
2826
+ return str(entry.span_kind) === "anchor" ? `- "${span}"${suffix}` : `- context: ${span}${suffix}`;
2827
+ });
2828
+ sections.push({ heading: "## Sources", items });
2829
+ }
2830
+ const confidence = num(obj(payload.confidence)?.overall);
2831
+ const notes = [];
2832
+ if (confidence !== void 0) notes.push(`overall confidence ${confidence}`);
2833
+ if (payload.has_more === true) notes.push("more results exist (has_more)");
2834
+ if (payload.seed_cap_hit === true) notes.push("seed cap hit \u2014 narrow the query for a complete set");
2835
+ if (notes.length > 0) sections.push({ heading: `Note: ${notes.join("; ")}.`, items: [] });
2836
+ return assemble(
2837
+ `# Ontology results (${objects.length} object${objects.length === 1 ? "" : "s"})`,
2838
+ sections,
2839
+ state
2840
+ );
2841
+ }
2842
+ function renderObjectList(response, title) {
2843
+ const payload = obj(response);
2844
+ if (!payload) return void 0;
2845
+ const rawItems = Array.isArray(payload.items) ? payload.items : Array.isArray(payload.rows) && payload.rows.some((row) => unwrapSearchResult(row)) ? payload.rows : void 0;
2846
+ const items = (rawItems ?? []).map(unwrapSearchResult).filter((node) => Boolean(node));
2847
+ if (items.length === 0) {
2848
+ return rawItems ? `# ${title} (0 results)` : void 0;
2849
+ }
2850
+ const state = { bounded: false };
2851
+ const sections = groupedNodeSections(items, state);
2852
+ const total = num(payload.total_count);
2853
+ const truncations = Array.isArray(payload.truncation_reasons) ? payload.truncation_reasons.filter((reason) => typeof reason === "string") : [];
2854
+ const notes = [];
2855
+ if (payload.has_more === true) notes.push("more results exist");
2856
+ if (truncations.length > 0) notes.push(`truncated: ${truncations.join(", ")}`);
2857
+ if (notes.length > 0) sections.push({ heading: `Note: ${notes.join("; ")}.`, items: [] });
2858
+ const count = total !== void 0 && total !== items.length ? `${items.length} of ${total}` : `${items.length}`;
2859
+ return assemble(`# ${title} (${count})`, sections, state);
2860
+ }
2861
+ function renderJobs(response) {
2862
+ const jobs = Array.isArray(response) ? response : obj(response)?.jobs;
2863
+ if (!Array.isArray(jobs)) return void 0;
2864
+ if (jobs.length === 0) return "No ingestion jobs.";
2865
+ const state = { bounded: false };
2866
+ const sorted = jobs.map((raw) => obj(raw)).filter((job) => Boolean(job)).sort((a, b) => (num(b.created_at) ?? 0) - (num(a.created_at) ?? 0));
2867
+ const shown = sorted.slice(0, JOBS_RENDER_MAX);
2868
+ if (shown.length < sorted.length) state.bounded = true;
2869
+ const items = shown.map((job) => {
2870
+ const progress = obj(job.progress);
2871
+ const done = num(progress?.processed_chunks);
2872
+ const totalChunks = num(progress?.total_chunks);
2873
+ const bits = [
2874
+ str(job.status) || "?",
2875
+ done !== void 0 && totalChunks !== void 0 ? `${done}/${totalChunks} chunks` : void 0,
2876
+ num(progress?.nodes_created) !== void 0 ? `${num(progress?.nodes_created)} nodes` : void 0,
2877
+ num(job.queue_position) !== void 0 ? `queue #${num(job.queue_position)}` : void 0,
2878
+ str(job.error) ? `error: ${clip(job.error, 160, state)}` : void 0
2879
+ ].filter(Boolean);
2880
+ return `- [${str(job.id) || "?"}] ${clip(job.title, 120, state) || "(untitled)"} \u2014 ${bits.join(", ")}`;
2881
+ });
2882
+ return assemble(
2883
+ `# Ingestion jobs (${shown.length}${shown.length < sorted.length ? ` of ${sorted.length}` : ""})`,
2884
+ [{ heading: "", items }],
2885
+ state
2886
+ );
2887
+ }
2888
+ function renderSignals(response) {
2889
+ const payload = obj(response);
2890
+ if (!payload) return void 0;
2891
+ const state = { bounded: false };
2892
+ const sections = [];
2893
+ for (const [key, value] of Object.entries(payload)) {
2894
+ if (!Array.isArray(value) || value.length === 0) continue;
2895
+ const items = value.map((raw) => obj(raw)).filter((item) => Boolean(item)).map((item) => {
2896
+ const fromLabel = str(item.from_label);
2897
+ const toLabel = str(item.to_label);
2898
+ const anyLabelKey = Object.keys(item).find(
2899
+ (k) => k.endsWith("_label") && str(item[k])
2900
+ );
2901
+ const label = str(item.label) ?? str(item.title) ?? str(item.name) ?? (fromLabel && toLabel ? `${fromLabel} \u2194 ${toLabel}` : void 0) ?? (anyLabelKey ? str(item[anyLabelKey]) : void 0) ?? str(item.uid) ?? str(item.target_uid) ?? "(item)";
2902
+ const uid = str(item.uid) ?? str(item.target_uid);
2903
+ const numbers = Object.entries(item).filter(([, v]) => typeof v === "number").slice(0, 4).map(([k, v]) => `${k} ${Math.round(v * 100) / 100}`);
2904
+ const lists = Object.entries(item).filter(([, v]) => Array.isArray(v) && v.length > 0).slice(0, 2).map(([k, v]) => `${k}: ${v.length}`);
2905
+ const meta = [...numbers, ...lists].join(", ");
2906
+ return `- ${clip(label, 160, state)}${uid && uid !== label ? ` [${uid}]` : ""}${meta ? ` (${meta})` : ""}`;
2907
+ });
2908
+ if (items.length > 0) {
2909
+ sections.push({ heading: `## ${key.replace(/_/g, " ")}`, items });
2910
+ }
2911
+ }
2912
+ if (sections.length === 0) return void 0;
2913
+ return assemble("# Synthesis signals", sections, state);
2914
+ }
2915
+ var LABEL_MAX, SUMMARY_MAX, QUOTE_MAX, ARTICLE_MAX, CHUNK_MAX, RELATIONSHIP_MAX, RENDER_CHAR_BUDGET, CURATION_EDGE_TYPES, INFRA_NODE_TYPES, JOBS_RENDER_MAX;
2916
+ var init_render = __esm({
2917
+ "src/render.ts"() {
2918
+ "use strict";
2919
+ LABEL_MAX = 200;
2920
+ SUMMARY_MAX = 400;
2921
+ QUOTE_MAX = 180;
2922
+ ARTICLE_MAX = 6e3;
2923
+ CHUNK_MAX = 6e3;
2924
+ RELATIONSHIP_MAX = 40;
2925
+ RENDER_CHAR_BUDGET = 24e3;
2926
+ CURATION_EDGE_TYPES = /* @__PURE__ */ new Set(["POSSIBLE_DUPLICATE", "PossibleDuplicate"]);
2927
+ INFRA_NODE_TYPES = /* @__PURE__ */ new Set(["Chunk", "Document", "Article"]);
2928
+ JOBS_RENDER_MAX = 20;
2929
+ }
2930
+ });
2931
+
2415
2932
  // src/tools.ts
2416
2933
  function toolsForProfile(profile = process.env.MINDGRAPH_PROFILE || "general") {
2417
2934
  if (profile === "coding") return TOOLS;
@@ -2421,9 +2938,14 @@ function toolsForProfile(profile = process.env.MINDGRAPH_PROFILE || "general") {
2421
2938
  }
2422
2939
  function ok3(data) {
2423
2940
  return {
2424
- content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
2941
+ content: [{ type: "text", text: JSON.stringify(data) }]
2425
2942
  };
2426
2943
  }
2944
+ function renderedResult(format, raw, render) {
2945
+ if (format === "json") return ok3(raw);
2946
+ const text = render(raw);
2947
+ return text !== void 0 ? { content: [{ type: "text", text }] } : ok3(raw);
2948
+ }
2427
2949
  function err3(message) {
2428
2950
  return {
2429
2951
  content: [{ type: "text", text: JSON.stringify({ error: message }) }],
@@ -3005,6 +3527,7 @@ async function handlePlan(client2, args) {
3005
3527
  async function handleRetrieve(client2, args) {
3006
3528
  const {
3007
3529
  action,
3530
+ format,
3008
3531
  query,
3009
3532
  start_uid,
3010
3533
  end_uid,
@@ -3025,12 +3548,20 @@ async function handleRetrieve(client2, args) {
3025
3548
  graph_max_depth,
3026
3549
  confidence_min,
3027
3550
  salience_min,
3551
+ article_limit,
3552
+ valid_at,
3553
+ created_after,
3554
+ created_before,
3555
+ explain,
3556
+ k,
3557
+ max_cost,
3028
3558
  agent_id
3029
3559
  } = args;
3560
+ const rendered2 = (raw, render) => renderedResult(format, raw, render);
3030
3561
  switch (action) {
3031
3562
  case "context":
3032
3563
  if (!query) return err3("query is required for context retrieval");
3033
- return ok3(
3564
+ return rendered2(
3034
3565
  await client2.retrieveContext({
3035
3566
  query,
3036
3567
  node_limit: limit,
@@ -3039,110 +3570,139 @@ async function handleRetrieve(client2, args) {
3039
3570
  chunk_limit: include_chunks ? limit ?? 5 : 0,
3040
3571
  // Scoped by default, never walled: the coding profile drops the
3041
3572
  // document/article leg (the "Bob Work" class of noise) unless the
3042
- // model opts in for a genuinely document-shaped question.
3043
- ...process.env.MINDGRAPH_PROFILE === "coding" && !include_documents ? { article_limit: 0 } : {},
3573
+ // model opts in for a genuinely document-shaped question. Any
3574
+ // profile can drop it with include_documents: false (previously a
3575
+ // silent no-op outside coding). An EXPLICIT article_limit is at
3576
+ // least as clear an opt-in as include_documents:true — it always
3577
+ // wins over profile defaults (a coding-profile model reaching for
3578
+ // the advertised knob must not get a silent no-op).
3579
+ ...article_limit !== void 0 ? { article_limit } : include_documents === false || process.env.MINDGRAPH_PROFILE === "coding" && !include_documents ? { article_limit: 0 } : {},
3580
+ valid_at,
3044
3581
  include_graph,
3045
3582
  graph_expansion_limit: graph_expansion_limit ?? 3,
3046
3583
  graph_max_depth: graph_max_depth ?? 2
3047
- })
3584
+ }),
3585
+ renderContext
3048
3586
  );
3049
3587
  case "text":
3050
3588
  if (!query) return err3("query is required for text search");
3051
- return ok3(
3589
+ return rendered2(
3052
3590
  await client2.retrieve({
3053
3591
  action: "text",
3054
3592
  query,
3055
3593
  limit,
3056
3594
  node_types,
3057
3595
  layer,
3596
+ explain,
3058
3597
  agent_id
3059
- })
3598
+ }),
3599
+ (raw) => renderNodeList(raw, `Text search: ${query}`)
3060
3600
  );
3061
3601
  case "semantic":
3062
3602
  if (!query) return err3("query is required for semantic search");
3063
- return ok3(
3603
+ return rendered2(
3064
3604
  await client2.retrieve({
3065
3605
  action: "semantic",
3066
3606
  query,
3067
3607
  k: limit,
3068
3608
  node_types,
3069
3609
  layer,
3610
+ explain,
3070
3611
  agent_id
3071
- })
3612
+ }),
3613
+ (raw) => renderNodeList(raw, `Semantic search: ${query}`)
3072
3614
  );
3073
3615
  case "hybrid":
3074
3616
  if (!query) return err3("query is required for hybrid search");
3075
- return ok3(
3617
+ return rendered2(
3076
3618
  await client2.retrieve({
3077
3619
  action: "hybrid",
3078
3620
  query,
3079
3621
  k: limit,
3080
3622
  node_types,
3081
3623
  layer,
3624
+ explain,
3082
3625
  agent_id
3083
- })
3626
+ }),
3627
+ // With explain, per-leg scoring must survive — raw JSON only.
3628
+ explain ? () => void 0 : (raw) => renderNodeList(raw, `Hybrid search: ${query}`)
3084
3629
  );
3085
- // Structured queries
3630
+ // Structured queries. These go through the generic /retrieve action
3631
+ // dispatch rather than the SDK's no-argument convenience methods — the
3632
+ // conveniences silently dropped `limit`/`offset` (the tool advertised
3633
+ // them; five actions ignored them).
3086
3634
  case "active_goals":
3087
- return ok3(await client2.getGoals());
3088
3635
  case "open_questions":
3089
- return ok3(await client2.getOpenQuestions());
3090
3636
  case "weak_claims":
3091
- return ok3(await client2.getWeakClaims());
3092
3637
  case "pending_approvals":
3093
- return ok3(await client2.getPendingApprovals());
3094
3638
  case "unresolved_contradictions":
3095
- return ok3(await client2.getContradictions());
3639
+ return rendered2(
3640
+ await client2.retrieve({
3641
+ action,
3642
+ limit,
3643
+ offset,
3644
+ agent_id
3645
+ }),
3646
+ (raw) => renderNodeList(raw, action.replace(/_/g, " "), limit)
3647
+ );
3096
3648
  case "stale_derivations":
3097
- return ok3(
3649
+ return rendered2(
3098
3650
  await client2.retrieve({
3099
3651
  action: "stale_derivations",
3100
3652
  limit,
3101
3653
  offset
3102
- })
3654
+ }),
3655
+ (raw) => renderNodeList(raw, "stale derivations", limit)
3103
3656
  );
3104
3657
  case "preferences":
3105
- return ok3(
3658
+ return rendered2(
3106
3659
  await client2.retrieve({
3107
3660
  action: "preferences",
3108
3661
  ...query ? { query, k: limit } : {},
3109
3662
  limit,
3110
3663
  agent_id
3111
- })
3664
+ }),
3665
+ (raw) => renderNodeList(raw, "Preferences", limit)
3112
3666
  );
3113
3667
  case "layer":
3114
3668
  if (!layer) return err3("layer is required for layer retrieval");
3115
- return ok3(
3669
+ return rendered2(
3116
3670
  await client2.retrieve({
3117
3671
  action: "layer",
3118
3672
  layer,
3119
3673
  limit,
3674
+ node_types,
3120
3675
  agent_id
3121
- })
3676
+ }),
3677
+ (raw) => renderNodeList(raw, `Layer: ${layer}`, limit)
3122
3678
  );
3123
3679
  case "recent":
3124
- return ok3(
3680
+ return rendered2(
3125
3681
  await client2.retrieve({
3126
3682
  action: "recent",
3127
3683
  limit,
3128
3684
  node_types,
3129
3685
  confidence_min,
3130
3686
  salience_min,
3687
+ created_after,
3688
+ created_before,
3131
3689
  agent_id
3132
- })
3690
+ }),
3691
+ (raw) => renderNodeList(raw, "Recently ingested", limit)
3133
3692
  );
3134
3693
  // Document inventory
3135
3694
  case "document_index":
3136
- return ok3(
3695
+ return rendered2(
3137
3696
  await client2.getNodes({
3138
3697
  node_type: "Document",
3139
3698
  limit: limit ?? 100
3140
- })
3699
+ }),
3700
+ (raw) => renderNodeList(raw, "Ingested documents")
3141
3701
  );
3142
3702
  // Graph traversal
3143
3703
  case "chain":
3144
3704
  if (!start_uid) return err3("start_uid is required for chain traversal");
3145
- return ok3(
3705
+ return rendered2(
3146
3706
  await client2.traverse({
3147
3707
  action: "chain",
3148
3708
  start_uid,
@@ -3150,11 +3710,12 @@ async function handleRetrieve(client2, args) {
3150
3710
  exclude_edge_types,
3151
3711
  include_provenance,
3152
3712
  max_nodes
3153
- })
3713
+ }),
3714
+ renderTraversal
3154
3715
  );
3155
3716
  case "neighborhood":
3156
3717
  if (!start_uid) return err3("start_uid is required for neighborhood traversal");
3157
- return ok3(
3718
+ return rendered2(
3158
3719
  await client2.traverse({
3159
3720
  action: "neighborhood",
3160
3721
  start_uid,
@@ -3164,12 +3725,13 @@ async function handleRetrieve(client2, args) {
3164
3725
  exclude_edge_types,
3165
3726
  include_provenance,
3166
3727
  max_nodes
3167
- })
3728
+ }),
3729
+ renderTraversal
3168
3730
  );
3169
3731
  case "path":
3170
3732
  if (!start_uid) return err3("start_uid is required for path traversal");
3171
3733
  if (!end_uid) return err3("end_uid is required for path traversal");
3172
- return ok3(
3734
+ return rendered2(
3173
3735
  await client2.traverse({
3174
3736
  action: "path",
3175
3737
  start_uid,
@@ -3180,11 +3742,12 @@ async function handleRetrieve(client2, args) {
3180
3742
  exclude_edge_types,
3181
3743
  include_provenance,
3182
3744
  max_nodes
3183
- })
3745
+ }),
3746
+ renderTraversal
3184
3747
  );
3185
3748
  case "subgraph":
3186
3749
  if (!start_uid) return err3("start_uid is required for subgraph traversal");
3187
- return ok3(
3750
+ return rendered2(
3188
3751
  await client2.traverse({
3189
3752
  action: "subgraph",
3190
3753
  start_uid,
@@ -3194,7 +3757,24 @@ async function handleRetrieve(client2, args) {
3194
3757
  exclude_edge_types,
3195
3758
  include_provenance,
3196
3759
  max_nodes
3197
- })
3760
+ }),
3761
+ renderTraversal
3762
+ );
3763
+ case "top_k_paths":
3764
+ if (!start_uid) return err3("start_uid is required for top_k_paths");
3765
+ if (!end_uid) return err3("end_uid is required for top_k_paths");
3766
+ return rendered2(
3767
+ await client2.traverse({
3768
+ action: "top_k_paths",
3769
+ start_uid,
3770
+ end_uid,
3771
+ k,
3772
+ max_hops: max_depth,
3773
+ max_cost,
3774
+ edge_types,
3775
+ exclude_edge_types
3776
+ }),
3777
+ renderTraversal
3198
3778
  );
3199
3779
  default:
3200
3780
  return err3(`Unknown retrieve action: ${action}`);
@@ -3220,6 +3800,7 @@ async function handleIngest(client2, args) {
3220
3800
  chunk_size,
3221
3801
  chunk_overlap,
3222
3802
  job_id,
3803
+ format,
3223
3804
  agent_id
3224
3805
  } = args;
3225
3806
  const src = source_uri ?? source;
@@ -3289,19 +3870,21 @@ async function handleIngest(client2, args) {
3289
3870
  if (job_id) {
3290
3871
  return ok3(await client2.getJob(job_id));
3291
3872
  }
3292
- return ok3(await client2.listJobs());
3873
+ return renderedResult(format, await client2.listJobs(), renderJobs);
3293
3874
  default:
3294
3875
  return err3(`Unknown ingest action: ${action}`);
3295
3876
  }
3296
3877
  }
3297
3878
  async function handleSynthesize(client2, args) {
3298
- const { action, project_uid, signals, target_types, job_id } = args;
3879
+ const { action, project_uid, signals, target_types, job_id, format } = args;
3299
3880
  switch (action) {
3300
3881
  case "signals":
3301
3882
  if (!project_uid)
3302
3883
  return err3("project_uid is required for action='signals'");
3303
- return ok3(
3304
- await client2.signals(project_uid, { signals, target_types })
3884
+ return renderedResult(
3885
+ format,
3886
+ await client2.signals(project_uid, { signals, target_types }),
3887
+ renderSignals
3305
3888
  );
3306
3889
  case "run":
3307
3890
  if (!project_uid)
@@ -3335,6 +3918,7 @@ async function handleOntology(client2, args) {
3335
3918
  relation_type,
3336
3919
  fields,
3337
3920
  source_uids,
3921
+ format,
3338
3922
  agent_id
3339
3923
  } = args;
3340
3924
  switch (action) {
@@ -3350,7 +3934,8 @@ async function handleOntology(client2, args) {
3350
3934
  "schema_id is required for action='query' (use action='schemas' to find the active schema)"
3351
3935
  );
3352
3936
  }
3353
- return ok3(
3937
+ return renderedResult(
3938
+ format,
3354
3939
  await client2.queryOntology({
3355
3940
  query,
3356
3941
  schema_id,
@@ -3359,29 +3944,42 @@ async function handleOntology(client2, args) {
3359
3944
  include_sources: include_sources ?? true,
3360
3945
  depth,
3361
3946
  limit
3362
- })
3947
+ }),
3948
+ renderOntologyAnswer
3363
3949
  );
3364
3950
  }
3365
3951
  case "search":
3366
3952
  if (!query) return err3("query is required for action='search'");
3367
- return ok3(
3953
+ return renderedResult(
3954
+ format,
3368
3955
  await client2.searchDomainObjects(query, {
3369
3956
  schema_id,
3370
3957
  object_types,
3371
3958
  limit
3372
- })
3959
+ }),
3960
+ (raw) => renderObjectList(raw, `Domain search: ${query}`)
3373
3961
  );
3374
3962
  case "objects":
3375
3963
  if (!schema_id) return err3("schema_id is required for action='objects'");
3376
- return ok3(
3377
- await client2.listDomainObjects({ schema_id, object_type, limit })
3964
+ return renderedResult(
3965
+ format,
3966
+ await client2.listDomainObjects({ schema_id, object_type, limit }),
3967
+ (raw) => renderObjectList(raw, `Domain objects${object_type ? `: ${object_type}` : ""}`)
3378
3968
  );
3379
3969
  case "object":
3380
3970
  if (!uid) return err3("uid is required for action='object'");
3381
- return ok3(await client2.getDomainObject(uid));
3971
+ return renderedResult(
3972
+ format,
3973
+ await client2.getDomainObject(uid),
3974
+ (raw) => renderNodeList([raw], "Domain object")
3975
+ );
3382
3976
  case "object_context":
3383
3977
  if (!uid) return err3("uid is required for action='object_context'");
3384
- return ok3(await client2.getDomainObjectContext(uid, depth));
3978
+ return renderedResult(
3979
+ format,
3980
+ await client2.getDomainObjectContext(uid, depth),
3981
+ renderOntologyAnswer
3982
+ );
3385
3983
  case "proposals":
3386
3984
  return ok3(
3387
3985
  await client2.listOntologyProposals({
@@ -3440,6 +4038,7 @@ var init_tools = __esm({
3440
4038
  init_code_tool();
3441
4039
  init_codegraph();
3442
4040
  init_sync_tool();
4041
+ init_render();
3443
4042
  TOOLS = [
3444
4043
  {
3445
4044
  name: "mindgraph_capture",
@@ -4013,10 +4612,16 @@ var init_tools = __esm({
4013
4612
  "neighborhood",
4014
4613
  "path",
4015
4614
  "subgraph",
4615
+ "top_k_paths",
4016
4616
  "document_index"
4017
4617
  ],
4018
4618
  description: "Retrieval or traversal strategy"
4019
4619
  },
4620
+ format: {
4621
+ type: "string",
4622
+ enum: ["text", "json"],
4623
+ 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)."
4624
+ },
4020
4625
  query: {
4021
4626
  type: "string",
4022
4627
  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 +4681,35 @@ var init_tools = __esm({
4076
4681
  },
4077
4682
  include_documents: {
4078
4683
  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."
4684
+ 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."
4685
+ },
4686
+ article_limit: {
4687
+ type: "number",
4688
+ 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."
4689
+ },
4690
+ valid_at: {
4691
+ type: "string",
4692
+ description: "ISO-8601 date (e.g. '2021-06') for context: nodes with validity windows are annotated as of this date instead of today"
4693
+ },
4694
+ created_after: {
4695
+ type: "number",
4696
+ description: "Unix seconds \u2014 'recent' action: only nodes ingested after this time"
4697
+ },
4698
+ created_before: {
4699
+ type: "number",
4700
+ description: "Unix seconds \u2014 'recent' action: only nodes ingested before this time"
4701
+ },
4702
+ explain: {
4703
+ type: "boolean",
4704
+ description: "hybrid action: include per-leg retrieval scoring (legs) in the response"
4705
+ },
4706
+ k: {
4707
+ type: "number",
4708
+ description: "top_k_paths action: number of cheapest paths (default 3, cap 25)"
4709
+ },
4710
+ max_cost: {
4711
+ type: "number",
4712
+ description: "top_k_paths action: optional cost ceiling for returned paths"
4080
4713
  },
4081
4714
  include_chunks: {
4082
4715
  type: "boolean",
@@ -4213,6 +4846,11 @@ var init_tools = __esm({
4213
4846
  type: "string",
4214
4847
  description: "Job ID to check status (for job_status action)"
4215
4848
  },
4849
+ format: {
4850
+ type: "string",
4851
+ enum: ["text", "json"],
4852
+ description: "job_status without job_id: 'text' (default) renders the 20 most recent jobs; 'json' returns the full raw list."
4853
+ },
4216
4854
  agent_id: {
4217
4855
  type: "string",
4218
4856
  description: "Agent identity"
@@ -4248,6 +4886,11 @@ var init_tools = __esm({
4248
4886
  type: "string",
4249
4887
  description: "Job ID to poll (for action='job_status')"
4250
4888
  },
4889
+ format: {
4890
+ type: "string",
4891
+ enum: ["text", "json"],
4892
+ description: "signals action: 'text' (default) renders signal sections; 'json' returns the raw payload."
4893
+ },
4251
4894
  agent_id: {
4252
4895
  type: "string",
4253
4896
  description: "Agent identity"
@@ -4356,6 +4999,11 @@ var init_tools = __esm({
4356
4999
  items: { type: "string" },
4357
5000
  description: "Chunk/document UIDs to extract from (for action=extract)"
4358
5001
  },
5002
+ format: {
5003
+ type: "string",
5004
+ enum: ["text", "json"],
5005
+ description: "Output format for the read actions query/search/objects/object/object_context (default 'text'): a compact rendered block. Pass 'json' for the raw server response."
5006
+ },
4359
5007
  agent_id: {
4360
5008
  type: "string",
4361
5009
  description: "Agent identity"
@@ -4418,10 +5066,21 @@ async function getGeneratedTools(client2) {
4418
5066
  );
4419
5067
  }
4420
5068
  byName.set(d.name, d);
5069
+ const inputSchema = d.maps_to === "structured_query" ? d.input_schema : {
5070
+ ...d.input_schema,
5071
+ properties: {
5072
+ ...d.input_schema.properties,
5073
+ format: {
5074
+ type: "string",
5075
+ enum: ["text", "json"],
5076
+ description: "Output format (default 'text': compact rendered block). Pass 'json' for the raw server response."
5077
+ }
5078
+ }
5079
+ };
4421
5080
  tools.push({
4422
5081
  name: d.name,
4423
5082
  description: d.description,
4424
- inputSchema: d.input_schema
5083
+ inputSchema
4425
5084
  });
4426
5085
  }
4427
5086
  cache = { tools, byName, fetchedAt: Date.now() };
@@ -4442,31 +5101,41 @@ async function handleGeneratedTool(client2, desc, args) {
4442
5101
  if (!query) return err4(`query is required for ${desc.name}`);
4443
5102
  const limit = typeof args.limit === "number" ? args.limit : void 0;
4444
5103
  const filters = Array.isArray(args.filters) ? args.filters : void 0;
4445
- return ok4(
5104
+ return rendered(
5105
+ args.format,
4446
5106
  await ontologyClient.searchDomainObjects(query, {
4447
5107
  schema_id: desc.schema_id,
4448
5108
  object_types: [desc.object_type],
4449
5109
  filters,
4450
5110
  limit
4451
- })
5111
+ }),
5112
+ (raw) => renderObjectList(raw, `${desc.object_type} search: ${query}`)
4452
5113
  );
4453
5114
  }
4454
5115
  case "object": {
4455
5116
  const uid = args.uid;
4456
5117
  if (!uid) return err4(`uid is required for ${desc.name}`);
4457
- return ok4(await ontologyClient.getDomainObject(uid, {
4458
- schema_id: desc.schema_id,
4459
- object_type: desc.object_type
4460
- }));
5118
+ return rendered(
5119
+ args.format,
5120
+ await ontologyClient.getDomainObject(uid, {
5121
+ schema_id: desc.schema_id,
5122
+ object_type: desc.object_type
5123
+ }),
5124
+ (raw) => renderNodeList([raw], desc.object_type)
5125
+ );
4461
5126
  }
4462
5127
  case "object_context": {
4463
5128
  const uid = args.uid;
4464
5129
  if (!uid) return err4(`uid is required for ${desc.name}`);
4465
5130
  const depth = typeof args.depth === "number" ? args.depth : void 0;
4466
- return ok4(await ontologyClient.getDomainObjectContext(uid, depth, {
4467
- schema_id: desc.schema_id,
4468
- object_type: desc.object_type
4469
- }));
5131
+ return rendered(
5132
+ args.format,
5133
+ await ontologyClient.getDomainObjectContext(uid, depth, {
5134
+ schema_id: desc.schema_id,
5135
+ object_type: desc.object_type
5136
+ }),
5137
+ renderOntologyAnswer
5138
+ );
4470
5139
  }
4471
5140
  case "related": {
4472
5141
  const uid = args.uid;
@@ -4479,18 +5148,22 @@ async function handleGeneratedTool(client2, desc, args) {
4479
5148
  "related ontology tools require a graph-aware version of the mindgraph SDK"
4480
5149
  );
4481
5150
  }
4482
- return ok4(await ontologyClient.queryRelatedDomainObjects({
4483
- schema_id: desc.schema_id,
4484
- entry_type: desc.object_type,
4485
- uid,
4486
- relation: desc.relation,
4487
- entry_role: desc.entry_role,
4488
- far_type: desc.far_type,
4489
- where: Array.isArray(args.where) ? args.where : void 0,
4490
- include_provenance: args.include_provenance === true,
4491
- limit: typeof args.limit === "number" ? args.limit : void 0,
4492
- offset: typeof args.offset === "number" ? args.offset : void 0
4493
- }));
5151
+ return rendered(
5152
+ args.format,
5153
+ await ontologyClient.queryRelatedDomainObjects({
5154
+ schema_id: desc.schema_id,
5155
+ entry_type: desc.object_type,
5156
+ uid,
5157
+ relation: desc.relation,
5158
+ entry_role: desc.entry_role,
5159
+ far_type: desc.far_type,
5160
+ where: Array.isArray(args.where) ? args.where : void 0,
5161
+ include_provenance: args.include_provenance === true,
5162
+ limit: typeof args.limit === "number" ? args.limit : void 0,
5163
+ offset: typeof args.offset === "number" ? args.offset : void 0
5164
+ }),
5165
+ (raw) => renderObjectList(raw, `${desc.far_type} related via ${desc.relation}`)
5166
+ );
4494
5167
  }
4495
5168
  case "structured_query": {
4496
5169
  const select = args.select;
@@ -4516,13 +5189,20 @@ async function handleGeneratedTool(client2, desc, args) {
4516
5189
  return err4(e instanceof Error ? e.message : String(e));
4517
5190
  }
4518
5191
  }
4519
- var ok4, err4, CACHE_TTL_MS, FAILURE_COOLDOWN_MS, cache, retryNotBefore, EMPTY, DuplicateGeneratedToolError, STRUCTURED_QUERY_PASSTHROUGH;
5192
+ var ok4, rendered, err4, CACHE_TTL_MS, FAILURE_COOLDOWN_MS, cache, retryNotBefore, EMPTY, DuplicateGeneratedToolError, STRUCTURED_QUERY_PASSTHROUGH;
4520
5193
  var init_generated_tools = __esm({
4521
5194
  "src/generated-tools.ts"() {
4522
5195
  "use strict";
5196
+ init_render();
4523
5197
  ok4 = (data) => ({
4524
- content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
5198
+ // Compact on purpose — pretty-printing doubled the token cost.
5199
+ content: [{ type: "text", text: JSON.stringify(data) }]
4525
5200
  });
5201
+ rendered = (format, raw, render) => {
5202
+ if (format === "json") return ok4(raw);
5203
+ const text = render(raw);
5204
+ return text !== void 0 ? { content: [{ type: "text", text }] } : ok4(raw);
5205
+ };
4526
5206
  err4 = (message) => ({
4527
5207
  content: [{ type: "text", text: JSON.stringify({ error: message }) }],
4528
5208
  isError: true
@@ -4793,7 +5473,7 @@ var init_governance = __esm({
4793
5473
  // src/resources.ts
4794
5474
  function json(uri, data) {
4795
5475
  return {
4796
- contents: [{ uri, mimeType: "application/json", text: JSON.stringify(data, null, 2) }]
5476
+ contents: [{ uri, mimeType: "application/json", text: JSON.stringify(data) }]
4797
5477
  };
4798
5478
  }
4799
5479
  async function readResource(client2, uri) {
@@ -4907,7 +5587,7 @@ async function main() {
4907
5587
  const transport = new import_stdio.StdioServerTransport();
4908
5588
  await server.connect(transport);
4909
5589
  console.error(
4910
- `MindGraph MCP server v${"0.15.0"} running on stdio (${BASE_URL})`
5590
+ `MindGraph MCP server v${"0.17.0"} running on stdio (${BASE_URL})`
4911
5591
  );
4912
5592
  }
4913
5593
  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 +5727,7 @@ Capture knowledge when the user shares something worth remembering:
5047
5727
  server = new import_server.Server(
5048
5728
  {
5049
5729
  name: "mindgraph",
5050
- version: "0.15.0"
5730
+ version: "0.17.0"
5051
5731
  },
5052
5732
  {
5053
5733
  capabilities: {
@@ -5612,8 +6292,8 @@ ${knowledgeLines.join("\n")}`);
5612
6292
  );
5613
6293
  }
5614
6294
  if (footer) lines.push(footer);
5615
- const rendered = lines.join("\n");
5616
- return rendered.length <= BRIEF_CHAR_BUDGET ? rendered : `${rendered.slice(0, BRIEF_CHAR_BUDGET - 40)}
6295
+ const rendered2 = lines.join("\n");
6296
+ return rendered2.length <= BRIEF_CHAR_BUDGET ? rendered2 : `${rendered2.slice(0, BRIEF_CHAR_BUDGET - 40)}
5617
6297
  \u2026[bounded work brief truncated]`;
5618
6298
  }
5619
6299
  async function repositoryScopeUids(input, client2, options, agentId) {