@unified-product-graph/cloud-server 0.6.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.
@@ -0,0 +1,3432 @@
1
+ {
2
+ "schema_version": "2",
3
+ "package": "@unified-product-graph/cloud-server",
4
+ "package_version": "0.6.0",
5
+ "tool_count": 91,
6
+ "domains": [
7
+ "products",
8
+ "context",
9
+ "nodes",
10
+ "edges",
11
+ "areas",
12
+ "schema",
13
+ "collaboration",
14
+ "analytics",
15
+ "webhooks",
16
+ "spec",
17
+ "portfolio",
18
+ "batch",
19
+ "validation",
20
+ "migrations"
21
+ ],
22
+ "tools": [
23
+ {
24
+ "name": "create_product",
25
+ "description": "Create a new product graph.",
26
+ "domain": "products",
27
+ "inputSchema": {
28
+ "type": "object",
29
+ "properties": {
30
+ "title": {
31
+ "type": "string",
32
+ "description": "Product name"
33
+ },
34
+ "description": {
35
+ "type": "string",
36
+ "description": "Optional description"
37
+ },
38
+ "stage": {
39
+ "type": "string",
40
+ "description": "idea | mvp | growth | scale"
41
+ }
42
+ },
43
+ "required": [
44
+ "title"
45
+ ]
46
+ },
47
+ "throws": [
48
+ "textError when `title` is missing."
49
+ ],
50
+ "examples": [],
51
+ "warnings": [
52
+ "Billing-relevant: product count typically drives plan tier;\ncreation may trigger a tier upgrade or hit the plan's product cap."
53
+ ],
54
+ "see": [
55
+ "list_products",
56
+ "grant_access",
57
+ "list_product_stages"
58
+ ],
59
+ "source": "src/tools/products.ts:39",
60
+ "symbol": "createProduct",
61
+ "returns": "JSON: `{ product: { id, title, description?, stage? } }`.",
62
+ "atomicity": "atomic"
63
+ },
64
+ {
65
+ "name": "get_audit_log",
66
+ "description": "Get recent changes (audit log) for a product.",
67
+ "domain": "products",
68
+ "inputSchema": {
69
+ "type": "object",
70
+ "properties": {
71
+ "product_id": {
72
+ "type": "string",
73
+ "description": "Product ID"
74
+ },
75
+ "limit": {
76
+ "type": "number",
77
+ "description": "Max entries (default 50)"
78
+ }
79
+ },
80
+ "required": [
81
+ "product_id"
82
+ ]
83
+ },
84
+ "throws": [
85
+ "textError when `product_id` is missing."
86
+ ],
87
+ "examples": [],
88
+ "warnings": [
89
+ "Retention-windowed: entries beyond the plan-tier retention period\nare pruned. An empty window may mean \"out of retention\", not \"no activity\"."
90
+ ],
91
+ "see": [
92
+ "get_graph_analytics",
93
+ "get_graph_digest",
94
+ "list_products"
95
+ ],
96
+ "source": "src/tools/products.ts:62",
97
+ "symbol": "getAuditLog",
98
+ "returns": "JSON: `{ entries: Array<{ ...mutation }> }`.",
99
+ "atomicity": "atomic (read-only)"
100
+ },
101
+ {
102
+ "name": "list_products",
103
+ "description": "List all products in this UPG cloud instance.",
104
+ "domain": "products",
105
+ "inputSchema": {
106
+ "type": "object",
107
+ "properties": {}
108
+ },
109
+ "throws": [],
110
+ "examples": [],
111
+ "warnings": [
112
+ "RLS-bounded; an empty list can mean \"no products\" or \"no access\".\nPair with `list_collaborators` to confirm scope on a specific product."
113
+ ],
114
+ "see": [
115
+ "create_product",
116
+ "get_product_context",
117
+ "get_graph_digest",
118
+ "list_collaborators"
119
+ ],
120
+ "source": "src/tools/products.ts:22",
121
+ "symbol": "listProducts",
122
+ "returns": "JSON: `{ products: Array<{ id, title, description?, stage? }> }`.",
123
+ "atomicity": "atomic (read-only)"
124
+ },
125
+ {
126
+ "name": "get_changes",
127
+ "description": "Get a log of recent changes from the audit log.",
128
+ "domain": "context",
129
+ "inputSchema": {
130
+ "type": "object",
131
+ "properties": {
132
+ "product_id": {
133
+ "type": "string",
134
+ "description": "The product ID"
135
+ },
136
+ "since": {
137
+ "type": "string",
138
+ "description": "ISO 8601 timestamp; only return changes after this time"
139
+ },
140
+ "limit": {
141
+ "type": "number",
142
+ "description": "Max results (default 50)"
143
+ }
144
+ },
145
+ "required": [
146
+ "product_id"
147
+ ]
148
+ },
149
+ "throws": [
150
+ "textError when `product_id` is missing."
151
+ ],
152
+ "examples": [],
153
+ "warnings": [
154
+ "Backed by the audit log: entries beyond the plan-tier\nretention window are pruned and stay out of this surface. The `since`\nfilter runs in-memory after the store fetches up to `limit` entries,\nso narrow `since` windows on busy products may surface fewer rows\nthan expected (raise `limit` to compensate)."
155
+ ],
156
+ "see": [
157
+ "get_audit_log",
158
+ "get_graph_digest",
159
+ "get_product_context"
160
+ ],
161
+ "source": "src/tools/context.ts:329",
162
+ "symbol": "getChanges",
163
+ "returns": "JSON: `{ changes: AuditEntry[], total }`.",
164
+ "atomicity": "atomic (read-only)"
165
+ },
166
+ {
167
+ "name": "get_graph_digest",
168
+ "description": "Pre-computed graph analytics: counts, health metrics, chain completeness, business area coverage, lifecycle balance. ~500 tokens vs ~5-8K for equivalent manual fetches.",
169
+ "domain": "context",
170
+ "inputSchema": {
171
+ "type": "object",
172
+ "properties": {
173
+ "product_id": {
174
+ "type": "string",
175
+ "description": "The product ID"
176
+ }
177
+ },
178
+ "required": [
179
+ "product_id"
180
+ ]
181
+ },
182
+ "throws": [
183
+ "textError when `product_id` is missing."
184
+ ],
185
+ "examples": [],
186
+ "warnings": [
187
+ "Chain keys carry v0.1 names (`persona_with_jtbd`,\n`hypothesis_total`) pending a canonical rename. Lifecycle bucketing\nuses local heuristics (`BUSINESS_AREAS` / `LIFECYCLE_PHASES`\nconstants in this file) rather than the canonical `UPG_DOMAINS` ring,\nso it may drift from spec across versions."
188
+ ],
189
+ "see": [
190
+ "get_product_context",
191
+ "get_graph_analytics",
192
+ "list_benchmarks",
193
+ "validate_graph"
194
+ ],
195
+ "source": "src/tools/context.ts:100",
196
+ "symbol": "getGraphDigest",
197
+ "returns": "JSON with `product`, `counts`, `health`, `chains`, `coverage`,\n`lifecycle` keys (~500 tokens of summary). Note: chain keys still use the\nv0.1 names (`persona_with_jtbd` etc.) pending a canonical rename.",
198
+ "atomicity": "atomic (read-only)"
199
+ },
200
+ {
201
+ "name": "get_product_context",
202
+ "description": "Returns the product summary, entity counts by type, and a human-readable overview of the graph. Use this first to understand what is in the graph.",
203
+ "domain": "context",
204
+ "inputSchema": {
205
+ "type": "object",
206
+ "properties": {
207
+ "product_id": {
208
+ "type": "string",
209
+ "description": "Product ID"
210
+ }
211
+ },
212
+ "required": [
213
+ "product_id"
214
+ ]
215
+ },
216
+ "throws": [
217
+ "textError when `product_id` is missing or the product\nis not visible to the caller."
218
+ ],
219
+ "examples": [],
220
+ "warnings": [],
221
+ "see": [
222
+ "get_graph_digest",
223
+ "get_graph_analytics",
224
+ "get_entity_schema",
225
+ "list_nodes"
226
+ ],
227
+ "source": "src/tools/context.ts:45",
228
+ "symbol": "getProductContext",
229
+ "returns": "Text: `## <product title>` followed by description/stage,\ngraph stats (node/edge/type counts), and a sorted breakdown of entities\nper type. Errors with `Product not found: <id>` for unknown products.",
230
+ "atomicity": "atomic (read-only)"
231
+ },
232
+ {
233
+ "name": "query",
234
+ "description": "Traverse the graph following typed edges. Returns a subgraph in a single call. Replaces multi-step fetch patterns. Supports edge type filtering (including !negation), field projection, and truncation metadata.",
235
+ "domain": "context",
236
+ "inputSchema": {
237
+ "type": "object",
238
+ "properties": {
239
+ "product_id": {
240
+ "type": "string",
241
+ "description": "The product ID"
242
+ },
243
+ "from": {
244
+ "type": "string",
245
+ "description": "Start from all nodes of this type"
246
+ },
247
+ "from_id": {
248
+ "type": "string",
249
+ "description": "Start from a specific node ID"
250
+ },
251
+ "traverse": {
252
+ "type": "array",
253
+ "items": {
254
+ "type": "string"
255
+ },
256
+ "description": "Edge types to follow per level. Prefix with ! to exclude."
257
+ },
258
+ "depth": {
259
+ "type": "number",
260
+ "description": "Max depth (default 3, max 10)"
261
+ },
262
+ "include": {
263
+ "type": "array",
264
+ "items": {
265
+ "type": "string"
266
+ },
267
+ "description": "Node fields: \"title\", \"status\", \"tags\", \"description\", \"properties\""
268
+ },
269
+ "limit": {
270
+ "type": "number",
271
+ "description": "Max nodes (default 200, max 1000)"
272
+ },
273
+ "edge_include": {
274
+ "type": "array",
275
+ "items": {
276
+ "type": "string"
277
+ },
278
+ "description": "Edge fields to return. Empty = no edges."
279
+ }
280
+ },
281
+ "required": [
282
+ "product_id"
283
+ ]
284
+ },
285
+ "throws": [
286
+ "textError when `product_id` is missing, or when\nneither `from` nor `from_id` is provided, or when `from_id` does not\nresolve."
287
+ ],
288
+ "examples": [],
289
+ "warnings": [
290
+ "Pre-loads the entire product graph into memory before\nfiltering; for products beyond ~10K nodes this can be heavy. Use\n`from_id` plus a tight `depth` for narrow slices, and pair with\n`include` / `edge_include` to trim wire payload. Truncation is silent\nbeyond `limit`, so check `truncated` before assuming the result is\ncomplete."
291
+ ],
292
+ "see": [
293
+ "list_nodes",
294
+ "get_node",
295
+ "get_area_graph",
296
+ "search_nodes",
297
+ "resolve_edge_for_pair",
298
+ "trace"
299
+ ],
300
+ "source": "src/tools/context.ts:203",
301
+ "symbol": "query",
302
+ "returns": "JSON: `{ nodes, edges, total_nodes, total_edges,\ntruncated?, truncated_at_depth?, hint? }`. Truncates with a hint when\n`limit` is reached.",
303
+ "atomicity": "atomic (read-only)"
304
+ },
305
+ {
306
+ "name": "create_node",
307
+ "description": "Create a new entity in the graph. Optionally connect it to a parent node.",
308
+ "domain": "nodes",
309
+ "inputSchema": {
310
+ "type": "object",
311
+ "properties": {
312
+ "product_id": {
313
+ "type": "string",
314
+ "description": "Product ID"
315
+ },
316
+ "type": {
317
+ "type": "string",
318
+ "description": "UPG entity type (e.g. \"persona\", \"opportunity\")"
319
+ },
320
+ "title": {
321
+ "type": "string",
322
+ "description": "Entity title"
323
+ },
324
+ "description": {
325
+ "type": "string",
326
+ "description": "Optional description"
327
+ },
328
+ "tags": {
329
+ "type": "array",
330
+ "items": {
331
+ "type": "string"
332
+ },
333
+ "description": "Freeform tags"
334
+ },
335
+ "status": {
336
+ "type": "string",
337
+ "description": "Lifecycle status"
338
+ },
339
+ "properties": {
340
+ "type": "object",
341
+ "description": "Type-specific fields"
342
+ },
343
+ "parent_id": {
344
+ "type": "string",
345
+ "description": "Parent node ID; creates an edge automatically"
346
+ }
347
+ },
348
+ "required": [
349
+ "product_id",
350
+ "type",
351
+ "title"
352
+ ]
353
+ },
354
+ "throws": [
355
+ "textError when `product_id`, `type`, or `title` is\nmissing."
356
+ ],
357
+ "examples": [],
358
+ "warnings": [
359
+ "Pass `parent_id` to auto-create a containment edge with inferred\ntype; missing parents are reported via `warning` rather than failing\nthe create."
360
+ ],
361
+ "see": [
362
+ "batch_create_nodes",
363
+ "update_node",
364
+ "get_entity_schema",
365
+ "list_entity_types",
366
+ "get_valid_children"
367
+ ],
368
+ "source": "src/tools/nodes.ts:325",
369
+ "symbol": "createNode",
370
+ "returns": "JSON: `{ node, edge?, warning? }`. `edge` is null when no\n`parent_id` is passed; `warning` is present on lifecycle/parent issues.",
371
+ "atomicity": "atomic-with-rollback"
372
+ },
373
+ {
374
+ "name": "deduplicate_nodes",
375
+ "description": "Merge a set of duplicate nodes into a canonical node. Rebinds all edges from duplicates to canonical, removes self-loops and duplicate edges, merges properties (canonical wins on conflicts), then deletes the duplicates inside a single atomic Postgres transaction. Default dry_run: true previews the operation without modifying data.",
376
+ "domain": "nodes",
377
+ "inputSchema": {
378
+ "type": "object",
379
+ "properties": {
380
+ "product_id": {
381
+ "type": "string",
382
+ "description": "Product ID"
383
+ },
384
+ "canonical_id": {
385
+ "type": "string",
386
+ "description": "The node to keep"
387
+ },
388
+ "duplicate_ids": {
389
+ "type": "array",
390
+ "items": {
391
+ "type": "string"
392
+ },
393
+ "description": "Nodes to merge into canonical and delete (max 20)"
394
+ },
395
+ "dry_run": {
396
+ "type": "boolean",
397
+ "description": "Default true: report what would happen without changing anything."
398
+ }
399
+ },
400
+ "required": [
401
+ "product_id",
402
+ "canonical_id",
403
+ "duplicate_ids"
404
+ ]
405
+ },
406
+ "throws": [
407
+ "textError when `product_id`, `canonical_id`, or\n`duplicate_ids` are missing, when the arrays exceed limits, when\n`canonical_id` appears in `duplicate_ids`, or when any node does not\nexist / does not belong to the product."
408
+ ],
409
+ "examples": [],
410
+ "warnings": [
411
+ "Default `dry_run: true`; pass `dry_run: false` to commit. The\nmerge is permanent: duplicates are deleted, their edges rebound to\nthe canonical, self-loops removed, and duplicate edges deduplicated.\nThe change stands once committed (no undo); the audit log records\neach merge for the retention window."
412
+ ],
413
+ "see": [
414
+ "search_nodes",
415
+ "get_nodes",
416
+ "delete_node",
417
+ "validate_graph"
418
+ ],
419
+ "source": "src/tools/nodes.ts:587",
420
+ "symbol": "deduplicateNodes",
421
+ "returns": "With `dry_run: true` (default): `{ canonical_id, duplicate_ids,\nedges_to_rebind, nodes_to_delete, dry_run }`. With `dry_run: false`:\n`{ canonical_id, merged_ids, rebound_edges, removed_self_loops,\nremoved_duplicate_edges, dry_run }`.",
422
+ "atomicity": "atomic-with-rollback (all mutations committed or rolled back\ntogether)."
423
+ },
424
+ {
425
+ "name": "delete_node",
426
+ "description": "Remove an entity and all its connected edges from the graph.",
427
+ "domain": "nodes",
428
+ "inputSchema": {
429
+ "type": "object",
430
+ "properties": {
431
+ "node_id": {
432
+ "type": "string",
433
+ "description": "The node ID to delete"
434
+ }
435
+ },
436
+ "required": [
437
+ "node_id"
438
+ ]
439
+ },
440
+ "throws": [
441
+ "textError when `node_id` is missing or the store\nrejects the deletion."
442
+ ],
443
+ "examples": [],
444
+ "warnings": [
445
+ "Cascade-deletes ALL incident edges, including cross-product\nedges where the node is an endpoint. The operation is permanent (no\nsoft-delete or undo); the audit log records the removal. Pair with\n`get_node` first if you need a snapshot."
446
+ ],
447
+ "see": [
448
+ "batch_delete_nodes",
449
+ "get_node",
450
+ "deduplicate_nodes"
451
+ ],
452
+ "source": "src/tools/nodes.ts:491",
453
+ "symbol": "deleteNode",
454
+ "returns": "JSON: `{ deleted_node_id, deleted_node_title, deleted_edge_ids }`.\nErrors propagate from the store (e.g. unknown id).",
455
+ "atomicity": "atomic-with-rollback"
456
+ },
457
+ {
458
+ "name": "export_upg_document",
459
+ "description": "Export the full product graph as a UPG document: product metadata, all nodes, and all edges. Used by the upg pull CLI and apply_pull_changeset for sync/backup. Supports cursor pagination for large products (1000+ nodes): default limit 1000, max 10000. Pass next_cursor from a previous response as cursor to advance. Edges are returned in full on every page.",
460
+ "domain": "nodes",
461
+ "inputSchema": {
462
+ "type": "object",
463
+ "properties": {
464
+ "product_id": {
465
+ "type": "string",
466
+ "description": "Product ID"
467
+ },
468
+ "limit": {
469
+ "type": "number",
470
+ "description": "Max nodes per page (default 1000, max 10000)"
471
+ },
472
+ "cursor": {
473
+ "type": "string",
474
+ "description": "Opaque pagination cursor; pass next_cursor from a previous response to advance."
475
+ }
476
+ },
477
+ "required": [
478
+ "product_id"
479
+ ]
480
+ },
481
+ "throws": [
482
+ "textError when `product_id` is missing or the product is\nnot visible to the caller."
483
+ ],
484
+ "examples": [],
485
+ "warnings": [
486
+ "For very large products (10 000+ nodes) iterate via `cursor`\nplus `next_cursor` rather than relying on a single call. Every page\nreturns the full edge set, so deduplicate on the client when\nassembling multiple pages."
487
+ ],
488
+ "see": [
489
+ "apply_pull_changeset",
490
+ "get_product_graph",
491
+ "list_nodes"
492
+ ],
493
+ "source": "src/tools/nodes.ts:132",
494
+ "symbol": "exportUpgDocument",
495
+ "returns": "JSON: `{ product, nodes, edges, total_nodes, limit, next_cursor? }`.\n`next_cursor` is present when more node pages remain.",
496
+ "atomicity": "atomic (read-only)"
497
+ },
498
+ {
499
+ "name": "get_node",
500
+ "description": "Get a single entity by ID with its full properties and all connected edges.",
501
+ "domain": "nodes",
502
+ "inputSchema": {
503
+ "type": "object",
504
+ "properties": {
505
+ "node_id": {
506
+ "type": "string",
507
+ "description": "The node ID"
508
+ }
509
+ },
510
+ "required": [
511
+ "node_id"
512
+ ]
513
+ },
514
+ "throws": [
515
+ "textError when `node_id` is missing or the node does\nnot exist (or the caller has no access; RLS shares the same shape for\nboth)."
516
+ ],
517
+ "examples": [],
518
+ "warnings": [],
519
+ "see": [
520
+ "list_nodes",
521
+ "get_nodes",
522
+ "search_nodes",
523
+ "query"
524
+ ],
525
+ "source": "src/tools/nodes.ts:177",
526
+ "symbol": "getNode",
527
+ "returns": "JSON: `{ node, edges_out, edges_in }`. Errors with\n`Node not found: <id>` for unknown ids.",
528
+ "atomicity": "atomic (read-only)"
529
+ },
530
+ {
531
+ "name": "get_nodes",
532
+ "description": "Batch-fetch multiple entities by ID with edges. More efficient than multiple get_node calls.",
533
+ "domain": "nodes",
534
+ "inputSchema": {
535
+ "type": "object",
536
+ "properties": {
537
+ "product_id": {
538
+ "type": "string",
539
+ "description": "The product ID"
540
+ },
541
+ "ids": {
542
+ "type": "array",
543
+ "items": {
544
+ "type": "string"
545
+ },
546
+ "description": "Node IDs to fetch (max 50)"
547
+ },
548
+ "compact_edges": {
549
+ "type": "boolean",
550
+ "description": "Omit titles from edges"
551
+ }
552
+ },
553
+ "required": [
554
+ "product_id",
555
+ "ids"
556
+ ]
557
+ },
558
+ "throws": [
559
+ "textError when `product_id` or `ids` is missing/empty,\nor when `ids` exceeds 50."
560
+ ],
561
+ "examples": [],
562
+ "warnings": [
563
+ "`not_found` shares the same shape for \"node doesn't exist\" and\n\"node exists but caller lacks access\" (RLS treats them alike). Pass\n`compact_edges: true` to drop neighbour-title hydration on edge-heavy\nnodes (~30% smaller wire payload)."
564
+ ],
565
+ "see": [
566
+ "get_node",
567
+ "list_nodes",
568
+ "query"
569
+ ],
570
+ "source": "src/tools/nodes.ts:220",
571
+ "symbol": "getNodes",
572
+ "returns": "JSON: `{ nodes, total, not_found? }`. `not_found` lists any\nrequested ids that did not resolve and appears only when at least one\nmiss occurred.",
573
+ "atomicity": "atomic (read-only)"
574
+ },
575
+ {
576
+ "name": "get_product_graph",
577
+ "description": "Export the full graph for a product (all nodes + edges).",
578
+ "domain": "nodes",
579
+ "inputSchema": {
580
+ "type": "object",
581
+ "properties": {
582
+ "product_id": {
583
+ "type": "string",
584
+ "description": "Product ID"
585
+ }
586
+ },
587
+ "required": [
588
+ "product_id"
589
+ ]
590
+ },
591
+ "throws": [
592
+ "textError when `product_id` is missing or the product\nis not visible to the caller."
593
+ ],
594
+ "examples": [],
595
+ "warnings": [
596
+ "Returns the **entire** graph in one payload; for products\nwith thousands of nodes/edges this can be tens of MB. Prefer `query`\nwith a depth limit plus `include` projection for slices, or\n`list_nodes` plus cursor pagination for full enumeration without the\nwire-size hit."
597
+ ],
598
+ "see": [
599
+ "query",
600
+ "list_nodes",
601
+ "get_graph_digest",
602
+ "get_graph_analytics"
603
+ ],
604
+ "source": "src/tools/nodes.ts:652",
605
+ "symbol": "getProductGraph",
606
+ "returns": "JSON: `{ product, nodes, edges }`.",
607
+ "atomicity": "atomic (read-only)"
608
+ },
609
+ {
610
+ "name": "list_nodes",
611
+ "description": "List entities in the graph, optionally filtered by type. Supports cursor pagination for large products (1000+ nodes). Default limit 1000, max 10000. Pass next_cursor from a previous response as cursor to advance to the next page. Returns next_cursor in the response when more results remain. Legacy offset param still accepted when cursor is absent.",
612
+ "domain": "nodes",
613
+ "inputSchema": {
614
+ "type": "object",
615
+ "properties": {
616
+ "product_id": {
617
+ "type": "string",
618
+ "description": "Product ID"
619
+ },
620
+ "type": {
621
+ "type": "string",
622
+ "description": "Filter by entity type"
623
+ },
624
+ "limit": {
625
+ "type": "number",
626
+ "description": "Max results (default 1000, max 10000)"
627
+ },
628
+ "cursor": {
629
+ "type": "string",
630
+ "description": "Opaque pagination cursor; pass next_cursor from a previous response to advance."
631
+ },
632
+ "offset": {
633
+ "type": "number",
634
+ "description": "Legacy: skip N results (default 0). Use cursor instead for new callers."
635
+ }
636
+ },
637
+ "required": [
638
+ "product_id"
639
+ ]
640
+ },
641
+ "throws": [
642
+ "textError when `product_id` is missing."
643
+ ],
644
+ "examples": [],
645
+ "warnings": [
646
+ "RLS-bounded: only nodes in products the caller has read access\nto are returned. An empty list can mean \"no nodes\" or \"no access\".\nDefault `limit: 1000`, max 10000. For products with 1000+ nodes use\n`cursor` pagination: keep calling with the returned `next_cursor` until\nit is absent."
647
+ ],
648
+ "see": [
649
+ "get_node",
650
+ "get_nodes",
651
+ "search_nodes",
652
+ "query"
653
+ ],
654
+ "source": "src/tools/nodes.ts:75",
655
+ "symbol": "listNodes",
656
+ "returns": "JSON: `{ nodes, total, limit, next_cursor? }`. `next_cursor` is\npresent when more results remain. `total` reflects the filtered count\nbefore pagination.",
657
+ "atomicity": "atomic (read-only)"
658
+ },
659
+ {
660
+ "name": "move_node",
661
+ "description": "Reparent a node to a new parent within the same product. Removes the existing containment edge (if any) and creates a new one with an inferred type. Runs inside a single Postgres transaction.",
662
+ "domain": "nodes",
663
+ "inputSchema": {
664
+ "type": "object",
665
+ "properties": {
666
+ "product_id": {
667
+ "type": "string",
668
+ "description": "Product ID"
669
+ },
670
+ "node_id": {
671
+ "type": "string",
672
+ "description": "The node to reparent"
673
+ },
674
+ "new_parent_id": {
675
+ "type": "string",
676
+ "description": "The new parent node ID"
677
+ }
678
+ },
679
+ "required": [
680
+ "product_id",
681
+ "node_id",
682
+ "new_parent_id"
683
+ ]
684
+ },
685
+ "throws": [
686
+ "textError when either node is missing, the nodes belong\nto different products (cross-product reparenting is not allowed), or\nthe caller tries to move a node onto itself."
687
+ ],
688
+ "examples": [],
689
+ "warnings": [],
690
+ "see": [
691
+ "batch_move_nodes",
692
+ "resolve_edge_for_pair",
693
+ "get_valid_children"
694
+ ],
695
+ "source": "src/tools/nodes.ts:521",
696
+ "symbol": "moveNode",
697
+ "returns": "JSON: `{ node_id, old_parent_id, new_parent_id, edge_created }`.\n`old_parent_id` is `null` when the node had no prior containment edge.",
698
+ "atomicity": "atomic-with-rollback"
699
+ },
700
+ {
701
+ "name": "search_nodes",
702
+ "description": "Full-text search across node titles and descriptions. Title matches rank higher.",
703
+ "domain": "nodes",
704
+ "inputSchema": {
705
+ "type": "object",
706
+ "properties": {
707
+ "product_id": {
708
+ "type": "string",
709
+ "description": "Product ID"
710
+ },
711
+ "query": {
712
+ "type": "string",
713
+ "description": "Search text"
714
+ },
715
+ "type": {
716
+ "type": "string",
717
+ "description": "Optional type filter"
718
+ },
719
+ "limit": {
720
+ "type": "number",
721
+ "description": "Max results (default 20)"
722
+ }
723
+ },
724
+ "required": [
725
+ "product_id",
726
+ "query"
727
+ ]
728
+ },
729
+ "throws": [
730
+ "textError when `product_id` or `query` is missing."
731
+ ],
732
+ "examples": [],
733
+ "warnings": [
734
+ "RLS-bounded: only nodes in products the caller has read\naccess to participate. Substring match is case-insensitive and runs\nin-memory after a full product fetch; for very large products this\ncan be heavy. A Postgres-side full-text index is a future optimisation."
735
+ ],
736
+ "see": [
737
+ "list_nodes",
738
+ "get_node",
739
+ "query"
740
+ ],
741
+ "source": "src/tools/nodes.ts:272",
742
+ "symbol": "searchNodes",
743
+ "returns": "JSON: `{ results: Array<node & { match_field }>, total }`.",
744
+ "atomicity": "atomic (read-only)"
745
+ },
746
+ {
747
+ "name": "update_node",
748
+ "description": "Update an existing entity. Unspecified fields are preserved.",
749
+ "domain": "nodes",
750
+ "inputSchema": {
751
+ "type": "object",
752
+ "properties": {
753
+ "node_id": {
754
+ "type": "string",
755
+ "description": "The node ID to update"
756
+ },
757
+ "title": {
758
+ "type": "string"
759
+ },
760
+ "description": {
761
+ "type": "string"
762
+ },
763
+ "tags": {
764
+ "type": "array",
765
+ "items": {
766
+ "type": "string"
767
+ }
768
+ },
769
+ "status": {
770
+ "type": "string"
771
+ },
772
+ "properties": {
773
+ "type": "object",
774
+ "description": "Merged with existing properties"
775
+ }
776
+ },
777
+ "required": [
778
+ "node_id"
779
+ ]
780
+ },
781
+ "throws": [
782
+ "textError when `node_id` is missing or the store\nrejects the update (unknown id)."
783
+ ],
784
+ "examples": [],
785
+ "warnings": [
786
+ "Lifecycle-aware: invalid status values produce a `warning` but\nthe update still applies. For type changes, use `migrate_type`\ninstead; direct type mutation via this tool is unsupported."
787
+ ],
788
+ "see": [
789
+ "migrate_type",
790
+ "batch_update_nodes",
791
+ "get_lifecycle"
792
+ ],
793
+ "source": "src/tools/nodes.ts:419",
794
+ "symbol": "updateNode",
795
+ "returns": "JSON: `{ node: updatedNode, warning? }`. Errors propagate from\nthe store (e.g. unknown node id).",
796
+ "atomicity": "atomic-with-rollback"
797
+ },
798
+ {
799
+ "name": "create_edge",
800
+ "description": "Create a relationship between two nodes. Edge type is auto-inferred if omitted.",
801
+ "domain": "edges",
802
+ "inputSchema": {
803
+ "type": "object",
804
+ "properties": {
805
+ "source_id": {
806
+ "type": "string",
807
+ "description": "Source node ID"
808
+ },
809
+ "target_id": {
810
+ "type": "string",
811
+ "description": "Target node ID"
812
+ },
813
+ "type": {
814
+ "type": "string",
815
+ "description": "Edge type; auto-inferred if omitted"
816
+ }
817
+ },
818
+ "required": [
819
+ "source_id",
820
+ "target_id"
821
+ ]
822
+ },
823
+ "throws": [
824
+ "textError when `source_id`/`target_id` is missing, an endpoint\nlookup fails, source and target resolve to the same node, an explicit\n`type` violates the catalog's source/target pair, or no canonical edge\nexists for the pair and no `type` was supplied (enriched with resolver\nhints)."
825
+ ],
826
+ "examples": [],
827
+ "warnings": [],
828
+ "see": [
829
+ "resolve_edge_for_pair",
830
+ "list_edge_types",
831
+ "get_edge_type",
832
+ "batch_create_edges"
833
+ ],
834
+ "source": "src/tools/edges.ts:57",
835
+ "symbol": "createEdge",
836
+ "returns": "JSON: `{ edge: { id, source, target, type }, warning? }`.",
837
+ "atomicity": "atomic-with-rollback"
838
+ },
839
+ {
840
+ "name": "delete_edge",
841
+ "description": "Remove a relationship between two nodes.",
842
+ "domain": "edges",
843
+ "inputSchema": {
844
+ "type": "object",
845
+ "properties": {
846
+ "edge_id": {
847
+ "type": "string",
848
+ "description": "The edge ID to delete"
849
+ }
850
+ },
851
+ "required": [
852
+ "edge_id"
853
+ ]
854
+ },
855
+ "throws": [
856
+ "textError when `edge_id` is missing or unknown."
857
+ ],
858
+ "examples": [],
859
+ "warnings": [],
860
+ "see": [
861
+ "batch_delete_edges",
862
+ "export_edges"
863
+ ],
864
+ "source": "src/tools/edges.ts:129",
865
+ "symbol": "deleteEdge",
866
+ "returns": "JSON: `{ deleted_edge_id }`.",
867
+ "atomicity": "atomic-with-rollback"
868
+ },
869
+ {
870
+ "name": "export_edges",
871
+ "description": "Flat enumeration of all edges for a product, optionally filtered by type. Returns lightweight { id, source, target, type } rows ordered by id, intended for migration passes.",
872
+ "domain": "edges",
873
+ "inputSchema": {
874
+ "type": "object",
875
+ "properties": {
876
+ "product_id": {
877
+ "type": "string",
878
+ "description": "Product ID"
879
+ },
880
+ "types": {
881
+ "type": "array",
882
+ "items": {
883
+ "type": "string"
884
+ },
885
+ "description": "Optional edge type filter"
886
+ }
887
+ },
888
+ "required": [
889
+ "product_id"
890
+ ]
891
+ },
892
+ "throws": [
893
+ "textError when `product_id` is missing or the store rejects the read."
894
+ ],
895
+ "examples": [],
896
+ "warnings": [],
897
+ "see": [
898
+ "list_edge_types",
899
+ "rename_edge_type",
900
+ "query"
901
+ ],
902
+ "source": "src/tools/edges.ts:151",
903
+ "symbol": "exportEdges",
904
+ "returns": "JSON: `{ edges: [{ id, source, target, type }], total: number }`.",
905
+ "atomicity": "atomic (read-only)"
906
+ },
907
+ {
908
+ "name": "rename_edge_type",
909
+ "description": "Rename all edges of one type to another across a product. dry_run (default: true) previews the count.",
910
+ "domain": "edges",
911
+ "inputSchema": {
912
+ "type": "object",
913
+ "properties": {
914
+ "product_id": {
915
+ "type": "string",
916
+ "description": "Product ID"
917
+ },
918
+ "from": {
919
+ "type": "string",
920
+ "description": "Current edge type"
921
+ },
922
+ "to": {
923
+ "type": "string",
924
+ "description": "New edge type"
925
+ },
926
+ "dry_run": {
927
+ "type": "boolean",
928
+ "description": "If true, only count (default: true); pass false to apply."
929
+ }
930
+ },
931
+ "required": [
932
+ "product_id",
933
+ "from",
934
+ "to"
935
+ ]
936
+ },
937
+ "throws": [
938
+ "textError when `product_id`, `from`, or `to` is missing."
939
+ ],
940
+ "examples": [],
941
+ "warnings": [],
942
+ "see": [
943
+ "list_edge_types",
944
+ "get_edge_type",
945
+ "export_edges",
946
+ "migrate_type"
947
+ ],
948
+ "source": "src/tools/edges.ts:176",
949
+ "symbol": "renameEdgeType",
950
+ "returns": "JSON: `{ from, to, affected: number, dry_run: boolean }`.",
951
+ "atomicity": "atomic-with-rollback (write path)"
952
+ },
953
+ {
954
+ "name": "create_area",
955
+ "description": "Create a new product area node (type 'area') in a product. Product areas are top-level organisational units within a product.",
956
+ "domain": "areas",
957
+ "inputSchema": {
958
+ "type": "object",
959
+ "properties": {
960
+ "product_id": {
961
+ "type": "string",
962
+ "description": "Product ID"
963
+ },
964
+ "title": {
965
+ "type": "string",
966
+ "description": "Area title"
967
+ },
968
+ "description": {
969
+ "type": "string",
970
+ "description": "Optional description"
971
+ }
972
+ },
973
+ "required": [
974
+ "product_id",
975
+ "title"
976
+ ]
977
+ },
978
+ "throws": [
979
+ "textError when `product_id` or `title` is missing."
980
+ ],
981
+ "examples": [],
982
+ "warnings": [],
983
+ "see": [
984
+ "list_product_areas",
985
+ "get_area_context"
986
+ ],
987
+ "source": "src/tools/areas.ts:63",
988
+ "symbol": "createArea",
989
+ "returns": "JSON: `{ node }`.",
990
+ "atomicity": "atomic"
991
+ },
992
+ {
993
+ "name": "get_area_context",
994
+ "description": "Returns a summary of a product area: entity counts by type within it, child area count, and description. Traverses containment edges up to depth 2.",
995
+ "domain": "areas",
996
+ "inputSchema": {
997
+ "type": "object",
998
+ "properties": {
999
+ "product_id": {
1000
+ "type": "string",
1001
+ "description": "Product ID"
1002
+ },
1003
+ "area_id": {
1004
+ "type": "string",
1005
+ "description": "The area node ID"
1006
+ }
1007
+ },
1008
+ "required": [
1009
+ "product_id",
1010
+ "area_id"
1011
+ ]
1012
+ },
1013
+ "throws": [
1014
+ "textError when `product_id` or `area_id` is missing, or the area lookup fails."
1015
+ ],
1016
+ "examples": [],
1017
+ "warnings": [],
1018
+ "see": [
1019
+ "create_area",
1020
+ "get_area_graph"
1021
+ ],
1022
+ "source": "src/tools/areas.ts:93",
1023
+ "symbol": "getAreaContext",
1024
+ "returns": "JSON: `{ area: { id, title, description }, entity_counts, total_entities, child_areas }`.",
1025
+ "atomicity": "atomic (read-only)"
1026
+ },
1027
+ {
1028
+ "name": "get_area_graph",
1029
+ "description": "Get all entities and edges that belong to a product area. Returns the sub-graph scoped to that area.",
1030
+ "domain": "areas",
1031
+ "inputSchema": {
1032
+ "type": "object",
1033
+ "properties": {
1034
+ "product_id": {
1035
+ "type": "string",
1036
+ "description": "Product ID"
1037
+ },
1038
+ "area_id": {
1039
+ "type": "string",
1040
+ "description": "The product area node ID"
1041
+ },
1042
+ "depth": {
1043
+ "type": "number",
1044
+ "description": "How many levels deep to traverse (default 3, max 10)"
1045
+ }
1046
+ },
1047
+ "required": [
1048
+ "product_id",
1049
+ "area_id"
1050
+ ]
1051
+ },
1052
+ "throws": [
1053
+ "textError when `product_id` or `area_id` is missing, or store rejects."
1054
+ ],
1055
+ "examples": [],
1056
+ "warnings": [],
1057
+ "see": [
1058
+ "list_product_areas",
1059
+ "get_area_context",
1060
+ "query"
1061
+ ],
1062
+ "source": "src/tools/areas.ts:38",
1063
+ "symbol": "getAreaGraph",
1064
+ "returns": "JSON: `{ area, nodes, edges }`.",
1065
+ "atomicity": "atomic (read-only)"
1066
+ },
1067
+ {
1068
+ "name": "list_product_areas",
1069
+ "description": "List all product areas in a product. Product areas are top-level organizational units within a product.",
1070
+ "domain": "areas",
1071
+ "inputSchema": {
1072
+ "type": "object",
1073
+ "properties": {
1074
+ "product_id": {
1075
+ "type": "string",
1076
+ "description": "Product ID"
1077
+ }
1078
+ },
1079
+ "required": [
1080
+ "product_id"
1081
+ ]
1082
+ },
1083
+ "throws": [
1084
+ "textError when `product_id` is missing."
1085
+ ],
1086
+ "examples": [],
1087
+ "warnings": [],
1088
+ "see": [
1089
+ "get_area_graph",
1090
+ "get_area_context",
1091
+ "create_area"
1092
+ ],
1093
+ "source": "src/tools/areas.ts:20",
1094
+ "symbol": "listProductAreas",
1095
+ "returns": "JSON: `{ areas, total }`.",
1096
+ "atomicity": "atomic (read-only)"
1097
+ },
1098
+ {
1099
+ "name": "get_entity_schema",
1100
+ "description": "Returns the schema for a UPG entity type: valid parent→child edges, properties, lifecycle phases.",
1101
+ "domain": "schema",
1102
+ "inputSchema": {
1103
+ "type": "object",
1104
+ "properties": {
1105
+ "type": {
1106
+ "type": "string",
1107
+ "description": "The UPG entity type (e.g. \"feature\", \"persona\")"
1108
+ }
1109
+ },
1110
+ "required": [
1111
+ "type"
1112
+ ]
1113
+ },
1114
+ "throws": [
1115
+ "textError when `type` is missing or unknown\n(`UnknownEntityTypeError`)."
1116
+ ],
1117
+ "examples": [],
1118
+ "warnings": [],
1119
+ "see": [
1120
+ "get_entity_meta",
1121
+ "list_entity_types",
1122
+ "get_valid_children",
1123
+ "get_lifecycle",
1124
+ "get_domain_guide",
1125
+ "list_edge_types",
1126
+ "create_node"
1127
+ ],
1128
+ "source": "src/tools/schema.ts:32",
1129
+ "symbol": "getEntitySchema",
1130
+ "returns": "JSON: `{ type, alias_of?, domain, expected_properties,\nedges_out, edges_in, phases?, initial_phase?, terminal_phases?,\ndomain_guide? }`.",
1131
+ "atomicity": "atomic (read-only)"
1132
+ },
1133
+ {
1134
+ "name": "add_comment",
1135
+ "description": "Add a comment on a node in the graph.",
1136
+ "domain": "collaboration",
1137
+ "inputSchema": {
1138
+ "type": "object",
1139
+ "properties": {
1140
+ "product_id": {
1141
+ "type": "string",
1142
+ "description": "Product ID"
1143
+ },
1144
+ "node_id": {
1145
+ "type": "string",
1146
+ "description": "Node to comment on"
1147
+ },
1148
+ "user_id": {
1149
+ "type": "string",
1150
+ "description": "Author user ID"
1151
+ },
1152
+ "body": {
1153
+ "type": "string",
1154
+ "description": "Comment text"
1155
+ }
1156
+ },
1157
+ "required": [
1158
+ "product_id",
1159
+ "node_id",
1160
+ "user_id",
1161
+ "body"
1162
+ ]
1163
+ },
1164
+ "throws": [
1165
+ "textError when `product_id`, `node_id`, `user_id`, or `body` is missing."
1166
+ ],
1167
+ "examples": [],
1168
+ "warnings": [
1169
+ "`user_id` MUST resolve to a member of the product's collaborator set,\nor downstream RLS rejects the insert."
1170
+ ],
1171
+ "see": [
1172
+ "list_comments",
1173
+ "list_collaborators",
1174
+ "grant_access"
1175
+ ],
1176
+ "source": "src/tools/collaboration.ts:20",
1177
+ "symbol": "addComment",
1178
+ "returns": "JSON: `{ comment: { id, product_id, node_id, user_id, body, created_at } }`.",
1179
+ "atomicity": "atomic"
1180
+ },
1181
+ {
1182
+ "name": "grant_access",
1183
+ "description": "Grant or update a user's role on a product (owner, editor, viewer).",
1184
+ "domain": "collaboration",
1185
+ "inputSchema": {
1186
+ "type": "object",
1187
+ "properties": {
1188
+ "product_id": {
1189
+ "type": "string",
1190
+ "description": "Product ID"
1191
+ },
1192
+ "user_id": {
1193
+ "type": "string",
1194
+ "description": "User to grant access to"
1195
+ },
1196
+ "role": {
1197
+ "type": "string",
1198
+ "description": "Role: owner | editor | viewer",
1199
+ "enum": [
1200
+ "owner",
1201
+ "editor",
1202
+ "viewer"
1203
+ ]
1204
+ }
1205
+ },
1206
+ "required": [
1207
+ "product_id",
1208
+ "user_id",
1209
+ "role"
1210
+ ]
1211
+ },
1212
+ "throws": [
1213
+ "textError when `product_id`, `user_id`, or `role` is missing."
1214
+ ],
1215
+ "examples": [],
1216
+ "warnings": [
1217
+ "Billing-relevant: collaborator count typically drives plan tier;\na grant may trigger a tier upgrade or hit a seat-limit cap."
1218
+ ],
1219
+ "see": [
1220
+ "list_collaborators",
1221
+ "add_comment"
1222
+ ],
1223
+ "source": "src/tools/collaboration.ts:63",
1224
+ "symbol": "grantAccess",
1225
+ "returns": "JSON: `{ granted: { product_id, user_id, role } }`.",
1226
+ "atomicity": "atomic"
1227
+ },
1228
+ {
1229
+ "name": "list_collaborators",
1230
+ "description": "List all collaborators and their roles for a product.",
1231
+ "domain": "collaboration",
1232
+ "inputSchema": {
1233
+ "type": "object",
1234
+ "properties": {
1235
+ "product_id": {
1236
+ "type": "string",
1237
+ "description": "Product ID"
1238
+ }
1239
+ },
1240
+ "required": [
1241
+ "product_id"
1242
+ ]
1243
+ },
1244
+ "throws": [
1245
+ "textError when `product_id` is missing."
1246
+ ],
1247
+ "examples": [],
1248
+ "warnings": [],
1249
+ "see": [
1250
+ "grant_access",
1251
+ "list_comments"
1252
+ ],
1253
+ "source": "src/tools/collaboration.ts:92",
1254
+ "symbol": "listCollaborators",
1255
+ "returns": "JSON: `{ collaborators: Array<{ user_id, role, granted_at }> }`.",
1256
+ "atomicity": "atomic (read-only)"
1257
+ },
1258
+ {
1259
+ "name": "list_comments",
1260
+ "description": "List comments on a node, newest first.",
1261
+ "domain": "collaboration",
1262
+ "inputSchema": {
1263
+ "type": "object",
1264
+ "properties": {
1265
+ "node_id": {
1266
+ "type": "string",
1267
+ "description": "Node ID"
1268
+ }
1269
+ },
1270
+ "required": [
1271
+ "node_id"
1272
+ ]
1273
+ },
1274
+ "throws": [
1275
+ "textError when `node_id` is missing."
1276
+ ],
1277
+ "examples": [],
1278
+ "warnings": [
1279
+ "RLS-bounded; an empty array can mean \"no comments\" or \"no access\"."
1280
+ ],
1281
+ "see": [
1282
+ "add_comment",
1283
+ "list_collaborators"
1284
+ ],
1285
+ "source": "src/tools/collaboration.ts:44",
1286
+ "symbol": "listComments",
1287
+ "returns": "JSON: `{ comments: Comment[] }`.",
1288
+ "atomicity": "atomic (read-only)"
1289
+ },
1290
+ {
1291
+ "name": "get_graph_analytics",
1292
+ "description": "Computed product thinking metrics: hypothesis velocity, persona coverage ratio, evidence density, stale entity rate, orphan rate.",
1293
+ "domain": "analytics",
1294
+ "inputSchema": {
1295
+ "type": "object",
1296
+ "properties": {
1297
+ "product_id": {
1298
+ "type": "string",
1299
+ "description": "Product ID"
1300
+ }
1301
+ },
1302
+ "required": [
1303
+ "product_id"
1304
+ ]
1305
+ },
1306
+ "throws": [
1307
+ "textError when `product_id` is missing or the product is invisible\nto the caller (RLS-bounded; \"not found\" and \"no access\" share wording)."
1308
+ ],
1309
+ "examples": [],
1310
+ "warnings": [],
1311
+ "see": [
1312
+ "get_graph_digest",
1313
+ "get_product_context",
1314
+ "get_audit_log"
1315
+ ],
1316
+ "source": "src/tools/analytics.ts:21",
1317
+ "symbol": "getGraphAnalytics",
1318
+ "returns": "JSON: `{ product: { id, title }, analytics }`.",
1319
+ "atomicity": "atomic (read-only)"
1320
+ },
1321
+ {
1322
+ "name": "list_webhooks",
1323
+ "description": "List all registered webhooks for a product.",
1324
+ "domain": "webhooks",
1325
+ "inputSchema": {
1326
+ "type": "object",
1327
+ "properties": {
1328
+ "product_id": {
1329
+ "type": "string",
1330
+ "description": "Product ID"
1331
+ }
1332
+ },
1333
+ "required": [
1334
+ "product_id"
1335
+ ]
1336
+ },
1337
+ "throws": [
1338
+ "textError when `product_id` is missing."
1339
+ ],
1340
+ "examples": [],
1341
+ "warnings": [],
1342
+ "see": [
1343
+ "register_webhook",
1344
+ "remove_webhook"
1345
+ ],
1346
+ "source": "src/tools/webhooks.ts:46",
1347
+ "symbol": "listWebhooks",
1348
+ "returns": "JSON: `{ webhooks: Webhook[] }`.",
1349
+ "atomicity": "atomic (read-only)"
1350
+ },
1351
+ {
1352
+ "name": "register_webhook",
1353
+ "description": "Register a webhook called when an event occurs on a product (node.created, node.updated, node.deleted, edge.created, edge.deleted; use '*' for all). Delivered async after commit, HMAC-signed via the optional secret (X-UPG-Signature header), with bounded retry; a persistent 4xx auto-disables the registration.",
1354
+ "domain": "webhooks",
1355
+ "inputSchema": {
1356
+ "type": "object",
1357
+ "properties": {
1358
+ "product_id": {
1359
+ "type": "string",
1360
+ "description": "Product ID"
1361
+ },
1362
+ "event": {
1363
+ "type": "string",
1364
+ "description": "Event name (e.g. node.created, node.updated, node.deleted, edge.created, edge.deleted)"
1365
+ },
1366
+ "url": {
1367
+ "type": "string",
1368
+ "description": "Webhook URL to POST to"
1369
+ },
1370
+ "secret": {
1371
+ "type": "string",
1372
+ "description": "Optional shared secret for HMAC signature verification"
1373
+ }
1374
+ },
1375
+ "required": [
1376
+ "product_id",
1377
+ "event",
1378
+ "url"
1379
+ ]
1380
+ },
1381
+ "throws": [
1382
+ "textError when `product_id`, `event`, or `url` is missing."
1383
+ ],
1384
+ "examples": [],
1385
+ "warnings": [],
1386
+ "see": [
1387
+ "list_webhooks",
1388
+ "remove_webhook"
1389
+ ],
1390
+ "source": "src/tools/webhooks.ts:24",
1391
+ "symbol": "registerWebhook",
1392
+ "returns": "JSON: `{ webhook: { id, product_id, event, url, secret?, created_at } }`.",
1393
+ "atomicity": "atomic"
1394
+ },
1395
+ {
1396
+ "name": "remove_webhook",
1397
+ "description": "Remove a registered webhook by ID.",
1398
+ "domain": "webhooks",
1399
+ "inputSchema": {
1400
+ "type": "object",
1401
+ "properties": {
1402
+ "webhook_id": {
1403
+ "type": "string",
1404
+ "description": "Webhook ID to remove"
1405
+ }
1406
+ },
1407
+ "required": [
1408
+ "webhook_id"
1409
+ ]
1410
+ },
1411
+ "throws": [
1412
+ "textError when `webhook_id` is missing or the store rejects the deletion."
1413
+ ],
1414
+ "examples": [],
1415
+ "warnings": [],
1416
+ "see": [
1417
+ "register_webhook",
1418
+ "list_webhooks"
1419
+ ],
1420
+ "source": "src/tools/webhooks.ts:63",
1421
+ "symbol": "removeWebhook",
1422
+ "returns": "JSON: `{ removed: <webhook_id> }`.",
1423
+ "atomicity": "atomic"
1424
+ },
1425
+ {
1426
+ "name": "get_anti_pattern",
1427
+ "description": "Return one curated anti-pattern by id (kebab-case slug, e.g. \"features-without-hypotheses\", \"personas-without-jobs\"). Includes the full body: structured condition, why-it-matters, remediation, applicable stages, severity, and optional source citation. IDs are stable URL fragments and remain frozen once published.",
1428
+ "domain": "spec",
1429
+ "inputSchema": {
1430
+ "type": "object",
1431
+ "properties": {
1432
+ "id": {
1433
+ "type": "string",
1434
+ "description": "Anti-pattern id (kebab-case slug)."
1435
+ }
1436
+ },
1437
+ "required": [
1438
+ "id"
1439
+ ]
1440
+ },
1441
+ "throws": [
1442
+ "textError when `id` is missing or unknown."
1443
+ ],
1444
+ "examples": [],
1445
+ "warnings": [],
1446
+ "see": [
1447
+ "list_anti_patterns",
1448
+ "inspect",
1449
+ "validate_graph"
1450
+ ],
1451
+ "source": "src/tools/spec.ts:1150",
1452
+ "symbol": "getAntiPattern",
1453
+ "returns": "JSON: `UPGCuratedAntiPattern`",
1454
+ "atomicity": "atomic (read-only)"
1455
+ },
1456
+ {
1457
+ "name": "get_approach",
1458
+ "description": "Return one canonical UPGApproach by id. Valid ids are the bare verbs: plan, inspect, prioritise, trace, reflect. Same names as the verb-led MCP tools.",
1459
+ "domain": "spec",
1460
+ "inputSchema": {
1461
+ "type": "object",
1462
+ "properties": {
1463
+ "id": {
1464
+ "type": "string",
1465
+ "description": "Approach id, one of: plan, inspect, prioritise, trace, reflect.",
1466
+ "enum": [
1467
+ "plan",
1468
+ "inspect",
1469
+ "prioritise",
1470
+ "trace",
1471
+ "reflect"
1472
+ ]
1473
+ }
1474
+ },
1475
+ "required": [
1476
+ "id"
1477
+ ]
1478
+ },
1479
+ "throws": [
1480
+ "textError when `id` is missing or unknown."
1481
+ ],
1482
+ "examples": [],
1483
+ "warnings": [],
1484
+ "see": [
1485
+ "list_approaches",
1486
+ "plan",
1487
+ "inspect",
1488
+ "prioritise",
1489
+ "trace",
1490
+ "reflect"
1491
+ ],
1492
+ "source": "src/tools/spec.ts:234",
1493
+ "symbol": "getApproach",
1494
+ "returns": "JSON: the full `UPGApproach` record.",
1495
+ "atomicity": "atomic (read-only)"
1496
+ },
1497
+ {
1498
+ "name": "get_domain_guide",
1499
+ "description": "Return the full UPGDomainUsageGuide for a domain: anchor entity, creation sequence, named patterns (entity and edge chains), required cross-domain bridges, and anti-patterns.",
1500
+ "domain": "spec",
1501
+ "inputSchema": {
1502
+ "type": "object",
1503
+ "properties": {
1504
+ "domain_id": {
1505
+ "type": "string",
1506
+ "description": "Canonical domain id (e.g. \"user\", \"market_intelligence\", \"growth\")."
1507
+ }
1508
+ },
1509
+ "required": [
1510
+ "domain_id"
1511
+ ]
1512
+ },
1513
+ "throws": [
1514
+ "textError when `domain_id` is missing or unknown."
1515
+ ],
1516
+ "examples": [],
1517
+ "warnings": [],
1518
+ "see": [
1519
+ "list_domains",
1520
+ "list_anti_patterns",
1521
+ "get_playbook"
1522
+ ],
1523
+ "source": "src/tools/spec.ts:525",
1524
+ "symbol": "getDomainGuide",
1525
+ "returns": "JSON: the full `UPGDomainUsageGuide` record.",
1526
+ "atomicity": "atomic (read-only)"
1527
+ },
1528
+ {
1529
+ "name": "get_domain_ring",
1530
+ "description": "Return one UPGDomainRing by id (e.g. \"nucleus\", \"understand\", \"define\", \"build\", \"grow\", \"operate\", \"extend\").",
1531
+ "domain": "spec",
1532
+ "inputSchema": {
1533
+ "type": "object",
1534
+ "properties": {
1535
+ "id": {
1536
+ "type": "string",
1537
+ "description": "Ring id, one of: nucleus, understand, define, build, grow, operate, extend."
1538
+ }
1539
+ },
1540
+ "required": [
1541
+ "id"
1542
+ ]
1543
+ },
1544
+ "throws": [
1545
+ "textError when `id` is missing or unknown."
1546
+ ],
1547
+ "examples": [],
1548
+ "warnings": [],
1549
+ "see": [
1550
+ "list_domain_rings",
1551
+ "list_domains",
1552
+ "get_domain_guide"
1553
+ ],
1554
+ "source": "src/tools/spec.ts:1589",
1555
+ "symbol": "getDomainRing",
1556
+ "returns": "JSON: the full `UPGDomainRing` record.",
1557
+ "atomicity": "atomic (read-only)"
1558
+ },
1559
+ {
1560
+ "name": "get_edge_type",
1561
+ "description": "Return one canonical edge catalogue entry by edge type key (e.g. \"persona_pursues_job\", \"feature_addresses_need\").",
1562
+ "domain": "spec",
1563
+ "inputSchema": {
1564
+ "type": "object",
1565
+ "properties": {
1566
+ "type": {
1567
+ "type": "string",
1568
+ "description": "Edge type key from UPG_EDGE_CATALOG."
1569
+ }
1570
+ },
1571
+ "required": [
1572
+ "type"
1573
+ ]
1574
+ },
1575
+ "throws": [
1576
+ "textError when `type` is missing or unknown."
1577
+ ],
1578
+ "examples": [],
1579
+ "warnings": [],
1580
+ "see": [
1581
+ "list_edge_types",
1582
+ "resolve_edge_for_pair",
1583
+ "rename_edge_type"
1584
+ ],
1585
+ "source": "src/tools/spec.ts:651",
1586
+ "symbol": "getEdgeType",
1587
+ "returns": "JSON: `{ type, forward_verb, reverse_verb, classification, source_type, target_type }`",
1588
+ "atomicity": "atomic (read-only)"
1589
+ },
1590
+ {
1591
+ "name": "get_entity_meta",
1592
+ "description": "Return one canonical EntityTypeMeta record by entity type name, plus the resolved domain_id (or null if the type has no atomic-domain mapping). Pairs with list_entity_types; drill into a single type's lifecycle metadata (maturity tier, since-version, replacement target if deprecated). Pass the canonical name (e.g. \"persona\", \"pain_point\"), not the immutable type_id.",
1593
+ "domain": "spec",
1594
+ "inputSchema": {
1595
+ "type": "object",
1596
+ "properties": {
1597
+ "name": {
1598
+ "type": "string",
1599
+ "description": "Canonical entity type name."
1600
+ }
1601
+ },
1602
+ "required": [
1603
+ "name"
1604
+ ]
1605
+ },
1606
+ "throws": [
1607
+ "textError when `name` is missing or unknown."
1608
+ ],
1609
+ "examples": [],
1610
+ "warnings": [],
1611
+ "see": [
1612
+ "list_entity_types",
1613
+ "get_type_label",
1614
+ "get_entity_schema"
1615
+ ],
1616
+ "source": "src/tools/spec.ts:1069",
1617
+ "symbol": "getEntityMeta",
1618
+ "returns": "JSON: `EntityTypeMeta & { domain_id: string | null }`",
1619
+ "atomicity": "atomic (read-only)"
1620
+ },
1621
+ {
1622
+ "name": "get_framework",
1623
+ "description": "Return one canonical UPGFramework by id (e.g. \"rice-scoring\", \"lean-canvas\"). Includes all four layers: data, structure, presentation, education.",
1624
+ "domain": "spec",
1625
+ "inputSchema": {
1626
+ "type": "object",
1627
+ "properties": {
1628
+ "id": {
1629
+ "type": "string",
1630
+ "description": "Framework id (kebab-case)."
1631
+ }
1632
+ },
1633
+ "required": [
1634
+ "id"
1635
+ ]
1636
+ },
1637
+ "throws": [
1638
+ "textError when `id` is missing or unknown."
1639
+ ],
1640
+ "examples": [],
1641
+ "warnings": [],
1642
+ "see": [
1643
+ "list_frameworks",
1644
+ "prioritise",
1645
+ "get_playbook",
1646
+ "get_approach"
1647
+ ],
1648
+ "source": "src/tools/spec.ts:590",
1649
+ "symbol": "getFramework",
1650
+ "returns": "JSON: the full `UPGFramework` record.",
1651
+ "atomicity": "atomic (read-only)"
1652
+ },
1653
+ {
1654
+ "name": "get_lens",
1655
+ "description": "Return the full UPGLens record by id (e.g. \"product\", \"ux_design\", \"engineering\", \"full\") plus the resolved list of entity types visible through that lens. Combines the lens record with visible_types in one response, saving the common \"fetch lens, then resolve types\" round-trip.",
1656
+ "domain": "spec",
1657
+ "inputSchema": {
1658
+ "type": "object",
1659
+ "properties": {
1660
+ "id": {
1661
+ "type": "string",
1662
+ "description": "Lens id (e.g. \"product\", \"ux_design\", \"full\")."
1663
+ }
1664
+ },
1665
+ "required": [
1666
+ "id"
1667
+ ]
1668
+ },
1669
+ "throws": [
1670
+ "textError when `id` is missing or unknown."
1671
+ ],
1672
+ "examples": [],
1673
+ "warnings": [],
1674
+ "see": [
1675
+ "list_lenses",
1676
+ "get_playbook",
1677
+ "get_framework",
1678
+ "list_entity_types"
1679
+ ],
1680
+ "source": "src/tools/spec.ts:889",
1681
+ "symbol": "getLensTool",
1682
+ "returns": "JSON: `{ ...UPGLens, visible_types: string[] }`",
1683
+ "atomicity": "atomic (read-only)"
1684
+ },
1685
+ {
1686
+ "name": "get_lifecycle",
1687
+ "description": "Return the full UPGLifecycle definition for one entity type: initial phase, terminal phases, and the ordered array of phases with transitions and core states. Returns a descriptive message (not an error) when the type has no lifecycle defined.",
1688
+ "domain": "spec",
1689
+ "inputSchema": {
1690
+ "type": "object",
1691
+ "properties": {
1692
+ "entity_type": {
1693
+ "type": "string",
1694
+ "description": "Canonical entity type name (e.g. \"feature\", \"hypothesis_claim\", \"opportunity\")."
1695
+ }
1696
+ },
1697
+ "required": [
1698
+ "entity_type"
1699
+ ]
1700
+ },
1701
+ "throws": [
1702
+ "textError when `entity_type` is missing, lifecycle-free,\nlifecycle-planned, or unknown."
1703
+ ],
1704
+ "examples": [],
1705
+ "warnings": [],
1706
+ "see": [
1707
+ "list_lifecycles",
1708
+ "get_entity_meta",
1709
+ "get_entity_schema"
1710
+ ],
1711
+ "source": "src/tools/spec.ts:1458",
1712
+ "symbol": "getLifecycle",
1713
+ "returns": "JSON: the full `UPGLifecycle` record, or a descriptive message.",
1714
+ "atomicity": "atomic (read-only)"
1715
+ },
1716
+ {
1717
+ "name": "get_playbook",
1718
+ "description": "Return one canonical UPGPlaybook by id (e.g. \"playbook:strategy-outcomes\", \"playbook:business-model-bmc\"). Includes the ordered creation_sequence with full step kinds and prompts. IDs are namespace-prefixed; calling with an \"approach:*\" id (or one of the 5 bare-verb approach ids) returns null; route via get_approach for the approach catalog.",
1719
+ "domain": "spec",
1720
+ "inputSchema": {
1721
+ "type": "object",
1722
+ "properties": {
1723
+ "id": {
1724
+ "type": "string",
1725
+ "description": "Playbook id (namespace-prefixed: playbook:*)."
1726
+ }
1727
+ },
1728
+ "required": [
1729
+ "id"
1730
+ ]
1731
+ },
1732
+ "throws": [
1733
+ "textError when `id` is missing or unknown."
1734
+ ],
1735
+ "examples": [],
1736
+ "warnings": [],
1737
+ "see": [
1738
+ "list_playbooks",
1739
+ "get_approach",
1740
+ "get_framework",
1741
+ "get_region"
1742
+ ],
1743
+ "source": "src/tools/spec.ts:170",
1744
+ "symbol": "getPlaybook",
1745
+ "returns": "JSON: the full `UPGPlaybook` record.",
1746
+ "atomicity": "atomic (read-only)"
1747
+ },
1748
+ {
1749
+ "name": "get_region",
1750
+ "description": "Return the full UPGRegion record by id: anchor entity (with rationale and inbound/outbound cross-edge counts), entity memberships with structural roles, intra-domain edge keys, boundary edges to other regions, shape archetype, and the atomic-domain composition.",
1751
+ "domain": "spec",
1752
+ "inputSchema": {
1753
+ "type": "object",
1754
+ "properties": {
1755
+ "id": {
1756
+ "type": "string",
1757
+ "description": "Region id (e.g. \"strategy_outcomes\", \"users_needs\", \"product_delivery\"). See UPG_REGIONS for the full list of 10."
1758
+ }
1759
+ },
1760
+ "required": [
1761
+ "id"
1762
+ ]
1763
+ },
1764
+ "throws": [
1765
+ "textError when `id` is missing or unknown."
1766
+ ],
1767
+ "examples": [],
1768
+ "warnings": [],
1769
+ "see": [
1770
+ "list_regions",
1771
+ "get_region_for_entity_type",
1772
+ "get_playbook",
1773
+ "list_lenses"
1774
+ ],
1775
+ "source": "src/tools/spec.ts:710",
1776
+ "symbol": "getRegion",
1777
+ "returns": "JSON: the full `UPGRegion` record.",
1778
+ "atomicity": "atomic (read-only)"
1779
+ },
1780
+ {
1781
+ "name": "get_region_for_entity_type",
1782
+ "description": "Resolve which super-domain region contains a given entity type. Wraps getRegionForEntityType. Returns the full UPGRegion record. Useful for adapters and copilots that need to route or render an entity based on its super-domain.",
1783
+ "domain": "spec",
1784
+ "inputSchema": {
1785
+ "type": "object",
1786
+ "properties": {
1787
+ "entity_type": {
1788
+ "type": "string",
1789
+ "description": "Canonical entity type (e.g. \"persona\", \"feature\", \"metric\")."
1790
+ }
1791
+ },
1792
+ "required": [
1793
+ "entity_type"
1794
+ ]
1795
+ },
1796
+ "throws": [
1797
+ "textError when `entity_type` is missing or no region contains it."
1798
+ ],
1799
+ "examples": [],
1800
+ "warnings": [],
1801
+ "see": [
1802
+ "get_region",
1803
+ "list_regions",
1804
+ "get_entity_meta",
1805
+ "list_entity_types"
1806
+ ],
1807
+ "source": "src/tools/spec.ts:732",
1808
+ "symbol": "getRegionForEntity",
1809
+ "returns": "JSON: the full `UPGRegion` record.",
1810
+ "atomicity": "atomic (read-only)"
1811
+ },
1812
+ {
1813
+ "name": "get_scale",
1814
+ "description": "Return one spec-defined assessment scale by id (e.g. \"reach_5\", \"severity_5\", \"confidence_binary\"). Includes the full point array.",
1815
+ "domain": "spec",
1816
+ "inputSchema": {
1817
+ "type": "object",
1818
+ "properties": {
1819
+ "id": {
1820
+ "type": "string",
1821
+ "description": "Scale id (e.g. \"reach_5\", \"frequency_5\", \"severity_5\", \"importance_5\", \"confidence_binary\")."
1822
+ }
1823
+ },
1824
+ "required": [
1825
+ "id"
1826
+ ]
1827
+ },
1828
+ "throws": [
1829
+ "textError when `id` is missing or unknown."
1830
+ ],
1831
+ "examples": [],
1832
+ "warnings": [],
1833
+ "see": [
1834
+ "list_scales",
1835
+ "get_entity_schema"
1836
+ ],
1837
+ "source": "src/tools/spec.ts:1505",
1838
+ "symbol": "getScale",
1839
+ "returns": "JSON: the full `UPGScaleDefinition` record including all points.",
1840
+ "atomicity": "atomic (read-only)"
1841
+ },
1842
+ {
1843
+ "name": "get_spec_version",
1844
+ "description": "Return spec-level metadata for adopter compatibility checks: upg_version, markdown_format_version, and canonical counts (entity types, edge types, atomic domains, super-domain regions). Pin against the version pair; counts are informational.",
1845
+ "domain": "spec",
1846
+ "inputSchema": {
1847
+ "type": "object",
1848
+ "properties": {}
1849
+ },
1850
+ "throws": [],
1851
+ "examples": [],
1852
+ "warnings": [],
1853
+ "see": [
1854
+ "list_entity_types",
1855
+ "list_edge_types",
1856
+ "list_regions"
1857
+ ],
1858
+ "source": "src/tools/spec.ts:759",
1859
+ "symbol": "getSpecVersion",
1860
+ "returns": "JSON: `{ upg_version, markdown_format_version, entity_count, edge_count, domain_count, region_count }`",
1861
+ "atomicity": "atomic (read-only)"
1862
+ },
1863
+ {
1864
+ "name": "get_type_label",
1865
+ "description": "Return one canonical UPGTypeLabel by entity type, plus a resolved display label for an optional framework_id and/or designation (wraps resolveLabel). Lookup is exact-match against UPG_TYPE_LABELS_MAP.",
1866
+ "domain": "spec",
1867
+ "inputSchema": {
1868
+ "type": "object",
1869
+ "properties": {
1870
+ "entity_type": {
1871
+ "type": "string",
1872
+ "description": "Canonical entity type id."
1873
+ },
1874
+ "framework_id": {
1875
+ "type": "string",
1876
+ "description": "Optional framework id (e.g. \"lean_canvas\", \"ost\", \"design_thinking\"); when set, resolved_label uses the framework-specific label."
1877
+ },
1878
+ "designation": {
1879
+ "type": "string",
1880
+ "description": "Optional designation key (e.g. \"pain\", \"gap\", \"desire\") for types that use the designation pattern."
1881
+ }
1882
+ },
1883
+ "required": [
1884
+ "entity_type"
1885
+ ]
1886
+ },
1887
+ "throws": [
1888
+ "textError when `entity_type` is missing or unknown."
1889
+ ],
1890
+ "examples": [],
1891
+ "warnings": [],
1892
+ "see": [
1893
+ "list_type_labels",
1894
+ "get_entity_meta",
1895
+ "list_frameworks"
1896
+ ],
1897
+ "source": "src/tools/spec.ts:950",
1898
+ "symbol": "getTypeLabel",
1899
+ "returns": "JSON: `{ ...UPGTypeLabel, resolved_label: string }`",
1900
+ "atomicity": "atomic (read-only)"
1901
+ },
1902
+ {
1903
+ "name": "get_valid_children",
1904
+ "description": "Return the list of valid direct-child entity types for a parent type. Wraps getValidChildren / UPG_VALID_CHILDREN. Returns an empty array when the parent has no registered children. Pairs with get_entity_schema; the natural tool name for \"what can I create under this?\".",
1905
+ "domain": "spec",
1906
+ "inputSchema": {
1907
+ "type": "object",
1908
+ "properties": {
1909
+ "parent_type": {
1910
+ "type": "string",
1911
+ "description": "Canonical parent entity type."
1912
+ }
1913
+ },
1914
+ "required": [
1915
+ "parent_type"
1916
+ ]
1917
+ },
1918
+ "throws": [
1919
+ "textError when `parent_type` is missing."
1920
+ ],
1921
+ "examples": [],
1922
+ "warnings": [],
1923
+ "see": [
1924
+ "get_entity_schema",
1925
+ "list_entity_types",
1926
+ "get_entity_meta",
1927
+ "create_node"
1928
+ ],
1929
+ "source": "src/tools/spec.ts:976",
1930
+ "symbol": "getValidChildrenTool",
1931
+ "returns": "JSON: `{ parent_type, valid_children: string[] }`",
1932
+ "atomicity": "atomic (read-only)"
1933
+ },
1934
+ {
1935
+ "name": "inspect",
1936
+ "description": "Inspect approach: the path of arrival to \"what's broken?\". v0.3.0 ships as a definition lookup: returns the Inspect approach record plus invocation params wrapped in the family-resemblance envelope. The LLM consumes the signature_hint and emits { violations: [{ severity, kind, entity_id, description, fix_hint }] } against UPG_ANTI_PATTERNS plus the live graph. Structured execution lands in v0.3.x. Optional region OR optional entities[] scope the audit.",
1937
+ "domain": "spec",
1938
+ "inputSchema": {
1939
+ "type": "object",
1940
+ "properties": {
1941
+ "region": {
1942
+ "type": "string",
1943
+ "description": "Optional UPGRegionId; narrows inspection scope to a single region."
1944
+ },
1945
+ "entities": {
1946
+ "type": "array",
1947
+ "items": {
1948
+ "type": "string"
1949
+ },
1950
+ "description": "Optional entity_id[]; narrows inspection scope to a specific candidate set. Mutually composable with region."
1951
+ }
1952
+ }
1953
+ },
1954
+ "throws": [],
1955
+ "examples": [],
1956
+ "warnings": [
1957
+ "v0.3.0 returns the approach record only; the caller (LLM) is\nthe executor. Structured execution (run anti-pattern matchers plus\nstructural lints) lands in v0.3.x."
1958
+ ],
1959
+ "see": [
1960
+ "get_approach",
1961
+ "list_anti_patterns",
1962
+ "get_anti_pattern",
1963
+ "validate_graph",
1964
+ "plan",
1965
+ "reflect"
1966
+ ],
1967
+ "source": "src/tools/spec.ts:326",
1968
+ "symbol": "inspect",
1969
+ "returns": "JSON envelope: `{ approach_id: 'inspect', scope, generated_at, approach, params }`",
1970
+ "atomicity": "atomic (read-only)"
1971
+ },
1972
+ {
1973
+ "name": "list_anti_patterns",
1974
+ "description": "List the curated cross-domain anti-patterns from UPG_ANTI_PATTERNS. Each row pairs a memorable name with a machine-evaluable IntelligenceCondition, the stages where it can fire, severity, and remediation. Graph-health patterns evaluated against the whole graph, distinct from per-domain anti-patterns surfaced via get_domain_guide. Paginated (default limit 50, max 200). Filters AND together: severity (\"high\" | \"medium\" | \"low\"), stage (UPGProductStage, keeps patterns whose stages[] includes it).",
1975
+ "domain": "spec",
1976
+ "inputSchema": {
1977
+ "type": "object",
1978
+ "properties": {
1979
+ "severity": {
1980
+ "type": "string",
1981
+ "enum": [
1982
+ "high",
1983
+ "medium",
1984
+ "low"
1985
+ ],
1986
+ "description": "Exact-match UPGAntiPatternSeverity."
1987
+ },
1988
+ "stage": {
1989
+ "type": "string",
1990
+ "enum": [
1991
+ "concept",
1992
+ "validation",
1993
+ "build",
1994
+ "beta",
1995
+ "launch",
1996
+ "growth",
1997
+ "mature",
1998
+ "maintenance",
1999
+ "sunset"
2000
+ ],
2001
+ "description": "Keeps anti-patterns whose stages[] includes the given UPGProductStage."
2002
+ },
2003
+ "limit": {
2004
+ "type": "number",
2005
+ "description": "Page size (default 50, max 200)."
2006
+ },
2007
+ "cursor": {
2008
+ "type": "string",
2009
+ "description": "Opaque pagination cursor; pass next_cursor from a previous response."
2010
+ }
2011
+ }
2012
+ },
2013
+ "throws": [],
2014
+ "examples": [],
2015
+ "warnings": [],
2016
+ "see": [
2017
+ "get_anti_pattern",
2018
+ "validate_graph",
2019
+ "inspect",
2020
+ "get_domain_guide"
2021
+ ],
2022
+ "source": "src/tools/spec.ts:1110",
2023
+ "symbol": "listAntiPatterns",
2024
+ "returns": "JSON: `{ total, count, next_cursor?, anti_patterns: UPGCuratedAntiPattern[] }`",
2025
+ "atomicity": "atomic (read-only)"
2026
+ },
2027
+ {
2028
+ "name": "list_approaches",
2029
+ "description": "List the 5 canonical UPGApproach records: Plan / Inspect / Prioritise / Trace / Reflect. An approach is the *path of arrival* to a region of the graph (cartographic sense: final approach to an airport, coastline approach), distinct from the strategy-meeting sense. Each record carries id, label, description (with cartographic framing), question_answered, signature_hint, framework_id_examples. Optional filter: framework_id (narrows to approaches whose framework_id_examples include the given id).",
2030
+ "domain": "spec",
2031
+ "inputSchema": {
2032
+ "type": "object",
2033
+ "properties": {
2034
+ "framework_id": {
2035
+ "type": "string",
2036
+ "description": "Exact-match framework id; narrows to approaches whose framework_id_examples include it (discoverability surface; full reverse lookup is on UPGFramework.approach_ids)."
2037
+ }
2038
+ }
2039
+ },
2040
+ "throws": [],
2041
+ "examples": [],
2042
+ "warnings": [],
2043
+ "see": [
2044
+ "get_approach",
2045
+ "plan",
2046
+ "inspect",
2047
+ "prioritise",
2048
+ "trace",
2049
+ "reflect",
2050
+ "list_playbooks"
2051
+ ],
2052
+ "source": "src/tools/spec.ts:206",
2053
+ "symbol": "listApproaches",
2054
+ "returns": "JSON: `{ count, approaches: UPGApproach[] }`",
2055
+ "atomicity": "atomic (read-only)"
2056
+ },
2057
+ {
2058
+ "name": "list_benchmarks",
2059
+ "description": "Return one of the four canonical benchmark catalogs, the data behind get_graph_digest health logic. The kind parameter is REQUIRED and routes to the matching source: \"count\" → UPG_COUNT_BENCHMARKS (per-entity-type ranges across the 9-stage journey); \"relationship\" → UPG_RELATIONSHIP_BENCHMARKS (parent → child minimum counts per stage); \"ratio\" → UPG_RATIO_BENCHMARKS (expected ratios between entity-type counts); \"domain_activation\" → UPG_DOMAIN_ACTIVATION (when each atomic domain is expected to turn on). Optional filters AND together: stage (UPGProductStage), domain (atomic-domain id). Non-paginated (each catalog is small).",
2060
+ "domain": "spec",
2061
+ "inputSchema": {
2062
+ "type": "object",
2063
+ "properties": {
2064
+ "kind": {
2065
+ "type": "string",
2066
+ "enum": [
2067
+ "count",
2068
+ "relationship",
2069
+ "ratio",
2070
+ "domain_activation"
2071
+ ],
2072
+ "description": "Required: which benchmark catalog to return."
2073
+ },
2074
+ "stage": {
2075
+ "type": "string",
2076
+ "enum": [
2077
+ "concept",
2078
+ "validation",
2079
+ "build",
2080
+ "beta",
2081
+ "launch",
2082
+ "growth",
2083
+ "mature",
2084
+ "maintenance",
2085
+ "sunset"
2086
+ ],
2087
+ "description": "Optional UPGProductStage filter. Semantics depend on kind; see tool description."
2088
+ },
2089
+ "domain": {
2090
+ "type": "string",
2091
+ "description": "Optional atomic-domain id filter. Semantics depend on kind; see tool description."
2092
+ }
2093
+ },
2094
+ "required": [
2095
+ "kind"
2096
+ ]
2097
+ },
2098
+ "throws": [
2099
+ "textError when `kind` is missing or not one of the four supported values."
2100
+ ],
2101
+ "examples": [],
2102
+ "warnings": [],
2103
+ "see": [
2104
+ "get_graph_digest",
2105
+ "list_product_stages",
2106
+ "list_domains",
2107
+ "list_anti_patterns"
2108
+ ],
2109
+ "source": "src/tools/spec.ts:1184",
2110
+ "symbol": "listBenchmarks",
2111
+ "returns": "JSON: `{ kind, total, count, benchmarks: ... }`",
2112
+ "atomicity": "atomic (read-only)"
2113
+ },
2114
+ {
2115
+ "name": "list_cross_edge_types",
2116
+ "description": "List the canonical cross-product edge types from UPG_CROSS_EDGE_TYPES (shares_persona, shares_competitor, shares_metric, depends_on_product, cannibalises, succeeds). Portfolio-level relationships between entities in different products, separate from the within-product UPG_EDGE_CATALOG.",
2117
+ "domain": "spec",
2118
+ "inputSchema": {
2119
+ "type": "object",
2120
+ "properties": {}
2121
+ },
2122
+ "throws": [],
2123
+ "examples": [],
2124
+ "warnings": [],
2125
+ "see": [
2126
+ "list_edge_types",
2127
+ "list_portfolio_cross_edges",
2128
+ "migrate_cross_edges"
2129
+ ],
2130
+ "source": "src/tools/spec.ts:830",
2131
+ "symbol": "listCrossEdgeTypes",
2132
+ "returns": "JSON: `{ count, types: readonly UPGCrossEdgeType[] }`",
2133
+ "atomicity": "atomic (read-only)"
2134
+ },
2135
+ {
2136
+ "name": "list_domain_rings",
2137
+ "description": "List every UPGDomainRing from UPG_DOMAIN_RINGS in canonical order (Nucleus → Understand → Define → Build → Grow → Operate → Extend). Rings are the 7 concentric groupings of the 36 UPG atomic domains. Each ring carries { id, label, description, domain_ids }. Non-paginated.",
2138
+ "domain": "spec",
2139
+ "inputSchema": {
2140
+ "type": "object",
2141
+ "properties": {}
2142
+ },
2143
+ "throws": [],
2144
+ "examples": [],
2145
+ "warnings": [],
2146
+ "see": [
2147
+ "get_domain_ring",
2148
+ "list_domains",
2149
+ "get_domain_guide"
2150
+ ],
2151
+ "source": "src/tools/spec.ts:1568",
2152
+ "symbol": "listDomainRings",
2153
+ "returns": "JSON: `{ rings: UPGDomainRing[], total: number }`",
2154
+ "atomicity": "atomic (read-only)"
2155
+ },
2156
+ {
2157
+ "name": "list_domains",
2158
+ "description": "List domains. Default (with_guide_only: true) returns every domain that has a canonical usage guide: id, anchor_entity, and creation_sequence per domain. Pass with_guide_only: false to enumerate every atomic domain from UPG_DOMAINS (~36 at v0.3.0); each row carries id, label, description, types, has_guide. The two shapes share one tool surface, disjoint by the boolean.",
2159
+ "domain": "spec",
2160
+ "inputSchema": {
2161
+ "type": "object",
2162
+ "properties": {
2163
+ "with_guide_only": {
2164
+ "type": "boolean",
2165
+ "description": "Default true: return only domains with a canonical usage guide (compact id, anchor_entity, creation_sequence). Pass false to return every atomic domain (id, label, description, types, has_guide)."
2166
+ }
2167
+ }
2168
+ },
2169
+ "throws": [],
2170
+ "examples": [],
2171
+ "warnings": [],
2172
+ "see": [
2173
+ "get_domain_guide",
2174
+ "list_regions",
2175
+ "list_entity_types"
2176
+ ],
2177
+ "source": "src/tools/spec.ts:491",
2178
+ "symbol": "listDomains",
2179
+ "returns": "JSON: `{ count, domains: Array<{ domain_id, anchor_entity, creation_sequence } | { domain_id, label, description, types, has_guide }> }`",
2180
+ "atomicity": "atomic (read-only)"
2181
+ },
2182
+ {
2183
+ "name": "list_edge_migrations",
2184
+ "description": "List every edge-key migration from UPG_EDGE_MIGRATIONS: renamed or dropped canonical edge type keys (e.g. persona_has_jtbd → persona_pursues_job). Each row carries { kind, from, to?, since }. kind is \"rename\" or \"drop\". Optional from_edge filter exact-matches on the from field.",
2185
+ "domain": "spec",
2186
+ "inputSchema": {
2187
+ "type": "object",
2188
+ "properties": {
2189
+ "from_edge": {
2190
+ "type": "string",
2191
+ "description": "Exact-match filter on the deprecated edge key (e.g. \"persona_has_jtbd\")."
2192
+ }
2193
+ }
2194
+ },
2195
+ "throws": [],
2196
+ "examples": [],
2197
+ "warnings": [],
2198
+ "see": [
2199
+ "list_type_migrations",
2200
+ "list_split_migrations",
2201
+ "rename_edge_type",
2202
+ "list_edge_types",
2203
+ "validate_graph"
2204
+ ],
2205
+ "source": "src/tools/spec.ts:1350",
2206
+ "symbol": "listEdgeMigrations",
2207
+ "returns": "JSON: `{ migrations: [{ kind, from, to?, since }], total: number }`",
2208
+ "atomicity": "atomic (read-only)"
2209
+ },
2210
+ {
2211
+ "name": "list_edge_types",
2212
+ "description": "List every canonical edge type from UPG_EDGE_CATALOG, optionally narrowed by source_type and/or target_type. Each entry carries the edge key (type), forward/reverse verbs, classification, and endpoint types. The polymorphic wildcard \"node\" is preserved on registered polymorphic edges.",
2213
+ "domain": "spec",
2214
+ "inputSchema": {
2215
+ "type": "object",
2216
+ "properties": {
2217
+ "source_type": {
2218
+ "type": "string",
2219
+ "description": "Exact-match filter on UPGEdgeDefinition.source_type. Pass \"node\" to find polymorphic edges with a wildcard source."
2220
+ },
2221
+ "target_type": {
2222
+ "type": "string",
2223
+ "description": "Exact-match filter on UPGEdgeDefinition.target_type."
2224
+ }
2225
+ }
2226
+ },
2227
+ "throws": [],
2228
+ "examples": [],
2229
+ "warnings": [],
2230
+ "see": [
2231
+ "get_edge_type",
2232
+ "resolve_edge_for_pair",
2233
+ "list_cross_edge_types",
2234
+ "create_edge"
2235
+ ],
2236
+ "source": "src/tools/spec.ts:633",
2237
+ "symbol": "listEdgeTypes",
2238
+ "returns": "JSON: `{ count, edges: Array<{ type, forward_verb, reverse_verb, classification, source_type, target_type }> }`",
2239
+ "atomicity": "atomic (read-only)"
2240
+ },
2241
+ {
2242
+ "name": "list_entity_types",
2243
+ "description": "List canonical entity types from UPG_ENTITY_META, the source of truth for ontology evolution (every active, deprecated, or removed type with its immutable type_id, maturity tier, and version metadata). Paginated (default limit 50, max 200). Filters AND together and apply before pagination: domain (atomic-domain id), maturity (\"draft\" | \"proposed\" | \"stable\" | \"deprecated\" | \"removed\"), deprecated (boolean shortcut). Each row carries the full EntityTypeMeta plus resolved domain_id (null if no atomic-domain mapping).",
2244
+ "domain": "spec",
2245
+ "inputSchema": {
2246
+ "type": "object",
2247
+ "properties": {
2248
+ "domain": {
2249
+ "type": "string",
2250
+ "description": "Exact-match atomic-domain id (e.g. \"user\", \"market_intelligence\")."
2251
+ },
2252
+ "maturity": {
2253
+ "type": "string",
2254
+ "enum": [
2255
+ "draft",
2256
+ "proposed",
2257
+ "stable",
2258
+ "deprecated",
2259
+ "removed"
2260
+ ],
2261
+ "description": "Exact-match UPGEntityTypeMaturity."
2262
+ },
2263
+ "deprecated": {
2264
+ "type": "boolean",
2265
+ "description": "true → only deprecated types; false → exclude deprecated and removed types (the active set). Composes with maturity via AND."
2266
+ },
2267
+ "limit": {
2268
+ "type": "number",
2269
+ "description": "Page size (default 50, max 200)."
2270
+ },
2271
+ "cursor": {
2272
+ "type": "string",
2273
+ "description": "Opaque pagination cursor; pass next_cursor from a previous response."
2274
+ }
2275
+ }
2276
+ },
2277
+ "throws": [],
2278
+ "examples": [],
2279
+ "warnings": [],
2280
+ "see": [
2281
+ "get_entity_meta",
2282
+ "get_entity_schema",
2283
+ "list_type_labels",
2284
+ "list_domains"
2285
+ ],
2286
+ "source": "src/tools/spec.ts:1019",
2287
+ "symbol": "listEntityTypes",
2288
+ "returns": "JSON: `{ total, count, next_cursor?, types: Array<EntityTypeMeta & { domain_id: string | null }> }`",
2289
+ "atomicity": "atomic (read-only)"
2290
+ },
2291
+ {
2292
+ "name": "list_framework_categories",
2293
+ "description": "List all valid framework category values from UPG_FRAMEWORK_CATEGORIES (e.g. \"strategy\", \"prioritization\", \"discovery\", \"growth\", \"engineering\"). Use as valid values for the category filter on list_frameworks / get_framework.",
2294
+ "domain": "spec",
2295
+ "inputSchema": {
2296
+ "type": "object",
2297
+ "properties": {}
2298
+ },
2299
+ "throws": [],
2300
+ "examples": [],
2301
+ "warnings": [],
2302
+ "see": [
2303
+ "list_frameworks",
2304
+ "list_framework_structure_patterns"
2305
+ ],
2306
+ "source": "src/tools/spec.ts:1524",
2307
+ "symbol": "listFrameworkCategories",
2308
+ "returns": "JSON: `{ categories: string[], total: number }`",
2309
+ "atomicity": "atomic (read-only)"
2310
+ },
2311
+ {
2312
+ "name": "list_framework_structure_patterns",
2313
+ "description": "List all valid framework structure pattern values from UPG_STRUCTURE_PATTERNS: the visual topological shapes (tree, table, matrix, funnel, collection, quadrant, flow). Mirrors UPGFramework.structure.pattern.",
2314
+ "domain": "spec",
2315
+ "inputSchema": {
2316
+ "type": "object",
2317
+ "properties": {}
2318
+ },
2319
+ "throws": [],
2320
+ "examples": [],
2321
+ "warnings": [],
2322
+ "see": [
2323
+ "list_frameworks",
2324
+ "list_framework_categories",
2325
+ "get_framework"
2326
+ ],
2327
+ "source": "src/tools/spec.ts:1545",
2328
+ "symbol": "listFrameworkStructurePatterns",
2329
+ "returns": "JSON: `{ patterns: string[], total: number }`",
2330
+ "atomicity": "atomic (read-only)"
2331
+ },
2332
+ {
2333
+ "name": "list_frameworks",
2334
+ "description": "List canonical UPGFramework definitions (351 total at v0.3.0). Paginated (default limit 50, max 200) to avoid transport overflow. Cursor is opaque; pass next_cursor from a previous response to advance. Optional category filter is exact-match against UPGFramework.category and applied before pagination.",
2335
+ "domain": "spec",
2336
+ "inputSchema": {
2337
+ "type": "object",
2338
+ "properties": {
2339
+ "category": {
2340
+ "type": "string",
2341
+ "description": "Exact-match filter on UPGFramework.category (e.g. \"strategy\", \"prioritization\")."
2342
+ },
2343
+ "limit": {
2344
+ "type": "number",
2345
+ "description": "Page size (default 50, max 200)."
2346
+ },
2347
+ "cursor": {
2348
+ "type": "string",
2349
+ "description": "Opaque pagination cursor; pass next_cursor from a previous response."
2350
+ }
2351
+ }
2352
+ },
2353
+ "throws": [],
2354
+ "examples": [],
2355
+ "warnings": [],
2356
+ "see": [
2357
+ "get_framework",
2358
+ "prioritise",
2359
+ "list_approaches"
2360
+ ],
2361
+ "source": "src/tools/spec.ts:554",
2362
+ "symbol": "listFrameworks",
2363
+ "returns": "JSON: `{ total, count, next_cursor?, frameworks: UPGFramework[] }`",
2364
+ "atomicity": "atomic (read-only)"
2365
+ },
2366
+ {
2367
+ "name": "list_lenses",
2368
+ "description": "List every canonical UPGLens shipped with @unified-product-graph/core: Product, Design, Engineering, Growth, Business, Research, Marketing, Full. Returns a compact summary per lens (id, name, description, icon, audience, perspective, framework_id, playbook_id, visible_domain_count, intelligence_prompt_count). Drill into get_lens for the full record.",
2369
+ "domain": "spec",
2370
+ "inputSchema": {
2371
+ "type": "object",
2372
+ "properties": {}
2373
+ },
2374
+ "throws": [],
2375
+ "examples": [],
2376
+ "warnings": [],
2377
+ "see": [
2378
+ "get_lens",
2379
+ "list_regions",
2380
+ "list_playbooks",
2381
+ "list_frameworks"
2382
+ ],
2383
+ "source": "src/tools/spec.ts:856",
2384
+ "symbol": "listLenses",
2385
+ "returns": "JSON: `{ count, lenses: Array<{ id, name, description, icon, audience, perspective, framework_id?, playbook_id?, visible_domain_count, intelligence_prompt_count }> }`",
2386
+ "atomicity": "atomic (read-only)"
2387
+ },
2388
+ {
2389
+ "name": "list_lifecycles",
2390
+ "description": "List lifecycle definitions from UPG_LIFECYCLES. Response includes free_types (UPG_LIFECYCLE_FREE_TYPES: static types with no phase progression) and planned_types (UPG_LIFECYCLE_PLANNED_TYPES: lifecycle planned but not yet authored). Filters: entity_type (exact-match); lifecycle_only (when true, omits free/planned lists).",
2391
+ "domain": "spec",
2392
+ "inputSchema": {
2393
+ "type": "object",
2394
+ "properties": {
2395
+ "entity_type": {
2396
+ "type": "string",
2397
+ "description": "Exact-match entity type name (e.g. \"feature\", \"hypothesis_claim\"). Returns at most one lifecycle."
2398
+ },
2399
+ "lifecycle_only": {
2400
+ "type": "boolean",
2401
+ "description": "When true, omit free_types and planned_types from response."
2402
+ }
2403
+ }
2404
+ },
2405
+ "throws": [],
2406
+ "examples": [],
2407
+ "warnings": [],
2408
+ "see": [
2409
+ "get_lifecycle",
2410
+ "list_entity_types",
2411
+ "get_entity_meta"
2412
+ ],
2413
+ "source": "src/tools/spec.ts:1415",
2414
+ "symbol": "listLifecycles",
2415
+ "returns": "JSON: `{ lifecycles, total, free_types: string[], planned_types: string[] }`",
2416
+ "atomicity": "atomic (read-only)"
2417
+ },
2418
+ {
2419
+ "name": "list_playbooks",
2420
+ "description": "List the canonical UPG playbooks shipped with @unified-product-graph/core. Each playbook bootstraps a region; its creation_sequence answers \"what to create when populating this region\". Optional filters: region, canonical_only, framework_id. v0.3.0 ships 23 playbooks across 10 regions (10 canonical plus 13 specialised; 3 carry framework_id: BMC, AARRR, build-measure-learn).",
2421
+ "domain": "spec",
2422
+ "inputSchema": {
2423
+ "type": "object",
2424
+ "properties": {
2425
+ "region": {
2426
+ "type": "string",
2427
+ "description": "Exact-match UPGRegionId (e.g. \"users_needs\", \"business_gtm_growth\")."
2428
+ },
2429
+ "canonical_only": {
2430
+ "type": "boolean",
2431
+ "description": "When true, return only the canonical playbook per region (W1 invariant restated)."
2432
+ },
2433
+ "framework_id": {
2434
+ "type": "string",
2435
+ "description": "Exact-match UPGFramework.id (e.g. \"business-model-canvas\", \"pirate-metrics-aarrr\")."
2436
+ }
2437
+ }
2438
+ },
2439
+ "throws": [],
2440
+ "examples": [],
2441
+ "warnings": [],
2442
+ "see": [
2443
+ "get_playbook",
2444
+ "list_regions",
2445
+ "list_approaches",
2446
+ "list_frameworks"
2447
+ ],
2448
+ "source": "src/tools/spec.ts:140",
2449
+ "symbol": "listPlaybooks",
2450
+ "returns": "JSON: `{ count, playbooks: UPGPlaybook[] }`",
2451
+ "atomicity": "atomic (read-only)"
2452
+ },
2453
+ {
2454
+ "name": "list_product_stages",
2455
+ "description": "Return the canonical 9-stage product journey from UPG_PRODUCT_STAGES: the closed enum used by create_product, get_graph_digest health logic, benchmark stage scoping, and anti-pattern stage filters. Order is canonical: earliest → latest (concept, validation, build, beta, launch, growth, mature, maintenance, sunset). Trivial enum surface, no filters, no pagination.",
2456
+ "domain": "spec",
2457
+ "inputSchema": {
2458
+ "type": "object",
2459
+ "properties": {}
2460
+ },
2461
+ "throws": [],
2462
+ "examples": [],
2463
+ "warnings": [],
2464
+ "see": [
2465
+ "list_benchmarks",
2466
+ "list_anti_patterns",
2467
+ "create_product"
2468
+ ],
2469
+ "source": "src/tools/spec.ts:1287",
2470
+ "symbol": "listProductStages",
2471
+ "returns": "JSON: `{ count, stages: readonly UPGProductStage[] }`",
2472
+ "atomicity": "atomic (read-only)"
2473
+ },
2474
+ {
2475
+ "name": "list_regions",
2476
+ "description": "List the 10 canonical UPG super-domain regions from UPG_REGIONS: pure graph topology (entities, anchors, intra/boundary edges, shape archetype). Returns a compact summary per region (id, label, order, shape, mental_model, anchor_type, composes_atomic_domains, entity_count, intra_edge_count, boundary_edge_count). Fixed list, non-paginated.",
2477
+ "domain": "spec",
2478
+ "inputSchema": {
2479
+ "type": "object",
2480
+ "properties": {}
2481
+ },
2482
+ "throws": [],
2483
+ "examples": [],
2484
+ "warnings": [],
2485
+ "see": [
2486
+ "get_region",
2487
+ "get_region_for_entity_type",
2488
+ "list_domains",
2489
+ "list_playbooks"
2490
+ ],
2491
+ "source": "src/tools/spec.ts:678",
2492
+ "symbol": "listRegions",
2493
+ "returns": "JSON: `{ count, regions: Array<{ id, label, order, shape, mental_model, anchor_type, composes_atomic_domains, entity_count, intra_edge_count, boundary_edge_count }> }`",
2494
+ "atomicity": "atomic (read-only)"
2495
+ },
2496
+ {
2497
+ "name": "list_scales",
2498
+ "description": "List every spec-defined assessment scale from UPG_SCALES: the canonical vocabulary for UPGAssessment values. Each scale carries id, label, description, min, max, steps, and per-point labels plus descriptions. Non-paginated. External scale_extensions are graph-instance–scoped and stay out of this surface.",
2499
+ "domain": "spec",
2500
+ "inputSchema": {
2501
+ "type": "object",
2502
+ "properties": {}
2503
+ },
2504
+ "throws": [],
2505
+ "examples": [],
2506
+ "warnings": [],
2507
+ "see": [
2508
+ "get_scale",
2509
+ "get_entity_schema"
2510
+ ],
2511
+ "source": "src/tools/spec.ts:1490",
2512
+ "symbol": "listScales",
2513
+ "returns": "JSON: `{ scales: UPGScaleDefinition[], total: number }`",
2514
+ "atomicity": "atomic (read-only)"
2515
+ },
2516
+ {
2517
+ "name": "list_split_migrations",
2518
+ "description": "List every 1→N split migration from UPG_SPLIT_MIGRATIONS: \"one type became multiple types\" rules (e.g. experiment → experiment_plan + experiment_run; hypothesis → hypothesis_claim + hypothesis_evidence). Each row includes the full UPGSplitMigration record plus since. Non-paginated.",
2519
+ "domain": "spec",
2520
+ "inputSchema": {
2521
+ "type": "object",
2522
+ "properties": {}
2523
+ },
2524
+ "throws": [],
2525
+ "examples": [],
2526
+ "warnings": [],
2527
+ "see": [
2528
+ "list_type_migrations",
2529
+ "list_edge_migrations",
2530
+ "migrate_type",
2531
+ "validate_graph"
2532
+ ],
2533
+ "source": "src/tools/spec.ts:1387",
2534
+ "symbol": "listSplitMigrations",
2535
+ "returns": "JSON: `{ splits: [...], total: number }`",
2536
+ "atomicity": "atomic (read-only)"
2537
+ },
2538
+ {
2539
+ "name": "list_type_labels",
2540
+ "description": "List canonical UPGTypeLabel entries: every entity type's display label, alt-labels (synonyms), per-framework labels, and (where applicable) designation labels. Paginated (default limit 100, max 500). Cursor is opaque base64 (offset:N) following the list_frameworks convention. External MCP apps need labels for rendering.",
2541
+ "domain": "spec",
2542
+ "inputSchema": {
2543
+ "type": "object",
2544
+ "properties": {
2545
+ "limit": {
2546
+ "type": "number",
2547
+ "description": "Page size (default 100, max 500)."
2548
+ },
2549
+ "cursor": {
2550
+ "type": "string",
2551
+ "description": "Opaque pagination cursor; pass next_cursor from a previous response."
2552
+ }
2553
+ }
2554
+ },
2555
+ "throws": [],
2556
+ "examples": [],
2557
+ "warnings": [],
2558
+ "see": [
2559
+ "get_type_label",
2560
+ "list_entity_types",
2561
+ "get_entity_meta"
2562
+ ],
2563
+ "source": "src/tools/spec.ts:919",
2564
+ "symbol": "listTypeLabels",
2565
+ "returns": "JSON: `{ total, count, next_cursor?, labels: UPGTypeLabel[] }`",
2566
+ "atomicity": "atomic (read-only)"
2567
+ },
2568
+ {
2569
+ "name": "list_type_migrations",
2570
+ "description": "List every type-rename migration from UPG_MIGRATIONS: the version-scoped registry of deprecated from → canonical to renames (e.g. pain_point → need, hypothesis → hypothesis_claim). Each row carries { from, to, since } where since is the spec version that introduced the migration. Optional from_type filter exact-matches on the from field.",
2571
+ "domain": "spec",
2572
+ "inputSchema": {
2573
+ "type": "object",
2574
+ "properties": {
2575
+ "from_type": {
2576
+ "type": "string",
2577
+ "description": "Exact-match filter on the deprecated type name (e.g. \"pain_point\", \"hypothesis\")."
2578
+ }
2579
+ }
2580
+ },
2581
+ "throws": [],
2582
+ "examples": [],
2583
+ "warnings": [],
2584
+ "see": [
2585
+ "list_edge_migrations",
2586
+ "list_split_migrations",
2587
+ "migrate_type",
2588
+ "validate_graph",
2589
+ "list_entity_types"
2590
+ ],
2591
+ "source": "src/tools/spec.ts:1319",
2592
+ "symbol": "listTypeMigrations",
2593
+ "returns": "JSON: `{ migrations: [{ from, to, since }], total: number }`",
2594
+ "atomicity": "atomic (read-only)"
2595
+ },
2596
+ {
2597
+ "name": "plan",
2598
+ "description": "Plan approach: the path of arrival to \"what should I build next?\". v0.3.0 ships as a definition lookup: returns the Plan approach record plus invocation params wrapped in the family-resemblance envelope { approach_id, scope, generated_at, approach, params }. The LLM consumes the signature_hint and synthesises { missing_entities, coverage_score } against the live graph. Structured execution lands in v0.3.x. Optional region narrows the scope.",
2599
+ "domain": "spec",
2600
+ "inputSchema": {
2601
+ "type": "object",
2602
+ "properties": {
2603
+ "region": {
2604
+ "type": "string",
2605
+ "description": "Optional UPGRegionId; narrows planning scope to a single region (e.g. \"users_needs\", \"business_gtm_growth\"). Omit for whole-graph planning."
2606
+ }
2607
+ }
2608
+ },
2609
+ "throws": [],
2610
+ "examples": [],
2611
+ "warnings": [
2612
+ "v0.3.0 returns the approach record only; the caller (LLM) is\nthe executor. Structured execution (compute coverage_score from\ncanonical region playbooks) lands in v0.3.x."
2613
+ ],
2614
+ "see": [
2615
+ "get_approach",
2616
+ "list_playbooks",
2617
+ "get_region",
2618
+ "inspect",
2619
+ "prioritise"
2620
+ ],
2621
+ "source": "src/tools/spec.ts:298",
2622
+ "symbol": "plan",
2623
+ "returns": "JSON envelope: `{ approach_id: 'plan', scope, generated_at, approach, params }`",
2624
+ "atomicity": "atomic (read-only)"
2625
+ },
2626
+ {
2627
+ "name": "prioritise",
2628
+ "description": "Prioritise approach: the path of arrival to \"what's most important?\". v0.3.0 ships as a definition lookup: returns the Prioritise approach record plus invocation params plus framework metadata wrapped in the family-resemblance envelope. Both candidates and framework_id are required. The LLM looks up the framework via get_framework, reads the scoring spec, and emits { ranked: [{ entity_id, score, rationale }], framework_used }. Structured execution lands in v0.3.x.",
2629
+ "domain": "spec",
2630
+ "inputSchema": {
2631
+ "type": "object",
2632
+ "properties": {
2633
+ "candidates": {
2634
+ "type": "array",
2635
+ "items": {
2636
+ "type": "string"
2637
+ },
2638
+ "description": "Required: entity_id[] to rank."
2639
+ },
2640
+ "framework_id": {
2641
+ "type": "string",
2642
+ "description": "Required: UPGFramework.id of the scoring lens (e.g. \"rice-scoring\", \"ice-scoring\", \"kano-model\", \"cost-of-delay\", \"wsjf\")."
2643
+ }
2644
+ },
2645
+ "required": [
2646
+ "candidates",
2647
+ "framework_id"
2648
+ ]
2649
+ },
2650
+ "throws": [
2651
+ "textError when `candidates` or `framework_id` are missing/empty."
2652
+ ],
2653
+ "examples": [],
2654
+ "warnings": [
2655
+ "v0.3.0 returns the approach record plus framework lookup only.\nStructured execution (apply framework's `computed_properties` to each\ncandidate, return ranked output) lands in v0.3.x."
2656
+ ],
2657
+ "see": [
2658
+ "get_approach",
2659
+ "list_frameworks",
2660
+ "get_framework",
2661
+ "plan",
2662
+ "trace"
2663
+ ],
2664
+ "source": "src/tools/spec.ts:359",
2665
+ "symbol": "prioritise",
2666
+ "returns": "JSON envelope: `{ approach_id: 'prioritise', scope, generated_at, approach, params }`",
2667
+ "atomicity": "atomic (read-only)"
2668
+ },
2669
+ {
2670
+ "name": "reflect",
2671
+ "description": "Reflect approach: the path of arrival to \"what should I be questioning?\". v0.3.0 ships as a definition lookup: returns the Reflect approach record plus invocation params wrapped in the family-resemblance envelope. The LLM consumes mode plus scope plus signature_hint and emits { prompts: [{ kind, question, target_entities? }] }. Optional mode is one of the 4 canonical nouns: assumptions / alternatives / blind-spots / load-bearing. Absence of mode signals open reflection. Optional scope accepts a region id, entity id, or null for whole-graph reflection.",
2672
+ "domain": "spec",
2673
+ "inputSchema": {
2674
+ "type": "object",
2675
+ "properties": {
2676
+ "scope": {
2677
+ "type": [
2678
+ "string",
2679
+ "null"
2680
+ ],
2681
+ "description": "Optional: region id, entity id, or null for whole-graph."
2682
+ },
2683
+ "mode": {
2684
+ "type": "string",
2685
+ "description": "Optional: one of assumptions, alternatives, blind-spots, load-bearing. Omit for open reflection.",
2686
+ "enum": [
2687
+ "assumptions",
2688
+ "alternatives",
2689
+ "blind-spots",
2690
+ "load-bearing"
2691
+ ]
2692
+ }
2693
+ }
2694
+ },
2695
+ "throws": [
2696
+ "textError when `mode` is provided but not one of the 4 canonical nouns."
2697
+ ],
2698
+ "examples": [],
2699
+ "warnings": [
2700
+ "v0.3.0 returns the approach record only; the caller (LLM)\nemits the prompts. Structured execution (template-driven prompt\ngeneration per mode plus targeted entity selection) lands in v0.3.x."
2701
+ ],
2702
+ "see": [
2703
+ "get_approach",
2704
+ "inspect",
2705
+ "plan",
2706
+ "get_anti_pattern"
2707
+ ],
2708
+ "source": "src/tools/spec.ts:450",
2709
+ "symbol": "reflect",
2710
+ "returns": "JSON envelope: `{ approach_id: 'reflect', scope, generated_at, approach, params }`",
2711
+ "atomicity": "atomic (read-only)"
2712
+ },
2713
+ {
2714
+ "name": "resolve_edge_for_pair",
2715
+ "description": "Resolve the canonical UPGEdgeType for a source_type → target_type containment pair. Wraps resolveContainmentEdge / UPG_EDGE_PAIR_MAP. Adapter-critical: every import adapter (Markdown, Notion, Linear, GitHub) uses this to look up the right \"_contains_\" edge before falling back to a polymorphic edge or skipping. Returns { edge_type: null } when the pair is not catalogued.",
2716
+ "domain": "spec",
2717
+ "inputSchema": {
2718
+ "type": "object",
2719
+ "properties": {
2720
+ "source_type": {
2721
+ "type": "string",
2722
+ "description": "Parent / source entity type."
2723
+ },
2724
+ "target_type": {
2725
+ "type": "string",
2726
+ "description": "Child / target entity type."
2727
+ }
2728
+ },
2729
+ "required": [
2730
+ "source_type",
2731
+ "target_type"
2732
+ ]
2733
+ },
2734
+ "throws": [
2735
+ "textError when `source_type` or `target_type` is missing."
2736
+ ],
2737
+ "examples": [],
2738
+ "warnings": [
2739
+ "Returns `edge_type: null` when no canonical pair is registered;\nadapters MUST fall back to a polymorphic edge or skip the relationship\nrather than synthesise a non-canonical key."
2740
+ ],
2741
+ "see": [
2742
+ "list_edge_types",
2743
+ "get_edge_type",
2744
+ "create_edge",
2745
+ "trace"
2746
+ ],
2747
+ "source": "src/tools/spec.ts:800",
2748
+ "symbol": "resolveEdgeForPair",
2749
+ "returns": "JSON: `{ source_type, target_type, edge_type: string | null }`",
2750
+ "atomicity": "atomic (read-only)"
2751
+ },
2752
+ {
2753
+ "name": "trace",
2754
+ "description": "Trace approach: the path of arrival to \"walk a meaningful path through existing graph\". v0.3.0 ships as a definition lookup: returns the Trace approach record plus invocation params wrapped in the family-resemblance envelope. The LLM uses anchor plus path to compose query() calls and emits { trail: [{ depth, entity_id, edge_type_in }], reached: entity_id[] }. Path is type-shorthand: [\"persona\",\"job\",\"feature\"] walks persona→job→feature using the canonical edge per pair. Optional edges_override selects non-canonical edges per hop; element null means \"use canonical\".",
2755
+ "domain": "spec",
2756
+ "inputSchema": {
2757
+ "type": "object",
2758
+ "properties": {
2759
+ "anchor": {
2760
+ "type": "string",
2761
+ "description": "Required: entity_id where the traversal starts."
2762
+ },
2763
+ "path": {
2764
+ "type": "array",
2765
+ "items": {
2766
+ "type": "string"
2767
+ },
2768
+ "description": "Required: UPGEntityType[] type-shorthand path. Each step walks via the canonical edge for the source→target pair."
2769
+ },
2770
+ "edges_override": {
2771
+ "type": "array",
2772
+ "items": {
2773
+ "type": [
2774
+ "string",
2775
+ "null"
2776
+ ]
2777
+ },
2778
+ "description": "Optional per-hop edge override array. Length must match path length; element null means \"use canonical edge for this pair\"."
2779
+ }
2780
+ },
2781
+ "required": [
2782
+ "anchor",
2783
+ "path"
2784
+ ]
2785
+ },
2786
+ "throws": [
2787
+ "textError when `anchor` or `path` are missing/invalid."
2788
+ ],
2789
+ "examples": [],
2790
+ "warnings": [
2791
+ "v0.3.0 returns the approach record only; the LLM composes the\nactual traversal via `query()`. Structured execution (BFS walker that\nreturns `{ trail, reached }`) lands in v0.3.x."
2792
+ ],
2793
+ "see": [
2794
+ "get_approach",
2795
+ "resolve_edge_for_pair",
2796
+ "query",
2797
+ "get_node",
2798
+ "plan",
2799
+ "prioritise"
2800
+ ],
2801
+ "source": "src/tools/spec.ts:403",
2802
+ "symbol": "trace",
2803
+ "returns": "JSON envelope: `{ approach_id: 'trace', scope, generated_at, approach, params }`",
2804
+ "atomicity": "atomic (read-only)"
2805
+ },
2806
+ {
2807
+ "name": "create_cross_product_edge",
2808
+ "description": "Create a cross-product edge linking entities across different products. Type must be one of the canonical UPG cross-edge types: shares_persona, shares_competitor, shares_metric, depends_on_product, cannibalises, succeeds.",
2809
+ "domain": "portfolio",
2810
+ "inputSchema": {
2811
+ "type": "object",
2812
+ "properties": {
2813
+ "product_id": {
2814
+ "type": "string",
2815
+ "description": "The product creating this cross-edge"
2816
+ },
2817
+ "source": {
2818
+ "type": "string",
2819
+ "description": "Qualified source: {product_id}/{node_id}"
2820
+ },
2821
+ "target": {
2822
+ "type": "string",
2823
+ "description": "Qualified target: {product_id}/{node_id}"
2824
+ },
2825
+ "type": {
2826
+ "type": "string",
2827
+ "description": "Cross-edge type",
2828
+ "enum": [
2829
+ "shares_persona",
2830
+ "shares_competitor",
2831
+ "shares_metric",
2832
+ "depends_on_product",
2833
+ "cannibalises",
2834
+ "succeeds"
2835
+ ]
2836
+ }
2837
+ },
2838
+ "required": [
2839
+ "product_id",
2840
+ "source",
2841
+ "target",
2842
+ "type"
2843
+ ]
2844
+ },
2845
+ "throws": [
2846
+ "textError when `product_id`, `source`, `target`, or\n`type` is missing, or `type` is not a UPG cross-edge type."
2847
+ ],
2848
+ "examples": [],
2849
+ "warnings": [
2850
+ "Source/target are qualified strings (`{product_id}/{node_id}`)\nand skip FK validation against the products table. A target\nreferencing a deleted product becomes a dangling cross-edge; sweep\nperiodically with `repair_dangling_edges`."
2851
+ ],
2852
+ "see": [
2853
+ "list_cross_edge_types",
2854
+ "list_portfolio_cross_edges",
2855
+ "repair_dangling_edges",
2856
+ "migrate_cross_edges"
2857
+ ],
2858
+ "source": "src/tools/portfolio.ts:84",
2859
+ "symbol": "createCrossProductEdge",
2860
+ "returns": "JSON: `{ edge: { id, source, target, type, created_by_product_id } }`",
2861
+ "atomicity": "atomic"
2862
+ },
2863
+ {
2864
+ "name": "list_portfolio_cross_edges",
2865
+ "description": "List all cross-product edges created by a product. Cross-product edges link entities across different products (e.g. shares_persona, depends_on_product).",
2866
+ "domain": "portfolio",
2867
+ "inputSchema": {
2868
+ "type": "object",
2869
+ "properties": {
2870
+ "product_id": {
2871
+ "type": "string",
2872
+ "description": "Product ID"
2873
+ }
2874
+ },
2875
+ "required": [
2876
+ "product_id"
2877
+ ]
2878
+ },
2879
+ "throws": [
2880
+ "textError when `product_id` is missing."
2881
+ ],
2882
+ "examples": [],
2883
+ "warnings": [
2884
+ "Returns only edges this product **created**; edges another\nproduct created targeting this product surface through that product's\nown call. To audit all incident cross-edges, query each product in\nthe portfolio."
2885
+ ],
2886
+ "see": [
2887
+ "create_cross_product_edge",
2888
+ "list_cross_edge_types",
2889
+ "migrate_cross_edges"
2890
+ ],
2891
+ "source": "src/tools/portfolio.ts:55",
2892
+ "symbol": "listPortfolioCrossEdges",
2893
+ "returns": "JSON: `{ edges: [{ id, source, target, type }], total: number }`",
2894
+ "atomicity": "atomic (read-only)"
2895
+ },
2896
+ {
2897
+ "name": "list_portfolios",
2898
+ "description": "List the product portfolio for this UPG cloud instance. For v1, returns all products as a single portfolio. Use before creating cross-product edges to discover valid product IDs.",
2899
+ "domain": "portfolio",
2900
+ "inputSchema": {
2901
+ "type": "object",
2902
+ "properties": {}
2903
+ },
2904
+ "throws": [],
2905
+ "examples": [],
2906
+ "warnings": [
2907
+ "v1 returns a single synthetic `'default'` portfolio per\ninstance; multi-portfolio scoping arrives once auth is wired.\nTreat the `id: 'default'` shape as transitional."
2908
+ ],
2909
+ "see": [
2910
+ "list_products",
2911
+ "list_portfolio_cross_edges"
2912
+ ],
2913
+ "source": "src/tools/portfolio.ts:29",
2914
+ "symbol": "listPortfolios",
2915
+ "returns": "JSON: `{ portfolios: [{ id, title, products: [{ id, title, stage? }] }], total: number }`",
2916
+ "atomicity": "atomic (read-only)"
2917
+ },
2918
+ {
2919
+ "name": "repair_dangling_edges",
2920
+ "description": "Find (and optionally remove) cross-product edges that reference a product that no longer exists. Default is dry_run=true.",
2921
+ "domain": "portfolio",
2922
+ "inputSchema": {
2923
+ "type": "object",
2924
+ "properties": {
2925
+ "product_id": {
2926
+ "type": "string",
2927
+ "description": "Product ID"
2928
+ },
2929
+ "dry_run": {
2930
+ "type": "boolean",
2931
+ "description": "Default true: report only."
2932
+ },
2933
+ "drop": {
2934
+ "type": "array",
2935
+ "items": {
2936
+ "type": "string"
2937
+ },
2938
+ "description": "Categories to drop when dry_run=false"
2939
+ }
2940
+ },
2941
+ "required": [
2942
+ "product_id"
2943
+ ]
2944
+ },
2945
+ "throws": [
2946
+ "textError when `product_id` is missing."
2947
+ ],
2948
+ "examples": [],
2949
+ "warnings": [
2950
+ "Default is `dry_run: true`. Pass `dry_run: false` AND\n`drop: ['dangling_cross_edges']` to actually delete; the second\nguard prevents accidental drops. Per-edge errors during deletion\n(concurrent removal) are swallowed; check `dropped` against\n`dangling_count` to detect partial application."
2951
+ ],
2952
+ "see": [
2953
+ "create_cross_product_edge",
2954
+ "list_portfolio_cross_edges",
2955
+ "migrate_cross_edges"
2956
+ ],
2957
+ "source": "src/tools/portfolio.ts:137",
2958
+ "symbol": "repairDanglingEdges",
2959
+ "returns": "JSON: `{ dangling: [{ id, source, target, type }], dangling_count, dry_run, dropped }`",
2960
+ "atomicity": "atomic-with-rollback (when drop is requested)"
2961
+ },
2962
+ {
2963
+ "name": "batch_create_edges",
2964
+ "description": "Create up to 50 edges in a single atomic Postgres transaction. Edge type is auto-inferred from source/target types when omitted. All-or-nothing: any failure rolls back the entire batch.",
2965
+ "domain": "batch",
2966
+ "inputSchema": {
2967
+ "type": "object",
2968
+ "properties": {
2969
+ "product_id": {
2970
+ "type": "string",
2971
+ "description": "Product ID"
2972
+ },
2973
+ "edges": {
2974
+ "type": "array",
2975
+ "description": "Edges to create (max 50)",
2976
+ "items": {
2977
+ "type": "object",
2978
+ "properties": {
2979
+ "source_id": {
2980
+ "type": "string",
2981
+ "description": "Source node ID"
2982
+ },
2983
+ "target_id": {
2984
+ "type": "string",
2985
+ "description": "Target node ID"
2986
+ },
2987
+ "type": {
2988
+ "type": "string",
2989
+ "description": "Edge type; auto-inferred if omitted"
2990
+ }
2991
+ },
2992
+ "required": [
2993
+ "source_id",
2994
+ "target_id"
2995
+ ]
2996
+ }
2997
+ }
2998
+ },
2999
+ "required": [
3000
+ "product_id",
3001
+ "edges"
3002
+ ]
3003
+ },
3004
+ "throws": [
3005
+ "textError when `edges` is missing / non-array / empty / >50, any\nitem is missing `source_id` / `target_id`, any endpoint does not exist, an\nexplicit `type` violates the catalog's source/target pair, or an inferred\npair has no canonical edge. Any such failure rejects the whole batch\nbefore BEGIN."
3006
+ ],
3007
+ "examples": [],
3008
+ "warnings": [
3009
+ "Inference is catalog-strict — an unmapped pair is refused rather\nthan fabricating a `${source}_contains_${target}` edge. Pass an explicit\n`type` (resolved via `resolve_edge_for_pair`) for non-catalog edges."
3010
+ ],
3011
+ "see": [
3012
+ "create_edge",
3013
+ "resolve_edge_for_pair",
3014
+ "batch_delete_edges"
3015
+ ],
3016
+ "source": "src/tools/batch.ts:399",
3017
+ "symbol": "batchCreateEdges",
3018
+ "returns": "JSON: `{ created: [{ id, source_id, target_id, type }], count }`.",
3019
+ "atomicity": "atomic-with-rollback (BEGIN / COMMIT / ROLLBACK)."
3020
+ },
3021
+ {
3022
+ "name": "batch_create_nodes",
3023
+ "description": "Create up to 50 entities in a single atomic Postgres transaction. For each node with a parent_id, a containment edge is created in the same transaction. All-or-nothing: any failure rolls back the entire batch.",
3024
+ "domain": "batch",
3025
+ "inputSchema": {
3026
+ "type": "object",
3027
+ "properties": {
3028
+ "product_id": {
3029
+ "type": "string",
3030
+ "description": "Product ID"
3031
+ },
3032
+ "nodes": {
3033
+ "type": "array",
3034
+ "description": "Nodes to create (max 50)",
3035
+ "items": {
3036
+ "type": "object",
3037
+ "properties": {
3038
+ "type": {
3039
+ "type": "string",
3040
+ "description": "UPG entity type"
3041
+ },
3042
+ "title": {
3043
+ "type": "string",
3044
+ "description": "Entity title"
3045
+ },
3046
+ "description": {
3047
+ "type": "string",
3048
+ "description": "Optional description"
3049
+ },
3050
+ "tags": {
3051
+ "type": "array",
3052
+ "items": {
3053
+ "type": "string"
3054
+ },
3055
+ "description": "Freeform tags"
3056
+ },
3057
+ "status": {
3058
+ "type": "string",
3059
+ "description": "Lifecycle status"
3060
+ },
3061
+ "properties": {
3062
+ "type": "object",
3063
+ "description": "Type-specific fields"
3064
+ },
3065
+ "parent_id": {
3066
+ "type": "string",
3067
+ "description": "Parent node ID; creates a containment edge automatically"
3068
+ }
3069
+ },
3070
+ "required": [
3071
+ "type",
3072
+ "title"
3073
+ ]
3074
+ }
3075
+ }
3076
+ },
3077
+ "required": [
3078
+ "product_id",
3079
+ "nodes"
3080
+ ]
3081
+ },
3082
+ "throws": [
3083
+ "textError when `nodes` is missing / non-array, any required field\n(`type`, `title`) is absent, or any node carries a declared property whose\nvalue type mismatches the schema (rejects the whole batch before BEGIN)."
3084
+ ],
3085
+ "examples": [],
3086
+ "warnings": [
3087
+ "Validation runs inline before BEGIN; a single bad item rejects\nthe entire batch before any database mutation. Parent containment edges\nare catalog-strict: a non-canonical parent→child pair skips the edge with\na warning rather than fabricating a `_contains_` edge (matches\n`create_node`). A missing parent likewise skips the edge."
3088
+ ],
3089
+ "see": [
3090
+ "create_node",
3091
+ "batch_create_edges",
3092
+ "batch_update_nodes"
3093
+ ],
3094
+ "source": "src/tools/batch.ts:63",
3095
+ "symbol": "batchCreateNodes",
3096
+ "returns": "JSON: `{ created: [{ id, type, title }], count, warnings? }`.",
3097
+ "atomicity": "atomic-with-rollback (BEGIN / COMMIT / ROLLBACK)."
3098
+ },
3099
+ {
3100
+ "name": "batch_delete_edges",
3101
+ "description": "Delete up to 50 edges in a single atomic Postgres transaction. All-or-nothing: any failure rolls back the entire batch.",
3102
+ "domain": "batch",
3103
+ "inputSchema": {
3104
+ "type": "object",
3105
+ "properties": {
3106
+ "product_id": {
3107
+ "type": "string",
3108
+ "description": "Product ID"
3109
+ },
3110
+ "edge_ids": {
3111
+ "type": "array",
3112
+ "items": {
3113
+ "type": "string"
3114
+ },
3115
+ "description": "Edge IDs to delete (max 50)"
3116
+ }
3117
+ },
3118
+ "required": [
3119
+ "product_id",
3120
+ "edge_ids"
3121
+ ]
3122
+ },
3123
+ "throws": [
3124
+ "textError when `edge_ids` is missing / non-array / empty / >50, or\nany ID does not resolve in the given product."
3125
+ ],
3126
+ "examples": [],
3127
+ "warnings": [],
3128
+ "see": [
3129
+ "delete_edge",
3130
+ "batch_create_edges",
3131
+ "export_edges"
3132
+ ],
3133
+ "source": "src/tools/batch.ts:496",
3134
+ "symbol": "batchDeleteEdges",
3135
+ "returns": "JSON: `{ deleted: [id], count }`.",
3136
+ "atomicity": "atomic-with-rollback (BEGIN / COMMIT / ROLLBACK)."
3137
+ },
3138
+ {
3139
+ "name": "batch_delete_nodes",
3140
+ "description": "Delete up to 50 entities and all their connected edges in a single atomic Postgres transaction. All-or-nothing: any failure rolls back the entire batch.",
3141
+ "domain": "batch",
3142
+ "inputSchema": {
3143
+ "type": "object",
3144
+ "properties": {
3145
+ "product_id": {
3146
+ "type": "string",
3147
+ "description": "Product ID"
3148
+ },
3149
+ "node_ids": {
3150
+ "type": "array",
3151
+ "items": {
3152
+ "type": "string"
3153
+ },
3154
+ "description": "Node IDs to delete (max 50)"
3155
+ }
3156
+ },
3157
+ "required": [
3158
+ "product_id",
3159
+ "node_ids"
3160
+ ]
3161
+ },
3162
+ "throws": [
3163
+ "textError when `node_ids` is missing / non-array / empty / >50, or\nany ID does not resolve."
3164
+ ],
3165
+ "examples": [],
3166
+ "warnings": [
3167
+ "Cascade-deletes ALL edges incident on each node, in either\ndirection. Removal is hard; recovery flows through the audit log,\nwhich records each removal for the retention window."
3168
+ ],
3169
+ "see": [
3170
+ "delete_node",
3171
+ "batch_delete_edges",
3172
+ "deduplicate_nodes"
3173
+ ],
3174
+ "source": "src/tools/batch.ts:332",
3175
+ "symbol": "batchDeleteNodes",
3176
+ "returns": "JSON: `{ deleted: [id], count }`.",
3177
+ "atomicity": "atomic-with-rollback (BEGIN / COMMIT / ROLLBACK)."
3178
+ },
3179
+ {
3180
+ "name": "batch_move_nodes",
3181
+ "description": "Re-parent up to 50 nodes in a single atomic Postgres transaction. For each move, old containment edges are removed and a new containment edge to new_parent_id is created. All-or-nothing: any failure rolls back the entire batch.",
3182
+ "domain": "batch",
3183
+ "inputSchema": {
3184
+ "type": "object",
3185
+ "properties": {
3186
+ "product_id": {
3187
+ "type": "string",
3188
+ "description": "Product ID"
3189
+ },
3190
+ "moves": {
3191
+ "type": "array",
3192
+ "description": "Move operations (max 50)",
3193
+ "items": {
3194
+ "type": "object",
3195
+ "properties": {
3196
+ "node_id": {
3197
+ "type": "string",
3198
+ "description": "The node to re-parent"
3199
+ },
3200
+ "new_parent_id": {
3201
+ "type": "string",
3202
+ "description": "The new parent node ID"
3203
+ }
3204
+ },
3205
+ "required": [
3206
+ "node_id",
3207
+ "new_parent_id"
3208
+ ]
3209
+ }
3210
+ }
3211
+ },
3212
+ "required": [
3213
+ "product_id",
3214
+ "moves"
3215
+ ]
3216
+ },
3217
+ "throws": [
3218
+ "textError when `moves` is missing / non-array / empty / >50, or any\n`node_id` / `new_parent_id` does not resolve."
3219
+ ],
3220
+ "examples": [],
3221
+ "warnings": [
3222
+ "Heuristic deletion of \"old containment\" edges relies on LIKE\npatterns (`%_contains_%`, `%_has_%`, `%_produces_%`) rather than the\ncanonical edge catalog. Edges matching the patterns yet semantically\nnon-containment may be removed alongside the intended ones. A follow-up\nwill tighten this to catalog-aware classification."
3223
+ ],
3224
+ "see": [
3225
+ "move_node",
3226
+ "batch_create_edges",
3227
+ "resolve_edge_for_pair"
3228
+ ],
3229
+ "source": "src/tools/batch.ts:567",
3230
+ "symbol": "batchMoveNodes",
3231
+ "returns": "JSON: `{ moved: [{ node_id, new_parent_id }], count }`.",
3232
+ "atomicity": "atomic-with-rollback (BEGIN / COMMIT / ROLLBACK)."
3233
+ },
3234
+ {
3235
+ "name": "batch_update_nodes",
3236
+ "description": "Update up to 50 entities in a single atomic Postgres transaction. Properties are merged with existing (not replaced). Unspecified fields are preserved. All-or-nothing: any failure rolls back the entire batch.",
3237
+ "domain": "batch",
3238
+ "inputSchema": {
3239
+ "type": "object",
3240
+ "properties": {
3241
+ "product_id": {
3242
+ "type": "string",
3243
+ "description": "Product ID"
3244
+ },
3245
+ "nodes": {
3246
+ "type": "array",
3247
+ "description": "Nodes to update (max 50)",
3248
+ "items": {
3249
+ "type": "object",
3250
+ "properties": {
3251
+ "id": {
3252
+ "type": "string",
3253
+ "description": "Node ID to update"
3254
+ },
3255
+ "title": {
3256
+ "type": "string"
3257
+ },
3258
+ "description": {
3259
+ "type": "string"
3260
+ },
3261
+ "tags": {
3262
+ "type": "array",
3263
+ "items": {
3264
+ "type": "string"
3265
+ }
3266
+ },
3267
+ "status": {
3268
+ "type": "string"
3269
+ },
3270
+ "properties": {
3271
+ "type": "object",
3272
+ "description": "Merged with existing properties"
3273
+ }
3274
+ },
3275
+ "required": [
3276
+ "id"
3277
+ ]
3278
+ }
3279
+ }
3280
+ },
3281
+ "required": [
3282
+ "product_id",
3283
+ "nodes"
3284
+ ]
3285
+ },
3286
+ "throws": [
3287
+ "textError when `nodes` is missing / non-array / empty / >50, or any\nitem is missing `id`, or any `id` does not resolve."
3288
+ ],
3289
+ "examples": [],
3290
+ "warnings": [
3291
+ "Properties merge with `||`: top-level keys overwrite while nested\nkeys stay shallow (deep-merge stays out of scope). To clear a property,\npass it as `null`. Items with no setClauses (every field undefined) are\nsilently skipped."
3292
+ ],
3293
+ "see": [
3294
+ "update_node",
3295
+ "batch_create_nodes",
3296
+ "migrate_type"
3297
+ ],
3298
+ "source": "src/tools/batch.ts:210",
3299
+ "symbol": "batchUpdateNodes",
3300
+ "returns": "JSON: `{ updated: [id], count }`.",
3301
+ "atomicity": "atomic-with-rollback (BEGIN / COMMIT / ROLLBACK)."
3302
+ },
3303
+ {
3304
+ "name": "validate_graph",
3305
+ "description": "Validate a product graph for schema drift. Detects entity type drift (unknown types), edge type drift (unknown edge types), and property drift (missing expected properties, sampled over 500 nodes). Dangling edge checks are enforced by Postgres FK constraints and not re-reported here.",
3306
+ "domain": "validation",
3307
+ "inputSchema": {
3308
+ "type": "object",
3309
+ "properties": {
3310
+ "product_id": {
3311
+ "type": "string",
3312
+ "description": "Product ID"
3313
+ },
3314
+ "dry_run": {
3315
+ "type": "boolean",
3316
+ "description": "Always true for validate_graph (validation is read-only)."
3317
+ }
3318
+ },
3319
+ "required": [
3320
+ "product_id"
3321
+ ]
3322
+ },
3323
+ "throws": [
3324
+ "textError when `product_id` is missing or the product\nis not visible to the caller."
3325
+ ],
3326
+ "examples": [],
3327
+ "warnings": [
3328
+ "*Property drift is sampled** (first 500 nodes by id order);\nfor products beyond 500 nodes the drift list is incomplete. Each\nreported type carries one example node id; run again or query\n`list_nodes` for full coverage."
3329
+ ],
3330
+ "see": [
3331
+ "migrate_type",
3332
+ "migrate_cross_edges",
3333
+ "rename_edge_type",
3334
+ "list_anti_patterns",
3335
+ "list_type_migrations",
3336
+ "list_edge_migrations",
3337
+ "inspect"
3338
+ ],
3339
+ "source": "src/tools/validation.ts:83",
3340
+ "symbol": "validateGraph",
3341
+ "returns": "JSON: `{ valid, product_id, summary, entity_type_drift,\nedge_type_drift, property_drift, notes }`.",
3342
+ "atomicity": "atomic (read-only)"
3343
+ },
3344
+ {
3345
+ "name": "migrate_cross_edges",
3346
+ "description": "Find edges in upg.edges that carry a cross-product edge type and move them to upg.cross_product_edges. Cross-product edge types belong in the cross-product table; this tool corrects data from before the tightening. Defaults to dry_run=true.",
3347
+ "domain": "migrations",
3348
+ "inputSchema": {
3349
+ "type": "object",
3350
+ "properties": {
3351
+ "product_id": {
3352
+ "type": "string",
3353
+ "description": "Product ID"
3354
+ },
3355
+ "dry_run": {
3356
+ "type": "boolean",
3357
+ "description": "Default true: report what would move without moving."
3358
+ }
3359
+ },
3360
+ "required": [
3361
+ "product_id"
3362
+ ]
3363
+ },
3364
+ "throws": [
3365
+ "textError when `product_id` is missing."
3366
+ ],
3367
+ "examples": [],
3368
+ "warnings": [
3369
+ "Default is `dry_run: true`; pass `dry_run: false` to commit.\nIdempotent on retry: a second `dry_run: false` finds zero matching\nintra-product rows and reports `count: 0`. Migrated rows get a fresh\n`ce_*` id while the original edge id falls away; the audit log retains\nthe trail."
3370
+ ],
3371
+ "see": [
3372
+ "list_cross_edge_types",
3373
+ "list_portfolio_cross_edges",
3374
+ "validate_graph",
3375
+ "migrate_type"
3376
+ ],
3377
+ "source": "src/tools/migrations.ts:194",
3378
+ "symbol": "migrateCrossEdges",
3379
+ "returns": "JSON: `{ product_id, migrated, count, dry_run }`.",
3380
+ "atomicity": "atomic-with-rollback (false only)"
3381
+ },
3382
+ {
3383
+ "name": "migrate_type",
3384
+ "description": "Bulk-retype all nodes of one entity type to another within a product. Catalog-aware: after renaming, re-infers edge types for all edges connected to the migrated nodes. Defaults to dry_run=true; pass dry_run=false to apply.",
3385
+ "domain": "migrations",
3386
+ "inputSchema": {
3387
+ "type": "object",
3388
+ "properties": {
3389
+ "product_id": {
3390
+ "type": "string",
3391
+ "description": "Product ID"
3392
+ },
3393
+ "from_type": {
3394
+ "type": "string",
3395
+ "description": "Current entity type to migrate away from"
3396
+ },
3397
+ "to_type": {
3398
+ "type": "string",
3399
+ "description": "New entity type. Must be a valid UPG entity type."
3400
+ },
3401
+ "dry_run": {
3402
+ "type": "boolean",
3403
+ "description": "Default true: count affected nodes without changing anything."
3404
+ }
3405
+ },
3406
+ "required": [
3407
+ "product_id",
3408
+ "from_type",
3409
+ "to_type"
3410
+ ]
3411
+ },
3412
+ "throws": [
3413
+ "textError when `product_id`, `from_type`, or `to_type`\nis missing, or when `to_type` is not a known UPG entity type."
3414
+ ],
3415
+ "examples": [],
3416
+ "warnings": [
3417
+ "Default is `dry_run: true`; pass `dry_run: false` to commit.\nIdempotent on retry: a second `dry_run: false` finds zero `from_type`\nnodes and reports `affected_nodes: 0`. Edge re-inference uses\n`resolveContainmentEdge`, so already-canonical edges may change type\nwhen the new pair has a different canonical edge."
3418
+ ],
3419
+ "see": [
3420
+ "validate_graph",
3421
+ "migrate_cross_edges",
3422
+ "rename_edge_type",
3423
+ "list_type_migrations",
3424
+ "list_entity_types"
3425
+ ],
3426
+ "source": "src/tools/migrations.ts:83",
3427
+ "symbol": "migrateType",
3428
+ "returns": "JSON: `{ from_type, to_type, affected_nodes, retyped_edges, dry_run }`.",
3429
+ "atomicity": "atomic-with-rollback (false only)"
3430
+ }
3431
+ ]
3432
+ }