ast-mcp-server 0.5.1 → 0.7.0

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.
Files changed (57) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/README.md +63 -14
  3. package/SECURITY.md +31 -0
  4. package/dist/cli.js +0 -0
  5. package/dist/index.js +129 -10
  6. package/dist/server.js +25 -2
  7. package/dist/services/agent-setup.js +36 -117
  8. package/dist/services/agent-targets.js +122 -0
  9. package/dist/services/context-builder.js +262 -0
  10. package/dist/services/diagnostics.js +10 -5
  11. package/dist/services/file-fingerprints.js +104 -0
  12. package/dist/services/file-snapshot.js +103 -0
  13. package/dist/services/impact.js +334 -0
  14. package/dist/services/operations.js +343 -254
  15. package/dist/services/outline.js +94 -21
  16. package/dist/services/pagination.js +10 -4
  17. package/dist/services/project-operation-scheduler.js +475 -0
  18. package/dist/services/project-status.js +134 -52
  19. package/dist/services/project-watcher.js +230 -0
  20. package/dist/services/project.js +646 -66
  21. package/dist/services/public-errors.js +255 -0
  22. package/dist/services/references.js +54 -0
  23. package/dist/services/relationships.js +1198 -0
  24. package/dist/services/request-context.js +42 -0
  25. package/dist/services/runtime-activity.js +65 -0
  26. package/dist/services/runtime-logger.js +50 -0
  27. package/dist/services/runtime-policy.js +47 -0
  28. package/dist/services/setup-wizard.js +4 -7
  29. package/dist/services/shutdown.js +93 -0
  30. package/dist/services/symbol-index-limits.js +5 -0
  31. package/dist/services/symbol-index-order.js +13 -0
  32. package/dist/services/symbol-index-policy.js +91 -0
  33. package/dist/services/symbol-index-sqlite.js +850 -0
  34. package/dist/services/symbol-index.js +447 -0
  35. package/dist/services/symbols.js +430 -47
  36. package/dist/services/test-candidates.js +209 -0
  37. package/dist/services/workspace.js +18 -7
  38. package/dist/tools/apply_operation.js +8 -4
  39. package/dist/tools/explore.js +176 -0
  40. package/dist/tools/find_references.js +17 -55
  41. package/dist/tools/get_diagnostics.js +10 -7
  42. package/dist/tools/get_file.js +107 -0
  43. package/dist/tools/get_impact.js +134 -0
  44. package/dist/tools/get_operation_preview.js +7 -4
  45. package/dist/tools/get_outline.js +8 -6
  46. package/dist/tools/get_project_status.js +77 -8
  47. package/dist/tools/get_symbol_source.js +8 -6
  48. package/dist/tools/list_files.js +11 -6
  49. package/dist/tools/rename_symbol.js +6 -4
  50. package/dist/tools/replace_symbol_body.js +6 -4
  51. package/dist/tools/result.js +27 -17
  52. package/dist/tools/scaffold_class.js +6 -4
  53. package/dist/tools/search_symbols.js +15 -63
  54. package/docs/support.md +80 -0
  55. package/package.json +18 -6
  56. package/skills/structural-code-editing/SKILL.md +42 -8
  57. package/benchmark/README.md +0 -129
@@ -1,6 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { prepareScaffoldClass } from "../services/operations.js";
3
- import { errorResult, structuredResult } from "./result.js";
3
+ import { createRequestContext } from "../services/request-context.js";
4
+ import { createToolErrorContext, errorResult, structuredResult } from "./result.js";
4
5
  import { PreparedOperationOutputSchema, serializePreparedOperation } from "./operation-schema.js";
5
6
  const IdentifierSchema = z.string().min(1).max(200);
6
7
  const TypeTextSchema = z.string().min(1).max(10_000);
@@ -121,14 +122,15 @@ export function registerScaffoldClass(server) {
121
122
  idempotentHint: false,
122
123
  openWorldHint: false,
123
124
  },
124
- }, async (args) => {
125
+ }, async (args, extra) => {
126
+ const requestContext = createRequestContext(extra.signal);
125
127
  try {
126
128
  const prepared = await prepareScaffoldClass({
127
129
  projectRoot: args.project_root,
128
130
  filePath: args.file_path,
129
131
  spec: toSpec(args),
130
132
  allowNewErrors: args.allow_new_errors,
131
- });
133
+ }, requestContext);
132
134
  const structuredContent = {
133
135
  ...serializePreparedOperation(prepared.operation),
134
136
  file: prepared.file,
@@ -139,7 +141,7 @@ export function registerScaffoldClass(server) {
139
141
  return structuredResult(structuredContent);
140
142
  }
141
143
  catch (error) {
142
- return errorResult(error);
144
+ return errorResult(error, createToolErrorContext("ast_scaffold_class", args.project_root));
143
145
  }
144
146
  });
