@adhisang/minecraft-modding-mcp 5.0.0 → 6.1.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 (83) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/dist/access-widener-parser.d.ts +1 -0
  3. package/dist/access-widener-parser.js +6 -3
  4. package/dist/cache-registry.js +35 -31
  5. package/dist/config.js +1 -1
  6. package/dist/decompiler/vineflower.js +8 -2
  7. package/dist/entry-tools/analyze-mod-service.js +2 -1
  8. package/dist/entry-tools/analyze-symbol-service.js +18 -9
  9. package/dist/entry-tools/batch-class-members-service.d.ts +2 -0
  10. package/dist/entry-tools/batch-class-members-service.js +1 -0
  11. package/dist/entry-tools/compare-minecraft-service.js +1 -1
  12. package/dist/entry-tools/inspect-minecraft/handlers/class-members.js +2 -1
  13. package/dist/entry-tools/inspect-minecraft/handlers/file.js +26 -1
  14. package/dist/entry-tools/inspect-minecraft/handlers/versions.js +2 -1
  15. package/dist/entry-tools/inspect-minecraft/internal.js +2 -0
  16. package/dist/entry-tools/response-contract.d.ts +2 -0
  17. package/dist/entry-tools/response-contract.js +2 -2
  18. package/dist/entry-tools/validate-project/cases/project-summary.js +30 -12
  19. package/dist/entry-tools/validate-project-service.d.ts +4 -4
  20. package/dist/entry-tools/verify-mixin-target-service.js +7 -7
  21. package/dist/index.js +21 -16
  22. package/dist/java-process.js +1 -1
  23. package/dist/json-rpc-framing.d.ts +4 -0
  24. package/dist/json-rpc-framing.js +23 -1
  25. package/dist/mapping/internal-types.d.ts +17 -0
  26. package/dist/mapping/loaders/tiny-maven.js +46 -20
  27. package/dist/mapping/lookup.d.ts +12 -1
  28. package/dist/mapping/lookup.js +53 -1
  29. package/dist/mapping-service.js +37 -22
  30. package/dist/minecraft-explorer-service.js +2 -1
  31. package/dist/mixin/annotation-validators.js +21 -3
  32. package/dist/mixin/parsed-validator.js +1 -0
  33. package/dist/mixin-parser.js +71 -3
  34. package/dist/mod-decompile-service.d.ts +2 -0
  35. package/dist/mod-decompile-service.js +19 -2
  36. package/dist/mod-remap-service.js +6 -5
  37. package/dist/mojang-tiny-mapping-service.js +5 -2
  38. package/dist/nbt/java-nbt-codec.js +40 -16
  39. package/dist/nbt/json-patch.js +51 -12
  40. package/dist/nbt/typed-json.d.ts +1 -0
  41. package/dist/nbt/typed-json.js +12 -2
  42. package/dist/nbt/types.d.ts +2 -2
  43. package/dist/path-converter.js +10 -3
  44. package/dist/registry-service.d.ts +2 -0
  45. package/dist/registry-service.js +16 -1
  46. package/dist/repo-downloader.js +4 -3
  47. package/dist/source/artifact-resolver.js +1 -1
  48. package/dist/source/class-source/members-builder.d.ts +26 -0
  49. package/dist/source/class-source/members-builder.js +31 -4
  50. package/dist/source/class-source/snippet-builder.js +6 -0
  51. package/dist/source/class-source-helpers.d.ts +4 -4
  52. package/dist/source/class-source-helpers.js +12 -11
  53. package/dist/source/class-source.d.ts +19 -0
  54. package/dist/source/class-source.js +87 -26
  55. package/dist/source/file-access.js +3 -2
  56. package/dist/source/indexer.js +24 -7
  57. package/dist/source/lifecycle/mapping-helpers.js +28 -3
  58. package/dist/source/lifecycle/runtime-check.js +20 -6
  59. package/dist/source/member-pattern.d.ts +16 -0
  60. package/dist/source/member-pattern.js +27 -0
  61. package/dist/source/search.js +23 -8
  62. package/dist/source/validate-mixin/pipeline/resolve.js +23 -3
  63. package/dist/source/workspace-target.js +2 -1
  64. package/dist/source-resolver.js +2 -2
  65. package/dist/source-service.d.ts +8 -4
  66. package/dist/source-service.js +9 -1
  67. package/dist/stdio-supervisor.d.ts +2 -2
  68. package/dist/stdio-supervisor.js +29 -9
  69. package/dist/storage/db.js +24 -1
  70. package/dist/storage/files-repo.d.ts +3 -0
  71. package/dist/storage/files-repo.js +43 -37
  72. package/dist/storage/symbols-repo.d.ts +9 -0
  73. package/dist/storage/symbols-repo.js +47 -19
  74. package/dist/symbols/symbol-extractor.js +1 -1
  75. package/dist/tool-guidance.d.ts +2 -1
  76. package/dist/tool-guidance.js +27 -2
  77. package/dist/tool-schemas.d.ts +15 -4
  78. package/dist/tool-schemas.js +102 -97
  79. package/dist/warning-details.d.ts +12 -0
  80. package/dist/warning-details.js +17 -0
  81. package/dist/workspace-mapping-service.js +9 -0
  82. package/docs/tool-reference.md +23 -0
  83. package/package.json +1 -1
@@ -1,12 +1,12 @@
1
- import { isAbsolute as pathIsAbsolute, resolve as pathResolve } from "node:path";
1
+ import { resolve as pathResolve } from "node:path";
2
2
  import { z } from "zod";
3
3
  import { DETAIL_LEVELS, CANONICAL_INCLUDE_GROUPS } from "./entry-tools/response-contract.js";
4
4
  // Shared response-shape controls for the expert + batch tools (replacing the old
5
5
  // per-tool `compact` boolean). detail: summary|standard|full + include[] mirrors the
6
6
  // entry-tool contract. Defaults are per-tool (see DEFAULT_DETAIL_BY_TOOL in response-utils):
7
7
  // resolution/mapping tools + batch default "summary"; source/file tools default "standard".
8
- const DETAIL_DESCRIPTION = "Response detail: 'summary' (terse: drops diagnostics/empties, slims candidates), 'standard' (default for source/file tools: keeps fields but drops heavy diagnostics), 'full' (everything). Replaces the old `compact` flag.";
9
- const RESPONSE_INCLUDE_DESCRIPTION = "Opt specific field groups back in regardless of detail, e.g. [\"provenance\"], [\"candidates\"], [\"samples\"], [\"artifact\"], [\"descriptors\"].";
8
+ const DETAIL_DESCRIPTION = "summary = terse (drops diagnostics/empties, slims candidates); standard = keeps fields, drops heavy diagnostics; full = everything.";
9
+ const RESPONSE_INCLUDE_DESCRIPTION = 'Field groups to include regardless of detail, e.g. ["provenance"].';
10
10
  function detailParam(defaultLevel) {
11
11
  return z.enum(DETAIL_LEVELS).default(defaultLevel).describe(DETAIL_DESCRIPTION);
12
12
  }
