@unified-product-graph/cloud-server 0.7.1 → 0.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -6,7 +6,7 @@ This package co-versions with `@unified-product-graph/core` and `@unified-produc
6
6
 
7
7
  ## 0.6.0 · 2026-05-22 · Launch train alignment
8
8
 
9
- Aligned with `@unified-product-graph/core@0.6.0`. Bumped `@unified-product-graph/core` dep to `^0.6.0`. No cloud-server surface changes co-versioned for the launch train.
9
+ Aligned with `@unified-product-graph/core@0.6.0`. Bumped `@unified-product-graph/core` dep to `^0.6.0`. No cloud-server surface changes; co-versioned for the launch train.
10
10
 
11
11
  ## 0.5.0 · 2026-05-19 · Inaugural public release
12
12
 
package/SELF-HOSTING.md CHANGED
@@ -2,20 +2,20 @@
2
2
 
3
3
  The cloud server is a **Postgres-backed MCP server**. Unlike the local server
4
4
  (`@unified-product-graph/mcp-server`), which reads and writes a single `.upg`
5
- file, the cloud server stores the graph in Postgres so a team can share one
5
+ file, the cloud server stores the graph in Postgres, so a team can share one
6
6
  source of truth with concurrent, transactional writes, multiple products, an
7
7
  audit log, comments, and webhooks.
8
8
 
9
9
  > **Mental model:** the local server is to Git as the cloud server is to a
10
- > shared database. Same tools, same UPG semantics different substrate.
10
+ > shared database. Same tools, same UPG semantics; different substrate.
11
11
 
12
12
  This guide covers three deployment tiers, from "ready today" to "real SaaS."
13
13
 
14
14
  | Tier | What you host | Auth model | Effort | Use when |
15
15
  |------|---------------|-----------|--------|----------|
16
- | **1 Shared DB** | Just Postgres; each client runs the server locally | Connection string = full access | Minutes | A small, trusting team |
17
- | **2 Central endpoint** | The server behind a stdio→HTTP bridge + auth proxy | One shared identity, gated by a proxy | A day of ops | One shared endpoint, still one trust domain |
18
- | **3 Multi-tenant SaaS** | The server with auth + per-user identity + RLS | Per-user, per-product RBAC | A real build | External / untrusted users |
16
+ | **Tier 1: Shared DB** | Just Postgres; each client runs the server locally | Connection string = full access | Minutes | A small, trusting team |
17
+ | **Tier 2: Central endpoint** | The server behind a stdio→HTTP bridge + auth proxy | One shared identity, gated by a proxy | A day of ops | One shared endpoint, still one trust domain |
18
+ | **Tier 3: Multi-tenant SaaS** | The server with auth + per-user identity + RLS | Per-user, per-product RBAC | A real build | External / untrusted users |
19
19
 
20
20
  ---
21
21
 
@@ -23,9 +23,9 @@ This guide covers three deployment tiers, from "ready today" to "real SaaS."
23
23
 
24
24
  - **Transport is stdio.** The server speaks MCP over stdin/stdout to a single
25
25
  trusted client (Claude Code, Cursor, etc.). There is **no built-in network
26
- endpoint** Tiers 2 and 3 add one.
26
+ endpoint** (Tiers 2 and 3 add one).
27
27
  - **One config knob:** `UPG_DATABASE_URL` (or `--database-url`). The server
28
- opens a default `pg.Pool` against it and runs `SELECT 1` at startup **it
28
+ opens a default `pg.Pool` against it and runs `SELECT 1` at startup; **it
29
29
  exits immediately if the database is unreachable.**
30
30
  - **Postgres 13+** is required (the schema uses `gen_random_uuid()`).
31
31
  - **RBAC is recorded, not yet enforced.** The schema has an `access` table
@@ -36,14 +36,14 @@ This guide covers three deployment tiers, from "ready today" to "real SaaS."
36
36
 
37
37
  ---
38
38
 
39
- ## Tier 1 Shared managed Postgres (recommended starting point)
39
+ ## Tier 1: Shared managed Postgres (recommended starting point)
40
40
 
41
41
  Host the **database**; every team member runs the server **locally** against it.
42
42
  Production-ready today for a team inside one trust boundary.
43
43
 
44
44
  ### 1. Provision Postgres
45
45
 
46
- Any Postgres 13+ works [Neon](https://neon.tech), [Supabase](https://supabase.com),
46
+ Any Postgres 13+ works: [Neon](https://neon.tech), [Supabase](https://supabase.com),
47
47
  [Railway](https://railway.app), RDS, or a VM. Grab the connection string:
48
48
 
49
49
  ```
@@ -66,7 +66,7 @@ The four migrations create the `upg` schema: `products`, `nodes`, `edges`,
66
66
  ### 3. Point each member's client at the shared DB
67
67
 
68
68
  Add an MCP server entry to each member's client config. Keep the connection
69
- string in **personal / local** config never a committed file.
69
+ string in **personal / local** config, never a committed file.
70
70
 