145
147
  }
@@ -1,10 +1,9 @@
1
- import path from "node:path";
2
1
  import { z } from "zod";
3
- import { buildFileOutline } from "../services/outline.js";
4
- import { createPaginationInputSchema, PaginationOutputSchema, paginate, } from "../services/pagination.js";
5
- import { withProject } from "../services/project.js";
6
- import { collectSymbols, symbolMatchRank } from "../services/symbols.js";
7
- import { errorResult, formattedResult, ToolOutputFormatInputSchema } from "./result.js";
2
+ import { createPaginationInputSchema, PaginationOutputSchema } from "../services/pagination.js";
3
+ import { withProject, reportSymbolIndexFailure } from "../services/project.js";
4
+ import { createRequestContext } from "../services/request-context.js";
5
+ import { searchProjectSymbolsPage, searchProjectSymbolsPageWithIndex, } from "../services/symbols.js";
6
+ import { createToolErrorContext, errorResult, formattedResult, ToolOutputFormatInputSchema, } from "./result.js";
8
7
  const SEARCH_DEFAULT_LIMIT = 20;
9
8
  const SearchDetailSchema = z.enum(["selectors", "summary", "full"]).default("summary");
10
9
  const AstSearchSymbolsInputSchema = z.object({
@@ -88,75 +87,28 @@ export function registerSearchSymbols(server) {
88
87
  idempotentHint: true,
89
88
  openWorldHint: false,
90
89
  },
91
- }, async ({ project_root, query, kinds, file_filter, detail, output_format, offset, limit }) => {
90
+ }, async ({ project_root, query, kinds, file_filter, detail, output_format, offset, limit }, extra) => {
91
+ const requestContext = createRequestContext(extra.signal);
92
92
  try {
93
- const structuredContent = await withProject(project_root, ({ project, projectRoot }) => {
93
+ const structuredContent = await withProject(project_root, async (context, operationContext) => {
94
+ const { project, projectRoot } = context;
94
95
  const startedAt = performance.now();
95
- const normalizedQuery = query.toLowerCase();
96
- const normalizedFileFilter = file_filter?.toLowerCase();
97
- const kindSet = kinds ? new Set(kinds) : undefined;
98
- const matches = [];
99
- for (const sourceFile of project.getSourceFiles()) {
100
- const file = path.relative(projectRoot, sourceFile.getFilePath());
101
- if (normalizedFileFilter && !file.toLowerCase().includes(normalizedFileFilter)) {
102
- continue;
103
- }
104
- const matchingSymbols = collectSymbols(sourceFile).filter((symbol) => {
105
- const selector = `${symbol.symbolPath}@${symbol.line}`.toLowerCase();
106
- return ((!kindSet || kindSet.has(symbol.kind)) &&
107
- (symbol.name.toLowerCase().includes(normalizedQuery) ||
108
- symbol.symbolPath.toLowerCase().includes(normalizedQuery) ||
109
- selector.includes(normalizedQuery)));
110
- });
111
- if (matchingSymbols.length === 0)
112
- continue;
113
- const signatures = new Map(buildFileOutline(sourceFile).symbols.map((symbol) => [
114
- `${symbol.symbolPath}@${symbol.startLine}`,
115
- symbol.signature,
116
- ]));
117
- for (const symbol of matchingSymbols) {
118
- matches.push({
119
- file,
120
- symbol_path: symbol.symbolPath,
121
- selector: `${symbol.symbolPath}@${symbol.line}`,
122
- name: symbol.name,
123
- kind: symbol.kind,
124
- line: symbol.line,
125
- signature: signatures.get(`${symbol.symbolPath}@${symbol.line}`) ?? symbol.node.getText(),
126
- });
127
- }
128
- }
129
- matches.sort((left, right) => {
130
- const rank = symbolMatchRank(query, {
131
- symbolPath: left.symbol_path,
132
- name: left.name,
133
- line: left.line,
134
- }) -
135
- symbolMatchRank(query, {
136
- symbolPath: right.symbol_path,
137
- name: right.name,
138
- line: right.line,
139
- });
140
- if (rank !== 0)
141
- return rank;
142
- return (left.file.localeCompare(right.file) ||
143
- left.line - right.line ||
144
- left.symbol_path.localeCompare(right.symbol_path) ||
145
- left.kind.localeCompare(right.kind));
146
- });
147
- const page = paginate(matches, offset, limit);
96
+ const page = (await searchProjectSymbolsPageWithIndex(project, projectRoot, context.status.project, context.symbolIndex, context.symbolIndexReady, { query, kinds, fileFilter: file_filter }, offset, limit, async (reason) => {
97
+ await reportSymbolIndexFailure(projectRoot, reason);
98
+ }, operationContext)) ??
99
+ searchProjectSymbolsPage(project, projectRoot, { query, kinds, fileFilter: file_filter }, offset, limit, operationContext);
148
100
  const { items, ...metadata } = page;
149
101
  return {
150
102
  symbols: projectSymbols(detail, items),
151
103
  duration_ms: performance.now() - startedAt,
152
104
  ...metadata,
153
105
  };
154
- });
106
+ }, requestContext);
155
107
  const outputSchema = SearchOutputSchemas[detail];
156
108
  return formattedResult(outputSchema, structuredContent, output_format);
157
109
  }
158
110
  catch (error) {
159
- return errorResult(error);
111
+ return errorResult(error, createToolErrorContext("ast_search_symbols", project_root));
160
112
  }
161
113
  });
