ast-mcp-server 0.14.2 → 0.15.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.
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { PaginationInputSchema, PaginationOutputSchema } from "../services/pagination.js";
3
3
  import { findDeclarationByName, getSourceFileOrThrow, withProject } from "../services/project.js";
4
- import { collectSymbolReferences } from "../services/references.js";
4
+ import { collectSymbolReferencesWithTemplates } from "../services/references.js";
5
5
  import { createRequestContext } from "../services/request-context.js";
6
6
  import { createToolErrorContext, errorResult, formattedResult, ToolOutputFormatInputSchema, } from "./result.js";
7
7
  const TOOL_NAME = "ast_find_references";
@@ -25,13 +25,34 @@ const ContextReferenceSchema = z.object({
25
25
  is_declaration: z.boolean(),
26
26
  context: z.string(),
27
27
  });
28
- const LocationReferenceSchema = ContextReferenceSchema.omit({ context: true });
28
+ const ContextReferenceSchemaWithOrigin = ContextReferenceSchema.extend({
29
+ origin: z.enum(["compiler", "template_external", "template_inline"]).optional(),
30
+ });
31
+ const LocationReferenceSchema = ContextReferenceSchemaWithOrigin.omit({ context: true });
32
+ const TemplateCoverageSchema = z.object({
33
+ status: z.enum(["completed", "unfinished", "unsupported"]),
34
+ reason: z
35
+ .enum([
36
+ "compiler_missing",
37
+ "compiler_unsupported",
38
+ "template_unresolved",
39
+ "template_unsafe",
40
+ "template_parse_error",
41
+ "size_exceeded",
42
+ "work_exhausted",
43
+ "inherited_member",
44
+ ])
45
+ .optional(),
46
+ templates: z.number().int().min(0).optional(),
47
+ });
29
48
  const ReferenceOutputBase = {
30
49
  symbol: z.string(),
31
50
  include_declaration: z.boolean(),
32
51
  declaration_count: z.number().int().min(0),
33
52
  reference_count: z.number().int().min(0),
34
53
  affected_files: z.array(z.string()),
54
+ template_coverage: TemplateCoverageSchema.optional(),
55
+ template_reference_count: z.number().int().min(0).optional(),
35
56
  ...PaginationOutputSchema,
36
57
  };
37
58
  const ReferenceOutputSchemas = {
@@ -41,13 +62,13 @@ const ReferenceOutputSchemas = {
41
62
  }),
42
63
  context: z.object({
43
64
  ...ReferenceOutputBase,
44
- references: z.array(ContextReferenceSchema),
65
+ references: z.array(ContextReferenceSchemaWithOrigin),
45
66
  }),
46
67
  };
