@rubytech/create-realagent 1.0.634 → 1.0.636
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 +26 -0
- package/package.json +1 -1
- package/payload/platform/lib/graph-mcp/dist/index.js +98 -8
- package/payload/platform/lib/graph-mcp/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-mcp/src/index.ts +115 -12
- package/payload/platform/plugins/admin/mcp/dist/index.js +6 -7
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/docs/references/plugins-guide.md +8 -0
- package/payload/platform/plugins/memory/references/graph-primitives.md +15 -0
- package/payload/server/chunk-LM5BMENF.js +77 -0
- package/payload/server/index.js +7225 -0
- package/payload/server/public/assets/{admin-Cacbe9nP.js → admin-E3EIZIw0.js} +30 -30
- package/payload/server/public/assets/public-Lwkux_Q4.js +5 -0
- package/payload/server/public/assets/useVoiceRecorder-Cbo-LQPY.css +1 -0
- package/payload/server/public/assets/{useVoiceRecorder-xC0cS-GO.js → useVoiceRecorder-D4y_EM_A.js} +2 -2
- package/payload/server/public/index.html +3 -3
- package/payload/server/public/public.html +3 -3
- package/payload/server/server.js +132 -84
- package/payload/server/upgrade-progress-server.js +83 -0
- package/payload/server/public/assets/public-CBVU1ErT.js +0 -5
- package/payload/server/public/assets/useVoiceRecorder-baV6L6a0.css +0 -1
package/dist/index.js
CHANGED
|
@@ -8,6 +8,7 @@ const PAYLOAD_DIR = resolve(import.meta.dirname, "../payload");
|
|
|
8
8
|
// The bundler stamps brand.json into the payload at build time.
|
|
9
9
|
const BRAND_PATH = join(PAYLOAD_DIR, "platform", "config", "brand.json");
|
|
10
10
|
if (!existsSync(BRAND_PATH)) {
|
|
11
|
+
console.error("Setup failed: brand.json not found in payload (package may be corrupted)");
|
|
11
12
|
console.error("FATAL: brand.json not found in payload. Package may be corrupted.");
|
|
12
13
|
process.exit(1);
|
|
13
14
|
}
|
|
@@ -16,6 +17,7 @@ try {
|
|
|
16
17
|
BRAND = JSON.parse(readFileSync(BRAND_PATH, "utf-8"));
|
|
17
18
|
}
|
|
18
19
|
catch (err) {
|
|
20
|
+
console.error(`Setup failed: failed to parse brand.json: ${err.message}`);
|
|
19
21
|
console.error(`FATAL: Failed to parse brand.json: ${err.message}`);
|
|
20
22
|
process.exit(1);
|
|
21
23
|
}
|
|
@@ -1335,6 +1337,7 @@ function createTunnelSymlink(linkPath, target) {
|
|
|
1335
1337
|
catch (err) {
|
|
1336
1338
|
const code = err.code;
|
|
1337
1339
|
if (code !== "ENOENT") {
|
|
1340
|
+
console.error(`Setup failed: ${linkPath} lstat failed: ${err.message}`);
|
|
1338
1341
|
console.error(`[create-maxy:error] ${linkPath} lstat failed: ${err.message}`);
|
|
1339
1342
|
process.exit(1);
|
|
1340
1343
|
}
|
|
@@ -1346,6 +1349,7 @@ function createTunnelSymlink(linkPath, target) {
|
|
|
1346
1349
|
return;
|
|
1347
1350
|
}
|
|
1348
1351
|
if (!lstat.isSymbolicLink()) {
|
|
1352
|
+
console.error(`Setup failed: ${linkPath} collision (regular file)`);
|
|
1349
1353
|
console.error(`[create-maxy:collision] ${linkPath} already exists target=<regular-file>`);
|
|
1350
1354
|
console.error(` Remove the file and re-run: npx -y @rubytech/create-maxy`);
|
|
1351
1355
|
process.exit(1);
|
|
@@ -1356,6 +1360,7 @@ function createTunnelSymlink(linkPath, target) {
|
|
|
1356
1360
|
resolvedTarget = resolve(dirname(linkPath), raw);
|
|
1357
1361
|
}
|
|
1358
1362
|
catch (err) {
|
|
1363
|
+
console.error(`Setup failed: ${linkPath} symlink unreadable: ${err.message}`);
|
|
1359
1364
|
console.error(`[create-maxy:collision] ${linkPath} symlink unreadable: ${err.message}`);
|
|
1360
1365
|
console.error(` Remove the file and re-run: npx -y @rubytech/create-maxy`);
|
|
1361
1366
|
process.exit(1);
|
|
@@ -1386,6 +1391,7 @@ function createTunnelSymlink(linkPath, target) {
|
|
|
1386
1391
|
logFile(` symlink repaired (dangling): ${linkPath} was ${resolvedTarget}, now ${targetAbs}`);
|
|
1387
1392
|
return;
|
|
1388
1393
|
}
|
|
1394
|
+
console.error(`Setup failed: ${linkPath} collision (symlink to ${resolvedTarget})`);
|
|
1389
1395
|
console.error(`[create-maxy:collision] ${linkPath} already exists target=${resolvedTarget}`);
|
|
1390
1396
|
console.error(` This symlink was created by a different install (not within ${INSTALL_DIR}).`);
|
|
1391
1397
|
console.error(` Remove or relocate it, then re-run: npx -y @rubytech/create-maxy`);
|
|
@@ -1401,6 +1407,7 @@ function installTunnelScripts() {
|
|
|
1401
1407
|
chmodSync(src, 0o755);
|
|
1402
1408
|
}
|
|
1403
1409
|
catch (err) {
|
|
1410
|
+
console.error(`Setup failed: tunnel script missing or chmod failed: ${src}`);
|
|
1404
1411
|
console.error(`[create-maxy:error] tunnel script missing or chmod failed: ${src}`);
|
|
1405
1412
|
console.error(` ${err.message}`);
|
|
1406
1413
|
process.exit(1);
|
|
@@ -1578,6 +1585,7 @@ Environment=KEEP_ALIVE_TIMEOUT=61000
|
|
|
1578
1585
|
Environment=DISPLAY=:99
|
|
1579
1586
|
Environment=PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/chromium
|
|
1580
1587
|
Environment=MAXY_PLATFORM_ROOT=${INSTALL_DIR}/platform
|
|
1588
|
+
Environment=PATH=%h/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
|
|
1581
1589
|
EnvironmentFile=-${persistDir}/.env
|
|
1582
1590
|
StandardOutput=append:${persistDir}/logs/server.log
|
|
1583
1591
|
StandardError=append:${persistDir}/logs/server.log
|
|
@@ -1586,6 +1594,11 @@ StandardError=append:${persistDir}/logs/server.log
|
|
|
1586
1594
|
WantedBy=default.target
|
|
1587
1595
|
`;
|
|
1588
1596
|
writeFileSync(join(serviceDir, BRAND.serviceName), serviceFile);
|
|
1597
|
+
// Task 560: the unit declares Environment=PATH=%h/.local/bin:... so the graph
|
|
1598
|
+
// MCP shim's spawn("uvx", ...) resolves against uv's install location. Without
|
|
1599
|
+
// this line, the service inherits the default systemd-user PATH — which
|
|
1600
|
+
// excludes ~/.local/bin — and the shim exits ENOENT before any query lands.
|
|
1601
|
+
logFile(` ${BRAND.serviceName}: PATH env includes %h/.local/bin for uvx resolution`);
|
|
1589
1602
|
// WiFi AP provisioning service — system-level (requires root for hostapd/dnsmasq).
|
|
1590
1603
|
// Runs at boot to check connectivity. If no saved WiFi and no ethernet, activates
|
|
1591
1604
|
// a temporary AP with a captive portal for first-time WiFi configuration.
|
|
@@ -1678,6 +1691,7 @@ WantedBy=multi-user.target
|
|
|
1678
1691
|
vncLog = `(no boot log found at ${vncLogPath})`;
|
|
1679
1692
|
}
|
|
1680
1693
|
console.error("");
|
|
1694
|
+
console.error("Setup failed: Browser automation unavailable — CDP port 9222 not responding");
|
|
1681
1695
|
console.error(" ERROR: Browser automation unavailable — CDP port 9222 not responding.");
|
|
1682
1696
|
console.error(" Chromium should be started by vnc.sh (ExecStartPre). Check the boot log:");
|
|
1683
1697
|
console.error("");
|
|
@@ -1698,6 +1712,7 @@ WantedBy=multi-user.target
|
|
|
1698
1712
|
}
|
|
1699
1713
|
catch (err) {
|
|
1700
1714
|
console.error("");
|
|
1715
|
+
console.error(`Setup failed: tunnel script symlink missing or not executable: ${linkPath}`);
|
|
1701
1716
|
console.error(` [create-maxy:error] tunnel script symlink missing or not executable: ${linkPath}`);
|
|
1702
1717
|
console.error(` ${err.message}`);
|
|
1703
1718
|
process.exit(1);
|
|
@@ -1715,6 +1730,7 @@ if (_args.includes("--uninstall")) {
|
|
|
1715
1730
|
const exportPath = exportIdx !== -1 ? _args[exportIdx + 1] : undefined;
|
|
1716
1731
|
const skipConfirm = _args.includes("--yes");
|
|
1717
1732
|
if (exportIdx !== -1 && !exportPath) {
|
|
1733
|
+
console.error("Setup failed: --export-data requires a path argument");
|
|
1718
1734
|
console.error("--export-data requires a path argument.");
|
|
1719
1735
|
process.exit(1);
|
|
1720
1736
|
}
|
|
@@ -1735,6 +1751,7 @@ if (portIdx !== -1) {
|
|
|
1735
1751
|
const raw = _args[portIdx + 1];
|
|
1736
1752
|
const parsed = raw ? parseInt(raw, 10) : NaN;
|
|
1737
1753
|
if (isNaN(parsed) || parsed < 1024 || parsed > 65535) {
|
|
1754
|
+
console.error(`Setup failed: --port requires a numeric value between 1024 and 65535 (got: ${raw ?? "nothing"})`);
|
|
1738
1755
|
console.error(`Error: --port requires a numeric value between 1024 and 65535 (got: ${raw ?? "nothing"}).`);
|
|
1739
1756
|
process.exit(1);
|
|
1740
1757
|
}
|
|
@@ -1791,11 +1808,13 @@ const hostnameIdx = _args.indexOf("--hostname");
|
|
|
1791
1808
|
if (hostnameIdx !== -1) {
|
|
1792
1809
|
const raw = _args[hostnameIdx + 1];
|
|
1793
1810
|
if (!raw || raw.startsWith("--")) {
|
|
1811
|
+
console.error("Setup failed: --hostname requires a value");
|
|
1794
1812
|
console.error("Error: --hostname requires a value (e.g. --hostname muvin).");
|
|
1795
1813
|
process.exit(1);
|
|
1796
1814
|
}
|
|
1797
1815
|
// RFC 1123: lowercase alphanumeric + hyphens, max 63 chars, no leading/trailing hyphen
|
|
1798
1816
|
if (!/^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/.test(raw)) {
|
|
1817
|
+
console.error(`Setup failed: --hostname value '${raw}' is invalid`);
|
|
1799
1818
|
console.error(`Error: --hostname value '${raw}' is invalid. Must be lowercase letters, digits, and hyphens only (max 63 chars, no leading/trailing hyphen).`);
|
|
1800
1819
|
process.exit(1);
|
|
1801
1820
|
}
|
|
@@ -1807,10 +1826,12 @@ const displayIdx = _args.indexOf("--display");
|
|
|
1807
1826
|
if (displayIdx !== -1) {
|
|
1808
1827
|
const raw = _args[displayIdx + 1];
|
|
1809
1828
|
if (!raw || raw.startsWith("--")) {
|
|
1829
|
+
console.error("Setup failed: --display requires a value");
|
|
1810
1830
|
console.error("Error: --display requires a value: native or virtual.");
|
|
1811
1831
|
process.exit(1);
|
|
1812
1832
|
}
|
|
1813
1833
|
if (raw !== "native" && raw !== "virtual") {
|
|
1834
|
+
console.error(`Setup failed: --display value '${raw}' is invalid`);
|
|
1814
1835
|
console.error(`Error: --display value '${raw}' is invalid. Must be 'native' or 'virtual'.`);
|
|
1815
1836
|
process.exit(1);
|
|
1816
1837
|
}
|
|
@@ -1858,12 +1879,14 @@ let EMBED_SOURCE = "default";
|
|
|
1858
1879
|
const embedModelIdx = _args.indexOf("--embed-model");
|
|
1859
1880
|
const embedDimsIdx = _args.indexOf("--embed-dimensions");
|
|
1860
1881
|
if (embedDimsIdx !== -1 && embedModelIdx === -1) {
|
|
1882
|
+
console.error("Setup failed: --embed-dimensions requires --embed-model");
|
|
1861
1883
|
console.error("Error: --embed-dimensions requires --embed-model (dimensions are model-specific).");
|
|
1862
1884
|
process.exit(1);
|
|
1863
1885
|
}
|
|
1864
1886
|
if (embedModelIdx !== -1) {
|
|
1865
1887
|
const raw = _args[embedModelIdx + 1];
|
|
1866
1888
|
if (!raw || raw.startsWith("--")) {
|
|
1889
|
+
console.error("Setup failed: --embed-model requires a value");
|
|
1867
1890
|
console.error("Error: --embed-model requires a value (e.g. --embed-model mxbai-embed-large).");
|
|
1868
1891
|
process.exit(1);
|
|
1869
1892
|
}
|
|
@@ -1874,6 +1897,7 @@ if (embedModelIdx !== -1) {
|
|
|
1874
1897
|
const rawDims = _args[embedDimsIdx + 1];
|
|
1875
1898
|
const parsed = rawDims ? parseInt(rawDims, 10) : NaN;
|
|
1876
1899
|
if (isNaN(parsed) || parsed <= 0) {
|
|
1900
|
+
console.error(`Setup failed: --embed-dimensions requires a positive integer (got: ${rawDims ?? "nothing"})`);
|
|
1877
1901
|
console.error(`Error: --embed-dimensions requires a positive integer (got: ${rawDims ?? "nothing"}).`);
|
|
1878
1902
|
process.exit(1);
|
|
1879
1903
|
}
|
|
@@ -1885,6 +1909,7 @@ if (embedModelIdx !== -1) {
|
|
|
1885
1909
|
}
|
|
1886
1910
|
else {
|
|
1887
1911
|
// Unknown model without explicit dimensions — cannot proceed
|
|
1912
|
+
console.error(`Setup failed: unknown embedding model '${EMBED_MODEL}'`);
|
|
1888
1913
|
console.error(`Error: Unknown embedding model '${EMBED_MODEL}'.`);
|
|
1889
1914
|
console.error("Known models and their dimensions:");
|
|
1890
1915
|
for (const [model, dims] of Object.entries(EMBED_MODEL_DIMS)) {
|
|
@@ -1937,6 +1962,7 @@ if (neo4jPortIdx !== -1) {
|
|
|
1937
1962
|
const raw = _args[neo4jPortIdx + 1];
|
|
1938
1963
|
const parsed = raw ? parseInt(raw, 10) : NaN;
|
|
1939
1964
|
if (isNaN(parsed) || parsed < 1024 || parsed > 65535) {
|
|
1965
|
+
console.error(`Setup failed: --neo4j-port requires a numeric value between 1024 and 65535 (got: ${raw ?? "nothing"})`);
|
|
1940
1966
|
console.error(`Error: --neo4j-port requires a numeric value between 1024 and 65535 (got: ${raw ?? "nothing"}).`);
|
|
1941
1967
|
process.exit(1);
|
|
1942
1968
|
}
|
package/package.json
CHANGED
|
@@ -32,6 +32,46 @@ const index_js_1 = require("../../mcp-stderr-tee/dist/index.js");
|
|
|
32
32
|
const SERVER_NAME = "graph";
|
|
33
33
|
const UPSTREAM_PACKAGE = "mcp-neo4j-cypher@0.6.0";
|
|
34
34
|
(0, index_js_1.initStderrTee)(SERVER_NAME);
|
|
35
|
+
// Sync-write failure line to the per-server raw file AND the per-conversation
|
|
36
|
+
// stream log. Used only on exit paths (missing password, uvx unresolvable,
|
|
37
|
+
// upstream spawn/exit error) where the async stderr tee's createWriteStream
|
|
38
|
+
// buffer will not flush before process.exit() outraces it. `appendFileSync`
|
|
39
|
+
// returns after the kernel has queued the write, so the line survives an
|
|
40
|
+
// immediate exit. Each destination is wrapped independently — an unwritable
|
|
41
|
+
// destination must not mask the primary failure. The final process.stderr.write
|
|
42
|
+
// goes through the patched writer installed by initStderrTee; any async tee
|
|
43
|
+
// writes it schedules are discarded by the subsequent process.exit, but its
|
|
44
|
+
// inline write to the original stderr still reaches Claude Code's consumer.
|
|
45
|
+
function syncEmit(line) {
|
|
46
|
+
const msg = `[graph-mcp] ${line}`;
|
|
47
|
+
const iso = new Date().toISOString();
|
|
48
|
+
const logDir = process.env.LOG_DIR;
|
|
49
|
+
if (logDir) {
|
|
50
|
+
try {
|
|
51
|
+
(0, node_fs_1.mkdirSync)(logDir, { recursive: true });
|
|
52
|
+
const date = iso.slice(0, 10);
|
|
53
|
+
(0, node_fs_1.appendFileSync)((0, node_path_1.resolve)(logDir, `mcp-${SERVER_NAME}-stderr-${date}.log`), `${msg}\n`);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
/* unwritable destination — preserve primary failure */
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const streamLogPath = process.env.STREAM_LOG_PATH;
|
|
60
|
+
if (streamLogPath) {
|
|
61
|
+
try {
|
|
62
|
+
(0, node_fs_1.appendFileSync)(streamLogPath, `[${iso}] [mcp:${SERVER_NAME}] ${msg}\n`);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
/* unwritable destination — preserve primary failure */
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
process.stderr.write(`${msg}\n`);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
/* stderr closed — nothing else to do */
|
|
73
|
+
}
|
|
74
|
+
}
|
|
35
75
|
function resolvePassword() {
|
|
36
76
|
if (process.env.NEO4J_PASSWORD)
|
|
37
77
|
return process.env.NEO4J_PASSWORD;
|
|
@@ -43,10 +83,57 @@ function resolvePassword() {
|
|
|
43
83
|
return (0, node_fs_1.readFileSync)(file, "utf-8").trim();
|
|
44
84
|
}
|
|
45
85
|
catch {
|
|
46
|
-
|
|
86
|
+
syncEmit(`password unavailable — file=${file} env=NEO4J_PASSWORD=unset`);
|
|
47
87
|
process.exit(1);
|
|
48
88
|
}
|
|
49
89
|
}
|
|
90
|
+
function resolveUvxPath() {
|
|
91
|
+
const envPath = process.env.UVX_PATH ?? "";
|
|
92
|
+
if (envPath) {
|
|
93
|
+
try {
|
|
94
|
+
(0, node_fs_1.accessSync)(envPath, node_fs_1.constants.X_OK);
|
|
95
|
+
return { path: envPath, source: "env", envChecked: envPath, homeLocalChecked: "skipped", whichChecked: "skipped" };
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
/* fall through */
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const home = process.env.HOME ?? "";
|
|
102
|
+
const homeLocalPath = home ? (0, node_path_1.resolve)(home, ".local/bin/uvx") : "";
|
|
103
|
+
if (homeLocalPath) {
|
|
104
|
+
try {
|
|
105
|
+
const stats = (0, node_fs_1.statSync)(homeLocalPath);
|
|
106
|
+
if (stats.isFile()) {
|
|
107
|
+
(0, node_fs_1.accessSync)(homeLocalPath, node_fs_1.constants.X_OK);
|
|
108
|
+
return { path: homeLocalPath, source: "home-local", envChecked: envPath || "unset", homeLocalChecked: homeLocalPath, whichChecked: "skipped" };
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
/* fall through */
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
let whichPath = "";
|
|
116
|
+
try {
|
|
117
|
+
whichPath = (0, node_child_process_1.execFileSync)("which", ["uvx"], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
whichPath = "";
|
|
121
|
+
}
|
|
122
|
+
if (whichPath) {
|
|
123
|
+
// Verify the result is still executable. `which` prints the first PATH
|
|
124
|
+
// hit by name; if the target was removed or the symlink dangles, the
|
|
125
|
+
// spawn would ENOENT and reintroduce the silent-fail class Task 560
|
|
126
|
+
// closes.
|
|
127
|
+
try {
|
|
128
|
+
(0, node_fs_1.accessSync)(whichPath, node_fs_1.constants.X_OK);
|
|
129
|
+
return { path: whichPath, source: "which", envChecked: envPath || "unset", homeLocalChecked: homeLocalPath || "unset", whichChecked: whichPath };
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
return { path: null, source: null, envChecked: envPath || "unset", homeLocalChecked: homeLocalPath || "unset", whichChecked: `${whichPath} (not-executable)` };
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return { path: null, source: null, envChecked: envPath || "unset", homeLocalChecked: homeLocalPath || "unset", whichChecked: "not-found" };
|
|
136
|
+
}
|
|
50
137
|
const brand = process.env.BRAND ?? "maxy";
|
|
51
138
|
const neo4jUri = process.env.NEO4J_URI ?? "bolt://localhost:7687";
|
|
52
139
|
const neo4jUser = process.env.NEO4J_USERNAME ?? process.env.NEO4J_USER ?? "neo4j";
|
|
@@ -68,7 +155,14 @@ const childEnv = {
|
|
|
68
155
|
};
|
|
69
156
|
console.error(`[graph-mcp] boot brand=${brand} uri=${neo4jUri} user=${neo4jUser} ` +
|
|
70
157
|
`namespace=${namespace} readOnly=${readOnly} tokenLimit=${responseTokenLimit}`);
|
|
71
|
-
const
|
|
158
|
+
const uvx = resolveUvxPath();
|
|
159
|
+
if (!uvx.path) {
|
|
160
|
+
syncEmit(`uvx unresolvable — checked env=${uvx.envChecked} home-local=${uvx.homeLocalChecked} which=${uvx.whichChecked}`);
|
|
161
|
+
syncEmit("install uv (if missing): curl -LsSf https://astral.sh/uv/install.sh | sh");
|
|
162
|
+
process.exit(127);
|
|
163
|
+
}
|
|
164
|
+
console.error(`[graph-mcp] uvx resolved path=${uvx.path} source=${uvx.source}`);
|
|
165
|
+
const child = (0, node_child_process_1.spawn)(uvx.path, [UPSTREAM_PACKAGE, "--transport", "stdio"], {
|
|
72
166
|
stdio: ["pipe", "pipe", "pipe"],
|
|
73
167
|
env: childEnv,
|
|
74
168
|
});
|
|
@@ -77,15 +171,11 @@ child.on("spawn", () => {
|
|
|
77
171
|
});
|
|
78
172
|
child.on("error", (err) => {
|
|
79
173
|
const msg = err instanceof Error ? err.message : String(err);
|
|
80
|
-
|
|
81
|
-
if (err.code === "ENOENT") {
|
|
82
|
-
console.error("[graph-mcp] uvx not found on PATH — install uv: " +
|
|
83
|
-
"curl -LsSf https://astral.sh/uv/install.sh | sh -s -- -y");
|
|
84
|
-
}
|
|
174
|
+
syncEmit(`spawn error: ${msg} uvx=${uvx.path ?? "unresolved"}`);
|
|
85
175
|
process.exit(127);
|
|
86
176
|
});
|
|
87
177
|
child.on("exit", (code, signal) => {
|
|
88
|
-
|
|
178
|
+
syncEmit(`uvx exited code=${code ?? "null"} signal=${signal ?? "null"}`);
|
|
89
179
|
process.exit(code ?? (signal ? 1 : 0));
|
|
90
180
|
});
|
|
91
181
|
// Forward child stderr to our own stderr so the stderr tee picks it up.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;AACA;;;;;;;;;;;;;;;;;;;;;;GAsBG;;AAEH,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
* own password. This shim trusts that — no query-layer tenant filtering.
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
import { spawn } from "node:child_process";
|
|
27
|
-
import { readFileSync } from "node:fs";
|
|
26
|
+
import { execFileSync, spawn } from "node:child_process";
|
|
27
|
+
import { accessSync, appendFileSync, constants, mkdirSync, readFileSync, statSync } from "node:fs";
|
|
28
28
|
import { resolve } from "node:path";
|
|
29
29
|
import { StringDecoder } from "node:string_decoder";
|
|
30
30
|
import { initStderrTee } from "../../mcp-stderr-tee/dist/index.js";
|
|
@@ -34,6 +34,44 @@ const UPSTREAM_PACKAGE = "mcp-neo4j-cypher@0.6.0";
|
|
|
34
34
|
|
|
35
35
|
initStderrTee(SERVER_NAME);
|
|
36
36
|
|
|
37
|
+
// Sync-write failure line to the per-server raw file AND the per-conversation
|
|
38
|
+
// stream log. Used only on exit paths (missing password, uvx unresolvable,
|
|
39
|
+
// upstream spawn/exit error) where the async stderr tee's createWriteStream
|
|
40
|
+
// buffer will not flush before process.exit() outraces it. `appendFileSync`
|
|
41
|
+
// returns after the kernel has queued the write, so the line survives an
|
|
42
|
+
// immediate exit. Each destination is wrapped independently — an unwritable
|
|
43
|
+
// destination must not mask the primary failure. The final process.stderr.write
|
|
44
|
+
// goes through the patched writer installed by initStderrTee; any async tee
|
|
45
|
+
// writes it schedules are discarded by the subsequent process.exit, but its
|
|
46
|
+
// inline write to the original stderr still reaches Claude Code's consumer.
|
|
47
|
+
function syncEmit(line: string): void {
|
|
48
|
+
const msg = `[graph-mcp] ${line}`;
|
|
49
|
+
const iso = new Date().toISOString();
|
|
50
|
+
const logDir = process.env.LOG_DIR;
|
|
51
|
+
if (logDir) {
|
|
52
|
+
try {
|
|
53
|
+
mkdirSync(logDir, { recursive: true });
|
|
54
|
+
const date = iso.slice(0, 10);
|
|
55
|
+
appendFileSync(resolve(logDir, `mcp-${SERVER_NAME}-stderr-${date}.log`), `${msg}\n`);
|
|
56
|
+
} catch {
|
|
57
|
+
/* unwritable destination — preserve primary failure */
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const streamLogPath = process.env.STREAM_LOG_PATH;
|
|
61
|
+
if (streamLogPath) {
|
|
62
|
+
try {
|
|
63
|
+
appendFileSync(streamLogPath, `[${iso}] [mcp:${SERVER_NAME}] ${msg}\n`);
|
|
64
|
+
} catch {
|
|
65
|
+
/* unwritable destination — preserve primary failure */
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
process.stderr.write(`${msg}\n`);
|
|
70
|
+
} catch {
|
|
71
|
+
/* stderr closed — nothing else to do */
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
37
75
|
function resolvePassword(): string {
|
|
38
76
|
if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
|
|
39
77
|
// __dirname points at `lib/graph-mcp/dist` after compilation; the platform
|
|
@@ -43,11 +81,70 @@ function resolvePassword(): string {
|
|
|
43
81
|
try {
|
|
44
82
|
return readFileSync(file, "utf-8").trim();
|
|
45
83
|
} catch {
|
|
46
|
-
|
|
84
|
+
syncEmit(`password unavailable — file=${file} env=NEO4J_PASSWORD=unset`);
|
|
47
85
|
process.exit(1);
|
|
48
86
|
}
|
|
49
87
|
}
|
|
50
88
|
|
|
89
|
+
// Resolve `uvx` to an absolute path at boot so the systemd-user default PATH
|
|
90
|
+
// (which excludes ~/.local/bin where the installer's installUv drops uvx)
|
|
91
|
+
// does not ENOENT the spawn. Precedence: UVX_PATH env override > ~/.local/bin/uvx
|
|
92
|
+
// (direct filesystem check, skips PATH lookup) > `which uvx` (catches custom
|
|
93
|
+
// installs). Each source is recorded in the diagnostic log line so a later
|
|
94
|
+
// "uvx unresolvable" emission names every checked location, not just a bare
|
|
95
|
+
// "not found".
|
|
96
|
+
interface UvxResolution {
|
|
97
|
+
path: string | null;
|
|
98
|
+
source: "env" | "home-local" | "which" | null;
|
|
99
|
+
envChecked: string;
|
|
100
|
+
homeLocalChecked: string;
|
|
101
|
+
whichChecked: string;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function resolveUvxPath(): UvxResolution {
|
|
105
|
+
const envPath = process.env.UVX_PATH ?? "";
|
|
106
|
+
if (envPath) {
|
|
107
|
+
try {
|
|
108
|
+
accessSync(envPath, constants.X_OK);
|
|
109
|
+
return { path: envPath, source: "env", envChecked: envPath, homeLocalChecked: "skipped", whichChecked: "skipped" };
|
|
110
|
+
} catch {
|
|
111
|
+
/* fall through */
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const home = process.env.HOME ?? "";
|
|
115
|
+
const homeLocalPath = home ? resolve(home, ".local/bin/uvx") : "";
|
|
116
|
+
if (homeLocalPath) {
|
|
117
|
+
try {
|
|
118
|
+
const stats = statSync(homeLocalPath);
|
|
119
|
+
if (stats.isFile()) {
|
|
120
|
+
accessSync(homeLocalPath, constants.X_OK);
|
|
121
|
+
return { path: homeLocalPath, source: "home-local", envChecked: envPath || "unset", homeLocalChecked: homeLocalPath, whichChecked: "skipped" };
|
|
122
|
+
}
|
|
123
|
+
} catch {
|
|
124
|
+
/* fall through */
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
let whichPath = "";
|
|
128
|
+
try {
|
|
129
|
+
whichPath = execFileSync("which", ["uvx"], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
130
|
+
} catch {
|
|
131
|
+
whichPath = "";
|
|
132
|
+
}
|
|
133
|
+
if (whichPath) {
|
|
134
|
+
// Verify the result is still executable. `which` prints the first PATH
|
|
135
|
+
// hit by name; if the target was removed or the symlink dangles, the
|
|
136
|
+
// spawn would ENOENT and reintroduce the silent-fail class Task 560
|
|
137
|
+
// closes.
|
|
138
|
+
try {
|
|
139
|
+
accessSync(whichPath, constants.X_OK);
|
|
140
|
+
return { path: whichPath, source: "which", envChecked: envPath || "unset", homeLocalChecked: homeLocalPath || "unset", whichChecked: whichPath };
|
|
141
|
+
} catch {
|
|
142
|
+
return { path: null, source: null, envChecked: envPath || "unset", homeLocalChecked: homeLocalPath || "unset", whichChecked: `${whichPath} (not-executable)` };
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return { path: null, source: null, envChecked: envPath || "unset", homeLocalChecked: homeLocalPath || "unset", whichChecked: "not-found" };
|
|
146
|
+
}
|
|
147
|
+
|
|
51
148
|
const brand = process.env.BRAND ?? "maxy";
|
|
52
149
|
const neo4jUri = process.env.NEO4J_URI ?? "bolt://localhost:7687";
|
|
53
150
|
const neo4jUser = process.env.NEO4J_USERNAME ?? process.env.NEO4J_USER ?? "neo4j";
|
|
@@ -74,7 +171,19 @@ console.error(
|
|
|
74
171
|
`namespace=${namespace} readOnly=${readOnly} tokenLimit=${responseTokenLimit}`,
|
|
75
172
|
);
|
|
76
173
|
|
|
77
|
-
const
|
|
174
|
+
const uvx = resolveUvxPath();
|
|
175
|
+
if (!uvx.path) {
|
|
176
|
+
syncEmit(
|
|
177
|
+
`uvx unresolvable — checked env=${uvx.envChecked} home-local=${uvx.homeLocalChecked} which=${uvx.whichChecked}`,
|
|
178
|
+
);
|
|
179
|
+
syncEmit(
|
|
180
|
+
"install uv (if missing): curl -LsSf https://astral.sh/uv/install.sh | sh",
|
|
181
|
+
);
|
|
182
|
+
process.exit(127);
|
|
183
|
+
}
|
|
184
|
+
console.error(`[graph-mcp] uvx resolved path=${uvx.path} source=${uvx.source}`);
|
|
185
|
+
|
|
186
|
+
const child = spawn(uvx.path, [UPSTREAM_PACKAGE, "--transport", "stdio"], {
|
|
78
187
|
stdio: ["pipe", "pipe", "pipe"],
|
|
79
188
|
env: childEnv,
|
|
80
189
|
});
|
|
@@ -85,18 +194,12 @@ child.on("spawn", () => {
|
|
|
85
194
|
|
|
86
195
|
child.on("error", (err) => {
|
|
87
196
|
const msg = err instanceof Error ? err.message : String(err);
|
|
88
|
-
|
|
89
|
-
if ((err as NodeJS.ErrnoException).code === "ENOENT") {
|
|
90
|
-
console.error(
|
|
91
|
-
"[graph-mcp] uvx not found on PATH — install uv: " +
|
|
92
|
-
"curl -LsSf https://astral.sh/uv/install.sh | sh -s -- -y",
|
|
93
|
-
);
|
|
94
|
-
}
|
|
197
|
+
syncEmit(`spawn error: ${msg} uvx=${uvx.path ?? "unresolved"}`);
|
|
95
198
|
process.exit(127);
|
|
96
199
|
});
|
|
97
200
|
|
|
98
201
|
child.on("exit", (code, signal) => {
|
|
99
|
-
|
|
202
|
+
syncEmit(`uvx exited code=${code ?? "null"} signal=${signal ?? "null"}`);
|
|
100
203
|
process.exit(code ?? (signal ? 1 : 0));
|
|
101
204
|
});
|
|
102
205
|
|
|
@@ -1235,16 +1235,15 @@ server.tool("session-resume", "Resume a previous session. Loads the selected ses
|
|
|
1235
1235
|
"and routes new messages to that conversation. Call after the user selects a session from the " +
|
|
1236
1236
|
"session-list results. Pass the conversationId from the selected session.", { conversationId: z.string().uuid().describe("The conversationId of the session to resume") }, async () => ({ content: [{ type: "text", text: "resumed" }] }));
|
|
1237
1237
|
server.tool("remote-auth-set-password", "Set the remote access password. Hashes with scrypt and writes to the platform's brand-specific config directory with mode 0600. " +
|
|
1238
|
-
"Validates strength (8+ chars, digit, special character, no
|
|
1238
|
+
"Validates strength (8+ chars, digit, special character, no spaces anywhere). " +
|
|
1239
1239
|
"Protects the admin interface when exposed over the tunnel — has no effect on the public endpoint or the tunnel itself. " +
|
|
1240
1240
|
"Set before `setup-tunnel.sh` — the script's post-restart verification curls the admin hostname and fails if remote-auth is not configured.", { password: z.string() }, async ({ password }) => {
|
|
1241
|
-
const trimmed = password.trim();
|
|
1242
1241
|
// Validate strength — same rules as the web server's validatePasswordStrength
|
|
1243
1242
|
const checks = [
|
|
1244
|
-
{ label: "at least 8 characters", met:
|
|
1245
|
-
{ label: "contains a number", met: /\d/.test(
|
|
1246
|
-
{ label: "contains a special character", met: /[^A-Za-z0-9]/.test(
|
|
1247
|
-
{ label: "no
|
|
1243
|
+
{ label: "at least 8 characters", met: password.length >= 8 },
|
|
1244
|
+
{ label: "contains a number", met: /\d/.test(password) },
|
|
1245
|
+
{ label: "contains a special character", met: /[^A-Za-z0-9]/.test(password) },
|
|
1246
|
+
{ label: "no spaces", met: password.length > 0 && !/\s/.test(password) },
|
|
1248
1247
|
];
|
|
1249
1248
|
const failed = checks.filter((c) => !c.met);
|
|
1250
1249
|
if (failed.length > 0) {
|
|
@@ -1257,7 +1256,7 @@ server.tool("remote-auth-set-password", "Set the remote access password. Hashes
|
|
|
1257
1256
|
const { scrypt, randomBytes } = await import("node:crypto");
|
|
1258
1257
|
const salt = randomBytes(32);
|
|
1259
1258
|
const hash = await new Promise((res, rej) => {
|
|
1260
|
-
scrypt(
|
|
1259
|
+
scrypt(password, salt, 64, { N: 16384, r: 8, p: 1 }, (err, key) => {
|
|
1261
1260
|
if (err)
|
|
1262
1261
|
rej(err);
|
|
1263
1262
|
else
|