162
114
  }
@@ -0,0 +1,80 @@
1
+ # Support policy
2
+
3
+ This document defines the supported platform, runtime, persistence, and operational boundary for `ast-mcp-server` v0.7.0.
4
+
5
+ ## Supported release target
6
+
7
+ | Environment | Status | Contract |
8
+ | ------------------------------------------------------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
9
+ | Linux x64 with required GNU coreutils, Node.js 22.5.0 | Supported | Full repository, MCP/CLI/package, lifecycle, mutation, and production-readiness gates. Persistence canary runs require `--experimental-sqlite`. |
10
+ | Linux x64 with required GNU coreutils, current Node.js 24 line | Supported | Full repository, MCP/CLI/package, lifecycle, mutation, and production-readiness gates. |
11
+ | Other Linux architectures or systems without required GNU coreutils | Unverified | Not supported until equivalent architecture and filesystem-publication gates pass. |
12
+ | macOS | Unverified | Not supported until equivalent filesystem, process, package, mutation, and canary gates pass. |
13
+ | Windows | Unverified | Not supported until equivalent filesystem, process, package, mutation, and canary gates pass. |
14
+ | Remote, untrusted, or multi-tenant service use | Unsupported | No network authentication, sandbox, or tenant-isolation boundary is provided. |
15
+
16
+ The package engine floor is Node.js `>=22.5.0`, but release evidence is deliberately narrower: exact Node.js 22.5.0 and the current Node.js 24 line on Linux. A newer version satisfying the engine range is not automatically a verified release target.
17
+
18
+ The checked-evidence freezer requires GNU coreutils `mv` with `--update=none-fail`, `--no-copy`, and `--no-target-directory`. Equivalent publication semantics have not been verified on other operating systems or architectures.
19
+
20
+ ## Local stdio trust boundary
21
+
22
+ `ast-mcp-server` is a local stdio process. It inherits the invoking user's filesystem authority, and an MCP client may request any `project_root` or `tsconfig.json` that user can access.
23
+
24
+ The server does not provide:
25
+
26
+ - HTTP or network authentication;
27
+ - filesystem or process sandboxing;
28
+ - privilege separation;
29
+ - tenant isolation;
30
+ - protection from a malicious process running with the same filesystem authority.
31
+
32
+ Operation locks coordinate cooperating same-user processes. They do not stop editors, network filesystems, or hostile same-user writers. Report-set freezer coordination likewise assumes cooperating same-UID processes and cannot defend checked evidence against a malicious process with the same filesystem authority. The freezer guarantees no-replace atomic visibility of the checked directory; it does not claim persistence across sudden power loss because parent-directory `fsync` durability is not established.
33
+
34
+ ## Symbol-index persistence
35
+
36
+ The supported default is memory-only indexing. When `AST_SYMBOL_INDEX_PERSISTENCE` is absent, persistence remains disabled and no persistent cache is created.
37
+
38
+ The only candidate-authorized opt-in persistence policy is `canary`; no public v0.7.0 release transition has occurred yet:
39
+
40
+ ```bash
41
+ AST_SYMBOL_INDEX_PERSISTENCE=canary \
42
+ AST_SYMBOL_INDEX_CACHE_ROOT=/absolute/isolated/cache/root \
43
+ ast-mcp-server
44
+ ```
45
+
46
+ The cache root must be explicit and absolute. Use a dedicated local directory with permissions appropriate for the invoking user. Node.js 22.5.0 additionally requires `--experimental-sqlite` for this canary path.
47
+
48
+ `AST_SYMBOL_INDEX_PERSISTENCE=enabled` is reserved and intentionally fails closed to memory-only with `policy_reason: "enabled_not_released"`. Unknown values and invalid/missing canary cache roots also fail closed. Canary evidence authorizes the opt-in capability; it does not authorize global or default activation.
49
+
50
+ ## Production-readiness acceptance
51
+
52
+ The checked Linux x64 matrix covers `ast-mcp-server` and `x-scraper` under Node.js 22.5.0 and Node.js 24. MCP-PROD-404 is normative; its complete release gate requires all four reports to pass the following contract:
53
+
54
+ - zero semantic mismatches across disabled, cold, warm, restart, and rollback reads;
55
+ - byte-identical real-repository status and worktree trees;
56
+ - compiler-equivalent corruption and write-failure fallback with explicit recovery;
57
+ - exact mutation postimages, replay, byte-exact rollback, and no cache side effect in disposable fixtures;
58
+ - bounded queue/session behavior and cancellation;
59
+ - one process running identical deterministic-fixture reads under Node.js launched with `--expose-gc`;
60
+ - exactly 10 warmups, 50 measured reads, and one explicit `global.gc()` immediately before every measured RSS sample;
61
+ - deterministic-fixture final-five median RSS no more than `max(32 MiB, 20%)` above the first-five median;
62
+ - immutable-cache growth from the first complete build to the third unchanged restart no more than `max(1 MiB, 5%)`;
63
+ - recursive cache accounting through `lstat` without following symlinks, including SQLite main, WAL, SHM, quarantine, and temporary files in a stable sorted relative-file manifest; any unreadable, symbolic, or non-regular entry fails the gate.
64
+
65
+ Any semantic mismatch, mutation-boundary effect, unrecovered fallback, repository mutation, path/secret disclosure, unexpected or accepted invalid cache entry, or failed runtime/identity check fails the gate. Deliberately injected corruption is valid only when it is rejected, falls back to the exact compiler baseline, and then recovers explicitly.
66
+
67
+ Real-repository latency and RSS are retained as observations only. They are not capacity guarantees, SLAs, or PASS/FAIL criteria for this release.
68
+
69
+ ## Compatibility and issue handling
70
+
71
+ Before reporting a compatibility problem, capture:
72
+
73
+ - operating system and architecture;
74
+ - exact `node --version` output and Node binary path;
75
+ - package version;
76
+ - MCP client and transport configuration;
77
+ - whether persistence is disabled or running in explicit canary mode;
78
+ - the bounded public error code and correlation identifier, if present.
79
+
80
+ Do not attach source code, absolute project paths, cache contents, credentials, tokens, or private configuration unless they have been intentionally sanitized. Report suspected vulnerabilities through [SECURITY.md](../SECURITY.md), not a public issue.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ast-mcp-server",
3
- "version": "0.5.1",
3
+ "version": "0.7.0",
4
4
  "description": "Structural code MCP server and batch CLI for compact TypeScript/JavaScript reads and hash-bound AST edits.",
