ucn 3.7.38 → 3.7.39
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/mcp/server.js +44 -61
- package/package.json +1 -1
package/mcp/server.js
CHANGED
|
@@ -257,13 +257,12 @@ server.registerTool(
|
|
|
257
257
|
})
|
|
258
258
|
},
|
|
259
259
|
async (args) => {
|
|
260
|
-
const { command, project_dir
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
case_sensitive, regex, functions, all, top_level, class_name } = args;
|
|
260
|
+
const { command, project_dir } = args;
|
|
261
|
+
|
|
262
|
+
// Normalize ALL params once — execute() handlers pick what they need.
|
|
263
|
+
// This eliminates per-case param selection and prevents CLI/MCP drift.
|
|
264
|
+
const { command: _c, project_dir: _p, ...rawParams } = args;
|
|
265
|
+
const ep = normalizeParams(rawParams);
|
|
267
266
|
|
|
268
267
|
try {
|
|
269
268
|
switch (command) {
|
|
@@ -276,7 +275,6 @@ server.registerTool(
|
|
|
276
275
|
|
|
277
276
|
case 'about': {
|
|
278
277
|
const index = getIndex(project_dir);
|
|
279
|
-
const ep = normalizeParams({ name, file, exclude, with_types, all, include_methods, include_uncertain, top, class_name });
|
|
280
278
|
const { ok, result, error } = execute(index, 'about', ep);
|
|
281
279
|
if (!ok) return toolResult(error); // soft error — won't kill sibling calls
|
|
282
280
|
return toolResult(output.formatAbout(result, {
|
|
@@ -287,19 +285,17 @@ server.registerTool(
|
|
|
287
285
|
|
|
288
286
|
case 'context': {
|
|
289
287
|
const index = getIndex(project_dir);
|
|
290
|
-
const ep = normalizeParams({ name, file, exclude, include_methods, include_uncertain, class_name });
|
|
291
288
|
const { ok, result: ctx, error } = execute(index, 'context', ep);
|
|
292
289
|
if (!ok) return toolResult(error); // context uses soft error (not toolError)
|
|
293
290
|
const { text, expandable } = output.formatContext(ctx, {
|
|
294
291
|
expandHint: 'Use expand command with item number to see code for any item.'
|
|
295
292
|
});
|
|
296
|
-
expandCacheInstance.save(index.root, name, file, expandable);
|
|
293
|
+
expandCacheInstance.save(index.root, ep.name, ep.file, expandable);
|
|
297
294
|
return toolResult(text);
|
|
298
295
|
}
|
|
299
296
|
|
|
300
297
|
case 'impact': {
|
|
301
298
|
const index = getIndex(project_dir);
|
|
302
|
-
const ep = normalizeParams({ name, file, exclude, top, class_name });
|
|
303
299
|
const { ok, result, error } = execute(index, 'impact', ep);
|
|
304
300
|
if (!ok) return toolResult(error); // soft error
|
|
305
301
|
return toolResult(output.formatImpact(result));
|
|
@@ -307,7 +303,6 @@ server.registerTool(
|
|
|
307
303
|
|
|
308
304
|
case 'smart': {
|
|
309
305
|
const index = getIndex(project_dir);
|
|
310
|
-
const ep = normalizeParams({ name, file, with_types, include_methods, include_uncertain });
|
|
311
306
|
const { ok, result, error } = execute(index, 'smart', ep);
|
|
312
307
|
if (!ok) return toolResult(error); // soft error
|
|
313
308
|
return toolResult(output.formatSmart(result));
|
|
@@ -315,7 +310,6 @@ server.registerTool(
|
|
|
315
310
|
|
|
316
311
|
case 'trace': {
|
|
317
312
|
const index = getIndex(project_dir);
|
|
318
|
-
const ep = normalizeParams({ name, file, depth, all, include_methods, include_uncertain });
|
|
319
313
|
const { ok, result, error } = execute(index, 'trace', ep);
|
|
320
314
|
if (!ok) return toolResult(error); // soft error
|
|
321
315
|
return toolResult(output.formatTrace(result, {
|
|
@@ -326,19 +320,19 @@ server.registerTool(
|
|
|
326
320
|
|
|
327
321
|
case 'example': {
|
|
328
322
|
const index = getIndex(project_dir);
|
|
329
|
-
const { ok, result, error } = execute(index, 'example',
|
|
323
|
+
const { ok, result, error } = execute(index, 'example', ep);
|
|
330
324
|
if (!ok) return toolResult(error);
|
|
331
|
-
if (!result) return toolResult(`No usage examples found for "${name}".`);
|
|
332
|
-
return toolResult(output.formatExample(result, name));
|
|
325
|
+
if (!result) return toolResult(`No usage examples found for "${ep.name}".`);
|
|
326
|
+
return toolResult(output.formatExample(result, ep.name));
|
|
333
327
|
}
|
|
334
328
|
|
|
335
329
|
case 'related': {
|
|
336
330
|
const index = getIndex(project_dir);
|
|
337
|
-
const { ok, result, error } = execute(index, 'related',
|
|
331
|
+
const { ok, result, error } = execute(index, 'related', ep);
|
|
338
332
|
if (!ok) return toolResult(error);
|
|
339
|
-
if (!result) return toolResult(`Symbol "${name}" not found.`);
|
|
333
|
+
if (!result) return toolResult(`Symbol "${ep.name}" not found.`);
|
|
340
334
|
return toolResult(output.formatRelated(result, {
|
|
341
|
-
all: all || false, top,
|
|
335
|
+
all: ep.all || false, top: ep.top,
|
|
342
336
|
allHint: 'Repeat with all=true to show all.'
|
|
343
337
|
}));
|
|
344
338
|
}
|
|
@@ -347,25 +341,22 @@ server.registerTool(
|
|
|
347
341
|
|
|
348
342
|
case 'find': {
|
|
349
343
|
const index = getIndex(project_dir);
|
|
350
|
-
const ep = normalizeParams({ name, file, exclude, include_tests, exact, in: inPath });
|
|
351
344
|
const { ok, result, error, note } = execute(index, 'find', ep);
|
|
352
345
|
if (!ok) return toolResult(error); // soft error
|
|
353
|
-
let text = output.formatFind(result, name, top);
|
|
346
|
+
let text = output.formatFind(result, ep.name, ep.top);
|
|
354
347
|
if (note) text += '\n\n' + note;
|
|
355
348
|
return toolResult(text);
|
|
356
349
|
}
|
|
357
350
|
|
|
358
351
|
case 'usages': {
|
|
359
352
|
const index = getIndex(project_dir);
|
|
360
|
-
const ep = normalizeParams({ name, exclude, include_tests, code_only, context: ctxLines, in: inPath });
|
|
361
353
|
const { ok, result, error } = execute(index, 'usages', ep);
|
|
362
354
|
if (!ok) return toolResult(error); // soft error
|
|
363
|
-
return toolResult(output.formatUsages(result, name));
|
|
355
|
+
return toolResult(output.formatUsages(result, ep.name));
|
|
364
356
|
}
|
|
365
357
|
|
|
366
358
|
case 'toc': {
|
|
367
359
|
const index = getIndex(project_dir);
|
|
368
|
-
const ep = normalizeParams({ detailed, top_level, all, top });
|
|
369
360
|
const { ok, result, error } = execute(index, 'toc', ep);
|
|
370
361
|
if (!ok) return toolResult(error); // soft error
|
|
371
362
|
return toolResult(output.formatToc(result, {
|
|
@@ -375,29 +366,26 @@ server.registerTool(
|
|
|
375
366
|
|
|
376
367
|
case 'search': {
|
|
377
368
|
const index = getIndex(project_dir);
|
|
378
|
-
const ep = normalizeParams({ term, exclude, include_tests, code_only, context: ctxLines, case_sensitive, in: inPath, regex, top });
|
|
379
369
|
const { ok, result, error } = execute(index, 'search', ep);
|
|
380
370
|
if (!ok) return toolResult(error); // soft error
|
|
381
|
-
return toolResult(output.formatSearch(result, term));
|
|
371
|
+
return toolResult(output.formatSearch(result, ep.term));
|
|
382
372
|
}
|
|
383
373
|
|
|
384
374
|
case 'tests': {
|
|
385
375
|
const index = getIndex(project_dir);
|
|
386
|
-
const ep = normalizeParams({ name, calls_only });
|
|
387
376
|
const { ok, result, error } = execute(index, 'tests', ep);
|
|
388
377
|
if (!ok) return toolResult(error); // soft error
|
|
389
|
-
return toolResult(output.formatTests(result, name));
|
|
378
|
+
return toolResult(output.formatTests(result, ep.name));
|
|
390
379
|
}
|
|
391
380
|
|
|
392
381
|
case 'deadcode': {
|
|
393
382
|
const index = getIndex(project_dir);
|
|
394
|
-
const ep = normalizeParams({ exclude, in: inPath, include_exported, include_decorated, include_tests });
|
|
395
383
|
const { ok, result, error } = execute(index, 'deadcode', ep);
|
|
396
384
|
if (!ok) return toolResult(error); // soft error
|
|
397
385
|
return toolResult(output.formatDeadcode(result, {
|
|
398
|
-
top: top || 0,
|
|
399
|
-
decoratedHint: !
|
|
400
|
-
exportedHint: !
|
|
386
|
+
top: ep.top || 0,
|
|
387
|
+
decoratedHint: !ep.includeDecorated && result.excludedDecorated > 0 ? `${result.excludedDecorated} decorated/annotated symbol(s) hidden (framework-registered). Use include_decorated=true to include them.` : undefined,
|
|
388
|
+
exportedHint: !ep.includeExported && result.excludedExported > 0 ? `${result.excludedExported} exported symbol(s) excluded (all have callers). Use include_exported=true to audit them.` : undefined
|
|
401
389
|
}));
|
|
402
390
|
}
|
|
403
391
|
|
|
@@ -405,32 +393,32 @@ server.registerTool(
|
|
|
405
393
|
|
|
406
394
|
case 'imports': {
|
|
407
395
|
const index = getIndex(project_dir);
|
|
408
|
-
const { ok, result, error } = execute(index, 'imports',
|
|
396
|
+
const { ok, result, error } = execute(index, 'imports', ep);
|
|
409
397
|
if (!ok) return toolResult(error); // soft error
|
|
410
|
-
return toolResult(output.formatImports(result, file));
|
|
398
|
+
return toolResult(output.formatImports(result, ep.file));
|
|
411
399
|
}
|
|
412
400
|
|
|
413
401
|
case 'exporters': {
|
|
414
402
|
const index = getIndex(project_dir);
|
|
415
|
-
const { ok, result, error } = execute(index, 'exporters',
|
|
403
|
+
const { ok, result, error } = execute(index, 'exporters', ep);
|
|
416
404
|
if (!ok) return toolResult(error); // soft error
|
|
417
|
-
return toolResult(output.formatExporters(result, file));
|
|
405
|
+
return toolResult(output.formatExporters(result, ep.file));
|
|
418
406
|
}
|
|
419
407
|
|
|
420
408
|
case 'file_exports': {
|
|
421
409
|
const index = getIndex(project_dir);
|
|
422
|
-
const { ok, result, error } = execute(index, 'fileExports',
|
|
410
|
+
const { ok, result, error } = execute(index, 'fileExports', ep);
|
|
423
411
|
if (!ok) return toolResult(error); // soft error
|
|
424
|
-
return toolResult(output.formatFileExports(result, file));
|
|
412
|
+
return toolResult(output.formatFileExports(result, ep.file));
|
|
425
413
|
}
|
|
426
414
|
|
|
427
415
|
case 'graph': {
|
|
428
416
|
const index = getIndex(project_dir);
|
|
429
|
-
const { ok, result, error } = execute(index, 'graph',
|
|
417
|
+
const { ok, result, error } = execute(index, 'graph', ep);
|
|
430
418
|
if (!ok) return toolResult(error); // soft error
|
|
431
419
|
return toolResult(output.formatGraph(result, {
|
|
432
|
-
showAll: all || depth !== undefined,
|
|
433
|
-
maxDepth: depth ?? 2, file,
|
|
420
|
+
showAll: ep.all || ep.depth !== undefined,
|
|
421
|
+
maxDepth: ep.depth ?? 2, file: ep.file,
|
|
434
422
|
depthHint: 'Set depth parameter for deeper graph.',
|
|
435
423
|
allHint: 'Set depth to expand all children.'
|
|
436
424
|
}));
|
|
@@ -440,7 +428,6 @@ server.registerTool(
|
|
|
440
428
|
|
|
441
429
|
case 'verify': {
|
|
442
430
|
const index = getIndex(project_dir);
|
|
443
|
-
const ep = normalizeParams({ name, file, class_name });
|
|
444
431
|
const { ok, result, error } = execute(index, 'verify', ep);
|
|
445
432
|
if (!ok) return toolResult(error); // soft error
|
|
446
433
|
return toolResult(output.formatVerify(result));
|
|
@@ -448,7 +435,6 @@ server.registerTool(
|
|
|
448
435
|
|
|
449
436
|
case 'plan': {
|
|
450
437
|
const index = getIndex(project_dir);
|
|
451
|
-
const ep = normalizeParams({ name, add_param, remove_param, rename_to, default_value, file, class_name });
|
|
452
438
|
const { ok, result, error } = execute(index, 'plan', ep);
|
|
453
439
|
if (!ok) return toolResult(error); // soft error
|
|
454
440
|
return toolResult(output.formatPlan(result));
|
|
@@ -456,7 +442,7 @@ server.registerTool(
|
|
|
456
442
|
|
|
457
443
|
case 'diff_impact': {
|
|
458
444
|
const index = getIndex(project_dir);
|
|
459
|
-
const { ok, result, error } = execute(index, 'diffImpact',
|
|
445
|
+
const { ok, result, error } = execute(index, 'diffImpact', ep);
|
|
460
446
|
if (!ok) return toolResult(error); // soft error — e.g. "not a git repo"
|
|
461
447
|
return toolResult(output.formatDiffImpact(result));
|
|
462
448
|
}
|
|
@@ -465,39 +451,38 @@ server.registerTool(
|
|
|
465
451
|
|
|
466
452
|
case 'typedef': {
|
|
467
453
|
const index = getIndex(project_dir);
|
|
468
|
-
const { ok, result, error } = execute(index, 'typedef',
|
|
454
|
+
const { ok, result, error } = execute(index, 'typedef', ep);
|
|
469
455
|
if (!ok) return toolResult(error); // soft error
|
|
470
|
-
return toolResult(output.formatTypedef(result, name));
|
|
456
|
+
return toolResult(output.formatTypedef(result, ep.name));
|
|
471
457
|
}
|
|
472
458
|
|
|
473
459
|
case 'stacktrace': {
|
|
474
460
|
const index = getIndex(project_dir);
|
|
475
|
-
const { ok, result, error } = execute(index, 'stacktrace',
|
|
461
|
+
const { ok, result, error } = execute(index, 'stacktrace', ep);
|
|
476
462
|
if (!ok) return toolResult(error); // soft error
|
|
477
463
|
return toolResult(output.formatStackTrace(result));
|
|
478
464
|
}
|
|
479
465
|
|
|
480
466
|
case 'api': {
|
|
481
467
|
const index = getIndex(project_dir);
|
|
482
|
-
const { ok, result, error } = execute(index, 'api',
|
|
468
|
+
const { ok, result, error } = execute(index, 'api', ep);
|
|
483
469
|
if (!ok) return toolResult(error); // soft error
|
|
484
|
-
return toolResult(output.formatApi(result, file || '.'));
|
|
470
|
+
return toolResult(output.formatApi(result, ep.file || '.'));
|
|
485
471
|
}
|
|
486
472
|
|
|
487
473
|
case 'stats': {
|
|
488
474
|
const index = getIndex(project_dir);
|
|
489
|
-
const { ok, result, error } = execute(index, 'stats',
|
|
475
|
+
const { ok, result, error } = execute(index, 'stats', ep);
|
|
490
476
|
if (!ok) return toolResult(error); // soft error
|
|
491
|
-
return toolResult(output.formatStats(result, { top: top || 0 }));
|
|
477
|
+
return toolResult(output.formatStats(result, { top: ep.top || 0 }));
|
|
492
478
|
}
|
|
493
479
|
|
|
494
480
|
// ── Extracting Code (via execute) ────────────────────────────
|
|
495
481
|
|
|
496
482
|
case 'fn': {
|
|
497
|
-
const err = requireName(name);
|
|
483
|
+
const err = requireName(ep.name);
|
|
498
484
|
if (err) return err;
|
|
499
485
|
const index = getIndex(project_dir);
|
|
500
|
-
const ep = normalizeParams({ name, file, all });
|
|
501
486
|
const { ok, result, error } = execute(index, 'fn', ep);
|
|
502
487
|
if (!ok) return toolResult(error); // soft error
|
|
503
488
|
// MCP path security: validate all result files are within project root
|
|
@@ -510,13 +495,12 @@ server.registerTool(
|
|
|
510
495
|
}
|
|
511
496
|
|
|
512
497
|
case 'class': {
|
|
513
|
-
const err = requireName(name);
|
|
498
|
+
const err = requireName(ep.name);
|
|
514
499
|
if (err) return err;
|
|
515
|
-
if (
|
|
516
|
-
return toolError(`Invalid max_lines: ${
|
|
500
|
+
if (ep.maxLines !== undefined && (!Number.isInteger(ep.maxLines) || ep.maxLines < 1)) {
|
|
501
|
+
return toolError(`Invalid max_lines: ${ep.maxLines}. Must be a positive integer.`);
|
|
517
502
|
}
|
|
518
503
|
const index = getIndex(project_dir);
|
|
519
|
-
const ep = normalizeParams({ name, file, all, max_lines });
|
|
520
504
|
const { ok, result, error } = execute(index, 'class', ep);
|
|
521
505
|
if (!ok) return toolResult(error); // soft error (class not found)
|
|
522
506
|
// MCP path security: validate all result files are within project root
|
|
@@ -530,7 +514,6 @@ server.registerTool(
|
|
|
530
514
|
|
|
531
515
|
case 'lines': {
|
|
532
516
|
const index = getIndex(project_dir);
|
|
533
|
-
const ep = normalizeParams({ file, range });
|
|
534
517
|
const { ok, result, error } = execute(index, 'lines', ep);
|
|
535
518
|
if (!ok) return toolResult(error); // soft error
|
|
536
519
|
// MCP path security: validate file is within project root
|
|
@@ -540,13 +523,13 @@ server.registerTool(
|
|
|
540
523
|
}
|
|
541
524
|
|
|
542
525
|
case 'expand': {
|
|
543
|
-
if (item === undefined || item === null) {
|
|
526
|
+
if (ep.item === undefined || ep.item === null) {
|
|
544
527
|
return toolError('Item number is required (e.g. item=1).');
|
|
545
528
|
}
|
|
546
529
|
const index = getIndex(project_dir);
|
|
547
|
-
const lookup = expandCacheInstance.lookup(index.root, item);
|
|
530
|
+
const lookup = expandCacheInstance.lookup(index.root, ep.item);
|
|
548
531
|
const { ok, result, error } = execute(index, 'expand', {
|
|
549
|
-
match: lookup.match, itemNum: item,
|
|
532
|
+
match: lookup.match, itemNum: ep.item,
|
|
550
533
|
itemCount: lookup.itemCount, symbolName: lookup.symbolName,
|
|
551
534
|
validateRoot: true
|
|
552
535
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ucn",
|
|
3
|
-
"version": "3.7.
|
|
3
|
+
"version": "3.7.39",
|
|
4
4
|
"mcpName": "io.github.mleoca/ucn",
|
|
5
5
|
"description": "Universal Code Navigator — AST-based call graph analysis for AI agents. Find callers, trace impact, detect dead code across JS/TS, Python, Go, Rust, Java, and HTML. CLI, MCP server, and agent skill.",
|
|
6
6
|
"main": "index.js",
|