@adhisang/minecraft-modding-mcp 4.2.1 → 5.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.
Files changed (114) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +5 -4
  3. package/dist/access-transformer-parser.d.ts +2 -1
  4. package/dist/access-transformer-parser.js +10 -1
  5. package/dist/build-suggested-call.js +31 -10
  6. package/dist/cache-registry.d.ts +8 -0
  7. package/dist/cache-registry.js +15 -1
  8. package/dist/config.d.ts +1 -0
  9. package/dist/config.js +4 -0
  10. package/dist/entry-tools/analyze-mod-service.d.ts +24 -24
  11. package/dist/entry-tools/analyze-symbol-service.d.ts +57 -22
  12. package/dist/entry-tools/analyze-symbol-service.js +124 -28
  13. package/dist/entry-tools/batch-class-members-service.d.ts +3 -1
  14. package/dist/entry-tools/batch-class-members-service.js +6 -6
  15. package/dist/entry-tools/batch-class-source-service.d.ts +3 -1
  16. package/dist/entry-tools/batch-class-source-service.js +5 -6
  17. package/dist/entry-tools/batch-mappings-service.d.ts +3 -1
  18. package/dist/entry-tools/batch-mappings-service.js +4 -5
  19. package/dist/entry-tools/batch-symbol-exists-service.d.ts +3 -1
  20. package/dist/entry-tools/batch-symbol-exists-service.js +4 -5
  21. package/dist/entry-tools/compare-minecraft-service.d.ts +10 -10
  22. package/dist/entry-tools/entry-tool-schema.d.ts +1 -1
  23. package/dist/entry-tools/inspect-minecraft/handlers/artifact.js +1 -3
  24. package/dist/entry-tools/inspect-minecraft/handlers/class-members.js +2 -2
  25. package/dist/entry-tools/inspect-minecraft/handlers/class-overview.js +0 -5
  26. package/dist/entry-tools/inspect-minecraft/handlers/class-source.js +0 -1
  27. package/dist/entry-tools/inspect-minecraft/handlers/file.js +0 -1
  28. package/dist/entry-tools/inspect-minecraft/handlers/list-files.js +0 -1
  29. package/dist/entry-tools/inspect-minecraft/handlers/search.d.ts +8 -0
  30. package/dist/entry-tools/inspect-minecraft/handlers/search.js +13 -2
  31. package/dist/entry-tools/inspect-minecraft/handlers/versions.d.ts +1 -1
  32. package/dist/entry-tools/inspect-minecraft/handlers/versions.js +1 -1
  33. package/dist/entry-tools/inspect-minecraft/internal.d.ts +90 -89
  34. package/dist/entry-tools/inspect-minecraft/internal.js +30 -14
  35. package/dist/entry-tools/inspect-minecraft-service.d.ts +218 -218
  36. package/dist/entry-tools/inspect-minecraft-service.js +1 -1
  37. package/dist/entry-tools/manage-cache-service.d.ts +4 -4
  38. package/dist/entry-tools/response-contract.d.ts +1 -1
  39. package/dist/entry-tools/response-contract.js +1 -0
  40. package/dist/entry-tools/validate-project/cases/mixin.js +2 -1
  41. package/dist/entry-tools/validate-project/cases/project-summary.js +5 -3
  42. package/dist/entry-tools/validate-project-service.d.ts +42 -36
  43. package/dist/entry-tools/validate-project-service.js +3 -1
  44. package/dist/entry-tools/verify-mixin-target-service.d.ts +30 -0
  45. package/dist/entry-tools/verify-mixin-target-service.js +113 -22
  46. package/dist/error-mapping.d.ts +51 -0
  47. package/dist/error-mapping.js +157 -2
  48. package/dist/index.js +138 -71
  49. package/dist/mapping/internal-types.d.ts +7 -0
  50. package/dist/mapping/lookup.d.ts +1 -1
  51. package/dist/mapping/lookup.js +7 -1
  52. package/dist/mapping/types.d.ts +6 -0
  53. package/dist/mapping-service.d.ts +26 -2
  54. package/dist/mapping-service.js +178 -36
  55. package/dist/mcp-helpers.d.ts +1 -0
  56. package/dist/mcp-helpers.js +25 -3
  57. package/dist/minecraft-explorer-service.js +24 -11
  58. package/dist/mixin/access-validators.js +33 -1
  59. package/dist/mixin/annotation-validators.js +53 -7
  60. package/dist/mixin/parsed-validator.js +2 -2
  61. package/dist/mixin-parser.js +48 -4
  62. package/dist/mod-decompile-service.js +2 -1
  63. package/dist/mod-remap-service.d.ts +20 -1
  64. package/dist/mod-remap-service.js +95 -40
  65. package/dist/nbt/java-nbt-codec.js +6 -0
  66. package/dist/observability.d.ts +6 -0
  67. package/dist/observability.js +10 -0
  68. package/dist/page-cursor.d.ts +17 -0
  69. package/dist/page-cursor.js +0 -0
  70. package/dist/registry-service.js +25 -1
  71. package/dist/resources.js +45 -9
  72. package/dist/response-utils.d.ts +29 -4
  73. package/dist/response-utils.js +95 -5
  74. package/dist/source/access-validate.js +12 -2
  75. package/dist/source/artifact-resolver.js +1 -1
  76. package/dist/source/class-source/members-builder.d.ts +44 -1
  77. package/dist/source/class-source/members-builder.js +51 -4
  78. package/dist/source/class-source/snippet-builder.d.ts +13 -0
  79. package/dist/source/class-source/snippet-builder.js +48 -4
  80. package/dist/source/class-source-helpers.js +67 -11
  81. package/dist/source/class-source.js +97 -20
  82. package/dist/source/indexer.js +5 -2
  83. package/dist/source/search.d.ts +1 -1
  84. package/dist/source/search.js +94 -22
  85. package/dist/source/validate-mixin.d.ts +2 -1
  86. package/dist/source/validate-mixin.js +45 -4
  87. package/dist/source/workspace-target.js +15 -9
  88. package/dist/source-jar-reader.d.ts +54 -1
  89. package/dist/source-jar-reader.js +121 -10
  90. package/dist/source-service.d.ts +28 -7
  91. package/dist/storage/files-repo.d.ts +12 -0
  92. package/dist/storage/files-repo.js +24 -0
  93. package/dist/storage/symbols-repo.d.ts +6 -0
  94. package/dist/storage/symbols-repo.js +6 -0
  95. package/dist/symbols/symbol-extractor.js +35 -6
  96. package/dist/text-truncate.d.ts +7 -0
  97. package/dist/text-truncate.js +21 -0
  98. package/dist/tiny-remapper-resolver.js +10 -0
  99. package/dist/tool-contract-manifest.js +2 -2
  100. package/dist/tool-execution-gate.js +12 -1
  101. package/dist/tool-guidance.d.ts +4 -1
  102. package/dist/tool-guidance.js +37 -12
  103. package/dist/tool-schemas.d.ts +559 -494
  104. package/dist/tool-schemas.js +99 -63
  105. package/dist/types.d.ts +1 -0
  106. package/dist/version-diff-service.js +5 -0
  107. package/dist/version-service.d.ts +3 -0
  108. package/dist/version-service.js +18 -2
  109. package/dist/warning-details.d.ts +18 -0
  110. package/dist/warning-details.js +63 -0
  111. package/docs/README-ja.md +2 -2
  112. package/docs/examples.md +26 -2
  113. package/docs/tool-reference.md +22 -17
  114. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,39 @@ All notable changes to this project are documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project aims to follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [Unreleased]