5
5
  "keywords": [
6
6
  "mcp",
@@ -35,13 +35,16 @@
35
35
  "dist",
36
36
  "skills",
37
37
  "README.md",
38
- "CHANGELOG.md"
38
+ "CHANGELOG.md",
39
+ "SECURITY.md",
40
+ "docs/support.md"
39
41
  ],
40
42
  "engines": {
41
- "node": ">=20.19"
43
+ "node": ">=22.5.0"
42
44
  },
43
45
  "resolutions": {
44
- "hono": "4.12.34"
46
+ "hono": "4.12.34",
47
+ "nanoid": "3.3.17"
45
48
  },
46
49
  "scripts": {
47
50
  "build": "tsc",
@@ -49,13 +52,21 @@
49
52
  "test": "vitest run",
50
53
  "test:watch": "vitest",
51
54
  "test:mcp": "yarn build && node scripts/mcp-smoke.mjs",
55
+ "test:lifecycle": "yarn build && node scripts/mcp-lifecycle-smoke.mjs",
52
56
  "test:cli": "yarn build && node scripts/cli-smoke.mjs",
57
+ "test:errors": "yarn build && node scripts/public-error-smoke.mjs",
53
58
  "test:package": "node scripts/package-smoke.mjs",
59
+ "test:release-candidate": "node scripts/release-candidate-matrix.mjs",
54
60
  "benchmark": "yarn build && node scripts/benchmark.mjs",
55
61
  "benchmark:corpus": "yarn build && node scripts/benchmark-corpus.mjs",
56
62
  "benchmark:batch": "yarn build && node scripts/benchmark-batch.mjs",
57
63
  "benchmark:formats": "yarn build && node scripts/benchmark-formats.mjs",
58
64
  "benchmark:shapes": "yarn build && node scripts/benchmark-result-shapes.mjs",
65
+ "benchmark:agent-workflows": "yarn build && node scripts/benchmark-agent-workflows.mjs",
66
+ "benchmark:index-storage": "yarn build && node scripts/benchmark-index-storage.mjs",
67
+ "benchmark:index-integration": "yarn build && node scripts/benchmark-symbol-index-integration.mjs",
68
+ "benchmark:production-readiness": "yarn build && node scripts/canary-local-mcp.mjs run",
69
+ "benchmark:freeze-production-readiness": "node scripts/canary-local-mcp.mjs freeze-report-set",
59
70
  "lint": "eslint src test scripts vitest.config.ts eslint.config.js",
60
71
  "format": "prettier --write .",
61
72
  "format:check": "prettier --check .",
@@ -81,5 +92,6 @@
81
92
  "typescript": "^5.6.0",
82
93
  "typescript-eslint": "^8.65.0",
83
94
  "vitest": "^4.1.10"
84
- }
85
- }
95
+ },
96
+ "gitHead": "5fc09f4a874b8e6eca20433ba2d1a719e96aa1de"
97
+ }
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: structural-code-editing
3
3
  description: Leer, navegar y editar proyectos TypeScript/JavaScript mediante el resolver real del compilador y operaciones AST preparadas, revisadas y vinculadas por hash.
