@adhisang/minecraft-modding-mcp 7.1.0 → 7.1.1

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 (58) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/README.md +1 -1
  3. package/dist/access-transformer-parser.d.ts +8 -0
  4. package/dist/access-transformer-parser.js +8 -1
  5. package/dist/access-widener-parser.d.ts +17 -0
  6. package/dist/access-widener-parser.js +12 -1
  7. package/dist/cache-registry.js +19 -6
  8. package/dist/entry-tools/inspect-minecraft/handlers/versions.js +15 -9
  9. package/dist/entry-tools/manage-cache-service.js +10 -14
  10. package/dist/entry-tools/validate-project/cases/access-transformer.js +22 -3
  11. package/dist/entry-tools/validate-project/cases/access-widener.js +31 -4
  12. package/dist/entry-tools/validate-project/cases/mixin.js +11 -3
  13. package/dist/entry-tools/validate-project/cases/project-summary.js +24 -5
  14. package/dist/entry-tools/verify-mixin-target-service.js +19 -8
  15. package/dist/index.js +1 -0
  16. package/dist/java-process.d.ts +1 -0
  17. package/dist/java-process.js +14 -0
  18. package/dist/mapping/lookup.js +16 -1
  19. package/dist/mapping-service.d.ts +14 -0
  20. package/dist/mapping-service.js +35 -15
  21. package/dist/minecraft-explorer-service.js +70 -8
  22. package/dist/mixin/access-validators.js +38 -2
  23. package/dist/mixin/annotation-validators.js +137 -43
  24. package/dist/mixin/parsed-validator.js +21 -7
  25. package/dist/mixin-parser.d.ts +52 -0
  26. package/dist/mixin-parser.js +709 -130
  27. package/dist/mod-decompile-service.js +11 -1
  28. package/dist/mod-remap-service.js +6 -6
  29. package/dist/nbt/java-nbt-codec.js +7 -1
  30. package/dist/source/access-validate.js +10 -0
  31. package/dist/source/artifact-resolver.d.ts +13 -3
  32. package/dist/source/artifact-resolver.js +129 -18
  33. package/dist/source/class-source.js +13 -2
  34. package/dist/source/nested-jars.d.ts +15 -1
  35. package/dist/source/nested-jars.js +14 -5
  36. package/dist/source/search.d.ts +10 -2
  37. package/dist/source/search.js +60 -13
  38. package/dist/source/validate-mixin/pipeline/mapping-health.js +20 -1
  39. package/dist/source/validate-mixin/pipeline/target-lookup.js +16 -7
  40. package/dist/source/validate-mixin.d.ts +5 -0
  41. package/dist/source/validate-mixin.js +136 -21
  42. package/dist/source/workspace-target.js +75 -7
  43. package/dist/source-jar-reader.d.ts +15 -1
  44. package/dist/source-jar-reader.js +35 -3
  45. package/dist/source-resolver.js +2 -9
  46. package/dist/stdio-supervisor.d.ts +35 -1
  47. package/dist/stdio-supervisor.js +77 -2
  48. package/dist/storage/files-repo.d.ts +7 -0
  49. package/dist/storage/files-repo.js +17 -4
  50. package/dist/tool-contract-manifest.js +2 -2
  51. package/dist/tool-execution-gate.js +2 -1
  52. package/dist/version-service.js +7 -0
  53. package/dist/workspace-context-cache.d.ts +25 -0
  54. package/dist/workspace-context-cache.js +52 -2
  55. package/dist/workspace-mapping-service.js +116 -14
  56. package/docs/README-ja.md +1 -1
  57. package/docs/tool-reference.md +14 -11
  58. package/package.json +1 -1
