ucn 3.8.18 → 3.8.20
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/.claude/skills/ucn/SKILL.md +2 -2
- package/cli/index.js +226 -58
- package/core/analysis.js +6 -2
- package/core/execute.js +17 -0
- package/core/output/analysis.js +1 -1
- package/core/registry.js +55 -22
- package/core/search.js +482 -127
- package/core/tracing.js +251 -39
- package/languages/javascript.js +19 -2
- package/languages/rust.js +40 -0
- package/mcp/server.js +73 -44
- package/package.json +1 -1
package/mcp/server.js
CHANGED
|
@@ -33,7 +33,7 @@ try {
|
|
|
33
33
|
const { ProjectIndex } = require('../core/project');
|
|
34
34
|
const { findProjectRoot } = require('../core/discovery');
|
|
35
35
|
const output = require('../core/output');
|
|
36
|
-
const { getMcpCommandEnum, normalizeParams, BROAD_COMMANDS: BROAD_CANONICAL, toMcpName } = require('../core/registry');
|
|
36
|
+
const { getMcpCommandEnum, normalizeParams, BROAD_COMMANDS: BROAD_CANONICAL, toMcpName, FLAG_APPLICABILITY, REVERSE_PARAM_MAP, generateMcpParamSection } = require('../core/registry');
|
|
37
37
|
const { execute } = require('../core/execute');
|
|
38
38
|
const { ExpandCache } = require('../core/expand-cache');
|
|
39
39
|
|
|
@@ -123,8 +123,9 @@ const MAX_OUTPUT_CHARS = 100000; // hard ceiling even with max_chars overrid
|
|
|
123
123
|
// Broad commands (derived from registry): output is project-wide, truncation means you need a filter
|
|
124
124
|
const BROAD_COMMANDS = new Set([...BROAD_CANONICAL].map(toMcpName));
|
|
125
125
|
|
|
126
|
-
function toolResult(text, command, maxChars) {
|
|
127
|
-
|
|
126
|
+
function toolResult(text, command, maxChars, suffixNote) {
|
|
127
|
+
const suffix = suffixNote || '';
|
|
128
|
+
if (!text) return { content: [{ type: 'text', text: '(no output)' + suffix }] };
|
|
128
129
|
const defaultLimit = BROAD_COMMANDS.has(command) ? BROAD_OUTPUT_CHARS : DEFAULT_OUTPUT_CHARS;
|
|
129
130
|
const limit = Math.min(maxChars || defaultLimit, MAX_OUTPUT_CHARS);
|
|
130
131
|
if (text.length > limit) {
|
|
@@ -144,9 +145,9 @@ function toolResult(text, command, maxChars) {
|
|
|
144
145
|
usages: 'Use file= to scope to specific files.',
|
|
145
146
|
};
|
|
146
147
|
const narrow = hints[command] || 'Use file=/in=/exclude= to narrow scope.';
|
|
147
|
-
return { content: [{ type: 'text', text: cleanCut + `\n\n... OUTPUT TRUNCATED: showing ${limit} of ${fullSize} chars. Full output would be ~${fullTokens} tokens. ${narrow} Or use all=true to see everything (warning: ~${fullTokens} tokens).` }] };
|
|
148
|
+
return { content: [{ type: 'text', text: cleanCut + `\n\n... OUTPUT TRUNCATED: showing ${limit} of ${fullSize} chars. Full output would be ~${fullTokens} tokens. ${narrow} Or use all=true to see everything (warning: ~${fullTokens} tokens).` + suffix }] };
|
|
148
149
|
}
|
|
149
|
-
return { content: [{ type: 'text', text }] };
|
|
150
|
+
return { content: [{ type: 'text', text: text + suffix }] };
|
|
150
151
|
}
|
|
151
152
|
|
|
152
153
|
function toolError(message) {
|
|
@@ -238,7 +239,7 @@ OTHER:
|
|
|
238
239
|
- typedef <name>: Find type definitions matching a name: interfaces, enums, structs, traits, type aliases. See field shapes, required methods, or enum values.
|
|
239
240
|
- stacktrace: Parse a stack trace, show source context per frame. Requires stack param. Handles JS, Python, Go, Rust, Java formats.
|
|
240
241
|
- api: Public API surface of project or file: all exported/public symbols with signatures. Use to understand what a library exposes. Pass file to scope to one file. Python needs __all__; use toc instead.
|
|
241
|
-
- stats: Quick project stats: file counts, symbol counts, lines of code by language and symbol type. Use functions=true for per-function line counts sorted by size (complexity audit)
|
|
242
|
+
- stats: Quick project stats: file counts, symbol counts, lines of code by language and symbol type. Use functions=true for per-function line counts sorted by size (complexity audit).` + generateMcpParamSection();
|
|
242
243
|
|
|
243
244
|
server.registerTool(
|
|
244
245
|
'ucn',
|
|
@@ -307,11 +308,39 @@ server.registerTool(
|
|
|
307
308
|
// This eliminates per-case param selection and prevents CLI/MCP drift.
|
|
308
309
|
const { command: _c, project_dir: _p, ...rawParams } = args;
|
|
309
310
|
const ep = normalizeParams(rawParams);
|
|
311
|
+
|
|
312
|
+
// Strip params not applicable to this command (prevents silent no-ops).
|
|
313
|
+
// Global/core params are always allowed — only optional flags are filtered.
|
|
314
|
+
const strippedParams = [];
|
|
315
|
+
const applicable = FLAG_APPLICABILITY[command];
|
|
316
|
+
if (applicable) {
|
|
317
|
+
// Truly global options — apply to all commands (build/display control).
|
|
318
|
+
// Command-specific params (name, term, stack, range, etc.) are in FLAG_APPLICABILITY.
|
|
319
|
+
const coreParams = new Set(['maxChars', 'maxFiles', 'followSymlinks']);
|
|
320
|
+
for (const key of Object.keys(ep)) {
|
|
321
|
+
if (coreParams.has(key)) continue;
|
|
322
|
+
if (!applicable.includes(key) && ep[key] !== undefined &&
|
|
323
|
+
!(Array.isArray(ep[key]) && ep[key].length === 0)) {
|
|
324
|
+
strippedParams.push(REVERSE_PARAM_MAP[key] || key);
|
|
325
|
+
delete ep[key];
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
310
330
|
// all=true bypasses both formatter caps AND char truncation (parity with CLI --all)
|
|
311
331
|
const maxChars = ep.all ? MAX_OUTPUT_CHARS : ep.maxChars;
|
|
312
332
|
|
|
313
|
-
//
|
|
314
|
-
const
|
|
333
|
+
// Build stripping note (appended inside truncation boundary on success paths)
|
|
334
|
+
const strippedNote = strippedParams.length > 0
|
|
335
|
+
? `\n\nNote: ${strippedParams.join(', ')} ignored (not applicable to ${command}).`
|
|
336
|
+
: '';
|
|
337
|
+
|
|
338
|
+
// Wrap toolResult to auto-inject command + maxChars + stripping note
|
|
339
|
+
const tr = (text) => toolResult(text, command, maxChars, strippedNote);
|
|
340
|
+
// Wrap toolError to include stripping note on error paths too
|
|
341
|
+
const te = strippedNote
|
|
342
|
+
? (msg) => toolError(msg + strippedNote)
|
|
343
|
+
: toolError;
|
|
315
344
|
|
|
316
345
|
let index = null; // Track for post-command cache save
|
|
317
346
|
try {
|
|
@@ -326,7 +355,7 @@ server.registerTool(
|
|
|
326
355
|
case 'about': {
|
|
327
356
|
index = getIndex(project_dir, ep);
|
|
328
357
|
const { ok, result, error, note } = execute(index, 'about', ep);
|
|
329
|
-
if (!ok) return
|
|
358
|
+
if (!ok) return te(error);
|
|
330
359
|
let aboutText = output.formatAbout(result, {
|
|
331
360
|
allHint: 'Repeat with all=true to show all.',
|
|
332
361
|
methodsHint: 'Note: obj.method() callers/callees excluded. Use include_methods=true to include them.',
|
|
@@ -339,7 +368,7 @@ server.registerTool(
|
|
|
339
368
|
case 'context': {
|
|
340
369
|
index = getIndex(project_dir, ep);
|
|
341
370
|
const { ok, result: ctx, error, note } = execute(index, 'context', ep);
|
|
342
|
-
if (!ok) return
|
|
371
|
+
if (!ok) return te(error);
|
|
343
372
|
const { text, expandable } = output.formatContext(ctx, {
|
|
344
373
|
expandHint: 'Use expand command with item number to see code for any item.',
|
|
345
374
|
showConfidence: ep.showConfidence !== false,
|
|
@@ -353,7 +382,7 @@ server.registerTool(
|
|
|
353
382
|
case 'impact': {
|
|
354
383
|
index = getIndex(project_dir, ep);
|
|
355
384
|
const { ok, result, error, note } = execute(index, 'impact', ep);
|
|
356
|
-
if (!ok) return
|
|
385
|
+
if (!ok) return te(error);
|
|
357
386
|
let impactText = output.formatImpact(result);
|
|
358
387
|
if (note) impactText += '\n\n' + note;
|
|
359
388
|
return tr(impactText);
|
|
@@ -362,7 +391,7 @@ server.registerTool(
|
|
|
362
391
|
case 'blast': {
|
|
363
392
|
index = getIndex(project_dir, ep);
|
|
364
393
|
const { ok, result, error, note } = execute(index, 'blast', ep);
|
|
365
|
-
if (!ok) return
|
|
394
|
+
if (!ok) return te(error);
|
|
366
395
|
let blastText = output.formatBlast(result, {
|
|
367
396
|
allHint: 'Set depth to expand all children.',
|
|
368
397
|
});
|
|
@@ -373,7 +402,7 @@ server.registerTool(
|
|
|
373
402
|
case 'smart': {
|
|
374
403
|
index = getIndex(project_dir, ep);
|
|
375
404
|
const { ok, result, error, note } = execute(index, 'smart', ep);
|
|
376
|
-
if (!ok) return
|
|
405
|
+
if (!ok) return te(error);
|
|
377
406
|
let smartText = output.formatSmart(result);
|
|
378
407
|
if (note) smartText += '\n\n' + note;
|
|
379
408
|
return tr(smartText);
|
|
@@ -382,7 +411,7 @@ server.registerTool(
|
|
|
382
411
|
case 'trace': {
|
|
383
412
|
index = getIndex(project_dir, ep);
|
|
384
413
|
const { ok, result, error, note } = execute(index, 'trace', ep);
|
|
385
|
-
if (!ok) return
|
|
414
|
+
if (!ok) return te(error);
|
|
386
415
|
let traceText = output.formatTrace(result, {
|
|
387
416
|
allHint: 'Set depth to expand all children.',
|
|
388
417
|
methodsHint: 'Note: obj.method() calls excluded. Use include_methods=true to include them.'
|
|
@@ -394,7 +423,7 @@ server.registerTool(
|
|
|
394
423
|
case 'reverse_trace': {
|
|
395
424
|
index = getIndex(project_dir, ep);
|
|
396
425
|
const { ok, result, error, note } = execute(index, 'reverseTrace', ep);
|
|
397
|
-
if (!ok) return
|
|
426
|
+
if (!ok) return te(error);
|
|
398
427
|
let rtText = output.formatReverseTrace(result, {
|
|
399
428
|
allHint: 'Set depth to expand all children.',
|
|
400
429
|
});
|
|
@@ -405,14 +434,14 @@ server.registerTool(
|
|
|
405
434
|
case 'example': {
|
|
406
435
|
index = getIndex(project_dir, ep);
|
|
407
436
|
const { ok, result, error } = execute(index, 'example', ep);
|
|
408
|
-
if (!ok) return
|
|
437
|
+
if (!ok) return te(error);
|
|
409
438
|
return tr(output.formatExample(result, ep.name));
|
|
410
439
|
}
|
|
411
440
|
|
|
412
441
|
case 'related': {
|
|
413
442
|
index = getIndex(project_dir, ep);
|
|
414
443
|
const { ok, result, error, note } = execute(index, 'related', ep);
|
|
415
|
-
if (!ok) return
|
|
444
|
+
if (!ok) return te(error);
|
|
416
445
|
let relText = output.formatRelated(result, {
|
|
417
446
|
all: ep.all || false, top: ep.top,
|
|
418
447
|
allHint: 'Repeat with all=true to show all.'
|
|
@@ -426,7 +455,7 @@ server.registerTool(
|
|
|
426
455
|
case 'find': {
|
|
427
456
|
index = getIndex(project_dir, ep);
|
|
428
457
|
const { ok, result, error, note } = execute(index, 'find', ep);
|
|
429
|
-
if (!ok) return
|
|
458
|
+
if (!ok) return te(error);
|
|
430
459
|
let text = output.formatFind(result, ep.name, ep.top);
|
|
431
460
|
if (note) text += '\n\n' + note;
|
|
432
461
|
return tr(text);
|
|
@@ -435,7 +464,7 @@ server.registerTool(
|
|
|
435
464
|
case 'usages': {
|
|
436
465
|
index = getIndex(project_dir, ep);
|
|
437
466
|
const { ok, result, error, note } = execute(index, 'usages', ep);
|
|
438
|
-
if (!ok) return
|
|
467
|
+
if (!ok) return te(error);
|
|
439
468
|
let text = output.formatUsages(result, ep.name);
|
|
440
469
|
if (note) text += '\n\n' + note;
|
|
441
470
|
return tr(text);
|
|
@@ -444,7 +473,7 @@ server.registerTool(
|
|
|
444
473
|
case 'toc': {
|
|
445
474
|
index = getIndex(project_dir, ep);
|
|
446
475
|
const { ok, result, error, note } = execute(index, 'toc', ep);
|
|
447
|
-
if (!ok) return
|
|
476
|
+
if (!ok) return te(error);
|
|
448
477
|
let text = output.formatToc(result, {
|
|
449
478
|
topHint: 'Set top=N or use detailed=false for compact view.'
|
|
450
479
|
});
|
|
@@ -455,7 +484,7 @@ server.registerTool(
|
|
|
455
484
|
case 'search': {
|
|
456
485
|
index = getIndex(project_dir, ep);
|
|
457
486
|
const { ok, result, error, structural, note } = execute(index, 'search', ep);
|
|
458
|
-
if (!ok) return
|
|
487
|
+
if (!ok) return te(error);
|
|
459
488
|
let searchText;
|
|
460
489
|
if (structural) {
|
|
461
490
|
searchText = output.formatStructuralSearch(result);
|
|
@@ -469,7 +498,7 @@ server.registerTool(
|
|
|
469
498
|
case 'tests': {
|
|
470
499
|
index = getIndex(project_dir, ep);
|
|
471
500
|
const { ok, result, error, note } = execute(index, 'tests', ep);
|
|
472
|
-
if (!ok) return
|
|
501
|
+
if (!ok) return te(error);
|
|
473
502
|
let testsText = output.formatTests(result, ep.name);
|
|
474
503
|
if (note) testsText += '\n\n' + note;
|
|
475
504
|
return tr(testsText);
|
|
@@ -478,7 +507,7 @@ server.registerTool(
|
|
|
478
507
|
case 'affected_tests': {
|
|
479
508
|
index = getIndex(project_dir, ep);
|
|
480
509
|
const { ok, result, error, note } = execute(index, 'affectedTests', ep);
|
|
481
|
-
if (!ok) return
|
|
510
|
+
if (!ok) return te(error);
|
|
482
511
|
let atText = output.formatAffectedTests(result, { all: ep.all });
|
|
483
512
|
if (note) atText += '\n\n' + note;
|
|
484
513
|
return tr(atText);
|
|
@@ -487,7 +516,7 @@ server.registerTool(
|
|
|
487
516
|
case 'deadcode': {
|
|
488
517
|
index = getIndex(project_dir, ep);
|
|
489
518
|
const { ok, result, error, note } = execute(index, 'deadcode', ep);
|
|
490
|
-
if (!ok) return
|
|
519
|
+
if (!ok) return te(error);
|
|
491
520
|
const dcNote = note;
|
|
492
521
|
let dcText = output.formatDeadcode(result, {
|
|
493
522
|
top: ep.top || 0,
|
|
@@ -501,7 +530,7 @@ server.registerTool(
|
|
|
501
530
|
case 'entrypoints': {
|
|
502
531
|
index = getIndex(project_dir, ep);
|
|
503
532
|
const { ok, result, error, note } = execute(index, 'entrypoints', ep);
|
|
504
|
-
if (!ok) return
|
|
533
|
+
if (!ok) return te(error);
|
|
505
534
|
let epText = output.formatEntrypoints(result);
|
|
506
535
|
if (note) epText += '\n\n' + note;
|
|
507
536
|
return tr(epText);
|
|
@@ -512,28 +541,28 @@ server.registerTool(
|
|
|
512
541
|
case 'imports': {
|
|
513
542
|
index = getIndex(project_dir, ep);
|
|
514
543
|
const { ok, result, error } = execute(index, 'imports', ep);
|
|
515
|
-
if (!ok) return
|
|
544
|
+
if (!ok) return te(error);
|
|
516
545
|
return tr(output.formatImports(result, ep.file));
|
|
517
546
|
}
|
|
518
547
|
|
|
519
548
|
case 'exporters': {
|
|
520
549
|
index = getIndex(project_dir, ep);
|
|
521
550
|
const { ok, result, error } = execute(index, 'exporters', ep);
|
|
522
|
-
if (!ok) return
|
|
551
|
+
if (!ok) return te(error);
|
|
523
552
|
return tr(output.formatExporters(result, ep.file));
|
|
524
553
|
}
|
|
525
554
|
|
|
526
555
|
case 'file_exports': {
|
|
527
556
|
index = getIndex(project_dir, ep);
|
|
528
557
|
const { ok, result, error } = execute(index, 'fileExports', ep);
|
|
529
|
-
if (!ok) return
|
|
558
|
+
if (!ok) return te(error);
|
|
530
559
|
return tr(output.formatFileExports(result, ep.file));
|
|
531
560
|
}
|
|
532
561
|
|
|
533
562
|
case 'graph': {
|
|
534
563
|
index = getIndex(project_dir, ep);
|
|
535
564
|
const { ok, result, error } = execute(index, 'graph', ep);
|
|
536
|
-
if (!ok) return
|
|
565
|
+
if (!ok) return te(error);
|
|
537
566
|
return tr(output.formatGraph(result, {
|
|
538
567
|
showAll: ep.all || ep.depth !== undefined,
|
|
539
568
|
maxDepth: ep.depth ?? 2, file: ep.file,
|
|
@@ -545,7 +574,7 @@ server.registerTool(
|
|
|
545
574
|
case 'circular_deps': {
|
|
546
575
|
index = getIndex(project_dir, ep);
|
|
547
576
|
const { ok, result, error } = execute(index, 'circularDeps', ep);
|
|
548
|
-
if (!ok) return
|
|
577
|
+
if (!ok) return te(error);
|
|
549
578
|
return tr(output.formatCircularDeps(result));
|
|
550
579
|
}
|
|
551
580
|
|
|
@@ -554,21 +583,21 @@ server.registerTool(
|
|
|
554
583
|
case 'verify': {
|
|
555
584
|
index = getIndex(project_dir, ep);
|
|
556
585
|
const { ok, result, error } = execute(index, 'verify', ep);
|
|
557
|
-
if (!ok) return
|
|
586
|
+
if (!ok) return te(error);
|
|
558
587
|
return tr(output.formatVerify(result));
|
|
559
588
|
}
|
|
560
589
|
|
|
561
590
|
case 'plan': {
|
|
562
591
|
index = getIndex(project_dir, ep);
|
|
563
592
|
const { ok, result, error } = execute(index, 'plan', ep);
|
|
564
|
-
if (!ok) return
|
|
593
|
+
if (!ok) return te(error);
|
|
565
594
|
return tr(output.formatPlan(result));
|
|
566
595
|
}
|
|
567
596
|
|
|
568
597
|
case 'diff_impact': {
|
|
569
598
|
index = getIndex(project_dir, ep);
|
|
570
599
|
const { ok, result, error, note } = execute(index, 'diffImpact', ep);
|
|
571
|
-
if (!ok) return
|
|
600
|
+
if (!ok) return te(error);
|
|
572
601
|
let diText = output.formatDiffImpact(result, { all: ep.all });
|
|
573
602
|
if (note) diText += '\n\n' + note;
|
|
574
603
|
return tr(diText);
|
|
@@ -579,21 +608,21 @@ server.registerTool(
|
|
|
579
608
|
case 'typedef': {
|
|
580
609
|
index = getIndex(project_dir, ep);
|
|
581
610
|
const { ok, result, error } = execute(index, 'typedef', ep);
|
|
582
|
-
if (!ok) return
|
|
611
|
+
if (!ok) return te(error);
|
|
583
612
|
return tr(output.formatTypedef(result, ep.name));
|
|
584
613
|
}
|
|
585
614
|
|
|
586
615
|
case 'stacktrace': {
|
|
587
616
|
index = getIndex(project_dir, ep);
|
|
588
617
|
const { ok, result, error } = execute(index, 'stacktrace', ep);
|
|
589
|
-
if (!ok) return
|
|
618
|
+
if (!ok) return te(error);
|
|
590
619
|
return tr(output.formatStackTrace(result));
|
|
591
620
|
}
|
|
592
621
|
|
|
593
622
|
case 'api': {
|
|
594
623
|
index = getIndex(project_dir, ep);
|
|
595
624
|
const { ok, result, error, note } = execute(index, 'api', ep);
|
|
596
|
-
if (!ok) return
|
|
625
|
+
if (!ok) return te(error);
|
|
597
626
|
let apiText = output.formatApi(result, ep.file || '.');
|
|
598
627
|
if (note) apiText += '\n\n' + note;
|
|
599
628
|
return tr(apiText);
|
|
@@ -602,7 +631,7 @@ server.registerTool(
|
|
|
602
631
|
case 'stats': {
|
|
603
632
|
index = getIndex(project_dir, ep);
|
|
604
633
|
const { ok, result, error, note } = execute(index, 'stats', ep);
|
|
605
|
-
if (!ok) return
|
|
634
|
+
if (!ok) return te(error);
|
|
606
635
|
let statsText = output.formatStats(result, { top: ep.top || 0 });
|
|
607
636
|
if (note) statsText += '\n\n' + note;
|
|
608
637
|
return tr(statsText);
|
|
@@ -613,7 +642,7 @@ server.registerTool(
|
|
|
613
642
|
case 'fn': {
|
|
614
643
|
index = getIndex(project_dir, ep);
|
|
615
644
|
const { ok, result, error, note } = execute(index, 'fn', ep);
|
|
616
|
-
if (!ok) return
|
|
645
|
+
if (!ok) return te(error);
|
|
617
646
|
// MCP path security: validate all result files are within project root
|
|
618
647
|
for (const entry of result.entries) {
|
|
619
648
|
const check = resolveAndValidatePath(index, entry.match.relativePath || path.relative(index.root, entry.match.file));
|
|
@@ -626,7 +655,7 @@ server.registerTool(
|
|
|
626
655
|
case 'class': {
|
|
627
656
|
index = getIndex(project_dir, ep);
|
|
628
657
|
const { ok, result, error, note } = execute(index, 'class', ep);
|
|
629
|
-
if (!ok) return
|
|
658
|
+
if (!ok) return te(error); // soft error (class not found)
|
|
630
659
|
// MCP path security: validate all result files are within project root
|
|
631
660
|
for (const entry of result.entries) {
|
|
632
661
|
const check = resolveAndValidatePath(index, entry.match.relativePath || path.relative(index.root, entry.match.file));
|
|
@@ -639,7 +668,7 @@ server.registerTool(
|
|
|
639
668
|
case 'lines': {
|
|
640
669
|
index = getIndex(project_dir, ep);
|
|
641
670
|
const { ok, result, error } = execute(index, 'lines', ep);
|
|
642
|
-
if (!ok) return
|
|
671
|
+
if (!ok) return te(error);
|
|
643
672
|
// MCP path security: validate file is within project root
|
|
644
673
|
const check = resolveAndValidatePath(index, result.relativePath);
|
|
645
674
|
if (typeof check !== 'string') return check;
|
|
@@ -648,7 +677,7 @@ server.registerTool(
|
|
|
648
677
|
|
|
649
678
|
case 'expand': {
|
|
650
679
|
if (ep.item === undefined || ep.item === null) {
|
|
651
|
-
return
|
|
680
|
+
return te('Item number is required (e.g. item=1).');
|
|
652
681
|
}
|
|
653
682
|
index = getIndex(project_dir, ep);
|
|
654
683
|
const lookup = expandCacheInstance.lookup(index.root, ep.item);
|
|
@@ -657,15 +686,15 @@ server.registerTool(
|
|
|
657
686
|
itemCount: lookup.itemCount, symbolName: lookup.symbolName,
|
|
658
687
|
validateRoot: true
|
|
659
688
|
});
|
|
660
|
-
if (!ok) return
|
|
689
|
+
if (!ok) return te(error);
|
|
661
690
|
return tr(result.text);
|
|
662
691
|
}
|
|
663
692
|
|
|
664
693
|
default:
|
|
665
|
-
return
|
|
694
|
+
return te(`Unknown command: ${command}`);
|
|
666
695
|
}
|
|
667
696
|
} catch (e) {
|
|
668
|
-
return
|
|
697
|
+
return te(e.message);
|
|
669
698
|
} finally {
|
|
670
699
|
// Persist calls cache after command execution.
|
|
671
700
|
// getIndex() only saves after build (when callsCache is empty).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ucn",
|
|
3
|
-
"version": "3.8.
|
|
3
|
+
"version": "3.8.20",
|
|
4
4
|
"mcpName": "io.github.mleoca/ucn",
|
|
5
5
|
"description": "Code intelligence toolkit for AI agents — extract functions, trace call chains, find callers, detect dead code without reading entire files. Works as MCP server, CLI, or agent skill. Supports JS/TS, Python, Go, Rust, Java.",
|
|
6
6
|
"main": "index.js",
|