4
- version: "4.0.0"
4
+ version: "4.1.0"
5
5
  author: "yail"
6
6
  license: "ISC"
7
7
  metadata:
@@ -17,6 +17,7 @@ metadata:
17
17
  Usar las tools del servidor MCP `ast` en proyectos TypeScript/JavaScript con `tsconfig.json` cuando haya que:
18
18
 
19
19
  - orientarse en archivos o módulos grandes/desconocidos;
20
+ - leer rangos acotados del source exacto de un archivo conocido;
20
21
  - encontrar declaraciones o referencias reales;
21
22
  - medir el radio de impacto de un rename;
22
23
  - renombrar un símbolo en varios archivos;
@@ -37,26 +38,59 @@ La presencia de este skill no demuestra que el transporte MCP esté configurado.
37
38
 
38
39
  El setup falla cerrado ante un registro `ast` conflictivo. No eliminar ni reemplazar una configuración existente sin revisión explícita.
39
40
 
41
+ ## Runtime, soporte y errores
42
+
43
+ El paquete requiere Node.js `>=22.5.0`. El target verificado para v0.7.0 es Linux x64 con Node.js 22.5.0 o la línea actual de Node.js 24 y el `mv` de GNU coreutils requerido por la publicación atómica de evidencia. Otras arquitecturas Linux, sistemas sin ese primitivo, macOS y Windows están sin verificar.
44
+
45
+ El transporte soportado es stdio local bajo los permisos filesystem del usuario que invoca el proceso. No asumir autenticación HTTP, sandbox, aislamiento entre tenants ni una frontera segura para clientes remotos o no confiables.
46
+
47
+ La persistencia del índice está deshabilitada por default. El modo `canary` requiere `AST_SYMBOL_INDEX_PERSISTENCE=canary` y un `AST_SYMBOL_INDEX_CACHE_ROOT` absoluto y aislado; `enabled` sigue sin publicarse y falla cerrado a memoria con `enabled_not_released`.
48
+
49
+ Los errores públicos usan un vocabulario cerrado dentro de `{ "error": { "code", "message", "correlation_id" } }`, con `isError: true`. Conservar el `correlation_id` al reportar un fallo; no pedir ni exponer stacks, paths absolutos, source, cache, argumentos crudos, environment ni credenciales para diagnosticarlo.
50
+
40
51
  ## Flujo compacto de lectura
41
52
 
42
53
  1. `ast_list_files` para descubrir archivos. Paginar; no pedir todo un monorepo si alcanza con un filtro.
43
- 2. `ast_search_symbols` si todavía no se conoce el archivo o el selector exacto.
44
- 3. `ast_get_outline` para ver contratos sin cuerpos. No pedir `include_symbols` salvo que haga falta la metadata detallada.
45
- 4. `ast_get_symbol_source` solo para las declaraciones cuya implementación haya que inspeccionar.
46
- 5. `ast_find_references` antes de renames o cambios con impacto cross-file.
47
- 6. `ast_get_diagnostics` para establecer y verificar el estado del proyecto.
54
+ 2. `ast_get_project_status` para comprobar compiler, freshness, watcher, index y operation queue antes de confiar en una lectura sensible.
55
+ 3. `ast_explore` para una pregunta de lectura que combine descubrimiento y evidencia. Usar `summary` por defecto, `context` para source seleccionado y `full` para source más referencias.
56
+ 4. `ast_get_file` si ya se conoce el archivo y hace falta source exacto. Usar `offset`/`limit`; no cargar el archivo completo por defecto.
57
+ 5. `ast_search_symbols` si todavía no se conoce el archivo o el selector exacto.
58
+ 6. `ast_get_outline` para ver contratos sin cuerpos. No pedir `include_symbols` salvo que haga falta la metadata detallada.
59
+ 7. `ast_get_symbol_source` solo para las declaraciones cuya implementación haya que inspeccionar.
60
+ 8. `ast_find_references` antes de renames o cambios con impacto cross-file.
61
+ 9. `ast_get_impact` para explorar impacto directo/transitivo con relaciones compiler-backed y límites explícitos.
62
+ 10. `ast_get_diagnostics` para establecer y verificar el estado del proyecto.
48
63
 