@@ -40,6 +40,7 @@ Start here when you are not sure which tool to reach for. In every row, the left
40
40
  - `ERR_CLASS_NOT_FOUND` errors from the class tools carry a top-level `didYouMean` array (parallel to `suggestedCall`) with ranked near-miss candidates from the artifact's symbol index: each entry is `{ className, matchReason }` where `matchReason` is `"exact-simple-name"` (same simple name in another package — the moved-class case, ranked first), `"case-insensitive"`, or `"edit-distance:N"`. Candidates come from the symbol index of the artifact the caller REQUESTED first, followed by any the artifact the lookup ended on contributes, deduplicated by FQN. A candidate found outside the requested artifact carries an extra `artifactId` naming where it was found; an entry WITHOUT that field is always from the artifact the caller asked about. The union matters because the requested artifact is, in the partial-source fallback scenario, the one with no `net.minecraft` symbols — which is why the fallback fired — so collecting from it alone returns `[]` in exactly the case the fallback exists to serve. Candidates are hints, never assertions that the class exists at the suggested location; the array is empty when neither index has anything usable. All same-simple-name FQNs are enumerated rather than collapsed. When an internal redirect resolved a different artifact while trying to answer the call — the binary fallback, or the nested-jar redirect that follows a shell jar's bundled inner jar — the error carries `details.fallbackArtifactId` naming it, alongside `details.binaryFallbackAttempted` for the binary case. `details.artifactId`, `details.mapping` and `details.qualityFlags` all describe the REQUESTED artifact, so identity, namespace and quality never disagree about which artifact is being reported. `fallbackArtifactId` and `binaryFallbackAttempted` are internal `AppError.details` fields for diagnosis and are NOT published on the wire; the envelope reports the requested artifact as `error.context.artifactId`.
41
41
  - `find-class` searches the nested `.class` inventories of Jar-in-Jar shell artifacts such as the Fabric API umbrella JAR. It accepts simple or qualified names, returns dotted names for inner classes, deduplicates a class bundled more than once, and honors `limit`. The returned source path is inferred from the outer class. For top-level matches, `get-class-source` and `get-class-members` resolve the actual containing nested JAR before reading content; dotted inner-class matches are also readable through `get-class-source`.
42
42
  - Source-oriented tools expose `artifactContents` so callers can tell whether the backing artifact is a `source-jar` or a `decompiled-binary`. `get-class-source`, `get-class-members`, `search-class-source`, and `get-artifact-file` also expose `returnedNamespace`.
43
+ - Known issue: `get-class-source` with `mode: "metadata"` returns a generated outline of the class's symbols in `sourceText`, not a slice of its source lines, yet reports `returnedRange: { start: 1, end: totalLines }` and, unless `maxChars` cuts the outline, `truncated: false`. Those values describe the whole source file the outline was built from, not the outline text, so do not use them to address lines of `sourceText`; use `mode: "snippet"` or `"full"` when you need line numbers. They are kept for now because changing a response field is a breaking change under this project's semantic-versioning policy; a future major release will replace them with fields specific to the metadata mode, and the CHANGELOG will announce it as a breaking change.
43
44
  - When `mapping` is omitted, `get-class-source`, `get-class-members`, and `batch-class-members` all inherit the mapping the target artifact was resolved with, and report it as `returnedNamespace`. The `mapping` parameter's advertised schema description still says the default is `obfuscated`; that wording is pinned by the frozen legacy `inputSchema` bytes and is accurate only for targets that carry no resolved mapping of their own. An explicit `mapping` is never overridden.
44
45
  - Cache-backed source, mapping, validation, batch, and workflow tools accept `gradleUserHome?: string` when they need Loom cache data. Use it for builds that used an isolated `GRADLE_USER_HOME`; the server searches `<gradleUserHome>/loom-cache` and `<gradleUserHome>/caches/fabric-loom` before the MCP process default. The value selects a Gradle User Home, not arbitrary Loom cache roots.
45
46
  - `resolve-artifact` adds the `binary-jar-no-classes` quality flag when the binary jar it accepted opens cleanly but holds no `.class` entry — a `yarn`/`intermediary` `v2` mapping jar, a resource-only mod jar, or a jar of nothing but directory entries. The flag describes such an artifact and never refuses it, so an empty decompile can be told apart from a decompiler fault. It is set for `target.kind="coordinate"`, `"jar"` and `"version"`. A class-free jar that is not a Jar-in-Jar shell still fails during indexing with `ERR_DECOMPILER_FAILED`, so the flag does not reach the response in that case.
