ast-mcp-server 0.14.2 → 0.14.3

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 CHANGED
@@ -6,7 +6,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
- ## [0.14.2] — not yet published (2026-10-28)
9
+ ## [0.14.3] — not yet published (2026-10-28)
10
+
11
+ ### Security
12
+
13
+ - Updated `@hono/node-server` to the patched `2.1.5` (GHSA-rmxm-3fg6-px4f, `serveStatic` decoded the request path a second time and could bypass middleware on static paths). It reaches the server only through `@modelcontextprotocol/sdk`, and the pinned resolution moved from `2.0.12`.
14
+ - Updated `minimatch` to the patched `11.0.3` (GHSA-g3hv-75wg-m249, potential denial of service through globparts amplification). The pin sits above the `^10.x` ranges still declared by `eslint`, `@eslint/config-array`, `@typescript-eslint/typescript-estree` and `@ts-morph/common`, a documented exception: those packages only call `minimatch()` and `Minimatch#match()`, none of the API removed in `11`, and the dependency-policy test records the exception so it can be dropped once the parents admit `11`.
15
+
16
+ ### Fixed
17
+
18
+ - `ast_get_impact` with `direction: "incoming"` no longer returns an empty, incomplete result on larger projects. The compiler resolver now finds incoming references through the language service instead of walking every AST node, so its work scales with the number of references rather than the project size, and the project-wide computed-call scan runs on its own budget so it can no longer starve the reference lookup (#353).
19
+
20
+ ## [0.14.2] — published to `latest` and `next`, registry verified (2026-10-28)
10
21
 
11
22
  ### Security
12
23
 
@@ -18,6 +29,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
18
29
  - Updated development tooling: Vitest 5 (now with an explicit `vite` development dependency), `@types/node` 26, `gpt-tokenizer` 4, and patch updates to ESLint, Prettier, `typescript-eslint` and `yaml`. `process.disconnect` is now called optionally in the compiler worker entry to match the stricter Node 26 typings.
19
30
  - Updated reviewed GitHub Actions pins (`actions/setup-node` 7.1.0, `actions/upload-artifact` 7.0.2, `actions/download-artifact` 8.0.2, `github/codeql-action` 4.39.1), including the release workflow.
20
31
 
32
+ ### Release status
33
+
34
+ - npm published immutable `0.14.2` bytes with integrity `sha512-fUKylMtamBEIBdLcGz27udjRLLl1+OTRir+0vRegN8x7JzM13uIZZ5lGFNEc7/rkUiFqmEE9HrrxvTRw2cgnYg==`. Public-registry verification passed; `latest` and `next` both resolve to `0.14.2`; and npm `gitHead`, the annotated `v0.14.2` tag, and the GitHub Release target exact commit `425a967002178dde685cae1dcf0ec5fad533ff54`.
35
+ - Publish run `40753973275` succeeded on its first attempt, with no ambiguous readback; verification `40755636111` and promotion `40756782907` are green and authoritative.
36
+
21
37
  ## [0.14.1] — published to `latest` and `next`, registry verified (2026-10-26)
22
38
 
23
39
  ### Security
package/README.md CHANGED
@@ -353,7 +353,7 @@ For a local candidate, pack and install its tarball instead:
353
353
 
354
354
  ```bash
355
355
  yarn pack --out ast-mcp-server-%v.tgz
356
- dsh plugin --profile web add ./ast-mcp-server-0.14.2.tgz
356
+ dsh plugin --profile web add ./ast-mcp-server-0.14.3.tgz
357
357
  ```
358
358
 
359
359
  The first supported surface is **reads + prepare + preview**. Every apply path is
package/cordis.patch.yml CHANGED
@@ -3,7 +3,7 @@
3
3
  # Mounts the packaged `ast-mcp-server` stdio command through the official
4
4
  # `@deepseek-ai/dsh-mcp-client` bridge. Harness does not resolve MCP binaries
5
5
  # from PATH, so the entrypoint is resolved relative to the profile directory
6
- # (`baseUrl`), where `dsh plugin --profile <name> add ./ast-mcp-server-0.14.2.tgz`
6
+ # (`baseUrl`), where `dsh plugin --profile <name> add ./ast-mcp-server-0.14.3.tgz`
7
7
  # installs a locally packed package as a dependency.
8
8
  #
9
9
  # First-slice surface: reads + prepare + preview. Every apply path is denied
@@ -946,10 +946,12 @@ function addScopedIncomingHeritageRelationships(state, edges) {
946
946
  addScopedCandidates(edges, candidates);
947
947
  }
948
948
  function addScopedIncomingReferences(state, edges, target) {
949
- for (const sourceFile of scopedSourceFiles(state, edges)) {
949
+ const sourceFiles = scopedSourceFiles(state, edges);
950
+ const candidateReferences = scopedReferenceCandidates(state, edges, target, sourceFiles);
951
+ for (const sourceFile of sourceFiles) {
950
952
  consumeScopedWork(state, edges);
951
953
  const candidates = createScopedCandidateSet(edges);
952
- sourceFile.forEachDescendant((reference) => {
954
+ const visitReference = (reference) => {
953
955
  consumeScopedWork(state, edges);
954
956
  if (!isCompilerReferenceNode(reference))
955
957
  return;
@@ -967,10 +969,43 @@ function addScopedIncomingReferences(state, edges, target) {
967
969
  if (source && source.node !== target.node) {
968
970
  candidates.push(createScopedEdge(state, symbolEndpoint(source, state.projectRoot), symbolEndpoint(target, state.projectRoot), "reference"));
969
971
  }
970
- });
972
+ };
973
+ const known = candidateReferences?.get(sourceFile);
974
+ if (candidateReferences) {
975
+ for (const reference of known ?? [])
976
+ visitReference(reference);
977
+ }
978
+ else {
979
+ sourceFile.forEachDescendant(visitReference);
980
+ }
971
981
  addScopedCandidates(edges, candidates);
972
982
  }
973
983
  }
984
+ // Asking the language service for the target's references costs work
985
+ // proportional to the references found, not to the size of the project. Targets
986
+ // it cannot answer for fall back to the exhaustive walk (undefined).
987
+ function scopedReferenceCandidates(state, collector, target, sourceFiles) {
988
+ if (!Node.isReferenceFindable(target.node))
989
+ return undefined;
990
+ consumeScopedWork(state, collector);
991
+ const references = target.node.findReferencesAsNodes();
992
+ reserveScopedWork(state, collector, references.length);
993
+ const inScope = new Set(sourceFiles);
994
+ const byFile = new Map();
995
+ for (const reference of references) {
996
+ const sourceFile = reference.getSourceFile();
997
+ if (!inScope.has(sourceFile))
998
+ continue;
999
+ const bucket = byFile.get(sourceFile);
1000
+ if (bucket)
1001
+ bucket.push(reference);
1002
+ else
1003
+ byFile.set(sourceFile, [reference]);
1004
+ }
1005
+ for (const bucket of byFile.values())
1006
+ bucket.sort((left, right) => left.getStart() - right.getStart());
1007
+ return byFile;
1008
+ }
974
1009
  function addScopedIncomingImports(state, edges, target) {
975
1010
  const targetEndpoint = symbolEndpoint(target, state.projectRoot);
976
1011
  for (const sourceFile of scopedSourceFiles(state, edges)) {
@@ -1096,23 +1131,44 @@ function computedCallAuthority(projectRoot, invocation) {
1096
1131
  workItems: workItems + alternatives.size,
1097
1132
  };
1098
1133
  }
1134
+ // The project-wide scan is independent of the endpoint being resolved, so it
1135
+ // runs on its own budget: spending the endpoint's budget here left large
1136
+ // projects with no work for the reference lookup. Exhausting it keeps the
1137
+ // sites found so far and reports the result as incomplete.
1099
1138
  function scopedComputedCallSites(state, collector) {
1100
1139
  if (state.computedCallSites) {
1101
1140
  reserveScopedWork(state, collector, state.computedCallSites.length);
1102
- return state.computedCallSites;
1141
+ return { sites: state.computedCallSites, complete: state.computedCallSitesComplete === true };
1103
1142
  }
1143
+ const scanCollector = {
1144
+ ...collector,
1145
+ workBudget: {
1146
+ remaining: collector.workBudget.remaining + collector.workBudget.consumed,
1147
+ consumed: 0,
1148
+ limitReached: false,
1149
+ },
1150
+ };
1104
1151
  const sites = [];
1105
- for (const sourceFile of scopedSourceFiles(state, collector)) {
1106
- sourceFile.forEachDescendant((node) => {
1107
- consumeScopedWork(state, collector);
1108
- const expression = callLikeExpression(node);
1109
- if (expression && Node.isElementAccessExpression(unwrapInvocationExpression(expression))) {
1110
- sites.push(node);
1111
- }
1112
- });
1152
+ let complete = true;
1153
+ try {
1154
+ for (const sourceFile of scopedSourceFiles(state, scanCollector)) {
1155
+ sourceFile.forEachDescendant((node) => {
1156
+ consumeScopedWork(state, scanCollector);
1157
+ const expression = callLikeExpression(node);
1158
+ if (expression && Node.isElementAccessExpression(unwrapInvocationExpression(expression))) {
1159
+ sites.push(node);
1160
+ }
1161
+ });
1162
+ }
1163
+ }
1164
+ catch (error) {
1165
+ if (!(error instanceof ScopedRelationshipWorkLimitReached))
1166
+ throw error;
1167
+ complete = false;
1113
1168
  }
1114
1169
  state.computedCallSites = sites;
1115
- return sites;
1170
+ state.computedCallSitesComplete = complete;
1171
+ return { sites, complete };
1116
1172
  }
1117
1173
  function scopedDirectCallTarget(state, collector, invocation) {
1118
1174
  const expression = callLikeExpression(invocation);
@@ -1148,7 +1204,10 @@ function scopedReferenceFlowsToUncertainCall(state, collector, reference) {
1148
1204
  function addScopedIncomingCalls(state, collector, target) {
1149
1205
  if (!Node.isReferenceFindable(target.node))
1150
1206
  return;
1151
- for (const invocation of scopedComputedCallSites(state, collector)) {
1207
+ const computed = scopedComputedCallSites(state, collector);
1208
+ if (!computed.complete)
1209
+ collector.semanticUnfinished = true;
1210
+ for (const invocation of computed.sites) {
1152
1211
  const authority = computedCallAuthority(state.projectRoot, invocation);
1153
1212
  reserveScopedWork(state, collector, authority.workItems);
1154
1213
  if (authority.state === "unfinished" &&
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ast-mcp-server",
3
- "version": "0.14.2",
3
+ "version": "0.14.3",
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
  },
@@ -119,5 +121,5 @@
119
121
  "vitest": "^5.0.5",
120
122
  "yaml": "^2.9.0"
121
123
  },
122
- "gitHead": "425a967002178dde685cae1dcf0ec5fad533ff54"
124
+ "gitHead": "da7adf2662572454fdd388b549eb538fbfc5a380"
123
125
  }