@massu/core 0.6.2 → 0.6.3
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/README.md +40 -0
- package/dist/cli.js +442 -81
- package/package.json +1 -1
- package/src/mcp-bridge-tools.ts +459 -0
- package/src/tools.ts +8 -0
package/dist/cli.js
CHANGED
|
@@ -989,12 +989,12 @@ function addSummary(db, sessionId, summary) {
|
|
|
989
989
|
Math.floor(now.getTime() / 1e3)
|
|
990
990
|
);
|
|
991
991
|
}
|
|
992
|
-
function addUserPrompt(db, sessionId,
|
|
992
|
+
function addUserPrompt(db, sessionId, text19, promptNumber) {
|
|
993
993
|
const now = /* @__PURE__ */ new Date();
|
|
994
994
|
db.prepare(`
|
|
995
995
|
INSERT INTO user_prompts (session_id, prompt_text, prompt_number, created_at, created_at_epoch)
|
|
996
996
|
VALUES (?, ?, ?, ?, ?)
|
|
997
|
-
`).run(sessionId,
|
|
997
|
+
`).run(sessionId, text19, promptNumber, now.toISOString(), Math.floor(now.getTime() / 1e3));
|
|
998
998
|
}
|
|
999
999
|
function searchObservations(db, query, opts) {
|
|
1000
1000
|
const limit = opts?.limit ?? 20;
|
|
@@ -3980,7 +3980,7 @@ function parseFromImportLine(line, lineNum) {
|
|
|
3980
3980
|
function parsePlainImportLine(line, lineNum) {
|
|
3981
3981
|
const results = [];
|
|
3982
3982
|
const rest = line.replace(/^import\s+/, "");
|
|
3983
|
-
const parts = rest.split(",").map((
|
|
3983
|
+
const parts = rest.split(",").map((p19) => p19.trim()).filter((p19) => p19.length > 0);
|
|
3984
3984
|
for (const part of parts) {
|
|
3985
3985
|
const asMatch = part.match(/^(\S+)\s+as\s+(\S+)$/);
|
|
3986
3986
|
const moduleName = asMatch ? asMatch[1] : part;
|
|
@@ -4141,8 +4141,8 @@ function joinLogicalLines(source) {
|
|
|
4141
4141
|
}
|
|
4142
4142
|
return logical;
|
|
4143
4143
|
}
|
|
4144
|
-
function extractQuotedString(
|
|
4145
|
-
const m =
|
|
4144
|
+
function extractQuotedString(text19) {
|
|
4145
|
+
const m = text19.match(/(['"])(.*?)\1/);
|
|
4146
4146
|
return m ? m[2] : null;
|
|
4147
4147
|
}
|
|
4148
4148
|
function extractResponseModel(argStr) {
|
|
@@ -4209,8 +4209,8 @@ function parsePythonRoutes(source) {
|
|
|
4209
4209
|
const logicalLines = joinLogicalLines(source);
|
|
4210
4210
|
const pendingDecorators = [];
|
|
4211
4211
|
for (let i = 0; i < logicalLines.length; i++) {
|
|
4212
|
-
const { text:
|
|
4213
|
-
const trimmed =
|
|
4212
|
+
const { text: text19, startLine } = logicalLines[i];
|
|
4213
|
+
const trimmed = text19.trim();
|
|
4214
4214
|
const decoratorMatch = trimmed.match(
|
|
4215
4215
|
/^@\s*(\w+)\s*\.\s*(\w+)\s*\((.*)\)\s*$/s
|
|
4216
4216
|
);
|
|
@@ -4445,11 +4445,11 @@ function parseRelationship(argsStr) {
|
|
|
4445
4445
|
back_populates: bpMatch ? bpMatch[1] : null
|
|
4446
4446
|
};
|
|
4447
4447
|
}
|
|
4448
|
-
function findClosingParen(
|
|
4448
|
+
function findClosingParen(text19, openIndex, openChar, closeChar) {
|
|
4449
4449
|
let depth = 1;
|
|
4450
|
-
for (let i = openIndex + 1; i <
|
|
4451
|
-
if (
|
|
4452
|
-
else if (
|
|
4450
|
+
for (let i = openIndex + 1; i < text19.length; i++) {
|
|
4451
|
+
if (text19[i] === openChar) depth++;
|
|
4452
|
+
else if (text19[i] === closeChar) {
|
|
4453
4453
|
depth--;
|
|
4454
4454
|
if (depth === 0) return i;
|
|
4455
4455
|
}
|
|
@@ -4725,16 +4725,16 @@ function extractTableAndColumn(argsStr) {
|
|
|
4725
4725
|
}
|
|
4726
4726
|
return { table, column };
|
|
4727
4727
|
}
|
|
4728
|
-
function splitTopLevelCommas(
|
|
4728
|
+
function splitTopLevelCommas(text19) {
|
|
4729
4729
|
const parts = [];
|
|
4730
4730
|
let current = "";
|
|
4731
4731
|
let depth = 0;
|
|
4732
4732
|
let inString = null;
|
|
4733
|
-
for (let i = 0; i <
|
|
4734
|
-
const ch =
|
|
4733
|
+
for (let i = 0; i < text19.length; i++) {
|
|
4734
|
+
const ch = text19[i];
|
|
4735
4735
|
if (inString) {
|
|
4736
4736
|
current += ch;
|
|
4737
|
-
if (ch === inString &&
|
|
4737
|
+
if (ch === inString && text19[i - 1] !== "\\") {
|
|
4738
4738
|
inString = null;
|
|
4739
4739
|
}
|
|
4740
4740
|
continue;
|
|
@@ -5579,7 +5579,7 @@ function handleDocsAudit(args2) {
|
|
|
5579
5579
|
const fileName = basename3(file);
|
|
5580
5580
|
if (mapping.routers.includes(fileName)) {
|
|
5581
5581
|
const procedures = extractProcedureNames(file);
|
|
5582
|
-
const undocumented = procedures.filter((
|
|
5582
|
+
const undocumented = procedures.filter((p19) => !contentMentions(content, p19));
|
|
5583
5583
|
if (undocumented.length > 0) {
|
|
5584
5584
|
staleReasons.push(
|
|
5585
5585
|
`Router ${fileName}: procedures not documented: ${undocumented.slice(0, 5).join(", ")}${undocumented.length > 5 ? ` (+${undocumented.length - 5} more)` : ""}`
|
|
@@ -5905,26 +5905,26 @@ function handleToolPatterns(args2, db) {
|
|
|
5905
5905
|
case "tool":
|
|
5906
5906
|
lines.push("| Tool | Calls | Successes | Failures | Success Rate | Avg Output Size | Avg Input Size |");
|
|
5907
5907
|
lines.push("|------|-------|-----------|----------|--------------|-----------------|----------------|");
|
|
5908
|
-
for (const
|
|
5909
|
-
const total =
|
|
5910
|
-
const successes =
|
|
5911
|
-
const failures =
|
|
5908
|
+
for (const p19 of patterns) {
|
|
5909
|
+
const total = p19.call_count;
|
|
5910
|
+
const successes = p19.successes;
|
|
5911
|
+
const failures = p19.failures;
|
|
5912
5912
|
const rate = total > 0 ? Math.round(successes / total * 100) : 0;
|
|
5913
|
-
lines.push(`| ${
|
|
5913
|
+
lines.push(`| ${p19.tool_name} | ${total} | ${successes} | ${failures} | ${rate}% | ${Math.round(p19.avg_output_size ?? 0)} | ${Math.round(p19.avg_input_size ?? 0)} |`);
|
|
5914
5914
|
}
|
|
5915
5915
|
break;
|
|
5916
5916
|
case "session":
|
|
5917
5917
|
lines.push("| Session | Calls | Unique Tools | Successes | Failures | Avg Output Size |");
|
|
5918
5918
|
lines.push("|---------|-------|--------------|-----------|----------|-----------------|");
|
|
5919
|
-
for (const
|
|
5920
|
-
lines.push(`| ${
|
|
5919
|
+
for (const p19 of patterns) {
|
|
5920
|
+
lines.push(`| ${p19.session_id.slice(0, 8)}... | ${p19.call_count} | ${p19.unique_tools} | ${p19.successes} | ${p19.failures} | ${Math.round(p19.avg_output_size ?? 0)} |`);
|
|
5921
5921
|
}
|
|
5922
5922
|
break;
|
|
5923
5923
|
case "day":
|
|
5924
5924
|
lines.push("| Day | Calls | Unique Tools | Successes |");
|
|
5925
5925
|
lines.push("|-----|-------|--------------|-----------|");
|
|
5926
|
-
for (const
|
|
5927
|
-
lines.push(`| ${
|
|
5926
|
+
for (const p19 of patterns) {
|
|
5927
|
+
lines.push(`| ${p19.day} | ${p19.call_count} | ${p19.unique_tools} | ${p19.successes} |`);
|
|
5928
5928
|
}
|
|
5929
5929
|
break;
|
|
5930
5930
|
}
|
|
@@ -6572,15 +6572,15 @@ function handleDetail2(args2, db) {
|
|
|
6572
6572
|
}
|
|
6573
6573
|
if (detail.procedures.length > 0) {
|
|
6574
6574
|
lines.push(`### Procedures (${detail.procedures.length})`);
|
|
6575
|
-
for (const
|
|
6576
|
-
lines.push(`- ${
|
|
6575
|
+
for (const p19 of detail.procedures) {
|
|
6576
|
+
lines.push(`- ${p19.router_name}.${p19.procedure_name}${p19.procedure_type ? " (" + p19.procedure_type + ")" : ""}`);
|
|
6577
6577
|
}
|
|
6578
6578
|
lines.push("");
|
|
6579
6579
|
}
|
|
6580
6580
|
if (detail.pages.length > 0) {
|
|
6581
6581
|
lines.push(`### Pages (${detail.pages.length})`);
|
|
6582
|
-
for (const
|
|
6583
|
-
lines.push(`- ${
|
|
6582
|
+
for (const p19 of detail.pages) {
|
|
6583
|
+
lines.push(`- ${p19.page_route}${p19.portal ? " (" + p19.portal + ")" : ""}`);
|
|
6584
6584
|
}
|
|
6585
6585
|
lines.push("");
|
|
6586
6586
|
}
|
|
@@ -6663,7 +6663,7 @@ function handleValidate(args2, db) {
|
|
|
6663
6663
|
lines.push(` Missing components: ${item.missing_components.join(", ")}`);
|
|
6664
6664
|
}
|
|
6665
6665
|
if (item.missing_procedures.length > 0) {
|
|
6666
|
-
lines.push(` Missing procedures: ${item.missing_procedures.map((
|
|
6666
|
+
lines.push(` Missing procedures: ${item.missing_procedures.map((p19) => `${p19.router}.${p19.procedure}`).join(", ")}`);
|
|
6667
6667
|
}
|
|
6668
6668
|
if (item.missing_pages.length > 0) {
|
|
6669
6669
|
lines.push(` Missing pages: ${item.missing_pages.join(", ")}`);
|
|
@@ -6710,14 +6710,14 @@ function handleRegister(args2, db) {
|
|
|
6710
6710
|
}
|
|
6711
6711
|
const procedures = args2.procedures;
|
|
6712
6712
|
if (procedures) {
|
|
6713
|
-
for (const
|
|
6714
|
-
linkProcedure(db, featureId,
|
|
6713
|
+
for (const p19 of procedures) {
|
|
6714
|
+
linkProcedure(db, featureId, p19.router, p19.procedure, p19.type);
|
|
6715
6715
|
}
|
|
6716
6716
|
}
|
|
6717
6717
|
const pages = args2.pages;
|
|
6718
6718
|
if (pages) {
|
|
6719
|
-
for (const
|
|
6720
|
-
linkPage(db, featureId,
|
|
6719
|
+
for (const p19 of pages) {
|
|
6720
|
+
linkPage(db, featureId, p19.route, p19.portal);
|
|
6721
6721
|
}
|
|
6722
6722
|
}
|
|
6723
6723
|
logChange(db, featureId, "created", `Registered via ${p4("sentinel_register")}`);
|
|
@@ -9652,22 +9652,22 @@ function buildCrossReferences(db) {
|
|
|
9652
9652
|
}
|
|
9653
9653
|
const chunks = db.prepare("SELECT id, content, metadata FROM knowledge_chunks").all();
|
|
9654
9654
|
for (const chunk of chunks) {
|
|
9655
|
-
const
|
|
9656
|
-
const crRefs =
|
|
9655
|
+
const text19 = chunk.content;
|
|
9656
|
+
const crRefs = text19.match(/CR-\d+/g);
|
|
9657
9657
|
if (crRefs) {
|
|
9658
9658
|
for (const cr of [...new Set(crRefs)]) {
|
|
9659
9659
|
insertEdge.run("chunk", String(chunk.id), "cr", cr, "references");
|
|
9660
9660
|
edgeCount++;
|
|
9661
9661
|
}
|
|
9662
9662
|
}
|
|
9663
|
-
const vrRefs =
|
|
9663
|
+
const vrRefs = text19.match(/VR-[\w-]+/g);
|
|
9664
9664
|
if (vrRefs) {
|
|
9665
9665
|
for (const vr of [...new Set(vrRefs)]) {
|
|
9666
9666
|
insertEdge.run("chunk", String(chunk.id), "vr", vr, "references");
|
|
9667
9667
|
edgeCount++;
|
|
9668
9668
|
}
|
|
9669
9669
|
}
|
|
9670
|
-
const incRefs =
|
|
9670
|
+
const incRefs = text19.match(/Incident #(\d+)/gi);
|
|
9671
9671
|
if (incRefs) {
|
|
9672
9672
|
for (const ref of incRefs) {
|
|
9673
9673
|
const numMatch = ref.match(/\d+/);
|
|
@@ -9713,7 +9713,7 @@ function indexAllKnowledge(db) {
|
|
|
9713
9713
|
}
|
|
9714
9714
|
if (existsSync19(paths.docsDir)) {
|
|
9715
9715
|
const excludePatterns = getConfig().conventions?.excludePatterns ?? ["/ARCHIVE/", "/SESSION-HISTORY/"];
|
|
9716
|
-
const docsFiles = discoverMarkdownFiles(paths.docsDir).filter((f) => !f.includes("/plans/") && !excludePatterns.some((
|
|
9716
|
+
const docsFiles = discoverMarkdownFiles(paths.docsDir).filter((f) => !f.includes("/plans/") && !excludePatterns.some((p19) => f.includes(p19)));
|
|
9717
9717
|
files.push(...docsFiles);
|
|
9718
9718
|
}
|
|
9719
9719
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -9880,7 +9880,7 @@ function isKnowledgeStale(db) {
|
|
|
9880
9880
|
}
|
|
9881
9881
|
if (existsSync19(paths.docsDir)) {
|
|
9882
9882
|
const excludePatterns = getConfig().conventions?.excludePatterns ?? ["/ARCHIVE/", "/SESSION-HISTORY/"];
|
|
9883
|
-
const docsFiles = discoverMarkdownFiles(paths.docsDir).filter((f) => !f.includes("/plans/") && !excludePatterns.some((
|
|
9883
|
+
const docsFiles = discoverMarkdownFiles(paths.docsDir).filter((f) => !f.includes("/plans/") && !excludePatterns.some((p19) => f.includes(p19)));
|
|
9884
9884
|
files.push(...docsFiles);
|
|
9885
9885
|
}
|
|
9886
9886
|
for (const filePath of files) {
|
|
@@ -11734,13 +11734,368 @@ var init_python_tools = __esm({
|
|
|
11734
11734
|
}
|
|
11735
11735
|
});
|
|
11736
11736
|
|
|
11737
|
-
// src/tools.ts
|
|
11737
|
+
// src/mcp-bridge-tools.ts
|
|
11738
|
+
import { spawn } from "child_process";
|
|
11738
11739
|
import { readFileSync as readFileSync22, existsSync as existsSync22 } from "fs";
|
|
11739
|
-
import { resolve as resolve18
|
|
11740
|
+
import { resolve as resolve18 } from "path";
|
|
11741
|
+
function p17(baseName) {
|
|
11742
|
+
return `${getConfig().toolPrefix}_${baseName}`;
|
|
11743
|
+
}
|
|
11744
|
+
function buildSubprocessEnv() {
|
|
11745
|
+
const env = {};
|
|
11746
|
+
const projectName = getConfig().project?.name?.toUpperCase() || "";
|
|
11747
|
+
const safePrefixes = projectName ? [`${projectName}_CONFIG_`, `${projectName}_LOG_`] : [];
|
|
11748
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
11749
|
+
if (!value) continue;
|
|
11750
|
+
if (ENV_DENY_PATTERNS.some((pat) => key.includes(pat))) continue;
|
|
11751
|
+
if (ENV_ALLOW_LIST.has(key)) {
|
|
11752
|
+
env[key] = value;
|
|
11753
|
+
} else if (safePrefixes.length > 0 && safePrefixes.some((pfx) => key.startsWith(pfx))) {
|
|
11754
|
+
env[key] = value;
|
|
11755
|
+
}
|
|
11756
|
+
}
|
|
11757
|
+
return env;
|
|
11758
|
+
}
|
|
11759
|
+
function loadMcpConfig() {
|
|
11760
|
+
const root = getProjectRoot();
|
|
11761
|
+
const mcpPath = resolve18(root, ".mcp.json");
|
|
11762
|
+
if (!existsSync22(mcpPath)) return {};
|
|
11763
|
+
try {
|
|
11764
|
+
const raw = JSON.parse(readFileSync22(mcpPath, "utf-8"));
|
|
11765
|
+
const servers = {};
|
|
11766
|
+
const mcpServers = raw.mcpServers || {};
|
|
11767
|
+
const pfx = getConfig().toolPrefix;
|
|
11768
|
+
for (const [name, config] of Object.entries(mcpServers)) {
|
|
11769
|
+
if (name === pfx) continue;
|
|
11770
|
+
servers[name] = config;
|
|
11771
|
+
}
|
|
11772
|
+
return servers;
|
|
11773
|
+
} catch (e) {
|
|
11774
|
+
console.error("[mcp-bridge] Failed to parse .mcp.json:", e);
|
|
11775
|
+
return {};
|
|
11776
|
+
}
|
|
11777
|
+
}
|
|
11778
|
+
async function mcpRequest(conn, method, params = {}) {
|
|
11779
|
+
if (!conn.process || !conn.process.stdin || !conn.process.stdout) {
|
|
11780
|
+
throw new Error("MCP process not connected");
|
|
11781
|
+
}
|
|
11782
|
+
conn.requestId++;
|
|
11783
|
+
const request = {
|
|
11784
|
+
jsonrpc: "2.0",
|
|
11785
|
+
id: conn.requestId,
|
|
11786
|
+
method,
|
|
11787
|
+
params
|
|
11788
|
+
};
|
|
11789
|
+
return new Promise((resolve22, reject) => {
|
|
11790
|
+
const timeout = setTimeout(() => {
|
|
11791
|
+
conn.process?.stdout?.removeListener("data", onData);
|
|
11792
|
+
reject(new Error(`MCP request timed out: ${method}`));
|
|
11793
|
+
}, 3e4);
|
|
11794
|
+
let buffer2 = "";
|
|
11795
|
+
const onData = (data) => {
|
|
11796
|
+
buffer2 += data.toString("utf-8");
|
|
11797
|
+
const lines = buffer2.split("\n");
|
|
11798
|
+
buffer2 = lines.pop() || "";
|
|
11799
|
+
for (const line of lines) {
|
|
11800
|
+
if (!line.trim()) continue;
|
|
11801
|
+
try {
|
|
11802
|
+
const response = JSON.parse(line);
|
|
11803
|
+
if (response.id === conn.requestId) {
|
|
11804
|
+
clearTimeout(timeout);
|
|
11805
|
+
conn.process?.stdout?.removeListener("data", onData);
|
|
11806
|
+
if (response.error) {
|
|
11807
|
+
reject(new Error(`MCP error ${response.error.code}: ${response.error.message}`));
|
|
11808
|
+
} else {
|
|
11809
|
+
resolve22(response.result || {});
|
|
11810
|
+
}
|
|
11811
|
+
return;
|
|
11812
|
+
}
|
|
11813
|
+
} catch (e) {
|
|
11814
|
+
clearTimeout(timeout);
|
|
11815
|
+
conn.process?.stdout?.removeListener("data", onData);
|
|
11816
|
+
reject(new Error(`Failed to parse MCP response: ${e}`));
|
|
11817
|
+
return;
|
|
11818
|
+
}
|
|
11819
|
+
}
|
|
11820
|
+
};
|
|
11821
|
+
conn.process.stdout.on("data", onData);
|
|
11822
|
+
conn.process.stdin.write(JSON.stringify(request) + "\n");
|
|
11823
|
+
});
|
|
11824
|
+
}
|
|
11825
|
+
async function connectToServer(name, config) {
|
|
11826
|
+
const existing = connections.get(name);
|
|
11827
|
+
if (existing?.connected && existing.process && !existing.process.killed) {
|
|
11828
|
+
return existing;
|
|
11829
|
+
}
|
|
11830
|
+
const root = getProjectRoot();
|
|
11831
|
+
const cwd = config.cwd ? resolve18(root, config.cwd) : root;
|
|
11832
|
+
const args2 = config.args || [];
|
|
11833
|
+
const proc = spawn(config.command, args2, {
|
|
11834
|
+
cwd,
|
|
11835
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
11836
|
+
env: buildSubprocessEnv()
|
|
11837
|
+
});
|
|
11838
|
+
const conn = {
|
|
11839
|
+
config,
|
|
11840
|
+
process: proc,
|
|
11841
|
+
connected: false,
|
|
11842
|
+
tools: [],
|
|
11843
|
+
requestId: 0
|
|
11844
|
+
};
|
|
11845
|
+
try {
|
|
11846
|
+
await mcpRequest(conn, "initialize", {
|
|
11847
|
+
protocolVersion: "2024-11-05",
|
|
11848
|
+
capabilities: {},
|
|
11849
|
+
clientInfo: { name: "massu-mcp-bridge", version: "1.0.0" }
|
|
11850
|
+
});
|
|
11851
|
+
if (proc.stdin) {
|
|
11852
|
+
proc.stdin.write(JSON.stringify({
|
|
11853
|
+
jsonrpc: "2.0",
|
|
11854
|
+
method: "notifications/initialized",
|
|
11855
|
+
params: {}
|
|
11856
|
+
}) + "\n");
|
|
11857
|
+
}
|
|
11858
|
+
conn.connected = true;
|
|
11859
|
+
const toolsResult = await mcpRequest(conn, "tools/list", {});
|
|
11860
|
+
conn.tools = toolsResult.tools || [];
|
|
11861
|
+
connections.set(name, conn);
|
|
11862
|
+
return conn;
|
|
11863
|
+
} catch (e) {
|
|
11864
|
+
if (!proc.killed) proc.kill("SIGTERM");
|
|
11865
|
+
throw e;
|
|
11866
|
+
}
|
|
11867
|
+
}
|
|
11868
|
+
function disconnectServer(name) {
|
|
11869
|
+
const conn = connections.get(name);
|
|
11870
|
+
if (conn) {
|
|
11871
|
+
conn.connected = false;
|
|
11872
|
+
if (conn.process && !conn.process.killed) {
|
|
11873
|
+
conn.process.kill("SIGTERM");
|
|
11874
|
+
const proc = conn.process;
|
|
11875
|
+
setTimeout(() => {
|
|
11876
|
+
if (!proc.killed) {
|
|
11877
|
+
try {
|
|
11878
|
+
proc.kill("SIGKILL");
|
|
11879
|
+
} catch {
|
|
11880
|
+
}
|
|
11881
|
+
}
|
|
11882
|
+
}, 3e3);
|
|
11883
|
+
}
|
|
11884
|
+
connections.delete(name);
|
|
11885
|
+
}
|
|
11886
|
+
}
|
|
11887
|
+
function text17(s) {
|
|
11888
|
+
return { content: [{ type: "text", text: s }] };
|
|
11889
|
+
}
|
|
11890
|
+
function getMcpBridgeToolDefinitions() {
|
|
11891
|
+
return [
|
|
11892
|
+
{
|
|
11893
|
+
name: p17("mcp_servers"),
|
|
11894
|
+
description: "List all configured MCP servers from .mcp.json and their connection status.",
|
|
11895
|
+
inputSchema: {
|
|
11896
|
+
type: "object",
|
|
11897
|
+
properties: {},
|
|
11898
|
+
required: []
|
|
11899
|
+
}
|
|
11900
|
+
},
|
|
11901
|
+
{
|
|
11902
|
+
name: p17("mcp_tools"),
|
|
11903
|
+
description: "List tools available from a specific MCP server. Connects to the server if not already connected.",
|
|
11904
|
+
inputSchema: {
|
|
11905
|
+
type: "object",
|
|
11906
|
+
properties: {
|
|
11907
|
+
server: { type: "string", description: "MCP server name from .mcp.json" }
|
|
11908
|
+
},
|
|
11909
|
+
required: ["server"]
|
|
11910
|
+
}
|
|
11911
|
+
},
|
|
11912
|
+
{
|
|
11913
|
+
name: p17("mcp_call"),
|
|
11914
|
+
description: "Call a tool on a connected MCP server. Connects automatically if needed.",
|
|
11915
|
+
inputSchema: {
|
|
11916
|
+
type: "object",
|
|
11917
|
+
properties: {
|
|
11918
|
+
server: { type: "string", description: "MCP server name from .mcp.json" },
|
|
11919
|
+
tool: { type: "string", description: "Tool name to call on the remote server" },
|
|
11920
|
+
arguments: {
|
|
11921
|
+
type: "object",
|
|
11922
|
+
description: "Arguments to pass to the remote tool",
|
|
11923
|
+
additionalProperties: true
|
|
11924
|
+
}
|
|
11925
|
+
},
|
|
11926
|
+
required: ["server", "tool"]
|
|
11927
|
+
}
|
|
11928
|
+
},
|
|
11929
|
+
{
|
|
11930
|
+
name: p17("mcp_status"),
|
|
11931
|
+
description: "Health check all MCP server connections. Shows which are connected, disconnected, or errored.",
|
|
11932
|
+
inputSchema: {
|
|
11933
|
+
type: "object",
|
|
11934
|
+
properties: {},
|
|
11935
|
+
required: []
|
|
11936
|
+
}
|
|
11937
|
+
}
|
|
11938
|
+
];
|
|
11939
|
+
}
|
|
11940
|
+
function isMcpBridgeTool(name) {
|
|
11941
|
+
const pfx = getConfig().toolPrefix;
|
|
11942
|
+
return name.startsWith(`${pfx}_mcp_`);
|
|
11943
|
+
}
|
|
11944
|
+
async function handleMcpBridgeToolCall(name, args2) {
|
|
11945
|
+
const pfx = getConfig().toolPrefix;
|
|
11946
|
+
const baseName = name.startsWith(`${pfx}_`) ? name.slice(pfx.length + 1) : name;
|
|
11947
|
+
switch (baseName) {
|
|
11948
|
+
case "mcp_servers":
|
|
11949
|
+
return handleMcpServers();
|
|
11950
|
+
case "mcp_tools":
|
|
11951
|
+
return await handleMcpTools(args2.server);
|
|
11952
|
+
case "mcp_call":
|
|
11953
|
+
return await handleMcpCall(
|
|
11954
|
+
args2.server,
|
|
11955
|
+
args2.tool,
|
|
11956
|
+
args2.arguments || {}
|
|
11957
|
+
);
|
|
11958
|
+
case "mcp_status":
|
|
11959
|
+
return handleMcpStatus();
|
|
11960
|
+
default:
|
|
11961
|
+
return text17(`Unknown MCP bridge tool: ${name}`);
|
|
11962
|
+
}
|
|
11963
|
+
}
|
|
11964
|
+
function handleMcpServers() {
|
|
11965
|
+
const configs = loadMcpConfig();
|
|
11966
|
+
const servers = Object.entries(configs).map(([name, config]) => {
|
|
11967
|
+
const conn = connections.get(name);
|
|
11968
|
+
return {
|
|
11969
|
+
name,
|
|
11970
|
+
command: config.command,
|
|
11971
|
+
args: config.args || [],
|
|
11972
|
+
cwd: config.cwd || ".",
|
|
11973
|
+
status: conn?.connected ? "connected" : "disconnected",
|
|
11974
|
+
toolCount: conn?.tools.length || 0
|
|
11975
|
+
};
|
|
11976
|
+
});
|
|
11977
|
+
if (servers.length === 0) {
|
|
11978
|
+
return text17("No MCP servers configured in .mcp.json (excluding self).");
|
|
11979
|
+
}
|
|
11980
|
+
const lines = ["# MCP Servers\n"];
|
|
11981
|
+
for (const srv of servers) {
|
|
11982
|
+
const status = srv.status === "connected" ? "CONNECTED" : "DISCONNECTED";
|
|
11983
|
+
lines.push(`- **${srv.name}** [${status}] \u2014 \`${srv.command} ${srv.args.join(" ")}\` (${srv.toolCount} tools)`);
|
|
11984
|
+
}
|
|
11985
|
+
return text17(lines.join("\n"));
|
|
11986
|
+
}
|
|
11987
|
+
async function handleMcpTools(server) {
|
|
11988
|
+
const configs = loadMcpConfig();
|
|
11989
|
+
const config = configs[server];
|
|
11990
|
+
if (!config) {
|
|
11991
|
+
return text17(`MCP server "${server}" not found in .mcp.json. Available: ${Object.keys(configs).join(", ") || "none"}`);
|
|
11992
|
+
}
|
|
11993
|
+
try {
|
|
11994
|
+
const conn = await connectToServer(server, config);
|
|
11995
|
+
if (conn.tools.length === 0) {
|
|
11996
|
+
return text17(`MCP server "${server}" is connected but has no tools.`);
|
|
11997
|
+
}
|
|
11998
|
+
const lines = [`# Tools from ${server} (${conn.tools.length})
|
|
11999
|
+
`];
|
|
12000
|
+
for (const tool of conn.tools) {
|
|
12001
|
+
lines.push(`- **${tool.name}**: ${tool.description}`);
|
|
12002
|
+
}
|
|
12003
|
+
return text17(lines.join("\n"));
|
|
12004
|
+
} catch (e) {
|
|
12005
|
+
return text17(`Failed to connect to MCP server "${server}": ${e}`);
|
|
12006
|
+
}
|
|
12007
|
+
}
|
|
12008
|
+
async function handleMcpCall(server, tool, args2) {
|
|
12009
|
+
const configs = loadMcpConfig();
|
|
12010
|
+
const config = configs[server];
|
|
12011
|
+
if (!config) {
|
|
12012
|
+
return text17(`MCP server "${server}" not found in .mcp.json.`);
|
|
12013
|
+
}
|
|
12014
|
+
try {
|
|
12015
|
+
const conn = await connectToServer(server, config);
|
|
12016
|
+
const result = await mcpRequest(conn, "tools/call", { name: tool, arguments: args2 });
|
|
12017
|
+
const content = result.content;
|
|
12018
|
+
if (Array.isArray(content)) {
|
|
12019
|
+
const texts = content.filter((c) => c.type === "text").map((c) => c.text);
|
|
12020
|
+
if (result.isError) {
|
|
12021
|
+
return text17(`MCP tool error: ${texts.join("\n")}`);
|
|
12022
|
+
}
|
|
12023
|
+
return text17(texts.join("\n"));
|
|
12024
|
+
}
|
|
12025
|
+
return text17(JSON.stringify(result, null, 2));
|
|
12026
|
+
} catch (e) {
|
|
12027
|
+
const conn = connections.get(server);
|
|
12028
|
+
if (conn) conn.connected = false;
|
|
12029
|
+
return text17(`MCP call failed (${server}/${tool}): ${e}`);
|
|
12030
|
+
}
|
|
12031
|
+
}
|
|
12032
|
+
function handleMcpStatus() {
|
|
12033
|
+
const configs = loadMcpConfig();
|
|
12034
|
+
const lines = ["# MCP Connection Status\n"];
|
|
12035
|
+
for (const [name] of Object.entries(configs)) {
|
|
12036
|
+
const conn = connections.get(name);
|
|
12037
|
+
if (!conn) {
|
|
12038
|
+
lines.push(`- **${name}**: NOT CONNECTED`);
|
|
12039
|
+
} else if (conn.connected && conn.process && !conn.process.killed) {
|
|
12040
|
+
lines.push(`- **${name}**: HEALTHY (pid=${conn.process.pid}, ${conn.tools.length} tools)`);
|
|
12041
|
+
} else {
|
|
12042
|
+
lines.push(`- **${name}**: DISCONNECTED`);
|
|
12043
|
+
disconnectServer(name);
|
|
12044
|
+
}
|
|
12045
|
+
}
|
|
12046
|
+
if (Object.keys(configs).length === 0) {
|
|
12047
|
+
lines.push("No MCP servers configured.");
|
|
12048
|
+
}
|
|
12049
|
+
return text17(lines.join("\n"));
|
|
12050
|
+
}
|
|
12051
|
+
var connections, ENV_ALLOW_LIST, ENV_DENY_PATTERNS;
|
|
12052
|
+
var init_mcp_bridge_tools = __esm({
|
|
12053
|
+
"src/mcp-bridge-tools.ts"() {
|
|
12054
|
+
"use strict";
|
|
12055
|
+
init_config();
|
|
12056
|
+
connections = /* @__PURE__ */ new Map();
|
|
12057
|
+
process.on("exit", () => {
|
|
12058
|
+
for (const [, conn] of connections) {
|
|
12059
|
+
if (conn.process && !conn.process.killed) {
|
|
12060
|
+
try {
|
|
12061
|
+
conn.process.kill("SIGTERM");
|
|
12062
|
+
} catch {
|
|
12063
|
+
}
|
|
12064
|
+
}
|
|
12065
|
+
}
|
|
12066
|
+
});
|
|
12067
|
+
process.on("SIGTERM", () => {
|
|
12068
|
+
for (const [name] of connections) disconnectServer(name);
|
|
12069
|
+
process.exit(0);
|
|
12070
|
+
});
|
|
12071
|
+
ENV_ALLOW_LIST = /* @__PURE__ */ new Set([
|
|
12072
|
+
"PATH",
|
|
12073
|
+
"HOME",
|
|
12074
|
+
"LANG",
|
|
12075
|
+
"LC_ALL",
|
|
12076
|
+
"TERM",
|
|
12077
|
+
"PYTHONPATH",
|
|
12078
|
+
"NODE_PATH"
|
|
12079
|
+
]);
|
|
12080
|
+
ENV_DENY_PATTERNS = [
|
|
12081
|
+
"PRIVATE_KEY",
|
|
12082
|
+
"SECRET_KEY",
|
|
12083
|
+
"API_SECRET",
|
|
12084
|
+
"AUTH_DISABLED",
|
|
12085
|
+
"COLD_STORAGE",
|
|
12086
|
+
"PASSWORD",
|
|
12087
|
+
"TOKEN"
|
|
12088
|
+
];
|
|
12089
|
+
}
|
|
12090
|
+
});
|
|
12091
|
+
|
|
12092
|
+
// src/tools.ts
|
|
12093
|
+
import { readFileSync as readFileSync23, existsSync as existsSync23 } from "fs";
|
|
12094
|
+
import { resolve as resolve19, basename as basename7 } from "path";
|
|
11740
12095
|
function prefix() {
|
|
11741
12096
|
return getConfig().toolPrefix;
|
|
11742
12097
|
}
|
|
11743
|
-
function
|
|
12098
|
+
function p18(name) {
|
|
11744
12099
|
return `${prefix()}_${name}`;
|
|
11745
12100
|
}
|
|
11746
12101
|
function stripPrefix3(name) {
|
|
@@ -11771,7 +12126,7 @@ function ensureIndexes(dataDb2, codegraphDb2, force = false) {
|
|
|
11771
12126
|
if (config.python?.root) {
|
|
11772
12127
|
const pythonRoot = config.python.root;
|
|
11773
12128
|
const excludeDirs = config.python.exclude_dirs || ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"];
|
|
11774
|
-
if (force || isPythonDataStale(dataDb2,
|
|
12129
|
+
if (force || isPythonDataStale(dataDb2, resolve19(getProjectRoot(), pythonRoot))) {
|
|
11775
12130
|
const pyImports = buildPythonImportIndex(dataDb2, pythonRoot, excludeDirs);
|
|
11776
12131
|
results.push(`Python imports: ${pyImports}`);
|
|
11777
12132
|
const pyRoutes = buildPythonRouteIndex(dataDb2, pythonRoot, excludeDirs);
|
|
@@ -11820,11 +12175,13 @@ function getToolDefinitions() {
|
|
|
11820
12175
|
...getKnowledgeToolDefinitions(),
|
|
11821
12176
|
// Python code intelligence tools
|
|
11822
12177
|
...getPythonToolDefinitions(),
|
|
12178
|
+
// MCP bridge tools (cross-project tool mesh)
|
|
12179
|
+
...getMcpBridgeToolDefinitions(),
|
|
11823
12180
|
// License tools (always available)
|
|
11824
12181
|
...getLicenseToolDefinitions(),
|
|
11825
12182
|
// Core tools
|
|
11826
12183
|
{
|
|
11827
|
-
name:
|
|
12184
|
+
name: p18("sync"),
|
|
11828
12185
|
description: "Force rebuild all indexes (import edges, tRPC mappings, page deps, middleware tree). Run this after significant code changes.",
|
|
11829
12186
|
inputSchema: {
|
|
11830
12187
|
type: "object",
|
|
@@ -11833,7 +12190,7 @@ function getToolDefinitions() {
|
|
|
11833
12190
|
}
|
|
11834
12191
|
},
|
|
11835
12192
|
{
|
|
11836
|
-
name:
|
|
12193
|
+
name: p18("context"),
|
|
11837
12194
|
description: "Get context for a file: applicable rules, pattern warnings, schema mismatch alerts, and whether the file is in the middleware import tree.",
|
|
11838
12195
|
inputSchema: {
|
|
11839
12196
|
type: "object",
|
|
@@ -11845,7 +12202,7 @@ function getToolDefinitions() {
|
|
|
11845
12202
|
},
|
|
11846
12203
|
...config.framework.router === "trpc" ? [
|
|
11847
12204
|
{
|
|
11848
|
-
name:
|
|
12205
|
+
name: p18("trpc_map"),
|
|
11849
12206
|
description: "Map tRPC procedures to their UI call sites. Find which components call a router, which procedures have no UI callers, or list all procedures for a router.",
|
|
11850
12207
|
inputSchema: {
|
|
11851
12208
|
type: "object",
|
|
@@ -11858,7 +12215,7 @@ function getToolDefinitions() {
|
|
|
11858
12215
|
}
|
|
11859
12216
|
},
|
|
11860
12217
|
{
|
|
11861
|
-
name:
|
|
12218
|
+
name: p18("coupling_check"),
|
|
11862
12219
|
description: "Automated coupling check. Finds all procedures with zero UI callers and components not rendered in any page.",
|
|
11863
12220
|
inputSchema: {
|
|
11864
12221
|
type: "object",
|
|
@@ -11874,7 +12231,7 @@ function getToolDefinitions() {
|
|
|
11874
12231
|
}
|
|
11875
12232
|
] : [],
|
|
11876
12233
|
{
|
|
11877
|
-
name:
|
|
12234
|
+
name: p18("impact"),
|
|
11878
12235
|
description: "Full impact analysis for a file: which pages are affected, which database tables are in the chain, middleware tree membership, domain crossings.",
|
|
11879
12236
|
inputSchema: {
|
|
11880
12237
|
type: "object",
|
|
@@ -11885,7 +12242,7 @@ function getToolDefinitions() {
|
|
|
11885
12242
|
}
|
|
11886
12243
|
},
|
|
11887
12244
|
...config.domains.length > 0 ? [{
|
|
11888
|
-
name:
|
|
12245
|
+
name: p18("domains"),
|
|
11889
12246
|
description: "Domain boundary information. Classify a file into its domain, show cross-domain imports, or list all files in a domain.",
|
|
11890
12247
|
inputSchema: {
|
|
11891
12248
|
type: "object",
|
|
@@ -11898,7 +12255,7 @@ function getToolDefinitions() {
|
|
|
11898
12255
|
}
|
|
11899
12256
|
}] : [],
|
|
11900
12257
|
...config.framework.orm === "prisma" ? [{
|
|
11901
|
-
name:
|
|
12258
|
+
name: p18("schema"),
|
|
11902
12259
|
description: "Prisma schema cross-reference. Show columns for a table, detect mismatches between code and schema, or verify column references in a file.",
|
|
11903
12260
|
inputSchema: {
|
|
11904
12261
|
type: "object",
|
|
@@ -11916,7 +12273,7 @@ async function handleToolCall(name, args2, dataDb2, codegraphDb2) {
|
|
|
11916
12273
|
const userTier = await getCurrentTier();
|
|
11917
12274
|
const requiredTier = getToolTier(name);
|
|
11918
12275
|
if (!isToolAllowed(name, userTier)) {
|
|
11919
|
-
return
|
|
12276
|
+
return text18(`This tool requires ${requiredTier} tier. Current tier: ${userTier}. Upgrade at https://massu.ai/pricing`);
|
|
11920
12277
|
}
|
|
11921
12278
|
const syncMessage = ensureIndexes(dataDb2, codegraphDb2);
|
|
11922
12279
|
const pfx = prefix();
|
|
@@ -12034,6 +12391,9 @@ async function handleToolCall(name, args2, dataDb2, codegraphDb2) {
|
|
|
12034
12391
|
if (isPythonTool(name)) {
|
|
12035
12392
|
return handlePythonToolCall(name, args2, dataDb2);
|
|
12036
12393
|
}
|
|
12394
|
+
if (isMcpBridgeTool(name)) {
|
|
12395
|
+
return await handleMcpBridgeToolCall(name, args2);
|
|
12396
|
+
}
|
|
12037
12397
|
if (isLicenseTool(name)) {
|
|
12038
12398
|
const memDb = getMemoryDb();
|
|
12039
12399
|
try {
|
|
@@ -12059,26 +12419,26 @@ async function handleToolCall(name, args2, dataDb2, codegraphDb2) {
|
|
|
12059
12419
|
case "schema":
|
|
12060
12420
|
return handleSchema(args2);
|
|
12061
12421
|
default:
|
|
12062
|
-
return
|
|
12422
|
+
return text18(`Unknown tool: ${name}`);
|
|
12063
12423
|
}
|
|
12064
12424
|
} catch (error) {
|
|
12065
12425
|
const msg = error instanceof Error ? error.message : String(error);
|
|
12066
12426
|
const safeMsg = msg.split("\n")[0].replace(/\/(Users|home|var|tmp)\/[^\s:]+/g, "<path>");
|
|
12067
|
-
return
|
|
12427
|
+
return text18(`Error in ${name}: ${safeMsg}`);
|
|
12068
12428
|
}
|
|
12069
12429
|
}
|
|
12070
|
-
function
|
|
12430
|
+
function text18(content) {
|
|
12071
12431
|
return { content: [{ type: "text", text: content }] };
|
|
12072
12432
|
}
|
|
12073
12433
|
function handleSync(dataDb2, codegraphDb2) {
|
|
12074
12434
|
const result = ensureIndexes(dataDb2, codegraphDb2, true);
|
|
12075
12435
|
try {
|
|
12076
12436
|
const scanResult = runFeatureScan(dataDb2);
|
|
12077
|
-
return
|
|
12437
|
+
return text18(`${result}
|
|
12078
12438
|
|
|
12079
12439
|
Feature scan: ${scanResult.registered} features registered (${scanResult.fromProcedures} from procedures, ${scanResult.fromPages} from pages, ${scanResult.fromComponents} from components)`);
|
|
12080
12440
|
} catch (error) {
|
|
12081
|
-
return
|
|
12441
|
+
return text18(`${result}
|
|
12082
12442
|
|
|
12083
12443
|
Feature scan failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
12084
12444
|
}
|
|
@@ -12170,9 +12530,9 @@ function handleContext(file, dataDb2, codegraphDb2) {
|
|
|
12170
12530
|
try {
|
|
12171
12531
|
const resolvedPaths = getResolvedPaths();
|
|
12172
12532
|
const root = getProjectRoot();
|
|
12173
|
-
const absFilePath = ensureWithinRoot(
|
|
12174
|
-
if (
|
|
12175
|
-
const fileContent =
|
|
12533
|
+
const absFilePath = ensureWithinRoot(resolve19(resolvedPaths.srcDir, "..", file), root);
|
|
12534
|
+
if (existsSync23(absFilePath)) {
|
|
12535
|
+
const fileContent = readFileSync23(absFilePath, "utf-8").slice(0, 3e3);
|
|
12176
12536
|
const keywords = [];
|
|
12177
12537
|
if (fileContent.includes("ctx.db")) keywords.push("database", "schema");
|
|
12178
12538
|
if (fileContent.includes("BigInt") || fileContent.includes("Decimal")) keywords.push("BigInt", "serialization");
|
|
@@ -12294,7 +12654,7 @@ function handleContext(file, dataDb2, codegraphDb2) {
|
|
|
12294
12654
|
} finally {
|
|
12295
12655
|
memDb?.close();
|
|
12296
12656
|
}
|
|
12297
|
-
return
|
|
12657
|
+
return text18(lines.join("\n") || "No context available for this file.");
|
|
12298
12658
|
}
|
|
12299
12659
|
function handleTrpcMap(args2, dataDb2) {
|
|
12300
12660
|
const lines = [];
|
|
@@ -12369,7 +12729,7 @@ function handleTrpcMap(args2, dataDb2) {
|
|
|
12369
12729
|
lines.push('Use { router: "name" } to see details for a specific router.');
|
|
12370
12730
|
lines.push("Use { uncoupled: true } to see all procedures without UI callers.");
|
|
12371
12731
|
}
|
|
12372
|
-
return
|
|
12732
|
+
return text18(lines.join("\n"));
|
|
12373
12733
|
}
|
|
12374
12734
|
function handleCouplingCheck(args2, dataDb2, codegraphDb2) {
|
|
12375
12735
|
const lines = [];
|
|
@@ -12427,7 +12787,7 @@ function handleCouplingCheck(args2, dataDb2, codegraphDb2) {
|
|
|
12427
12787
|
}
|
|
12428
12788
|
const totalIssues = uncoupledProcs.length + orphanComponents.length;
|
|
12429
12789
|
lines.push(`### RESULT: ${totalIssues === 0 ? "PASS" : `FAIL (${totalIssues} issues)`}`);
|
|
12430
|
-
return
|
|
12790
|
+
return text18(lines.join("\n"));
|
|
12431
12791
|
}
|
|
12432
12792
|
function handleImpact2(file, dataDb2, codegraphDb2) {
|
|
12433
12793
|
const lines = [];
|
|
@@ -12435,9 +12795,9 @@ function handleImpact2(file, dataDb2, codegraphDb2) {
|
|
|
12435
12795
|
lines.push("");
|
|
12436
12796
|
const affectedPages = findAffectedPages(dataDb2, file);
|
|
12437
12797
|
if (affectedPages.length > 0) {
|
|
12438
|
-
const portals = [...new Set(affectedPages.map((
|
|
12439
|
-
const allTables = [...new Set(affectedPages.flatMap((
|
|
12440
|
-
const allRouters = [...new Set(affectedPages.flatMap((
|
|
12798
|
+
const portals = [...new Set(affectedPages.map((p19) => p19.portal))];
|
|
12799
|
+
const allTables = [...new Set(affectedPages.flatMap((p19) => p19.tables))];
|
|
12800
|
+
const allRouters = [...new Set(affectedPages.flatMap((p19) => p19.routers))];
|
|
12441
12801
|
lines.push(`### Pages Affected: ${affectedPages.length}`);
|
|
12442
12802
|
for (const page of affectedPages) {
|
|
12443
12803
|
lines.push(`- ${page.route} (${page.portal})`);
|
|
@@ -12489,7 +12849,7 @@ function handleImpact2(file, dataDb2, codegraphDb2) {
|
|
|
12489
12849
|
lines.push(`- -> ${crossing}`);
|
|
12490
12850
|
}
|
|
12491
12851
|
}
|
|
12492
|
-
return
|
|
12852
|
+
return text18(lines.join("\n"));
|
|
12493
12853
|
}
|
|
12494
12854
|
function handleDomains(args2, dataDb2, codegraphDb2) {
|
|
12495
12855
|
const lines = [];
|
|
@@ -12534,7 +12894,7 @@ function handleDomains(args2, dataDb2, codegraphDb2) {
|
|
|
12534
12894
|
for (const r of files.routers) lines.push(`- ${r}`);
|
|
12535
12895
|
lines.push("");
|
|
12536
12896
|
lines.push(`### Pages (${files.pages.length})`);
|
|
12537
|
-
for (const
|
|
12897
|
+
for (const p19 of files.pages) lines.push(`- ${p19}`);
|
|
12538
12898
|
lines.push("");
|
|
12539
12899
|
lines.push(`### Components (${files.components.length})`);
|
|
12540
12900
|
for (const c of files.components.slice(0, 30)) lines.push(`- ${c}`);
|
|
@@ -12545,7 +12905,7 @@ function handleDomains(args2, dataDb2, codegraphDb2) {
|
|
|
12545
12905
|
lines.push(`- **${domain.name}**: ${domain.routers.length} router patterns, imports from: ${domain.allowedImportsFrom.join(", ") || "any"}`);
|
|
12546
12906
|
}
|
|
12547
12907
|
}
|
|
12548
|
-
return
|
|
12908
|
+
return text18(lines.join("\n"));
|
|
12549
12909
|
}
|
|
12550
12910
|
function handleSchema(args2) {
|
|
12551
12911
|
const lines = [];
|
|
@@ -12570,7 +12930,7 @@ function handleSchema(args2) {
|
|
|
12570
12930
|
const tableName = args2.table;
|
|
12571
12931
|
const model = models.find((m) => m.tableName === tableName || m.name === tableName);
|
|
12572
12932
|
if (!model) {
|
|
12573
|
-
return
|
|
12933
|
+
return text18(`Model/table "${tableName}" not found in Prisma schema.`);
|
|
12574
12934
|
}
|
|
12575
12935
|
lines.push(`## ${model.name} (table: ${model.tableName})`);
|
|
12576
12936
|
lines.push("");
|
|
@@ -12596,11 +12956,11 @@ function handleSchema(args2) {
|
|
|
12596
12956
|
lines.push("Checking all column references against Prisma schema...");
|
|
12597
12957
|
lines.push("");
|
|
12598
12958
|
const projectRoot = getProjectRoot();
|
|
12599
|
-
const absPath = ensureWithinRoot(
|
|
12600
|
-
if (!
|
|
12601
|
-
return
|
|
12959
|
+
const absPath = ensureWithinRoot(resolve19(projectRoot, file), projectRoot);
|
|
12960
|
+
if (!existsSync23(absPath)) {
|
|
12961
|
+
return text18(`File not found: ${file}`);
|
|
12602
12962
|
}
|
|
12603
|
-
const source =
|
|
12963
|
+
const source = readFileSync23(absPath, "utf-8");
|
|
12604
12964
|
const config = getConfig();
|
|
12605
12965
|
const dbPattern = config.dbAccessPattern ?? "ctx.db.{table}";
|
|
12606
12966
|
const regexStr = dbPattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace("\\{table\\}", "(\\w+)");
|
|
@@ -12633,7 +12993,7 @@ function handleSchema(args2) {
|
|
|
12633
12993
|
}
|
|
12634
12994
|
}
|
|
12635
12995
|
}
|
|
12636
|
-
return
|
|
12996
|
+
return text18(lines.join("\n"));
|
|
12637
12997
|
}
|
|
12638
12998
|
var init_tools = __esm({
|
|
12639
12999
|
"src/tools.ts"() {
|
|
@@ -12673,13 +13033,14 @@ var init_tools = __esm({
|
|
|
12673
13033
|
init_python_tools();
|
|
12674
13034
|
init_config();
|
|
12675
13035
|
init_license();
|
|
13036
|
+
init_mcp_bridge_tools();
|
|
12676
13037
|
}
|
|
12677
13038
|
});
|
|
12678
13039
|
|
|
12679
13040
|
// src/server.ts
|
|
12680
13041
|
var server_exports = {};
|
|
12681
|
-
import { readFileSync as
|
|
12682
|
-
import { resolve as
|
|
13042
|
+
import { readFileSync as readFileSync24 } from "fs";
|
|
13043
|
+
import { resolve as resolve20, dirname as dirname11 } from "path";
|
|
12683
13044
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
12684
13045
|
function getDb() {
|
|
12685
13046
|
if (!codegraphDb) codegraphDb = getCodeGraphDb();
|
|
@@ -12774,7 +13135,7 @@ var init_server = __esm({
|
|
|
12774
13135
|
__dirname4 = dirname11(fileURLToPath4(import.meta.url));
|
|
12775
13136
|
PKG_VERSION = (() => {
|
|
12776
13137
|
try {
|
|
12777
|
-
const pkg = JSON.parse(
|
|
13138
|
+
const pkg = JSON.parse(readFileSync24(resolve20(__dirname4, "..", "package.json"), "utf-8"));
|
|
12778
13139
|
return pkg.version ?? "0.0.0";
|
|
12779
13140
|
} catch {
|
|
12780
13141
|
return "0.0.0";
|
|
@@ -12838,8 +13199,8 @@ var init_server = __esm({
|
|
|
12838
13199
|
});
|
|
12839
13200
|
|
|
12840
13201
|
// src/cli.ts
|
|
12841
|
-
import { readFileSync as
|
|
12842
|
-
import { resolve as
|
|
13202
|
+
import { readFileSync as readFileSync25 } from "fs";
|
|
13203
|
+
import { resolve as resolve21, dirname as dirname12 } from "path";
|
|
12843
13204
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
12844
13205
|
var __filename4 = fileURLToPath5(import.meta.url);
|
|
12845
13206
|
var __dirname5 = dirname12(__filename4);
|
|
@@ -12913,7 +13274,7 @@ Documentation: https://massu.ai/docs
|
|
|
12913
13274
|
}
|
|
12914
13275
|
function printVersion() {
|
|
12915
13276
|
try {
|
|
12916
|
-
const pkg = JSON.parse(
|
|
13277
|
+
const pkg = JSON.parse(readFileSync25(resolve21(__dirname5, "../package.json"), "utf-8"));
|
|
12917
13278
|
console.log(`massu v${pkg.version}`);
|
|
12918
13279
|
} catch {
|
|
12919
13280
|
console.log("massu v0.1.0");
|