49
64
  `ast_search_symbols` devuelve por default hasta 20 records `summary` rankeados. Su campo `selector` es el valor que se pasa como `symbol_path` a la siguiente tool; pedir `detail: "selectors"` para routing puro o `detail: "full", limit: 100` para el perfil v0.4.0. `ast_find_references` devuelve `detail: "locations"` por default; expandir a `detail: "context"` únicamente cuando la línea fuente aporte evidencia necesaria.
50
65
 
51
66
  `file_path` debe ser preferentemente relativo al proyecto. Si un suffix coincide con varios archivos, la tool falla y devuelve candidatos en vez de elegir uno silenciosamente.
52
67
 
68
+ `ast_get_file` es read-only y solo acepta archivos incluidos por el tsconfig activo. Su modo normal devuelve líneas exactas, paginadas y numeradas desde 1, junto con el hash SHA-256 de los bytes actuales. `symbols_only: true` devuelve selectors y signatures sin cuerpos. `snapshot_state: "fresh"` describe sincronización con el snapshot del compilador, no ausencia de diagnostics; consultar `ast_get_diagnostics` por separado.
69
+
70
+ `ast_explore` es read-only y no prepara ni aplica operaciones. Sus selectors son directamente reutilizables por las tools primitivas y de mutación. La respuesta declara `freshness`, `completeness`, `truncation`, `unresolved` y `budget`; si el límite de bytes impide incluir todo el contexto, el resultado queda marcado como incompleto.
71
+
72
+ ## Modelo de confianza y freshness
73
+
74
+ No tratar toda salida como evidencia equivalente. Cuando una relación expone metadata de trust:
75
+
76
+ - `provenance: "compiler"` + `confidence: "exact"` + `resolution: "resolved"` + `freshness.state: "fresh"` es la única combinación que puede llevar `compiler_authoritative: true`.
77
+ - `provenance: "syntax"` describe estructura AST sin resolución semántica; sirve para navegación, no para afirmar que dos símbolos están conectados.
78
+ - `provenance: "heuristic"` describe una sugerencia por convención o nombre; nunca autoriza una mutación ni un candidato de test compiler-backed.
79
+ - El índice es una aceleración derivada y no una autoridad. Validar cualquier selector contra el compilador; si el índice está stale, falta o no coincide, usar el fallback compiler-backed o fallar cerrado.
80
+
81
+ `fresh` significa que la evidencia coincide con el snapshot sincronizado. `pending`, `rebuilding`, `stale` y `degraded` no deben presentarse como evidencia actual. Las respuestas preservan `causes` (`source_change`, `config_change`, `index_failure`, `watcher_failure`, `compiler_rebuild`) y `checked_at`. `ast_get_impact` rechaza relaciones que no estén fresh; `ast_explore` conserva `completeness`, `unresolved`, `budget` y `truncation` para que una lectura parcial no parezca un negativo.
82
+
83
+ El resolver interno de candidatos de tests solo acepta impacto fresh y exacto, devuelve evidencia de relación directa o transitiva con IDs bounded y no ejecuta tests. Si la relación es stale, está truncada, es ambigua o solo heurística, no genera candidatos.
84
+
85
+ Todos los reads son bounded. Respetar `limit`, `reference_limit`, `max_bytes`, y en impacto `max_depth`, `max_nodes` y `max_edges`; revisar siempre `budget` y `truncation` antes de razonar sobre ausencia. Los límites de batch son independientes y también deben mantenerse explícitos.
86
+
53
87
  Los clientes pueden prefijar los nombres publicados (`ast_*`) según su convención MCP; elegir por el nombre base y el schema, no adivinar el prefijo.
54
88
 
55
89
  ## Formato de resultado para el modelo
56
90
 
57
- `ast_search_symbols`, `ast_find_references` y `ast_get_diagnostics` aceptan `output_format: "toon"`. Usarlo cuando la respuesta vaya directo al modelo y se espere una colección uniforme con varios registros. Omitirlo, o usar `json`, para automatización que dependa del objeto canónico.
91
+ `ast_search_symbols`, `ast_find_references`, `ast_get_impact` y `ast_get_diagnostics` aceptan `output_format: "toon"`. Usarlo cuando la respuesta vaya directo al modelo y se espere una colección uniforme con varios registros. Omitirlo, o usar `json`, para automatización que dependa del objeto canónico.
58
92
 
