|
1 | 1 | # Install |
2 | 2 |
|
| 3 | +How to build sysmledgraph, run the SysML indexer, and optionally use a **long-lived TCP worker** so CLI and MCP share one Kuzu database without file-lock errors. If you split **models** and **application code** across two Git repos, use [Two Git repos](#two-git-repos-modelbase-and-codebase). |
| 4 | + |
| 5 | +**Contents:** [Quick start](#quick-start) · [LSP](#lsp) · [Kuzu](#kuzu) · [Long-lived graph worker](#long-lived-graph-worker-optional) · [Worker CLI exit codes & examples](#worker-cli-exit-codes-and-examples) · [Two Git repos](#two-git-repos-modelbase-and-codebase) |
| 6 | + |
| 7 | +--- |
| 8 | + |
3 | 9 | ## Quick start |
4 | 10 |
|
5 | 11 | 1. **Root:** `npm install` then `npm run build`. On **Windows**, if **`sysml-v2-lsp`** fails during install (its script uses shell syntax), use **`npm install --ignore-scripts`** then **`node node_modules/kuzu/install.js`**. |
6 | 12 | 2. **LSP (for indexing):** The canonical LSP for this repo is in **`lsp/`**. Run **`npm run setup-lsp`** (uses `--ignore-scripts`) or **`cd lsp && npm install --ignore-scripts`**. No need to set **`SYSMLLSP_SERVER_PATH`** when using this default. |
7 | | -3. **Index:** `npx sysmledgraph analyze <path>` or use the MCP tool **indexDbGraph** after enabling the sysmledgraph MCP in Cursor (see docs/MCP_SERVER_FOR_CURSOR.md). |
| 13 | +3. **Index:** `npx sysmledgraph analyze <path>`, or enable the sysmledgraph MCP in Cursor and use **indexDbGraph** (see [MCP_SERVER_FOR_CURSOR.md](MCP_SERVER_FOR_CURSOR.md)). |
| 14 | +4. **Modelbase + Codebase (two Git repos):** If models and app code live in different repos but should share one graph, read [Two Git repos](#two-git-repos-modelbase-and-codebase) below. |
| 15 | + |
| 16 | +**Using CLI and MCP together on the same graph:** Start **`npx sysmledgraph worker start --detach`** once (see [Long-lived graph worker](#long-lived-graph-worker-optional)) so both use TCP instead of opening **`graph.kuzu`** twice. |
| 17 | + |
| 18 | +--- |
8 | 19 |
|
9 | 20 | ## LSP |
10 | 21 |
|
11 | | -- **Source:** [sysml-v2-lsp](https://www.npmjs.com/package/sysml-v2-lsp) (npm). The only copy used by sysmledgraph for indexing is in **`lsp/`** (see docs/PLAN_INDEPENDENT_LSP.md). |
| 22 | +- **Source:** [sysml-v2-lsp](https://www.npmjs.com/package/sysml-v2-lsp) (npm). The only copy used by sysmledgraph for indexing is in **`lsp/`** (see [PLAN_INDEPENDENT_LSP.md](PLAN_INDEPENDENT_LSP.md)). |
12 | 23 | - **Setup:** `npm run setup-lsp` installs it in `lsp/` with `--ignore-scripts` (required on **Windows** for **sysml-v2-lsp ≥ 0.8**). Manual: `cd lsp && npm install --ignore-scripts`. |
13 | 24 |
|
14 | 25 | ### Updating language support (“grammar” via the server) |
|
27 | 38 |
|
28 | 39 | **Coloring / TextMate “grammar”** in Cursor or VS Code usually comes from a **SysML or SysML v2 extension** in the marketplace, not from sysmledgraph. To change that: install or update the relevant extension, or adjust **`files.associations`** / theme settings in the editor — see the extension’s docs. |
29 | 40 |
|
| 41 | +--- |
| 42 | + |
30 | 43 | ## Kuzu |
31 | 44 |
|
32 | | -If `npm install` fails or you use `--ignore-scripts`, build Kuzu manually: `node node_modules/kuzu/install.js`. See docs/INSTALL_NOTES.md if present. |
| 45 | +If `npm install` fails or you use `--ignore-scripts`, build Kuzu manually: `node node_modules/kuzu/install.js`. If **`docs/INSTALL_NOTES.md`** exists in your checkout, see it for platform notes. |
| 46 | + |
| 47 | +--- |
33 | 48 |
|
34 | 49 | ## Long-lived graph worker (optional) |
35 | 50 |
|
36 | | -To avoid **Kuzu file-lock** conflicts when using the **CLI** and **MCP server** at the same time on the same storage root, run a single TCP worker that owns the database: |
| 51 | +To avoid **Kuzu file-lock** conflicts when using the **CLI** and **MCP server** at the same time on the same storage root, run **one** TCP worker process that owns **`db/graph.kuzu`**. CLI and MCP then talk to it over **localhost** (see [DESIGN_LONG_LIVED_WORKER.md](DESIGN_LONG_LIVED_WORKER.md), [WORKER_CONTRACT.md](WORKER_CONTRACT.md)). |
| 52 | + |
| 53 | +| Step | Command / setting | |
| 54 | +|------|-------------------| |
| 55 | +| **Start** | `npx sysmledgraph worker start --detach` (background) or `npx sysmledgraph worker start` (foreground). Writes **`worker.port`** under the storage root (default `~/.sysmledgraph`). | |
| 56 | +| **Same storage** | CLI (`--storage` / **`SYSMEDGRAPH_STORAGE_ROOT`**) and MCP **`env`** must use the **same** root. With **`worker.port`** or **`SYSMLEGRAPH_WORKER_URL`** (e.g. `http://127.0.0.1:9123`), clients use TCP instead of in-process Kuzu. | |
| 57 | +| **Stop** | `npx sysmledgraph worker stop` — graceful shutdown, then removes **`worker.port`**. | |
| 58 | +| **Status** | `npx sysmledgraph worker status` — exit **0** if the port responds. | |
| 59 | +| **Strict** | **`SYSMLEGRAPH_WORKER_STRICT=1`** — if a worker URL/port is configured but the daemon is unreachable, **fail** instead of falling back to in-process (avoids mixed mode). | |
| 60 | + |
| 61 | +Fixed listen port (optional): **`SYSMLEGRAPH_WORKER_PORT`** (e.g. `9192`); otherwise the OS assigns a port and writes it to **`worker.port`** line 1. |
| 62 | + |
| 63 | +**E2E smoke:** `npm run test:daemon` (uses `vitest.e2e.config.ts`; not part of default `npm test`). |
| 64 | + |
| 65 | +**Export / map:** `npx sysmledgraph graph export` / `npx sysmledgraph graph map` use the same gateway as MCP. `npm run export-graph` / `npm run generate-map` call those CLI commands. |
| 66 | + |
| 67 | +### Worker CLI: exit codes and examples |
| 68 | + |
| 69 | +Use the same **`SYSMEDGRAPH_STORAGE_ROOT`** (or **`--storage`**) for every command below. |
| 70 | + |
| 71 | +| Command | **0** (success) | **1** (failure / not ready) | **2** (already running) | |
| 72 | +|---------|-----------------|----------------------------|-------------------------| |
| 73 | +| **`worker start`** | Foreground: runs until shutdown. **`--detach`:** background start | Daemon not built, stale **`worker.port`** with PID still alive, other errors | **`worker.port`** exists and **TCP responds** — worker already up | |
| 74 | +| **`worker stop`** | Shutdown attempted; **`worker.port`** removed | No **`worker.port`** | — | |
| 75 | +| **`worker status`** | TCP responds (stdout: `running 127.0.0.1:<port>`) | No port file, TCP down, or **stale** **`worker.port`** (stderr explains); invalid port file | — | |
| 76 | + |
| 77 | +**Examples (stderr/stdout):** |
| 78 | + |
| 79 | +```text |
| 80 | +# Up — exit 0 |
| 81 | +$ npx sysmledgraph worker status |
| 82 | +running 127.0.0.1:9123 |
| 83 | +``` |
| 84 | + |
| 85 | +```text |
| 86 | +# Down — exit 1 |
| 87 | +$ npx sysmledgraph worker status |
| 88 | +not running |
| 89 | +``` |
| 90 | + |
| 91 | +```text |
| 92 | +# Port file left over, process gone — exit 1 (clean up with worker stop or delete worker.port) |
| 93 | +$ npx sysmledgraph worker status |
| 94 | +not running (stale worker.port: TCP closed on port 9123; run worker stop or delete worker.port) |
| 95 | +``` |
| 96 | + |
| 97 | +```text |
| 98 | +# Second start while daemon is up — exit 2 |
| 99 | +$ npx sysmledgraph worker start --detach |
| 100 | +Worker already running (worker.port + TCP responds). |
| 101 | +``` |
| 102 | + |
| 103 | +If you run **`dist/.../daemon.js`** directly and another worker holds the lock, the process prints an error and exits **2** when the message indicates *already running*, otherwise **1**. Details: [WORKER_CONTRACT.md](WORKER_CONTRACT.md), [DESIGN_LONG_LIVED_WORKER.md](DESIGN_LONG_LIVED_WORKER.md) §9.4. |
| 104 | + |
| 105 | +### Which repo starts the TCP server? |
| 106 | + |
| 107 | +The daemon is **not** tied to which Git repo you have open. It is tied to **`SYSMEDGRAPH_STORAGE_ROOT`** (and the **`sysmledgraph`** binary you invoke). You can run **`worker start`** from a terminal whose cwd is **either** repo, as long as the environment points at the **same** storage root. |
| 108 | + |
| 109 | +In a **Publisher / Subscriber** setup ([below](#two-git-repos-modelbase-and-codebase)): |
| 110 | + |
| 111 | +- **Start the worker once** where you operate the graph (conventionally **Modelbase / Publisher**). |
| 112 | +- **Codebase / Subscriber** does **not** need to start the TCP server; it sets **`SYSMLEGRAPH_WORKER_URL`** and **connects** to the existing process. Starting a **second** worker for the **same** root fails (**`worker.lock`** or **`worker start`** exit **2**). |
| 113 | + |
| 114 | +--- |
| 115 | + |
| 116 | +### Two Git repos: Modelbase and Codebase |
| 117 | + |
| 118 | +Models often live in a **Modelbase** repo; implementation in a **Codebase** repo. Git remotes differ, but **one graph** is keyed by a shared **`SYSMEDGRAPH_STORAGE_ROOT`** directory, not by repo name. |
| 119 | + |
| 120 | +| Short name | Typical repo | Role in one sentence | |
| 121 | +|------------|--------------|----------------------| |
| 122 | +| **Publisher** | SysML / modelbase | Owns indexing and usually **`worker start`**; one process holds **`graph.kuzu`**. | |
| 123 | +| **Subscriber** | App, firmware, services | Same storage root + **`SYSMLEGRAPH_WORKER_URL`** (or readable **`worker.port`**); uses query / context / impact / … | |
| 124 | + |
| 125 | +**Minimal env (same machine)** |
| 126 | + |
| 127 | +| Role | Required | Typical extras | |
| 128 | +|------|----------|----------------| |
| 129 | +| **Publisher** | **`SYSMEDGRAPH_STORAGE_ROOT`** | After **`worker start`**: none for local clients; or set **`SYSMLEGRAPH_WORKER_URL`** if you do not rely on **`worker.port`**. | |
| 130 | +| **Subscriber** | **`SYSMEDGRAPH_STORAGE_ROOT`** (same path) + **`SYSMLEGRAPH_WORKER_URL`** | **`SYSMLEGRAPH_SUBSCRIBER=1`**, **`SYSMLEGRAPH_WORKER_STRICT=1`** | |
| 131 | + |
| 132 | +#### Publisher (Modelbase) |
| 133 | + |
| 134 | +- Set **`SYSMEDGRAPH_STORAGE_ROOT`** to the directory that will contain **`db/graph.kuzu`** (default `~/.sysmledgraph` or a project path such as **`.sysmledgraph-local`**). |
| 135 | +- Build the graph: **`npx sysmledgraph analyze <path>`** or MCP **indexDbGraph**. |
| 136 | +- For CLI + MCP without Kuzu lock fights: **`npx sysmledgraph worker start --detach`**. Port → **`worker.port`** line 1; optional fixed port: **`SYSMLEGRAPH_WORKER_PORT`**. |
| 137 | + |
| 138 | +#### Subscriber (Codebase) |
| 139 | + |
| 140 | +- Use the **same absolute** **`SYSMEDGRAPH_STORAGE_ROOT`** as the Publisher. |
| 141 | +- Set **`SYSMLEGRAPH_WORKER_URL`** to **`127.0.0.1:<port>`** (from the Publisher’s **`worker.port`**, or the port you fixed with **`SYSMLEGRAPH_WORKER_PORT`**). The **MCP process does not start** the TCP server; it only connects. |
| 142 | +- Optional: **`SYSMLEGRAPH_SUBSCRIBER=1`** in MCP **`env`** — **indexDbGraph** / **clean_index** are not registered (fewer accidents from the wrong workspace). |
| 143 | +- Optional: **`SYSMLEGRAPH_WORKER_STRICT=1`** — graph calls fail if the worker is down (no silent in-process DB). |
| 144 | +- Do **not** run **`worker start`** for that same root from Codebase unless the Publisher worker is **not** running and you are deliberately taking over the same storage root from a shell (still only **one** daemon). |
| 145 | + |
| 146 | +**`mcp.json` examples:** [MCP_SERVER_FOR_CURSOR.md](MCP_SERVER_FOR_CURSOR.md). |
| 147 | + |
| 148 | +#### Same PC, two Cursor workspaces |
| 149 | + |
| 150 | +One absolute **`SYSMEDGRAPH_STORAGE_ROOT`** in **both** projects. **Modelbase:** index + **`worker start`**. **Codebase:** **`SYSMLEGRAPH_WORKER_URL`** only — no second daemon for that root. |
| 151 | + |
| 152 | +#### Different PCs |
| 153 | + |
| 154 | +The daemon listens on **localhost** only on the machine where it runs. From **Codebase**, typical options: **SSH port forward** from the **Modelbase** host’s worker port to local **`127.0.0.1`**, then **`SYSMLEGRAPH_WORKER_URL`** to that local port; or a **shared filesystem** for the storage root (harder). See [PLAN.md](PLAN.md) Phase 7. |
| 155 | + |
| 156 | +#### Only one worker per storage root |
37 | 157 |
|
38 | | -1. **Start:** `npx sysmledgraph worker start --detach` (background) or `npx sysmledgraph worker start` (foreground). Writes **`worker.port`** under your storage root (default `~/.sysmledgraph`). |
39 | | -2. **Same storage:** Point CLI (`--storage` / `SYSMEDGRAPH_STORAGE_ROOT`) and the MCP server env at the **same** root. With `worker.port` present (or **`SYSMLEGRAPH_WORKER_URL`** e.g. `http://127.0.0.1:9123`), CLI and MCP talk to the worker over **localhost TCP** instead of opening Kuzu in-process. |
40 | | -3. **Stop:** `npx sysmledgraph worker stop` — graceful shutdown via TCP, then removes `worker.port`. |
41 | | -4. **Status:** `npx sysmledgraph worker status` — exit `0` if the port responds. |
42 | | -5. **Strict:** If the worker is configured but unreachable, set **`SYSMLEGRAPH_WORKER_STRICT=1`** to fail instead of falling back to in-process (avoids accidental mixed mode). See **docs/DESIGN_LONG_LIVED_WORKER.md**. |
| 158 | +What **actually** enforces a single daemon (hiding MCP tools does **not**): |
43 | 159 |
|
44 | | -Optional port: **`SYSMLEGRAPH_WORKER_PORT`** (e.g. `9192`); default is an OS-assigned port written to `worker.port`. |
| 160 | +- **`worker.lock`** — exclusive lock; a second process on the **same** root gets an error such as *Another graph worker is already running*. |
| 161 | +- **`worker start`** — if TCP already answers on **`worker.port`**, start exits **2** (already running). |
45 | 162 |
|
46 | | -E2E smoke (subprocess daemon): `npm run test:daemon` (uses `vitest.e2e.config.ts`; not part of default `npm test`). |
| 163 | +**`SYSMLEGRAPH_SUBSCRIBER=1`** only removes **index** / **clean** from the MCP tool list; it does not stop **`worker start`** from a terminal. |
47 | 164 |
|
48 | | -**Export / map (worker-aware):** `npx sysmledgraph graph export [file]` and `npx sysmledgraph graph map [file]` use the same gateway as MCP (TCP worker when configured). `npm run export-graph` / `npm run generate-map` call those CLI commands. |
| 165 | +For a full env var list, see [README.md](../README.md) (repository root). |
0 commit comments