|
| 1 | +# sysmledgraph |
| 2 | + |
| 3 | +Path-only SysML indexer: builds a knowledge graph from `.sysml` files and exposes it via **MCP** (query, context, impact, rename, cypher, list, clean) and **CLI** (analyze, list, clean). Follows the [GitNexus](https://github.com/abhigyanpatwari/GitNexus) pattern (Kuzu graph, MCP tools) but indexes SysML only; grouping is SysML-native. |
| 4 | + |
| 5 | +**Design and plan:** SysML v2 model and development plan live in a separate repo (`sysml-v2-models/projects/sysmledgraph`). This repo is the implementation. |
| 6 | + |
| 7 | +## Requirements |
| 8 | + |
| 9 | +- **Node.js 20+** (see `.nvmrc`) |
| 10 | + |
| 11 | +## Libraries |
| 12 | + |
| 13 | +| Library | Purpose | |
| 14 | +|--------|--------| |
| 15 | +| **[Kuzu](https://kuzudb.com/)** (`kuzu`) | Embedded property graph database; Cypher queries; stores SysML nodes and edges. | |
| 16 | +| **[@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/typescript-sdk)** | MCP server (stdio), tool and resource registration; used for the sysmledgraph MCP server. | |
| 17 | +| **[Commander](https://github.com/tj/commander.js)** (`commander`) | CLI subcommands: `analyze`, `list`, `clean`. | |
| 18 | +| **[fast-glob](https://github.com/mrmlnc/fast-glob)** | File discovery: find all `.sysml` / `.kerml` under path(s). | |
| 19 | +| **[Zod](https://github.com/colinhacks/zod)** | Schema validation for MCP tool parameters. | |
| 20 | +| **[sysml-v2-lsp](https://github.com/daltskin/sysml-v2-lsp)** | SysML v2 parser via LSP stdio; **required** for indexing. Provides document symbols (Package, PartDef, PartUsage, etc.) and IN_DOCUMENT / IN_PACKAGE edges. | |
| 21 | + |
| 22 | +**sysml-v2-lsp (required)** |
| 23 | +After `npm install`, build the LSP once: |
| 24 | +`cd node_modules/sysml-v2-lsp && npm run build` |
| 25 | +Or set **SYSMLLSP_SERVER_PATH** to your built `dist/server/server.js`. Indexing will fail with a clear error if the LSP is not found. |
| 26 | + |
| 27 | +*Dev:* TypeScript, Vitest, @types/node. |
| 28 | + |
| 29 | +## Install |
| 30 | + |
| 31 | +```bash |
| 32 | +npm install |
| 33 | +npm run build |
| 34 | +``` |
| 35 | + |
| 36 | +## Usage |
| 37 | + |
| 38 | +### CLI |
| 39 | + |
| 40 | +Run from the project root (after `npm run build`) or via `npx sysmledgraph` if linked/published. |
| 41 | + |
| 42 | +| Command | Description | |
| 43 | +|--------|-------------| |
| 44 | +| **analyze** `<paths...>` | Index one or more directory trees: discover `.sysml` and `.kerml`, parse via LSP, build the graph. Paths are resolved to absolute and stored in the registry. | |
| 45 | +| **list** | Print all indexed root paths (from the registry). | |
| 46 | +| **clean** `[path]` | Remove the index for a given path, or for **all** indexed paths if `path` is omitted. Deletes the DB file and registry entry. | |
| 47 | + |
| 48 | +**Examples:** |
| 49 | + |
| 50 | +```bash |
| 51 | +# Index a single model directory |
| 52 | +npx sysmledgraph analyze ./path/to/sysml-models |
| 53 | + |
| 54 | +# Index multiple roots (each gets its own DB) |
| 55 | +npx sysmledgraph analyze ./repo1/models ./repo2/models |
| 56 | + |
| 57 | +# See what is indexed |
| 58 | +npx sysmledgraph list |
| 59 | + |
| 60 | +# Remove index for one path |
| 61 | +npx sysmledgraph clean ./path/to/sysml-models |
| 62 | + |
| 63 | +# Remove all indexed paths |
| 64 | +npx sysmledgraph clean |
| 65 | +``` |
| 66 | + |
| 67 | +**Options and environment:** |
| 68 | + |
| 69 | +- **`--storage <path>`** — Override the storage root (default: `~/.sysmledgraph`). Same as env **`SYSMEDGRAPH_STORAGE_ROOT`**. |
| 70 | +- **`SYSMLLSP_SERVER_PATH`** — Optional. Path to the sysml-v2-lsp server JS (e.g. `dist/server/server.js`). If unset, the CLI uses `node_modules/sysml-v2-lsp/dist/server/server.js` (must be built). |
| 71 | + |
| 72 | +**Storage layout:** Under the storage root: `registry.json` (list of indexed paths), and `db/<sanitized-path>.kuzu` (one Kuzu database per indexed path). On failure, the CLI writes errors to stderr and exits non-zero. |
| 73 | + |
| 74 | +--- |
| 75 | + |
| 76 | +### MCP |
| 77 | + |
| 78 | +Server name: **sysmledgraph**. The MCP server uses the same storage root as the CLI (default `~/.sysmledgraph`), so tools operate on whatever paths you indexed via the CLI or via the **indexDbGraph** tool. |
| 79 | + |
| 80 | +**Setup (Cursor):** Add to `.cursor/mcp.json` or Cursor MCP settings. |
| 81 | + |
| 82 | +**Option A — local build:** |
| 83 | + |
| 84 | +```json |
| 85 | +{ |
| 86 | + "mcpServers": { |
| 87 | + "sysmledgraph": { |
| 88 | + "command": "node", |
| 89 | + "args": ["C:/path/to/codebase-sysmledgraph/dist/mcp/index.js"], |
| 90 | + "env": { |
| 91 | + "SYSMEDGRAPH_STORAGE_ROOT": "C:/Users/you/.sysmledgraph", |
| 92 | + "SYSMLLSP_SERVER_PATH": "C:/path/to/sysml-v2-lsp/dist/server/server.js" |
| 93 | + } |
| 94 | + } |
| 95 | + } |
| 96 | +} |
| 97 | +``` |
| 98 | + |
| 99 | +**Option B — npx (when published):** |
| 100 | + |
| 101 | +```json |
| 102 | +{ |
| 103 | + "mcpServers": { |
| 104 | + "sysmledgraph": { |
| 105 | + "command": "npx", |
| 106 | + "args": ["-y", "sysmledgraph-mcp"] |
| 107 | + } |
| 108 | + } |
| 109 | +} |
| 110 | +``` |
| 111 | + |
| 112 | +**Tools:** |
| 113 | + |
| 114 | +| Tool | Parameters | Description | |
| 115 | +|------|------------|-------------| |
| 116 | +| **indexDbGraph** | `path` (string) or `paths` (string[]) | Build the graph for the given path(s). Same logic as CLI `analyze`. Uses first indexed path if none given (no-op). | |
| 117 | +| **list_indexed** | — | Return the list of indexed root paths (same as CLI `list`). | |
| 118 | +| **clean_index** | `path` (string, optional) | Remove index for one path or all (same as CLI `clean`). | |
| 119 | +| **cypher** | `query` (string) | Run a Cypher query on the graph for the **first** indexed path. Example: `MATCH (n:Node) RETURN n.id, n.label LIMIT 10`. | |
| 120 | +| **query** | `query` (string), `kind` (string, optional) | Concept search over node names/labels. Filters by node label if `kind` is set. | |
| 121 | +| **context** | `name` (string) | Get one node by id or name and its adjacent edges (types and targets). | |
| 122 | +| **impact** | `target` (string), `direction` (`"upstream"` \| `"downstream"`, optional) | List nodes that depend on `target` (upstream) or that `target` depends on (downstream). | |
| 123 | +| **rename** | `symbol` (string), `newName` (string), `dry_run` (boolean, optional) | Preview or perform a rename of a symbol across the graph. | |
| 124 | + |
| 125 | +**Resources:** |
| 126 | + |
| 127 | +- **sysmledgraph://context** — Index stats and list of indexed paths (Markdown). |
| 128 | +- **sysmledgraph://schema** — Graph node and edge schema (Markdown). |
| 129 | + |
| 130 | +Tools that need a graph (cypher, query, context, impact) use the **first** entry in the registry as the target DB. Ensure at least one path is indexed (CLI or indexDbGraph) before calling them. |
| 131 | + |
| 132 | +--- |
| 133 | + |
| 134 | +### Querying the graph (scripts) |
| 135 | + |
| 136 | +For ad-hoc Cypher or exporting the graph without MCP: |
| 137 | + |
| 138 | +- **Run one Cypher query** (uses first indexed path, outputs JSON): |
| 139 | + |
| 140 | + ```bash |
| 141 | + node scripts/query-one.mjs "MATCH (n:Node) RETURN count(n) AS total" |
| 142 | + node scripts/query-one.mjs "MATCH (n:Node) RETURN n.label, count(*) AS c ORDER BY c DESC LIMIT 5" |
| 143 | + ``` |
| 144 | + |
| 145 | + The graph uses a single node table **Node**; always use the label in Cypher: `MATCH (n:Node) ...`. |
| 146 | + |
| 147 | +- **Export graph for viewing:** Run `npm run export-graph` (writes `graph-export.json` in the current directory). Open `viewer/view.html` in a browser, click “Load graph.json”, and select that file for a force-directed view; click a node for details. Custom output path: `node scripts/export-graph.mjs path/to/out.json`. |
| 148 | + |
| 149 | +## Project layout |
| 150 | + |
| 151 | +- `src/` — Core: indexer, graph, mcp, cli, discovery, parser, symbol-to-graph, storage. |
| 152 | +- `bin/cli.ts` — CLI entrypoint. |
| 153 | +- `mcp/index.ts` — MCP server entrypoint (stdio). |
| 154 | +- `test/` — Unit and integration tests. |
| 155 | + |
| 156 | +## Development |
| 157 | + |
| 158 | +- `npm run build` — Compile TypeScript. |
| 159 | +- `npm run test` — Run tests. |
| 160 | +- `npm run test:watch` — Watch mode. |
| 161 | + |
| 162 | +## View the graph |
| 163 | + |
| 164 | +See **Usage → Querying the graph (scripts)** for the full flow. Short version: `npm run export-graph` writes `graph-export.json`; open `viewer/view.html` in a browser and load that file for a force-directed graph (click nodes for id/label/path). |
| 165 | + |
| 166 | +## Schema (graph) |
| 167 | + |
| 168 | +Implemented with **Kuzu**: one `Node` table (id, name, path, label) and one rel table per edge type (Node→Node). Label values: Document, Package, PartDef, PartUsage, etc. Edge types: IN_DOCUMENT, IN_PACKAGE, PARENT, TYPES, REFERENCES, IMPORTS, SATISFY, DERIVE, VERIFY, BINDING, CONNECTION_END. See design doc (GITNEXUS_FEATURES.md) and deploy model. |
| 169 | + |
| 170 | +## License |
| 171 | + |
| 172 | +MIT |
0 commit comments