@unified-product-graph/cloud-server 0.9.5 → 0.9.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/id-helpers.ts","../src/lib/audit.ts","../src/store/pg-store.ts","../src/lib/webhook-dispatcher.ts","../src/server.ts","../../upg-mcp-tooling/src/result.ts","../../upg-mcp-tooling/src/catalog.ts","../src/tools/products.ts","../src/tools/context.ts","../src/tools/nodes.ts","../src/tools/edges.ts","../src/tools/frameworks.ts","../src/tools/areas.ts","../src/tools/schema.ts","../src/tools/collaboration.ts","../src/tools/analytics.ts","../src/tools/webhooks.ts","../src/tools/spec.ts","../src/tools/portfolio.ts","../src/tools/batch.ts","../src/tools/validation.ts","../src/tools/migrations.ts","../src/lib/tool-registry.ts"],"sourcesContent":["/**\n * UPG Cloud MCP Server\n *\n * Usage: upg-cloud-server\n * --database-url postgres://user:pass@host:5432/db (or UPG_DATABASE_URL env)\n *\n * A Postgres-backed MCP server for the Unified Product Graph.\n * Self-hostable, open source.\n */\n\nimport { parseArgs } from 'node:util'\nimport { Pool } from 'pg'\nimport { PgStore } from './store/pg-store.js'\nimport { WebhookDispatcher } from './lib/webhook-dispatcher.js'\nimport { createServer } from './server.js'\n\nasync function main() {\n const { values } = parseArgs({\n options: {\n 'database-url': { type: 'string', short: 'd' },\n },\n })\n\n const databaseUrl = values['database-url'] || process.env.UPG_DATABASE_URL\n if (!databaseUrl) {\n process.stderr.write(\n 'Usage: upg-cloud-server --database-url postgres://...\\n' +\n ' Or set UPG_DATABASE_URL environment variable\\n'\n )\n process.exit(1)\n }\n\n const pool = new Pool({ connectionString: databaseUrl })\n\n // Test connection\n try {\n await pool.query('SELECT 1')\n } catch (err) {\n process.stderr.write(`Database connection failed: ${(err as Error).message}\\n`)\n process.exit(1)\n }\n\n const store = new PgStore(pool)\n // Wire webhook delivery (UPG-553): mutations emit events post-commit; the\n // dispatcher fans them out to registered webhooks (fire-and-forget).\n const dispatcher = new WebhookDispatcher(pool)\n store.setEventSink((event) => dispatcher.emit(event))\n const server = createServer(store)\n await server.start()\n\n process.stderr.write('UPG Cloud MCP server running\\n')\n\n const shutdown = async () => {\n await pool.end()\n process.exit(0)\n }\n process.on('SIGINT', shutdown)\n process.on('SIGTERM', shutdown)\n}\n\nmain().catch((err) => {\n process.stderr.write(`Fatal: ${err}\\n`)\n process.exit(1)\n})\n","/**\n * Cloud-side id minters.\n *\n * The cloud schema (`migrations/001_initial.sql`) declares `id` / `source` /\n * `target` as Postgres `UUID` columns, so cloud mints native UUIDs. This is\n * deliberately NOT the file-SDK's `nodeId`/`edgeId` (which produce the\n * human-readable `n_…` / `e_…` ids used inside `.upg` files): ids are a\n * storage-format concern, not shared graph semantics. The shared logic cloud\n * derives from `@unified-product-graph/sdk/logic` is edge inference and the\n * validators, not id format.\n *\n * Before this, cloud reused the `n_…` / `e_…` generators, which Postgres\n * rejected with \"invalid input syntax for type uuid\", a bug masked by the\n * mocked-pool unit tests and only surfaced by running the server for real.\n */\nimport { randomUUID } from 'node:crypto'\n\n/** Generate a node id (Postgres UUID). */\nexport function nodeId(): string {\n return randomUUID()\n}\n\n/** Generate an edge id (Postgres UUID). */\nexport function edgeId(): string {\n return randomUUID()\n}\n","/**\n * Audit-log writer for the cloud server.\n *\n * `upg.audit_log` is the canonical history of mutations, read back by\n * `get_audit_log` and `get_changes`. Every mutation must record a row here, or\n * those tools return empty forever (the bug fixed in UPG-552).\n *\n * `appendAudit` MUST be called with a **transaction-scoped client**, in the\n * same transaction as the mutation it records, so the audit row commits or\n * rolls back atomically with the write; important for the batch tools'\n * all-or-nothing semantics.\n *\n * `userId` is `null` on the stdio path until request-level auth context is\n * plumbed (see UPG-551, Tier-3 enforcement).\n */\nimport type { PoolClient } from 'pg'\n\nexport type AuditAction = 'create' | 'update' | 'delete'\nexport type AuditEntityType = 'node' | 'edge' | 'product'\n\nexport interface AuditEntry {\n productId: string\n action: AuditAction\n entityType: AuditEntityType\n entityId: string\n /** Optional JSON payload (e.g. the created entity, the patch, or merge info). */\n changes?: unknown\n /** Actor; null on the unauthenticated stdio path. */\n userId?: string | null\n}\n\nexport async function appendAudit(client: PoolClient, entry: AuditEntry): Promise<void> {\n await client.query(\n `INSERT INTO upg.audit_log (product_id, user_id, action, entity_type, entity_id, changes)\n VALUES ($1, $2, $3, $4, $5, $6)`,\n [\n entry.productId,\n entry.userId ?? null,\n entry.action,\n entry.entityType,\n entry.entityId,\n entry.changes !== undefined && entry.changes !== null ? JSON.stringify(entry.changes) : null,\n ],\n )\n}\n","import type { Pool, PoolClient } from 'pg'\nimport type { UPGBaseNode, UPGEdge } from '@unified-product-graph/core'\nimport { nodeId, edgeId } from '../id-helpers.js'\nimport { appendAudit } from '../lib/audit.js'\nimport type { WebhookEvent } from '../lib/webhook-dispatcher.js'\n\n// ─── Local types ─────────────────────────────────────────────────────────────\n\nexport interface Product {\n id: string\n title: string\n description?: string\n stage?: string\n}\n\nexport interface GraphSummary {\n product: { id: string; title: string; stage?: string }\n node_count: number\n edge_count: number\n nodes_by_type: Record<string, number>\n edges_by_type: Record<string, number>\n orphan_count: number\n}\n\nexport interface AuditEntry {\n id: string\n user_id: string\n action: string\n entity_type: string\n entity_id: string\n changes: unknown\n created_at: string\n}\n\nexport interface Comment {\n id: string\n node_id: string\n user_id: string\n body: string\n created_at: string\n}\n\nexport interface AccessRecord {\n id: string\n user_id: string\n role: string\n created_at: string\n}\n\nexport interface GraphAnalytics {\n hypothesis_velocity: { untested: number; testing: number; validated: number; invalidated: number }\n coverage_ratio: number // 0-100\n evidence_density: number // ratio\n stale_entity_rate: number // 0-100\n orphan_rate: number // 0-100\n}\n\nexport interface CrossProductEdge {\n id: string\n source: string\n target: string\n type: string\n created_by_product_id: string\n created_at: string\n}\n\nexport interface Webhook {\n id: string\n event: string\n url: string\n active: boolean\n created_at: string\n}\n\n// ─── Postgres-backed UPG Store ───────────────────────────────────────────────\n\nexport { UPGPgStore as PgStore }\n\nexport class UPGPgStore {\n constructor(private pool: Pool) {}\n\n // ── Event sink (webhook delivery, UPG-553) ──────────────────────────────────\n // Set by the server entry point. Mutations call `emit` AFTER they commit, so\n // events only fire for durable writes. Default is a no-op, so the store works\n // standalone (and in tests) with no dispatcher wired.\n private eventSink: ((event: WebhookEvent) => void) | null = null\n\n setEventSink(sink: (event: WebhookEvent) => void): void {\n this.eventSink = sink\n }\n\n /** Emit a post-commit event to the sink, if one is set. Public so the batch\n * tools (which run their own transactions) can emit too. */\n emit(productId: string, event: string, payload: Record<string, unknown>): void {\n this.eventSink?.({ productId, event, payload })\n }\n\n // ── Products ───────────────────────────────────────────────────────────────\n\n async listProducts(): Promise<Product[]> {\n const { rows } = await this.pool.query<Product>(\n `SELECT id, title, description, stage FROM upg.products ORDER BY title`,\n )\n return rows\n }\n\n async getProduct(productId: string): Promise<Product> {\n const { rows } = await this.pool.query<Product>(\n `SELECT id, title, description, stage FROM upg.products WHERE id = $1`,\n [productId],\n )\n if (rows.length === 0) throw new Error(`Product not found: ${productId}`)\n return rows[0]\n }\n\n async createProduct(\n title: string,\n description?: string,\n stage?: string,\n ): Promise<Product> {\n const id = nodeId()\n const client = await this.pool.connect()\n try {\n await client.query('BEGIN')\n const { rows } = await client.query<Product>(\n `INSERT INTO upg.products (id, title, description, stage)\n VALUES ($1, $2, $3, $4)\n RETURNING id, title, description, stage`,\n [id, title, description ?? null, stage ?? null],\n )\n await appendAudit(client, {\n productId: id,\n action: 'create',\n entityType: 'product',\n entityId: id,\n changes: { title, description: description ?? null, stage: stage ?? null },\n })\n await client.query('COMMIT')\n this.emit(id, 'product.created', { id, title })\n return rows[0]\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n } finally {\n client.release()\n }\n }\n\n // ── Nodes ──────────────────────────────────────────────────────────────────\n\n private static NODE_COLS = 'id, product_id, type, title, description, status, tags, data'\n\n async getNode(id: string): Promise<(UPGBaseNode & { product_id: string }) | undefined> {\n const { rows } = await this.pool.query(\n `SELECT ${UPGPgStore.NODE_COLS} FROM upg.nodes WHERE id = $1`,\n [id],\n )\n if (rows.length === 0) return undefined\n return rowToNode(rows[0])\n }\n\n async getAllNodes(productId: string): Promise<UPGBaseNode[]> {\n const { rows } = await this.pool.query(\n `SELECT ${UPGPgStore.NODE_COLS} FROM upg.nodes\n WHERE product_id = $1 ORDER BY title`,\n [productId],\n )\n return rows.map(rowToNode)\n }\n\n async addNode(productId: string, node: UPGBaseNode): Promise<UPGBaseNode> {\n const id = node.id || nodeId()\n const client = await this.pool.connect()\n try {\n await client.query('BEGIN')\n const { rows } = await client.query(\n `INSERT INTO upg.nodes (id, product_id, type, title, description, status, tags, data)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8)\n RETURNING ${UPGPgStore.NODE_COLS}`,\n [\n id,\n productId,\n node.type,\n node.title,\n node.description ?? null,\n node.status ?? null,\n node.tags ?? null,\n node.properties ? JSON.stringify(node.properties) : null,\n ],\n )\n await appendAudit(client, {\n productId,\n action: 'create',\n entityType: 'node',\n entityId: id,\n changes: { type: node.type, title: node.title },\n })\n await client.query('COMMIT')\n this.emit(productId, 'node.created', { id, type: node.type, title: node.title })\n return rowToNode(rows[0])\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n } finally {\n client.release()\n }\n }\n\n async updateNode(id: string, patch: Partial<UPGBaseNode>): Promise<UPGBaseNode> {\n const setClauses: string[] = []\n const values: unknown[] = []\n let p = 1\n\n if (patch.title !== undefined) { setClauses.push(`title = $${p++}`); values.push(patch.title) }\n if (patch.description !== undefined) { setClauses.push(`description = $${p++}`); values.push(patch.description) }\n if (patch.status !== undefined) { setClauses.push(`status = $${p++}`); values.push(patch.status) }\n if (patch.tags !== undefined) { setClauses.push(`tags = $${p++}`); values.push(patch.tags) }\n if (patch.properties !== undefined) {\n setClauses.push(`data = COALESCE(data, '{}'::jsonb) || $${p++}::jsonb`)\n values.push(JSON.stringify(patch.properties))\n }\n\n if (setClauses.length === 0) {\n const existing = await this.getNode(id)\n if (!existing) throw new Error(`Node not found: ${id}`)\n return existing\n }\n\n values.push(id)\n const client = await this.pool.connect()\n try {\n await client.query('BEGIN')\n const { rows } = await client.query(\n `UPDATE upg.nodes SET ${setClauses.join(', ')}\n WHERE id = $${p}\n RETURNING ${UPGPgStore.NODE_COLS}`,\n values,\n )\n if (rows.length === 0) {\n await client.query('ROLLBACK')\n throw new Error(`Node not found: ${id}`)\n }\n await appendAudit(client, {\n productId: rows[0].product_id,\n action: 'update',\n entityType: 'node',\n entityId: id,\n changes: patch,\n })\n await client.query('COMMIT')\n this.emit(rows[0].product_id, 'node.updated', { id, ...patch } as Record<string, unknown>)\n return rowToNode(rows[0])\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n } finally {\n client.release()\n }\n }\n\n async removeNode(id: string): Promise<{ node: UPGBaseNode; removedEdgeIds: string[] }> {\n const client = await this.pool.connect()\n try {\n await client.query('BEGIN')\n\n const { rows: nodeRows } = await client.query(\n `SELECT ${UPGPgStore.NODE_COLS} FROM upg.nodes WHERE id = $1`,\n [id],\n )\n if (nodeRows.length === 0) {\n await client.query('ROLLBACK')\n throw new Error(`Node not found: ${id}`)\n }\n\n const { rows: edgeRows } = await client.query<{ id: string }>(\n `DELETE FROM upg.edges WHERE source = $1 OR target = $1 RETURNING id`,\n [id],\n )\n\n await client.query(`DELETE FROM upg.nodes WHERE id = $1`, [id])\n await appendAudit(client, {\n productId: nodeRows[0].product_id,\n action: 'delete',\n entityType: 'node',\n entityId: id,\n changes: { removed_edge_ids: edgeRows.map((r) => r.id) },\n })\n await client.query('COMMIT')\n\n this.emit(nodeRows[0].product_id, 'node.deleted', { id })\n return {\n node: rowToNode(nodeRows[0]),\n removedEdgeIds: edgeRows.map((r) => r.id),\n }\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n } finally {\n client.release()\n }\n }\n\n /**\n * Atomically reparent a node. Deletes any existing containment edge targeting\n * `nodeId`, then inserts a new edge from `newParentId` to `nodeId`.\n *\n * @returns `{ node_id, old_parent_id, new_parent_id, edge_created }`.\n * `old_parent_id` is `null` when no prior containment edge existed.\n */\n async moveNode(\n productId: string,\n nodeId: string,\n newParentId: string,\n newEdgeType: string,\n newEdgeIdParam: string,\n ): Promise<{\n node_id: string\n old_parent_id: string | null\n new_parent_id: string\n edge_created: { id: string; source: string; target: string; type: string }\n }> {\n const client = await this.pool.connect()\n try {\n await client.query('BEGIN')\n\n // Delete any containment edge where this node is the target\n const { rows: deletedEdges } = await client.query<{ id: string; source: string }>(\n `DELETE FROM upg.edges\n WHERE product_id = $1 AND target = $2\n RETURNING id, source`,\n [productId, nodeId],\n )\n const oldParentId = deletedEdges.length > 0 ? deletedEdges[0].source : null\n\n // Insert new containment edge\n await client.query(\n `INSERT INTO upg.edges (id, product_id, source, target, type)\n VALUES ($1, $2, $3, $4, $5)`,\n [newEdgeIdParam, productId, newParentId, nodeId, newEdgeType],\n )\n\n await appendAudit(client, {\n productId,\n action: 'update',\n entityType: 'node',\n entityId: nodeId,\n changes: { old_parent_id: oldParentId, new_parent_id: newParentId, edge_type: newEdgeType },\n })\n\n await client.query('COMMIT')\n\n this.emit(productId, 'node.updated', { id: nodeId, new_parent_id: newParentId })\n return {\n node_id: nodeId,\n old_parent_id: oldParentId,\n new_parent_id: newParentId,\n edge_created: { id: newEdgeIdParam, source: newParentId, target: nodeId, type: newEdgeType },\n }\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n } finally {\n client.release()\n }\n }\n\n /**\n * Merge duplicate nodes into a canonical node inside an all-or-nothing\n * Postgres transaction.\n *\n * Steps (in order, inside a single transaction):\n * 1. Rebind all edges from each duplicate to the canonical.\n * 2. Delete self-loops (source = canonical AND target = canonical).\n * 3. Remove duplicate edges (same source, target, type), keeping one.\n * 4. Shallow-merge duplicate properties into canonical (canonical wins).\n * 5. Delete the duplicate nodes.\n *\n * @returns Counts of rebound edges, removed self-loops, removed duplicate\n * edges, plus the canonical_id and list of merged ids.\n */\n async deduplicateNodes(\n productId: string,\n canonicalId: string,\n duplicateIds: string[],\n ): Promise<{\n canonical_id: string\n merged_ids: string[]\n rebound_edges: number\n removed_self_loops: number\n removed_duplicate_edges: number\n }> {\n const client = await this.pool.connect()\n try {\n await client.query('BEGIN')\n\n let reboundEdges = 0\n\n // Step 1: Rebind edges from each duplicate to canonical\n for (const dupId of duplicateIds) {\n const { rowCount: srcCount } = await client.query(\n `UPDATE upg.edges SET source = $1 WHERE source = $2 AND product_id = $3`,\n [canonicalId, dupId, productId],\n )\n const { rowCount: tgtCount } = await client.query(\n `UPDATE upg.edges SET target = $1 WHERE target = $2 AND product_id = $3`,\n [canonicalId, dupId, productId],\n )\n reboundEdges += (srcCount ?? 0) + (tgtCount ?? 0)\n }\n\n // Step 2: Delete self-loops created by rebinding\n const { rowCount: selfLoopCount } = await client.query(\n `DELETE FROM upg.edges\n WHERE product_id = $1 AND source = $2 AND target = $2`,\n [productId, canonicalId],\n )\n\n // Step 3: Remove duplicate edges (same source, target, type), keeping one\n const { rowCount: dupEdgeCount } = await client.query(\n `DELETE FROM upg.edges\n WHERE id IN (\n SELECT id FROM (\n SELECT id, ROW_NUMBER() OVER (\n PARTITION BY source, target, type ORDER BY id\n ) AS rn\n FROM upg.edges WHERE product_id = $1\n ) t WHERE rn > 1\n )`,\n [productId],\n )\n\n // Step 4: Merge duplicate properties into canonical (canonical wins)\n await client.query(\n `UPDATE upg.nodes\n SET data = (\n SELECT COALESCE(jsonb_object_agg(key, value), '{}'::jsonb)\n FROM (\n SELECT key, value\n FROM upg.nodes, jsonb_each(COALESCE(data, '{}'::jsonb))\n WHERE id = ANY($2::text[])\n UNION ALL\n SELECT key, value\n FROM upg.nodes, jsonb_each(COALESCE(data, '{}'::jsonb))\n WHERE id = $3\n ) merged\n )\n WHERE id = $3`,\n [productId, duplicateIds, canonicalId],\n )\n\n // Step 5: Delete duplicates\n await client.query(\n `DELETE FROM upg.nodes WHERE id = ANY($1::text[]) AND product_id = $2`,\n [duplicateIds, productId],\n )\n\n // Audit each merged-away duplicate as a deletion.\n for (const dupId of duplicateIds) {\n await appendAudit(client, {\n productId,\n action: 'delete',\n entityType: 'node',\n entityId: dupId,\n changes: { merged_into: canonicalId },\n })\n }\n\n await client.query('COMMIT')\n\n for (const dupId of duplicateIds) {\n this.emit(productId, 'node.deleted', { id: dupId, merged_into: canonicalId })\n }\n return {\n canonical_id: canonicalId,\n merged_ids: duplicateIds,\n rebound_edges: reboundEdges,\n removed_self_loops: selfLoopCount ?? 0,\n removed_duplicate_edges: dupEdgeCount ?? 0,\n }\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n } finally {\n client.release()\n }\n }\n\n // ── Edges ──────────────────────────────────────────────────────────────────\n\n async getAllEdges(productId: string): Promise<UPGEdge[]> {\n const { rows } = await this.pool.query(\n `SELECT id, source, target, type, properties FROM upg.edges WHERE product_id = $1`,\n [productId],\n )\n return rows.map(rowToEdge)\n }\n\n async getEdgesForNode(nodeId: string): Promise<UPGEdge[]> {\n const { rows } = await this.pool.query(\n `SELECT id, source, target, type, properties FROM upg.edges\n WHERE source = $1 OR target = $1`,\n [nodeId],\n )\n return rows.map(rowToEdge)\n }\n\n async addEdge(productId: string, edge: UPGEdge): Promise<void> {\n const id = edge.id || edgeId()\n const client = await this.pool.connect()\n try {\n await client.query('BEGIN')\n await client.query(\n `INSERT INTO upg.edges (id, product_id, source, target, type, properties)\n VALUES ($1, $2, $3, $4, $5, $6)`,\n [\n id,\n productId,\n edge.source,\n edge.target,\n edge.type,\n edge.properties ? JSON.stringify(edge.properties) : null,\n ],\n )\n await appendAudit(client, {\n productId,\n action: 'create',\n entityType: 'edge',\n entityId: id,\n changes: { source: edge.source, target: edge.target, type: edge.type },\n })\n await client.query('COMMIT')\n this.emit(productId, 'edge.created', { id, source: edge.source, target: edge.target, type: edge.type })\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n } finally {\n client.release()\n }\n }\n\n async removeEdge(id: string): Promise<UPGEdge> {\n const client = await this.pool.connect()\n try {\n await client.query('BEGIN')\n const { rows } = await client.query(\n `DELETE FROM upg.edges WHERE id = $1\n RETURNING id, product_id, source, target, type, properties`,\n [id],\n )\n if (rows.length === 0) {\n await client.query('ROLLBACK')\n throw new Error(`Edge not found: ${id}`)\n }\n await appendAudit(client, {\n productId: rows[0].product_id,\n action: 'delete',\n entityType: 'edge',\n entityId: id,\n changes: { source: rows[0].source, target: rows[0].target, type: rows[0].type },\n })\n await client.query('COMMIT')\n this.emit(rows[0].product_id, 'edge.deleted', { id, source: rows[0].source, target: rows[0].target, type: rows[0].type })\n return rowToEdge(rows[0])\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n } finally {\n client.release()\n }\n }\n\n /**\n * Set (or merge) the gated `properties` payload on a single edge (0.8.6\n * framework-exercise parity). Merge is the default: the supplied keys are\n * deep-merged at the top level via JSONB `||`; pass `{ merge: false }` to\n * replace the payload wholesale. Mirrors the local SDK's `setEdgeProperties`.\n */\n async setEdgeProperties(\n id: string,\n values: Record<string, unknown>,\n opts: { merge?: boolean } = {},\n ): Promise<UPGEdge> {\n const merge = opts.merge !== false\n const client = await this.pool.connect()\n try {\n await client.query('BEGIN')\n const sql = merge\n ? `UPDATE upg.edges\n SET properties = COALESCE(properties, '{}'::jsonb) || $1::jsonb\n WHERE id = $2\n RETURNING id, product_id, source, target, type, properties`\n : `UPDATE upg.edges\n SET properties = $1::jsonb\n WHERE id = $2\n RETURNING id, product_id, source, target, type, properties`\n const { rows } = await client.query(sql, [JSON.stringify(values), id])\n if (rows.length === 0) {\n await client.query('ROLLBACK')\n throw new Error(`Edge not found: ${id}`)\n }\n await appendAudit(client, {\n productId: rows[0].product_id,\n action: 'update',\n entityType: 'edge',\n entityId: id,\n changes: values,\n })\n await client.query('COMMIT')\n this.emit(rows[0].product_id, 'edge.updated', { id, properties: values })\n return rowToEdge(rows[0])\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n } finally {\n client.release()\n }\n }\n\n // ── Edge utilities ─────────────────────────────────────\n\n /**\n * Flat enumeration of all edges for a product, optionally filtered by type.\n * Used for migration passes. Returns lightweight { id, source, target, type }\n * rows ordered by id without full node payloads.\n */\n async exportEdges(productId: string, types?: string[]) {\n const { rows } = await this.pool.query(\n `SELECT id, source, target, type\n FROM upg.edges\n WHERE product_id = $1\n AND ($2::text[] IS NULL OR type = ANY($2))\n ORDER BY id`,\n [productId, types ?? null],\n )\n return rows\n }\n\n /**\n * Rename all edges of one type to another within a product.\n * dryRun=true (default): count only. dryRun=false: transactional UPDATE.\n */\n async renameEdgeType(productId: string, from: string, to: string, dryRun = true) {\n if (dryRun) {\n const { rows } = await this.pool.query(\n `SELECT COUNT(*)::text AS count FROM upg.edges WHERE product_id = $1 AND type = $2`,\n [productId, from],\n )\n return parseInt(rows[0].count, 10)\n }\n const client = await this.pool.connect()\n try {\n await client.query('BEGIN')\n const { rowCount } = await client.query(\n `UPDATE upg.edges SET type = $3 WHERE product_id = $1 AND type = $2`,\n [productId, from, to],\n )\n await client.query('COMMIT')\n return rowCount ?? 0\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n } finally {\n client.release()\n }\n }\n\n // ── Search ─────────────────────────────────────────────────────────────────\n\n async searchNodes(\n productId: string,\n query: string,\n type?: string,\n limit: number = 20,\n ): Promise<UPGBaseNode[]> {\n const conditions = [\n `product_id = $1`,\n `to_tsvector('english', coalesce(title,'') || ' ' || coalesce(description,'')) @@ plainto_tsquery('english', $2)`,\n ]\n const values: unknown[] = [productId, query]\n let p = 3\n\n if (type) {\n conditions.push(`type = $${p++}`)\n values.push(type)\n }\n\n values.push(limit)\n\n const { rows } = await this.pool.query(\n `SELECT ${UPGPgStore.NODE_COLS} FROM upg.nodes\n WHERE ${conditions.join(' AND ')}\n ORDER BY ts_rank(\n to_tsvector('english', coalesce(title,'') || ' ' || coalesce(description,'')),\n plainto_tsquery('english', $2)\n ) DESC\n LIMIT $${p}`,\n values,\n )\n return rows.map(rowToNode)\n }\n\n // ── Aggregation ────────────────────────────────────────────────────────────\n\n async getGraphSummary(productId: string): Promise<GraphSummary> {\n const product = await this.getProduct(productId)\n\n const { rows: ntRows } = await this.pool.query<{ type: string; count: string }>(\n `SELECT type, COUNT(*)::text AS count FROM upg.nodes WHERE product_id = $1 GROUP BY type`,\n [productId],\n )\n const nodes_by_type: Record<string, number> = {}\n let node_count = 0\n for (const r of ntRows) { const c = parseInt(r.count, 10); nodes_by_type[r.type] = c; node_count += c }\n\n const { rows: etRows } = await this.pool.query<{ type: string; count: string }>(\n `SELECT type, COUNT(*)::text AS count FROM upg.edges WHERE product_id = $1 GROUP BY type`,\n [productId],\n )\n const edges_by_type: Record<string, number> = {}\n let edge_count = 0\n for (const r of etRows) { const c = parseInt(r.count, 10); edges_by_type[r.type] = c; edge_count += c }\n\n const { rows: oRows } = await this.pool.query<{ count: string }>(\n `SELECT COUNT(*)::text AS count FROM upg.nodes n\n WHERE n.product_id = $1\n AND NOT EXISTS (\n SELECT 1 FROM upg.edges e\n WHERE e.product_id = $1 AND (e.source = n.id OR e.target = n.id)\n )`,\n [productId],\n )\n const orphan_count = parseInt(oRows[0].count, 10)\n\n return { product: { id: product.id, title: product.title, stage: product.stage }, node_count, edge_count, nodes_by_type, edges_by_type, orphan_count }\n }\n\n // ── Export ─────────────────────────────────────────────────────────────────\n\n async getProductGraph(productId: string): Promise<{ product: Product; nodes: UPGBaseNode[]; edges: UPGEdge[] }> {\n const [product, nodes, edges] = await Promise.all([\n this.getProduct(productId),\n this.getAllNodes(productId),\n this.getAllEdges(productId),\n ])\n return { product, nodes, edges }\n }\n\n // ── Collaboration: Audit Log ────────────────────────────────────────────────\n\n async getAuditLog(productId: string, limit = 50): Promise<AuditEntry[]> {\n const { rows } = await this.pool.query<AuditEntry>(\n `SELECT id, user_id, action, entity_type, entity_id, changes, created_at\n FROM upg.audit_log\n WHERE product_id = $1\n ORDER BY created_at DESC\n LIMIT $2`,\n [productId, limit],\n )\n return rows\n }\n\n // ── Collaboration: Comments ─────────────────────────────────────────────────\n\n async addComment(productId: string, nodeId: string, userId: string, body: string): Promise<Comment> {\n const { rows } = await this.pool.query<Comment>(\n `INSERT INTO upg.comments (product_id, node_id, user_id, body)\n VALUES ($1, $2, $3, $4)\n RETURNING id, node_id, user_id, body, created_at`,\n [productId, nodeId, userId, body],\n )\n return rows[0]\n }\n\n async listComments(nodeId: string): Promise<Comment[]> {\n const { rows } = await this.pool.query<Comment>(\n `SELECT id, node_id, user_id, body, created_at\n FROM upg.comments\n WHERE node_id = $1\n ORDER BY created_at DESC`,\n [nodeId],\n )\n return rows\n }\n\n // ── Collaboration: Access Control ───────────────────────────────────────────\n\n async grantAccess(productId: string, userId: string, role: string): Promise<void> {\n await this.pool.query(\n `INSERT INTO upg.access (product_id, user_id, role)\n VALUES ($1, $2, $3)\n ON CONFLICT (product_id, user_id) DO UPDATE SET role = EXCLUDED.role`,\n [productId, userId, role],\n )\n }\n\n async listCollaborators(productId: string): Promise<AccessRecord[]> {\n const { rows } = await this.pool.query<AccessRecord>(\n `SELECT id, user_id, role, created_at\n FROM upg.access\n WHERE product_id = $1\n ORDER BY created_at`,\n [productId],\n )\n return rows\n }\n\n // ── Analytics ─────────────────────────────────────────────────────────────────\n\n async getGraphAnalytics(productId: string): Promise<GraphAnalytics> {\n // 1. Hypothesis velocity: count by status\n const { rows: hvRows } = await this.pool.query<{ status: string; count: string }>(\n `SELECT COALESCE(status, 'untested') AS status, COUNT(*)::text AS count\n FROM upg.nodes\n WHERE product_id = $1 AND type = 'hypothesis'\n GROUP BY COALESCE(status, 'untested')`,\n [productId],\n )\n const hv = { untested: 0, testing: 0, validated: 0, invalidated: 0 }\n for (const r of hvRows) {\n const s = r.status as keyof typeof hv\n if (s in hv) hv[s] = parseInt(r.count, 10)\n }\n\n // 2. Coverage ratio: % of personas with complete chains\n // (persona → jtbd → pain_point → opportunity → solution)\n const { rows: coverageRows } = await this.pool.query<{ total: string; covered: string }>(\n `WITH personas AS (\n SELECT id FROM upg.nodes WHERE product_id = $1 AND type = 'persona'\n ),\n covered AS (\n SELECT DISTINCT p.id\n FROM personas p\n JOIN upg.edges e1 ON e1.source = p.id\n JOIN upg.nodes jtbd ON jtbd.id = e1.target AND jtbd.type = 'jtbd'\n JOIN upg.edges e2 ON e2.source = jtbd.id\n JOIN upg.nodes pp ON pp.id = e2.target AND pp.type = 'pain_point'\n JOIN upg.edges e3 ON e3.source = pp.id\n JOIN upg.nodes opp ON opp.id = e3.target AND opp.type = 'opportunity'\n JOIN upg.edges e4 ON e4.source = opp.id\n JOIN upg.nodes sol ON sol.id = e4.target AND sol.type = 'solution'\n )\n SELECT\n (SELECT COUNT(*)::text FROM personas) AS total,\n (SELECT COUNT(*)::text FROM covered) AS covered`,\n [productId],\n )\n const totalPersonas = parseInt(coverageRows[0].total, 10)\n const coveredPersonas = parseInt(coverageRows[0].covered, 10)\n const coverage_ratio = totalPersonas > 0\n ? Math.round((coveredPersonas / totalPersonas) * 100)\n : 0\n\n // 3. Evidence density: (learnings + research_insights) / hypotheses\n const { rows: edRows } = await this.pool.query<{ evidence: string; hypotheses: string }>(\n `SELECT\n (SELECT COUNT(*)::text FROM upg.nodes WHERE product_id = $1 AND type IN ('learning', 'research_insight')) AS evidence,\n (SELECT COUNT(*)::text FROM upg.nodes WHERE product_id = $1 AND type = 'hypothesis') AS hypotheses`,\n [productId],\n )\n const evidenceCount = parseInt(edRows[0].evidence, 10)\n const hypothesisCount = parseInt(edRows[0].hypotheses, 10)\n const evidence_density = hypothesisCount > 0\n ? Math.round((evidenceCount / hypothesisCount) * 100) / 100\n : 0\n\n // 4. Stale entity rate: % of nodes whose last update is 14+ days old\n const { rows: staleRows } = await this.pool.query<{ total: string; stale: string }>(\n `SELECT\n COUNT(*)::text AS total,\n COUNT(*) FILTER (WHERE updated_at < now() - INTERVAL '14 days')::text AS stale\n FROM upg.nodes\n WHERE product_id = $1`,\n [productId],\n )\n const totalNodes = parseInt(staleRows[0].total, 10)\n const staleNodes = parseInt(staleRows[0].stale, 10)\n const stale_entity_rate = totalNodes > 0\n ? Math.round((staleNodes / totalNodes) * 100)\n : 0\n\n // 5. Orphan rate: % of nodes with no edges\n const { rows: orphanRows } = await this.pool.query<{ total: string; orphans: string }>(\n `SELECT\n COUNT(*)::text AS total,\n COUNT(*) FILTER (\n WHERE NOT EXISTS (\n SELECT 1 FROM upg.edges e\n WHERE e.product_id = $1 AND (e.source = n.id OR e.target = n.id)\n )\n )::text AS orphans\n FROM upg.nodes n\n WHERE n.product_id = $1`,\n [productId],\n )\n const totalForOrphan = parseInt(orphanRows[0].total, 10)\n const orphanCount = parseInt(orphanRows[0].orphans, 10)\n const orphan_rate = totalForOrphan > 0\n ? Math.round((orphanCount / totalForOrphan) * 100)\n : 0\n\n return { hypothesis_velocity: hv, coverage_ratio, evidence_density, stale_entity_rate, orphan_rate }\n }\n\n // ── Product Area Scoping ─────────────────────────────────────────────────────\n\n async listProductAreas(productId: string): Promise<Array<{ id: string; title: string; child_count: number }>> {\n const { rows } = await this.pool.query<{ id: string; title: string; child_count: string }>(\n `SELECT n.id, n.title,\n (SELECT COUNT(*)::text FROM upg.edges e WHERE e.source = n.id AND e.product_id = $1) AS child_count\n FROM upg.nodes n\n WHERE n.product_id = $1 AND n.type = 'product_area'\n ORDER BY n.title`,\n [productId],\n )\n return rows.map((r) => ({ id: r.id, title: r.title, child_count: parseInt(r.child_count, 10) }))\n }\n\n async getAreaGraph(\n productId: string,\n areaId: string,\n depth: number,\n ): Promise<{\n area: { id: string; title: string; type: string }\n nodes: UPGBaseNode[]\n edges: UPGEdge[]\n node_count: number\n edge_count: number\n }> {\n // Verify the area node exists and is a product_area\n const areaNode = await this.getNode(areaId)\n if (!areaNode) throw new Error(`Area node not found: ${areaId}`)\n if (areaNode.type !== 'product_area')\n throw new Error(`Node ${areaId} is type \"${areaNode.type}\", not \"product_area\"`)\n if (areaNode.product_id !== productId)\n throw new Error(`Area node ${areaId} does not belong to product ${productId}`)\n\n // Recursive CTE to walk the graph from the area node\n const { rows: nodeIds } = await this.pool.query<{ id: string }>(\n `WITH RECURSIVE area_walk AS (\n SELECT id, 0 AS depth FROM upg.nodes WHERE id = $1 AND product_id = $2\n UNION\n SELECT CASE WHEN e.source = aw.id THEN e.target ELSE e.source END, aw.depth + 1\n FROM area_walk aw\n JOIN upg.edges e ON (e.source = aw.id OR e.target = aw.id) AND e.product_id = $2\n WHERE aw.depth < $3\n )\n SELECT DISTINCT id FROM area_walk`,\n [areaId, productId, depth],\n )\n\n if (nodeIds.length === 0) {\n return {\n area: { id: areaNode.id, title: areaNode.title, type: areaNode.type },\n nodes: [],\n edges: [],\n node_count: 0,\n edge_count: 0,\n }\n }\n\n const ids = nodeIds.map((r) => r.id)\n\n // Fetch all nodes by IDs\n const { rows: nodeRows } = await this.pool.query(\n `SELECT ${UPGPgStore.NODE_COLS} FROM upg.nodes\n WHERE id = ANY($1) AND product_id = $2`,\n [ids, productId],\n )\n const nodes = nodeRows.map(rowToNode)\n\n // Fetch all edges between these nodes\n const { rows: edgeRows } = await this.pool.query(\n `SELECT id, source, target, type FROM upg.edges\n WHERE product_id = $1 AND source = ANY($2) AND target = ANY($2)`,\n [productId, ids],\n )\n const edges = edgeRows.map(rowToEdge)\n\n return {\n area: { id: areaNode.id, title: areaNode.title, type: areaNode.type },\n nodes,\n edges,\n node_count: nodes.length,\n edge_count: edges.length,\n }\n }\n\n /**\n * Count descendant nodes by type, starting from any node (not restricted to\n * `product_area`). Used by `get_area_context` to summarise area contents.\n *\n * @returns Array of `{ type, count }` rows, excluding the root node itself.\n */\n async getDescendantTypeCounts(\n productId: string,\n rootNodeId: string,\n depth: number,\n ): Promise<Array<{ type: string; count: number }>> {\n const { rows } = await this.pool.query<{ type: string; count: string }>(\n `WITH RECURSIVE sub AS (\n SELECT id, 0 AS depth FROM upg.nodes WHERE id = $1 AND product_id = $2\n UNION\n SELECT CASE WHEN e.source = s.id THEN e.target ELSE e.source END, s.depth + 1\n FROM sub s\n JOIN upg.edges e ON (e.source = s.id OR e.target = s.id) AND e.product_id = $2\n WHERE s.depth < $3\n )\n SELECT n.type, COUNT(*)::text AS count\n FROM sub\n JOIN upg.nodes n ON n.id = sub.id AND n.id != $1\n GROUP BY n.type`,\n [rootNodeId, productId, depth],\n )\n return rows.map((r) => ({ type: r.type, count: parseInt(r.count, 10) }))\n }\n\n // ── Webhooks ──────────────────────────────────────────────────────────────────\n\n async registerWebhook(productId: string, event: string, url: string, secret?: string): Promise<Webhook> {\n const { rows } = await this.pool.query<Webhook>(\n `INSERT INTO upg.webhooks (product_id, event, url, secret)\n VALUES ($1, $2, $3, $4)\n RETURNING id, event, url, active, created_at`,\n [productId, event, url, secret ?? null],\n )\n return rows[0]\n }\n\n async listWebhooks(productId: string): Promise<Webhook[]> {\n const { rows } = await this.pool.query<Webhook>(\n `SELECT id, event, url, active, created_at\n FROM upg.webhooks\n WHERE product_id = $1\n ORDER BY created_at`,\n [productId],\n )\n return rows\n }\n\n async removeWebhook(id: string): Promise<void> {\n const { rows } = await this.pool.query(\n `DELETE FROM upg.webhooks WHERE id = $1 RETURNING id`,\n [id],\n )\n if (rows.length === 0) throw new Error(`Webhook not found: ${id}`)\n }\n\n // ── Cross-product edges ────────────────────────────────────────────────────\n\n async listCrossProductEdges(productId: string): Promise<CrossProductEdge[]> {\n const { rows } = await this.pool.query<CrossProductEdge>(\n `SELECT id, source, target, type, created_by_product_id, created_at\n FROM upg.cross_product_edges\n WHERE created_by_product_id = $1\n ORDER BY created_at DESC`,\n [productId],\n )\n return rows\n }\n\n async addCrossProductEdge(\n id: string,\n productId: string,\n source: string,\n target: string,\n type: string,\n ): Promise<CrossProductEdge> {\n const { rows } = await this.pool.query<CrossProductEdge>(\n `INSERT INTO upg.cross_product_edges (id, source, target, type, created_by_product_id)\n VALUES ($1, $2, $3, $4, $5)\n RETURNING id, source, target, type, created_by_product_id, created_at`,\n [id, source, target, type, productId],\n )\n return rows[0]\n }\n\n async deleteCrossProductEdge(id: string): Promise<CrossProductEdge> {\n const { rows } = await this.pool.query<CrossProductEdge>(\n `DELETE FROM upg.cross_product_edges WHERE id = $1\n RETURNING id, source, target, type, created_by_product_id, created_at`,\n [id],\n )\n if (rows.length === 0) throw new Error(`Cross-product edge not found: ${id}`)\n return rows[0]\n }\n\n async productExists(productId: string): Promise<boolean> {\n const { rows } = await this.pool.query<{ exists: boolean }>(\n `SELECT EXISTS(SELECT 1 FROM upg.products WHERE id = $1) AS exists`,\n [productId],\n )\n return rows[0].exists\n }\n}\n\n// ─── Row mappers ─────────────────────────────────────────────────────────────\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction rowToNode(row: any): UPGBaseNode & { product_id: string } {\n const node: UPGBaseNode & { product_id: string } = {\n id: row.id,\n type: row.type,\n title: row.title,\n product_id: row.product_id,\n }\n if (row.description != null) node.description = row.description\n if (row.status != null) node.status = row.status\n if (row.tags != null) node.tags = row.tags\n if (row.data != null) node.properties = row.data\n return node\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction rowToEdge(row: any): UPGEdge {\n const edge: UPGEdge = { id: row.id, source: row.source, target: row.target, type: row.type }\n // Gated edge payload (0.8.6). JSONB comes back parsed; null/absent → no key.\n if (row.properties != null) edge.properties = row.properties\n return edge\n}\n","/**\n * Webhook delivery (UPG-553).\n *\n * Mutations emit `WebhookEvent`s (via `PgStore`'s event sink); this dispatcher\n * looks up the active webhooks registered for that product + event and POSTs\n * the payload to each, async, HMAC-signed, with bounded retry/backoff and\n * auto-disable on a permanent 4xx.\n *\n * Delivery is fire-and-forget from the mutation's perspective (`emit`): it runs\n * AFTER the mutation commits and never throws back into the write path. The\n * outbound `fetch` is injectable so tests exercise delivery without real HTTP.\n */\nimport type { Pool } from 'pg'\nimport { createHmac } from 'node:crypto'\n\nexport interface WebhookEvent {\n productId: string\n /** e.g. `node.created`, `edge.deleted`. */\n event: string\n payload: Record<string, unknown>\n}\n\nexport interface DeliveryResponse { status: number }\nexport type FetchLike = (\n url: string,\n init: { method: string; headers: Record<string, string>; body: string },\n) => Promise<DeliveryResponse>\n\nexport interface DispatcherOptions {\n /** Injectable outbound HTTP. Defaults to global fetch. */\n fetchFn?: FetchLike\n /** Total attempts per delivery (default 3). */\n maxAttempts?: number\n /** Base backoff; attempt n waits backoffMs * 2^(n-1) (default 250ms). */\n backoffMs?: number\n}\n\ninterface WebhookRow { id: string; event: string; url: string; secret: string | null }\n\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms))\n\nexport class WebhookDispatcher {\n private readonly fetchFn: FetchLike\n private readonly maxAttempts: number\n private readonly backoffMs: number\n\n constructor(private readonly pool: Pool, opts: DispatcherOptions = {}) {\n this.fetchFn =\n opts.fetchFn ??\n ((url, init) => fetch(url, init).then((r) => ({ status: r.status })))\n this.maxAttempts = opts.maxAttempts ?? 3\n this.backoffMs = opts.backoffMs ?? 250\n }\n\n /**\n * Fire-and-forget entry point used by the store's event sink. Never throws\n * into the caller; delivery errors are logged to stderr.\n */\n emit(event: WebhookEvent): void {\n void this.dispatch(event).catch((err) =>\n process.stderr.write(`[webhook] dispatch failed for ${event.event}: ${String(err)}\\n`),\n )\n }\n\n /** Deliver `event` to every active webhook registered for its product+event. */\n async dispatch(event: WebhookEvent): Promise<void> {\n const { rows } = await this.pool.query<WebhookRow>(\n `SELECT id, event, url, secret FROM upg.webhooks\n WHERE product_id = $1 AND active = true AND (event = $2 OR event = '*')`,\n [event.productId, event.event],\n )\n await Promise.all(rows.map((hook) => this.deliver(hook, event)))\n }\n\n private async deliver(hook: WebhookRow, event: WebhookEvent): Promise<void> {\n const body = JSON.stringify({\n webhook_id: hook.id,\n event: event.event,\n product_id: event.productId,\n data: event.payload,\n delivered_at: new Date().toISOString(),\n })\n const headers: Record<string, string> = {\n 'content-type': 'application/json',\n 'x-upg-event': event.event,\n }\n if (hook.secret) {\n headers['x-upg-signature'] =\n 'sha256=' + createHmac('sha256', hook.secret).update(body).digest('hex')\n }\n\n for (let attempt = 1; attempt <= this.maxAttempts; attempt++) {\n let status: number\n try {\n ;({ status } = await this.fetchFn(hook.url, { method: 'POST', headers, body }))\n } catch {\n status = 0 // network/transport error → treat as transient\n }\n if (status >= 200 && status < 300) return // delivered\n // Permanent client error (except 429 rate-limit) → disable the registration.\n if (status >= 400 && status < 500 && status !== 429) {\n await this.disable(hook.id)\n return\n }\n // Transient (5xx / 429 / network): back off and retry.\n if (attempt < this.maxAttempts) await sleep(this.backoffMs * 2 ** (attempt - 1))\n }\n // Retries exhausted on a transient error; leave active for the next event.\n }\n\n private async disable(id: string): Promise<void> {\n await this.pool.query(`UPDATE upg.webhooks SET active = false WHERE id = $1`, [id])\n }\n}\n","/**\n * UPG cloud MCP server over stdio. Builds the runtime context and\n * dispatches every `tools/call` through `src/lib/tool-registry.ts`.\n */\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n} from '@modelcontextprotocol/sdk/types.js'\nimport type { PgStore } from './store/pg-store.js'\nimport type { CloudContext } from './lib/server-context.js'\nimport { textError } from './lib/server-context.js'\nimport { TOOL_DEFINITIONS, getToolHandler } from './lib/tool-registry.js'\nimport { createRequire } from 'node:module'\n\nconst require = createRequire(import.meta.url)\nconst pkg = require('../package.json') as { version: string }\n\nexport function createServer(store: PgStore) {\n const server = new Server(\n { name: 'unified-product-graph-cloud', version: pkg.version },\n { capabilities: { tools: {} } },\n )\n\n const ctx: CloudContext = { store }\n\n // ── tools/list ────────────────────────────────────────────────────────────\n\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n return { tools: TOOL_DEFINITIONS }\n })\n\n // ── tools/call ────────────────────────────────────────────────────────────\n\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args = {} } = request.params\n\n const handler = getToolHandler(name)\n const result = handler\n ? await handler(args as Record<string, unknown>, ctx)\n : textError(`Unknown tool: ${name}`)\n // ToolResult is structurally identical to the SDK's CallToolResult variant\n // of ServerResult, but the SDK's union has an index signature this narrower\n // type doesn't satisfy. Cast at the boundary so handlers can stay typed.\n return result as { content: typeof result.content; isError?: true }\n })\n\n // ── Transport ─────────────────────────────────────────────────────────────\n\n return {\n async start() {\n const transport = new StdioServerTransport()\n await server.connect(transport)\n },\n }\n}\n","/**\n * MCP wire-shape primitives. One response shape across stdio, HTTP, and\n * embedded servers.\n */\n\nexport interface ToolTextContent {\n type: 'text'\n text: string\n}\n\nexport interface ToolResult {\n content: ToolTextContent[]\n isError?: true\n}\n\nexport function text(s: string): ToolResult {\n return { content: [{ type: 'text', text: s }] }\n}\n\nexport function textError(s: string): ToolResult {\n return { content: [{ type: 'text', text: s }], isError: true }\n}\n","/**\n * Catalog helpers: alias resolution and an entity-schema builder.\n *\n * One code path answers `get_entity_schema('jtbd')` the same way on\n * local, cloud, and embedded servers (canonical `job` + `alias_of`\n * trail).\n *\n * Entity-type alias resolution (`resolveEntityType`, `UnknownEntityTypeError`,\n * `EntityTypeResolution`) now lives in `@unified-product-graph/core` so a\n * SINGLE `UnknownEntityTypeError` class is shared across the SDK and every\n * server (instance-safe `instanceof` across package boundaries). They are\n * re-exported here so existing mcp-tooling consumers keep their import path.\n *\n * This is the only module in `mcp-tooling` that depends on\n * `@unified-product-graph/core`. Importers that skip the catalog keep\n * using `./result.js`, `./tool-definition.js`, `./transport.js` core-free.\n */\n\nimport {\n UPG_EDGE_CATALOG,\n UPG_PROPERTY_SCHEMA,\n getDomainForType,\n getGuideForDomain,\n getLifecycleForType,\n getPropertySchema,\n resolveEntityType,\n UnknownEntityTypeError,\n type EntityTypeResolution,\n type UPGEntityType,\n} from '@unified-product-graph/core'\n\n// Re-export the shared entity-type resolution surface (defined in core) so\n// existing mcp-tooling consumers keep importing it from `mcp-tooling`.\nexport { resolveEntityType, UnknownEntityTypeError }\nexport type { EntityTypeResolution }\n\n/* ---------------------------------------------------------------------------\n * Entity-schema builder: `get_entity_schema` shared response shape\n * ------------------------------------------------------------------------- */\n\nexport interface EntitySchemaEdgeOut {\n edge_type: string\n target_type: string\n forward_verb: string\n}\n\nexport interface EntitySchemaEdgeIn {\n edge_type: string\n source_type: string\n reverse_verb: string\n}\n\nexport interface EntitySchemaDomainGuideAntiPattern {\n name?: string\n description: string\n affected_entity?: string\n remediation?: string\n}\n\nexport interface EntitySchemaDomainGuide {\n anchor_entity: string\n creation_sequence: readonly string[]\n position_in_sequence: number\n anti_patterns: EntitySchemaDomainGuideAntiPattern[]\n}\n\nexport interface EntitySchema {\n type: string\n alias_of?: { from: string; to: string }\n domain: { id: string; label: string } | null\n expected_properties: Record<string, unknown>\n edges_out: EntitySchemaEdgeOut[]\n edges_in: EntitySchemaEdgeIn[]\n phases?: string[]\n initial_phase?: string\n terminal_phases?: string[]\n domain_guide?: EntitySchemaDomainGuide\n}\n\nexport interface BuildEntitySchemaOptions {\n /**\n * Include the domain guide slice (anchor entity, creation sequence,\n * relevant anti-patterns). Default `true`. Servers that need to keep\n * a smaller response shape (e.g. legacy embedders) can pass `false`.\n */\n include_domain_guide?: boolean\n}\n\n/**\n * Resolve an entity-type input and build the canonical\n * `get_entity_schema` response shape.\n *\n * Walks `UPG_EDGE_CATALOG` for `edges_out` and `edges_in` (the same\n * source the LSP uses for completions and diagnostics), surfaces the\n * domain and the domain guide when one exists, and folds in lifecycle\n * phases when the type is registered.\n *\n * Throws `UnknownEntityTypeError` for unknown or mistyped inputs.\n * Servers catch and translate to a `textError` envelope.\n */\nexport function buildEntitySchema(\n rawType: unknown,\n options: BuildEntitySchemaOptions = {},\n): EntitySchema {\n const includeDomainGuide = options.include_domain_guide ?? true\n\n const resolved = resolveEntityType(rawType)\n const entityType = resolved.canonical\n\n const domain = getDomainForType(entityType)\n\n const edgesOut: EntitySchemaEdgeOut[] = []\n const edgesIn: EntitySchemaEdgeIn[] = []\n for (const [edgeKey, def] of Object.entries(UPG_EDGE_CATALOG)) {\n if (def.source_type === entityType) {\n edgesOut.push({\n edge_type: edgeKey,\n target_type: def.target_type,\n forward_verb: def.forward_verb,\n })\n }\n if (def.target_type === entityType) {\n edgesIn.push({\n edge_type: edgeKey,\n source_type: def.source_type,\n reverse_verb: def.reverse_verb,\n })\n }\n }\n\n const propertySchema = getPropertySchema(entityType)\n\n const schema: EntitySchema = {\n type: entityType,\n ...(resolved.alias ? { alias_of: resolved.alias } : {}),\n domain: domain ? { id: domain.id, label: domain.label } : null,\n expected_properties: propertySchema ?? {},\n edges_out: edgesOut,\n edges_in: edgesIn,\n }\n\n const lifecycle = getLifecycleForType(entityType)\n if (lifecycle) {\n schema.phases = lifecycle.phases.map((p) => p.id)\n schema.initial_phase = lifecycle.initial_phase\n schema.terminal_phases = [...lifecycle.terminal_phases]\n }\n\n if (includeDomainGuide && domain) {\n const guide = getGuideForDomain(domain.id)\n if (guide) {\n const normalise = (ap: unknown): EntitySchemaDomainGuideAntiPattern => {\n if (typeof ap === 'string') return { description: ap }\n const o = (ap as Record<string, unknown>) ?? {}\n const desc = typeof o.description === 'string' ? o.description : ''\n const out: EntitySchemaDomainGuideAntiPattern = { description: desc }\n if (typeof o.name === 'string') out.name = o.name\n if (typeof o.affected_entity === 'string') out.affected_entity = o.affected_entity\n if (typeof o.remediation === 'string') out.remediation = o.remediation\n return out\n }\n const allAntiPatterns = guide.anti_patterns.map(normalise)\n\n const needle = entityType.replace(/_/g, ' ').toLowerCase()\n const mentions = allAntiPatterns.filter(\n (ap) => ap.affected_entity === entityType || ap.description.toLowerCase().includes(needle),\n )\n const relevant = mentions.length > 0 ? mentions : allAntiPatterns.slice(0, 3)\n\n schema.domain_guide = {\n anchor_entity: guide.anchor_entity,\n creation_sequence: guide.creation_sequence,\n position_in_sequence: guide.creation_sequence.indexOf(entityType as UPGEntityType),\n anti_patterns: relevant,\n }\n }\n }\n\n return schema\n}\n\n/* ---------------------------------------------------------------------------\n * Entity-fields builder: `get_entity_fields` shared response shape\n * ------------------------------------------------------------------------- */\n\n/**\n * Format a single property entry from `UPG_PROPERTY_SCHEMA` into the\n * legacy `<type-hint> — <description>` string the cloud\n * `get_entity_fields` wire shape uses. Enum values render as quoted\n * alternatives (`'a' | 'b' | 'c'`), arrays as `<elem>[]`, everything\n * else as the bare JSON-schema `type`.\n *\n * Centralised in `@unified-product-graph/mcp-tooling` so every server\n * encodes the format once. The local server omits `get_entity_fields`\n * (its `get_entity_schema` returns the richer `expected_properties`\n * map). This helper is the dedicated bridge that keeps the cloud and\n * embedded responses in lockstep with `UPG_PROPERTY_SCHEMA`.\n */\nfunction formatPropertyHint(prop: unknown): string {\n if (!prop || typeof prop !== 'object') return 'any'\n const p = prop as Record<string, unknown>\n const desc = typeof p.description === 'string' ? p.description : ''\n\n let hint: string\n if (Array.isArray(p.enum) && p.enum.length > 0) {\n hint = (p.enum as unknown[]).map((v) => (typeof v === 'string' ? `'${v}'` : String(v))).join(' | ')\n } else if (p.type === 'array') {\n const items = (p.items as Record<string, unknown> | undefined) ?? {}\n const elem = typeof items.type === 'string' ? items.type : 'any'\n hint = `${elem}[]`\n } else if (typeof p.type === 'string') {\n hint = p.type\n } else {\n hint = 'any'\n }\n\n return desc ? `${hint} — ${desc}` : hint\n}\n\n/**\n * Build the `Record<string, string>` field map for a single entity type\n * by walking `UPG_PROPERTY_SCHEMA[type]`. Returns `undefined` when the\n * type has no canonical property schema (for example, v0.1 names that\n * map to a v0.2 canonical). Callers resolve aliases via\n * `resolveEntityType` first.\n */\nexport function buildEntityFields(type: string): Record<string, string> | undefined {\n // Defensive: some bundlers (notably Turbopack) have been observed to\n // drop named imports from workspace-linked `\"type\": \"module\"` packages\n // during ESM interop. If `UPG_PROPERTY_SCHEMA` didn't resolve, degrade\n // to \"schema unavailable\" rather than crashing the caller's module\n // evaluation.\n if (!UPG_PROPERTY_SCHEMA) return undefined\n const schema = UPG_PROPERTY_SCHEMA[type as UPGEntityType] as\n | Record<string, unknown>\n | undefined\n if (!schema) return undefined\n const out: Record<string, string> = {}\n for (const [name, prop] of Object.entries(schema)) {\n out[name] = formatPropertyHint(prop)\n }\n return out\n}\n\n/**\n * Build the full `{ [type]: { [field]: hint } }` map across every entity\n * type that has a canonical property schema. Used by the cloud route to\n * derive its `ENTITY_FIELDS` export from `@unified-product-graph/core`\n * at module load. One source, zero hand-maintained drift.\n */\nexport function buildAllEntityFields(): Record<string, Record<string, string>> {\n const out: Record<string, Record<string, string>> = {}\n // Defensive: see `buildEntityFields` above. If the named import dropped,\n // return an empty map. Consumers already handle missing types.\n if (!UPG_PROPERTY_SCHEMA) return out\n for (const type of Object.keys(UPG_PROPERTY_SCHEMA)) {\n const fields = buildEntityFields(type)\n if (fields) out[type] = fields\n }\n return out\n}\n","/**\n * Multi-tenant primitives: products and audit log. Cloud-only; three handlers\n * (`list_products`, `create_product`, `get_audit_log`) manage the `product_id`\n * scope every other tool takes.\n */\n\nimport { type ToolHandler, text, textError } from '../lib/server-context.js'\n\n/**\n * List every product visible to the caller. Discovery surface for valid\n * `product_id` values before scoped queries.\n *\n * @returns JSON: `{ products: Array<{ id, title, description?, stage? }> }`.\n * @atomicity atomic (read-only)\n * @warning RLS-bounded; an empty list can mean \"no products\" or \"no access\".\n * Pair with `list_collaborators` to confirm scope on a specific product.\n * @see create_product\n * @see get_product_context\n * @see get_graph_digest\n * @see list_collaborators\n */\nexport const listProducts: ToolHandler = async (_args, { store }) => {\n const products = await store.listProducts()\n return text(JSON.stringify({ products }, null, 2))\n}\n\n/**\n * Create a new product graph. Caller is auto-granted `owner` on the new product.\n *\n * @returns JSON: `{ product: { id, title, description?, stage? } }`.\n * @throws textError when `title` is missing.\n * @atomicity atomic\n * @warning Billing-relevant: product count typically drives plan tier;\n * creation may trigger a tier upgrade or hit the plan's product cap.\n * @see list_products\n * @see grant_access\n * @see list_product_stages\n */\nexport const createProduct: ToolHandler = async (args, { store }) => {\n if (!args.title) return textError(`Missing required parameter: title`)\n const product = await store.createProduct(\n args.title as string,\n args.description as string | undefined,\n args.stage as string | undefined,\n )\n return text(JSON.stringify({ product }, null, 2))\n}\n\n/**\n * Read the product's audit log: canonical history of mutations,\n * most-recent first. Default `limit: 50`.\n *\n * @returns JSON: `{ entries: Array<{ ...mutation }> }`.\n * @throws textError when `product_id` is missing.\n * @atomicity atomic (read-only)\n * @warning Retention-windowed: entries beyond the plan-tier retention period\n * are pruned. An empty window may mean \"out of retention\", not \"no activity\".\n * @see get_graph_analytics\n * @see get_graph_digest\n * @see list_products\n */\nexport const getAuditLog: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError(`Missing required parameter: product_id`)\n const productId = args.product_id as string\n const limit = (args.limit as number) ?? 50\n const entries = await store.getAuditLog(productId, limit)\n return text(JSON.stringify({ entries }, null, 2))\n}\n","/**\n * Context, digest, traversal, and change-feed handlers. Multi-product\n * scoping via `product_id`.\n */\n\nimport type { UPGBaseNode, UPGEdge } from '@unified-product-graph/core'\nimport { type ToolHandler, text, textError } from '../lib/server-context.js'\n\nconst BUSINESS_AREAS: Record<string, string[]> = {\n identity: ['product', 'vision', 'mission'],\n understanding: ['persona', 'jtbd', 'pain_point', 'need', 'research_study', 'research_insight'],\n // : post- canonical type names (hypothesis_claim/evidence, experiment_plan/run).\n discovery: ['opportunity', 'solution', 'competitor', 'hypothesis_claim', 'hypothesis_evidence', 'experiment_plan', 'experiment_run', 'learning'],\n reaching: ['ideal_customer_profile', 'positioning', 'messaging', 'acquisition_channel', 'content_strategy'],\n converting: ['value_proposition', 'pricing_tier', 'funnel', 'funnel_step'],\n building: ['feature', 'user_story', 'epic', 'release', 'user_journey', 'user_flow'],\n sustaining: ['business_model', 'revenue_stream', 'cost_structure', 'unit_economics', 'pricing_strategy'],\n learning: ['outcome', 'kpi', 'metric', 'objective', 'key_result', 'retrospective'],\n}\n\nconst LIFECYCLE_PHASES: Record<string, string[]> = {\n strategy: ['product', 'outcome', 'metric', 'kpi', 'objective', 'key_result', 'vision', 'mission'],\n users: ['persona', 'jtbd', 'need', 'pain_point', 'desired_outcome'],\n discovery: ['opportunity', 'solution', 'research_study', 'insight', 'research_insight', 'competitor'],\n // : canonical names + legacy back-compat.\n validation: ['hypothesis_claim', 'hypothesis_evidence', 'experiment_plan', 'experiment_run', 'learning', 'evidence', 'hypothesis', 'experiment'],\n execution: ['feature', 'epic', 'user_story', 'release', 'task', 'bug'],\n}\n\n/**\n * Product summary, entity counts by type, and a human-readable overview.\n * Use this as the first call to orient an agent in a freshly-loaded product.\n *\n * @returns Text: `## <product title>` followed by description/stage,\n * graph stats (node/edge/type counts), and a sorted breakdown of entities\n * per type. Errors with `Product not found: <id>` for unknown products.\n * @throws textError when `product_id` is missing or the product\n * is not visible to the caller.\n * @atomicity atomic (read-only)\n * @see get_graph_digest\n * @see get_graph_analytics\n * @see get_entity_schema\n * @see list_nodes\n */\nexport const getProductContext: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError(`Missing required parameter: product_id`)\n const productId = args.product_id as string\n const product = await store.getProduct(productId)\n if (!product) return textError(`Product not found: ${productId}`)\n\n const nodes = await store.getAllNodes(productId)\n const edges = await store.getAllEdges(productId)\n\n const countsByType: Record<string, number> = {}\n for (const n of nodes) {\n countsByType[n.type] = (countsByType[n.type] ?? 0) + 1\n }\n\n const lines: string[] = [\n `## ${product.title}`,\n product.description ? `\\n${product.description}` : '',\n product.stage ? `\\nStage: ${product.stage}` : '',\n `\\n### Graph Stats`,\n `- Nodes: ${nodes.length}`,\n `- Edges: ${edges.length}`,\n `- Entity types: ${Object.keys(countsByType).length}`,\n `\\n### Entities by Type`,\n ...Object.entries(countsByType)\n .sort(([, a], [, b]) => b - a)\n .map(([type, count]) => `- ${type}: ${count}`),\n ]\n\n return text(lines.filter(Boolean).join('\\n'))\n}\n\n/**\n * Pre-computed analytics digest: counts by type, health metrics\n * (orphan rate, connectivity, validation rate, user coverage), key chain\n * stats (persona→jtbd→pain_point, opportunity→solution, hypothesis→\n * experiment→learning), business-area coverage, lifecycle counts.\n *\n * Cheaper than re-deriving from `list_nodes` or `query`; the agent's first\n * stop for \"how healthy is this graph?\".\n *\n * @returns JSON with `product`, `counts`, `health`, `chains`, `coverage`,\n * `lifecycle` keys (~500 tokens of summary). Note: chain keys still use the\n * v0.1 names (`persona_with_jtbd` etc.) pending a canonical rename.\n * @throws textError when `product_id` is missing.\n * @atomicity atomic (read-only)\n * @warning Chain keys carry v0.1 names (`persona_with_jtbd`,\n * `hypothesis_total`) pending a canonical rename. Lifecycle bucketing\n * uses local heuristics (`BUSINESS_AREAS` / `LIFECYCLE_PHASES`\n * constants in this file) rather than the canonical `UPG_DOMAINS` ring,\n * so it may drift from spec across versions.\n * @see get_product_context\n * @see get_graph_analytics\n * @see list_benchmarks\n * @see validate_graph\n */\nexport const getGraphDigest: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n const productId = args.product_id as string\n const nodes = await store.getAllNodes(productId)\n const edges = await store.getAllEdges(productId)\n const product = await store.getProduct(productId)\n\n const byType: Record<string, number> = {}\n for (const n of nodes) byType[n.type] = (byType[n.type] ?? 0) + 1\n\n const connected = new Set<string>()\n for (const e of edges) { connected.add(e.source); connected.add(e.target) }\n const orphanCount = nodes.filter((n) => !connected.has(n.id)).length\n\n const hypothesisCount = byType['hypothesis'] ?? 0\n const experimentCount = byType['experiment'] ?? 0\n const personaCount = byType['persona'] ?? 0\n\n const chainStats = (parentType: string, pattern: string) => {\n let w = 0\n const parents = nodes.filter((n) => n.type === parentType)\n for (const p of parents) {\n if (edges.some((e) => e.source === p.id && e.type.includes(pattern))) w++\n }\n return { with_child: w, total: parents.length }\n }\n\n const pj = chainStats('persona', 'jtbd')\n const jp = chainStats('jtbd', 'pain_point')\n const os = chainStats('opportunity', 'solution')\n const he = chainStats('hypothesis', 'experiment')\n const el = chainStats('experiment', 'learning')\n\n const typeSet = new Set(Object.keys(byType))\n const coverage: Record<string, { covered: number; total: number; types_present: string[]; types_missing: string[] }> = {}\n for (const [area, types] of Object.entries(BUSINESS_AREAS)) {\n const present = types.filter((t) => typeSet.has(t))\n const missing = types.filter((t) => !typeSet.has(t))\n coverage[area] = { covered: present.length, total: types.length, types_present: present, types_missing: missing }\n }\n\n const lifecycle: Record<string, number> = {}\n for (const [phase, types] of Object.entries(LIFECYCLE_PHASES)) {\n lifecycle[phase] = types.reduce((s, t) => s + (byType[t] ?? 0), 0)\n }\n\n return text(JSON.stringify({\n product: { title: product?.title ?? 'Unknown', stage: product?.stage ?? 'unknown' },\n counts: { total_nodes: nodes.length, total_edges: edges.length, by_type: byType },\n health: {\n orphan_count: orphanCount,\n orphan_rate: nodes.length > 0 ? Math.round((orphanCount / nodes.length) * 100) / 100 : 0,\n connectivity: nodes.length > 0 ? Math.round(((nodes.length - orphanCount) / nodes.length) * 100) / 100 : 0,\n validation_rate: hypothesisCount > 0 ? Math.round((experimentCount / hypothesisCount) * 100) / 100 : 0,\n user_coverage: personaCount > 0 ? Math.round((pj.with_child / personaCount) * 100) / 100 : 0,\n },\n chains: {\n persona_with_jtbd: pj.with_child, persona_total: pj.total,\n jtbd_with_pain_point: jp.with_child, jtbd_total: jp.total,\n opportunity_with_solution: os.with_child, opportunity_total: os.total,\n hypothesis_untested: hypothesisCount - he.with_child, hypothesis_total: hypothesisCount,\n experiment_with_learning: el.with_child, experiment_total: experimentCount,\n },\n coverage,\n lifecycle,\n }, null, 2))\n}\n\n/**\n * BFS traversal from a starting node (or every node of a given type),\n * following typed edges and returning a projected subgraph in one call.\n * Far cheaper than walking via `list_nodes` plus `get_node` for graph-wide\n * reads.\n *\n * Edge filtering: pass `traverse: ['persona_pursues_job', '!noisy_edge']`\n * to require the first edge type at depth 0 and exclude `noisy_edge` at\n * deeper levels. The last entry repeats for any depth beyond the array\n * length.\n *\n * Field projection: `include` is a whitelist of node fields (id and type\n * are returned by default). `edge_include` of `[]` returns the empty set of\n * edge metadata.\n *\n * @returns JSON: `{ nodes, edges, total_nodes, total_edges,\n * truncated?, truncated_at_depth?, hint? }`. Truncates with a hint when\n * `limit` is reached.\n * @throws textError when `product_id` is missing, or when\n * neither `from` nor `from_id` is provided, or when `from_id` does not\n * resolve.\n * @atomicity atomic (read-only)\n * @warning Pre-loads the entire product graph into memory before\n * filtering; 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\n * beyond `limit`, so check `truncated` before assuming the result is\n * complete.\n * @see list_nodes\n * @see get_node\n * @see get_area_graph\n * @see search_nodes\n * @see resolve_edge_for_pair\n * @see trace\n */\nexport const query: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n const productId = args.product_id as string\n const fromType = args.from as string | undefined\n const fromId = args.from_id as string | undefined\n if (!fromType && !fromId) return textError('Provide either \"from\" or \"from_id\"')\n\n const traverseEdgeTypes = args.traverse as string[] | undefined\n const maxDepth = Math.min(Math.max((args.depth as number) ?? 3, 1), 10)\n const maxNodes = Math.min(Math.max((args.limit as number) ?? 200, 1), 1000)\n const includeFields = new Set((args.include as string[] | undefined) ?? ['title', 'status', 'type'])\n includeFields.add('id'); includeFields.add('type')\n\n const allNodes = await store.getAllNodes(productId)\n const allEdges = await store.getAllEdges(productId)\n\n const edgesBySource = new Map<string, UPGEdge[]>()\n for (const e of allEdges) {\n let list = edgesBySource.get(e.source)\n if (!list) { list = []; edgesBySource.set(e.source, list) }\n list.push(e)\n }\n\n let startNodes: UPGBaseNode[]\n if (fromId) {\n const n = allNodes.find((n) => n.id === fromId)\n if (!n) return textError(`Node not found: ${fromId}`)\n startNodes = [n]\n } else {\n startNodes = allNodes.filter((n) => n.type === fromType)\n }\n\n const visited = new Set<string>()\n const collectedNodes: UPGBaseNode[] = []\n const collectedEdges: UPGEdge[] = []\n const queue: Array<{ id: string; level: number }> = []\n let truncated = false\n let maxDepthReached = 0\n\n for (const n of startNodes) {\n if (collectedNodes.length >= maxNodes) { truncated = true; break }\n visited.add(n.id); collectedNodes.push(n); queue.push({ id: n.id, level: 0 })\n }\n\n while (queue.length > 0) {\n if (collectedNodes.length >= maxNodes) { truncated = true; break }\n const { id, level } = queue.shift()!\n if (level > maxDepthReached) maxDepthReached = level\n if (level >= maxDepth) continue\n\n for (const edge of edgesBySource.get(id) ?? []) {\n if (traverseEdgeTypes && traverseEdgeTypes.length > 0) {\n const etl = level < traverseEdgeTypes.length ? traverseEdgeTypes[level] : traverseEdgeTypes[traverseEdgeTypes.length - 1]\n if (etl.startsWith('!')) { if (edge.type === etl.slice(1)) continue }\n else { if (edge.type !== etl) continue }\n }\n collectedEdges.push(edge)\n if (!visited.has(edge.target)) {\n visited.add(edge.target)\n const neighbor = allNodes.find((n) => n.id === edge.target)\n if (neighbor) {\n if (collectedNodes.length >= maxNodes) { truncated = true; break }\n collectedNodes.push(neighbor); queue.push({ id: edge.target, level: level + 1 })\n }\n }\n }\n }\n\n const edgeInclude = args.edge_include as string[] | undefined\n const projectedNodes = collectedNodes.map((n) => {\n const p: Record<string, unknown> = { id: n.id, type: n.type }\n if (includeFields.has('title')) p.title = n.title\n if (includeFields.has('status')) p.status = n.status\n if (includeFields.has('tags')) p.tags = n.tags\n if (includeFields.has('description')) p.description = n.description\n if (includeFields.has('properties')) p.properties = n.properties\n return p\n })\n\n let edgeArray: Array<Record<string, unknown>>\n if (edgeInclude !== undefined && edgeInclude.length === 0) {\n edgeArray = []\n } else {\n const ef = edgeInclude ? new Set(edgeInclude) : null\n edgeArray = collectedEdges.map((e) => {\n if (!ef) return { id: e.id, type: e.type, source: e.source, target: e.target }\n const p: Record<string, unknown> = {}\n if (ef.has('id')) p.id = e.id\n if (ef.has('type')) p.type = e.type\n if (ef.has('source')) p.source = e.source\n if (ef.has('target')) p.target = e.target\n return p\n })\n }\n\n const resp: Record<string, unknown> = {\n nodes: projectedNodes,\n edges: edgeArray,\n total_nodes: projectedNodes.length,\n total_edges: edgeArray.length,\n }\n if (truncated) {\n resp.truncated = true\n resp.truncated_at_depth = maxDepthReached\n resp.hint = `Limit of ${maxNodes} reached at depth ${maxDepthReached}.`\n }\n return text(JSON.stringify(resp, null, 2))\n}\n\n/**\n * Audit-log feed scoped to a single product. Returns mutations newer than\n * the optional `since` timestamp, capped at `limit` (default 50, max 200).\n * Cloud equivalent of the local server's `get_changes`.\n *\n * @returns JSON: `{ changes: AuditEntry[], total }`.\n * @throws textError when `product_id` is missing.\n * @atomicity atomic (read-only)\n * @warning Backed by the audit log: entries beyond the plan-tier\n * retention window are pruned and stay out of this surface. The `since`\n * filter runs in-memory after the store fetches up to `limit` entries,\n * so narrow `since` windows on busy products may surface fewer rows\n * than expected (raise `limit` to compensate).\n * @see get_audit_log\n * @see get_graph_digest\n * @see get_product_context\n */\nexport const getChanges: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n const since = args.since as string | undefined\n const limit = Math.min((args.limit as number) ?? 50, 200)\n const entries = await store.getAuditLog(args.product_id as string, limit)\n const filtered = since ? entries.filter((e) => e.created_at >= since) : entries\n return text(JSON.stringify({ changes: filtered, total: filtered.length }, null, 2))\n}\n","/**\n * Node CRUD and read handlers. Every tool scopes to a single product\n * via `product_id` and routes through `PgStore`.\n */\n\nimport type { UPGBaseNode, UPGEntityType } from '@unified-product-graph/core'\nimport { getLifecycleForType, resolveEntityType, UnknownEntityTypeError } from '@unified-product-graph/core'\nimport { type ToolHandler, text, textError } from '../lib/server-context.js'\nimport {\n inferEdgeTypeWithTier,\n checkPropertyTypes,\n checkLengthCaps,\n renderPropertyTypeWarning,\n} from '@unified-product-graph/sdk/logic'\nimport { nodeId, edgeId } from '../id-helpers.js'\n\n// ── Pagination helpers (mirrored from spec.ts) ──────────────────────────────\n\nconst LIST_NODES_DEFAULT_LIMIT = 1000\nconst LIST_NODES_MAX_LIMIT = 10000\n\nconst EXPORT_DOC_DEFAULT_LIMIT = 1000\nconst EXPORT_DOC_MAX_LIMIT = 10000\n\nfunction clampLimit(raw: unknown, def: number, max: number): number {\n const n = typeof raw === 'number' ? raw : def\n if (!Number.isFinite(n) || n <= 0) return def\n return Math.min(Math.floor(n), max)\n}\n\n/** Decode opaque base64 cursor `offset:N` into a numeric offset. Returns 0 on bad input. */\nfunction decodeCursor(raw: unknown): number {\n if (typeof raw !== 'string' || raw.length === 0) return 0\n try {\n const decoded = Buffer.from(raw, 'base64').toString('utf-8')\n const m = decoded.match(/^offset:(\\d+)$/)\n if (!m) return 0\n return Number.parseInt(m[1], 10)\n } catch {\n return 0\n }\n}\n\n/** Encode a numeric offset into the opaque base64 cursor `offset:N`. */\nfunction encodeCursor(offset: number): string {\n return Buffer.from(`offset:${offset}`, 'utf-8').toString('base64')\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n\n/**\n * Page through entities in a product, optionally filtered by type. Returns\n * a slim row shape (id, type, title, status, tags); for the full node plus\n * edges, follow up with `get_node` or `get_nodes`.\n *\n * Supports cursor-based pagination for large products. Pass `next_cursor`\n * from a previous response as `cursor` to advance to the next page.\n * Legacy `offset` parameter is still honoured when `cursor` is absent.\n *\n * @returns JSON: `{ nodes, total, limit, next_cursor? }`. `next_cursor` is\n * present when more results remain. `total` reflects the filtered count\n * before pagination.\n * @throws textError when `product_id` is missing.\n * @atomicity atomic (read-only)\n * @warning RLS-bounded: only nodes in products the caller has read access\n * to are returned. An empty list can mean \"no nodes\" or \"no access\".\n * Default `limit: 1000`, max 10000. For products with 1000+ nodes use\n * `cursor` pagination: keep calling with the returned `next_cursor` until\n * it is absent.\n * @see get_node\n * @see get_nodes\n * @see search_nodes\n * @see query\n */\nexport const listNodes: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError(`Missing required parameter: product_id`)\n const productId = args.product_id as string\n const filterType = args.type as string | undefined\n const limit = clampLimit(args.limit, LIST_NODES_DEFAULT_LIMIT, LIST_NODES_MAX_LIMIT)\n\n // Cursor takes precedence over legacy offset param\n const cursorOffset = args.cursor !== undefined\n ? decodeCursor(args.cursor)\n : ((args.offset as number) ?? 0)\n\n let nodes = await store.getAllNodes(productId)\n if (filterType) nodes = nodes.filter((n) => n.type === filterType)\n\n const total = nodes.length\n const slice = nodes.slice(cursorOffset, cursorOffset + limit)\n const nextOffset = cursorOffset + slice.length\n const nextCursor = nextOffset < total ? encodeCursor(nextOffset) : undefined\n\n const page = slice.map((n) => ({\n id: n.id,\n type: n.type,\n title: n.title,\n status: n.status,\n tags: n.tags,\n }))\n\n const body: Record<string, unknown> = { nodes: page, total, limit }\n if (nextCursor) body.next_cursor = nextCursor\n\n return text(JSON.stringify(body, null, 2))\n}\n\n/**\n * Export the full product graph as a UPG document: product metadata,\n * nodes, and edges, suitable for sync, backup, or import into a local\n * .upg file. Used by the `upg pull` CLI command and `apply_pull_changeset`\n * in the local MCP server.\n *\n * Supports cursor-based pagination for large products. Pass `next_cursor`\n * from a previous response as `cursor` to advance to the next page of nodes.\n * All edges are returned regardless of pagination (edges are typically far\n * fewer than nodes and safe to return in full).\n *\n * @returns JSON: `{ product, nodes, edges, total_nodes, limit, next_cursor? }`.\n * `next_cursor` is present when more node pages remain.\n * @throws textError when `product_id` is missing or the product is\n * not visible to the caller.\n * @atomicity atomic (read-only)\n * @warning For very large products (10 000+ nodes) iterate via `cursor`\n * plus `next_cursor` rather than relying on a single call. Every page\n * returns the full edge set, so deduplicate on the client when\n * assembling multiple pages.\n * @see apply_pull_changeset\n * @see get_product_graph\n * @see list_nodes\n */\nexport const exportUpgDocument: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError(`Missing required parameter: product_id`)\n const productId = args.product_id as string\n const product = await store.getProduct(productId)\n if (!product) return textError(`Product not found: ${productId}`)\n\n const limit = clampLimit(args.limit, EXPORT_DOC_DEFAULT_LIMIT, EXPORT_DOC_MAX_LIMIT)\n const cursorOffset = decodeCursor(args.cursor)\n\n const allNodes = await store.getAllNodes(productId)\n const edges = await store.getAllEdges(productId)\n\n const totalNodes = allNodes.length\n const nodeSlice = allNodes.slice(cursorOffset, cursorOffset + limit)\n const nextOffset = cursorOffset + nodeSlice.length\n const nextCursor = nextOffset < totalNodes ? encodeCursor(nextOffset) : undefined\n\n const body: Record<string, unknown> = {\n product,\n nodes: nodeSlice,\n edges,\n total_nodes: totalNodes,\n limit,\n }\n if (nextCursor) body.next_cursor = nextCursor\n\n return text(JSON.stringify(body, null, 2))\n}\n\n/**\n * Read one node with full properties and every connected edge, separated\n * into `edges_out` and `edges_in`. Edge entries carry the neighbour node's\n * title for a single-call human-readable view.\n *\n * @returns JSON: `{ node, edges_out, edges_in }`. Errors with\n * `Node not found: <id>` for unknown ids.\n * @throws textError when `node_id` is missing or the node does\n * not exist (or the caller has no access; RLS shares the same shape for\n * both).\n * @atomicity atomic (read-only)\n * @see list_nodes\n * @see get_nodes\n * @see search_nodes\n * @see query\n */\nexport const getNode: ToolHandler = async (args, { store }) => {\n if (!args.node_id) return textError(`Missing required parameter: node_id`)\n const nid = args.node_id as string\n const node = await store.getNode(nid)\n if (!node) return textError(`Node not found: ${nid}`)\n\n const edges = await store.getEdgesForNode(nid)\n const edgesOut = []\n const edgesIn = []\n\n for (const e of edges) {\n if (e.source === nid) {\n const targetNode = await store.getNode(e.target)\n edgesOut.push({ ...e, target_title: targetNode?.title ?? '(unknown)' })\n }\n if (e.target === nid) {\n const sourceNode = await store.getNode(e.source)\n edgesIn.push({ ...e, source_title: sourceNode?.title ?? '(unknown)' })\n }\n }\n\n return text(JSON.stringify({ node, edges_out: edgesOut, edges_in: edgesIn }, null, 2))\n}\n\n/**\n * Batch-read up to 50 nodes by id, each with their incident edges. Pass\n * `compact_edges: true` to drop neighbour-title hydration for a leaner\n * payload (id/type/source/target only).\n *\n * @returns JSON: `{ nodes, total, not_found? }`. `not_found` lists any\n * requested ids that did not resolve and appears only when at least one\n * miss occurred.\n * @throws textError when `product_id` or `ids` is missing/empty,\n * or when `ids` exceeds 50.\n * @atomicity atomic (read-only)\n * @warning `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\n * nodes (~30% smaller wire payload).\n * @see get_node\n * @see list_nodes\n * @see query\n */\nexport const getNodes: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n const ids = args.ids as string[] | undefined\n if (!ids || ids.length === 0) return textError('Missing required parameter: ids')\n if (ids.length > 50) return textError('Maximum 50 IDs per batch')\n\n const productId = args.product_id as string\n const compact = (args.compact_edges as boolean) ?? false\n const allNodes = await store.getAllNodes(productId)\n const allEdges = await store.getAllEdges(productId)\n const nodeMap = new Map(allNodes.map((n) => [n.id, n]))\n\n const results: Array<Record<string, unknown>> = []\n const notFound: string[] = []\n\n for (const id of ids) {\n const node = nodeMap.get(id)\n if (!node) { notFound.push(id); continue }\n const edgesOut = allEdges.filter((e) => e.source === id)\n const edgesIn = allEdges.filter((e) => e.target === id)\n results.push({\n node,\n edges_out: compact\n ? edgesOut.map((e) => ({ id: e.id, type: e.type, source: e.source, target: e.target }))\n : edgesOut.map((e) => ({ ...e, target_title: nodeMap.get(e.target)?.title ?? '(unknown)' })),\n edges_in: compact\n ? edgesIn.map((e) => ({ id: e.id, type: e.type, source: e.source, target: e.target }))\n : edgesIn.map((e) => ({ ...e, source_title: nodeMap.get(e.source)?.title ?? '(unknown)' })),\n })\n }\n\n const resp: Record<string, unknown> = { nodes: results, total: results.length }\n if (notFound.length > 0) resp.not_found = notFound\n return text(JSON.stringify(resp, null, 2))\n}\n\n/**\n * Substring search over node titles plus descriptions. Title hits score 2,\n * description hits score 1, results sorted by score then truncated to\n * `limit` (default 20, max 100).\n *\n * @returns JSON: `{ results: Array<node & { match_field }>, total }`.\n * @throws textError when `product_id` or `query` is missing.\n * @atomicity atomic (read-only)\n * @warning RLS-bounded: only nodes in products the caller has read\n * access to participate. Substring match is case-insensitive and runs\n * in-memory after a full product fetch; for very large products this\n * can be heavy. A Postgres-side full-text index is a future optimisation.\n * @see list_nodes\n * @see get_node\n * @see query\n */\nexport const searchNodes: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError(`Missing required parameter: product_id`)\n if (!args.query) return textError(`Missing required parameter: query`)\n const productId = args.product_id as string\n const q = (args.query as string).toLowerCase()\n const filterType = args.type as string | undefined\n const limit = Math.min((args.limit as number) ?? 20, 100)\n\n let nodes = await store.getAllNodes(productId)\n if (filterType) nodes = nodes.filter((n) => n.type === filterType)\n\n const scored = nodes\n .map((n) => {\n const titleMatch = n.title.toLowerCase().includes(q)\n const descMatch = n.description?.toLowerCase().includes(q) ?? false\n if (!titleMatch && !descMatch) return null\n return {\n node: n,\n score: titleMatch ? 2 : 1,\n match_field: titleMatch ? 'title' : 'description',\n }\n })\n .filter((s): s is { node: (typeof nodes)[0]; score: number; match_field: string } => s !== null)\n .sort((a, b) => b.score - a.score)\n .slice(0, limit)\n\n return text(JSON.stringify({\n results: scored.map((s) => ({ ...s.node, match_field: s.match_field })),\n total: scored.length,\n }, null, 2))\n}\n\n/**\n * Create a new entity, optionally connected to a parent via an inferred\n * edge type. Lifecycle-aware: when `status` is omitted and the type has a\n * registered lifecycle, the initial phase is set automatically. When\n * `status` is provided but doesn't match the lifecycle's phases, the\n * response carries a `warning` (the node is still created).\n *\n * @returns JSON: `{ node, edge?, warning? }`. `edge` is null when no\n * `parent_id` is passed; `warning` is present on lifecycle/parent issues.\n * @throws textError when `product_id`, `type`, or `title` is\n * missing.\n * @atomicity atomic-with-rollback\n * @warning Pass `parent_id` to auto-create a containment edge with inferred\n * type; missing parents are reported via `warning` rather than failing\n * the create.\n * @see batch_create_nodes\n * @see update_node\n * @see get_entity_schema\n * @see list_entity_types\n * @see get_valid_children\n */\nexport const createNode: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError(`Missing required parameter: product_id`)\n if (!args.type) return textError(`Missing required parameter: type`)\n if (!args.title) return textError(`Missing required parameter: title`)\n\n // Entity-type validation: resolve aliases to canonical, refuse uncatalogued\n // types before the write lands. Matches the local MCP server's create_node.\n let resolvedType: ReturnType<typeof resolveEntityType>\n try {\n resolvedType = resolveEntityType(args.type)\n } catch (err) {\n if (err instanceof UnknownEntityTypeError) return textError(err.message)\n throw err\n }\n const canonicalType = resolvedType.canonical\n\n const productId = args.product_id as string\n const newNode: UPGBaseNode = {\n id: nodeId(),\n type: canonicalType as UPGEntityType,\n title: args.title as string,\n }\n if (args.description) newNode.description = args.description as string\n if (args.tags) newNode.tags = args.tags as string[]\n if (args.properties) newNode.properties = args.properties as Record<string, unknown>\n\n const nodeType = canonicalType\n const nId = newNode.id\n const properties = newNode.properties\n\n // Property type validation: refuse declared-but-mismatched-type values\n // before the write lands. Matches the local MCP server's create_node.\n const { violations } = checkPropertyTypes(nodeType, properties)\n if (violations.length > 0) {\n return textError(renderPropertyTypeWarning(nodeType, violations)!)\n }\n\n // Length caps: soft warnings only, never refusals.\n const { warnings: lengthWarnings } = checkLengthCaps({\n title: args.title as string,\n description: args.description as string | undefined,\n properties,\n })\n\n const warnings: string[] = [...lengthWarnings]\n if (resolvedType.alias) {\n warnings.push(`Type \"${resolvedType.alias.from}\" is deprecated; using canonical \"${resolvedType.alias.to}\".`)\n }\n if (args.status) {\n newNode.status = args.status as string\n const lifecycle = getLifecycleForType(nodeType)\n if (lifecycle) {\n const validPhases = lifecycle.phases.map((p) => p.id)\n if (!validPhases.includes(newNode.status)) {\n warnings.push(`Status \"${newNode.status}\" is not a valid phase for type \"${nodeType}\". Valid phases: [${validPhases.join(', ')}]`)\n }\n }\n } else {\n const lifecycle = getLifecycleForType(nodeType)\n if (lifecycle) newNode.status = lifecycle.initial_phase\n }\n await store.addNode(productId, newNode)\n\n let edge = null\n const parentId = args.parent_id as string | undefined\n if (parentId) {\n const parent = await store.getNode(parentId)\n if (!parent) {\n warnings.push(`Parent node ${parentId} not found. Node created without edge.`)\n } else {\n // Catalog-strict parent-edge inference. Do NOT fabricate a\n // `_contains_` edge: the node still lands, but a non-canonical pair\n // yields a warning so the caller can wire an explicit edge type.\n const inference = inferEdgeTypeWithTier(parent.type, nodeType)\n if (!inference.ok) {\n const suggestion = inference.suggestions.length > 0\n ? ` Suggestions: ${inference.suggestions.map((s) => `${s.source_type} → ${s.target_type} (${s.edge_type})`).join('; ')}.`\n : ''\n warnings.push(`Parent edge not created; no canonical edge for ${parent.type} → ${nodeType}.${suggestion}`)\n } else {\n edge = { id: edgeId(), source: parentId, target: nId, type: inference.edgeType }\n await store.addEdge(productId, edge as Parameters<typeof store.addEdge>[1])\n }\n }\n }\n\n const result: Record<string, unknown> = { node: newNode, edge }\n if (warnings.length > 0) result.warning = warnings.join(' | ')\n return text(JSON.stringify(result, null, 2))\n}\n\n/**\n * Merge-update a node's fields. Unspecified fields are preserved.\n * Lifecycle-aware: when `status` changes to a phase not declared in the\n * type's lifecycle, the response carries a `warning` (update still\n * applied).\n *\n * @returns JSON: `{ node: updatedNode, warning? }`. Errors propagate from\n * the store (e.g. unknown node id).\n * @throws textError when `node_id` is missing or the store\n * rejects the update (unknown id).\n * @atomicity atomic-with-rollback\n * @warning Lifecycle-aware: invalid status values produce a `warning` but\n * the update still applies. For type changes, use `migrate_type`\n * instead; direct type mutation via this tool is unsupported.\n * @see migrate_type\n * @see batch_update_nodes\n * @see get_lifecycle\n */\nexport const updateNode: ToolHandler = async (args, { store }) => {\n if (!args.node_id) return textError(`Missing required parameter: node_id`)\n const nid = args.node_id as string\n const patch: Record<string, unknown> = {}\n if (args.title !== undefined) patch.title = args.title\n if (args.description !== undefined) patch.description = args.description\n if (args.tags !== undefined) patch.tags = args.tags\n if (args.status !== undefined) patch.status = args.status\n if (args.properties !== undefined) patch.properties = args.properties\n\n const warnings: string[] = []\n // Resolve the entity type once if either a status or property check needs it.\n let entityType: string | undefined\n if (args.status !== undefined || args.properties !== undefined) {\n const existingNode = await store.getNode(nid)\n entityType = existingNode?.type\n }\n\n if (args.status !== undefined && entityType) {\n const lifecycle = getLifecycleForType(entityType)\n if (lifecycle) {\n const validPhases = lifecycle.phases.map((p) => p.id)\n if (!validPhases.includes(args.status as string)) {\n warnings.push(`Status \"${args.status}\" is not a valid phase for type \"${entityType}\". Valid phases: [${validPhases.join(', ')}]`)\n }\n }\n }\n\n if (args.properties !== undefined && entityType) {\n // Property type validation: refuse declared-but-mismatched-type values.\n const { violations } = checkPropertyTypes(entityType, args.properties as Record<string, unknown>)\n if (violations.length > 0) {\n return textError(renderPropertyTypeWarning(entityType, violations)!)\n }\n }\n\n // Length caps: soft warnings only, never refusals.\n const { warnings: lengthWarnings } = checkLengthCaps({\n title: args.title as string | undefined,\n description: args.description as string | undefined,\n properties: args.properties as Record<string, unknown> | undefined,\n })\n warnings.push(...lengthWarnings)\n\n try {\n const updated = await store.updateNode(nid, patch)\n const result: Record<string, unknown> = { node: updated }\n if (warnings.length > 0) result.warning = warnings.join(' | ')\n return text(JSON.stringify(result, null, 2))\n } catch (err) {\n return textError((err as Error).message)\n }\n}\n\n/**\n * Delete a node and cascade-delete every connected edge in a single store\n * call. The response surfaces the dropped edge ids so the caller can\n * reconcile any local mirror.\n *\n * @returns JSON: `{ deleted_node_id, deleted_node_title, deleted_edge_ids }`.\n * Errors propagate from the store (e.g. unknown id).\n * @throws textError when `node_id` is missing or the store\n * rejects the deletion.\n * @atomicity atomic-with-rollback\n * @warning Cascade-deletes ALL incident edges, including cross-product\n * edges where the node is an endpoint. The operation is permanent (no\n * soft-delete or undo); the audit log records the removal. Pair with\n * `get_node` first if you need a snapshot.\n * @see batch_delete_nodes\n * @see get_node\n * @see deduplicate_nodes\n */\nexport const deleteNode: ToolHandler = async (args, { store }) => {\n if (!args.node_id) return textError(`Missing required parameter: node_id`)\n try {\n const { node, removedEdgeIds } = await store.removeNode(args.node_id as string)\n return text(JSON.stringify({\n deleted_node_id: node.id,\n deleted_node_title: node.title,\n deleted_edge_ids: removedEdgeIds,\n }, null, 2))\n } catch (err) {\n return textError((err as Error).message)\n }\n}\n\n/**\n * Reparent a node to a new parent within the same product. Removes any\n * existing containment edge where the node is the target, then creates a\n * new edge from `new_parent_id` to `node_id` with an inferred type. All\n * mutations run inside a single Postgres transaction.\n *\n * @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.\n * @throws textError when either node is missing, the nodes belong\n * to different products (cross-product reparenting is not allowed), or\n * the caller tries to move a node onto itself.\n * @atomicity atomic-with-rollback\n * @see batch_move_nodes\n * @see resolve_edge_for_pair\n * @see get_valid_children\n */\nexport const moveNode: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n if (!args.node_id) return textError('Missing required parameter: node_id')\n if (!args.new_parent_id) return textError('Missing required parameter: new_parent_id')\n\n const productId = args.product_id as string\n const nid = args.node_id as string\n const newParentId = args.new_parent_id as string\n\n if (nid === newParentId) return textError('Cannot move a node onto itself.')\n\n // Ownership checks (both nodes must exist and belong to this product)\n const node = await store.getNode(nid)\n if (!node) return textError(`Node not found: ${nid}`)\n if (node.product_id !== productId) return textError(`Node ${nid} does not belong to product ${productId}`)\n\n const newParent = await store.getNode(newParentId)\n if (!newParent) return textError(`New parent not found: ${newParentId}`)\n if (newParent.product_id !== productId) return textError(`New parent ${newParentId} does not belong to product ${productId}`)\n\n // Catalog-strict: validate the new containment edge BEFORE any mutation.\n // On a non-canonical pair the graph is left exactly as it started.\n const inference = inferEdgeTypeWithTier(newParent.type, node.type)\n if (!inference.ok) {\n const suggestion = inference.suggestions.length > 0\n ? ` Suggestions: ${inference.suggestions.map((s) => `${s.source_type} → ${s.target_type} (${s.edge_type})`).join('; ')}.`\n : ''\n return textError(`No canonical edge type for ${newParent.type} → ${node.type}.${suggestion} Reparenting refused.`)\n }\n const newEdgeId = edgeId()\n\n try {\n const result = await store.moveNode(productId, nid, newParentId, inference.edgeType, newEdgeId)\n return text(JSON.stringify(result, null, 2))\n } catch (err) {\n return textError((err as Error).message)\n }\n}\n\n/**\n * Merge a set of duplicate nodes into a canonical node and delete the\n * duplicates. All edge rebinding, self-loop cleanup, duplicate-edge removal,\n * property merge, and deletion run inside a single Postgres transaction\n * (all-or-nothing). Default `dry_run: true` previews what would change\n * without touching any data.\n *\n * @returns With `dry_run: true` (default): `{ canonical_id, duplicate_ids,\n * edges_to_rebind, nodes_to_delete, dry_run }`. With `dry_run: false`:\n * `{ canonical_id, merged_ids, rebound_edges, removed_self_loops,\n * removed_duplicate_edges, dry_run }`.\n * @throws 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\n * exist / does not belong to the product.\n * @atomicity atomic-with-rollback (all mutations committed or rolled back\n * together).\n * @warning Default `dry_run: true`; pass `dry_run: false` to commit. The\n * merge is permanent: duplicates are deleted, their edges rebound to\n * the canonical, self-loops removed, and duplicate edges deduplicated.\n * The change stands once committed (no undo); the audit log records\n * each merge for the retention window.\n * @see search_nodes\n * @see get_nodes\n * @see delete_node\n * @see validate_graph\n */\nexport const deduplicateNodes: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n if (!args.canonical_id) return textError('Missing required parameter: canonical_id')\n if (!args.duplicate_ids || !Array.isArray(args.duplicate_ids) || (args.duplicate_ids as string[]).length === 0)\n return textError('Missing required parameter: duplicate_ids (non-empty array)')\n\n const productId = args.product_id as string\n const canonicalId = args.canonical_id as string\n const duplicateIds = args.duplicate_ids as string[]\n const dryRun = (args.dry_run as boolean) ?? true\n\n if (duplicateIds.length > 20)\n return textError('Maximum 20 duplicate IDs per call')\n if (duplicateIds.includes(canonicalId))\n return textError('canonical_id must not appear in duplicate_ids')\n\n // Ownership validation: all nodes must exist and belong to product_id\n const allIds = [canonicalId, ...duplicateIds]\n for (const id of allIds) {\n const node = await store.getNode(id)\n if (!node) return textError(`Node not found: ${id}`)\n if (node.product_id !== productId)\n return textError(`Node ${id} does not belong to product ${productId}`)\n }\n\n if (dryRun) {\n // Count edges that would be rebound (all edges incident on any duplicate)\n let edgesToRebind = 0\n for (const dupId of duplicateIds) {\n const edges = await store.getEdgesForNode(dupId)\n edgesToRebind += edges.length\n }\n return text(JSON.stringify({\n canonical_id: canonicalId,\n duplicate_ids: duplicateIds,\n edges_to_rebind: edgesToRebind,\n nodes_to_delete: duplicateIds.length,\n dry_run: true,\n }, null, 2))\n }\n\n // Execute merge in a single Postgres transaction\n const result = await store.deduplicateNodes(productId, canonicalId, duplicateIds)\n return text(JSON.stringify({ ...result, dry_run: false }, null, 2))\n}\n\n/**\n * Whole-product graph dump: every node and edge, along with the product\n * row. Cloud-only convenience for full snapshots; expensive on large\n * products. Prefer `query` with a depth limit when you need a slice.\n *\n * @returns JSON: `{ product, nodes, edges }`.\n * @throws textError when `product_id` is missing or the product\n * is not visible to the caller.\n * @atomicity atomic (read-only)\n * @warning Returns the **entire** graph in one payload; for products\n * with thousands of nodes/edges this can be tens of MB. Prefer `query`\n * with a depth limit plus `include` projection for slices, or\n * `list_nodes` plus cursor pagination for full enumeration without the\n * wire-size hit.\n * @see query\n * @see list_nodes\n * @see get_graph_digest\n * @see get_graph_analytics\n */\nexport const getProductGraph: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError(`Missing required parameter: product_id`)\n const productId = args.product_id as string\n const product = await store.getProduct(productId)\n if (!product) return textError(`Product not found: ${productId}`)\n\n const nodes = await store.getAllNodes(productId)\n const edges = await store.getAllEdges(productId)\n\n return text(JSON.stringify({ product, nodes, edges }, null, 2))\n}\n","/**\n * Edge create / delete handlers.\n */\n\nimport { type ToolHandler, text, textError, type ToolResult } from '../lib/server-context.js'\nimport {\n inferEdgeTypeWithTier,\n validateEdgeTypePair,\n buildResolverHints,\n} from '@unified-product-graph/sdk/logic'\nimport { edgeId } from '../id-helpers.js'\n\n/**\n * Build an `isError` result whose body carries the UPG-505/UPG-515 resolver\n * enrichment (`anchor_hint` / `alternate_anchors` / `adjacent_edges`) for a\n * \"no canonical edge\" miss. Falls back to a plain `textError` when no\n * enrichment applies. Mirrors the local MCP server's `edgeResolverError` so\n * the failure boundary is identical on both servers.\n */\nfunction edgeResolverError(message: string, sourceType: string, targetType: string): ToolResult {\n const hints = buildResolverHints(sourceType, targetType)\n if (\n !hints.anchor_hint &&\n (!hints.alternate_anchors || hints.alternate_anchors.length === 0) &&\n (!hints.adjacent_edges || hints.adjacent_edges.length === 0)\n ) {\n return textError(message)\n }\n const body: Record<string, unknown> = {\n error: message,\n source_type: sourceType,\n target_type: targetType,\n ...hints,\n }\n return { content: [{ type: 'text', text: JSON.stringify(body, null, 2) }], isError: true }\n}\n\n/**\n * Create a relationship between two existing nodes. Edge type is inferred\n * from the source/target types when `type` is omitted. Inference is\n * catalog-strict: an unmapped pair is refused (with resolver hints) rather\n * than fabricating a `${source}_contains_${target}` edge, matching the\n * local MCP server's `create_edge`.\n *\n * @returns JSON: `{ edge: { id, source, target, type }, warning? }`.\n * @throws textError when `source_id`/`target_id` is missing, an endpoint\n * lookup fails, source and target resolve to the same node, an explicit\n * `type` violates the catalog's source/target pair, or no canonical edge\n * exists for the pair and no `type` was supplied (enriched with resolver\n * hints).\n * @atomicity atomic-with-rollback\n * @see resolve_edge_for_pair\n * @see list_edge_types\n * @see get_edge_type\n * @see batch_create_edges\n */\nexport const createEdge: ToolHandler = async (args, { store }) => {\n if (!args.source_id) return textError(`Missing required parameter: source_id`)\n if (!args.target_id) return textError(`Missing required parameter: target_id`)\n const sourceId = args.source_id as string\n const targetId = args.target_id as string\n const source = await store.getNode(sourceId)\n const target = await store.getNode(targetId)\n if (!source) return textError(`Source not found: ${sourceId}`)\n if (!target) return textError(`Target not found: ${targetId}`)\n\n // Refuse graph-topology self-loops up front. No canonical UPG edge type is\n // self-referential.\n if (sourceId === targetId) {\n return textError(\n `Self-loop refused: source and target resolve to the same node \"${sourceId}\". ` +\n `No canonical UPG edge type is self-referential. ` +\n `If you genuinely need a self-referential edge, file a spec proposal first.`,\n )\n }\n\n let edgeType: string\n let warning: string | undefined\n const explicitType = args.type as string | undefined\n if (explicitType) {\n // Verify a user-supplied canonical type against the catalog's expected\n // source/target pair. Non-canonical types fall through (surfaced later by\n // validate_graph as edge_drift).\n const pairCheck = validateEdgeTypePair(explicitType, source.type, target.type)\n if (!pairCheck.valid) return textError(pairCheck.reason!)\n edgeType = explicitType\n } else {\n const inference = inferEdgeTypeWithTier(source.type, target.type)\n if (!inference.ok) {\n const suggestion =\n inference.suggestions.length > 0\n ? ` Try one of: ${inference.suggestions\n .map((s) => `${s.source_type} → ${s.target_type} (${s.edge_type})`)\n .join('; ')}.`\n : ''\n return edgeResolverError(\n `No canonical edge type for ${source.type} → ${target.type}.${suggestion} Pass an explicit \\`type\\` if you need a non-catalog edge.`,\n source.type,\n target.type,\n )\n }\n edgeType = inference.edgeType\n if (inference.aliased) {\n warning = `Edge inferred from canonical (${inference.aliased.map((a) => `${a.from} → ${a.to}`).join(', ')}).`\n }\n }\n\n const edge = { id: edgeId(), source: sourceId, target: targetId, type: edgeType }\n\n try {\n await store.addEdge(source.product_id, edge as Parameters<typeof store.addEdge>[1])\n const body: Record<string, unknown> = { edge }\n if (warning) body.warning = warning\n return text(JSON.stringify(body, null, 2))\n } catch (err) {\n return textError((err as Error).message)\n }\n}\n\n/**\n * Drop a single edge by id.\n *\n * @returns JSON: `{ deleted_edge_id }`.\n * @throws textError when `edge_id` is missing or unknown.\n * @atomicity atomic-with-rollback\n * @see batch_delete_edges\n * @see export_edges\n */\nexport const deleteEdge: ToolHandler = async (args, { store }) => {\n if (!args.edge_id) return textError(`Missing required parameter: edge_id`)\n try {\n const edge = await store.removeEdge(args.edge_id as string)\n return text(JSON.stringify({ deleted_edge_id: edge.id }, null, 2))\n } catch (err) {\n return textError((err as Error).message)\n }\n}\n\n/**\n * Flat enumeration of all edges for a product, optionally filtered by type.\n * Returns the full edge set in one payload; for large products page via\n * `query` with edge filters.\n *\n * @returns JSON: `{ edges: [{ id, source, target, type }], total: number }`.\n * @throws textError when `product_id` is missing or the store rejects the read.\n * @atomicity atomic (read-only)\n * @see list_edge_types\n * @see rename_edge_type\n * @see query\n */\nexport const exportEdges: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n const types = args.types as string[] | undefined\n try {\n const edges = await store.exportEdges(args.product_id as string, types)\n return text(JSON.stringify({ edges, total: edges.length }, null, 2))\n } catch (err) {\n return textError((err as Error).message)\n }\n}\n\n/**\n * Rename all edges of one type to another across a product. Default\n * `dry_run: true`; pass `dry_run: false` to commit. Idempotent: a second\n * commit after a successful rename reports `affected: 0`. Catalog-aware\n * migrations should look up the canonical `from → to` via `list_edge_migrations`.\n *\n * @returns JSON: `{ from, to, affected: number, dry_run: boolean }`.\n * @throws textError when `product_id`, `from`, or `to` is missing.\n * @atomicity atomic-with-rollback (write path)\n * @see list_edge_types\n * @see get_edge_type\n * @see export_edges\n * @see migrate_type\n */\nexport const renameEdgeType: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n if (!args.from) return textError('Missing required parameter: from')\n if (!args.to) return textError('Missing required parameter: to')\n const dryRun = args.dry_run !== undefined ? Boolean(args.dry_run) : true\n try {\n const affected = await store.renameEdgeType(\n args.product_id as string,\n args.from as string,\n args.to as string,\n dryRun,\n )\n return text(JSON.stringify({ from: args.from, to: args.to, affected, dry_run: dryRun }, null, 2))\n } catch (err) {\n return textError((err as Error).message)\n }\n}\n","/**\n * Framework-exercise tools (cloud parity, UPG 0.8.6): apply a framework to a set\n * of entities and record each entity's result on the exercise's `includes` edge\n * — the value lives on the edge, not the entity node. Postgres-backed mirror of\n * the local server's `apply_framework` / `score_entity`. See ADR\n * 2026-06-02-framework-exercises and migration 005_edge_properties.sql.\n */\nimport type { UPGEntityType } from '@unified-product-graph/core'\nimport { UPG_FRAMEWORKS_BY_ID } from '@unified-product-graph/core'\nimport {\n frameworkInputKeys,\n FRAMEWORK_EXERCISE_INCLUDES_EDGE,\n} from '@unified-product-graph/sdk'\nimport { type ToolHandler, text, textError } from '../lib/server-context.js'\nimport { nodeId, edgeId } from '../id-helpers.js'\n\n/**\n * Create a framework_exercise and an `includes` edge to each entity it scores,\n * scoped to one product. Edges start blank; fill results with `score_entity`.\n *\n * @returns JSON: `{ exercise_id, exercise, included: [{ edge_id, entity_id }], warnings }`.\n * @throws textError on a missing product_id/framework_id or an unknown framework_id.\n * @atomicity per-write atomic; the exercise node and each includes edge commit\n * independently (a target that cannot be included is reported in `warnings`).\n * @see score_entity\n */\nexport const applyFramework: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n const frameworkId = args.framework_id as string | undefined\n if (!frameworkId) {\n return textError('Missing required parameter: framework_id (e.g. \"moscow\", \"rice-scoring\")')\n }\n const framework = UPG_FRAMEWORKS_BY_ID[frameworkId]\n if (!framework) {\n return textError(\n `Unknown framework: \"${frameworkId}\". Pass a framework id from the catalog ` +\n `(e.g. 'moscow', 'rice-scoring', 'kano-model').`,\n )\n }\n\n const productId = args.product_id as string\n const warnings: string[] = []\n try {\n const exercise = await store.addNode(productId, {\n id: nodeId(),\n type: 'framework_exercise' as UPGEntityType,\n title: (args.title as string | undefined) ?? `${framework.name} exercise`,\n status: (args.status as string | undefined) ?? 'draft',\n properties: { framework_id: frameworkId },\n })\n\n const included: Array<{ edge_id: string; entity_id: string }> = []\n for (const entityId of (args.entity_ids as string[] | undefined) ?? []) {\n const target = await store.getNode(entityId)\n if (!target) {\n warnings.push(`Could not include ${entityId}: target not found`)\n continue\n }\n const edge = {\n id: edgeId(),\n source: exercise.id,\n target: entityId,\n type: FRAMEWORK_EXERCISE_INCLUDES_EDGE,\n }\n try {\n await store.addEdge(productId, edge as Parameters<typeof store.addEdge>[1])\n included.push({ edge_id: edge.id, entity_id: entityId })\n } catch (err) {\n warnings.push(`Could not include ${entityId}: ${(err as Error).message}`)\n }\n }\n\n return text(\n JSON.stringify(\n { exercise_id: exercise.id, exercise, included, warnings },\n null,\n 2,\n ),\n )\n } catch (err) {\n return textError((err as Error).message)\n }\n}\n\n/**\n * Record a framework's result for one entity on the exercise's `includes` edge.\n * Auto-includes the entity when it is not yet in scope. Merges into existing\n * edge properties unless `replace` is set. Product is resolved from the\n * exercise node, so no product_id is needed.\n *\n * @returns JSON: `{ edge, warnings }`.\n * @throws textError when the exercise/entity is missing or the node is not a\n * framework_exercise.\n * @atomicity atomic-with-rollback (single edge upsert).\n * @see apply_framework\n */\nexport const scoreEntity: ToolHandler = async (args, { store }) => {\n const exerciseId = args.exercise_id as string | undefined\n const entityId = args.entity_id as string | undefined\n const values = args.values as Record<string, unknown> | undefined\n if (!exerciseId) return textError('Missing required parameter: exercise_id')\n if (!entityId) return textError('Missing required parameter: entity_id')\n if (!values || typeof values !== 'object' || Array.isArray(values)) {\n return textError(\n 'Missing required parameter: values (object of input → value, e.g. { \"moscow\": \"must\" })',\n )\n }\n\n const exercise = await store.getNode(exerciseId)\n if (!exercise) return textError(`Exercise not found: ${exerciseId}`)\n if (exercise.type !== 'framework_exercise') {\n return textError(`Node ${exerciseId} is a ${exercise.type}, not a framework_exercise.`)\n }\n\n const warnings: string[] = []\n const frameworkId = (exercise.properties as { framework_id?: string } | undefined)?.framework_id\n const framework = frameworkId ? UPG_FRAMEWORKS_BY_ID[frameworkId] : undefined\n if (framework) {\n const known = new Set(frameworkInputKeys(framework))\n if (known.size > 0) {\n const unknown = Object.keys(values).filter((k) => !known.has(k))\n if (unknown.length > 0) {\n warnings.push(\n `Value key(s) not declared by ${frameworkId}: ${unknown.join(', ')}. Stored anyway (permissive).`,\n )\n }\n }\n }\n\n try {\n const edges = await store.getEdgesForNode(exerciseId)\n const existing = edges.find(\n (e) =>\n e.type === FRAMEWORK_EXERCISE_INCLUDES_EDGE &&\n e.source === exerciseId &&\n e.target === entityId,\n )\n if (existing) {\n const edge = await store.setEdgeProperties(existing.id, values, { merge: !args.replace })\n return text(JSON.stringify({ edge, warnings }, null, 2))\n }\n\n // Not yet included: create the edge carrying the values in one step.\n const target = await store.getNode(entityId)\n if (!target) return textError(`Entity not found: ${entityId}`)\n const edge = {\n id: edgeId(),\n source: exerciseId,\n target: entityId,\n type: FRAMEWORK_EXERCISE_INCLUDES_EDGE,\n properties: values,\n }\n await store.addEdge(exercise.product_id, edge as Parameters<typeof store.addEdge>[1])\n return text(JSON.stringify({ edge, warnings }, null, 2))\n } catch (err) {\n return textError((err as Error).message)\n }\n}\n","/**\n * Product-area handlers. Read: list, subgraph BFS, summary. Write: create area.\n */\n\nimport type { UPGBaseNode, UPGEntityType } from '@unified-product-graph/core'\nimport { type ToolHandler, text, textError } from '../lib/server-context.js'\nimport { nodeId } from '../id-helpers.js'\n\n/**\n * List the product-area entities registered for a product. Ordered by the\n * store's default (typically created-at).\n *\n * @returns JSON: `{ areas, total }`.\n * @throws textError when `product_id` is missing.\n * @atomicity atomic (read-only)\n * @see get_area_graph\n * @see get_area_context\n * @see create_area\n */\nexport const listProductAreas: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError(`Missing required parameter: product_id`)\n const productId = args.product_id as string\n const areas = await store.listProductAreas(productId)\n return text(JSON.stringify({ areas, total: areas.length }, null, 2))\n}\n\n/**\n * BFS the subgraph rooted at a product-area. Depth default 3, clamped to 1..10.\n * Cheaper than a full `query` when you only need the area's surroundings.\n *\n * @returns JSON: `{ area, nodes, edges }`.\n * @throws textError when `product_id` or `area_id` is missing, or store rejects.\n * @atomicity atomic (read-only)\n * @see list_product_areas\n * @see get_area_context\n * @see query\n */\nexport const getAreaGraph: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError(`Missing required parameter: product_id`)\n if (!args.area_id) return textError(`Missing required parameter: area_id`)\n const productId = args.product_id as string\n const areaId = args.area_id as string\n const maxDepth = Math.min(Math.max((args.depth as number) ?? 3, 1), 10)\n\n try {\n const result = await store.getAreaGraph(productId, areaId, maxDepth)\n return text(JSON.stringify(result, null, 2))\n } catch (err) {\n return textError((err as Error).message)\n }\n}\n\n/**\n * Create a product area (type `area`) under a product. Top-level\n * organisational unit; the \"who owns what\" axis. Delegates to `store.addNode`.\n *\n * @returns JSON: `{ node }`.\n * @throws textError when `product_id` or `title` is missing.\n * @atomicity atomic\n * @see list_product_areas\n * @see get_area_context\n */\nexport const createArea: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n if (!args.title) return textError('Missing required parameter: title')\n\n const productId = args.product_id as string\n const areaNode: UPGBaseNode = {\n id: nodeId(),\n type: 'area' as UPGEntityType,\n title: args.title as string,\n }\n if (args.description) areaNode.description = args.description as string\n\n try {\n const created = await store.addNode(productId, areaNode)\n return text(JSON.stringify({ node: created }, null, 2))\n } catch (err) {\n return textError((err as Error).message)\n }\n}\n\n/**\n * Summarise a product area: entity counts by type, child-area count, and\n * description. Traverses containment edges up to depth 2.\n *\n * @returns JSON: `{ area: { id, title, description }, entity_counts, total_entities, child_areas }`.\n * @throws textError when `product_id` or `area_id` is missing, or the area lookup fails.\n * @atomicity atomic (read-only)\n * @see create_area\n * @see get_area_graph\n */\nexport const getAreaContext: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n if (!args.area_id) return textError('Missing required parameter: area_id')\n\n const productId = args.product_id as string\n const areaId = args.area_id as string\n\n const areaNode = await store.getNode(areaId)\n if (!areaNode) return textError(`Area node not found: ${areaId}`)\n if (areaNode.product_id !== productId) {\n return textError(`Area node ${areaId} does not belong to product ${productId}`)\n }\n\n try {\n // Count descendants directly; bypasses the product_area-only restriction of getAreaGraph\n const typeCounts = await store.getDescendantTypeCounts(productId, areaId, 2)\n\n const entity_counts: Record<string, number> = {}\n let child_areas = 0\n\n for (const { type: t, count } of typeCounts) {\n entity_counts[t] = count\n if (t === 'product_area' || t === 'area') child_areas += count\n }\n\n const total_entities = Object.values(entity_counts).reduce((sum, n) => sum + n, 0)\n\n return text(JSON.stringify({\n area: {\n id: areaNode.id,\n title: areaNode.title,\n description: areaNode.description ?? null,\n },\n entity_counts,\n total_entities,\n child_areas,\n }, null, 2))\n } catch (err) {\n return textError((err as Error).message)\n }\n}\n","/**\n * Schema introspection. Returns expected properties and valid edges\n * (in and out) for any UPG entity type. Delegates to\n * `@unified-product-graph/mcp-tooling`'s `buildEntitySchema`. Includes\n * alias resolution (e.g. `jtbd → job`).\n */\n\nimport { buildEntitySchema, UnknownEntityTypeError } from '@unified-product-graph/mcp-tooling'\nimport { type ToolHandler, text, textError } from '../lib/server-context.js'\n\n/**\n * Resolve the spec contract for an entity type: domain it belongs to,\n * expected properties + types/descriptions, valid edge types both\n * outbound and inbound, and lifecycle phases when registered. Aliases\n * deprecated synonyms (e.g. `jtbd → job`) and surfaces an `alias_of`\n * trail so the caller can warn.\n *\n * @returns JSON: `{ type, alias_of?, domain, expected_properties,\n * edges_out, edges_in, phases?, initial_phase?, terminal_phases?,\n * domain_guide? }`.\n * @throws textError when `type` is missing or unknown\n * (`UnknownEntityTypeError`).\n * @atomicity atomic (read-only)\n * @see get_entity_meta\n * @see list_entity_types\n * @see get_valid_children\n * @see get_lifecycle\n * @see get_domain_guide\n * @see list_edge_types\n * @see create_node\n */\nexport const getEntitySchema: ToolHandler = async (args) => {\n const entityType = args.type as string | undefined\n if (!entityType) return textError('Missing required parameter: type')\n\n try {\n const schema = buildEntitySchema(entityType)\n return text(JSON.stringify(schema, null, 2))\n } catch (err) {\n if (err instanceof UnknownEntityTypeError) return textError(err.message)\n throw err\n }\n}\n","/**\n * Multi-user collaboration: comments and role-based access. Cloud-only.\n */\n\nimport { type ToolHandler, text, textError } from '../lib/server-context.js'\n\n/**\n * Attach a comment to a node. Scoped to product and user; the cloud\n * schema enforces both joins.\n *\n * @returns JSON: `{ comment: { id, product_id, node_id, user_id, body, created_at } }`.\n * @throws textError when `product_id`, `node_id`, `user_id`, or `body` is missing.\n * @atomicity atomic\n * @warning `user_id` MUST resolve to a member of the product's collaborator set,\n * or downstream RLS rejects the insert.\n * @see list_comments\n * @see list_collaborators\n * @see grant_access\n */\nexport const addComment: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError(`Missing required parameter: product_id`)\n if (!args.node_id) return textError(`Missing required parameter: node_id`)\n if (!args.user_id) return textError(`Missing required parameter: user_id`)\n if (!args.body) return textError(`Missing required parameter: body`)\n const comment = await store.addComment(\n args.product_id as string,\n args.node_id as string,\n args.user_id as string,\n args.body as string,\n )\n return text(JSON.stringify({ comment }, null, 2))\n}\n\n/**\n * List the comment thread for a single node, ordered most-recent-last.\n *\n * @returns JSON: `{ comments: Comment[] }`.\n * @throws textError when `node_id` is missing.\n * @atomicity atomic (read-only)\n * @warning RLS-bounded; an empty array can mean \"no comments\" or \"no access\".\n * @see add_comment\n * @see list_collaborators\n */\nexport const listComments: ToolHandler = async (args, { store }) => {\n if (!args.node_id) return textError(`Missing required parameter: node_id`)\n const comments = await store.listComments(args.node_id as string)\n return text(JSON.stringify({ comments }, null, 2))\n}\n\n/**\n * Grant a user a role on a product. RBAC tier mapping is enforced\n * downstream of the store. Idempotent: same-role re-grants are a no-op;\n * different-role re-grants overwrite the previous role.\n *\n * @returns JSON: `{ granted: { product_id, user_id, role } }`.\n * @throws textError when `product_id`, `user_id`, or `role` is missing.\n * @atomicity atomic\n * @warning Billing-relevant: collaborator count typically drives plan tier;\n * a grant may trigger a tier upgrade or hit a seat-limit cap.\n * @see list_collaborators\n * @see add_comment\n */\nexport const grantAccess: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError(`Missing required parameter: product_id`)\n if (!args.user_id) return textError(`Missing required parameter: user_id`)\n if (!args.role) return textError(`Missing required parameter: role`)\n await store.grantAccess(\n args.product_id as string,\n args.user_id as string,\n args.role as string,\n )\n return text(JSON.stringify({\n granted: {\n product_id: args.product_id,\n user_id: args.user_id,\n role: args.role,\n },\n }, null, 2))\n}\n\n/**\n * List every user with explicit access to a product. Returns explicit\n * grants only; the product owner is implicit and may be omitted depending\n * on schema. Useful as a pre-check for seat-count before `grant_access`.\n *\n * @returns JSON: `{ collaborators: Array<{ user_id, role, granted_at }> }`.\n * @throws textError when `product_id` is missing.\n * @atomicity atomic (read-only)\n * @see grant_access\n * @see list_comments\n */\nexport const listCollaborators: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError(`Missing required parameter: product_id`)\n const collaborators = await store.listCollaborators(args.product_id as string)\n return text(JSON.stringify({ collaborators }, null, 2))\n}\n","/**\n * Postgres-side analytics aggregator over a product graph. Cloud-only;\n * heavier than `get_graph_digest`.\n */\n\nimport { type ToolHandler, text, textError } from '../lib/server-context.js'\n\n/**\n * Run the analytics aggregator on a product. Heavier than\n * `get_graph_digest`; results may lag recent writes when the aggregation\n * is cached at the storage layer.\n *\n * @returns JSON: `{ product: { id, title }, analytics }`.\n * @throws textError when `product_id` is missing or the product is invisible\n * to the caller (RLS-bounded; \"not found\" and \"no access\" share wording).\n * @atomicity atomic (read-only)\n * @see get_graph_digest\n * @see get_product_context\n * @see get_audit_log\n */\nexport const getGraphAnalytics: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError(`Missing required parameter: product_id`)\n const productId = args.product_id as string\n const product = await store.getProduct(productId)\n if (!product) return textError(`Product not found: ${productId}`)\n const analytics = await store.getGraphAnalytics(productId)\n return text(JSON.stringify({\n product: { id: product.id, title: product.title },\n analytics,\n }, null, 2))\n}\n","/**\n * Webhook registration. Cloud-only event sink for external systems\n * subscribing to graph mutations.\n */\n\nimport { type ToolHandler, text, textError } from '../lib/server-context.js'\n\n/**\n * Register a webhook endpoint for a product. `event` selects which\n * mutations dispatch (e.g. `node.created`, `edge.deleted`); `secret` is\n * stored alongside the registration for outgoing-request signing.\n *\n * Delivery: at-least-once with exponential backoff. Receivers MUST be\n * idempotent on `webhook.id` plus payload. Permanent 4xx eventually\n * disables the registration. Plan-tier may cap webhook count per product;\n * pre-check via `list_webhooks`.\n *\n * @returns JSON: `{ webhook: { id, product_id, event, url, secret?, created_at } }`.\n * @throws textError when `product_id`, `event`, or `url` is missing.\n * @atomicity atomic\n * @see list_webhooks\n * @see remove_webhook\n */\nexport const registerWebhook: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError(`Missing required parameter: product_id`)\n if (!args.event) return textError(`Missing required parameter: event`)\n if (!args.url) return textError(`Missing required parameter: url`)\n const webhook = await store.registerWebhook(\n args.product_id as string,\n args.event as string,\n args.url as string,\n args.secret as string | undefined,\n )\n return text(JSON.stringify({ webhook }, null, 2))\n}\n\n/**\n * List every webhook registered for a product.\n *\n * @returns JSON: `{ webhooks: Webhook[] }`.\n * @throws textError when `product_id` is missing.\n * @atomicity atomic (read-only)\n * @see register_webhook\n * @see remove_webhook\n */\nexport const listWebhooks: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError(`Missing required parameter: product_id`)\n const webhooks = await store.listWebhooks(args.product_id as string)\n return text(JSON.stringify({ webhooks }, null, 2))\n}\n\n/**\n * Drop a webhook registration by id. In-flight queued deliveries may\n * still fire after this returns; receivers should treat late events as\n * no-ops while tracking lifecycle.\n *\n * @returns JSON: `{ removed: <webhook_id> }`.\n * @throws textError when `webhook_id` is missing or the store rejects the deletion.\n * @atomicity atomic\n * @see register_webhook\n * @see list_webhooks\n */\nexport const removeWebhook: ToolHandler = async (args, { store }) => {\n if (!args.webhook_id) return textError(`Missing required parameter: webhook_id`)\n try {\n await store.removeWebhook(args.webhook_id as string)\n return text(JSON.stringify({ removed: args.webhook_id }, null, 2))\n } catch (err) {\n return textError((err as Error).message)\n }\n}\n","/**\n * Spec introspection handlers. Read-only snapshot of `@unified-product-graph/core`\n * compiled at server boot: playbooks, approaches, domain guides, frameworks,\n * edge catalogue, regions, lenses, type labels, entity meta, anti-patterns,\n * benchmarks, lifecycles, scales, migrations.\n *\n * Every handler is atomic and ignores `CloudContext`; the parameter satisfies\n * `ToolHandler<CloudContext>`. See `TOOLS.md` for the generated per-tool reference.\n */\n\nimport {\n UPG_PLAYBOOKS,\n UPG_APPROACHES,\n UPG_APPROACHES_BY_ID,\n REFLECT_MODES,\n UPG_DOMAIN_GUIDES,\n UPG_DOMAINS,\n UPG_FRAMEWORKS,\n UPG_FRAMEWORKS_BY_ID,\n UPG_EDGE_CATALOG,\n UPG_REGIONS,\n UPG_REGION_MAP,\n UPG_REGION_COUNT,\n UPG_LENSES,\n UPG_TYPE_LABELS,\n UPG_TYPE_LABELS_MAP,\n UPG_VALID_CHILDREN,\n UPG_CROSS_EDGE_TYPES,\n UPG_VERSION,\n MARKDOWN_FORMAT_VERSION,\n UPG_ENTITY_COUNT,\n UPG_DOMAIN_COUNT,\n UPG_EDGE_COUNT,\n UPG_ENTITY_META,\n UPG_ENTITY_META_BY_NAME,\n UPG_ENTITY_TO_DOMAIN,\n UPG_ANTI_PATTERNS,\n UPG_COUNT_BENCHMARKS,\n UPG_RELATIONSHIP_BENCHMARKS,\n UPG_RATIO_BENCHMARKS,\n UPG_DOMAIN_ACTIVATION,\n UPG_PRODUCT_STAGES,\n UPG_MIGRATIONS,\n UPG_EDGE_MIGRATIONS,\n UPG_SPLIT_MIGRATIONS,\n UPG_LIFECYCLES,\n UPG_LIFECYCLE_FREE_TYPES,\n UPG_LIFECYCLE_PLANNED_TYPES,\n UPG_SCALES,\n UPG_FRAMEWORK_CATEGORIES,\n UPG_STRUCTURE_PATTERNS,\n UPG_DOMAIN_RINGS,\n resolveContainmentEdge,\n resolveLabel,\n getRegionForEntityType,\n getLens,\n getVisibleTypes,\n getValidChildren,\n type UPGPlaybook,\n type UPGApproach,\n type UPGApproachId,\n type ReflectMode,\n type UPGRegion,\n type UPGDomainUsageGuide,\n type UPGFramework,\n type UPGEdgeDefinition,\n type UPGLens,\n type UPGTypeLabel,\n type EntityTypeMeta,\n type UPGEntityTypeMaturity,\n type UPGCuratedAntiPattern,\n type UPGAntiPatternSeverity,\n type UPGProductStage,\n type CountBenchmark,\n type RelationshipBenchmark,\n type RatioBenchmark,\n type DomainActivation,\n type UPGLifecycle,\n type UPGScaleDefinition,\n type UPGDomainRing,\n} from '@unified-product-graph/core'\n\nimport type { ToolHandler, ToolResult } from '../lib/server-context.js'\nimport { text, textError } from '../lib/server-context.js'\n\n// ── Pagination helpers (mirror list_nodes convention) ───────────────────────\n\nconst FRAMEWORKS_DEFAULT_LIMIT = 50\nconst FRAMEWORKS_MAX_LIMIT = 200\n\nfunction clampLimit(raw: unknown, def: number, max: number): number {\n const n = typeof raw === 'number' ? raw : def\n if (!Number.isFinite(n) || n <= 0) return def\n return Math.min(Math.floor(n), max)\n}\n\nfunction decodeCursor(raw: unknown): number {\n if (typeof raw !== 'string' || raw.length === 0) return 0\n // Cursor format: base64-encoded \"offset:N\". Tolerant: fall back to 0 on\n // malformed input rather than erroring (matches `list_nodes` UX).\n try {\n const decoded = Buffer.from(raw, 'base64').toString('utf-8')\n const m = decoded.match(/^offset:(\\d+)$/)\n if (!m) return 0\n return Number.parseInt(m[1], 10)\n } catch {\n return 0\n }\n}\n\nfunction encodeCursor(offset: number): string {\n return Buffer.from(`offset:${offset}`, 'utf-8').toString('base64')\n}\n\n// ── Playbooks ─────────────────────────────────────────────────────\n\n/**\n * List every canonical UPGPlaybook shipped with `@unified-product-graph/core`,\n * optionally narrowed by region, canonical-only flag, or framework_id.\n * Returns full playbook records: id, name, version, description, region,\n * is_canonical, framework_id, target_anchor_entity, and the ordered\n * creation_sequence.\n *\n * - region: exact-match filter on `UPGPlaybook.region` (a UPGRegionId).\n * - canonical_only: when true, return only playbooks with `is_canonical: true`\n * (exactly one per region, restating W1 invariant).\n * - framework_id: exact-match filter on `UPGPlaybook.framework_id`. 3 playbooks\n * are framework-anchored at v0.3.0 (BMC, AARRR, build-measure-learn).\n *\n * Filters AND together. Result is the canonical array order from `UPG_PLAYBOOKS`\n * (canonical first within each region).\n *\n * @returns JSON: `{ count, playbooks: UPGPlaybook[] }`\n * @atomicity atomic (read-only)\n * @see get_playbook\n * @see list_regions\n * @see list_approaches\n * @see list_frameworks\n */\nexport const listPlaybooks: ToolHandler = (args): ToolResult => {\n const region = args.region as string | undefined\n const canonicalOnly = args.canonical_only as boolean | undefined\n const frameworkId = args.framework_id as string | undefined\n\n let playbooks: readonly UPGPlaybook[] = UPG_PLAYBOOKS\n if (region) playbooks = playbooks.filter((p) => p.region === region)\n if (canonicalOnly === true) playbooks = playbooks.filter((p) => p.is_canonical === true)\n if (frameworkId) playbooks = playbooks.filter((p) => p.framework_id === frameworkId)\n\n return text(JSON.stringify({ count: playbooks.length, playbooks }, null, 2))\n}\n\n/**\n * Return one canonical UPGPlaybook by id (e.g. \"playbook:strategy-outcomes\",\n * \"playbook:business-gtm-growth\"). Includes the ordered creation_sequence with\n * full step kinds and prompts.\n *\n * IDs are namespace-prefixed (`playbook:*`). Calling with an `approach:*` id\n * (or one of the 5 bare-verb approach ids) returns null; route via\n * `get_approach` for the approach catalog.\n *\n * @returns JSON: the full `UPGPlaybook` record.\n * @throws textError when `id` is missing or unknown.\n * @atomicity atomic (read-only)\n * @see list_playbooks\n * @see get_approach\n * @see get_framework\n * @see get_region\n */\nexport const getPlaybook: ToolHandler = (args): ToolResult => {\n const id = args.id as string | undefined\n if (!id) return textError('Missing required parameter: id')\n const playbook = UPG_PLAYBOOKS.find((p) => p.id === id)\n if (!playbook) return textError(`Unknown playbook id: ${id}`)\n return text(JSON.stringify(playbook, null, 2))\n}\n\n// ── Approaches ────────────────────────────────────────────────────\n\n/**\n * List the five canonical UPGApproach records shipped with\n * `@unified-product-graph/core`: Plan / Inspect / Prioritise / Trace /\n * Reflect. Returns the full approach record per entry (id, label,\n * description with cartographic framing, question_answered, signature_hint,\n * framework_id_examples).\n *\n * **Cartographic framing**: an approach is the *path of arrival* to a\n * region of the graph (final approach to an airport, coastline approach),\n * distinct from the strategy-meeting sense (\"what's our approach to this\n * problem?\").\n *\n * Optional `framework_id` filter narrows to approaches whose\n * `framework_id_examples` include the given id (discoverability surface;\n * full reverse lookup is on `UPGFramework.approach_ids`).\n *\n * @returns JSON: `{ count, approaches: UPGApproach[] }`\n * @atomicity atomic (read-only)\n * @see get_approach\n * @see plan\n * @see inspect\n * @see prioritise\n * @see trace\n * @see reflect\n * @see list_playbooks\n */\nexport const listApproaches: ToolHandler = (args): ToolResult => {\n const frameworkId = args.framework_id as string | undefined\n\n let approaches: readonly UPGApproach[] = UPG_APPROACHES\n if (frameworkId) {\n approaches = approaches.filter((a) =>\n a.framework_id_examples?.includes(frameworkId) ?? false,\n )\n }\n\n return text(JSON.stringify({ count: approaches.length, approaches }, null, 2))\n}\n\n/**\n * Return one canonical UPGApproach by id. Valid ids are the bare verbs\n * `'plan' | 'inspect' | 'prioritise' | 'trace' | 'reflect'`: the same\n * names as the verb-led MCP tools.\n *\n * @returns JSON: the full `UPGApproach` record.\n * @throws textError when `id` is missing or unknown.\n * @atomicity atomic (read-only)\n * @see list_approaches\n * @see plan\n * @see inspect\n * @see prioritise\n * @see trace\n * @see reflect\n */\nexport const getApproach: ToolHandler = (args): ToolResult => {\n const id = args.id as string | undefined\n if (!id) return textError('Missing required parameter: id')\n const approach = UPG_APPROACHES_BY_ID[id]\n if (!approach) {\n return textError(\n `Unknown approach id: ${id}. Valid ids: plan, inspect, prioritise, trace, reflect.`,\n )\n }\n return text(JSON.stringify(approach, null, 2))\n}\n\n// ── Approach verb handlers ────────────────────────────────────────\n//\n// Five bare-verb handlers (`plan`, `inspect`, `prioritise`, `trace`,\n// `reflect`) exposed as direct MCP tools (no `apply_*` prefix). At v0.3.0\n// every handler is a **definition lookup**: it validates inputs, looks up\n// the approach record, and returns the family-resemblance envelope\n// `{ approach_id, scope, generated_at, ...payload }` where `payload` echoes\n// the caller's invocation parameters plus the approach record. The LLM is\n// the executor; it reads the signature_hint and synthesises the structured\n// projection (Plan's coverage_score, Inspect's violations[], etc.).\n\nfunction approachEnvelope(\n approachId: UPGApproachId,\n scope: unknown,\n payload: Record<string, unknown>,\n): ReturnType<typeof text> {\n const approach = UPG_APPROACHES_BY_ID[approachId]\n return text(\n JSON.stringify(\n {\n approach_id: approachId,\n scope,\n generated_at: new Date().toISOString(),\n approach,\n ...payload,\n },\n null,\n 2,\n ),\n )\n}\n\n/**\n * `plan`: the path of arrival to \"what should I build next?\".\n *\n * v0.3.0 ships as a definition lookup. Returns the Plan approach record\n * wrapped in the family-resemblance envelope, with the caller's `region`\n * echoed in `scope` and surfaced in `params`. The LLM consumes the\n * approach record's `signature_hint` and synthesises\n * `{ missing_entities, coverage_score }` against the live graph.\n *\n * @returns JSON envelope: `{ approach_id: 'plan', scope, generated_at, approach, params }`\n * @atomicity atomic (read-only)\n * @warning v0.3.0 returns the approach record only; the caller (LLM) is\n * the executor. Structured execution (compute coverage_score from\n * canonical region playbooks) lands in v0.3.x.\n * @see get_approach\n * @see list_playbooks\n * @see get_region\n * @see inspect\n * @see prioritise\n */\nexport const plan: ToolHandler = (args): ToolResult => {\n const region = args.region as string | undefined\n return approachEnvelope('plan', region ?? null, {\n params: { region: region ?? null },\n execution_mode: 'definition_lookup_v0_3_0',\n })\n}\n\n/**\n * `inspect`: the path of arrival to \"what's broken?\".\n *\n * v0.3.0 ships as a definition lookup. Returns the Inspect approach record\n * wrapped in the family-resemblance envelope. The LLM consumes the\n * `signature_hint` and synthesises `{ violations: [...] }` against\n * `UPG_ANTI_PATTERNS` and the live graph.\n *\n * @returns JSON envelope: `{ approach_id: 'inspect', scope, generated_at, approach, params }`\n * @atomicity atomic (read-only)\n * @warning v0.3.0 returns the approach record only; the caller (LLM) is\n * the executor. Structured execution (run anti-pattern matchers plus\n * structural lints) lands in v0.3.x.\n * @see get_approach\n * @see list_anti_patterns\n * @see get_anti_pattern\n * @see validate_graph\n * @see plan\n * @see reflect\n */\nexport const inspect: ToolHandler = (args): ToolResult => {\n const region = args.region as string | undefined\n const entities = args.entities as string[] | undefined\n const scope = region ?? entities ?? null\n return approachEnvelope('inspect', scope, {\n params: {\n region: region ?? null,\n entities: Array.isArray(entities) ? entities : null,\n },\n execution_mode: 'definition_lookup_v0_3_0',\n })\n}\n\n/**\n * `prioritise`: the path of arrival to \"what's most important?\".\n *\n * v0.3.0 ships as a definition lookup. Both `candidates` and `framework_id`\n * are required; prioritisation without an explicit candidate set or a\n * declared scoring lens is incoherent. Returns the Prioritise approach\n * record wrapped in the family-resemblance envelope.\n *\n * @returns JSON envelope: `{ approach_id: 'prioritise', scope, generated_at, approach, params }`\n * @throws textError when `candidates` or `framework_id` are missing/empty.\n * @atomicity atomic (read-only)\n * @warning v0.3.0 returns the approach record plus framework lookup only.\n * Structured execution (apply framework's `computed_properties` to each\n * candidate, return ranked output) lands in v0.3.x.\n * @see get_approach\n * @see list_frameworks\n * @see get_framework\n * @see plan\n * @see trace\n */\nexport const prioritise: ToolHandler = (args): ToolResult => {\n const candidates = args.candidates as string[] | undefined\n const frameworkId = args.framework_id as string | undefined\n if (!candidates || !Array.isArray(candidates) || candidates.length === 0) {\n return textError('Missing required parameter: candidates (entity_id[])')\n }\n if (!frameworkId) {\n return textError(\n 'Missing required parameter: framework_id (e.g. \"rice-scoring\", \"ice-scoring\", \"kano-model\")',\n )\n }\n const framework = UPG_FRAMEWORKS_BY_ID[frameworkId]\n return approachEnvelope('prioritise', candidates, {\n params: { candidates, framework_id: frameworkId },\n framework_resolved: framework\n ? { id: framework.id, name: framework.name, category: framework.category }\n : null,\n execution_mode: 'definition_lookup_v0_3_0',\n })\n}\n\n/**\n * `trace`: the path of arrival to \"walk a meaningful path through existing\n * graph\". Path is type-shorthand: `[\"persona\", \"job\", \"feature\"]` walks\n * persona→job→feature using the canonical edge for each pair (resolve via\n * `resolve_edge_for_pair`). Optional `edges_override` selects non-canonical\n * edges per hop; element `null` means \"use canonical\". The path expression\n * is the UPGEntityType[] shorthand itself (no DSL).\n *\n * v0.3.0 ships as a definition lookup.\n *\n * @returns JSON envelope: `{ approach_id: 'trace', scope, generated_at, approach, params }`\n * @throws textError when `anchor` or `path` are missing/invalid.\n * @atomicity atomic (read-only)\n * @warning v0.3.0 returns the approach record only; the LLM composes the\n * actual traversal via `query()`. Structured execution (BFS walker that\n * returns `{ trail, reached }`) lands in v0.3.x.\n * @see get_approach\n * @see resolve_edge_for_pair\n * @see query\n * @see get_node\n * @see plan\n * @see prioritise\n */\nexport const trace: ToolHandler = (args): ToolResult => {\n const anchor = args.anchor as string | undefined\n const path = args.path as string[] | undefined\n const edgesOverride = args.edges_override as (string | null)[] | undefined\n if (!anchor) {\n return textError('Missing required parameter: anchor (entity_id)')\n }\n if (!path || !Array.isArray(path) || path.length === 0) {\n return textError('Missing required parameter: path (UPGEntityType[])')\n }\n if (edgesOverride && edgesOverride.length !== path.length) {\n return textError(\n `edges_override length (${edgesOverride.length}) must match path length (${path.length})`,\n )\n }\n return approachEnvelope('trace', anchor, {\n params: {\n anchor,\n path,\n edges_override: edgesOverride ?? null,\n },\n execution_mode: 'definition_lookup_v0_3_0',\n })\n}\n\n/**\n * `reflect`: the path of arrival to \"what should I be questioning?\".\n *\n * Optional `mode` is one of the 4 canonical nouns:\n * `'assumptions' | 'alternatives' | 'blind-spots' | 'load-bearing'`.\n * Absence of mode means open reflection (undefined IS the open case;\n * there's no `'open'` literal). Optional `scope` accepts a region id,\n * an entity id, or `null` for whole-graph reflection.\n *\n * v0.3.0 ships as a definition lookup.\n *\n * @returns JSON envelope: `{ approach_id: 'reflect', scope, generated_at, approach, params }`\n * @throws textError when `mode` is provided but not one of the 4 canonical nouns.\n * @atomicity atomic (read-only)\n * @warning v0.3.0 returns the approach record only; the caller (LLM)\n * emits the prompts. Structured execution (template-driven prompt\n * generation per mode plus targeted entity selection) lands in v0.3.x.\n * @see get_approach\n * @see inspect\n * @see plan\n * @see get_anti_pattern\n */\nexport const reflect: ToolHandler = (args): ToolResult => {\n const scope = args.scope as string | null | undefined\n const mode = args.mode as string | undefined\n if (mode !== undefined && !REFLECT_MODES.includes(mode as ReflectMode)) {\n return textError(\n `Invalid mode: ${mode}. Valid modes: ${REFLECT_MODES.join(', ')}. Omit mode for open reflection.`,\n )\n }\n return approachEnvelope('reflect', scope ?? null, {\n params: {\n scope: scope ?? null,\n mode: mode ?? null,\n },\n execution_mode: 'definition_lookup_v0_3_0',\n })\n}\n\n// ── Domains ─────────────────────────────────────────────────────────────────\n\n/**\n * List every domain in `@unified-product-graph/core`.\n *\n * Two modes via `with_guide_only` (default `true`, preserving the\n * historical shape):\n * - `with_guide_only: true` (default): returns only domains that have a\n * canonical `UPGDomainUsageGuide`. Each row carries `{ domain_id,\n * anchor_entity, creation_sequence }` (the surface needed before drilling\n * into `get_domain_guide`). Order matches the canonical ring layout used\n * by `UPG_DOMAIN_GUIDES`.\n * - `with_guide_only: false`: returns every atomic domain from\n * `UPG_DOMAINS` (~36 at v0.3.0). Each row carries `{ domain_id, label,\n * description, types, has_guide }`. Use this to enumerate the full\n * catalog when authoring or auditing; `has_guide` flags whether a guide\n * is available for `get_domain_guide`.\n *\n * @returns JSON: `{ count, domains: Array<{ domain_id, anchor_entity, creation_sequence } | { domain_id, label, description, types, has_guide }> }`\n * @atomicity atomic (read-only)\n * @see get_domain_guide\n * @see list_regions\n * @see list_entity_types\n */\nexport const listDomains: ToolHandler = (args): ToolResult => {\n const withGuideOnly = args.with_guide_only as boolean | undefined\n if (withGuideOnly === false) {\n const guideIds = new Set(UPG_DOMAIN_GUIDES.map((g) => g.domain_id))\n const domains = UPG_DOMAINS.map((d) => ({\n domain_id: d.id,\n label: d.label,\n description: d.description,\n types: d.types,\n has_guide: guideIds.has(d.id),\n }))\n return text(JSON.stringify({ count: domains.length, domains }, null, 2))\n }\n const domains = UPG_DOMAIN_GUIDES.map((g) => ({\n domain_id: g.domain_id,\n anchor_entity: g.anchor_entity,\n creation_sequence: g.creation_sequence,\n }))\n return text(JSON.stringify({ count: domains.length, domains }, null, 2))\n}\n\n/**\n * Return the full UPGDomainUsageGuide for a domain: anchor entity, creation\n * sequence, named patterns (entity and edge chains), required cross-domain\n * bridges, and anti-patterns. This is the canonical \"how do I work in this\n * domain\" surface for MCP agents.\n *\n * @returns JSON: the full `UPGDomainUsageGuide` record.\n * @throws textError when `domain_id` is missing or unknown.\n * @atomicity atomic (read-only)\n * @see list_domains\n * @see list_anti_patterns\n * @see get_playbook\n */\nexport const getDomainGuide: ToolHandler = (args): ToolResult => {\n const domainId = args.domain_id as string | undefined\n if (!domainId) return textError('Missing required parameter: domain_id')\n const guide: UPGDomainUsageGuide | undefined = UPG_DOMAIN_GUIDES.find(\n (g) => g.domain_id === domainId,\n )\n if (!guide) return textError(`Unknown domain_id: ${domainId}`)\n return text(JSON.stringify(guide, null, 2))\n}\n\n// ── Frameworks ──────────────────────────────────────────────────────────────\n\n/**\n * List the canonical UPGFramework definitions: the curated, famous product\n * frameworks that anchor the public catalog. Paginated (default `limit: 50`,\n * max 200) because the full payload is large enough to overflow MCP transports\n * if returned in one shot.\n *\n * Cursor is opaque base64 (`offset:N`). Pass the `next_cursor` from a previous\n * response to advance; omit to start from the first page. The optional\n * `category` filter is exact-match against `UPGFramework.category` (e.g.\n * \"strategy\", \"prioritization\", \"discovery\") and applied BEFORE pagination,\n * so `total` reflects the filtered count.\n *\n * @returns JSON: `{ total, count, next_cursor?, frameworks: UPGFramework[] }`\n * @atomicity atomic (read-only)\n * @see get_framework\n * @see prioritise\n * @see list_approaches\n */\nexport const listFrameworks: ToolHandler = (args): ToolResult => {\n const category = args.category as string | undefined\n const limit = clampLimit(args.limit, FRAMEWORKS_DEFAULT_LIMIT, FRAMEWORKS_MAX_LIMIT)\n const cursorOffset = decodeCursor(args.cursor)\n\n let pool: readonly UPGFramework[] = UPG_FRAMEWORKS\n if (category) pool = pool.filter((f) => f.category === category)\n\n const total = pool.length\n const slice = pool.slice(cursorOffset, cursorOffset + limit)\n const nextOffset = cursorOffset + slice.length\n const nextCursor = nextOffset < total ? encodeCursor(nextOffset) : undefined\n\n const body: Record<string, unknown> = {\n total,\n count: slice.length,\n frameworks: slice,\n }\n if (nextCursor) body.next_cursor = nextCursor\n\n return text(JSON.stringify(body, null, 2))\n}\n\n/**\n * Return one canonical UPGFramework by id (e.g. \"rice-scoring\",\n * \"lean-canvas\"). Includes all four layers: data, structure, presentation,\n * education.\n *\n * @returns JSON: the full `UPGFramework` record.\n * @throws textError when `id` is missing or unknown.\n * @atomicity atomic (read-only)\n * @see list_frameworks\n * @see prioritise\n * @see get_playbook\n * @see get_approach\n */\nexport const getFramework: ToolHandler = (args): ToolResult => {\n const id = args.id as string | undefined\n if (!id) return textError('Missing required parameter: id')\n const framework = UPG_FRAMEWORKS_BY_ID[id]\n if (!framework) return textError(`Unknown framework id: ${id}`)\n return text(JSON.stringify(framework, null, 2))\n}\n\n// ── Edge catalogue ──────────────────────────────────────────────────────────\n\ninterface EdgeCatalogEntry extends UPGEdgeDefinition {\n type: string\n}\n\nfunction buildEdgeEntries(\n filterSource?: string,\n filterTarget?: string,\n): EdgeCatalogEntry[] {\n const entries: EdgeCatalogEntry[] = []\n for (const [type, def] of Object.entries(UPG_EDGE_CATALOG) as Array<\n [string, UPGEdgeDefinition]\n >) {\n if (filterSource && def.source_type !== filterSource) continue\n if (filterTarget && def.target_type !== filterTarget) continue\n entries.push({ type, ...def })\n }\n return entries\n}\n\n/**\n * List every canonical edge type from `UPG_EDGE_CATALOG`, optionally narrowed\n * by source_type and/or target_type. Each entry carries the edge key (`type`),\n * its forward/reverse verbs, structural classification, and endpoint types.\n * The polymorphic wildcard `'node'` is preserved on edges that are registered\n * as polymorphic; callers should treat it as \"matches any entity type\".\n *\n * @returns JSON: `{ count, edges: Array<{ type, forward_verb, reverse_verb, classification, source_type, target_type }> }`\n * @atomicity atomic (read-only)\n * @see get_edge_type\n * @see resolve_edge_for_pair\n * @see list_cross_edge_types\n * @see create_edge\n */\nexport const listEdgeTypes: ToolHandler = (args): ToolResult => {\n const sourceType = args.source_type as string | undefined\n const targetType = args.target_type as string | undefined\n const edges = buildEdgeEntries(sourceType, targetType)\n return text(JSON.stringify({ count: edges.length, edges }, null, 2))\n}\n\n/**\n * Return one canonical edge catalogue entry by edge type key (e.g.\n * \"persona_pursues_job\", \"feature_addresses_need\").\n *\n * @returns JSON: `{ type, forward_verb, reverse_verb, classification, source_type, target_type }`\n * @throws textError when `type` is missing or unknown.\n * @atomicity atomic (read-only)\n * @see list_edge_types\n * @see resolve_edge_for_pair\n * @see rename_edge_type\n */\nexport const getEdgeType: ToolHandler = (args): ToolResult => {\n const type = args.type as string | undefined\n if (!type) return textError('Missing required parameter: type')\n const def = (UPG_EDGE_CATALOG as Record<string, UPGEdgeDefinition>)[type]\n if (!def) return textError(`Unknown edge type: ${type}`)\n return text(JSON.stringify({ type, ...def }, null, 2))\n}\n\n// ── Regions ───────────────────────────────────────────────────────\n\n/**\n * List every canonical UPGRegion shipped with `@unified-product-graph/core`.\n * Returns a compact summary per region (id, label, order, shape, mental_model,\n * anchor entity type, atomic-domain composition, entity / edge counts): the\n * minimum surface needed to decide whether to drill into `get_region`. The\n * region count is fixed (10) so this endpoint stays non-paginated.\n *\n * Order matches the canonical 1..10 ring sequence from `UPG_REGIONS`\n * (Strategy & Outcomes → Operations & Quality).\n *\n * @returns JSON: `{ count, regions: Array<{ id, label, order, shape, mental_model, anchor_type, composes_atomic_domains, entity_count, intra_edge_count, boundary_edge_count }> }`\n * @atomicity atomic (read-only)\n * @see get_region\n * @see get_region_for_entity_type\n * @see list_domains\n * @see list_playbooks\n */\nexport const listRegions: ToolHandler = (): ToolResult => {\n const regions = UPG_REGIONS.map((r) => ({\n id: r.id,\n label: r.label,\n order: r.order,\n shape: r.shape,\n mental_model: r.mental_model,\n anchor_type: r.anchor.type,\n composes_atomic_domains: r.composes_atomic_domains,\n entity_count: r.entities.length,\n intra_edge_count: r.intra_edges.length,\n boundary_edge_count: r.boundary_edges.length,\n }))\n return text(\n JSON.stringify({ count: UPG_REGION_COUNT, regions }, null, 2),\n )\n}\n\n/**\n * Return the full UPGRegion record by id: anchor entity (with rationale and\n * inbound/outbound cross-edge counts), entity memberships with structural\n * roles, intra-domain edge keys, boundary edges to other regions, shape\n * archetype, and the atomic-domain composition.\n *\n * @returns JSON: the full `UPGRegion` record.\n * @throws textError when `id` is missing or unknown.\n * @atomicity atomic (read-only)\n * @see list_regions\n * @see get_region_for_entity_type\n * @see get_playbook\n * @see list_lenses\n */\nexport const getRegion: ToolHandler = (args): ToolResult => {\n const id = args.id as string | undefined\n if (!id) return textError('Missing required parameter: id')\n const region: UPGRegion | undefined = UPG_REGION_MAP[id]\n if (!region) return textError(`Unknown region id: ${id}`)\n return text(JSON.stringify(region, null, 2))\n}\n\n/**\n * Return the canonical UPGRegion that contains a given entity type. Wraps\n * `getRegionForEntityType`. Useful for adapters and copilots that need to\n * resolve \"which super-domain does this type belong to\" before deciding how\n * to render or route it.\n *\n * @returns JSON: the full `UPGRegion` record.\n * @throws textError when `entity_type` is missing or no region contains it.\n * @atomicity atomic (read-only)\n * @see get_region\n * @see list_regions\n * @see get_entity_meta\n * @see list_entity_types\n */\nexport const getRegionForEntity: ToolHandler = (args): ToolResult => {\n const entityType = args.entity_type as string | undefined\n if (!entityType) return textError('Missing required parameter: entity_type')\n const region = getRegionForEntityType(entityType)\n if (!region) return textError(`No region contains entity_type: ${entityType}`)\n return text(JSON.stringify(region, null, 2))\n}\n\n// ── Spec version ──────────────────────────────────────────────────\n\n/**\n * Return spec-level metadata for adopter compatibility checks: the spec\n * version, markdown format version, and canonical counts (entity types, edge\n * types, atomic domains, super-domain regions). Standalone tool: adopters\n * read it cleaner than folding it into `get_workspace_info` (which is\n * graph-instance-scoped, not spec-scoped).\n *\n * The pair `(upg_version, markdown_format_version)` is the right thing to\n * pin against; counts are informational only and may shift between patch\n * releases.\n *\n * @returns JSON: `{ upg_version, markdown_format_version, entity_count, edge_count, domain_count, region_count }`\n * @atomicity atomic (read-only)\n * @see list_entity_types\n * @see list_edge_types\n * @see list_regions\n */\nexport const getSpecVersion: ToolHandler = (): ToolResult => {\n return text(\n JSON.stringify(\n {\n upg_version: UPG_VERSION,\n markdown_format_version: MARKDOWN_FORMAT_VERSION,\n entity_count: UPG_ENTITY_COUNT,\n edge_count: UPG_EDGE_COUNT,\n domain_count: UPG_DOMAIN_COUNT,\n region_count: UPG_REGION_COUNT,\n },\n null,\n 2,\n ),\n )\n}\n\n// ── Edge resolver ─────────────────────────────────────────────────\n\n/**\n * Resolve the canonical UPGEdgeType for a `source_type` → `target_type`\n * containment pair. Wraps `resolveContainmentEdge` / `UPG_EDGE_PAIR_MAP`.\n *\n * Adapter-critical: every import adapter (Markdown, Notion, Linear, GitHub)\n * needs this when constructing `_contains_` edges. The catalog is closed,\n * so raw `${parent}_contains_${child}` template strings are unsafe because\n * most pairs are not registered. Use this tool to resolve the canonical\n * key, then fall back to a polymorphic edge (e.g. `node_informs_node`) or\n * skip when `edge_type` is `null`.\n *\n * @returns JSON: `{ source_type, target_type, edge_type: string | null }`\n * @throws textError when `source_type` or `target_type` is missing.\n * @atomicity atomic (read-only)\n * @warning Returns `edge_type: null` when no canonical pair is registered;\n * adapters MUST fall back to a polymorphic edge or skip the relationship\n * rather than synthesise a non-canonical key.\n * @see list_edge_types\n * @see get_edge_type\n * @see create_edge\n * @see trace\n */\nexport const resolveEdgeForPair: ToolHandler = (args): ToolResult => {\n const sourceType = args.source_type as string | undefined\n const targetType = args.target_type as string | undefined\n if (!sourceType) return textError('Missing required parameter: source_type')\n if (!targetType) return textError('Missing required parameter: target_type')\n const edgeType = resolveContainmentEdge(sourceType, targetType)\n return text(\n JSON.stringify(\n { source_type: sourceType, target_type: targetType, edge_type: edgeType },\n null,\n 2,\n ),\n )\n}\n\n// ── Cross-edge types ──────────────────────────────────────────────\n\n/**\n * List the canonical cross-product edge types from `UPG_CROSS_EDGE_TYPES`\n * (`shares_persona`, `shares_competitor`, `shares_metric`,\n * `depends_on_product`, `cannibalises`, `succeeds`, `hosts`, `contributes_to`). These are portfolio-level\n * relationships between entities in different products, separate from the\n * within-product `UPG_EDGE_CATALOG` and previously invisible to MCP.\n *\n * @returns JSON: `{ count, types: readonly UPGCrossEdgeType[] }`\n * @atomicity atomic (read-only)\n * @see list_edge_types\n * @see list_portfolio_cross_edges\n * @see migrate_cross_edges\n */\nexport const listCrossEdgeTypes: ToolHandler = (): ToolResult => {\n return text(\n JSON.stringify(\n { count: UPG_CROSS_EDGE_TYPES.length, types: UPG_CROSS_EDGE_TYPES },\n null,\n 2,\n ),\n )\n}\n\n// ── Lenses ────────────────────────────────────────────────────────\n\n/**\n * List every canonical UPGLens shipped with `@unified-product-graph/core`.\n * Returns a compact summary per lens (id, name, description, icon, audience,\n * perspective, framework_id, playbook_id, visible-domain count, intelligence-\n * prompt count): the minimum surface needed to choose a lens before drilling\n * into `get_lens`.\n *\n * @returns JSON: `{ count, lenses: Array<{ id, name, description, icon, audience, perspective, framework_id?, playbook_id?, visible_domain_count, intelligence_prompt_count }> }`\n * @atomicity atomic (read-only)\n * @see get_lens\n * @see list_regions\n * @see list_playbooks\n * @see list_frameworks\n */\nexport const listLenses: ToolHandler = (): ToolResult => {\n const lenses = UPG_LENSES.map((l) => ({\n id: l.id,\n name: l.name,\n description: l.description,\n icon: l.icon,\n audience: l.audience,\n perspective: l.perspective,\n framework_id: l.framework_id,\n playbook_id: l.playbook_id,\n visible_domain_count: l.visible_domains.length,\n intelligence_prompt_count: l.intelligence_prompts.length,\n }))\n return text(JSON.stringify({ count: UPG_LENSES.length, lenses }, null, 2))\n}\n\n/**\n * Return the full UPGLens record by id (e.g. `'product'`, `'ux_design'`,\n * `'engineering'`, `'full'`) plus the resolved list of entity types visible\n * through that lens (`getVisibleTypes` applies `visible_domains`,\n * `always_show_types`, `always_hide_types`).\n *\n * Combining the lens record with `visible_types` in one response avoids the\n * common \"fetch lens, then resolve types\" round-trip for renderers.\n *\n * @returns JSON: `{ ...UPGLens, visible_types: string[] }`\n * @throws textError when `id` is missing or unknown.\n * @atomicity atomic (read-only)\n * @see list_lenses\n * @see get_playbook\n * @see get_framework\n * @see list_entity_types\n */\nexport const getLensTool: ToolHandler = (args): ToolResult => {\n const id = args.id as string | undefined\n if (!id) return textError('Missing required parameter: id')\n const lens: UPGLens | undefined = getLens(id)\n if (!lens) return textError(`Unknown lens id: ${id}`)\n const visibleTypes = getVisibleTypes(lens)\n return text(JSON.stringify({ ...lens, visible_types: visibleTypes }, null, 2))\n}\n\n// ── Type labels ───────────────────────────────────────────────────\n\nconst TYPE_LABELS_DEFAULT_LIMIT = 100\nconst TYPE_LABELS_MAX_LIMIT = 500\n\n/**\n * List canonical UPGTypeLabel entries: every entity type's display label,\n * alt-labels (synonyms across frameworks plus common usage), per-framework\n * labels, and (where applicable) designation labels. Paginated (default\n * `limit: 100`, max 500) because the full list spans every active type\n * (~140+) and can balloon when alt_labels are dense.\n *\n * Cursor is opaque base64 (`offset:N`) following the `list_frameworks`\n * convention. Pass the `next_cursor` from a previous response to advance.\n *\n * @returns JSON: `{ total, count, next_cursor?, labels: UPGTypeLabel[] }`\n * @atomicity atomic (read-only)\n * @see get_type_label\n * @see list_entity_types\n * @see get_entity_meta\n */\nexport const listTypeLabels: ToolHandler = (args): ToolResult => {\n const limit = clampLimit(args.limit, TYPE_LABELS_DEFAULT_LIMIT, TYPE_LABELS_MAX_LIMIT)\n const cursorOffset = decodeCursor(args.cursor)\n\n const total = UPG_TYPE_LABELS.length\n const slice = UPG_TYPE_LABELS.slice(cursorOffset, cursorOffset + limit)\n const nextOffset = cursorOffset + slice.length\n const nextCursor = nextOffset < total ? encodeCursor(nextOffset) : undefined\n\n const body: Record<string, unknown> = {\n total,\n count: slice.length,\n labels: slice,\n }\n if (nextCursor) body.next_cursor = nextCursor\n\n return text(JSON.stringify(body, null, 2))\n}\n\n/**\n * Return one canonical UPGTypeLabel by entity type, plus the resolved display\n * label for an optional `framework_id` and/or `designation` (mirrors\n * `resolveLabel`). Lookup is exact-match against `UPG_TYPE_LABELS_MAP`.\n *\n * @returns JSON: `{ ...UPGTypeLabel, resolved_label: string }`\n * @throws textError when `entity_type` is missing or unknown.\n * @atomicity atomic (read-only)\n * @see list_type_labels\n * @see get_entity_meta\n * @see list_frameworks\n */\nexport const getTypeLabel: ToolHandler = (args): ToolResult => {\n const entityType = args.entity_type as string | undefined\n if (!entityType) return textError('Missing required parameter: entity_type')\n const label: UPGTypeLabel | undefined = UPG_TYPE_LABELS_MAP.get(entityType)\n if (!label) return textError(`Unknown entity_type: ${entityType}`)\n const frameworkId = args.framework_id as string | undefined\n const designation = args.designation as string | undefined\n const resolved = resolveLabel(entityType, frameworkId, designation)\n return text(JSON.stringify({ ...label, resolved_label: resolved }, null, 2))\n}\n\n// ── Hierarchy ─────────────────────────────────────────────────────\n\n/**\n * Return the list of valid direct-child entity types for a given parent\n * type. Wraps `getValidChildren` / `UPG_VALID_CHILDREN`. Returns an empty\n * array when the parent type has no registered children (or is unknown).\n *\n * @returns JSON: `{ parent_type, valid_children: string[] }`\n * @throws textError when `parent_type` is missing.\n * @atomicity atomic (read-only)\n * @see get_entity_schema\n * @see list_entity_types\n * @see get_entity_meta\n * @see create_node\n */\nexport const getValidChildrenTool: ToolHandler = (args): ToolResult => {\n const parentType = args.parent_type as string | undefined\n if (!parentType) return textError('Missing required parameter: parent_type')\n const validChildren = getValidChildren(parentType)\n return text(\n JSON.stringify(\n { parent_type: parentType, valid_children: validChildren },\n null,\n 2,\n ),\n )\n}\n\n// ── Entity meta + types ───────────────────────────────────────────\n\nconst ENTITY_TYPES_DEFAULT_LIMIT = 50\nconst ENTITY_TYPES_MAX_LIMIT = 200\n\n/**\n * List canonical entity types from `UPG_ENTITY_META`, the source of truth\n * for ontology evolution (every active, deprecated, or removed type with\n * its immutable `type_id`, maturity tier, and version metadata). Paginated\n * (default `limit: 50`, max 200) because the catalog is large (~348\n * entries at v0.3.0).\n *\n * Filters are AND-composed and applied **before** pagination, so `total`\n * reflects the filtered count:\n * - `domain`: exact-match against `UPG_ENTITY_TO_DOMAIN[name]` (the type's\n * atomic-domain id, e.g. `'user'`, `'market_intelligence'`).\n * - `maturity`: exact-match against `EntityTypeMeta.maturity`\n * (`'draft' | 'proposed' | 'stable' | 'deprecated' | 'removed'`).\n * - `deprecated`: boolean shortcut. `true` keeps only deprecated types;\n * `false` excludes deprecated and removed types (the active set).\n * Composes with `maturity` via AND, so when both are passed the row\n * must satisfy both.\n *\n * @returns JSON: `{ total, count, next_cursor?, types: Array<EntityTypeMeta & { domain_id: string | null }> }`\n * @atomicity atomic (read-only)\n * @see get_entity_meta\n * @see get_entity_schema\n * @see list_type_labels\n * @see list_domains\n */\nexport const listEntityTypes: ToolHandler = (args): ToolResult => {\n const domain = args.domain as string | undefined\n const maturity = args.maturity as UPGEntityTypeMaturity | undefined\n const deprecated = args.deprecated as boolean | undefined\n const limit = clampLimit(args.limit, ENTITY_TYPES_DEFAULT_LIMIT, ENTITY_TYPES_MAX_LIMIT)\n const cursorOffset = decodeCursor(args.cursor)\n\n // UPG_ENTITY_TO_DOMAIN is keyed by canonical type name → atomic-domain id.\n const typeToDomain = UPG_ENTITY_TO_DOMAIN as Readonly<Record<string, string>>\n\n let pool: readonly EntityTypeMeta[] = UPG_ENTITY_META\n if (maturity) pool = pool.filter((m) => m.maturity === maturity)\n if (deprecated === true) {\n pool = pool.filter((m) => m.maturity === 'deprecated')\n } else if (deprecated === false) {\n pool = pool.filter((m) => m.maturity !== 'deprecated' && m.maturity !== 'removed')\n }\n if (domain) {\n pool = pool.filter((m) => typeToDomain[m.name] === domain)\n }\n\n const total = pool.length\n const slice = pool.slice(cursorOffset, cursorOffset + limit).map((m) => ({\n ...m,\n domain_id: typeToDomain[m.name] ?? null,\n }))\n const nextOffset = cursorOffset + slice.length\n const nextCursor = nextOffset < total ? encodeCursor(nextOffset) : undefined\n\n const body: Record<string, unknown> = {\n total,\n count: slice.length,\n types: slice,\n }\n if (nextCursor) body.next_cursor = nextCursor\n\n return text(JSON.stringify(body, null, 2))\n}\n\n/**\n * Return one canonical `EntityTypeMeta` record by entity type name, plus the\n * resolved `domain_id` (or `null` if the type has no atomic-domain mapping).\n *\n * @returns JSON: `EntityTypeMeta & { domain_id: string | null }`\n * @throws textError when `name` is missing or unknown.\n * @atomicity atomic (read-only)\n * @see list_entity_types\n * @see get_type_label\n * @see get_entity_schema\n */\nexport const getEntityMeta: ToolHandler = (args): ToolResult => {\n const name = args.name as string | undefined\n if (!name) return textError('Missing required parameter: name')\n const meta = UPG_ENTITY_META_BY_NAME.get(name)\n if (!meta) return textError(`Unknown entity type: ${name}`)\n const typeToDomain = UPG_ENTITY_TO_DOMAIN as Readonly<Record<string, string>>\n return text(\n JSON.stringify({ ...meta, domain_id: typeToDomain[name] ?? null }, null, 2),\n )\n}\n\n// ── Anti-patterns ─────────────────────────────────────────────────\n\nconst ANTI_PATTERNS_DEFAULT_LIMIT = 50\nconst ANTI_PATTERNS_MAX_LIMIT = 200\n\n/**\n * List the curated cross-domain anti-patterns from `UPG_ANTI_PATTERNS`. Each\n * row pairs a memorable name with a machine-evaluable\n * `IntelligenceCondition`, the stages where it can fire, severity, and\n * remediation. These are graph-health patterns evaluated against the\n * **whole graph**, distinct from per-domain anti-patterns surfaced via\n * `get_domain_guide`.\n *\n * Paginated (default `limit: 50`, max 200) to leave headroom as the\n * catalog grows.\n *\n * Filters AND together and apply **before** pagination so `total` reflects\n * the filtered count:\n * - `severity`: exact-match against `UPGAntiPatternSeverity`\n * (`'high' | 'medium' | 'low'`).\n * - `stage`: keep only patterns whose `stages[]` includes the given\n * `UPGProductStage` (e.g. `'concept'`, `'launch'`).\n *\n * @returns JSON: `{ total, count, next_cursor?, anti_patterns: UPGCuratedAntiPattern[] }`\n * @atomicity atomic (read-only)\n * @see get_anti_pattern\n * @see validate_graph\n * @see inspect\n * @see get_domain_guide\n */\nexport const listAntiPatterns: ToolHandler = (args): ToolResult => {\n const severity = args.severity as UPGAntiPatternSeverity | undefined\n const stage = args.stage as UPGProductStage | undefined\n const limit = clampLimit(args.limit, ANTI_PATTERNS_DEFAULT_LIMIT, ANTI_PATTERNS_MAX_LIMIT)\n const cursorOffset = decodeCursor(args.cursor)\n\n let pool: readonly UPGCuratedAntiPattern[] = UPG_ANTI_PATTERNS\n if (severity) pool = pool.filter((p) => p.severity === severity)\n if (stage) pool = pool.filter((p) => p.stages.includes(stage))\n\n const total = pool.length\n const slice = pool.slice(cursorOffset, cursorOffset + limit)\n const nextOffset = cursorOffset + slice.length\n const nextCursor = nextOffset < total ? encodeCursor(nextOffset) : undefined\n\n const body: Record<string, unknown> = {\n total,\n count: slice.length,\n anti_patterns: slice,\n }\n if (nextCursor) body.next_cursor = nextCursor\n\n return text(JSON.stringify(body, null, 2))\n}\n\n/**\n * Return one curated anti-pattern by id (kebab-case slug, e.g.\n * `'features-without-hypotheses'`, `'personas-without-jobs'`). Includes the\n * full body: structured condition, why-it-matters, remediation, applicable\n * stages, severity, and optional source citation.\n *\n * IDs are stable URL fragments and remain frozen once published.\n *\n * @returns JSON: `UPGCuratedAntiPattern`\n * @throws textError when `id` is missing or unknown.\n * @atomicity atomic (read-only)\n * @see list_anti_patterns\n * @see inspect\n * @see validate_graph\n */\nexport const getAntiPattern: ToolHandler = (args): ToolResult => {\n const id = args.id as string | undefined\n if (!id) return textError('Missing required parameter: id')\n const pattern = UPG_ANTI_PATTERNS.find((p) => p.id === id)\n if (!pattern) return textError(`Unknown anti-pattern id: ${id}`)\n return text(JSON.stringify(pattern, null, 2))\n}\n\n// ── Benchmarks ────────────────────────────────────────────────────\n\n/**\n * Return one of the four canonical benchmark catalogs, the data behind\n * `get_graph_digest`'s health logic. The `kind` parameter is **required**\n * and routes to the matching source:\n * - `'count'` → `UPG_COUNT_BENCHMARKS`: per-entity-type expected ranges\n * across the canonical 9-stage product journey.\n * - `'relationship'` → `UPG_RELATIONSHIP_BENCHMARKS`: minimum\n * parent → child connection counts per stage.\n * - `'ratio'` → `UPG_RATIO_BENCHMARKS`: expected ratios between entity-type\n * counts (e.g. learnings / hypotheses ≥ 1).\n * - `'domain_activation'` → `UPG_DOMAIN_ACTIVATION`: when each atomic\n * domain is expected to \"turn on\" across the journey.\n *\n * Optional filters AND together; applied **after** the kind routes to a\n * catalog (so `total` reflects the filtered count).\n *\n * @returns JSON: `{ kind, total, count, benchmarks: ... }`\n * @throws textError when `kind` is missing or not one of the four supported values.\n * @atomicity atomic (read-only)\n * @see get_graph_digest\n * @see list_product_stages\n * @see list_domains\n * @see list_anti_patterns\n */\nexport const listBenchmarks: ToolHandler = (args): ToolResult => {\n const kind = args.kind as string | undefined\n if (!kind) return textError('Missing required parameter: kind')\n const stage = args.stage as UPGProductStage | undefined\n const domain = args.domain as string | undefined\n const typeToDomain = UPG_ENTITY_TO_DOMAIN as Readonly<Record<string, string>>\n\n if (kind === 'count') {\n let pool: readonly CountBenchmark[] = UPG_COUNT_BENCHMARKS\n if (domain) pool = pool.filter((b) => b.domain === domain)\n if (stage) pool = pool.filter((b) => b[stage] !== null)\n return text(\n JSON.stringify(\n { kind, total: UPG_COUNT_BENCHMARKS.length, count: pool.length, benchmarks: pool },\n null,\n 2,\n ),\n )\n }\n\n if (kind === 'relationship') {\n let pool: readonly RelationshipBenchmark[] = UPG_RELATIONSHIP_BENCHMARKS\n if (stage) pool = pool.filter((b) => b.stages.includes(stage))\n if (domain) {\n pool = pool.filter(\n (b) =>\n typeToDomain[b.parent_type] === domain || typeToDomain[b.child_type] === domain,\n )\n }\n return text(\n JSON.stringify(\n {\n kind,\n total: UPG_RELATIONSHIP_BENCHMARKS.length,\n count: pool.length,\n benchmarks: pool,\n },\n null,\n 2,\n ),\n )\n }\n\n if (kind === 'ratio') {\n let pool: readonly RatioBenchmark[] = UPG_RATIO_BENCHMARKS\n if (stage) pool = pool.filter((b) => b.stages.includes(stage))\n if (domain) {\n pool = pool.filter((b) => {\n const num = Array.isArray(b.numerator_type) ? b.numerator_type : [b.numerator_type]\n const den = Array.isArray(b.denominator_type) ? b.denominator_type : [b.denominator_type]\n return [...num, ...den].some((t) => typeToDomain[t] === domain)\n })\n }\n return text(\n JSON.stringify(\n { kind, total: UPG_RATIO_BENCHMARKS.length, count: pool.length, benchmarks: pool },\n null,\n 2,\n ),\n )\n }\n\n if (kind === 'domain_activation') {\n let pool: readonly DomainActivation[] = UPG_DOMAIN_ACTIVATION\n if (domain) pool = pool.filter((b) => b.domain_id === domain)\n if (stage) {\n pool = pool.filter((b) => b.expected_from === stage || b.expected_mature === stage)\n }\n return text(\n JSON.stringify(\n {\n kind,\n total: UPG_DOMAIN_ACTIVATION.length,\n count: pool.length,\n benchmarks: pool,\n },\n null,\n 2,\n ),\n )\n }\n\n return textError(\n `Unknown kind: ${kind}. Expected one of: count, relationship, ratio, domain_activation.`,\n )\n}\n\n// ── Product stages ────────────────────────────────────────────────\n\n/**\n * Return the canonical 9-stage product journey from `UPG_PRODUCT_STAGES`:\n * the closed enum used by `create_product`, `get_graph_digest` health logic,\n * benchmark stage scoping, and anti-pattern stage filters.\n *\n * Order is canonical: earliest → latest (`concept`, `validation`, `build`,\n * `beta`, `launch`, `growth`, `mature`, `maintenance`, `sunset`).\n *\n * @returns JSON: `{ count, stages: readonly UPGProductStage[] }`\n * @atomicity atomic (read-only)\n * @see list_benchmarks\n * @see list_anti_patterns\n * @see create_product\n */\nexport const listProductStages: ToolHandler = (): ToolResult => {\n return text(\n JSON.stringify(\n { count: UPG_PRODUCT_STAGES.length, stages: UPG_PRODUCT_STAGES },\n null,\n 2,\n ),\n )\n}\n\n// ── Spec introspection round 5 ──────────────────────────\n\n// ── Migrations ──────────────────────────────────────────────────────────────\n\n/**\n * List every type rename migration from `UPG_MIGRATIONS`: the version-scoped\n * map of deprecated `from` → canonical `to` renames (e.g. `pain_point → need`,\n * `hypothesis → hypothesis_claim`).\n *\n * Each row carries `{ from, to, since }` where `since` is the spec version\n * that introduced the migration. Optional `from_type` filter exact-matches\n * on the `from` field (useful for adopters checking whether a specific\n * legacy type is covered).\n *\n * @returns JSON: `{ migrations: [{ from, to, since }], total: number }`\n * @atomicity atomic (read-only)\n * @see list_edge_migrations\n * @see list_split_migrations\n * @see migrate_type\n * @see validate_graph\n * @see list_entity_types\n */\nexport const listTypeMigrations: ToolHandler = (args): ToolResult => {\n const fromType = args.from_type as string | undefined\n\n const migrations: Array<{ from: string; to: string; since: string }> = []\n for (const [since, entries] of Object.entries(UPG_MIGRATIONS)) {\n for (const m of entries) {\n if (!fromType || m.from === fromType) {\n migrations.push({ from: m.from, to: m.to, since })\n }\n }\n }\n\n return text(JSON.stringify({ migrations, total: migrations.length }, null, 2))\n}\n\n/**\n * List every edge-key migration from `UPG_EDGE_MIGRATIONS`: the\n * version-scoped map of renamed or dropped edge type keys (e.g.\n * `persona_has_jtbd → persona_pursues_job`).\n *\n * Each row carries `{ kind, from, to?, since }` where `kind` is `'rename'` or\n * `'drop'`. Optional `from_edge` filter exact-matches on the `from` field.\n *\n * @returns JSON: `{ migrations: [{ kind, from, to?, since }], total: number }`\n * @atomicity atomic (read-only)\n * @see list_type_migrations\n * @see list_split_migrations\n * @see rename_edge_type\n * @see list_edge_types\n * @see validate_graph\n */\nexport const listEdgeMigrations: ToolHandler = (args): ToolResult => {\n const fromEdge = args.from_edge as string | undefined\n\n const migrations: Array<{ kind: string; from: string; to?: string; since: string }> = []\n for (const [since, entries] of Object.entries(UPG_EDGE_MIGRATIONS)) {\n for (const m of entries) {\n if (!fromEdge || m.from === fromEdge) {\n migrations.push({\n kind: m.kind,\n from: m.from,\n ...(m.kind === 'rename' ? { to: m.to } : {}),\n since,\n })\n }\n }\n }\n\n return text(JSON.stringify({ migrations, total: migrations.length }, null, 2))\n}\n\n/**\n * List every 1→N split migration from `UPG_SPLIT_MIGRATIONS`: the\n * version-scoped registry of \"one type became multiple types\" rules (e.g.\n * `experiment → experiment_plan + experiment_run`, `hypothesis →\n * hypothesis_claim + hypothesis_evidence`).\n *\n * Each row carries the full `UPGSplitMigration` record plus `since` (the\n * introducing spec version). No filter arguments: the catalog is small\n * (≤ a handful of entries) and non-paginated.\n *\n * @returns JSON: `{ splits: [...], total: number }`\n * @atomicity atomic (read-only)\n * @see list_type_migrations\n * @see list_edge_migrations\n * @see migrate_type\n * @see validate_graph\n */\nexport const listSplitMigrations: ToolHandler = (): ToolResult => {\n const splits: Array<Record<string, unknown>> = []\n for (const [since, entries] of Object.entries(UPG_SPLIT_MIGRATIONS)) {\n for (const m of entries) {\n splits.push({ ...m, since })\n }\n }\n\n return text(JSON.stringify({ splits, total: splits.length }, null, 2))\n}\n\n// ── Lifecycles ──────────────────────────────────────────────────────────────\n\n/**\n * List lifecycle definitions from `UPG_LIFECYCLES`.\n *\n * Top-level response includes `lifecycles`, `free_types` (from\n * `UPG_LIFECYCLE_FREE_TYPES`: static types with no phase progression),\n * and `planned_types` (from `UPG_LIFECYCLE_PLANNED_TYPES`: lifecycle\n * planned but not yet authored). Filters: `entity_type` (exact-match);\n * `lifecycle_only` (when true, omits free/planned lists).\n *\n * @returns JSON: `{ lifecycles, total, free_types: string[], planned_types: string[] }`\n * @atomicity atomic (read-only)\n * @see get_lifecycle\n * @see list_entity_types\n * @see get_entity_meta\n */\nexport const listLifecycles: ToolHandler = (args): ToolResult => {\n const entityType = args.entity_type as string | undefined\n const lifecycleOnly = args.lifecycle_only as boolean | undefined\n\n let pool: readonly UPGLifecycle[] = UPG_LIFECYCLES\n if (entityType) pool = pool.filter((l) => l.entity_type === entityType)\n\n // When `lifecycle_only` is true, omit the free/planned blocks\n // entirely (matches the wire-shape `description`). The earlier version\n // returned empty arrays, which added wire bloat for callers asking for\n // lifecycle-only output.\n return text(\n JSON.stringify(\n {\n total: pool.length,\n lifecycles: pool,\n ...(lifecycleOnly === true\n ? {}\n : {\n free_types: Array.from(UPG_LIFECYCLE_FREE_TYPES).sort(),\n planned_types: Array.from(UPG_LIFECYCLE_PLANNED_TYPES).sort(),\n }),\n },\n null,\n 2,\n ),\n )\n}\n\n/**\n * Return the full `UPGLifecycle` definition for one entity type: initial\n * phase, terminal phases, and the ordered array of phases with their\n * transitions and core states. Returns a descriptive text message (not an\n * error) when the type is lifecycle-free or lifecycle-planned.\n *\n * @returns JSON: the full `UPGLifecycle` record, or a descriptive message.\n * @throws textError when `entity_type` is missing, lifecycle-free,\n * lifecycle-planned, or unknown.\n * @atomicity atomic (read-only)\n * @see list_lifecycles\n * @see get_entity_meta\n * @see get_entity_schema\n */\nexport const getLifecycle: ToolHandler = (args): ToolResult => {\n const entityType = args.entity_type as string | undefined\n if (!entityType) return textError('Missing required parameter: entity_type')\n const lifecycle = UPG_LIFECYCLES.find((l) => l.entity_type === entityType)\n if (!lifecycle) {\n if (UPG_LIFECYCLE_FREE_TYPES.has(entityType)) {\n return textError(`No lifecycle defined for entity type: ${entityType} (lifecycle-free: static type with no phase progression)`)\n }\n if (UPG_LIFECYCLE_PLANNED_TYPES.has(entityType)) {\n return textError(`No lifecycle defined for entity type: ${entityType} (lifecycle planned but not yet authored in this spec version)`)\n }\n return textError(`No lifecycle defined for entity type: ${entityType}`)\n }\n return text(JSON.stringify(lifecycle, null, 2))\n}\n\n// ── Scales ──────────────────────────────────────────────────────────────────\n\n/**\n * List every spec-defined assessment scale from `UPG_SCALES`. Scales define\n * the vocabulary for human judgments stored as `UPGAssessment` values:\n * numeric encoding, qualitative labels, and per-point descriptions.\n *\n * Non-paginated (the catalog is small). External scales in\n * `scale_extensions` are graph-instance–scoped and stay outside this\n * surface.\n *\n * @returns JSON: `{ scales: UPGScaleDefinition[], total: number }`\n * @atomicity atomic (read-only)\n * @see get_scale\n * @see get_entity_schema\n */\nexport const listScales: ToolHandler = (): ToolResult => {\n const scales: UPGScaleDefinition[] = Object.values(UPG_SCALES)\n return text(JSON.stringify({ scales, total: scales.length }, null, 2))\n}\n\n/**\n * Return one spec-defined assessment scale by id (e.g. `'reach_5'`,\n * `'severity_5'`).\n *\n * @returns JSON: the full `UPGScaleDefinition` record including all points.\n * @throws textError when `id` is missing or unknown.\n * @atomicity atomic (read-only)\n * @see list_scales\n * @see get_entity_schema\n */\nexport const getScale: ToolHandler = (args): ToolResult => {\n const id = args.id as string | undefined\n if (!id) return textError('Missing required parameter: id')\n const scale = UPG_SCALES[id]\n if (!scale) return textError(`Scale not found: ${id}`)\n return text(JSON.stringify(scale, null, 2))\n}\n\n// ── Framework metadata ────────────────────────────────────────────\n\n/**\n * List all valid framework category values from `UPG_FRAMEWORK_CATEGORIES`.\n * Use as valid values for the `category` filter on `list_frameworks`.\n *\n * @returns JSON: `{ categories: string[], total: number }`\n * @atomicity atomic (read-only)\n * @see list_frameworks\n * @see list_framework_structure_patterns\n */\nexport const listFrameworkCategories: ToolHandler = (): ToolResult => {\n return text(\n JSON.stringify(\n { categories: UPG_FRAMEWORK_CATEGORIES, total: UPG_FRAMEWORK_CATEGORIES.length },\n null,\n 2,\n ),\n )\n}\n\n/**\n * List all valid framework structure pattern values from\n * `UPG_STRUCTURE_PATTERNS` (tree, table, matrix, funnel, collection,\n * quadrant, flow). Mirrors `UPGFramework.structure.pattern`.\n *\n * @returns JSON: `{ patterns: string[], total: number }`\n * @atomicity atomic (read-only)\n * @see list_frameworks\n * @see list_framework_categories\n * @see get_framework\n */\nexport const listFrameworkStructurePatterns: ToolHandler = (): ToolResult => {\n return text(\n JSON.stringify(\n { patterns: UPG_STRUCTURE_PATTERNS, total: UPG_STRUCTURE_PATTERNS.length },\n null,\n 2,\n ),\n )\n}\n\n// ── Domain rings ──────────────────────────────────────────────────\n\n/**\n * List every `UPGDomainRing` from `UPG_DOMAIN_RINGS` in canonical order\n * (Nucleus → Understand → Define → Build → Grow → Operate → Extend). Rings\n * are the 7 concentric groupings of the 36 UPG atomic domains.\n *\n * @returns JSON: `{ rings: UPGDomainRing[], total: number }`\n * @atomicity atomic (read-only)\n * @see get_domain_ring\n * @see list_domains\n * @see get_domain_guide\n */\nexport const listDomainRings: ToolHandler = (): ToolResult => {\n return text(\n JSON.stringify(\n { rings: UPG_DOMAIN_RINGS, total: UPG_DOMAIN_RINGS.length },\n null,\n 2,\n ),\n )\n}\n\n/**\n * Return one `UPGDomainRing` by id (e.g. `'nucleus'`, `'understand'`,\n * `'define'`, `'build'`, `'grow'`, `'operate'`, `'extend'`).\n *\n * @returns JSON: the full `UPGDomainRing` record.\n * @throws textError when `id` is missing or unknown.\n * @atomicity atomic (read-only)\n * @see list_domain_rings\n * @see list_domains\n * @see get_domain_guide\n */\nexport const getDomainRing: ToolHandler = (args): ToolResult => {\n const id = args.id as string | undefined\n if (!id) return textError('Missing required parameter: id')\n const ring: UPGDomainRing | undefined = UPG_DOMAIN_RINGS.find((r) => r.id === id)\n if (!ring) return textError(`Domain ring not found: ${id}`)\n return text(JSON.stringify(ring, null, 2))\n}\n","/**\n * Portfolio family: cross-product relationships and the portfolio view.\n * Cross-product edges live in `upg.cross_product_edges` (migration 004),\n * separate from the within-product `upg.edges` table. Includes\n * `repair_dangling_edges` for orphaned cross-product rows.\n */\n\nimport { UPG_CROSS_EDGE_TYPES } from '@unified-product-graph/core'\nimport { type ToolHandler, text, textError } from '../lib/server-context.js'\nimport { edgeId } from '../id-helpers.js'\n\nconst crossEdgeTypeSet = new Set<string>(UPG_CROSS_EDGE_TYPES)\n\n// ── Portfolio family ─────────────────────────────────────────────────────\n\n/**\n * List the calling user's product portfolio. For v1, one portfolio per\n * instance: returns all products as a single portfolio named \"My\n * Portfolio\". Multi-portfolio scoping arrives once auth is wired.\n *\n * @returns JSON: `{ portfolios: [{ id, title, products: [{ id, title, stage? }] }], total: number }`\n * @atomicity atomic (read-only)\n * @warning v1 returns a single synthetic `'default'` portfolio per\n * instance; multi-portfolio scoping arrives once auth is wired.\n * Treat the `id: 'default'` shape as transitional.\n * @see list_products\n * @see list_portfolio_cross_edges\n */\nexport const listPortfolios: ToolHandler = async (_args, { store }) => {\n const products = await store.listProducts()\n const portfolio = {\n id: 'default',\n title: 'My Portfolio',\n products: products.map((p) => ({ id: p.id, title: p.title, stage: p.stage })),\n }\n return text(JSON.stringify({ portfolios: [portfolio], total: 1 }, null, 2))\n}\n\n/**\n * List all cross-product edges created by a specific product. Returns the\n * edges this product owns; edges where another product is the source or\n * target are visible via their creating product's call.\n *\n * @returns JSON: `{ edges: [{ id, source, target, type }], total: number }`\n * @throws textError when `product_id` is missing.\n * @atomicity atomic (read-only)\n * @warning Returns only edges this product **created**; edges another\n * product created targeting this product surface through that product's\n * own call. To audit all incident cross-edges, query each product in\n * the portfolio.\n * @see create_cross_product_edge\n * @see list_cross_edge_types\n * @see migrate_cross_edges\n */\nexport const listPortfolioCrossEdges: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError(`Missing required parameter: product_id`)\n const productId = args.product_id as string\n const edges = await store.listCrossProductEdges(productId)\n return text(JSON.stringify({\n edges: edges.map((e) => ({ id: e.id, source: e.source, target: e.target, type: e.type })),\n total: edges.length,\n }, null, 2))\n}\n\n/**\n * Create a cross-product edge owned by the given product. The edge type must\n * be one of the eight UPG cross-edge types (`shares_persona`, `shares_competitor`,\n * `shares_metric`, `depends_on_product`, `cannibalises`, `succeeds`, `hosts`,\n * `contributes_to`). Source and\n * target are qualified IDs: `{product_id}/{node_id}`.\n *\n * @returns JSON: `{ edge: { id, source, target, type, created_by_product_id } }`\n * @throws textError when `product_id`, `source`, `target`, or\n * `type` is missing, or `type` is not a UPG cross-edge type.\n * @atomicity atomic\n * @warning Source/target are qualified strings (`{product_id}/{node_id}`)\n * and skip FK validation against the products table. A target\n * referencing a deleted product becomes a dangling cross-edge; sweep\n * periodically with `repair_dangling_edges`.\n * @see list_cross_edge_types\n * @see list_portfolio_cross_edges\n * @see repair_dangling_edges\n * @see migrate_cross_edges\n */\nexport const createCrossProductEdge: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError(`Missing required parameter: product_id`)\n if (!args.source) return textError(`Missing required parameter: source`)\n if (!args.target) return textError(`Missing required parameter: target`)\n if (!args.type) return textError(`Missing required parameter: type`)\n\n const productId = args.product_id as string\n const source = args.source as string\n const target = args.target as string\n const type = args.type as string\n\n if (!crossEdgeTypeSet.has(type)) {\n return textError(\n `Invalid cross-edge type: \"${type}\". Must be one of: ${UPG_CROSS_EDGE_TYPES.join(', ')}`,\n )\n }\n\n try {\n const edge = await store.addCrossProductEdge(edgeId(), productId, source, target, type)\n return text(JSON.stringify({ edge }, null, 2))\n } catch (err) {\n return textError((err as Error).message)\n }\n}\n\n// ── repair_dangling_edges ─────────────────────────────────────────────────\n\n/**\n * Find and optionally remove cross-product edges that reference a product\n * that no longer exists.\n *\n * In cloud, Postgres FK constraints keep intra-product edges in sync (node\n * and edge tables cascade on product delete). The \"dangling\" scenario is\n * a cross-product edge whose target product has been deleted, because\n * `cross_product_edges.target` is a qualified string (`{product_id}/{node_id}`)\n * outside the FK reach of `upg.products`.\n *\n * `dry_run: true` (default): report without mutating.\n * `dry_run: false` plus `drop: ['dangling_cross_edges']`: delete the\n * dangling set.\n *\n * @returns JSON: `{ dangling: [{ id, source, target, type }], dangling_count, dry_run, dropped }`\n * @throws textError when `product_id` is missing.\n * @atomicity atomic-with-rollback (when drop is requested)\n * @warning Default is `dry_run: true`. Pass `dry_run: false` AND\n * `drop: ['dangling_cross_edges']` to actually delete; the second\n * guard prevents accidental drops. Per-edge errors during deletion\n * (concurrent removal) are swallowed; check `dropped` against\n * `dangling_count` to detect partial application.\n * @see create_cross_product_edge\n * @see list_portfolio_cross_edges\n * @see migrate_cross_edges\n */\nexport const repairDanglingEdges: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError(`Missing required parameter: product_id`)\n\n const productId = args.product_id as string\n const dryRun: boolean = args.dry_run !== false // default true\n const drop = (args.drop as string[] | undefined) ?? []\n\n // Fetch all cross-product edges owned by this product\n const allEdges = await store.listCrossProductEdges(productId)\n\n // For each edge, parse the target qualified ID to extract the target product_id.\n // Qualified format: \"{product_id}/{node_id}\"\n const dangling: Array<{ id: string; source: string; target: string; type: string }> = []\n\n for (const edge of allEdges) {\n const slashIdx = edge.target.indexOf('/')\n if (slashIdx === -1) {\n // Malformed qualified ID: classify as dangling\n dangling.push({ id: edge.id, source: edge.source, target: edge.target, type: edge.type })\n continue\n }\n const targetProductId = edge.target.slice(0, slashIdx)\n const exists = await store.productExists(targetProductId)\n if (!exists) {\n dangling.push({ id: edge.id, source: edge.source, target: edge.target, type: edge.type })\n }\n }\n\n let dropped = 0\n\n if (!dryRun && drop.includes('dangling_cross_edges') && dangling.length > 0) {\n for (const edge of dangling) {\n try {\n await store.deleteCrossProductEdge(edge.id)\n dropped++\n } catch {\n // Edge may have already been deleted concurrently; skip\n }\n }\n }\n\n return text(JSON.stringify({\n dangling,\n dangling_count: dangling.length,\n dry_run: dryRun,\n dropped,\n }, null, 2))\n}\n","/**\n * Atomic batch tools. Six handlers wrap multi-row writes in a single\n * `PoolClient` BEGIN/COMMIT, rolling back on any error.\n *\n * Tools: batch_create_nodes, batch_update_nodes, batch_delete_nodes,\n * batch_create_edges, batch_delete_edges, batch_move_nodes.\n */\n\nimport type { UPGBaseNode, UPGEntityType } from '@unified-product-graph/core'\nimport { getLifecycleForType, resolveEntityType, UnknownEntityTypeError } from '@unified-product-graph/core'\nimport { type ToolHandler, text, textError } from '../lib/server-context.js'\nimport {\n inferEdgeTypeWithTier,\n validateEdgeTypePair,\n checkPropertyTypes,\n checkLengthCaps,\n renderPropertyTypeWarning,\n} from '@unified-product-graph/sdk/logic'\nimport { nodeId, edgeId } from '../id-helpers.js'\nimport { appendAudit } from '../lib/audit.js'\n\n// ─── helpers ─────────────────────────────────────────────────────────────────\n\n/** Column list kept in sync with `UPGPgStore.NODE_COLS`. */\nconst NODE_COLS = 'id, product_id, type, title, description, status, tags, data'\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction rowToNode(row: any): UPGBaseNode & { product_id: string } {\n const node: UPGBaseNode & { product_id: string } = {\n id: row.id,\n type: row.type,\n title: row.title,\n product_id: row.product_id,\n }\n if (row.description != null) node.description = row.description\n if (row.status != null) node.status = row.status\n if (row.tags != null) node.tags = row.tags\n if (row.data != null) node.properties = row.data\n return node\n}\n\n// ─── batch_create_nodes ───────────────────────────────────────────────────────\n\n/**\n * Create up to 50 entities in a single atomic Postgres transaction. For each\n * node with a `parent_id`, a containment edge is created in the same\n * transaction. On any failure the entire batch is rolled back.\n *\n * @returns JSON: `{ created: [{ id, type, title }], count, warnings? }`.\n * @throws textError when `nodes` is missing / non-array, any required field\n * (`type`, `title`) is absent, or any node carries a declared property whose\n * value type mismatches the schema (rejects the whole batch before BEGIN).\n * @atomicity atomic-with-rollback (BEGIN / COMMIT / ROLLBACK).\n * @warning Validation runs inline before BEGIN; a single bad item rejects\n * the entire batch before any database mutation. Parent containment edges\n * are catalog-strict: a non-canonical parent→child pair skips the edge with\n * a warning rather than fabricating a `_contains_` edge (matches\n * `create_node`). A missing parent likewise skips the edge.\n * @see create_node\n * @see batch_create_edges\n * @see batch_update_nodes\n */\nexport const batchCreateNodes: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n const nodes = args.nodes as Array<Record<string, unknown>> | undefined\n if (!nodes || !Array.isArray(nodes)) return textError('Missing required parameter: nodes (array)')\n if (nodes.length === 0) return textError('nodes array is empty')\n if (nodes.length > 50) return textError('Maximum 50 nodes per batch')\n\n // Validate before touching the database. Property-type violations reject the\n // whole batch before BEGIN, matching the local server's batch_create_nodes.\n for (let i = 0; i < nodes.length; i++) {\n const n = nodes[i]\n if (!n.type) return textError(`Node at index ${i}: missing required field \"type\"`)\n if (!n.title) return textError(`Node at index ${i}: missing required field \"title\"`)\n try {\n resolveEntityType(n.type)\n } catch (err) {\n if (err instanceof UnknownEntityTypeError) return textError(`Node at index ${i}: ${err.message}`)\n throw err\n }\n if (n.properties !== undefined) {\n const { violations } = checkPropertyTypes(n.type as string, n.properties as Record<string, unknown>)\n if (violations.length > 0) {\n return textError(`Node at index ${i}: ${renderPropertyTypeWarning(n.type as string, violations)!}`)\n }\n }\n }\n\n const productId = args.product_id as string\n const warnings: string[] = []\n\n // Access the underlying pool via the store's private field.\n // PgStore exposes pool via the constructor closure; we reach it through\n // the store's pool property which is typed as `private` but accessible\n // at runtime via bracket notation.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const pool = (store as any).pool as import('pg').Pool\n const client = await pool.connect()\n\n try {\n await client.query('BEGIN')\n\n const created: Array<{ id: string; type: string; title: string }> = []\n\n for (let i = 0; i < nodes.length; i++) {\n const n = nodes[i]\n const nodeType = resolveEntityType(n.type).canonical\n const newId = nodeId()\n\n let status: string | null = null\n if (n.status) {\n status = n.status as string\n } else {\n const lifecycle = getLifecycleForType(nodeType)\n if (lifecycle) status = lifecycle.initial_phase\n }\n\n await client.query(\n `INSERT INTO upg.nodes (id, product_id, type, title, description, status, tags, data)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`,\n [\n newId,\n productId,\n nodeType,\n n.title as string,\n (n.description as string | undefined) ?? null,\n status,\n (n.tags as string[] | undefined) ?? null,\n n.properties ? JSON.stringify(n.properties) : null,\n ],\n )\n await appendAudit(client, {\n productId, action: 'create', entityType: 'node', entityId: newId,\n changes: { type: nodeType, title: n.title as string },\n })\n\n // Length-cap soft warnings (per-item, never refusals).\n const { warnings: lengthWarnings } = checkLengthCaps({\n title: n.title as string,\n description: n.description as string | undefined,\n properties: n.properties as Record<string, unknown> | undefined,\n })\n for (const w of lengthWarnings) warnings.push(`Node \"${newId}\": ${w}`)\n\n // If a parent_id was supplied, look up the parent and create a\n // catalog-strict containment edge inferred from parent type → new type.\n const parentId = n.parent_id as string | undefined\n if (parentId) {\n const { rows: parentRows } = await client.query(\n `SELECT ${NODE_COLS} FROM upg.nodes WHERE id = $1 AND product_id = $2`,\n [parentId, productId],\n )\n if (parentRows.length > 0) {\n const parentType = parentRows[0].type as string\n // Do NOT fabricate a `_contains_` edge: skip with a warning when the\n // pair has no canonical edge, matching create_node.\n const inference = inferEdgeTypeWithTier(parentType, nodeType)\n if (!inference.ok) {\n warnings.push(`Node \"${newId}\": parent edge not created; no canonical edge for ${parentType} → ${nodeType}.`)\n } else {\n const eid = edgeId()\n await client.query(\n `INSERT INTO upg.edges (id, product_id, source, target, type)\n VALUES ($1, $2, $3, $4, $5)`,\n [eid, productId, parentId, newId, inference.edgeType],\n )\n await appendAudit(client, {\n productId, action: 'create', entityType: 'edge', entityId: eid,\n changes: { source: parentId, target: newId, type: inference.edgeType },\n })\n }\n } else {\n warnings.push(`Node \"${newId}\": parent ${parentId} not found. Node created without edge.`)\n }\n }\n\n created.push({ id: newId, type: nodeType, title: n.title as string })\n }\n\n await client.query('COMMIT')\n // Emit post-commit. (Auto-created parent containment edges are not emitted\n // individually; the node.created event is the primary signal.)\n for (const c of created) store.emit(productId, 'node.created', { id: c.id, type: c.type, title: c.title })\n const body: Record<string, unknown> = { created, count: created.length }\n if (warnings.length > 0) body.warnings = warnings\n return text(JSON.stringify(body, null, 2))\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n } finally {\n client.release()\n }\n}\n\n// ─── batch_update_nodes ───────────────────────────────────────────────────────\n\n/**\n * Update up to 50 existing entities in a single atomic transaction. Properties\n * are MERGED with existing (not replaced). Unspecified fields are preserved.\n * All node IDs are resolved before the transaction opens; a missing ID rejects\n * the whole batch before any mutation lands.\n *\n * @returns JSON: `{ updated: [id], count }`.\n * @throws textError when `nodes` is missing / non-array / empty / >50, or any\n * item is missing `id`, or any `id` does not resolve.\n * @atomicity atomic-with-rollback (BEGIN / COMMIT / ROLLBACK).\n * @warning Properties merge with `||`: top-level keys overwrite while nested\n * keys stay shallow (deep-merge stays out of scope). To clear a property,\n * pass it as `null`. Items with no setClauses (every field undefined) are\n * silently skipped.\n * @see update_node\n * @see batch_create_nodes\n * @see migrate_type\n */\nexport const batchUpdateNodes: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n const nodes = args.nodes as Array<Record<string, unknown>> | undefined\n if (!nodes || !Array.isArray(nodes)) return textError('Missing required parameter: nodes (array)')\n if (nodes.length === 0) return textError('nodes array is empty')\n if (nodes.length > 50) return textError('Maximum 50 updates per batch')\n\n // Pre-validate: all IDs must exist. Property-type violations reject the\n // whole batch before BEGIN, matching the local server's batch_update_nodes.\n // Cache the resolved type so the mutation loop need not re-fetch.\n const typeById = new Map<string, string>()\n for (let i = 0; i < nodes.length; i++) {\n const n = nodes[i]\n if (!n.id) return textError(`Node at index ${i}: missing required field \"id\"`)\n const existing = await store.getNode(n.id as string)\n if (!existing) return textError(`Node at index ${i}: node \"${n.id}\" not found`)\n typeById.set(n.id as string, existing.type)\n if (n.properties !== undefined) {\n const { violations } = checkPropertyTypes(existing.type, n.properties as Record<string, unknown>)\n if (violations.length > 0) {\n return textError(`Node at index ${i}: ${renderPropertyTypeWarning(existing.type, violations)!}`)\n }\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const pool = (store as any).pool as import('pg').Pool\n const client = await pool.connect()\n\n try {\n await client.query('BEGIN')\n\n const updated: string[] = []\n const warnings: string[] = []\n\n for (const n of nodes) {\n const nid = n.id as string\n const setClauses: string[] = []\n const values: unknown[] = []\n let p = 1\n\n if (n.title !== undefined) { setClauses.push(`title = $${p++}`); values.push(n.title) }\n if (n.description !== undefined) { setClauses.push(`description = $${p++}`); values.push(n.description) }\n if (n.status !== undefined) { setClauses.push(`status = $${p++}`); values.push(n.status) }\n if (n.tags !== undefined) { setClauses.push(`tags = $${p++}`); values.push(n.tags) }\n if (n.properties !== undefined) {\n setClauses.push(`data = COALESCE(data, '{}'::jsonb) || $${p++}::jsonb`)\n values.push(JSON.stringify(n.properties))\n }\n\n if (setClauses.length > 0) {\n values.push(nid)\n await client.query(\n `UPDATE upg.nodes SET ${setClauses.join(', ')} WHERE id = $${p}`,\n values,\n )\n await appendAudit(client, {\n productId: args.product_id as string,\n action: 'update', entityType: 'node', entityId: nid,\n changes: {\n ...(n.title !== undefined ? { title: n.title } : {}),\n ...(n.description !== undefined ? { description: n.description } : {}),\n ...(n.status !== undefined ? { status: n.status } : {}),\n ...(n.tags !== undefined ? { tags: n.tags } : {}),\n ...(n.properties !== undefined ? { properties: n.properties } : {}),\n },\n })\n }\n\n // Status lifecycle + length-cap soft warnings (per-item, never refusals).\n if (n.status !== undefined) {\n const entityType = typeById.get(nid)\n if (entityType) {\n const lifecycle = getLifecycleForType(entityType)\n if (lifecycle) {\n const validPhases = lifecycle.phases.map((ph) => ph.id)\n if (!validPhases.includes(n.status as string)) {\n warnings.push(`Node \"${nid}\": status \"${n.status}\" is not a valid phase for type \"${entityType}\". Valid phases: [${validPhases.join(', ')}]`)\n }\n }\n }\n }\n const { warnings: lengthWarnings } = checkLengthCaps({\n title: n.title as string | undefined,\n description: n.description as string | undefined,\n properties: n.properties as Record<string, unknown> | undefined,\n })\n for (const w of lengthWarnings) warnings.push(`Node \"${nid}\": ${w}`)\n\n updated.push(nid)\n }\n\n await client.query('COMMIT')\n for (const nid of updated) store.emit(args.product_id as string, 'node.updated', { id: nid })\n const body: Record<string, unknown> = { updated, count: updated.length }\n if (warnings.length > 0) body.warnings = warnings\n return text(JSON.stringify(body, null, 2))\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n } finally {\n client.release()\n }\n}\n\n// ─── batch_delete_nodes ───────────────────────────────────────────────────────\n\n/**\n * Delete up to 50 entities and all their connected edges in a single atomic\n * transaction. All IDs are resolved before the transaction opens.\n *\n * @returns JSON: `{ deleted: [id], count }`.\n * @throws textError when `node_ids` is missing / non-array / empty / >50, or\n * any ID does not resolve.\n * @atomicity atomic-with-rollback (BEGIN / COMMIT / ROLLBACK).\n * @warning Cascade-deletes ALL edges incident on each node, in either\n * direction. Removal is hard; recovery flows through the audit log,\n * which records each removal for the retention window.\n * @see delete_node\n * @see batch_delete_edges\n * @see deduplicate_nodes\n */\nexport const batchDeleteNodes: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n const nodeIds = args.node_ids as string[] | undefined\n if (!nodeIds || !Array.isArray(nodeIds)) return textError('Missing required parameter: node_ids (array)')\n if (nodeIds.length === 0) return textError('node_ids array is empty')\n if (nodeIds.length > 50) return textError('Maximum 50 node IDs per batch')\n\n // Pre-validate: all IDs must exist\n for (let i = 0; i < nodeIds.length; i++) {\n const existing = await store.getNode(nodeIds[i])\n if (!existing) return textError(`Node at index ${i}: \"${nodeIds[i]}\" not found`)\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const pool = (store as any).pool as import('pg').Pool\n const client = await pool.connect()\n\n try {\n await client.query('BEGIN')\n\n const deleted: string[] = []\n\n for (const nid of nodeIds) {\n // Cascade-delete connected edges first\n await client.query(\n `DELETE FROM upg.edges WHERE source = $1 OR target = $1`,\n [nid],\n )\n await client.query(`DELETE FROM upg.nodes WHERE id = $1`, [nid])\n await appendAudit(client, {\n productId: args.product_id as string,\n action: 'delete', entityType: 'node', entityId: nid,\n })\n deleted.push(nid)\n }\n\n await client.query('COMMIT')\n for (const nid of deleted) store.emit(args.product_id as string, 'node.deleted', { id: nid })\n return text(JSON.stringify({ deleted, count: deleted.length }, null, 2))\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n } finally {\n client.release()\n }\n}\n\n// ─── batch_create_edges ───────────────────────────────────────────────────────\n\n/**\n * Create up to 50 edges in a single atomic transaction. Edge type is\n * auto-inferred from source/target node types when omitted.\n *\n * @returns JSON: `{ created: [{ id, source_id, target_id, type }], count }`.\n * @throws textError when `edges` is missing / non-array / empty / >50, any\n * item is missing `source_id` / `target_id`, any endpoint does not exist, an\n * explicit `type` violates the catalog's source/target pair, or an inferred\n * pair has no canonical edge. Any such failure rejects the whole batch\n * before BEGIN.\n * @atomicity atomic-with-rollback (BEGIN / COMMIT / ROLLBACK).\n * @warning Inference is catalog-strict: an unmapped pair is refused rather\n * than fabricating a `${source}_contains_${target}` edge. Pass an explicit\n * `type` (resolved via `resolve_edge_for_pair`) for non-catalog edges.\n * @see create_edge\n * @see resolve_edge_for_pair\n * @see batch_delete_edges\n */\nexport const batchCreateEdges: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n const edges = args.edges as Array<Record<string, unknown>> | undefined\n if (!edges || !Array.isArray(edges)) return textError('Missing required parameter: edges (array)')\n if (edges.length === 0) return textError('edges array is empty')\n if (edges.length > 50) return textError('Maximum 50 edges per batch')\n\n const productId = args.product_id as string\n\n // Pre-validate all edges before opening the transaction. Catalog-strict:\n // resolve every edge type up front and reject the whole batch on the first\n // invalid pair / unmapped inference; no `_contains_` fabrication.\n const resolvedEdgeTypes: string[] = []\n for (let i = 0; i < edges.length; i++) {\n const e = edges[i]\n if (!e.source_id) return textError(`Edge at index ${i}: missing required field \"source_id\"`)\n if (!e.target_id) return textError(`Edge at index ${i}: missing required field \"target_id\"`)\n if (e.source_id === e.target_id) return textError(`Edge at index ${i}: self-loop refused (source equals target \"${e.source_id}\").`)\n const source = await store.getNode(e.source_id as string)\n const target = await store.getNode(e.target_id as string)\n if (!source) return textError(`Edge at index ${i}: source node \"${e.source_id}\" not found`)\n if (!target) return textError(`Edge at index ${i}: target node \"${e.target_id}\" not found`)\n\n const explicitType = e.type as string | undefined\n if (explicitType) {\n const pairCheck = validateEdgeTypePair(explicitType, source.type, target.type)\n if (!pairCheck.valid) return textError(`Edge at index ${i}: ${pairCheck.reason}`)\n resolvedEdgeTypes.push(explicitType)\n } else {\n const inference = inferEdgeTypeWithTier(source.type, target.type)\n if (!inference.ok) {\n const suggestion = inference.suggestions.length > 0\n ? ` Try one of: ${inference.suggestions.map((s) => `${s.source_type} → ${s.target_type} (${s.edge_type})`).join('; ')}.`\n : ''\n return textError(`Edge at index ${i}: no canonical edge type for ${source.type} → ${target.type}.${suggestion} Pass an explicit \\`type\\` if you need a non-catalog edge.`)\n }\n resolvedEdgeTypes.push(inference.edgeType)\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const pool = (store as any).pool as import('pg').Pool\n const client = await pool.connect()\n\n try {\n await client.query('BEGIN')\n\n const created: Array<{ id: string; source_id: string; target_id: string; type: string }> = []\n\n for (let i = 0; i < edges.length; i++) {\n const e = edges[i]\n const eid = edgeId()\n const edgeType = resolvedEdgeTypes[i]\n\n await client.query(\n `INSERT INTO upg.edges (id, product_id, source, target, type)\n VALUES ($1, $2, $3, $4, $5)`,\n [eid, productId, e.source_id as string, e.target_id as string, edgeType],\n )\n await appendAudit(client, {\n productId, action: 'create', entityType: 'edge', entityId: eid,\n changes: { source: e.source_id as string, target: e.target_id as string, type: edgeType },\n })\n\n created.push({\n id: eid,\n source_id: e.source_id as string,\n target_id: e.target_id as string,\n type: edgeType,\n })\n }\n\n await client.query('COMMIT')\n for (const c of created) store.emit(productId, 'edge.created', { id: c.id, source: c.source_id, target: c.target_id, type: c.type })\n return text(JSON.stringify({ created, count: created.length }, null, 2))\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n } finally {\n client.release()\n }\n}\n\n// ─── batch_delete_edges ───────────────────────────────────────────────────────\n\n/**\n * Delete up to 50 edges in a single atomic transaction. All edge IDs are\n * resolved before the transaction opens.\n *\n * @returns JSON: `{ deleted: [id], count }`.\n * @throws textError when `edge_ids` is missing / non-array / empty / >50, or\n * any ID does not resolve in the given product.\n * @atomicity atomic-with-rollback (BEGIN / COMMIT / ROLLBACK).\n * @see delete_edge\n * @see batch_create_edges\n * @see export_edges\n */\nexport const batchDeleteEdges: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n const edgeIds = args.edge_ids as string[] | undefined\n if (!edgeIds || !Array.isArray(edgeIds)) return textError('Missing required parameter: edge_ids (array)')\n if (edgeIds.length === 0) return textError('edge_ids array is empty')\n if (edgeIds.length > 50) return textError('Maximum 50 edge IDs per batch')\n\n const productId = args.product_id as string\n\n // Pre-validate: verify all edge IDs exist in this product\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const pool = (store as any).pool as import('pg').Pool\n\n // Quick existence check outside the transaction\n for (let i = 0; i < edgeIds.length; i++) {\n const { rows } = await pool.query(\n `SELECT id FROM upg.edges WHERE id = $1 AND product_id = $2`,\n [edgeIds[i], productId],\n )\n if (rows.length === 0) {\n return textError(`Edge at index ${i}: \"${edgeIds[i]}\" not found in product \"${productId}\"`)\n }\n }\n\n const client = await pool.connect()\n\n try {\n await client.query('BEGIN')\n\n const deleted: string[] = []\n\n for (const eid of edgeIds) {\n await client.query(`DELETE FROM upg.edges WHERE id = $1`, [eid])\n await appendAudit(client, {\n productId, action: 'delete', entityType: 'edge', entityId: eid,\n })\n deleted.push(eid)\n }\n\n await client.query('COMMIT')\n for (const eid of deleted) store.emit(productId, 'edge.deleted', { id: eid })\n return text(JSON.stringify({ deleted, count: deleted.length }, null, 2))\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n } finally {\n client.release()\n }\n}\n\n// ─── batch_move_nodes ─────────────────────────────────────────────────────────\n\n/**\n * Re-parent up to 50 nodes in a single atomic transaction. For each move,\n * the old containment edge is deleted and a new containment edge to\n * `new_parent_id` is created (type inferred from parent→child types). The\n * transaction is rolled back entirely if any step fails.\n *\n * @returns JSON: `{ moved: [{ node_id, new_parent_id }], count }`.\n * @throws textError when `moves` is missing / non-array / empty / >50, or any\n * `node_id` / `new_parent_id` does not resolve.\n * @atomicity atomic-with-rollback (BEGIN / COMMIT / ROLLBACK).\n * @warning Heuristic deletion of \"old containment\" edges relies on LIKE\n * patterns (`%_contains_%`, `%_has_%`, `%_produces_%`) rather than the\n * canonical edge catalog. Edges matching the patterns yet semantically\n * non-containment may be removed alongside the intended ones. A follow-up\n * will tighten this to catalog-aware classification.\n * @see move_node\n * @see batch_create_edges\n * @see resolve_edge_for_pair\n */\nexport const batchMoveNodes: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n const moves = args.moves as Array<Record<string, unknown>> | undefined\n if (!moves || !Array.isArray(moves)) return textError('Missing required parameter: moves (array)')\n if (moves.length === 0) return textError('moves array is empty')\n if (moves.length > 50) return textError('Maximum 50 moves per batch')\n\n const productId = args.product_id as string\n\n // Pre-validate all moves before opening the transaction. Catalog-strict:\n // resolve each new containment edge up front and reject the whole batch on a\n // non-canonical pair; no `_contains_` fabrication.\n const resolvedMoveEdgeTypes: string[] = []\n for (let i = 0; i < moves.length; i++) {\n const m = moves[i]\n if (!m.node_id) return textError(`Move at index ${i}: missing required field \"node_id\"`)\n if (!m.new_parent_id) return textError(`Move at index ${i}: missing required field \"new_parent_id\"`)\n const node = await store.getNode(m.node_id as string)\n if (!node) return textError(`Move at index ${i}: node \"${m.node_id}\" not found`)\n const newParent = await store.getNode(m.new_parent_id as string)\n if (!newParent) return textError(`Move at index ${i}: new parent \"${m.new_parent_id}\" not found`)\n const inference = inferEdgeTypeWithTier(newParent.type, node.type)\n if (!inference.ok) {\n const suggestion = inference.suggestions.length > 0\n ? ` Suggestions: ${inference.suggestions.map((s) => `${s.source_type} → ${s.target_type} (${s.edge_type})`).join('; ')}.`\n : ''\n return textError(`Move at index ${i}: no canonical edge type for ${newParent.type} → ${node.type}.${suggestion} Reparenting refused.`)\n }\n resolvedMoveEdgeTypes.push(inference.edgeType)\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const pool = (store as any).pool as import('pg').Pool\n const client = await pool.connect()\n\n try {\n await client.query('BEGIN')\n\n const moved: Array<{ node_id: string; new_parent_id: string }> = []\n\n for (let i = 0; i < moves.length; i++) {\n const m = moves[i]\n const nid = m.node_id as string\n const newParentId = m.new_parent_id as string\n\n // Existence re-check inside the tx for consistency; the edge type was\n // resolved catalog-strict in the pre-validate pass above.\n const { rows: nodeRows } = await client.query(\n `SELECT type FROM upg.nodes WHERE id = $1 AND product_id = $2`,\n [nid, productId],\n )\n const { rows: parentRows } = await client.query(\n `SELECT type FROM upg.nodes WHERE id = $1 AND product_id = $2`,\n [newParentId, productId],\n )\n\n if (nodeRows.length === 0) throw new Error(`Node not found in transaction: ${nid}`)\n if (parentRows.length === 0) throw new Error(`New parent not found in transaction: ${newParentId}`)\n\n const newEdgeType = resolvedMoveEdgeTypes[i]\n\n // Delete ALL existing containment edges targeting this node from within\n // this product. \"Containment\" edges are those whose target is this node.\n // We delete based on the inferred edge type pattern: edges where this\n // node is the target, scoped to the product.\n await client.query(\n `DELETE FROM upg.edges\n WHERE product_id = $1 AND target = $2\n AND type LIKE '%_contains_%'\n OR (product_id = $1 AND target = $2 AND type LIKE '%_has_%')\n OR (product_id = $1 AND target = $2 AND type LIKE '%_produces_%')`,\n [productId, nid],\n )\n\n // Create the new containment edge to the new parent\n const eid = edgeId()\n await client.query(\n `INSERT INTO upg.edges (id, product_id, source, target, type)\n VALUES ($1, $2, $3, $4, $5)`,\n [eid, productId, newParentId, nid, newEdgeType],\n )\n await appendAudit(client, {\n productId, action: 'update', entityType: 'node', entityId: nid,\n changes: { new_parent_id: newParentId, edge_type: newEdgeType },\n })\n\n moved.push({ node_id: nid, new_parent_id: newParentId })\n }\n\n await client.query('COMMIT')\n for (const m of moved) store.emit(productId, 'node.updated', { id: m.node_id, new_parent_id: m.new_parent_id })\n return text(JSON.stringify({ moved, count: moved.length }, null, 2))\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n } finally {\n client.release()\n }\n}\n","/**\n * `validate_graph`: schema-drift detection over a product's Postgres graph.\n *\n * Three drift surfaces:\n * 1. Entity-type drift (nodes outside UPG_TYPES_SET).\n * 2. Edge-type drift (edges outside UPG_EDGE_CATALOG).\n * 3. Property drift (sampled over 500 nodes).\n *\n * Read-only. FK constraints handle dangling-edge enforcement at the database.\n * Pair with `migrate_type` and `rename_edge_type` for remediation.\n */\n\nimport {\n UPG_TYPES_SET,\n UPG_EDGE_CATALOG,\n UPG_PROPERTY_SCHEMA,\n UPG_ENTITY_META_BY_NAME,\n} from '@unified-product-graph/core'\nimport { type ToolHandler, text, textError } from '../lib/server-context.js'\n\n/** All valid edge type strings, derived from the edge catalog keys. */\nconst VALID_EDGE_TYPES: ReadonlySet<string> = new Set(Object.keys(UPG_EDGE_CATALOG))\n\n/** For each entity type, the list of property keys defined in the schema. */\nconst SCHEMA_PROPERTIES_BY_TYPE: ReadonlyMap<string, string[]> = buildSchemaPropertiesMap()\n\nfunction buildSchemaPropertiesMap(): Map<string, string[]> {\n const m = new Map<string, string[]>()\n for (const [entityType, schema] of Object.entries(UPG_PROPERTY_SCHEMA)) {\n const keys = Object.keys(schema)\n if (keys.length > 0) m.set(entityType, keys)\n }\n return m\n}\n\n/** Suggest a canonical replacement for an unknown entity type, if one exists. */\nfunction suggestMigration(unknownType: string): string | null {\n const meta = UPG_ENTITY_META_BY_NAME.get(unknownType)\n if (meta?.replacement) return meta.replacement\n return null\n}\n\n// ─── Internal row types for SQL results ───────────────────────────────────────\n\ninterface EntityTypeDriftRow { type: string; count: string }\ninterface EdgeTypeDriftRow { type: string; count: string }\ninterface CountRow { count: string }\ninterface NodeSampleRow { type: string; id: string; data: Record<string, unknown> | null }\n\n// ─── Pool accessor ───────────────────────────────────────────────────────────\n\ninterface PoolLike {\n query<T = unknown>(sql: string, values?: unknown[]): Promise<{ rows: T[] }>\n}\n\nfunction getPool(store: object): PoolLike {\n return (store as unknown as { pool: PoolLike }).pool\n}\n\n/**\n * Validate a product graph for schema drift. Detects entity type drift,\n * edge type drift, and property drift (sampled over 500 nodes). Postgres\n * FK constraints enforce endpoint existence, so intra-product edges stay\n * tied to live endpoints.\n *\n * @returns JSON: `{ valid, product_id, summary, entity_type_drift,\n * edge_type_drift, property_drift, notes }`.\n * @throws textError when `product_id` is missing or the product\n * is not visible to the caller.\n * @atomicity atomic (read-only)\n * @warning **Property drift is sampled** (first 500 nodes by id order);\n * for products beyond 500 nodes the drift list is incomplete. Each\n * reported type carries one example node id; run again or query\n * `list_nodes` for full coverage.\n * @see migrate_type\n * @see migrate_cross_edges\n * @see rename_edge_type\n * @see list_anti_patterns\n * @see list_type_migrations\n * @see list_edge_migrations\n * @see inspect\n */\nexport const validateGraph: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n const productId = args.product_id as string\n\n // Verify the product exists first.\n try {\n await store.getProduct(productId)\n } catch {\n return textError(`Product not found: ${productId}`)\n }\n\n const pool = getPool(store)\n\n // ── 1. Total counts ──────────────────────────────────────────────────────────\n\n const [{ rows: nodeCountRows }, { rows: edgeCountRows }] = await Promise.all([\n pool.query<CountRow>(`SELECT COUNT(*)::text AS count FROM upg.nodes WHERE product_id = $1`, [productId]),\n pool.query<CountRow>(`SELECT COUNT(*)::text AS count FROM upg.edges WHERE product_id = $1`, [productId]),\n ])\n const totalNodes = parseInt(nodeCountRows[0].count, 10)\n const totalEdges = parseInt(edgeCountRows[0].count, 10)\n\n // ── 2. Entity type drift ─────────────────────────────────────────────────────\n // SQL: select types not in the canonical set, grouped and counted.\n const validTypeLiterals = [...UPG_TYPES_SET].map((t) => `'${t.replace(/'/g, \"''\")}'`).join(', ')\n\n const { rows: entityDriftRows } = await pool.query<EntityTypeDriftRow>(\n `SELECT type, COUNT(*)::text AS count\n FROM upg.nodes\n WHERE product_id = $1\n AND type NOT IN (${validTypeLiterals})\n GROUP BY type\n ORDER BY count DESC`,\n [productId],\n )\n\n const entityTypeDrift = entityDriftRows.map((row) => ({\n type: row.type,\n count: parseInt(row.count, 10),\n suggested_migration: suggestMigration(row.type),\n }))\n\n // ── 3. Edge type drift ───────────────────────────────────────────────────────\n const validEdgeLiterals = [...VALID_EDGE_TYPES].map((t) => `'${t.replace(/'/g, \"''\")}'`).join(', ')\n\n const { rows: edgeDriftRows } = await pool.query<EdgeTypeDriftRow>(\n `SELECT type, COUNT(*)::text AS count\n FROM upg.edges\n WHERE product_id = $1\n AND type NOT IN (${validEdgeLiterals})\n GROUP BY type\n ORDER BY count DESC`,\n [productId],\n )\n\n const edgeTypeDrift = edgeDriftRows.map((row) => ({\n type: row.type,\n count: parseInt(row.count, 10),\n }))\n\n // ── 4. Property drift (sampled) ──────────────────────────────────────────────\n // Sample up to 500 nodes. For each typed node that has a property schema,\n // check which schema fields are absent from the node's stored properties.\n // Report one example per entity type (first mismatch found).\n\n const { rows: sampleRows } = await pool.query<NodeSampleRow>(\n `SELECT type, id, data\n FROM upg.nodes\n WHERE product_id = $1\n LIMIT 500`,\n [productId],\n )\n\n const propertyDriftByType = new Map<string, { missingFields: string[]; exampleNodeId: string }>()\n\n for (const row of sampleRows) {\n if (propertyDriftByType.has(row.type)) continue // already have an example for this type\n const schemaFields = SCHEMA_PROPERTIES_BY_TYPE.get(row.type)\n if (!schemaFields) continue // no property schema for this entity type\n\n const nodeProperties = row.data ?? {}\n const missingFields = schemaFields.filter((field) => !(field in nodeProperties))\n if (missingFields.length > 0) {\n propertyDriftByType.set(row.type, {\n missingFields,\n exampleNodeId: row.id,\n })\n }\n }\n\n const propertyDrift = [...propertyDriftByType.entries()].map(([entityType, info]) => ({\n entity_type: entityType,\n missing_fields: info.missingFields,\n example_node_id: info.exampleNodeId,\n }))\n\n // ── Build result ─────────────────────────────────────────────────────────────\n\n const unknownTypeNodes = entityTypeDrift.reduce((sum, e) => sum + e.count, 0)\n const unknownTypeEdges = edgeTypeDrift.reduce((sum, e) => sum + e.count, 0)\n const valid = unknownTypeNodes === 0 && unknownTypeEdges === 0 && propertyDrift.length === 0\n\n return text(JSON.stringify({\n valid,\n product_id: productId,\n summary: {\n total_nodes: totalNodes,\n total_edges: totalEdges,\n unknown_type_nodes: unknownTypeNodes,\n unknown_type_edges: unknownTypeEdges,\n property_drift_types: propertyDrift.length,\n },\n entity_type_drift: entityTypeDrift,\n edge_type_drift: edgeTypeDrift,\n property_drift: propertyDrift,\n notes: [\n 'Endpoint-existence checks are enforced by Postgres FK constraints, so intra-product edges always have live endpoints.',\n 'Property drift is sampled (first 500 nodes). Run again for full coverage on large graphs.',\n ],\n }, null, 2))\n}\n","/**\n * Catalog-aware migration tools. Two handlers, both with `dry_run: true` default.\n * - `migrate_type`: bulk-retype nodes, then re-infer affected edge types.\n * - `migrate_cross_edges`: move cross-product edges from `upg.edges` to\n * `upg.cross_product_edges`.\n */\n\nimport {\n UPG_TYPES_SET,\n UPG_CROSS_EDGE_TYPES,\n resolveContainmentEdge,\n} from '@unified-product-graph/core'\nimport { nanoid } from 'nanoid'\nimport { type ToolHandler, text, textError } from '../lib/server-context.js'\n\n// ─── Pool accessor (mirrors validation.ts pattern) ────────────────────────────\n\ninterface PoolLike {\n query<T = unknown>(sql: string, values?: unknown[]): Promise<{ rows: T[] }>\n connect(): Promise<ClientLike>\n}\n\ninterface ClientLike {\n query<T = unknown>(sql: string, values?: unknown[]): Promise<{ rows: T[]; rowCount?: number | null }>\n release(): void\n}\n\nfunction getPool(store: object): PoolLike {\n return (store as unknown as { pool: PoolLike }).pool\n}\n\n// ─── Internal row types ───────────────────────────────────────────────────────\n\ninterface AffectedEdgeRow {\n id: string\n source: string\n target: string\n type: string\n source_type: string\n target_type: string\n}\n\ninterface CrossEdgeRow {\n id: string\n source: string\n target: string\n type: string\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\n/** Prefix for cross-product edge IDs generated by this tool. */\nfunction crossEdgeId(): string {\n return `ce_${nanoid(16)}`\n}\n\n/**\n * Migrate all nodes of one entity type to another within a product.\n * After renaming nodes, re-infers edge types for all edges connected to the\n * migrated nodes (because the correct edge type depends on the\n * source/target entity type pair).\n *\n * - `product_id`: Product to scope the migration to.\n * - `from_type`: Current entity type to migrate away from.\n * - `to_type`: Target entity type. Must be a valid UPG entity type.\n * - `dry_run` (default `true`): When true, counts affected rows without mutating.\n *\n * @returns JSON: `{ from_type, to_type, affected_nodes, retyped_edges, dry_run }`.\n * @throws textError when `product_id`, `from_type`, or `to_type`\n * is missing, or when `to_type` is not a known UPG entity type.\n * @atomicity atomic-with-rollback (false only)\n * @warning Default is `dry_run: true`; pass `dry_run: false` to commit.\n * Idempotent on retry: a second `dry_run: false` finds zero `from_type`\n * nodes and reports `affected_nodes: 0`. Edge re-inference uses\n * `resolveContainmentEdge`, so already-canonical edges may change type\n * when the new pair has a different canonical edge.\n * @see validate_graph\n * @see migrate_cross_edges\n * @see rename_edge_type\n * @see list_type_migrations\n * @see list_entity_types\n */\nexport const migrateType: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n if (!args.from_type) return textError('Missing required parameter: from_type')\n if (!args.to_type) return textError('Missing required parameter: to_type')\n\n const productId = args.product_id as string\n const fromType = args.from_type as string\n const toType = args.to_type as string\n const dryRun = (args.dry_run as boolean | undefined) ?? true\n\n // Validate to_type is a known UPG entity type.\n if (!UPG_TYPES_SET.has(toType)) {\n return textError(\n `Unknown entity type: \"${toType}\". ` +\n `Run get_entity_schema or validate_graph to inspect valid types.`,\n )\n }\n\n const pool = getPool(store)\n\n if (dryRun) {\n const { rows } = await pool.query<{ count: string }>(\n `SELECT COUNT(*)::text AS count FROM upg.nodes WHERE product_id = $1 AND type = $2`,\n [productId, fromType],\n )\n const affected = parseInt(rows[0]?.count ?? '0', 10)\n return text(JSON.stringify({\n from_type: fromType,\n to_type: toType,\n affected_nodes: affected,\n dry_run: true,\n }, null, 2))\n }\n\n // Apply mode: run inside a transaction.\n const client = await pool.connect()\n try {\n await client.query('BEGIN')\n\n // 1. Retype nodes.\n const { rowCount: updatedNodes } = await client.query(\n `UPDATE upg.nodes SET type = $1 WHERE product_id = $2 AND type = $3`,\n [toType, productId, fromType],\n )\n const affectedNodes = updatedNodes ?? 0\n\n // 2. Re-infer edge types for all edges connected to migrated nodes.\n // Run AFTER the node UPDATE so source_type/target_type reflect the new type.\n const { rows: affectedEdges } = await client.query<AffectedEdgeRow>(\n `SELECT e.id, e.source, e.target, e.type,\n ns.type AS source_type, nt.type AS target_type\n FROM upg.edges e\n JOIN upg.nodes ns ON ns.id = e.source\n JOIN upg.nodes nt ON nt.id = e.target\n WHERE e.product_id = $1\n AND (ns.id IN (SELECT id FROM upg.nodes WHERE product_id = $1 AND type = $2)\n OR nt.id IN (SELECT id FROM upg.nodes WHERE product_id = $1 AND type = $2))`,\n [productId, toType],\n )\n\n let retypedEdges = 0\n for (const edge of affectedEdges) {\n const newType = resolveContainmentEdge(edge.source_type, edge.target_type)\n if (newType && newType !== edge.type) {\n await client.query(\n `UPDATE upg.edges SET type = $1 WHERE id = $2`,\n [newType, edge.id],\n )\n retypedEdges++\n }\n }\n\n await client.query('COMMIT')\n\n return text(JSON.stringify({\n from_type: fromType,\n to_type: toType,\n affected_nodes: affectedNodes,\n retyped_edges: retypedEdges,\n dry_run: false,\n }, null, 2))\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n } finally {\n client.release()\n }\n}\n\n/**\n * Find edges in `upg.edges` that carry a cross-product edge type and move\n * them to `upg.cross_product_edges`. Cross-product edge types belong in\n * the cross-product table; this tool corrects data that predates the\n * tightening introduced in.\n *\n * - `product_id`: Product to scope the migration to.\n * - `dry_run` (default `true`): When true, reports what would move without moving it.\n *\n * @returns JSON: `{ product_id, migrated, count, dry_run }`.\n * @throws textError when `product_id` is missing.\n * @atomicity atomic-with-rollback (false only)\n * @warning Default is `dry_run: true`; pass `dry_run: false` to commit.\n * Idempotent on retry: a second `dry_run: false` finds zero matching\n * intra-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\n * the trail.\n * @see list_cross_edge_types\n * @see list_portfolio_cross_edges\n * @see validate_graph\n * @see migrate_type\n */\nexport const migrateCrossEdges: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n\n const productId = args.product_id as string\n const dryRun = (args.dry_run as boolean | undefined) ?? true\n\n const pool = getPool(store)\n const crossEdgeTypes = Array.from(UPG_CROSS_EDGE_TYPES)\n\n if (dryRun) {\n const { rows } = await pool.query<CrossEdgeRow>(\n `SELECT id, source, target, type\n FROM upg.edges\n WHERE product_id = $1\n AND type = ANY($2::text[])`,\n [productId, crossEdgeTypes],\n )\n return text(JSON.stringify({\n product_id: productId,\n migrated: rows,\n count: rows.length,\n dry_run: true,\n }, null, 2))\n }\n\n // Apply mode: run inside a transaction.\n const client = await pool.connect()\n try {\n await client.query('BEGIN')\n\n // 1. Select the edges to migrate.\n const { rows: edgesToMigrate } = await client.query<CrossEdgeRow>(\n `SELECT id, source, target, type\n FROM upg.edges\n WHERE product_id = $1\n AND type = ANY($2::text[])`,\n [productId, crossEdgeTypes],\n )\n\n // 2. Insert into cross_product_edges.\n for (const edge of edgesToMigrate) {\n await client.query(\n `INSERT INTO upg.cross_product_edges (id, source, target, type, created_by_product_id)\n VALUES ($1, $2, $3, $4, $5)`,\n [crossEdgeId(), edge.source, edge.target, edge.type, productId],\n )\n }\n\n // 3. Delete from upg.edges.\n if (edgesToMigrate.length > 0) {\n const ids = edgesToMigrate.map((e) => e.id)\n await client.query(\n `DELETE FROM upg.edges WHERE id = ANY($1::text[])`,\n [ids],\n )\n }\n\n await client.query('COMMIT')\n\n return text(JSON.stringify({\n product_id: productId,\n migrated: edgesToMigrate,\n count: edgesToMigrate.length,\n dry_run: false,\n }, null, 2))\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n } finally {\n client.release()\n }\n}\n","/**\n * Tool registry for the UPG cloud server. Maps each tool name to its\n * wire-level definition and handler. `server.ts` dispatches by name lookup.\n */\n\nimport type { ToolBinding, ToolDefinition } from '@unified-product-graph/mcp-tooling'\nimport type { CloudContext } from './server-context.js'\nimport { listProducts, createProduct, getAuditLog } from '../tools/products.js'\nimport { getProductContext, getGraphDigest, query, getChanges } from '../tools/context.js'\nimport {\n listNodes, getNode, getNodes, searchNodes,\n createNode, updateNode, deleteNode, getProductGraph, moveNode,\n deduplicateNodes, exportUpgDocument,\n} from '../tools/nodes.js'\nimport { createEdge, deleteEdge, exportEdges, renameEdgeType } from '../tools/edges.js'\nimport { applyFramework, scoreEntity } from '../tools/frameworks.js'\nimport { listProductAreas, getAreaGraph, createArea, getAreaContext } from '../tools/areas.js'\nimport { getEntitySchema } from '../tools/schema.js'\nimport { addComment, listComments, grantAccess, listCollaborators } from '../tools/collaboration.js'\nimport { getGraphAnalytics } from '../tools/analytics.js'\nimport { registerWebhook, listWebhooks, removeWebhook } from '../tools/webhooks.js'\nimport {\n listPlaybooks, getPlaybook,\n listApproaches, getApproach,\n plan, inspect, prioritise, trace, reflect,\n listDomains, getDomainGuide,\n listFrameworks, getFramework,\n listEdgeTypes, getEdgeType,\n listRegions, getRegion, getRegionForEntity,\n getSpecVersion, resolveEdgeForPair, listCrossEdgeTypes,\n listLenses, getLensTool,\n listTypeLabels, getTypeLabel, getValidChildrenTool,\n listEntityTypes, getEntityMeta,\n listAntiPatterns, getAntiPattern,\n listBenchmarks, listProductStages,\n // Spec catalogues (migrations, lifecycles, scales, framework metadata, domain rings)\n listTypeMigrations, listEdgeMigrations, listSplitMigrations,\n listLifecycles, getLifecycle,\n listScales, getScale,\n listFrameworkCategories, listFrameworkStructurePatterns,\n listDomainRings, getDomainRing,\n} from '../tools/spec.js'\nimport { listPortfolios, listPortfolioCrossEdges, createCrossProductEdge, repairDanglingEdges } from '../tools/portfolio.js'\nimport {\n batchCreateNodes, batchUpdateNodes, batchDeleteNodes,\n batchCreateEdges, batchDeleteEdges, batchMoveNodes,\n} from '../tools/batch.js'\nimport { validateGraph } from '../tools/validation.js'\nimport { migrateType, migrateCrossEdges } from '../tools/migrations.js'\n\n/** Wire-shape definitions only, passed to `tools/list`. */\nexport const TOOL_DEFINITIONS: ToolDefinition[] = [\n {\n \"name\": \"list_products\",\n \"description\": \"List all products in this UPG cloud instance.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {}\n }\n },\n {\n \"name\": \"create_product\",\n \"description\": \"Create a new product graph.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"title\": {\n \"type\": \"string\",\n \"description\": \"Product name\"\n },\n \"description\": {\n \"type\": \"string\",\n \"description\": \"Optional description\"\n },\n \"stage\": {\n \"type\": \"string\",\n \"description\": \"idea | mvp | growth | scale\"\n }\n },\n \"required\": [\n \"title\"\n ]\n }\n },\n {\n \"name\": \"get_product_context\",\n \"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.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n }\n },\n \"required\": [\n \"product_id\"\n ]\n }\n },\n {\n \"name\": \"list_nodes\",\n \"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.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n },\n \"type\": {\n \"type\": \"string\",\n \"description\": \"Filter by entity type\"\n },\n \"limit\": {\n \"type\": \"number\",\n \"description\": \"Max results (default 1000, max 10000)\"\n },\n \"cursor\": {\n \"type\": \"string\",\n \"description\": \"Opaque pagination cursor; pass next_cursor from a previous response to advance.\"\n },\n \"offset\": {\n \"type\": \"number\",\n \"description\": \"Legacy: skip N results (default 0). Use cursor instead for new callers.\"\n }\n },\n \"required\": [\n \"product_id\"\n ]\n }\n },\n {\n \"name\": \"export_upg_document\",\n \"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.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n },\n \"limit\": {\n \"type\": \"number\",\n \"description\": \"Max nodes per page (default 1000, max 10000)\"\n },\n \"cursor\": {\n \"type\": \"string\",\n \"description\": \"Opaque pagination cursor; pass next_cursor from a previous response to advance.\"\n }\n },\n \"required\": [\n \"product_id\"\n ]\n }\n },\n {\n \"name\": \"get_node\",\n \"description\": \"Get a single entity by ID with its full properties and all connected edges.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"node_id\": {\n \"type\": \"string\",\n \"description\": \"The node ID\"\n }\n },\n \"required\": [\n \"node_id\"\n ]\n }\n },\n {\n \"name\": \"search_nodes\",\n \"description\": \"Full-text search across node titles and descriptions. Title matches rank higher.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n },\n \"query\": {\n \"type\": \"string\",\n \"description\": \"Search text\"\n },\n \"type\": {\n \"type\": \"string\",\n \"description\": \"Optional type filter\"\n },\n \"limit\": {\n \"type\": \"number\",\n \"description\": \"Max results (default 20)\"\n }\n },\n \"required\": [\n \"product_id\",\n \"query\"\n ]\n }\n },\n {\n \"name\": \"create_node\",\n \"description\": \"Create a new entity in the graph. Optionally connect it to a parent node.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n },\n \"type\": {\n \"type\": \"string\",\n \"description\": \"UPG entity type (e.g. \\\"persona\\\", \\\"opportunity\\\")\"\n },\n \"title\": {\n \"type\": \"string\",\n \"description\": \"Entity title\"\n },\n \"description\": {\n \"type\": \"string\",\n \"description\": \"Optional description\"\n },\n \"tags\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Freeform tags\"\n },\n \"status\": {\n \"type\": \"string\",\n \"description\": \"Lifecycle status\"\n },\n \"properties\": {\n \"type\": \"object\",\n \"description\": \"Type-specific fields\"\n },\n \"parent_id\": {\n \"type\": \"string\",\n \"description\": \"Parent node ID; creates an edge automatically\"\n }\n },\n \"required\": [\n \"product_id\",\n \"type\",\n \"title\"\n ]\n }\n },\n {\n \"name\": \"update_node\",\n \"description\": \"Update an existing entity. Unspecified fields are preserved.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"node_id\": {\n \"type\": \"string\",\n \"description\": \"The node ID to update\"\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"description\": {\n \"type\": \"string\"\n },\n \"tags\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n }\n },\n \"status\": {\n \"type\": \"string\"\n },\n \"properties\": {\n \"type\": \"object\",\n \"description\": \"Merged with existing properties\"\n }\n },\n \"required\": [\n \"node_id\"\n ]\n }\n },\n {\n \"name\": \"delete_node\",\n \"description\": \"Remove an entity and all its connected edges from the graph.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"node_id\": {\n \"type\": \"string\",\n \"description\": \"The node ID to delete\"\n }\n },\n \"required\": [\n \"node_id\"\n ]\n }\n },\n {\n \"name\": \"create_edge\",\n \"description\": \"Create a relationship between two nodes. Edge type is auto-inferred if omitted.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"source_id\": {\n \"type\": \"string\",\n \"description\": \"Source node ID\"\n },\n \"target_id\": {\n \"type\": \"string\",\n \"description\": \"Target node ID\"\n },\n \"type\": {\n \"type\": \"string\",\n \"description\": \"Edge type; auto-inferred if omitted\"\n }\n },\n \"required\": [\n \"source_id\",\n \"target_id\"\n ]\n }\n },\n {\n \"name\": \"delete_edge\",\n \"description\": \"Remove a relationship between two nodes.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"edge_id\": {\n \"type\": \"string\",\n \"description\": \"The edge ID to delete\"\n }\n },\n \"required\": [\n \"edge_id\"\n ]\n }\n },\n {\n \"name\": \"export_edges\",\n \"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.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": { \"type\": \"string\", \"description\": \"Product ID\" },\n \"types\": { \"type\": \"array\", \"items\": { \"type\": \"string\" }, \"description\": \"Optional edge type filter\" }\n },\n \"required\": [\"product_id\"]\n }\n },\n {\n \"name\": \"rename_edge_type\",\n \"description\": \"Rename all edges of one type to another across a product. dry_run (default: true) previews the count.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": { \"type\": \"string\", \"description\": \"Product ID\" },\n \"from\": { \"type\": \"string\", \"description\": \"Current edge type\" },\n \"to\": { \"type\": \"string\", \"description\": \"New edge type\" },\n \"dry_run\": { \"type\": \"boolean\", \"description\": \"If true, only count (default: true); pass false to apply.\" }\n },\n \"required\": [\"product_id\", \"from\", \"to\"]\n }\n },\n {\n \"name\": \"get_product_graph\",\n \"description\": \"Export the full graph for a product (all nodes + edges).\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n }\n },\n \"required\": [\n \"product_id\"\n ]\n }\n },\n {\n \"name\": \"get_audit_log\",\n \"description\": \"Get recent changes (audit log) for a product.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n },\n \"limit\": {\n \"type\": \"number\",\n \"description\": \"Max entries (default 50)\"\n }\n },\n \"required\": [\n \"product_id\"\n ]\n }\n },\n {\n \"name\": \"add_comment\",\n \"description\": \"Add a comment on a node in the graph.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n },\n \"node_id\": {\n \"type\": \"string\",\n \"description\": \"Node to comment on\"\n },\n \"user_id\": {\n \"type\": \"string\",\n \"description\": \"Author user ID\"\n },\n \"body\": {\n \"type\": \"string\",\n \"description\": \"Comment text\"\n }\n },\n \"required\": [\n \"product_id\",\n \"node_id\",\n \"user_id\",\n \"body\"\n ]\n }\n },\n {\n \"name\": \"list_comments\",\n \"description\": \"List comments on a node, newest first.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"node_id\": {\n \"type\": \"string\",\n \"description\": \"Node ID\"\n }\n },\n \"required\": [\n \"node_id\"\n ]\n }\n },\n {\n \"name\": \"grant_access\",\n \"description\": \"Grant or update a user's role on a product (owner, editor, viewer).\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n },\n \"user_id\": {\n \"type\": \"string\",\n \"description\": \"User to grant access to\"\n },\n \"role\": {\n \"type\": \"string\",\n \"description\": \"Role: owner | editor | viewer\",\n \"enum\": [\n \"owner\",\n \"editor\",\n \"viewer\"\n ]\n }\n },\n \"required\": [\n \"product_id\",\n \"user_id\",\n \"role\"\n ]\n }\n },\n {\n \"name\": \"list_collaborators\",\n \"description\": \"List all collaborators and their roles for a product.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n }\n },\n \"required\": [\n \"product_id\"\n ]\n }\n },\n {\n \"name\": \"list_product_areas\",\n \"description\": \"List all product areas in a product. Product areas are top-level organizational units within a product.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n }\n },\n \"required\": [\n \"product_id\"\n ]\n }\n },\n {\n \"name\": \"get_area_graph\",\n \"description\": \"Get all entities and edges that belong to a product area. Returns the sub-graph scoped to that area.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n },\n \"area_id\": {\n \"type\": \"string\",\n \"description\": \"The product area node ID\"\n },\n \"depth\": {\n \"type\": \"number\",\n \"description\": \"How many levels deep to traverse (default 3, max 10)\"\n }\n },\n \"required\": [\n \"product_id\",\n \"area_id\"\n ]\n }\n },\n {\n \"name\": \"get_graph_analytics\",\n \"description\": \"Computed product thinking metrics: hypothesis velocity, persona coverage ratio, evidence density, stale entity rate, orphan rate.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n }\n },\n \"required\": [\n \"product_id\"\n ]\n }\n },\n {\n \"name\": \"register_webhook\",\n \"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.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n },\n \"event\": {\n \"type\": \"string\",\n \"description\": \"Event name (e.g. node.created, node.updated, node.deleted, edge.created, edge.deleted)\"\n },\n \"url\": {\n \"type\": \"string\",\n \"description\": \"Webhook URL to POST to\"\n },\n \"secret\": {\n \"type\": \"string\",\n \"description\": \"Optional shared secret for HMAC signature verification\"\n }\n },\n \"required\": [\n \"product_id\",\n \"event\",\n \"url\"\n ]\n }\n },\n {\n \"name\": \"list_webhooks\",\n \"description\": \"List all registered webhooks for a product.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n }\n },\n \"required\": [\n \"product_id\"\n ]\n }\n },\n {\n \"name\": \"remove_webhook\",\n \"description\": \"Remove a registered webhook by ID.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"webhook_id\": {\n \"type\": \"string\",\n \"description\": \"Webhook ID to remove\"\n }\n },\n \"required\": [\n \"webhook_id\"\n ]\n }\n },\n {\n \"name\": \"query\",\n \"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.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"The product ID\"\n },\n \"from\": {\n \"type\": \"string\",\n \"description\": \"Start from all nodes of this type\"\n },\n \"from_id\": {\n \"type\": \"string\",\n \"description\": \"Start from a specific node ID\"\n },\n \"traverse\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Edge types to follow per level. Prefix with ! to exclude.\"\n },\n \"depth\": {\n \"type\": \"number\",\n \"description\": \"Max depth (default 3, max 10)\"\n },\n \"include\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Node fields: \\\"title\\\", \\\"status\\\", \\\"tags\\\", \\\"description\\\", \\\"properties\\\"\"\n },\n \"limit\": {\n \"type\": \"number\",\n \"description\": \"Max nodes (default 200, max 1000)\"\n },\n \"edge_include\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Edge fields to return. Empty = no edges.\"\n }\n },\n \"required\": [\n \"product_id\"\n ]\n }\n },\n {\n \"name\": \"get_graph_digest\",\n \"description\": \"Pre-computed graph analytics: counts, health metrics, chain completeness, business area coverage, lifecycle balance. ~500 tokens vs ~5-8K for equivalent manual fetches.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"The product ID\"\n }\n },\n \"required\": [\n \"product_id\"\n ]\n }\n },\n {\n \"name\": \"get_nodes\",\n \"description\": \"Batch-fetch multiple entities by ID with edges. More efficient than multiple get_node calls.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"The product ID\"\n },\n \"ids\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Node IDs to fetch (max 50)\"\n },\n \"compact_edges\": {\n \"type\": \"boolean\",\n \"description\": \"Omit titles from edges\"\n }\n },\n \"required\": [\n \"product_id\",\n \"ids\"\n ]\n }\n },\n {\n \"name\": \"get_changes\",\n \"description\": \"Get a log of recent changes from the audit log.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"The product ID\"\n },\n \"since\": {\n \"type\": \"string\",\n \"description\": \"ISO 8601 timestamp; only return changes after this time\"\n },\n \"limit\": {\n \"type\": \"number\",\n \"description\": \"Max results (default 50)\"\n }\n },\n \"required\": [\n \"product_id\"\n ]\n }\n },\n {\n \"name\": \"get_entity_schema\",\n \"description\": \"Returns the schema for a UPG entity type: valid parent→child edges, properties, lifecycle phases.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"type\": {\n \"type\": \"string\",\n \"description\": \"The UPG entity type (e.g. \\\"feature\\\", \\\"persona\\\")\"\n }\n },\n \"required\": [\n \"type\"\n ]\n }\n },\n // ── Spec introspection round 1 ───────────────\n {\n name: 'list_playbooks',\n description:\n '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).',\n inputSchema: {\n type: 'object' as const,\n properties: {\n region: { type: 'string', description: 'Exact-match UPGRegionId (e.g. \"users_needs\", \"business_gtm_growth\").' },\n canonical_only: { type: 'boolean', description: 'When true, return only the canonical playbook per region (W1 invariant restated).' },\n framework_id: { type: 'string', description: 'Exact-match UPGFramework.id (e.g. \"business-model-canvas\", \"pirate-metrics-aarrr\").' },\n },\n },\n },\n {\n name: 'get_playbook',\n description:\n 'Return one canonical UPGPlaybook by id (e.g. \"playbook:strategy-outcomes\", \"playbook:business-gtm-growth\"). 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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n id: { type: 'string', description: 'Playbook id (namespace-prefixed: playbook:*).' },\n },\n required: ['id'],\n },\n },\n {\n name: 'list_approaches',\n description:\n '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).',\n inputSchema: {\n type: 'object' as const,\n properties: {\n framework_id: { type: 'string', description: 'Exact-match framework id; narrows to approaches whose framework_id_examples include it (discoverability surface; full reverse lookup is on UPGFramework.approach_ids).' },\n },\n },\n },\n {\n name: 'get_approach',\n description:\n '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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n id: { type: 'string', description: 'Approach id, one of: plan, inspect, prioritise, trace, reflect.', enum: ['plan', 'inspect', 'prioritise', 'trace', 'reflect'] },\n },\n required: ['id'],\n },\n },\n {\n name: 'plan',\n description:\n '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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n region: { type: 'string', description: 'Optional UPGRegionId; narrows planning scope to a single region (e.g. \"users_needs\", \"business_gtm_growth\"). Omit for whole-graph planning.' },\n },\n },\n },\n {\n name: 'inspect',\n description:\n '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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n region: { type: 'string', description: 'Optional UPGRegionId; narrows inspection scope to a single region.' },\n entities: { type: 'array', items: { type: 'string' }, description: 'Optional entity_id[]; narrows inspection scope to a specific candidate set. Mutually composable with region.' },\n },\n },\n },\n {\n name: 'prioritise',\n description:\n '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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n candidates: { type: 'array', items: { type: 'string' }, description: 'Required: entity_id[] to rank.' },\n framework_id: { type: 'string', description: 'Required: UPGFramework.id of the scoring lens (e.g. \"rice-scoring\", \"ice-scoring\", \"kano-model\", \"cost-of-delay\", \"wsjf\").' },\n },\n required: ['candidates', 'framework_id'],\n },\n },\n {\n name: 'trace',\n description:\n '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\".',\n inputSchema: {\n type: 'object' as const,\n properties: {\n anchor: { type: 'string', description: 'Required: entity_id where the traversal starts.' },\n path: { type: 'array', items: { type: 'string' }, description: 'Required: UPGEntityType[] type-shorthand path. Each step walks via the canonical edge for the source→target pair.' },\n edges_override: { type: 'array', items: { type: ['string', 'null'] }, description: 'Optional per-hop edge override array. Length must match path length; element null means \"use canonical edge for this pair\".' },\n },\n required: ['anchor', 'path'],\n },\n },\n {\n name: 'reflect',\n description:\n '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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n scope: { type: ['string', 'null'], description: 'Optional: region id, entity id, or null for whole-graph.' },\n mode: { type: 'string', description: 'Optional: one of assumptions, alternatives, blind-spots, load-bearing. Omit for open reflection.', enum: ['assumptions', 'alternatives', 'blind-spots', 'load-bearing'] },\n },\n },\n },\n {\n name: 'list_domains',\n description:\n '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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n with_guide_only: {\n type: 'boolean',\n description:\n '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).',\n },\n },\n },\n },\n {\n name: 'get_domain_guide',\n description:\n 'Return the full UPGDomainUsageGuide for a domain: anchor entity, creation sequence, named patterns (entity and edge chains), required cross-domain bridges, and anti-patterns.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n domain_id: { type: 'string', description: 'Canonical domain id (e.g. \"user\", \"market_intelligence\", \"growth\").' },\n },\n required: ['domain_id'],\n },\n },\n {\n name: 'list_frameworks',\n description:\n 'List the canonical UPGFramework definitions: the curated, famous product frameworks that anchor the public catalog. 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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n category: { type: 'string', description: 'Exact-match filter on UPGFramework.category (e.g. \"strategy\", \"prioritization\").' },\n limit: { type: 'number', description: 'Page size (default 50, max 200).' },\n cursor: { type: 'string', description: 'Opaque pagination cursor; pass next_cursor from a previous response.' },\n },\n },\n },\n {\n name: 'get_framework',\n description:\n 'Return one canonical UPGFramework by id (e.g. \"rice-scoring\", \"lean-canvas\"). Includes all four layers: data, structure, presentation, education.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n id: { type: 'string', description: 'Framework id (kebab-case).' },\n },\n required: ['id'],\n },\n },\n {\n name: 'list_edge_types',\n description:\n '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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n source_type: { type: 'string', description: 'Exact-match filter on UPGEdgeDefinition.source_type. Pass \"node\" to find polymorphic edges with a wildcard source.' },\n target_type: { type: 'string', description: 'Exact-match filter on UPGEdgeDefinition.target_type.' },\n },\n },\n },\n {\n name: 'get_edge_type',\n description:\n 'Return one canonical edge catalogue entry by edge type key (e.g. \"persona_pursues_job\", \"feature_addresses_need\").',\n inputSchema: {\n type: 'object' as const,\n properties: {\n type: { type: 'string', description: 'Edge type key from UPG_EDGE_CATALOG.' },\n },\n required: ['type'],\n },\n },\n // ── Spec introspection round 2 ─────────────────────────────────\n {\n name: 'list_regions',\n description:\n '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.',\n inputSchema: { type: 'object' as const, properties: {} },\n },\n {\n name: 'get_region',\n description:\n '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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n id: {\n type: 'string',\n description:\n 'Region id (e.g. \"strategy_outcomes\", \"users_needs\", \"product_delivery\"). See UPG_REGIONS for the full list of 10.',\n },\n },\n required: ['id'],\n },\n },\n {\n name: 'get_region_for_entity_type',\n description:\n '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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n entity_type: {\n type: 'string',\n description: 'Canonical entity type (e.g. \"persona\", \"feature\", \"metric\").',\n },\n },\n required: ['entity_type'],\n },\n },\n {\n name: 'get_spec_version',\n description:\n '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.',\n inputSchema: { type: 'object' as const, properties: {} },\n },\n {\n name: 'resolve_edge_for_pair',\n description:\n '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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n source_type: { type: 'string', description: 'Parent / source entity type.' },\n target_type: { type: 'string', description: 'Child / target entity type.' },\n },\n required: ['source_type', 'target_type'],\n },\n },\n {\n name: 'list_cross_edge_types',\n description:\n 'List the canonical cross-product edge types from UPG_CROSS_EDGE_TYPES (shares_persona, shares_competitor, shares_metric, depends_on_product, cannibalises, succeeds, hosts, contributes_to). Portfolio-level relationships between entities in different products, separate from the within-product UPG_EDGE_CATALOG.',\n inputSchema: { type: 'object' as const, properties: {} },\n },\n {\n name: 'list_lenses',\n description:\n '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.',\n inputSchema: { type: 'object' as const, properties: {} },\n },\n {\n name: 'get_lens',\n description:\n '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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n id: { type: 'string', description: 'Lens id (e.g. \"product\", \"ux_design\", \"full\").' },\n },\n required: ['id'],\n },\n },\n {\n name: 'list_type_labels',\n description:\n '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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n limit: { type: 'number', description: 'Page size (default 100, max 500).' },\n cursor: {\n type: 'string',\n description: 'Opaque pagination cursor; pass next_cursor from a previous response.',\n },\n },\n },\n },\n {\n name: 'get_type_label',\n description:\n '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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n entity_type: { type: 'string', description: 'Canonical entity type id.' },\n framework_id: {\n type: 'string',\n description: 'Optional framework id (e.g. \"lean_canvas\", \"ost\", \"design_thinking\"); when set, resolved_label uses the framework-specific label.',\n },\n designation: {\n type: 'string',\n description: 'Optional designation key (e.g. \"pain\", \"gap\", \"desire\") for types that use the designation pattern.',\n },\n },\n required: ['entity_type'],\n },\n },\n {\n name: 'get_valid_children',\n description:\n '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?\".',\n inputSchema: {\n type: 'object' as const,\n properties: {\n parent_type: { type: 'string', description: 'Canonical parent entity type.' },\n },\n required: ['parent_type'],\n },\n },\n // ── Spec introspection round 3 ─────────────────────────────────\n {\n name: 'list_entity_types',\n description:\n '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).',\n inputSchema: {\n type: 'object' as const,\n properties: {\n domain: { type: 'string', description: 'Exact-match atomic-domain id (e.g. \"user\", \"market_intelligence\").' },\n maturity: {\n type: 'string',\n enum: ['draft', 'proposed', 'stable', 'deprecated', 'removed'],\n description: 'Exact-match UPGEntityTypeMaturity.',\n },\n deprecated: {\n type: 'boolean',\n description: 'true → only deprecated types; false → exclude deprecated and removed types (the active set). Composes with maturity via AND.',\n },\n limit: { type: 'number', description: 'Page size (default 50, max 200).' },\n cursor: {\n type: 'string',\n description: 'Opaque pagination cursor; pass next_cursor from a previous response.',\n },\n },\n },\n },\n {\n name: 'get_entity_meta',\n description:\n '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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n name: { type: 'string', description: 'Canonical entity type name.' },\n },\n required: ['name'],\n },\n },\n {\n name: 'list_anti_patterns',\n description:\n '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).',\n inputSchema: {\n type: 'object' as const,\n properties: {\n severity: {\n type: 'string',\n enum: ['high', 'medium', 'low'],\n description: 'Exact-match UPGAntiPatternSeverity.',\n },\n stage: {\n type: 'string',\n enum: [\n 'concept', 'validation', 'build', 'beta', 'launch',\n 'growth', 'mature', 'maintenance', 'sunset',\n ],\n description: 'Keeps anti-patterns whose stages[] includes the given UPGProductStage.',\n },\n limit: { type: 'number', description: 'Page size (default 50, max 200).' },\n cursor: {\n type: 'string',\n description: 'Opaque pagination cursor; pass next_cursor from a previous response.',\n },\n },\n },\n },\n {\n name: 'get_anti_pattern',\n description:\n '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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n id: { type: 'string', description: 'Anti-pattern id (kebab-case slug).' },\n },\n required: ['id'],\n },\n },\n {\n name: 'list_benchmarks',\n description:\n '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).',\n inputSchema: {\n type: 'object' as const,\n properties: {\n kind: {\n type: 'string',\n enum: ['count', 'relationship', 'ratio', 'domain_activation'],\n description: 'Required: which benchmark catalog to return.',\n },\n stage: {\n type: 'string',\n enum: [\n 'concept', 'validation', 'build', 'beta', 'launch',\n 'growth', 'mature', 'maintenance', 'sunset',\n ],\n description: 'Optional UPGProductStage filter. Semantics depend on kind; see tool description.',\n },\n domain: {\n type: 'string',\n description: 'Optional atomic-domain id filter. Semantics depend on kind; see tool description.',\n },\n },\n required: ['kind'],\n },\n },\n {\n name: 'list_product_stages',\n description:\n '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.',\n inputSchema: { type: 'object' as const, properties: {} },\n },\n // ── Spec introspection round 5 ─────────\n {\n name: 'list_type_migrations',\n description:\n '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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n from_type: { type: 'string', description: 'Exact-match filter on the deprecated type name (e.g. \"pain_point\", \"hypothesis\").' },\n },\n },\n },\n {\n name: 'list_edge_migrations',\n description:\n '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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n from_edge: { type: 'string', description: 'Exact-match filter on the deprecated edge key (e.g. \"persona_has_jtbd\").' },\n },\n },\n },\n {\n name: 'list_split_migrations',\n description:\n '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.',\n inputSchema: { type: 'object' as const, properties: {} },\n },\n {\n name: 'list_lifecycles',\n description:\n '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).',\n inputSchema: {\n type: 'object' as const,\n properties: {\n entity_type: { type: 'string', description: 'Exact-match entity type name (e.g. \"feature\", \"hypothesis_claim\"). Returns at most one lifecycle.' },\n lifecycle_only: { type: 'boolean', description: 'When true, omit free_types and planned_types from response.' },\n },\n },\n },\n {\n name: 'get_lifecycle',\n description:\n '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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n entity_type: { type: 'string', description: 'Canonical entity type name (e.g. \"feature\", \"hypothesis_claim\", \"opportunity\").' },\n },\n required: ['entity_type'],\n },\n },\n {\n name: 'list_scales',\n description:\n '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.',\n inputSchema: { type: 'object' as const, properties: {} },\n },\n {\n name: 'get_scale',\n description:\n 'Return one spec-defined assessment scale by id (e.g. \"reach_5\", \"severity_5\", \"confidence_binary\"). Includes the full point array.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n id: { type: 'string', description: 'Scale id (e.g. \"reach_5\", \"frequency_5\", \"severity_5\", \"importance_5\", \"confidence_binary\").' },\n },\n required: ['id'],\n },\n },\n {\n name: 'list_framework_categories',\n description:\n '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.',\n inputSchema: { type: 'object' as const, properties: {} },\n },\n {\n name: 'list_framework_structure_patterns',\n description:\n '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.',\n inputSchema: { type: 'object' as const, properties: {} },\n },\n {\n name: 'list_domain_rings',\n description:\n '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.',\n inputSchema: { type: 'object' as const, properties: {} },\n },\n {\n name: 'get_domain_ring',\n description:\n 'Return one UPGDomainRing by id (e.g. \"nucleus\", \"understand\", \"define\", \"build\", \"grow\", \"operate\", \"extend\").',\n inputSchema: {\n type: 'object' as const,\n properties: {\n id: { type: 'string', description: 'Ring id, one of: nucleus, understand, define, build, grow, operate, extend.' },\n },\n required: ['id'],\n },\n },\n {\n \"name\": \"move_node\",\n \"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.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n },\n \"node_id\": {\n \"type\": \"string\",\n \"description\": \"The node to reparent\"\n },\n \"new_parent_id\": {\n \"type\": \"string\",\n \"description\": \"The new parent node ID\"\n }\n },\n \"required\": [\n \"product_id\",\n \"node_id\",\n \"new_parent_id\"\n ]\n }\n },\n {\n \"name\": \"create_area\",\n \"description\": \"Create a new product area node (type 'area') in a product. Product areas are top-level organisational units within a product.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n },\n \"title\": {\n \"type\": \"string\",\n \"description\": \"Area title\"\n },\n \"description\": {\n \"type\": \"string\",\n \"description\": \"Optional description\"\n }\n },\n \"required\": [\n \"product_id\",\n \"title\"\n ]\n }\n },\n {\n \"name\": \"get_area_context\",\n \"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.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n },\n \"area_id\": {\n \"type\": \"string\",\n \"description\": \"The area node ID\"\n }\n },\n \"required\": [\n \"product_id\",\n \"area_id\"\n ]\n }\n },\n {\n \"name\": \"list_portfolios\",\n \"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.\",\n \"inputSchema\": { \"type\": \"object\", \"properties\": {} }\n },\n {\n \"name\": \"list_portfolio_cross_edges\",\n \"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).\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": { \"type\": \"string\", \"description\": \"Product ID\" }\n },\n \"required\": [\"product_id\"]\n }\n },\n {\n \"name\": \"create_cross_product_edge\",\n \"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, hosts, contributes_to.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": { \"type\": \"string\", \"description\": \"The product creating this cross-edge\" },\n \"source\": { \"type\": \"string\", \"description\": \"Qualified source: {product_id}/{node_id}\" },\n \"target\": { \"type\": \"string\", \"description\": \"Qualified target: {product_id}/{node_id}\" },\n \"type\": { \"type\": \"string\", \"description\": \"Cross-edge type\", \"enum\": [\"shares_persona\", \"shares_competitor\", \"shares_metric\", \"depends_on_product\", \"cannibalises\", \"succeeds\", \"hosts\", \"contributes_to\"] }\n },\n \"required\": [\"product_id\", \"source\", \"target\", \"type\"]\n }\n },\n {\n \"name\": \"repair_dangling_edges\",\n \"description\": \"Find (and optionally remove) cross-product edges that reference a product that no longer exists. Default is dry_run=true.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": { \"type\": \"string\", \"description\": \"Product ID\" },\n \"dry_run\": { \"type\": \"boolean\", \"description\": \"Default true: report only.\" },\n \"drop\": { \"type\": \"array\", \"items\": { \"type\": \"string\" }, \"description\": \"Categories to drop when dry_run=false\" }\n },\n \"required\": [\"product_id\"]\n }\n },\n {\n \"name\": \"batch_create_nodes\",\n \"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.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": { \"type\": \"string\", \"description\": \"Product ID\" },\n \"nodes\": {\n \"type\": \"array\",\n \"description\": \"Nodes to create (max 50)\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"type\": { \"type\": \"string\", \"description\": \"UPG entity type\" },\n \"title\": { \"type\": \"string\", \"description\": \"Entity title\" },\n \"description\": { \"type\": \"string\", \"description\": \"Optional description\" },\n \"tags\": { \"type\": \"array\", \"items\": { \"type\": \"string\" }, \"description\": \"Freeform tags\" },\n \"status\": { \"type\": \"string\", \"description\": \"Lifecycle status\" },\n \"properties\": { \"type\": \"object\", \"description\": \"Type-specific fields\" },\n \"parent_id\": { \"type\": \"string\", \"description\": \"Parent node ID; creates a containment edge automatically\" }\n },\n \"required\": [\"type\", \"title\"]\n }\n }\n },\n \"required\": [\"product_id\", \"nodes\"]\n }\n },\n {\n \"name\": \"batch_update_nodes\",\n \"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.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": { \"type\": \"string\", \"description\": \"Product ID\" },\n \"nodes\": {\n \"type\": \"array\",\n \"description\": \"Nodes to update (max 50)\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"id\": { \"type\": \"string\", \"description\": \"Node ID to update\" },\n \"title\": { \"type\": \"string\" },\n \"description\": { \"type\": \"string\" },\n \"tags\": { \"type\": \"array\", \"items\": { \"type\": \"string\" } },\n \"status\": { \"type\": \"string\" },\n \"properties\": { \"type\": \"object\", \"description\": \"Merged with existing properties\" }\n },\n \"required\": [\"id\"]\n }\n }\n },\n \"required\": [\"product_id\", \"nodes\"]\n }\n },\n {\n \"name\": \"batch_delete_nodes\",\n \"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.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": { \"type\": \"string\", \"description\": \"Product ID\" },\n \"node_ids\": {\n \"type\": \"array\",\n \"items\": { \"type\": \"string\" },\n \"description\": \"Node IDs to delete (max 50)\"\n }\n },\n \"required\": [\"product_id\", \"node_ids\"]\n }\n },\n {\n \"name\": \"batch_create_edges\",\n \"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.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": { \"type\": \"string\", \"description\": \"Product ID\" },\n \"edges\": {\n \"type\": \"array\",\n \"description\": \"Edges to create (max 50)\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"source_id\": { \"type\": \"string\", \"description\": \"Source node ID\" },\n \"target_id\": { \"type\": \"string\", \"description\": \"Target node ID\" },\n \"type\": { \"type\": \"string\", \"description\": \"Edge type; auto-inferred if omitted\" }\n },\n \"required\": [\"source_id\", \"target_id\"]\n }\n }\n },\n \"required\": [\"product_id\", \"edges\"]\n }\n },\n {\n \"name\": \"batch_delete_edges\",\n \"description\": \"Delete up to 50 edges in a single atomic Postgres transaction. All-or-nothing: any failure rolls back the entire batch.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": { \"type\": \"string\", \"description\": \"Product ID\" },\n \"edge_ids\": {\n \"type\": \"array\",\n \"items\": { \"type\": \"string\" },\n \"description\": \"Edge IDs to delete (max 50)\"\n }\n },\n \"required\": [\"product_id\", \"edge_ids\"]\n }\n },\n {\n \"name\": \"batch_move_nodes\",\n \"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.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": { \"type\": \"string\", \"description\": \"Product ID\" },\n \"moves\": {\n \"type\": \"array\",\n \"description\": \"Move operations (max 50)\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"node_id\": { \"type\": \"string\", \"description\": \"The node to re-parent\" },\n \"new_parent_id\": { \"type\": \"string\", \"description\": \"The new parent node ID\" }\n },\n \"required\": [\"node_id\", \"new_parent_id\"]\n }\n }\n },\n \"required\": [\"product_id\", \"moves\"]\n }\n },\n {\n \"name\": \"validate_graph\",\n \"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.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n },\n \"dry_run\": {\n \"type\": \"boolean\",\n \"description\": \"Always true for validate_graph (validation is read-only).\"\n }\n },\n \"required\": [\n \"product_id\"\n ]\n }\n },\n {\n \"name\": \"deduplicate_nodes\",\n \"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.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n },\n \"canonical_id\": {\n \"type\": \"string\",\n \"description\": \"The node to keep\"\n },\n \"duplicate_ids\": {\n \"type\": \"array\",\n \"items\": { \"type\": \"string\" },\n \"description\": \"Nodes to merge into canonical and delete (max 20)\"\n },\n \"dry_run\": {\n \"type\": \"boolean\",\n \"description\": \"Default true: report what would happen without changing anything.\"\n }\n },\n \"required\": [\n \"product_id\",\n \"canonical_id\",\n \"duplicate_ids\"\n ]\n }\n },\n {\n \"name\": \"migrate_type\",\n \"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.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n },\n \"from_type\": {\n \"type\": \"string\",\n \"description\": \"Current entity type to migrate away from\"\n },\n \"to_type\": {\n \"type\": \"string\",\n \"description\": \"New entity type. Must be a valid UPG entity type.\"\n },\n \"dry_run\": {\n \"type\": \"boolean\",\n \"description\": \"Default true: count affected nodes without changing anything.\"\n }\n },\n \"required\": [\n \"product_id\",\n \"from_type\",\n \"to_type\"\n ]\n }\n },\n {\n \"name\": \"migrate_cross_edges\",\n \"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.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n },\n \"dry_run\": {\n \"type\": \"boolean\",\n \"description\": \"Default true: report what would move without moving.\"\n }\n },\n \"required\": [\n \"product_id\"\n ]\n }\n },\n {\n \"name\": \"apply_framework\",\n \"description\": \"Apply a framework (MoSCoW, RICE, Kano, ...) to a set of entities in a product: creates a framework_exercise node and an `includes` edge to each entity. The per-entity result is recorded on the edge via score_entity, never on the entity node, so the same entity can sit in many exercises and any entity type can be scored. Returns { exercise_id, exercise, included, warnings }.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": { \"type\": \"string\", \"description\": \"Required. Product the exercise belongs to.\" },\n \"framework_id\": { \"type\": \"string\", \"description\": \"Required. UPGFramework.id (e.g. \\\"moscow\\\", \\\"rice-scoring\\\").\" },\n \"title\": { \"type\": \"string\", \"description\": \"Human label for the exercise (default \\\"<Framework> exercise\\\").\" },\n \"entity_ids\": { \"type\": \"array\", \"items\": { \"type\": \"string\" }, \"description\": \"Entities to pull into the exercise (any type).\" },\n \"status\": { \"type\": \"string\", \"description\": \"Lifecycle phase: draft | active | archived (default draft).\" }\n },\n \"required\": [\n \"product_id\",\n \"framework_id\"\n ]\n }\n },\n {\n \"name\": \"score_entity\",\n \"description\": \"Record a framework's result for one entity on the exercise's includes edge (a MoSCoW bucket, a RICE score, a canvas slot). Auto-includes the entity if not already in scope. Merges into existing edge properties unless replace is set. The product is resolved from the exercise node. Returns { edge, warnings }.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"exercise_id\": { \"type\": \"string\", \"description\": \"Required. The framework_exercise id.\" },\n \"entity_id\": { \"type\": \"string\", \"description\": \"Required. The entity being scored.\" },\n \"values\": { \"type\": \"object\", \"description\": \"Required. The result as { input: value }, e.g. { \\\"moscow\\\": \\\"must\\\" } or { \\\"reach\\\": 800, \\\"impact\\\": 3 }.\" },\n \"replace\": { \"type\": \"boolean\", \"description\": \"Replace the edge properties instead of merging (default false).\" }\n },\n \"required\": [\n \"exercise_id\",\n \"entity_id\",\n \"values\"\n ]\n }\n }\n]\n\nconst HANDLERS: Record<string, ToolBinding<CloudContext>['handler']> = {\n list_products: listProducts,\n create_product: createProduct,\n get_audit_log: getAuditLog,\n get_product_context: getProductContext,\n get_graph_digest: getGraphDigest,\n query,\n get_changes: getChanges,\n list_nodes: listNodes,\n export_upg_document: exportUpgDocument,\n get_node: getNode,\n get_nodes: getNodes,\n search_nodes: searchNodes,\n create_node: createNode,\n update_node: updateNode,\n delete_node: deleteNode,\n get_product_graph: getProductGraph,\n create_edge: createEdge,\n delete_edge: deleteEdge,\n export_edges: exportEdges,\n rename_edge_type: renameEdgeType,\n // ── Framework exercises (0.8.6 cloud parity) ────────────────────\n apply_framework: applyFramework,\n score_entity: scoreEntity,\n list_product_areas: listProductAreas,\n get_area_graph: getAreaGraph,\n create_area: createArea,\n get_area_context: getAreaContext,\n move_node: moveNode,\n get_entity_schema: getEntitySchema,\n add_comment: addComment,\n list_comments: listComments,\n grant_access: grantAccess,\n list_collaborators: listCollaborators,\n get_graph_analytics: getGraphAnalytics,\n register_webhook: registerWebhook,\n list_webhooks: listWebhooks,\n remove_webhook: removeWebhook,\n // ── Spec introspection ─────────────────────────────────────────\n list_playbooks: listPlaybooks,\n get_playbook: getPlaybook,\n list_approaches: listApproaches,\n get_approach: getApproach,\n plan,\n inspect,\n prioritise,\n trace,\n reflect,\n list_domains: listDomains,\n get_domain_guide: getDomainGuide,\n list_frameworks: listFrameworks,\n get_framework: getFramework,\n list_edge_types: listEdgeTypes,\n get_edge_type: getEdgeType,\n list_regions: listRegions,\n get_region: getRegion,\n get_region_for_entity_type: getRegionForEntity,\n get_spec_version: getSpecVersion,\n resolve_edge_for_pair: resolveEdgeForPair,\n list_cross_edge_types: listCrossEdgeTypes,\n list_lenses: listLenses,\n get_lens: getLensTool,\n list_type_labels: listTypeLabels,\n get_type_label: getTypeLabel,\n get_valid_children: getValidChildrenTool,\n list_entity_types: listEntityTypes,\n get_entity_meta: getEntityMeta,\n list_anti_patterns: listAntiPatterns,\n get_anti_pattern: getAntiPattern,\n list_benchmarks: listBenchmarks,\n list_product_stages: listProductStages,\n // ── Spec catalogues (migrations, lifecycles, scales, framework metadata, domain rings) ──\n list_type_migrations: listTypeMigrations,\n list_edge_migrations: listEdgeMigrations,\n list_split_migrations: listSplitMigrations,\n list_lifecycles: listLifecycles,\n get_lifecycle: getLifecycle,\n list_scales: listScales,\n get_scale: getScale,\n list_framework_categories: listFrameworkCategories,\n list_framework_structure_patterns: listFrameworkStructurePatterns,\n list_domain_rings: listDomainRings,\n get_domain_ring: getDomainRing,\n list_portfolios: listPortfolios,\n list_portfolio_cross_edges: listPortfolioCrossEdges,\n create_cross_product_edge: createCrossProductEdge,\n repair_dangling_edges: repairDanglingEdges,\n batch_create_nodes: batchCreateNodes,\n batch_update_nodes: batchUpdateNodes,\n batch_delete_nodes: batchDeleteNodes,\n batch_create_edges: batchCreateEdges,\n batch_delete_edges: batchDeleteEdges,\n batch_move_nodes: batchMoveNodes,\n validate_graph: validateGraph,\n deduplicate_nodes: deduplicateNodes,\n migrate_type: migrateType,\n migrate_cross_edges: migrateCrossEdges,\n}\n\nexport function getToolHandler(name: string): ToolBinding<CloudContext>['handler'] | undefined {\n return HANDLERS[name]\n}\n"],"mappings":"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","names":["nodeId","query","n","getLifecycleForType","resolveEntityType","UnknownEntityTypeError","resolveEntityType","UnknownEntityTypeError","getLifecycleForType","inferEdgeTypeWithTier","inferEdgeTypeWithTier","edge","UPG_FRAMEWORKS_BY_ID","UPG_EDGE_CATALOG","clampLimit","decodeCursor","encodeCursor","UPG_FRAMEWORKS_BY_ID","domains","UPG_EDGE_CATALOG","UPG_CROSS_EDGE_TYPES","UPG_CROSS_EDGE_TYPES","getLifecycleForType","resolveEntityType","UnknownEntityTypeError","inferEdgeTypeWithTier","validateEdgeTypePair","checkPropertyTypes","checkLengthCaps","renderPropertyTypeWarning","resolveEntityType","UnknownEntityTypeError","checkPropertyTypes","renderPropertyTypeWarning","getLifecycleForType","checkLengthCaps","inferEdgeTypeWithTier","validateEdgeTypePair","UPG_EDGE_CATALOG","UPG_PROPERTY_SCHEMA","UPG_ENTITY_META_BY_NAME","UPG_EDGE_CATALOG","UPG_PROPERTY_SCHEMA","UPG_ENTITY_META_BY_NAME","UPG_TYPES_SET","UPG_CROSS_EDGE_TYPES","resolveContainmentEdge","getPool","UPG_TYPES_SET","resolveContainmentEdge","UPG_CROSS_EDGE_TYPES","require"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/id-helpers.ts","../src/lib/audit.ts","../src/store/pg-store.ts","../src/lib/webhook-dispatcher.ts","../src/server.ts","../../upg-mcp-tooling/src/result.ts","../../upg-mcp-tooling/src/catalog.ts","../src/tools/products.ts","../src/tools/context.ts","../src/tools/nodes.ts","../src/tools/edges.ts","../src/tools/frameworks.ts","../src/tools/areas.ts","../src/tools/schema.ts","../src/tools/collaboration.ts","../src/tools/analytics.ts","../src/tools/webhooks.ts","../src/tools/spec.ts","../src/tools/portfolio.ts","../src/tools/batch.ts","../src/tools/validation.ts","../src/tools/migrations.ts","../src/lib/tool-registry.ts"],"sourcesContent":["/**\n * UPG Cloud MCP Server\n *\n * Usage: upg-cloud-server\n * --database-url postgres://user:pass@host:5432/db (or UPG_DATABASE_URL env)\n *\n * A Postgres-backed MCP server for the Unified Product Graph.\n * Self-hostable, open source.\n */\n\nimport { parseArgs } from 'node:util'\nimport { Pool } from 'pg'\nimport { PgStore } from './store/pg-store.js'\nimport { WebhookDispatcher } from './lib/webhook-dispatcher.js'\nimport { createServer } from './server.js'\n\nasync function main() {\n const { values } = parseArgs({\n options: {\n 'database-url': { type: 'string', short: 'd' },\n },\n })\n\n const databaseUrl = values['database-url'] || process.env.UPG_DATABASE_URL\n if (!databaseUrl) {\n process.stderr.write(\n 'Usage: upg-cloud-server --database-url postgres://...\\n' +\n ' Or set UPG_DATABASE_URL environment variable\\n'\n )\n process.exit(1)\n }\n\n const pool = new Pool({ connectionString: databaseUrl })\n\n // Test connection\n try {\n await pool.query('SELECT 1')\n } catch (err) {\n process.stderr.write(`Database connection failed: ${(err as Error).message}\\n`)\n process.exit(1)\n }\n\n const store = new PgStore(pool)\n // Wire webhook delivery (UPG-553): mutations emit events post-commit; the\n // dispatcher fans them out to registered webhooks (fire-and-forget).\n const dispatcher = new WebhookDispatcher(pool)\n store.setEventSink((event) => dispatcher.emit(event))\n const server = createServer(store)\n await server.start()\n\n process.stderr.write('UPG Cloud MCP server running\\n')\n\n const shutdown = async () => {\n await pool.end()\n process.exit(0)\n }\n process.on('SIGINT', shutdown)\n process.on('SIGTERM', shutdown)\n}\n\nmain().catch((err) => {\n process.stderr.write(`Fatal: ${err}\\n`)\n process.exit(1)\n})\n","/**\n * Cloud-side id minters.\n *\n * The cloud schema (`migrations/001_initial.sql`) declares `id` / `source` /\n * `target` as Postgres `UUID` columns, so cloud mints native UUIDs. This is\n * deliberately NOT the file-SDK's `nodeId`/`edgeId` (which produce the\n * human-readable `n_…` / `e_…` ids used inside `.upg` files): ids are a\n * storage-format concern, not shared graph semantics. The shared logic cloud\n * derives from `@unified-product-graph/sdk/logic` is edge inference and the\n * validators, not id format.\n *\n * Before this, cloud reused the `n_…` / `e_…` generators, which Postgres\n * rejected with \"invalid input syntax for type uuid\", a bug masked by the\n * mocked-pool unit tests and only surfaced by running the server for real.\n */\nimport { randomUUID } from 'node:crypto'\n\n/** Generate a node id (Postgres UUID). */\nexport function nodeId(): string {\n return randomUUID()\n}\n\n/** Generate an edge id (Postgres UUID). */\nexport function edgeId(): string {\n return randomUUID()\n}\n","/**\n * Audit-log writer for the cloud server.\n *\n * `upg.audit_log` is the canonical history of mutations, read back by\n * `get_audit_log` and `get_changes`. Every mutation must record a row here, or\n * those tools return empty forever (the bug fixed in UPG-552).\n *\n * `appendAudit` MUST be called with a **transaction-scoped client**, in the\n * same transaction as the mutation it records, so the audit row commits or\n * rolls back atomically with the write; important for the batch tools'\n * all-or-nothing semantics.\n *\n * `userId` is `null` on the stdio path until request-level auth context is\n * plumbed (see UPG-551, Tier-3 enforcement).\n */\nimport type { PoolClient } from 'pg'\n\nexport type AuditAction = 'create' | 'update' | 'delete'\nexport type AuditEntityType = 'node' | 'edge' | 'product'\n\nexport interface AuditEntry {\n productId: string\n action: AuditAction\n entityType: AuditEntityType\n entityId: string\n /** Optional JSON payload (e.g. the created entity, the patch, or merge info). */\n changes?: unknown\n /** Actor; null on the unauthenticated stdio path. */\n userId?: string | null\n}\n\nexport async function appendAudit(client: PoolClient, entry: AuditEntry): Promise<void> {\n await client.query(\n `INSERT INTO upg.audit_log (product_id, user_id, action, entity_type, entity_id, changes)\n VALUES ($1, $2, $3, $4, $5, $6)`,\n [\n entry.productId,\n entry.userId ?? null,\n entry.action,\n entry.entityType,\n entry.entityId,\n entry.changes !== undefined && entry.changes !== null ? JSON.stringify(entry.changes) : null,\n ],\n )\n}\n","import type { Pool, PoolClient } from 'pg'\nimport type { UPGBaseNode, UPGEdge } from '@unified-product-graph/core'\nimport { nodeId, edgeId } from '../id-helpers.js'\nimport { appendAudit } from '../lib/audit.js'\nimport type { WebhookEvent } from '../lib/webhook-dispatcher.js'\n\n// ─── Local types ─────────────────────────────────────────────────────────────\n\nexport interface Product {\n id: string\n title: string\n description?: string\n stage?: string\n}\n\nexport interface GraphSummary {\n product: { id: string; title: string; stage?: string }\n node_count: number\n edge_count: number\n nodes_by_type: Record<string, number>\n edges_by_type: Record<string, number>\n orphan_count: number\n}\n\nexport interface AuditEntry {\n id: string\n user_id: string\n action: string\n entity_type: string\n entity_id: string\n changes: unknown\n created_at: string\n}\n\nexport interface Comment {\n id: string\n node_id: string\n user_id: string\n body: string\n created_at: string\n}\n\nexport interface AccessRecord {\n id: string\n user_id: string\n role: string\n created_at: string\n}\n\nexport interface GraphAnalytics {\n hypothesis_velocity: { untested: number; testing: number; validated: number; invalidated: number }\n coverage_ratio: number // 0-100\n evidence_density: number // ratio\n stale_entity_rate: number // 0-100\n orphan_rate: number // 0-100\n}\n\nexport interface CrossProductEdge {\n id: string\n source: string\n target: string\n type: string\n created_by_product_id: string\n created_at: string\n}\n\nexport interface Webhook {\n id: string\n event: string\n url: string\n active: boolean\n created_at: string\n}\n\n// ─── Postgres-backed UPG Store ───────────────────────────────────────────────\n\nexport { UPGPgStore as PgStore }\n\nexport class UPGPgStore {\n constructor(private pool: Pool) {}\n\n // ── Event sink (webhook delivery, UPG-553) ──────────────────────────────────\n // Set by the server entry point. Mutations call `emit` AFTER they commit, so\n // events only fire for durable writes. Default is a no-op, so the store works\n // standalone (and in tests) with no dispatcher wired.\n private eventSink: ((event: WebhookEvent) => void) | null = null\n\n setEventSink(sink: (event: WebhookEvent) => void): void {\n this.eventSink = sink\n }\n\n /** Emit a post-commit event to the sink, if one is set. Public so the batch\n * tools (which run their own transactions) can emit too. */\n emit(productId: string, event: string, payload: Record<string, unknown>): void {\n this.eventSink?.({ productId, event, payload })\n }\n\n // ── Products ───────────────────────────────────────────────────────────────\n\n async listProducts(): Promise<Product[]> {\n const { rows } = await this.pool.query<Product>(\n `SELECT id, title, description, stage FROM upg.products ORDER BY title`,\n )\n return rows\n }\n\n async getProduct(productId: string): Promise<Product> {\n const { rows } = await this.pool.query<Product>(\n `SELECT id, title, description, stage FROM upg.products WHERE id = $1`,\n [productId],\n )\n if (rows.length === 0) throw new Error(`Product not found: ${productId}`)\n return rows[0]\n }\n\n async createProduct(\n title: string,\n description?: string,\n stage?: string,\n ): Promise<Product> {\n const id = nodeId()\n const client = await this.pool.connect()\n try {\n await client.query('BEGIN')\n const { rows } = await client.query<Product>(\n `INSERT INTO upg.products (id, title, description, stage)\n VALUES ($1, $2, $3, $4)\n RETURNING id, title, description, stage`,\n [id, title, description ?? null, stage ?? null],\n )\n await appendAudit(client, {\n productId: id,\n action: 'create',\n entityType: 'product',\n entityId: id,\n changes: { title, description: description ?? null, stage: stage ?? null },\n })\n await client.query('COMMIT')\n this.emit(id, 'product.created', { id, title })\n return rows[0]\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n } finally {\n client.release()\n }\n }\n\n // ── Nodes ──────────────────────────────────────────────────────────────────\n\n private static NODE_COLS = 'id, product_id, type, title, description, status, tags, data'\n\n async getNode(id: string): Promise<(UPGBaseNode & { product_id: string }) | undefined> {\n const { rows } = await this.pool.query(\n `SELECT ${UPGPgStore.NODE_COLS} FROM upg.nodes WHERE id = $1`,\n [id],\n )\n if (rows.length === 0) return undefined\n return rowToNode(rows[0])\n }\n\n async getAllNodes(productId: string): Promise<UPGBaseNode[]> {\n const { rows } = await this.pool.query(\n `SELECT ${UPGPgStore.NODE_COLS} FROM upg.nodes\n WHERE product_id = $1 ORDER BY title`,\n [productId],\n )\n return rows.map(rowToNode)\n }\n\n async addNode(productId: string, node: UPGBaseNode): Promise<UPGBaseNode> {\n const id = node.id || nodeId()\n const client = await this.pool.connect()\n try {\n await client.query('BEGIN')\n const { rows } = await client.query(\n `INSERT INTO upg.nodes (id, product_id, type, title, description, status, tags, data)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8)\n RETURNING ${UPGPgStore.NODE_COLS}`,\n [\n id,\n productId,\n node.type,\n node.title,\n node.description ?? null,\n node.status ?? null,\n node.tags ?? null,\n node.properties ? JSON.stringify(node.properties) : null,\n ],\n )\n await appendAudit(client, {\n productId,\n action: 'create',\n entityType: 'node',\n entityId: id,\n changes: { type: node.type, title: node.title },\n })\n await client.query('COMMIT')\n this.emit(productId, 'node.created', { id, type: node.type, title: node.title })\n return rowToNode(rows[0])\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n } finally {\n client.release()\n }\n }\n\n async updateNode(id: string, patch: Partial<UPGBaseNode>): Promise<UPGBaseNode> {\n const setClauses: string[] = []\n const values: unknown[] = []\n let p = 1\n\n if (patch.title !== undefined) { setClauses.push(`title = $${p++}`); values.push(patch.title) }\n if (patch.description !== undefined) { setClauses.push(`description = $${p++}`); values.push(patch.description) }\n if (patch.status !== undefined) { setClauses.push(`status = $${p++}`); values.push(patch.status) }\n if (patch.tags !== undefined) { setClauses.push(`tags = $${p++}`); values.push(patch.tags) }\n if (patch.properties !== undefined) {\n setClauses.push(`data = COALESCE(data, '{}'::jsonb) || $${p++}::jsonb`)\n values.push(JSON.stringify(patch.properties))\n }\n\n if (setClauses.length === 0) {\n const existing = await this.getNode(id)\n if (!existing) throw new Error(`Node not found: ${id}`)\n return existing\n }\n\n values.push(id)\n const client = await this.pool.connect()\n try {\n await client.query('BEGIN')\n const { rows } = await client.query(\n `UPDATE upg.nodes SET ${setClauses.join(', ')}\n WHERE id = $${p}\n RETURNING ${UPGPgStore.NODE_COLS}`,\n values,\n )\n if (rows.length === 0) {\n await client.query('ROLLBACK')\n throw new Error(`Node not found: ${id}`)\n }\n await appendAudit(client, {\n productId: rows[0].product_id,\n action: 'update',\n entityType: 'node',\n entityId: id,\n changes: patch,\n })\n await client.query('COMMIT')\n this.emit(rows[0].product_id, 'node.updated', { id, ...patch } as Record<string, unknown>)\n return rowToNode(rows[0])\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n } finally {\n client.release()\n }\n }\n\n async removeNode(id: string): Promise<{ node: UPGBaseNode; removedEdgeIds: string[] }> {\n const client = await this.pool.connect()\n try {\n await client.query('BEGIN')\n\n const { rows: nodeRows } = await client.query(\n `SELECT ${UPGPgStore.NODE_COLS} FROM upg.nodes WHERE id = $1`,\n [id],\n )\n if (nodeRows.length === 0) {\n await client.query('ROLLBACK')\n throw new Error(`Node not found: ${id}`)\n }\n\n const { rows: edgeRows } = await client.query<{ id: string }>(\n `DELETE FROM upg.edges WHERE source = $1 OR target = $1 RETURNING id`,\n [id],\n )\n\n await client.query(`DELETE FROM upg.nodes WHERE id = $1`, [id])\n await appendAudit(client, {\n productId: nodeRows[0].product_id,\n action: 'delete',\n entityType: 'node',\n entityId: id,\n changes: { removed_edge_ids: edgeRows.map((r) => r.id) },\n })\n await client.query('COMMIT')\n\n this.emit(nodeRows[0].product_id, 'node.deleted', { id })\n return {\n node: rowToNode(nodeRows[0]),\n removedEdgeIds: edgeRows.map((r) => r.id),\n }\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n } finally {\n client.release()\n }\n }\n\n /**\n * Atomically reparent a node. Deletes any existing containment edge targeting\n * `nodeId`, then inserts a new edge from `newParentId` to `nodeId`.\n *\n * @returns `{ node_id, old_parent_id, new_parent_id, edge_created }`.\n * `old_parent_id` is `null` when no prior containment edge existed.\n */\n async moveNode(\n productId: string,\n nodeId: string,\n newParentId: string,\n newEdgeType: string,\n newEdgeIdParam: string,\n ): Promise<{\n node_id: string\n old_parent_id: string | null\n new_parent_id: string\n edge_created: { id: string; source: string; target: string; type: string }\n }> {\n const client = await this.pool.connect()\n try {\n await client.query('BEGIN')\n\n // Delete any containment edge where this node is the target\n const { rows: deletedEdges } = await client.query<{ id: string; source: string }>(\n `DELETE FROM upg.edges\n WHERE product_id = $1 AND target = $2\n RETURNING id, source`,\n [productId, nodeId],\n )\n const oldParentId = deletedEdges.length > 0 ? deletedEdges[0].source : null\n\n // Insert new containment edge\n await client.query(\n `INSERT INTO upg.edges (id, product_id, source, target, type)\n VALUES ($1, $2, $3, $4, $5)`,\n [newEdgeIdParam, productId, newParentId, nodeId, newEdgeType],\n )\n\n await appendAudit(client, {\n productId,\n action: 'update',\n entityType: 'node',\n entityId: nodeId,\n changes: { old_parent_id: oldParentId, new_parent_id: newParentId, edge_type: newEdgeType },\n })\n\n await client.query('COMMIT')\n\n this.emit(productId, 'node.updated', { id: nodeId, new_parent_id: newParentId })\n return {\n node_id: nodeId,\n old_parent_id: oldParentId,\n new_parent_id: newParentId,\n edge_created: { id: newEdgeIdParam, source: newParentId, target: nodeId, type: newEdgeType },\n }\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n } finally {\n client.release()\n }\n }\n\n /**\n * Merge duplicate nodes into a canonical node inside an all-or-nothing\n * Postgres transaction.\n *\n * Steps (in order, inside a single transaction):\n * 1. Rebind all edges from each duplicate to the canonical.\n * 2. Delete self-loops (source = canonical AND target = canonical).\n * 3. Remove duplicate edges (same source, target, type), keeping one.\n * 4. Shallow-merge duplicate properties into canonical (canonical wins).\n * 5. Delete the duplicate nodes.\n *\n * @returns Counts of rebound edges, removed self-loops, removed duplicate\n * edges, plus the canonical_id and list of merged ids.\n */\n async deduplicateNodes(\n productId: string,\n canonicalId: string,\n duplicateIds: string[],\n ): Promise<{\n canonical_id: string\n merged_ids: string[]\n rebound_edges: number\n removed_self_loops: number\n removed_duplicate_edges: number\n }> {\n const client = await this.pool.connect()\n try {\n await client.query('BEGIN')\n\n let reboundEdges = 0\n\n // Step 1: Rebind edges from each duplicate to canonical\n for (const dupId of duplicateIds) {\n const { rowCount: srcCount } = await client.query(\n `UPDATE upg.edges SET source = $1 WHERE source = $2 AND product_id = $3`,\n [canonicalId, dupId, productId],\n )\n const { rowCount: tgtCount } = await client.query(\n `UPDATE upg.edges SET target = $1 WHERE target = $2 AND product_id = $3`,\n [canonicalId, dupId, productId],\n )\n reboundEdges += (srcCount ?? 0) + (tgtCount ?? 0)\n }\n\n // Step 2: Delete self-loops created by rebinding\n const { rowCount: selfLoopCount } = await client.query(\n `DELETE FROM upg.edges\n WHERE product_id = $1 AND source = $2 AND target = $2`,\n [productId, canonicalId],\n )\n\n // Step 3: Remove duplicate edges (same source, target, type), keeping one\n const { rowCount: dupEdgeCount } = await client.query(\n `DELETE FROM upg.edges\n WHERE id IN (\n SELECT id FROM (\n SELECT id, ROW_NUMBER() OVER (\n PARTITION BY source, target, type ORDER BY id\n ) AS rn\n FROM upg.edges WHERE product_id = $1\n ) t WHERE rn > 1\n )`,\n [productId],\n )\n\n // Step 4: Merge duplicate properties into canonical (canonical wins)\n await client.query(\n `UPDATE upg.nodes\n SET data = (\n SELECT COALESCE(jsonb_object_agg(key, value), '{}'::jsonb)\n FROM (\n SELECT key, value\n FROM upg.nodes, jsonb_each(COALESCE(data, '{}'::jsonb))\n WHERE id = ANY($2::text[])\n UNION ALL\n SELECT key, value\n FROM upg.nodes, jsonb_each(COALESCE(data, '{}'::jsonb))\n WHERE id = $3\n ) merged\n )\n WHERE id = $3`,\n [productId, duplicateIds, canonicalId],\n )\n\n // Step 5: Delete duplicates\n await client.query(\n `DELETE FROM upg.nodes WHERE id = ANY($1::text[]) AND product_id = $2`,\n [duplicateIds, productId],\n )\n\n // Audit each merged-away duplicate as a deletion.\n for (const dupId of duplicateIds) {\n await appendAudit(client, {\n productId,\n action: 'delete',\n entityType: 'node',\n entityId: dupId,\n changes: { merged_into: canonicalId },\n })\n }\n\n await client.query('COMMIT')\n\n for (const dupId of duplicateIds) {\n this.emit(productId, 'node.deleted', { id: dupId, merged_into: canonicalId })\n }\n return {\n canonical_id: canonicalId,\n merged_ids: duplicateIds,\n rebound_edges: reboundEdges,\n removed_self_loops: selfLoopCount ?? 0,\n removed_duplicate_edges: dupEdgeCount ?? 0,\n }\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n } finally {\n client.release()\n }\n }\n\n // ── Edges ──────────────────────────────────────────────────────────────────\n\n async getAllEdges(productId: string): Promise<UPGEdge[]> {\n const { rows } = await this.pool.query(\n `SELECT id, source, target, type, properties FROM upg.edges WHERE product_id = $1`,\n [productId],\n )\n return rows.map(rowToEdge)\n }\n\n async getEdgesForNode(nodeId: string): Promise<UPGEdge[]> {\n const { rows } = await this.pool.query(\n `SELECT id, source, target, type, properties FROM upg.edges\n WHERE source = $1 OR target = $1`,\n [nodeId],\n )\n return rows.map(rowToEdge)\n }\n\n async addEdge(productId: string, edge: UPGEdge): Promise<void> {\n const id = edge.id || edgeId()\n const client = await this.pool.connect()\n try {\n await client.query('BEGIN')\n await client.query(\n `INSERT INTO upg.edges (id, product_id, source, target, type, properties)\n VALUES ($1, $2, $3, $4, $5, $6)`,\n [\n id,\n productId,\n edge.source,\n edge.target,\n edge.type,\n edge.properties ? JSON.stringify(edge.properties) : null,\n ],\n )\n await appendAudit(client, {\n productId,\n action: 'create',\n entityType: 'edge',\n entityId: id,\n changes: { source: edge.source, target: edge.target, type: edge.type },\n })\n await client.query('COMMIT')\n this.emit(productId, 'edge.created', { id, source: edge.source, target: edge.target, type: edge.type })\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n } finally {\n client.release()\n }\n }\n\n async removeEdge(id: string): Promise<UPGEdge> {\n const client = await this.pool.connect()\n try {\n await client.query('BEGIN')\n const { rows } = await client.query(\n `DELETE FROM upg.edges WHERE id = $1\n RETURNING id, product_id, source, target, type, properties`,\n [id],\n )\n if (rows.length === 0) {\n await client.query('ROLLBACK')\n throw new Error(`Edge not found: ${id}`)\n }\n await appendAudit(client, {\n productId: rows[0].product_id,\n action: 'delete',\n entityType: 'edge',\n entityId: id,\n changes: { source: rows[0].source, target: rows[0].target, type: rows[0].type },\n })\n await client.query('COMMIT')\n this.emit(rows[0].product_id, 'edge.deleted', { id, source: rows[0].source, target: rows[0].target, type: rows[0].type })\n return rowToEdge(rows[0])\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n } finally {\n client.release()\n }\n }\n\n /**\n * Set (or merge) the gated `properties` payload on a single edge (0.8.6\n * framework-exercise parity). Merge is the default: the supplied keys are\n * deep-merged at the top level via JSONB `||`; pass `{ merge: false }` to\n * replace the payload wholesale. Mirrors the local SDK's `setEdgeProperties`.\n */\n async setEdgeProperties(\n id: string,\n values: Record<string, unknown>,\n opts: { merge?: boolean } = {},\n ): Promise<UPGEdge> {\n const merge = opts.merge !== false\n const client = await this.pool.connect()\n try {\n await client.query('BEGIN')\n const sql = merge\n ? `UPDATE upg.edges\n SET properties = COALESCE(properties, '{}'::jsonb) || $1::jsonb\n WHERE id = $2\n RETURNING id, product_id, source, target, type, properties`\n : `UPDATE upg.edges\n SET properties = $1::jsonb\n WHERE id = $2\n RETURNING id, product_id, source, target, type, properties`\n const { rows } = await client.query(sql, [JSON.stringify(values), id])\n if (rows.length === 0) {\n await client.query('ROLLBACK')\n throw new Error(`Edge not found: ${id}`)\n }\n await appendAudit(client, {\n productId: rows[0].product_id,\n action: 'update',\n entityType: 'edge',\n entityId: id,\n changes: values,\n })\n await client.query('COMMIT')\n this.emit(rows[0].product_id, 'edge.updated', { id, properties: values })\n return rowToEdge(rows[0])\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n } finally {\n client.release()\n }\n }\n\n // ── Edge utilities ─────────────────────────────────────\n\n /**\n * Flat enumeration of all edges for a product, optionally filtered by type.\n * Used for migration passes. Returns lightweight { id, source, target, type }\n * rows ordered by id without full node payloads.\n */\n async exportEdges(productId: string, types?: string[]) {\n const { rows } = await this.pool.query(\n `SELECT id, source, target, type\n FROM upg.edges\n WHERE product_id = $1\n AND ($2::text[] IS NULL OR type = ANY($2))\n ORDER BY id`,\n [productId, types ?? null],\n )\n return rows\n }\n\n /**\n * Rename all edges of one type to another within a product.\n * dryRun=true (default): count only. dryRun=false: transactional UPDATE.\n */\n async renameEdgeType(productId: string, from: string, to: string, dryRun = true) {\n if (dryRun) {\n const { rows } = await this.pool.query(\n `SELECT COUNT(*)::text AS count FROM upg.edges WHERE product_id = $1 AND type = $2`,\n [productId, from],\n )\n return parseInt(rows[0].count, 10)\n }\n const client = await this.pool.connect()\n try {\n await client.query('BEGIN')\n const { rowCount } = await client.query(\n `UPDATE upg.edges SET type = $3 WHERE product_id = $1 AND type = $2`,\n [productId, from, to],\n )\n await client.query('COMMIT')\n return rowCount ?? 0\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n } finally {\n client.release()\n }\n }\n\n // ── Search ─────────────────────────────────────────────────────────────────\n\n async searchNodes(\n productId: string,\n query: string,\n type?: string,\n limit: number = 20,\n ): Promise<UPGBaseNode[]> {\n const conditions = [\n `product_id = $1`,\n `to_tsvector('english', coalesce(title,'') || ' ' || coalesce(description,'')) @@ plainto_tsquery('english', $2)`,\n ]\n const values: unknown[] = [productId, query]\n let p = 3\n\n if (type) {\n conditions.push(`type = $${p++}`)\n values.push(type)\n }\n\n values.push(limit)\n\n const { rows } = await this.pool.query(\n `SELECT ${UPGPgStore.NODE_COLS} FROM upg.nodes\n WHERE ${conditions.join(' AND ')}\n ORDER BY ts_rank(\n to_tsvector('english', coalesce(title,'') || ' ' || coalesce(description,'')),\n plainto_tsquery('english', $2)\n ) DESC\n LIMIT $${p}`,\n values,\n )\n return rows.map(rowToNode)\n }\n\n // ── Aggregation ────────────────────────────────────────────────────────────\n\n async getGraphSummary(productId: string): Promise<GraphSummary> {\n const product = await this.getProduct(productId)\n\n const { rows: ntRows } = await this.pool.query<{ type: string; count: string }>(\n `SELECT type, COUNT(*)::text AS count FROM upg.nodes WHERE product_id = $1 GROUP BY type`,\n [productId],\n )\n const nodes_by_type: Record<string, number> = {}\n let node_count = 0\n for (const r of ntRows) { const c = parseInt(r.count, 10); nodes_by_type[r.type] = c; node_count += c }\n\n const { rows: etRows } = await this.pool.query<{ type: string; count: string }>(\n `SELECT type, COUNT(*)::text AS count FROM upg.edges WHERE product_id = $1 GROUP BY type`,\n [productId],\n )\n const edges_by_type: Record<string, number> = {}\n let edge_count = 0\n for (const r of etRows) { const c = parseInt(r.count, 10); edges_by_type[r.type] = c; edge_count += c }\n\n const { rows: oRows } = await this.pool.query<{ count: string }>(\n `SELECT COUNT(*)::text AS count FROM upg.nodes n\n WHERE n.product_id = $1\n AND NOT EXISTS (\n SELECT 1 FROM upg.edges e\n WHERE e.product_id = $1 AND (e.source = n.id OR e.target = n.id)\n )`,\n [productId],\n )\n const orphan_count = parseInt(oRows[0].count, 10)\n\n return { product: { id: product.id, title: product.title, stage: product.stage }, node_count, edge_count, nodes_by_type, edges_by_type, orphan_count }\n }\n\n // ── Export ─────────────────────────────────────────────────────────────────\n\n async getProductGraph(productId: string): Promise<{ product: Product; nodes: UPGBaseNode[]; edges: UPGEdge[] }> {\n const [product, nodes, edges] = await Promise.all([\n this.getProduct(productId),\n this.getAllNodes(productId),\n this.getAllEdges(productId),\n ])\n return { product, nodes, edges }\n }\n\n // ── Collaboration: Audit Log ────────────────────────────────────────────────\n\n async getAuditLog(productId: string, limit = 50): Promise<AuditEntry[]> {\n const { rows } = await this.pool.query<AuditEntry>(\n `SELECT id, user_id, action, entity_type, entity_id, changes, created_at\n FROM upg.audit_log\n WHERE product_id = $1\n ORDER BY created_at DESC\n LIMIT $2`,\n [productId, limit],\n )\n return rows\n }\n\n // ── Collaboration: Comments ─────────────────────────────────────────────────\n\n async addComment(productId: string, nodeId: string, userId: string, body: string): Promise<Comment> {\n const { rows } = await this.pool.query<Comment>(\n `INSERT INTO upg.comments (product_id, node_id, user_id, body)\n VALUES ($1, $2, $3, $4)\n RETURNING id, node_id, user_id, body, created_at`,\n [productId, nodeId, userId, body],\n )\n return rows[0]\n }\n\n async listComments(nodeId: string): Promise<Comment[]> {\n const { rows } = await this.pool.query<Comment>(\n `SELECT id, node_id, user_id, body, created_at\n FROM upg.comments\n WHERE node_id = $1\n ORDER BY created_at DESC`,\n [nodeId],\n )\n return rows\n }\n\n // ── Collaboration: Access Control ───────────────────────────────────────────\n\n async grantAccess(productId: string, userId: string, role: string): Promise<void> {\n await this.pool.query(\n `INSERT INTO upg.access (product_id, user_id, role)\n VALUES ($1, $2, $3)\n ON CONFLICT (product_id, user_id) DO UPDATE SET role = EXCLUDED.role`,\n [productId, userId, role],\n )\n }\n\n async listCollaborators(productId: string): Promise<AccessRecord[]> {\n const { rows } = await this.pool.query<AccessRecord>(\n `SELECT id, user_id, role, created_at\n FROM upg.access\n WHERE product_id = $1\n ORDER BY created_at`,\n [productId],\n )\n return rows\n }\n\n // ── Analytics ─────────────────────────────────────────────────────────────────\n\n async getGraphAnalytics(productId: string): Promise<GraphAnalytics> {\n // 1. Hypothesis velocity: count by status\n const { rows: hvRows } = await this.pool.query<{ status: string; count: string }>(\n `SELECT COALESCE(status, 'untested') AS status, COUNT(*)::text AS count\n FROM upg.nodes\n WHERE product_id = $1 AND type = 'hypothesis'\n GROUP BY COALESCE(status, 'untested')`,\n [productId],\n )\n const hv = { untested: 0, testing: 0, validated: 0, invalidated: 0 }\n for (const r of hvRows) {\n const s = r.status as keyof typeof hv\n if (s in hv) hv[s] = parseInt(r.count, 10)\n }\n\n // 2. Coverage ratio: % of personas with complete chains\n // (persona → jtbd → pain_point → opportunity → solution)\n const { rows: coverageRows } = await this.pool.query<{ total: string; covered: string }>(\n `WITH personas AS (\n SELECT id FROM upg.nodes WHERE product_id = $1 AND type = 'persona'\n ),\n covered AS (\n SELECT DISTINCT p.id\n FROM personas p\n JOIN upg.edges e1 ON e1.source = p.id\n JOIN upg.nodes jtbd ON jtbd.id = e1.target AND jtbd.type = 'jtbd'\n JOIN upg.edges e2 ON e2.source = jtbd.id\n JOIN upg.nodes pp ON pp.id = e2.target AND pp.type = 'pain_point'\n JOIN upg.edges e3 ON e3.source = pp.id\n JOIN upg.nodes opp ON opp.id = e3.target AND opp.type = 'opportunity'\n JOIN upg.edges e4 ON e4.source = opp.id\n JOIN upg.nodes sol ON sol.id = e4.target AND sol.type = 'solution'\n )\n SELECT\n (SELECT COUNT(*)::text FROM personas) AS total,\n (SELECT COUNT(*)::text FROM covered) AS covered`,\n [productId],\n )\n const totalPersonas = parseInt(coverageRows[0].total, 10)\n const coveredPersonas = parseInt(coverageRows[0].covered, 10)\n const coverage_ratio = totalPersonas > 0\n ? Math.round((coveredPersonas / totalPersonas) * 100)\n : 0\n\n // 3. Evidence density: (learnings + research_insights) / hypotheses\n const { rows: edRows } = await this.pool.query<{ evidence: string; hypotheses: string }>(\n `SELECT\n (SELECT COUNT(*)::text FROM upg.nodes WHERE product_id = $1 AND type IN ('learning', 'research_insight')) AS evidence,\n (SELECT COUNT(*)::text FROM upg.nodes WHERE product_id = $1 AND type = 'hypothesis') AS hypotheses`,\n [productId],\n )\n const evidenceCount = parseInt(edRows[0].evidence, 10)\n const hypothesisCount = parseInt(edRows[0].hypotheses, 10)\n const evidence_density = hypothesisCount > 0\n ? Math.round((evidenceCount / hypothesisCount) * 100) / 100\n : 0\n\n // 4. Stale entity rate: % of nodes whose last update is 14+ days old\n const { rows: staleRows } = await this.pool.query<{ total: string; stale: string }>(\n `SELECT\n COUNT(*)::text AS total,\n COUNT(*) FILTER (WHERE updated_at < now() - INTERVAL '14 days')::text AS stale\n FROM upg.nodes\n WHERE product_id = $1`,\n [productId],\n )\n const totalNodes = parseInt(staleRows[0].total, 10)\n const staleNodes = parseInt(staleRows[0].stale, 10)\n const stale_entity_rate = totalNodes > 0\n ? Math.round((staleNodes / totalNodes) * 100)\n : 0\n\n // 5. Orphan rate: % of nodes with no edges\n const { rows: orphanRows } = await this.pool.query<{ total: string; orphans: string }>(\n `SELECT\n COUNT(*)::text AS total,\n COUNT(*) FILTER (\n WHERE NOT EXISTS (\n SELECT 1 FROM upg.edges e\n WHERE e.product_id = $1 AND (e.source = n.id OR e.target = n.id)\n )\n )::text AS orphans\n FROM upg.nodes n\n WHERE n.product_id = $1`,\n [productId],\n )\n const totalForOrphan = parseInt(orphanRows[0].total, 10)\n const orphanCount = parseInt(orphanRows[0].orphans, 10)\n const orphan_rate = totalForOrphan > 0\n ? Math.round((orphanCount / totalForOrphan) * 100)\n : 0\n\n return { hypothesis_velocity: hv, coverage_ratio, evidence_density, stale_entity_rate, orphan_rate }\n }\n\n // ── Product Area Scoping ─────────────────────────────────────────────────────\n\n async listProductAreas(productId: string): Promise<Array<{ id: string; title: string; child_count: number }>> {\n const { rows } = await this.pool.query<{ id: string; title: string; child_count: string }>(\n `SELECT n.id, n.title,\n (SELECT COUNT(*)::text FROM upg.edges e WHERE e.source = n.id AND e.product_id = $1) AS child_count\n FROM upg.nodes n\n WHERE n.product_id = $1 AND n.type = 'product_area'\n ORDER BY n.title`,\n [productId],\n )\n return rows.map((r) => ({ id: r.id, title: r.title, child_count: parseInt(r.child_count, 10) }))\n }\n\n async getAreaGraph(\n productId: string,\n areaId: string,\n depth: number,\n ): Promise<{\n area: { id: string; title: string; type: string }\n nodes: UPGBaseNode[]\n edges: UPGEdge[]\n node_count: number\n edge_count: number\n }> {\n // Verify the area node exists and is a product_area\n const areaNode = await this.getNode(areaId)\n if (!areaNode) throw new Error(`Area node not found: ${areaId}`)\n if (areaNode.type !== 'product_area')\n throw new Error(`Node ${areaId} is type \"${areaNode.type}\", not \"product_area\"`)\n if (areaNode.product_id !== productId)\n throw new Error(`Area node ${areaId} does not belong to product ${productId}`)\n\n // Recursive CTE to walk the graph from the area node\n const { rows: nodeIds } = await this.pool.query<{ id: string }>(\n `WITH RECURSIVE area_walk AS (\n SELECT id, 0 AS depth FROM upg.nodes WHERE id = $1 AND product_id = $2\n UNION\n SELECT CASE WHEN e.source = aw.id THEN e.target ELSE e.source END, aw.depth + 1\n FROM area_walk aw\n JOIN upg.edges e ON (e.source = aw.id OR e.target = aw.id) AND e.product_id = $2\n WHERE aw.depth < $3\n )\n SELECT DISTINCT id FROM area_walk`,\n [areaId, productId, depth],\n )\n\n if (nodeIds.length === 0) {\n return {\n area: { id: areaNode.id, title: areaNode.title, type: areaNode.type },\n nodes: [],\n edges: [],\n node_count: 0,\n edge_count: 0,\n }\n }\n\n const ids = nodeIds.map((r) => r.id)\n\n // Fetch all nodes by IDs\n const { rows: nodeRows } = await this.pool.query(\n `SELECT ${UPGPgStore.NODE_COLS} FROM upg.nodes\n WHERE id = ANY($1) AND product_id = $2`,\n [ids, productId],\n )\n const nodes = nodeRows.map(rowToNode)\n\n // Fetch all edges between these nodes\n const { rows: edgeRows } = await this.pool.query(\n `SELECT id, source, target, type FROM upg.edges\n WHERE product_id = $1 AND source = ANY($2) AND target = ANY($2)`,\n [productId, ids],\n )\n const edges = edgeRows.map(rowToEdge)\n\n return {\n area: { id: areaNode.id, title: areaNode.title, type: areaNode.type },\n nodes,\n edges,\n node_count: nodes.length,\n edge_count: edges.length,\n }\n }\n\n /**\n * Count descendant nodes by type, starting from any node (not restricted to\n * `product_area`). Used by `get_area_context` to summarise area contents.\n *\n * @returns Array of `{ type, count }` rows, excluding the root node itself.\n */\n async getDescendantTypeCounts(\n productId: string,\n rootNodeId: string,\n depth: number,\n ): Promise<Array<{ type: string; count: number }>> {\n const { rows } = await this.pool.query<{ type: string; count: string }>(\n `WITH RECURSIVE sub AS (\n SELECT id, 0 AS depth FROM upg.nodes WHERE id = $1 AND product_id = $2\n UNION\n SELECT CASE WHEN e.source = s.id THEN e.target ELSE e.source END, s.depth + 1\n FROM sub s\n JOIN upg.edges e ON (e.source = s.id OR e.target = s.id) AND e.product_id = $2\n WHERE s.depth < $3\n )\n SELECT n.type, COUNT(*)::text AS count\n FROM sub\n JOIN upg.nodes n ON n.id = sub.id AND n.id != $1\n GROUP BY n.type`,\n [rootNodeId, productId, depth],\n )\n return rows.map((r) => ({ type: r.type, count: parseInt(r.count, 10) }))\n }\n\n // ── Webhooks ──────────────────────────────────────────────────────────────────\n\n async registerWebhook(productId: string, event: string, url: string, secret?: string): Promise<Webhook> {\n const { rows } = await this.pool.query<Webhook>(\n `INSERT INTO upg.webhooks (product_id, event, url, secret)\n VALUES ($1, $2, $3, $4)\n RETURNING id, event, url, active, created_at`,\n [productId, event, url, secret ?? null],\n )\n return rows[0]\n }\n\n async listWebhooks(productId: string): Promise<Webhook[]> {\n const { rows } = await this.pool.query<Webhook>(\n `SELECT id, event, url, active, created_at\n FROM upg.webhooks\n WHERE product_id = $1\n ORDER BY created_at`,\n [productId],\n )\n return rows\n }\n\n async removeWebhook(id: string): Promise<void> {\n const { rows } = await this.pool.query(\n `DELETE FROM upg.webhooks WHERE id = $1 RETURNING id`,\n [id],\n )\n if (rows.length === 0) throw new Error(`Webhook not found: ${id}`)\n }\n\n // ── Cross-product edges ────────────────────────────────────────────────────\n\n async listCrossProductEdges(productId: string): Promise<CrossProductEdge[]> {\n const { rows } = await this.pool.query<CrossProductEdge>(\n `SELECT id, source, target, type, created_by_product_id, created_at\n FROM upg.cross_product_edges\n WHERE created_by_product_id = $1\n ORDER BY created_at DESC`,\n [productId],\n )\n return rows\n }\n\n async addCrossProductEdge(\n id: string,\n productId: string,\n source: string,\n target: string,\n type: string,\n ): Promise<CrossProductEdge> {\n const { rows } = await this.pool.query<CrossProductEdge>(\n `INSERT INTO upg.cross_product_edges (id, source, target, type, created_by_product_id)\n VALUES ($1, $2, $3, $4, $5)\n RETURNING id, source, target, type, created_by_product_id, created_at`,\n [id, source, target, type, productId],\n )\n return rows[0]\n }\n\n async deleteCrossProductEdge(id: string): Promise<CrossProductEdge> {\n const { rows } = await this.pool.query<CrossProductEdge>(\n `DELETE FROM upg.cross_product_edges WHERE id = $1\n RETURNING id, source, target, type, created_by_product_id, created_at`,\n [id],\n )\n if (rows.length === 0) throw new Error(`Cross-product edge not found: ${id}`)\n return rows[0]\n }\n\n async productExists(productId: string): Promise<boolean> {\n const { rows } = await this.pool.query<{ exists: boolean }>(\n `SELECT EXISTS(SELECT 1 FROM upg.products WHERE id = $1) AS exists`,\n [productId],\n )\n return rows[0].exists\n }\n}\n\n// ─── Row mappers ─────────────────────────────────────────────────────────────\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction rowToNode(row: any): UPGBaseNode & { product_id: string } {\n const node: UPGBaseNode & { product_id: string } = {\n id: row.id,\n type: row.type,\n title: row.title,\n product_id: row.product_id,\n }\n if (row.description != null) node.description = row.description\n if (row.status != null) node.status = row.status\n if (row.tags != null) node.tags = row.tags\n if (row.data != null) node.properties = row.data\n return node\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction rowToEdge(row: any): UPGEdge {\n const edge: UPGEdge = { id: row.id, source: row.source, target: row.target, type: row.type }\n // Gated edge payload (0.8.6). JSONB comes back parsed; null/absent → no key.\n if (row.properties != null) edge.properties = row.properties\n return edge\n}\n","/**\n * Webhook delivery (UPG-553).\n *\n * Mutations emit `WebhookEvent`s (via `PgStore`'s event sink); this dispatcher\n * looks up the active webhooks registered for that product + event and POSTs\n * the payload to each, async, HMAC-signed, with bounded retry/backoff and\n * auto-disable on a permanent 4xx.\n *\n * Delivery is fire-and-forget from the mutation's perspective (`emit`): it runs\n * AFTER the mutation commits and never throws back into the write path. The\n * outbound `fetch` is injectable so tests exercise delivery without real HTTP.\n */\nimport type { Pool } from 'pg'\nimport { createHmac } from 'node:crypto'\n\nexport interface WebhookEvent {\n productId: string\n /** e.g. `node.created`, `edge.deleted`. */\n event: string\n payload: Record<string, unknown>\n}\n\nexport interface DeliveryResponse { status: number }\nexport type FetchLike = (\n url: string,\n init: { method: string; headers: Record<string, string>; body: string },\n) => Promise<DeliveryResponse>\n\nexport interface DispatcherOptions {\n /** Injectable outbound HTTP. Defaults to global fetch. */\n fetchFn?: FetchLike\n /** Total attempts per delivery (default 3). */\n maxAttempts?: number\n /** Base backoff; attempt n waits backoffMs * 2^(n-1) (default 250ms). */\n backoffMs?: number\n}\n\ninterface WebhookRow { id: string; event: string; url: string; secret: string | null }\n\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms))\n\nexport class WebhookDispatcher {\n private readonly fetchFn: FetchLike\n private readonly maxAttempts: number\n private readonly backoffMs: number\n\n constructor(private readonly pool: Pool, opts: DispatcherOptions = {}) {\n this.fetchFn =\n opts.fetchFn ??\n ((url, init) => fetch(url, init).then((r) => ({ status: r.status })))\n this.maxAttempts = opts.maxAttempts ?? 3\n this.backoffMs = opts.backoffMs ?? 250\n }\n\n /**\n * Fire-and-forget entry point used by the store's event sink. Never throws\n * into the caller; delivery errors are logged to stderr.\n */\n emit(event: WebhookEvent): void {\n void this.dispatch(event).catch((err) =>\n process.stderr.write(`[webhook] dispatch failed for ${event.event}: ${String(err)}\\n`),\n )\n }\n\n /** Deliver `event` to every active webhook registered for its product+event. */\n async dispatch(event: WebhookEvent): Promise<void> {\n const { rows } = await this.pool.query<WebhookRow>(\n `SELECT id, event, url, secret FROM upg.webhooks\n WHERE product_id = $1 AND active = true AND (event = $2 OR event = '*')`,\n [event.productId, event.event],\n )\n await Promise.all(rows.map((hook) => this.deliver(hook, event)))\n }\n\n private async deliver(hook: WebhookRow, event: WebhookEvent): Promise<void> {\n const body = JSON.stringify({\n webhook_id: hook.id,\n event: event.event,\n product_id: event.productId,\n data: event.payload,\n delivered_at: new Date().toISOString(),\n })\n const headers: Record<string, string> = {\n 'content-type': 'application/json',\n 'x-upg-event': event.event,\n }\n if (hook.secret) {\n headers['x-upg-signature'] =\n 'sha256=' + createHmac('sha256', hook.secret).update(body).digest('hex')\n }\n\n for (let attempt = 1; attempt <= this.maxAttempts; attempt++) {\n let status: number\n try {\n ;({ status } = await this.fetchFn(hook.url, { method: 'POST', headers, body }))\n } catch {\n status = 0 // network/transport error → treat as transient\n }\n if (status >= 200 && status < 300) return // delivered\n // Permanent client error (except 429 rate-limit) → disable the registration.\n if (status >= 400 && status < 500 && status !== 429) {\n await this.disable(hook.id)\n return\n }\n // Transient (5xx / 429 / network): back off and retry.\n if (attempt < this.maxAttempts) await sleep(this.backoffMs * 2 ** (attempt - 1))\n }\n // Retries exhausted on a transient error; leave active for the next event.\n }\n\n private async disable(id: string): Promise<void> {\n await this.pool.query(`UPDATE upg.webhooks SET active = false WHERE id = $1`, [id])\n }\n}\n","/**\n * UPG cloud MCP server over stdio. Builds the runtime context and\n * dispatches every `tools/call` through `src/lib/tool-registry.ts`.\n */\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n} from '@modelcontextprotocol/sdk/types.js'\nimport type { PgStore } from './store/pg-store.js'\nimport type { CloudContext } from './lib/server-context.js'\nimport { textError } from './lib/server-context.js'\nimport { TOOL_DEFINITIONS, getToolHandler } from './lib/tool-registry.js'\nimport { createRequire } from 'node:module'\n\nconst require = createRequire(import.meta.url)\nconst pkg = require('../package.json') as { version: string }\n\nexport function createServer(store: PgStore) {\n const server = new Server(\n { name: 'unified-product-graph-cloud', version: pkg.version },\n { capabilities: { tools: {} } },\n )\n\n const ctx: CloudContext = { store }\n\n // ── tools/list ────────────────────────────────────────────────────────────\n\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n return { tools: TOOL_DEFINITIONS }\n })\n\n // ── tools/call ────────────────────────────────────────────────────────────\n\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args = {} } = request.params\n\n const handler = getToolHandler(name)\n const result = handler\n ? await handler(args as Record<string, unknown>, ctx)\n : textError(`Unknown tool: ${name}`)\n // ToolResult is structurally identical to the SDK's CallToolResult variant\n // of ServerResult, but the SDK's union has an index signature this narrower\n // type doesn't satisfy. Cast at the boundary so handlers can stay typed.\n return result as { content: typeof result.content; isError?: true }\n })\n\n // ── Transport ─────────────────────────────────────────────────────────────\n\n return {\n async start() {\n const transport = new StdioServerTransport()\n await server.connect(transport)\n },\n }\n}\n","/**\n * MCP wire-shape primitives. One response shape across stdio, HTTP, and\n * embedded servers.\n */\n\nexport interface ToolTextContent {\n type: 'text'\n text: string\n}\n\nexport interface ToolResult {\n content: ToolTextContent[]\n isError?: true\n}\n\nexport function text(s: string): ToolResult {\n return { content: [{ type: 'text', text: s }] }\n}\n\nexport function textError(s: string): ToolResult {\n return { content: [{ type: 'text', text: s }], isError: true }\n}\n","/**\n * Catalog helpers: alias resolution and an entity-schema builder.\n *\n * One code path answers `get_entity_schema('jtbd')` the same way on\n * local, cloud, and embedded servers (canonical `job` + `alias_of`\n * trail).\n *\n * Entity-type alias resolution (`resolveEntityType`, `UnknownEntityTypeError`,\n * `EntityTypeResolution`) now lives in `@unified-product-graph/core` so a\n * SINGLE `UnknownEntityTypeError` class is shared across the SDK and every\n * server (instance-safe `instanceof` across package boundaries). They are\n * re-exported here so existing mcp-tooling consumers keep their import path.\n *\n * This is the only module in `mcp-tooling` that depends on\n * `@unified-product-graph/core`. Importers that skip the catalog keep\n * using `./result.js`, `./tool-definition.js`, `./transport.js` core-free.\n */\n\nimport {\n UPG_EDGE_CATALOG,\n UPG_PROPERTY_SCHEMA,\n getDomainForType,\n getGuideForDomain,\n getLifecycleForType,\n getPropertySchema,\n resolveEntityType,\n UnknownEntityTypeError,\n type EntityTypeResolution,\n type UPGEntityType,\n} from '@unified-product-graph/core'\n\n// Re-export the shared entity-type resolution surface (defined in core) so\n// existing mcp-tooling consumers keep importing it from `mcp-tooling`.\nexport { resolveEntityType, UnknownEntityTypeError }\nexport type { EntityTypeResolution }\n\n/* ---------------------------------------------------------------------------\n * Entity-schema builder: `get_entity_schema` shared response shape\n * ------------------------------------------------------------------------- */\n\nexport interface EntitySchemaEdgeOut {\n edge_type: string\n target_type: string\n forward_verb: string\n}\n\nexport interface EntitySchemaEdgeIn {\n edge_type: string\n source_type: string\n reverse_verb: string\n}\n\nexport interface EntitySchemaDomainGuideAntiPattern {\n name?: string\n description: string\n affected_entity?: string\n remediation?: string\n}\n\nexport interface EntitySchemaDomainGuide {\n anchor_entity: string\n creation_sequence: readonly string[]\n position_in_sequence: number\n anti_patterns: EntitySchemaDomainGuideAntiPattern[]\n}\n\nexport interface EntitySchema {\n type: string\n alias_of?: { from: string; to: string }\n domain: { id: string; label: string } | null\n expected_properties: Record<string, unknown>\n edges_out: EntitySchemaEdgeOut[]\n edges_in: EntitySchemaEdgeIn[]\n phases?: string[]\n initial_phase?: string\n terminal_phases?: string[]\n domain_guide?: EntitySchemaDomainGuide\n}\n\nexport interface BuildEntitySchemaOptions {\n /**\n * Include the domain guide slice (anchor entity, creation sequence,\n * relevant anti-patterns). Default `true`. Servers that need to keep\n * a smaller response shape (e.g. legacy embedders) can pass `false`.\n */\n include_domain_guide?: boolean\n}\n\n/**\n * Resolve an entity-type input and build the canonical\n * `get_entity_schema` response shape.\n *\n * Walks `UPG_EDGE_CATALOG` for `edges_out` and `edges_in` (the same\n * source the LSP uses for completions and diagnostics), surfaces the\n * domain and the domain guide when one exists, and folds in lifecycle\n * phases when the type is registered.\n *\n * Throws `UnknownEntityTypeError` for unknown or mistyped inputs.\n * Servers catch and translate to a `textError` envelope.\n */\nexport function buildEntitySchema(\n rawType: unknown,\n options: BuildEntitySchemaOptions = {},\n): EntitySchema {\n const includeDomainGuide = options.include_domain_guide ?? true\n\n const resolved = resolveEntityType(rawType)\n const entityType = resolved.canonical\n\n const domain = getDomainForType(entityType)\n\n const edgesOut: EntitySchemaEdgeOut[] = []\n const edgesIn: EntitySchemaEdgeIn[] = []\n for (const [edgeKey, def] of Object.entries(UPG_EDGE_CATALOG)) {\n if (def.source_type === entityType) {\n edgesOut.push({\n edge_type: edgeKey,\n target_type: def.target_type,\n forward_verb: def.forward_verb,\n })\n }\n if (def.target_type === entityType) {\n edgesIn.push({\n edge_type: edgeKey,\n source_type: def.source_type,\n reverse_verb: def.reverse_verb,\n })\n }\n }\n\n const propertySchema = getPropertySchema(entityType)\n\n const schema: EntitySchema = {\n type: entityType,\n ...(resolved.alias ? { alias_of: resolved.alias } : {}),\n domain: domain ? { id: domain.id, label: domain.label } : null,\n expected_properties: propertySchema ?? {},\n edges_out: edgesOut,\n edges_in: edgesIn,\n }\n\n const lifecycle = getLifecycleForType(entityType)\n if (lifecycle) {\n schema.phases = lifecycle.phases.map((p) => p.id)\n schema.initial_phase = lifecycle.initial_phase\n schema.terminal_phases = [...lifecycle.terminal_phases]\n }\n\n if (includeDomainGuide && domain) {\n const guide = getGuideForDomain(domain.id)\n if (guide) {\n const normalise = (ap: unknown): EntitySchemaDomainGuideAntiPattern => {\n if (typeof ap === 'string') return { description: ap }\n const o = (ap as Record<string, unknown>) ?? {}\n const desc = typeof o.description === 'string' ? o.description : ''\n const out: EntitySchemaDomainGuideAntiPattern = { description: desc }\n if (typeof o.name === 'string') out.name = o.name\n if (typeof o.affected_entity === 'string') out.affected_entity = o.affected_entity\n if (typeof o.remediation === 'string') out.remediation = o.remediation\n return out\n }\n const allAntiPatterns = guide.anti_patterns.map(normalise)\n\n const needle = entityType.replace(/_/g, ' ').toLowerCase()\n const mentions = allAntiPatterns.filter(\n (ap) => ap.affected_entity === entityType || ap.description.toLowerCase().includes(needle),\n )\n const relevant = mentions.length > 0 ? mentions : allAntiPatterns.slice(0, 3)\n\n schema.domain_guide = {\n anchor_entity: guide.anchor_entity,\n creation_sequence: guide.creation_sequence,\n position_in_sequence: guide.creation_sequence.indexOf(entityType as UPGEntityType),\n anti_patterns: relevant,\n }\n }\n }\n\n return schema\n}\n\n/* ---------------------------------------------------------------------------\n * Entity-fields builder: `get_entity_fields` shared response shape\n * ------------------------------------------------------------------------- */\n\n/**\n * Format a single property entry from `UPG_PROPERTY_SCHEMA` into the\n * legacy `<type-hint> — <description>` string the cloud\n * `get_entity_fields` wire shape uses. Enum values render as quoted\n * alternatives (`'a' | 'b' | 'c'`), arrays as `<elem>[]`, everything\n * else as the bare JSON-schema `type`.\n *\n * Centralised in `@unified-product-graph/mcp-tooling` so every server\n * encodes the format once. The local server omits `get_entity_fields`\n * (its `get_entity_schema` returns the richer `expected_properties`\n * map). This helper is the dedicated bridge that keeps the cloud and\n * embedded responses in lockstep with `UPG_PROPERTY_SCHEMA`.\n */\nfunction formatPropertyHint(prop: unknown): string {\n if (!prop || typeof prop !== 'object') return 'any'\n const p = prop as Record<string, unknown>\n const desc = typeof p.description === 'string' ? p.description : ''\n\n let hint: string\n if (Array.isArray(p.enum) && p.enum.length > 0) {\n hint = (p.enum as unknown[]).map((v) => (typeof v === 'string' ? `'${v}'` : String(v))).join(' | ')\n } else if (p.type === 'array') {\n const items = (p.items as Record<string, unknown> | undefined) ?? {}\n const elem = typeof items.type === 'string' ? items.type : 'any'\n hint = `${elem}[]`\n } else if (typeof p.type === 'string') {\n hint = p.type\n } else {\n hint = 'any'\n }\n\n return desc ? `${hint} — ${desc}` : hint\n}\n\n/**\n * Build the `Record<string, string>` field map for a single entity type\n * by walking `UPG_PROPERTY_SCHEMA[type]`. Returns `undefined` when the\n * type has no canonical property schema (for example, v0.1 names that\n * map to a v0.2 canonical). Callers resolve aliases via\n * `resolveEntityType` first.\n */\nexport function buildEntityFields(type: string): Record<string, string> | undefined {\n // Defensive: some bundlers (notably Turbopack) have been observed to\n // drop named imports from workspace-linked `\"type\": \"module\"` packages\n // during ESM interop. If `UPG_PROPERTY_SCHEMA` didn't resolve, degrade\n // to \"schema unavailable\" rather than crashing the caller's module\n // evaluation.\n if (!UPG_PROPERTY_SCHEMA) return undefined\n const schema = UPG_PROPERTY_SCHEMA[type as UPGEntityType] as\n | Record<string, unknown>\n | undefined\n if (!schema) return undefined\n const out: Record<string, string> = {}\n for (const [name, prop] of Object.entries(schema)) {\n out[name] = formatPropertyHint(prop)\n }\n return out\n}\n\n/**\n * Build the full `{ [type]: { [field]: hint } }` map across every entity\n * type that has a canonical property schema. Used by the cloud route to\n * derive its `ENTITY_FIELDS` export from `@unified-product-graph/core`\n * at module load. One source, zero hand-maintained drift.\n */\nexport function buildAllEntityFields(): Record<string, Record<string, string>> {\n const out: Record<string, Record<string, string>> = {}\n // Defensive: see `buildEntityFields` above. If the named import dropped,\n // return an empty map. Consumers already handle missing types.\n if (!UPG_PROPERTY_SCHEMA) return out\n for (const type of Object.keys(UPG_PROPERTY_SCHEMA)) {\n const fields = buildEntityFields(type)\n if (fields) out[type] = fields\n }\n return out\n}\n","/**\n * Multi-tenant primitives: products and audit log. Cloud-only; three handlers\n * (`list_products`, `create_product`, `get_audit_log`) manage the `product_id`\n * scope every other tool takes.\n */\n\nimport { type ToolHandler, text, textError } from '../lib/server-context.js'\n\n/**\n * List every product visible to the caller. Discovery surface for valid\n * `product_id` values before scoped queries.\n *\n * @returns JSON: `{ products: Array<{ id, title, description?, stage? }> }`.\n * @atomicity atomic (read-only)\n * @warning RLS-bounded; an empty list can mean \"no products\" or \"no access\".\n * Pair with `list_collaborators` to confirm scope on a specific product.\n * @see create_product\n * @see get_product_context\n * @see get_graph_digest\n * @see list_collaborators\n */\nexport const listProducts: ToolHandler = async (_args, { store }) => {\n const products = await store.listProducts()\n return text(JSON.stringify({ products }, null, 2))\n}\n\n/**\n * Create a new product graph. Caller is auto-granted `owner` on the new product.\n *\n * @returns JSON: `{ product: { id, title, description?, stage? } }`.\n * @throws textError when `title` is missing.\n * @atomicity atomic\n * @warning Billing-relevant: product count typically drives plan tier;\n * creation may trigger a tier upgrade or hit the plan's product cap.\n * @see list_products\n * @see grant_access\n * @see list_product_stages\n */\nexport const createProduct: ToolHandler = async (args, { store }) => {\n if (!args.title) return textError(`Missing required parameter: title`)\n const product = await store.createProduct(\n args.title as string,\n args.description as string | undefined,\n args.stage as string | undefined,\n )\n return text(JSON.stringify({ product }, null, 2))\n}\n\n/**\n * Read the product's audit log: canonical history of mutations,\n * most-recent first. Default `limit: 50`.\n *\n * @returns JSON: `{ entries: Array<{ ...mutation }> }`.\n * @throws textError when `product_id` is missing.\n * @atomicity atomic (read-only)\n * @warning Retention-windowed: entries beyond the plan-tier retention period\n * are pruned. An empty window may mean \"out of retention\", not \"no activity\".\n * @see get_graph_analytics\n * @see get_graph_digest\n * @see list_products\n */\nexport const getAuditLog: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError(`Missing required parameter: product_id`)\n const productId = args.product_id as string\n const limit = (args.limit as number) ?? 50\n const entries = await store.getAuditLog(productId, limit)\n return text(JSON.stringify({ entries }, null, 2))\n}\n","/**\n * Context, digest, traversal, and change-feed handlers. Multi-product\n * scoping via `product_id`.\n */\n\nimport type { UPGBaseNode, UPGEdge } from '@unified-product-graph/core'\nimport { type ToolHandler, text, textError } from '../lib/server-context.js'\n\nconst BUSINESS_AREAS: Record<string, string[]> = {\n identity: ['product', 'vision', 'mission'],\n understanding: ['persona', 'jtbd', 'pain_point', 'need', 'research_study', 'research_insight'],\n // : post- canonical type names (hypothesis_claim/evidence, experiment_plan/run).\n discovery: ['opportunity', 'solution', 'competitor', 'hypothesis_claim', 'hypothesis_evidence', 'experiment_plan', 'experiment_run', 'learning'],\n reaching: ['ideal_customer_profile', 'positioning', 'messaging', 'acquisition_channel', 'content_strategy'],\n converting: ['value_proposition', 'pricing_tier', 'funnel', 'funnel_step'],\n building: ['feature', 'user_story', 'epic', 'release', 'user_journey', 'user_flow'],\n sustaining: ['business_model', 'revenue_stream', 'cost_structure', 'unit_economics', 'pricing_strategy'],\n learning: ['outcome', 'kpi', 'metric', 'objective', 'key_result', 'retrospective'],\n}\n\nconst LIFECYCLE_PHASES: Record<string, string[]> = {\n strategy: ['product', 'outcome', 'metric', 'kpi', 'objective', 'key_result', 'vision', 'mission'],\n users: ['persona', 'jtbd', 'need', 'pain_point', 'desired_outcome'],\n discovery: ['opportunity', 'solution', 'research_study', 'insight', 'research_insight', 'competitor'],\n // : canonical names + legacy back-compat.\n validation: ['hypothesis_claim', 'hypothesis_evidence', 'experiment_plan', 'experiment_run', 'learning', 'evidence', 'hypothesis', 'experiment'],\n execution: ['feature', 'epic', 'user_story', 'release', 'task', 'bug'],\n}\n\n/**\n * Product summary, entity counts by type, and a human-readable overview.\n * Use this as the first call to orient an agent in a freshly-loaded product.\n *\n * @returns Text: `## <product title>` followed by description/stage,\n * graph stats (node/edge/type counts), and a sorted breakdown of entities\n * per type. Errors with `Product not found: <id>` for unknown products.\n * @throws textError when `product_id` is missing or the product\n * is not visible to the caller.\n * @atomicity atomic (read-only)\n * @see get_graph_digest\n * @see get_graph_analytics\n * @see get_entity_schema\n * @see list_nodes\n */\nexport const getProductContext: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError(`Missing required parameter: product_id`)\n const productId = args.product_id as string\n const product = await store.getProduct(productId)\n if (!product) return textError(`Product not found: ${productId}`)\n\n const nodes = await store.getAllNodes(productId)\n const edges = await store.getAllEdges(productId)\n\n const countsByType: Record<string, number> = {}\n for (const n of nodes) {\n countsByType[n.type] = (countsByType[n.type] ?? 0) + 1\n }\n\n const lines: string[] = [\n `## ${product.title}`,\n product.description ? `\\n${product.description}` : '',\n product.stage ? `\\nStage: ${product.stage}` : '',\n `\\n### Graph Stats`,\n `- Nodes: ${nodes.length}`,\n `- Edges: ${edges.length}`,\n `- Entity types: ${Object.keys(countsByType).length}`,\n `\\n### Entities by Type`,\n ...Object.entries(countsByType)\n .sort(([, a], [, b]) => b - a)\n .map(([type, count]) => `- ${type}: ${count}`),\n ]\n\n return text(lines.filter(Boolean).join('\\n'))\n}\n\n/**\n * Pre-computed analytics digest: counts by type, health metrics\n * (orphan rate, connectivity, validation rate, user coverage), key chain\n * stats (persona→jtbd→pain_point, opportunity→solution, hypothesis→\n * experiment→learning), business-area coverage, lifecycle counts.\n *\n * Cheaper than re-deriving from `list_nodes` or `query`; the agent's first\n * stop for \"how healthy is this graph?\".\n *\n * @returns JSON with `product`, `counts`, `health`, `chains`, `coverage`,\n * `lifecycle` keys (~500 tokens of summary). Note: chain keys still use the\n * v0.1 names (`persona_with_jtbd` etc.) pending a canonical rename.\n * @throws textError when `product_id` is missing.\n * @atomicity atomic (read-only)\n * @warning Chain keys carry v0.1 names (`persona_with_jtbd`,\n * `hypothesis_total`) pending a canonical rename. Lifecycle bucketing\n * uses local heuristics (`BUSINESS_AREAS` / `LIFECYCLE_PHASES`\n * constants in this file) rather than the canonical `UPG_DOMAINS` ring,\n * so it may drift from spec across versions.\n * @see get_product_context\n * @see get_graph_analytics\n * @see list_benchmarks\n * @see validate_graph\n */\nexport const getGraphDigest: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n const productId = args.product_id as string\n const nodes = await store.getAllNodes(productId)\n const edges = await store.getAllEdges(productId)\n const product = await store.getProduct(productId)\n\n const byType: Record<string, number> = {}\n for (const n of nodes) byType[n.type] = (byType[n.type] ?? 0) + 1\n\n const connected = new Set<string>()\n for (const e of edges) { connected.add(e.source); connected.add(e.target) }\n const orphanCount = nodes.filter((n) => !connected.has(n.id)).length\n\n const hypothesisCount = byType['hypothesis'] ?? 0\n const experimentCount = byType['experiment'] ?? 0\n const personaCount = byType['persona'] ?? 0\n\n const chainStats = (parentType: string, pattern: string) => {\n let w = 0\n const parents = nodes.filter((n) => n.type === parentType)\n for (const p of parents) {\n if (edges.some((e) => e.source === p.id && e.type.includes(pattern))) w++\n }\n return { with_child: w, total: parents.length }\n }\n\n const pj = chainStats('persona', 'jtbd')\n const jp = chainStats('jtbd', 'pain_point')\n const os = chainStats('opportunity', 'solution')\n const he = chainStats('hypothesis', 'experiment')\n const el = chainStats('experiment', 'learning')\n\n const typeSet = new Set(Object.keys(byType))\n const coverage: Record<string, { covered: number; total: number; types_present: string[]; types_missing: string[] }> = {}\n for (const [area, types] of Object.entries(BUSINESS_AREAS)) {\n const present = types.filter((t) => typeSet.has(t))\n const missing = types.filter((t) => !typeSet.has(t))\n coverage[area] = { covered: present.length, total: types.length, types_present: present, types_missing: missing }\n }\n\n const lifecycle: Record<string, number> = {}\n for (const [phase, types] of Object.entries(LIFECYCLE_PHASES)) {\n lifecycle[phase] = types.reduce((s, t) => s + (byType[t] ?? 0), 0)\n }\n\n return text(JSON.stringify({\n product: { title: product?.title ?? 'Unknown', stage: product?.stage ?? 'unknown' },\n counts: { total_nodes: nodes.length, total_edges: edges.length, by_type: byType },\n health: {\n orphan_count: orphanCount,\n orphan_rate: nodes.length > 0 ? Math.round((orphanCount / nodes.length) * 100) / 100 : 0,\n connectivity: nodes.length > 0 ? Math.round(((nodes.length - orphanCount) / nodes.length) * 100) / 100 : 0,\n validation_rate: hypothesisCount > 0 ? Math.round((experimentCount / hypothesisCount) * 100) / 100 : 0,\n user_coverage: personaCount > 0 ? Math.round((pj.with_child / personaCount) * 100) / 100 : 0,\n },\n chains: {\n persona_with_jtbd: pj.with_child, persona_total: pj.total,\n jtbd_with_pain_point: jp.with_child, jtbd_total: jp.total,\n opportunity_with_solution: os.with_child, opportunity_total: os.total,\n hypothesis_untested: hypothesisCount - he.with_child, hypothesis_total: hypothesisCount,\n experiment_with_learning: el.with_child, experiment_total: experimentCount,\n },\n coverage,\n lifecycle,\n }, null, 2))\n}\n\n/**\n * BFS traversal from a starting node (or every node of a given type),\n * following typed edges and returning a projected subgraph in one call.\n * Far cheaper than walking via `list_nodes` plus `get_node` for graph-wide\n * reads.\n *\n * Edge filtering: pass `traverse: ['persona_pursues_job', '!noisy_edge']`\n * to require the first edge type at depth 0 and exclude `noisy_edge` at\n * deeper levels. The last entry repeats for any depth beyond the array\n * length.\n *\n * Field projection: `include` is a whitelist of node fields (id and type\n * are returned by default). `edge_include` of `[]` returns the empty set of\n * edge metadata.\n *\n * @returns JSON: `{ nodes, edges, total_nodes, total_edges,\n * truncated?, truncated_at_depth?, hint? }`. Truncates with a hint when\n * `limit` is reached.\n * @throws textError when `product_id` is missing, or when\n * neither `from` nor `from_id` is provided, or when `from_id` does not\n * resolve.\n * @atomicity atomic (read-only)\n * @warning Pre-loads the entire product graph into memory before\n * filtering; 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\n * beyond `limit`, so check `truncated` before assuming the result is\n * complete.\n * @see list_nodes\n * @see get_node\n * @see get_area_graph\n * @see search_nodes\n * @see resolve_edge_for_pair\n * @see trace\n */\nexport const query: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n const productId = args.product_id as string\n const fromType = args.from as string | undefined\n const fromId = args.from_id as string | undefined\n if (!fromType && !fromId) return textError('Provide either \"from\" or \"from_id\"')\n\n const traverseEdgeTypes = args.traverse as string[] | undefined\n const maxDepth = Math.min(Math.max((args.depth as number) ?? 3, 1), 10)\n const maxNodes = Math.min(Math.max((args.limit as number) ?? 200, 1), 1000)\n const includeFields = new Set((args.include as string[] | undefined) ?? ['title', 'status', 'type'])\n includeFields.add('id'); includeFields.add('type')\n\n const allNodes = await store.getAllNodes(productId)\n const allEdges = await store.getAllEdges(productId)\n\n const edgesBySource = new Map<string, UPGEdge[]>()\n for (const e of allEdges) {\n let list = edgesBySource.get(e.source)\n if (!list) { list = []; edgesBySource.set(e.source, list) }\n list.push(e)\n }\n\n let startNodes: UPGBaseNode[]\n if (fromId) {\n const n = allNodes.find((n) => n.id === fromId)\n if (!n) return textError(`Node not found: ${fromId}`)\n startNodes = [n]\n } else {\n startNodes = allNodes.filter((n) => n.type === fromType)\n }\n\n const visited = new Set<string>()\n const collectedNodes: UPGBaseNode[] = []\n const collectedEdges: UPGEdge[] = []\n const queue: Array<{ id: string; level: number }> = []\n let truncated = false\n let maxDepthReached = 0\n\n for (const n of startNodes) {\n if (collectedNodes.length >= maxNodes) { truncated = true; break }\n visited.add(n.id); collectedNodes.push(n); queue.push({ id: n.id, level: 0 })\n }\n\n while (queue.length > 0) {\n if (collectedNodes.length >= maxNodes) { truncated = true; break }\n const { id, level } = queue.shift()!\n if (level > maxDepthReached) maxDepthReached = level\n if (level >= maxDepth) continue\n\n for (const edge of edgesBySource.get(id) ?? []) {\n if (traverseEdgeTypes && traverseEdgeTypes.length > 0) {\n const etl = level < traverseEdgeTypes.length ? traverseEdgeTypes[level] : traverseEdgeTypes[traverseEdgeTypes.length - 1]\n if (etl.startsWith('!')) { if (edge.type === etl.slice(1)) continue }\n else { if (edge.type !== etl) continue }\n }\n collectedEdges.push(edge)\n if (!visited.has(edge.target)) {\n visited.add(edge.target)\n const neighbor = allNodes.find((n) => n.id === edge.target)\n if (neighbor) {\n if (collectedNodes.length >= maxNodes) { truncated = true; break }\n collectedNodes.push(neighbor); queue.push({ id: edge.target, level: level + 1 })\n }\n }\n }\n }\n\n const edgeInclude = args.edge_include as string[] | undefined\n const projectedNodes = collectedNodes.map((n) => {\n const p: Record<string, unknown> = { id: n.id, type: n.type }\n if (includeFields.has('title')) p.title = n.title\n if (includeFields.has('status')) p.status = n.status\n if (includeFields.has('tags')) p.tags = n.tags\n if (includeFields.has('description')) p.description = n.description\n if (includeFields.has('properties')) p.properties = n.properties\n return p\n })\n\n let edgeArray: Array<Record<string, unknown>>\n if (edgeInclude !== undefined && edgeInclude.length === 0) {\n edgeArray = []\n } else {\n const ef = edgeInclude ? new Set(edgeInclude) : null\n edgeArray = collectedEdges.map((e) => {\n if (!ef) return { id: e.id, type: e.type, source: e.source, target: e.target }\n const p: Record<string, unknown> = {}\n if (ef.has('id')) p.id = e.id\n if (ef.has('type')) p.type = e.type\n if (ef.has('source')) p.source = e.source\n if (ef.has('target')) p.target = e.target\n return p\n })\n }\n\n const resp: Record<string, unknown> = {\n nodes: projectedNodes,\n edges: edgeArray,\n total_nodes: projectedNodes.length,\n total_edges: edgeArray.length,\n }\n if (truncated) {\n resp.truncated = true\n resp.truncated_at_depth = maxDepthReached\n resp.hint = `Limit of ${maxNodes} reached at depth ${maxDepthReached}.`\n }\n return text(JSON.stringify(resp, null, 2))\n}\n\n/**\n * Audit-log feed scoped to a single product. Returns mutations newer than\n * the optional `since` timestamp, capped at `limit` (default 50, max 200).\n * Cloud equivalent of the local server's `get_changes`.\n *\n * @returns JSON: `{ changes: AuditEntry[], total }`.\n * @throws textError when `product_id` is missing.\n * @atomicity atomic (read-only)\n * @warning Backed by the audit log: entries beyond the plan-tier\n * retention window are pruned and stay out of this surface. The `since`\n * filter runs in-memory after the store fetches up to `limit` entries,\n * so narrow `since` windows on busy products may surface fewer rows\n * than expected (raise `limit` to compensate).\n * @see get_audit_log\n * @see get_graph_digest\n * @see get_product_context\n */\nexport const getChanges: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n const since = args.since as string | undefined\n const limit = Math.min((args.limit as number) ?? 50, 200)\n const entries = await store.getAuditLog(args.product_id as string, limit)\n const filtered = since ? entries.filter((e) => e.created_at >= since) : entries\n return text(JSON.stringify({ changes: filtered, total: filtered.length }, null, 2))\n}\n","/**\n * Node CRUD and read handlers. Every tool scopes to a single product\n * via `product_id` and routes through `PgStore`.\n */\n\nimport type { UPGBaseNode, UPGEntityType } from '@unified-product-graph/core'\nimport { getLifecycleForType, resolveEntityType, UnknownEntityTypeError } from '@unified-product-graph/core'\nimport { type ToolHandler, text, textError } from '../lib/server-context.js'\nimport {\n inferEdgeTypeWithTier,\n checkPropertyTypes,\n checkLengthCaps,\n renderPropertyTypeWarning,\n} from '@unified-product-graph/sdk/logic'\nimport { nodeId, edgeId } from '../id-helpers.js'\n\n// ── Pagination helpers (mirrored from spec.ts) ──────────────────────────────\n\nconst LIST_NODES_DEFAULT_LIMIT = 1000\nconst LIST_NODES_MAX_LIMIT = 10000\n\nconst EXPORT_DOC_DEFAULT_LIMIT = 1000\nconst EXPORT_DOC_MAX_LIMIT = 10000\n\nfunction clampLimit(raw: unknown, def: number, max: number): number {\n const n = typeof raw === 'number' ? raw : def\n if (!Number.isFinite(n) || n <= 0) return def\n return Math.min(Math.floor(n), max)\n}\n\n/** Decode opaque base64 cursor `offset:N` into a numeric offset. Returns 0 on bad input. */\nfunction decodeCursor(raw: unknown): number {\n if (typeof raw !== 'string' || raw.length === 0) return 0\n try {\n const decoded = Buffer.from(raw, 'base64').toString('utf-8')\n const m = decoded.match(/^offset:(\\d+)$/)\n if (!m) return 0\n return Number.parseInt(m[1], 10)\n } catch {\n return 0\n }\n}\n\n/** Encode a numeric offset into the opaque base64 cursor `offset:N`. */\nfunction encodeCursor(offset: number): string {\n return Buffer.from(`offset:${offset}`, 'utf-8').toString('base64')\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n\n/**\n * Page through entities in a product, optionally filtered by type. Returns\n * a slim row shape (id, type, title, status, tags); for the full node plus\n * edges, follow up with `get_node` or `get_nodes`.\n *\n * Supports cursor-based pagination for large products. Pass `next_cursor`\n * from a previous response as `cursor` to advance to the next page.\n * Legacy `offset` parameter is still honoured when `cursor` is absent.\n *\n * @returns JSON: `{ nodes, total, limit, next_cursor? }`. `next_cursor` is\n * present when more results remain. `total` reflects the filtered count\n * before pagination.\n * @throws textError when `product_id` is missing.\n * @atomicity atomic (read-only)\n * @warning RLS-bounded: only nodes in products the caller has read access\n * to are returned. An empty list can mean \"no nodes\" or \"no access\".\n * Default `limit: 1000`, max 10000. For products with 1000+ nodes use\n * `cursor` pagination: keep calling with the returned `next_cursor` until\n * it is absent.\n * @see get_node\n * @see get_nodes\n * @see search_nodes\n * @see query\n */\nexport const listNodes: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError(`Missing required parameter: product_id`)\n const productId = args.product_id as string\n const filterType = args.type as string | undefined\n const limit = clampLimit(args.limit, LIST_NODES_DEFAULT_LIMIT, LIST_NODES_MAX_LIMIT)\n\n // Cursor takes precedence over legacy offset param\n const cursorOffset = args.cursor !== undefined\n ? decodeCursor(args.cursor)\n : ((args.offset as number) ?? 0)\n\n let nodes = await store.getAllNodes(productId)\n if (filterType) nodes = nodes.filter((n) => n.type === filterType)\n\n const total = nodes.length\n const slice = nodes.slice(cursorOffset, cursorOffset + limit)\n const nextOffset = cursorOffset + slice.length\n const nextCursor = nextOffset < total ? encodeCursor(nextOffset) : undefined\n\n const page = slice.map((n) => ({\n id: n.id,\n type: n.type,\n title: n.title,\n status: n.status,\n tags: n.tags,\n }))\n\n const body: Record<string, unknown> = { nodes: page, total, limit }\n if (nextCursor) body.next_cursor = nextCursor\n\n return text(JSON.stringify(body, null, 2))\n}\n\n/**\n * Export the full product graph as a UPG document: product metadata,\n * nodes, and edges, suitable for sync, backup, or import into a local\n * .upg file. Used by the `upg pull` CLI command and `apply_pull_changeset`\n * in the local MCP server.\n *\n * Supports cursor-based pagination for large products. Pass `next_cursor`\n * from a previous response as `cursor` to advance to the next page of nodes.\n * All edges are returned regardless of pagination (edges are typically far\n * fewer than nodes and safe to return in full).\n *\n * @returns JSON: `{ product, nodes, edges, total_nodes, limit, next_cursor? }`.\n * `next_cursor` is present when more node pages remain.\n * @throws textError when `product_id` is missing or the product is\n * not visible to the caller.\n * @atomicity atomic (read-only)\n * @warning For very large products (10 000+ nodes) iterate via `cursor`\n * plus `next_cursor` rather than relying on a single call. Every page\n * returns the full edge set, so deduplicate on the client when\n * assembling multiple pages.\n * @see apply_pull_changeset\n * @see get_product_graph\n * @see list_nodes\n */\nexport const exportUpgDocument: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError(`Missing required parameter: product_id`)\n const productId = args.product_id as string\n const product = await store.getProduct(productId)\n if (!product) return textError(`Product not found: ${productId}`)\n\n const limit = clampLimit(args.limit, EXPORT_DOC_DEFAULT_LIMIT, EXPORT_DOC_MAX_LIMIT)\n const cursorOffset = decodeCursor(args.cursor)\n\n const allNodes = await store.getAllNodes(productId)\n const edges = await store.getAllEdges(productId)\n\n const totalNodes = allNodes.length\n const nodeSlice = allNodes.slice(cursorOffset, cursorOffset + limit)\n const nextOffset = cursorOffset + nodeSlice.length\n const nextCursor = nextOffset < totalNodes ? encodeCursor(nextOffset) : undefined\n\n const body: Record<string, unknown> = {\n product,\n nodes: nodeSlice,\n edges,\n total_nodes: totalNodes,\n limit,\n }\n if (nextCursor) body.next_cursor = nextCursor\n\n return text(JSON.stringify(body, null, 2))\n}\n\n/**\n * Read one node with full properties and every connected edge, separated\n * into `edges_out` and `edges_in`. Edge entries carry the neighbour node's\n * title for a single-call human-readable view.\n *\n * @returns JSON: `{ node, edges_out, edges_in }`. Errors with\n * `Node not found: <id>` for unknown ids.\n * @throws textError when `node_id` is missing or the node does\n * not exist (or the caller has no access; RLS shares the same shape for\n * both).\n * @atomicity atomic (read-only)\n * @see list_nodes\n * @see get_nodes\n * @see search_nodes\n * @see query\n */\nexport const getNode: ToolHandler = async (args, { store }) => {\n if (!args.node_id) return textError(`Missing required parameter: node_id`)\n const nid = args.node_id as string\n const node = await store.getNode(nid)\n if (!node) return textError(`Node not found: ${nid}`)\n\n const edges = await store.getEdgesForNode(nid)\n const edgesOut = []\n const edgesIn = []\n\n for (const e of edges) {\n if (e.source === nid) {\n const targetNode = await store.getNode(e.target)\n edgesOut.push({ ...e, target_title: targetNode?.title ?? '(unknown)' })\n }\n if (e.target === nid) {\n const sourceNode = await store.getNode(e.source)\n edgesIn.push({ ...e, source_title: sourceNode?.title ?? '(unknown)' })\n }\n }\n\n return text(JSON.stringify({ node, edges_out: edgesOut, edges_in: edgesIn }, null, 2))\n}\n\n/**\n * Batch-read up to 50 nodes by id, each with their incident edges. Pass\n * `compact_edges: true` to drop neighbour-title hydration for a leaner\n * payload (id/type/source/target only).\n *\n * @returns JSON: `{ nodes, total, not_found? }`. `not_found` lists any\n * requested ids that did not resolve and appears only when at least one\n * miss occurred.\n * @throws textError when `product_id` or `ids` is missing/empty,\n * or when `ids` exceeds 50.\n * @atomicity atomic (read-only)\n * @warning `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\n * nodes (~30% smaller wire payload).\n * @see get_node\n * @see list_nodes\n * @see query\n */\nexport const getNodes: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n const ids = args.ids as string[] | undefined\n if (!ids || ids.length === 0) return textError('Missing required parameter: ids')\n if (ids.length > 50) return textError('Maximum 50 IDs per batch')\n\n const productId = args.product_id as string\n const compact = (args.compact_edges as boolean) ?? false\n const allNodes = await store.getAllNodes(productId)\n const allEdges = await store.getAllEdges(productId)\n const nodeMap = new Map(allNodes.map((n) => [n.id, n]))\n\n const results: Array<Record<string, unknown>> = []\n const notFound: string[] = []\n\n for (const id of ids) {\n const node = nodeMap.get(id)\n if (!node) { notFound.push(id); continue }\n const edgesOut = allEdges.filter((e) => e.source === id)\n const edgesIn = allEdges.filter((e) => e.target === id)\n results.push({\n node,\n edges_out: compact\n ? edgesOut.map((e) => ({ id: e.id, type: e.type, source: e.source, target: e.target }))\n : edgesOut.map((e) => ({ ...e, target_title: nodeMap.get(e.target)?.title ?? '(unknown)' })),\n edges_in: compact\n ? edgesIn.map((e) => ({ id: e.id, type: e.type, source: e.source, target: e.target }))\n : edgesIn.map((e) => ({ ...e, source_title: nodeMap.get(e.source)?.title ?? '(unknown)' })),\n })\n }\n\n const resp: Record<string, unknown> = { nodes: results, total: results.length }\n if (notFound.length > 0) resp.not_found = notFound\n return text(JSON.stringify(resp, null, 2))\n}\n\n/**\n * Substring search over node titles plus descriptions. Title hits score 2,\n * description hits score 1, results sorted by score then truncated to\n * `limit` (default 20, max 100).\n *\n * @returns JSON: `{ results: Array<node & { match_field }>, total }`.\n * @throws textError when `product_id` or `query` is missing.\n * @atomicity atomic (read-only)\n * @warning RLS-bounded: only nodes in products the caller has read\n * access to participate. Substring match is case-insensitive and runs\n * in-memory after a full product fetch; for very large products this\n * can be heavy. A Postgres-side full-text index is a future optimisation.\n * @see list_nodes\n * @see get_node\n * @see query\n */\nexport const searchNodes: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError(`Missing required parameter: product_id`)\n if (!args.query) return textError(`Missing required parameter: query`)\n const productId = args.product_id as string\n const q = (args.query as string).toLowerCase()\n const filterType = args.type as string | undefined\n const limit = Math.min((args.limit as number) ?? 20, 100)\n\n let nodes = await store.getAllNodes(productId)\n if (filterType) nodes = nodes.filter((n) => n.type === filterType)\n\n const scored = nodes\n .map((n) => {\n const titleMatch = n.title.toLowerCase().includes(q)\n const descMatch = n.description?.toLowerCase().includes(q) ?? false\n if (!titleMatch && !descMatch) return null\n return {\n node: n,\n score: titleMatch ? 2 : 1,\n match_field: titleMatch ? 'title' : 'description',\n }\n })\n .filter((s): s is { node: (typeof nodes)[0]; score: number; match_field: string } => s !== null)\n .sort((a, b) => b.score - a.score)\n .slice(0, limit)\n\n return text(JSON.stringify({\n results: scored.map((s) => ({ ...s.node, match_field: s.match_field })),\n total: scored.length,\n }, null, 2))\n}\n\n/**\n * Create a new entity, optionally connected to a parent via an inferred\n * edge type. Lifecycle-aware: when `status` is omitted and the type has a\n * registered lifecycle, the initial phase is set automatically. When\n * `status` is provided but doesn't match the lifecycle's phases, the\n * response carries a `warning` (the node is still created).\n *\n * @returns JSON: `{ node, edge?, warning? }`. `edge` is null when no\n * `parent_id` is passed; `warning` is present on lifecycle/parent issues.\n * @throws textError when `product_id`, `type`, or `title` is\n * missing.\n * @atomicity atomic-with-rollback\n * @warning Pass `parent_id` to auto-create a containment edge with inferred\n * type; missing parents are reported via `warning` rather than failing\n * the create.\n * @see batch_create_nodes\n * @see update_node\n * @see get_entity_schema\n * @see list_entity_types\n * @see get_valid_children\n */\nexport const createNode: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError(`Missing required parameter: product_id`)\n if (!args.type) return textError(`Missing required parameter: type`)\n if (!args.title) return textError(`Missing required parameter: title`)\n\n // Entity-type validation: resolve aliases to canonical, refuse uncatalogued\n // types before the write lands. Matches the local MCP server's create_node.\n let resolvedType: ReturnType<typeof resolveEntityType>\n try {\n resolvedType = resolveEntityType(args.type)\n } catch (err) {\n if (err instanceof UnknownEntityTypeError) return textError(err.message)\n throw err\n }\n const canonicalType = resolvedType.canonical\n\n const productId = args.product_id as string\n const newNode: UPGBaseNode = {\n id: nodeId(),\n type: canonicalType as UPGEntityType,\n title: args.title as string,\n }\n if (args.description) newNode.description = args.description as string\n if (args.tags) newNode.tags = args.tags as string[]\n if (args.properties) newNode.properties = args.properties as Record<string, unknown>\n\n const nodeType = canonicalType\n const nId = newNode.id\n const properties = newNode.properties\n\n // Property type validation: refuse declared-but-mismatched-type values\n // before the write lands. Matches the local MCP server's create_node.\n const { violations } = checkPropertyTypes(nodeType, properties)\n if (violations.length > 0) {\n return textError(renderPropertyTypeWarning(nodeType, violations)!)\n }\n\n // Length caps: soft warnings only, never refusals.\n const { warnings: lengthWarnings } = checkLengthCaps({\n title: args.title as string,\n description: args.description as string | undefined,\n properties,\n })\n\n const warnings: string[] = [...lengthWarnings]\n if (resolvedType.alias) {\n warnings.push(`Type \"${resolvedType.alias.from}\" is deprecated; using canonical \"${resolvedType.alias.to}\".`)\n }\n if (args.status) {\n newNode.status = args.status as string\n const lifecycle = getLifecycleForType(nodeType)\n if (lifecycle) {\n const validPhases = lifecycle.phases.map((p) => p.id)\n if (!validPhases.includes(newNode.status)) {\n warnings.push(`Status \"${newNode.status}\" is not a valid phase for type \"${nodeType}\". Valid phases: [${validPhases.join(', ')}]`)\n }\n }\n } else {\n const lifecycle = getLifecycleForType(nodeType)\n if (lifecycle) newNode.status = lifecycle.initial_phase\n }\n await store.addNode(productId, newNode)\n\n let edge = null\n const parentId = args.parent_id as string | undefined\n if (parentId) {\n const parent = await store.getNode(parentId)\n if (!parent) {\n warnings.push(`Parent node ${parentId} not found. Node created without edge.`)\n } else {\n // Catalog-strict parent-edge inference. Do NOT fabricate a\n // `_contains_` edge: the node still lands, but a non-canonical pair\n // yields a warning so the caller can wire an explicit edge type.\n const inference = inferEdgeTypeWithTier(parent.type, nodeType)\n if (!inference.ok) {\n const suggestion = inference.suggestions.length > 0\n ? ` Suggestions: ${inference.suggestions.map((s) => `${s.source_type} → ${s.target_type} (${s.edge_type})`).join('; ')}.`\n : ''\n warnings.push(`Parent edge not created; no canonical edge for ${parent.type} → ${nodeType}.${suggestion}`)\n } else {\n edge = { id: edgeId(), source: parentId, target: nId, type: inference.edgeType }\n await store.addEdge(productId, edge as Parameters<typeof store.addEdge>[1])\n }\n }\n }\n\n const result: Record<string, unknown> = { node: newNode, edge }\n if (warnings.length > 0) result.warning = warnings.join(' | ')\n return text(JSON.stringify(result, null, 2))\n}\n\n/**\n * Merge-update a node's fields. Unspecified fields are preserved.\n * Lifecycle-aware: when `status` changes to a phase not declared in the\n * type's lifecycle, the response carries a `warning` (update still\n * applied).\n *\n * @returns JSON: `{ node: updatedNode, warning? }`. Errors propagate from\n * the store (e.g. unknown node id).\n * @throws textError when `node_id` is missing or the store\n * rejects the update (unknown id).\n * @atomicity atomic-with-rollback\n * @warning Lifecycle-aware: invalid status values produce a `warning` but\n * the update still applies. For type changes, use `migrate_type`\n * instead; direct type mutation via this tool is unsupported.\n * @see migrate_type\n * @see batch_update_nodes\n * @see get_lifecycle\n */\nexport const updateNode: ToolHandler = async (args, { store }) => {\n if (!args.node_id) return textError(`Missing required parameter: node_id`)\n const nid = args.node_id as string\n const patch: Record<string, unknown> = {}\n if (args.title !== undefined) patch.title = args.title\n if (args.description !== undefined) patch.description = args.description\n if (args.tags !== undefined) patch.tags = args.tags\n if (args.status !== undefined) patch.status = args.status\n if (args.properties !== undefined) patch.properties = args.properties\n\n const warnings: string[] = []\n // Resolve the entity type once if either a status or property check needs it.\n let entityType: string | undefined\n if (args.status !== undefined || args.properties !== undefined) {\n const existingNode = await store.getNode(nid)\n entityType = existingNode?.type\n }\n\n if (args.status !== undefined && entityType) {\n const lifecycle = getLifecycleForType(entityType)\n if (lifecycle) {\n const validPhases = lifecycle.phases.map((p) => p.id)\n if (!validPhases.includes(args.status as string)) {\n warnings.push(`Status \"${args.status}\" is not a valid phase for type \"${entityType}\". Valid phases: [${validPhases.join(', ')}]`)\n }\n }\n }\n\n if (args.properties !== undefined && entityType) {\n // Property type validation: refuse declared-but-mismatched-type values.\n const { violations } = checkPropertyTypes(entityType, args.properties as Record<string, unknown>)\n if (violations.length > 0) {\n return textError(renderPropertyTypeWarning(entityType, violations)!)\n }\n }\n\n // Length caps: soft warnings only, never refusals.\n const { warnings: lengthWarnings } = checkLengthCaps({\n title: args.title as string | undefined,\n description: args.description as string | undefined,\n properties: args.properties as Record<string, unknown> | undefined,\n })\n warnings.push(...lengthWarnings)\n\n try {\n const updated = await store.updateNode(nid, patch)\n const result: Record<string, unknown> = { node: updated }\n if (warnings.length > 0) result.warning = warnings.join(' | ')\n return text(JSON.stringify(result, null, 2))\n } catch (err) {\n return textError((err as Error).message)\n }\n}\n\n/**\n * Delete a node and cascade-delete every connected edge in a single store\n * call. The response surfaces the dropped edge ids so the caller can\n * reconcile any local mirror.\n *\n * @returns JSON: `{ deleted_node_id, deleted_node_title, deleted_edge_ids }`.\n * Errors propagate from the store (e.g. unknown id).\n * @throws textError when `node_id` is missing or the store\n * rejects the deletion.\n * @atomicity atomic-with-rollback\n * @warning Cascade-deletes ALL incident edges, including cross-product\n * edges where the node is an endpoint. The operation is permanent (no\n * soft-delete or undo); the audit log records the removal. Pair with\n * `get_node` first if you need a snapshot.\n * @see batch_delete_nodes\n * @see get_node\n * @see deduplicate_nodes\n */\nexport const deleteNode: ToolHandler = async (args, { store }) => {\n if (!args.node_id) return textError(`Missing required parameter: node_id`)\n try {\n const { node, removedEdgeIds } = await store.removeNode(args.node_id as string)\n return text(JSON.stringify({\n deleted_node_id: node.id,\n deleted_node_title: node.title,\n deleted_edge_ids: removedEdgeIds,\n }, null, 2))\n } catch (err) {\n return textError((err as Error).message)\n }\n}\n\n/**\n * Reparent a node to a new parent within the same product. Removes any\n * existing containment edge where the node is the target, then creates a\n * new edge from `new_parent_id` to `node_id` with an inferred type. All\n * mutations run inside a single Postgres transaction.\n *\n * @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.\n * @throws textError when either node is missing, the nodes belong\n * to different products (cross-product reparenting is not allowed), or\n * the caller tries to move a node onto itself.\n * @atomicity atomic-with-rollback\n * @see batch_move_nodes\n * @see resolve_edge_for_pair\n * @see get_valid_children\n */\nexport const moveNode: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n if (!args.node_id) return textError('Missing required parameter: node_id')\n if (!args.new_parent_id) return textError('Missing required parameter: new_parent_id')\n\n const productId = args.product_id as string\n const nid = args.node_id as string\n const newParentId = args.new_parent_id as string\n\n if (nid === newParentId) return textError('Cannot move a node onto itself.')\n\n // Ownership checks (both nodes must exist and belong to this product)\n const node = await store.getNode(nid)\n if (!node) return textError(`Node not found: ${nid}`)\n if (node.product_id !== productId) return textError(`Node ${nid} does not belong to product ${productId}`)\n\n const newParent = await store.getNode(newParentId)\n if (!newParent) return textError(`New parent not found: ${newParentId}`)\n if (newParent.product_id !== productId) return textError(`New parent ${newParentId} does not belong to product ${productId}`)\n\n // Catalog-strict: validate the new containment edge BEFORE any mutation.\n // On a non-canonical pair the graph is left exactly as it started.\n const inference = inferEdgeTypeWithTier(newParent.type, node.type)\n if (!inference.ok) {\n const suggestion = inference.suggestions.length > 0\n ? ` Suggestions: ${inference.suggestions.map((s) => `${s.source_type} → ${s.target_type} (${s.edge_type})`).join('; ')}.`\n : ''\n return textError(`No canonical edge type for ${newParent.type} → ${node.type}.${suggestion} Reparenting refused.`)\n }\n const newEdgeId = edgeId()\n\n try {\n const result = await store.moveNode(productId, nid, newParentId, inference.edgeType, newEdgeId)\n return text(JSON.stringify(result, null, 2))\n } catch (err) {\n return textError((err as Error).message)\n }\n}\n\n/**\n * Merge a set of duplicate nodes into a canonical node and delete the\n * duplicates. All edge rebinding, self-loop cleanup, duplicate-edge removal,\n * property merge, and deletion run inside a single Postgres transaction\n * (all-or-nothing). Default `dry_run: true` previews what would change\n * without touching any data.\n *\n * @returns With `dry_run: true` (default): `{ canonical_id, duplicate_ids,\n * edges_to_rebind, nodes_to_delete, dry_run }`. With `dry_run: false`:\n * `{ canonical_id, merged_ids, rebound_edges, removed_self_loops,\n * removed_duplicate_edges, dry_run }`.\n * @throws 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\n * exist / does not belong to the product.\n * @atomicity atomic-with-rollback (all mutations committed or rolled back\n * together).\n * @warning Default `dry_run: true`; pass `dry_run: false` to commit. The\n * merge is permanent: duplicates are deleted, their edges rebound to\n * the canonical, self-loops removed, and duplicate edges deduplicated.\n * The change stands once committed (no undo); the audit log records\n * each merge for the retention window.\n * @see search_nodes\n * @see get_nodes\n * @see delete_node\n * @see validate_graph\n */\nexport const deduplicateNodes: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n if (!args.canonical_id) return textError('Missing required parameter: canonical_id')\n if (!args.duplicate_ids || !Array.isArray(args.duplicate_ids) || (args.duplicate_ids as string[]).length === 0)\n return textError('Missing required parameter: duplicate_ids (non-empty array)')\n\n const productId = args.product_id as string\n const canonicalId = args.canonical_id as string\n const duplicateIds = args.duplicate_ids as string[]\n const dryRun = (args.dry_run as boolean) ?? true\n\n if (duplicateIds.length > 20)\n return textError('Maximum 20 duplicate IDs per call')\n if (duplicateIds.includes(canonicalId))\n return textError('canonical_id must not appear in duplicate_ids')\n\n // Ownership validation: all nodes must exist and belong to product_id\n const allIds = [canonicalId, ...duplicateIds]\n for (const id of allIds) {\n const node = await store.getNode(id)\n if (!node) return textError(`Node not found: ${id}`)\n if (node.product_id !== productId)\n return textError(`Node ${id} does not belong to product ${productId}`)\n }\n\n if (dryRun) {\n // Count edges that would be rebound (all edges incident on any duplicate)\n let edgesToRebind = 0\n for (const dupId of duplicateIds) {\n const edges = await store.getEdgesForNode(dupId)\n edgesToRebind += edges.length\n }\n return text(JSON.stringify({\n canonical_id: canonicalId,\n duplicate_ids: duplicateIds,\n edges_to_rebind: edgesToRebind,\n nodes_to_delete: duplicateIds.length,\n dry_run: true,\n }, null, 2))\n }\n\n // Execute merge in a single Postgres transaction\n const result = await store.deduplicateNodes(productId, canonicalId, duplicateIds)\n return text(JSON.stringify({ ...result, dry_run: false }, null, 2))\n}\n\n/**\n * Whole-product graph dump: every node and edge, along with the product\n * row. Cloud-only convenience for full snapshots; expensive on large\n * products. Prefer `query` with a depth limit when you need a slice.\n *\n * @returns JSON: `{ product, nodes, edges }`.\n * @throws textError when `product_id` is missing or the product\n * is not visible to the caller.\n * @atomicity atomic (read-only)\n * @warning Returns the **entire** graph in one payload; for products\n * with thousands of nodes/edges this can be tens of MB. Prefer `query`\n * with a depth limit plus `include` projection for slices, or\n * `list_nodes` plus cursor pagination for full enumeration without the\n * wire-size hit.\n * @see query\n * @see list_nodes\n * @see get_graph_digest\n * @see get_graph_analytics\n */\nexport const getProductGraph: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError(`Missing required parameter: product_id`)\n const productId = args.product_id as string\n const product = await store.getProduct(productId)\n if (!product) return textError(`Product not found: ${productId}`)\n\n const nodes = await store.getAllNodes(productId)\n const edges = await store.getAllEdges(productId)\n\n return text(JSON.stringify({ product, nodes, edges }, null, 2))\n}\n","/**\n * Edge create / delete handlers.\n */\n\nimport { type ToolHandler, text, textError, type ToolResult } from '../lib/server-context.js'\nimport {\n inferEdgeTypeWithTier,\n validateEdgeTypePair,\n buildResolverHints,\n} from '@unified-product-graph/sdk/logic'\nimport { edgeId } from '../id-helpers.js'\n\n/**\n * Build an `isError` result whose body carries the UPG-505/UPG-515 resolver\n * enrichment (`anchor_hint` / `alternate_anchors` / `adjacent_edges`) for a\n * \"no canonical edge\" miss. Falls back to a plain `textError` when no\n * enrichment applies. Mirrors the local MCP server's `edgeResolverError` so\n * the failure boundary is identical on both servers.\n */\nfunction edgeResolverError(message: string, sourceType: string, targetType: string): ToolResult {\n const hints = buildResolverHints(sourceType, targetType)\n if (\n !hints.anchor_hint &&\n (!hints.alternate_anchors || hints.alternate_anchors.length === 0) &&\n (!hints.adjacent_edges || hints.adjacent_edges.length === 0)\n ) {\n return textError(message)\n }\n const body: Record<string, unknown> = {\n error: message,\n source_type: sourceType,\n target_type: targetType,\n ...hints,\n }\n return { content: [{ type: 'text', text: JSON.stringify(body, null, 2) }], isError: true }\n}\n\n/**\n * Create a relationship between two existing nodes. Edge type is inferred\n * from the source/target types when `type` is omitted. Inference is\n * catalog-strict: an unmapped pair is refused (with resolver hints) rather\n * than fabricating a `${source}_contains_${target}` edge, matching the\n * local MCP server's `create_edge`.\n *\n * @returns JSON: `{ edge: { id, source, target, type }, warning? }`.\n * @throws textError when `source_id`/`target_id` is missing, an endpoint\n * lookup fails, source and target resolve to the same node, an explicit\n * `type` violates the catalog's source/target pair, or no canonical edge\n * exists for the pair and no `type` was supplied (enriched with resolver\n * hints).\n * @atomicity atomic-with-rollback\n * @see resolve_edge_for_pair\n * @see list_edge_types\n * @see get_edge_type\n * @see batch_create_edges\n */\nexport const createEdge: ToolHandler = async (args, { store }) => {\n if (!args.source_id) return textError(`Missing required parameter: source_id`)\n if (!args.target_id) return textError(`Missing required parameter: target_id`)\n const sourceId = args.source_id as string\n const targetId = args.target_id as string\n const source = await store.getNode(sourceId)\n const target = await store.getNode(targetId)\n if (!source) return textError(`Source not found: ${sourceId}`)\n if (!target) return textError(`Target not found: ${targetId}`)\n\n // Refuse graph-topology self-loops up front. No canonical UPG edge type is\n // self-referential.\n if (sourceId === targetId) {\n return textError(\n `Self-loop refused: source and target resolve to the same node \"${sourceId}\". ` +\n `No canonical UPG edge type is self-referential. ` +\n `If you genuinely need a self-referential edge, file a spec proposal first.`,\n )\n }\n\n let edgeType: string\n let warning: string | undefined\n const explicitType = args.type as string | undefined\n if (explicitType) {\n // Verify a user-supplied canonical type against the catalog's expected\n // source/target pair. Non-canonical types fall through (surfaced later by\n // validate_graph as edge_drift).\n const pairCheck = validateEdgeTypePair(explicitType, source.type, target.type)\n if (!pairCheck.valid) return textError(pairCheck.reason!)\n edgeType = explicitType\n } else {\n const inference = inferEdgeTypeWithTier(source.type, target.type)\n if (!inference.ok) {\n const suggestion =\n inference.suggestions.length > 0\n ? ` Try one of: ${inference.suggestions\n .map((s) => `${s.source_type} → ${s.target_type} (${s.edge_type})`)\n .join('; ')}.`\n : ''\n return edgeResolverError(\n `No canonical edge type for ${source.type} → ${target.type}.${suggestion} Pass an explicit \\`type\\` if you need a non-catalog edge.`,\n source.type,\n target.type,\n )\n }\n edgeType = inference.edgeType\n if (inference.aliased) {\n warning = `Edge inferred from canonical (${inference.aliased.map((a) => `${a.from} → ${a.to}`).join(', ')}).`\n }\n }\n\n const edge = { id: edgeId(), source: sourceId, target: targetId, type: edgeType }\n\n try {\n await store.addEdge(source.product_id, edge as Parameters<typeof store.addEdge>[1])\n const body: Record<string, unknown> = { edge }\n if (warning) body.warning = warning\n return text(JSON.stringify(body, null, 2))\n } catch (err) {\n return textError((err as Error).message)\n }\n}\n\n/**\n * Drop a single edge by id.\n *\n * @returns JSON: `{ deleted_edge_id }`.\n * @throws textError when `edge_id` is missing or unknown.\n * @atomicity atomic-with-rollback\n * @see batch_delete_edges\n * @see export_edges\n */\nexport const deleteEdge: ToolHandler = async (args, { store }) => {\n if (!args.edge_id) return textError(`Missing required parameter: edge_id`)\n try {\n const edge = await store.removeEdge(args.edge_id as string)\n return text(JSON.stringify({ deleted_edge_id: edge.id }, null, 2))\n } catch (err) {\n return textError((err as Error).message)\n }\n}\n\n/**\n * Flat enumeration of all edges for a product, optionally filtered by type.\n * Returns the full edge set in one payload; for large products page via\n * `query` with edge filters.\n *\n * @returns JSON: `{ edges: [{ id, source, target, type }], total: number }`.\n * @throws textError when `product_id` is missing or the store rejects the read.\n * @atomicity atomic (read-only)\n * @see list_edge_types\n * @see rename_edge_type\n * @see query\n */\nexport const exportEdges: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n const types = args.types as string[] | undefined\n try {\n const edges = await store.exportEdges(args.product_id as string, types)\n return text(JSON.stringify({ edges, total: edges.length }, null, 2))\n } catch (err) {\n return textError((err as Error).message)\n }\n}\n\n/**\n * Rename all edges of one type to another across a product. Default\n * `dry_run: true`; pass `dry_run: false` to commit. Idempotent: a second\n * commit after a successful rename reports `affected: 0`. Catalog-aware\n * migrations should look up the canonical `from → to` via `list_edge_migrations`.\n *\n * @returns JSON: `{ from, to, affected: number, dry_run: boolean }`.\n * @throws textError when `product_id`, `from`, or `to` is missing.\n * @atomicity atomic-with-rollback (write path)\n * @see list_edge_types\n * @see get_edge_type\n * @see export_edges\n * @see migrate_type\n */\nexport const renameEdgeType: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n if (!args.from) return textError('Missing required parameter: from')\n if (!args.to) return textError('Missing required parameter: to')\n const dryRun = args.dry_run !== undefined ? Boolean(args.dry_run) : true\n try {\n const affected = await store.renameEdgeType(\n args.product_id as string,\n args.from as string,\n args.to as string,\n dryRun,\n )\n return text(JSON.stringify({ from: args.from, to: args.to, affected, dry_run: dryRun }, null, 2))\n } catch (err) {\n return textError((err as Error).message)\n }\n}\n","/**\n * Framework-exercise tools (cloud parity, UPG 0.8.6): apply a framework to a set\n * of entities and record each entity's result on the exercise's `includes` edge\n * — the value lives on the edge, not the entity node. Postgres-backed mirror of\n * the local server's `apply_framework` / `score_entity`. See ADR\n * 2026-06-02-framework-exercises and migration 005_edge_properties.sql.\n */\nimport type { UPGEntityType } from '@unified-product-graph/core'\nimport { UPG_FRAMEWORKS_BY_ID } from '@unified-product-graph/core'\nimport {\n frameworkInputKeys,\n FRAMEWORK_EXERCISE_INCLUDES_EDGE,\n} from '@unified-product-graph/sdk'\nimport { type ToolHandler, text, textError } from '../lib/server-context.js'\nimport { nodeId, edgeId } from '../id-helpers.js'\n\n/**\n * Create a framework_exercise and an `includes` edge to each entity it scores,\n * scoped to one product. Edges start blank; fill results with `score_entity`.\n *\n * @returns JSON: `{ exercise_id, exercise, included: [{ edge_id, entity_id }], warnings }`.\n * @throws textError on a missing product_id/framework_id or an unknown framework_id.\n * @atomicity per-write atomic; the exercise node and each includes edge commit\n * independently (a target that cannot be included is reported in `warnings`).\n * @see score_entity\n */\nexport const applyFramework: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n const frameworkId = args.framework_id as string | undefined\n if (!frameworkId) {\n return textError('Missing required parameter: framework_id (e.g. \"moscow\", \"rice-scoring\")')\n }\n const framework = UPG_FRAMEWORKS_BY_ID[frameworkId]\n if (!framework) {\n return textError(\n `Unknown framework: \"${frameworkId}\". Pass a framework id from the catalog ` +\n `(e.g. 'moscow', 'rice-scoring', 'kano-model').`,\n )\n }\n\n const productId = args.product_id as string\n const warnings: string[] = []\n try {\n const exercise = await store.addNode(productId, {\n id: nodeId(),\n type: 'framework_exercise' as UPGEntityType,\n title: (args.title as string | undefined) ?? `${framework.name} exercise`,\n status: (args.status as string | undefined) ?? 'draft',\n properties: { framework_id: frameworkId },\n })\n\n const included: Array<{ edge_id: string; entity_id: string }> = []\n for (const entityId of (args.entity_ids as string[] | undefined) ?? []) {\n const target = await store.getNode(entityId)\n if (!target) {\n warnings.push(`Could not include ${entityId}: target not found`)\n continue\n }\n const edge = {\n id: edgeId(),\n source: exercise.id,\n target: entityId,\n type: FRAMEWORK_EXERCISE_INCLUDES_EDGE,\n }\n try {\n await store.addEdge(productId, edge as Parameters<typeof store.addEdge>[1])\n included.push({ edge_id: edge.id, entity_id: entityId })\n } catch (err) {\n warnings.push(`Could not include ${entityId}: ${(err as Error).message}`)\n }\n }\n\n return text(\n JSON.stringify(\n { exercise_id: exercise.id, exercise, included, warnings },\n null,\n 2,\n ),\n )\n } catch (err) {\n return textError((err as Error).message)\n }\n}\n\n/**\n * Record a framework's result for one entity on the exercise's `includes` edge.\n * Auto-includes the entity when it is not yet in scope. Merges into existing\n * edge properties unless `replace` is set. Product is resolved from the\n * exercise node, so no product_id is needed.\n *\n * @returns JSON: `{ edge, warnings }`.\n * @throws textError when the exercise/entity is missing or the node is not a\n * framework_exercise.\n * @atomicity atomic-with-rollback (single edge upsert).\n * @see apply_framework\n */\nexport const scoreEntity: ToolHandler = async (args, { store }) => {\n const exerciseId = args.exercise_id as string | undefined\n const entityId = args.entity_id as string | undefined\n const values = args.values as Record<string, unknown> | undefined\n if (!exerciseId) return textError('Missing required parameter: exercise_id')\n if (!entityId) return textError('Missing required parameter: entity_id')\n if (!values || typeof values !== 'object' || Array.isArray(values)) {\n return textError(\n 'Missing required parameter: values (object of input → value, e.g. { \"moscow\": \"must\" })',\n )\n }\n\n const exercise = await store.getNode(exerciseId)\n if (!exercise) return textError(`Exercise not found: ${exerciseId}`)\n if (exercise.type !== 'framework_exercise') {\n return textError(`Node ${exerciseId} is a ${exercise.type}, not a framework_exercise.`)\n }\n\n const warnings: string[] = []\n const frameworkId = (exercise.properties as { framework_id?: string } | undefined)?.framework_id\n const framework = frameworkId ? UPG_FRAMEWORKS_BY_ID[frameworkId] : undefined\n if (framework) {\n const known = new Set(frameworkInputKeys(framework))\n if (known.size > 0) {\n const unknown = Object.keys(values).filter((k) => !known.has(k))\n if (unknown.length > 0) {\n warnings.push(\n `Value key(s) not declared by ${frameworkId}: ${unknown.join(', ')}. Stored anyway (permissive).`,\n )\n }\n }\n }\n\n try {\n const edges = await store.getEdgesForNode(exerciseId)\n const existing = edges.find(\n (e) =>\n e.type === FRAMEWORK_EXERCISE_INCLUDES_EDGE &&\n e.source === exerciseId &&\n e.target === entityId,\n )\n if (existing) {\n const edge = await store.setEdgeProperties(existing.id, values, { merge: !args.replace })\n return text(JSON.stringify({ edge, warnings }, null, 2))\n }\n\n // Not yet included: create the edge carrying the values in one step.\n const target = await store.getNode(entityId)\n if (!target) return textError(`Entity not found: ${entityId}`)\n const edge = {\n id: edgeId(),\n source: exerciseId,\n target: entityId,\n type: FRAMEWORK_EXERCISE_INCLUDES_EDGE,\n properties: values,\n }\n await store.addEdge(exercise.product_id, edge as Parameters<typeof store.addEdge>[1])\n return text(JSON.stringify({ edge, warnings }, null, 2))\n } catch (err) {\n return textError((err as Error).message)\n }\n}\n","/**\n * Product-area handlers. Read: list, subgraph BFS, summary. Write: create area.\n */\n\nimport type { UPGBaseNode, UPGEntityType } from '@unified-product-graph/core'\nimport { type ToolHandler, text, textError } from '../lib/server-context.js'\nimport { nodeId } from '../id-helpers.js'\n\n/**\n * List the product-area entities registered for a product. Ordered by the\n * store's default (typically created-at).\n *\n * @returns JSON: `{ areas, total }`.\n * @throws textError when `product_id` is missing.\n * @atomicity atomic (read-only)\n * @see get_area_graph\n * @see get_area_context\n * @see create_area\n */\nexport const listProductAreas: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError(`Missing required parameter: product_id`)\n const productId = args.product_id as string\n const areas = await store.listProductAreas(productId)\n return text(JSON.stringify({ areas, total: areas.length }, null, 2))\n}\n\n/**\n * BFS the subgraph rooted at a product-area. Depth default 3, clamped to 1..10.\n * Cheaper than a full `query` when you only need the area's surroundings.\n *\n * @returns JSON: `{ area, nodes, edges }`.\n * @throws textError when `product_id` or `area_id` is missing, or store rejects.\n * @atomicity atomic (read-only)\n * @see list_product_areas\n * @see get_area_context\n * @see query\n */\nexport const getAreaGraph: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError(`Missing required parameter: product_id`)\n if (!args.area_id) return textError(`Missing required parameter: area_id`)\n const productId = args.product_id as string\n const areaId = args.area_id as string\n const maxDepth = Math.min(Math.max((args.depth as number) ?? 3, 1), 10)\n\n try {\n const result = await store.getAreaGraph(productId, areaId, maxDepth)\n return text(JSON.stringify(result, null, 2))\n } catch (err) {\n return textError((err as Error).message)\n }\n}\n\n/**\n * Create a product area (type `area`) under a product. Top-level\n * organisational unit; the \"who owns what\" axis. Delegates to `store.addNode`.\n *\n * @returns JSON: `{ node }`.\n * @throws textError when `product_id` or `title` is missing.\n * @atomicity atomic\n * @see list_product_areas\n * @see get_area_context\n */\nexport const createArea: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n if (!args.title) return textError('Missing required parameter: title')\n\n const productId = args.product_id as string\n const areaNode: UPGBaseNode = {\n id: nodeId(),\n type: 'area' as UPGEntityType,\n title: args.title as string,\n }\n if (args.description) areaNode.description = args.description as string\n\n try {\n const created = await store.addNode(productId, areaNode)\n return text(JSON.stringify({ node: created }, null, 2))\n } catch (err) {\n return textError((err as Error).message)\n }\n}\n\n/**\n * Summarise a product area: entity counts by type, child-area count, and\n * description. Traverses containment edges up to depth 2.\n *\n * @returns JSON: `{ area: { id, title, description }, entity_counts, total_entities, child_areas }`.\n * @throws textError when `product_id` or `area_id` is missing, or the area lookup fails.\n * @atomicity atomic (read-only)\n * @see create_area\n * @see get_area_graph\n */\nexport const getAreaContext: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n if (!args.area_id) return textError('Missing required parameter: area_id')\n\n const productId = args.product_id as string\n const areaId = args.area_id as string\n\n const areaNode = await store.getNode(areaId)\n if (!areaNode) return textError(`Area node not found: ${areaId}`)\n if (areaNode.product_id !== productId) {\n return textError(`Area node ${areaId} does not belong to product ${productId}`)\n }\n\n try {\n // Count descendants directly; bypasses the product_area-only restriction of getAreaGraph\n const typeCounts = await store.getDescendantTypeCounts(productId, areaId, 2)\n\n const entity_counts: Record<string, number> = {}\n let child_areas = 0\n\n for (const { type: t, count } of typeCounts) {\n entity_counts[t] = count\n if (t === 'product_area' || t === 'area') child_areas += count\n }\n\n const total_entities = Object.values(entity_counts).reduce((sum, n) => sum + n, 0)\n\n return text(JSON.stringify({\n area: {\n id: areaNode.id,\n title: areaNode.title,\n description: areaNode.description ?? null,\n },\n entity_counts,\n total_entities,\n child_areas,\n }, null, 2))\n } catch (err) {\n return textError((err as Error).message)\n }\n}\n","/**\n * Schema introspection. Returns expected properties and valid edges\n * (in and out) for any UPG entity type. Delegates to\n * `@unified-product-graph/mcp-tooling`'s `buildEntitySchema`. Includes\n * alias resolution (e.g. `jtbd → job`).\n */\n\nimport { buildEntitySchema, UnknownEntityTypeError } from '@unified-product-graph/mcp-tooling'\nimport { type ToolHandler, text, textError } from '../lib/server-context.js'\n\n/**\n * Resolve the spec contract for an entity type: domain it belongs to,\n * expected properties + types/descriptions, valid edge types both\n * outbound and inbound, and lifecycle phases when registered. Aliases\n * deprecated synonyms (e.g. `jtbd → job`) and surfaces an `alias_of`\n * trail so the caller can warn.\n *\n * @returns JSON: `{ type, alias_of?, domain, expected_properties,\n * edges_out, edges_in, phases?, initial_phase?, terminal_phases?,\n * domain_guide? }`.\n * @throws textError when `type` is missing or unknown\n * (`UnknownEntityTypeError`).\n * @atomicity atomic (read-only)\n * @see get_entity_meta\n * @see list_entity_types\n * @see get_valid_children\n * @see get_lifecycle\n * @see get_domain_guide\n * @see list_edge_types\n * @see create_node\n */\nexport const getEntitySchema: ToolHandler = async (args) => {\n const entityType = args.type as string | undefined\n if (!entityType) return textError('Missing required parameter: type')\n\n try {\n const schema = buildEntitySchema(entityType)\n return text(JSON.stringify(schema, null, 2))\n } catch (err) {\n if (err instanceof UnknownEntityTypeError) return textError(err.message)\n throw err\n }\n}\n","/**\n * Multi-user collaboration: comments and role-based access. Cloud-only.\n */\n\nimport { type ToolHandler, text, textError } from '../lib/server-context.js'\n\n/**\n * Attach a comment to a node. Scoped to product and user; the cloud\n * schema enforces both joins.\n *\n * @returns JSON: `{ comment: { id, product_id, node_id, user_id, body, created_at } }`.\n * @throws textError when `product_id`, `node_id`, `user_id`, or `body` is missing.\n * @atomicity atomic\n * @warning `user_id` MUST resolve to a member of the product's collaborator set,\n * or downstream RLS rejects the insert.\n * @see list_comments\n * @see list_collaborators\n * @see grant_access\n */\nexport const addComment: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError(`Missing required parameter: product_id`)\n if (!args.node_id) return textError(`Missing required parameter: node_id`)\n if (!args.user_id) return textError(`Missing required parameter: user_id`)\n if (!args.body) return textError(`Missing required parameter: body`)\n const comment = await store.addComment(\n args.product_id as string,\n args.node_id as string,\n args.user_id as string,\n args.body as string,\n )\n return text(JSON.stringify({ comment }, null, 2))\n}\n\n/**\n * List the comment thread for a single node, ordered most-recent-last.\n *\n * @returns JSON: `{ comments: Comment[] }`.\n * @throws textError when `node_id` is missing.\n * @atomicity atomic (read-only)\n * @warning RLS-bounded; an empty array can mean \"no comments\" or \"no access\".\n * @see add_comment\n * @see list_collaborators\n */\nexport const listComments: ToolHandler = async (args, { store }) => {\n if (!args.node_id) return textError(`Missing required parameter: node_id`)\n const comments = await store.listComments(args.node_id as string)\n return text(JSON.stringify({ comments }, null, 2))\n}\n\n/**\n * Grant a user a role on a product. RBAC tier mapping is enforced\n * downstream of the store. Idempotent: same-role re-grants are a no-op;\n * different-role re-grants overwrite the previous role.\n *\n * @returns JSON: `{ granted: { product_id, user_id, role } }`.\n * @throws textError when `product_id`, `user_id`, or `role` is missing.\n * @atomicity atomic\n * @warning Billing-relevant: collaborator count typically drives plan tier;\n * a grant may trigger a tier upgrade or hit a seat-limit cap.\n * @see list_collaborators\n * @see add_comment\n */\nexport const grantAccess: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError(`Missing required parameter: product_id`)\n if (!args.user_id) return textError(`Missing required parameter: user_id`)\n if (!args.role) return textError(`Missing required parameter: role`)\n await store.grantAccess(\n args.product_id as string,\n args.user_id as string,\n args.role as string,\n )\n return text(JSON.stringify({\n granted: {\n product_id: args.product_id,\n user_id: args.user_id,\n role: args.role,\n },\n }, null, 2))\n}\n\n/**\n * List every user with explicit access to a product. Returns explicit\n * grants only; the product owner is implicit and may be omitted depending\n * on schema. Useful as a pre-check for seat-count before `grant_access`.\n *\n * @returns JSON: `{ collaborators: Array<{ user_id, role, granted_at }> }`.\n * @throws textError when `product_id` is missing.\n * @atomicity atomic (read-only)\n * @see grant_access\n * @see list_comments\n */\nexport const listCollaborators: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError(`Missing required parameter: product_id`)\n const collaborators = await store.listCollaborators(args.product_id as string)\n return text(JSON.stringify({ collaborators }, null, 2))\n}\n","/**\n * Postgres-side analytics aggregator over a product graph. Cloud-only;\n * heavier than `get_graph_digest`.\n */\n\nimport { type ToolHandler, text, textError } from '../lib/server-context.js'\n\n/**\n * Run the analytics aggregator on a product. Heavier than\n * `get_graph_digest`; results may lag recent writes when the aggregation\n * is cached at the storage layer.\n *\n * @returns JSON: `{ product: { id, title }, analytics }`.\n * @throws textError when `product_id` is missing or the product is invisible\n * to the caller (RLS-bounded; \"not found\" and \"no access\" share wording).\n * @atomicity atomic (read-only)\n * @see get_graph_digest\n * @see get_product_context\n * @see get_audit_log\n */\nexport const getGraphAnalytics: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError(`Missing required parameter: product_id`)\n const productId = args.product_id as string\n const product = await store.getProduct(productId)\n if (!product) return textError(`Product not found: ${productId}`)\n const analytics = await store.getGraphAnalytics(productId)\n return text(JSON.stringify({\n product: { id: product.id, title: product.title },\n analytics,\n }, null, 2))\n}\n","/**\n * Webhook registration. Cloud-only event sink for external systems\n * subscribing to graph mutations.\n */\n\nimport { type ToolHandler, text, textError } from '../lib/server-context.js'\n\n/**\n * Register a webhook endpoint for a product. `event` selects which\n * mutations dispatch (e.g. `node.created`, `edge.deleted`); `secret` is\n * stored alongside the registration for outgoing-request signing.\n *\n * Delivery: at-least-once with exponential backoff. Receivers MUST be\n * idempotent on `webhook.id` plus payload. Permanent 4xx eventually\n * disables the registration. Plan-tier may cap webhook count per product;\n * pre-check via `list_webhooks`.\n *\n * @returns JSON: `{ webhook: { id, product_id, event, url, secret?, created_at } }`.\n * @throws textError when `product_id`, `event`, or `url` is missing.\n * @atomicity atomic\n * @see list_webhooks\n * @see remove_webhook\n */\nexport const registerWebhook: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError(`Missing required parameter: product_id`)\n if (!args.event) return textError(`Missing required parameter: event`)\n if (!args.url) return textError(`Missing required parameter: url`)\n const webhook = await store.registerWebhook(\n args.product_id as string,\n args.event as string,\n args.url as string,\n args.secret as string | undefined,\n )\n return text(JSON.stringify({ webhook }, null, 2))\n}\n\n/**\n * List every webhook registered for a product.\n *\n * @returns JSON: `{ webhooks: Webhook[] }`.\n * @throws textError when `product_id` is missing.\n * @atomicity atomic (read-only)\n * @see register_webhook\n * @see remove_webhook\n */\nexport const listWebhooks: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError(`Missing required parameter: product_id`)\n const webhooks = await store.listWebhooks(args.product_id as string)\n return text(JSON.stringify({ webhooks }, null, 2))\n}\n\n/**\n * Drop a webhook registration by id. In-flight queued deliveries may\n * still fire after this returns; receivers should treat late events as\n * no-ops while tracking lifecycle.\n *\n * @returns JSON: `{ removed: <webhook_id> }`.\n * @throws textError when `webhook_id` is missing or the store rejects the deletion.\n * @atomicity atomic\n * @see register_webhook\n * @see list_webhooks\n */\nexport const removeWebhook: ToolHandler = async (args, { store }) => {\n if (!args.webhook_id) return textError(`Missing required parameter: webhook_id`)\n try {\n await store.removeWebhook(args.webhook_id as string)\n return text(JSON.stringify({ removed: args.webhook_id }, null, 2))\n } catch (err) {\n return textError((err as Error).message)\n }\n}\n","/**\n * Spec introspection handlers. Read-only snapshot of `@unified-product-graph/core`\n * compiled at server boot: playbooks, approaches, domain guides, frameworks,\n * edge catalogue, regions, lenses, type labels, entity meta, anti-patterns,\n * benchmarks, lifecycles, scales, migrations.\n *\n * Every handler is atomic and ignores `CloudContext`; the parameter satisfies\n * `ToolHandler<CloudContext>`. See `TOOLS.md` for the generated per-tool reference.\n */\n\nimport {\n UPG_PLAYBOOKS,\n UPG_APPROACHES,\n UPG_APPROACHES_BY_ID,\n REFLECT_MODES,\n UPG_DOMAIN_GUIDES,\n UPG_DOMAINS,\n UPG_FRAMEWORKS,\n UPG_FRAMEWORKS_BY_ID,\n UPG_EDGE_CATALOG,\n UPG_REGIONS,\n UPG_REGION_MAP,\n UPG_REGION_COUNT,\n UPG_LENSES,\n UPG_TYPE_LABELS,\n UPG_TYPE_LABELS_MAP,\n UPG_VALID_CHILDREN,\n UPG_CROSS_EDGE_TYPES,\n UPG_VERSION,\n MARKDOWN_FORMAT_VERSION,\n UPG_ENTITY_COUNT,\n UPG_DOMAIN_COUNT,\n UPG_EDGE_COUNT,\n UPG_ENTITY_META,\n UPG_ENTITY_META_BY_NAME,\n UPG_ENTITY_TO_DOMAIN,\n UPG_ANTI_PATTERNS,\n UPG_COUNT_BENCHMARKS,\n UPG_RELATIONSHIP_BENCHMARKS,\n UPG_RATIO_BENCHMARKS,\n UPG_DOMAIN_ACTIVATION,\n UPG_PRODUCT_STAGES,\n UPG_MIGRATIONS,\n UPG_EDGE_MIGRATIONS,\n UPG_SPLIT_MIGRATIONS,\n UPG_LIFECYCLES,\n UPG_LIFECYCLE_FREE_TYPES,\n UPG_LIFECYCLE_PLANNED_TYPES,\n UPG_SCALES,\n UPG_FRAMEWORK_CATEGORIES,\n UPG_STRUCTURE_PATTERNS,\n UPG_DOMAIN_RINGS,\n resolveContainmentEdge,\n resolveLabel,\n getRegionForEntityType,\n getLens,\n getVisibleTypes,\n getValidChildren,\n type UPGPlaybook,\n type UPGApproach,\n type UPGApproachId,\n type ReflectMode,\n type UPGRegion,\n type UPGDomainUsageGuide,\n type UPGFramework,\n type UPGEdgeDefinition,\n type UPGLens,\n type UPGTypeLabel,\n type EntityTypeMeta,\n type UPGEntityTypeMaturity,\n type UPGCuratedAntiPattern,\n type UPGAntiPatternSeverity,\n type UPGProductStage,\n type CountBenchmark,\n type RelationshipBenchmark,\n type RatioBenchmark,\n type DomainActivation,\n type UPGLifecycle,\n type UPGScaleDefinition,\n type UPGDomainRing,\n} from '@unified-product-graph/core'\n\nimport type { ToolHandler, ToolResult } from '../lib/server-context.js'\nimport { text, textError } from '../lib/server-context.js'\n\n// ── Pagination helpers (mirror list_nodes convention) ───────────────────────\n\nconst FRAMEWORKS_DEFAULT_LIMIT = 50\nconst FRAMEWORKS_MAX_LIMIT = 200\n\nfunction clampLimit(raw: unknown, def: number, max: number): number {\n const n = typeof raw === 'number' ? raw : def\n if (!Number.isFinite(n) || n <= 0) return def\n return Math.min(Math.floor(n), max)\n}\n\nfunction decodeCursor(raw: unknown): number {\n if (typeof raw !== 'string' || raw.length === 0) return 0\n // Cursor format: base64-encoded \"offset:N\". Tolerant: fall back to 0 on\n // malformed input rather than erroring (matches `list_nodes` UX).\n try {\n const decoded = Buffer.from(raw, 'base64').toString('utf-8')\n const m = decoded.match(/^offset:(\\d+)$/)\n if (!m) return 0\n return Number.parseInt(m[1], 10)\n } catch {\n return 0\n }\n}\n\nfunction encodeCursor(offset: number): string {\n return Buffer.from(`offset:${offset}`, 'utf-8').toString('base64')\n}\n\n// ── Playbooks ─────────────────────────────────────────────────────\n\n/**\n * List every canonical UPGPlaybook shipped with `@unified-product-graph/core`,\n * optionally narrowed by region, canonical-only flag, or framework_id.\n * Returns full playbook records: id, name, version, description, region,\n * is_canonical, framework_id, target_anchor_entity, and the ordered\n * creation_sequence.\n *\n * - region: exact-match filter on `UPGPlaybook.region` (a UPGRegionId).\n * - canonical_only: when true, return only playbooks with `is_canonical: true`\n * (exactly one per region, restating W1 invariant).\n * - framework_id: exact-match filter on `UPGPlaybook.framework_id`. 3 playbooks\n * are framework-anchored at v0.3.0 (BMC, AARRR, build-measure-learn).\n *\n * Filters AND together. Result is the canonical array order from `UPG_PLAYBOOKS`\n * (canonical first within each region).\n *\n * @returns JSON: `{ count, playbooks: UPGPlaybook[] }`\n * @atomicity atomic (read-only)\n * @see get_playbook\n * @see list_regions\n * @see list_approaches\n * @see list_frameworks\n */\nexport const listPlaybooks: ToolHandler = (args): ToolResult => {\n const region = args.region as string | undefined\n const canonicalOnly = args.canonical_only as boolean | undefined\n const frameworkId = args.framework_id as string | undefined\n\n let playbooks: readonly UPGPlaybook[] = UPG_PLAYBOOKS\n if (region) playbooks = playbooks.filter((p) => p.region === region)\n if (canonicalOnly === true) playbooks = playbooks.filter((p) => p.is_canonical === true)\n if (frameworkId) playbooks = playbooks.filter((p) => p.framework_id === frameworkId)\n\n return text(JSON.stringify({ count: playbooks.length, playbooks }, null, 2))\n}\n\n/**\n * Return one canonical UPGPlaybook by id (e.g. \"playbook:strategy-outcomes\",\n * \"playbook:business-gtm-growth\"). Includes the ordered creation_sequence with\n * full step kinds and prompts.\n *\n * IDs are namespace-prefixed (`playbook:*`). Calling with an `approach:*` id\n * (or one of the 5 bare-verb approach ids) returns null; route via\n * `get_approach` for the approach catalog.\n *\n * @returns JSON: the full `UPGPlaybook` record.\n * @throws textError when `id` is missing or unknown.\n * @atomicity atomic (read-only)\n * @see list_playbooks\n * @see get_approach\n * @see get_framework\n * @see get_region\n */\nexport const getPlaybook: ToolHandler = (args): ToolResult => {\n const id = args.id as string | undefined\n if (!id) return textError('Missing required parameter: id')\n const playbook = UPG_PLAYBOOKS.find((p) => p.id === id)\n if (!playbook) return textError(`Unknown playbook id: ${id}`)\n return text(JSON.stringify(playbook, null, 2))\n}\n\n// ── Approaches ────────────────────────────────────────────────────\n\n/**\n * List the five canonical UPGApproach records shipped with\n * `@unified-product-graph/core`: Plan / Inspect / Prioritise / Trace /\n * Reflect. Returns the full approach record per entry (id, label,\n * description with cartographic framing, question_answered, signature_hint,\n * framework_id_examples).\n *\n * **Cartographic framing**: an approach is the *path of arrival* to a\n * region of the graph (final approach to an airport, coastline approach),\n * distinct from the strategy-meeting sense (\"what's our approach to this\n * problem?\").\n *\n * Optional `framework_id` filter narrows to approaches whose\n * `framework_id_examples` include the given id (discoverability surface;\n * full reverse lookup is on `UPGFramework.approach_ids`).\n *\n * @returns JSON: `{ count, approaches: UPGApproach[] }`\n * @atomicity atomic (read-only)\n * @see get_approach\n * @see plan\n * @see inspect\n * @see prioritise\n * @see trace\n * @see reflect\n * @see list_playbooks\n */\nexport const listApproaches: ToolHandler = (args): ToolResult => {\n const frameworkId = args.framework_id as string | undefined\n\n let approaches: readonly UPGApproach[] = UPG_APPROACHES\n if (frameworkId) {\n approaches = approaches.filter((a) =>\n a.framework_id_examples?.includes(frameworkId) ?? false,\n )\n }\n\n return text(JSON.stringify({ count: approaches.length, approaches }, null, 2))\n}\n\n/**\n * Return one canonical UPGApproach by id. Valid ids are the bare verbs\n * `'plan' | 'inspect' | 'prioritise' | 'trace' | 'reflect'`: the same\n * names as the verb-led MCP tools.\n *\n * @returns JSON: the full `UPGApproach` record.\n * @throws textError when `id` is missing or unknown.\n * @atomicity atomic (read-only)\n * @see list_approaches\n * @see plan\n * @see inspect\n * @see prioritise\n * @see trace\n * @see reflect\n */\nexport const getApproach: ToolHandler = (args): ToolResult => {\n const id = args.id as string | undefined\n if (!id) return textError('Missing required parameter: id')\n const approach = UPG_APPROACHES_BY_ID[id]\n if (!approach) {\n return textError(\n `Unknown approach id: ${id}. Valid ids: plan, inspect, prioritise, trace, reflect.`,\n )\n }\n return text(JSON.stringify(approach, null, 2))\n}\n\n// ── Approach verb handlers ────────────────────────────────────────\n//\n// Five bare-verb handlers (`plan`, `inspect`, `prioritise`, `trace`,\n// `reflect`) exposed as direct MCP tools (no `apply_*` prefix). At v0.3.0\n// every handler is a **definition lookup**: it validates inputs, looks up\n// the approach record, and returns the family-resemblance envelope\n// `{ approach_id, scope, generated_at, ...payload }` where `payload` echoes\n// the caller's invocation parameters plus the approach record. The LLM is\n// the executor; it reads the signature_hint and synthesises the structured\n// projection (Plan's coverage_score, Inspect's violations[], etc.).\n\nfunction approachEnvelope(\n approachId: UPGApproachId,\n scope: unknown,\n payload: Record<string, unknown>,\n): ReturnType<typeof text> {\n const approach = UPG_APPROACHES_BY_ID[approachId]\n return text(\n JSON.stringify(\n {\n approach_id: approachId,\n scope,\n generated_at: new Date().toISOString(),\n approach,\n ...payload,\n },\n null,\n 2,\n ),\n )\n}\n\n/**\n * `plan`: the path of arrival to \"what should I build next?\".\n *\n * v0.3.0 ships as a definition lookup. Returns the Plan approach record\n * wrapped in the family-resemblance envelope, with the caller's `region`\n * echoed in `scope` and surfaced in `params`. The LLM consumes the\n * approach record's `signature_hint` and synthesises\n * `{ missing_entities, coverage_score }` against the live graph.\n *\n * @returns JSON envelope: `{ approach_id: 'plan', scope, generated_at, approach, params }`\n * @atomicity atomic (read-only)\n * @warning v0.3.0 returns the approach record only; the caller (LLM) is\n * the executor. Structured execution (compute coverage_score from\n * canonical region playbooks) lands in v0.3.x.\n * @see get_approach\n * @see list_playbooks\n * @see get_region\n * @see inspect\n * @see prioritise\n */\nexport const plan: ToolHandler = (args): ToolResult => {\n const region = args.region as string | undefined\n return approachEnvelope('plan', region ?? null, {\n params: { region: region ?? null },\n execution_mode: 'definition_lookup_v0_3_0',\n })\n}\n\n/**\n * `inspect`: the path of arrival to \"what's broken?\".\n *\n * v0.3.0 ships as a definition lookup. Returns the Inspect approach record\n * wrapped in the family-resemblance envelope. The LLM consumes the\n * `signature_hint` and synthesises `{ violations: [...] }` against\n * `UPG_ANTI_PATTERNS` and the live graph.\n *\n * @returns JSON envelope: `{ approach_id: 'inspect', scope, generated_at, approach, params }`\n * @atomicity atomic (read-only)\n * @warning v0.3.0 returns the approach record only; the caller (LLM) is\n * the executor. Structured execution (run anti-pattern matchers plus\n * structural lints) lands in v0.3.x.\n * @see get_approach\n * @see list_anti_patterns\n * @see get_anti_pattern\n * @see validate_graph\n * @see plan\n * @see reflect\n */\nexport const inspect: ToolHandler = (args): ToolResult => {\n const region = args.region as string | undefined\n const entities = args.entities as string[] | undefined\n const scope = region ?? entities ?? null\n return approachEnvelope('inspect', scope, {\n params: {\n region: region ?? null,\n entities: Array.isArray(entities) ? entities : null,\n },\n execution_mode: 'definition_lookup_v0_3_0',\n })\n}\n\n/**\n * `prioritise`: the path of arrival to \"what's most important?\".\n *\n * v0.3.0 ships as a definition lookup. Both `candidates` and `framework_id`\n * are required; prioritisation without an explicit candidate set or a\n * declared scoring lens is incoherent. Returns the Prioritise approach\n * record wrapped in the family-resemblance envelope.\n *\n * @returns JSON envelope: `{ approach_id: 'prioritise', scope, generated_at, approach, params }`\n * @throws textError when `candidates` or `framework_id` are missing/empty.\n * @atomicity atomic (read-only)\n * @warning v0.3.0 returns the approach record plus framework lookup only.\n * Structured execution (apply framework's `computed_properties` to each\n * candidate, return ranked output) lands in v0.3.x.\n * @see get_approach\n * @see list_frameworks\n * @see get_framework\n * @see plan\n * @see trace\n */\nexport const prioritise: ToolHandler = (args): ToolResult => {\n const candidates = args.candidates as string[] | undefined\n const frameworkId = args.framework_id as string | undefined\n if (!candidates || !Array.isArray(candidates) || candidates.length === 0) {\n return textError('Missing required parameter: candidates (entity_id[])')\n }\n if (!frameworkId) {\n return textError(\n 'Missing required parameter: framework_id (e.g. \"rice-scoring\", \"ice-scoring\", \"kano-model\")',\n )\n }\n const framework = UPG_FRAMEWORKS_BY_ID[frameworkId]\n return approachEnvelope('prioritise', candidates, {\n params: { candidates, framework_id: frameworkId },\n framework_resolved: framework\n ? { id: framework.id, name: framework.name, category: framework.category }\n : null,\n execution_mode: 'definition_lookup_v0_3_0',\n })\n}\n\n/**\n * `trace`: the path of arrival to \"walk a meaningful path through existing\n * graph\". Path is type-shorthand: `[\"persona\", \"job\", \"feature\"]` walks\n * persona→job→feature using the canonical edge for each pair (resolve via\n * `resolve_edge_for_pair`). Optional `edges_override` selects non-canonical\n * edges per hop; element `null` means \"use canonical\". The path expression\n * is the UPGEntityType[] shorthand itself (no DSL).\n *\n * v0.3.0 ships as a definition lookup.\n *\n * @returns JSON envelope: `{ approach_id: 'trace', scope, generated_at, approach, params }`\n * @throws textError when `anchor` or `path` are missing/invalid.\n * @atomicity atomic (read-only)\n * @warning v0.3.0 returns the approach record only; the LLM composes the\n * actual traversal via `query()`. Structured execution (BFS walker that\n * returns `{ trail, reached }`) lands in v0.3.x.\n * @see get_approach\n * @see resolve_edge_for_pair\n * @see query\n * @see get_node\n * @see plan\n * @see prioritise\n */\nexport const trace: ToolHandler = (args): ToolResult => {\n const anchor = args.anchor as string | undefined\n const path = args.path as string[] | undefined\n const edgesOverride = args.edges_override as (string | null)[] | undefined\n if (!anchor) {\n return textError('Missing required parameter: anchor (entity_id)')\n }\n if (!path || !Array.isArray(path) || path.length === 0) {\n return textError('Missing required parameter: path (UPGEntityType[])')\n }\n if (edgesOverride && edgesOverride.length !== path.length) {\n return textError(\n `edges_override length (${edgesOverride.length}) must match path length (${path.length})`,\n )\n }\n return approachEnvelope('trace', anchor, {\n params: {\n anchor,\n path,\n edges_override: edgesOverride ?? null,\n },\n execution_mode: 'definition_lookup_v0_3_0',\n })\n}\n\n/**\n * `reflect`: the path of arrival to \"what should I be questioning?\".\n *\n * Optional `mode` is one of the 4 canonical nouns:\n * `'assumptions' | 'alternatives' | 'blind-spots' | 'load-bearing'`.\n * Absence of mode means open reflection (undefined IS the open case;\n * there's no `'open'` literal). Optional `scope` accepts a region id,\n * an entity id, or `null` for whole-graph reflection.\n *\n * v0.3.0 ships as a definition lookup.\n *\n * @returns JSON envelope: `{ approach_id: 'reflect', scope, generated_at, approach, params }`\n * @throws textError when `mode` is provided but not one of the 4 canonical nouns.\n * @atomicity atomic (read-only)\n * @warning v0.3.0 returns the approach record only; the caller (LLM)\n * emits the prompts. Structured execution (template-driven prompt\n * generation per mode plus targeted entity selection) lands in v0.3.x.\n * @see get_approach\n * @see inspect\n * @see plan\n * @see get_anti_pattern\n */\nexport const reflect: ToolHandler = (args): ToolResult => {\n const scope = args.scope as string | null | undefined\n const mode = args.mode as string | undefined\n if (mode !== undefined && !REFLECT_MODES.includes(mode as ReflectMode)) {\n return textError(\n `Invalid mode: ${mode}. Valid modes: ${REFLECT_MODES.join(', ')}. Omit mode for open reflection.`,\n )\n }\n return approachEnvelope('reflect', scope ?? null, {\n params: {\n scope: scope ?? null,\n mode: mode ?? null,\n },\n execution_mode: 'definition_lookup_v0_3_0',\n })\n}\n\n// ── Domains ─────────────────────────────────────────────────────────────────\n\n/**\n * List every domain in `@unified-product-graph/core`.\n *\n * Two modes via `with_guide_only` (default `true`, preserving the\n * historical shape):\n * - `with_guide_only: true` (default): returns only domains that have a\n * canonical `UPGDomainUsageGuide`. Each row carries `{ domain_id,\n * anchor_entity, creation_sequence }` (the surface needed before drilling\n * into `get_domain_guide`). Order matches the canonical ring layout used\n * by `UPG_DOMAIN_GUIDES`.\n * - `with_guide_only: false`: returns every atomic domain from\n * `UPG_DOMAINS` (~36 at v0.3.0). Each row carries `{ domain_id, label,\n * description, types, has_guide }`. Use this to enumerate the full\n * catalog when authoring or auditing; `has_guide` flags whether a guide\n * is available for `get_domain_guide`.\n *\n * @returns JSON: `{ count, domains: Array<{ domain_id, anchor_entity, creation_sequence } | { domain_id, label, description, types, has_guide }> }`\n * @atomicity atomic (read-only)\n * @see get_domain_guide\n * @see list_regions\n * @see list_entity_types\n */\nexport const listDomains: ToolHandler = (args): ToolResult => {\n const withGuideOnly = args.with_guide_only as boolean | undefined\n if (withGuideOnly === false) {\n const guideIds = new Set(UPG_DOMAIN_GUIDES.map((g) => g.domain_id))\n const domains = UPG_DOMAINS.map((d) => ({\n domain_id: d.id,\n label: d.label,\n description: d.description,\n types: d.types,\n has_guide: guideIds.has(d.id),\n }))\n return text(JSON.stringify({ count: domains.length, domains }, null, 2))\n }\n const domains = UPG_DOMAIN_GUIDES.map((g) => ({\n domain_id: g.domain_id,\n anchor_entity: g.anchor_entity,\n creation_sequence: g.creation_sequence,\n }))\n return text(JSON.stringify({ count: domains.length, domains }, null, 2))\n}\n\n/**\n * Return the full UPGDomainUsageGuide for a domain: anchor entity, creation\n * sequence, named patterns (entity and edge chains), required cross-domain\n * bridges, and anti-patterns. This is the canonical \"how do I work in this\n * domain\" surface for MCP agents.\n *\n * @returns JSON: the full `UPGDomainUsageGuide` record.\n * @throws textError when `domain_id` is missing or unknown.\n * @atomicity atomic (read-only)\n * @see list_domains\n * @see list_anti_patterns\n * @see get_playbook\n */\nexport const getDomainGuide: ToolHandler = (args): ToolResult => {\n const domainId = args.domain_id as string | undefined\n if (!domainId) return textError('Missing required parameter: domain_id')\n const guide: UPGDomainUsageGuide | undefined = UPG_DOMAIN_GUIDES.find(\n (g) => g.domain_id === domainId,\n )\n if (!guide) return textError(`Unknown domain_id: ${domainId}`)\n return text(JSON.stringify(guide, null, 2))\n}\n\n// ── Frameworks ──────────────────────────────────────────────────────────────\n\n/**\n * List the canonical UPGFramework definitions: the curated, famous product\n * frameworks that anchor the public catalog. Paginated (default `limit: 50`,\n * max 200) because the full payload is large enough to overflow MCP transports\n * if returned in one shot.\n *\n * Cursor is opaque base64 (`offset:N`). Pass the `next_cursor` from a previous\n * response to advance; omit to start from the first page. The optional\n * `category` filter is exact-match against `UPGFramework.category` (e.g.\n * \"strategy\", \"prioritization\", \"discovery\") and applied BEFORE pagination,\n * so `total` reflects the filtered count.\n *\n * @returns JSON: `{ total, count, next_cursor?, frameworks: UPGFramework[] }`\n * @atomicity atomic (read-only)\n * @see get_framework\n * @see prioritise\n * @see list_approaches\n */\nexport const listFrameworks: ToolHandler = (args): ToolResult => {\n const category = args.category as string | undefined\n const limit = clampLimit(args.limit, FRAMEWORKS_DEFAULT_LIMIT, FRAMEWORKS_MAX_LIMIT)\n const cursorOffset = decodeCursor(args.cursor)\n\n let pool: readonly UPGFramework[] = UPG_FRAMEWORKS\n if (category) pool = pool.filter((f) => f.category === category)\n\n const total = pool.length\n const slice = pool.slice(cursorOffset, cursorOffset + limit)\n const nextOffset = cursorOffset + slice.length\n const nextCursor = nextOffset < total ? encodeCursor(nextOffset) : undefined\n\n const body: Record<string, unknown> = {\n total,\n count: slice.length,\n frameworks: slice,\n }\n if (nextCursor) body.next_cursor = nextCursor\n\n return text(JSON.stringify(body, null, 2))\n}\n\n/**\n * Return one canonical UPGFramework by id (e.g. \"rice-scoring\",\n * \"lean-canvas\"). Includes all four layers: data, structure, presentation,\n * education.\n *\n * @returns JSON: the full `UPGFramework` record.\n * @throws textError when `id` is missing or unknown.\n * @atomicity atomic (read-only)\n * @see list_frameworks\n * @see prioritise\n * @see get_playbook\n * @see get_approach\n */\nexport const getFramework: ToolHandler = (args): ToolResult => {\n const id = args.id as string | undefined\n if (!id) return textError('Missing required parameter: id')\n const framework = UPG_FRAMEWORKS_BY_ID[id]\n if (!framework) return textError(`Unknown framework id: ${id}`)\n return text(JSON.stringify(framework, null, 2))\n}\n\n// ── Edge catalogue ──────────────────────────────────────────────────────────\n\ninterface EdgeCatalogEntry extends UPGEdgeDefinition {\n type: string\n}\n\nfunction buildEdgeEntries(\n filterSource?: string,\n filterTarget?: string,\n): EdgeCatalogEntry[] {\n const entries: EdgeCatalogEntry[] = []\n for (const [type, def] of Object.entries(UPG_EDGE_CATALOG) as Array<\n [string, UPGEdgeDefinition]\n >) {\n if (filterSource && def.source_type !== filterSource) continue\n if (filterTarget && def.target_type !== filterTarget) continue\n entries.push({ type, ...def })\n }\n return entries\n}\n\n/**\n * List every canonical edge type from `UPG_EDGE_CATALOG`, optionally narrowed\n * by source_type and/or target_type. Each entry carries the edge key (`type`),\n * its forward/reverse verbs, structural classification, and endpoint types.\n * The polymorphic wildcard `'node'` is preserved on edges that are registered\n * as polymorphic; callers should treat it as \"matches any entity type\".\n *\n * @returns JSON: `{ count, edges: Array<{ type, forward_verb, reverse_verb, classification, source_type, target_type }> }`\n * @atomicity atomic (read-only)\n * @see get_edge_type\n * @see resolve_edge_for_pair\n * @see list_cross_edge_types\n * @see create_edge\n */\nexport const listEdgeTypes: ToolHandler = (args): ToolResult => {\n const sourceType = args.source_type as string | undefined\n const targetType = args.target_type as string | undefined\n const edges = buildEdgeEntries(sourceType, targetType)\n return text(JSON.stringify({ count: edges.length, edges }, null, 2))\n}\n\n/**\n * Return one canonical edge catalogue entry by edge type key (e.g.\n * \"persona_pursues_job\", \"feature_addresses_need\").\n *\n * @returns JSON: `{ type, forward_verb, reverse_verb, classification, source_type, target_type }`\n * @throws textError when `type` is missing or unknown.\n * @atomicity atomic (read-only)\n * @see list_edge_types\n * @see resolve_edge_for_pair\n * @see rename_edge_type\n */\nexport const getEdgeType: ToolHandler = (args): ToolResult => {\n const type = args.type as string | undefined\n if (!type) return textError('Missing required parameter: type')\n const def = (UPG_EDGE_CATALOG as Record<string, UPGEdgeDefinition>)[type]\n if (!def) return textError(`Unknown edge type: ${type}`)\n return text(JSON.stringify({ type, ...def }, null, 2))\n}\n\n// ── Regions ───────────────────────────────────────────────────────\n\n/**\n * List every canonical UPGRegion shipped with `@unified-product-graph/core`.\n * Returns a compact summary per region (id, label, order, shape, mental_model,\n * anchor entity type, atomic-domain composition, entity / edge counts): the\n * minimum surface needed to decide whether to drill into `get_region`. The\n * region count is fixed (10) so this endpoint stays non-paginated.\n *\n * Order matches the canonical 1..10 ring sequence from `UPG_REGIONS`\n * (Strategy & Outcomes → Operations & Quality).\n *\n * @returns JSON: `{ count, regions: Array<{ id, label, order, shape, mental_model, anchor_type, composes_atomic_domains, entity_count, intra_edge_count, boundary_edge_count }> }`\n * @atomicity atomic (read-only)\n * @see get_region\n * @see get_region_for_entity_type\n * @see list_domains\n * @see list_playbooks\n */\nexport const listRegions: ToolHandler = (): ToolResult => {\n const regions = UPG_REGIONS.map((r) => ({\n id: r.id,\n label: r.label,\n order: r.order,\n shape: r.shape,\n mental_model: r.mental_model,\n anchor_type: r.anchor.type,\n composes_atomic_domains: r.composes_atomic_domains,\n entity_count: r.entities.length,\n intra_edge_count: r.intra_edges.length,\n boundary_edge_count: r.boundary_edges.length,\n }))\n return text(\n JSON.stringify({ count: UPG_REGION_COUNT, regions }, null, 2),\n )\n}\n\n/**\n * Return the full UPGRegion record by id: anchor entity (with rationale and\n * inbound/outbound cross-edge counts), entity memberships with structural\n * roles, intra-domain edge keys, boundary edges to other regions, shape\n * archetype, and the atomic-domain composition.\n *\n * @returns JSON: the full `UPGRegion` record.\n * @throws textError when `id` is missing or unknown.\n * @atomicity atomic (read-only)\n * @see list_regions\n * @see get_region_for_entity_type\n * @see get_playbook\n * @see list_lenses\n */\nexport const getRegion: ToolHandler = (args): ToolResult => {\n const id = args.id as string | undefined\n if (!id) return textError('Missing required parameter: id')\n const region: UPGRegion | undefined = UPG_REGION_MAP[id]\n if (!region) return textError(`Unknown region id: ${id}`)\n return text(JSON.stringify(region, null, 2))\n}\n\n/**\n * Return the canonical UPGRegion that contains a given entity type. Wraps\n * `getRegionForEntityType`. Useful for adapters and copilots that need to\n * resolve \"which super-domain does this type belong to\" before deciding how\n * to render or route it.\n *\n * @returns JSON: the full `UPGRegion` record.\n * @throws textError when `entity_type` is missing or no region contains it.\n * @atomicity atomic (read-only)\n * @see get_region\n * @see list_regions\n * @see get_entity_meta\n * @see list_entity_types\n */\nexport const getRegionForEntity: ToolHandler = (args): ToolResult => {\n const entityType = args.entity_type as string | undefined\n if (!entityType) return textError('Missing required parameter: entity_type')\n const region = getRegionForEntityType(entityType)\n if (!region) return textError(`No region contains entity_type: ${entityType}`)\n return text(JSON.stringify(region, null, 2))\n}\n\n// ── Spec version ──────────────────────────────────────────────────\n\n/**\n * Return spec-level metadata for adopter compatibility checks: the spec\n * version, markdown format version, and canonical counts (entity types, edge\n * types, atomic domains, super-domain regions). Standalone tool: adopters\n * read it cleaner than folding it into `get_workspace_info` (which is\n * graph-instance-scoped, not spec-scoped).\n *\n * The pair `(upg_version, markdown_format_version)` is the right thing to\n * pin against; counts are informational only and may shift between patch\n * releases.\n *\n * @returns JSON: `{ upg_version, markdown_format_version, entity_count, edge_count, domain_count, region_count }`\n * @atomicity atomic (read-only)\n * @see list_entity_types\n * @see list_edge_types\n * @see list_regions\n */\nexport const getSpecVersion: ToolHandler = (): ToolResult => {\n return text(\n JSON.stringify(\n {\n upg_version: UPG_VERSION,\n markdown_format_version: MARKDOWN_FORMAT_VERSION,\n entity_count: UPG_ENTITY_COUNT,\n edge_count: UPG_EDGE_COUNT,\n domain_count: UPG_DOMAIN_COUNT,\n region_count: UPG_REGION_COUNT,\n },\n null,\n 2,\n ),\n )\n}\n\n// ── Edge resolver ─────────────────────────────────────────────────\n\n/**\n * Resolve the canonical UPGEdgeType for a `source_type` → `target_type`\n * containment pair. Wraps `resolveContainmentEdge` / `UPG_EDGE_PAIR_MAP`.\n *\n * Adapter-critical: every import adapter (Markdown, Notion, Linear, GitHub)\n * needs this when constructing `_contains_` edges. The catalog is closed,\n * so raw `${parent}_contains_${child}` template strings are unsafe because\n * most pairs are not registered. Use this tool to resolve the canonical\n * key, then fall back to a polymorphic edge (e.g. `node_informs_node`) or\n * skip when `edge_type` is `null`.\n *\n * @returns JSON: `{ source_type, target_type, edge_type: string | null }`\n * @throws textError when `source_type` or `target_type` is missing.\n * @atomicity atomic (read-only)\n * @warning Returns `edge_type: null` when no canonical pair is registered;\n * adapters MUST fall back to a polymorphic edge or skip the relationship\n * rather than synthesise a non-canonical key.\n * @see list_edge_types\n * @see get_edge_type\n * @see create_edge\n * @see trace\n */\nexport const resolveEdgeForPair: ToolHandler = (args): ToolResult => {\n const sourceType = args.source_type as string | undefined\n const targetType = args.target_type as string | undefined\n if (!sourceType) return textError('Missing required parameter: source_type')\n if (!targetType) return textError('Missing required parameter: target_type')\n const edgeType = resolveContainmentEdge(sourceType, targetType)\n return text(\n JSON.stringify(\n { source_type: sourceType, target_type: targetType, edge_type: edgeType },\n null,\n 2,\n ),\n )\n}\n\n// ── Cross-edge types ──────────────────────────────────────────────\n\n/**\n * List the canonical cross-product edge types from `UPG_CROSS_EDGE_TYPES`\n * (`shares_persona`, `shares_competitor`, `shares_metric`,\n * `depends_on_product`, `cannibalises`, `succeeds`, `hosts`, `contributes_to`,\n * `instance_of`). These are portfolio-level relationships between entities in\n * different products, separate from the within-product `UPG_EDGE_CATALOG` and\n * previously invisible to MCP.\n *\n * @returns JSON: `{ count, types: readonly UPGCrossEdgeType[] }`\n * @atomicity atomic (read-only)\n * @see list_edge_types\n * @see list_portfolio_cross_edges\n * @see migrate_cross_edges\n */\nexport const listCrossEdgeTypes: ToolHandler = (): ToolResult => {\n return text(\n JSON.stringify(\n { count: UPG_CROSS_EDGE_TYPES.length, types: UPG_CROSS_EDGE_TYPES },\n null,\n 2,\n ),\n )\n}\n\n// ── Lenses ────────────────────────────────────────────────────────\n\n/**\n * List every canonical UPGLens shipped with `@unified-product-graph/core`.\n * Returns a compact summary per lens (id, name, description, icon, audience,\n * perspective, framework_id, playbook_id, visible-domain count, intelligence-\n * prompt count): the minimum surface needed to choose a lens before drilling\n * into `get_lens`.\n *\n * @returns JSON: `{ count, lenses: Array<{ id, name, description, icon, audience, perspective, framework_id?, playbook_id?, visible_domain_count, intelligence_prompt_count }> }`\n * @atomicity atomic (read-only)\n * @see get_lens\n * @see list_regions\n * @see list_playbooks\n * @see list_frameworks\n */\nexport const listLenses: ToolHandler = (): ToolResult => {\n const lenses = UPG_LENSES.map((l) => ({\n id: l.id,\n name: l.name,\n description: l.description,\n icon: l.icon,\n audience: l.audience,\n perspective: l.perspective,\n framework_id: l.framework_id,\n playbook_id: l.playbook_id,\n visible_domain_count: l.visible_domains.length,\n intelligence_prompt_count: l.intelligence_prompts.length,\n }))\n return text(JSON.stringify({ count: UPG_LENSES.length, lenses }, null, 2))\n}\n\n/**\n * Return the full UPGLens record by id (e.g. `'product'`, `'ux_design'`,\n * `'engineering'`, `'full'`) plus the resolved list of entity types visible\n * through that lens (`getVisibleTypes` applies `visible_domains`,\n * `always_show_types`, `always_hide_types`).\n *\n * Combining the lens record with `visible_types` in one response avoids the\n * common \"fetch lens, then resolve types\" round-trip for renderers.\n *\n * @returns JSON: `{ ...UPGLens, visible_types: string[] }`\n * @throws textError when `id` is missing or unknown.\n * @atomicity atomic (read-only)\n * @see list_lenses\n * @see get_playbook\n * @see get_framework\n * @see list_entity_types\n */\nexport const getLensTool: ToolHandler = (args): ToolResult => {\n const id = args.id as string | undefined\n if (!id) return textError('Missing required parameter: id')\n const lens: UPGLens | undefined = getLens(id)\n if (!lens) return textError(`Unknown lens id: ${id}`)\n const visibleTypes = getVisibleTypes(lens)\n return text(JSON.stringify({ ...lens, visible_types: visibleTypes }, null, 2))\n}\n\n// ── Type labels ───────────────────────────────────────────────────\n\nconst TYPE_LABELS_DEFAULT_LIMIT = 100\nconst TYPE_LABELS_MAX_LIMIT = 500\n\n/**\n * List canonical UPGTypeLabel entries: every entity type's display label,\n * alt-labels (synonyms across frameworks plus common usage), per-framework\n * labels, and (where applicable) designation labels. Paginated (default\n * `limit: 100`, max 500) because the full list spans every active type\n * (~140+) and can balloon when alt_labels are dense.\n *\n * Cursor is opaque base64 (`offset:N`) following the `list_frameworks`\n * convention. Pass the `next_cursor` from a previous response to advance.\n *\n * @returns JSON: `{ total, count, next_cursor?, labels: UPGTypeLabel[] }`\n * @atomicity atomic (read-only)\n * @see get_type_label\n * @see list_entity_types\n * @see get_entity_meta\n */\nexport const listTypeLabels: ToolHandler = (args): ToolResult => {\n const limit = clampLimit(args.limit, TYPE_LABELS_DEFAULT_LIMIT, TYPE_LABELS_MAX_LIMIT)\n const cursorOffset = decodeCursor(args.cursor)\n\n const total = UPG_TYPE_LABELS.length\n const slice = UPG_TYPE_LABELS.slice(cursorOffset, cursorOffset + limit)\n const nextOffset = cursorOffset + slice.length\n const nextCursor = nextOffset < total ? encodeCursor(nextOffset) : undefined\n\n const body: Record<string, unknown> = {\n total,\n count: slice.length,\n labels: slice,\n }\n if (nextCursor) body.next_cursor = nextCursor\n\n return text(JSON.stringify(body, null, 2))\n}\n\n/**\n * Return one canonical UPGTypeLabel by entity type, plus the resolved display\n * label for an optional `framework_id` and/or `designation` (mirrors\n * `resolveLabel`). Lookup is exact-match against `UPG_TYPE_LABELS_MAP`.\n *\n * @returns JSON: `{ ...UPGTypeLabel, resolved_label: string }`\n * @throws textError when `entity_type` is missing or unknown.\n * @atomicity atomic (read-only)\n * @see list_type_labels\n * @see get_entity_meta\n * @see list_frameworks\n */\nexport const getTypeLabel: ToolHandler = (args): ToolResult => {\n const entityType = args.entity_type as string | undefined\n if (!entityType) return textError('Missing required parameter: entity_type')\n const label: UPGTypeLabel | undefined = UPG_TYPE_LABELS_MAP.get(entityType)\n if (!label) return textError(`Unknown entity_type: ${entityType}`)\n const frameworkId = args.framework_id as string | undefined\n const designation = args.designation as string | undefined\n const resolved = resolveLabel(entityType, frameworkId, designation)\n return text(JSON.stringify({ ...label, resolved_label: resolved }, null, 2))\n}\n\n// ── Hierarchy ─────────────────────────────────────────────────────\n\n/**\n * Return the list of valid direct-child entity types for a given parent\n * type. Wraps `getValidChildren` / `UPG_VALID_CHILDREN`. Returns an empty\n * array when the parent type has no registered children (or is unknown).\n *\n * @returns JSON: `{ parent_type, valid_children: string[] }`\n * @throws textError when `parent_type` is missing.\n * @atomicity atomic (read-only)\n * @see get_entity_schema\n * @see list_entity_types\n * @see get_entity_meta\n * @see create_node\n */\nexport const getValidChildrenTool: ToolHandler = (args): ToolResult => {\n const parentType = args.parent_type as string | undefined\n if (!parentType) return textError('Missing required parameter: parent_type')\n const validChildren = getValidChildren(parentType)\n return text(\n JSON.stringify(\n { parent_type: parentType, valid_children: validChildren },\n null,\n 2,\n ),\n )\n}\n\n// ── Entity meta + types ───────────────────────────────────────────\n\nconst ENTITY_TYPES_DEFAULT_LIMIT = 50\nconst ENTITY_TYPES_MAX_LIMIT = 200\n\n/**\n * List canonical entity types from `UPG_ENTITY_META`, the source of truth\n * for ontology evolution (every active, deprecated, or removed type with\n * its immutable `type_id`, maturity tier, and version metadata). Paginated\n * (default `limit: 50`, max 200) because the catalog is large (~348\n * entries at v0.3.0).\n *\n * Filters are AND-composed and applied **before** pagination, so `total`\n * reflects the filtered count:\n * - `domain`: exact-match against `UPG_ENTITY_TO_DOMAIN[name]` (the type's\n * atomic-domain id, e.g. `'user'`, `'market_intelligence'`).\n * - `maturity`: exact-match against `EntityTypeMeta.maturity`\n * (`'draft' | 'proposed' | 'stable' | 'deprecated' | 'removed'`).\n * - `deprecated`: boolean shortcut. `true` keeps only deprecated types;\n * `false` excludes deprecated and removed types (the active set).\n * Composes with `maturity` via AND, so when both are passed the row\n * must satisfy both.\n *\n * @returns JSON: `{ total, count, next_cursor?, types: Array<EntityTypeMeta & { domain_id: string | null }> }`\n * @atomicity atomic (read-only)\n * @see get_entity_meta\n * @see get_entity_schema\n * @see list_type_labels\n * @see list_domains\n */\nexport const listEntityTypes: ToolHandler = (args): ToolResult => {\n const domain = args.domain as string | undefined\n const maturity = args.maturity as UPGEntityTypeMaturity | undefined\n const deprecated = args.deprecated as boolean | undefined\n const limit = clampLimit(args.limit, ENTITY_TYPES_DEFAULT_LIMIT, ENTITY_TYPES_MAX_LIMIT)\n const cursorOffset = decodeCursor(args.cursor)\n\n // UPG_ENTITY_TO_DOMAIN is keyed by canonical type name → atomic-domain id.\n const typeToDomain = UPG_ENTITY_TO_DOMAIN as Readonly<Record<string, string>>\n\n let pool: readonly EntityTypeMeta[] = UPG_ENTITY_META\n if (maturity) pool = pool.filter((m) => m.maturity === maturity)\n if (deprecated === true) {\n pool = pool.filter((m) => m.maturity === 'deprecated')\n } else if (deprecated === false) {\n pool = pool.filter((m) => m.maturity !== 'deprecated' && m.maturity !== 'removed')\n }\n if (domain) {\n pool = pool.filter((m) => typeToDomain[m.name] === domain)\n }\n\n const total = pool.length\n const slice = pool.slice(cursorOffset, cursorOffset + limit).map((m) => ({\n ...m,\n domain_id: typeToDomain[m.name] ?? null,\n }))\n const nextOffset = cursorOffset + slice.length\n const nextCursor = nextOffset < total ? encodeCursor(nextOffset) : undefined\n\n const body: Record<string, unknown> = {\n total,\n count: slice.length,\n types: slice,\n }\n if (nextCursor) body.next_cursor = nextCursor\n\n return text(JSON.stringify(body, null, 2))\n}\n\n/**\n * Return one canonical `EntityTypeMeta` record by entity type name, plus the\n * resolved `domain_id` (or `null` if the type has no atomic-domain mapping).\n *\n * @returns JSON: `EntityTypeMeta & { domain_id: string | null }`\n * @throws textError when `name` is missing or unknown.\n * @atomicity atomic (read-only)\n * @see list_entity_types\n * @see get_type_label\n * @see get_entity_schema\n */\nexport const getEntityMeta: ToolHandler = (args): ToolResult => {\n const name = args.name as string | undefined\n if (!name) return textError('Missing required parameter: name')\n const meta = UPG_ENTITY_META_BY_NAME.get(name)\n if (!meta) return textError(`Unknown entity type: ${name}`)\n const typeToDomain = UPG_ENTITY_TO_DOMAIN as Readonly<Record<string, string>>\n return text(\n JSON.stringify({ ...meta, domain_id: typeToDomain[name] ?? null }, null, 2),\n )\n}\n\n// ── Anti-patterns ─────────────────────────────────────────────────\n\nconst ANTI_PATTERNS_DEFAULT_LIMIT = 50\nconst ANTI_PATTERNS_MAX_LIMIT = 200\n\n/**\n * List the curated cross-domain anti-patterns from `UPG_ANTI_PATTERNS`. Each\n * row pairs a memorable name with a machine-evaluable\n * `IntelligenceCondition`, the stages where it can fire, severity, and\n * remediation. These are graph-health patterns evaluated against the\n * **whole graph**, distinct from per-domain anti-patterns surfaced via\n * `get_domain_guide`.\n *\n * Paginated (default `limit: 50`, max 200) to leave headroom as the\n * catalog grows.\n *\n * Filters AND together and apply **before** pagination so `total` reflects\n * the filtered count:\n * - `severity`: exact-match against `UPGAntiPatternSeverity`\n * (`'high' | 'medium' | 'low'`).\n * - `stage`: keep only patterns whose `stages[]` includes the given\n * `UPGProductStage` (e.g. `'concept'`, `'launch'`).\n *\n * @returns JSON: `{ total, count, next_cursor?, anti_patterns: UPGCuratedAntiPattern[] }`\n * @atomicity atomic (read-only)\n * @see get_anti_pattern\n * @see validate_graph\n * @see inspect\n * @see get_domain_guide\n */\nexport const listAntiPatterns: ToolHandler = (args): ToolResult => {\n const severity = args.severity as UPGAntiPatternSeverity | undefined\n const stage = args.stage as UPGProductStage | undefined\n const limit = clampLimit(args.limit, ANTI_PATTERNS_DEFAULT_LIMIT, ANTI_PATTERNS_MAX_LIMIT)\n const cursorOffset = decodeCursor(args.cursor)\n\n let pool: readonly UPGCuratedAntiPattern[] = UPG_ANTI_PATTERNS\n if (severity) pool = pool.filter((p) => p.severity === severity)\n if (stage) pool = pool.filter((p) => p.stages.includes(stage))\n\n const total = pool.length\n const slice = pool.slice(cursorOffset, cursorOffset + limit)\n const nextOffset = cursorOffset + slice.length\n const nextCursor = nextOffset < total ? encodeCursor(nextOffset) : undefined\n\n const body: Record<string, unknown> = {\n total,\n count: slice.length,\n anti_patterns: slice,\n }\n if (nextCursor) body.next_cursor = nextCursor\n\n return text(JSON.stringify(body, null, 2))\n}\n\n/**\n * Return one curated anti-pattern by id (kebab-case slug, e.g.\n * `'features-without-hypotheses'`, `'personas-without-jobs'`). Includes the\n * full body: structured condition, why-it-matters, remediation, applicable\n * stages, severity, and optional source citation.\n *\n * IDs are stable URL fragments and remain frozen once published.\n *\n * @returns JSON: `UPGCuratedAntiPattern`\n * @throws textError when `id` is missing or unknown.\n * @atomicity atomic (read-only)\n * @see list_anti_patterns\n * @see inspect\n * @see validate_graph\n */\nexport const getAntiPattern: ToolHandler = (args): ToolResult => {\n const id = args.id as string | undefined\n if (!id) return textError('Missing required parameter: id')\n const pattern = UPG_ANTI_PATTERNS.find((p) => p.id === id)\n if (!pattern) return textError(`Unknown anti-pattern id: ${id}`)\n return text(JSON.stringify(pattern, null, 2))\n}\n\n// ── Benchmarks ────────────────────────────────────────────────────\n\n/**\n * Return one of the four canonical benchmark catalogs, the data behind\n * `get_graph_digest`'s health logic. The `kind` parameter is **required**\n * and routes to the matching source:\n * - `'count'` → `UPG_COUNT_BENCHMARKS`: per-entity-type expected ranges\n * across the canonical 9-stage product journey.\n * - `'relationship'` → `UPG_RELATIONSHIP_BENCHMARKS`: minimum\n * parent → child connection counts per stage.\n * - `'ratio'` → `UPG_RATIO_BENCHMARKS`: expected ratios between entity-type\n * counts (e.g. learnings / hypotheses ≥ 1).\n * - `'domain_activation'` → `UPG_DOMAIN_ACTIVATION`: when each atomic\n * domain is expected to \"turn on\" across the journey.\n *\n * Optional filters AND together; applied **after** the kind routes to a\n * catalog (so `total` reflects the filtered count).\n *\n * @returns JSON: `{ kind, total, count, benchmarks: ... }`\n * @throws textError when `kind` is missing or not one of the four supported values.\n * @atomicity atomic (read-only)\n * @see get_graph_digest\n * @see list_product_stages\n * @see list_domains\n * @see list_anti_patterns\n */\nexport const listBenchmarks: ToolHandler = (args): ToolResult => {\n const kind = args.kind as string | undefined\n if (!kind) return textError('Missing required parameter: kind')\n const stage = args.stage as UPGProductStage | undefined\n const domain = args.domain as string | undefined\n const typeToDomain = UPG_ENTITY_TO_DOMAIN as Readonly<Record<string, string>>\n\n if (kind === 'count') {\n let pool: readonly CountBenchmark[] = UPG_COUNT_BENCHMARKS\n if (domain) pool = pool.filter((b) => b.domain === domain)\n if (stage) pool = pool.filter((b) => b[stage] !== null)\n return text(\n JSON.stringify(\n { kind, total: UPG_COUNT_BENCHMARKS.length, count: pool.length, benchmarks: pool },\n null,\n 2,\n ),\n )\n }\n\n if (kind === 'relationship') {\n let pool: readonly RelationshipBenchmark[] = UPG_RELATIONSHIP_BENCHMARKS\n if (stage) pool = pool.filter((b) => b.stages.includes(stage))\n if (domain) {\n pool = pool.filter(\n (b) =>\n typeToDomain[b.parent_type] === domain || typeToDomain[b.child_type] === domain,\n )\n }\n return text(\n JSON.stringify(\n {\n kind,\n total: UPG_RELATIONSHIP_BENCHMARKS.length,\n count: pool.length,\n benchmarks: pool,\n },\n null,\n 2,\n ),\n )\n }\n\n if (kind === 'ratio') {\n let pool: readonly RatioBenchmark[] = UPG_RATIO_BENCHMARKS\n if (stage) pool = pool.filter((b) => b.stages.includes(stage))\n if (domain) {\n pool = pool.filter((b) => {\n const num = Array.isArray(b.numerator_type) ? b.numerator_type : [b.numerator_type]\n const den = Array.isArray(b.denominator_type) ? b.denominator_type : [b.denominator_type]\n return [...num, ...den].some((t) => typeToDomain[t] === domain)\n })\n }\n return text(\n JSON.stringify(\n { kind, total: UPG_RATIO_BENCHMARKS.length, count: pool.length, benchmarks: pool },\n null,\n 2,\n ),\n )\n }\n\n if (kind === 'domain_activation') {\n let pool: readonly DomainActivation[] = UPG_DOMAIN_ACTIVATION\n if (domain) pool = pool.filter((b) => b.domain_id === domain)\n if (stage) {\n pool = pool.filter((b) => b.expected_from === stage || b.expected_mature === stage)\n }\n return text(\n JSON.stringify(\n {\n kind,\n total: UPG_DOMAIN_ACTIVATION.length,\n count: pool.length,\n benchmarks: pool,\n },\n null,\n 2,\n ),\n )\n }\n\n return textError(\n `Unknown kind: ${kind}. Expected one of: count, relationship, ratio, domain_activation.`,\n )\n}\n\n// ── Product stages ────────────────────────────────────────────────\n\n/**\n * Return the canonical 9-stage product journey from `UPG_PRODUCT_STAGES`:\n * the closed enum used by `create_product`, `get_graph_digest` health logic,\n * benchmark stage scoping, and anti-pattern stage filters.\n *\n * Order is canonical: earliest → latest (`concept`, `validation`, `build`,\n * `beta`, `launch`, `growth`, `mature`, `maintenance`, `sunset`).\n *\n * @returns JSON: `{ count, stages: readonly UPGProductStage[] }`\n * @atomicity atomic (read-only)\n * @see list_benchmarks\n * @see list_anti_patterns\n * @see create_product\n */\nexport const listProductStages: ToolHandler = (): ToolResult => {\n return text(\n JSON.stringify(\n { count: UPG_PRODUCT_STAGES.length, stages: UPG_PRODUCT_STAGES },\n null,\n 2,\n ),\n )\n}\n\n// ── Spec introspection round 5 ──────────────────────────\n\n// ── Migrations ──────────────────────────────────────────────────────────────\n\n/**\n * List every type rename migration from `UPG_MIGRATIONS`: the version-scoped\n * map of deprecated `from` → canonical `to` renames (e.g. `pain_point → need`,\n * `hypothesis → hypothesis_claim`).\n *\n * Each row carries `{ from, to, since }` where `since` is the spec version\n * that introduced the migration. Optional `from_type` filter exact-matches\n * on the `from` field (useful for adopters checking whether a specific\n * legacy type is covered).\n *\n * @returns JSON: `{ migrations: [{ from, to, since }], total: number }`\n * @atomicity atomic (read-only)\n * @see list_edge_migrations\n * @see list_split_migrations\n * @see migrate_type\n * @see validate_graph\n * @see list_entity_types\n */\nexport const listTypeMigrations: ToolHandler = (args): ToolResult => {\n const fromType = args.from_type as string | undefined\n\n const migrations: Array<{ from: string; to: string; since: string }> = []\n for (const [since, entries] of Object.entries(UPG_MIGRATIONS)) {\n for (const m of entries) {\n if (!fromType || m.from === fromType) {\n migrations.push({ from: m.from, to: m.to, since })\n }\n }\n }\n\n return text(JSON.stringify({ migrations, total: migrations.length }, null, 2))\n}\n\n/**\n * List every edge-key migration from `UPG_EDGE_MIGRATIONS`: the\n * version-scoped map of renamed or dropped edge type keys (e.g.\n * `persona_has_jtbd → persona_pursues_job`).\n *\n * Each row carries `{ kind, from, to?, since }` where `kind` is `'rename'` or\n * `'drop'`. Optional `from_edge` filter exact-matches on the `from` field.\n *\n * @returns JSON: `{ migrations: [{ kind, from, to?, since }], total: number }`\n * @atomicity atomic (read-only)\n * @see list_type_migrations\n * @see list_split_migrations\n * @see rename_edge_type\n * @see list_edge_types\n * @see validate_graph\n */\nexport const listEdgeMigrations: ToolHandler = (args): ToolResult => {\n const fromEdge = args.from_edge as string | undefined\n\n const migrations: Array<{ kind: string; from: string; to?: string; since: string }> = []\n for (const [since, entries] of Object.entries(UPG_EDGE_MIGRATIONS)) {\n for (const m of entries) {\n if (!fromEdge || m.from === fromEdge) {\n migrations.push({\n kind: m.kind,\n from: m.from,\n ...(m.kind === 'rename' ? { to: m.to } : {}),\n since,\n })\n }\n }\n }\n\n return text(JSON.stringify({ migrations, total: migrations.length }, null, 2))\n}\n\n/**\n * List every 1→N split migration from `UPG_SPLIT_MIGRATIONS`: the\n * version-scoped registry of \"one type became multiple types\" rules (e.g.\n * `experiment → experiment_plan + experiment_run`, `hypothesis →\n * hypothesis_claim + hypothesis_evidence`).\n *\n * Each row carries the full `UPGSplitMigration` record plus `since` (the\n * introducing spec version). No filter arguments: the catalog is small\n * (≤ a handful of entries) and non-paginated.\n *\n * @returns JSON: `{ splits: [...], total: number }`\n * @atomicity atomic (read-only)\n * @see list_type_migrations\n * @see list_edge_migrations\n * @see migrate_type\n * @see validate_graph\n */\nexport const listSplitMigrations: ToolHandler = (): ToolResult => {\n const splits: Array<Record<string, unknown>> = []\n for (const [since, entries] of Object.entries(UPG_SPLIT_MIGRATIONS)) {\n for (const m of entries) {\n splits.push({ ...m, since })\n }\n }\n\n return text(JSON.stringify({ splits, total: splits.length }, null, 2))\n}\n\n// ── Lifecycles ──────────────────────────────────────────────────────────────\n\n/**\n * List lifecycle definitions from `UPG_LIFECYCLES`.\n *\n * Top-level response includes `lifecycles`, `free_types` (from\n * `UPG_LIFECYCLE_FREE_TYPES`: static types with no phase progression),\n * and `planned_types` (from `UPG_LIFECYCLE_PLANNED_TYPES`: lifecycle\n * planned but not yet authored). Filters: `entity_type` (exact-match);\n * `lifecycle_only` (when true, omits free/planned lists).\n *\n * @returns JSON: `{ lifecycles, total, free_types: string[], planned_types: string[] }`\n * @atomicity atomic (read-only)\n * @see get_lifecycle\n * @see list_entity_types\n * @see get_entity_meta\n */\nexport const listLifecycles: ToolHandler = (args): ToolResult => {\n const entityType = args.entity_type as string | undefined\n const lifecycleOnly = args.lifecycle_only as boolean | undefined\n\n let pool: readonly UPGLifecycle[] = UPG_LIFECYCLES\n if (entityType) pool = pool.filter((l) => l.entity_type === entityType)\n\n // When `lifecycle_only` is true, omit the free/planned blocks\n // entirely (matches the wire-shape `description`). The earlier version\n // returned empty arrays, which added wire bloat for callers asking for\n // lifecycle-only output.\n return text(\n JSON.stringify(\n {\n total: pool.length,\n lifecycles: pool,\n ...(lifecycleOnly === true\n ? {}\n : {\n free_types: Array.from(UPG_LIFECYCLE_FREE_TYPES).sort(),\n planned_types: Array.from(UPG_LIFECYCLE_PLANNED_TYPES).sort(),\n }),\n },\n null,\n 2,\n ),\n )\n}\n\n/**\n * Return the full `UPGLifecycle` definition for one entity type: initial\n * phase, terminal phases, and the ordered array of phases with their\n * transitions and core states. Returns a descriptive text message (not an\n * error) when the type is lifecycle-free or lifecycle-planned.\n *\n * @returns JSON: the full `UPGLifecycle` record, or a descriptive message.\n * @throws textError when `entity_type` is missing, lifecycle-free,\n * lifecycle-planned, or unknown.\n * @atomicity atomic (read-only)\n * @see list_lifecycles\n * @see get_entity_meta\n * @see get_entity_schema\n */\nexport const getLifecycle: ToolHandler = (args): ToolResult => {\n const entityType = args.entity_type as string | undefined\n if (!entityType) return textError('Missing required parameter: entity_type')\n const lifecycle = UPG_LIFECYCLES.find((l) => l.entity_type === entityType)\n if (!lifecycle) {\n if (UPG_LIFECYCLE_FREE_TYPES.has(entityType)) {\n return textError(`No lifecycle defined for entity type: ${entityType} (lifecycle-free: static type with no phase progression)`)\n }\n if (UPG_LIFECYCLE_PLANNED_TYPES.has(entityType)) {\n return textError(`No lifecycle defined for entity type: ${entityType} (lifecycle planned but not yet authored in this spec version)`)\n }\n return textError(`No lifecycle defined for entity type: ${entityType}`)\n }\n return text(JSON.stringify(lifecycle, null, 2))\n}\n\n// ── Scales ──────────────────────────────────────────────────────────────────\n\n/**\n * List every spec-defined assessment scale from `UPG_SCALES`. Scales define\n * the vocabulary for human judgments stored as `UPGAssessment` values:\n * numeric encoding, qualitative labels, and per-point descriptions.\n *\n * Non-paginated (the catalog is small). External scales in\n * `scale_extensions` are graph-instance–scoped and stay outside this\n * surface.\n *\n * @returns JSON: `{ scales: UPGScaleDefinition[], total: number }`\n * @atomicity atomic (read-only)\n * @see get_scale\n * @see get_entity_schema\n */\nexport const listScales: ToolHandler = (): ToolResult => {\n const scales: UPGScaleDefinition[] = Object.values(UPG_SCALES)\n return text(JSON.stringify({ scales, total: scales.length }, null, 2))\n}\n\n/**\n * Return one spec-defined assessment scale by id (e.g. `'reach_5'`,\n * `'severity_5'`).\n *\n * @returns JSON: the full `UPGScaleDefinition` record including all points.\n * @throws textError when `id` is missing or unknown.\n * @atomicity atomic (read-only)\n * @see list_scales\n * @see get_entity_schema\n */\nexport const getScale: ToolHandler = (args): ToolResult => {\n const id = args.id as string | undefined\n if (!id) return textError('Missing required parameter: id')\n const scale = UPG_SCALES[id]\n if (!scale) return textError(`Scale not found: ${id}`)\n return text(JSON.stringify(scale, null, 2))\n}\n\n// ── Framework metadata ────────────────────────────────────────────\n\n/**\n * List all valid framework category values from `UPG_FRAMEWORK_CATEGORIES`.\n * Use as valid values for the `category` filter on `list_frameworks`.\n *\n * @returns JSON: `{ categories: string[], total: number }`\n * @atomicity atomic (read-only)\n * @see list_frameworks\n * @see list_framework_structure_patterns\n */\nexport const listFrameworkCategories: ToolHandler = (): ToolResult => {\n return text(\n JSON.stringify(\n { categories: UPG_FRAMEWORK_CATEGORIES, total: UPG_FRAMEWORK_CATEGORIES.length },\n null,\n 2,\n ),\n )\n}\n\n/**\n * List all valid framework structure pattern values from\n * `UPG_STRUCTURE_PATTERNS` (tree, table, matrix, funnel, collection,\n * quadrant, flow). Mirrors `UPGFramework.structure.pattern`.\n *\n * @returns JSON: `{ patterns: string[], total: number }`\n * @atomicity atomic (read-only)\n * @see list_frameworks\n * @see list_framework_categories\n * @see get_framework\n */\nexport const listFrameworkStructurePatterns: ToolHandler = (): ToolResult => {\n return text(\n JSON.stringify(\n { patterns: UPG_STRUCTURE_PATTERNS, total: UPG_STRUCTURE_PATTERNS.length },\n null,\n 2,\n ),\n )\n}\n\n// ── Domain rings ──────────────────────────────────────────────────\n\n/**\n * List every `UPGDomainRing` from `UPG_DOMAIN_RINGS` in canonical order\n * (Nucleus → Understand → Define → Build → Grow → Operate → Extend). Rings\n * are the 7 concentric groupings of the 36 UPG atomic domains.\n *\n * @returns JSON: `{ rings: UPGDomainRing[], total: number }`\n * @atomicity atomic (read-only)\n * @see get_domain_ring\n * @see list_domains\n * @see get_domain_guide\n */\nexport const listDomainRings: ToolHandler = (): ToolResult => {\n return text(\n JSON.stringify(\n { rings: UPG_DOMAIN_RINGS, total: UPG_DOMAIN_RINGS.length },\n null,\n 2,\n ),\n )\n}\n\n/**\n * Return one `UPGDomainRing` by id (e.g. `'nucleus'`, `'understand'`,\n * `'define'`, `'build'`, `'grow'`, `'operate'`, `'extend'`).\n *\n * @returns JSON: the full `UPGDomainRing` record.\n * @throws textError when `id` is missing or unknown.\n * @atomicity atomic (read-only)\n * @see list_domain_rings\n * @see list_domains\n * @see get_domain_guide\n */\nexport const getDomainRing: ToolHandler = (args): ToolResult => {\n const id = args.id as string | undefined\n if (!id) return textError('Missing required parameter: id')\n const ring: UPGDomainRing | undefined = UPG_DOMAIN_RINGS.find((r) => r.id === id)\n if (!ring) return textError(`Domain ring not found: ${id}`)\n return text(JSON.stringify(ring, null, 2))\n}\n","/**\n * Portfolio family: cross-product relationships and the portfolio view.\n * Cross-product edges live in `upg.cross_product_edges` (migration 004),\n * separate from the within-product `upg.edges` table. Includes\n * `repair_dangling_edges` for orphaned cross-product rows.\n */\n\nimport { UPG_CROSS_EDGE_TYPES } from '@unified-product-graph/core'\nimport { type ToolHandler, text, textError } from '../lib/server-context.js'\nimport { edgeId } from '../id-helpers.js'\n\nconst crossEdgeTypeSet = new Set<string>(UPG_CROSS_EDGE_TYPES)\n\n// ── Portfolio family ─────────────────────────────────────────────────────\n\n/**\n * List the calling user's product portfolio. For v1, one portfolio per\n * instance: returns all products as a single portfolio named \"My\n * Portfolio\". Multi-portfolio scoping arrives once auth is wired.\n *\n * @returns JSON: `{ portfolios: [{ id, title, products: [{ id, title, stage? }] }], total: number }`\n * @atomicity atomic (read-only)\n * @warning v1 returns a single synthetic `'default'` portfolio per\n * instance; multi-portfolio scoping arrives once auth is wired.\n * Treat the `id: 'default'` shape as transitional.\n * @see list_products\n * @see list_portfolio_cross_edges\n */\nexport const listPortfolios: ToolHandler = async (_args, { store }) => {\n const products = await store.listProducts()\n const portfolio = {\n id: 'default',\n title: 'My Portfolio',\n products: products.map((p) => ({ id: p.id, title: p.title, stage: p.stage })),\n }\n return text(JSON.stringify({ portfolios: [portfolio], total: 1 }, null, 2))\n}\n\n/**\n * List all cross-product edges created by a specific product. Returns the\n * edges this product owns; edges where another product is the source or\n * target are visible via their creating product's call.\n *\n * @returns JSON: `{ edges: [{ id, source, target, type }], total: number }`\n * @throws textError when `product_id` is missing.\n * @atomicity atomic (read-only)\n * @warning Returns only edges this product **created**; edges another\n * product created targeting this product surface through that product's\n * own call. To audit all incident cross-edges, query each product in\n * the portfolio.\n * @see create_cross_product_edge\n * @see list_cross_edge_types\n * @see migrate_cross_edges\n */\nexport const listPortfolioCrossEdges: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError(`Missing required parameter: product_id`)\n const productId = args.product_id as string\n const edges = await store.listCrossProductEdges(productId)\n return text(JSON.stringify({\n edges: edges.map((e) => ({ id: e.id, source: e.source, target: e.target, type: e.type })),\n total: edges.length,\n }, null, 2))\n}\n\n/**\n * Create a cross-product edge owned by the given product. The edge type must\n * be one of the eight UPG cross-edge types (`shares_persona`, `shares_competitor`,\n * `shares_metric`, `depends_on_product`, `cannibalises`, `succeeds`, `hosts`,\n * `contributes_to`). Source and\n * target are qualified IDs: `{product_id}/{node_id}`.\n *\n * @returns JSON: `{ edge: { id, source, target, type, created_by_product_id } }`\n * @throws textError when `product_id`, `source`, `target`, or\n * `type` is missing, or `type` is not a UPG cross-edge type.\n * @atomicity atomic\n * @warning Source/target are qualified strings (`{product_id}/{node_id}`)\n * and skip FK validation against the products table. A target\n * referencing a deleted product becomes a dangling cross-edge; sweep\n * periodically with `repair_dangling_edges`.\n * @see list_cross_edge_types\n * @see list_portfolio_cross_edges\n * @see repair_dangling_edges\n * @see migrate_cross_edges\n */\nexport const createCrossProductEdge: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError(`Missing required parameter: product_id`)\n if (!args.source) return textError(`Missing required parameter: source`)\n if (!args.target) return textError(`Missing required parameter: target`)\n if (!args.type) return textError(`Missing required parameter: type`)\n\n const productId = args.product_id as string\n const source = args.source as string\n const target = args.target as string\n const type = args.type as string\n\n if (!crossEdgeTypeSet.has(type)) {\n return textError(\n `Invalid cross-edge type: \"${type}\". Must be one of: ${UPG_CROSS_EDGE_TYPES.join(', ')}`,\n )\n }\n if (type === 'instance_of') {\n return textError(\n `instance_of links a product entity to a canonical registry entity and is created via the ` +\n `registry tooling (register_instance) in the local MCP server, not create_cross_product_edge.`,\n )\n }\n\n try {\n const edge = await store.addCrossProductEdge(edgeId(), productId, source, target, type)\n return text(JSON.stringify({ edge }, null, 2))\n } catch (err) {\n return textError((err as Error).message)\n }\n}\n\n// ── repair_dangling_edges ─────────────────────────────────────────────────\n\n/**\n * Find and optionally remove cross-product edges that reference a product\n * that no longer exists.\n *\n * In cloud, Postgres FK constraints keep intra-product edges in sync (node\n * and edge tables cascade on product delete). The \"dangling\" scenario is\n * a cross-product edge whose target product has been deleted, because\n * `cross_product_edges.target` is a qualified string (`{product_id}/{node_id}`)\n * outside the FK reach of `upg.products`.\n *\n * `dry_run: true` (default): report without mutating.\n * `dry_run: false` plus `drop: ['dangling_cross_edges']`: delete the\n * dangling set.\n *\n * @returns JSON: `{ dangling: [{ id, source, target, type }], dangling_count, dry_run, dropped }`\n * @throws textError when `product_id` is missing.\n * @atomicity atomic-with-rollback (when drop is requested)\n * @warning Default is `dry_run: true`. Pass `dry_run: false` AND\n * `drop: ['dangling_cross_edges']` to actually delete; the second\n * guard prevents accidental drops. Per-edge errors during deletion\n * (concurrent removal) are swallowed; check `dropped` against\n * `dangling_count` to detect partial application.\n * @see create_cross_product_edge\n * @see list_portfolio_cross_edges\n * @see migrate_cross_edges\n */\nexport const repairDanglingEdges: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError(`Missing required parameter: product_id`)\n\n const productId = args.product_id as string\n const dryRun: boolean = args.dry_run !== false // default true\n const drop = (args.drop as string[] | undefined) ?? []\n\n // Fetch all cross-product edges owned by this product\n const allEdges = await store.listCrossProductEdges(productId)\n\n // For each edge, parse the target qualified ID to extract the target product_id.\n // Qualified format: \"{product_id}/{node_id}\"\n const dangling: Array<{ id: string; source: string; target: string; type: string }> = []\n\n for (const edge of allEdges) {\n const slashIdx = edge.target.indexOf('/')\n if (slashIdx === -1) {\n // Malformed qualified ID: classify as dangling\n dangling.push({ id: edge.id, source: edge.source, target: edge.target, type: edge.type })\n continue\n }\n const targetProductId = edge.target.slice(0, slashIdx)\n const exists = await store.productExists(targetProductId)\n if (!exists) {\n dangling.push({ id: edge.id, source: edge.source, target: edge.target, type: edge.type })\n }\n }\n\n let dropped = 0\n\n if (!dryRun && drop.includes('dangling_cross_edges') && dangling.length > 0) {\n for (const edge of dangling) {\n try {\n await store.deleteCrossProductEdge(edge.id)\n dropped++\n } catch {\n // Edge may have already been deleted concurrently; skip\n }\n }\n }\n\n return text(JSON.stringify({\n dangling,\n dangling_count: dangling.length,\n dry_run: dryRun,\n dropped,\n }, null, 2))\n}\n","/**\n * Atomic batch tools. Six handlers wrap multi-row writes in a single\n * `PoolClient` BEGIN/COMMIT, rolling back on any error.\n *\n * Tools: batch_create_nodes, batch_update_nodes, batch_delete_nodes,\n * batch_create_edges, batch_delete_edges, batch_move_nodes.\n */\n\nimport type { UPGBaseNode, UPGEntityType } from '@unified-product-graph/core'\nimport { getLifecycleForType, resolveEntityType, UnknownEntityTypeError } from '@unified-product-graph/core'\nimport { type ToolHandler, text, textError } from '../lib/server-context.js'\nimport {\n inferEdgeTypeWithTier,\n validateEdgeTypePair,\n checkPropertyTypes,\n checkLengthCaps,\n renderPropertyTypeWarning,\n} from '@unified-product-graph/sdk/logic'\nimport { nodeId, edgeId } from '../id-helpers.js'\nimport { appendAudit } from '../lib/audit.js'\n\n// ─── helpers ─────────────────────────────────────────────────────────────────\n\n/** Column list kept in sync with `UPGPgStore.NODE_COLS`. */\nconst NODE_COLS = 'id, product_id, type, title, description, status, tags, data'\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction rowToNode(row: any): UPGBaseNode & { product_id: string } {\n const node: UPGBaseNode & { product_id: string } = {\n id: row.id,\n type: row.type,\n title: row.title,\n product_id: row.product_id,\n }\n if (row.description != null) node.description = row.description\n if (row.status != null) node.status = row.status\n if (row.tags != null) node.tags = row.tags\n if (row.data != null) node.properties = row.data\n return node\n}\n\n// ─── batch_create_nodes ───────────────────────────────────────────────────────\n\n/**\n * Create up to 50 entities in a single atomic Postgres transaction. For each\n * node with a `parent_id`, a containment edge is created in the same\n * transaction. On any failure the entire batch is rolled back.\n *\n * @returns JSON: `{ created: [{ id, type, title }], count, warnings? }`.\n * @throws textError when `nodes` is missing / non-array, any required field\n * (`type`, `title`) is absent, or any node carries a declared property whose\n * value type mismatches the schema (rejects the whole batch before BEGIN).\n * @atomicity atomic-with-rollback (BEGIN / COMMIT / ROLLBACK).\n * @warning Validation runs inline before BEGIN; a single bad item rejects\n * the entire batch before any database mutation. Parent containment edges\n * are catalog-strict: a non-canonical parent→child pair skips the edge with\n * a warning rather than fabricating a `_contains_` edge (matches\n * `create_node`). A missing parent likewise skips the edge.\n * @see create_node\n * @see batch_create_edges\n * @see batch_update_nodes\n */\nexport const batchCreateNodes: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n const nodes = args.nodes as Array<Record<string, unknown>> | undefined\n if (!nodes || !Array.isArray(nodes)) return textError('Missing required parameter: nodes (array)')\n if (nodes.length === 0) return textError('nodes array is empty')\n if (nodes.length > 50) return textError('Maximum 50 nodes per batch')\n\n // Validate before touching the database. Property-type violations reject the\n // whole batch before BEGIN, matching the local server's batch_create_nodes.\n for (let i = 0; i < nodes.length; i++) {\n const n = nodes[i]\n if (!n.type) return textError(`Node at index ${i}: missing required field \"type\"`)\n if (!n.title) return textError(`Node at index ${i}: missing required field \"title\"`)\n try {\n resolveEntityType(n.type)\n } catch (err) {\n if (err instanceof UnknownEntityTypeError) return textError(`Node at index ${i}: ${err.message}`)\n throw err\n }\n if (n.properties !== undefined) {\n const { violations } = checkPropertyTypes(n.type as string, n.properties as Record<string, unknown>)\n if (violations.length > 0) {\n return textError(`Node at index ${i}: ${renderPropertyTypeWarning(n.type as string, violations)!}`)\n }\n }\n }\n\n const productId = args.product_id as string\n const warnings: string[] = []\n\n // Access the underlying pool via the store's private field.\n // PgStore exposes pool via the constructor closure; we reach it through\n // the store's pool property which is typed as `private` but accessible\n // at runtime via bracket notation.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const pool = (store as any).pool as import('pg').Pool\n const client = await pool.connect()\n\n try {\n await client.query('BEGIN')\n\n const created: Array<{ id: string; type: string; title: string }> = []\n\n for (let i = 0; i < nodes.length; i++) {\n const n = nodes[i]\n const nodeType = resolveEntityType(n.type).canonical\n const newId = nodeId()\n\n let status: string | null = null\n if (n.status) {\n status = n.status as string\n } else {\n const lifecycle = getLifecycleForType(nodeType)\n if (lifecycle) status = lifecycle.initial_phase\n }\n\n await client.query(\n `INSERT INTO upg.nodes (id, product_id, type, title, description, status, tags, data)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`,\n [\n newId,\n productId,\n nodeType,\n n.title as string,\n (n.description as string | undefined) ?? null,\n status,\n (n.tags as string[] | undefined) ?? null,\n n.properties ? JSON.stringify(n.properties) : null,\n ],\n )\n await appendAudit(client, {\n productId, action: 'create', entityType: 'node', entityId: newId,\n changes: { type: nodeType, title: n.title as string },\n })\n\n // Length-cap soft warnings (per-item, never refusals).\n const { warnings: lengthWarnings } = checkLengthCaps({\n title: n.title as string,\n description: n.description as string | undefined,\n properties: n.properties as Record<string, unknown> | undefined,\n })\n for (const w of lengthWarnings) warnings.push(`Node \"${newId}\": ${w}`)\n\n // If a parent_id was supplied, look up the parent and create a\n // catalog-strict containment edge inferred from parent type → new type.\n const parentId = n.parent_id as string | undefined\n if (parentId) {\n const { rows: parentRows } = await client.query(\n `SELECT ${NODE_COLS} FROM upg.nodes WHERE id = $1 AND product_id = $2`,\n [parentId, productId],\n )\n if (parentRows.length > 0) {\n const parentType = parentRows[0].type as string\n // Do NOT fabricate a `_contains_` edge: skip with a warning when the\n // pair has no canonical edge, matching create_node.\n const inference = inferEdgeTypeWithTier(parentType, nodeType)\n if (!inference.ok) {\n warnings.push(`Node \"${newId}\": parent edge not created; no canonical edge for ${parentType} → ${nodeType}.`)\n } else {\n const eid = edgeId()\n await client.query(\n `INSERT INTO upg.edges (id, product_id, source, target, type)\n VALUES ($1, $2, $3, $4, $5)`,\n [eid, productId, parentId, newId, inference.edgeType],\n )\n await appendAudit(client, {\n productId, action: 'create', entityType: 'edge', entityId: eid,\n changes: { source: parentId, target: newId, type: inference.edgeType },\n })\n }\n } else {\n warnings.push(`Node \"${newId}\": parent ${parentId} not found. Node created without edge.`)\n }\n }\n\n created.push({ id: newId, type: nodeType, title: n.title as string })\n }\n\n await client.query('COMMIT')\n // Emit post-commit. (Auto-created parent containment edges are not emitted\n // individually; the node.created event is the primary signal.)\n for (const c of created) store.emit(productId, 'node.created', { id: c.id, type: c.type, title: c.title })\n const body: Record<string, unknown> = { created, count: created.length }\n if (warnings.length > 0) body.warnings = warnings\n return text(JSON.stringify(body, null, 2))\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n } finally {\n client.release()\n }\n}\n\n// ─── batch_update_nodes ───────────────────────────────────────────────────────\n\n/**\n * Update up to 50 existing entities in a single atomic transaction. Properties\n * are MERGED with existing (not replaced). Unspecified fields are preserved.\n * All node IDs are resolved before the transaction opens; a missing ID rejects\n * the whole batch before any mutation lands.\n *\n * @returns JSON: `{ updated: [id], count }`.\n * @throws textError when `nodes` is missing / non-array / empty / >50, or any\n * item is missing `id`, or any `id` does not resolve.\n * @atomicity atomic-with-rollback (BEGIN / COMMIT / ROLLBACK).\n * @warning Properties merge with `||`: top-level keys overwrite while nested\n * keys stay shallow (deep-merge stays out of scope). To clear a property,\n * pass it as `null`. Items with no setClauses (every field undefined) are\n * silently skipped.\n * @see update_node\n * @see batch_create_nodes\n * @see migrate_type\n */\nexport const batchUpdateNodes: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n const nodes = args.nodes as Array<Record<string, unknown>> | undefined\n if (!nodes || !Array.isArray(nodes)) return textError('Missing required parameter: nodes (array)')\n if (nodes.length === 0) return textError('nodes array is empty')\n if (nodes.length > 50) return textError('Maximum 50 updates per batch')\n\n // Pre-validate: all IDs must exist. Property-type violations reject the\n // whole batch before BEGIN, matching the local server's batch_update_nodes.\n // Cache the resolved type so the mutation loop need not re-fetch.\n const typeById = new Map<string, string>()\n for (let i = 0; i < nodes.length; i++) {\n const n = nodes[i]\n if (!n.id) return textError(`Node at index ${i}: missing required field \"id\"`)\n const existing = await store.getNode(n.id as string)\n if (!existing) return textError(`Node at index ${i}: node \"${n.id}\" not found`)\n typeById.set(n.id as string, existing.type)\n if (n.properties !== undefined) {\n const { violations } = checkPropertyTypes(existing.type, n.properties as Record<string, unknown>)\n if (violations.length > 0) {\n return textError(`Node at index ${i}: ${renderPropertyTypeWarning(existing.type, violations)!}`)\n }\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const pool = (store as any).pool as import('pg').Pool\n const client = await pool.connect()\n\n try {\n await client.query('BEGIN')\n\n const updated: string[] = []\n const warnings: string[] = []\n\n for (const n of nodes) {\n const nid = n.id as string\n const setClauses: string[] = []\n const values: unknown[] = []\n let p = 1\n\n if (n.title !== undefined) { setClauses.push(`title = $${p++}`); values.push(n.title) }\n if (n.description !== undefined) { setClauses.push(`description = $${p++}`); values.push(n.description) }\n if (n.status !== undefined) { setClauses.push(`status = $${p++}`); values.push(n.status) }\n if (n.tags !== undefined) { setClauses.push(`tags = $${p++}`); values.push(n.tags) }\n if (n.properties !== undefined) {\n setClauses.push(`data = COALESCE(data, '{}'::jsonb) || $${p++}::jsonb`)\n values.push(JSON.stringify(n.properties))\n }\n\n if (setClauses.length > 0) {\n values.push(nid)\n await client.query(\n `UPDATE upg.nodes SET ${setClauses.join(', ')} WHERE id = $${p}`,\n values,\n )\n await appendAudit(client, {\n productId: args.product_id as string,\n action: 'update', entityType: 'node', entityId: nid,\n changes: {\n ...(n.title !== undefined ? { title: n.title } : {}),\n ...(n.description !== undefined ? { description: n.description } : {}),\n ...(n.status !== undefined ? { status: n.status } : {}),\n ...(n.tags !== undefined ? { tags: n.tags } : {}),\n ...(n.properties !== undefined ? { properties: n.properties } : {}),\n },\n })\n }\n\n // Status lifecycle + length-cap soft warnings (per-item, never refusals).\n if (n.status !== undefined) {\n const entityType = typeById.get(nid)\n if (entityType) {\n const lifecycle = getLifecycleForType(entityType)\n if (lifecycle) {\n const validPhases = lifecycle.phases.map((ph) => ph.id)\n if (!validPhases.includes(n.status as string)) {\n warnings.push(`Node \"${nid}\": status \"${n.status}\" is not a valid phase for type \"${entityType}\". Valid phases: [${validPhases.join(', ')}]`)\n }\n }\n }\n }\n const { warnings: lengthWarnings } = checkLengthCaps({\n title: n.title as string | undefined,\n description: n.description as string | undefined,\n properties: n.properties as Record<string, unknown> | undefined,\n })\n for (const w of lengthWarnings) warnings.push(`Node \"${nid}\": ${w}`)\n\n updated.push(nid)\n }\n\n await client.query('COMMIT')\n for (const nid of updated) store.emit(args.product_id as string, 'node.updated', { id: nid })\n const body: Record<string, unknown> = { updated, count: updated.length }\n if (warnings.length > 0) body.warnings = warnings\n return text(JSON.stringify(body, null, 2))\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n } finally {\n client.release()\n }\n}\n\n// ─── batch_delete_nodes ───────────────────────────────────────────────────────\n\n/**\n * Delete up to 50 entities and all their connected edges in a single atomic\n * transaction. All IDs are resolved before the transaction opens.\n *\n * @returns JSON: `{ deleted: [id], count }`.\n * @throws textError when `node_ids` is missing / non-array / empty / >50, or\n * any ID does not resolve.\n * @atomicity atomic-with-rollback (BEGIN / COMMIT / ROLLBACK).\n * @warning Cascade-deletes ALL edges incident on each node, in either\n * direction. Removal is hard; recovery flows through the audit log,\n * which records each removal for the retention window.\n * @see delete_node\n * @see batch_delete_edges\n * @see deduplicate_nodes\n */\nexport const batchDeleteNodes: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n const nodeIds = args.node_ids as string[] | undefined\n if (!nodeIds || !Array.isArray(nodeIds)) return textError('Missing required parameter: node_ids (array)')\n if (nodeIds.length === 0) return textError('node_ids array is empty')\n if (nodeIds.length > 50) return textError('Maximum 50 node IDs per batch')\n\n // Pre-validate: all IDs must exist\n for (let i = 0; i < nodeIds.length; i++) {\n const existing = await store.getNode(nodeIds[i])\n if (!existing) return textError(`Node at index ${i}: \"${nodeIds[i]}\" not found`)\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const pool = (store as any).pool as import('pg').Pool\n const client = await pool.connect()\n\n try {\n await client.query('BEGIN')\n\n const deleted: string[] = []\n\n for (const nid of nodeIds) {\n // Cascade-delete connected edges first\n await client.query(\n `DELETE FROM upg.edges WHERE source = $1 OR target = $1`,\n [nid],\n )\n await client.query(`DELETE FROM upg.nodes WHERE id = $1`, [nid])\n await appendAudit(client, {\n productId: args.product_id as string,\n action: 'delete', entityType: 'node', entityId: nid,\n })\n deleted.push(nid)\n }\n\n await client.query('COMMIT')\n for (const nid of deleted) store.emit(args.product_id as string, 'node.deleted', { id: nid })\n return text(JSON.stringify({ deleted, count: deleted.length }, null, 2))\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n } finally {\n client.release()\n }\n}\n\n// ─── batch_create_edges ───────────────────────────────────────────────────────\n\n/**\n * Create up to 50 edges in a single atomic transaction. Edge type is\n * auto-inferred from source/target node types when omitted.\n *\n * @returns JSON: `{ created: [{ id, source_id, target_id, type }], count }`.\n * @throws textError when `edges` is missing / non-array / empty / >50, any\n * item is missing `source_id` / `target_id`, any endpoint does not exist, an\n * explicit `type` violates the catalog's source/target pair, or an inferred\n * pair has no canonical edge. Any such failure rejects the whole batch\n * before BEGIN.\n * @atomicity atomic-with-rollback (BEGIN / COMMIT / ROLLBACK).\n * @warning Inference is catalog-strict: an unmapped pair is refused rather\n * than fabricating a `${source}_contains_${target}` edge. Pass an explicit\n * `type` (resolved via `resolve_edge_for_pair`) for non-catalog edges.\n * @see create_edge\n * @see resolve_edge_for_pair\n * @see batch_delete_edges\n */\nexport const batchCreateEdges: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n const edges = args.edges as Array<Record<string, unknown>> | undefined\n if (!edges || !Array.isArray(edges)) return textError('Missing required parameter: edges (array)')\n if (edges.length === 0) return textError('edges array is empty')\n if (edges.length > 50) return textError('Maximum 50 edges per batch')\n\n const productId = args.product_id as string\n\n // Pre-validate all edges before opening the transaction. Catalog-strict:\n // resolve every edge type up front and reject the whole batch on the first\n // invalid pair / unmapped inference; no `_contains_` fabrication.\n const resolvedEdgeTypes: string[] = []\n for (let i = 0; i < edges.length; i++) {\n const e = edges[i]\n if (!e.source_id) return textError(`Edge at index ${i}: missing required field \"source_id\"`)\n if (!e.target_id) return textError(`Edge at index ${i}: missing required field \"target_id\"`)\n if (e.source_id === e.target_id) return textError(`Edge at index ${i}: self-loop refused (source equals target \"${e.source_id}\").`)\n const source = await store.getNode(e.source_id as string)\n const target = await store.getNode(e.target_id as string)\n if (!source) return textError(`Edge at index ${i}: source node \"${e.source_id}\" not found`)\n if (!target) return textError(`Edge at index ${i}: target node \"${e.target_id}\" not found`)\n\n const explicitType = e.type as string | undefined\n if (explicitType) {\n const pairCheck = validateEdgeTypePair(explicitType, source.type, target.type)\n if (!pairCheck.valid) return textError(`Edge at index ${i}: ${pairCheck.reason}`)\n resolvedEdgeTypes.push(explicitType)\n } else {\n const inference = inferEdgeTypeWithTier(source.type, target.type)\n if (!inference.ok) {\n const suggestion = inference.suggestions.length > 0\n ? ` Try one of: ${inference.suggestions.map((s) => `${s.source_type} → ${s.target_type} (${s.edge_type})`).join('; ')}.`\n : ''\n return textError(`Edge at index ${i}: no canonical edge type for ${source.type} → ${target.type}.${suggestion} Pass an explicit \\`type\\` if you need a non-catalog edge.`)\n }\n resolvedEdgeTypes.push(inference.edgeType)\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const pool = (store as any).pool as import('pg').Pool\n const client = await pool.connect()\n\n try {\n await client.query('BEGIN')\n\n const created: Array<{ id: string; source_id: string; target_id: string; type: string }> = []\n\n for (let i = 0; i < edges.length; i++) {\n const e = edges[i]\n const eid = edgeId()\n const edgeType = resolvedEdgeTypes[i]\n\n await client.query(\n `INSERT INTO upg.edges (id, product_id, source, target, type)\n VALUES ($1, $2, $3, $4, $5)`,\n [eid, productId, e.source_id as string, e.target_id as string, edgeType],\n )\n await appendAudit(client, {\n productId, action: 'create', entityType: 'edge', entityId: eid,\n changes: { source: e.source_id as string, target: e.target_id as string, type: edgeType },\n })\n\n created.push({\n id: eid,\n source_id: e.source_id as string,\n target_id: e.target_id as string,\n type: edgeType,\n })\n }\n\n await client.query('COMMIT')\n for (const c of created) store.emit(productId, 'edge.created', { id: c.id, source: c.source_id, target: c.target_id, type: c.type })\n return text(JSON.stringify({ created, count: created.length }, null, 2))\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n } finally {\n client.release()\n }\n}\n\n// ─── batch_delete_edges ───────────────────────────────────────────────────────\n\n/**\n * Delete up to 50 edges in a single atomic transaction. All edge IDs are\n * resolved before the transaction opens.\n *\n * @returns JSON: `{ deleted: [id], count }`.\n * @throws textError when `edge_ids` is missing / non-array / empty / >50, or\n * any ID does not resolve in the given product.\n * @atomicity atomic-with-rollback (BEGIN / COMMIT / ROLLBACK).\n * @see delete_edge\n * @see batch_create_edges\n * @see export_edges\n */\nexport const batchDeleteEdges: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n const edgeIds = args.edge_ids as string[] | undefined\n if (!edgeIds || !Array.isArray(edgeIds)) return textError('Missing required parameter: edge_ids (array)')\n if (edgeIds.length === 0) return textError('edge_ids array is empty')\n if (edgeIds.length > 50) return textError('Maximum 50 edge IDs per batch')\n\n const productId = args.product_id as string\n\n // Pre-validate: verify all edge IDs exist in this product\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const pool = (store as any).pool as import('pg').Pool\n\n // Quick existence check outside the transaction\n for (let i = 0; i < edgeIds.length; i++) {\n const { rows } = await pool.query(\n `SELECT id FROM upg.edges WHERE id = $1 AND product_id = $2`,\n [edgeIds[i], productId],\n )\n if (rows.length === 0) {\n return textError(`Edge at index ${i}: \"${edgeIds[i]}\" not found in product \"${productId}\"`)\n }\n }\n\n const client = await pool.connect()\n\n try {\n await client.query('BEGIN')\n\n const deleted: string[] = []\n\n for (const eid of edgeIds) {\n await client.query(`DELETE FROM upg.edges WHERE id = $1`, [eid])\n await appendAudit(client, {\n productId, action: 'delete', entityType: 'edge', entityId: eid,\n })\n deleted.push(eid)\n }\n\n await client.query('COMMIT')\n for (const eid of deleted) store.emit(productId, 'edge.deleted', { id: eid })\n return text(JSON.stringify({ deleted, count: deleted.length }, null, 2))\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n } finally {\n client.release()\n }\n}\n\n// ─── batch_move_nodes ─────────────────────────────────────────────────────────\n\n/**\n * Re-parent up to 50 nodes in a single atomic transaction. For each move,\n * the old containment edge is deleted and a new containment edge to\n * `new_parent_id` is created (type inferred from parent→child types). The\n * transaction is rolled back entirely if any step fails.\n *\n * @returns JSON: `{ moved: [{ node_id, new_parent_id }], count }`.\n * @throws textError when `moves` is missing / non-array / empty / >50, or any\n * `node_id` / `new_parent_id` does not resolve.\n * @atomicity atomic-with-rollback (BEGIN / COMMIT / ROLLBACK).\n * @warning Heuristic deletion of \"old containment\" edges relies on LIKE\n * patterns (`%_contains_%`, `%_has_%`, `%_produces_%`) rather than the\n * canonical edge catalog. Edges matching the patterns yet semantically\n * non-containment may be removed alongside the intended ones. A follow-up\n * will tighten this to catalog-aware classification.\n * @see move_node\n * @see batch_create_edges\n * @see resolve_edge_for_pair\n */\nexport const batchMoveNodes: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n const moves = args.moves as Array<Record<string, unknown>> | undefined\n if (!moves || !Array.isArray(moves)) return textError('Missing required parameter: moves (array)')\n if (moves.length === 0) return textError('moves array is empty')\n if (moves.length > 50) return textError('Maximum 50 moves per batch')\n\n const productId = args.product_id as string\n\n // Pre-validate all moves before opening the transaction. Catalog-strict:\n // resolve each new containment edge up front and reject the whole batch on a\n // non-canonical pair; no `_contains_` fabrication.\n const resolvedMoveEdgeTypes: string[] = []\n for (let i = 0; i < moves.length; i++) {\n const m = moves[i]\n if (!m.node_id) return textError(`Move at index ${i}: missing required field \"node_id\"`)\n if (!m.new_parent_id) return textError(`Move at index ${i}: missing required field \"new_parent_id\"`)\n const node = await store.getNode(m.node_id as string)\n if (!node) return textError(`Move at index ${i}: node \"${m.node_id}\" not found`)\n const newParent = await store.getNode(m.new_parent_id as string)\n if (!newParent) return textError(`Move at index ${i}: new parent \"${m.new_parent_id}\" not found`)\n const inference = inferEdgeTypeWithTier(newParent.type, node.type)\n if (!inference.ok) {\n const suggestion = inference.suggestions.length > 0\n ? ` Suggestions: ${inference.suggestions.map((s) => `${s.source_type} → ${s.target_type} (${s.edge_type})`).join('; ')}.`\n : ''\n return textError(`Move at index ${i}: no canonical edge type for ${newParent.type} → ${node.type}.${suggestion} Reparenting refused.`)\n }\n resolvedMoveEdgeTypes.push(inference.edgeType)\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const pool = (store as any).pool as import('pg').Pool\n const client = await pool.connect()\n\n try {\n await client.query('BEGIN')\n\n const moved: Array<{ node_id: string; new_parent_id: string }> = []\n\n for (let i = 0; i < moves.length; i++) {\n const m = moves[i]\n const nid = m.node_id as string\n const newParentId = m.new_parent_id as string\n\n // Existence re-check inside the tx for consistency; the edge type was\n // resolved catalog-strict in the pre-validate pass above.\n const { rows: nodeRows } = await client.query(\n `SELECT type FROM upg.nodes WHERE id = $1 AND product_id = $2`,\n [nid, productId],\n )\n const { rows: parentRows } = await client.query(\n `SELECT type FROM upg.nodes WHERE id = $1 AND product_id = $2`,\n [newParentId, productId],\n )\n\n if (nodeRows.length === 0) throw new Error(`Node not found in transaction: ${nid}`)\n if (parentRows.length === 0) throw new Error(`New parent not found in transaction: ${newParentId}`)\n\n const newEdgeType = resolvedMoveEdgeTypes[i]\n\n // Delete ALL existing containment edges targeting this node from within\n // this product. \"Containment\" edges are those whose target is this node.\n // We delete based on the inferred edge type pattern: edges where this\n // node is the target, scoped to the product.\n await client.query(\n `DELETE FROM upg.edges\n WHERE product_id = $1 AND target = $2\n AND type LIKE '%_contains_%'\n OR (product_id = $1 AND target = $2 AND type LIKE '%_has_%')\n OR (product_id = $1 AND target = $2 AND type LIKE '%_produces_%')`,\n [productId, nid],\n )\n\n // Create the new containment edge to the new parent\n const eid = edgeId()\n await client.query(\n `INSERT INTO upg.edges (id, product_id, source, target, type)\n VALUES ($1, $2, $3, $4, $5)`,\n [eid, productId, newParentId, nid, newEdgeType],\n )\n await appendAudit(client, {\n productId, action: 'update', entityType: 'node', entityId: nid,\n changes: { new_parent_id: newParentId, edge_type: newEdgeType },\n })\n\n moved.push({ node_id: nid, new_parent_id: newParentId })\n }\n\n await client.query('COMMIT')\n for (const m of moved) store.emit(productId, 'node.updated', { id: m.node_id, new_parent_id: m.new_parent_id })\n return text(JSON.stringify({ moved, count: moved.length }, null, 2))\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n } finally {\n client.release()\n }\n}\n","/**\n * `validate_graph`: schema-drift detection over a product's Postgres graph.\n *\n * Three drift surfaces:\n * 1. Entity-type drift (nodes outside UPG_TYPES_SET).\n * 2. Edge-type drift (edges outside UPG_EDGE_CATALOG).\n * 3. Property drift (sampled over 500 nodes).\n *\n * Read-only. FK constraints handle dangling-edge enforcement at the database.\n * Pair with `migrate_type` and `rename_edge_type` for remediation.\n */\n\nimport {\n UPG_TYPES_SET,\n UPG_EDGE_CATALOG,\n UPG_PROPERTY_SCHEMA,\n UPG_ENTITY_META_BY_NAME,\n} from '@unified-product-graph/core'\nimport { type ToolHandler, text, textError } from '../lib/server-context.js'\n\n/** All valid edge type strings, derived from the edge catalog keys. */\nconst VALID_EDGE_TYPES: ReadonlySet<string> = new Set(Object.keys(UPG_EDGE_CATALOG))\n\n/** For each entity type, the list of property keys defined in the schema. */\nconst SCHEMA_PROPERTIES_BY_TYPE: ReadonlyMap<string, string[]> = buildSchemaPropertiesMap()\n\nfunction buildSchemaPropertiesMap(): Map<string, string[]> {\n const m = new Map<string, string[]>()\n for (const [entityType, schema] of Object.entries(UPG_PROPERTY_SCHEMA)) {\n const keys = Object.keys(schema)\n if (keys.length > 0) m.set(entityType, keys)\n }\n return m\n}\n\n/** Suggest a canonical replacement for an unknown entity type, if one exists. */\nfunction suggestMigration(unknownType: string): string | null {\n const meta = UPG_ENTITY_META_BY_NAME.get(unknownType)\n if (meta?.replacement) return meta.replacement\n return null\n}\n\n// ─── Internal row types for SQL results ───────────────────────────────────────\n\ninterface EntityTypeDriftRow { type: string; count: string }\ninterface EdgeTypeDriftRow { type: string; count: string }\ninterface CountRow { count: string }\ninterface NodeSampleRow { type: string; id: string; data: Record<string, unknown> | null }\n\n// ─── Pool accessor ───────────────────────────────────────────────────────────\n\ninterface PoolLike {\n query<T = unknown>(sql: string, values?: unknown[]): Promise<{ rows: T[] }>\n}\n\nfunction getPool(store: object): PoolLike {\n return (store as unknown as { pool: PoolLike }).pool\n}\n\n/**\n * Validate a product graph for schema drift. Detects entity type drift,\n * edge type drift, and property drift (sampled over 500 nodes). Postgres\n * FK constraints enforce endpoint existence, so intra-product edges stay\n * tied to live endpoints.\n *\n * @returns JSON: `{ valid, product_id, summary, entity_type_drift,\n * edge_type_drift, property_drift, notes }`.\n * @throws textError when `product_id` is missing or the product\n * is not visible to the caller.\n * @atomicity atomic (read-only)\n * @warning **Property drift is sampled** (first 500 nodes by id order);\n * for products beyond 500 nodes the drift list is incomplete. Each\n * reported type carries one example node id; run again or query\n * `list_nodes` for full coverage.\n * @see migrate_type\n * @see migrate_cross_edges\n * @see rename_edge_type\n * @see list_anti_patterns\n * @see list_type_migrations\n * @see list_edge_migrations\n * @see inspect\n */\nexport const validateGraph: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n const productId = args.product_id as string\n\n // Verify the product exists first.\n try {\n await store.getProduct(productId)\n } catch {\n return textError(`Product not found: ${productId}`)\n }\n\n const pool = getPool(store)\n\n // ── 1. Total counts ──────────────────────────────────────────────────────────\n\n const [{ rows: nodeCountRows }, { rows: edgeCountRows }] = await Promise.all([\n pool.query<CountRow>(`SELECT COUNT(*)::text AS count FROM upg.nodes WHERE product_id = $1`, [productId]),\n pool.query<CountRow>(`SELECT COUNT(*)::text AS count FROM upg.edges WHERE product_id = $1`, [productId]),\n ])\n const totalNodes = parseInt(nodeCountRows[0].count, 10)\n const totalEdges = parseInt(edgeCountRows[0].count, 10)\n\n // ── 2. Entity type drift ─────────────────────────────────────────────────────\n // SQL: select types not in the canonical set, grouped and counted.\n const validTypeLiterals = [...UPG_TYPES_SET].map((t) => `'${t.replace(/'/g, \"''\")}'`).join(', ')\n\n const { rows: entityDriftRows } = await pool.query<EntityTypeDriftRow>(\n `SELECT type, COUNT(*)::text AS count\n FROM upg.nodes\n WHERE product_id = $1\n AND type NOT IN (${validTypeLiterals})\n GROUP BY type\n ORDER BY count DESC`,\n [productId],\n )\n\n const entityTypeDrift = entityDriftRows.map((row) => ({\n type: row.type,\n count: parseInt(row.count, 10),\n suggested_migration: suggestMigration(row.type),\n }))\n\n // ── 3. Edge type drift ───────────────────────────────────────────────────────\n const validEdgeLiterals = [...VALID_EDGE_TYPES].map((t) => `'${t.replace(/'/g, \"''\")}'`).join(', ')\n\n const { rows: edgeDriftRows } = await pool.query<EdgeTypeDriftRow>(\n `SELECT type, COUNT(*)::text AS count\n FROM upg.edges\n WHERE product_id = $1\n AND type NOT IN (${validEdgeLiterals})\n GROUP BY type\n ORDER BY count DESC`,\n [productId],\n )\n\n const edgeTypeDrift = edgeDriftRows.map((row) => ({\n type: row.type,\n count: parseInt(row.count, 10),\n }))\n\n // ── 4. Property drift (sampled) ──────────────────────────────────────────────\n // Sample up to 500 nodes. For each typed node that has a property schema,\n // check which schema fields are absent from the node's stored properties.\n // Report one example per entity type (first mismatch found).\n\n const { rows: sampleRows } = await pool.query<NodeSampleRow>(\n `SELECT type, id, data\n FROM upg.nodes\n WHERE product_id = $1\n LIMIT 500`,\n [productId],\n )\n\n const propertyDriftByType = new Map<string, { missingFields: string[]; exampleNodeId: string }>()\n\n for (const row of sampleRows) {\n if (propertyDriftByType.has(row.type)) continue // already have an example for this type\n const schemaFields = SCHEMA_PROPERTIES_BY_TYPE.get(row.type)\n if (!schemaFields) continue // no property schema for this entity type\n\n const nodeProperties = row.data ?? {}\n const missingFields = schemaFields.filter((field) => !(field in nodeProperties))\n if (missingFields.length > 0) {\n propertyDriftByType.set(row.type, {\n missingFields,\n exampleNodeId: row.id,\n })\n }\n }\n\n const propertyDrift = [...propertyDriftByType.entries()].map(([entityType, info]) => ({\n entity_type: entityType,\n missing_fields: info.missingFields,\n example_node_id: info.exampleNodeId,\n }))\n\n // ── Build result ─────────────────────────────────────────────────────────────\n\n const unknownTypeNodes = entityTypeDrift.reduce((sum, e) => sum + e.count, 0)\n const unknownTypeEdges = edgeTypeDrift.reduce((sum, e) => sum + e.count, 0)\n const valid = unknownTypeNodes === 0 && unknownTypeEdges === 0 && propertyDrift.length === 0\n\n return text(JSON.stringify({\n valid,\n product_id: productId,\n summary: {\n total_nodes: totalNodes,\n total_edges: totalEdges,\n unknown_type_nodes: unknownTypeNodes,\n unknown_type_edges: unknownTypeEdges,\n property_drift_types: propertyDrift.length,\n },\n entity_type_drift: entityTypeDrift,\n edge_type_drift: edgeTypeDrift,\n property_drift: propertyDrift,\n notes: [\n 'Endpoint-existence checks are enforced by Postgres FK constraints, so intra-product edges always have live endpoints.',\n 'Property drift is sampled (first 500 nodes). Run again for full coverage on large graphs.',\n ],\n }, null, 2))\n}\n","/**\n * Catalog-aware migration tools. Two handlers, both with `dry_run: true` default.\n * - `migrate_type`: bulk-retype nodes, then re-infer affected edge types.\n * - `migrate_cross_edges`: move cross-product edges from `upg.edges` to\n * `upg.cross_product_edges`.\n */\n\nimport {\n UPG_TYPES_SET,\n UPG_CROSS_EDGE_TYPES,\n resolveContainmentEdge,\n} from '@unified-product-graph/core'\nimport { nanoid } from 'nanoid'\nimport { type ToolHandler, text, textError } from '../lib/server-context.js'\n\n// ─── Pool accessor (mirrors validation.ts pattern) ────────────────────────────\n\ninterface PoolLike {\n query<T = unknown>(sql: string, values?: unknown[]): Promise<{ rows: T[] }>\n connect(): Promise<ClientLike>\n}\n\ninterface ClientLike {\n query<T = unknown>(sql: string, values?: unknown[]): Promise<{ rows: T[]; rowCount?: number | null }>\n release(): void\n}\n\nfunction getPool(store: object): PoolLike {\n return (store as unknown as { pool: PoolLike }).pool\n}\n\n// ─── Internal row types ───────────────────────────────────────────────────────\n\ninterface AffectedEdgeRow {\n id: string\n source: string\n target: string\n type: string\n source_type: string\n target_type: string\n}\n\ninterface CrossEdgeRow {\n id: string\n source: string\n target: string\n type: string\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\n/** Prefix for cross-product edge IDs generated by this tool. */\nfunction crossEdgeId(): string {\n return `ce_${nanoid(16)}`\n}\n\n/**\n * Migrate all nodes of one entity type to another within a product.\n * After renaming nodes, re-infers edge types for all edges connected to the\n * migrated nodes (because the correct edge type depends on the\n * source/target entity type pair).\n *\n * - `product_id`: Product to scope the migration to.\n * - `from_type`: Current entity type to migrate away from.\n * - `to_type`: Target entity type. Must be a valid UPG entity type.\n * - `dry_run` (default `true`): When true, counts affected rows without mutating.\n *\n * @returns JSON: `{ from_type, to_type, affected_nodes, retyped_edges, dry_run }`.\n * @throws textError when `product_id`, `from_type`, or `to_type`\n * is missing, or when `to_type` is not a known UPG entity type.\n * @atomicity atomic-with-rollback (false only)\n * @warning Default is `dry_run: true`; pass `dry_run: false` to commit.\n * Idempotent on retry: a second `dry_run: false` finds zero `from_type`\n * nodes and reports `affected_nodes: 0`. Edge re-inference uses\n * `resolveContainmentEdge`, so already-canonical edges may change type\n * when the new pair has a different canonical edge.\n * @see validate_graph\n * @see migrate_cross_edges\n * @see rename_edge_type\n * @see list_type_migrations\n * @see list_entity_types\n */\nexport const migrateType: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n if (!args.from_type) return textError('Missing required parameter: from_type')\n if (!args.to_type) return textError('Missing required parameter: to_type')\n\n const productId = args.product_id as string\n const fromType = args.from_type as string\n const toType = args.to_type as string\n const dryRun = (args.dry_run as boolean | undefined) ?? true\n\n // Validate to_type is a known UPG entity type.\n if (!UPG_TYPES_SET.has(toType)) {\n return textError(\n `Unknown entity type: \"${toType}\". ` +\n `Run get_entity_schema or validate_graph to inspect valid types.`,\n )\n }\n\n const pool = getPool(store)\n\n if (dryRun) {\n const { rows } = await pool.query<{ count: string }>(\n `SELECT COUNT(*)::text AS count FROM upg.nodes WHERE product_id = $1 AND type = $2`,\n [productId, fromType],\n )\n const affected = parseInt(rows[0]?.count ?? '0', 10)\n return text(JSON.stringify({\n from_type: fromType,\n to_type: toType,\n affected_nodes: affected,\n dry_run: true,\n }, null, 2))\n }\n\n // Apply mode: run inside a transaction.\n const client = await pool.connect()\n try {\n await client.query('BEGIN')\n\n // 1. Retype nodes.\n const { rowCount: updatedNodes } = await client.query(\n `UPDATE upg.nodes SET type = $1 WHERE product_id = $2 AND type = $3`,\n [toType, productId, fromType],\n )\n const affectedNodes = updatedNodes ?? 0\n\n // 2. Re-infer edge types for all edges connected to migrated nodes.\n // Run AFTER the node UPDATE so source_type/target_type reflect the new type.\n const { rows: affectedEdges } = await client.query<AffectedEdgeRow>(\n `SELECT e.id, e.source, e.target, e.type,\n ns.type AS source_type, nt.type AS target_type\n FROM upg.edges e\n JOIN upg.nodes ns ON ns.id = e.source\n JOIN upg.nodes nt ON nt.id = e.target\n WHERE e.product_id = $1\n AND (ns.id IN (SELECT id FROM upg.nodes WHERE product_id = $1 AND type = $2)\n OR nt.id IN (SELECT id FROM upg.nodes WHERE product_id = $1 AND type = $2))`,\n [productId, toType],\n )\n\n let retypedEdges = 0\n for (const edge of affectedEdges) {\n const newType = resolveContainmentEdge(edge.source_type, edge.target_type)\n if (newType && newType !== edge.type) {\n await client.query(\n `UPDATE upg.edges SET type = $1 WHERE id = $2`,\n [newType, edge.id],\n )\n retypedEdges++\n }\n }\n\n await client.query('COMMIT')\n\n return text(JSON.stringify({\n from_type: fromType,\n to_type: toType,\n affected_nodes: affectedNodes,\n retyped_edges: retypedEdges,\n dry_run: false,\n }, null, 2))\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n } finally {\n client.release()\n }\n}\n\n/**\n * Find edges in `upg.edges` that carry a cross-product edge type and move\n * them to `upg.cross_product_edges`. Cross-product edge types belong in\n * the cross-product table; this tool corrects data that predates the\n * tightening introduced in.\n *\n * - `product_id`: Product to scope the migration to.\n * - `dry_run` (default `true`): When true, reports what would move without moving it.\n *\n * @returns JSON: `{ product_id, migrated, count, dry_run }`.\n * @throws textError when `product_id` is missing.\n * @atomicity atomic-with-rollback (false only)\n * @warning Default is `dry_run: true`; pass `dry_run: false` to commit.\n * Idempotent on retry: a second `dry_run: false` finds zero matching\n * intra-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\n * the trail.\n * @see list_cross_edge_types\n * @see list_portfolio_cross_edges\n * @see validate_graph\n * @see migrate_type\n */\nexport const migrateCrossEdges: ToolHandler = async (args, { store }) => {\n if (!args.product_id) return textError('Missing required parameter: product_id')\n\n const productId = args.product_id as string\n const dryRun = (args.dry_run as boolean | undefined) ?? true\n\n const pool = getPool(store)\n const crossEdgeTypes = Array.from(UPG_CROSS_EDGE_TYPES)\n\n if (dryRun) {\n const { rows } = await pool.query<CrossEdgeRow>(\n `SELECT id, source, target, type\n FROM upg.edges\n WHERE product_id = $1\n AND type = ANY($2::text[])`,\n [productId, crossEdgeTypes],\n )\n return text(JSON.stringify({\n product_id: productId,\n migrated: rows,\n count: rows.length,\n dry_run: true,\n }, null, 2))\n }\n\n // Apply mode: run inside a transaction.\n const client = await pool.connect()\n try {\n await client.query('BEGIN')\n\n // 1. Select the edges to migrate.\n const { rows: edgesToMigrate } = await client.query<CrossEdgeRow>(\n `SELECT id, source, target, type\n FROM upg.edges\n WHERE product_id = $1\n AND type = ANY($2::text[])`,\n [productId, crossEdgeTypes],\n )\n\n // 2. Insert into cross_product_edges.\n for (const edge of edgesToMigrate) {\n await client.query(\n `INSERT INTO upg.cross_product_edges (id, source, target, type, created_by_product_id)\n VALUES ($1, $2, $3, $4, $5)`,\n [crossEdgeId(), edge.source, edge.target, edge.type, productId],\n )\n }\n\n // 3. Delete from upg.edges.\n if (edgesToMigrate.length > 0) {\n const ids = edgesToMigrate.map((e) => e.id)\n await client.query(\n `DELETE FROM upg.edges WHERE id = ANY($1::text[])`,\n [ids],\n )\n }\n\n await client.query('COMMIT')\n\n return text(JSON.stringify({\n product_id: productId,\n migrated: edgesToMigrate,\n count: edgesToMigrate.length,\n dry_run: false,\n }, null, 2))\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n } finally {\n client.release()\n }\n}\n","/**\n * Tool registry for the UPG cloud server. Maps each tool name to its\n * wire-level definition and handler. `server.ts` dispatches by name lookup.\n */\n\nimport type { ToolBinding, ToolDefinition } from '@unified-product-graph/mcp-tooling'\nimport type { CloudContext } from './server-context.js'\nimport { listProducts, createProduct, getAuditLog } from '../tools/products.js'\nimport { getProductContext, getGraphDigest, query, getChanges } from '../tools/context.js'\nimport {\n listNodes, getNode, getNodes, searchNodes,\n createNode, updateNode, deleteNode, getProductGraph, moveNode,\n deduplicateNodes, exportUpgDocument,\n} from '../tools/nodes.js'\nimport { createEdge, deleteEdge, exportEdges, renameEdgeType } from '../tools/edges.js'\nimport { applyFramework, scoreEntity } from '../tools/frameworks.js'\nimport { listProductAreas, getAreaGraph, createArea, getAreaContext } from '../tools/areas.js'\nimport { getEntitySchema } from '../tools/schema.js'\nimport { addComment, listComments, grantAccess, listCollaborators } from '../tools/collaboration.js'\nimport { getGraphAnalytics } from '../tools/analytics.js'\nimport { registerWebhook, listWebhooks, removeWebhook } from '../tools/webhooks.js'\nimport {\n listPlaybooks, getPlaybook,\n listApproaches, getApproach,\n plan, inspect, prioritise, trace, reflect,\n listDomains, getDomainGuide,\n listFrameworks, getFramework,\n listEdgeTypes, getEdgeType,\n listRegions, getRegion, getRegionForEntity,\n getSpecVersion, resolveEdgeForPair, listCrossEdgeTypes,\n listLenses, getLensTool,\n listTypeLabels, getTypeLabel, getValidChildrenTool,\n listEntityTypes, getEntityMeta,\n listAntiPatterns, getAntiPattern,\n listBenchmarks, listProductStages,\n // Spec catalogues (migrations, lifecycles, scales, framework metadata, domain rings)\n listTypeMigrations, listEdgeMigrations, listSplitMigrations,\n listLifecycles, getLifecycle,\n listScales, getScale,\n listFrameworkCategories, listFrameworkStructurePatterns,\n listDomainRings, getDomainRing,\n} from '../tools/spec.js'\nimport { listPortfolios, listPortfolioCrossEdges, createCrossProductEdge, repairDanglingEdges } from '../tools/portfolio.js'\nimport {\n batchCreateNodes, batchUpdateNodes, batchDeleteNodes,\n batchCreateEdges, batchDeleteEdges, batchMoveNodes,\n} from '../tools/batch.js'\nimport { validateGraph } from '../tools/validation.js'\nimport { migrateType, migrateCrossEdges } from '../tools/migrations.js'\n\n/** Wire-shape definitions only, passed to `tools/list`. */\nexport const TOOL_DEFINITIONS: ToolDefinition[] = [\n {\n \"name\": \"list_products\",\n \"description\": \"List all products in this UPG cloud instance.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {}\n }\n },\n {\n \"name\": \"create_product\",\n \"description\": \"Create a new product graph.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"title\": {\n \"type\": \"string\",\n \"description\": \"Product name\"\n },\n \"description\": {\n \"type\": \"string\",\n \"description\": \"Optional description\"\n },\n \"stage\": {\n \"type\": \"string\",\n \"description\": \"idea | mvp | growth | scale\"\n }\n },\n \"required\": [\n \"title\"\n ]\n }\n },\n {\n \"name\": \"get_product_context\",\n \"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.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n }\n },\n \"required\": [\n \"product_id\"\n ]\n }\n },\n {\n \"name\": \"list_nodes\",\n \"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.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n },\n \"type\": {\n \"type\": \"string\",\n \"description\": \"Filter by entity type\"\n },\n \"limit\": {\n \"type\": \"number\",\n \"description\": \"Max results (default 1000, max 10000)\"\n },\n \"cursor\": {\n \"type\": \"string\",\n \"description\": \"Opaque pagination cursor; pass next_cursor from a previous response to advance.\"\n },\n \"offset\": {\n \"type\": \"number\",\n \"description\": \"Legacy: skip N results (default 0). Use cursor instead for new callers.\"\n }\n },\n \"required\": [\n \"product_id\"\n ]\n }\n },\n {\n \"name\": \"export_upg_document\",\n \"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.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n },\n \"limit\": {\n \"type\": \"number\",\n \"description\": \"Max nodes per page (default 1000, max 10000)\"\n },\n \"cursor\": {\n \"type\": \"string\",\n \"description\": \"Opaque pagination cursor; pass next_cursor from a previous response to advance.\"\n }\n },\n \"required\": [\n \"product_id\"\n ]\n }\n },\n {\n \"name\": \"get_node\",\n \"description\": \"Get a single entity by ID with its full properties and all connected edges.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"node_id\": {\n \"type\": \"string\",\n \"description\": \"The node ID\"\n }\n },\n \"required\": [\n \"node_id\"\n ]\n }\n },\n {\n \"name\": \"search_nodes\",\n \"description\": \"Full-text search across node titles and descriptions. Title matches rank higher.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n },\n \"query\": {\n \"type\": \"string\",\n \"description\": \"Search text\"\n },\n \"type\": {\n \"type\": \"string\",\n \"description\": \"Optional type filter\"\n },\n \"limit\": {\n \"type\": \"number\",\n \"description\": \"Max results (default 20)\"\n }\n },\n \"required\": [\n \"product_id\",\n \"query\"\n ]\n }\n },\n {\n \"name\": \"create_node\",\n \"description\": \"Create a new entity in the graph. Optionally connect it to a parent node.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n },\n \"type\": {\n \"type\": \"string\",\n \"description\": \"UPG entity type (e.g. \\\"persona\\\", \\\"opportunity\\\")\"\n },\n \"title\": {\n \"type\": \"string\",\n \"description\": \"Entity title\"\n },\n \"description\": {\n \"type\": \"string\",\n \"description\": \"Optional description\"\n },\n \"tags\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Freeform tags\"\n },\n \"status\": {\n \"type\": \"string\",\n \"description\": \"Lifecycle status\"\n },\n \"properties\": {\n \"type\": \"object\",\n \"description\": \"Type-specific fields\"\n },\n \"parent_id\": {\n \"type\": \"string\",\n \"description\": \"Parent node ID; creates an edge automatically\"\n }\n },\n \"required\": [\n \"product_id\",\n \"type\",\n \"title\"\n ]\n }\n },\n {\n \"name\": \"update_node\",\n \"description\": \"Update an existing entity. Unspecified fields are preserved.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"node_id\": {\n \"type\": \"string\",\n \"description\": \"The node ID to update\"\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"description\": {\n \"type\": \"string\"\n },\n \"tags\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n }\n },\n \"status\": {\n \"type\": \"string\"\n },\n \"properties\": {\n \"type\": \"object\",\n \"description\": \"Merged with existing properties\"\n }\n },\n \"required\": [\n \"node_id\"\n ]\n }\n },\n {\n \"name\": \"delete_node\",\n \"description\": \"Remove an entity and all its connected edges from the graph.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"node_id\": {\n \"type\": \"string\",\n \"description\": \"The node ID to delete\"\n }\n },\n \"required\": [\n \"node_id\"\n ]\n }\n },\n {\n \"name\": \"create_edge\",\n \"description\": \"Create a relationship between two nodes. Edge type is auto-inferred if omitted.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"source_id\": {\n \"type\": \"string\",\n \"description\": \"Source node ID\"\n },\n \"target_id\": {\n \"type\": \"string\",\n \"description\": \"Target node ID\"\n },\n \"type\": {\n \"type\": \"string\",\n \"description\": \"Edge type; auto-inferred if omitted\"\n }\n },\n \"required\": [\n \"source_id\",\n \"target_id\"\n ]\n }\n },\n {\n \"name\": \"delete_edge\",\n \"description\": \"Remove a relationship between two nodes.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"edge_id\": {\n \"type\": \"string\",\n \"description\": \"The edge ID to delete\"\n }\n },\n \"required\": [\n \"edge_id\"\n ]\n }\n },\n {\n \"name\": \"export_edges\",\n \"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.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": { \"type\": \"string\", \"description\": \"Product ID\" },\n \"types\": { \"type\": \"array\", \"items\": { \"type\": \"string\" }, \"description\": \"Optional edge type filter\" }\n },\n \"required\": [\"product_id\"]\n }\n },\n {\n \"name\": \"rename_edge_type\",\n \"description\": \"Rename all edges of one type to another across a product. dry_run (default: true) previews the count.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": { \"type\": \"string\", \"description\": \"Product ID\" },\n \"from\": { \"type\": \"string\", \"description\": \"Current edge type\" },\n \"to\": { \"type\": \"string\", \"description\": \"New edge type\" },\n \"dry_run\": { \"type\": \"boolean\", \"description\": \"If true, only count (default: true); pass false to apply.\" }\n },\n \"required\": [\"product_id\", \"from\", \"to\"]\n }\n },\n {\n \"name\": \"get_product_graph\",\n \"description\": \"Export the full graph for a product (all nodes + edges).\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n }\n },\n \"required\": [\n \"product_id\"\n ]\n }\n },\n {\n \"name\": \"get_audit_log\",\n \"description\": \"Get recent changes (audit log) for a product.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n },\n \"limit\": {\n \"type\": \"number\",\n \"description\": \"Max entries (default 50)\"\n }\n },\n \"required\": [\n \"product_id\"\n ]\n }\n },\n {\n \"name\": \"add_comment\",\n \"description\": \"Add a comment on a node in the graph.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n },\n \"node_id\": {\n \"type\": \"string\",\n \"description\": \"Node to comment on\"\n },\n \"user_id\": {\n \"type\": \"string\",\n \"description\": \"Author user ID\"\n },\n \"body\": {\n \"type\": \"string\",\n \"description\": \"Comment text\"\n }\n },\n \"required\": [\n \"product_id\",\n \"node_id\",\n \"user_id\",\n \"body\"\n ]\n }\n },\n {\n \"name\": \"list_comments\",\n \"description\": \"List comments on a node, newest first.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"node_id\": {\n \"type\": \"string\",\n \"description\": \"Node ID\"\n }\n },\n \"required\": [\n \"node_id\"\n ]\n }\n },\n {\n \"name\": \"grant_access\",\n \"description\": \"Grant or update a user's role on a product (owner, editor, viewer).\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n },\n \"user_id\": {\n \"type\": \"string\",\n \"description\": \"User to grant access to\"\n },\n \"role\": {\n \"type\": \"string\",\n \"description\": \"Role: owner | editor | viewer\",\n \"enum\": [\n \"owner\",\n \"editor\",\n \"viewer\"\n ]\n }\n },\n \"required\": [\n \"product_id\",\n \"user_id\",\n \"role\"\n ]\n }\n },\n {\n \"name\": \"list_collaborators\",\n \"description\": \"List all collaborators and their roles for a product.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n }\n },\n \"required\": [\n \"product_id\"\n ]\n }\n },\n {\n \"name\": \"list_product_areas\",\n \"description\": \"List all product areas in a product. Product areas are top-level organizational units within a product.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n }\n },\n \"required\": [\n \"product_id\"\n ]\n }\n },\n {\n \"name\": \"get_area_graph\",\n \"description\": \"Get all entities and edges that belong to a product area. Returns the sub-graph scoped to that area.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n },\n \"area_id\": {\n \"type\": \"string\",\n \"description\": \"The product area node ID\"\n },\n \"depth\": {\n \"type\": \"number\",\n \"description\": \"How many levels deep to traverse (default 3, max 10)\"\n }\n },\n \"required\": [\n \"product_id\",\n \"area_id\"\n ]\n }\n },\n {\n \"name\": \"get_graph_analytics\",\n \"description\": \"Computed product thinking metrics: hypothesis velocity, persona coverage ratio, evidence density, stale entity rate, orphan rate.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n }\n },\n \"required\": [\n \"product_id\"\n ]\n }\n },\n {\n \"name\": \"register_webhook\",\n \"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.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n },\n \"event\": {\n \"type\": \"string\",\n \"description\": \"Event name (e.g. node.created, node.updated, node.deleted, edge.created, edge.deleted)\"\n },\n \"url\": {\n \"type\": \"string\",\n \"description\": \"Webhook URL to POST to\"\n },\n \"secret\": {\n \"type\": \"string\",\n \"description\": \"Optional shared secret for HMAC signature verification\"\n }\n },\n \"required\": [\n \"product_id\",\n \"event\",\n \"url\"\n ]\n }\n },\n {\n \"name\": \"list_webhooks\",\n \"description\": \"List all registered webhooks for a product.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n }\n },\n \"required\": [\n \"product_id\"\n ]\n }\n },\n {\n \"name\": \"remove_webhook\",\n \"description\": \"Remove a registered webhook by ID.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"webhook_id\": {\n \"type\": \"string\",\n \"description\": \"Webhook ID to remove\"\n }\n },\n \"required\": [\n \"webhook_id\"\n ]\n }\n },\n {\n \"name\": \"query\",\n \"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.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"The product ID\"\n },\n \"from\": {\n \"type\": \"string\",\n \"description\": \"Start from all nodes of this type\"\n },\n \"from_id\": {\n \"type\": \"string\",\n \"description\": \"Start from a specific node ID\"\n },\n \"traverse\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Edge types to follow per level. Prefix with ! to exclude.\"\n },\n \"depth\": {\n \"type\": \"number\",\n \"description\": \"Max depth (default 3, max 10)\"\n },\n \"include\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Node fields: \\\"title\\\", \\\"status\\\", \\\"tags\\\", \\\"description\\\", \\\"properties\\\"\"\n },\n \"limit\": {\n \"type\": \"number\",\n \"description\": \"Max nodes (default 200, max 1000)\"\n },\n \"edge_include\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Edge fields to return. Empty = no edges.\"\n }\n },\n \"required\": [\n \"product_id\"\n ]\n }\n },\n {\n \"name\": \"get_graph_digest\",\n \"description\": \"Pre-computed graph analytics: counts, health metrics, chain completeness, business area coverage, lifecycle balance. ~500 tokens vs ~5-8K for equivalent manual fetches.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"The product ID\"\n }\n },\n \"required\": [\n \"product_id\"\n ]\n }\n },\n {\n \"name\": \"get_nodes\",\n \"description\": \"Batch-fetch multiple entities by ID with edges. More efficient than multiple get_node calls.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"The product ID\"\n },\n \"ids\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"Node IDs to fetch (max 50)\"\n },\n \"compact_edges\": {\n \"type\": \"boolean\",\n \"description\": \"Omit titles from edges\"\n }\n },\n \"required\": [\n \"product_id\",\n \"ids\"\n ]\n }\n },\n {\n \"name\": \"get_changes\",\n \"description\": \"Get a log of recent changes from the audit log.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"The product ID\"\n },\n \"since\": {\n \"type\": \"string\",\n \"description\": \"ISO 8601 timestamp; only return changes after this time\"\n },\n \"limit\": {\n \"type\": \"number\",\n \"description\": \"Max results (default 50)\"\n }\n },\n \"required\": [\n \"product_id\"\n ]\n }\n },\n {\n \"name\": \"get_entity_schema\",\n \"description\": \"Returns the schema for a UPG entity type: valid parent→child edges, properties, lifecycle phases.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"type\": {\n \"type\": \"string\",\n \"description\": \"The UPG entity type (e.g. \\\"feature\\\", \\\"persona\\\")\"\n }\n },\n \"required\": [\n \"type\"\n ]\n }\n },\n // ── Spec introspection round 1 ───────────────\n {\n name: 'list_playbooks',\n description:\n '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).',\n inputSchema: {\n type: 'object' as const,\n properties: {\n region: { type: 'string', description: 'Exact-match UPGRegionId (e.g. \"users_needs\", \"business_gtm_growth\").' },\n canonical_only: { type: 'boolean', description: 'When true, return only the canonical playbook per region (W1 invariant restated).' },\n framework_id: { type: 'string', description: 'Exact-match UPGFramework.id (e.g. \"business-model-canvas\", \"pirate-metrics-aarrr\").' },\n },\n },\n },\n {\n name: 'get_playbook',\n description:\n 'Return one canonical UPGPlaybook by id (e.g. \"playbook:strategy-outcomes\", \"playbook:business-gtm-growth\"). 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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n id: { type: 'string', description: 'Playbook id (namespace-prefixed: playbook:*).' },\n },\n required: ['id'],\n },\n },\n {\n name: 'list_approaches',\n description:\n '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).',\n inputSchema: {\n type: 'object' as const,\n properties: {\n framework_id: { type: 'string', description: 'Exact-match framework id; narrows to approaches whose framework_id_examples include it (discoverability surface; full reverse lookup is on UPGFramework.approach_ids).' },\n },\n },\n },\n {\n name: 'get_approach',\n description:\n '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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n id: { type: 'string', description: 'Approach id, one of: plan, inspect, prioritise, trace, reflect.', enum: ['plan', 'inspect', 'prioritise', 'trace', 'reflect'] },\n },\n required: ['id'],\n },\n },\n {\n name: 'plan',\n description:\n '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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n region: { type: 'string', description: 'Optional UPGRegionId; narrows planning scope to a single region (e.g. \"users_needs\", \"business_gtm_growth\"). Omit for whole-graph planning.' },\n },\n },\n },\n {\n name: 'inspect',\n description:\n '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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n region: { type: 'string', description: 'Optional UPGRegionId; narrows inspection scope to a single region.' },\n entities: { type: 'array', items: { type: 'string' }, description: 'Optional entity_id[]; narrows inspection scope to a specific candidate set. Mutually composable with region.' },\n },\n },\n },\n {\n name: 'prioritise',\n description:\n '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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n candidates: { type: 'array', items: { type: 'string' }, description: 'Required: entity_id[] to rank.' },\n framework_id: { type: 'string', description: 'Required: UPGFramework.id of the scoring lens (e.g. \"rice-scoring\", \"ice-scoring\", \"kano-model\", \"cost-of-delay\", \"wsjf\").' },\n },\n required: ['candidates', 'framework_id'],\n },\n },\n {\n name: 'trace',\n description:\n '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\".',\n inputSchema: {\n type: 'object' as const,\n properties: {\n anchor: { type: 'string', description: 'Required: entity_id where the traversal starts.' },\n path: { type: 'array', items: { type: 'string' }, description: 'Required: UPGEntityType[] type-shorthand path. Each step walks via the canonical edge for the source→target pair.' },\n edges_override: { type: 'array', items: { type: ['string', 'null'] }, description: 'Optional per-hop edge override array. Length must match path length; element null means \"use canonical edge for this pair\".' },\n },\n required: ['anchor', 'path'],\n },\n },\n {\n name: 'reflect',\n description:\n '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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n scope: { type: ['string', 'null'], description: 'Optional: region id, entity id, or null for whole-graph.' },\n mode: { type: 'string', description: 'Optional: one of assumptions, alternatives, blind-spots, load-bearing. Omit for open reflection.', enum: ['assumptions', 'alternatives', 'blind-spots', 'load-bearing'] },\n },\n },\n },\n {\n name: 'list_domains',\n description:\n '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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n with_guide_only: {\n type: 'boolean',\n description:\n '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).',\n },\n },\n },\n },\n {\n name: 'get_domain_guide',\n description:\n 'Return the full UPGDomainUsageGuide for a domain: anchor entity, creation sequence, named patterns (entity and edge chains), required cross-domain bridges, and anti-patterns.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n domain_id: { type: 'string', description: 'Canonical domain id (e.g. \"user\", \"market_intelligence\", \"growth\").' },\n },\n required: ['domain_id'],\n },\n },\n {\n name: 'list_frameworks',\n description:\n 'List the canonical UPGFramework definitions: the curated, famous product frameworks that anchor the public catalog. 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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n category: { type: 'string', description: 'Exact-match filter on UPGFramework.category (e.g. \"strategy\", \"prioritization\").' },\n limit: { type: 'number', description: 'Page size (default 50, max 200).' },\n cursor: { type: 'string', description: 'Opaque pagination cursor; pass next_cursor from a previous response.' },\n },\n },\n },\n {\n name: 'get_framework',\n description:\n 'Return one canonical UPGFramework by id (e.g. \"rice-scoring\", \"lean-canvas\"). Includes all four layers: data, structure, presentation, education.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n id: { type: 'string', description: 'Framework id (kebab-case).' },\n },\n required: ['id'],\n },\n },\n {\n name: 'list_edge_types',\n description:\n '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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n source_type: { type: 'string', description: 'Exact-match filter on UPGEdgeDefinition.source_type. Pass \"node\" to find polymorphic edges with a wildcard source.' },\n target_type: { type: 'string', description: 'Exact-match filter on UPGEdgeDefinition.target_type.' },\n },\n },\n },\n {\n name: 'get_edge_type',\n description:\n 'Return one canonical edge catalogue entry by edge type key (e.g. \"persona_pursues_job\", \"feature_addresses_need\").',\n inputSchema: {\n type: 'object' as const,\n properties: {\n type: { type: 'string', description: 'Edge type key from UPG_EDGE_CATALOG.' },\n },\n required: ['type'],\n },\n },\n // ── Spec introspection round 2 ─────────────────────────────────\n {\n name: 'list_regions',\n description:\n '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.',\n inputSchema: { type: 'object' as const, properties: {} },\n },\n {\n name: 'get_region',\n description:\n '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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n id: {\n type: 'string',\n description:\n 'Region id (e.g. \"strategy_outcomes\", \"users_needs\", \"product_delivery\"). See UPG_REGIONS for the full list of 10.',\n },\n },\n required: ['id'],\n },\n },\n {\n name: 'get_region_for_entity_type',\n description:\n '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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n entity_type: {\n type: 'string',\n description: 'Canonical entity type (e.g. \"persona\", \"feature\", \"metric\").',\n },\n },\n required: ['entity_type'],\n },\n },\n {\n name: 'get_spec_version',\n description:\n '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.',\n inputSchema: { type: 'object' as const, properties: {} },\n },\n {\n name: 'resolve_edge_for_pair',\n description:\n '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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n source_type: { type: 'string', description: 'Parent / source entity type.' },\n target_type: { type: 'string', description: 'Child / target entity type.' },\n },\n required: ['source_type', 'target_type'],\n },\n },\n {\n name: 'list_cross_edge_types',\n description:\n 'List the canonical cross-product edge types from UPG_CROSS_EDGE_TYPES (shares_persona, shares_competitor, shares_metric, depends_on_product, cannibalises, succeeds, hosts, contributes_to, instance_of). Portfolio-level relationships between entities in different products, separate from the within-product UPG_EDGE_CATALOG. instance_of (product entity to a canonical registry entity) is created via the registry tooling in the local MCP server.',\n inputSchema: { type: 'object' as const, properties: {} },\n },\n {\n name: 'list_lenses',\n description:\n '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.',\n inputSchema: { type: 'object' as const, properties: {} },\n },\n {\n name: 'get_lens',\n description:\n '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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n id: { type: 'string', description: 'Lens id (e.g. \"product\", \"ux_design\", \"full\").' },\n },\n required: ['id'],\n },\n },\n {\n name: 'list_type_labels',\n description:\n '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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n limit: { type: 'number', description: 'Page size (default 100, max 500).' },\n cursor: {\n type: 'string',\n description: 'Opaque pagination cursor; pass next_cursor from a previous response.',\n },\n },\n },\n },\n {\n name: 'get_type_label',\n description:\n '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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n entity_type: { type: 'string', description: 'Canonical entity type id.' },\n framework_id: {\n type: 'string',\n description: 'Optional framework id (e.g. \"lean_canvas\", \"ost\", \"design_thinking\"); when set, resolved_label uses the framework-specific label.',\n },\n designation: {\n type: 'string',\n description: 'Optional designation key (e.g. \"pain\", \"gap\", \"desire\") for types that use the designation pattern.',\n },\n },\n required: ['entity_type'],\n },\n },\n {\n name: 'get_valid_children',\n description:\n '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?\".',\n inputSchema: {\n type: 'object' as const,\n properties: {\n parent_type: { type: 'string', description: 'Canonical parent entity type.' },\n },\n required: ['parent_type'],\n },\n },\n // ── Spec introspection round 3 ─────────────────────────────────\n {\n name: 'list_entity_types',\n description:\n '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).',\n inputSchema: {\n type: 'object' as const,\n properties: {\n domain: { type: 'string', description: 'Exact-match atomic-domain id (e.g. \"user\", \"market_intelligence\").' },\n maturity: {\n type: 'string',\n enum: ['draft', 'proposed', 'stable', 'deprecated', 'removed'],\n description: 'Exact-match UPGEntityTypeMaturity.',\n },\n deprecated: {\n type: 'boolean',\n description: 'true → only deprecated types; false → exclude deprecated and removed types (the active set). Composes with maturity via AND.',\n },\n limit: { type: 'number', description: 'Page size (default 50, max 200).' },\n cursor: {\n type: 'string',\n description: 'Opaque pagination cursor; pass next_cursor from a previous response.',\n },\n },\n },\n },\n {\n name: 'get_entity_meta',\n description:\n '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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n name: { type: 'string', description: 'Canonical entity type name.' },\n },\n required: ['name'],\n },\n },\n {\n name: 'list_anti_patterns',\n description:\n '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).',\n inputSchema: {\n type: 'object' as const,\n properties: {\n severity: {\n type: 'string',\n enum: ['high', 'medium', 'low'],\n description: 'Exact-match UPGAntiPatternSeverity.',\n },\n stage: {\n type: 'string',\n enum: [\n 'concept', 'validation', 'build', 'beta', 'launch',\n 'growth', 'mature', 'maintenance', 'sunset',\n ],\n description: 'Keeps anti-patterns whose stages[] includes the given UPGProductStage.',\n },\n limit: { type: 'number', description: 'Page size (default 50, max 200).' },\n cursor: {\n type: 'string',\n description: 'Opaque pagination cursor; pass next_cursor from a previous response.',\n },\n },\n },\n },\n {\n name: 'get_anti_pattern',\n description:\n '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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n id: { type: 'string', description: 'Anti-pattern id (kebab-case slug).' },\n },\n required: ['id'],\n },\n },\n {\n name: 'list_benchmarks',\n description:\n '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).',\n inputSchema: {\n type: 'object' as const,\n properties: {\n kind: {\n type: 'string',\n enum: ['count', 'relationship', 'ratio', 'domain_activation'],\n description: 'Required: which benchmark catalog to return.',\n },\n stage: {\n type: 'string',\n enum: [\n 'concept', 'validation', 'build', 'beta', 'launch',\n 'growth', 'mature', 'maintenance', 'sunset',\n ],\n description: 'Optional UPGProductStage filter. Semantics depend on kind; see tool description.',\n },\n domain: {\n type: 'string',\n description: 'Optional atomic-domain id filter. Semantics depend on kind; see tool description.',\n },\n },\n required: ['kind'],\n },\n },\n {\n name: 'list_product_stages',\n description:\n '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.',\n inputSchema: { type: 'object' as const, properties: {} },\n },\n // ── Spec introspection round 5 ─────────\n {\n name: 'list_type_migrations',\n description:\n '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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n from_type: { type: 'string', description: 'Exact-match filter on the deprecated type name (e.g. \"pain_point\", \"hypothesis\").' },\n },\n },\n },\n {\n name: 'list_edge_migrations',\n description:\n '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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n from_edge: { type: 'string', description: 'Exact-match filter on the deprecated edge key (e.g. \"persona_has_jtbd\").' },\n },\n },\n },\n {\n name: 'list_split_migrations',\n description:\n '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.',\n inputSchema: { type: 'object' as const, properties: {} },\n },\n {\n name: 'list_lifecycles',\n description:\n '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).',\n inputSchema: {\n type: 'object' as const,\n properties: {\n entity_type: { type: 'string', description: 'Exact-match entity type name (e.g. \"feature\", \"hypothesis_claim\"). Returns at most one lifecycle.' },\n lifecycle_only: { type: 'boolean', description: 'When true, omit free_types and planned_types from response.' },\n },\n },\n },\n {\n name: 'get_lifecycle',\n description:\n '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.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n entity_type: { type: 'string', description: 'Canonical entity type name (e.g. \"feature\", \"hypothesis_claim\", \"opportunity\").' },\n },\n required: ['entity_type'],\n },\n },\n {\n name: 'list_scales',\n description:\n '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.',\n inputSchema: { type: 'object' as const, properties: {} },\n },\n {\n name: 'get_scale',\n description:\n 'Return one spec-defined assessment scale by id (e.g. \"reach_5\", \"severity_5\", \"confidence_binary\"). Includes the full point array.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n id: { type: 'string', description: 'Scale id (e.g. \"reach_5\", \"frequency_5\", \"severity_5\", \"importance_5\", \"confidence_binary\").' },\n },\n required: ['id'],\n },\n },\n {\n name: 'list_framework_categories',\n description:\n '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.',\n inputSchema: { type: 'object' as const, properties: {} },\n },\n {\n name: 'list_framework_structure_patterns',\n description:\n '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.',\n inputSchema: { type: 'object' as const, properties: {} },\n },\n {\n name: 'list_domain_rings',\n description:\n '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.',\n inputSchema: { type: 'object' as const, properties: {} },\n },\n {\n name: 'get_domain_ring',\n description:\n 'Return one UPGDomainRing by id (e.g. \"nucleus\", \"understand\", \"define\", \"build\", \"grow\", \"operate\", \"extend\").',\n inputSchema: {\n type: 'object' as const,\n properties: {\n id: { type: 'string', description: 'Ring id, one of: nucleus, understand, define, build, grow, operate, extend.' },\n },\n required: ['id'],\n },\n },\n {\n \"name\": \"move_node\",\n \"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.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n },\n \"node_id\": {\n \"type\": \"string\",\n \"description\": \"The node to reparent\"\n },\n \"new_parent_id\": {\n \"type\": \"string\",\n \"description\": \"The new parent node ID\"\n }\n },\n \"required\": [\n \"product_id\",\n \"node_id\",\n \"new_parent_id\"\n ]\n }\n },\n {\n \"name\": \"create_area\",\n \"description\": \"Create a new product area node (type 'area') in a product. Product areas are top-level organisational units within a product.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n },\n \"title\": {\n \"type\": \"string\",\n \"description\": \"Area title\"\n },\n \"description\": {\n \"type\": \"string\",\n \"description\": \"Optional description\"\n }\n },\n \"required\": [\n \"product_id\",\n \"title\"\n ]\n }\n },\n {\n \"name\": \"get_area_context\",\n \"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.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n },\n \"area_id\": {\n \"type\": \"string\",\n \"description\": \"The area node ID\"\n }\n },\n \"required\": [\n \"product_id\",\n \"area_id\"\n ]\n }\n },\n {\n \"name\": \"list_portfolios\",\n \"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.\",\n \"inputSchema\": { \"type\": \"object\", \"properties\": {} }\n },\n {\n \"name\": \"list_portfolio_cross_edges\",\n \"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).\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": { \"type\": \"string\", \"description\": \"Product ID\" }\n },\n \"required\": [\"product_id\"]\n }\n },\n {\n \"name\": \"create_cross_product_edge\",\n \"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, hosts, contributes_to.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": { \"type\": \"string\", \"description\": \"The product creating this cross-edge\" },\n \"source\": { \"type\": \"string\", \"description\": \"Qualified source: {product_id}/{node_id}\" },\n \"target\": { \"type\": \"string\", \"description\": \"Qualified target: {product_id}/{node_id}\" },\n \"type\": { \"type\": \"string\", \"description\": \"Cross-edge type\", \"enum\": [\"shares_persona\", \"shares_competitor\", \"shares_metric\", \"depends_on_product\", \"cannibalises\", \"succeeds\", \"hosts\", \"contributes_to\"] }\n },\n \"required\": [\"product_id\", \"source\", \"target\", \"type\"]\n }\n },\n {\n \"name\": \"repair_dangling_edges\",\n \"description\": \"Find (and optionally remove) cross-product edges that reference a product that no longer exists. Default is dry_run=true.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": { \"type\": \"string\", \"description\": \"Product ID\" },\n \"dry_run\": { \"type\": \"boolean\", \"description\": \"Default true: report only.\" },\n \"drop\": { \"type\": \"array\", \"items\": { \"type\": \"string\" }, \"description\": \"Categories to drop when dry_run=false\" }\n },\n \"required\": [\"product_id\"]\n }\n },\n {\n \"name\": \"batch_create_nodes\",\n \"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.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": { \"type\": \"string\", \"description\": \"Product ID\" },\n \"nodes\": {\n \"type\": \"array\",\n \"description\": \"Nodes to create (max 50)\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"type\": { \"type\": \"string\", \"description\": \"UPG entity type\" },\n \"title\": { \"type\": \"string\", \"description\": \"Entity title\" },\n \"description\": { \"type\": \"string\", \"description\": \"Optional description\" },\n \"tags\": { \"type\": \"array\", \"items\": { \"type\": \"string\" }, \"description\": \"Freeform tags\" },\n \"status\": { \"type\": \"string\", \"description\": \"Lifecycle status\" },\n \"properties\": { \"type\": \"object\", \"description\": \"Type-specific fields\" },\n \"parent_id\": { \"type\": \"string\", \"description\": \"Parent node ID; creates a containment edge automatically\" }\n },\n \"required\": [\"type\", \"title\"]\n }\n }\n },\n \"required\": [\"product_id\", \"nodes\"]\n }\n },\n {\n \"name\": \"batch_update_nodes\",\n \"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.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": { \"type\": \"string\", \"description\": \"Product ID\" },\n \"nodes\": {\n \"type\": \"array\",\n \"description\": \"Nodes to update (max 50)\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"id\": { \"type\": \"string\", \"description\": \"Node ID to update\" },\n \"title\": { \"type\": \"string\" },\n \"description\": { \"type\": \"string\" },\n \"tags\": { \"type\": \"array\", \"items\": { \"type\": \"string\" } },\n \"status\": { \"type\": \"string\" },\n \"properties\": { \"type\": \"object\", \"description\": \"Merged with existing properties\" }\n },\n \"required\": [\"id\"]\n }\n }\n },\n \"required\": [\"product_id\", \"nodes\"]\n }\n },\n {\n \"name\": \"batch_delete_nodes\",\n \"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.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": { \"type\": \"string\", \"description\": \"Product ID\" },\n \"node_ids\": {\n \"type\": \"array\",\n \"items\": { \"type\": \"string\" },\n \"description\": \"Node IDs to delete (max 50)\"\n }\n },\n \"required\": [\"product_id\", \"node_ids\"]\n }\n },\n {\n \"name\": \"batch_create_edges\",\n \"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.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": { \"type\": \"string\", \"description\": \"Product ID\" },\n \"edges\": {\n \"type\": \"array\",\n \"description\": \"Edges to create (max 50)\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"source_id\": { \"type\": \"string\", \"description\": \"Source node ID\" },\n \"target_id\": { \"type\": \"string\", \"description\": \"Target node ID\" },\n \"type\": { \"type\": \"string\", \"description\": \"Edge type; auto-inferred if omitted\" }\n },\n \"required\": [\"source_id\", \"target_id\"]\n }\n }\n },\n \"required\": [\"product_id\", \"edges\"]\n }\n },\n {\n \"name\": \"batch_delete_edges\",\n \"description\": \"Delete up to 50 edges in a single atomic Postgres transaction. All-or-nothing: any failure rolls back the entire batch.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": { \"type\": \"string\", \"description\": \"Product ID\" },\n \"edge_ids\": {\n \"type\": \"array\",\n \"items\": { \"type\": \"string\" },\n \"description\": \"Edge IDs to delete (max 50)\"\n }\n },\n \"required\": [\"product_id\", \"edge_ids\"]\n }\n },\n {\n \"name\": \"batch_move_nodes\",\n \"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.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": { \"type\": \"string\", \"description\": \"Product ID\" },\n \"moves\": {\n \"type\": \"array\",\n \"description\": \"Move operations (max 50)\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"node_id\": { \"type\": \"string\", \"description\": \"The node to re-parent\" },\n \"new_parent_id\": { \"type\": \"string\", \"description\": \"The new parent node ID\" }\n },\n \"required\": [\"node_id\", \"new_parent_id\"]\n }\n }\n },\n \"required\": [\"product_id\", \"moves\"]\n }\n },\n {\n \"name\": \"validate_graph\",\n \"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.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n },\n \"dry_run\": {\n \"type\": \"boolean\",\n \"description\": \"Always true for validate_graph (validation is read-only).\"\n }\n },\n \"required\": [\n \"product_id\"\n ]\n }\n },\n {\n \"name\": \"deduplicate_nodes\",\n \"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.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n },\n \"canonical_id\": {\n \"type\": \"string\",\n \"description\": \"The node to keep\"\n },\n \"duplicate_ids\": {\n \"type\": \"array\",\n \"items\": { \"type\": \"string\" },\n \"description\": \"Nodes to merge into canonical and delete (max 20)\"\n },\n \"dry_run\": {\n \"type\": \"boolean\",\n \"description\": \"Default true: report what would happen without changing anything.\"\n }\n },\n \"required\": [\n \"product_id\",\n \"canonical_id\",\n \"duplicate_ids\"\n ]\n }\n },\n {\n \"name\": \"migrate_type\",\n \"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.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n },\n \"from_type\": {\n \"type\": \"string\",\n \"description\": \"Current entity type to migrate away from\"\n },\n \"to_type\": {\n \"type\": \"string\",\n \"description\": \"New entity type. Must be a valid UPG entity type.\"\n },\n \"dry_run\": {\n \"type\": \"boolean\",\n \"description\": \"Default true: count affected nodes without changing anything.\"\n }\n },\n \"required\": [\n \"product_id\",\n \"from_type\",\n \"to_type\"\n ]\n }\n },\n {\n \"name\": \"migrate_cross_edges\",\n \"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.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": {\n \"type\": \"string\",\n \"description\": \"Product ID\"\n },\n \"dry_run\": {\n \"type\": \"boolean\",\n \"description\": \"Default true: report what would move without moving.\"\n }\n },\n \"required\": [\n \"product_id\"\n ]\n }\n },\n {\n \"name\": \"apply_framework\",\n \"description\": \"Apply a framework (MoSCoW, RICE, Kano, ...) to a set of entities in a product: creates a framework_exercise node and an `includes` edge to each entity. The per-entity result is recorded on the edge via score_entity, never on the entity node, so the same entity can sit in many exercises and any entity type can be scored. Returns { exercise_id, exercise, included, warnings }.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"product_id\": { \"type\": \"string\", \"description\": \"Required. Product the exercise belongs to.\" },\n \"framework_id\": { \"type\": \"string\", \"description\": \"Required. UPGFramework.id (e.g. \\\"moscow\\\", \\\"rice-scoring\\\").\" },\n \"title\": { \"type\": \"string\", \"description\": \"Human label for the exercise (default \\\"<Framework> exercise\\\").\" },\n \"entity_ids\": { \"type\": \"array\", \"items\": { \"type\": \"string\" }, \"description\": \"Entities to pull into the exercise (any type).\" },\n \"status\": { \"type\": \"string\", \"description\": \"Lifecycle phase: draft | active | archived (default draft).\" }\n },\n \"required\": [\n \"product_id\",\n \"framework_id\"\n ]\n }\n },\n {\n \"name\": \"score_entity\",\n \"description\": \"Record a framework's result for one entity on the exercise's includes edge (a MoSCoW bucket, a RICE score, a canvas slot). Auto-includes the entity if not already in scope. Merges into existing edge properties unless replace is set. The product is resolved from the exercise node. Returns { edge, warnings }.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"exercise_id\": { \"type\": \"string\", \"description\": \"Required. The framework_exercise id.\" },\n \"entity_id\": { \"type\": \"string\", \"description\": \"Required. The entity being scored.\" },\n \"values\": { \"type\": \"object\", \"description\": \"Required. The result as { input: value }, e.g. { \\\"moscow\\\": \\\"must\\\" } or { \\\"reach\\\": 800, \\\"impact\\\": 3 }.\" },\n \"replace\": { \"type\": \"boolean\", \"description\": \"Replace the edge properties instead of merging (default false).\" }\n },\n \"required\": [\n \"exercise_id\",\n \"entity_id\",\n \"values\"\n ]\n }\n }\n]\n\nconst HANDLERS: Record<string, ToolBinding<CloudContext>['handler']> = {\n list_products: listProducts,\n create_product: createProduct,\n get_audit_log: getAuditLog,\n get_product_context: getProductContext,\n get_graph_digest: getGraphDigest,\n query,\n get_changes: getChanges,\n list_nodes: listNodes,\n export_upg_document: exportUpgDocument,\n get_node: getNode,\n get_nodes: getNodes,\n search_nodes: searchNodes,\n create_node: createNode,\n update_node: updateNode,\n delete_node: deleteNode,\n get_product_graph: getProductGraph,\n create_edge: createEdge,\n delete_edge: deleteEdge,\n export_edges: exportEdges,\n rename_edge_type: renameEdgeType,\n // ── Framework exercises (0.8.6 cloud parity) ────────────────────\n apply_framework: applyFramework,\n score_entity: scoreEntity,\n list_product_areas: listProductAreas,\n get_area_graph: getAreaGraph,\n create_area: createArea,\n get_area_context: getAreaContext,\n move_node: moveNode,\n get_entity_schema: getEntitySchema,\n add_comment: addComment,\n list_comments: listComments,\n grant_access: grantAccess,\n list_collaborators: listCollaborators,\n get_graph_analytics: getGraphAnalytics,\n register_webhook: registerWebhook,\n list_webhooks: listWebhooks,\n remove_webhook: removeWebhook,\n // ── Spec introspection ─────────────────────────────────────────\n list_playbooks: listPlaybooks,\n get_playbook: getPlaybook,\n list_approaches: listApproaches,\n get_approach: getApproach,\n plan,\n inspect,\n prioritise,\n trace,\n reflect,\n list_domains: listDomains,\n get_domain_guide: getDomainGuide,\n list_frameworks: listFrameworks,\n get_framework: getFramework,\n list_edge_types: listEdgeTypes,\n get_edge_type: getEdgeType,\n list_regions: listRegions,\n get_region: getRegion,\n get_region_for_entity_type: getRegionForEntity,\n get_spec_version: getSpecVersion,\n resolve_edge_for_pair: resolveEdgeForPair,\n list_cross_edge_types: listCrossEdgeTypes,\n list_lenses: listLenses,\n get_lens: getLensTool,\n list_type_labels: listTypeLabels,\n get_type_label: getTypeLabel,\n get_valid_children: getValidChildrenTool,\n list_entity_types: listEntityTypes,\n get_entity_meta: getEntityMeta,\n list_anti_patterns: listAntiPatterns,\n get_anti_pattern: getAntiPattern,\n list_benchmarks: listBenchmarks,\n list_product_stages: listProductStages,\n // ── Spec catalogues (migrations, lifecycles, scales, framework metadata, domain rings) ──\n list_type_migrations: listTypeMigrations,\n list_edge_migrations: listEdgeMigrations,\n list_split_migrations: listSplitMigrations,\n list_lifecycles: listLifecycles,\n get_lifecycle: getLifecycle,\n list_scales: listScales,\n get_scale: getScale,\n list_framework_categories: listFrameworkCategories,\n list_framework_structure_patterns: listFrameworkStructurePatterns,\n list_domain_rings: listDomainRings,\n get_domain_ring: getDomainRing,\n list_portfolios: listPortfolios,\n list_portfolio_cross_edges: listPortfolioCrossEdges,\n create_cross_product_edge: createCrossProductEdge,\n repair_dangling_edges: repairDanglingEdges,\n batch_create_nodes: batchCreateNodes,\n batch_update_nodes: batchUpdateNodes,\n batch_delete_nodes: batchDeleteNodes,\n batch_create_edges: batchCreateEdges,\n batch_delete_edges: batchDeleteEdges,\n batch_move_nodes: batchMoveNodes,\n validate_graph: validateGraph,\n deduplicate_nodes: deduplicateNodes,\n migrate_type: migrateType,\n migrate_cross_edges: migrateCrossEdges,\n}\n\nexport function getToolHandler(name: string): ToolBinding<CloudContext>['handler'] | undefined {\n return HANDLERS[name]\n}\n"],"mappings":"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","names":["nodeId","query","n","getLifecycleForType","resolveEntityType","UnknownEntityTypeError","resolveEntityType","UnknownEntityTypeError","getLifecycleForType","inferEdgeTypeWithTier","inferEdgeTypeWithTier","edge","UPG_FRAMEWORKS_BY_ID","UPG_EDGE_CATALOG","clampLimit","decodeCursor","encodeCursor","UPG_FRAMEWORKS_BY_ID","domains","UPG_EDGE_CATALOG","UPG_CROSS_EDGE_TYPES","UPG_CROSS_EDGE_TYPES","getLifecycleForType","resolveEntityType","UnknownEntityTypeError","inferEdgeTypeWithTier","validateEdgeTypePair","checkPropertyTypes","checkLengthCaps","renderPropertyTypeWarning","resolveEntityType","UnknownEntityTypeError","checkPropertyTypes","renderPropertyTypeWarning","getLifecycleForType","checkLengthCaps","inferEdgeTypeWithTier","validateEdgeTypePair","UPG_EDGE_CATALOG","UPG_PROPERTY_SCHEMA","UPG_ENTITY_META_BY_NAME","UPG_EDGE_CATALOG","UPG_PROPERTY_SCHEMA","UPG_ENTITY_META_BY_NAME","UPG_TYPES_SET","UPG_CROSS_EDGE_TYPES","resolveContainmentEdge","getPool","UPG_TYPES_SET","resolveContainmentEdge","UPG_CROSS_EDGE_TYPES","require"]}