@adhisang/minecraft-modding-mcp 7.0.0-rc.2 → 7.0.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.
@@ -135,7 +135,11 @@ export function openDatabase(config, logger = buildDefaultLogger()) {
135
135
  path: config.sqlitePath,
136
136
  reason: errorMessage
137
137
  });
138
- throw caughtError;
138
+ throw createError({
139
+ code: ERROR_CODES.DB_FAILURE,
140
+ message: `Failed to open SQLite database at ${config.sqlitePath}: ${errorMessage}`,
141
+ details: { sqlitePath: config.sqlitePath, reason: caughtError?.code }
142
+ });
139
143
  }
140
144
  // The rebuild runs INSIDE the handler for the failure it is recovering
141
145
  // from, so it needs a guard of its own: without one a throw from here left
@@ -258,10 +258,10 @@ export declare const resolveArtifactShape: {
258
258
  full: "full";
259
259
  }>>;
260
260
  include: z.ZodOptional<z.ZodArray<z.ZodEnum<{
261
- entries: "entries";
262
261
  workspace: "workspace";
263
- candidates: "candidates";
262
+ entries: "entries";
264
263
  source: "source";
264
+ candidates: "candidates";
265
265
  warnings: "warnings";
266
266
  registry: "registry";
267
267
  preview: "preview";
@@ -335,10 +335,10 @@ export declare const resolveArtifactSchema: z.ZodObject<{
335
335
  full: "full";
336
336
  }>>;
337
337
  include: z.ZodOptional<z.ZodArray<z.ZodEnum<{
338
- entries: "entries";
339
338
  workspace: "workspace";
340
- candidates: "candidates";
339
+ entries: "entries";
341
340
  source: "source";
341
+ candidates: "candidates";
342
342
  warnings: "warnings";
343
343
  registry: "registry";
344
344
  preview: "preview";
@@ -426,10 +426,10 @@ export declare const getClassSourceShape: {
426
426
  full: "full";
427
427
  }>>;
428
428
  include: z.ZodOptional<z.ZodArray<z.ZodEnum<{
429
- entries: "entries";
430
429
  workspace: "workspace";
431
- candidates: "candidates";
430
+ entries: "entries";
432
431
  source: "source";
432
+ candidates: "candidates";
433
433
  warnings: "warnings";
434
434
  registry: "registry";
435
435
  preview: "preview";
@@ -518,10 +518,10 @@ export declare const getClassSourceSchema: z.ZodObject<{
518
518
  full: "full";
519
519
  }>>;
520
520
  include: z.ZodOptional<z.ZodArray<z.ZodEnum<{
521
- entries: "entries";
522
521
  workspace: "workspace";
523
- candidates: "candidates";
522
+ entries: "entries";
524
523
  source: "source";
524
+ candidates: "candidates";
525
525
  warnings: "warnings";
526
526
  registry: "registry";
527
527
  preview: "preview";
@@ -615,10 +615,10 @@ export declare const getClassMembersShape: {
615
615
  full: "full";
616
616
  }>>;
617
617
  include: z.ZodOptional<z.ZodArray<z.ZodEnum<{
618
- entries: "entries";
619
618
  workspace: "workspace";
620
- candidates: "candidates";
619
+ entries: "entries";
621
620
  source: "source";
621
+ candidates: "candidates";
622
622
  warnings: "warnings";
623
623
  registry: "registry";
624
624
  preview: "preview";
@@ -713,10 +713,10 @@ export declare const getClassMembersSchema: z.ZodObject<{
713
713
  full: "full";
714
714
  }>>;
715
715
  include: z.ZodOptional<z.ZodArray<z.ZodEnum<{
716
- entries: "entries";
717
716
  workspace: "workspace";
718
- candidates: "candidates";
717
+ entries: "entries";
719
718
  source: "source";
719
+ candidates: "candidates";
720
720
  warnings: "warnings";
721
721
  registry: "registry";
722
722
  preview: "preview";
@@ -935,10 +935,10 @@ export declare const batchClassSourceShape: {
935
935
  full: "full";
936
936
  }>>;
937
937
  include: z.ZodOptional<z.ZodArray<z.ZodEnum<{
938
- entries: "entries";
939
938
  workspace: "workspace";
940
- candidates: "candidates";
939
+ entries: "entries";
941
940
  source: "source";
941
+ candidates: "candidates";
942
942
  warnings: "warnings";
943
943
  registry: "registry";
944
944
  preview: "preview";
@@ -1027,10 +1027,10 @@ export declare const batchClassSourceSchema: z.ZodObject<{
1027
1027
  full: "full";
1028
1028
  }>>;
1029
1029
  include: z.ZodOptional<z.ZodArray<z.ZodEnum<{
1030
- entries: "entries";
1031
1030
  workspace: "workspace";
1032
- candidates: "candidates";
1031
+ entries: "entries";
1033
1032
  source: "source";
1033
+ candidates: "candidates";
1034
1034
  warnings: "warnings";
1035
1035
  registry: "registry";
1036
1036
  preview: "preview";
@@ -1135,10 +1135,10 @@ export declare const batchClassMembersShape: {
1135
1135
  signatures: "signatures";
1136
1136
  }>>;
1137
1137
  include: z.ZodOptional<z.ZodArray<z.ZodEnum<{
1138
- entries: "entries";
1139
1138
  workspace: "workspace";
1140
- candidates: "candidates";
1139
+ entries: "entries";
1141
1140
  source: "source";
1141
+ candidates: "candidates";
1142
1142
  warnings: "warnings";
1143
1143
  registry: "registry";
1144
1144
  preview: "preview";
@@ -1230,10 +1230,10 @@ export declare const batchClassMembersSchema: z.ZodObject<{
1230
1230
  signatures: "signatures";
1231
1231
  }>>;
1232
1232
  include: z.ZodOptional<z.ZodArray<z.ZodEnum<{
1233
- entries: "entries";
1234
1233
  workspace: "workspace";
1235
- candidates: "candidates";
1234
+ entries: "entries";
1236
1235
  source: "source";
1236
+ candidates: "candidates";
1237
1237
  warnings: "warnings";
1238
1238
  registry: "registry";
1239
1239
  preview: "preview";
@@ -1339,10 +1339,10 @@ export declare const batchSymbolExistsShape: {
1339
1339
  full: "full";
1340
1340
  }>>;
1341
1341
  include: z.ZodOptional<z.ZodArray<z.ZodEnum<{
1342
- entries: "entries";
1343
1342
  workspace: "workspace";
1344
- candidates: "candidates";
1343
+ entries: "entries";
1345
1344
  source: "source";
1345
+ candidates: "candidates";
1346
1346
  warnings: "warnings";
1347
1347
  registry: "registry";
1348
1348
  preview: "preview";
@@ -1425,10 +1425,10 @@ export declare const batchSymbolExistsSchema: z.ZodObject<{
1425
1425
  full: "full";
1426
1426
  }>>;
1427
1427
  include: z.ZodOptional<z.ZodArray<z.ZodEnum<{
1428
- entries: "entries";
1429
1428
  workspace: "workspace";
1430
- candidates: "candidates";
1429
+ entries: "entries";
1431
1430
  source: "source";
1431
+ candidates: "candidates";
1432
1432
  warnings: "warnings";
1433
1433
  registry: "registry";
1434
1434
  preview: "preview";
@@ -1517,10 +1517,10 @@ export declare const batchMappingsShape: {
1517
1517
  full: "full";
1518
1518
  }>>;
1519
1519
  include: z.ZodOptional<z.ZodArray<z.ZodEnum<{
1520
- entries: "entries";
1521
1520
  workspace: "workspace";
1522
- candidates: "candidates";
1521
+ entries: "entries";
1523
1522
  source: "source";
1523
+ candidates: "candidates";
1524
1524
  warnings: "warnings";
1525
1525
  registry: "registry";
1526
1526
  preview: "preview";
@@ -1590,10 +1590,10 @@ export declare const batchMappingsSchema: z.ZodObject<{
1590
1590
  full: "full";
1591
1591
  }>>;
1592
1592
  include: z.ZodOptional<z.ZodArray<z.ZodEnum<{
1593
- entries: "entries";
1594
1593
  workspace: "workspace";
1595
- candidates: "candidates";
1594
+ entries: "entries";
1596
1595
  source: "source";
1596
+ candidates: "candidates";
1597
1597
  warnings: "warnings";
1598
1598
  registry: "registry";
1599
1599
  preview: "preview";
@@ -1789,10 +1789,10 @@ export declare const searchClassSourceShape: {
1789
1789
  full: "full";
1790
1790
  }>>;
1791
1791
  include: z.ZodOptional<z.ZodArray<z.ZodEnum<{
1792
- entries: "entries";
1793
1792
  workspace: "workspace";
1794
- candidates: "candidates";
1793
+ entries: "entries";
1795
1794
  source: "source";
1795
+ candidates: "candidates";
1796
1796
  warnings: "warnings";
1797
1797
  registry: "registry";
1798
1798
  preview: "preview";
@@ -1891,10 +1891,10 @@ export declare const searchClassSourceSchema: z.ZodObject<{
1891
1891
  full: "full";
1892
1892
  }>>;
1893
1893
  include: z.ZodOptional<z.ZodArray<z.ZodEnum<{
1894
- entries: "entries";
1895
1894
  workspace: "workspace";
1896
- candidates: "candidates";
1895
+ entries: "entries";
1897
1896
  source: "source";
1897
+ candidates: "candidates";
1898
1898
  warnings: "warnings";
1899
1899
  registry: "registry";
1900
1900
  preview: "preview";
@@ -2022,10 +2022,10 @@ export declare const listArtifactFilesShape: {
2022
2022
  full: "full";
2023
2023
  }>>;
2024
2024
  include: z.ZodOptional<z.ZodArray<z.ZodEnum<{
2025
- entries: "entries";
2026
2025
  workspace: "workspace";
2027
- candidates: "candidates";
2026
+ entries: "entries";
2028
2027
  source: "source";
2028
+ candidates: "candidates";
2029
2029
  warnings: "warnings";
2030
2030
  registry: "registry";
2031
2031
  preview: "preview";
@@ -2087,10 +2087,10 @@ export declare const listArtifactFilesSchema: z.ZodObject<{
2087
2087
  full: "full";
2088
2088
  }>>;
2089
2089
  include: z.ZodOptional<z.ZodArray<z.ZodEnum<{
2090
- entries: "entries";
2091
2090
  workspace: "workspace";
2092
- candidates: "candidates";
2091
+ entries: "entries";
2093
2092
  source: "source";
2093
+ candidates: "candidates";
2094
2094
  warnings: "warnings";
2095
2095
  registry: "registry";
2096
2096
  preview: "preview";
@@ -2233,10 +2233,10 @@ export declare const findMappingShape: {
2233
2233
  full: "full";
2234
2234
  }>>;
2235
2235
  include: z.ZodOptional<z.ZodArray<z.ZodEnum<{
2236
- entries: "entries";
2237
2236
  workspace: "workspace";
2238
- candidates: "candidates";
2237
+ entries: "entries";
2239
2238
  source: "source";
2239
+ candidates: "candidates";
2240
2240
  warnings: "warnings";
2241
2241
  registry: "registry";
2242
2242
  preview: "preview";
@@ -2305,10 +2305,10 @@ export declare const findMappingSchema: z.ZodObject<{
2305
2305
  full: "full";
2306
2306
  }>>;
2307
2307
  include: z.ZodOptional<z.ZodArray<z.ZodEnum<{
2308
- entries: "entries";
2309
2308
  workspace: "workspace";
2310
- candidates: "candidates";
2309
+ entries: "entries";
2311
2310
  source: "source";
2311
+ candidates: "candidates";
2312
2312
  warnings: "warnings";
2313
2313
  registry: "registry";
2314
2314
  preview: "preview";
@@ -2360,10 +2360,10 @@ export declare const resolveMethodMappingExactShape: {
2360
2360
  full: "full";
2361
2361
  }>>;
2362
2362
  include: z.ZodOptional<z.ZodArray<z.ZodEnum<{
2363
- entries: "entries";
2364
2363
  workspace: "workspace";
2365
- candidates: "candidates";
2364
+ entries: "entries";
2366
2365
  source: "source";
2366
+ candidates: "candidates";
2367
2367
  warnings: "warnings";
2368
2368
  registry: "registry";
2369
2369
  preview: "preview";
@@ -2415,10 +2415,10 @@ export declare const resolveMethodMappingExactSchema: z.ZodObject<{
2415
2415
  full: "full";
2416
2416
  }>>;
2417
2417
  include: z.ZodOptional<z.ZodArray<z.ZodEnum<{
2418
- entries: "entries";
2419
2418
  workspace: "workspace";
2420
- candidates: "candidates";
2419
+ entries: "entries";
2421
2420
  source: "source";
2421
+ candidates: "candidates";
2422
2422
  warnings: "warnings";
2423
2423
  registry: "registry";
2424
2424
  preview: "preview";
@@ -2507,10 +2507,10 @@ export declare const resolveWorkspaceSymbolShape: {
2507
2507
  full: "full";
2508
2508
  }>>;
2509
2509
  include: z.ZodOptional<z.ZodArray<z.ZodEnum<{
2510
- entries: "entries";
2511
2510
  workspace: "workspace";
2512
- candidates: "candidates";
2511
+ entries: "entries";
2513
2512
  source: "source";
2513
+ candidates: "candidates";
2514
2514
  warnings: "warnings";
2515
2515
  registry: "registry";
2516
2516
  preview: "preview";
@@ -2562,10 +2562,10 @@ export declare const resolveWorkspaceSymbolSchema: z.ZodObject<{
2562
2562
  full: "full";
2563
2563
  }>>;
2564
2564
  include: z.ZodOptional<z.ZodArray<z.ZodEnum<{
2565
- entries: "entries";
2566
2565
  workspace: "workspace";
2567
- candidates: "candidates";
2566
+ entries: "entries";
2568
2567
  source: "source";
2568
+ candidates: "candidates";
2569
2569
  warnings: "warnings";
2570
2570
  registry: "registry";
2571
2571
  preview: "preview";
@@ -2624,10 +2624,10 @@ export declare const checkSymbolExistsShape: {
2624
2624
  full: "full";
2625
2625
  }>>;
2626
2626
  include: z.ZodOptional<z.ZodArray<z.ZodEnum<{
2627
- entries: "entries";
2628
2627
  workspace: "workspace";
2629
- candidates: "candidates";
2628
+ entries: "entries";
2630
2629
  source: "source";
2630
+ candidates: "candidates";
2631
2631
  warnings: "warnings";
2632
2632
  registry: "registry";
2633
2633
  preview: "preview";
@@ -2686,10 +2686,10 @@ export declare const checkSymbolExistsSchema: z.ZodObject<{
2686
2686
  full: "full";
2687
2687
  }>>;
2688
2688
  include: z.ZodOptional<z.ZodArray<z.ZodEnum<{
2689
- entries: "entries";
2690
2689
  workspace: "workspace";
2691
- candidates: "candidates";
2690
+ entries: "entries";
2692
2691
  source: "source";
2692
+ candidates: "candidates";
2693
2693
  warnings: "warnings";
2694
2694
  registry: "registry";
2695
2695
  preview: "preview";
@@ -3125,8 +3125,8 @@ export declare const modSearchTypeSchema: z.ZodEnum<{
3125
3125
  class: "class";
3126
3126
  field: "field";
3127
3127
  method: "method";
3128
- all: "all";
3129
3128
  content: "content";
3129
+ all: "all";
3130
3130
  }>;
3131
3131
  export declare const searchModSourceShape: {
3132
3132
  jarPath: z.ZodString;
@@ -3135,8 +3135,8 @@ export declare const searchModSourceShape: {
3135
3135
  class: "class";
3136
3136
  field: "field";
3137
3137
  method: "method";
3138
- all: "all";
3139
3138
  content: "content";
3139
+ all: "all";
3140
3140
  }>>;
3141
3141
  limit: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
3142
3142
  };
@@ -3147,8 +3147,8 @@ export declare const searchModSourceSchema: z.ZodObject<{
3147
3147
  class: "class";
3148
3148
  field: "field";
3149
3149
  method: "method";
3150
- all: "all";
3151
3150
  content: "content";
3151
+ all: "all";
3152
3152
  }>>;
3153
3153
  limit: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
3154
3154
  }, z.core.$strip>;
package/dist/types.d.ts CHANGED
@@ -182,9 +182,26 @@ export interface Config {
182
182
  remapMaxMemoryMb: number;
183
183
  decompileMaxMemoryMb: number;
184
184
  }
185
- export interface ArtifactSignature {
185
+ /**
186
+ * A jar's filesystem stat, stamped. NOT an artifact identity: nothing here is
187
+ * read from the jar's bytes, so a `touch` moves it and a byte-for-byte
188
+ * replacement of the same length at the same mtime does not.
189
+ *
190
+ * Artifact identity lives in `src/artifact-identity.ts`. This shape feeds the
191
+ * `jarSignature` field `get-project-context` publishes, and that field's
192
+ * stat-based value is part of the tool's output contract.
193
+ */
194
+ export interface JarStatStamp {
186
195
  sourcePath: string;
196
+ /**
197
+ * sha256 of `jar|<resolved path>|<mtimeMs>:<size>`.
198
+ *
199
+ * The name is inherited and overstates it: this is not the artifactId of
200
+ * anything. Its only consumer is the `jarSignature` field in
201
+ * `src/minecraft-explorer-service.ts`.
202
+ */
187
203
  sourceArtifactId: string;
204
+ /** The `<truncated mtimeMs>:<size>` stamp itself. */
188
205
  signature: string;
189
206
  signatureParts: {
190
207
  mtimeMs: number;
@@ -142,7 +142,7 @@ Artifact-relative file read from a workspace:
142
142
  | target shape | When to use | Required input | Result |
143
143
  |---|---|---|---|
144
144
  | `{ kind: "workspace", scope?, strict? }` | When the caller already passes a `projectPath` and wants the tool to detect Minecraft version, compile mapping, and loader from `gradle.properties` and `build.gradle(.kts)`. | `projectPath` | Synthesised to `{ kind: "version", value: <detected> }`. Scope precedence is `target.scope` → top-level `scope` → loader-derived default (`"merged"` when a loader is detected, `"vanilla"` otherwise). When the version is not detected, raises `ERR_WORKSPACE_VERSION_UNRESOLVED` regardless of `strict`. Detected facts surface on `provenance.workspaceResolution`. |
145
- | `{ kind: "dependency", group, name, version?, versionFromProject? }` | When the caller wants to resolve a Maven-coordinate dependency (e.g. `dev.architectury:architectury`) without computing the exact version themselves. | `projectPath` (unless `version` is given) | Synthesised to `{ kind: "coordinate", value: "<group>:<name>:<version>" }`. The dependency JAR is treated as non-vanilla: binary remap is suppressed, so a non-obfuscated mapping request is honored only if a source-backed jar can supply it. If a source-backed jar is available, the resolver returns `mappingApplied` equal to the requested mapping with `qualityFlags` including `"source-backed"`, and no warning is added. Otherwise the resolver returns the JAR with `mappingApplied: "obfuscated"` and `qualityFlags: ["dependency-mapping-unverified"]`, plus a warning that the caller must validate symbol availability. Resolution metadata appears on `provenance.dependencyResolution`. |
145
+ | `{ kind: "dependency", group, name, version?, versionFromProject? }` | When the caller wants to resolve a Maven-coordinate dependency (e.g. `dev.architectury:architectury`) without computing the exact version themselves. | `projectPath` (unless `version` is given) | Synthesised to `{ kind: "coordinate", value: "<group>:<name>:<version>" }`. The dependency JAR is treated as non-vanilla: binary remap is suppressed, so a non-obfuscated mapping request is honored only if a source-backed jar can supply it. If a source-backed jar is available, the resolver returns `mappingApplied` equal to the requested mapping with `qualityFlags` including `"source-backed"`, and no warning is added. If none is, that same non-obfuscated request comes back with `mappingApplied: "obfuscated"` and `qualityFlags` including `"dependency-mapping-unverified"`, plus a warning that the caller must validate symbol availability. An explicit `mapping: "obfuscated"` request never takes that branch: it passes through as `mappingApplied: "obfuscated"` with `qualityFlags` including `"source-backed"` (or `"decompiled"`), and never carries `"dependency-mapping-unverified"`. Resolution metadata appears on `provenance.dependencyResolution`. |
146
146
 
147
147
  ### Inspecting a Fabric / loader dependency like vanilla
148
148
 
@@ -161,11 +161,18 @@ the same way you read vanilla, pass a `dependency` target straight to
161
161
  ```
162
162
 
163
163
  Use an explicit `"version"` instead of `versionFromProject` when you already
164
- know it. Because dependency JARs are not remapped, members come back in the
165
- dependency's own namespace with `qualityFlags: ["dependency-mapping-unverified"]`;
166
- treat the names as the JAR's compiled names. For repeated lookups against the
167
- same dependency, call `resolve-artifact` once and reuse the returned
168
- `artifactId` via `target: { kind: "artifact", artifactId }`.
164
+ know it. Because dependency JARs are not binary-remapped, a non-obfuscated
165
+ mapping request is honored only when a source-backed jar can supply it: there
166
+ `mappingApplied` equals the requested mapping and `qualityFlags` includes
167
+ `"source-backed"`. When no source-backed jar can supply it, that same
168
+ non-obfuscated request comes back in the dependency's own namespace with
169
+ `mappingApplied: "obfuscated"` and `qualityFlags` including
170
+ `"dependency-mapping-unverified"`, and the names are the JAR's compiled names.
171
+ A request that asks for `"obfuscated"` outright is a separate branch: it passes
172
+ through as `mappingApplied: "obfuscated"` with `"source-backed"` or
173
+ `"decompiled"`, never with `"dependency-mapping-unverified"`.
174
+ For repeated lookups against the same dependency, call `resolve-artifact` once
175
+ and reuse the returned `artifactId` via `target: { kind: "artifact", artifactId }`.
169
176
 
170
177
  Workspace detection is memoised in a process-resident `WorkspaceContextCache` (16-entry LRU, 5-minute TTL). The cache is observable through `manage-cache` with `cacheKinds: ["workspace"]`, and individual entries can be invalidated via `selector.projectPath`.
171
178
 
@@ -382,6 +389,7 @@ Because only `issueOrigin` is overridable, the two axes can disagree, and one pa
382
389
  - `get-class-members` — every `target.kind` except `"jar"`, `{ "kind": "artifact", "artifactId": ... }` included. An `artifactId` is an opaque handle: its holder cannot tell from it whether the artifact has a binary jar, and cannot re-resolve it either, since `resolve-artifact` does not accept an `artifactId`. Only `target: { "kind": "jar", ... }`, where the caller named the exact jar, reports `issueOrigin: "code_issue"`.
383
390
  - `batch-class-members` — same rule, applied once to the shared target and repeated on every failed entry. Its target schema has no `"artifact"` kind at all, so only `"jar"` reports `code_issue`.
384
391
  - `inspect-minecraft` `task="class-members"` — same rule once more: only a subject whose artifact reference is `target: { "kind": "jar", ... }` reports `code_issue`. A subject naming a resolved artifact (`{ "type": "resolved-id", "artifactId": ... }`) reports `tool_issue`, matching the equivalent `get-class-members` target: it is the same opaque handle from an earlier resolve, and wrapping it in a subject does not give the caller any way to vet the artifact's binary jar.
392
+ - `verify-mixin-target` — the same rule on its own `target`: the tool reads the target's members from bytecode, so every `target.kind` except `"jar"` reports `tool_issue`. Like `batch-class-members`, its target schema has no `"artifact"` kind at all, so only `"jar"` reports `code_issue`.
385
393
 
386
394
  To check whether an artifact carries a binary jar before retrying, call `manage-cache` with `action: "inspect"`, `selector: { "artifactId": ... }` and `include: ["cacheEntries"]`. The `cacheEntries` block reports that artifact's stored `meta.binaryJarPath` — read from the same row the members lookup reads, and absent when the row has none. The `include` is not optional: `manage-cache` defaults to `detail: "summary"`, which drops `cacheEntries` entirely, so without it the reply never shows the field (`detail: "standard"` or `"full"` opts the block back in as well).
387
395
 
@@ -443,7 +451,7 @@ These environment variables are read once at worker startup and provide rollback
443
451
  | `workspace.detected` | A `gradle.properties`, `settings.gradle{,.kts}`, or `build.gradle{,.kts}` file exists at `subject.projectPath`. | `evidence: ["gradle.properties", ...]` lists the gradle files that were found. | `missing` when no gradle files exist; `error` when the filesystem read itself failed. |
444
452
  | `gradle.readable` | `gradle.properties` can be read and the workspace's gradle build scripts are enumerable. | `propertiesPath` and `buildScripts[]` (relative paths). | `skipped` when `workspace.detected` is not `ok`; `missing` when no gradle files at all; `error` on parse / read failure. |
445
453
  | `loom.cache.found` | A Loom (Fabric / Quilt) cache directory exists under the workspace, `gradleUserHome`, or the process `GRADLE_USER_HOME`. It is independent of `workspace.detected`, so callers can detect a global Loom cache even on non-Gradle workspaces. | `cachePath` of the first matching directory. | `missing` when none of the candidate roots exist; `error` on filesystem failure. |
446
- | `minecraft.artifact.resolved` | A lightweight artifact metadata probe can locate `target: { kind: "version", value: <resolvedVersion> }` against the workspace context. The probe does not decompile Minecraft or rebuild the source index. | `artifactId` and `mappingApplied`. | `skipped` when `workspace.detected` or `gradle.readable` is not `ok`; `error` when the lightweight probe cannot verify the artifact or requested mapping without full resolution (carries `error.code` and `error.detail`). |
454
+ | `minecraft.artifact.resolved` | A lightweight artifact metadata probe can locate `target: { kind: "version", value: <resolvedVersion> }` against the workspace context. The probe does not decompile Minecraft or rebuild the source index. It does read the runtime jar's bytes to derive the reported `artifactId`, which is composed from that digest and the jar's symlink-resolved path; repeat probes of the same jar in one process reuse the digest. | `artifactId` and `mappingApplied`. | `skipped` when `workspace.detected` or `gradle.readable` is not `ok`; `error` when the lightweight probe cannot verify the artifact or requested mapping without full resolution (carries `error.code` and `error.detail`). |
447
455
  | `mixins.validated` | At least one `*.mixins.json` file was discovered AND every per-config validation completed without throwing. | `counts: { ok, partial, invalid }` (validation outcomes from `validate-mixin`). | `error` when any per-config validation threw (still emits `counts`); `skipped` when discovery was empty AND `workspace.detected` / `gradle.readable` blocked; `missing` when discovery returned 0 paths and upstream probes were `ok`. A failed `minecraft.artifact.resolved` does not flip executed validators to `skipped`. |
448
456
  | `accessWideners.validated` | At least one Access Widener file was discovered AND every validation completed without throwing. | `counts: { ok, invalid }`. | Same rules as `mixins.validated`. |
449
457
  | `accessTransformers.validated` | At least one Access Transformer file was discovered AND every validation completed without throwing. | `counts: { ok, invalid }`. | Same rules as `mixins.validated`. |
@@ -481,7 +489,7 @@ MCP resources provide URI-based access to Minecraft data for clients that suppor
481
489
  | `find-mapping` | `mc://mappings/{version}/{sourceMapping}/{targetMapping}/{kind}/{name}` | Look up a **class** mapping between two naming namespaces. The URI carries no `owner`, so use `find-member-mapping` (or the `find-mapping` tool) for field/method lookups. |
482
490
  | `find-member-mapping` | `mc://mappings/{version}/{sourceMapping}/{targetMapping}/{kind}/{owner}/{name}` | Look up a **field or method** mapping, including the `owner` class the member belongs to. For exact method overload resolution use the `find-mapping` tool with a `descriptor`. |
483
491
  | `class-members` | `mc://artifact/{artifactId}/members/{className}` | List constructors, methods, and fields for a class |
484
- | `artifact-metadata` | `mc://artifact/{artifactId}` | Metadata for a previously resolved artifact |
492
+ | `artifact-metadata` | `mc://artifact/{artifactId}` | Metadata for a previously resolved artifact. Its `artifactSignature` field carries the artifact's identity signature: a bare 64-character sha256 hex digest of the identified jar's bytes, or that digest followed by `:decompile`. A client that pattern-matched the older `<mtimeMs>:<size>` shape no longer matches. |
485
493
 
486
494
  `versions-list`, `runtime-metrics`, `find-mapping`, `find-member-mapping`, `class-source-json`, `class-members`, and `artifact-metadata` return structured JSON envelopes on success (`{ result, meta }`) and failure (`{ error, meta }`).
487
495
 
@@ -524,7 +532,7 @@ The supervisor caches the completed `initialize` / `notifications/initialized` p
524
532
  The legacy wire contract is byte-compatible with the pre-migration (SDK v1) server, with these recorded exceptions:
525
533
 
526
534
  - `tools/call` with `arguments` omitted reaches the application validator as `{}`. Three outcome arms: (a) input-free tools succeed (`get-runtime-metrics`); (b) required-field schemas answer per-field `ERR_INVALID_INPUT` (`analyze-mod`); (c) a `{}`-accepting schema whose handler requires input answers that tool's own handler-level ProblemDetails — `json-to-nbt` answers `ERR_NBT_INVALID_TYPED_JSON` (status 400, `isError: true`) instead of the pre-migration validation-layer `ERR_INVALID_INPUT`.
527
- - Legacy `tools/list` entries omit the v1-only `execution: {"taskSupport":"forbidden"}` field (SDK v2 does not emit it); every other advertised field, including the `inputSchema` bytes, is identical to the pre-migration snapshots.
535
+ - Legacy `tools/list` entries omit the v1-only `execution: {"taskSupport":"forbidden"}` field (SDK v2 does not emit it); the migration introduced no other drift, and the `inputSchema` bytes are served verbatim from the frozen pre-migration contracts in `tests/fixtures/premigration/tool-contracts/` (through the generated `src/v1-parity-schemas.ts`, which `tools/list` advertises in preference to the zod shape). Those frozen contracts are deliberately extended when a tool genuinely gains a parameter in a later release — 7.0.0-rc.2 added `projectPath` to four of the flat-`artifactId` tools — so "identical to the pre-migration snapshots" describes the migration, not a promise that a tool's schema never changes across releases.
528
536
  - An unknown or disabled tool is answered immediately at the supervisor, before queueing: the reply bytes are identical to v1, but the reply consumes no queue slot and no worker round-trip, so reply ordering and queue-overflow outcomes can differ from v1 under concurrent load.
529
537
  - The unmatched-resource-URI error keeps the raw JSON-RPC `-32602` code, but its message changed from the pre-migration `MCP error -32602: Resource <uri> not found` to `Resource not found: <uri>`, and the error carries a `data.uri` field (SDK v2 wording).
530
538
  - The `initialize` result advertises `resources: { listChanged: false }` and `tools: { listChanged: false }`, where the pre-migration server advertised `true` for both. The suppression is deliberate and is NOT era-gated — both eras read one `getCapabilities()`, so gating it would leave one process advertising two different contracts; the reasoning is under the modern-era capability paragraph below. Every other byte of the `initialize` result is unchanged.
@@ -616,11 +624,13 @@ Notification variants: a modern-era notification without a valid claim (for exam
616
624
 
617
625
  ### Synthetic terminal responses and retry semantics
618
626
 
619
- Supervisor-synthesized replies (queue overflow, worker restart, `validate-project` timeout, startup failure — shapes in `## Errors` and `## Meta fields`) are FINAL for that request instance: a late worker answer for the same id is discarded, so a client sees exactly one response per id. Retrying with the SAME id after a synthetic terminal reply is legal — the retry re-forwards and clears the finality bookkeeping. Era rejections synthesize no such finality (they answer without forwarding), and dropped notifications receive no response at all. Modern-era synthetic results are decorated like ordinary modern results (`resultType: "complete"` plus the identity `_meta` echo); legacy synthetic results remain byte-compatible with the pre-migration shapes.
627
+ Supervisor-synthesized replies (queue overflow, worker restart, `validate-project` timeout, startup failure — shapes in `## Errors` and `## Meta fields`) are FINAL for that request instance: a late worker answer for the same id is discarded, so a client sees exactly one response per id. A raw JSON-RPC `-32603` whose message begins "MCP supervisor failed to" belongs to the same class: the supervisor emits one when an internal fault stops it from admitting a request, dispatching a queued one, or handing a worker response back to the client, and it is terminal and discards the worker's own later answer in exactly the same way. One case where the supervisor deliberately sends nothing: when its own cleanup after such a fault cannot prove the request was removed, it reports and stands down rather than risk a second reply for one id. Only `validate-project` carries a deadline of its own, so any other request left in that state waits for the worker to exit. Retrying with the SAME id after a synthetic terminal reply is legal — the retry re-forwards and clears the finality bookkeeping. Era rejections synthesize no such finality (they answer without forwarding), and dropped notifications receive no response at all. Modern-era synthetic results are decorated like ordinary modern results (`resultType: "complete"` plus the identity `_meta` echo); legacy synthetic results remain byte-compatible with the pre-migration shapes.
620
628
 
621
629
  ### Framing
622
630
 
623
- Standard framing is newline-delimited JSON-RPC (the MCP stdio standard). `Content-Length` header framing is a LOCAL, NONSTANDARD compatibility extension — it is not part of MCP. The reader auto-detects both and may switch mid-stream; every response, including synthetic replies and replies released after a worker restart, uses the framing of the request it answers. `MCP_MAX_FRAME_BYTES` bounds accepted frames (see `## Environment Variables`).
631
+ Standard framing is newline-delimited JSON-RPC (the MCP stdio standard). `Content-Length` header framing is a LOCAL, NONSTANDARD compatibility extension — it is not part of MCP. The reader auto-detects both and may switch mid-stream; every response, including synthetic replies and replies released after a worker restart, uses the framing of the request it answers. `MCP_MAX_FRAME_BYTES` bounds accepted frames (see `## Environment Variables`), and a header block is capped at 8 KiB.
632
+
633
+ A `Content-Length` header block ends at its FIRST EMPTY LINE. A header line ends at an LF, and a CR immediately before that LF belongs to the terminator, so all four combinations a peer can write — `\r\n\r\n`, `\n\n`, `\n\r\n` and `\r\n\n` — delimit the block, and no `\r\n\r\n` sequence inside the JSON body can be mistaken for one. An EXTRA empty line after that terminator is body, not header: the peer's declared `Content-Length` does not count it, so the body window opens two or three bytes early and normally runs off the end of the JSON. That is a body-parse failure, which is framing-fatal — the session ends and the frames behind it are lost. Such a frame is malformed, but the outcome is worth knowing: it is not recovered from. It is not certain to fail, either. The window is shifted rather than mis-sized, so a declared length that happens to count trailing whitespace can absorb the shift exactly and the frame is delivered normally; the reader never detects the extra blank line, only where the bytes fall.
624
634
 
625
635
  ### Adopted-policy note
626
636
 
@@ -708,7 +718,9 @@ Path-based overrides treat blank values and the literal strings `undefined` and
708
718
  | `MCP_MAPPING_SOURCE_PRIORITY` | `loom-first` | Mapping source priority (`loom-first` or `maven-first`) |
709
719
  | `MCP_VERSION_MANIFEST_URL` | Mojang manifest URL | Override the Minecraft version manifest endpoint |
710
720
 
711
- Jars downloaded from the repositories in `MCP_SOURCE_REPOS` are cached under `MCP_CACHE_DIR` and identified by a sha256 of their bytes, so the same jar keeps one `artifactId` no matter which repository served it. (Minecraft version jars and mapping archives share that cache directory but are identified separately.) Whether a cached jar is re-checked depends on the coordinate's **version**, not on which repository serves it. A release version is served from cache with no request. A version ending in `-SNAPSHOT` is treated as mutable, as Maven defines it, and is re-checked against every configured repository including the defaults: where the cached copy recorded an `ETag` or `Last-Modified` the check is a conditional request and an unchanged answer costs no transfer, otherwise the jar is transferred again. A republished jar replaces the cached one and yields a new `artifactId`. If the check cannot be completed anything short of a definitive answer, including unreachable, a timeout, 5xx, 429, or an authentication failure the cached copy is still served; if the repository answers definitively about the artifact itself (404 or 410 gone, 403 refused) it is treated as absent there and resolution moves on to the next repository. Note that `MCP_SOURCE_REPOS` replaces the default list rather than adding to it.
721
+ Jars downloaded from the repositories in `MCP_SOURCE_REPOS` are cached under `MCP_CACHE_DIR` and identified by a sha256 of their bytes, so the same jar keeps one `artifactId` no matter which repository served it. (Minecraft version jars and mapping archives share that cache directory but sit outside this URL-keyed download-cache identity; a Minecraft version jar's own `artifactId` is covered by the jar rule below.) Jars served from the local stores `MCP_LOCAL_M2` and the Gradle dependency cache are identified from their bytes the same way whenever they are reached through a Maven coordinate (`target.kind="dependency"` and `target.kind="coordinate"`), so touching one, or restoring byte-identical contents, keeps its `artifactId`. A jar named directly by path (`target.kind="jar"`) is identified from its bytes as well, and so is a Minecraft runtime jar, because `target.kind="version"` and `target.kind="workspace"` both reach one through that same route. The digest is taken over whichever jar the id is actually about: the named jar's own bytes; or, when the named jar carries no Java sources and its same-name `-sources.jar` sibling supplies them, that sibling's bytes; or, when neither carries sources and the jar goes to the decompiler, the binary jar's bytes under a separate `decompile` identity. The jar's symlink-resolved path still enters the id beside the digest, so one file reached through two names is one artifact, while two byte-identical jars at two real paths stay two artifacts. One bound on "identified from its bytes": within a single server process the digest for a path is reused while that file's size, modification time, inode and inode-change time all still match, so on a filesystem that does not advance the inode-change time on a write, an in-place replacement of the same length whose modification time was restored can be answered from the earlier digest until that memo entry is evicted.
722
+
723
+ Whether a cached jar is re-checked depends on the coordinate's **version**, not on which repository serves it. A release version is served from cache with no request. A version ending in `-SNAPSHOT` is treated as mutable, as Maven defines it, and is re-checked against every configured repository — including the defaults: where the cached copy recorded an `ETag` or `Last-Modified` the check is a conditional request and an unchanged answer costs no transfer, otherwise the jar is transferred again. A republished jar replaces the cached one and yields a new `artifactId`. If the check cannot be completed — anything short of a definitive answer, including unreachable, a timeout, 5xx, 429, or an authentication failure — the cached copy is still served; if the repository answers definitively about the artifact itself (404 or 410 gone, 403 refused) it is treated as absent there and resolution moves on to the next repository. A definitive answer is also remembered for about five minutes, so a repeat resolve of the same target inside that window is answered from the record instead of asking that repository again. The record is bounded in every direction: only 403, 404 and 410 are eligible, so a timeout, a 5xx, a 429, an authentication failure and a network error are all retried on the next call; a `-SNAPSHOT` coordinate is never recorded; the records are held per cache directory; and they live in memory only, so restarting the server clears them and an artifact published after its rejection was recorded becomes reachable again as soon as the record expires. Note that `MCP_SOURCE_REPOS` replaces the default list rather than adding to it.
712
724
 
713
725
  ### Search, Index, and Cache Tuning
714
726
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhisang/minecraft-modding-mcp",
3
- "version": "7.0.0-rc.2",
3
+ "version": "7.0.0",
4
4
  "description": "MCP server for AI-assisted Minecraft modding: inspect decompiled source, resolve Mojang/Yarn/Intermediary mappings, diff versions, analyze Fabric/Forge/NeoForge mod JARs, and validate Mixin, Access Widener, and Access Transformer files.",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@10.30.1",