ucn 4.1.3 → 4.2.1

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 CHANGED
@@ -123,6 +123,37 @@ 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
+ const CONTRACT_LINE_RE = /^\s*(?:ACCOUNT|CONTRACT|WARNING|FILTERED|CALLEE ACCOUNT|TREE ACCOUNT):/;
127
+ const MAX_PRESERVED_CONTRACT_LINES = 24;
128
+ const MAX_PRESERVED_CONTRACT_CHARS = 8000;
129
+
130
+ /**
131
+ * Keep trust/accounting metadata visible even when the human-readable body is
132
+ * truncated. A first-N slice without this footer can turn a qualified answer
133
+ * into an apparently complete one for an agent.
134
+ */
135
+ function preservedContractMetadata(fullText, visibleText) {
136
+ const visible = new Set(visibleText.split('\n').map(line => line.trim()));
137
+ const selected = [];
138
+ let selectedChars = 0;
139
+ let omitted = 0;
140
+
141
+ for (const rawLine of fullText.split('\n')) {
142
+ if (!CONTRACT_LINE_RE.test(rawLine)) continue;
143
+ const line = rawLine.trim();
144
+ if (!line || visible.has(line)) continue;
145
+ if (selected.length >= MAX_PRESERVED_CONTRACT_LINES ||
146
+ selectedChars + line.length + 1 > MAX_PRESERVED_CONTRACT_CHARS) {
147
+ omitted++;
148
+ continue;
149
+ }
150
+ selected.push(line);
151
+ selectedChars += line.length + 1;
152
+ }
153
+
154
+ return { lines: selected, omitted, complete: omitted === 0 };
155
+ }
156
+
126
157
  function toolResult(text, command, maxChars, suffixNote) {
127
158
  const suffix = suffixNote || '';
128
159
  if (!text) return { content: [{ type: 'text', text: '(no output)' + suffix }] };
@@ -135,6 +166,7 @@ function toolResult(text, command, maxChars, suffixNote) {
135
166
  // Cut at last newline to avoid breaking mid-line
136
167
  const lastNewline = truncated.lastIndexOf('\n');
137
168
  const cleanCut = lastNewline > limit * 0.8 ? truncated.substring(0, lastNewline) : truncated;
169
+ const contractMetadata = preservedContractMetadata(text, cleanCut);
138
170
  // Command-specific narrowing hints
139
171
  const hints = {
140
172
  toc: 'Use in= to scope to a subdirectory, or detailed=false for compact view.',
@@ -146,7 +178,25 @@ function toolResult(text, command, maxChars, suffixNote) {
146
178
  usages: 'Use file= to scope to specific files.',
147
179
  };
148
180
  const narrow = hints[command] || 'Use file=/in=/exclude= to narrow scope.';
149
- 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 }] };
181
+ let rendered = cleanCut + `\n\n... OUTPUT TRUNCATED: showing ${limit} of ${fullSize} chars. Full output would be ~${fullTokens} tokens. ${narrow} Use all=true to lift formatter caps; the MCP transport still has a 100K character ceiling.`;
182
+ if (contractMetadata.lines.length > 0 || contractMetadata.omitted > 0) {
183
+ rendered += '\n\nPRESERVED CONTRACT METADATA (from omitted output):';
184
+ if (contractMetadata.lines.length > 0) rendered += '\n' + contractMetadata.lines.join('\n');
185
+ if (contractMetadata.omitted > 0) {
186
+ rendered += `\nWARNING: ${contractMetadata.omitted} additional contract line(s) could not fit the preservation budget; narrow scope before acting.`;
187
+ }
188
+ }
189
+ rendered += suffix;
190
+ return {
191
+ content: [{ type: 'text', text: rendered }],
192
+ structuredContent: {
193
+ truncated: true,
194
+ fullChars: fullSize,
195
+ requestedLimit: limit,
196
+ contractMetadata: contractMetadata.lines,
197
+ contractMetadataComplete: contractMetadata.complete,
198
+ },
199
+ };
150
200
  }
