@adhisang/minecraft-modding-mcp 6.2.0 → 7.0.0-rc.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.
Files changed (98) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/README.md +21 -5
  3. package/dist/cache-policy.d.ts +71 -0
  4. package/dist/cache-policy.js +83 -0
  5. package/dist/cache-registry.js +6 -6
  6. package/dist/cli.js +74 -3
  7. package/dist/compat-stdio-transport.d.ts +1 -1
  8. package/dist/compat-stdio-transport.js +13 -1
  9. package/dist/config.d.ts +3 -0
  10. package/dist/config.js +8 -2
  11. package/dist/decompiler/vineflower.d.ts +1 -0
  12. package/dist/decompiler/vineflower.js +8 -5
  13. package/dist/entry-tools/analyze-mod-service.d.ts +70 -136
  14. package/dist/entry-tools/analyze-symbol-service.d.ts +112 -150
  15. package/dist/entry-tools/compare-minecraft-service.d.ts +59 -145
  16. package/dist/entry-tools/entry-tool-schema.d.ts +38 -4
  17. package/dist/entry-tools/entry-tool-schema.js +4 -1
  18. package/dist/entry-tools/inspect-minecraft/internal.d.ts +235 -799
  19. package/dist/entry-tools/inspect-minecraft/internal.js +65 -28
  20. package/dist/entry-tools/inspect-minecraft-service.d.ts +372 -1736
  21. package/dist/entry-tools/inspect-minecraft-service.js +1 -1
  22. package/dist/entry-tools/manage-cache-service.d.ts +81 -91
  23. package/dist/entry-tools/validate-project/cases/project-summary.d.ts +7 -7
  24. package/dist/entry-tools/validate-project-service.d.ts +164 -592
  25. package/dist/entry-tools/verify-mixin-target-service.d.ts +3 -19
  26. package/dist/era-classifier.d.ts +161 -0
  27. package/dist/era-classifier.js +292 -0
  28. package/dist/error-mapping.js +9 -2
  29. package/dist/index.d.ts +42 -4
  30. package/dist/index.js +637 -475
  31. package/dist/java-process.d.ts +2 -0
  32. package/dist/java-process.js +22 -2
  33. package/dist/json-rpc-framing.d.ts +77 -1
  34. package/dist/json-rpc-framing.js +249 -13
  35. package/dist/mapping/loaders/tiny-loom-selection.d.ts +88 -0
  36. package/dist/mapping/loaders/tiny-loom-selection.js +223 -0
  37. package/dist/mapping/loaders/tiny-loom.js +45 -33
  38. package/dist/mapping/loaders/tiny-maven.js +6 -11
  39. package/dist/mapping/parsers/tiny.d.ts +57 -0
  40. package/dist/mapping/parsers/tiny.js +99 -22
  41. package/dist/mapping-service.d.ts +19 -0
  42. package/dist/mapping-service.js +93 -9
  43. package/dist/mcp-helpers.d.ts +19 -2
  44. package/dist/mcp-helpers.js +48 -6
  45. package/dist/minecraft-explorer-service.d.ts +1 -1
  46. package/dist/mixin/types.d.ts +8 -0
  47. package/dist/mod-analyzer.js +7 -7
  48. package/dist/mod-decompile-service.js +1 -0
  49. package/dist/nbt/java-nbt-codec.js +12 -2
  50. package/dist/nbt/json-patch.js +14 -3
  51. package/dist/nbt/pipeline.js +40 -3
  52. package/dist/nbt/typed-json.js +26 -1
  53. package/dist/registration-adapter.d.ts +32 -0
  54. package/dist/registration-adapter.js +52 -0
  55. package/dist/request-context.d.ts +7 -0
  56. package/dist/request-context.js +9 -0
  57. package/dist/resources.d.ts +1 -1
  58. package/dist/resources.js +25 -19
  59. package/dist/server-identity.d.ts +27 -0
  60. package/dist/server-identity.js +26 -0
  61. package/dist/source/access-validate.js +53 -0
  62. package/dist/source/artifact-resolver.d.ts +69 -1
  63. package/dist/source/artifact-resolver.js +215 -14
  64. package/dist/source/class-source.d.ts +22 -0
  65. package/dist/source/class-source.js +162 -29
  66. package/dist/source/did-you-mean.d.ts +12 -1
  67. package/dist/source/did-you-mean.js +6 -2
  68. package/dist/source/file-access.js +150 -46
  69. package/dist/source/indexer.js +1 -0
  70. package/dist/source/nested-jars.d.ts +19 -0
  71. package/dist/source/nested-jars.js +90 -21
  72. package/dist/source/shared-utils.d.ts +21 -0
  73. package/dist/source/shared-utils.js +23 -0
  74. package/dist/source-service.d.ts +12 -0
  75. package/dist/source-service.js +3 -0
  76. package/dist/stdio-supervisor.d.ts +357 -2
  77. package/dist/stdio-supervisor.js +1031 -80
  78. package/dist/storage/db.d.ts +2 -1
  79. package/dist/storage/db.js +15 -8
  80. package/dist/synthetic-decorator.d.ts +24 -0
  81. package/dist/synthetic-decorator.js +48 -0
  82. package/dist/tool-contract-manifest.js +1 -1
  83. package/dist/tool-guidance.d.ts +17 -1
  84. package/dist/tool-guidance.js +417 -13
  85. package/dist/tool-schema-registry.d.ts +2 -0
  86. package/dist/tool-schema-registry.js +4 -0
  87. package/dist/tool-schemas.d.ts +2214 -3915
  88. package/dist/tool-schemas.js +34 -7
  89. package/dist/types.d.ts +35 -0
  90. package/dist/v1-parity-schemas.d.ts +7 -0
  91. package/dist/v1-parity-schemas.js +5584 -0
  92. package/dist/version-diff-service.d.ts +33 -0
  93. package/dist/version-diff-service.js +148 -3
  94. package/dist/version-service.js +36 -14
  95. package/dist/warning-details.js +18 -1
  96. package/docs/README-ja.md +9 -3
  97. package/docs/tool-reference.md +267 -22
  98. package/package.json +12 -9