9
+
10
+ ## [5.0.0] - 2026-06-04
11
+
12
+ ### Changed
13
+
14
+ - **Breaking:** `get-class-members` now returns at most **150** members per page by default (was 500). The existing `nextCursor` continuation already covers paging; callers that need more in one response can pass an explicit `maxMembers` (still capped at 5000). `maxClassResults` and `maxVersions` defaults are unchanged.
15
+ - **Breaking:** `inspect-minecraft` responses no longer echo the requested subject inside `summary.subject`. The raw requested subject still surfaces exactly once, in the always-on top-level `subject` block as `subject.requested` (paired with `subject.resolved`). Clients reading `summary.subject.requested` must switch to `subject.requested` (or correlate via `requestId`).
16
+ - **Breaking:** `meta.warningDetails[]` entries no longer duplicate the warning text. Each entry now carries `index: number` referencing its string in `meta.warnings[]` (1:1, order-preserving) instead of a `message` field. Clients reading `warningDetails[].message` must switch to `meta.warnings[detail.index]`.
17
+ - **Breaking:** `get-class-source` and `get-class-members` now omit the diagnostic fields `provenance`, `qualityFlags`, and `artifactContents` by default. Pass the new `includeProvenance: true` to include them. (`compact` keeps its existing meaning — strip empty fields, and for `get-class-members` also `context`.) Callers that previously read those fields without setting `compact` must now pass `includeProvenance: true`.
18
+ - **Breaking:** `get-class-members` member objects are slimmed on the wire. `accessFlags` is dropped (the modifiers are already encoded in `javaSignature`), `isSynthetic` is emitted only when `true`, and `ownerFqn` is hoisted to a single block-level `members.ownerFqn` when every member shares one owner (the common, non-`includeInherited` case); it stays per-member when `includeInherited` surfaces multiple owners. `name` and `javaSignature` are unchanged. Read the owner from `members.ownerFqn` (falling back to `member.ownerFqn`), and derive access modifiers from `javaSignature` instead of `accessFlags`. `diff-class-signatures` / `verify-mixin-target` member shapes are unchanged.
19
+ - **Breaking:** `get-class-members` omits `jvmDescriptor` on **field** members by default (the type is already in `javaSignature`). Method/constructor `jvmDescriptor` is always present for overload disambiguation. Pass `includeDescriptors: true` (legacy `get-class-members`) or `include: ["descriptors"]` (`inspect-minecraft` task=`class-members`) to also emit field descriptors.
20
+ - **Breaking:** `analyze-symbol` (and the `inspect-minecraft`-adjacent mapping flows `exists`/`map`/`exact-map`/`workspace`) now reuse the expert-tool candidate projection: when a lookup resolves to a single exact candidate that just duplicates `match`, the `candidates` block is omitted; for unresolved results with more than three candidates the tail is slimmed and `candidateDetailsTruncated: true` is surfaced. Read the resolved symbol from `match` rather than `candidates[0]`.
21
+ - **Breaking:** `validate-mixin` (and `validate-project` task=`mixin`/`project-summary`) now default `reportMode` to `summary-first` (was `full`) — for every caller, not just the MCP wire boundary. The default response hoists shared provenance/warnings/incomplete reasons and omits per-result `resolvedMembers`, `toolHealth`, `structuredWarnings`, `aggregatedWarnings`, and `confidenceBreakdown`; `summary`, `issues`, `issueSummary`, and top-level provenance/toolHealth are retained. Pass `reportMode="full"` or `explain=true` to restore per-result `resolvedMembers`/`toolHealth`/`resolutionTrace`. `explain=true` now also preserves `resolutionTrace` under `reportMode="compact"`.
22
+ - `verify-mixin-target`: `member.descriptor` now accepts empty / whitespace-only strings as "omitted" (normalized to `undefined`), matching every other optional descriptor field on the surface. Previously rejected with `ERR_INVALID_INPUT`. Non-breaking — no change for callers passing a real descriptor.
23
+ - **Breaking:** `get-class-source` and `get-class-members` now take the same `kind`-based `target` shape as `resolve-artifact`: `target: { kind: "version"|"jar"|"coordinate"|"workspace"|"dependency", ... }`, plus `target: { kind: "artifact", artifactId }` to reuse an already-resolved artifact. The old `target: { type: "artifact", artifactId }` form is removed — migrate to `{ kind: "artifact", artifactId }`. The redundant `{ type: "resolve", kind, value }` wrapper is still accepted (the `type` key is now ignored), but `{ kind, value }` is the canonical shape. Published `suggestedCall` targets for these tools now use the `kind`-based shape.
24
+ - **Breaking:** `analyze-symbol` `task="lifecycle"` gains explicit range controls `fromVersion`, `toVersion`, `maxVersions`, `includeTimeline`, and `includeSnapshots` (accepted only for `task="lifecycle"`; supplying them on another task is now rejected instead of silently ignored). The existing `version` field is kept as a back-compat alias for `toVersion` (range end; `toVersion` takes precedence). **Behavior change:** the lifecycle scan default grew from the previous hard-coded **5 versions** to the service default of **120** (clamped at 400) — pass `maxVersions: 5` to restore the old window. A lifecycle request still requires an end anchor (`version` or `toVersion`).
25
+ - `analyze-symbol` `subject.kind="symbol"` is now real selector-based auto-detect (owner+descriptor ⇒ method, owner only ⇒ field, otherwise ⇒ class) with the inferred kind surfaced as a warning and documented via the schema `describe()`. Previously `"symbol"` was silently coerced to `class` (exists/map/workspace), rejected (api-overview), or ignored (exact-map/lifecycle). `summary.subject.kind` now reports the resolved concrete kind instead of echoing `"symbol"`. Non-breaking — the enum keeps all four values and explicit kinds are unaffected.
26
+ - **Breaking:** `check-symbol-exists` now defaults `signatureMode` to `name-only` (was `exact`), matching `find-mapping` so the two sibling lookup tools behave identically. A `kind="method"` query with no descriptor now matches by owner+name (resolving uniquely or reporting ambiguous overloads) instead of failing with `ERR_INVALID_INPUT`; pass `signatureMode="exact"` to require a descriptor. `find-mapping` gains a `nameMode` parameter (default `auto`, mirroring `check-symbol-exists`): `auto` accepts a dotless class name for any `sourceMapping` (previously only `obfuscated`), `fqcn` requires a fully-qualified name. `analyze-symbol`'s own `signatureMode` default is intentionally left at `exact`.
27
+ - **Breaking:** the per-tool `compact` boolean is removed from all expert tools (`resolve-artifact`, `find-mapping`, `resolve-method-mapping-exact`, `resolve-workspace-symbol`, `check-symbol-exists`, `get-class-source`, `get-class-members`, `search-class-source`, `list-artifact-files`) and the four `batch-*` tools, replaced by the entry-tool **`detail`** (`summary` | `standard` | `full`) + **`include[]`** response contract — one response-shaping vocabulary across the whole server. Per-tool defaults keep every default response byte-identical: resolution/mapping tools + batch default `detail: "summary"` (= old `compact: true`); `get-class-source`/`get-class-members`/`search-class-source`/`list-artifact-files` default `detail: "standard"` (= old `compact: false`, post-Phase-4 diagnostic strip). **Migration:** `compact: true` → `detail: "summary"`, `compact: false` → `detail: "full"`. `include[]` opts specific field groups back in (`["provenance"]`, `["candidates"]`, `["samples"]`, `["artifact"]`, `["paths"]`, `["descriptors"]`). The Phase-4 `includeProvenance` / `includeDescriptors` flags are retained as aliases for `include: ["provenance"]` / `include: ["descriptors"]`. Expert and batch responses now also report `meta.detailApplied` / `meta.includeApplied`.
28
+ - `resolve-method-mapping-exact` is now documented as a strict shortcut for `find-mapping` (kind=method, signatureMode=exact): its description, schema, and the README/tool-reference entries explain that it additionally requires a **complete** descriptor projection (returns `mapping_unavailable` when class references cannot all be projected, where `find-mapping`'s exact mode resolves leniently). Documentation-only — no wire, behavior, or input-schema change; the tool is retained.
29
+ - The display caps that were previously clamped silently now emit a clamp warning so the documented contract is truthful: `list-versions` `limit` (capped at 200), `get-class-api-matrix` `maxRows` (capped at 5000), and `compare-versions` `maxClassResults` (capped at 5000) push `"<field> was clamped to <cap> from <value>."` when an over-range value is supplied (and `inspect-minecraft task="versions"` surfaces the `list-versions` warning). Non-breaking — out-of-range values still clamp and succeed; the caps are unchanged.
30
+ - Resource read errors (`mc://…` URIs) now carry the same recovery metadata as the equivalent tool call: `retryClass` and `issueOrigin` are always present, and `hints`, `suggestedCall`, `exampleCalls`, `fieldErrors`, and `context` are surfaced whenever the underlying `AppError` supplies them (previously the resource envelope returned only `code` + `detail`). The envelope keeps its existing nested `{ error, meta }` shape and the generic `problems/resource` type/title. Additive and non-breaking for clients reading by key.
31
+ - **Breaking:** error envelopes add a new `retryClass` value `"server"` for non-recoverable internal server faults. `ERR_INTERNAL` (sanitized programming bugs / unexpected throws, including every non-`AppError` thrown by a handler) and `ERR_DB_FAILURE` (SQLite integrity/migration failures) now report `retryClass: "server"` instead of `"transient"`, so agents stop retrying deterministic server bugs. Genuinely-transient codes are unchanged — `ERR_REPO_FETCH_FAILED`, `ERR_ARTIFACT_RESOLUTION_FAILED`, `ERR_STAGE_BUDGET_PRE_PARSE`, `ERR_LIMIT_EXCEEDED`, and `ERR_JAVA_PROCESS_FAILED` stay `"transient"`. Clients that exhaustively `switch` on `retryClass` must add a `"server"` case (treat it like `"permanent"` for retry posture: do not retry the identical call). `issueOrigin` is unchanged (`ERR_INTERNAL` stays `tool_issue`).
32
+
33
+ ### Fixed
34
+
35
+ - `remap-mod-jar` no longer serves a stale remapped JAR after a newer Fabric **yarn** build is published for the same Minecraft version. Yarn Tiny mappings are now cached per build coordinate (`mappings/yarn/${coordinate}.tiny`) and the resolved yarn coordinate is part of the remap cache key, so a new build produces a fresh remap instead of reusing the old jar. The newest build is re-checked at most once per 24h per MC version (a small on-disk metadata marker keeps the check off the cache-hit fast path; on a Fabric Maven outage the last-known-good build is reused rather than failing). Mojang mappings are immutable per release and unaffected. `remap-mod-jar` gains an optional `forceRemap: true` to bypass the cache and re-resolve on demand. The remap pipeline version bumps (`v2`→`v3`), a one-time invalidation of previously cached remapped jars; obsolete `mappings/${version}-yarn.tiny` files can be reclaimed via `manage-cache` on the `mapping` kind.
36
+
37
+ ### Performance
38
+
39
+ - `get-class-api-matrix` now computes per-row cross-namespace mappings only for the requested page (`maxRows`/`cursor` window) instead of the whole class on every request, so paginated/deep-page calls scale with the window rather than the full member set. `rows`, row order, `rowCount`, `nextCursor`, and `cursorIgnored` are byte-identical. **Behavior change:** on paginated calls, `warnings` (per-row ambiguity notes) and `ambiguousRowCount` now reflect only the returned page; an unpaginated call (no `maxRows`) still reports them for the whole class.
40
+
8
41
  ## [4.2.1] - 2026-05-23
9
42
 
10
43
  ### Fixed
package/README.md CHANGED
@@ -15,7 +15,7 @@
15
15
 
16
16
  It runs over stdio and works with Claude Desktop, Claude Code, VS Code, Codex CLI, Gemini CLI, and other MCP-capable clients.
17
17
 
18
- **37 tools** (6 entry + 31 expert) | **7 resources** | **4 namespace mappings** | **SQLite-backed cache**
18
+ **41 tools** (6 entry + 35 expert) | **9 resources** | **4 namespace mappings** | **SQLite-backed cache**
19
19
 
20
20
  ## Features
21
21
 
@@ -203,7 +203,8 @@ These notes cover high-frequency decisions during onboarding. For the full pitfa
203
203
  "version": "1.21.10",
204
204
  "sourceMapping": "mojang",
205
205
  "targetMapping": "intermediary",
206
- "signatureMode": "name-only"
206
+ "signatureMode": "name-only",
207
+ "detail": "standard"
207
208
  }
