ucn 3.8.18 → 3.8.19
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 +37 -4
- 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 +75 -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,41 @@ 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
|
+
// Core params that are always allowed (primary args, global options)
|
|
318
|
+
const coreParams = new Set([
|
|
319
|
+
'name', 'term', 'stack', 'range', 'base', 'staged',
|
|
320
|
+
'all', 'json', 'maxChars', 'maxFiles', 'workers',
|
|
321
|
+
]);
|
|
322
|
+
for (const key of Object.keys(ep)) {
|
|
323
|
+
if (coreParams.has(key)) continue;
|
|
324
|
+
if (!applicable.includes(key) && ep[key] !== undefined &&
|
|
325
|
+
!(Array.isArray(ep[key]) && ep[key].length === 0)) {
|
|
326
|
+
strippedParams.push(REVERSE_PARAM_MAP[key] || key);
|
|
327
|
+
delete ep[key];
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
310
332
|
// all=true bypasses both formatter caps AND char truncation (parity with CLI --all)
|
|
311
333
|
const maxChars = ep.all ? MAX_OUTPUT_CHARS : ep.maxChars;
|
|
312
334
|
|
|
313
|
-
//
|
|
314
|
-
const
|
|
335
|
+
// Build stripping note (appended inside truncation boundary on success paths)
|
|
336
|
+
const strippedNote = strippedParams.length > 0
|
|
337
|
+
? `\n\nNote: ${strippedParams.join(', ')} ignored (not applicable to ${command}).`
|
|
338
|
+
: '';
|
|
339
|
+
|
|
340
|
+
// Wrap toolResult to auto-inject command + maxChars + stripping note
|
|
341
|
+
const tr = (text) => toolResult(text, command, maxChars, strippedNote);
|
|
342
|
+
// Wrap toolError to include stripping note on error paths too
|
|
343
|
+
const te = strippedNote
|
|
344
|
+
? (msg) => toolError(msg + strippedNote)
|
|
345
|
+
: toolError;
|
|
315
346
|
|
|
316
347
|
let index = null; // Track for post-command cache save
|
|
317
348
|
try {
|
|
@@ -326,7 +357,7 @@ server.registerTool(
|
|
|
326
357
|
case 'about': {
|
|
327
358
|
index = getIndex(project_dir, ep);
|
|
328
359
|
const { ok, result, error, note } = execute(index, 'about', ep);
|
|
329
|
-
if (!ok) return
|
|
360
|
+
if (!ok) return te(error);
|
|
330
361
|
let aboutText = output.formatAbout(result, {
|
|
331
362
|
allHint: 'Repeat with all=true to show all.',
|
|
332
363
|
methodsHint: 'Note: obj.method() callers/callees excluded. Use include_methods=true to include them.',
|
|
@@ -339,7 +370,7 @@ server.registerTool(
|
|
|
339
370
|
case 'context': {
|
|
340
371
|
index = getIndex(project_dir, ep);
|
|
341
372
|
const { ok, result: ctx, error, note } = execute(index, 'context', ep);
|
|
342
|
-
if (!ok) return
|
|
373
|
+
if (!ok) return te(error);
|
|
343
374
|
const { text, expandable } = output.formatContext(ctx, {
|
|
344
375
|
expandHint: 'Use expand command with item number to see code for any item.',
|
|
345
376
|
showConfidence: ep.showConfidence !== false,
|
|
@@ -353,7 +384,7 @@ server.registerTool(
|
|
|
353
384
|
case 'impact': {
|
|
354
385
|
index = getIndex(project_dir, ep);
|
|
355
386
|
const { ok, result, error, note } = execute(index, 'impact', ep);
|
|
356
|
-
if (!ok) return
|
|
387
|
+
if (!ok) return te(error);
|
|
357
388
|
let impactText = output.formatImpact(result);
|
|
358
389
|
if (note) impactText += '\n\n' + note;
|
|
359
390
|
return tr(impactText);
|
|
@@ -362,7 +393,7 @@ server.registerTool(
|
|
|
362
393
|
case 'blast': {
|
|
363
394
|
index = getIndex(project_dir, ep);
|
|
364
395
|
const { ok, result, error, note } = execute(index, 'blast', ep);
|
|
365
|
-
if (!ok) return
|
|
396
|
+
if (!ok) return te(error);
|
|
366
397
|
let blastText = output.formatBlast(result, {
|
|
367
398
|
allHint: 'Set depth to expand all children.',
|
|
368
399
|
});
|
|
@@ -373,7 +404,7 @@ server.registerTool(
|
|
|
373
404
|
case 'smart': {
|
|
374
405
|
index = getIndex(project_dir, ep);
|
|
375
406
|
const { ok, result, error, note } = execute(index, 'smart', ep);
|
|
376
|
-
if (!ok) return
|
|
407
|
+
if (!ok) return te(error);
|
|
377
408
|
let smartText = output.formatSmart(result);
|
|
378
409
|
if (note) smartText += '\n\n' + note;
|
|
379
410
|
return tr(smartText);
|
|
@@ -382,7 +413,7 @@ server.registerTool(
|
|
|
382
413
|
case 'trace': {
|
|
383
414
|
index = getIndex(project_dir, ep);
|
|
384
415
|
const { ok, result, error, note } = execute(index, 'trace', ep);
|
|
385
|
-
if (!ok) return
|
|
416
|
+
if (!ok) return te(error);
|
|
386
417
|
let traceText = output.formatTrace(result, {
|
|
387
418
|
allHint: 'Set depth to expand all children.',
|
|
388
419
|
methodsHint: 'Note: obj.method() calls excluded. Use include_methods=true to include them.'
|
|
@@ -394,7 +425,7 @@ server.registerTool(
|
|
|
394
425
|
case 'reverse_trace': {
|
|
395
426
|
index = getIndex(project_dir, ep);
|
|
396
427
|
const { ok, result, error, note } = execute(index, 'reverseTrace', ep);
|
|
397
|
-
if (!ok) return
|
|
428
|
+
if (!ok) return te(error);
|
|
398
429
|
let rtText = output.formatReverseTrace(result, {
|
|
399
430
|
allHint: 'Set depth to expand all children.',
|
|
400
431
|
});
|
|
@@ -405,14 +436,14 @@ server.registerTool(
|
|
|
405
436
|
case 'example': {
|
|
406
437
|
index = getIndex(project_dir, ep);
|
|
407
438
|
const { ok, result, error } = execute(index, 'example', ep);
|
|
408
|
-
if (!ok) return
|
|
439
|
+
if (!ok) return te(error);
|
|
409
440
|
return tr(output.formatExample(result, ep.name));
|
|
410
441
|
}
|
|
411
442
|
|
|
412
443
|
case 'related': {
|
|
413
444
|
index = getIndex(project_dir, ep);
|
|
414
445
|
const { ok, result, error, note } = execute(index, 'related', ep);
|
|
415
|
-
if (!ok) return
|
|
446
|
+
if (!ok) return te(error);
|
|
416
447
|
let relText = output.formatRelated(result, {
|
|
417
448
|
all: ep.all || false, top: ep.top,
|
|
418
449
|
allHint: 'Repeat with all=true to show all.'
|
|
@@ -426,7 +457,7 @@ server.registerTool(
|
|
|
426
457
|
case 'find': {
|
|
427
458
|
index = getIndex(project_dir, ep);
|
|
428
459
|
const { ok, result, error, note } = execute(index, 'find', ep);
|
|
429
|
-
if (!ok) return
|
|
460
|
+
if (!ok) return te(error);
|
|
430
461
|
let text = output.formatFind(result, ep.name, ep.top);
|
|
431
462
|
if (note) text += '\n\n' + note;
|
|
432
463
|
return tr(text);
|
|
@@ -435,7 +466,7 @@ server.registerTool(
|
|
|
435
466
|
case 'usages': {
|
|
436
467
|
index = getIndex(project_dir, ep);
|
|
437
468
|
const { ok, result, error, note } = execute(index, 'usages', ep);
|
|
438
|
-
if (!ok) return
|
|
469
|
+
if (!ok) return te(error);
|
|
439
470
|
let text = output.formatUsages(result, ep.name);
|
|
440
471
|
if (note) text += '\n\n' + note;
|
|
441
472
|
return tr(text);
|
|
@@ -444,7 +475,7 @@ server.registerTool(
|
|
|
444
475
|
case 'toc': {
|
|
445
476
|
index = getIndex(project_dir, ep);
|
|
446
477
|
const { ok, result, error, note } = execute(index, 'toc', ep);
|
|
447
|
-
if (!ok) return
|
|
478
|
+
if (!ok) return te(error);
|
|
448
479
|
let text = output.formatToc(result, {
|
|
449
480
|
topHint: 'Set top=N or use detailed=false for compact view.'
|
|
450
481
|
});
|
|
@@ -455,7 +486,7 @@ server.registerTool(
|
|
|
455
486
|
case 'search': {
|
|
456
487
|
index = getIndex(project_dir, ep);
|
|
457
488
|
const { ok, result, error, structural, note } = execute(index, 'search', ep);
|
|
458
|
-
if (!ok) return
|
|
489
|
+
if (!ok) return te(error);
|
|
459
490
|
let searchText;
|
|
460
491
|
if (structural) {
|
|
461
492
|
searchText = output.formatStructuralSearch(result);
|
|
@@ -469,7 +500,7 @@ server.registerTool(
|
|
|
469
500
|
case 'tests': {
|
|
470
501
|
index = getIndex(project_dir, ep);
|
|
471
502
|
const { ok, result, error, note } = execute(index, 'tests', ep);
|
|
472
|
-
if (!ok) return
|
|
503
|
+
if (!ok) return te(error);
|
|
473
504
|
let testsText = output.formatTests(result, ep.name);
|
|
474
505
|
if (note) testsText += '\n\n' + note;
|
|
475
506
|
return tr(testsText);
|
|
@@ -478,7 +509,7 @@ server.registerTool(
|
|
|
478
509
|
case 'affected_tests': {
|
|
479
510
|
index = getIndex(project_dir, ep);
|
|
480
511
|
const { ok, result, error, note } = execute(index, 'affectedTests', ep);
|
|
481
|
-
if (!ok) return
|
|
512
|
+
if (!ok) return te(error);
|
|
482
513
|
let atText = output.formatAffectedTests(result, { all: ep.all });
|
|
483
514
|
if (note) atText += '\n\n' + note;
|
|
484
515
|
return tr(atText);
|
|
@@ -487,7 +518,7 @@ server.registerTool(
|
|
|
487
518
|
case 'deadcode': {
|
|
488
519
|
index = getIndex(project_dir, ep);
|
|
489
520
|
const { ok, result, error, note } = execute(index, 'deadcode', ep);
|
|
490
|
-
if (!ok) return
|
|
521
|
+
if (!ok) return te(error);
|
|
491
522
|
const dcNote = note;
|
|
492
523
|
let dcText = output.formatDeadcode(result, {
|
|
493
524
|
top: ep.top || 0,
|
|
@@ -501,7 +532,7 @@ server.registerTool(
|
|
|
501
532
|
case 'entrypoints': {
|
|
502
533
|
index = getIndex(project_dir, ep);
|
|
503
534
|
const { ok, result, error, note } = execute(index, 'entrypoints', ep);
|
|
504
|
-
if (!ok) return
|
|
535
|
+
if (!ok) return te(error);
|
|
505
536
|
let epText = output.formatEntrypoints(result);
|
|
506
537
|
if (note) epText += '\n\n' + note;
|
|
507
538
|
return tr(epText);
|
|
@@ -512,28 +543,28 @@ server.registerTool(
|
|
|
512
543
|
case 'imports': {
|
|
513
544
|
index = getIndex(project_dir, ep);
|
|
514
545
|
const { ok, result, error } = execute(index, 'imports', ep);
|
|
515
|
-
if (!ok) return
|
|
546
|
+
if (!ok) return te(error);
|
|
516
547
|
return tr(output.formatImports(result, ep.file));
|
|
517
548
|
}
|
|
518
549
|
|
|
519
550
|
case 'exporters': {
|
|
520
551
|
index = getIndex(project_dir, ep);
|
|
521
552
|
const { ok, result, error } = execute(index, 'exporters', ep);
|
|
522
|
-
if (!ok) return
|
|
553
|
+
if (!ok) return te(error);
|
|
523
554
|
return tr(output.formatExporters(result, ep.file));
|
|
524
555
|
}
|
|
525
556
|
|
|
526
557
|
case 'file_exports': {
|
|
527
558
|
index = getIndex(project_dir, ep);
|
|
528
559
|
const { ok, result, error } = execute(index, 'fileExports', ep);
|
|
529
|
-
if (!ok) return
|
|
560
|
+
if (!ok) return te(error);
|
|
530
561
|
return tr(output.formatFileExports(result, ep.file));
|
|
531
562
|
}
|
|
532
563
|
|
|
533
564
|
case 'graph': {
|
|
534
565
|
index = getIndex(project_dir, ep);
|
|
535
566
|
const { ok, result, error } = execute(index, 'graph', ep);
|
|
536
|
-
if (!ok) return
|
|
567
|
+
if (!ok) return te(error);
|
|
537
568
|
return tr(output.formatGraph(result, {
|
|
538
569
|
showAll: ep.all || ep.depth !== undefined,
|
|
539
570
|
maxDepth: ep.depth ?? 2, file: ep.file,
|
|
@@ -545,7 +576,7 @@ server.registerTool(
|
|
|
545
576
|
case 'circular_deps': {
|
|
546
577
|
index = getIndex(project_dir, ep);
|
|
547
578
|
const { ok, result, error } = execute(index, 'circularDeps', ep);
|
|
548
|
-
if (!ok) return
|
|
579
|
+
if (!ok) return te(error);
|
|
549
580
|
return tr(output.formatCircularDeps(result));
|
|
550
581
|
}
|
|
551
582
|
|
|
@@ -554,21 +585,21 @@ server.registerTool(
|
|
|
554
585
|
case 'verify': {
|
|
555
586
|
index = getIndex(project_dir, ep);
|
|
556
587
|
const { ok, result, error } = execute(index, 'verify', ep);
|
|
557
|
-
if (!ok) return
|
|
588
|
+
if (!ok) return te(error);
|
|
558
589
|
return tr(output.formatVerify(result));
|
|
559
590
|
}
|
|
560
591
|
|
|
561
592
|
case 'plan': {
|
|
562
593
|
index = getIndex(project_dir, ep);
|
|
563
594
|
const { ok, result, error } = execute(index, 'plan', ep);
|
|
564
|
-
if (!ok) return
|
|
595
|
+
if (!ok) return te(error);
|
|
565
596
|
return tr(output.formatPlan(result));
|
|
566
597
|
}
|
|
567
598
|
|
|
568
599
|
case 'diff_impact': {
|
|
569
600
|
index = getIndex(project_dir, ep);
|
|
570
601
|
const { ok, result, error, note } = execute(index, 'diffImpact', ep);
|
|
571
|
-
if (!ok) return
|
|
602
|
+
if (!ok) return te(error);
|
|
572
603
|
let diText = output.formatDiffImpact(result, { all: ep.all });
|
|
573
604
|
if (note) diText += '\n\n' + note;
|
|
574
605
|
return tr(diText);
|
|
@@ -579,21 +610,21 @@ server.registerTool(
|
|
|
579
610
|
case 'typedef': {
|
|
580
611
|
index = getIndex(project_dir, ep);
|
|
581
612
|
const { ok, result, error } = execute(index, 'typedef', ep);
|
|
582
|
-
if (!ok) return
|
|
613
|
+
if (!ok) return te(error);
|
|
583
614
|
return tr(output.formatTypedef(result, ep.name));
|
|
584
615
|
}
|
|
585
616
|
|
|
586
617
|
case 'stacktrace': {
|
|
587
618
|
index = getIndex(project_dir, ep);
|
|
588
619
|
const { ok, result, error } = execute(index, 'stacktrace', ep);
|
|
589
|
-
if (!ok) return
|
|
620
|
+
if (!ok) return te(error);
|
|
590
621
|
return tr(output.formatStackTrace(result));
|
|
591
622
|
}
|
|
592
623
|
|
|
593
624
|
case 'api': {
|
|
594
625
|
index = getIndex(project_dir, ep);
|
|
595
626
|
const { ok, result, error, note } = execute(index, 'api', ep);
|
|
596
|
-
if (!ok) return
|
|
627
|
+
if (!ok) return te(error);
|
|
597
628
|
let apiText = output.formatApi(result, ep.file || '.');
|
|
598
629
|
if (note) apiText += '\n\n' + note;
|
|
599
630
|
return tr(apiText);
|
|
@@ -602,7 +633,7 @@ server.registerTool(
|
|
|
602
633
|
case 'stats': {
|
|
603
634
|
index = getIndex(project_dir, ep);
|
|
604
635
|
const { ok, result, error, note } = execute(index, 'stats', ep);
|
|
605
|
-
if (!ok) return
|
|
636
|
+
if (!ok) return te(error);
|
|
606
637
|
let statsText = output.formatStats(result, { top: ep.top || 0 });
|
|
607
638
|
if (note) statsText += '\n\n' + note;
|
|
608
639
|
return tr(statsText);
|
|
@@ -613,7 +644,7 @@ server.registerTool(
|
|
|
613
644
|
case 'fn': {
|
|
614
645
|
index = getIndex(project_dir, ep);
|
|
615
646
|
const { ok, result, error, note } = execute(index, 'fn', ep);
|
|
616
|
-
if (!ok) return
|
|
647
|
+
if (!ok) return te(error);
|
|
617
648
|
// MCP path security: validate all result files are within project root
|
|
618
649
|
for (const entry of result.entries) {
|
|
619
650
|
const check = resolveAndValidatePath(index, entry.match.relativePath || path.relative(index.root, entry.match.file));
|
|
@@ -626,7 +657,7 @@ server.registerTool(
|
|
|
626
657
|
case 'class': {
|
|
627
658
|
index = getIndex(project_dir, ep);
|
|
628
659
|
const { ok, result, error, note } = execute(index, 'class', ep);
|
|
629
|
-
if (!ok) return
|
|
660
|
+
if (!ok) return te(error); // soft error (class not found)
|
|
630
661
|
// MCP path security: validate all result files are within project root
|
|
631
662
|
for (const entry of result.entries) {
|
|
632
663
|
const check = resolveAndValidatePath(index, entry.match.relativePath || path.relative(index.root, entry.match.file));
|
|
@@ -639,7 +670,7 @@ server.registerTool(
|
|
|
639
670
|
case 'lines': {
|
|
640
671
|
index = getIndex(project_dir, ep);
|
|
641
672
|
const { ok, result, error } = execute(index, 'lines', ep);
|
|
642
|
-
if (!ok) return
|
|
673
|
+
if (!ok) return te(error);
|
|
643
674
|
// MCP path security: validate file is within project root
|
|
644
675
|
const check = resolveAndValidatePath(index, result.relativePath);
|
|
645
676
|
if (typeof check !== 'string') return check;
|
|
@@ -648,7 +679,7 @@ server.registerTool(
|
|
|
648
679
|
|
|
649
680
|
case 'expand': {
|
|
650
681
|
if (ep.item === undefined || ep.item === null) {
|
|
651
|
-
return
|
|
682
|
+
return te('Item number is required (e.g. item=1).');
|
|
652
683
|
}
|
|
653
684
|
index = getIndex(project_dir, ep);
|
|
654
685
|
const lookup = expandCacheInstance.lookup(index.root, ep.item);
|
|
@@ -657,15 +688,15 @@ server.registerTool(
|
|
|
657
688
|
itemCount: lookup.itemCount, symbolName: lookup.symbolName,
|
|
658
689
|
validateRoot: true
|
|
659
690
|
});
|
|
660
|
-
if (!ok) return
|
|
691
|
+
if (!ok) return te(error);
|
|
661
692
|
return tr(result.text);
|
|
662
693
|
}
|
|
663
694
|
|
|
664
695
|
default:
|
|
665
|
-
return
|
|
696
|
+
return te(`Unknown command: ${command}`);
|
|
666
697
|
}
|
|
667
698
|
} catch (e) {
|
|
668
|
-
return
|
|
699
|
+
return te(e.message);
|
|
669
700
|
} finally {
|
|
670
701
|
// Persist calls cache after command execution.
|
|
671
702
|
// 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.19",
|
|
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",
|