@neat.is/mcp 0.7.10 → 0.8.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/index.cjs +73 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +73 -0
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -424,6 +424,14 @@ async function getRootCause(client2, input) {
|
|
|
424
424
|
if (result.fixRecommendation) {
|
|
425
425
|
blockLines.push(`Recommended fix: ${result.fixRecommendation}`);
|
|
426
426
|
}
|
|
427
|
+
if (result.candidates && result.candidates.length > 0) {
|
|
428
|
+
blockLines.push("", "Candidates (ranked, most likely cause first):");
|
|
429
|
+
for (const c of result.candidates) {
|
|
430
|
+
blockLines.push(
|
|
431
|
+
` \u2022 ${c.node} \u2014 ${c.classification} (confidence ${c.confidence.toFixed(2)}): ${c.reason}`
|
|
432
|
+
);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
427
435
|
return formatToolResponse({
|
|
428
436
|
summary,
|
|
429
437
|
block: blockLines.join("\n"),
|
|
@@ -558,6 +566,50 @@ function formatDuration(ms) {
|
|
|
558
566
|
if (h < 48) return `${h}h`;
|
|
559
567
|
return `${Math.round(h / 24)}d`;
|
|
560
568
|
}
|
|
569
|
+
async function expandNode(client2, input) {
|
|
570
|
+
const path = projectPath(
|
|
571
|
+
input.project,
|
|
572
|
+
`/graph/expand/${encodeURIComponent(input.nodeId)}?direction=${input.direction}`
|
|
573
|
+
);
|
|
574
|
+
return withMissingNodeFallback(async () => {
|
|
575
|
+
const result = await client2.get(path);
|
|
576
|
+
const dirWord = input.direction === "up" ? "callers/dependents (up)" : "callees/dependencies (down)";
|
|
577
|
+
const summary = `${result.node.id} is ${result.node.classification}. ${result.neighbours.length} ${dirWord}.`;
|
|
578
|
+
const blockLines = result.neighbours.map(
|
|
579
|
+
(n) => ` \u2022 ${n.node} \u2014 ${n.classification} via ${n.edgeType} (${n.provenance})`
|
|
580
|
+
);
|
|
581
|
+
return formatToolResponse({
|
|
582
|
+
summary,
|
|
583
|
+
block: blockLines.length ? blockLines.join("\n") : "(no runtime neighbours in this direction)"
|
|
584
|
+
});
|
|
585
|
+
}, `Node ${input.nodeId} not found in the graph.`);
|
|
586
|
+
}
|
|
587
|
+
async function relate(client2, input) {
|
|
588
|
+
const qs = input.maxDepth !== void 0 ? `&maxDepth=${input.maxDepth}` : "";
|
|
589
|
+
const path = projectPath(
|
|
590
|
+
input.project,
|
|
591
|
+
`/graph/relate?a=${encodeURIComponent(input.a)}&b=${encodeURIComponent(input.b)}${qs}`
|
|
592
|
+
);
|
|
593
|
+
try {
|
|
594
|
+
const result = await client2.get(path);
|
|
595
|
+
if (!result.related) {
|
|
596
|
+
return formatEmptyResponse(
|
|
597
|
+
`${input.a} and ${input.b} are not related \u2014 ${result.note ?? "no path found"}.`
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
const arrow = result.direction === "a->b" ? `${input.a} \u2192 ${input.b}` : `${input.b} \u2192 ${input.a}`;
|
|
601
|
+
const carries = result.paths[0]?.carriesSignal ? "and the path carries the failure end to end" : "but the path carries no failure signal";
|
|
602
|
+
const gap = result.grainGap ? " (grain gap \u2014 only a coarser link is in evidence)" : "";
|
|
603
|
+
const summary = `${arrow}: a path exists ${carries}${gap}.`;
|
|
604
|
+
const blockLines = result.paths.map(
|
|
605
|
+
(p) => ` ${p.nodes.join(" \u2192 ")} [${p.edgeTypes.join(", ")}] carriesSignal=${p.carriesSignal}`
|
|
606
|
+
);
|
|
607
|
+
return formatToolResponse({ summary, block: blockLines.join("\n") });
|
|
608
|
+
} catch (err) {
|
|
609
|
+
if (err instanceof ProjectNotFoundError) return formatErrorResponse(err.message);
|
|
610
|
+
return formatErrorResponse(`Error talking to neat-core: ${err.message}`);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
561
613
|
async function getIncidentHistory(client2, input) {
|
|
562
614
|
return withMissingNodeFallback(async () => {
|
|
563
615
|
const body = await client2.get(
|
|
@@ -1081,6 +1133,27 @@ registerTool(
|
|
|
1081
1133
|
},
|
|
1082
1134
|
async (input) => getObservedDependencies(client, { ...input, project: projectFor(input) })
|
|
1083
1135
|
);
|
|
1136
|
+
registerTool(
|
|
1137
|
+
"expand",
|
|
1138
|
+
'Take one navigation step from a node and classify the neighbourhood (ADR-189). direction "up" walks to callers/dependents (who calls this), "down" walks to callees/dependencies (what this calls). Each neighbour comes back classified primary-failure / symptom-only / unrelated. Use this to navigate a failure one hop at a time instead of trusting a single verdict \u2014 a symptom-only node is a downstream victim, so walk "up" from it toward the real cause.',
|
|
1139
|
+
{
|
|
1140
|
+
nodeId: z.string().describe("Graph node id to step from"),
|
|
1141
|
+
direction: z.enum(["up", "down"]).describe("up = callers/dependents (toward the cause), down = callees/dependencies"),
|
|
1142
|
+
project: projectField
|
|
1143
|
+
},
|
|
1144
|
+
async (input) => expandNode(client, { ...input, project: projectFor(input) })
|
|
1145
|
+
);
|
|
1146
|
+
registerTool(
|
|
1147
|
+
"relate",
|
|
1148
|
+
'Confirm whether two nodes are connected, which way, and whether the connecting path carries the failure (ADR-189). Returns the direction (a\u2192b or b\u2192a), the path with per-hop provenance, and carriesSignal \u2014 whether errors/latency run end to end, which turns "a path exists" into "a is actually causing b". No path within the depth bound returns "no path within N hops", never a false "unrelated".',
|
|
1149
|
+
{
|
|
1150
|
+
a: z.string().describe("First node id (the hypothesised cause)"),
|
|
1151
|
+
b: z.string().describe("Second node id (the hypothesised symptom)"),
|
|
1152
|
+
maxDepth: z.number().int().min(1).max(10).optional().describe("Max path length to search (default 5)"),
|
|
1153
|
+
project: projectField
|
|
1154
|
+
},
|
|
1155
|
+
async (input) => relate(client, { ...input, project: projectFor(input) })
|
|
1156
|
+
);
|
|
1084
1157
|
registerTool(
|
|
1085
1158
|
"get_incident_history",
|
|
1086
1159
|
"Return recent OTel error events recorded against a node, most recent first.",
|