@@ -174,7 +175,7 @@ through as `mappingApplied: "obfuscated"` with `"source-backed"` or
174
175
  For repeated lookups against the same dependency, call `resolve-artifact` once
175
176
  and reuse the returned `artifactId` via `target: { kind: "artifact", artifactId }`.
176
177
 
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`.
178
+ Workspace detection is memoised in a process-resident `WorkspaceContextCache` (16-entry LRU, 5-minute TTL). A cache hit also re-stats the project files the detection read: unconditionally, the project root's `gradle.properties`, `build.gradle`, and `build.gradle.kts` (present or not, stat taken before detection runs so even an edit racing the read is caught); plus any subproject `build.gradle`/`build.gradle.kts` or mod descriptor (`fabric.mod.json`, `quilt.mod.json`, `META-INF/mods.toml`, `META-INF/neoforge.mods.toml`) that actually contributed a compile-mapping or loader declaration (stat taken after detection returns, since which files those are is not known beforehand - an edit landing in the narrow window between detection reading such a file and this later stat is not guaranteed to be caught immediately, though the next read() re-stats it again). If a covered file was edited, created, or deleted since the entry was written, the hit is discarded and the workspace is re-detected, even within the TTL. Two things are NOT covered by any of this: a subproject build script or descriptor that was scanned but declared nothing (editing one to newly declare a mapping/loader is only picked up once the 5-minute TTL expires and the workspace is re-detected from scratch); and `settings.gradle(.kts)`/`libs.versions.toml`, which detection does not read at all, at any point - editing either changes nothing detection reports, regardless of caching or TTL. The cache is observable through `manage-cache` with `cacheKinds: ["workspace"]`, and individual entries can still be invalidated manually via `selector.projectPath`.
178
179
 
179
180
  `target.kind="dependency"` resolution probes up to six de-duplicated `gradle.properties` keys in order — `name_version`, `snake_case(name)_version`, `camelCaseVersion`, `lastSegment(group)_name_version`, `snake_case(lastSegment(group)_name)_version`, and `camelCase(lastSegment(group)_name)Version`. Hyphens become underscores in the snake_case forms (`fabric-api` probes `fabric_api_version`); for hyphen-less names the snake_case forms deduplicate into the raw keys, leaving four. The probe falls back to the modules-2 cache layout `~/.gradle/caches/modules-2/files-2.1/<group>/<name>/`. `group` and `name` are held to the coordinate route's identifier rule (`[A-Za-z0-9._+-]`, no leading `.`, no `..`) **before** either probe runs, so no directory is listed on behalf of a coordinate that would be refused later — a blocklist of `/`, `\`, `..` and NUL was not enough, since `group="D:"` with `name="."` carries none of them and is drive-relative on Windows. Version tokens that contain path separators, `..`, NUL, control characters, or any character outside `[A-Za-z0-9._+-]` and the space are rejected; in `gradle.properties` the rejection is recorded under `attempts[]` as `gradle.properties:<key>:rejected-unsafe-version` and the next key is tried. An explicit `target.version` is trimmed before it is checked, matching the coordinate route. Snapshot and dev directories are excluded by default. The modules-2 fallback resolves directly when exactly one valid entry remains. When several entries remain and the dependency is a submodule of an umbrella package (artifact name differs from the group's last segment, e.g. `net.fabricmc.fabric-api:fabric-screen-handler-api-v1`), the declared umbrella version property (`fabric_api_version` / `fabricApiVersion`) locates the cached umbrella POM and the submodule adopts the version that POM names — the resolving response records `provenance.submoduleVersionSource: "umbrella-pom"` with the POM path in `provenance.source` (workspace-context-cache hits within the TTL return the cached version with `provenance.source: "workspace-context-cache"` instead). Umbrella properties are never adopted verbatim as a submodule's version. Every remaining ambiguity raises `ERR_DEPENDENCY_VERSION_UNRESOLVED` with `candidatesSeen` so a global cache cannot supply a version the workspace did not declare.
180
181
 
@@ -192,6 +193,7 @@ Workspace detection is memoised in a process-resident `WorkspaceContextCache` (1
192
193
  - `list-artifact-files` indexes Java source paths only — `assets/` and `data/` prefixes list nothing. Text files under those prefixes ARE retrievable by exact path with `get-artifact-file`: when the index has no row, the tool reads the entry directly from the backing jar (`deliveryMode: "jar-read-through"` marks such responses). This works for any artifact with a backing jar — vanilla client jars and mod jars alike; for Jar-in-Jar shells it reads the shell's own entries only (resolve a nested jar as its own artifact to reach its resources). Read-through delivery is text-only with a 512 KiB per-file cap (`truncated: true` beyond it); binary entries (e.g. `.png`, `.ogg`) answer with size metadata plus `contentOmittedReason` instead of content. Traversal-shaped paths (`..`, absolute) are rejected with `ERR_INVALID_INPUT`, and a miss returns `ERR_FILE_NOT_FOUND` with `nearbyPaths` naming same-named entries elsewhere in the jar (directory layouts move between versions, e.g. `assets/minecraft/models/item/*` → `assets/minecraft/items/*`).
193
194
  - `search-class-source` defaults to `queryMode="auto"`. Use `queryMode="literal"` for explicit substring scans. `match="regex"` enforces `query.length <= 200` and caps results at `100`.
194
195
  - `search-class-source` returns compact hits only. Use `get-artifact-file` or `get-class-source` to inspect returned files.
196
+ - Indexed `intent="text"` and `intent="path"` searches (any `match` except `"regex"`; for text, any `queryMode` except `"literal"`) read one bounded candidate window from the index in index-relevance order and verify only those candidates: 500 for `match="exact"`/`"prefix"`, and five times the maximum hit count (500 to 5,000; 1,000 by default) for `"contains"`. `scope.packagePrefix`, or the literal directory prefix before the first `*`/`?` of `scope.fileGlob`, narrows that window inside the index, so in-scope matches are found even when many out-of-scope files also match. A glob without a literal prefix, such as `**/*Entity.java`, is applied only to the candidates already in the window, so its hits stay limited to that window. When the index matched more candidates than the window holds and the returned page is shorter than `limit`, a `warnings` entry says the search examined only its candidate window and results may be incomplete; a page that returns `limit` hits carries no such warning even when the window was full. To search past the window, use `queryMode="literal"` with `intent="text"` (a full substring scan, still bounded by the scan byte budget; a `warnings` entry reports when that budget is reached), or `match="regex"` for paths.
195
197
  - On legacy (1.x) versions, `find-class` and `get-class-source` on `mapping="obfuscated"` expect Mojang obfuscated names. Deobfuscated queries warn and usually need `mapping="mojang"` or a `find-mapping` step first. On `26.1+` the `obfuscated` names already are Mojang names, so no mapping step applies; a miss points at near-miss class names instead (see [Lookup Rules](#lookup-rules)).
196
198
  - `get-class-members` exposes `annotationDefault` on annotation-type members (the `default` value of an `@interface` member) whenever the classfile carries it, and accepts `includeAnnotations: true` to additionally list runtime-visible member annotations such as `@java.lang.Deprecated` per member. The leaner `names`/`signatures` projections drop annotation fields. `analyze-mod` `task="members"` includes both without a flag.
197
199
  - On unobfuscated versions, `find-mapping`, `resolve-method-mapping-exact`, and `check-symbol-exists` report two structured `mappingContext` flags instead of per-response warning sentences (`get-class-api-matrix` reports a top-level `unobfuscatedRuntime: true` — its non-mojang columns are empty by design there): `unobfuscatedRuntime: true` replaces "Version X is unobfuscated; mapping graph is empty because the runtime already uses deobfuscated names.", and `runtimeValidated: true` replaces "Version X is unobfuscated; validated symbol existence against runtime bytecode." Do not pattern-match the old sentences.
@@ -243,14 +245,15 @@ Output shape:
243
245
 
244
246
  | # | `member.kind` | target visibility | `mixinMemberName` regex | `suggestedAnnotation` |
245
247
  |---|---|---|---|---|
246
- | 1 | any | `public` / `protected` | (any) | `"@Inject-only"` (target already visible to mixin) |
247
- | 2 | `field` | `private` | `^get[A-Z]\w*$` / `^set[A-Z]\w*$` / `^is[A-Z]\w*$` | `"@Accessor"` (target field name inferred via prefix removal) |
248
- | 3 | `field` | `private` | (anything else, including absent) | `"@Shadow"` (or `"@Shadow @Final"` when target is `final`) |
249
- | 4 | `method` | `private` | `^invoke[A-Z]\w*$` / `^call[A-Z]\w*$` | `"@Invoker"` |
250
- | 5 | `method` | `private` | any other non-empty value | `"@Shadow"` |
251
- | 6 | `method` | `private` | (absent) | `null` + `candidates: [@Shadow, @Invoker]` |
248
+ | 1 | `field` | `public` / `protected` | (any) | `"@Shadow"` (no `@Accessor` needed for outside access, but the mixin still needs `@Shadow` to reference the field) |
249
+ | 2 | `method` | `public` / `protected` | (any) | `"@Inject-only"` (target already visible to mixin; no `@Invoker` needed) |
250
+ | 3 | `field` | `private` / `package-private` | `^get[A-Z]\w*$` / `^set[A-Z]\w*$` / `^is[A-Z]\w*$` | `"@Accessor"` (target field name inferred via prefix removal) |
251
+ | 4 | `field` | `private` / `package-private` | (anything else, including absent) | `"@Shadow"` (or `"@Shadow @Final"` when target is `final`) |
252
+ | 5 | `method` | `private` / `package-private` | `^invoke[A-Z]\w*$` / `^call[A-Z]\w*$` | `"@Invoker"` |
253
+ | 6 | `method` | `private` / `package-private` | any other non-empty value | `"@Shadow"` |
254
+ | 7 | `method` | `private` / `package-private` | (absent) | `null` + `candidates: [@Shadow, @Invoker]` |
252
255
 
253
- `"@Inject-only"` is a pseudo-tag, NOT a real Mixin annotation. It signals "no `@Shadow` is needed because the target is already accessible to the mixin"; the caller can use `@Inject` (or a direct method call) without declaring a shadow. `accessorAdvice.exampleSnippet` is a deterministic Java fragment built from the recommendation; the tool does NOT compile-check the snippet — run a Gradle build before committing.
256
+ `"@Inject-only"` is a pseudo-tag, NOT a real Mixin annotation, and only applies to visible (`public`/`protected`) **methods**. It signals "no `@Invoker` is needed because the target method is already accessible to the mixin"; use `@Inject` to hook it, or `@Shadow` to call it directly from mixin code. A visible **field** gets a concrete `"@Shadow"` recommendation instead — no `@Accessor` is needed to read/write it from outside, but mixin code still needs `@Shadow` to reference it. `accessorAdvice.exampleSnippet` is a deterministic Java fragment built from the recommendation; the tool does NOT compile-check the snippet — run a Gradle build before committing.
254
257
 
255
258
  Errors:
256
259
 
@@ -388,8 +391,8 @@ Rejections name the offending node twice: `error.fieldErrors[0].path` is the RFC
388
391
  - `ERR_TOOL_TIMEOUT` — Synthetic `CallToolResult` produced only for `validate-project` when its supervisor-owned end-to-end deadline expires. The default is 120,000 ms and includes supervisor queue time. `meta.timeout.phase` is `"queue"` when the call expired before dispatch and `"running"` after dispatch. A queue timeout does not restart the worker; a running timeout isolates the worker process tree, initiates replacement, and sets `meta.timeout.workerRestartInitiated: true`. Cancellation suppresses the timeout result while retaining the deadline for worker cleanup.
389
392
  - `ERR_MIXIN_PARSE_FAILED` — Reserved code for `validate-mixin` parse-stage failures in single / inline mode. Today the runtime parser is permissive (no hard parse failure path), but emitting this code keeps the contract stable for future strict-parse modes.
390
393
  - `ERR_STAGE_BUDGET_PRE_PARSE` — Raised when a pre-parse stage (`resolve`, `mapping-health`, or `parse` itself) exhausts its independent soft-deadline before validation can proceed. The error carries `failedStage: <stage name>`, `meta.stageBudgetExhausted: true`, and the `budgetMs` / `elapsedMs` for the offending stage. Recovery: shrink `mixinConfigPath` (e.g. validate one config file at a time) or set `MIXIN_STAGE_BUDGETS_OFF=1` to disable budgets for diagnostic reruns.
391
- - `ERR_WORKSPACE_VERSION_UNRESOLVED` — Raised by `resolve-artifact`, `get-class-source`, `get-class-members`, and `inspect-minecraft` (workspace subject, every artifact-context task) when `target.kind="workspace"` cannot detect a Minecraft version from `gradle.properties`. Both `strict: true` and `strict: false` raise; `details.strict` records which value was supplied. The error carries `details.projectPath` and a `suggestedCall` with `target: { kind: "version", value: "<your-mc-version>" }` so the caller can fall back to an explicit version target.
392
- - `ERR_DEPENDENCY_VERSION_UNRESOLVED` — Raised by the same three tools when `target.kind="dependency"` cannot infer a version from `gradle.properties` or `~/.gradle/caches/modules-2/files-2.1/<group>/<name>/`. Multiple non-snapshot entries in modules-2 also raise this error: the synthesizer refuses to pick without project-specific evidence, sets `details.ambiguous=true`, and lists the cached versions in `details.candidatesSeen`. The error carries `details.attempts` (the gradle.properties keys that were probed) plus a `suggestedCall` with `target: { kind: "dependency", group, name, version: "<your-version>" }`.
394
+ - `ERR_WORKSPACE_VERSION_UNRESOLVED` — Raised by `resolve-artifact`, `get-class-source`, `get-class-members`, and `inspect-minecraft` (workspace subject, every artifact-context task) when `target.kind="workspace"` cannot detect a Minecraft version from `gradle.properties`. Both `strict: true` and `strict: false` raise; `details.strict` records which value was supplied. The error carries `details.projectPath` and `exampleCalls` with `target: { kind: "version", value: "<your-mc-version>" }` so the caller can fall back to an explicit version target.
395
+ - `ERR_DEPENDENCY_VERSION_UNRESOLVED` — Raised by `resolve-artifact`, `get-class-source`, and `get-class-members` (not `inspect-minecraft`, whose target schema has no `"dependency"` kind) when `target.kind="dependency"` cannot infer a version from `gradle.properties` or `~/.gradle/caches/modules-2/files-2.1/<group>/<name>/`. Multiple non-snapshot entries in modules-2 also raise this error: the synthesizer refuses to pick without project-specific evidence, sets `details.ambiguous=true`, and lists the cached versions in `details.candidatesSeen`. The error carries `details.attempts` (the gradle.properties keys that were probed) plus `exampleCalls` with `target: { kind: "dependency", group, name, version: "<your-version>" }`.
393
396
 
394
397
  ### Error classification: `retryClass` and `issueOrigin`
395
398
 
@@ -424,7 +427,7 @@ The optional `error.exampleCalls?: Array<{ tool: string; params: Record<string,
424
427
  - `meta.restart` (synthetic worker-restart responses only) — emitted exclusively when the supervisor returns an `ERR_WORKER_RESTART` envelope. Shape: `{ tool, durationMs, lastStage, lastStageElapsedMs, lastStageMeta, exit: { code, signal }, retryRecommendation }`. `retryRecommendation` is one of `"narrow-query" | "clear-cache" | "report-bug" | "same-request"`, decided by the supervisor from `lastStage`, `exit.signal`, and the recent restart cadence (3 restarts within 60 s of the same tool → `"report-bug"`).
425
428
  - `meta.timeout` (`ERR_TOOL_TIMEOUT` only) — exact shape: `{ tool: "validate-project", phase: "queue" | "running", durationMs, deadlineMs, lastStage, lastStageElapsedMs, lastStageMeta, redactedToolArgs, redactedToolArgsModified, retryRecommendation, workerRestartInitiated }`. Stage and argument diagnostics use the supervisor's bounded redaction rules. `workerRestartInitiated` reports initiation only; it does not claim that replacement startup or initialization replay completed.
426
429
  - `meta.queue` (supervisor queue overflow only) — `{ reason: "supervisor-request-queue", maxQueued: 2, queuedCount: 2 }`. The FIFO retains at most two total worker-bound requests; a queued `validate-project` barrier consumes one slot, while a running barrier is outside the FIFO. Overflowing `tools/call` receives a synthetic `ERR_LIMIT_EXCEEDED` result. An overflowing non-tool request receives raw JSON-RPC `-32000` with message `"MCP supervisor request queue is full."`; notifications do not consume slots.
427
- - Replacement startup/replay uses a 10–30 second internal watchdog and bounded retry backoff. Successful replay releases queued work. Spawn, pre-ready, replay, or watchdog failure terminalizes queued requests with the existing worker-restart response shapes. At the two-slot live-generation cap, new worker-bound requests, including `initialize`, fail immediately instead of entering the FIFO; notifications that cannot be delivered are emitted as structured supervisor warnings and dropped. Tree-cleanup helper attempts are bounded to five seconds so recovery and shutdown cannot wait forever on `taskkill` or an equivalent platform operation. On POSIX, `ESRCH` while signaling the saved process group means the group is already gone and is treated as completed cleanup.
430
+ - Replacement startup/replay uses a 10–30 second internal watchdog and bounded retry backoff. Successful replay releases queued work. Spawn, pre-ready, replay, or watchdog failure terminalizes queued requests with the existing worker-restart response shapes. At the two-slot live-generation cap, new worker-bound requests, including `initialize`, fail immediately instead of entering the FIFO; notifications that cannot be delivered are emitted as structured supervisor warnings and dropped. Tree-cleanup helper attempts are bounded to five seconds so recovery and shutdown cannot wait forever on `taskkill` or an equivalent platform operation. On POSIX, `ESRCH` while signaling the saved process group means the group is already gone and is treated as completed cleanup. When a worker exits on its own (a crash or an external kill), the supervisor also signals that worker's process group on POSIX, so descendants such as a Java process are not left running; this does not delay the replacement. On Windows, descendants of a worker that exited on its own are not reaped, because `taskkill /T` cannot walk a tree whose root has already exited.
428
431
  - `meta.stageBudgetExhausted` (`ERR_STAGE_BUDGET_PRE_PARSE` errors only) — set to `true` to flag that the failure is budget-driven rather than a genuine resolve / mapping-health / parse error. Pair with `failedStage` and `error.detail` (`"Stage <name> exhausted budget before parse completed."`) for diagnostics. The companion fields `meta.budgetMs` (the stage budget that was exceeded) and `meta.elapsedMs` (the actual stage elapsed time) are emitted alongside it on the same envelope so callers can decide between retry, input-shrink (`mixinConfigPath`), or `MIXIN_STAGE_BUDGETS_OFF=1` rollback without parsing message text.
429
432
  - `validate-mixin` batch-mode entries (`results[i]`, when invoked with `input.mode = "paths" | "config" | "project"`) preserve typed error metadata when an entry fails: optional `errorCode` (e.g. `"ERR_STAGE_BUDGET_PRE_PARSE"`) and `errorDetails` (the `failedStage` / `stageBudgetExhausted` / `budgetMs` / `elapsedMs` shape from the underlying AppError) sit alongside the legacy `error` string. The shape is additive — callers that only read `error` continue to see the same human-readable message.
430
433
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhisang/minecraft-modding-mcp",
3
- "version": "7.1.0",
3
+ "version": "7.1.1",
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",