59
- En MCP, TOON llega como un único envelope estructurado `{ "format": "toon", "data": "..." }`; `data` contiene el documento TOON lossless. No hay una copia JSON completa en paralelo. Las tres tools validan primero el valor canónico con Zod, pero no publican un `outputSchema` MCP único porque tienen dos representaciones de éxito.
93
+ En MCP, TOON llega como un único envelope estructurado `{ "format": "toon", "data": "..." }`; `data` contiene el documento TOON lossless. No hay una copia JSON completa en paralelo. Las cuatro tools validan primero el valor canónico con Zod, pero no publican un `outputSchema` MCP único porque tienen dos representaciones de éxito.
60
94
 
61
95
  No pedir TOON para `ast_list_files`, outlines, source, previews ni mutaciones: los benchmarks muestran que el envelope empeora esas formas pequeñas, multiline o diff-heavy. Para un pipeline read-only conocido, se puede compactar únicamente el resultado final con:
62
96
 
@@ -1,129 +0,0 @@
1
- # Benchmark methodology
2
-
3
- The benchmark suite measures structural payload reduction, batch orchestration, and model-facing result formats. The first two benchmarks deliberately avoid converting characters into tokens. The format benchmark pins and names one tokenizer so its estimates are reproducible rather than universal.
4
-
5
- ## Project payload and latency
6
-
7
- `scripts/benchmark.mjs`:
8
-
9
- 1. Loads a real TypeScript project from disk.
10
- 2. Selects the lexicographically first non-declaration source files after an optional filter.
11
- 3. Serializes the default compact outline payload (`file` plus `outline`).
12
- 4. Also records the larger opt-in payload containing detailed symbol metadata.
13
- 5. Reports fresh project load, cold cached-session, warm cached-session, and per-file outline latency.
14
-
15
- Run:
16
-
17
- ```bash
18
- yarn benchmark /absolute/project/path --sample 20 --output benchmark/results/project.json
19
- ```
20
-
21
- The selection rule is deterministic but not statistically representative of every repository. Use `--filter` and multiple samples when comparing particular modules.
22
-
23
- ## Compact-first task corpus
24
-
25
- `task-corpus.json` defines twelve questions over this repository. Each task declares:
26
-
27
- - files a full-file workflow would load;
28
- - declarations a compact-first workflow would request after inspecting outlines;
29
- - evidence strings necessary to answer that task.
30
-
31
- `scripts/benchmark-corpus.mjs` compares:
32
-
33
- - baseline: complete text of every declared file;
34
- - compact: default outline payloads plus only the selected declaration sources.
35
-
36
- It fails unless every predefined evidence string is present in the compact payload. This makes the check reproducible, but it does not prove that every possible reviewer would reach an equivalent conclusion.
37
-
38
- Run:
39
-
40
- ```bash
41
- yarn benchmark:corpus benchmark/task-corpus.json --output benchmark/results/self-corpus.json
42
- ```
43
-
44
- ## Interpreting results
45
-
46
- - Character reduction measures serialized payload size, not billed model tokens.
47
- - Negative reduction on a tiny file is valid: protocol structure can cost more than reading that file directly.
48
- - Project-load latency and warm-session latency should be considered separately.
49
- - Detailed symbol metadata is opt-in because it repeats signature text and can dominate small files.
50
- - Benchmark scripts record absolute local paths and timestamps. Checked reports replace local paths with descriptive placeholders; regenerate them when comparing another machine or revision.
51
-
52
- ## Batch orchestration
53
-
54
- `scripts/benchmark-batch.mjs` compares a two-operation search-to-source workflow in fresh Node processes:
55
-
56
- - separate: the client receives the broad symbol-search payload, selects the first match, then requests its source;
57
- - batch: `ast-tool` performs the same two MCP tool invocations in process and emits only the final source result.
58
-
59
- Run:
60
-
61
- ```bash
62
- yarn benchmark:batch --iterations 5 --output benchmark/results/self-batch.json
63
- ```
64
-
65
- The report records logical model round-trips, actual tool invocations, median wall time, process maximum RSS, final-result characters, and total characters exposed to the model. It does not include model inference latency, so the practical wall-time benefit of removing a model round-trip will be larger than this local process-only comparison. Maximum RSS includes the Node runtime and TypeScript project in each fresh worker.
66
-
67
- ## Model-facing JSON and TOON
68
-
69
- `scripts/benchmark-formats.mjs` compares compact JSON with TOON for actual MCP logical results. It uses this repository for broad symbol search and deterministic temporary TypeScript fixtures for repeated references and non-empty diagnostics. File lists, outlines, exact source, and a prepared rename are retained as negative controls.
70
-
71
- For each payload it records:
72
-
73
- - exact JSON→TOON→value round-trip equality;
74
- - UTF-8 bytes;
75
- - `gpt-tokenizer@3.4.0` `o200k_base` token estimates;
76
- - plain TOON for CLI output and the real MCP `{format,data}` envelope;
77
- - median encode/decode time over 50 local iterations;
78
- - serialized `tools/list` metadata against the checked v0.3.0 baseline.
79
-
80
- Run:
81
-
82
- ```bash
83
- yarn benchmark:formats
84
- ```
85
-
86
- The script fails unless every round trip is exact, the broad-search MCP envelope reduces estimated tokens by at least 20%, and the aggregate eligible corpus reduces them by at least 15%. Tokenizer estimates are not provider usage records and do not establish billed-token or prompt-cache savings.
87
-
88
- ## Checked results
89
-
90
- Results generated on 2026-08-03 with Node.js v24.16.0:
91
-
92
- | Workload | Sample | Full source chars | Compact chars | Reduction | Fresh load | Warm session |
93
- | --------------- | -------: | ----------------: | ------------: | --------: | ---------: | -----------: |
94
- | This repository | 20 files | 77,982 | 12,512 | 83.96% | 543 ms | 1.86 ms |
95
- | `x-scraper` | 20 files | 178,110 | 17,036 | 90.44% | 3,951 ms | 180.16 ms |
96
- | Task corpus | 12 tasks | 83,289 | 28,372 | 65.94% | n/a | n/a |
97
-
98
- The task corpus passed all 12 predefined evidence checks. Its compact count includes the selected declaration bodies, not just outlines. Exact raw reports are under `benchmark/results/`.
99
-
100
- The checked broad search-to-source batch result used five fresh-process samples:
101
-
102
- | Mode | Model round-trips | Tool invocations | Context chars | Median wall time | Median max RSS |
103
- | -------- | ----------------: | ---------------: | ------------: | ---------------: | -------------: |
104
- | Separate | 2 | 2 | 7,818 | 587.03 ms | 380.68 MB |
105
- | Batch | 1 | 2 | 417 | 607.95 ms | 379.15 MB |
106
-
107
- That run reduced model round-trips by 50% and serialized context by 94.67%. Local execution latency increased by 3.56% and RSS decreased by 0.40%, both small enough to treat as noise rather than a performance claim. The batch value is orchestration/context reduction; it does not make TypeScript analysis free.
108
-
109
- The checked model-facing format corpus used the complete MCP envelope for TOON:
110
-
111
- | Payload | Records | JSON tokens | MCP TOON tokens | Reduction |
112
- | ------------------- | ------: | ----------: | --------------: | --------: |
113
- | Broad symbol search | 100 | 5,382 | 4,061 | 24.54% |
114
- | References | 71 | 3,094 | 2,159 | 30.22% |
115
- | Diagnostics | 30 | 1,309 | 1,034 | 21.01% |
116
- | Eligible aggregate | 201 | 9,785 | 7,254 | 25.87% |
117
-
118
- All seven positive and negative payloads round-tripped exactly. File list, outline, source, and prepare envelopes were 5.13% to 13.56% worse in estimated tokens, which is why they remain JSON-only. The complete eleven-tool metadata is 3,372 serialized characters larger than the retained v0.3.0 baseline; this static protocol cost is reported separately from dynamic result savings.
119
-
120
- The checked result-shaping corpus compares the v0.4.0-compatible `full/100/context` profiles with the new public defaults across exact-name, exact-path, prefix, broad-substring, and multi-file-reference workflows:
121
-
122
- | Profile | Logical calls | MCP TOON tokens |
123
- | --------- | ------------: | --------------: |
124
- | Baseline | 6 | 3,910 |
125
- | Candidate | 6 | 1,220 |
126
-
127
- Every declared selector and reference coordinate remained present, and the aggregate reduction was 68.80%, above the checked 35% gate. `duration_ms` is omitted from both measured representations to make token counts deterministic; real JSON/TOON outputs are still decode-compared before measurement. These are local `o200k_base` estimates, not provider billing, cache, or latency evidence.
128
-
129
- The complete v0.5.0 `tools/list` metadata is 22,473 serialized characters versus the retained v0.4.0 value of 16,650 (`+5,823`). Removing only `ast_scaffold_class` from the current list reduces it by 5,411 characters and 1,295 local `o200k_base` tokens. No v0.4.0 metadata token count was retained, so the report intentionally leaves the historical token delta unset.