71
71
  ```jsonc
72
72
  // If installed from npm (the package ships a `upg-cloud-server` bin):
@@ -97,7 +97,7 @@ The cloud server is **multi-product**, so every tool is scoped by `product_id`:
97
97
  2. Pass that `product_id` to `create_node`, `create_edge`, `query`, etc.
98
98
 
99
99
  Concurrent writes from different members land as atomic Postgres transactions
100
- with foreign-key integrity no `.upg` merge conflicts.
100
+ with foreign-key integrity; no `.upg` merge conflicts.
101
101
 
102
102
  ### Local Postgres for development
103
103
 
@@ -112,12 +112,12 @@ docker compose up -d postgres
112
112
 
113
113
  ---
114
114
 
115
- ## Tier 2 A single shared endpoint (stdio → HTTP bridge)
115
+ ## Tier 2: A single shared endpoint (stdio → HTTP bridge)
116
116
 
117
117
  Centralize the **process** so clients don't run anything locally. Wrap the
118
118
  stdio server in an MCP HTTP bridge (e.g. [`supergateway`](https://github.com/supercorp-ai/supergateway)
119
119
  or `mcp-proxy`, or swap in the SDK's StreamableHTTP transport) and put an
120
- **auth proxy in front** the server itself has no authentication.
120
+ **auth proxy in front** (the server itself has no authentication).
121
121
 
122
122
  ```
123
123
  [client] --HTTPS--> [auth proxy] --> [bridge + upg-cloud-server (stdio)] --> [managed Postgres]
@@ -144,9 +144,9 @@ clients must be isolated from one another.
144
144
 
145
145
  ---
146
146
 
147
- ## Tier 3 Multi-tenant SaaS (the enforcement tier)
147
+ ## Tier 3: Multi-tenant SaaS (the enforcement tier)
148
148
 
149
- The data model is already built for this `products`, `access` roles,
149
+ The data model is already built for this: `products`, `access` roles,
150
150
  `audit_log`, `webhooks`, `cross_product_edges`. What's missing is the
151
151
  **enforcement layer**. Reaching genuine per-user multi-tenancy requires:
152
152
 
@@ -155,8 +155,8 @@ The data model is already built for this — `products`, `access` roles,
155
155
  2. **Identity propagation** into the store, so every query carries the calling
156
156
  user (e.g. `SET LOCAL app.user_id = …` per transaction).
157
157
  3. **RLS policies** on `upg.*` keyed off that identity and the `access` table,
158
- so reads/writes are gated at the database not by convention.
159
- 4. **Operational scale** stateless server replicas behind a load balancer,
158
+ so reads/writes are gated at the database, not by convention.
159
+ 4. **Operational scale:** stateless server replicas behind a load balancer,
160
160
  connection pooling (e.g. PgBouncer; the server currently opens an untuned
161
161
  default `pg.Pool`), and the existing webhook/audit machinery wired to fire
162
162
  on mutations.
@@ -164,7 +164,7 @@ The data model is already built for this — `products`, `access` roles,
164
164
  Until those land, treat the `access` roles as **advisory metadata**, not a
165
165
  security boundary.
166
166
 
167
- > Tracked as a roadmap item see the UPG issue tracker for "Tier-3 enforcement:
167
+ > Tracked as a roadmap item; see the UPG issue tracker for "Tier-3 enforcement:
168
168
  > HTTP transport + per-user identity + RLS."
169
169
 
170
170
  ---
@@ -181,5 +181,5 @@ security boundary.
181
181
  |----------|----------|-------------|
182
182
  | `UPG_DATABASE_URL` | Yes | Postgres connection string. Also accepted as `--database-url`. |
183
183
 
184
- The server is intentionally minimal in configuration everything else lives
184
+ The server is intentionally minimal in configuration; everything else lives
185
185
  in the database.
package/dist/index.js CHANGED
@@ -1469,7 +1469,7 @@ var createNode = async (args, { store }) => {
1469
1469
  });
1470
1470
  const warnings = [...lengthWarnings];
1471
1471
  if (resolvedType.alias) {
1472
- warnings.push(`Type "${resolvedType.alias.from}" is deprecated \u2014 using canonical "${resolvedType.alias.to}".`);
1472
+ warnings.push(`Type "${resolvedType.alias.from}" is deprecated; using canonical "${resolvedType.alias.to}".`);
1473
1473
  }
1474
1474
  if (args.status) {
1475
1475
  newNode.status = args.status;
@@ -1495,7 +1495,7 @@ var createNode = async (args, { store }) => {
1495
1495
  const inference = inferEdgeTypeWithTier(parent.type, nodeType);
1496
1496
  if (!inference.ok) {
1497
1497
  const suggestion = inference.suggestions.length > 0 ? ` Suggestions: ${inference.suggestions.map((s) => `${s.source_type} \u2192 ${s.target_type} (${s.edge_type})`).join("; ")}.` : "";
1498
- warnings.push(`Parent edge not created \u2014 no canonical edge for ${parent.type} \u2192 ${nodeType}.${suggestion}`);
1498
+ warnings.push(`Parent edge not created; no canonical edge for ${parent.type} \u2192 ${nodeType}.${suggestion}`);
1499
1499
  } else {
1500
1500
  edge = { id: edgeId(), source: parentId, target: nId, type: inference.edgeType };
1501
1501
  await store.addEdge(productId, edge);
@@ -2764,7 +2764,7 @@ var batchCreateNodes = async (args, { store }) => {
2764
2764
  const parentType = parentRows[0].type;
2765
2765
  const inference = inferEdgeTypeWithTier3(parentType, nodeType);
2766
2766
  if (!inference.ok) {
2767
- warnings.push(`Node "${newId}": parent edge not created \u2014 no canonical edge for ${parentType} \u2192 ${nodeType}.`);
2767
+ warnings.push(`Node "${newId}": parent edge not created; no canonical edge for ${parentType} \u2192 ${nodeType}.`);
2768
2768
  } else {
2769
2769
  const eid = edgeId();
2770
2770
  await client.query(