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 +17 -1
- package/README.md +1 -1
- package/cordis.patch.yml +1 -1
- package/dist/services/relationships.js +73 -14
- package/package.json +4 -2
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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": "
|
|
124
|
+
"gitHead": "da7adf2662572454fdd388b549eb538fbfc5a380"
|
|
123
125
|
}
|