@@ -30,7 +30,7 @@ export const ENCODE_COMPRESSIONS = ["none", "gzip"];
30
30
  export const nonEmptyString = z.string().trim().min(1);
31
31
  export const optionalNonEmptyString = z.string().trim().min(1).optional();
32
32
  export const optionalPositiveInt = z.number().int().positive().optional();
33
- export const gradleUserHomeSchema = optionalNonEmptyString.describe("Gradle User Home to use for Loom/Gradle cache lookups instead of the MCP process GRADLE_USER_HOME.");
33
+ export const gradleUserHomeSchema = optionalNonEmptyString.describe("Gradle user home for Loom cache lookups (overrides GRADLE_USER_HOME).");
34
34
  // Optional descriptor: "" and whitespace-only strings are normalized to undefined so that
35
35
  // tools with signatureMode="name-only" can accept "caller omitted descriptor" inputs whether
36
36
  // the caller passed an empty string or omitted the field entirely. Malformed descriptors are
@@ -64,10 +64,10 @@ export const workspaceTargetSchema = z.object({
64
64
  });
65
65
  export const dependencyTargetSchema = z.object({
66
66
  kind: z.literal("dependency"),
67
- group: nonEmptyString,
68
- name: nonEmptyString,
69
- version: z.string().trim().min(1).optional(),
70
- versionFromProject: z.boolean().optional()
67
+ group: nonEmptyString.describe('Maven group, e.g. "net.fabricmc.fabric-api" or "dev.architectury".'),
68
+ name: nonEmptyString.describe('Maven artifact name, e.g. "fabric-api" or "architectury".'),
69
+ version: z.string().trim().min(1).optional().describe("Explicit version; omit and set versionFromProject to resolve it from the workspace."),
70
+ versionFromProject: z.boolean().optional().describe("Resolve the version from the project's gradle.properties / gradle cache (requires projectPath).")
71
71
  });
