ucn 3.7.37 → 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/core/registry.js +1 -0
- package/mcp/server.js +48 -62
- package/package.json +1 -1
package/core/registry.js
CHANGED
package/mcp/server.js
CHANGED
|
@@ -251,17 +251,18 @@ server.registerTool(
|
|
|
251
251
|
staged: z.boolean().optional().describe('Analyze staged changes (diff_impact command)'),
|
|
252
252
|
case_sensitive: z.boolean().optional().describe('Case-sensitive search (default: false, case-insensitive)'),
|
|
253
253
|
all: z.boolean().optional().describe('Show all results (expand truncated sections). Applies to about, toc, related, trace, and others.'),
|
|
254
|
-
top_level: z.boolean().optional().describe('Show only top-level functions in toc (exclude nested/indented)')
|
|
254
|
+
top_level: z.boolean().optional().describe('Show only top-level functions in toc (exclude nested/indented)'),
|
|
255
|
+
class_name: z.string().optional().describe('Class name to scope method analysis (e.g. "MarketDataFetcher" for close)')
|
|
256
|
+
|
|
255
257
|
})
|
|
256
258
|
},
|
|
257
259
|
async (args) => {
|
|
258
|
-
const { command, project_dir
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
case_sensitive, regex, functions, all, top_level } = 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);
|
|
265
266
|
|
|
266
267
|
try {
|
|
267
268
|
switch (command) {
|
|
@@ -274,7 +275,6 @@ server.registerTool(
|
|
|
274
275
|
|
|
275
276
|
case 'about': {
|
|
276
277
|
const index = getIndex(project_dir);
|
|
277
|
-
const ep = normalizeParams({ name, file, exclude, with_types, all, include_methods, include_uncertain, top });
|
|
278
278
|
const { ok, result, error } = execute(index, 'about', ep);
|
|
279
279
|
if (!ok) return toolResult(error); // soft error — won't kill sibling calls
|
|
280
280
|
return toolResult(output.formatAbout(result, {
|
|
@@ -285,19 +285,17 @@ server.registerTool(
|
|
|
285
285
|
|
|
286
286
|
case 'context': {
|
|
287
287
|
const index = getIndex(project_dir);
|
|
288
|
-
const ep = normalizeParams({ name, file, exclude, include_methods, include_uncertain });
|
|
289
288
|
const { ok, result: ctx, error } = execute(index, 'context', ep);
|
|
290
289
|
if (!ok) return toolResult(error); // context uses soft error (not toolError)
|
|
291
290
|
const { text, expandable } = output.formatContext(ctx, {
|
|
292
291
|
expandHint: 'Use expand command with item number to see code for any item.'
|
|
293
292
|
});
|
|
294
|
-
expandCacheInstance.save(index.root, name, file, expandable);
|
|
293
|
+
expandCacheInstance.save(index.root, ep.name, ep.file, expandable);
|
|
295
294
|
return toolResult(text);
|
|
296
295
|
}
|
|
297
296
|
|
|
298
297
|
case 'impact': {
|
|
299
298
|
const index = getIndex(project_dir);
|
|
300
|
-
const ep = normalizeParams({ name, file, exclude, top });
|
|
301
299
|
const { ok, result, error } = execute(index, 'impact', ep);
|
|
302
300
|
if (!ok) return toolResult(error); // soft error
|
|
303
301
|
return toolResult(output.formatImpact(result));
|
|
@@ -305,7 +303,6 @@ server.registerTool(
|
|
|
305
303
|
|
|
306
304
|
case 'smart': {
|
|
307
305
|
const index = getIndex(project_dir);
|
|
308
|
-
const ep = normalizeParams({ name, file, with_types, include_methods, include_uncertain });
|
|
309
306
|
const { ok, result, error } = execute(index, 'smart', ep);
|
|
310
307
|
if (!ok) return toolResult(error); // soft error
|
|
311
308
|
return toolResult(output.formatSmart(result));
|
|
@@ -313,7 +310,6 @@ server.registerTool(
|
|
|
313
310
|
|
|
314
311
|
case 'trace': {
|
|
315
312
|
const index = getIndex(project_dir);
|
|
316
|
-
const ep = normalizeParams({ name, file, depth, all, include_methods, include_uncertain });
|
|
317
313
|
const { ok, result, error } = execute(index, 'trace', ep);
|
|
318
314
|
if (!ok) return toolResult(error); // soft error
|
|
319
315
|
return toolResult(output.formatTrace(result, {
|
|
@@ -324,19 +320,19 @@ server.registerTool(
|
|
|
324
320
|
|
|
325
321
|
case 'example': {
|
|
326
322
|
const index = getIndex(project_dir);
|
|
327
|
-
const { ok, result, error } = execute(index, 'example',
|
|
323
|
+
const { ok, result, error } = execute(index, 'example', ep);
|
|
328
324
|
if (!ok) return toolResult(error);
|
|
329
|
-
if (!result) return toolResult(`No usage examples found for "${name}".`);
|
|
330
|
-
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));
|
|
331
327
|
}
|
|
332
328
|
|
|
333
329
|
case 'related': {
|
|
334
330
|
const index = getIndex(project_dir);
|
|
335
|
-
const { ok, result, error } = execute(index, 'related',
|
|
331
|
+
const { ok, result, error } = execute(index, 'related', ep);
|
|
336
332
|
if (!ok) return toolResult(error);
|
|
337
|
-
if (!result) return toolResult(`Symbol "${name}" not found.`);
|
|
333
|
+
if (!result) return toolResult(`Symbol "${ep.name}" not found.`);
|
|
338
334
|
return toolResult(output.formatRelated(result, {
|
|
339
|
-
all: all || false, top,
|
|
335
|
+
all: ep.all || false, top: ep.top,
|
|
340
336
|
allHint: 'Repeat with all=true to show all.'
|
|
341
337
|
}));
|
|
342
338
|
}
|
|
@@ -345,25 +341,22 @@ server.registerTool(
|
|
|
345
341
|
|
|
346
342
|
case 'find': {
|
|
347
343
|
const index = getIndex(project_dir);
|
|
348
|
-
const ep = normalizeParams({ name, file, exclude, include_tests, exact, in: inPath });
|
|
349
344
|
const { ok, result, error, note } = execute(index, 'find', ep);
|
|
350
345
|
if (!ok) return toolResult(error); // soft error
|
|
351
|
-
let text = output.formatFind(result, name, top);
|
|
346
|
+
let text = output.formatFind(result, ep.name, ep.top);
|
|
352
347
|
if (note) text += '\n\n' + note;
|
|
353
348
|
return toolResult(text);
|
|
354
349
|
}
|
|
355
350
|
|
|
356
351
|
case 'usages': {
|
|
357
352
|
const index = getIndex(project_dir);
|
|
358
|
-
const ep = normalizeParams({ name, exclude, include_tests, code_only, context: ctxLines, in: inPath });
|
|
359
353
|
const { ok, result, error } = execute(index, 'usages', ep);
|
|
360
354
|
if (!ok) return toolResult(error); // soft error
|
|
361
|
-
return toolResult(output.formatUsages(result, name));
|
|
355
|
+
return toolResult(output.formatUsages(result, ep.name));
|
|
362
356
|
}
|
|
363
357
|
|
|
364
358
|
case 'toc': {
|
|
365
359
|
const index = getIndex(project_dir);
|
|
366
|
-
const ep = normalizeParams({ detailed, top_level, all, top });
|
|
367
360
|
const { ok, result, error } = execute(index, 'toc', ep);
|
|
368
361
|
if (!ok) return toolResult(error); // soft error
|
|
369
362
|
return toolResult(output.formatToc(result, {
|
|
@@ -373,29 +366,26 @@ server.registerTool(
|
|
|
373
366
|
|
|
374
367
|
case 'search': {
|
|
375
368
|
const index = getIndex(project_dir);
|
|
376
|
-
const ep = normalizeParams({ term, exclude, include_tests, code_only, context: ctxLines, case_sensitive, in: inPath, regex });
|
|
377
369
|
const { ok, result, error } = execute(index, 'search', ep);
|
|
378
370
|
if (!ok) return toolResult(error); // soft error
|
|
379
|
-
return toolResult(output.formatSearch(result, term));
|
|
371
|
+
return toolResult(output.formatSearch(result, ep.term));
|
|
380
372
|
}
|
|
381
373
|
|
|
382
374
|
case 'tests': {
|
|
383
375
|
const index = getIndex(project_dir);
|
|
384
|
-
const ep = normalizeParams({ name, calls_only });
|
|
385
376
|
const { ok, result, error } = execute(index, 'tests', ep);
|
|
386
377
|
if (!ok) return toolResult(error); // soft error
|
|
387
|
-
return toolResult(output.formatTests(result, name));
|
|
378
|
+
return toolResult(output.formatTests(result, ep.name));
|
|
388
379
|
}
|
|
389
380
|
|
|
390
381
|
case 'deadcode': {
|
|
391
382
|
const index = getIndex(project_dir);
|
|
392
|
-
const ep = normalizeParams({ exclude, in: inPath, include_exported, include_decorated, include_tests });
|
|
393
383
|
const { ok, result, error } = execute(index, 'deadcode', ep);
|
|
394
384
|
if (!ok) return toolResult(error); // soft error
|
|
395
385
|
return toolResult(output.formatDeadcode(result, {
|
|
396
|
-
top: top || 0,
|
|
397
|
-
decoratedHint: !
|
|
398
|
-
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
|
|
399
389
|
}));
|
|
400
390
|
}
|
|
401
391
|
|
|
@@ -403,32 +393,32 @@ server.registerTool(
|
|
|
403
393
|
|
|
404
394
|
case 'imports': {
|
|
405
395
|
const index = getIndex(project_dir);
|
|
406
|
-
const { ok, result, error } = execute(index, 'imports',
|
|
396
|
+
const { ok, result, error } = execute(index, 'imports', ep);
|
|
407
397
|
if (!ok) return toolResult(error); // soft error
|
|
408
|
-
return toolResult(output.formatImports(result, file));
|
|
398
|
+
return toolResult(output.formatImports(result, ep.file));
|
|
409
399
|
}
|
|
410
400
|
|
|
411
401
|
case 'exporters': {
|
|
412
402
|
const index = getIndex(project_dir);
|
|
413
|
-
const { ok, result, error } = execute(index, 'exporters',
|
|
403
|
+
const { ok, result, error } = execute(index, 'exporters', ep);
|
|
414
404
|
if (!ok) return toolResult(error); // soft error
|
|
415
|
-
return toolResult(output.formatExporters(result, file));
|
|
405
|
+
return toolResult(output.formatExporters(result, ep.file));
|
|
416
406
|
}
|
|
417
407
|
|
|
418
408
|
case 'file_exports': {
|
|
419
409
|
const index = getIndex(project_dir);
|
|
420
|
-
const { ok, result, error } = execute(index, 'fileExports',
|
|
410
|
+
const { ok, result, error } = execute(index, 'fileExports', ep);
|
|
421
411
|
if (!ok) return toolResult(error); // soft error
|
|
422
|
-
return toolResult(output.formatFileExports(result, file));
|
|
412
|
+
return toolResult(output.formatFileExports(result, ep.file));
|
|
423
413
|
}
|
|
424
414
|
|
|
425
415
|
case 'graph': {
|
|
426
416
|
const index = getIndex(project_dir);
|
|
427
|
-
const { ok, result, error } = execute(index, 'graph',
|
|
417
|
+
const { ok, result, error } = execute(index, 'graph', ep);
|
|
428
418
|
if (!ok) return toolResult(error); // soft error
|
|
429
419
|
return toolResult(output.formatGraph(result, {
|
|
430
|
-
showAll: all || depth !== undefined,
|
|
431
|
-
maxDepth: depth ?? 2, file,
|
|
420
|
+
showAll: ep.all || ep.depth !== undefined,
|
|
421
|
+
maxDepth: ep.depth ?? 2, file: ep.file,
|
|
432
422
|
depthHint: 'Set depth parameter for deeper graph.',
|
|
433
423
|
allHint: 'Set depth to expand all children.'
|
|
434
424
|
}));
|
|
@@ -438,14 +428,13 @@ server.registerTool(
|
|
|
438
428
|
|
|
439
429
|
case 'verify': {
|
|
440
430
|
const index = getIndex(project_dir);
|
|
441
|
-
const { ok, result, error } = execute(index, 'verify',
|
|
431
|
+
const { ok, result, error } = execute(index, 'verify', ep);
|
|
442
432
|
if (!ok) return toolResult(error); // soft error
|
|
443
433
|
return toolResult(output.formatVerify(result));
|
|
444
434
|
}
|
|
445
435
|
|
|
446
436
|
case 'plan': {
|
|
447
437
|
const index = getIndex(project_dir);
|
|
448
|
-
const ep = normalizeParams({ name, add_param, remove_param, rename_to, default_value, file });
|
|
449
438
|
const { ok, result, error } = execute(index, 'plan', ep);
|
|
450
439
|
if (!ok) return toolResult(error); // soft error
|
|
451
440
|
return toolResult(output.formatPlan(result));
|
|
@@ -453,7 +442,7 @@ server.registerTool(
|
|
|
453
442
|
|
|
454
443
|
case 'diff_impact': {
|
|
455
444
|
const index = getIndex(project_dir);
|
|
456
|
-
const { ok, result, error } = execute(index, 'diffImpact',
|
|
445
|
+
const { ok, result, error } = execute(index, 'diffImpact', ep);
|
|
457
446
|
if (!ok) return toolResult(error); // soft error — e.g. "not a git repo"
|
|
458
447
|
return toolResult(output.formatDiffImpact(result));
|
|
459
448
|
}
|
|
@@ -462,39 +451,38 @@ server.registerTool(
|
|
|
462
451
|
|
|
463
452
|
case 'typedef': {
|
|
464
453
|
const index = getIndex(project_dir);
|
|
465
|
-
const { ok, result, error } = execute(index, 'typedef',
|
|
454
|
+
const { ok, result, error } = execute(index, 'typedef', ep);
|
|
466
455
|
if (!ok) return toolResult(error); // soft error
|
|
467
|
-
return toolResult(output.formatTypedef(result, name));
|
|
456
|
+
return toolResult(output.formatTypedef(result, ep.name));
|
|
468
457
|
}
|
|
469
458
|
|
|
470
459
|
case 'stacktrace': {
|
|
471
460
|
const index = getIndex(project_dir);
|
|
472
|
-
const { ok, result, error } = execute(index, 'stacktrace',
|
|
461
|
+
const { ok, result, error } = execute(index, 'stacktrace', ep);
|
|
473
462
|
if (!ok) return toolResult(error); // soft error
|
|
474
463
|
return toolResult(output.formatStackTrace(result));
|
|
475
464
|
}
|
|
476
465
|
|
|
477
466
|
case 'api': {
|
|
478
467
|
const index = getIndex(project_dir);
|
|
479
|
-
const { ok, result, error } = execute(index, 'api',
|
|
468
|
+
const { ok, result, error } = execute(index, 'api', ep);
|
|
480
469
|
if (!ok) return toolResult(error); // soft error
|
|
481
|
-
return toolResult(output.formatApi(result, file || '.'));
|
|
470
|
+
return toolResult(output.formatApi(result, ep.file || '.'));
|
|
482
471
|
}
|
|
483
472
|
|
|
484
473
|
case 'stats': {
|
|
485
474
|
const index = getIndex(project_dir);
|
|
486
|
-
const { ok, result, error } = execute(index, 'stats',
|
|
475
|
+
const { ok, result, error } = execute(index, 'stats', ep);
|
|
487
476
|
if (!ok) return toolResult(error); // soft error
|
|
488
|
-
return toolResult(output.formatStats(result, { top: top || 0 }));
|
|
477
|
+
return toolResult(output.formatStats(result, { top: ep.top || 0 }));
|
|
489
478
|
}
|
|
490
479
|
|
|
491
480
|
// ── Extracting Code (via execute) ────────────────────────────
|
|
492
481
|
|
|
493
482
|
case 'fn': {
|
|
494
|
-
const err = requireName(name);
|
|
483
|
+
const err = requireName(ep.name);
|
|
495
484
|
if (err) return err;
|
|
496
485
|
const index = getIndex(project_dir);
|
|
497
|
-
const ep = normalizeParams({ name, file, all });
|
|
498
486
|
const { ok, result, error } = execute(index, 'fn', ep);
|
|
499
487
|
if (!ok) return toolResult(error); // soft error
|
|
500
488
|
// MCP path security: validate all result files are within project root
|
|
@@ -507,13 +495,12 @@ server.registerTool(
|
|
|
507
495
|
}
|
|
508
496
|
|
|
509
497
|
case 'class': {
|
|
510
|
-
const err = requireName(name);
|
|
498
|
+
const err = requireName(ep.name);
|
|
511
499
|
if (err) return err;
|
|
512
|
-
if (
|
|
513
|
-
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.`);
|
|
514
502
|
}
|
|
515
503
|
const index = getIndex(project_dir);
|
|
516
|
-
const ep = normalizeParams({ name, file, all, max_lines });
|
|
517
504
|
const { ok, result, error } = execute(index, 'class', ep);
|
|
518
505
|
if (!ok) return toolResult(error); // soft error (class not found)
|
|
519
506
|
// MCP path security: validate all result files are within project root
|
|
@@ -527,7 +514,6 @@ server.registerTool(
|
|
|
527
514
|
|
|
528
515
|
case 'lines': {
|
|
529
516
|
const index = getIndex(project_dir);
|
|
530
|
-
const ep = normalizeParams({ file, range });
|
|
531
517
|
const { ok, result, error } = execute(index, 'lines', ep);
|
|
532
518
|
if (!ok) return toolResult(error); // soft error
|
|
533
519
|
// MCP path security: validate file is within project root
|
|
@@ -537,13 +523,13 @@ server.registerTool(
|
|
|
537
523
|
}
|
|
538
524
|
|
|
539
525
|
case 'expand': {
|
|
540
|
-
if (item === undefined || item === null) {
|
|
526
|
+
if (ep.item === undefined || ep.item === null) {
|
|
541
527
|
return toolError('Item number is required (e.g. item=1).');
|
|
542
528
|
}
|
|
543
529
|
const index = getIndex(project_dir);
|
|
544
|
-
const lookup = expandCacheInstance.lookup(index.root, item);
|
|
530
|
+
const lookup = expandCacheInstance.lookup(index.root, ep.item);
|
|
545
531
|
const { ok, result, error } = execute(index, 'expand', {
|
|
546
|
-
match: lookup.match, itemNum: item,
|
|
532
|
+
match: lookup.match, itemNum: ep.item,
|
|
547
533
|
itemCount: lookup.itemCount, symbolName: lookup.symbolName,
|
|
548
534
|
validateRoot: true
|
|
549
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",
|