ast-mcp-server 0.3.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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,27 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [0.3.0] - 2026-08-03
8
+
9
+ Initial public release.
10
+
11
+ ### Added
12
+
13
+ - Bounded structural reads for project files, symbols, outlines, references, exact declaration source, and TypeScript diagnostics.
14
+ - Hash-bound `prepare → review → apply` operations for project-wide symbol renames and body replacements.
15
+ - Persistent CLI plans with workspace/config fingerprints, cooperative locks, exact postimages, conservative rollback, and idempotent receipts.
16
+ - Declarative batch CLI with bounded fan-out, deterministic aggregation, compact `emit`, and persisted prepare plans.
17
+ - Bundled `structural-code-editing` skill for Claude Code and Hermes.
18
+ - Interactive and non-interactive agent setup that detects clients, installs the skill, registers the MCP server, verifies its tools, and replays safely.
19
+ - Yarn 4 package workflow with dependency lifecycle scripts disabled and isolated installed-tarball smokes.
20
+
21
+ ### Security
22
+
23
+ - New TypeScript diagnostics fail a prepared mutation closed unless explicitly reviewed with `allow_new_errors`.
24
+ - Apply rejects stale workspaces, changed configs/sources, mismatched plan hashes, unsafe plan files, conflicting MCP registrations, and conflicting skill content.
25
+ - Inputs, outputs, operation stores, subprocesses, batch fan-out, plan lifetime, and filesystem access are bounded.
26
+
27
+ [0.3.0]: https://github.com/yailPeralta/ast-mcp-server/releases/tag/v0.3.0
package/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ ISC License
2
+
3
+ Copyright (c) 2026 Yail Peralta
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,382 @@
1
+ # ast-mcp-server
2
+
3
+ [![CI](https://github.com/yailPeralta/ast-mcp-server/actions/workflows/ci.yml/badge.svg)](https://github.com/yailPeralta/ast-mcp-server/actions/workflows/ci.yml)
4
+ [![npm version](https://img.shields.io/npm/v/ast-mcp-server.svg)](https://www.npmjs.com/package/ast-mcp-server)
5
+ [![Node.js 20.19+](https://img.shields.io/badge/Node.js-20.19%2B-339933?logo=node.js&logoColor=white)](https://nodejs.org/)
6
+ [![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](LICENSE)
7
+
8
+ `ast-mcp-server` gives coding agents compact, type-aware access to TypeScript and JavaScript projects. It uses the real compiler project model through `ts-morph`, so declarations, references, rename locations, and diagnostics come from the AST instead of text-search guesses.
9
+
10
+ Reads are bounded and structured. Writes follow an explicit `prepare → review → apply` protocol with immutable hashes, workspace freshness checks, diagnostic guards, and idempotent receipts.
11
+
12
+ ## The problem
13
+
14
+ Coding agents often fall back to two generic operations: read files as plain text and write text patches. That works, but it has three predictable costs:
15
+
16
+ 1. **Too much context.** The agent may load hundreds of lines when it only needs one signature or method body. That consumes model context and tokens without improving the answer.
17
+ 2. **Fragile edits.** Text patches do not inherently understand declarations, scopes, overloads, or TypeScript diagnostics. A plausible-looking edit can target the wrong construct or introduce a new compiler error.
18
+ 3. **Weak cross-file reasoning.** Text search can find matching words, but it cannot reliably distinguish two unrelated symbols with the same name. Project-wide references and renames need the compiler's understanding of the program.
19
+
20
+ ## What this tool does instead
21
+
22
+ This MCP server gives the agent structural code tools in addition to generic file reads and writes. Under the hood, `ts-morph` uses the TypeScript compiler project model, so the server can reason about declarations and references as code rather than undifferentiated text.
23
+
24
+ | Need | Structural operation | Returned scope |
25
+ | ------------------------------ | ------------------------- | ----------------------------------------------------- |
26
+ | Understand a file | `ast_get_outline` | Signatures without implementation bodies |
27
+ | Inspect one declaration | `ast_get_symbol_source` | Exact source for one function, method, class, or type |
28
+ | Find usages across the project | `ast_find_references` | Compiler-resolved reference locations |
29
+ | Rename a symbol everywhere | `ast_rename_symbol` | A reviewed project-wide rename plan |
30
+ | Change one implementation | `ast_replace_symbol_body` | A body-only plan that preserves the declaration |
31
+
32
+ Reads can start with a compact outline and fetch exact source only when needed. Mutations are prepared in memory first, compared against baseline diagnostics, and returned as immutable, hash-bound plans. Nothing is written until the caller reviews and explicitly applies the plan.
33
+
34
+ ## Why this helps
35
+
36
+ - **Less context:** the agent retrieves the smallest structural unit that answers the question instead of loading the complete file by default.
37
+ - **Safer changes:** exact symbol selection, diagnostic deltas, workspace freshness checks, and `prepare → review → apply` reduce the failure modes of ad hoc text editing.
38
+ - **Accurate project-wide operations:** references and renames use compiler resolution rather than matching identifier text with grep.
39
+
40
+ AST-aware editing is not a proof that a change is semantically correct. The safety comes from combining structural selection with diagnostics, exact previews, reviewed hashes, freshness checks, and fail-closed apply semantics.
41
+
42
+ The included batch benchmark records a 50% reduction in model round-trips and a 95.21% reduction in serialized context for its search-to-source scenario. These are reproducible scenario measurements, not universal token or latency claims.
43
+
44
+ ## Requirements
45
+
46
+ - Node.js 20.19 or newer
47
+ - Corepack with Yarn 4.15.0 (pinned by `packageManager`)
48
+ - A target project with a `tsconfig.json`
49
+
50
+ ## Install
51
+
52
+ Install the published CLI globally while keeping dependency lifecycle scripts disabled:
53
+
54
+ ```bash
55
+ npm install --global ast-mcp-server --ignore-scripts
56
+ ast-tool setup
57
+ ```
58
+
59
+ `--ignore-scripts` prevents dependencies from running `preinstall`, `install`, or `postinstall` hooks. The package and its current runtime dependencies do not require those hooks.
60
+
61
+ ### Install from source
62
+
63
+ To build the current source instead:
64
+
65
+ ```bash
66
+ git clone https://github.com/yailPeralta/ast-mcp-server.git
67
+ cd ast-mcp-server
68
+ corepack enable
69
+ yarn install --immutable
70
+ yarn build
71
+ ```
72
+
73
+ The repository pins Yarn 4 and commits `enableScripts: false` in `.yarnrc.yml`. Dependency lifecycle scripts are therefore disabled during installation; switching from npm without this setting would merely change logos while preserving the risk.
74
+
75
+ The package exposes two executables when installed:
76
+
77
+ - `ast-mcp-server`: MCP stdio server.
78
+ - `ast-tool`: batch, skill-installation, and agent-setup CLI.
79
+
80
+ ## Guided agent setup
81
+
82
+ The installed package opens the interactive wizard with:
83
+
84
+ ```bash
85
+ ast-tool setup
86
+ ```
87
+
88
+ From a source checkout, use the Yarn script; it builds first and then opens the same wizard:
89
+
90
+ ```bash
91
+ yarn setup
92
+ ```
93
+
94
+ The wizard detects Claude Code and Hermes from `PATH`, shows their executable and version, selects every detected agent by default, and lets you deselect agents before confirmation. For each selected agent it:
95
+
96
+ 1. preflights any existing `ast` MCP registration;
97
+ 2. installs the bundled `structural-code-editing` skill;
98
+ 3. registers this package's MCP server through the agent's official CLI;
99
+ 4. reconnects and verifies the expected tools.
100
+
101
+ Existing matching registrations and skill files are unchanged. Conflicting MCP registrations fail before any write; remove or rename them explicitly instead of letting a setup script guess. Conflicting skill content also fails closed unless `--force-skill` is explicit.
102
+
103
+ For automation, make the target set and confirmation explicit:
104
+
105
+ ```bash
106
+ ast-tool setup --agents all --yes
107
+ ast-tool setup --agents claude --yes
108
+ ```
109
+
110
+ From a source checkout, replace `ast-tool` with `yarn setup` in those commands.
111
+
112
+ ## Install the agent skill
113
+
114
+ The package bundles a `structural-code-editing` skill that teaches an agent when to use the AST tools, how to minimize context, and how to review mutations safely. Install it for both Claude Code and Hermes with one command:
115
+
116
+ ```bash
117
+ ast-tool install-skill all
118
+ ```
119
+
120
+ Or install one target at a time:
121
+
122
+ ```bash
123
+ ast-tool install-skill claude
124
+ ast-tool install-skill hermes
125
+ ```
126
+
127
+ The default is user scope. It writes to Claude Code's personal skill directory and to the active `HERMES_HOME`:
128
+
129
+ | Target | Destination |
130
+ | ----------- | ---------------------------------------------------------------------------------------------------------- |
131
+ | Claude Code | `$CLAUDE_CONFIG_DIR/skills/structural-code-editing/SKILL.md`, or `~/.claude/skills/...` by default |
132
+ | Hermes | `$HERMES_HOME/skills/software-development/structural-code-editing/SKILL.md`, or `~/.hermes/...` by default |
133
+
134
+ To commit the skill into one project for Claude Code, use project scope:
135
+
136
+ ```bash
137
+ ast-tool install-skill claude --scope project --project-root /absolute/project
138
+ ```
139
+
140
+ This writes `.claude/skills/structural-code-editing/SKILL.md` below that project. Project scope is intentionally rejected for Hermes because Hermes skills belong to a profile, not a source repository.
141
+
142
+ Installation is idempotent. Existing identical content is left untouched; different content fails closed unless `--force` is explicit. From an unlinked source checkout, replace `ast-tool` with `yarn node /absolute/path/to/ast-mcp-server/dist/cli.js`.
143
+
144
+ Claude Code detects changes in an existing skill directory live; restart it if the top-level skills directory did not exist when the session started. In Hermes, run `/reload-skills` or start a new session, then verify with `hermes skills list`.
145
+
146
+ `install-skill` only installs the skill; it does not configure the MCP transport. Use the guided `setup` command to do both, or complete the client-specific MCP setup below—the instructions are useful, but they have not yet learned to open a stdio socket through positive thinking.
147
+
148
+ ## Use with Claude Code
149
+
150
+ Claude Code supports local stdio MCP servers. After building this repository, register the server with an absolute entrypoint:
151
+
152
+ ```bash
153
+ AST_MCP_DIR="$(pwd)"
154
+ claude mcp add --scope user --transport stdio ast -- \
155
+ node "$AST_MCP_DIR/dist/index.js"
156
+ claude mcp get ast
157
+ ```
158
+
159
+ `claude mcp get ast` should report `Status: ✔ Connected`. The `--` separator is required: everything after it is the server command, not a Claude Code option.
160
+
161
+ The example uses `--scope user`, which makes the server available in all your projects. Use `--scope local` instead to register it only for the project from which you run the command. Avoid committing a project-scoped `.mcp.json` that contains another developer's absolute checkout path.
162
+
163
+ Start Claude Code inside any TypeScript project with a `tsconfig.json`:
164
+
165
+ ```bash
166
+ cd /absolute/path/to/your-typescript-project
167
+ claude
168
+ ```
169
+
170
+ Then ask Claude to use the `ast` tools. For example:
171
+
172
+ ```text
173
+ Use the ast MCP server to inspect this project.
174
+ First search for UserService, then fetch only the exact source of its create method.
175
+ ```
176
+
177
+ For a reviewed rename:
178
+
179
+ ```text
180
+ Use ast_rename_symbol to prepare renaming UserService.create to createUser.
181
+ Do not apply it yet. Show me the affected files, diagnostic delta, plan hash,
182
+ and the complete operation preview.
183
+ ```
184
+
185
+ After reviewing the preview:
186
+
187
+ ```text
188
+ Apply that operation with ast_apply_operation using the exact operation_id and
189
+ plan_hash returned by the prepare step.
190
+ ```
191
+
192
+ Project-scoped read and prepare tools require `project_root`. Claude should pass the current project directory or its explicit `tsconfig.json` path. Preview and apply calls instead use the prepared operation coordinates; the MCP server itself contains no repository-specific paths.
193
+
194
+ ### MCP or batch CLI?
195
+
196
+ | Workflow | Recommended interface |
197
+ | ------------------------------------------------ | ----------------------------------------- |
198
+ | Interactive exploration or one reviewed mutation | Claude Code MCP tools |
199
+ | A known multi-step read pipeline | `ast-tool run pipeline.json` through Bash |
200
+ | Prepare now and apply in a later process | `ast-tool run`, then `ast-tool apply` |
201
+
202
+ Use `/mcp` inside Claude Code to inspect server status and tools. Outside the session, use `claude mcp list`, `claude mcp get ast`, or `claude mcp remove ast -s user`.
203
+
204
+ ## Other MCP clients
205
+
206
+ Hermes Agent:
207
+
208
+ ```bash
209
+ hermes mcp add ast --command node --args /absolute/path/to/ast-mcp-server/dist/index.js
210
+ hermes mcp test ast
211
+ ```
212
+
213
+ Project-scoped tools accept `project_root`, either the project directory or an explicit `tsconfig.json` path. The server contains no repository-specific paths.
214
+
215
+ ## MCP tools
216
+
217
+ | Tool | Purpose | Mutates files |
218
+ | --------------------------- | -------------------------------------------------------------------- | ------------- |
219
+ | `ast_list_files` | Paginated, project-relative source file inventory | No |
220
+ | `ast_get_outline` | Body-free declaration signatures; detailed symbol metadata is opt-in | No |
221
+ | `ast_get_symbol_source` | Exact source for one declaration | No |
222
+ | `ast_search_symbols` | Paginated structural symbol discovery | No |
223
+ | `ast_find_references` | Compiler-resolved references with bounded context | No |
224
+ | `ast_get_diagnostics` | Project- or file-scoped TypeScript diagnostics | No |
225
+ | `ast_rename_symbol` | Prepare a project-wide rename | No |
226
+ | `ast_replace_symbol_body` | Prepare a body-only replacement while preserving the signature | No |
227
+ | `ast_get_operation_preview` | Retrieve the complete retained diff for a prepared plan | No |
228
+ | `ast_apply_operation` | Apply one reviewed, hash-bound plan | Yes |
229
+
230
+ Read results use project-relative paths, deterministic ordering, structured MCP output, and pagination where result sets can grow with the project.
231
+
232
+ ## Batch CLI
233
+
234
+ `ast-tool` lets Claude Code and other Bash-capable clients collapse a known structural pipeline into one shell call:
235
+
236
+ ```bash
237
+ ast-tool validate pipeline.json
238
+ ast-tool run pipeline.json
239
+ cat pipeline.json | ast-tool run -
240
+ ```
241
+
242
+ Example search-to-source pipeline:
243
+
244
+ ```json
245
+ {
246
+ "version": 1,
247
+ "project_root": "/absolute/project",
248
+ "steps": [
249
+ {
250
+ "id": "search",
251
+ "tool": "ast_search_symbols",
252
+ "input": { "query": "UserService", "limit": 20 }
253
+ },
254
+ {
255
+ "id": "source",
256
+ "tool": "ast_get_symbol_source",
257
+ "input": {
258
+ "file_path": { "$ref": "#/steps/search/symbols/0/file" },
259
+ "symbol_path": { "$ref": "#/steps/search/symbols/0/symbol_path" }
260
+ }
261
+ }
262
+ ],
263
+ "emit": { "$ref": "#/steps/source" }
264
+ }
265
+ ```
266
+
267
+ A `$ref` is an RFC 6901 JSON Pointer rooted at prior step results. References cannot point forward. If `emit` is omitted, only the final step result is returned; intermediate results remain inside the process.
268
+
269
+ ### Bounded foreach
270
+
271
+ ```json
272
+ {
273
+ "version": 1,
274
+ "project_root": "/absolute/project",
275
+ "limits": { "concurrency": 4 },
276
+ "steps": [
277
+ { "id": "files", "tool": "ast_list_files", "input": { "limit": 20 } },
278
+ {
279
+ "id": "outlines",
280
+ "tool": "ast_get_outline",
281
+ "foreach": { "$ref": "#/steps/files/files" },
282
+ "input": { "file_path": { "$item": "" } }
283
+ }
284
+ ]
285
+ }
286
+ ```
287
+
288
+ `$item` accepts an empty pointer for the complete item or `/field` for one field. Foreach is read-only, order-preserving, fail-fast, and concurrency-bounded.
289
+
290
+ ### Batch limits
291
+
292
+ - Input document: 1 MiB.
293
+ - Steps: 50.
294
+ - Total tool invocations: 500.
295
+ - Foreach items per step: 200.
296
+ - Read concurrency: default 4, maximum 16.
297
+ - Each retained step result and final serialized output: 10 MiB.
298
+ - Total retained intermediate context: 50 MiB.
299
+ - One project root per pipeline.
300
+ - No branches, eval, embedded JavaScript, while loops, or arbitrary transformations.
301
+
302
+ Success is one JSON value on stdout. Errors are structured JSON on stderr. Exit code 0 is success, 1 is execution/apply failure, and 2 is usage or schema failure.
303
+
304
+ ## Reviewed mutations
305
+
306
+ ### MCP process
307
+
308
+ Rename and body replacement never write directly:
309
+
310
+ 1. Call `ast_rename_symbol` or `ast_replace_symbol_body`.
311
+ 2. Review the diagnostic delta, affected files, `blocked`, and `plan_hash`.
312
+ 3. Fetch complete diffs with `ast_get_operation_preview` when needed.
313
+ 4. Call `ast_apply_operation` with both `operation_id` and `plan_hash`.
314
+
315
+ MCP plans live in a bounded in-memory store and do not survive a server restart.
316
+
317
+ ### CLI process boundary
318
+
319
+ A batch may contain at most one prepare operation. It must be the final step and cannot use foreach. `ast_apply_operation` and arbitrary preview calls are forbidden inside batch documents.
320
+
321
+ A CLI prepare writes an exact private plan and returns top-level `operation_id`, `plan_hash`, and `plan_file` even when `emit` omits them:
322
+
323
+ ```bash
324
+ ast-tool run prepare-rename.json
325
+ ast-tool apply /path/from/plan_file.astplan --plan-hash <reviewed-sha256>
326
+ ```
327
+
328
+ The default plan directory is:
329
+
330
+ ```text
331
+ ${XDG_STATE_HOME:-~/.local/state}/ast-tool/plans
332
+ ```
333
+
334
+ Set `AST_TOOL_STATE_DIR` to isolate it. Directories are mode `0700`; plans are mode `0600`, atomically replaced, size-bounded, versioned, and expire with the prepared operation. Plan files contain exact proposed source bytes and must be treated as private code.
335
+
336
+ Apply loads the exact retained postimages, requires the separately supplied reviewed hash, validates serialized byte hashes and contained paths, rechecks the complete source/config workspace, stages writes, verifies postimages, and persists an applied receipt inside the same cooperative workspace lock. A later CLI invocation can replay that receipt idempotently, including after the preparation TTL.
337
+
338
+ ### Guarantee boundary
339
+
340
+ The server does **not** claim a filesystem-wide transaction:
341
+
342
+ - Replacement is atomic per file where the local filesystem provides atomic rename.
343
+ - A multi-file apply has a short interval in which some replacements may already be visible.
344
+ - Rollback is best effort and refuses to overwrite a file changed by another writer after replacement.
345
+ - MCP and CLI apply share a fail-closed filesystem lock keyed by canonical `tsconfig.json` when they use the same state directory. It does not coordinate editors, NFS writers, or hostile external processes.
346
+ - Receipt persistence runs before that lock is released. If receipt storage fails after source replacement, apply exits non-zero and reports that verified postimages may be present; retry recovers the receipt only when the complete workspace exactly matches the reviewed post-workspace fingerprint.
347
+ - A hard process crash can leave a stale lock. Remove it only after inspecting its metadata and proving no apply is running; exact complete postimages can then recover the receipt, while partial or divergent state remains a conflict.
348
+ - Source encoding support is UTF-8, with or without BOM. Unsupported encodings are rejected.
349
+
350
+ ## Development gates
351
+
352
+ ```bash
353
+ yarn format:check
354
+ yarn lint
355
+ yarn typecheck
356
+ yarn test
357
+ yarn build
358
+ yarn test:mcp
359
+ yarn test:cli
360
+ yarn test:package
361
+ yarn npm audit --all --recursive
362
+ yarn pack --dry-run
363
+ ```
364
+
365
+ `test:mcp` exercises the built stdio server. `test:cli` runs a read pipeline and a prepare/apply/replay workflow across separate Node processes.
366
+
367
+ ## Benchmarks
368
+
369
+ ```bash
370
+ yarn benchmark /absolute/project --sample 20 --output benchmark/results/project.json
371
+ yarn benchmark:corpus benchmark/task-corpus.json --output benchmark/results/self-corpus.json
372
+ yarn benchmark:batch --iterations 5 --output benchmark/results/self-batch.json
373
+ ```
374
+
375
+ The batch benchmark compares two separate client calls with one batch invocation in fresh Node processes, recording model round-trips, actual tool invocations, wall time, maximum RSS, and serialized character counts. Character counts are not model-specific token estimates. See `benchmark/README.md` for methodology and limitations.
376
+
377
+ ## Scope
378
+
379
+ - TypeScript and JavaScript projects understood by the TypeScript compiler.
380
+ - Structural rename and callable-body replacement.
381
+ - Declarative DAG-like pipelines with prior-result references and bounded foreach.
382
+ - No arbitrary signature migration, file creation/deletion plans, cross-language refactors, or general-purpose scripting language.
@@ -0,0 +1,86 @@
1
+ # Benchmark methodology
2
+
3
+ The benchmark suite measures two separate properties. It deliberately avoids converting characters into tokens because tokenizer choice changes the answer.
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
+ ## Checked results
68
+
69
+ Results generated on 2026-08-03 with Node.js v24.16.0:
70
+
71
+ | Workload | Sample | Full source chars | Compact chars | Reduction | Fresh load | Warm session |
72
+ | --------------- | -------: | ----------------: | ------------: | --------: | ---------: | -----------: |
73
+ | This repository | 20 files | 77,982 | 12,512 | 83.96% | 543 ms | 1.86 ms |
74
+ | `x-scraper` | 20 files | 178,110 | 17,036 | 90.44% | 3,951 ms | 180.16 ms |
75
+ | Task corpus | 12 tasks | 83,289 | 28,372 | 65.94% | n/a | n/a |
76
+
77
+ 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/`.
78
+
79
+ The checked broad search-to-source batch result used five fresh-process samples:
80
+
81
+ | Mode | Model round-trips | Tool invocations | Context chars | Median wall time | Median max RSS |
82
+ | -------- | ----------------: | ---------------: | ------------: | ---------------: | -------------: |
83
+ | Separate | 2 | 2 | 11,514 | 536.19 ms | 377.54 MB |
84
+ | Batch | 1 | 2 | 552 | 535.24 ms | 378.35 MB |
85
+
86
+ That run reduced model round-trips by 50% and serialized context by 95.21%. Local execution latency decreased by 0.18% and RSS increased by 0.21%, 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.