package/dist/resources.js CHANGED
@@ -1,4 +1,5 @@
1
- import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
1
+ import { ResourceTemplate } from "@modelcontextprotocol/server";
2
+ import { METRICS_READ_CACHE_HINT, RESOURCE_READ_CACHE_HINT, VERSIONS_LIST_READ_CACHE_HINT } from "./cache-policy.js";
2
3
  import { createError, ERROR_CODES, isAppError } from "./errors.js";
3
4
  import { textResource, objectResource, errorResource } from "./mcp-helpers.js";
4
5
  function decodeTemplateParam(params, key) {
@@ -21,32 +22,37 @@ function decodeTemplateParam(params, key) {
21
22
  });
22
23
  }
23
24
  }
25
+ // Cache-hint rows (adopted policy, src/cache-policy.ts): per-registration
26
+ // hints ride the SDK's never-serialized carrier and only surface on
27
+ // 2026-07-28 results. The errorResource(...) calls also receive the request
28
+ // ctx so the ProblemDetails-read override (ttlMs 0, unconditional precedence
29
+ // over these class rows) can be applied structurally on the error path.
24
30
  export function registerResources(server, sourceService) {
25
31
  // ── Fixed resources ──────────────────────────────────────────────
26
- server.resource("versions-list", "mc://versions/list", { description: "List all available Minecraft versions with their metadata.", mimeType: "application/json" }, async (uri) => {
32
+ server.registerResource("versions-list", "mc://versions/list", { description: "List all available Minecraft versions with their metadata.", mimeType: "application/json", cacheHint: VERSIONS_LIST_READ_CACHE_HINT }, async (uri, ctx) => {
27
33
  try {
28
34
  const result = await sourceService.listVersions();
29
35
  return objectResource(uri.href, result);
30
36
  }
31
37
  catch (e) {
32
38
  if (isAppError(e))
33
- return errorResource(uri.href, { message: e.message, code: e.code, details: e.details });
39
+ return errorResource(uri.href, { message: e.message, code: e.code, details: e.details }, ctx);
34
40
  throw e;
35
41
  }
36
42
  });
37
- server.resource("runtime-metrics", "mc://metrics", { description: "Runtime metrics and performance counters for the MCP server.", mimeType: "application/json" }, async (uri) => {
43
+ server.registerResource("runtime-metrics", "mc://metrics", { description: "Runtime metrics and performance counters for the MCP server.", mimeType: "application/json", cacheHint: METRICS_READ_CACHE_HINT }, async (uri, ctx) => {
38
44
  try {
39
45
  const result = sourceService.getRuntimeMetrics();
40
46
  return objectResource(uri.href, result);
41
47
  }
42
48
  catch (e) {
43
49
  if (isAppError(e))
44
- return errorResource(uri.href, { message: e.message, code: e.code, details: e.details });
50
+ return errorResource(uri.href, { message: e.message, code: e.code, details: e.details }, ctx);
45
51
  throw e;
46
52
  }
47
53
  });
48
54
  // ── Template resources ───────────────────────────────────────────
49
- server.resource("class-source", new ResourceTemplate("mc://source/{artifactId}/{className}", { list: undefined }), { description: "Java source code for a class within a resolved artifact. className may use dot or slash separators.", mimeType: "text/x-java" }, async (uri, params) => {
55
+ server.registerResource("class-source", new ResourceTemplate("mc://source/{artifactId}/{className}", { list: undefined }), { description: "Java source code for a class within a resolved artifact. className may use dot or slash separators.", mimeType: "text/x-java", cacheHint: RESOURCE_READ_CACHE_HINT }, async (uri, params, ctx) => {
50
56
  try {
51
57
  const result = await sourceService.getClassSource({
52
58
  artifactId: params.artifactId,
@@ -59,11 +65,11 @@ export function registerResources(server, sourceService) {
59
65
  }
60
66
  catch (e) {
61
67
  if (isAppError(e))
62
- return errorResource(uri.href, { message: e.message, code: e.code, details: e.details });
68
+ return errorResource(uri.href, { message: e.message, code: e.code, details: e.details }, ctx);
63
69
  throw e;
64
70
  }
65
71
  });
66
- server.resource("class-source-json", new ResourceTemplate("mc://source-json/{artifactId}/{className}", { list: undefined }), { description: "JSON envelope of a class's full source plus metadata (artifactId, mappingApplied, totalLines, returnedRange, provenance, warnings) — the structured alternative to the raw-text class-source resource, easier to cite and continue.", mimeType: "application/json" }, async (uri, params) => {
72
+ server.registerResource("class-source-json", new ResourceTemplate("mc://source-json/{artifactId}/{className}", { list: undefined }), { description: "JSON envelope of a class's full source plus metadata (artifactId, mappingApplied, totalLines, returnedRange, provenance, warnings) — the structured alternative to the raw-text class-source resource, easier to cite and continue.", mimeType: "application/json", cacheHint: RESOURCE_READ_CACHE_HINT }, async (uri, params, ctx) => {
67
73
  try {
68
74
  const result = await sourceService.getClassSource({
69
75
  artifactId: params.artifactId,
@@ -74,11 +80,11 @@ export function registerResources(server, sourceService) {
74
80
  }
75
81
  catch (e) {
76
82
  if (isAppError(e))
77
- return errorResource(uri.href, { message: e.message, code: e.code, details: e.details });
83
+ return errorResource(uri.href, { message: e.message, code: e.code, details: e.details }, ctx);
78
84
  throw e;
79
85
  }
80
86
  });
81
- server.resource("artifact-file", new ResourceTemplate("mc://artifact/{artifactId}/files/{filePath}", { list: undefined }), { description: "Raw content of a file within a resolved artifact. filePath is the archive-relative path.", mimeType: "text/plain" }, async (uri, params) => {
87
+ server.registerResource("artifact-file", new ResourceTemplate("mc://artifact/{artifactId}/files/{filePath}", { list: undefined }), { description: "Raw content of a file within a resolved artifact. filePath is the archive-relative path.", mimeType: "text/plain", cacheHint: RESOURCE_READ_CACHE_HINT }, async (uri, params, ctx) => {
82
88
  try {
83
89
  const result = await sourceService.getArtifactFile({
84
90
  artifactId: params.artifactId,
@@ -88,11 +94,11 @@ export function registerResources(server, sourceService) {
88
94
  }
89
95
  catch (e) {
90
96
  if (isAppError(e))
91
- return errorResource(uri.href, { message: e.message, code: e.code, details: e.details });
97
+ return errorResource(uri.href, { message: e.message, code: e.code, details: e.details }, ctx);
92
98
  throw e;
93
99
  }
94
100
  });
95
- server.resource("find-mapping", new ResourceTemplate("mc://mappings/{version}/{sourceMapping}/{targetMapping}/{kind}/{name}", { list: undefined }), { description: "Look up a CLASS mapping between two naming namespaces. This URI carries no owner, so field/method lookups (which need an owner) must use the find-member-mapping resource or the find-mapping tool.", mimeType: "application/json" }, async (uri, params) => {
101
+ server.registerResource("find-mapping", new ResourceTemplate("mc://mappings/{version}/{sourceMapping}/{targetMapping}/{kind}/{name}", { list: undefined }), { description: "Look up a CLASS mapping between two naming namespaces. This URI carries no owner, so field/method lookups (which need an owner) must use the find-member-mapping resource or the find-mapping tool.", mimeType: "application/json", cacheHint: RESOURCE_READ_CACHE_HINT }, async (uri, params, ctx) => {
96
102
  try {
97
103
  const result = await sourceService.findMapping({
98
104
  version: params.version,
@@ -105,11 +111,11 @@ export function registerResources(server, sourceService) {
105
111
  }
106
112
  catch (e) {
107
113
  if (isAppError(e))
108
- return errorResource(uri.href, { message: e.message, code: e.code, details: e.details });
114
+ return errorResource(uri.href, { message: e.message, code: e.code, details: e.details }, ctx);
109
115
  throw e;
110
116
  }
111
117
  });
112
- server.resource("find-member-mapping", new ResourceTemplate("mc://mappings/{version}/{sourceMapping}/{targetMapping}/{kind}/{owner}/{name}", { list: undefined }), { description: "Look up a FIELD or METHOD mapping between two naming namespaces, including the owner class the member belongs to (required for member lookups). For exact method overload resolution, use the find-mapping tool with a descriptor.", mimeType: "application/json" }, async (uri, params) => {
118
+ server.registerResource("find-member-mapping", new ResourceTemplate("mc://mappings/{version}/{sourceMapping}/{targetMapping}/{kind}/{owner}/{name}", { list: undefined }), { description: "Look up a FIELD or METHOD mapping between two naming namespaces, including the owner class the member belongs to (required for member lookups). For exact method overload resolution, use the find-mapping tool with a descriptor.", mimeType: "application/json", cacheHint: RESOURCE_READ_CACHE_HINT }, async (uri, params, ctx) => {
113
119
  try {
114
120
  const result = await sourceService.findMapping({
115
121
  version: params.version,
@@ -123,11 +129,11 @@ export function registerResources(server, sourceService) {
123
129
  }
124
130
  catch (e) {
125
131
  if (isAppError(e))
126
- return errorResource(uri.href, { message: e.message, code: e.code, details: e.details });
132
+ return errorResource(uri.href, { message: e.message, code: e.code, details: e.details }, ctx);
127
133
  throw e;
128
134
  }
129
135
  });
130
- server.resource("class-members", new ResourceTemplate("mc://artifact/{artifactId}/members/{className}", { list: undefined }), { description: "List constructors, methods, and fields for a class within a resolved artifact.", mimeType: "application/json" }, async (uri, params) => {
136
+ server.registerResource("class-members", new ResourceTemplate("mc://artifact/{artifactId}/members/{className}", { list: undefined }), { description: "List constructors, methods, and fields for a class within a resolved artifact.", mimeType: "application/json", cacheHint: RESOURCE_READ_CACHE_HINT }, async (uri, params, ctx) => {
131
137
  try {
132
138
  const result = await sourceService.getClassMembers({
133
139
  artifactId: params.artifactId,
@@ -137,18 +143,18 @@ export function registerResources(server, sourceService) {
137
143
  }
138
144
  catch (e) {
139
145
  if (isAppError(e))
140
- return errorResource(uri.href, { message: e.message, code: e.code, details: e.details });
146
+ return errorResource(uri.href, { message: e.message, code: e.code, details: e.details }, ctx);
141
147
  throw e;
142
148
  }
143
149
  });
144
- server.resource("artifact-metadata", new ResourceTemplate("mc://artifact/{artifactId}", { list: undefined }), { description: "Metadata for a previously resolved artifact (origin, coordinate, mapping, provenance).", mimeType: "application/json" }, async (uri, params) => {
150
+ server.registerResource("artifact-metadata", new ResourceTemplate("mc://artifact/{artifactId}", { list: undefined }), { description: "Metadata for a previously resolved artifact (origin, coordinate, mapping, provenance).", mimeType: "application/json", cacheHint: RESOURCE_READ_CACHE_HINT }, async (uri, params, ctx) => {
145
151
  try {
146
152
  const artifact = sourceService.getArtifact(params.artifactId);
147
153
  return objectResource(uri.href, artifact);
148
154
  }
149
155
  catch (e) {
150
156
  if (isAppError(e))
151
- return errorResource(uri.href, { message: e.message, code: e.code, details: e.details });
157
+ return errorResource(uri.href, { message: e.message, code: e.code, details: e.details }, ctx);
152
158
  throw e;
153
159
  }
154
160
  });
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Canonical server identity — the single source of the {name, version}
3
+ * Implementation this process advertises.
4
+ *
5
+ * Consumers:
6
+ * - src/index.ts constructs the McpServer from these values; the SDK stamps
7
+ * that Implementation verbatim as `_meta["io.modelcontextprotocol/serverInfo"]`
8
+ * on every modern result it produces (and it is the initialize/discover
9
+ * serverInfo).
10
+ * - src/synthetic-decorator.ts stamps the same identity onto modern-era
11
+ * supervisor-synthesized results.
12
+ *
13
+ * Equality between live SDK results and synthetic decoration is therefore by
14
+ * construction; the live-SDK drift-guard test
15
+ * (tests/stdio/stdio-supervisor-synthetic-drift.test.ts) proves it end-to-end.
16
+ */
17
+ export type ServerIdentity = {
18
+ name: string;
19
+ version: string;
20
+ };
21
+ export declare const SERVER_NAME = "@adhisang/minecraft-modding-mcp";
22
+ export declare function getServerVersionFromPackageJson(): string;
23
+ export declare const SERVER_VERSION: string;
24
+ /** Frozen canonical identity; consumers needing a mutable object must copy. */
25
+ export declare const SERVER_IDENTITY: Readonly<ServerIdentity>;
26
+ /** Fresh, mutation-safe copy of the canonical identity. */
27
+ export declare function serverIdentitySnapshot(): ServerIdentity;
@@ -0,0 +1,26 @@
1
+ import { readFileSync } from "node:fs";
2
+ export const SERVER_NAME = "@adhisang/minecraft-modding-mcp";
3
+ export function getServerVersionFromPackageJson() {
4
+ try {
5
+ const packageJsonUrl = new URL("../package.json", import.meta.url);
6
+ const packageJson = JSON.parse(readFileSync(packageJsonUrl, "utf8"));
7
+ if (typeof packageJson.version === "string" && packageJson.version.trim()) {
8
+ return packageJson.version.trim();
9
+ }
10
+ }
11
+ catch {
12
+ // ignore and fallback
13
+ }
14
+ return "0.3.0";
15
+ }
16
+ export const SERVER_VERSION = getServerVersionFromPackageJson();
17
+ /** Frozen canonical identity; consumers needing a mutable object must copy. */
18
+ export const SERVER_IDENTITY = Object.freeze({
19
+ name: SERVER_NAME,
20
+ version: SERVER_VERSION
21
+ });
22
+ /** Fresh, mutation-safe copy of the canonical identity. */
23
+ export function serverIdentitySnapshot() {
24
+ return { name: SERVER_IDENTITY.name, version: SERVER_IDENTITY.version };
25
+ }
26
+ //# sourceMappingURL=server-identity.js.map
@@ -30,6 +30,47 @@ function isSourceMappingNamespace(namespace) {
30
30
  namespace === "intermediary" ||
31
31
  namespace === "yarn");
32
32
  }
33
+ /**
34
+ * Refuses a verdict that would be computed against another loader's bytecode,
35
+ * and marks a cross-version one as approximate.
36
+ *
37
+ * A runtime fallback could serve a NeoForge 1.21.10 AT-patched jar to a Fabric
38
+ * 1.21.11 workspace; validation then reported `valid: true` with a
39
+ * `resolvedRuntimeJavaSignature` taken from bytecode the project never runs.
40
+ * A different loader's access flags are simply not evidence about this one, so
41
+ * that case fails loudly. A same-loader version drift still yields a verdict —
42
+ * flagged, never silent.
43
+ */
44
+ function guardRuntimeEvidence(provenance, warnings, subject) {
45
+ if (!provenance) {
46
+ return {};
47
+ }
48
+ if (provenance.loaderMismatch) {
49
+ throw createError({
50
+ code: ERROR_CODES.CONTEXT_UNRESOLVED,
51
+ message: `${subject} validation resolved a ${provenance.servedLoader} runtime jar for a ` +
52
+ `${provenance.expectedLoader} workspace; a different loader's bytecode cannot certify these entries.`,
53
+ details: {
54
+ jarPath: provenance.jarPath,
55
+ servedLoader: provenance.servedLoader,
56
+ expectedLoader: provenance.expectedLoader,
57
+ version: provenance.version,
58
+ ...(provenance.requestedVersion ? { requestedVersion: provenance.requestedVersion } : {}),
59
+ requestedScope: provenance.requestedScope,
60
+ appliedScope: provenance.appliedScope,
61
+ nextAction: `Point gradleUserHome at the Gradle home holding this workspace's own ${provenance.expectedLoader} runtime jars, ` +
62
+ "or run the Gradle task that generates them. Omitting projectPath validates against the vanilla jar instead of another loader's."
63
+ }
64
+ });
65
+ }
66
+ if (!provenance.versionApproximated) {
67
+ return {};
68
+ }
69
+ const reason = `Runtime evidence comes from Minecraft ${provenance.version}, not the requested ` +
70
+ `${provenance.requestedVersion}; entries were checked against a different version's bytecode.`;
71
+ warnings.push(reason);
72
+ return { approximate: true, approximationReasons: [reason] };
73
+ }
33
74
  export async function validateAccessWidener(svc, input) {
34
75
  const version = input.version.trim();
35
76
  if (!version) {
@@ -61,6 +102,7 @@ export async function validateAccessWidener(svc, input) {
61
102
  let jarPath;
62
103
  let lookupMapping = "obfuscated";
63
104
  let provenance;
105
+ let approximation = {};
64
106
  if (runtimeAware) {
65
107
  provenance = await svc.resolveAccessWidenerRuntimeArtifact({
66
108
  version,
@@ -70,6 +112,7 @@ export async function validateAccessWidener(svc, input) {
70
112
  scope: input.scope,
71
113
  preferProjectVersion: input.preferProjectVersion
72
114
  });
115
+ approximation = guardRuntimeEvidence(provenance, warnings, "Access Widener");
73
116
  resolvedVersion = provenance.version;
74
117
  jarPath = provenance.jarPath;
75
118
  lookupMapping = provenance.mappingApplied;
@@ -148,6 +191,10 @@ export async function validateAccessWidener(svc, input) {
148
191
  if (provenance) {
149
192
  result.provenance = provenance;
150
193
  }
194
+ if (approximation.approximate) {
195
+ result.approximate = true;
196
+ result.approximationReasons = approximation.approximationReasons;
197
+ }
151
198
  return result;
152
199
  }
153
200
  export async function validateAccessTransformer(svc, input) {
@@ -170,6 +217,7 @@ export async function validateAccessTransformer(svc, input) {
170
217
  let jarPath;
171
218
  let lookupMapping = "obfuscated";
172
219
  let provenance;
220
+ let approximation = {};
173
221
  if (runtimeAware) {
174
222
  provenance = await svc.resolveAccessTransformerRuntimeArtifact({
175
223
  version,
@@ -179,6 +227,7 @@ export async function validateAccessTransformer(svc, input) {
179
227
  scope: input.scope,
180
228
  preferProjectVersion: input.preferProjectVersion
181
229
  });
230
+ approximation = guardRuntimeEvidence(provenance, warnings, "Access Transformer");
182
231
  resolvedVersion = provenance.version;
183
232
  jarPath = provenance.jarPath;
184
233
  lookupMapping = provenance.mappingApplied;
@@ -263,6 +312,10 @@ export async function validateAccessTransformer(svc, input) {
263
312
  if (provenance) {
264
313
  result.provenance = provenance;
265
314
  }
315
+ if (approximation.approximate) {
316
+ result.approximate = true;
317
+ result.approximationReasons = approximation.approximationReasons;
318
+ }
266
319
  return result;
267
320
  }
268
321
  //# sourceMappingURL=access-validate.js.map
@@ -1,6 +1,6 @@
1
1
  import type { SourceService } from "../source-service.js";
2
2
  import type { ArtifactContentsSummary, ProbeMinecraftArtifactInput, ProbeMinecraftArtifactOutput, ResolveArtifactInput, ResolveArtifactOutput } from "../source-service.js";
3
- import type { AccessTransformerNamespace, ArtifactProvenance, ArtifactScope, ArtifactTargetKind, ResolvedSourceArtifact, RuntimeValidationProvenance, SourceMapping } from "../types.js";
3
+ import type { AccessTransformerNamespace, ArtifactProvenance, ArtifactScope, ArtifactTargetKind, ResolvedSourceArtifact, RuntimeLoader, RuntimeValidationProvenance, SourceMapping } from "../types.js";
4
4
  import type { WorkspaceProjectLoader } from "../workspace-mapping-service.js";
5
5
  export type VersionSourceDiscovery = {
6
6
  searchedPaths: string[];
@@ -16,7 +16,19 @@ type RuntimeJarCandidate = {
16
16
  appliedScope: ArtifactScope;
17
17
  origin: RuntimeValidationProvenance["origin"];
18
18
  namespaceHint?: "intermediary" | "mojang" | "named";
19
+ /** How the candidate was tied to the requested Minecraft version. */
20
+ versionEvidence?: RuntimeVersionEvidence;
19
21
  };
22
+ /**
23
+ * Why a runtime jar is believed to belong to the requested Minecraft version.
24
+ *
25
+ * - `exact-token`: the Minecraft version appears verbatim in the path.
26
+ * - `loader-token`: the path carries the loader version that maps 1:1 onto the
27
+ * Minecraft version (NeoForge `21.11.x` <-> Minecraft `1.21.11`).
28
+ * - `project-anchored`: the jar sits inside the caller's own project build
29
+ * directory and the project declares exactly the requested Minecraft version.
30
+ */
31
+ export type RuntimeVersionEvidence = "exact-token" | "loader-token" | "project-anchored";
20
32
  export type MappingFallbackSuggestion = {
21
33
  suggestedCall?: {
22
34
  tool: string;
@@ -31,6 +43,58 @@ export type MappingFallbackSuggestion = {
31
43
  _suggestedCallPrimaryDropped?: boolean;
32
44
  };
33
45
  export declare function hasExactVersionToken(path: string, version: string): boolean;
46
+ /**
47
+ * True when a path carries the NeoForge/Forge loader version that corresponds
48
+ * 1:1 to `mcVersion`.
49
+ *
50
+ * ModDevGradle names every artifact after the LOADER version and never after
51
+ * Minecraft: MC 1.21.11 produces `build/moddev/artifacts/neoforge-21.11.38-beta-merged.jar`.
52
+ * A plain `hasExactVersionToken(path, "1.21.11")` therefore rejected every
53
+ * artifact of the canonical NeoForge workspace, which made
54
+ * validate-access-transformer unusable there.
55
+ *
56
+ * NeoForge derives its version from Minecraft as `<minor>.<patch>.<build>`, so
57
+ * `1.21.11` -> `21.11.<build>` and `1.21` -> `21.0.<build>`. The trailing dot
58
+ * before the build number keeps `21.1.` from matching `21.10.5`.
59
+ */
60
+ export declare function hasLoaderRuntimeVersionToken(path: string, mcVersion: string): boolean;
61
+ /**
62
+ * Minecraft version a runtime jar path carries, or undefined when the path
63
+ * names none. Loom lays its cache out as
64
+ * `<gradle>/caches/fabric-loom/<mcVersion>/...`, so the first `1.x[.y]` token
65
+ * of the path is the version the jar was built for.
66
+ */
67
+ export declare function inferRuntimeJarMinecraftVersion(path: string): string | undefined;
68
+ /**
69
+ * Loader a runtime jar belongs to, read from its path.
70
+ *
71
+ * A Loom cache holds NeoForge-patched jars under a `/neoforge/` segment
72
+ * (`caches/fabric-loom/1.21.10/neoforge/21.10.50-beta/minecraft-merged-mojang-at-patched.jar`).
73
+ * Serving one of those to a Fabric workspace silently validated a Fabric access
74
+ * widener against NeoForge bytecode, so the loader has to travel with the jar.
75
+ */
76
+ export declare function inferRuntimeJarLoader(path: string): RuntimeLoader;
77
+ /**
78
+ * Fills in the truthful version/loader half of a runtime provenance record.
79
+ *
80
+ * `version` becomes the version the SERVED jar carries; the caller's original
81
+ * request is preserved under `requestedVersion` and flagged. A served loader
82
+ * that contradicts a KNOWN expected loader is flagged too — never silently
83
+ * dropped.
84
+ */
85
+ export declare function describeServedRuntimeJar(input: {
86
+ jarPath: string;
87
+ requestedVersion: string;
88
+ expectedLoader?: RuntimeLoader;
89
+ }): {
90
+ version: string;
91
+ requestedVersion?: string;
92
+ versionApproximated?: boolean;
93
+ servedLoader: RuntimeLoader;
94
+ expectedLoader?: RuntimeLoader;
95
+ loaderMismatch?: boolean;
96
+ notes: string[];
97
+ };
34
98
  export declare function discoverVersionSourceJar(_svc: SourceService, input: {
35
99
  version: string;
36
100
  projectPath?: string;
@@ -54,6 +118,8 @@ export declare function discoverAccessTransformerRuntimeCandidates(_svc: SourceS
54
118
  requestedScope: ArtifactScope;
55
119
  atNamespace: AccessTransformerNamespace;
56
120
  loader: WorkspaceProjectLoader | "unknown";
121
+ /** Minecraft version the workspace itself declares, when it could be read. */
122
+ projectMinecraftVersion?: string;
57
123
  }): Promise<{
58
124
  searchedPaths: string[];
59
125
  candidateArtifacts: string[];
@@ -100,6 +166,8 @@ export declare function resolveBinaryFallbackArtifact(svc: SourceService, input:
100
166
  mappingApplied: SourceMapping;
101
167
  provenance?: ArtifactProvenance;
102
168
  qualityFlags: string[];
169
+ /** Forwarded from the caller; unset keeps the historical decompile-on-demand behaviour. */
170
+ allowDecompile?: boolean;
103
171
  }): Promise<ResolvedSourceArtifact | undefined>;
104
172
  export declare function buildArtifactContentsSummary(_svc: SourceService, input: {
105
173
  origin: ResolvedSourceArtifact["origin"];