@bynlk/codeomnivis 0.0.0-bootstrap.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/LICENSE +77 -0
- package/README.md +62 -0
- package/bin/codeomnivis.js +19 -0
- package/dist/chunk-D2FE2QSJ.js +10271 -0
- package/dist/dist-YNCI4IFF.js +462 -0
- package/dist/index.js +2219 -0
- package/dist/ui/assets/index-BlLl2ApB.css +1 -0
- package/dist/ui/assets/index-RYkt2Yk8.js +1 -0
- package/dist/ui/assets/vendor-cytoscape-CUqq0XTU.js +331 -0
- package/dist/ui/assets/vendor-i18n-DMayGKXS.js +1 -0
- package/dist/ui/assets/vendor-l0sNRNKZ.js +1 -0
- package/dist/ui/assets/vendor-query-CdMHRlIH.js +1 -0
- package/dist/ui/assets/vendor-react-CHCXY0b5.js +48 -0
- package/dist/ui/brand/favicon.svg +4 -0
- package/dist/ui/brand/logo-mark-dark.svg +5 -0
- package/dist/ui/brand/logo-mark-light.svg +5 -0
- package/dist/ui/brand/logo-mark-mono.svg +5 -0
- package/dist/ui/index.html +18 -0
- package/dist/wasm/tree-sitter-kotlin.wasm +0 -0
- package/package.json +87 -0
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DataFlowTracer,
|
|
3
|
+
OmniDatabase,
|
|
4
|
+
getDbPath,
|
|
5
|
+
hasDbCache,
|
|
6
|
+
isJsonObject,
|
|
7
|
+
isNodeOfType,
|
|
8
|
+
isTestFramework,
|
|
9
|
+
projectTestView,
|
|
10
|
+
runFullAnalysis
|
|
11
|
+
} from "./chunk-D2FE2QSJ.js";
|
|
12
|
+
|
|
13
|
+
// ../mcp/dist/index.js
|
|
14
|
+
import { pathToFileURL } from "url";
|
|
15
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
16
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
17
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
18
|
+
var API_NODE_TYPES = [
|
|
19
|
+
"api_route",
|
|
20
|
+
"trpc_procedure",
|
|
21
|
+
"express_route",
|
|
22
|
+
"tsrpc_api",
|
|
23
|
+
"tsrpc_service"
|
|
24
|
+
];
|
|
25
|
+
var API_DOWNSTREAM_EDGE_TYPES = ["handles", "calls_service", "queries_db"];
|
|
26
|
+
var API_CALLER_EDGE_TYPES = ["calls_api"];
|
|
27
|
+
var CALL_CHAIN_EDGE_TYPES = ["calls_api", "handles", "calls_service", "queries_db"];
|
|
28
|
+
var RENDERS_EDGE_TYPE = "renders";
|
|
29
|
+
var DB_MODEL_NODE_TYPE = "db_model";
|
|
30
|
+
var MAX_COMPONENT_TREE_DEPTH = 100;
|
|
31
|
+
function stringArg(args, key) {
|
|
32
|
+
if (!isJsonObject(args)) return void 0;
|
|
33
|
+
const value = args[key];
|
|
34
|
+
return typeof value === "string" ? value : void 0;
|
|
35
|
+
}
|
|
36
|
+
function validateDepth(args, fallback) {
|
|
37
|
+
const raw = isJsonObject(args) ? args.depth : void 0;
|
|
38
|
+
if (raw === void 0 || raw === null) return { ok: true, value: fallback };
|
|
39
|
+
let value;
|
|
40
|
+
if (typeof raw === "number") {
|
|
41
|
+
value = raw;
|
|
42
|
+
} else if (typeof raw === "string" && raw.trim() !== "") {
|
|
43
|
+
value = Number(raw);
|
|
44
|
+
} else {
|
|
45
|
+
return {
|
|
46
|
+
ok: false,
|
|
47
|
+
message: `depth must be a non-negative integer, got: ${JSON.stringify(raw)}`
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
if (!Number.isFinite(value) || !Number.isInteger(value)) {
|
|
51
|
+
return { ok: false, message: `depth must be a finite integer, got: ${JSON.stringify(raw)}` };
|
|
52
|
+
}
|
|
53
|
+
if (value < 0) return { ok: false, message: `depth must be >= 0, got: ${value}` };
|
|
54
|
+
if (value > MAX_COMPONENT_TREE_DEPTH) {
|
|
55
|
+
return {
|
|
56
|
+
ok: false,
|
|
57
|
+
message: `depth exceeds maximum ${MAX_COMPONENT_TREE_DEPTH}, got: ${value}`
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
return { ok: true, value };
|
|
61
|
+
}
|
|
62
|
+
function getRouteDisplay(node) {
|
|
63
|
+
if (isNodeOfType(node, "api_route")) {
|
|
64
|
+
return { method: node.metadata.method, path: node.metadata.route };
|
|
65
|
+
}
|
|
66
|
+
if (isNodeOfType(node, "express_route")) {
|
|
67
|
+
return { method: node.metadata.method, path: node.metadata.route };
|
|
68
|
+
}
|
|
69
|
+
if (isNodeOfType(node, "trpc_procedure")) {
|
|
70
|
+
return { method: node.metadata.procedureType.toUpperCase(), path: node.name };
|
|
71
|
+
}
|
|
72
|
+
if (isNodeOfType(node, "tsrpc_api")) {
|
|
73
|
+
return { method: node.metadata.transport.toUpperCase(), path: node.metadata.apiPath };
|
|
74
|
+
}
|
|
75
|
+
if (isNodeOfType(node, "tsrpc_service")) {
|
|
76
|
+
return { method: node.metadata.transport.toUpperCase(), path: node.metadata.servicePath };
|
|
77
|
+
}
|
|
78
|
+
return { method: "UNKNOWN", path: node.name };
|
|
79
|
+
}
|
|
80
|
+
function getNodeRoute(node) {
|
|
81
|
+
if (isNodeOfType(node, "page")) return node.metadata.route;
|
|
82
|
+
if (isNodeOfType(node, "api_route")) return node.metadata.route;
|
|
83
|
+
if (isNodeOfType(node, "express_route")) return node.metadata.route;
|
|
84
|
+
return node.name;
|
|
85
|
+
}
|
|
86
|
+
function success(data) {
|
|
87
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
88
|
+
}
|
|
89
|
+
function errorResponse(message) {
|
|
90
|
+
return {
|
|
91
|
+
content: [{ type: "text", text: JSON.stringify({ error: message }) }],
|
|
92
|
+
isError: true
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function handleGetApiRoutes(db, args) {
|
|
96
|
+
const filter = stringArg(args, "filter")?.toLowerCase();
|
|
97
|
+
const apiNodes = db.getNodesByTypes(API_NODE_TYPES);
|
|
98
|
+
const filtered = filter ? apiNodes.filter((node) => {
|
|
99
|
+
if (node.name.toLowerCase().includes(filter)) return true;
|
|
100
|
+
return getRouteDisplay(node).path.toLowerCase().includes(filter);
|
|
101
|
+
}) : apiNodes;
|
|
102
|
+
const routes = filtered.map((node) => {
|
|
103
|
+
const { method, path } = getRouteDisplay(node);
|
|
104
|
+
const downstream = db.getDownstreamNodes(node.id, API_DOWNSTREAM_EDGE_TYPES);
|
|
105
|
+
const callers = db.getUpstreamNodes(node.id, API_CALLER_EDGE_TYPES);
|
|
106
|
+
return {
|
|
107
|
+
id: node.id,
|
|
108
|
+
method,
|
|
109
|
+
path,
|
|
110
|
+
file: node.filePath,
|
|
111
|
+
line: node.line,
|
|
112
|
+
calledBy: callers.map((caller) => ({ id: caller.id, name: caller.name, type: caller.type })),
|
|
113
|
+
dbOperations: downstream.filter((candidate) => candidate.type === "db_model").map((model) => ({ model: model.name, file: model.filePath }))
|
|
114
|
+
};
|
|
115
|
+
});
|
|
116
|
+
return success({ routes, totalCount: routes.length });
|
|
117
|
+
}
|
|
118
|
+
function handleGetComponentTree(db, args) {
|
|
119
|
+
const rootPath = stringArg(args, "rootPath");
|
|
120
|
+
if (!rootPath) return errorResponse("rootPath is required");
|
|
121
|
+
const depthResult = validateDepth(args, 3);
|
|
122
|
+
if (!depthResult.ok) return errorResponse(depthResult.message);
|
|
123
|
+
const rootNode = db.findNodeByRoute(rootPath) ?? db.findNodeByFilePath(rootPath);
|
|
124
|
+
if (!rootNode) {
|
|
125
|
+
return success({
|
|
126
|
+
error: `No node found for: ${rootPath}`,
|
|
127
|
+
suggestion: 'Try with a file path like "app/booking/page.tsx" or a route like "/booking"'
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
const tree = db.getSubtree(rootNode.id, RENDERS_EDGE_TYPE, depthResult.value);
|
|
131
|
+
if (tree === null) {
|
|
132
|
+
return success({
|
|
133
|
+
error: `No node found for: ${rootPath}`,
|
|
134
|
+
suggestion: 'Try with a file path like "app/booking/page.tsx" or a route like "/booking"'
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
if (tree.children.length === 0) {
|
|
138
|
+
return success({ root: rootNode.name, children: [], message: "No child components found" });
|
|
139
|
+
}
|
|
140
|
+
return success(tree);
|
|
141
|
+
}
|
|
142
|
+
function handleFindCallers(db, args) {
|
|
143
|
+
const target = stringArg(args, "target");
|
|
144
|
+
if (!target) return errorResponse("target is required");
|
|
145
|
+
const targetNode = db.findNodeByAny(target);
|
|
146
|
+
if (!targetNode) {
|
|
147
|
+
return success({
|
|
148
|
+
error: `Not found: ${target}`,
|
|
149
|
+
suggestion: 'Try with a model name like "User" or a route like "/api/booking"'
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
const callers = db.getUpstreamNodes(targetNode.id, CALL_CHAIN_EDGE_TYPES);
|
|
153
|
+
const affectedPages = db.getAffectedPages(targetNode.id);
|
|
154
|
+
return success({
|
|
155
|
+
target: targetNode.name,
|
|
156
|
+
targetType: targetNode.type,
|
|
157
|
+
file: targetNode.filePath,
|
|
158
|
+
callers: callers.map((caller) => ({
|
|
159
|
+
id: caller.id,
|
|
160
|
+
type: caller.type,
|
|
161
|
+
name: caller.name,
|
|
162
|
+
file: caller.filePath
|
|
163
|
+
})),
|
|
164
|
+
affectedFrontendPages: affectedPages.map((page) => ({
|
|
165
|
+
name: page.name,
|
|
166
|
+
route: getNodeRoute(page),
|
|
167
|
+
file: page.filePath
|
|
168
|
+
}))
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
function handleListDbModels(db) {
|
|
172
|
+
const models = db.getNodesByType(DB_MODEL_NODE_TYPE);
|
|
173
|
+
return success({
|
|
174
|
+
models: models.map((model) => ({
|
|
175
|
+
id: model.id,
|
|
176
|
+
name: model.name,
|
|
177
|
+
file: model.filePath,
|
|
178
|
+
tableName: isNodeOfType(model, "db_model") ? model.metadata.tableName : model.name,
|
|
179
|
+
fieldCount: isNodeOfType(model, "db_model") ? model.metadata.fieldCount : 0
|
|
180
|
+
})),
|
|
181
|
+
totalCount: models.length
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
function handleGetDataFlow(db, args) {
|
|
185
|
+
const graph = db.loadGraph();
|
|
186
|
+
const tracer = new DataFlowTracer(graph);
|
|
187
|
+
const modelName = stringArg(args, "model");
|
|
188
|
+
if (modelName) {
|
|
189
|
+
const modelNode = graph.nodes.find(
|
|
190
|
+
(node) => node.type === "db_model" && node.name.toLowerCase() === modelName.toLowerCase()
|
|
191
|
+
);
|
|
192
|
+
if (!modelNode) {
|
|
193
|
+
return success({
|
|
194
|
+
error: `Model not found: ${modelName}`,
|
|
195
|
+
availableModels: graph.nodes.filter((node) => node.type === "db_model").map((node) => node.name)
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
const path = tracer.traceModelFlow(modelNode);
|
|
199
|
+
return success({
|
|
200
|
+
model: modelNode.name,
|
|
201
|
+
routes: path.apiNodes.map((node) => ({ name: node.name, file: node.filePath })),
|
|
202
|
+
components: path.componentNodes.map((node) => ({ name: node.name, file: node.filePath })),
|
|
203
|
+
summary: `${modelNode.name} \u2192 ${path.apiNodes.length} routes \u2192 ${path.componentNodes.length} components`
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
const results = tracer.traceAllModels();
|
|
207
|
+
return success({
|
|
208
|
+
models: results.map((result) => ({
|
|
209
|
+
name: result.modelName,
|
|
210
|
+
routes: result.totalRoutes,
|
|
211
|
+
components: result.totalComponents,
|
|
212
|
+
summary: `${result.modelName} \u2192 ${result.totalRoutes} routes \u2192 ${result.totalComponents} components`
|
|
213
|
+
})),
|
|
214
|
+
totalCount: results.length
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
function handleGetTestCoverage(db, args) {
|
|
218
|
+
const frameworkValue = isJsonObject(args) ? args.framework : void 0;
|
|
219
|
+
const framework = typeof frameworkValue === "string" && isTestFramework(frameworkValue) ? frameworkValue : void 0;
|
|
220
|
+
const targetValue = isJsonObject(args) ? args.target : void 0;
|
|
221
|
+
const target = typeof targetValue === "string" ? targetValue : void 0;
|
|
222
|
+
return {
|
|
223
|
+
content: [
|
|
224
|
+
{
|
|
225
|
+
type: "text",
|
|
226
|
+
text: JSON.stringify(projectTestView(db.loadGraph(), { framework, target }), null, 2)
|
|
227
|
+
}
|
|
228
|
+
]
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
var MCP_TOOL_NAMES = {
|
|
232
|
+
getApiRoutes: "get_api_routes",
|
|
233
|
+
getComponentTree: "get_component_tree",
|
|
234
|
+
findCallers: "find_callers",
|
|
235
|
+
listDbModels: "list_db_models",
|
|
236
|
+
getDataflow: "get_dataflow",
|
|
237
|
+
getTestCoverage: "get_test_coverage"
|
|
238
|
+
};
|
|
239
|
+
var PUBLIC_TOOL_NAMES = Object.freeze(Object.values(MCP_TOOL_NAMES));
|
|
240
|
+
var TOOL_DEFINITIONS = [
|
|
241
|
+
{
|
|
242
|
+
name: MCP_TOOL_NAMES.getApiRoutes,
|
|
243
|
+
description: "Get all API routes and their downstream database dependencies",
|
|
244
|
+
inputSchema: {
|
|
245
|
+
type: "object",
|
|
246
|
+
properties: {
|
|
247
|
+
filter: {
|
|
248
|
+
type: "string",
|
|
249
|
+
description: "Optional case-insensitive route or path filter"
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
name: MCP_TOOL_NAMES.getComponentTree,
|
|
256
|
+
description: "Get the component tree starting from a source path or route",
|
|
257
|
+
inputSchema: {
|
|
258
|
+
type: "object",
|
|
259
|
+
properties: {
|
|
260
|
+
rootPath: { type: "string", description: "Component file path or route" },
|
|
261
|
+
depth: { type: "number", description: "Maximum traversal depth; default 3" }
|
|
262
|
+
},
|
|
263
|
+
required: ["rootPath"]
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
name: MCP_TOOL_NAMES.findCallers,
|
|
268
|
+
description: "Find callers and affected pages for a node, route, or source path",
|
|
269
|
+
inputSchema: {
|
|
270
|
+
type: "object",
|
|
271
|
+
properties: {
|
|
272
|
+
target: { type: "string", description: "Node name, route, or source path" }
|
|
273
|
+
},
|
|
274
|
+
required: ["target"]
|
|
275
|
+
}
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
name: MCP_TOOL_NAMES.listDbModels,
|
|
279
|
+
description: "List database models discovered in the project",
|
|
280
|
+
inputSchema: { type: "object", properties: {} }
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
name: MCP_TOOL_NAMES.getDataflow,
|
|
284
|
+
description: "Trace data flow from database models through APIs to components",
|
|
285
|
+
inputSchema: {
|
|
286
|
+
type: "object",
|
|
287
|
+
properties: {
|
|
288
|
+
model: { type: "string", description: "Optional database model name" }
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
name: MCP_TOOL_NAMES.getTestCoverage,
|
|
294
|
+
description: "Get discovered test suites, cases, fixtures and static production coverage",
|
|
295
|
+
inputSchema: {
|
|
296
|
+
type: "object",
|
|
297
|
+
properties: {
|
|
298
|
+
target: { type: "string", description: "Optional production target filter" },
|
|
299
|
+
framework: { type: "string", description: "Optional test framework filter" }
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
];
|
|
304
|
+
function attachSnapshot(result, db) {
|
|
305
|
+
const snapshot = db.loadSnapshot();
|
|
306
|
+
const first = result.content[0];
|
|
307
|
+
if (!snapshot || first?.type !== "text") return result;
|
|
308
|
+
try {
|
|
309
|
+
const parsed = JSON.parse(first.text);
|
|
310
|
+
const body = parsed && typeof parsed === "object" && !Array.isArray(parsed) ? { ...parsed, snapshot } : { data: parsed, snapshot };
|
|
311
|
+
return { ...result, content: [{ type: "text", text: JSON.stringify(body, null, 2) }] };
|
|
312
|
+
} catch {
|
|
313
|
+
return result;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
function executeMcpTool(db, name, args) {
|
|
317
|
+
let result;
|
|
318
|
+
switch (name) {
|
|
319
|
+
case MCP_TOOL_NAMES.getApiRoutes:
|
|
320
|
+
result = handleGetApiRoutes(db, args);
|
|
321
|
+
break;
|
|
322
|
+
case MCP_TOOL_NAMES.getComponentTree:
|
|
323
|
+
result = handleGetComponentTree(db, args);
|
|
324
|
+
break;
|
|
325
|
+
case MCP_TOOL_NAMES.findCallers:
|
|
326
|
+
result = handleFindCallers(db, args);
|
|
327
|
+
break;
|
|
328
|
+
case MCP_TOOL_NAMES.listDbModels:
|
|
329
|
+
result = handleListDbModels(db);
|
|
330
|
+
break;
|
|
331
|
+
case MCP_TOOL_NAMES.getDataflow:
|
|
332
|
+
result = handleGetDataFlow(db, args);
|
|
333
|
+
break;
|
|
334
|
+
case MCP_TOOL_NAMES.getTestCoverage:
|
|
335
|
+
result = handleGetTestCoverage(db, args);
|
|
336
|
+
break;
|
|
337
|
+
default:
|
|
338
|
+
result = errorResponse(`Unknown tool: ${name}`);
|
|
339
|
+
}
|
|
340
|
+
return attachSnapshot(result, db);
|
|
341
|
+
}
|
|
342
|
+
function createMcpServer(options) {
|
|
343
|
+
const log = options.log ?? (() => {
|
|
344
|
+
});
|
|
345
|
+
const server = new Server(
|
|
346
|
+
{ name: "codeomnivis", version: "0.0.1" },
|
|
347
|
+
{ capabilities: { tools: {} } }
|
|
348
|
+
);
|
|
349
|
+
let cachedDb = null;
|
|
350
|
+
let dbInitPromise = null;
|
|
351
|
+
let closed = false;
|
|
352
|
+
async function getDb() {
|
|
353
|
+
if (cachedDb) return cachedDb;
|
|
354
|
+
if (dbInitPromise) return dbInitPromise;
|
|
355
|
+
dbInitPromise = (async () => {
|
|
356
|
+
const dbPath = getDbPath(options.projectRoot);
|
|
357
|
+
if (!hasDbCache(options.projectRoot)) {
|
|
358
|
+
log("No cache found, running full analysis");
|
|
359
|
+
await runFullAnalysis({ projectRoot: options.projectRoot, dbPath });
|
|
360
|
+
}
|
|
361
|
+
const db = new OmniDatabase(dbPath);
|
|
362
|
+
await db.ready();
|
|
363
|
+
cachedDb = db;
|
|
364
|
+
log("Database ready");
|
|
365
|
+
return db;
|
|
366
|
+
})();
|
|
367
|
+
try {
|
|
368
|
+
return await dbInitPromise;
|
|
369
|
+
} catch (err) {
|
|
370
|
+
dbInitPromise = null;
|
|
371
|
+
throw err;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOL_DEFINITIONS }));
|
|
375
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
376
|
+
const { name, arguments: args } = request.params;
|
|
377
|
+
try {
|
|
378
|
+
const db = await getDb();
|
|
379
|
+
return executeMcpTool(db, name, args);
|
|
380
|
+
} catch (err) {
|
|
381
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
382
|
+
log(`Tool "${name}" failed: ${message}`);
|
|
383
|
+
return errorResponse(message);
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
return {
|
|
387
|
+
server,
|
|
388
|
+
close: async () => {
|
|
389
|
+
if (closed) return;
|
|
390
|
+
closed = true;
|
|
391
|
+
await server.close();
|
|
392
|
+
cachedDb?.close();
|
|
393
|
+
cachedDb = null;
|
|
394
|
+
dbInitPromise = null;
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
function logToStderr(message) {
|
|
399
|
+
process.stderr.write(`[codeomnivis-mcp] ${message}
|
|
400
|
+
`);
|
|
401
|
+
}
|
|
402
|
+
async function startMcpServer(options) {
|
|
403
|
+
const log = options.log ?? logToStderr;
|
|
404
|
+
const runtime = createMcpServer({ projectRoot: options.projectRoot, log });
|
|
405
|
+
const transport = new StdioServerTransport();
|
|
406
|
+
try {
|
|
407
|
+
await runtime.server.connect(transport);
|
|
408
|
+
log("MCP Server running on stdio");
|
|
409
|
+
return { close: runtime.close };
|
|
410
|
+
} catch (err) {
|
|
411
|
+
await runtime.close().catch(() => {
|
|
412
|
+
});
|
|
413
|
+
throw err;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
function isMainModule(entry, moduleUrl) {
|
|
417
|
+
return entry !== void 0 && moduleUrl === pathToFileURL(entry).href;
|
|
418
|
+
}
|
|
419
|
+
function runMcpProcess(runtime = process, start = startMcpServer) {
|
|
420
|
+
let handle;
|
|
421
|
+
let stopRequested = false;
|
|
422
|
+
let stopping = false;
|
|
423
|
+
const stop = () => {
|
|
424
|
+
stopRequested = true;
|
|
425
|
+
if (!handle || stopping) return;
|
|
426
|
+
stopping = true;
|
|
427
|
+
void handle.close().then(() => runtime.exit(0)).catch((err) => {
|
|
428
|
+
runtime.stderr.write(`[codeomnivis-mcp] Shutdown failed: ${String(err)}
|
|
429
|
+
`);
|
|
430
|
+
runtime.exit(1);
|
|
431
|
+
});
|
|
432
|
+
};
|
|
433
|
+
runtime.once("SIGINT", stop);
|
|
434
|
+
runtime.once("SIGTERM", stop);
|
|
435
|
+
void start({ projectRoot: runtime.env.CODEOMNIVIS_PROJECT ?? runtime.cwd() }).then((started) => {
|
|
436
|
+
handle = started;
|
|
437
|
+
if (stopRequested) stop();
|
|
438
|
+
}).catch((err) => {
|
|
439
|
+
runtime.stderr.write(`[codeomnivis-mcp] Fatal: ${String(err)}
|
|
440
|
+
`);
|
|
441
|
+
runtime.exit(1);
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
if (isMainModule(process.argv[1], import.meta.url)) runMcpProcess();
|
|
445
|
+
export {
|
|
446
|
+
MCP_TOOL_NAMES,
|
|
447
|
+
PUBLIC_TOOL_NAMES,
|
|
448
|
+
createMcpServer,
|
|
449
|
+
errorResponse,
|
|
450
|
+
executeMcpTool,
|
|
451
|
+
handleFindCallers,
|
|
452
|
+
handleGetApiRoutes,
|
|
453
|
+
handleGetComponentTree,
|
|
454
|
+
handleGetDataFlow,
|
|
455
|
+
handleGetTestCoverage,
|
|
456
|
+
handleListDbModels,
|
|
457
|
+
isMainModule,
|
|
458
|
+
logToStderr,
|
|
459
|
+
runMcpProcess,
|
|
460
|
+
startMcpServer,
|
|
461
|
+
validateDepth
|
|
462
|
+
};
|