@unified-product-graph/cloud-server 0.9.14 → 0.9.16
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.js +247 -3
- package/dist/index.js.map +1 -1
- package/dist/tools-manifest.json +108 -50
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -998,6 +998,148 @@ function textError(s) {
|
|
|
998
998
|
return { content: [{ type: "text", text: s }], isError: true };
|
|
999
999
|
}
|
|
1000
1000
|
|
|
1001
|
+
// ../upg-mcp-tooling/dist/tree-assemble.js
|
|
1002
|
+
var DEFAULT_MAX_NODES = 400;
|
|
1003
|
+
function shell(reader, id, includeProps) {
|
|
1004
|
+
const n = reader.getNode(id);
|
|
1005
|
+
if (!n)
|
|
1006
|
+
return void 0;
|
|
1007
|
+
const node = { id: n.id, type: n.type, title: n.title, children: [] };
|
|
1008
|
+
if (n.status)
|
|
1009
|
+
node.status = n.status;
|
|
1010
|
+
if (includeProps && includeProps.length > 0 && n.properties) {
|
|
1011
|
+
const picked = {};
|
|
1012
|
+
for (const k of includeProps) {
|
|
1013
|
+
if (k in n.properties)
|
|
1014
|
+
picked[k] = n.properties[k];
|
|
1015
|
+
}
|
|
1016
|
+
if (Object.keys(picked).length > 0)
|
|
1017
|
+
node.properties = picked;
|
|
1018
|
+
}
|
|
1019
|
+
return node;
|
|
1020
|
+
}
|
|
1021
|
+
function childrenOf(reader, nodeId2, childTypes) {
|
|
1022
|
+
if (childTypes.length === 0)
|
|
1023
|
+
return [];
|
|
1024
|
+
const allow = new Set(childTypes);
|
|
1025
|
+
const out = [];
|
|
1026
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1027
|
+
for (const e of reader.getEdgesForNode(nodeId2)) {
|
|
1028
|
+
if (e.source !== nodeId2)
|
|
1029
|
+
continue;
|
|
1030
|
+
if (seen.has(e.target))
|
|
1031
|
+
continue;
|
|
1032
|
+
const t = reader.getNode(e.target);
|
|
1033
|
+
if (t && allow.has(t.type)) {
|
|
1034
|
+
out.push(e.target);
|
|
1035
|
+
seen.add(e.target);
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
return out;
|
|
1039
|
+
}
|
|
1040
|
+
function assembleTree(reader, pattern, opts) {
|
|
1041
|
+
const maxNodes = Math.min(Math.max(opts.max_nodes ?? DEFAULT_MAX_NODES, 1), 2e3);
|
|
1042
|
+
const maxDepth = Math.min(Math.max(opts.depth ?? pattern.natural_depth, 1), 12);
|
|
1043
|
+
const includeProps = opts.include_properties;
|
|
1044
|
+
const buildFrom = (rootIds, anchorType) => {
|
|
1045
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1046
|
+
const gaps = [];
|
|
1047
|
+
let nodes = 0;
|
|
1048
|
+
let levels = 0;
|
|
1049
|
+
let truncated = false;
|
|
1050
|
+
let childCount = 0;
|
|
1051
|
+
const expand = (node, depth) => {
|
|
1052
|
+
if (depth > levels)
|
|
1053
|
+
levels = depth;
|
|
1054
|
+
const childTypes = pattern.child_map[node.type] ?? [];
|
|
1055
|
+
if (depth >= maxDepth) {
|
|
1056
|
+
if (childTypes.length > 0 && childrenOf(reader, node.id, childTypes).length > 0)
|
|
1057
|
+
truncated = true;
|
|
1058
|
+
return;
|
|
1059
|
+
}
|
|
1060
|
+
const childIds = childrenOf(reader, node.id, childTypes);
|
|
1061
|
+
if (childTypes.length > 0 && childIds.length === 0) {
|
|
1062
|
+
gaps.push({ node_id: node.id, type: node.type, title: node.title, missing: childTypes });
|
|
1063
|
+
return;
|
|
1064
|
+
}
|
|
1065
|
+
for (const cid of childIds) {
|
|
1066
|
+
if (visited.has(cid))
|
|
1067
|
+
continue;
|
|
1068
|
+
if (nodes >= maxNodes) {
|
|
1069
|
+
truncated = true;
|
|
1070
|
+
return;
|
|
1071
|
+
}
|
|
1072
|
+
const child = shell(reader, cid, includeProps);
|
|
1073
|
+
if (!child)
|
|
1074
|
+
continue;
|
|
1075
|
+
visited.add(cid);
|
|
1076
|
+
nodes++;
|
|
1077
|
+
childCount++;
|
|
1078
|
+
node.children.push(child);
|
|
1079
|
+
expand(child, depth + 1);
|
|
1080
|
+
}
|
|
1081
|
+
};
|
|
1082
|
+
const roots = [];
|
|
1083
|
+
for (const rid of rootIds) {
|
|
1084
|
+
if (visited.has(rid))
|
|
1085
|
+
continue;
|
|
1086
|
+
if (nodes >= maxNodes) {
|
|
1087
|
+
truncated = true;
|
|
1088
|
+
break;
|
|
1089
|
+
}
|
|
1090
|
+
const root = shell(reader, rid, includeProps);
|
|
1091
|
+
if (!root)
|
|
1092
|
+
continue;
|
|
1093
|
+
visited.add(rid);
|
|
1094
|
+
nodes++;
|
|
1095
|
+
roots.push(root);
|
|
1096
|
+
expand(root, 1);
|
|
1097
|
+
}
|
|
1098
|
+
return { anchorType, roots, nodes, levels, truncated, gaps, childCount };
|
|
1099
|
+
};
|
|
1100
|
+
if (opts.from_id) {
|
|
1101
|
+
const n = reader.getNode(opts.from_id);
|
|
1102
|
+
const anchorType = n?.type ?? pattern.anchor_type;
|
|
1103
|
+
const b = buildFrom([opts.from_id], anchorType);
|
|
1104
|
+
return {
|
|
1105
|
+
pattern: pattern.id,
|
|
1106
|
+
framework_id: pattern.framework_id,
|
|
1107
|
+
anchor_type: pattern.anchor_type,
|
|
1108
|
+
anchor_used: anchorType,
|
|
1109
|
+
roots: b.roots,
|
|
1110
|
+
stats: { nodes: b.nodes, levels: b.levels, truncated: b.truncated },
|
|
1111
|
+
gaps: b.gaps
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
1114
|
+
const candidates = [pattern.anchor_type, ...pattern.fallback_anchors];
|
|
1115
|
+
const idsOfType = (type) => reader.getAllNodes().filter((n) => n.type === type).map((n) => n.id);
|
|
1116
|
+
let chosen;
|
|
1117
|
+
for (const cand of candidates) {
|
|
1118
|
+
const rootIds = idsOfType(cand);
|
|
1119
|
+
if (rootIds.length === 0)
|
|
1120
|
+
continue;
|
|
1121
|
+
const b = buildFrom(rootIds, cand);
|
|
1122
|
+
chosen = b;
|
|
1123
|
+
if (b.childCount > 0)
|
|
1124
|
+
break;
|
|
1125
|
+
}
|
|
1126
|
+
if (!chosen) {
|
|
1127
|
+
chosen = { anchorType: pattern.anchor_type, roots: [], nodes: 0, levels: 0, truncated: false, gaps: [], childCount: 0 };
|
|
1128
|
+
}
|
|
1129
|
+
const result = {
|
|
1130
|
+
pattern: pattern.id,
|
|
1131
|
+
framework_id: pattern.framework_id,
|
|
1132
|
+
anchor_type: pattern.anchor_type,
|
|
1133
|
+
anchor_used: chosen.anchorType,
|
|
1134
|
+
roots: chosen.roots,
|
|
1135
|
+
stats: { nodes: chosen.nodes, levels: chosen.levels, truncated: chosen.truncated },
|
|
1136
|
+
gaps: chosen.gaps
|
|
1137
|
+
};
|
|
1138
|
+
if (chosen.anchorType !== pattern.anchor_type)
|
|
1139
|
+
result.anchor_resolved_from = pattern.anchor_type;
|
|
1140
|
+
return result;
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1001
1143
|
// ../upg-mcp-tooling/dist/catalog.js
|
|
1002
1144
|
import { UPG_EDGE_CATALOG, UPG_PROPERTY_SCHEMA, getDomainForType, getGuideForDomain, getLifecycleForType, getPropertySchema, resolveEntityType, UnknownEntityTypeError } from "@unified-product-graph/core";
|
|
1003
1145
|
function buildEntitySchema(rawType, options = {}) {
|
|
@@ -1332,6 +1474,58 @@ var getChanges = async (args, { store }) => {
|
|
|
1332
1474
|
return text(JSON.stringify({ changes: filtered, total: filtered.length }, null, 2));
|
|
1333
1475
|
};
|
|
1334
1476
|
|
|
1477
|
+
// src/tools/tree.ts
|
|
1478
|
+
import { getTreePattern, UPG_TREE_PATTERNS } from "@unified-product-graph/core";
|
|
1479
|
+
var getTree = async (args, { store }) => {
|
|
1480
|
+
if (!args.product_id) return textError("Missing required parameter: product_id");
|
|
1481
|
+
const productId = args.product_id;
|
|
1482
|
+
const patternId = args.pattern;
|
|
1483
|
+
if (!patternId) {
|
|
1484
|
+
return textError(
|
|
1485
|
+
`Missing required parameter: pattern. One of: ${UPG_TREE_PATTERNS.map((p) => p.id).join(", ")}.`
|
|
1486
|
+
);
|
|
1487
|
+
}
|
|
1488
|
+
const pattern = getTreePattern(patternId);
|
|
1489
|
+
if (!pattern) {
|
|
1490
|
+
return textError(
|
|
1491
|
+
`Unknown tree pattern: "${patternId}". Valid patterns: ${UPG_TREE_PATTERNS.map((p) => p.id).join(", ")}.`
|
|
1492
|
+
);
|
|
1493
|
+
}
|
|
1494
|
+
const includeProperties = Array.isArray(args.include_properties) ? args.include_properties : void 0;
|
|
1495
|
+
const allNodes = await store.getAllNodes(productId);
|
|
1496
|
+
const allEdges = await store.getAllEdges(productId);
|
|
1497
|
+
const nodeById = new Map(allNodes.map((n) => [n.id, n]));
|
|
1498
|
+
const edgesByNode = /* @__PURE__ */ new Map();
|
|
1499
|
+
for (const e of allEdges) {
|
|
1500
|
+
let src = edgesByNode.get(e.source);
|
|
1501
|
+
if (!src) {
|
|
1502
|
+
src = [];
|
|
1503
|
+
edgesByNode.set(e.source, src);
|
|
1504
|
+
}
|
|
1505
|
+
src.push(e);
|
|
1506
|
+
if (e.target !== e.source) {
|
|
1507
|
+
let tgt = edgesByNode.get(e.target);
|
|
1508
|
+
if (!tgt) {
|
|
1509
|
+
tgt = [];
|
|
1510
|
+
edgesByNode.set(e.target, tgt);
|
|
1511
|
+
}
|
|
1512
|
+
tgt.push(e);
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
const reader = {
|
|
1516
|
+
getNode: (id) => nodeById.get(id),
|
|
1517
|
+
getAllNodes: () => allNodes,
|
|
1518
|
+
getEdgesForNode: (id) => edgesByNode.get(id) ?? []
|
|
1519
|
+
};
|
|
1520
|
+
const result = assembleTree(reader, pattern, {
|
|
1521
|
+
from_id: args.from_id,
|
|
1522
|
+
depth: args.depth,
|
|
1523
|
+
include_properties: includeProperties,
|
|
1524
|
+
max_nodes: args.max_nodes
|
|
1525
|
+
});
|
|
1526
|
+
return text(JSON.stringify(result, null, 2));
|
|
1527
|
+
};
|
|
1528
|
+
|
|
1335
1529
|
// src/tools/nodes.ts
|
|
1336
1530
|
import { getLifecycleForType as getLifecycleForType2, resolveEntityType as resolveEntityType2, UnknownEntityTypeError as UnknownEntityTypeError2 } from "@unified-product-graph/core";
|
|
1337
1531
|
import {
|
|
@@ -2084,6 +2278,9 @@ import {
|
|
|
2084
2278
|
UPG_REGIONS,
|
|
2085
2279
|
UPG_REGION_MAP,
|
|
2086
2280
|
UPG_REGION_COUNT,
|
|
2281
|
+
UPG_AREA_TAXONOMY,
|
|
2282
|
+
getCoverageKeysForRegion,
|
|
2283
|
+
getBusinessAreasForRegion,
|
|
2087
2284
|
UPG_LENSES,
|
|
2088
2285
|
UPG_TYPE_LABELS,
|
|
2089
2286
|
UPG_TYPE_LABELS_MAP,
|
|
@@ -2358,10 +2555,12 @@ var listRegions = () => {
|
|
|
2358
2555
|
composes_atomic_domains: r.composes_atomic_domains,
|
|
2359
2556
|
entity_count: r.entities.length,
|
|
2360
2557
|
intra_edge_count: r.intra_edges.length,
|
|
2361
|
-
boundary_edge_count: r.boundary_edges.length
|
|
2558
|
+
boundary_edge_count: r.boundary_edges.length,
|
|
2559
|
+
coverage_keys: getCoverageKeysForRegion(r.id),
|
|
2560
|
+
business_areas: getBusinessAreasForRegion(r.id)
|
|
2362
2561
|
}));
|
|
2363
2562
|
return text(
|
|
2364
|
-
JSON.stringify({ count: UPG_REGION_COUNT, regions }, null, 2)
|
|
2563
|
+
JSON.stringify({ count: UPG_REGION_COUNT, regions, area_taxonomy: UPG_AREA_TAXONOMY }, null, 2)
|
|
2365
2564
|
);
|
|
2366
2565
|
};
|
|
2367
2566
|
var getRegion = (args) => {
|
|
@@ -2369,7 +2568,11 @@ var getRegion = (args) => {
|
|
|
2369
2568
|
if (!id) return textError("Missing required parameter: id");
|
|
2370
2569
|
const region = UPG_REGION_MAP[id];
|
|
2371
2570
|
if (!region) return textError(`Unknown region id: ${id}`);
|
|
2372
|
-
return text(JSON.stringify(
|
|
2571
|
+
return text(JSON.stringify({
|
|
2572
|
+
...region,
|
|
2573
|
+
coverage_keys: getCoverageKeysForRegion(id),
|
|
2574
|
+
business_areas: getBusinessAreasForRegion(id)
|
|
2575
|
+
}, null, 2));
|
|
2373
2576
|
};
|
|
2374
2577
|
var getRegionForEntity = (args) => {
|
|
2375
2578
|
const entityType = args.entity_type;
|
|
@@ -4235,6 +4438,46 @@ var TOOL_DEFINITIONS = [
|
|
|
4235
4438
|
]
|
|
4236
4439
|
}
|
|
4237
4440
|
},
|
|
4441
|
+
{
|
|
4442
|
+
"name": "get_tree",
|
|
4443
|
+
"description": "Assemble a canonical tree pattern (ost, okr, user, product, validation, strategy, feature_areas) from the product graph. Walks the pattern's type-driven child map over the live graph (drift-proof, follows whatever edge wired each parent to a child of the expected type), roots at the pattern anchor with fallback, and reports structural gaps. Returns nested data, not rendered text.",
|
|
4444
|
+
"inputSchema": {
|
|
4445
|
+
"type": "object",
|
|
4446
|
+
"properties": {
|
|
4447
|
+
"product_id": {
|
|
4448
|
+
"type": "string",
|
|
4449
|
+
"description": "The product ID"
|
|
4450
|
+
},
|
|
4451
|
+
"pattern": {
|
|
4452
|
+
"type": "string",
|
|
4453
|
+
"description": "Tree pattern id: ost, okr, user, product, validation, strategy, feature_areas"
|
|
4454
|
+
},
|
|
4455
|
+
"from_id": {
|
|
4456
|
+
"type": "string",
|
|
4457
|
+
"description": "Explicit root node id. Defaults to the pattern's canonical anchor type."
|
|
4458
|
+
},
|
|
4459
|
+
"depth": {
|
|
4460
|
+
"type": "number",
|
|
4461
|
+
"description": "Max levels. Defaults to the pattern's natural depth."
|
|
4462
|
+
},
|
|
4463
|
+
"include_properties": {
|
|
4464
|
+
"type": "array",
|
|
4465
|
+
"items": {
|
|
4466
|
+
"type": "string"
|
|
4467
|
+
},
|
|
4468
|
+
"description": "Node property keys to inline on each tree node."
|
|
4469
|
+
},
|
|
4470
|
+
"max_nodes": {
|
|
4471
|
+
"type": "number",
|
|
4472
|
+
"description": "Cap on assembled nodes. The tree is summarised (stats.truncated) rather than silently cut."
|
|
4473
|
+
}
|
|
4474
|
+
},
|
|
4475
|
+
"required": [
|
|
4476
|
+
"product_id",
|
|
4477
|
+
"pattern"
|
|
4478
|
+
]
|
|
4479
|
+
}
|
|
4480
|
+
},
|
|
4238
4481
|
{
|
|
4239
4482
|
"name": "get_graph_digest",
|
|
4240
4483
|
"description": "Pre-computed graph analytics: counts, health metrics, chain completeness, business area coverage, lifecycle balance. ~500 tokens vs ~5-8K for equivalent manual fetches.",
|
|
@@ -5221,6 +5464,7 @@ var HANDLERS = {
|
|
|
5221
5464
|
get_product_context: getProductContext,
|
|
5222
5465
|
get_graph_digest: getGraphDigest,
|
|
5223
5466
|
query,
|
|
5467
|
+
get_tree: getTree,
|
|
5224
5468
|
get_changes: getChanges,
|
|
5225
5469
|
list_nodes: listNodes,
|
|
5226
5470
|
export_upg_document: exportUpgDocument,
|