208
209
  }
209
210
  ```
@@ -306,13 +307,13 @@ Tools for converting symbol names between namespaces and checking symbol existen
306
307
  | Tool | Purpose |
307
308
  | --- | --- |
308
309
  | `find-mapping` | Look up mapping candidates for class, field, or method symbols |
309
- | `resolve-method-mapping-exact` | Resolve one method mapping with strict owner, name, and descriptor matching |
310
+ | `resolve-method-mapping-exact` | Strict shortcut for `find-mapping` (kind=method, signatureMode=exact); requires the full owner+name+descriptor triple |
310
311
  | `get-class-api-matrix` | Show one class API across `obfuscated`, `mojang`, `intermediary`, and `yarn` |
311
312
  | `resolve-workspace-symbol` | Resolve compile-visible symbol names from a Gradle workspace |
312
313
  | `check-symbol-exists` | Check whether a class, field, or method exists in a namespace |
313
314
  <!-- END GENERATED TOOL TABLE: mapping-symbols -->
314
315
 
315
- Several lookup tools support `compact` result shaping for shorter responses. See [docs/tool-reference.md](docs/tool-reference.md) for defaults and the full per-tool field list.
316
+ Expert and batch tools share the `detail` (`summary` | `standard` | `full`) + `include[]` response contract for shorter responses (replacing the old per-tool `compact` flag). See [docs/tool-reference.md](docs/tool-reference.md) for per-tool defaults and the full field list.
316
317
 
317
318
  ### NBT Utilities
318
319
 
@@ -1,8 +1,9 @@
1
1
  export type AccessTransformerAccessAction = "public" | "protected" | "package-private" | "private";
2
2
  export type AccessTransformerFinalAction = "add" | "remove";
3
+ export type AccessTransformerTargetKind = "class" | "field" | "method" | "wildcard-all" | "wildcard-method";
3
4
  export type AccessTransformerEntry = {
4
5
  line: number;
5
- targetKind: "class" | "field" | "method";
6
+ targetKind: AccessTransformerTargetKind;
6
7
  owner: string;
7
8
  target: string;
8
9
  name?: string;
@@ -19,10 +19,19 @@ function splitMemberToken(tokens) {
19
19
  if (tokens.length === 1) {
20
20
  const token = tokens[0] ?? "";
21
21
  const descriptorStart = token.indexOf("(");
22
+ // Wildcards: "*" targets all fields and methods; "*()" targets all methods.
23
+ // (Standard Forge/NeoForge AT syntax — previously parsed as a literal name.)
24
+ if (token === "*") {
25
+ return { targetKind: "wildcard-all", name: "*" };
26
+ }
22
27
  if (descriptorStart >= 0) {
28
+ const namePart = token.slice(0, descriptorStart);
29
+ if (namePart === "*") {
30
+ return { targetKind: "wildcard-method", name: "*", descriptor: token.slice(descriptorStart) };
31
+ }
23
32
  return {
24
33
  targetKind: "method",
25
- name: token.slice(0, descriptorStart),
34
+ name: namePart,
26
35
  descriptor: token.slice(descriptorStart)
27
36
  };
28
37
  }
@@ -6,16 +6,33 @@ function asParamsRecord(params) {
6
6
  }
7
7
  return params;
8
8
  }
9
- export function buildSuggestedCall(spec) {
10
- if (SUGGESTED_CALL_VALIDATE_OFF) {
11
- const rawParams = asParamsRecord(spec.params);
12
- if (rawParams) {
13
- return { suggestedCall: { tool: spec.tool, params: rawParams } };
14
- }
15
- return {};
9
+ // Placeholder sentinels are `<...>` tokens that a caller must substitute before
10
+ // the call is executable (e.g. "<version>", "<fully-qualified-class-name>").
11
+ // The two JVM pseudo-method-names `<init>` / `<clinit>` are real, valid values
12
+ // and are explicitly excluded so constructor lookups are not misclassified.
13
+ const PLACEHOLDER_SENTINEL_RE = /<(?!init>)(?!clinit>)[^<>]+>/;
14
+ function containsPlaceholderSentinel(value) {
15
+ if (typeof value === "string") {
16
+ return PLACEHOLDER_SENTINEL_RE.test(value);
17
+ }
18
+ if (Array.isArray(value)) {
19
+ return value.some(containsPlaceholderSentinel);
20
+ }
21
+ if (value && typeof value === "object") {
22
+ return Object.values(value).some(containsPlaceholderSentinel);
16
23
  }
24
+ return false;
25
+ }
26
+ export function buildSuggestedCall(spec) {
17
27
  const primaryParams = asParamsRecord(spec.params);
18
- if (primaryParams) {
28
+ // A `suggestedCall` must be directly executable. Reject any primary payload
29
+ // that still carries a placeholder sentinel (e.g. "<version>") even when it
30
+ // is schema-valid: those belong in `exampleCalls` as templates, not as a
31
+ // call the caller can replay verbatim.
32
+ if (primaryParams && !containsPlaceholderSentinel(primaryParams)) {
33
+ if (SUGGESTED_CALL_VALIDATE_OFF) {
34
+ return { suggestedCall: { tool: spec.tool, params: primaryParams } };
35
+ }
19
36
  // Unknown-tool fail-open: the registry is populated at index.ts startup;
20
37
  // service-level tests that do not boot index.ts run with an empty
21
38
  // registry and rely on this pass-through. Callers that synthesize a tool
@@ -31,14 +48,18 @@ export function buildSuggestedCall(spec) {
31
48
  }
32
49
  const droppedMarker = spec.params !== undefined ? { _suggestedCallPrimaryDropped: true } : {};
33
50
  if (spec.examples && spec.examples.length > 0) {
51
+ // Example calls are templates (placeholders allowed) and only their
52
+ // schema-shape is checked. Mirror the primary path's unknown-tool
53
+ // fail-open so service-level tests with an unpopulated registry still
54
+ // surface templates instead of silently dropping them.
55
+ const exampleSchemaUnavailable = SUGGESTED_CALL_VALIDATE_OFF || !getToolSchema(spec.tool);
34
56
  const validated = spec.examples
35
57
  .map((example) => {
36
58
  const exampleParams = asParamsRecord(example.params);
37
59
  if (!exampleParams) {
38
60
  return null;
39
61
  }
40
- const result = validateToolParams(spec.tool, exampleParams);
41
- if (!result.valid) {
62
+ if (!exampleSchemaUnavailable && !validateToolParams(spec.tool, exampleParams).valid) {
42
63
  return null;
43
64
  }
44
65
  return {
@@ -34,6 +34,14 @@ type CacheEntryPage = {
34
34
  entries: CacheEntry[];
35
35
  nextCursor?: string;
36
36
  };
37
+ /**
38
+ * Whether a cache entry path contains the selector version as a whole version
39
+ * token. Used as a fallback when an entry has no structured meta.version. The
40
+ * match is anchored so that a coarse selector like "1.2" does NOT match a "1.21"
41
+ * path (which would cause destructive prune/delete to hit the wrong caches),
42
+ * while still allowing major.minor sweeps where "1.21" matches "1.21.4".
43
+ */
44
+ export declare function pathContainsVersion(path: string, version: string): boolean;
37
45
  export type CacheRegistryConfig = {
38
46
  cacheDir: string;
39
47
  sqlitePath: string;
@@ -99,6 +99,20 @@ function inferVersion(...candidates) {
99
99
  }
100
100
  return undefined;
101
101
  }
102
+ /**
103
+ * Whether a cache entry path contains the selector version as a whole version
104
+ * token. Used as a fallback when an entry has no structured meta.version. The
105
+ * match is anchored so that a coarse selector like "1.2" does NOT match a "1.21"
106
+ * path (which would cause destructive prune/delete to hit the wrong caches),
107
+ * while still allowing major.minor sweeps where "1.21" matches "1.21.4".
108
+ */
109
+ export function pathContainsVersion(path, version) {
110
+ const escaped = version.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
111
+ // Preceding char must not be a digit or dot (avoid matching inside a longer
112
+ // version like 1.21 -> 21); following char must not be a digit (block
113
+ // 1.2 -> 1.21) but a dot is allowed so 1.21 sweeps 1.21.4.
114
+ return new RegExp(`(?<![\\d.])${escaped}(?!\\d)`).test(path);
115
+ }
102
116
  function inferMapping(...candidates) {
103
117
  for (const candidate of candidates) {
104
118
  const normalized = candidate?.toLowerCase();
@@ -329,7 +343,7 @@ function matchesSelector(entry, selector, runtimeInfo) {
329
343
  }
330
344
  if (selector.version) {
331
345
  const version = typeof maybeMeta.version === "string" ? maybeMeta.version : undefined;
332
- if (version !== selector.version && !entry.path.includes(selector.version)) {
346
+ if (version !== selector.version && !pathContainsVersion(entry.path, selector.version)) {
333
347
  return false;
334
348
  }
335
349
  }
package/dist/config.d.ts CHANGED
@@ -12,6 +12,7 @@ declare const DEFAULTS: {
12
12
  readonly fetchTimeoutMs: 15000;
13
13
  readonly fetchRetries: 2;
14
14
  readonly searchScanPageSize: 250;
15
+ readonly searchScanMaxBytes: 67108864;
15
16
  readonly indexInsertChunkSize: 200;
16
17
  readonly maxMappingGraphCache: 16;
17
18
  readonly maxSignatureCache: 2000;
package/dist/config.js CHANGED
@@ -20,6 +20,7 @@ const DEFAULTS = {
20
20
  fetchTimeoutMs: 15000,
21
21
  fetchRetries: 2,
22
22
  searchScanPageSize: 250,
23
+ searchScanMaxBytes: 67_108_864,
23
24
  indexInsertChunkSize: 200,
24
25
  maxMappingGraphCache: 16,
25
26
  maxSignatureCache: 2_000,
@@ -41,6 +42,8 @@ const MAX_CACHE_BYTES_LOWER_BOUND = 1_024;
41
42
  const TIMEOUT_LOWER_BOUND_MS = 500;
42
43
  const SEARCH_SCAN_PAGE_SIZE_LOWER_BOUND = 1;
43
44
  const SEARCH_SCAN_PAGE_SIZE_UPPER_BOUND = 10_000;
45
+ const SEARCH_SCAN_MAX_BYTES_LOWER_BOUND = 1_024;
46
+ const SEARCH_SCAN_MAX_BYTES_UPPER_BOUND = 4_294_967_296;
44
47
  const INDEX_INSERT_CHUNK_SIZE_LOWER_BOUND = 1;
45
48
  const INDEX_INSERT_CHUNK_SIZE_UPPER_BOUND = 20_000;
46
49
  const CACHE_ENTRIES_LOWER_BOUND = 1;
@@ -163,6 +166,7 @@ export function loadConfig() {
163
166
  fetchTimeoutMs: parseNumber(process.env.MCP_FETCH_TIMEOUT_MS, DEFAULTS.fetchTimeoutMs, TIMEOUT_LOWER_BOUND_MS, Number.MAX_SAFE_INTEGER),
164
167
  fetchRetries: parseNumber(process.env.MCP_FETCH_RETRIES, DEFAULTS.fetchRetries, MAX_RETRIES_LOWER_BOUND, MAX_RETRIES_UPPER_BOUND),
165
168
  searchScanPageSize: parseNumber(process.env.MCP_SEARCH_SCAN_PAGE_SIZE, DEFAULTS.searchScanPageSize, SEARCH_SCAN_PAGE_SIZE_LOWER_BOUND, SEARCH_SCAN_PAGE_SIZE_UPPER_BOUND),
169
+ searchScanMaxBytes: parseNumber(process.env.MCP_SEARCH_SCAN_MAX_BYTES, DEFAULTS.searchScanMaxBytes, SEARCH_SCAN_MAX_BYTES_LOWER_BOUND, SEARCH_SCAN_MAX_BYTES_UPPER_BOUND),
166
170
  indexInsertChunkSize: parseNumber(process.env.MCP_INDEX_INSERT_CHUNK_SIZE, DEFAULTS.indexInsertChunkSize, INDEX_INSERT_CHUNK_SIZE_LOWER_BOUND, INDEX_INSERT_CHUNK_SIZE_UPPER_BOUND),
167
171
  maxMappingGraphCache: parseNumber(process.env.MCP_CACHE_GRAPH_MAX, DEFAULTS.maxMappingGraphCache, CACHE_ENTRIES_LOWER_BOUND, CACHE_ENTRIES_UPPER_BOUND),
168
172
  maxSignatureCache: parseNumber(process.env.MCP_CACHE_SIGNATURE_MAX, DEFAULTS.maxSignatureCache, CACHE_ENTRIES_LOWER_BOUND, CACHE_ENTRIES_UPPER_BOUND),
@@ -75,8 +75,7 @@ export declare const analyzeModSchema: z.ZodEffects<z.ZodObject<{
75
75
  include: z.ZodOptional<z.ZodArray<z.ZodEnum<[string, ...string[]]>, "many">>;
76
76
  }, "strip", z.ZodTypeAny, {
77
77
  limit: number;
78
- searchType: "class" | "field" | "method" | "all" | "content";
79
- task: "search" | "remap" | "summary" | "class-source" | "decompile";
78
+ task: "search" | "summary" | "class-source" | "remap" | "decompile";
80
79
  subject: {
81
80
  kind: "jar";
82
81
  jarPath: string;
@@ -85,18 +84,19 @@ export declare const analyzeModSchema: z.ZodEffects<z.ZodObject<{
85
84
  jarPath: string;
86
85
  className: string;
87
86
  };
88
- includeFiles: boolean;
87
+ searchType: "class" | "field" | "method" | "all" | "content";
89
88
  executionMode: "preview" | "apply";
89
+ includeFiles: boolean;
90
90
  maxLines?: number | undefined;
91
91
  maxChars?: number | undefined;
92
+ detail?: "summary" | "standard" | "full" | undefined;
93
+ include?: string[] | undefined;
94
+ query?: string | undefined;
92
95
  targetMapping?: "mojang" | "yarn" | undefined;
93
96
  outputJar?: string | undefined;
94
- query?: string | undefined;
95
- detail?: "full" | "summary" | "standard" | undefined;
96
- include?: string[] | undefined;
97
97
  maxFiles?: number | undefined;
98
98
  }, {
99
- task: "search" | "remap" | "summary" | "class-source" | "decompile";
99
+ task: "search" | "summary" | "class-source" | "remap" | "decompile";
100
100
  subject: {
101
101
  kind: "jar";
102
102
  jarPath: string;
@@ -108,19 +108,18 @@ export declare const analyzeModSchema: z.ZodEffects<z.ZodObject<{
108
108
  limit?: number | undefined;
109
109
  maxLines?: number | undefined;
110
110
  maxChars?: number | undefined;
111
- targetMapping?: "mojang" | "yarn" | undefined;
112
- outputJar?: string | undefined;
111
+ detail?: "summary" | "standard" | "full" | undefined;
112
+ include?: string[] | undefined;
113
113
  query?: string | undefined;
114
114
  searchType?: "class" | "field" | "method" | "all" | "content" | undefined;
115
- detail?: "full" | "summary" | "standard" | undefined;
116
- include?: string[] | undefined;
115
+ targetMapping?: "mojang" | "yarn" | undefined;
116
+ outputJar?: string | undefined;
117
+ executionMode?: "preview" | "apply" | undefined;
117
118
  includeFiles?: boolean | undefined;
118
119
  maxFiles?: number | undefined;
119
- executionMode?: "preview" | "apply" | undefined;
120
120
  }>, {
121
121
  limit: number;
122
- searchType: "class" | "field" | "method" | "all" | "content";
123
- task: "search" | "remap" | "summary" | "class-source" | "decompile";
122
+ task: "search" | "summary" | "class-source" | "remap" | "decompile";
124
123
  subject: {
125
124
  kind: "jar";
126
125
  jarPath: string;
@@ -129,18 +128,19 @@ export declare const analyzeModSchema: z.ZodEffects<z.ZodObject<{
129
128
  jarPath: string;
130
129
  className: string;
131
130
  };
132
- includeFiles: boolean;
131
+ searchType: "class" | "field" | "method" | "all" | "content";
133
132
  executionMode: "preview" | "apply";
133
+ includeFiles: boolean;
134
134
  maxLines?: number | undefined;
135
135
  maxChars?: number | undefined;
136
+ detail?: "summary" | "standard" | "full" | undefined;
137
+ include?: string[] | undefined;
138
+ query?: string | undefined;
136
139
  targetMapping?: "mojang" | "yarn" | undefined;
137
140
  outputJar?: string | undefined;
138
- query?: string | undefined;
139
- detail?: "full" | "summary" | "standard" | undefined;
140
- include?: string[] | undefined;
141
141
  maxFiles?: number | undefined;
142
142
  }, {
143
- task: "search" | "remap" | "summary" | "class-source" | "decompile";
143
+ task: "search" | "summary" | "class-source" | "remap" | "decompile";
144
144
  subject: {
145
145
  kind: "jar";
146
146
  jarPath: string;
@@ -152,15 +152,15 @@ export declare const analyzeModSchema: z.ZodEffects<z.ZodObject<{
152
152
  limit?: number | undefined;
153
153
  maxLines?: number | undefined;
154
154
  maxChars?: number | undefined;
155
- targetMapping?: "mojang" | "yarn" | undefined;
156
- outputJar?: string | undefined;
155
+ detail?: "summary" | "standard" | "full" | undefined;
156
+ include?: string[] | undefined;
157
157
  query?: string | undefined;
158
158
  searchType?: "class" | "field" | "method" | "all" | "content" | undefined;
159
- detail?: "full" | "summary" | "standard" | undefined;
160
- include?: string[] | undefined;
159
+ targetMapping?: "mojang" | "yarn" | undefined;
160
+ outputJar?: string | undefined;
161
+ executionMode?: "preview" | "apply" | undefined;
161
162
  includeFiles?: boolean | undefined;
162
163
  maxFiles?: number | undefined;
163
- executionMode?: "preview" | "apply" | undefined;
164
164
  }>;
165
165
  export type AnalyzeModInput = z.infer<typeof analyzeModSchema>;
166
166
  type AnalyzeModDeps = {
@@ -19,6 +19,11 @@ export declare const analyzeSymbolShape: {
19
19
  owner?: string | undefined;
20
20
  }>;
21
21
  version: z.ZodOptional<z.ZodString>;
22
+ fromVersion: z.ZodOptional<z.ZodString>;
23
+ toVersion: z.ZodOptional<z.ZodString>;
24
+ maxVersions: z.ZodOptional<z.ZodNumber>;
25
+ includeTimeline: z.ZodOptional<z.ZodBoolean>;
26
+ includeSnapshots: z.ZodOptional<z.ZodBoolean>;
22
27
  sourceMapping: z.ZodOptional<z.ZodEnum<["obfuscated", "mojang", "intermediary", "yarn"]>>;
23
28
  targetMapping: z.ZodOptional<z.ZodEnum<["obfuscated", "mojang", "intermediary", "yarn"]>>;
24
29
  classNameMapping: z.ZodOptional<z.ZodEnum<["obfuscated", "mojang", "intermediary", "yarn"]>>;
@@ -51,6 +56,11 @@ export declare const analyzeSymbolSchema: z.ZodEffects<z.ZodObject<{
51
56
  owner?: string | undefined;
52
57
  }>;
53
58
  version: z.ZodOptional<z.ZodString>;
59
+ fromVersion: z.ZodOptional<z.ZodString>;
60
+ toVersion: z.ZodOptional<z.ZodString>;
61
+ maxVersions: z.ZodOptional<z.ZodNumber>;
62
+ includeTimeline: z.ZodOptional<z.ZodBoolean>;
63
+ includeSnapshots: z.ZodOptional<z.ZodBoolean>;
54
64
  sourceMapping: z.ZodOptional<z.ZodEnum<["obfuscated", "mojang", "intermediary", "yarn"]>>;
55
65
  targetMapping: z.ZodOptional<z.ZodEnum<["obfuscated", "mojang", "intermediary", "yarn"]>>;
56
66
  classNameMapping: z.ZodOptional<z.ZodEnum<["obfuscated", "mojang", "intermediary", "yarn"]>>;
@@ -64,7 +74,7 @@ export declare const analyzeSymbolSchema: z.ZodEffects<z.ZodObject<{
64
74
  detail: z.ZodOptional<z.ZodEnum<["summary", "standard", "full"]>>;
65
75
  include: z.ZodOptional<z.ZodArray<z.ZodEnum<[string, ...string[]]>, "many">>;
66
76
  }, "strip", z.ZodTypeAny, {
67
- nameMode: "auto" | "fqcn";
77
+ maxCandidates: number;
68
78
  task: "map" | "workspace" | "exists" | "exact-map" | "lifecycle" | "api-overview";
69
79
  subject: {
70
80
  name: string;
@@ -72,18 +82,23 @@ export declare const analyzeSymbolSchema: z.ZodEffects<z.ZodObject<{
72
82
  descriptor?: string | undefined;
73
83
  owner?: string | undefined;
74
84
  };
85
+ nameMode: "auto" | "fqcn";
75
86
  signatureMode: "exact" | "name-only";
76
- maxCandidates: number;
77
87
  version?: string | undefined;
78
88
  projectPath?: string | undefined;
79
- sourceMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
89
+ maxRows?: number | undefined;
90
+ detail?: "summary" | "standard" | "full" | undefined;
91
+ include?: string[] | undefined;
80
92
  targetMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
93
+ maxVersions?: number | undefined;
94
+ sourceMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
81
95
  classNameMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
82
96
  gradleUserHome?: string | undefined;
83
- detail?: "full" | "summary" | "standard" | undefined;
84
- include?: string[] | undefined;
97
+ includeSnapshots?: boolean | undefined;
98
+ fromVersion?: string | undefined;
99
+ toVersion?: string | undefined;
100
+ includeTimeline?: boolean | undefined;
85
101
  includeKinds?: ("class" | "field" | "method")[] | undefined;
86
- maxRows?: number | undefined;
87
102
  }, {
88
103
  task: "map" | "workspace" | "exists" | "exact-map" | "lifecycle" | "api-overview";
89
104
  subject: {
@@ -94,19 +109,24 @@ export declare const analyzeSymbolSchema: z.ZodEffects<z.ZodObject<{
94
109
  };
95
110
  version?: string | undefined;
96
111
  projectPath?: string | undefined;
97
- sourceMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
112
+ maxRows?: number | undefined;
113
+ maxCandidates?: number | undefined;
114
+ detail?: "summary" | "standard" | "full" | undefined;
115
+ include?: string[] | undefined;
98
116
  targetMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
117
+ maxVersions?: number | undefined;
118
+ sourceMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
99
119
  classNameMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
100
120
  gradleUserHome?: string | undefined;
101
121
  nameMode?: "auto" | "fqcn" | undefined;
102
- detail?: "full" | "summary" | "standard" | undefined;
103
- include?: string[] | undefined;
122
+ includeSnapshots?: boolean | undefined;
123
+ fromVersion?: string | undefined;
124
+ toVersion?: string | undefined;
125
+ includeTimeline?: boolean | undefined;
104
126
  signatureMode?: "exact" | "name-only" | undefined;
105
127
  includeKinds?: ("class" | "field" | "method")[] | undefined;
106
- maxRows?: number | undefined;
107
- maxCandidates?: number | undefined;
108
128
  }>, {
109
- nameMode: "auto" | "fqcn";
129
+ maxCandidates: number;
110
130
  task: "map" | "workspace" | "exists" | "exact-map" | "lifecycle" | "api-overview";
111
131
  subject: {
112
132
  name: string;
@@ -114,18 +134,23 @@ export declare const analyzeSymbolSchema: z.ZodEffects<z.ZodObject<{
114
134
  descriptor?: string | undefined;
115
135
  owner?: string | undefined;
116
136
  };
137
+ nameMode: "auto" | "fqcn";
117
138
  signatureMode: "exact" | "name-only";
118
- maxCandidates: number;
119
139
  version?: string | undefined;
120
140
  projectPath?: string | undefined;
121
- sourceMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
141
+ maxRows?: number | undefined;
142
+ detail?: "summary" | "standard" | "full" | undefined;
143
+ include?: string[] | undefined;
122
144
  targetMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
145
+ maxVersions?: number | undefined;
146
+ sourceMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
123
147
  classNameMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
124
148
  gradleUserHome?: string | undefined;
125
- detail?: "full" | "summary" | "standard" | undefined;
126
- include?: string[] | undefined;
149
+ includeSnapshots?: boolean | undefined;
150
+ fromVersion?: string | undefined;
151
+ toVersion?: string | undefined;
152
+ includeTimeline?: boolean | undefined;
127
153
  includeKinds?: ("class" | "field" | "method")[] | undefined;
128
- maxRows?: number | undefined;
129
154
  }, {
130
155
  task: "map" | "workspace" | "exists" | "exact-map" | "lifecycle" | "api-overview";
131
156
  subject: {
@@ -136,17 +161,22 @@ export declare const analyzeSymbolSchema: z.ZodEffects<z.ZodObject<{
136
161
  };
137
162
  version?: string | undefined;
138
163
  projectPath?: string | undefined;
139
- sourceMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
164
+ maxRows?: number | undefined;
165
+ maxCandidates?: number | undefined;
166
+ detail?: "summary" | "standard" | "full" | undefined;
167
+ include?: string[] | undefined;
140
168
  targetMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
169
+ maxVersions?: number | undefined;
170
+ sourceMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
141
171
  classNameMapping?: "obfuscated" | "mojang" | "intermediary" | "yarn" | undefined;
142
172
  gradleUserHome?: string | undefined;
143
173
  nameMode?: "auto" | "fqcn" | undefined;
144
- detail?: "full" | "summary" | "standard" | undefined;
145
- include?: string[] | undefined;
174
+ includeSnapshots?: boolean | undefined;
175
+ fromVersion?: string | undefined;
176
+ toVersion?: string | undefined;
177
+ includeTimeline?: boolean | undefined;
146
178
  signatureMode?: "exact" | "name-only" | undefined;
147
179
  includeKinds?: ("class" | "field" | "method")[] | undefined;
148
- maxRows?: number | undefined;
149
- maxCandidates?: number | undefined;
150
180
  }>;
151
181
  export type AnalyzeSymbolInput = z.infer<typeof analyzeSymbolSchema>;
152
182
  type AnalyzeSymbolDeps = {
@@ -172,6 +202,7 @@ type AnalyzeSymbolDeps = {
172
202
  sourceMapping: "obfuscated" | "mojang" | "intermediary" | "yarn";
173
203
  targetMapping: "obfuscated" | "mojang" | "intermediary" | "yarn";
174
204
  gradleUserHome?: string;
205
+ nameMode?: "fqcn" | "auto";
175
206
  signatureMode?: "exact" | "name-only";
176
207
  maxCandidates?: number;
177
208
  }) => Promise<FindMappingOutput>;
@@ -189,9 +220,13 @@ type AnalyzeSymbolDeps = {
189
220
  symbol: string;
190
221
  descriptor?: string;
191
222
  mapping?: "obfuscated" | "mojang" | "intermediary" | "yarn";
223
+ sourcePriority?: "loom-first" | "maven-first";
192
224
  gradleUserHome?: string;
225
+ fromVersion?: string;
193
226
  toVersion?: string;
194
227
  maxVersions?: number;
228
+ includeSnapshots?: boolean;
229
+ includeTimeline?: boolean;
195
230
  }) => Promise<TraceSymbolLifecycleOutput>;
196
231
  resolveWorkspaceSymbol: (input: {
197
232
  projectPath: string;