72
72
  export const resolveArtifactTargetSchema = z.discriminatedUnion("kind", [
73
73
  z.object({ kind: z.literal("version"), value: nonEmptyString }),
@@ -89,13 +89,16 @@ export const sourceLookupTargetSchema = z.discriminatedUnion("kind", [
89
89
  dependencyTargetSchema,
90
90
  z.object({ kind: z.literal("artifact"), artifactId: nonEmptyString })
91
91
  ]);
92
- export const RESOLVE_ARTIFACT_TARGET_DESCRIPTION = 'Object with kind. Examples: {"kind":"version","value":"1.21.10"}, {"kind":"workspace"} (uses projectPath), or {"kind":"dependency","group":"dev.architectury","name":"architectury"}. Must be an object, not a string.';
93
- export const SOURCE_LOOKUP_TARGET_DESCRIPTION = 'Object with kind (same shape as resolve-artifact). Examples: {"kind":"version","value":"1.21.10"}, {"kind":"workspace"} (uses projectPath), {"kind":"dependency","group":"...","name":"..."}, or {"kind":"artifact","artifactId":"..."} to reuse an already-resolved artifact. Must be an object, not a string.';
94
- export const SOURCE_SCOPE_DESCRIPTION = "vanilla = Mojang client jar only; merged = source-oriented merged runtime discovery; loader = loader/runtime artifact discovery when the workspace exposes transformed runtime jars.";
92
+ export const RESOLVE_ARTIFACT_TARGET_DESCRIPTION = 'Object, not string. e.g. {"kind":"version","value":"1.21.10"}, {"kind":"workspace"}, or to inspect a loader/Fabric dependency like vanilla: {"kind":"dependency","group":"net.fabricmc.fabric-api","name":"fabric-api","versionFromProject":true} (needs projectPath) or with an explicit "version".';
93
+ export const SOURCE_LOOKUP_TARGET_DESCRIPTION = 'Same shape as resolve-artifact target (incl. {"kind":"dependency",...} to read a Fabric/loader dependency class like vanilla), plus {"kind":"artifact","artifactId":"..."} to reuse a resolved artifact. Object, not string.';
94
+ export const SOURCE_SCOPE_DESCRIPTION = "vanilla = Mojang client jar only; merged = merged runtime discovery; loader = loader-transformed runtime jars.";
95
95
  // Shared describe() text reused by every symbol-lookup tool so the contract reads
96
96
  // identically on find-mapping and check-symbol-exists (and any future sibling).
97
+ export const MEMBER_PATTERN_DESCRIPTION = 'Case-insensitive substring filter on member names. Use "|" for OR alternatives, e.g. "getStateForPlacement|canSurvive|setPlacedBy" (each token is matched as a substring, not a regex).';
98
+ export const memberProjectionSchema = z.enum(["names", "signatures", "full"]);
99
+ export const MEMBER_PROJECTION_DESCRIPTION = 'Per-member field projection: "names" (member name only), "signatures" (name + javaSignature, no jvmDescriptor), or "full" (default; complete member shape). Use "names"/"signatures" to cut response tokens for existence/signature checks.';
97
100
  export const SIGNATURE_MODE_DESCRIPTION = "exact: descriptor required for kind=method; name-only (default): match by owner+name only.";
98
- export const NAME_MODE_DESCRIPTION = "auto (default): accept a fully-qualified name, or a dotless name where the tool allows it; fqcn: require a fully-qualified name.";
101
+ export const NAME_MODE_DESCRIPTION = "auto (default): FQCN, or dotless name where allowed; fqcn: require FQCN.";
99
102
  export const listVersionsShape = {
100
103
  includeSnapshots: z.boolean().default(false),
101
104
  limit: optionalPositiveInt.default(20).describe("max 200")
@@ -103,14 +106,14 @@ export const listVersionsShape = {
103
106
  export const listVersionsSchema = z.object(listVersionsShape);
104
107
  export const resolveArtifactShape = {
105
108
  target: resolveArtifactTargetSchema.describe(RESOLVE_ARTIFACT_TARGET_DESCRIPTION),
106
- mapping: sourceMappingSchema.optional().describe("obfuscated | mojang | intermediary | yarn"),
107
- sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
109
+ mapping: sourceMappingSchema.optional(),
110
+ sourcePriority: mappingSourcePrioritySchema.optional(),
108
111
  allowDecompile: z.boolean().default(true),
109
- projectPath: optionalNonEmptyString.describe("Optional workspace root path for Loom cache-assisted source resolution"),
112
+ projectPath: optionalNonEmptyString.describe("Workspace root for Loom cache-assisted resolution"),
110
113
  gradleUserHome: gradleUserHomeSchema,
111
114
  scope: artifactScopeSchema.optional().describe(SOURCE_SCOPE_DESCRIPTION),
112
- preferProjectVersion: z.boolean().optional().describe("When true, detect MC version from gradle.properties and override target.value"),
113
- strictVersion: z.boolean().optional().describe("When true, reject version-approximated results instead of returning them. Default false."),
115
+ preferProjectVersion: z.boolean().optional().describe("Detect MC version from gradle.properties and override target.value"),
116
+ strictVersion: z.boolean().optional().describe("Reject version-approximated results (default false)"),
114
117
  detail: detailParam("summary"),
115
118
  include: responseIncludeParam
116
119
  };
@@ -119,14 +122,14 @@ export const getClassSourceShape = {
119
122
  className: nonEmptyString,
120
123
  mode: sourceModeSchema.default("metadata").describe("metadata = symbol outline only; snippet = source with default maxLines=200; full = entire source"),
121
124
  target: sourceLookupTargetSchema.describe(SOURCE_LOOKUP_TARGET_DESCRIPTION),
122
- mapping: sourceMappingSchema.optional().describe("obfuscated | mojang | intermediary | yarn"),
123
- sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
125
+ mapping: sourceMappingSchema.optional(),
126
+ sourcePriority: mappingSourcePrioritySchema.optional(),
124
127
  allowDecompile: z.boolean().default(true),
125
- projectPath: optionalNonEmptyString.describe("Optional workspace root path for Loom cache-assisted source resolution"),
128
+ projectPath: optionalNonEmptyString.describe("Workspace root for Loom cache-assisted resolution"),
126
129
  gradleUserHome: gradleUserHomeSchema,
127
130
  scope: artifactScopeSchema.optional().describe(SOURCE_SCOPE_DESCRIPTION),
128
- preferProjectVersion: z.boolean().optional().describe("When true, detect MC version from gradle.properties and override target.value"),
129
- strictVersion: z.boolean().optional().describe("When true, reject version-approximated results instead of returning them. Default false."),
131
+ preferProjectVersion: z.boolean().optional().describe("Detect MC version from gradle.properties and override target.value"),
132
+ strictVersion: z.boolean().optional().describe("Reject version-approximated results (default false)"),
130
133
  startLine: optionalPositiveInt,
131
134
  endLine: optionalPositiveInt,
132
135
  maxLines: optionalPositiveInt,
@@ -134,7 +137,7 @@ export const getClassSourceShape = {
134
137
  outputFile: optionalNonEmptyString.describe("Write source to this file path and return metadata-only response"),
135
138
  detail: detailParam("standard"),
136
139
  include: responseIncludeParam,
137
- includeProvenance: z.boolean().default(false).describe("Alias for include:[\"provenance\"]. When true, include diagnostic metadata (provenance, qualityFlags, artifactContents). Default false — omitted to keep the common path lean.")
140
+ includeProvenance: z.boolean().default(false).describe('Alias for include:["provenance"] (diagnostic metadata). Default false.')
138
141
  };
139
142
  export const getClassSourceSchema = z
140
143
  .object(getClassSourceShape)
@@ -152,24 +155,25 @@ export const getClassSourceSchema = z
152
155
  export const getClassMembersShape = {
153
156
  className: nonEmptyString,
154
157
  target: sourceLookupTargetSchema.describe(SOURCE_LOOKUP_TARGET_DESCRIPTION),
155
- mapping: sourceMappingSchema.optional().describe("obfuscated | mojang | intermediary | yarn (default obfuscated)"),
156
- sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
158
+ mapping: sourceMappingSchema.optional().describe("default obfuscated"),
159
+ sourcePriority: mappingSourcePrioritySchema.optional(),
157
160
  allowDecompile: z.boolean().default(true),
158
- access: memberAccessSchema.default("public").describe("public | all"),
161
+ access: memberAccessSchema.default("public"),
159
162
  includeSynthetic: z.boolean().default(false),
160
163
  includeInherited: z.boolean().default(false),
161
- memberPattern: optionalNonEmptyString,
164
+ memberPattern: optionalNonEmptyString.describe(MEMBER_PATTERN_DESCRIPTION),
165
+ projection: memberProjectionSchema.optional().describe(MEMBER_PROJECTION_DESCRIPTION),
162
166
  maxMembers: optionalPositiveInt.describe("default 150, max 5000. Page beyond the first 150 with cursor."),
163
- cursor: optionalNonEmptyString.describe("Continuation cursor from a previous response's nextCursor; resumes the member list after the last returned page."),
167
+ cursor: optionalNonEmptyString.describe("nextCursor from the previous response."),
164
168
  projectPath: optionalNonEmptyString,
165
169
  gradleUserHome: gradleUserHomeSchema,
166
170
  scope: artifactScopeSchema.optional().describe(SOURCE_SCOPE_DESCRIPTION),
167
- preferProjectVersion: z.boolean().optional().describe("When true, detect MC version from gradle.properties and override version"),
168
- strictVersion: z.boolean().optional().describe("When true, reject version-approximated results instead of returning them. Default false."),
171
+ preferProjectVersion: z.boolean().optional().describe("Detect MC version from gradle.properties and override version"),
172
+ strictVersion: z.boolean().optional().describe("Reject version-approximated results (default false)"),
169
173
  detail: detailParam("standard"),
170
174
  include: responseIncludeParam,
171
- includeProvenance: z.boolean().default(false).describe("Alias for include:[\"provenance\"]. When true, include diagnostic metadata (provenance, qualityFlags, artifactContents). Default false — omitted to keep the common path lean."),
172
- includeDescriptors: z.boolean().default(false).describe("Alias for include:[\"descriptors\"]. When true, also emit jvmDescriptor on FIELD members. Default false: field descriptors are omitted (the type is already in javaSignature). Method/constructor descriptors are always present for overload disambiguation.")
175
+ includeProvenance: z.boolean().default(false).describe('Alias for include:["provenance"] (diagnostic metadata). Default false.'),
176
+ includeDescriptors: z.boolean().default(false).describe('Alias for include:["descriptors"]: also emit jvmDescriptor on FIELD members (method/constructor descriptors are always present). Default false.')
173
177
  };
174
178
  export const getClassMembersSchema = z.object(getClassMembersShape);
175
179
  export const verifyMixinTargetMemberSchema = z.discriminatedUnion("kind", [
@@ -185,18 +189,18 @@ export const verifyMixinTargetMemberSchema = z.discriminatedUnion("kind", [
185
189
  })
186
190
  ]);
187
191
  export const verifyMixinTargetShape = {
188
- owner: nonEmptyString.describe("Fully-qualified class name of the target owner (e.g. net.minecraft.world.entity.LivingEntity)."),
189
- member: verifyMixinTargetMemberSchema.describe('Member to verify. Object with kind. Examples: {"kind":"method","name":"tick","descriptor":"()V"} or {"kind":"field","name":"airSupply"}.'),
190
- mixinMemberName: optionalNonEmptyString.describe("Optional caller-authored mixin field/method name. Drives @Accessor (getXxx/setXxx) and @Invoker (invokeXxx/callXxx) advice when the target is private."),
191
- mapping: sourceMappingSchema.optional().describe("obfuscated | mojang | intermediary | yarn"),
192
- autoRemap: z.boolean().optional().describe("When true and mapping differs from the artifact namespace, translate owner+member via find-mapping instead of failing ERR_NAMESPACE_MISMATCH. Requires a version-based target."),
193
- sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
192
+ owner: nonEmptyString.describe("Fully-qualified target owner class name."),
193
+ member: verifyMixinTargetMemberSchema.describe('e.g. {"kind":"method","name":"tick","descriptor":"()V"} or {"kind":"field","name":"airSupply"}.'),
194
+ mixinMemberName: optionalNonEmptyString.describe("Caller-authored mixin member name; drives @Accessor/@Invoker advice when the target is private."),
195
+ mapping: sourceMappingSchema.optional(),
196
+ autoRemap: z.boolean().optional().describe("Translate owner+member via find-mapping when mapping differs from the artifact namespace (requires a version-based target)."),
197
+ sourcePriority: mappingSourcePrioritySchema.optional(),
194
198
  projectPath: optionalNonEmptyString.describe("Workspace root path for target.kind=workspace and Loom cache assistance."),
195
199
  gradleUserHome: gradleUserHomeSchema,
196
200
  target: resolveArtifactTargetSchema.describe(RESOLVE_ARTIFACT_TARGET_DESCRIPTION),
197
201
  scope: artifactScopeSchema.optional().describe(SOURCE_SCOPE_DESCRIPTION),
198
- preferProjectVersion: z.boolean().optional().describe("When true, detect MC version from gradle.properties and override target.value"),
199
- strictVersion: z.boolean().optional().describe("When true, reject version-approximated results instead of returning them. Default false.")
202
+ preferProjectVersion: z.boolean().optional().describe("Detect MC version from gradle.properties and override target.value"),
203
+ strictVersion: z.boolean().optional().describe("Reject version-approximated results (default false)")
200
204
  };
201
205
  export const verifyMixinTargetSchema = z.object(verifyMixinTargetShape);
202
206
  export const batchSymbolKindSchema = z.enum(["class", "field", "method"]);
@@ -211,7 +215,7 @@ export const batchClassSourceEntrySchema = z.object({
211
215
  });
212
216
  export const batchClassSourceShape = {
213
217
  target: resolveArtifactTargetSchema.describe(RESOLVE_ARTIFACT_TARGET_DESCRIPTION),
214
- mapping: sourceMappingSchema.optional().describe("obfuscated | mojang | intermediary | yarn"),
218
+ mapping: sourceMappingSchema.optional(),
215
219
  sourcePriority: mappingSourcePrioritySchema.optional(),
216
220
  allowDecompile: z.boolean().optional(),
217
221
  projectPath: optionalNonEmptyString,
@@ -233,8 +237,8 @@ export const batchClassSourceSchema = z.object(batchClassSourceShape).superRefin
233
237
  // Per-entry `outputFile` is forwarded into the concurrently-dispatched
234
238
  // `getClassSource` calls; two entries resolving to the same physical file
235
239
  // would race on `writeFile`. Normalize via `path.resolve` so aliases like
236
- // `out.java` / `./out.java` / `dir/../out.java` collide as expected,
237
- // matching the writer's `isAbsolute(p) ? p : resolvePath(p)` rule.
240
+ // `out.java` / `./out.java` / `dir/../out.java` / `/tmp/dir/../out.java`
241
+ // collide as expected, covering relative and absolute spellings alike.
238
242
  const seen = new Map();
239
243
  for (let i = 0; i < value.entries.length; i += 1) {
240
244
  const entry = value.entries[i];
@@ -243,7 +247,7 @@ export const batchClassSourceSchema = z.object(batchClassSourceShape).superRefin
243
247
  const trimmed = entry.outputFile.trim();
244
248
  if (trimmed.length === 0)
245
249
  continue;
246
- const canonical = pathIsAbsolute(trimmed) ? trimmed : pathResolve(trimmed);
250
+ const canonical = pathResolve(trimmed);
247
251
  const previous = seen.get(canonical);
248
252
  if (previous !== undefined) {
249
253
  ctx.addIssue({
@@ -262,7 +266,7 @@ export const batchClassMembersEntrySchema = z.object({
262
266
  access: memberAccessSchema.optional(),
263
267
  includeSynthetic: z.boolean().optional(),
264
268
  includeInherited: z.boolean().optional(),
265
- memberPattern: optionalNonEmptyString,
269
+ memberPattern: optionalNonEmptyString.describe(MEMBER_PATTERN_DESCRIPTION),
266
270
  maxMembers: optionalPositiveInt
267
271
  });
268
272
  export const batchClassMembersShape = {
@@ -278,6 +282,7 @@ export const batchClassMembersShape = {
278
282
  concurrency: z.number().int().min(1).max(8).optional(),
279
283
  failFast: z.boolean().optional(),
280
284
  detail: detailParam("summary"),
285
+ projection: memberProjectionSchema.optional().describe(MEMBER_PROJECTION_DESCRIPTION),
281
286
  include: responseIncludeParam,
282
287
  entries: z.array(batchClassMembersEntrySchema).min(1).max(50)
283
288
  };
@@ -285,7 +290,7 @@ export const batchClassMembersSchema = z.object(batchClassMembersShape);
285
290
  export const batchSymbolExistsTargetSchema = z.discriminatedUnion("kind", [
286
291
  z.object({ kind: z.literal("version"), value: nonEmptyString }),
287
292
  workspaceTargetSchema
288
- ]).describe('Object with kind. Only kind="version" or kind="workspace" is accepted; dependency/jar/coordinate targets carry library versions, not Minecraft versions, and would corrupt the mapping query.');
293
+ ]).describe('Only kind="version" or kind="workspace"; other kinds carry library versions, not MC versions.');
289
294
  export const batchSymbolExistsEntrySchema = z.object({
290
295
  kind: batchSymbolKindSchema,
291
296
  name: nonEmptyString,
@@ -332,7 +337,7 @@ export const batchMappingsEntrySchema = z
332
337
  })
333
338
  .strict();
334
339
  export const batchMappingsShape = {
335
- version: nonEmptyString.describe("Minecraft version shared by every entry. Per-entry version is rejected; this batch shape is intentionally single-version."),
340
+ version: nonEmptyString.describe("Minecraft version shared by every entry (single-version batch)."),
336
341
  sourcePriority: mappingSourcePrioritySchema.optional(),
337
342
  projectPath: optionalNonEmptyString,
338
343
  gradleUserHome: gradleUserHomeSchema,
@@ -346,16 +351,16 @@ export const batchMappingsSchema = z.object(batchMappingsShape);
346
351
  export const searchClassSourceShape = {
347
352
  artifactId: nonEmptyString,
348
353
  query: nonEmptyString,
349
- intent: searchIntentSchema.optional().describe("symbol | text | path"),
350
- match: searchMatchSchema.optional().describe("exact | prefix | contains | regex"),
354
+ intent: searchIntentSchema.optional(),
355
+ match: searchMatchSchema.optional(),
351
356
  packagePrefix: optionalNonEmptyString,
352
357
  fileGlob: optionalNonEmptyString,
353
- symbolKind: searchSymbolKindSchema.optional().describe("class | interface | enum | record | method | field"),
354
- queryMode: z.enum(["auto", "token", "literal"]).default("auto").describe("auto: indexed search, including separator queries like foo.bar; token: indexed-only; literal: explicit substring scan only"),
358
+ symbolKind: searchSymbolKindSchema.optional(),
359
+ queryMode: z.enum(["auto", "token", "literal"]).default("auto").describe("auto: indexed search incl. separator queries like foo.bar; token: indexed-only; literal: substring scan only"),
355
360
  limit: optionalPositiveInt.default(20),
356
361
  cursor: optionalNonEmptyString,
357
- queryNamespace: sourceMappingSchema.optional().describe("Namespace of the query. When set and intent='symbol' with a fully-qualified class name, the query is translated through find-mapping before searching the artifact namespace. Ignored for text/path intents (warning surfaced)."),
358
- sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first. Used only when queryNamespace triggers translation."),
362
+ queryNamespace: sourceMappingSchema.optional().describe("Query namespace; symbol-intent FQCN queries are translated via find-mapping first. Ignored for text/path intents."),
363
+ sourcePriority: mappingSourcePrioritySchema.optional().describe("Used only when queryNamespace triggers translation."),
359
364
  gradleUserHome: gradleUserHomeSchema,
360
365
  detail: detailParam("standard"),
361
366
  include: responseIncludeParam
@@ -389,8 +394,8 @@ export const traceSymbolLifecycleShape = {
389
394
  descriptor: optionalDescriptorString.describe('optional JVM descriptor, e.g. "(I)V". Empty strings are treated as omitted.'),
390
395
  fromVersion: optionalNonEmptyString,
391
396
  toVersion: optionalNonEmptyString,
392
- mapping: sourceMappingSchema.optional().describe("obfuscated | mojang | intermediary | yarn (default obfuscated)"),
393
- sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
397
+ mapping: sourceMappingSchema.optional().describe("default obfuscated"),
398
+ sourcePriority: mappingSourcePrioritySchema.optional(),
394
399
  gradleUserHome: gradleUserHomeSchema,
395
400
  includeSnapshots: z.boolean().default(false),
396
401
  maxVersions: optionalPositiveInt.default(120).describe("max 400"),
@@ -401,21 +406,21 @@ export const diffClassSignaturesShape = {
401
406
  className: nonEmptyString,
402
407
  fromVersion: nonEmptyString,
403
408
  toVersion: nonEmptyString,
404
- mapping: sourceMappingSchema.optional().describe("obfuscated | mojang | intermediary | yarn (default obfuscated)"),
405
- sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
409
+ mapping: sourceMappingSchema.optional().describe("default obfuscated"),
410
+ sourcePriority: mappingSourcePrioritySchema.optional(),
406
411
  gradleUserHome: gradleUserHomeSchema,
407
412
  includeFullDiff: z.boolean().default(true).describe("When false, omit from/to snapshots from modified entries and keep only key+changed")
408
413
  };
409
414
  export const diffClassSignaturesSchema = z.object(diffClassSignaturesShape);
410
415
  export const findMappingShape = {
411
416
  version: nonEmptyString,
412
- kind: workspaceSymbolKindSchema.describe("class | field | method"),
417
+ kind: workspaceSymbolKindSchema,
413
418
  name: nonEmptyString,
414
419
  owner: optionalNonEmptyString,
415
- descriptor: optionalDescriptorString.describe("JVM descriptor. Optional when signatureMode='name-only' (default). Empty strings are treated as omitted."),
416
- sourceMapping: sourceMappingSchema.describe("obfuscated | mojang | intermediary | yarn"),
417
- targetMapping: sourceMappingSchema.describe("obfuscated | mojang | intermediary | yarn"),
418
- sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
420
+ descriptor: optionalDescriptorString.describe("JVM descriptor; optional when signatureMode='name-only'. Empty = omitted."),
421
+ sourceMapping: sourceMappingSchema,
422
+ targetMapping: sourceMappingSchema,
423
+ sourcePriority: mappingSourcePrioritySchema.optional(),
419
424
  gradleUserHome: gradleUserHomeSchema,
420
425
  nameMode: classNameModeSchema.default("auto").describe(NAME_MODE_DESCRIPTION),
421
426
  signatureMode: z.enum(["exact", "name-only"]).default("name-only").describe(SIGNATURE_MODE_DESCRIPTION),
@@ -426,7 +431,7 @@ export const findMappingShape = {
426
431
  })
427
432
  .partial()
428
433
  .optional(),
429
- maxCandidates: optionalPositiveInt.default(5).describe("Limit returned candidates (default 5, max 200). Raise when you need the full candidate list."),
434
+ maxCandidates: optionalPositiveInt.default(5).describe("default 5, max 200"),
430
435
  detail: detailParam("summary"),
431
436
  include: responseIncludeParam
432
437
  };
@@ -496,11 +501,11 @@ export const resolveMethodMappingExactShape = {
496
501
  name: nonEmptyString,
497
502
  owner: nonEmptyString,
498
503
  descriptor: nonEmptyString.describe("required JVM descriptor"),
499
- sourceMapping: sourceMappingSchema.describe("obfuscated | mojang | intermediary | yarn"),
500
- targetMapping: sourceMappingSchema.describe("obfuscated | mojang | intermediary | yarn"),
501
- sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
504
+ sourceMapping: sourceMappingSchema,
505
+ targetMapping: sourceMappingSchema,
506
+ sourcePriority: mappingSourcePrioritySchema.optional(),
502
507
  gradleUserHome: gradleUserHomeSchema,
503
- maxCandidates: optionalPositiveInt.default(5).describe("Limit returned candidates (default 5, max 200). Raise when you need the full candidate list."),
508
+ maxCandidates: optionalPositiveInt.default(5).describe("default 5, max 200"),
504
509
  detail: detailParam("summary"),
505
510
  include: responseIncludeParam
506
511
  };
@@ -538,25 +543,25 @@ export const classApiKindsSchema = z.string().superRefine((value, ctx) => {
538
543
  export const getClassApiMatrixShape = {
539
544
  version: nonEmptyString,
540
545
  className: nonEmptyString,
541
- classNameMapping: sourceMappingSchema.describe("obfuscated | mojang | intermediary | yarn"),
546
+ classNameMapping: sourceMappingSchema,
542
547
  includeKinds: classApiKindsSchema.optional().describe("comma-separated: class,field,method"),
543
- sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
548
+ sourcePriority: mappingSourcePrioritySchema.optional(),
544
549
  gradleUserHome: gradleUserHomeSchema,
545
550
  maxRows: optionalPositiveInt.describe("Limit returned rows (max 5000)"),
546
- cursor: optionalNonEmptyString.describe("Continuation cursor from a previous response's nextCursor; resumes after the last returned row.")
551
+ cursor: optionalNonEmptyString.describe("nextCursor from the previous response.")
547
552
  };
548
553
  export const getClassApiMatrixSchema = z.object(getClassApiMatrixShape);
549
554
  export const resolveWorkspaceSymbolShape = {
550
555
  projectPath: nonEmptyString,
551
556
  version: nonEmptyString,
552
- kind: workspaceSymbolKindSchema.describe("class | field | method"),
557
+ kind: workspaceSymbolKindSchema,
553
558
  name: nonEmptyString,
554
559
  owner: optionalNonEmptyString,
555
560
  descriptor: optionalDescriptorString.describe("JVM descriptor. Empty strings are treated as omitted."),
556
- sourceMapping: sourceMappingSchema.describe("obfuscated | mojang | intermediary | yarn"),
557
- sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
561
+ sourceMapping: sourceMappingSchema,
562
+ sourcePriority: mappingSourcePrioritySchema.optional(),
558
563
  gradleUserHome: gradleUserHomeSchema,
559
- maxCandidates: optionalPositiveInt.default(5).describe("Limit returned candidates for field/method lookups (default 5, max 200). Raise when you need the full candidate list."),
564
+ maxCandidates: optionalPositiveInt.default(5).describe("default 5, max 200 (field/method lookups)"),
560
565
  detail: detailParam("summary"),
561
566
  include: responseIncludeParam
562
567
  };
@@ -621,16 +626,16 @@ export const resolveWorkspaceSymbolSchema = z
621
626
  });
622
627
  export const checkSymbolExistsShape = {
623
628
  version: nonEmptyString,
624
- kind: workspaceSymbolKindSchema.describe("class | field | method"),
629
+ kind: workspaceSymbolKindSchema,
625
630
  owner: optionalNonEmptyString,
626
631
  name: nonEmptyString,
627
- descriptor: optionalDescriptorString.describe("JVM descriptor. Optional when signatureMode='name-only' (default). Empty strings are treated as omitted."),
628
- sourceMapping: sourceMappingSchema.describe("obfuscated | mojang | intermediary | yarn"),
629
- sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
632
+ descriptor: optionalDescriptorString.describe("JVM descriptor; optional when signatureMode='name-only'. Empty = omitted."),
633
+ sourceMapping: sourceMappingSchema,
634
+ sourcePriority: mappingSourcePrioritySchema.optional(),
630
635
  gradleUserHome: gradleUserHomeSchema,
631
636
  nameMode: classNameModeSchema.default("auto").describe(NAME_MODE_DESCRIPTION),
632
637
  signatureMode: z.enum(["exact", "name-only"]).default("name-only").describe(SIGNATURE_MODE_DESCRIPTION),
633
- maxCandidates: optionalPositiveInt.default(5).describe("Limit returned candidates (default 5, max 200). Raise when you need the full candidate list."),
638
+ maxCandidates: optionalPositiveInt.default(5).describe("default 5, max 200"),
634
639
  detail: detailParam("summary"),
635
640
  include: responseIncludeParam
636
641
  };
@@ -693,7 +698,7 @@ export const checkSymbolExistsSchema = z.object(checkSymbolExistsShape).superRef
693
698
  });
694
699
  export const nbtToJsonShape = {
695
700
  nbtBase64: nonEmptyString,
696
- compression: decodeCompressionSchema.default("auto").describe("none | gzip | auto")
701
+ compression: decodeCompressionSchema.default("auto")
697
702
  };
698
703
  export const nbtToJsonSchema = z.object(nbtToJsonShape);
699
704
  const nbtPatchOperationSchema = z
@@ -710,7 +715,7 @@ export const nbtApplyJsonPatchShape = {
710
715
  export const nbtApplyJsonPatchSchema = z.object(nbtApplyJsonPatchShape);
711
716
  export const jsonToNbtShape = {
712
717
  typedJson: z.unknown(),
713
- compression: encodeCompressionSchema.default("none").describe("none | gzip")
718
+ compression: encodeCompressionSchema.default("none")
714
719
  };
715
720
  export const jsonToNbtSchema = z.object(jsonToNbtShape);
716
721
  export const indexArtifactShape = {
@@ -740,16 +745,16 @@ export const validateMixinShape = {
740
745
  mode: z.literal("project"),
741
746
  path: nonEmptyString.describe("Workspace root path used to discover *.mixins.json files automatically")
742
747
  })
743
- ]).describe("One of { mode: 'inline', source }, { mode: 'path', path }, { mode: 'paths', paths[] }, { mode: 'config', configPaths[] }, or { mode: 'project', path }."),
748
+ ]).describe("mode = inline | path | paths | config | project"),
744
749
  sourceRoots: z.array(z.string().min(1)).optional()
745
- .describe("Array of source roots for multi-module projects (e.g. ['common/src/main/java', 'neoforge/src/main/java'])"),
746
- version: optionalNonEmptyString.describe("Minecraft version. Optional when input.mode='project' (detected from the workspace) or preferProjectVersion=true with projectPath set; required otherwise."),
747
- mapping: sourceMappingSchema.optional().describe("obfuscated | mojang | intermediary | yarn"),
748
- sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
750
+ .describe("Source roots for multi-module projects (e.g. ['common/src/main/java'])"),
751
+ version: optionalNonEmptyString.describe("Minecraft version. Optional when input.mode='project' or preferProjectVersion=true with projectPath; required otherwise."),
752
+ mapping: sourceMappingSchema.optional(),
753
+ sourcePriority: mappingSourcePrioritySchema.optional(),
749
754
  scope: artifactScopeSchema.optional().describe(SOURCE_SCOPE_DESCRIPTION),
750
- projectPath: optionalNonEmptyString.describe("Optional workspace root path for Loom cache-assisted source resolution"),
755
+ projectPath: optionalNonEmptyString.describe("Workspace root for Loom cache-assisted resolution"),
751
756
  gradleUserHome: gradleUserHomeSchema,
752
- preferProjectVersion: z.boolean().optional().describe("When true, detect MC version from gradle.properties and override version"),
757
+ preferProjectVersion: z.boolean().optional().describe("Detect MC version from gradle.properties and override version"),
753
758
  minSeverity: z.enum(["error", "warning", "all"]).default("all")
754
759
  .describe("'error'=errors only, 'warning'=errors+warnings, 'all'=everything"),
755
760
  hideUncertain: z.boolean().default(false)
@@ -761,7 +766,7 @@ export const validateMixinShape = {
761
766
  preferProjectMapping: z.boolean().default(false)
762
767
  .describe("When true, auto-detect mapping from project config even if mapping is explicitly provided"),
763
768
  reportMode: z.enum(["compact", "full", "summary-first"]).default("summary-first")
764
- .describe("Default 'summary-first': hoists shared provenance/warnings/incomplete reasons and drops per-result resolvedMembers/toolHealth/structuredWarnings. 'compact' omits heavy per-result detail; 'full'=everything. resolvedMembers/per-result toolHealth/resolutionTrace require reportMode='full' or explain=true."),
769
+ .describe("summary-first (default) hoists shared provenance/warnings, drops per-result heavy detail; compact omits heavy per-result detail; full = everything (required for resolvedMembers/toolHealth/resolutionTrace unless explain=true)."),
765
770
  warningCategoryFilter: z.array(z.enum(["mapping", "configuration", "validation", "resolution", "parse"])).optional()
766
771
  .describe("Only include warnings/issues matching these categories (default: all)"),
767
772
  treatInfoAsWarning: z.boolean().default(true)
@@ -785,8 +790,8 @@ export const validateMixinSchema = z.object(validateMixinShape).superRefine((val
785
790
  export const validateAccessWidenerShape = {
786
791
  content: nonEmptyString.describe("Access Widener file content"),
787
792
  version: nonEmptyString.describe("Minecraft version"),
788
- mapping: sourceMappingSchema.optional().describe("obfuscated | mojang | intermediary | yarn"),
789
- sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
793
+ mapping: sourceMappingSchema.optional(),
794
+ sourcePriority: mappingSourcePrioritySchema.optional(),
790
795
  projectPath: optionalNonEmptyString.describe("Optional workspace root path for Loom cache-assisted runtime validation"),
791
796
  gradleUserHome: gradleUserHomeSchema,
792
797
  scope: artifactScopeSchema.optional().describe(SOURCE_SCOPE_DESCRIPTION),
@@ -797,8 +802,8 @@ export const validateAccessWidenerSchema = z.object(validateAccessWidenerShape);
797
802
  export const validateAccessTransformerShape = {
798
803
  content: nonEmptyString.describe("Access Transformer file content"),
799
804
  version: nonEmptyString.describe("Minecraft version"),
800
- atNamespace: z.enum(["srg", "mojang", "obfuscated"]).optional().describe("srg | mojang | obfuscated"),
801
- sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
805
+ atNamespace: z.enum(["srg", "mojang", "obfuscated"]).optional(),
806
+ sourcePriority: mappingSourcePrioritySchema.optional(),
802
807
  projectPath: optionalNonEmptyString.describe("Optional workspace root path for Forge/NeoForge runtime validation"),
803
808
  gradleUserHome: gradleUserHomeSchema,
804
809
  scope: artifactScopeSchema.optional().describe(SOURCE_SCOPE_DESCRIPTION),
@@ -814,7 +819,7 @@ export const analyzeModJarSchema = z.object(analyzeModJarShape);
814
819
  export const getRegistryDataShape = {
815
820
  version: nonEmptyString.describe("Minecraft version (e.g. 1.21)"),
816
821
  registry: optionalNonEmptyString.describe('Optional registry name (e.g. "block", "item", "minecraft:biome"). Omit to list all registries.'),
817
- includeData: z.boolean().default(true).describe("When false, return registry names/counts only. To discover which registries exist without pulling every entry body, pass includeData:false (omitting registry with the default includeData:true returns the full data for every registry)."),
822
+ includeData: z.boolean().default(true).describe("false = registry names/counts only (cheap discovery); true (default) = full entry bodies."),
818
823
  maxEntriesPerRegistry: optionalPositiveInt.describe("Limit returned entries per registry body")
819
824
  };
820
825
  export const getRegistryDataSchema = z.object(getRegistryDataShape);
@@ -823,7 +828,7 @@ export const compareVersionsCategorySchema = z.enum(COMPARE_VERSIONS_CATEGORIES)
823
828
  export const compareVersionsShape = {
824
829
  fromVersion: nonEmptyString.describe("Older Minecraft version (e.g. 1.20.4)"),
825
830
  toVersion: nonEmptyString.describe("Newer Minecraft version (e.g. 1.21)"),
826
- category: compareVersionsCategorySchema.default("all").describe("classes | registry | all"),
831
+ category: compareVersionsCategorySchema.default("all"),
827
832
  packageFilter: optionalNonEmptyString.describe("Filter classes to a package prefix (e.g. net.minecraft.world.item)"),
828
833
  maxClassResults: optionalPositiveInt.default(500).describe("Max class results per direction (max 5000)")
829
834
  };
@@ -848,7 +853,7 @@ export const modSearchTypeSchema = z.enum(MOD_SEARCH_TYPES);
848
853
  export const searchModSourceShape = {
849
854
  jarPath: nonEmptyString.describe("Local path to the mod JAR file"),
850
855
  query: nonEmptyString.describe("Search pattern (regex or literal string)"),
851
- searchType: modSearchTypeSchema.default("all").describe("class | method | field | content | all"),
856
+ searchType: modSearchTypeSchema.default("all"),
852
857
  limit: optionalPositiveInt.default(50).describe("Max results (max 200)")
853
858
  };
854
859
  export const searchModSourceSchema = z.object(searchModSourceShape);
@@ -858,7 +863,7 @@ export const remapModJarShape = {
858
863
  inputJar: nonEmptyString.describe("Path to the mod JAR file"),
859
864
  outputJar: optionalNonEmptyString.describe("Output path for remapped JAR (auto-generated if omitted)"),
860
865
  mcVersion: optionalNonEmptyString.describe("Minecraft version (auto-detected from mod metadata if omitted)"),
861
- targetMapping: remapTargetSchema.describe("yarn | mojang"),
866
+ targetMapping: remapTargetSchema,
862
867
  forceRemap: z
863
868
  .boolean()
864
869
  .optional()
@@ -16,3 +16,15 @@ export type WarningDetail = {
16
16
  * position and dereferences the text via `warnings[index]`.
17
17
  */
18
18
  export declare function classifyWarnings(warnings: string[]): WarningDetail[];
19
+ /**
20
+ * Cap of structured warningDetails entries emitted at `detail:"summary"`.
21
+ */
22
+ export declare const SUMMARY_WARNING_DETAIL_CAP = 5;
23
+ /**
24
+ * At `detail:"summary"` the structured warningDetails companion is capped to a small
25
+ * representative set to keep responses lean. The full human-readable text still lives
26
+ * in `meta.warnings[]`, and each kept entry's `index` continues to dereference it, so
27
+ * no information is lost — only the redundant structured duplicate is trimmed. Standard
28
+ * and full detail return the companion unchanged.
29
+ */
30
+ export declare function capWarningDetailsForSummary(warningDetails: WarningDetail[], isSummary: boolean): WarningDetail[];
@@ -60,4 +60,21 @@ function classifyWarning(message, index) {
60
60
  export function classifyWarnings(warnings) {
61
61
  return warnings.map((message, index) => classifyWarning(message, index));
62
62
  }
63
+ /**
64
+ * Cap of structured warningDetails entries emitted at `detail:"summary"`.
65
+ */
66
+ export const SUMMARY_WARNING_DETAIL_CAP = 5;
67
+ /**
68
+ * At `detail:"summary"` the structured warningDetails companion is capped to a small
69
+ * representative set to keep responses lean. The full human-readable text still lives
70
+ * in `meta.warnings[]`, and each kept entry's `index` continues to dereference it, so
71
+ * no information is lost — only the redundant structured duplicate is trimmed. Standard
72
+ * and full detail return the companion unchanged.
73
+ */
74
+ export function capWarningDetailsForSummary(warningDetails, isSummary) {
75
+ if (!isSummary || warningDetails.length <= SUMMARY_WARNING_DETAIL_CAP) {
76
+ return warningDetails;
77
+ }
78
+ return warningDetails.slice(0, SUMMARY_WARNING_DETAIL_CAP);
79
+ }
63
80
  //# sourceMappingURL=warning-details.js.map
@@ -65,6 +65,15 @@ function buildDependencyPropertyKeys(group, name) {
65
65
  `${groupSegment}_${name}_version`,
66
66
  `${camelGroupName}Version`
67
67
  ];
68
+ // Umbrella fallback: submodules of an umbrella package (e.g. Fabric API's
69
+ // net.fabricmc.fabric-api:fabric-screen-handler-api-v1) rarely declare their own
70
+ // version — the parent declares a single umbrella property (fabric_api_version /
71
+ // fabricApiVersion) that all submodules inherit. When the artifact name differs from
72
+ // the group's last segment, append the umbrella's keys as LOWER-priority candidates so
73
+ // submodule version detection no longer fails with ERR_DEPENDENCY_VERSION_UNRESOLVED.
74
+ if (groupSegment !== name) {
75
+ keys.push(`${groupSegment.replace(/-/g, "_")}_version`, `${camelCaseDependencyName(groupSegment)}Version`);
76
+ }
68
77
  const seen = new Set();
69
78
  const deduped = [];
70
79
  for (const key of keys) {
@@ -67,6 +67,29 @@ Start here when you are not sure which tool to reach for. In every row, the left
67
67
  | `{ 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`. |
68
68
  | `{ 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. When the caller asks for a non-obfuscated mapping 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`. |
69
69
 
70
+ ### Inspecting a Fabric / loader dependency like vanilla
71
+
72
+ To read members or source of a Fabric API (or any loader/Maven dependency) class
73
+ the same way you read vanilla, pass a `dependency` target straight to
74
+ `get-class-members` / `get-class-source` — no separate lookup tool is needed:
75
+
76
+ ```jsonc
77
+ // members of a Fabric API class, version taken from the workspace
78
+ {
79
+ "tool": "get-class-members",
80
+ "className": "net.fabricmc.fabric.api.event.player.UseEntityCallback",
81
+ "target": { "kind": "dependency", "group": "net.fabricmc.fabric-api", "name": "fabric-api", "versionFromProject": true },
82
+ "projectPath": "/path/to/workspace"
83
+ }
84
+ ```
85
+
86
+ Use an explicit `"version"` instead of `versionFromProject` when you already
87
+ know it. Because dependency JARs are not remapped, members come back in the
88
+ dependency's own namespace with `qualityFlags: ["dependency-mapping-unverified"]`;
89
+ treat the names as the JAR's compiled names. For repeated lookups against the
90
+ same dependency, call `resolve-artifact` once and reuse the returned
91
+ `artifactId` via `target: { kind: "artifact", artifactId }`.
92
+
70
93
  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`.
71
94
 
72
95
  `target.kind="dependency"` resolution probes four de-duplicated `gradle.properties` keys in order — `name_version`, `camelCaseVersion`, `lastSegment(group)_name_version`, and `camelCase(lastSegment(group)_name)Version` — before falling back to the modules-2 cache layout `~/.gradle/caches/modules-2/files-2.1/<group>/<name>/`. Version tokens that contain path separators, `..`, NUL, control characters, or any character outside `[A-Za-z0-9._+-]` are rejected; in `gradle.properties` the rejection is recorded under `attempts[]` as `gradle.properties:<key>:rejected-unsafe-version` and the next key is tried. Snapshot and dev directories are excluded by default. The modules-2 fallback only resolves when exactly one valid entry remains; multiple entries raise `ERR_DEPENDENCY_VERSION_UNRESOLVED` so a global cache cannot supply a version the workspace did not declare.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhisang/minecraft-modding-mcp",
3
- "version": "5.0.0",
3
+ "version": "6.1.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
  "main": "dist/index.js",