47
68
  export function registerFindReferences(server) {
48
69
  server.registerTool(TOOL_NAME, {
49
70
  title: "Find semantic symbol references",
50
- description: "Finds type-resolved references and returns bounded project-relative locations, including declaration impact by default.",
71
+ description: "Finds type-resolved references and returns bounded project-relative locations, including declaration impact by default. When @angular/compiler resolves, it also reports Angular template usages tagged by origin with a template_coverage verdict; incomplete coverage does not prove absence, and template errors are not diagnosed.",
51
72
  inputSchema: AstFindReferencesInputSchema,
52
73
  annotations: {
53
74
  readOnlyHint: true,
@@ -58,18 +79,31 @@ export function registerFindReferences(server) {
58
79
  }, async ({ project_root, file_path, symbol_path, include_declaration, detail, output_format, offset, limit, }, extra) => {
59
80
  const requestContext = createRequestContext(extra.signal);
60
81
  try {
61
- const structuredContent = await withProject(project_root, ({ project, projectRoot }, operationContext) => {
62
- const sourceFile = getSourceFileOrThrow(project, file_path);
82
+ const structuredContent = await withProject(project_root, async (context, operationContext) => {
83
+ const sourceFile = getSourceFileOrThrow(context.project, file_path);
63
84
  const node = findDeclarationByName(sourceFile, symbol_path);
64
- const collected = collectSymbolReferences(node, projectRoot, symbol_path, include_declaration, detail, offset, limit, operationContext);
85
+ const collected = await collectSymbolReferencesWithTemplates(node, context, {
86
+ symbolPath: symbol_path,
87
+ includeDeclaration: include_declaration,
88
+ detail,
89
+ offset,
90
+ limit,
91
+ }, operationContext);
65
92
  const { references, ...metadata } = collected;
66
93
  return {
67
94
  ...metadata,
68
95
  references: detail === "context"
69
96
  ? references
70
- : references.map(({ file, line, column, kind, is_declaration }) => {
97
+ : references.map(({ file, line, column, kind, is_declaration, origin }) => {
71
98
  operationContext.checkpoint();
72
- return { file, line, column, kind, is_declaration };
99
+ return {
100
+ file,
101
+ line,
102
+ column,
103
+ kind,
104
+ is_declaration,
105
+ ...(origin === undefined ? {} : { origin }),
106
+ };
73
107
  }),
74
108
  };
75
109
  }, requestContext);
@@ -21,7 +21,7 @@ const AstRenameSymbolInputSchema = z.object({
21
21
  export function registerRenameSymbol(server) {
22
22
  server.registerTool(TOOL_NAME, {
23
23
  title: "Prepare a project-wide structural rename",
24
- description: "Prepares an exact hash-bound rename plan without writing. Review the preview and apply its operation_id with ast_apply_operation.",
24
+ description: "Prepares an exact hash-bound rename plan without writing. Review the preview and apply its operation_id with ast_apply_operation. The plan also rewrites external Angular templates hash-bound to the workspace snapshot; diagnostics stay TypeScript-only.",
25
25
  inputSchema: AstRenameSymbolInputSchema,
26
26
  outputSchema: PreparedOperationOutputSchema,
27
27
  annotations: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ast-mcp-server",
3
- "version": "0.14.2",
3
+ "version": "0.15.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",
@@ -60,8 +60,10 @@
60
60
  "node": ">=22.13.0"
61
61
  },
62
62
  "resolutions": {
63
+ "@hono/node-server": "2.1.5",
63
64
  "fast-uri": "3.1.9",
64
65
  "hono": "4.13.13",
66
+ "minimatch": "11.0.3",
65
67
  "nanoid": "3.3.18",
66
68
  "qs": "6.16.1"
67
69
  },
@@ -107,17 +109,29 @@
107
109
  "ts-morph": "28.0.0",
108
110
  "zod": "^4.6.7"
109
111
  },
112
+ "peerDependencies": {
113
+ "@angular/compiler": ">=20.0.0 <23.0.0"
114
+ },
115
+ "peerDependenciesMeta": {
116
+ "@angular/compiler": {
117
+ "optional": true
118
+ }
119
+ },
110
120
  "devDependencies": {
111
121
  "@eslint/js": "^10.0.1",
112
122
  "@types/node": "^26.6.5",
123
+ "angular-compiler-v20": "npm:@angular/compiler@20.3.34",
124
+ "angular-compiler-v21": "npm:@angular/compiler@21.2.25",
125
+ "angular-compiler-v22": "npm:@angular/compiler@22.2.4",
113
126
  "eslint": "^10.8.0",
114
127
  "gpt-tokenizer": "4.1.0",
115
128
  "prettier": "^3.9.6",
129
+ "tar": "7.5.22",
116
130
  "typescript": "^5.6.0",
117
131
  "typescript-eslint": "^8.65.0",
118
132
  "vite": "^8.3.4",
119
133
  "vitest": "^5.0.5",
120
134
  "yaml": "^2.9.0"
121
135
  },
122
- "gitHead": "425a967002178dde685cae1dcf0ec5fad533ff54"
136
+ "gitHead": "5b2d483001d21ef29a760a9c0ae538dc719f11ea"
123
137
  }
@@ -43,6 +43,20 @@ La selección default conserva los siete kinds. `contains` aplicable es `unsuppo
43
43
 
44
44
  For each dimension, `groups.length + omitted_group_count = total_group_count` and group counts sum to `covered_diagnostic_count`. Code coverage plus omitted diagnostics equals the total; file coverage additionally includes `unfiled_diagnostic_count`. File groups expose normalized project-relative paths only and may include paths absent from the raw page.
45
45
 
46
+ ## Angular templates
47
+
48
+ In an Angular project (`@angular/core` resolvable from the tsconfig directory), `ast_find_references` also reports usages of a component member inside its external `templateUrl` template and inline `template`. Each item carries `origin` (`compiler`, `template_external` or `template_inline`), and the response adds `template_coverage` and `template_reference_count`. `reference_count` stays TypeScript-only; `total` counts both kinds.
49
+
50
+ Read `template_coverage` before inferring absence. Only `completed` proves every template of the declaring component was examined. `unfinished` (for example `compiler_missing`, `compiler_unsupported`, `template_unresolved`, `template_parse_error`, `size_exceeded`) and `unsupported` (for example an inherited member) never prove that no template usage exists. `not_applicable` means the feature is off and the response is unchanged.
51
+
52
+ `ast_rename_symbol` updates external templates in the same reviewed plan and rewrites them only through `ast_apply_operation`. It fails closed: coverage `unfinished` or `unsupported` returns `INCOMPLETE_EVIDENCE`, and an inline template usage, a new name that collides with a template local, or `$any` returns `MUTATION_BLOCKED`. Pipe names, `#ref`/`let-` targets, child inputs and outputs, host bindings and `styleUrls` are not renamed. Apply is sequential, not globally atomic; rollback restores the owned templates.
53
+
54
+ Limits to state when reporting:
55
+
56
+ - `ast_explore` is TypeScript-only: its references omit template usages. This is tracked in issue #380. Use `ast_find_references` for template evidence; rename still blocks independently.
57
+ - Diagnostics stay TypeScript-only: `ast_get_diagnostics` and the post-apply delta do not detect template errors. A template-only breakage produces no diagnostic, so run the project's Angular build or tests after a rename.
58
+ - Detection uses the user's own `@angular/compiler`, never a bundled one. It is an optional peer (`>=20.0.0 <23.0.0`) that the project already installs with Angular. If it is missing or out of range, coverage is `unfinished` and rename is blocked. Yarn PnP projects without `node_modules` resolve nothing and report `unfinished`.
59
+
46
60
  ## Contributor contract
47
61
 
48
62
  Al agregar o modificar una tool, mantener schemas, metadata, annotations, handler, errores y serialización en su módulo. Exportar un descriptor congelado y ubicarlo deliberadamente en `src/tools/catalog.ts`; el catálogo solo admite hechos estáticos de efecto, batch, compatibilidad y formatos directos.
@@ -20,6 +20,12 @@ El setup falla cerrado ante un registro `ast` conflictivo. No borrar ni reemplaz
20
20
 
21
21
  Preferir `file_path` relativo; un suffix ambiguo debe devolver candidatos. `ast_get_file` solo admite archivos del tsconfig activo y `snapshot_state: "fresh"` no implica cero diagnostics. `ast_explore` es read-only: revisar `budget`, `completeness`, `unresolved` y `truncation`; sus `call_spines` son recorridos estáticos, no stacks de runtime.
22
22
 
23
+ ### Angular templates
24
+
25
+ `ast_find_references` adds template usages (`origin: "template_external" | "template_inline"`, `template_coverage`, `template_reference_count`) only when `@angular/core` resolves; otherwise the response is unchanged. Treat `template_coverage.status` other than `completed` as not proving absence. `ast_explore` is TypeScript-only for references (tracked in issue #380), and diagnostics stay TypeScript-only, so template errors are never detected.
26
+
27
+ `ast_rename_symbol` edits external templates and returns `INCOMPLETE_EVIDENCE` (coverage `unfinished` or `unsupported`) or `MUTATION_BLOCKED` (inline usage, template-local collision, `$any`). The user's `@angular/compiler` is an optional peer, never bundled; Yarn PnP projects without `node_modules` report `unfinished`. See the Angular templates section of `SKILL.md`.
28
+
23
29
  ## Trust, freshness y ausencia
24
30
 
25
31
  Solo `provenance: "compiler"`, `confidence: "exact"`, `resolution: "resolved"` y `freshness.state: "fresh"` producen evidencia autoritativa. `syntax` describe estructura; `heuristic` nunca autoriza una mutación. El índice acelera, pero el compilador valida. Administrar su cache derivada solo con `ast-tool cache inspect` y `ast-tool cache clear --yes`, nunca borrándola manualmente. La ausencia usa el contrato específico: solo `ast_find_test_candidates` usa `completeness.proven_empty` para evidencia vacía de tests afectados; `ast_explore.call_spines` usa `empty_proven`; listados, búsquedas y referencias agotan paginación con `total`, `has_more` y `next_offset` según corresponda, nunca con `proven_empty`.
@@ -6,11 +6,11 @@
6
6
  "files": [
7
7
  {
8
8
  "path": "SKILL.md",
9
- "sha256": "33d0c6520c1ed54d42a502251a08baed86153bf9b6c275aed4525b2968f1553b"
9
+ "sha256": "94ff4ce2a97d33cad56b7c1ed8057d0ff7ca22988aebd7923b21319326307c18"
10
10
  },
11
11
  {
12
12
  "path": "references/operations.md",
13
- "sha256": "d52f01637e33e65ce523d19a810ce9488ab92228809768ec66a584120c24ad98"
13
+ "sha256": "ee5dd8138b4f4570ee256c820c69b94d650a3d454119fff80b1dc08a6297b380"
14
14
  }
15
15
  ]
16
16
  },