151
201
  return { content: [{ type: 'text', text: text + suffix }] };
152
202
  }
@@ -184,12 +234,12 @@ function resolveAndValidatePath(index, file) {
184
234
  // CONSOLIDATED TOOL REGISTRATION
185
235
  // ============================================================================
186
236
 
187
- const TOOL_DESCRIPTION = `Code intelligence toolkit for AI agents. Extract specific functions, trace call chains, find all callers, and detect dead code without reading entire files or scanning full projects. Use instead of grep/read for code relationships. Supports JavaScript/TypeScript, Python, Go, Rust, Java, and HTML.
237
+ const VERBOSE_TOOL_DESCRIPTION = `Code intelligence toolkit for AI agents. Extract specific functions, trace call chains, find all callers, and detect dead code without reading entire files or scanning full projects. Use instead of grep/read for code relationships. Supports JavaScript/TypeScript, Python, Go, Rust, Java, and HTML.
188
238
 
189
- TOP 5 (covers 90% of tasks): about, impact, trace, find, deadcode
239
+ COMMON STARTING COMMANDS: orient, about, impact, trace, find
190
240
 
191
- QUICK GUIDE choosing the right command:
192
- New/unfamiliar repo → orient (size, top dirs, hot functions, entry points, trust run FIRST)
241
+ QUICK GUIDE: choosing the right command:
242
+ New/unfamiliar repo → orient (size, top dirs, hot functions, entry points, readiness; run FIRST)
193
243
  Understand a symbol → about (everything), context (callers/callees only), smart (code + called functions inline)
194
244
  Before modifying → impact (all call sites with args), verify (signature check), plan (preview refactor)
195
245
  Execution flow → trace (function call tree) or graph (file imports/exports)
@@ -199,39 +249,39 @@ QUICK GUIDE — choosing the right command:
199
249
  Commands:
200
250
 
201
251
  UNDERSTANDING CODE:
202
- - about <name>: Definition, source, callers, callees, and tests — everything in one call. Replaces 3-4 grep+read cycles. Your first stop for any function or class. Pass git=true for last-modified, author, and recent-changes (last 30d).
252
+ - about <name>: Definition, source, callers, callees, and tests in one call. Replaces 3-4 grep+read cycles. Your first stop for any function or class. Pass git=true for last-modified, author, and recent-changes (last 30d).
203
253
  - context <name>: Who calls it and what does it call, without source code. Results are numbered for use with expand. For classes/structs, shows all methods instead.
204
- - impact <name>: Every call site with actual arguments passed, grouped by file. Essential before changing a function signature shows exactly what breaks.
205
- - blast <name>: Transitive blast radius callers of callers. Shows the full chain of functions affected if you change something. Like impact but recursive. Use depth (default: 3) to control how far up the chain to walk.
254
+ - impact <name>: Every call site with actual arguments passed, grouped by file. Use it before changing a function signature to see the affected sites.
255
+ - blast <name>: Transitive blast radius through callers of callers. Shows the full chain of functions affected by a change. Use depth (default: 3) to control how far up the chain to walk.
206
256
  - smart <name>: Get a function's source with all called functions expanded inline (not constants/variables). Use to understand or modify a function and its dependencies in one read.
207
- - trace <name>: Call tree from a function downward. Use to understand "what happens when X runs" maps which modules a pipeline touches without reading files. Set depth (default: 3); setting depth expands all children.
257
+ - trace <name>: Call tree from a function downward. Use to understand "what happens when X runs" and which modules a pipeline touches. Set depth (default: 3); setting depth expands all children.
208
258
  - example <name>: Best real-world usage example. Automatically scores call sites by quality and returns the top one with context. Use to understand expected calling patterns. Set diverse=true to cluster call sites by argument shape and return one representative per cluster (pair with top=N, default 3).
209
- - reverse_trace <name>: Upward call chain to entry points — who calls this, who calls those callers, etc. Use to find all paths that lead to a function. Set depth (default: 5) to control how far up. Complement to trace (which goes downward).
259
+ - reverse_trace <name>: Upward call chain to entry points. Use to find paths that lead to a function. Set depth (default: 5) to control how far up. This complements trace, which goes downward.
210
260
  - related <name>: Sibling functions: same file, similar names, or shared callers/callees. Find companions to update together (e.g., serialize when you're changing deserialize). Name-based, not semantic.
211
261
  - brief <name>: Compact summary of a function: typed signature, first sentence of docstring, side-effect classification (fs/network/process/global_mutation), complexity (branches, depth, lines). Cheaper than about; more useful than fn when you don't need the body. Pass git=true for last-modified info.
212
262
 
213
263
  FINDING CODE:
214
264
  - find <name>: Locate definitions ranked by usage count. Supports glob patterns (e.g. find "handle*" or "_update*"). Use when you know the name but not the file.
215
265
  - usages <name>: See every usage organized by type: definitions, calls, imports, references. Complete picture of how something is used. Use code_only=true to skip comments/strings.
216
- - toc: Get a quick overview of a project you haven't seen before — file counts, line counts, function/class counts, entry points. Use detailed=true for full symbol listing.
266
+ - toc: Get a quick overview of an unfamiliar project: file counts, line counts, function/class counts, and entry points. Use detailed=true for full symbol listing.
217
267
  - search <term>: Text search (like grep, respects .gitignore). Supports regex by default (e.g. "\\d+" or "foo|bar"). Supports context=N for surrounding lines, exclude/in for file filtering. Case-insensitive by default; set case_sensitive=true for exact case. Invalid regex auto-falls back to plain text. STRUCTURAL MODE: Add type=function|class|call|method|type to query the symbol index instead of text. Combine with param=, returns=, decorator=, receiver= (for calls), exported=true, unused=true. Term becomes optional name filter (glob). Example: type=function, param=Request → all functions taking Request.
218
268
  - tests <name>: Find test files covering a function, test case names, and how it's called in tests. Use before modifying or to find test patterns to follow.
219
269
  - affected_tests <name>: Which tests to run after changing a function. Combines blast (transitive callers) with test detection. Shows test files, coverage %, and uncovered functions. Use depth= to control depth.
220
- - deadcode: Find dead code: functions/classes with zero callers. Use during cleanup to identify safely deletable code. Excludes exported, decorated, and test symbols by default use include_exported/include_decorated/include_tests to expand.
270
+ - deadcode: Generate unreferenced-symbol candidates for review. Never treat the result as standalone deletion proof. Exported, decorated, and test symbols are excluded by default; use include_exported/include_decorated/include_tests to expand the audit.
221
271
  - entrypoints: Detect framework entry points: routes, handlers, DI providers, tasks. Auto-detects Express, Flask, Spring, Gin, Actix, and more. Use framework= to filter by specific framework.
222
- - endpoints: HTTP API surface list server routes (Express/Fastify/Koa/NestJS/Flask/FastAPI/Spring/JAX-RS/Gin/Echo/Chi/Fiber/axum/actix/Next.js) and client requests (fetch/axios/requests/httpx/http/restTemplate/webClient/reqwest). Use bridge=true to match clients to servers across language boundaries; method=/prefix= to filter; server_only/client_only to halve output.
272
+ - endpoints: HTTP API surface with server routes and client requests. Use bridge=true to match clients to servers across language boundaries; method=/prefix= to filter; server_only/client_only to reduce output.
223
273
 
224
274
  EXTRACTING CODE (use instead of reading entire files):
225
275
  - fn <name>: Extract one or more functions. Comma-separated for bulk extraction (e.g. "parse,format,validate"). Use file to disambiguate.
226
276
  - class <name>: Extract a class/struct/interface with all its methods. Handles all supported types: JS/TS, Python, Go, Rust, Java. Large classes (>200 lines) show summary; use max_lines for truncated source.
227
277
  - lines: Extract specific lines (e.g. range="10-20" or just "15"). Requires file and range. Use when you know the exact line range you need.
228
- - expand <item>: Drill into a numbered item from the last context result (requires running context first in the same session). Context returns numbered callers/callees — use this to see their full source code.
278
+ - expand <item>: Drill into a numbered item from the last context result. Run context first in the same session. Use expand to see the selected source code.
229
279
 
230
280
  FILE DEPENDENCIES (require file param):
231
281
  - imports: All imports with resolved file paths. Use to understand dependencies before modifying or moving a file. Resolves relative, package, and language-specific patterns.
232
- - exporters: Every file that imports/depends on this file — shows dependents rather than dependencies. Use before moving, renaming, or deleting.
282
+ - exporters: Every file that imports or depends on this file. Use before moving, renaming, or deleting.
233
283
  - file_exports: File's public API: all exported functions, classes, variables with signatures. Use to understand what a module offers before importing. Requires explicit export markers; use toc --detailed as fallback.
234
- - graph: File-level dependency tree. Use to understand module architecture which files form a cluster, what the dependency chain looks like. Set direction ("imports"/"importers"/"both"). Can be noisy — use depth=1 for large codebases.
284
+ - graph: File-level dependency tree. Use it to understand module clusters and dependency chains. Set direction ("imports"/"importers"/"both"). Use depth=1 for large codebases.
235
285
  - circular_deps: Detect circular import chains. Shows cycle paths and involved files. Use file= to check a specific file, exclude= to ignore paths.
236
286
 
237
287
  REFACTORING:
@@ -241,7 +291,7 @@ REFACTORING:
241
291
  - check: Pre-commit lint of pending changes against the index. Composes diff_impact + verify + affected_tests; flags ADDED functions with zero callers (ORPHAN), BROKEN_IMPORT, signature drift across call sites, and recommends which tests to run. Use base= to compare against a branch, staged=true for staged changes only.
242
292
 
243
293
  DIAGNOSTICS:
244
- - doctor: Index health/coverage report file/symbol counts, language breakdown, dynamic-import / eval / reflection blind spots, parse failures, and a verdict (HIGH/MEDIUM/LOW trust). Use deep=true to also sample resolution coverage and bucket edges by confidence. Use in= to scope to a subtree.
294
+ - doctor: Task-specific readiness report with index health, semantic blind spots, command proof classification, and navigation/refactor/deletion levels. deep=true adds a stratified resolution-evidence profile; it is not an accuracy estimate. Use in= to scope to a subtree.
245
295
  - orient: One-screen repo orientation for a codebase you just entered: size + language mix, densest directories, most-called functions, entry-point counts, and the trust verdict. Best FIRST command in a new repo.
246
296
 
247
297
  OTHER:
@@ -252,12 +302,43 @@ OTHER:
252
302
  - audit_async: Find async calls inside async functions that are likely missing await (probable bugs). JS/TS/Python only. Filter with file/exclude/limit.
253
303
 
254
304
  READING OUTPUT (trust contract):
255
- - Caller/impact answers PARTITION every text occurrence of the symbol — nothing is silently hidden. CONFIRMED entries carry binding/receiver/import evidence (safe to act on). UNVERIFIED entries are call-syntax matches without evidence, each with a reason — treat as possible callers before a breaking change. The ACCOUNT line reconciles the arithmetic; "0 unaccounted" means the partition is complete.
256
- - A CONFIRMED(0) + UNVERIFIED(0) answer with 0 unaccounted and no WARNING is a trustworthy zero: the symbol genuinely has no callers.
257
- - WARNING lines list unparsed files containing the symbol their lines were NOT analyzed; fall back to text search there.
305
+ - Caller/impact answers partition literal-name text lines. CONFIRMED entries carry binding/receiver/import evidence; UNVERIFIED entries are possible callers without target proof. ACCOUNT reconciles that text ground set. CONTRACT states the boundary explicitly.
306
+ - A zero account is an observed-text zero, not proof of zero semantic callers: aliases, indirect calls, generated code, and runtime dispatch can exist. Never use it alone as safe-delete evidence; review usages/deadcode, warnings, and tests.
307
+ - WARNING lines list unparsed files containing the symbol. Their lines were not analyzed; fall back to text search there.
258
308
  - verify arg-checks and plan plans CONFIRMED sites only; their UNVERIFIED CALL SITES sections list candidates to review manually. check reports "N callers (+M unverified)" per changed function.
259
309
  - context/smart/trace also account the callee side (CALLEE ACCOUNT line + unverified callees with reasons).
260
- - Advisory commands (related, example, stacktrace, endpoints bridge=true) mark output "Advisory:" ranked heuristics, not verified claims. Every other listed answer is contract-backed.` + generateMcpParamSection();
310
+ - Advisory commands (related, example, stacktrace, endpoints bridge=true) mark output "Advisory:". These are ranked heuristics, not verified claims. Other semantic answers expose their evidence/account boundaries.` + generateMcpParamSection();
311
+
312
+ const CONCISE_TOOL_DESCRIPTION = `AST code intelligence for JavaScript/TypeScript, Python, Go, Rust, Java, and HTML.
313
+
314
+ Start:
315
+ - orient: repo map and task-specific readiness.
316
+ - find: definitions by name; brief: cheap signature/summary; about: compact symbol card.
317
+
318
+ Understand and change:
319
+ - context: direct callers/callees. impact: caller sites and arguments.
320
+ - trace: downward execution tree. reverse_trace/blast: upward/transitive impact.
321
+ - fn/class/lines: extract only the source needed. smart: target plus dependencies.
322
+ - verify: confirmed-site arity check. plan: refactor preview. check/diff_impact: change preflight.
323
+ - tests/affected_tests: relevant tests. usages: all AST usage kinds. deadcode: conservative candidate list.
324
+
325
+ Architecture and search:
326
+ - toc/stats/api/entrypoints: project surface. imports/exporters/file_exports/graph/circular_deps: file graph.
327
+ - search: text or structural query. endpoints: server/client HTTP surface. typedef: types.
328
+ - example/related/stacktrace/endpoints bridge=true are advisory heuristics.
329
+
330
+ Trust contract:
331
+ - CONFIRMED means binding/receiver/import evidence supports this target. UNVERIFIED means possible target; review it before a breaking change.
332
+ - ACCOUNT conserves literal-name text lines only. CONTRACT states whether that partition is complete and always warns that semantic completeness is not proven. A clean zero is observed-text zero, never standalone deletion proof.
333
+ - WARNING means parse/read blind spots. FILTERED means the displayed answer intentionally hides accounted entries.
334
+ - Numeric confidence fields are ordinal evidence weights, not probabilities or calibrated accuracy.
335
+ - MCP defaults about/context/impact to compact output; set compact=false for expressions/source. Truncated responses preserve contract metadata and expose structuredContent.truncated.
336
+ - Use doctor deep=true for task readiness plus a stratified evidence profile; the profile is not an accuracy measurement.
337
+ ` + generateMcpParamSection();
338
+
339
+ const TOOL_DESCRIPTION = process.env.UCN_MCP_VERBOSE_DESCRIPTION === '1'
340
+ ? VERBOSE_TOOL_DESCRIPTION
341
+ : CONCISE_TOOL_DESCRIPTION;
261
342
 
262
343
  server.registerTool(
263
344
  'ucn',
@@ -271,12 +352,12 @@ server.registerTool(
271
352
  exclude: z.string().optional().describe('Comma-separated patterns to exclude (e.g. "test,mock,vendor")'),
272
353
  include_tests: z.boolean().optional().describe('Include test files in results (excluded by default)'),
273
354
  exclude_tests: z.boolean().optional().describe('Exclude test files from results. Used by entrypoints (where tests are included by default).'),
274
- include_methods: z.boolean().optional().describe('Include obj.method() callee expansion in trace/blast. No effect on about/context/impact/verify method calls are always analyzed and tiered by receiver evidence'),
275
- include_uncertain: z.boolean().optional().describe('No effect on tiered commands (about/context/impact/trace/blast/reverse_trace/affected_tests/verify/smart) unverified candidates are always shown with reasons'),
276
- expand_unverified: z.boolean().optional().describe('blast/reverse_trace: follow unverified caller edges in the tree downstream nodes are marked as unverified chains (possible, not confirmed, impact)'),
277
- min_confidence: z.number().min(0).max(1).optional().describe('Minimum confidence threshold (0.0-1.0) to filter caller/callee edges'),
278
- show_confidence: z.boolean().optional().describe('Show confidence scores per edge (default: true). Set false to hide.'),
279
- hide_confidence: z.boolean().optional().describe('Hide confidence scores per edge (alias of show_confidence=false).'),
355
+ include_methods: z.boolean().optional().describe('Include obj.method() callee expansion in trace/blast. No effect on about/context/impact/verify; method calls are always analyzed and tiered by receiver evidence'),
356
+ include_uncertain: z.boolean().optional().describe('No effect on tiered commands (about/context/impact/trace/blast/reverse_trace/affected_tests/verify/smart); unverified candidates are always shown with reasons'),
357
+ expand_unverified: z.boolean().optional().describe('blast/reverse_trace: follow unverified caller edges in the tree; downstream nodes are marked as possible, not confirmed, impact chains'),
358
+ min_confidence: z.number().min(0).max(1).optional().describe('Minimum ordinal evidence weight (legacy name; not a probability) for caller/callee edges'),
359
+ show_confidence: z.boolean().optional().describe('Show resolution-evidence labels. Numeric weights are ordinal, not probabilities.'),
360
+ hide_confidence: z.boolean().optional().describe('Hide resolution-evidence labels (alias of show_confidence=false).'),
280
361
  unreachable_only: z.boolean().optional().describe('Show only callers/callees that are unreachable from any detected entry point (about, context, impact).'),
281
362
  with_types: z.boolean().optional().describe('Include related type definitions in output'),
282
363
  detailed: z.boolean().optional().describe('Show full symbol listing per file'),
@@ -306,16 +387,16 @@ server.registerTool(
306
387
  range: z.string().optional().describe('Line range to extract, e.g. "10-20" or "15" (lines command)'),
307
388
  base: z.string().optional().describe('Git ref to diff against (default: HEAD). E.g. "HEAD~3", "main", a commit SHA'),
308
389
  staged: z.boolean().optional().describe('Analyze staged changes (diff_impact command)'),
309
- deep: z.boolean().optional().describe('Run a deeper analysis (doctor: sample resolution coverage)'),
310
- compact: z.boolean().optional().describe('Compact one-line-per-item output for about/context (saves tokens)'),
390
+ deep: z.boolean().optional().describe('Run deeper analysis (doctor: sample the ordinal resolution-evidence profile, not accuracy)'),
391
+ compact: z.boolean().optional().describe('Token-efficient output for about/context/impact. Defaults true on MCP; set false when source expressions are required.'),
311
392
  case_sensitive: z.boolean().optional().describe('Case-sensitive search (default: false, case-insensitive)'),
312
393
  all: z.boolean().optional().describe('Show all results (expand truncated sections). Applies to about, toc, related, trace, and others.'),
313
394
  top_level: z.boolean().optional().describe('Show only top-level functions in toc (exclude nested/indented)'),
314
395
  class_name: z.string().optional().describe('Class name to scope method analysis (e.g. "MarketDataFetcher" for close)'),
315
- line: z.number().int().positive().optional().describe('Definition line pin resolves the symbol defined at this exact line (the middle component of a file:line:name handle). Disambiguates same-file same-name definitions.'),
396
+ line: z.number().int().positive().optional().describe('Definition line pin. Resolves the symbol defined at this exact line (the middle component of a file:line:name handle). Disambiguates same-file same-name definitions.'),
316
397
  limit: z.number().int().positive().max(1000000).optional().describe('Max results to return (default: 500). Caps find, usages, search, deadcode, api, toc --detailed. Must be a positive integer.'),
317
398
  max_files: z.number().int().positive().max(10000000).optional().describe('Max files to index (default: 10000). Use for very large codebases. Must be a positive integer.'),
318
- max_chars: z.number().int().positive().max(10000000).optional().describe('Max output chars before truncation. Targeted commands (about, context, smart, etc.): 10K default. Broad commands (toc, entrypoints, deadcode, etc.): 3K default. Max: 100K. Use all=true to bypass all caps.'),
399
+ max_chars: z.number().int().positive().max(100000).optional().describe('Max output chars before truncation. Targeted commands default to 10K; broad commands default to 3K. Maximum: 100K. all=true lifts formatter caps but keeps the 100K transport ceiling.'),
319
400
  // Structural search flags (search command)
320
401
  type: z.string().optional().describe('Symbol type filter for structural search: function, class, call, method, type. Triggers index-based search.'),
321
402
  param: z.string().optional().describe('Filter by parameter name or type (structural search). E.g. "Request", "ctx".'),
@@ -331,18 +412,17 @@ server.registerTool(
331
412
  server_only: z.boolean().optional().describe('Only list server routes (endpoints command).'),
332
413
  client_only: z.boolean().optional().describe('Only list client requests (endpoints command).'),
333
414
  unmatched: z.boolean().optional().describe('Only show unmatched routes/requests (endpoints command).'),
334
- method: z.string().optional().describe('Filter by HTTP method (e.g. "GET", "POST") endpoints command.'),
415
+ method: z.string().optional().describe('Filter by HTTP method (e.g. "GET", "POST") for endpoints.'),
335
416
  prefix: z.string().optional().describe('Filter routes/requests by path prefix (endpoints command).'),
336
417
  hide_uncertain: z.boolean().optional().describe('Hide uncertain (interpolated-path) bridges (endpoints command).')
337
418
 
338
419
  })
339
420
  },
340
421
  async (args) => {
341
- const { command, project_dir } = args;
422
+ const { command, project_dir, ...rawParams } = args;
342
423
 
343
424
  // Normalize ALL params once — execute() handlers pick what they need.
344
425
  // This eliminates per-case param selection and prevents CLI/MCP drift.
345
- const { command: _c, project_dir: _p, ...rawParams } = args;
346
426
  const ep = normalizeParams(rawParams);
347
427
 
348
428
  // Translate hide_confidence → showConfidence:false (canonical inverse).
@@ -373,7 +453,7 @@ server.registerTool(
373
453
  }
374
454
  }
375
455
 
376
- // all=true bypasses both formatter caps AND char truncation (parity with CLI --all)
456
+ // all=true lifts formatter caps and raises MCP output to its hard ceiling.
377
457
  const maxChars = ep.all ? MAX_OUTPUT_CHARS : ep.maxChars;
378
458
 
379
459
  // Build stripping note (appended inside truncation boundary on success paths)
@@ -418,7 +498,7 @@ server.registerTool(
418
498
  let aboutText = output.formatAbout(result, {
419
499
  allHint: 'Repeat with all=true to show all.',
420
500
  showConfidence: ep.showConfidence !== false,
421
- compact: ep.compact,
501
+ compact: ep.compact !== false,
422
502
  });
423
503
  if (note) aboutText += '\n\n' + mn(note);
424
504
  return tr(aboutText);
@@ -431,7 +511,7 @@ server.registerTool(
431
511
  const { text, expandable } = output.formatContext(ctx, {
432
512
  expandHint: 'Use expand command with item number to see code for any item.',
433
513
  showConfidence: ep.showConfidence !== false,
434
- compact: ep.compact,
514
+ compact: ep.compact !== false,
435
515
  });
436
516
  expandCacheInstance.save(index.root, ep.name, ep.file, expandable);
437
517
  let ctxText = text;
@@ -443,7 +523,7 @@ server.registerTool(
443
523
  index = getIndex(project_dir, ep);
444
524
  const { ok, result, error, note } = execute(index, 'impact', ep);
445
525
  if (!ok) return te(error);
446
- let impactText = output.formatImpact(result, { compact: ep.compact });
526
+ let impactText = output.formatImpact(result, { compact: ep.compact !== false });
447
527
  if (note) impactText += '\n\n' + mn(note);
448
528
  return tr(impactText);
449
529
  }
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "ucn",
3
- "version": "4.1.3",
3
+ "version": "4.2.1",
4
4
  "mcpName": "io.github.mleoca/ucn",
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.",
5
+ "description": "Code intelligence toolkit for AI agents: extract functions, trace call chains, find callers, and 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",
7
7
  "bin": {
8
8
  "ucn": "cli/index.js",
@@ -10,12 +10,19 @@
10
10
  },
11
11
  "scripts": {
12
12
  "version": "node scripts/sync-server-version.js && git add server.json",
13
- "test": "node --test test/parser-unit.test.js test/integration.test.js test/cache.test.js test/formatter.test.js test/interactive.test.js test/feature.test.js test/regression-js.test.js test/regression-py.test.js test/regression-go.test.js test/regression-java.test.js test/regression-rust.test.js test/regression-cross.test.js test/regression-mcp.test.js test/regression-parser.test.js test/regression-commands.test.js test/regression-fixes.test.js test/regression-bugfixes.test.js test/cross-language.test.js test/accuracy.test.js test/command-coverage.test.js test/perf-optimizations.test.js test/systematic-test.js test/mcp-edge-cases.js test/conservation.test.js test/parity-test.js",
13
+ "test": "node --test test/parser-unit.test.js test/integration.test.js test/cache.test.js test/formatter.test.js test/interactive.test.js test/feature.test.js test/regression-js.test.js test/regression-py.test.js test/regression-go.test.js test/regression-java.test.js test/regression-rust.test.js test/regression-cross.test.js test/regression-mcp.test.js test/regression-parser.test.js test/regression-commands.test.js test/regression-fixes.test.js test/regression-bugfixes.test.js test/cross-language.test.js test/accuracy.test.js test/command-coverage.test.js test/perf-optimizations.test.js test/performance-gate-policy.test.js test/oracle-gate-policy.test.js test/systematic-test.js test/mcp-edge-cases.js test/conservation.test.js test/parity-test.js test/trust-matrix.test.js",
14
14
  "benchmark:agent": "node test/agent-understanding-benchmark.js",
15
15
  "eval:conservation": "node eval/conservation-real.js",
16
16
  "eval:oracle": "node eval/run-oracle-eval.js",
17
17
  "eval:deadcode": "node eval/run-deadcode-eval.js",
18
- "lint": "eslint core/ cli/ mcp/ languages/"
18
+ "eval:performance": "node --expose-gc eval/run-performance-gate.js",
19
+ "lint": "eslint core/ cli/ mcp/ languages/ eval/run-*.js eval/*-gate-policy.js",
20
+ "verify": "npm run lint && npm test",
21
+ "trust:gate:semantic": "node eval/run-oracle-eval.js --repo preact-signals,httpx,cobra,clap,javapoet --min-precision 0.98 --max-unscored-ratio 0.10",
22
+ "trust:gate:deadcode": "node eval/run-deadcode-eval.js --repo preact-signals,httpx,cobra,clap,javapoet --sample 100 --arm default",
23
+ "trust:gate:performance": "node --expose-gc eval/run-performance-gate.js --repo preact-signals,httpx,cobra,clap,javapoet --queries 40",
24
+ "trust:gate:fast": "node eval/run-oracle-eval.js --repo preact-signals,httpx --min-precision 0.98 && node --expose-gc eval/run-performance-gate.js --repo preact-signals,httpx --queries 20",
25
+ "trust:gate": "npm run trust:gate:semantic && npm run trust:gate:deadcode && npm run trust:gate:performance"
19
26
  },
20
27
  "keywords": [
21
28
  "mcp",