@optave/codegraph 1.4.1 → 2.1.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/README.md +127 -40
- package/package.json +10 -10
- package/src/builder.js +61 -8
- package/src/cli.js +147 -5
- package/src/config.js +1 -1
- package/src/constants.js +0 -2
- package/src/cycles.js +2 -2
- package/src/db.js +13 -0
- package/src/embedder.js +3 -3
- package/src/export.js +44 -9
- package/src/extractors/csharp.js +243 -0
- package/src/extractors/go.js +167 -0
- package/src/extractors/hcl.js +73 -0
- package/src/extractors/helpers.js +10 -0
- package/src/extractors/index.js +9 -0
- package/src/extractors/java.js +227 -0
- package/src/extractors/javascript.js +396 -0
- package/src/extractors/php.js +237 -0
- package/src/extractors/python.js +143 -0
- package/src/extractors/ruby.js +185 -0
- package/src/extractors/rust.js +215 -0
- package/src/index.js +22 -0
- package/src/mcp.js +141 -6
- package/src/parser.js +29 -1893
- package/src/queries.js +190 -4
- package/src/registry.js +162 -0
- package/src/resolve.js +4 -3
- package/src/structure.js +491 -0
- package/src/watcher.js +2 -2
package/src/mcp.js
CHANGED
|
@@ -9,7 +9,14 @@ import { createRequire } from 'node:module';
|
|
|
9
9
|
import { findCycles } from './cycles.js';
|
|
10
10
|
import { findDbPath } from './db.js';
|
|
11
11
|
|
|
12
|
-
const
|
|
12
|
+
const REPO_PROP = {
|
|
13
|
+
repo: {
|
|
14
|
+
type: 'string',
|
|
15
|
+
description: 'Repository name from the registry (omit for local project)',
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const BASE_TOOLS = [
|
|
13
20
|
{
|
|
14
21
|
name: 'query_function',
|
|
15
22
|
description: 'Find callers and callees of a function by name',
|
|
@@ -143,7 +150,7 @@ const TOOLS = [
|
|
|
143
150
|
{
|
|
144
151
|
name: 'list_functions',
|
|
145
152
|
description:
|
|
146
|
-
'List functions, methods, and
|
|
153
|
+
'List functions, methods, classes, structs, enums, traits, records, and modules in the codebase, optionally filtered by file or name pattern',
|
|
147
154
|
inputSchema: {
|
|
148
155
|
type: 'object',
|
|
149
156
|
properties: {
|
|
@@ -153,15 +160,91 @@ const TOOLS = [
|
|
|
153
160
|
},
|
|
154
161
|
},
|
|
155
162
|
},
|
|
163
|
+
{
|
|
164
|
+
name: 'structure',
|
|
165
|
+
description:
|
|
166
|
+
'Show project structure with directory hierarchy, cohesion scores, and per-file metrics',
|
|
167
|
+
inputSchema: {
|
|
168
|
+
type: 'object',
|
|
169
|
+
properties: {
|
|
170
|
+
directory: { type: 'string', description: 'Filter to a specific directory path' },
|
|
171
|
+
depth: { type: 'number', description: 'Max directory depth to show' },
|
|
172
|
+
sort: {
|
|
173
|
+
type: 'string',
|
|
174
|
+
enum: ['cohesion', 'fan-in', 'fan-out', 'density', 'files'],
|
|
175
|
+
description: 'Sort directories by metric',
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
name: 'hotspots',
|
|
182
|
+
description:
|
|
183
|
+
'Find structural hotspots: files or directories with extreme fan-in, fan-out, or symbol density',
|
|
184
|
+
inputSchema: {
|
|
185
|
+
type: 'object',
|
|
186
|
+
properties: {
|
|
187
|
+
metric: {
|
|
188
|
+
type: 'string',
|
|
189
|
+
enum: ['fan-in', 'fan-out', 'density', 'coupling'],
|
|
190
|
+
description: 'Metric to rank by',
|
|
191
|
+
},
|
|
192
|
+
level: {
|
|
193
|
+
type: 'string',
|
|
194
|
+
enum: ['file', 'directory'],
|
|
195
|
+
description: 'Rank files or directories',
|
|
196
|
+
},
|
|
197
|
+
limit: { type: 'number', description: 'Number of results to return', default: 10 },
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
},
|
|
156
201
|
];
|
|
157
202
|
|
|
158
|
-
|
|
203
|
+
const LIST_REPOS_TOOL = {
|
|
204
|
+
name: 'list_repos',
|
|
205
|
+
description: 'List all repositories registered in the codegraph registry',
|
|
206
|
+
inputSchema: {
|
|
207
|
+
type: 'object',
|
|
208
|
+
properties: {},
|
|
209
|
+
},
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Build the tool list based on multi-repo mode.
|
|
214
|
+
* @param {boolean} multiRepo - If true, inject `repo` prop into each tool and append `list_repos`
|
|
215
|
+
* @returns {object[]}
|
|
216
|
+
*/
|
|
217
|
+
function buildToolList(multiRepo) {
|
|
218
|
+
if (!multiRepo) return BASE_TOOLS;
|
|
219
|
+
return [
|
|
220
|
+
...BASE_TOOLS.map((tool) => ({
|
|
221
|
+
...tool,
|
|
222
|
+
inputSchema: {
|
|
223
|
+
...tool.inputSchema,
|
|
224
|
+
properties: { ...tool.inputSchema.properties, ...REPO_PROP },
|
|
225
|
+
},
|
|
226
|
+
})),
|
|
227
|
+
LIST_REPOS_TOOL,
|
|
228
|
+
];
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Backward-compatible export: full multi-repo tool list
|
|
232
|
+
const TOOLS = buildToolList(true);
|
|
233
|
+
|
|
234
|
+
export { TOOLS, buildToolList };
|
|
159
235
|
|
|
160
236
|
/**
|
|
161
237
|
* Start the MCP server.
|
|
162
238
|
* This function requires @modelcontextprotocol/sdk to be installed.
|
|
239
|
+
*
|
|
240
|
+
* @param {string} [customDbPath] - Path to a specific graph.db
|
|
241
|
+
* @param {object} [options]
|
|
242
|
+
* @param {boolean} [options.multiRepo] - Enable multi-repo access (default: false)
|
|
243
|
+
* @param {string[]} [options.allowedRepos] - Restrict access to these repo names only
|
|
163
244
|
*/
|
|
164
|
-
export async function startMCPServer(customDbPath) {
|
|
245
|
+
export async function startMCPServer(customDbPath, options = {}) {
|
|
246
|
+
const { allowedRepos } = options;
|
|
247
|
+
const multiRepo = options.multiRepo || !!allowedRepos;
|
|
165
248
|
let Server, StdioServerTransport;
|
|
166
249
|
try {
|
|
167
250
|
const sdk = await import('@modelcontextprotocol/sdk/server/index.js');
|
|
@@ -196,13 +279,37 @@ export async function startMCPServer(customDbPath) {
|
|
|
196
279
|
{ capabilities: { tools: {} } },
|
|
197
280
|
);
|
|
198
281
|
|
|
199
|
-
server.setRequestHandler('tools/list', async () => ({ tools:
|
|
282
|
+
server.setRequestHandler('tools/list', async () => ({ tools: buildToolList(multiRepo) }));
|
|
200
283
|
|
|
201
284
|
server.setRequestHandler('tools/call', async (request) => {
|
|
202
285
|
const { name, arguments: args } = request.params;
|
|
203
|
-
const dbPath = customDbPath || undefined;
|
|
204
286
|
|
|
205
287
|
try {
|
|
288
|
+
if (!multiRepo && args.repo) {
|
|
289
|
+
throw new Error(
|
|
290
|
+
'Multi-repo access is disabled. Restart with `codegraph mcp --multi-repo` to access other repositories.',
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
if (!multiRepo && name === 'list_repos') {
|
|
294
|
+
throw new Error(
|
|
295
|
+
'Multi-repo access is disabled. Restart with `codegraph mcp --multi-repo` to list repositories.',
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
let dbPath = customDbPath || undefined;
|
|
300
|
+
if (args.repo) {
|
|
301
|
+
if (allowedRepos && !allowedRepos.includes(args.repo)) {
|
|
302
|
+
throw new Error(`Repository "${args.repo}" is not in the allowed repos list.`);
|
|
303
|
+
}
|
|
304
|
+
const { resolveRepoDbPath } = await import('./registry.js');
|
|
305
|
+
const resolved = resolveRepoDbPath(args.repo);
|
|
306
|
+
if (!resolved)
|
|
307
|
+
throw new Error(
|
|
308
|
+
`Repository "${args.repo}" not found in registry or its database is missing.`,
|
|
309
|
+
);
|
|
310
|
+
dbPath = resolved;
|
|
311
|
+
}
|
|
312
|
+
|
|
206
313
|
let result;
|
|
207
314
|
switch (name) {
|
|
208
315
|
case 'query_function':
|
|
@@ -296,6 +403,34 @@ export async function startMCPServer(customDbPath) {
|
|
|
296
403
|
noTests: args.no_tests,
|
|
297
404
|
});
|
|
298
405
|
break;
|
|
406
|
+
case 'structure': {
|
|
407
|
+
const { structureData } = await import('./structure.js');
|
|
408
|
+
result = structureData(dbPath, {
|
|
409
|
+
directory: args.directory,
|
|
410
|
+
depth: args.depth,
|
|
411
|
+
sort: args.sort,
|
|
412
|
+
});
|
|
413
|
+
break;
|
|
414
|
+
}
|
|
415
|
+
case 'hotspots': {
|
|
416
|
+
const { hotspotsData } = await import('./structure.js');
|
|
417
|
+
result = hotspotsData(dbPath, {
|
|
418
|
+
metric: args.metric,
|
|
419
|
+
level: args.level,
|
|
420
|
+
limit: args.limit,
|
|
421
|
+
});
|
|
422
|
+
break;
|
|
423
|
+
}
|
|
424
|
+
case 'list_repos': {
|
|
425
|
+
const { listRepos, pruneRegistry } = await import('./registry.js');
|
|
426
|
+
pruneRegistry();
|
|
427
|
+
let repos = listRepos();
|
|
428
|
+
if (allowedRepos) {
|
|
429
|
+
repos = repos.filter((r) => allowedRepos.includes(r.name));
|
|
430
|
+
}
|
|
431
|
+
result = { repos };
|
|
432
|
+
break;
|
|
433
|
+
}
|
|
299
434
|
default:
|
|
300
435
|
return { content: [{ type: 'text', text: `Unknown tool: ${name}` }], isError: true };
|
|
301
436
|
}
|