@unified-product-graph/mcp-server 0.7.5 → 0.7.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  All notable changes to `@unified-product-graph/mcp-server` are documented in this file. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
4
4
 
5
+ ## [0.7.6] - 2026-05-30
6
+
7
+ ### Added
8
+
9
+ - **`start` tool (UPG-525):** a zero-state on-ramp. An empty or barely-started graph gives an agent nothing to plan against; `start` reads the live graph and recommends the first canonical playbook (from `UPG_PLAYBOOKS`) plus the exact `create_node` call for its anchor entity. Young graphs (< 8 non-product nodes) get the first canonical playbook whose anchor isn't present yet; established graphs are routed to `plan` / `inspect` / `get_graph_digest`. 94 tools total.
10
+
11
+ ### Changed
12
+
13
+ - **`create_node` orphan warning (UPG-525):** when a node lands with no parent and its type sits at position > 0 in its domain's creation sequence (the spec expects it under the domain anchor), the response carries a warning naming the typical parent and the canonical edge. Anchors, roots, and guide-less types never warn.
14
+ - **Tool reference return shapes are now authored-source-derived (UPG-556):** the generator parses each tool's `@returns` prose into a structured `return_shape` / `return_notes` at build time, shipped on the manifest. Fixed a `create_node` `@returns` wrap artifact at source.
15
+ - **Dissolved the singleton `Migrations` and `Skills Introspection` tool-reference sections (UPG-557):** `migrate_status` now groups under Nodes and `skill_audit` under Validation, without moving the source. Reference drops from 11 to 9 domains.
16
+
5
17
  ## [0.6.3] - 2026-05-27
6
18
 
7
19
  ### Changed
package/TOOLS.md CHANGED
@@ -1,20 +1,18 @@
1
1
  # UPG MCP Server: Tool Reference
2
2
 
3
- Reference for the 93 tools exposed by `@unified-product-graph/mcp-server`. Generated from JSDoc on `src/tools/*.ts` (do not edit by hand).
3
+ Reference for the 94 tools exposed by `@unified-product-graph/mcp-server`. Generated from JSDoc on `src/tools/*.ts` (do not edit by hand).
4
4
 
5
5
  ## Contents
6
6
 
