yailPeralta/ast-mcp-server ↗★ 4
ast-mcp-server
Structural code MCP server and batch CLI for compact TypeScript/JavaScript reads and hash-bound AST edits.
安装
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:yailPeralta/ast-mcp-server说明文档
阅读完整 README ↗
ast-mcp-server
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.
Reads are bounded and structured. Writes follow an explicit prepare → review → apply protocol with immutable hashes, workspace freshness checks, diagnostic guards, and idempotent receipts.
The problem
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:
- 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.
- 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.
- 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.
What this tool does instead
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.
| Need | Structural operation | Returned scope |
|---|---|---|
| Read a bounded file | ast_get_file | Exact selected source lines, hashes, and bounded freshness |
| Explore bounded context | ast_explore | Ranked selectors plus optional source and references |
| Understand a file | ast_get_outline | Signatures without implementation bodies |
| Inspect one declaration | ast_get_symbol_source | Exact source for one function, method, class, or type |
| Find usages across the project | ast_find_references | Compiler-resolved reference locations |
| Understand symbol impact | ast_get_impact | Bounded direct/transitive compiler-backed relationships |
| Select affected tests | ast_find_test_candidates | Whole candidate proofs from incoming compiler relationships |
| Rename a symbol everywhere | ast_rename_symbol | A reviewed project-wide rename plan |
| Change one implementation | ast_replace_symbol_body | A body-only plan that preserves the declaration |
Reads can start with a bounded file slice, a compact outline, or exact source only for the declaration that needs inspection. 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.
Choosing a read tool
- Use
ast_get_filewhen the file path is known and the agent needs exact source lines. It is read-only, uses zero-basedoffsetand boundedlimit, returns one-based line records, a SHA-256 byte hash, file-levelsnapshot_state, and bounded projectfreshnessmetadata (fresh,pending,stale,rebuilding, ordegraded). - Use
ast_get_filewithsymbols_only: truewhen only selectors and body-free signatures are needed from one known file. - Use
ast_explorewhen the question spans discovery and evidence. Its default summary is bounded; usedetail: "context"for selected source anddetail: "full"for source plus compiler references. - Use
ast_get_outlinefor a compact body-free view of a known file without source lines. - Use
ast_get_symbol_sourcewhen one declaration or implementation is the required evidence. - Use
ast_get_impactwhen the exact symbol is known and bounded direct/transitive compiler relationships are needed; it is read-only evidence, not a mutation plan. - Use
ast_find_test_candidateswhen an exact symbol should map to conservative test candidates. It forces incoming compiler traversal, returns complete relationship paths, and never executes tests.
snapshot_state: "fresh" means that the returned file bytes match the synchronized compiler snapshot. The separate freshness object describes the project/session state and preserves causes such as source changes or watcher failure. Neither field means that the project has zero TypeScript diagnostics; use ast_get_diagnostics for compiler errors and warnings.
Trust, freshness and completeness
The server exposes evidence labels instead of collapsing every result into an unqualified confidence score:
| Label | Meaning | Safe use |
|---|---|---|
provenance: "compiler", confidence: "exact", resolution: "resolved", freshness.state: "fresh" | A relationship resolved by the active TypeScript compiler snapshot. This is the only combination that sets compiler_authoritative: true. | May support bounded impact evidence and compiler-backed test candidates. |
provenance: "syntax" | Syntax or AST structure without semantic symbol resolution. | Navigation and structural context only; not proof that two symbols are related. |
provenance: "heuristic" | A convention or name-based suggestion. | Discovery hints only; never mutation authority or a compiler-backed test candidate. |
| index evidence | A derived query accelerator, not compiler authority. The production default uses native SQLite when persistence is absent or enabled; every indexed selector still requires compiler validation and a compiler fallback. | Faster routing only; stale, missing or mismatched entries must fail closed or fall back to the compiler. |
Freshness is orthogonal to TypeScript diagnostics. fresh means the evidence matches the synchronized snapshot; pending, rebuilding, stale, or degraded means the response must not be presented as current compiler evidence. Read tools expose the state, causes (source_change, config_change, index_failure, watcher_failure, or compiler_rebuild), and bounded checked_at timestamp. ast_get_impact refuses non-fresh compiler relationships. ast_explore returns the state together with completeness, unresolved, budget, and truncation metadata rather than silently dropping evidence.
All reads are budgeted. Callers control pagination and, where applicable, max_bytes, reference_limit, max_depth, max_nodes, and max_edges; responses report the effective limits and whether a record, byte, depth, edge, invocation, or serialization limit truncated the result. A truncated or unresolved result is incomplete evidence, not an empty negative result. ast_find_test_candidates follows the same rule: it accepts only fresh, exact compiler-backed impact, emits direct/transitive evidence and bounded relationship IDs, and never executes tests or guesses from filenames alone. Only a complete authoritative traversal may return candidates: [] with proven_empty: true.
Why this helps
- Less context: the agent retrieves the smallest structural unit that answers the question instead of loading the complete file by default.
- Safer changes: exact symbol selection, diagnostic deltas, workspace freshness checks, and
prepare → review → applyreduce the failure modes of ad hoc text editing. - Accurate project-wide operations: references and renames use compiler resolution rather than matching identifier text with grep.
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.
The included batch benchmark records a 50% reduction in model round-trips and a 94.67% reduction in serialized context for its search-to-source scenario. The result-shaping corpus records a 68.80% reduction in aggregate model-facing TOON tokens while preserving declared selectors/reference coordinates with the same six logical calls. The separate format benchmark records 25.87% across its eligible collection corpus. The context workflow benchmark verifies evidence preservation and call bounds for full-file, primitive, and ast_explore workflows. These are reproducible local o200k_base estimates, not universal token, billing, cache, or latency claims.
Requirements
- Node.js 22.13.0 or newer
- Corepack with Yarn 4.15.0 (pinned by
packageManager) - A target project with a
tsconfig.json
Supported environment and trust boundary
Published v0.12.0 requires Node.js >=22.13.0; its immutable evidence matrix targets exact Node.js 22.13.0 and the current Node.js 24 line. Managed setup-file publication additionally requires GNU coreutils 9.7 mv supporting --update=none-fail, --exchange, --no-copy, and --no-target-directory, GNU coreutils ln -L -T, procfs descriptor paths at /proc/self/fd, and O_DIRECTORY/O_NOFOLLOW. Other Linux architectures or systems without those filesystem primitives, macOS, and Windows remain unverified.
This is a local stdio server. It runs with the invoking user's filesystem permissions, and clients may request any project_root that user can access. It does not provide HTTP authentication, sandboxing, tenant isolation, or a remote-service security boundary. Remote, untrusted, and multi-tenant operation is unsupported.
Optional supervised compiler worker
The compiler runs in process by default. Linux operators may explicitly keep the stdio parent connected while allowing an idle compiler child to exit and lazily respawn:
AST_COMPILER_WORKER_MODE=supervised ast-mcp-server
The parent waits for child readiness before replaying bounded initialization state. Requests and cancellation remain generation-affine; mutation history, live operation leases, and completion-critical apply work prevent unsafe recycling. Set AST_COMPILER_WORKER_MODE=in_process for the full rollback, or set AST_COMPILER_WORKER_IDLE_TTL_MS=0 to retain the relay while disabling idle recycling.
The scoped Linux canary passed on exact Node.js 22.13.0 and Node.js 24 with repeatable PSS reclamation, stable compiler fingerprints, unchanged SQLite reuse, bounded redacted diagnostics, and no orphan after parent death. This is one child per connection, not a shared daemon, pool, or new default. See ADR 0014.
In published v0.12.0, an absent AST_SYMBOL_INDEX_PERSISTENCE or explicit enabled selects the private SQLite symbol-index cache. disabled is the immediate memory-only rollback. canary requires an explicit absolute normalized AST_SYMBOL_INDEX_CACHE_ROOT. Invalid policy or storage fails closed to compiler-authoritative memory reads with bounded path-free status.
The default cache root is selected from AST_SYMBOL_INDEX_CACHE_ROOT, then XDG_CACHE_HOME, then HOME. Inspect or clear only derived cache artifacts through the bounded CLI:
ast-tool cache inspect
ast-tool cache clear --yes
Clear requires exact confirmation, refuses unsafe or active SQLite artifacts, and preserves unknown regular files. No automatic cache GC is enabled.
See Support policy for the complete platform, runtime, persistence, and operational contract. Report security issues through SECURITY.md.
Install
Install the published CLI globally while keeping dependency lifecycle scripts disabled:
npm install --global ast-mcp-server --ignore-scripts
ast-tool setup
--ignore-scripts prevents dependencies from running preinstall, install, or postinstall hooks. The package and its current runtime dependencies do not require those hooks.
Install from source
To build the current source instead:
git clone https://github.com/yailPeralta/ast-mcp-server.git
cd ast-mcp-server
corepack enable
yarn install --immutable
yarn build
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.
The package exposes two executables when installed:
ast-mcp-server: MCP stdio server.ast-tool: batch, skill-installation, and agent-setup CLI.
Diagnose the active installation
ast-tool doctor [--project ]
Doctor reuses CLI project discovery and existing runtime authorities without changing project,
agent, package, or skill state. It prints bounded JSON; exit 0 is healthy, 1 degraded, and 2
failed. A healthy compiler remains usable when only the derived SQLite index is degraded.
Standalone diagnosis marks registered-session-only index and queue evidence as not_run rather
than fabricating healthy state.
Upgrade an installed package
Inspect the active global installation without writing, or update it immediately:
ast-tool upgrade --check
ast-tool upgrade
Upgrade supports only a direct package proven to belong to the active npm global prefix or to Volta. It rejects linked/source and ambiguous installations, never uses sudo, guesses from PATH, or accepts --yes. npm runs its proven npm-cli.js through the active Node with lifecycle scripts disabled and a disposable cache/config copy; cleanup is verified and blocks success i