Skip to content

Commit 2d127a9

Browse files
committed
Docs: two MCPs, Kuzu lock, LSP path; resolve LSP from repo root
- Add docs/MCP-AND-KUZU.md (sysmledgraph vs sysml-v2, no conflict; Kuzu lock; LSP path) - LSP resolver: walk up from cwd to find node_modules/sysml-v2-lsp (e.g. repo root) - README: env note, link to MCP-AND-KUZU.md - chore: release v0.3.0 Made-with: Cursor
1 parent 1374bbb commit 2d127a9

5 files changed

Lines changed: 67 additions & 7 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ npx sysmledgraph clean
6969
**Options and environment:**
7070

7171
- **`--storage <path>`** — Override the storage root (default: `~/.sysmledgraph`). Same as env **`SYSMEDGRAPH_STORAGE_ROOT`**.
72-
- **`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).
72+
- **`SYSMLLSP_SERVER_PATH`** — Optional. Path to the sysml-v2-lsp server JS (e.g. `dist/server/server.js`). If unset, the CLI looks in local `node_modules` and then walks up from the current working directory (e.g. repo root when run from a subfolder like `tools/sysmledgraph`).
7373

7474
**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.
7575

@@ -154,7 +154,7 @@ For ad-hoc Cypher or exporting the graph without MCP:
154154
- `bin/cli.ts` — CLI entrypoint.
155155
- `mcp/index.ts` — MCP server entrypoint (stdio).
156156
- `test/` — Unit and integration tests.
157-
- `docs/`[INSTALL_NOTES.md](docs/INSTALL_NOTES.md) (slow install, Windows workarounds), [grammar-and-mapping.md](docs/grammar-and-mapping.md).
157+
- `docs/`[INSTALL_NOTES.md](docs/INSTALL_NOTES.md) (slow install, Windows), [MCP-AND-KUZU.md](docs/MCP-AND-KUZU.md) (two MCPs, Kuzu lock, LSP path), [grammar-and-mapping.md](docs/grammar-and-mapping.md).
158158

159159
## Development
160160

docs/MCP-AND-KUZU.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Two MCPs, Kuzu lock, and LSP path
2+
3+
## How the two MCPs relate
4+
5+
| | **sysmledgraph** | **sysml-v2** (e.g. SysML v2 MCP) |
6+
|--|------------------|----------------------------------|
7+
| **Role** | Path-based indexer: builds a Kuzu graph from `.sysml` paths | SysML LSP MCP: parse, validate, symbols, loadProject, impact, context, query, preview |
8+
| **Uses LSP** | Spawns `server.js` (stdio) only when indexing | Runs `mcpServer.js` as the MCP server |
9+
| **Uses Kuzu** | Yes (one DB per indexed path) | No |
10+
| **Same package** | Same sysml-v2-lsp package, different entry points (`server.js` vs `mcpServer.js`) | Same |
11+
12+
So there is **no MCP-vs-MCP conflict**. The sysml-v2 MCP does not use Kuzu, so the two MCPs don’t contend over the database.
13+
14+
---
15+
16+
## Kuzu lock
17+
18+
**Only sysmledgraph uses Kuzu.** The “Could not set lock on file” (or similar) error happens when the **same DB file is opened more than once**:
19+
20+
- MCP has the DB open and you run the sysmledgraph **CLI** (e.g. `npx sysmledgraph analyze`), or
21+
- Two connections to the same DB in one process (we fixed that with a connection cache in the MCP).
22+
23+
**Fixes:**
24+
25+
- **Within one process:** The MCP uses a single cached connection per DB path (v0.2.0+), so repeated/concurrent tool calls don’t open the same file twice.
26+
- **Across processes:** Only one writer at a time. If you do a full re-index from the CLI, close Cursor (or stop the sysmledgraph MCP) first, or run the CLI when the MCP isn’t using that DB.
27+
28+
---
29+
30+
## “sysml-v2-lsp not found” / LSP path
31+
32+
That was **path resolution**: the sysmledgraph CLI (or MCP) was only looking in its own `node_modules` (e.g. `tools/sysmledgraph/node_modules/...`) and didn’t see the LSP at **repo root**.
33+
34+
**Fixes:**
35+
36+
- Resolver now also checks **repo root** (and parent directories) for `node_modules/sysml-v2-lsp/dist/server/server.js`, so when sysmledgraph runs from a subfolder (e.g. `tools/sysmledgraph`), it can find the LSP at the repo root.
37+
- You can always set **SYSMLLSP_SERVER_PATH** to the absolute path of your built `dist/server/server.js` if you want to point at a specific LSP install.
38+
39+
The sysml-v2 MCP runs from repo root and uses `mcpServer.js`; it doesn’t depend on this resolver and doesn’t block it.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "sysmledgraph",
3-
"version": "0.2.0",
3+
"version": "0.3.0",
44
"description": "Path-only SysML indexer: knowledge graph (Kuzu), MCP server (query, context, impact, rename, cypher), CLI (analyze, list, clean)",
55
"type": "module",
66
"main": "dist/src/index.js",

release-notes-v0.3.0.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
## Changes
2+
3+
- **Docs: Two MCPs, Kuzu lock, LSP path** — New [docs/MCP-AND-KUZU.md](docs/MCP-AND-KUZU.md) explains how sysmledgraph and the sysml-v2 MCP relate (no conflict; only sysmledgraph uses Kuzu), when Kuzu lock happens (same DB opened twice), and how to avoid it. Also documents the "sysml-v2-lsp not found" path-resolution fix.
4+
- **LSP path resolution** — Resolver now walks up from the current working directory to find `node_modules/sysml-v2-lsp/dist/server/server.js`. When sysmledgraph runs from a subfolder (e.g. `tools/sysmledgraph`), it can use the LSP at repo root without setting `SYSMLLSP_SERVER_PATH`.
5+
- **README** — CLI env note and project layout link to MCP-AND-KUZU.md.

src/parser/lsp-client.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* sysml-v2-lsp integration: stdio LSP client.
33
* Spawns the LSP server (dist/server/server.js). Uses LSP message framing (Content-Length).
4-
* Server path: SYSMLLSP_SERVER_PATH or node_modules/sysml-v2-lsp/dist/server/server.js.
4+
* Server path: SYSMLLSP_SERVER_PATH, or local node_modules, or walk up from cwd (e.g. repo root).
55
*/
66

77
import { spawn } from 'child_process';
@@ -12,6 +12,20 @@ import { existsSync } from 'fs';
1212

1313
const __dirname = dirname(fileURLToPath(import.meta.url));
1414

15+
const LSP_REL_PATH = join('sysml-v2-lsp', 'dist', 'server', 'server.js');
16+
17+
function findLspInNodeModules(startDir: string): string | null {
18+
let dir = startDir;
19+
for (;;) {
20+
const candidate = join(dir, 'node_modules', LSP_REL_PATH);
21+
if (existsSync(candidate)) return candidate;
22+
const parent = dirname(dir);
23+
if (parent === dir) break;
24+
dir = parent;
25+
}
26+
return null;
27+
}
28+
1529
export interface DocumentSymbolLsp {
1630
name: string;
1731
detail?: string;
@@ -25,9 +39,11 @@ async function resolveServerPath(): Promise<string | null> {
2539
if (process.env.SYSMLLSP_SERVER_PATH) {
2640
return process.env.SYSMLLSP_SERVER_PATH;
2741
}
28-
const root = join(__dirname, '..', '..', '..');
29-
const candidate = join(root, 'node_modules', 'sysml-v2-lsp', 'dist', 'server', 'server.js');
30-
if (existsSync(candidate)) return candidate;
42+
const packageRoot = join(__dirname, '..', '..', '..');
43+
const local = join(packageRoot, 'node_modules', LSP_REL_PATH);
44+
if (existsSync(local)) return local;
45+
const fromCwd = findLspInNodeModules(process.cwd());
46+
if (fromCwd) return fromCwd;
3147
return null;
3248
}
3349

0 commit comments

Comments
 (0)