lensmcp 1.17.4 → 1.18.0
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/bundled/main.js +105 -1
- package/bundled/shim.js +228 -109
- package/lib/mcp-mode.d.ts +13 -2
- package/lib/mcp-mode.d.ts.map +1 -1
- package/lib/mcp-mode.js +13 -2
- package/lib/workspace-scope.d.ts +22 -0
- package/lib/workspace-scope.d.ts.map +1 -1
- package/lib/workspace-scope.js +10 -0
- package/package.json +2 -2
- package/plugin/.claude-plugin/plugin.json +1 -1
package/bundled/main.js
CHANGED
|
@@ -21901,6 +21901,92 @@ function watchParentGateway(label) {
|
|
|
21901
21901
|
timer.unref();
|
|
21902
21902
|
}
|
|
21903
21903
|
|
|
21904
|
+
// servers/lensmcp-mcp/dist/shim/workspace.js
|
|
21905
|
+
import { existsSync as existsSync7, readFileSync as readFileSync5, realpathSync } from "node:fs";
|
|
21906
|
+
import { basename, dirname as dirname5, join as join5 } from "node:path";
|
|
21907
|
+
var BASE_PORTS = { dashboard: 4321, mcpHttp: 4500, bridge: 5747 };
|
|
21908
|
+
function readJson(path) {
|
|
21909
|
+
try {
|
|
21910
|
+
return JSON.parse(readFileSync5(path, "utf8"));
|
|
21911
|
+
} catch {
|
|
21912
|
+
return void 0;
|
|
21913
|
+
}
|
|
21914
|
+
}
|
|
21915
|
+
function hashToRange(s, range) {
|
|
21916
|
+
let h = 2166136261;
|
|
21917
|
+
for (let i = 0; i < s.length; i++) {
|
|
21918
|
+
h ^= s.charCodeAt(i);
|
|
21919
|
+
h = Math.imul(h, 16777619);
|
|
21920
|
+
}
|
|
21921
|
+
return Math.abs(h | 0) % range;
|
|
21922
|
+
}
|
|
21923
|
+
function slugify2(input) {
|
|
21924
|
+
return input.toLowerCase().replace(/^@[^/]+\//, "").replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "") || "workspace";
|
|
21925
|
+
}
|
|
21926
|
+
function resolveWorkspaceKey(root) {
|
|
21927
|
+
const nx = readJson(join5(root, "nx.json"));
|
|
21928
|
+
if (nx?.lensmcp?.key)
|
|
21929
|
+
return slugify2(nx.lensmcp.key);
|
|
21930
|
+
const pkg = readJson(join5(root, "package.json"));
|
|
21931
|
+
if (pkg?.name)
|
|
21932
|
+
return slugify2(pkg.name);
|
|
21933
|
+
if (nx?.name)
|
|
21934
|
+
return slugify2(nx.name);
|
|
21935
|
+
return slugify2(basename(root));
|
|
21936
|
+
}
|
|
21937
|
+
function deriveMcpHttpPort(key) {
|
|
21938
|
+
return BASE_PORTS.mcpHttp + hashToRange(key, 200);
|
|
21939
|
+
}
|
|
21940
|
+
function findWorkspaceRoot(start) {
|
|
21941
|
+
let nxFallback;
|
|
21942
|
+
let dir = start;
|
|
21943
|
+
for (; ; ) {
|
|
21944
|
+
if (existsSync7(join5(dir, ".lensmcp", "config.json")))
|
|
21945
|
+
return dir;
|
|
21946
|
+
if (nxFallback === void 0 && existsSync7(join5(dir, "nx.json")))
|
|
21947
|
+
nxFallback = dir;
|
|
21948
|
+
const parent = dirname5(dir);
|
|
21949
|
+
if (parent === dir)
|
|
21950
|
+
break;
|
|
21951
|
+
dir = parent;
|
|
21952
|
+
}
|
|
21953
|
+
return nxFallback ?? start;
|
|
21954
|
+
}
|
|
21955
|
+
function resolveWorkspaceScope(start, env = process.env) {
|
|
21956
|
+
const root = findWorkspaceRoot(start);
|
|
21957
|
+
const file2 = readJson(join5(root, ".lensmcp", "config.json"));
|
|
21958
|
+
const key = file2?.key ? slugify2(file2.key) : resolveWorkspaceKey(root);
|
|
21959
|
+
const mcpHttpPort = typeof file2?.ports?.mcpHttp === "number" ? file2.ports.mcpHttp : deriveMcpHttpPort(key);
|
|
21960
|
+
return {
|
|
21961
|
+
root,
|
|
21962
|
+
key,
|
|
21963
|
+
mcpHttpPort,
|
|
21964
|
+
eventFile: env["LENSMCP_EVENT_FILE"] ?? join5(root, ".lensmcp", "events.jsonl")
|
|
21965
|
+
};
|
|
21966
|
+
}
|
|
21967
|
+
var IDENTITY_PATH = "/lensmcp/identity";
|
|
21968
|
+
var IDENTITY_KIND = "lensmcp-mcp-identity";
|
|
21969
|
+
function canonicalRoot(path) {
|
|
21970
|
+
try {
|
|
21971
|
+
return realpathSync(path);
|
|
21972
|
+
} catch {
|
|
21973
|
+
return path;
|
|
21974
|
+
}
|
|
21975
|
+
}
|
|
21976
|
+
function resolveServerIdentity(env = process.env, cwd = process.cwd()) {
|
|
21977
|
+
const scope = resolveWorkspaceScope(cwd, env);
|
|
21978
|
+
const root = canonicalRoot(env["LENSMCP_WORKSPACE_ROOT"] ?? scope.root);
|
|
21979
|
+
const key = env["LENSMCP_WORKSPACE_KEY"] ? slugify2(env["LENSMCP_WORKSPACE_KEY"]) : env["LENSMCP_WORKSPACE_ROOT"] ? resolveWorkspaceScope(root, env).key : scope.key;
|
|
21980
|
+
return {
|
|
21981
|
+
kind: IDENTITY_KIND,
|
|
21982
|
+
key,
|
|
21983
|
+
root,
|
|
21984
|
+
eventFile: scope.eventFile,
|
|
21985
|
+
pid: process.pid,
|
|
21986
|
+
port: env["LENSMCP_PORT"] ? Number(env["LENSMCP_PORT"]) : void 0
|
|
21987
|
+
};
|
|
21988
|
+
}
|
|
21989
|
+
|
|
21904
21990
|
// servers/lensmcp-mcp/dist/main.js
|
|
21905
21991
|
watchParentGateway("mcp");
|
|
21906
21992
|
var { session, providers } = createLensSession();
|
|
@@ -21950,7 +22036,25 @@ if (transport === "stdio") {
|
|
|
21950
22036
|
});
|
|
21951
22037
|
} else {
|
|
21952
22038
|
const entryPath = process.env["LENSMCP_HTTP_PATH"] ?? "/mcp";
|
|
21953
|
-
const
|
|
22039
|
+
const identity = resolveServerIdentity();
|
|
22040
|
+
const identityRoute = {
|
|
22041
|
+
method: "GET",
|
|
22042
|
+
path: IDENTITY_PATH,
|
|
22043
|
+
handler: (_req, res) => {
|
|
22044
|
+
res.setHeader("content-type", "application/json");
|
|
22045
|
+
res.setHeader("cache-control", "no-store");
|
|
22046
|
+
res.end(JSON.stringify(identity));
|
|
22047
|
+
}
|
|
22048
|
+
};
|
|
22049
|
+
const http = transport === "socket" ? {
|
|
22050
|
+
socketPath: process.env["LENSMCP_SOCKET_PATH"] ?? "/tmp/lensmcp-mcp.sock",
|
|
22051
|
+
entryPath,
|
|
22052
|
+
routes: [identityRoute]
|
|
22053
|
+
} : {
|
|
22054
|
+
port: Number(process.env["LENSMCP_PORT"] ?? 3e3),
|
|
22055
|
+
entryPath,
|
|
22056
|
+
routes: [identityRoute]
|
|
22057
|
+
};
|
|
21954
22058
|
void FrontMcpInstance.bootstrap({
|
|
21955
22059
|
...serverConfig,
|
|
21956
22060
|
logging: { level: LogLevel.Info },
|
package/bundled/shim.js
CHANGED
|
@@ -8485,8 +8485,14 @@ async function ensureSharedServer(deps) {
|
|
|
8485
8485
|
const poll = deps.pollMs ?? READY_POLL_MS;
|
|
8486
8486
|
const ttl = deps.lockTtlMs ?? SPAWN_LOCK_TTL_MS;
|
|
8487
8487
|
const elapsed = () => deps.now() - started;
|
|
8488
|
+
const live = (outcome) => {
|
|
8489
|
+
const foreign = deps.verifyIdentity?.();
|
|
8490
|
+
if (foreign)
|
|
8491
|
+
return { outcome: "unavailable", reason: foreign, waitedMs: elapsed() };
|
|
8492
|
+
return { outcome, waitedMs: elapsed() };
|
|
8493
|
+
};
|
|
8488
8494
|
if (await deps.probe())
|
|
8489
|
-
return
|
|
8495
|
+
return live("already-running");
|
|
8490
8496
|
let holdsLock = deps.tryCreateLock(JSON.stringify({ pid: process.pid, at: deps.now() }));
|
|
8491
8497
|
if (!holdsLock) {
|
|
8492
8498
|
const existing = parseSpawnLock(deps.readLock());
|
|
@@ -8507,7 +8513,7 @@ async function ensureSharedServer(deps) {
|
|
|
8507
8513
|
while (elapsed() < timeout) {
|
|
8508
8514
|
await deps.sleep(poll);
|
|
8509
8515
|
if (await deps.probe())
|
|
8510
|
-
return
|
|
8516
|
+
return live("started");
|
|
8511
8517
|
if (deps.serverExited?.()) {
|
|
8512
8518
|
return {
|
|
8513
8519
|
outcome: "unavailable",
|
|
@@ -8528,7 +8534,7 @@ async function ensureSharedServer(deps) {
|
|
|
8528
8534
|
while (elapsed() < timeout) {
|
|
8529
8535
|
await deps.sleep(poll);
|
|
8530
8536
|
if (await deps.probe())
|
|
8531
|
-
return
|
|
8537
|
+
return live("waited");
|
|
8532
8538
|
const lock = parseSpawnLock(deps.readLock());
|
|
8533
8539
|
if (!isSpawnLockHeld(lock, deps.now(), deps.isAlive, ttl) && !await deps.probe()) {
|
|
8534
8540
|
return {
|
|
@@ -8590,7 +8596,9 @@ function startSharedServerWatched(opts) {
|
|
|
8590
8596
|
LENSMCP_TRANSPORT: "http",
|
|
8591
8597
|
LENSMCP_PORT: String(opts.port),
|
|
8592
8598
|
LENSMCP_EVENT_FILE: opts.eventFile,
|
|
8593
|
-
LENSMCP_SHARED_OWNER: "shim"
|
|
8599
|
+
LENSMCP_SHARED_OWNER: "shim",
|
|
8600
|
+
...opts.workspaceKey ? { LENSMCP_WORKSPACE_KEY: opts.workspaceKey } : {},
|
|
8601
|
+
...opts.workspaceRoot ? { LENSMCP_WORKSPACE_ROOT: opts.workspaceRoot } : {}
|
|
8594
8602
|
};
|
|
8595
8603
|
delete env["LENSMCP_PARENT_PID"];
|
|
8596
8604
|
const child = spawn(process.execPath, [opts.serverEntry], {
|
|
@@ -8616,6 +8624,174 @@ function startSharedServerWatched(opts) {
|
|
|
8616
8624
|
}
|
|
8617
8625
|
}
|
|
8618
8626
|
|
|
8627
|
+
// servers/lensmcp-mcp/dist/shim/workspace.js
|
|
8628
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, realpathSync } from "node:fs";
|
|
8629
|
+
import { basename, dirname, join as join2 } from "node:path";
|
|
8630
|
+
var BASE_PORTS = { dashboard: 4321, mcpHttp: 4500, bridge: 5747 };
|
|
8631
|
+
function readJson(path) {
|
|
8632
|
+
try {
|
|
8633
|
+
return JSON.parse(readFileSync2(path, "utf8"));
|
|
8634
|
+
} catch {
|
|
8635
|
+
return void 0;
|
|
8636
|
+
}
|
|
8637
|
+
}
|
|
8638
|
+
function hashToRange(s, range) {
|
|
8639
|
+
let h = 2166136261;
|
|
8640
|
+
for (let i = 0; i < s.length; i++) {
|
|
8641
|
+
h ^= s.charCodeAt(i);
|
|
8642
|
+
h = Math.imul(h, 16777619);
|
|
8643
|
+
}
|
|
8644
|
+
return Math.abs(h | 0) % range;
|
|
8645
|
+
}
|
|
8646
|
+
function slugify2(input) {
|
|
8647
|
+
return input.toLowerCase().replace(/^@[^/]+\//, "").replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "") || "workspace";
|
|
8648
|
+
}
|
|
8649
|
+
function resolveWorkspaceKey(root) {
|
|
8650
|
+
const nx = readJson(join2(root, "nx.json"));
|
|
8651
|
+
if (nx?.lensmcp?.key)
|
|
8652
|
+
return slugify2(nx.lensmcp.key);
|
|
8653
|
+
const pkg = readJson(join2(root, "package.json"));
|
|
8654
|
+
if (pkg?.name)
|
|
8655
|
+
return slugify2(pkg.name);
|
|
8656
|
+
if (nx?.name)
|
|
8657
|
+
return slugify2(nx.name);
|
|
8658
|
+
return slugify2(basename(root));
|
|
8659
|
+
}
|
|
8660
|
+
function deriveMcpHttpPort(key) {
|
|
8661
|
+
return BASE_PORTS.mcpHttp + hashToRange(key, 200);
|
|
8662
|
+
}
|
|
8663
|
+
function findWorkspaceRoot(start) {
|
|
8664
|
+
let nxFallback;
|
|
8665
|
+
let dir = start;
|
|
8666
|
+
for (; ; ) {
|
|
8667
|
+
if (existsSync2(join2(dir, ".lensmcp", "config.json")))
|
|
8668
|
+
return dir;
|
|
8669
|
+
if (nxFallback === void 0 && existsSync2(join2(dir, "nx.json")))
|
|
8670
|
+
nxFallback = dir;
|
|
8671
|
+
const parent = dirname(dir);
|
|
8672
|
+
if (parent === dir)
|
|
8673
|
+
break;
|
|
8674
|
+
dir = parent;
|
|
8675
|
+
}
|
|
8676
|
+
return nxFallback ?? start;
|
|
8677
|
+
}
|
|
8678
|
+
function resolveWorkspaceScope(start, env = process.env) {
|
|
8679
|
+
const root = findWorkspaceRoot(start);
|
|
8680
|
+
const file = readJson(join2(root, ".lensmcp", "config.json"));
|
|
8681
|
+
const key = file?.key ? slugify2(file.key) : resolveWorkspaceKey(root);
|
|
8682
|
+
const mcpHttpPort = typeof file?.ports?.mcpHttp === "number" ? file.ports.mcpHttp : deriveMcpHttpPort(key);
|
|
8683
|
+
return {
|
|
8684
|
+
root,
|
|
8685
|
+
key,
|
|
8686
|
+
mcpHttpPort,
|
|
8687
|
+
eventFile: env["LENSMCP_EVENT_FILE"] ?? join2(root, ".lensmcp", "events.jsonl")
|
|
8688
|
+
};
|
|
8689
|
+
}
|
|
8690
|
+
function sharedMcpUrl(port) {
|
|
8691
|
+
return `http://127.0.0.1:${port}/mcp`;
|
|
8692
|
+
}
|
|
8693
|
+
var IDENTITY_PATH = "/lensmcp/identity";
|
|
8694
|
+
var IDENTITY_KIND = "lensmcp-mcp-identity";
|
|
8695
|
+
function identityUrl(port) {
|
|
8696
|
+
return `http://127.0.0.1:${port}${IDENTITY_PATH}`;
|
|
8697
|
+
}
|
|
8698
|
+
function canonicalRoot(path) {
|
|
8699
|
+
try {
|
|
8700
|
+
return realpathSync(path);
|
|
8701
|
+
} catch {
|
|
8702
|
+
return path;
|
|
8703
|
+
}
|
|
8704
|
+
}
|
|
8705
|
+
function parseServerIdentity(value) {
|
|
8706
|
+
if (!value || typeof value !== "object")
|
|
8707
|
+
return void 0;
|
|
8708
|
+
const o = value;
|
|
8709
|
+
if (o["kind"] !== IDENTITY_KIND)
|
|
8710
|
+
return void 0;
|
|
8711
|
+
if (typeof o["key"] !== "string" || typeof o["root"] !== "string")
|
|
8712
|
+
return void 0;
|
|
8713
|
+
return {
|
|
8714
|
+
kind: IDENTITY_KIND,
|
|
8715
|
+
key: o["key"],
|
|
8716
|
+
root: o["root"],
|
|
8717
|
+
...typeof o["eventFile"] === "string" ? { eventFile: o["eventFile"] } : {},
|
|
8718
|
+
...typeof o["pid"] === "number" ? { pid: o["pid"] } : {},
|
|
8719
|
+
...typeof o["port"] === "number" ? { port: o["port"] } : {}
|
|
8720
|
+
};
|
|
8721
|
+
}
|
|
8722
|
+
function describeIdentityMismatch(expected, actual) {
|
|
8723
|
+
if (!actual)
|
|
8724
|
+
return void 0;
|
|
8725
|
+
if (actual.key !== expected.key) {
|
|
8726
|
+
return `it serves workspace "${actual.key}", not "${expected.key}"`;
|
|
8727
|
+
}
|
|
8728
|
+
const ours = canonicalRoot(expected.root);
|
|
8729
|
+
const theirs = canonicalRoot(actual.root);
|
|
8730
|
+
if (ours !== theirs) {
|
|
8731
|
+
return `it serves another checkout of "${actual.key}" (${theirs}), not ${ours}`;
|
|
8732
|
+
}
|
|
8733
|
+
return void 0;
|
|
8734
|
+
}
|
|
8735
|
+
|
|
8736
|
+
// servers/lensmcp-mcp/dist/shim/probe.js
|
|
8737
|
+
async function probeMcp(url2, timeoutMs) {
|
|
8738
|
+
const ctrl = new AbortController();
|
|
8739
|
+
const timer = setTimeout(() => ctrl.abort(), timeoutMs);
|
|
8740
|
+
try {
|
|
8741
|
+
const res = await fetch(url2, {
|
|
8742
|
+
method: "POST",
|
|
8743
|
+
signal: ctrl.signal,
|
|
8744
|
+
headers: {
|
|
8745
|
+
"content-type": "application/json",
|
|
8746
|
+
accept: "application/json, text/event-stream"
|
|
8747
|
+
},
|
|
8748
|
+
body: JSON.stringify({
|
|
8749
|
+
jsonrpc: "2.0",
|
|
8750
|
+
id: 1,
|
|
8751
|
+
method: "initialize",
|
|
8752
|
+
params: {
|
|
8753
|
+
protocolVersion: "2025-06-18",
|
|
8754
|
+
capabilities: {},
|
|
8755
|
+
clientInfo: { name: "lensmcp-shim-probe", version: "1" }
|
|
8756
|
+
}
|
|
8757
|
+
})
|
|
8758
|
+
});
|
|
8759
|
+
if (!res.ok)
|
|
8760
|
+
return false;
|
|
8761
|
+
const text = await res.text();
|
|
8762
|
+
return text.includes('"result"');
|
|
8763
|
+
} catch {
|
|
8764
|
+
return false;
|
|
8765
|
+
} finally {
|
|
8766
|
+
clearTimeout(timer);
|
|
8767
|
+
}
|
|
8768
|
+
}
|
|
8769
|
+
async function probeShared(port, timeoutMs = 2e3) {
|
|
8770
|
+
const ctrl = new AbortController();
|
|
8771
|
+
const timer = setTimeout(() => ctrl.abort(), timeoutMs);
|
|
8772
|
+
let answered = false;
|
|
8773
|
+
try {
|
|
8774
|
+
const res = await fetch(identityUrl(port), {
|
|
8775
|
+
signal: ctrl.signal,
|
|
8776
|
+
headers: { accept: "application/json" }
|
|
8777
|
+
});
|
|
8778
|
+
answered = true;
|
|
8779
|
+
if (res.ok) {
|
|
8780
|
+
const identity = parseServerIdentity(await res.json().catch(() => void 0));
|
|
8781
|
+
if (identity)
|
|
8782
|
+
return { alive: true, identity };
|
|
8783
|
+
} else {
|
|
8784
|
+
await res.text().catch(() => void 0);
|
|
8785
|
+
}
|
|
8786
|
+
} catch {
|
|
8787
|
+
} finally {
|
|
8788
|
+
clearTimeout(timer);
|
|
8789
|
+
}
|
|
8790
|
+
if (!answered)
|
|
8791
|
+
return { alive: false };
|
|
8792
|
+
return { alive: await probeMcp(sharedMcpUrl(port), timeoutMs) };
|
|
8793
|
+
}
|
|
8794
|
+
|
|
8619
8795
|
// servers/lensmcp-mcp/dist/shim/proxy.js
|
|
8620
8796
|
function isRequest(m) {
|
|
8621
8797
|
return typeof m.method === "string" && m.id !== void 0;
|
|
@@ -8769,73 +8945,6 @@ function startProxy(opts) {
|
|
|
8769
8945
|
};
|
|
8770
8946
|
}
|
|
8771
8947
|
|
|
8772
|
-
// servers/lensmcp-mcp/dist/shim/workspace.js
|
|
8773
|
-
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
|
|
8774
|
-
import { basename, dirname, join as join2 } from "node:path";
|
|
8775
|
-
var BASE_PORTS = { dashboard: 4321, mcpHttp: 4500, bridge: 5747 };
|
|
8776
|
-
function readJson(path) {
|
|
8777
|
-
try {
|
|
8778
|
-
return JSON.parse(readFileSync2(path, "utf8"));
|
|
8779
|
-
} catch {
|
|
8780
|
-
return void 0;
|
|
8781
|
-
}
|
|
8782
|
-
}
|
|
8783
|
-
function hashToRange(s, range) {
|
|
8784
|
-
let h = 2166136261;
|
|
8785
|
-
for (let i = 0; i < s.length; i++) {
|
|
8786
|
-
h ^= s.charCodeAt(i);
|
|
8787
|
-
h = Math.imul(h, 16777619);
|
|
8788
|
-
}
|
|
8789
|
-
return Math.abs(h | 0) % range;
|
|
8790
|
-
}
|
|
8791
|
-
function slugify2(input) {
|
|
8792
|
-
return input.toLowerCase().replace(/^@[^/]+\//, "").replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "") || "workspace";
|
|
8793
|
-
}
|
|
8794
|
-
function resolveWorkspaceKey(root) {
|
|
8795
|
-
const nx = readJson(join2(root, "nx.json"));
|
|
8796
|
-
if (nx?.lensmcp?.key)
|
|
8797
|
-
return slugify2(nx.lensmcp.key);
|
|
8798
|
-
const pkg = readJson(join2(root, "package.json"));
|
|
8799
|
-
if (pkg?.name)
|
|
8800
|
-
return slugify2(pkg.name);
|
|
8801
|
-
if (nx?.name)
|
|
8802
|
-
return slugify2(nx.name);
|
|
8803
|
-
return slugify2(basename(root));
|
|
8804
|
-
}
|
|
8805
|
-
function deriveMcpHttpPort(key) {
|
|
8806
|
-
return BASE_PORTS.mcpHttp + hashToRange(key, 200);
|
|
8807
|
-
}
|
|
8808
|
-
function findWorkspaceRoot(start) {
|
|
8809
|
-
let nxFallback;
|
|
8810
|
-
let dir = start;
|
|
8811
|
-
for (; ; ) {
|
|
8812
|
-
if (existsSync2(join2(dir, ".lensmcp", "config.json")))
|
|
8813
|
-
return dir;
|
|
8814
|
-
if (nxFallback === void 0 && existsSync2(join2(dir, "nx.json")))
|
|
8815
|
-
nxFallback = dir;
|
|
8816
|
-
const parent = dirname(dir);
|
|
8817
|
-
if (parent === dir)
|
|
8818
|
-
break;
|
|
8819
|
-
dir = parent;
|
|
8820
|
-
}
|
|
8821
|
-
return nxFallback ?? start;
|
|
8822
|
-
}
|
|
8823
|
-
function resolveWorkspaceScope(start, env = process.env) {
|
|
8824
|
-
const root = findWorkspaceRoot(start);
|
|
8825
|
-
const file = readJson(join2(root, ".lensmcp", "config.json"));
|
|
8826
|
-
const key = file?.key ? slugify2(file.key) : resolveWorkspaceKey(root);
|
|
8827
|
-
const mcpHttpPort = typeof file?.ports?.mcpHttp === "number" ? file.ports.mcpHttp : deriveMcpHttpPort(key);
|
|
8828
|
-
return {
|
|
8829
|
-
root,
|
|
8830
|
-
key,
|
|
8831
|
-
mcpHttpPort,
|
|
8832
|
-
eventFile: env["LENSMCP_EVENT_FILE"] ?? join2(root, ".lensmcp", "events.jsonl")
|
|
8833
|
-
};
|
|
8834
|
-
}
|
|
8835
|
-
function sharedMcpUrl(port) {
|
|
8836
|
-
return `http://127.0.0.1:${port}/mcp`;
|
|
8837
|
-
}
|
|
8838
|
-
|
|
8839
8948
|
// servers/lensmcp-mcp/dist/shim.js
|
|
8840
8949
|
function log(line) {
|
|
8841
8950
|
process.stderr.write(`${line}
|
|
@@ -8848,43 +8957,12 @@ function argValue(flag) {
|
|
|
8848
8957
|
function serverEntry() {
|
|
8849
8958
|
return join3(dirname2(fileURLToPath(import.meta.url)), "main.js");
|
|
8850
8959
|
}
|
|
8851
|
-
|
|
8852
|
-
const ctrl = new AbortController();
|
|
8853
|
-
const timer = setTimeout(() => ctrl.abort(), timeoutMs);
|
|
8854
|
-
try {
|
|
8855
|
-
const res = await fetch(url2, {
|
|
8856
|
-
method: "POST",
|
|
8857
|
-
signal: ctrl.signal,
|
|
8858
|
-
headers: {
|
|
8859
|
-
"content-type": "application/json",
|
|
8860
|
-
accept: "application/json, text/event-stream"
|
|
8861
|
-
},
|
|
8862
|
-
body: JSON.stringify({
|
|
8863
|
-
jsonrpc: "2.0",
|
|
8864
|
-
id: 1,
|
|
8865
|
-
method: "initialize",
|
|
8866
|
-
params: {
|
|
8867
|
-
protocolVersion: "2025-06-18",
|
|
8868
|
-
capabilities: {},
|
|
8869
|
-
clientInfo: { name: "lensmcp-shim-probe", version: "1" }
|
|
8870
|
-
}
|
|
8871
|
-
})
|
|
8872
|
-
});
|
|
8873
|
-
if (!res.ok)
|
|
8874
|
-
return false;
|
|
8875
|
-
const text = await res.text();
|
|
8876
|
-
return text.includes('"result"');
|
|
8877
|
-
} catch {
|
|
8878
|
-
return false;
|
|
8879
|
-
} finally {
|
|
8880
|
-
clearTimeout(timer);
|
|
8881
|
-
}
|
|
8882
|
-
}
|
|
8883
|
-
function runEmbedded(cwd, why) {
|
|
8960
|
+
function runEmbedded(cwd, why, advise = true) {
|
|
8884
8961
|
log(`[lensmcp] shared MCP server unavailable (${why}) \u2014 falling back to an embedded server.`);
|
|
8885
8962
|
log("[lensmcp] the lens works for this session, but it is pinned to the bundle it");
|
|
8886
8963
|
log("[lensmcp] starts with and will not pick up an upgrade until the session ends.");
|
|
8887
|
-
|
|
8964
|
+
if (advise)
|
|
8965
|
+
log("[lensmcp] to get the shared server: `lensmcp gateway start`");
|
|
8888
8966
|
const env = { ...process.env };
|
|
8889
8967
|
delete env["LENSMCP_MCP_MODE"];
|
|
8890
8968
|
env["LENSMCP_TRANSPORT"] = "stdio";
|
|
@@ -8898,16 +8976,48 @@ async function main() {
|
|
|
8898
8976
|
const scope = resolveWorkspaceScope(cwd);
|
|
8899
8977
|
const url2 = sharedMcpUrl(scope.mcpHttpPort);
|
|
8900
8978
|
log(`[lensmcp] shim \u2192 workspace "${scope.key}" (${scope.root}) \u2192 ${url2}`);
|
|
8979
|
+
let seen = { alive: false };
|
|
8980
|
+
let warnedUnknown = false;
|
|
8981
|
+
let refused = false;
|
|
8982
|
+
const verifyIdentity = () => {
|
|
8983
|
+
const mismatch = describeIdentityMismatch(scope, seen.identity);
|
|
8984
|
+
if (!mismatch) {
|
|
8985
|
+
if (seen.alive && !seen.identity && !warnedUnknown) {
|
|
8986
|
+
warnedUnknown = true;
|
|
8987
|
+
log(`[lensmcp] note: the server on :${scope.mcpHttpPort} does not report a workspace (it predates this check), so it cannot be verified as "${scope.key}". Restart it \u2014 \`lensmcp gateway restart\` \u2014 to get the check.`);
|
|
8988
|
+
}
|
|
8989
|
+
return void 0;
|
|
8990
|
+
}
|
|
8991
|
+
const theirs = seen.identity;
|
|
8992
|
+
refused = true;
|
|
8993
|
+
log("");
|
|
8994
|
+
log(`[lensmcp] REFUSING the shared MCP server on 127.0.0.1:${scope.mcpHttpPort} \u2014 ${mismatch}.`);
|
|
8995
|
+
log(`[lensmcp] this session's workspace : "${scope.key}" ${scope.root}`);
|
|
8996
|
+
log(`[lensmcp] the server's workspace : "${theirs?.key}" ${theirs?.root}` + (theirs?.pid ? ` (pid ${theirs.pid})` : ""));
|
|
8997
|
+
log("[lensmcp] Two workspaces derive the same MCP port. Attaching would show you the");
|
|
8998
|
+
log("[lensmcp] OTHER project's events, builds and flows, so this session will not.");
|
|
8999
|
+
log("[lensmcp] Fix: pin a distinct port in this workspace's .lensmcp/config.json \u2014");
|
|
9000
|
+
log(`[lensmcp] { "ports": { "mcpHttp": ${scope.mcpHttpPort + 1} } }`);
|
|
9001
|
+
log("[lensmcp] then restart this session. Any free 4500-4699 port works.");
|
|
9002
|
+
log("");
|
|
9003
|
+
return `the shared MCP port is served by workspace "${theirs?.key}", not "${scope.key}"`;
|
|
9004
|
+
};
|
|
8901
9005
|
const ensure = () => {
|
|
8902
9006
|
let watch;
|
|
8903
9007
|
return ensureSharedServer({
|
|
8904
|
-
probe: () =>
|
|
9008
|
+
probe: async () => {
|
|
9009
|
+
seen = await probeShared(scope.mcpHttpPort);
|
|
9010
|
+
return seen.alive;
|
|
9011
|
+
},
|
|
9012
|
+
verifyIdentity,
|
|
8905
9013
|
startServer: () => {
|
|
8906
9014
|
const started = startSharedServerWatched({
|
|
8907
9015
|
serverEntry: serverEntry(),
|
|
8908
9016
|
port: scope.mcpHttpPort,
|
|
8909
9017
|
eventFile: scope.eventFile,
|
|
8910
9018
|
cwd: scope.root,
|
|
9019
|
+
workspaceKey: scope.key,
|
|
9020
|
+
workspaceRoot: scope.root,
|
|
8911
9021
|
onLog: log
|
|
8912
9022
|
});
|
|
8913
9023
|
watch = started.exited;
|
|
@@ -8922,7 +9032,7 @@ async function main() {
|
|
|
8922
9032
|
};
|
|
8923
9033
|
const ensured = await ensure();
|
|
8924
9034
|
if (ensured.outcome === "unavailable") {
|
|
8925
|
-
runEmbedded(cwd, ensured.reason ?? "unknown");
|
|
9035
|
+
runEmbedded(cwd, ensured.reason ?? "unknown", !refused);
|
|
8926
9036
|
return;
|
|
8927
9037
|
}
|
|
8928
9038
|
log(`[lensmcp] shim: shared server ${ensured.outcome} (${ensured.waitedMs}ms)`);
|
|
@@ -8936,8 +9046,17 @@ async function main() {
|
|
|
8936
9046
|
}
|
|
8937
9047
|
return new StreamableHTTPClientTransport(new URL(url2));
|
|
8938
9048
|
},
|
|
8939
|
-
//
|
|
8940
|
-
|
|
9049
|
+
// Filters blips so a transient error does not cost a live session — but a
|
|
9050
|
+
// server that has become a STRANGER is not a blip. Reporting it unhealthy
|
|
9051
|
+
// sends the pump into `connect()`, which re-ensures, refuses, and ends the
|
|
9052
|
+
// session cleanly; calling it healthy would instead leave the pump erroring
|
|
9053
|
+
// forever against a server that has never heard of our session id.
|
|
9054
|
+
isUpstreamHealthy: async () => {
|
|
9055
|
+
const now = await probeShared(scope.mcpHttpPort, 1e3);
|
|
9056
|
+
if (!now.alive)
|
|
9057
|
+
return false;
|
|
9058
|
+
return describeIdentityMismatch(scope, now.identity) === void 0;
|
|
9059
|
+
},
|
|
8941
9060
|
log,
|
|
8942
9061
|
onFatal: (reason) => log(`[lensmcp] shim: ${reason}`)
|
|
8943
9062
|
});
|
package/lib/mcp-mode.d.ts
CHANGED
|
@@ -36,14 +36,25 @@
|
|
|
36
36
|
* spawns the server, waits, and if it still cannot get one it falls back to
|
|
37
37
|
* today's embedded server so a session is NEVER left without the lens.
|
|
38
38
|
*
|
|
39
|
-
* The isolation boundary is the PORT
|
|
40
|
-
*
|
|
39
|
+
* The isolation boundary is the PORT — and the port is VERIFIED
|
|
40
|
+
* -------------------------------------------------------------
|
|
41
41
|
* One daemon hosts many workspaces (foodguard + tetros), and their observability
|
|
42
42
|
* data must never mix. Each workspace already reserves its own `ports.mcpHttp`
|
|
43
43
|
* in `.lensmcp/config.json` (deterministic per workspace key). The shim resolves
|
|
44
44
|
* that port from the workspace it was pointed at — NEVER a shared default. Two
|
|
45
45
|
* workspaces landing on one port would cross-feed each other's events.
|
|
46
46
|
*
|
|
47
|
+
* But the port is DERIVED (`4500 + fnv1a(key) % 200`), so two workspaces CAN
|
|
48
|
+
* hash onto one — and unlike the dashboard port it must not step up on
|
|
49
|
+
* collision, because the port IS the shim's contract. Two checkouts of one repo
|
|
50
|
+
* are worse: same key, same port, different event files. So the port alone is
|
|
51
|
+
* not proof of ownership. The shared server STATES which workspace it serves, on
|
|
52
|
+
* a `GET /lensmcp/identity` route mounted beside the MCP endpoint, and the shim
|
|
53
|
+
* REFUSES a server whose workspace is not its own — degrading to the embedded
|
|
54
|
+
* server rather than silently reading another project's events. It is checked
|
|
55
|
+
* before a single JSON-RPC frame is proxied, and costs nothing: that GET
|
|
56
|
+
* REPLACES the `initialize` probe the shim already had to make.
|
|
57
|
+
*
|
|
47
58
|
* This module is pure (values in, values out) so every rule below is
|
|
48
59
|
* unit-testable without a filesystem, a daemon, or an agent client.
|
|
49
60
|
*/
|
package/lib/mcp-mode.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-mode.d.ts","sourceRoot":"","sources":["../../src/lib/mcp-mode.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"mcp-mode.d.ts","sourceRoot":"","sources":["../../src/lib/mcp-mode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AAEH,sDAAsD;AACtD,MAAM,MAAM,OAAO;AACjB,yFAAyF;AACvF,UAAU;AACZ,sFAAsF;GACpF,QAAQ,CAAC;AAEb,sFAAsF;AACtF,eAAO,MAAM,gBAAgB,EAAE,OAAoB,CAAC;AAEpD,0FAA0F;AAC1F,eAAO,MAAM,qBAAqB,SAAS,CAAC;AAE5C;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,cAAc,CAAC;AAEnD,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,OAAO,CAE1D;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,MAAgC,GAAG,MAAM,CAEzF;AAED,MAAM,WAAW,aAAa;IAC5B,gFAAgF;IAChF,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,2EAA2E;IAC3E,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,+EAA+E;IAC/E,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,MAAM,GAAE,aAAkB,GAAG,OAAO,CAKlE;AAED,kFAAkF;AAClF,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB;AAcD,kFAAkF;AAClF,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,OAAO,GAAG,QAAQ,CASnF"}
|
package/lib/mcp-mode.js
CHANGED
|
@@ -36,14 +36,25 @@
|
|
|
36
36
|
* spawns the server, waits, and if it still cannot get one it falls back to
|
|
37
37
|
* today's embedded server so a session is NEVER left without the lens.
|
|
38
38
|
*
|
|
39
|
-
* The isolation boundary is the PORT
|
|
40
|
-
*
|
|
39
|
+
* The isolation boundary is the PORT — and the port is VERIFIED
|
|
40
|
+
* -------------------------------------------------------------
|
|
41
41
|
* One daemon hosts many workspaces (foodguard + tetros), and their observability
|
|
42
42
|
* data must never mix. Each workspace already reserves its own `ports.mcpHttp`
|
|
43
43
|
* in `.lensmcp/config.json` (deterministic per workspace key). The shim resolves
|
|
44
44
|
* that port from the workspace it was pointed at — NEVER a shared default. Two
|
|
45
45
|
* workspaces landing on one port would cross-feed each other's events.
|
|
46
46
|
*
|
|
47
|
+
* But the port is DERIVED (`4500 + fnv1a(key) % 200`), so two workspaces CAN
|
|
48
|
+
* hash onto one — and unlike the dashboard port it must not step up on
|
|
49
|
+
* collision, because the port IS the shim's contract. Two checkouts of one repo
|
|
50
|
+
* are worse: same key, same port, different event files. So the port alone is
|
|
51
|
+
* not proof of ownership. The shared server STATES which workspace it serves, on
|
|
52
|
+
* a `GET /lensmcp/identity` route mounted beside the MCP endpoint, and the shim
|
|
53
|
+
* REFUSES a server whose workspace is not its own — degrading to the embedded
|
|
54
|
+
* server rather than silently reading another project's events. It is checked
|
|
55
|
+
* before a single JSON-RPC frame is proxied, and costs nothing: that GET
|
|
56
|
+
* REPLACES the `initialize` probe the shim already had to make.
|
|
57
|
+
*
|
|
47
58
|
* This module is pure (values in, values out) so every rule below is
|
|
48
59
|
* unit-testable without a filesystem, a daemon, or an agent client.
|
|
49
60
|
*/
|
package/lib/workspace-scope.d.ts
CHANGED
|
@@ -44,6 +44,20 @@ export interface LensConfig {
|
|
|
44
44
|
* just because this field was added.
|
|
45
45
|
*/
|
|
46
46
|
mcp?: LensMcpConfig;
|
|
47
|
+
/**
|
|
48
|
+
* Optional. Dirs removed from the source-set staleness signature + pod scope
|
|
49
|
+
* (e.g. `["docs"]` so a docs write does not recycle every vite pod). READ by
|
|
50
|
+
* `@lensmcp/cluster` (`runtime/scope.ts` `readSourceSetExclude`); declared here
|
|
51
|
+
* so this writer round-trips it instead of dropping it.
|
|
52
|
+
*/
|
|
53
|
+
sourceSet?: {
|
|
54
|
+
exclude?: string[];
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Any key this version does not know about. Preserved verbatim on write —
|
|
58
|
+
* see the round-trip contract on `readLensConfig`.
|
|
59
|
+
*/
|
|
60
|
+
[key: string]: unknown;
|
|
47
61
|
}
|
|
48
62
|
/** Lowercase kebab slug; empty input → `workspace`. */
|
|
49
63
|
export declare function slugify(input: string): string;
|
|
@@ -62,6 +76,14 @@ export declare function deriveConfig(root: string): LensConfig;
|
|
|
62
76
|
* Read `<root>/.lensmcp/config.json` if present, else the derived defaults. A present
|
|
63
77
|
* file OVERRIDES derived values field-by-field (so a user can pin a key/port without
|
|
64
78
|
* losing the rest), and is the seam the gateway + CLI both read so they agree.
|
|
79
|
+
*
|
|
80
|
+
* ROUND-TRIP CONTRACT: every key on disk survives, including ones this version does
|
|
81
|
+
* not model. `ensureLensConfig` writes whatever this returns, so anything dropped
|
|
82
|
+
* here is silently DELETED from the user's file. That was a real bug: the result was
|
|
83
|
+
* built field-by-field from the four known keys, so a hand-set `sourceSet.exclude`
|
|
84
|
+
* (honored by `@lensmcp/cluster`, but not declared here) was stripped on every
|
|
85
|
+
* `lensmcp setup` — the docs-exclusion never took effect, and `docs/**` writes kept
|
|
86
|
+
* recycling every vite pod. Spread the file FIRST, then normalize; never enumerate.
|
|
65
87
|
*/
|
|
66
88
|
export declare function readLensConfig(root: string): LensConfig;
|
|
67
89
|
/** Persist the (derived/merged) config so it is visible + overridable. Idempotent;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-scope.d.ts","sourceRoot":"","sources":["../../src/lib/workspace-scope.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,WAAW,SAAS;IACxB,mFAAmF;IACnF,SAAS,EAAE,MAAM,CAAC;IAClB,kFAAkF;IAClF,OAAO,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;CAC3B;AAED,MAAM,WAAW,UAAU;IACzB,aAAa,EAAE,CAAC,CAAC;IACjB,4CAA4C;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,SAAS,CAAC;IACjB,+DAA+D;IAC/D,iBAAiB,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,GAAG,CAAC,EAAE,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"workspace-scope.d.ts","sourceRoot":"","sources":["../../src/lib/workspace-scope.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,WAAW,SAAS;IACxB,mFAAmF;IACnF,SAAS,EAAE,MAAM,CAAC;IAClB,kFAAkF;IAClF,OAAO,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;CAC3B;AAED,MAAM,WAAW,UAAU;IACzB,aAAa,EAAE,CAAC,CAAC;IACjB,4CAA4C;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,SAAS,CAAC;IACjB,+DAA+D;IAC/D,iBAAiB,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,GAAG,CAAC,EAAE,aAAa,CAAC;IACpB;;;;;OAKG;IACH,SAAS,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IACnC;;;OAGG;IACH,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAaD,uDAAuD;AACvD,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQ7C;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOxD;AAOD,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAOlD;AAED,8EAA8E;AAC9E,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAGrD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAiBvD;AAED;yDACyD;AACzD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAQzD"}
|
package/lib/workspace-scope.js
CHANGED
|
@@ -57,6 +57,14 @@ export function deriveConfig(root) {
|
|
|
57
57
|
* Read `<root>/.lensmcp/config.json` if present, else the derived defaults. A present
|
|
58
58
|
* file OVERRIDES derived values field-by-field (so a user can pin a key/port without
|
|
59
59
|
* losing the rest), and is the seam the gateway + CLI both read so they agree.
|
|
60
|
+
*
|
|
61
|
+
* ROUND-TRIP CONTRACT: every key on disk survives, including ones this version does
|
|
62
|
+
* not model. `ensureLensConfig` writes whatever this returns, so anything dropped
|
|
63
|
+
* here is silently DELETED from the user's file. That was a real bug: the result was
|
|
64
|
+
* built field-by-field from the four known keys, so a hand-set `sourceSet.exclude`
|
|
65
|
+
* (honored by `@lensmcp/cluster`, but not declared here) was stripped on every
|
|
66
|
+
* `lensmcp setup` — the docs-exclusion never took effect, and `docs/**` writes kept
|
|
67
|
+
* recycling every vite pod. Spread the file FIRST, then normalize; never enumerate.
|
|
60
68
|
*/
|
|
61
69
|
export function readLensConfig(root) {
|
|
62
70
|
const derived = deriveConfig(root);
|
|
@@ -65,6 +73,8 @@ export function readLensConfig(root) {
|
|
|
65
73
|
return derived;
|
|
66
74
|
const key = file.key ? slugify(file.key) : derived.key;
|
|
67
75
|
return {
|
|
76
|
+
// Unknown//future keys ride through untouched — the normalized fields below win.
|
|
77
|
+
...file,
|
|
68
78
|
schemaVersion: 1,
|
|
69
79
|
key,
|
|
70
80
|
ports: { ...derived.ports, ...(file.ports ?? {}) },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lensmcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.18.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
}
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@frontmcp/sdk": "^1.5.
|
|
20
|
+
"@frontmcp/sdk": "^1.5.7",
|
|
21
21
|
"reflect-metadata": "^0.2.2",
|
|
22
22
|
"tslib": "^2.3.0",
|
|
23
23
|
"vectoriadb": "^2.2.0"
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "lensmcp",
|
|
3
3
|
"displayName": "LensMCP",
|
|
4
4
|
"description": "The observability lens for coding agents. One command brings up the dev cluster gateway (every project.json `cluster` decl → its host on :443), the per-project lens dashboard at https://lensmcp.local/<project>/, and the MCP server your agent connects to — scoped automatically to whatever project you opened Claude Code in.",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.18.0",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "David Antoon",
|
|
8
8
|
"email": "davidmantoon@gmail.com"
|