7
- - [Context & Session](#context-session): 4 tools
8
- - [Nodes](#nodes): 14 tools
7
+ - [Context & Session](#context-session): 5 tools
8
+ - [Nodes](#nodes): 15 tools
9
9
  - [Edges](#edges): 9 tools
10
10
  - [Areas & Change Log](#areas-change-log): 5 tools
11
11
  - [Workspace & Portfolios](#workspace-portfolios): 10 tools
12
12
  - [Schema](#schema): 1 tool
13
13
  - [Spec Introspection](#spec-introspection): 43 tools
14
14
  - [Cloud Sync](#cloud-sync): 3 tools
15
- - [Validation](#validation): 2 tools
16
- - [Migrations](#migrations): 1 tool
17
- - [Skills Introspection](#skills-introspection): 1 tool
15
+ - [Validation](#validation): 3 tools
18
16
 
19
17
  ## Context & Session
20
18
 
@@ -23,6 +21,7 @@ _Product overview, graph digest, lens-aware session state._
23
21
  - [`get_graph_digest`](#get-graph-digest)
24
22
  - [`get_product_context`](#get-product-context)
25
23
  - [`get_session_context`](#get-session-context)
24
+ - [`start`](#start)
26
25
  - [`update_session_context`](#update-session-context)
27
26
 
28
27
  ### `get_graph_digest`
@@ -116,6 +115,37 @@ dedup rule from prose.
116
115
  **See also:** `update_session_context`
117
116
 
118
117
 
118
+ ### `start`
119
+
120
+ Zero-state on-ramp: "there is nothing here yet, where do I begin?". Reads the live graph and, for an empty or barely-started graph, recommends the first canonical playbook (from UPG_PLAYBOOKS) plus the exact create_node call for its anchor entity. Established graphs are routed to plan / inspect / get_graph_digest instead. Takes no arguments.
121
+
122
+ **Atomicity:** `atomic (read-only)`
123
+
124
+ _No arguments._
125
+
126
+ **Returns:**
127
+
128
+ JSON: `{ graph_state: "empty" | "young" | "established", product,
129
+ node_count, recommended_playbook?, first_action?, recommendation,
130
+ next_tools }`. `recommended_playbook` and `first_action` are present only
131
+ for empty/young graphs.
132
+
133
+ **Examples:**
134
+
135
+ // Input (empty graph):
136
+ {}
137
+ // Output (truncated):
138
+ {
139
+ "graph_state": "empty",
140
+ "node_count": 0,
141
+ "recommended_playbook": { "id": "playbook:users-needs", "name": "Users & Needs", "target_anchor_entity": "persona" },
142
+ "first_action": { "tool": "create_node", "args": { "type": "persona", "title": "<your first persona>" } },
143
+ "recommendation": "Your graph is empty. Begin with the \"Users & Needs\" playbook: create your first persona."
144
+ }
145
+
146
+ **See also:** `plan`, `get_playbook`, `list_playbooks`, `get_graph_digest`
147
+
148
+
119
149
  ### `update_session_context`
120
150
 
121
151
  Update session context: register a skill invocation, record a recommendation, set focus area, switch lens, or store custom state for cross-skill coordination.
@@ -145,7 +175,7 @@ new state.
145
175
 
146
176
  ## Nodes
147
177
 
148
- _Read, search, traverse, mutate, batch, migrate, dedupe._
178
+ _Read, search, traverse, mutate, batch, migrate type/properties/status, dedupe._
149
179
 
150
180
  - [`batch_create_nodes`](#batch-create-nodes)
151
181
  - [`batch_delete_nodes`](#batch-delete-nodes)
@@ -157,6 +187,7 @@ _Read, search, traverse, mutate, batch, migrate, dedupe._
157
187
  - [`get_nodes`](#get-nodes)
158
188
  - [`list_nodes`](#list-nodes)
159
189
  - [`migrate_properties`](#migrate-properties)
190
+ - [`migrate_status`](#migrate-status)
160
191
  - [`migrate_type`](#migrate-type)
161
192
  - [`query`](#query)
162
193
  - [`search_nodes`](#search-nodes)
@@ -262,8 +293,8 @@ Create one entity, optionally with a parent edge. For 3+ entities, use `batch_cr
262
293
  JSON: `{ node, edge?, unknown_properties?, warning? }`. The `edge`
263
294
  field is present only when `parent_id` was supplied and a canonical
264
295
  hierarchy edge could be inferred. `unknown_properties` and `warning` are
265
- present when the caller passed properties not in the entity's schema
266
- . Pass `strict: true` to reject unknown properties instead of
296
+ present when the caller passed properties not in the entity's schema.
297
+ Pass `strict: true` to reject unknown properties instead of
267
298
  warning. For portfolio-scoped types the response shape is
268
299
  `{ node, portfolio_file, written_to, warning? }` where `node` is the
269
300
  persisted typed record.
@@ -466,6 +497,40 @@ changes (idempotent on the canonical-properties shape).
466
497
  **See also:** `migrate_type`, `validate_graph`, `list_type_migrations`
467
498
 
468
499
 
500
+ ### `migrate_status`
501
+
502
+ Apply `UPG_STATUS_MIGRATIONS` graph-wide: rewrite legacy lifecycle status values to canonical phase ids. Auto-mode (no filters) selects nodes whose current status is invalid against the entity type's lifecycle and has a registered replacement (the same invariant that drives `validate_graph` lifecycle_drift). Surgical mode (`from_status` + `to_status`) overrides the registry and rewrites every (entity_type?, from_status) match. Nodes with invalid statuses but no registered replacement surface under `skipped_no_migration`. Default `dry_run=true`; pass `dry_run=false` to commit.
503
+
504
+ **Atomicity:** `per-node. Status writes go through `store.updateNode`
505
+ one at a time. Dry-run is read-only.`
506
+
507
+ **Arguments:**
508
+
509
+ | Name | Type | Required | Description |
510
+ | ---- | ---- | -------- | ----------- |
511
+ | `dry_run` | boolean | | Preview changes without applying (default true). Pass false to commit. |
512
+ | `entity_type` | string | | Optional. Restrict the rewrite to nodes of this canonical entity type (e.g. "service", "feature"). |
513
+ | `from_status` | string | | Optional. Restrict the rewrite to nodes whose current status equals this exact value. When provided, `to_status` is required and the registry is bypassed. |
514
+ | `to_status` | string | | Required when `from_status` is provided. The canonical phase id to write. |
515
+
516
+ **Returns:**
517
+
518
+ JSON: `MigrateStatusResult`.
519
+
520
+ **Throws:**
521
+
522
+ - Returns a textError when `from_status` is provided without
523
+ `to_status`, or when `entity_type` is provided but isn't a string.
524
+
525
+ **Warnings (non-error surfaces):**
526
+
527
+ - Default is `dry_run: true`. Pass `dry_run: false` to commit.
528
+ Idempotent on retry; re-running after a successful commit reports
529
+ zero changes (canonical statuses pass the validity check).
530
+
531
+ **See also:** `migrate_type`, `migrate_properties`, `validate_graph`, `list_lifecycles`
532
+
533
+
469
534
  ### `migrate_type`
470
535
 
471
536
  Migrate every entity of one type to another, applying defaults from `UPG_MIGRATIONS`. Three passes commit as one write: (1) node rename, (2) edges through `UPG_EDGE_MIGRATIONS` (catalog-aware renames, direction flips, drops; endpoint guards check post-migration types; uncatalogued edges surface as `unmapped_legacy_edges`), (3) every node through `UPG_PROPERTY_MIGRATIONS` (top-level renames, lifts, drops, self-referential cleanup). Type-specific property rules see the post-rename type.
@@ -2388,9 +2453,10 @@ target an existing one.
2388
2453
 
2389
2454
  ## Validation
2390
2455
 
2391
- _Schema-drift detection and full per-node drift reports._
2456
+ _Schema-drift detection, full per-node drift reports, and source-vs-deployed integrity audits of UPG `/upg-*` skills._
2392
2457
 
2393
2458
  - [`get_anti_pattern_violations_for`](#get-anti-pattern-violations-for)
2459
+ - [`skill_audit`](#skill-audit)
2394
2460
  - [`validate_graph`](#validate-graph)
2395
2461
 
2396
2462
  ### `get_anti_pattern_violations_for`
@@ -2442,6 +2508,23 @@ per-id matching once `target_entities` carries ids.
2442
2508
  **See also:** `validate_graph`, `list_anti_patterns`, `get_anti_pattern`, `inspect`
2443
2509
 
2444
2510
 
2511
+ ### `skill_audit`
2512
+
2513
+ Audit one or every UPG skill for source-vs-deployed integrity. Use before recommending a skill to confirm `.claude/skills/<name>/SKILL.md` is a symlink to canonical source and the bodies match. When `in_sync: false`, what you read from `packages/upg-mcp-server/skills/` is NOT what the user will experience.
2514
+
2515
+ **Atomicity:** `atomic (read-only filesystem stat + read)`
2516
+
2517
+ **Arguments:**
2518
+
2519
+ | Name | Type | Required | Description |
2520
+ | ---- | ---- | -------- | ----------- |
2521
+ | `name` | string | | Optional skill name (e.g. "upg-trace"). If omitted, audits every canonical skill. |
2522
+
2523
+ **Returns:**
2524
+
2525
+ `{ skills: SkillAuditRecord[] }`
2526
+
2527
+
2445
2528
  ### `validate_graph`
2446
2529
 
2447
2530
  Walk the loaded graph and return a per-class, per-node report of schema drift plus anti-pattern violations from `UPG_ANTI_PATTERNS`. Schema-drift classes: non-canonical entity types, non-canonical edge types, top-level fields outside `UPGBaseNode`, invalid status values, self-referential `source_id`/`source_type`, properties matching `UPG_PROPERTY_MIGRATIONS` rules. Anti-patterns: catalog entries that fired against the live graph, sorted high → medium → low. Each entry carries `suggested_migration` (drift) or `remediation` (anti-pattern). Top-level `valid` is true iff drift is empty AND no violations fired. Read-only; pairs with `migrate_type`, `rename_edge_type`, `get_anti_pattern_violations_for`.
@@ -2534,66 +2617,3 @@ pure spec-shape check; `skip_drift: true` for catalog-only.
2534
2617
 
2535
2618
  **See also:** `migrate_type`, `migrate_properties`, `rename_edge_type`, `get_anti_pattern_violations_for`, `list_anti_patterns`, `list_type_migrations`, `list_edge_migrations`, `inspect`
2536
2619
 
2537
-
2538
- ## Migrations
2539
-
2540
- _Status value migration across the graph._
2541
-
2542
- - [`migrate_status`](#migrate-status)
2543
-
2544
- ### `migrate_status`
2545
-
2546
- Apply `UPG_STATUS_MIGRATIONS` graph-wide: rewrite legacy lifecycle status values to canonical phase ids. Auto-mode (no filters) selects nodes whose current status is invalid against the entity type's lifecycle and has a registered replacement (the same invariant that drives `validate_graph` lifecycle_drift). Surgical mode (`from_status` + `to_status`) overrides the registry and rewrites every (entity_type?, from_status) match. Nodes with invalid statuses but no registered replacement surface under `skipped_no_migration`. Default `dry_run=true`; pass `dry_run=false` to commit.
2547
-
2548
- **Atomicity:** `per-node. Status writes go through `store.updateNode`
2549
- one at a time. Dry-run is read-only.`
2550
-
2551
- **Arguments:**
2552
-
2553
- | Name | Type | Required | Description |
2554
- | ---- | ---- | -------- | ----------- |
2555
- | `dry_run` | boolean | | Preview changes without applying (default true). Pass false to commit. |
2556
- | `entity_type` | string | | Optional. Restrict the rewrite to nodes of this canonical entity type (e.g. "service", "feature"). |
2557
- | `from_status` | string | | Optional. Restrict the rewrite to nodes whose current status equals this exact value. When provided, `to_status` is required and the registry is bypassed. |
2558
- | `to_status` | string | | Required when `from_status` is provided. The canonical phase id to write. |
2559
-
2560
- **Returns:**
2561
-
2562
- JSON: `MigrateStatusResult`.
2563
-
2564
- **Throws:**
2565
-
2566
- - Returns a textError when `from_status` is provided without
2567
- `to_status`, or when `entity_type` is provided but isn't a string.
2568
-
2569
- **Warnings (non-error surfaces):**
2570
-
2571
- - Default is `dry_run: true`. Pass `dry_run: false` to commit.
2572
- Idempotent on retry; re-running after a successful commit reports
2573
- zero changes (canonical statuses pass the validity check).
2574
-
2575
- **See also:** `migrate_type`, `migrate_properties`, `validate_graph`, `list_lifecycles`
2576
-
2577
-
2578
- ## Skills Introspection
2579
-
2580
- _Verify source-vs-deployed integrity of UPG `/upg-*` skills before recommending them._
2581
-
2582
- - [`skill_audit`](#skill-audit)
2583
-
2584
- ### `skill_audit`
2585
-
2586
- Audit one or every UPG skill for source-vs-deployed integrity. Use before recommending a skill to confirm `.claude/skills/<name>/SKILL.md` is a symlink to canonical source and the bodies match. When `in_sync: false`, what you read from `packages/upg-mcp-server/skills/` is NOT what the user will experience.
2587
-
2588
- **Atomicity:** `atomic (read-only filesystem stat + read)`
2589
-
2590
- **Arguments:**
2591
-
2592
- | Name | Type | Required | Description |
2593
- | ---- | ---- | -------- | ----------- |
2594
- | `name` | string | | Optional skill name (e.g. "upg-trace"). If omitted, audits every canonical skill. |
2595
-
2596
- **Returns:**
2597
-
2598
- `{ skills: SkillAuditRecord[] }`
2599
-
package/dist/index.js CHANGED
@@ -23011,6 +23011,62 @@ var getGraphDigest = (args, ctx) => {
23011
23011
  }
23012
23012
  return text(JSON.stringify({ ...digest, lens: sessionContext.lens, lens_digest: lensDigest, _hash: currentHash }, null, 2));
23013
23013
  };
23014
+ var YOUNG_GRAPH_THRESHOLD = 8;
23015
+ var start = (_args, ctx) => {
23016
+ const { store } = ctx;
23017
+ const allNodes = store.getAllNodes();
23018
+ const realNodes = allNodes.filter((n) => n.type !== "product");
23019
+ const nodeCount = realNodes.length;
23020
+ const product = store.getProduct();
23021
+ const presentTypes = new Set(allNodes.map((n) => n.type));
23022
+ const productInfo = product ? { title: product.title, stage: product.stage ?? null } : null;
23023
+ const state = nodeCount === 0 ? "empty" : nodeCount < YOUNG_GRAPH_THRESHOLD ? "young" : "established";
23024
+ if (state === "established") {
23025
+ return text(
23026
+ JSON.stringify(
23027
+ {
23028
+ graph_state: state,
23029
+ product: productInfo,
23030
+ node_count: nodeCount,
23031
+ recommendation: `Graph is established (${nodeCount} entities). Use plan for the missing-entities backlog, inspect for issues, and get_graph_digest for health.`,
23032
+ next_tools: ["plan", "inspect", "get_graph_digest"]
23033
+ },
23034
+ null,
23035
+ 2
23036
+ )
23037
+ );
23038
+ }
23039
+ const canonicalPlaybooks = UPG_PLAYBOOKS.filter((p) => p.is_canonical === true);
23040
+ const recommend = canonicalPlaybooks.find(
23041
+ (p) => p.target_anchor_entity ? !presentTypes.has(p.target_anchor_entity) : true
23042
+ ) ?? canonicalPlaybooks[0];
23043
+ const response = {
23044
+ graph_state: state,
23045
+ product: productInfo,
23046
+ node_count: nodeCount
23047
+ };
23048
+ if (recommend) {
23049
+ const anchor = recommend.target_anchor_entity;
23050
+ response.recommended_playbook = {
23051
+ id: recommend.id,
23052
+ name: recommend.name,
23053
+ region: recommend.region,
23054
+ target_anchor_entity: anchor ?? null,
23055
+ description: recommend.description
23056
+ };
23057
+ if (anchor) {
23058
+ response.first_action = {
23059
+ tool: "create_node",
23060
+ args: { type: anchor, title: `<your first ${anchor}>` }
23061
+ };
23062
+ }
23063
+ response.recommendation = state === "empty" ? `Your graph is empty. Begin with the "${recommend.name}" playbook${anchor ? `: create your first ${anchor}` : ""}. Open the full sequence with get_playbook("${recommend.id}").` : `You have ${nodeCount} ${nodeCount === 1 ? "entity" : "entities"}. Continue with the "${recommend.name}" playbook${anchor ? `: add a ${anchor}` : ""}. Use plan for the full missing-entities backlog.`;
23064
+ } else {
23065
+ response.recommendation = "No canonical playbooks available. Use plan to see the missing-entities backlog.";
23066
+ }
23067
+ response.next_tools = state === "empty" ? ["get_playbook", "create_node", "list_playbooks"] : ["plan", "get_playbook", "get_graph_digest"];
23068
+ return text(JSON.stringify(response, null, 2));
23069
+ };
23014
23070
  var getSessionContext = (_args, ctx) => {
23015
23071
  const { sessionContext } = ctx;
23016
23072
  const recommendationsToAvoid = Array.from(
@@ -23738,6 +23794,21 @@ function buildFirstUseHints(canonicalType) {
23738
23794
  if (edgesOut.length > 0) hints.canonical_edges_out = edgesOut;
23739
23795
  return hints;
23740
23796
  }
23797
+ function buildOrphanWarning(canonicalType) {
23798
+ let schema;
23799
+ try {
23800
+ schema = buildEntitySchema(canonicalType);
23801
+ } catch {
23802
+ return void 0;
23803
+ }
23804
+ const guide = schema.domain_guide;
23805
+ if (!guide || guide.position_in_sequence <= 0) return void 0;
23806
+ const anchor = guide.anchor_entity;
23807
+ if (!anchor || anchor === canonicalType) return void 0;
23808
+ const edge = resolveContainmentEdge(anchor, canonicalType);
23809
+ const via = edge ? ` (canonical edge: ${edge})` : "";
23810
+ return `Orphan: created ${canonicalType} with no parent. ${canonicalType} typically attaches under ${anchor}${via}. Pass parent_id on create, or wire it later with create_edge.`;
23811
+ }
23741
23812
  var createNode = async (args, ctx) => {
23742
23813
  const { store } = ctx;
23743
23814
  if (!args.type) return textError(`Missing required parameter: type`);
@@ -23804,6 +23875,10 @@ var createNode = async (args, ctx) => {
23804
23875
  const aggregatedWarnings = [];
23805
23876
  if (warning) aggregatedWarnings.push(warning);
23806
23877
  if (lengthWarnings.length > 0) aggregatedWarnings.push(...lengthWarnings);
23878
+ if (!args.parent_id && !result.edge) {
23879
+ const orphanWarning = buildOrphanWarning(result.node.type);
23880
+ if (orphanWarning) aggregatedWarnings.push(orphanWarning);
23881
+ }
23807
23882
  const libWarning = result.warning;
23808
23883
  const combinedWarning = libWarning ? aggregatedWarnings.length > 0 ? `${libWarning} | ${aggregatedWarnings.join(" | ")}` : libWarning : aggregatedWarnings.length > 0 ? aggregatedWarnings.join(" | ") : void 0;
23809
23884
  if (unknown_properties.length > 0 || combinedWarning || hints) {
@@ -26753,6 +26828,14 @@ var TOOL_DEFINITIONS = [
26753
26828
  }
26754
26829
  }
26755
26830
  },
26831
+ {
26832
+ name: "start",
26833
+ description: 'Zero-state on-ramp: "there is nothing here yet, where do I begin?". Reads the live graph and, for an empty or barely-started graph, recommends the first canonical playbook (from UPG_PLAYBOOKS) plus the exact create_node call for its anchor entity. Established graphs are routed to plan / inspect / get_graph_digest instead. Takes no arguments.',
26834
+ inputSchema: {
26835
+ type: "object",
26836
+ properties: {}
26837
+ }
26838
+ },
26756
26839
  {
26757
26840
  name: "list_nodes",
26758
26841
  description: "List entities with filtering, edge inclusion, count-only mode, and pagination. For graph-wide edge enumeration, prefer `export_edges` (flat) or `query` (traversal). `list_nodes(include_edges:true)` is for entity-scoped reads.",
@@ -28052,6 +28135,7 @@ var TOOL_DEFINITIONS = [
28052
28135
  var HANDLERS = {
28053
28136
  get_product_context: getProductContext,
28054
28137
  get_graph_digest: getGraphDigest,
28138
+ start,
28055
28139
  list_nodes: listNodes,
28056
28140
  get_node: getNode,
28057
28141
  get_nodes: getNodes,