@adhisang/minecraft-modding-mcp 4.2.0 → 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 (120) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +6 -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 +99 -6
  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/errors.js +4 -1
  49. package/dist/index.js +138 -71
  50. package/dist/java-process.d.ts +9 -0
  51. package/dist/java-process.js +11 -5
  52. package/dist/mapping/internal-types.d.ts +7 -0
  53. package/dist/mapping/lookup.d.ts +1 -1
  54. package/dist/mapping/lookup.js +7 -1
  55. package/dist/mapping/types.d.ts +6 -0
  56. package/dist/mapping-service.d.ts +26 -2
  57. package/dist/mapping-service.js +178 -36
  58. package/dist/mcp-helpers.d.ts +1 -0
  59. package/dist/mcp-helpers.js +25 -3
  60. package/dist/minecraft-explorer-service.js +24 -11
  61. package/dist/mixin/access-validators.js +33 -1
  62. package/dist/mixin/annotation-validators.js +53 -7
  63. package/dist/mixin/parsed-validator.js +2 -2
  64. package/dist/mixin-parser.js +48 -4
  65. package/dist/mod-decompile-service.js +2 -1
  66. package/dist/mod-remap-service.d.ts +20 -1
  67. package/dist/mod-remap-service.js +95 -40
  68. package/dist/nbt/java-nbt-codec.js +87 -2
  69. package/dist/observability.d.ts +6 -0
  70. package/dist/observability.js +10 -0
  71. package/dist/page-cursor.d.ts +17 -0
  72. package/dist/page-cursor.js +0 -0
  73. package/dist/registry-service.js +25 -1
  74. package/dist/resources.js +45 -9
  75. package/dist/response-utils.d.ts +29 -4
  76. package/dist/response-utils.js +95 -5
  77. package/dist/source/access-validate.js +12 -2
  78. package/dist/source/artifact-resolver.js +1 -1
  79. package/dist/source/cache-metrics.js +2 -2
  80. package/dist/source/class-source/members-builder.d.ts +44 -1
  81. package/dist/source/class-source/members-builder.js +51 -4
  82. package/dist/source/class-source/snippet-builder.d.ts +13 -0
  83. package/dist/source/class-source/snippet-builder.js +48 -4
  84. package/dist/source/class-source-helpers.js +67 -11
  85. package/dist/source/class-source.js +97 -20
  86. package/dist/source/indexer.d.ts +31 -6
  87. package/dist/source/indexer.js +95 -25
  88. package/dist/source/search.d.ts +1 -1
  89. package/dist/source/search.js +94 -22
  90. package/dist/source/validate-mixin.d.ts +2 -1
  91. package/dist/source/validate-mixin.js +45 -4
  92. package/dist/source/workspace-target.js +15 -9
  93. package/dist/source-jar-reader.d.ts +54 -1
  94. package/dist/source-jar-reader.js +121 -10
  95. package/dist/source-service.d.ts +28 -7
  96. package/dist/storage/files-repo.d.ts +12 -0
  97. package/dist/storage/files-repo.js +24 -0
  98. package/dist/storage/symbols-repo.d.ts +6 -0
  99. package/dist/storage/symbols-repo.js +6 -0
  100. package/dist/symbols/symbol-extractor.js +35 -6
  101. package/dist/text-truncate.d.ts +7 -0
  102. package/dist/text-truncate.js +21 -0
  103. package/dist/tiny-remapper-resolver.js +10 -0
  104. package/dist/tiny-remapper-service.js +3 -3
  105. package/dist/tool-contract-manifest.js +2 -2
  106. package/dist/tool-execution-gate.js +12 -1
  107. package/dist/tool-guidance.d.ts +4 -1
  108. package/dist/tool-guidance.js +37 -12
  109. package/dist/tool-schemas.d.ts +559 -494
  110. package/dist/tool-schemas.js +99 -63
  111. package/dist/types.d.ts +1 -0
  112. package/dist/version-diff-service.js +5 -0
  113. package/dist/version-service.d.ts +3 -0
  114. package/dist/version-service.js +18 -2
  115. package/dist/warning-details.d.ts +18 -0
  116. package/dist/warning-details.js +63 -0
  117. package/docs/README-ja.md +2 -2
  118. package/docs/examples.md +26 -2
  119. package/docs/tool-reference.md +23 -17
  120. package/package.json +1 -1
@@ -1,5 +1,19 @@
1
1
  import { isAbsolute as pathIsAbsolute, resolve as pathResolve } from "node:path";
2
2
  import { z } from "zod";
3
+ import { DETAIL_LEVELS, CANONICAL_INCLUDE_GROUPS } from "./entry-tools/response-contract.js";
4
+ // Shared response-shape controls for the expert + batch tools (replacing the old
5
+ // per-tool `compact` boolean). detail: summary|standard|full + include[] mirrors the
6
+ // entry-tool contract. Defaults are per-tool (see DEFAULT_DETAIL_BY_TOOL in response-utils):
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\"].";
10
+ function detailParam(defaultLevel) {
11
+ return z.enum(DETAIL_LEVELS).default(defaultLevel).describe(DETAIL_DESCRIPTION);
12
+ }
13
+ const responseIncludeParam = z
14
+ .array(z.enum(CANONICAL_INCLUDE_GROUPS))
15
+ .optional()
16
+ .describe(RESPONSE_INCLUDE_DESCRIPTION);
3
17
  export const SOURCE_MAPPINGS = ["obfuscated", "mojang", "intermediary", "yarn"];
4
18
  export const SOURCE_PRIORITIES = ["loom-first", "maven-first"];
5
19
  export const TARGET_KINDS = ["version", "jar", "coordinate"];
@@ -62,32 +76,26 @@ export const resolveArtifactTargetSchema = z.discriminatedUnion("kind", [
62
76
  workspaceTargetSchema,
63
77
  dependencyTargetSchema
64
78
  ]);
65
- export const sourceLookupTargetSchema = z.union([
66
- z.object({
67
- type: z.literal("artifact"),
68
- artifactId: nonEmptyString
69
- }),
70
- z.object({ type: z.literal("resolve"), kind: z.literal("version"), value: nonEmptyString }),
71
- z.object({ type: z.literal("resolve"), kind: z.literal("jar"), value: nonEmptyString }),
72
- z.object({ type: z.literal("resolve"), kind: z.literal("coordinate"), value: nonEmptyString }),
73
- z.object({
74
- type: z.literal("resolve"),
75
- kind: z.literal("workspace"),
76
- scope: artifactScopeSchema.optional(),
77
- strict: z.boolean().optional()
78
- }),
79
- z.object({
80
- type: z.literal("resolve"),
81
- kind: z.literal("dependency"),
82
- group: nonEmptyString,
83
- name: nonEmptyString,
84
- version: z.string().trim().min(1).optional(),
85
- versionFromProject: z.boolean().optional()
86
- })
79
+ // Extended target schema for the source-lookup tools (get-class-source / get-class-members):
80
+ // the same kind-based shape as resolveArtifactTargetSchema, PLUS a `kind:"artifact"` variant
81
+ // that short-circuits resolution by reusing an already-resolved artifactId. The shared
82
+ // resolveArtifactTargetSchema is intentionally NOT widened — the artifact kind has no
83
+ // resolution meaning for resolve-artifact / verify-mixin-target / the batch tools.
84
+ export const sourceLookupTargetSchema = z.discriminatedUnion("kind", [
85
+ z.object({ kind: z.literal("version"), value: nonEmptyString }),
86
+ z.object({ kind: z.literal("jar"), value: nonEmptyString }),
87
+ z.object({ kind: z.literal("coordinate"), value: nonEmptyString }),
88
+ workspaceTargetSchema,
89
+ dependencyTargetSchema,
90
+ z.object({ kind: z.literal("artifact"), artifactId: nonEmptyString })
87
91
  ]);
88
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.';
89
- export const SOURCE_LOOKUP_TARGET_DESCRIPTION = 'Object: {"type":"resolve","kind":"version","value":"1.21.10"} or {"type":"resolve","kind":"workspace"} or {"type":"resolve","kind":"dependency","group":"...","name":"..."} or {"type":"artifact","artifactId":"..."}. 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.';
90
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.";
95
+ // Shared describe() text reused by every symbol-lookup tool so the contract reads
96
+ // identically on find-mapping and check-symbol-exists (and any future sibling).
97
+ 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.";
91
99
  export const listVersionsShape = {
92
100
  includeSnapshots: z.boolean().default(false),
93
101
  limit: optionalPositiveInt.default(20).describe("max 200")
@@ -103,9 +111,8 @@ export const resolveArtifactShape = {
103
111
  scope: artifactScopeSchema.optional().describe(SOURCE_SCOPE_DESCRIPTION),
104
112
  preferProjectVersion: z.boolean().optional().describe("When true, detect MC version from gradle.properties and override target.value"),
105
113
  strictVersion: z.boolean().optional().describe("When true, reject version-approximated results instead of returning them. Default false."),
106
- compact: z.boolean().default(true).describe("Return minimal fields (artifactId, origin, isDecompiled, version, requestedMapping, mappingApplied, qualityFlags). "
107
- + "Omit provenance, artifactContents, sampleEntries, adjacentSourceCandidates, binaryJarPath, coordinate, repoUrl, resolvedSourceJarPath. "
108
- + "Enabled by default; set to false for full output.")
114
+ detail: detailParam("summary"),
115
+ include: responseIncludeParam
109
116
  };
110
117
  export const resolveArtifactSchema = z.object(resolveArtifactShape);
111
118
  export const getClassSourceShape = {
@@ -125,7 +132,9 @@ export const getClassSourceShape = {
125
132
  maxLines: optionalPositiveInt,
126
133
  maxChars: optionalPositiveInt.describe("Hard character limit on sourceText; truncates if exceeded"),
127
134
  outputFile: optionalNonEmptyString.describe("Write source to this file path and return metadata-only response"),
128
- compact: z.boolean().default(false).describe("When true, strip debug metadata (provenance, artifactContents, qualityFlags) and empty fields from the response. Default false.")
135
+ detail: detailParam("standard"),
136
+ 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.")
129
138
  };
130
139
  export const getClassSourceSchema = z
131
140
  .object(getClassSourceShape)
@@ -150,25 +159,29 @@ export const getClassMembersShape = {
150
159
  includeSynthetic: z.boolean().default(false),
151
160
  includeInherited: z.boolean().default(false),
152
161
  memberPattern: optionalNonEmptyString,
153
- maxMembers: optionalPositiveInt.describe("default 500, max 5000"),
162
+ 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."),
154
164
  projectPath: optionalNonEmptyString,
155
165
  gradleUserHome: gradleUserHomeSchema,
156
166
  scope: artifactScopeSchema.optional().describe(SOURCE_SCOPE_DESCRIPTION),
157
167
  preferProjectVersion: z.boolean().optional().describe("When true, detect MC version from gradle.properties and override version"),
158
168
  strictVersion: z.boolean().optional().describe("When true, reject version-approximated results instead of returning them. Default false."),
159
- compact: z.boolean().default(false).describe("When true, strip debug metadata (provenance, artifactContents, qualityFlags, context) and empty fields from the response. Default false.")
169
+ detail: detailParam("standard"),
170
+ 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.")
160
173
  };
161
174
  export const getClassMembersSchema = z.object(getClassMembersShape);
162
175
  export const verifyMixinTargetMemberSchema = z.discriminatedUnion("kind", [
163
176
  z.object({
164
177
  kind: z.literal("method"),
165
178
  name: nonEmptyString,
166
- descriptor: z.string().trim().min(1).optional()
179
+ descriptor: optionalDescriptorString.describe('Optional JVM method descriptor, e.g. "()V". Empty/whitespace strings are treated as omitted.')
167
180
  }),
168
181
  z.object({
169
182
  kind: z.literal("field"),
170
183
  name: nonEmptyString,
171
- descriptor: z.string().trim().min(1).optional()
184
+ descriptor: optionalDescriptorString.describe('Optional JVM field descriptor, e.g. "I". Empty/whitespace strings are treated as omitted.')
172
185
  })
173
186
  ]);
174
187
  export const verifyMixinTargetShape = {
@@ -176,6 +189,7 @@ export const verifyMixinTargetShape = {
176
189
  member: verifyMixinTargetMemberSchema.describe('Member to verify. Object with kind. Examples: {"kind":"method","name":"tick","descriptor":"()V"} or {"kind":"field","name":"airSupply"}.'),
177
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."),
178
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."),
179
193
  sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
180
194
  projectPath: optionalNonEmptyString.describe("Workspace root path for target.kind=workspace and Loom cache assistance."),
181
195
  gradleUserHome: gradleUserHomeSchema,
@@ -207,7 +221,8 @@ export const batchClassSourceShape = {
207
221
  strictVersion: z.boolean().optional(),
208
222
  concurrency: z.number().int().min(1).max(8).optional().describe("1..8, default 4"),
209
223
  failFast: z.boolean().optional().describe("default false"),
210
- compact: z.boolean().optional().describe("default true"),
224
+ detail: detailParam("summary"),
225
+ include: responseIncludeParam,
211
226
  entries: z
212
227
  .array(batchClassSourceEntrySchema)
213
228
  .min(1)
@@ -262,7 +277,8 @@ export const batchClassMembersShape = {
262
277
  strictVersion: z.boolean().optional(),
263
278
  concurrency: z.number().int().min(1).max(8).optional(),
264
279
  failFast: z.boolean().optional(),
265
- compact: z.boolean().optional(),
280
+ detail: detailParam("summary"),
281
+ include: responseIncludeParam,
266
282
  entries: z.array(batchClassMembersEntrySchema).min(1).max(50)
267
283
  };
268
284
  export const batchClassMembersSchema = z.object(batchClassMembersShape);
@@ -291,7 +307,8 @@ export const batchSymbolExistsShape = {
291
307
  strictVersion: z.boolean().optional(),
292
308
  concurrency: z.number().int().min(1).max(8).optional(),
293
309
  failFast: z.boolean().optional(),
294
- compact: z.boolean().optional(),
310
+ detail: detailParam("summary"),
311
+ include: responseIncludeParam,
295
312
  entries: z.array(batchSymbolExistsEntrySchema).min(1).max(50)
296
313
  };
297
314
  export const batchSymbolExistsSchema = z.object(batchSymbolExistsShape);
@@ -321,7 +338,8 @@ export const batchMappingsShape = {
321
338
  gradleUserHome: gradleUserHomeSchema,
322
339
  concurrency: z.number().int().min(1).max(8).optional(),
323
340
  failFast: z.boolean().optional(),
324
- compact: z.boolean().optional(),
341
+ detail: detailParam("summary"),
342
+ include: responseIncludeParam,
325
343
  entries: z.array(batchMappingsEntrySchema).min(1).max(50)
326
344
  };
327
345
  export const batchMappingsSchema = z.object(batchMappingsShape);
@@ -339,7 +357,8 @@ export const searchClassSourceShape = {
339
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)."),
340
358
  sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first. Used only when queryNamespace triggers translation."),
341
359
  gradleUserHome: gradleUserHomeSchema,
342
- compact: z.boolean().default(false).describe("When true, strip the artifactContents summary and empty fields from the response. Default false.")
360
+ detail: detailParam("standard"),
361
+ include: responseIncludeParam
343
362
  };
344
363
  export const searchClassSourceSchema = z.object(searchClassSourceShape).superRefine((value, ctx) => {
345
364
  if (value.symbolKind && value.intent && value.intent !== "symbol") {
@@ -361,7 +380,8 @@ export const listArtifactFilesShape = {
361
380
  prefix: optionalNonEmptyString,
362
381
  limit: optionalPositiveInt,
363
382
  cursor: optionalNonEmptyString,
364
- compact: z.boolean().default(false).describe("When true, strip the artifactContents summary and empty fields from the response. Default false.")
383
+ detail: detailParam("standard"),
384
+ include: responseIncludeParam
365
385
  };
366
386
  export const listArtifactFilesSchema = z.object(listArtifactFilesShape);
367
387
  export const traceSymbolLifecycleShape = {
@@ -397,8 +417,8 @@ export const findMappingShape = {
397
417
  targetMapping: sourceMappingSchema.describe("obfuscated | mojang | intermediary | yarn"),
398
418
  sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
399
419
  gradleUserHome: gradleUserHomeSchema,
400
- signatureMode: z.enum(["exact", "name-only"]).default("name-only")
401
- .describe("exact: descriptor required for kind=method; name-only (default): match by owner+name only"),
420
+ nameMode: classNameModeSchema.default("auto").describe(NAME_MODE_DESCRIPTION),
421
+ signatureMode: z.enum(["exact", "name-only"]).default("name-only").describe(SIGNATURE_MODE_DESCRIPTION),
402
422
  disambiguation: z
403
423
  .object({
404
424
  ownerHint: optionalNonEmptyString,
@@ -407,9 +427,8 @@ export const findMappingShape = {
407
427
  .partial()
408
428
  .optional(),
409
429
  maxCandidates: optionalPositiveInt.default(5).describe("Limit returned candidates (default 5, max 200). Raise when you need the full candidate list."),
410
- compact: z.boolean().default(true).describe("Omit top-level empty arrays, null/undefined values, and empty objects from the response. "
411
- + "Also omit redundant candidates array for single full-confidence exact-match resolutions. "
412
- + "Enabled by default; set to false for full output.")
430
+ detail: detailParam("summary"),
431
+ include: responseIncludeParam
413
432
  };
414
433
  export const findMappingSchema = z.object(findMappingShape).superRefine((value, ctx) => {
415
434
  if (value.kind === "class") {
@@ -427,7 +446,7 @@ export const findMappingSchema = z.object(findMappingShape).superRefine((value,
427
446
  path: ["descriptor"]
428
447
  });
429
448
  }
430
- if (value.sourceMapping !== "obfuscated" && !value.name.includes(".")) {
449
+ if (value.nameMode !== "auto" && !value.name.includes(".")) {
431
450
  ctx.addIssue({
432
451
  code: z.ZodIssueCode.custom,
433
452
  message: "name must be fully-qualified class name when kind=class.",
@@ -468,6 +487,10 @@ export const findMappingSchema = z.object(findMappingShape).superRefine((value,
468
487
  });
469
488
  }
470
489
  });
490
+ // Strict shortcut for find-mapping(kind=method, signatureMode=exact): identical inputs,
491
+ // but requires a COMPLETE descriptor projection and returns mapping_unavailable when the
492
+ // descriptor's class references cannot all be projected (find-mapping's exact mode is more
493
+ // lenient there). Prefer find-mapping unless you need that strict-completeness guarantee.
471
494
  export const resolveMethodMappingExactShape = {
472
495
  version: nonEmptyString,
473
496
  name: nonEmptyString,
@@ -478,9 +501,8 @@ export const resolveMethodMappingExactShape = {
478
501
  sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
479
502
  gradleUserHome: gradleUserHomeSchema,
480
503
  maxCandidates: optionalPositiveInt.default(5).describe("Limit returned candidates (default 5, max 200). Raise when you need the full candidate list."),
481
- compact: z.boolean().default(true).describe("Omit top-level empty arrays, null/undefined values, and empty objects from the response. "
482
- + "Also omit redundant candidates array for single full-confidence exact-match resolutions. "
483
- + "Enabled by default; set to false for full output.")
504
+ detail: detailParam("summary"),
505
+ include: responseIncludeParam
484
506
  };
485
507
  export const resolveMethodMappingExactSchema = z
486
508
  .object(resolveMethodMappingExactShape)
@@ -520,7 +542,8 @@ export const getClassApiMatrixShape = {
520
542
  includeKinds: classApiKindsSchema.optional().describe("comma-separated: class,field,method"),
521
543
  sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
522
544
  gradleUserHome: gradleUserHomeSchema,
523
- maxRows: optionalPositiveInt.describe("Limit returned rows (max 5000)")
545
+ 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.")
524
547
  };
525
548
  export const getClassApiMatrixSchema = z.object(getClassApiMatrixShape);
526
549
  export const resolveWorkspaceSymbolShape = {
@@ -534,9 +557,8 @@ export const resolveWorkspaceSymbolShape = {
534
557
  sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
535
558
  gradleUserHome: gradleUserHomeSchema,
536
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."),
537
- compact: z.boolean().default(true).describe("Omit top-level empty arrays, null/undefined values, and empty objects from the response. "
538
- + "Also omit redundant candidates array for single full-confidence exact-match resolutions. "
539
- + "Enabled by default; set to false for full output.")
560
+ detail: detailParam("summary"),
561
+ include: responseIncludeParam
540
562
  };
541
563
  export const resolveWorkspaceSymbolSchema = z
542
564
  .object(resolveWorkspaceSymbolShape)
@@ -602,17 +624,15 @@ export const checkSymbolExistsShape = {
602
624
  kind: workspaceSymbolKindSchema.describe("class | field | method"),
603
625
  owner: optionalNonEmptyString,
604
626
  name: nonEmptyString,
605
- descriptor: optionalDescriptorString.describe("required for kind=method unless signatureMode=name-only. Empty strings are treated as omitted."),
627
+ descriptor: optionalDescriptorString.describe("JVM descriptor. Optional when signatureMode='name-only' (default). Empty strings are treated as omitted."),
606
628
  sourceMapping: sourceMappingSchema.describe("obfuscated | mojang | intermediary | yarn"),
607
629
  sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
608
630
  gradleUserHome: gradleUserHomeSchema,
609
- nameMode: classNameModeSchema.default("fqcn").describe("fqcn | auto"),
610
- signatureMode: z.enum(["exact", "name-only"]).default("exact")
611
- .describe("exact: require descriptor for methods; name-only: match by owner+name only"),
631
+ nameMode: classNameModeSchema.default("auto").describe(NAME_MODE_DESCRIPTION),
632
+ signatureMode: z.enum(["exact", "name-only"]).default("name-only").describe(SIGNATURE_MODE_DESCRIPTION),
612
633
  maxCandidates: optionalPositiveInt.default(5).describe("Limit returned candidates (default 5, max 200). Raise when you need the full candidate list."),
613
- compact: z.boolean().default(true).describe("Omit top-level empty arrays, null/undefined values, and empty objects from the response. "
614
- + "Also omit redundant candidates array for single full-confidence exact-match resolutions. "
615
- + "Enabled by default; set to false for full output.")
634
+ detail: detailParam("summary"),
635
+ include: responseIncludeParam
616
636
  };
617
637
  export const checkSymbolExistsSchema = z.object(checkSymbolExistsShape).superRefine((value, ctx) => {
618
638
  if (value.kind === "class") {
@@ -723,7 +743,7 @@ export const validateMixinShape = {
723
743
  ]).describe("One of { mode: 'inline', source }, { mode: 'path', path }, { mode: 'paths', paths[] }, { mode: 'config', configPaths[] }, or { mode: 'project', path }."),
724
744
  sourceRoots: z.array(z.string().min(1)).optional()
725
745
  .describe("Array of source roots for multi-module projects (e.g. ['common/src/main/java', 'neoforge/src/main/java'])"),
726
- version: nonEmptyString.describe("Minecraft version"),
746
+ version: optionalNonEmptyString.describe("Minecraft version. Optional when input.mode='project' (detected from the workspace) or preferProjectVersion=true with projectPath set; required otherwise."),
727
747
  mapping: sourceMappingSchema.optional().describe("obfuscated | mojang | intermediary | yarn"),
728
748
  sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
729
749
  scope: artifactScopeSchema.optional().describe(SOURCE_SCOPE_DESCRIPTION),
@@ -740,8 +760,8 @@ export const validateMixinShape = {
740
760
  .describe("'full'=all warnings; 'aggregated'=group warnings by category with counts and samples. Single validation uses the provided value as-is; batch validation defaults to 'aggregated'"),
741
761
  preferProjectMapping: z.boolean().default(false)
742
762
  .describe("When true, auto-detect mapping from project config even if mapping is explicitly provided"),
743
- reportMode: z.enum(["compact", "full", "summary-first"]).default("full")
744
- .describe("'compact' omits heavy per-result detail, 'summary-first' hoists shared provenance/warnings/incomplete reasons, 'full'=everything"),
763
+ 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."),
745
765
  warningCategoryFilter: z.array(z.enum(["mapping", "configuration", "validation", "resolution", "parse"])).optional()
746
766
  .describe("Only include warnings/issues matching these categories (default: all)"),
747
767
  treatInfoAsWarning: z.boolean().default(true)
@@ -749,7 +769,19 @@ export const validateMixinShape = {
749
769
  includeIssues: z.boolean().default(true)
750
770
  .describe("When false, keep summary fields but omit per-result issues[] payloads")
751
771
  };
752
- export const validateMixinSchema = z.object(validateMixinShape);
772
+ export const validateMixinSchema = z.object(validateMixinShape).superRefine((value, ctx) => {
773
+ if (value.version) {
774
+ return;
775
+ }
776
+ const canDetectVersion = value.input?.mode === "project" || (value.preferProjectVersion === true && Boolean(value.projectPath));
777
+ if (!canDetectVersion) {
778
+ ctx.addIssue({
779
+ code: z.ZodIssueCode.custom,
780
+ path: ["version"],
781
+ message: "version is required unless input.mode='project' or preferProjectVersion=true with projectPath set (so the Minecraft version can be detected from gradle.properties)."
782
+ });
783
+ }
784
+ });
753
785
  export const validateAccessWidenerShape = {
754
786
  content: nonEmptyString.describe("Access Widener file content"),
755
787
  version: nonEmptyString.describe("Minecraft version"),
@@ -782,7 +814,7 @@ export const analyzeModJarSchema = z.object(analyzeModJarShape);
782
814
  export const getRegistryDataShape = {
783
815
  version: nonEmptyString.describe("Minecraft version (e.g. 1.21)"),
784
816
  registry: optionalNonEmptyString.describe('Optional registry name (e.g. "block", "item", "minecraft:biome"). Omit to list all registries.'),
785
- includeData: z.boolean().default(true).describe("When false, return registry names/counts without full entry bodies"),
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)."),
786
818
  maxEntriesPerRegistry: optionalPositiveInt.describe("Limit returned entries per registry body")
787
819
  };
788
820
  export const getRegistryDataSchema = z.object(getRegistryDataShape);
@@ -826,7 +858,11 @@ export const remapModJarShape = {
826
858
  inputJar: nonEmptyString.describe("Path to the mod JAR file"),
827
859
  outputJar: optionalNonEmptyString.describe("Output path for remapped JAR (auto-generated if omitted)"),
828
860
  mcVersion: optionalNonEmptyString.describe("Minecraft version (auto-detected from mod metadata if omitted)"),
829
- targetMapping: remapTargetSchema.describe("yarn | mojang")
861
+ targetMapping: remapTargetSchema.describe("yarn | mojang"),
862
+ forceRemap: z
863
+ .boolean()
864
+ .optional()
865
+ .describe("Skip the cache and re-resolve the newest yarn build (busts a stale remap)")
830
866
  };
831
867
  export const remapModJarSchema = z.object(remapModJarShape);
832
868
  export const emptySchema = z.object({}).passthrough();
package/dist/types.d.ts CHANGED
@@ -112,6 +112,7 @@ export interface Config {
112
112
  fetchTimeoutMs: number;
113
113
  fetchRetries: number;
114
114
  searchScanPageSize: number;
115
+ searchScanMaxBytes: number;
115
116
  indexInsertChunkSize: number;
116
117
  maxMappingGraphCache: number;
117
118
  maxSignatureCache: number;
@@ -145,6 +145,11 @@ export class VersionDiffService {
145
145
  });
146
146
  }
147
147
  const warnings = [];
148
+ if (input.maxClassResults != null &&
149
+ Number.isFinite(input.maxClassResults) &&
150
+ input.maxClassResults > MAX_CLASS_RESULTS_LIMIT) {
151
+ warnings.push(`maxClassResults was clamped to ${MAX_CLASS_RESULTS_LIMIT} from ${input.maxClassResults}.`);
152
+ }
148
153
  const startedAt = Date.now();
149
154
  log("info", "version-diff.start", { fromVersion, toVersion, category });
150
155
  const includeClasses = category === "classes" || category === "all";
@@ -16,6 +16,7 @@ export type ListVersionsOutput = {
16
16
  snapshots?: VersionEntry[];
17
17
  cached: string[];
18
18
  totalAvailable: number;
19
+ warnings?: string[];
19
20
  };
20
21
  export type ResolvedVersionJar = {
21
22
  version: string;
@@ -47,6 +48,7 @@ export declare class VersionService {
47
48
  private manifestCache;
48
49
  private readonly versionDetailCache;
49
50
  private readonly resolveLocks;
51
+ private indexWriteChain;
50
52
  constructor(config: Config, fetchFn?: typeof fetch);
51
53
  listVersions(input?: ListVersionsInput): Promise<ListVersionsOutput>;
52
54
  listVersionIds(input?: ListVersionIdsInput): Promise<string[]>;
@@ -60,6 +62,7 @@ export declare class VersionService {
60
62
  private cacheIndexPath;
61
63
  private loadCacheIndex;
62
64
  private recordCacheEntry;
65
+ private recordCacheEntryInternal;
63
66
  private trimVersionDetailCache;
64
67
  }
65
68
  /**
@@ -38,6 +38,9 @@ export class VersionService {
38
38
  manifestCache;
39
39
  versionDetailCache = new Map();
40
40
  resolveLocks = new Map();
41
+ // Serializes index.json read-modify-write so parallel resolutions of different
42
+ // versions can't clobber each other (resolveLocks only guards the same version).
43
+ indexWriteChain = Promise.resolve();
41
44
  constructor(config, fetchFn = globalThis.fetch) {
42
45
  this.config = config;
43
46
  this.fetchFn = fetchFn;
@@ -47,6 +50,10 @@ export class VersionService {
47
50
  const manifest = await this.fetchManifest();
48
51
  const includeSnapshots = input.includeSnapshots ?? false;
49
52
  const limit = clampLimit(input.limit, 20, 200);
53
+ const warnings = [];
54
+ if (input.limit != null && Number.isFinite(input.limit) && input.limit > 200) {
55
+ warnings.push(`limit was clamped to 200 from ${input.limit}.`);
56
+ }
50
57
  const versions = manifest.versions ?? [];
51
58
  const releases = versions
52
59
  .filter((entry) => entry.type === "release")
@@ -65,7 +72,8 @@ export class VersionService {
65
72
  releases,
66
73
  snapshots: includeSnapshots ? snapshots : undefined,
67
74
  cached: Array.from(new Set(cached)).sort((a, b) => a.localeCompare(b)),
68
- totalAvailable: versions.length
75
+ totalAvailable: versions.length,
76
+ ...(warnings.length ? { warnings } : {})
69
77
  };
70
78
  }
71
79
  async listVersionIds(input = {}) {
@@ -389,7 +397,15 @@ export class VersionService {
389
397
  return { entries: [] };
390
398
  }
391
399
  }
392
- async recordCacheEntry(entry) {
400
+ recordCacheEntry(entry) {
401
+ // Chain onto the previous write so reads and writes never interleave. The
402
+ // caller still awaits its own write's result, but the shared chain swallows
403
+ // failures so one rejected write cannot poison subsequent writes.
404
+ const next = this.indexWriteChain.then(() => this.recordCacheEntryInternal(entry));
405
+ this.indexWriteChain = next.catch(() => undefined);
406
+ return next;
407
+ }
408
+ async recordCacheEntryInternal(entry) {
393
409
  const indexPath = this.cacheIndexPath();
394
410
  const existing = await this.loadCacheIndex();
395
411
  const deduped = existing.entries.filter((candidate) => candidate.version !== entry.version);
@@ -0,0 +1,18 @@
1
+ export type WarningCategory = "pagination" | "mapping" | "coverage" | "validation" | "general";
2
+ export type WarningSeverity = "warning" | "info";
3
+ export type WarningDetail = {
4
+ /** Stable machine code for the warning family. */
5
+ code: string;
6
+ category: WarningCategory;
7
+ severity: WarningSeverity;
8
+ /** Index into `meta.warnings[]` that holds the original human-readable text. */
9
+ index: number;
10
+ /** Input fields a caller can adjust to address the warning, when applicable. */
11
+ affectedFields?: string[];
12
+ };
13
+ /**
14
+ * Build the structured `warningDetails[]` companion for a list of warning strings.
15
+ * The mapping is 1:1 and order-preserving, so each entry's `index` equals its
16
+ * position and dereferences the text via `warnings[index]`.
17
+ */
18
+ export declare function classifyWarnings(warnings: string[]): WarningDetail[];
@@ -0,0 +1,63 @@
1
+ // Structured companion to the string `meta.warnings`. Each warning string is
2
+ // classified into a high-value family so agents can branch on category/severity
3
+ // without parsing prose. The original text is NOT duplicated here; each entry
4
+ // references its warning by position via `index` (read `meta.warnings[index]`).
5
+ // First matching rule wins; more specific patterns precede broader ones.
6
+ const WARNING_RULES = [
7
+ {
8
+ test: /Member list was truncated|truncated to \d+ (entries|rows)|Raise maxCandidates|Raise maxRows/i,
9
+ code: "result_truncated",
10
+ category: "pagination",
11
+ severity: "info",
12
+ affectedFields: ["maxMembers", "maxRows", "maxCandidates", "cursor"]
13
+ },
14
+ {
15
+ test: /\bwas clamped to\b/i,
16
+ code: "input_clamped",
17
+ category: "validation",
18
+ severity: "info"
19
+ },
20
+ {
21
+ test: /are not indexed|non-Java resources are not indexed/i,
22
+ code: "resource_not_indexed",
23
+ category: "coverage",
24
+ severity: "info"
25
+ },
26
+ {
27
+ test: /namespace translation requires a version|could not be applied because the artifact has no version|Could not (re)?map .* (from .* to|to .* namespace)|Remap failed for|Mapping lookup failed|No exact class symbol matched|Unsupported .* namespace/i,
28
+ code: "namespace_fallback",
29
+ category: "mapping",
30
+ severity: "warning",
31
+ affectedFields: ["mapping", "version"]
32
+ },
33
+ {
34
+ test: /falling back to vanilla|resolution failed; falling back|sources jar\.?\s*Falling back|does not include net\.minecraft/i,
35
+ code: "partial_coverage",
36
+ category: "coverage",
37
+ severity: "warning",
38
+ affectedFields: ["scope"]
39
+ }
40
+ ];
41
+ function classifyWarning(message, index) {
42
+ for (const rule of WARNING_RULES) {
43
+ if (rule.test.test(message)) {
44
+ return {
45
+ code: rule.code,
46
+ category: rule.category,
47
+ severity: rule.severity,
48
+ index,
49
+ ...(rule.affectedFields ? { affectedFields: rule.affectedFields } : {})
50
+ };
51
+ }
52
+ }
53
+ return { code: "general", category: "general", severity: "info", index };
54
+ }
55
+ /**
56
+ * Build the structured `warningDetails[]` companion for a list of warning strings.
57
+ * The mapping is 1:1 and order-preserving, so each entry's `index` equals its
58
+ * position and dereferences the text via `warnings[index]`.
59
+ */
60
+ export function classifyWarnings(warnings) {
61
+ return warnings.map((message, index) => classifyWarning(message, index));
62
+ }
63
+ //# sourceMappingURL=warning-details.js.map
package/docs/README-ja.md CHANGED
@@ -15,7 +15,7 @@
15
15
 
16
16
  stdio で動作し、Claude Desktop、Claude Code、VS Code、Codex CLI、Gemini CLI などの MCP 対応クライアントから利用できます。
17
17
 
18
- **37 ツール**(6 エントリー + 31 エキスパート) | **7 リソース** | **4 マッピング名前空間** | **SQLite ベースのキャッシュ**
18
+ **41 ツール**(6 エントリー + 35 エキスパート) | **9 リソース** | **4 マッピング名前空間** | **SQLite ベースのキャッシュ**
19
19
 
20
20
  ## 特長
21
21
 
@@ -301,7 +301,7 @@ Minecraft バージョン間でのクラス / レジストリ変更比較と、
301
301
  | ツール | 役割 |
302
302
  | --- | --- |
303
303
  | `find-mapping` | クラス、フィールド、メソッドのシンボルに対するマッピング候補を調べる |
304
- | `resolve-method-mapping-exact` | ownernamedescriptor の厳密一致で 1 つのメソッドマッピングを解決する |
304
+ | `resolve-method-mapping-exact` | `find-mapping`(kind=method, signatureMode=exact)の厳密版ショートカット。owner+name+descriptor の三つ組が必須 |
305
305
  | `get-class-api-matrix` | 1 つのクラス API を `obfuscated`、`mojang`、`intermediary`、`yarn` で見比べる |
306
306
  | `resolve-workspace-symbol` | Gradle ワークスペースからコンパイル時に見えるシンボル名を解決する |
307
307
  | `check-symbol-exists` | 名前空間内でクラス、フィールド、メソッドが存在するかを確認する |
package/docs/examples.md CHANGED
@@ -105,7 +105,7 @@ Start with these top-level workflow tools when possible. They cover the common w
105
105
  "tool": "get-class-source",
106
106
  "arguments": {
107
107
  "target": {
108
- "type": "artifact",
108
+ "kind": "artifact",
109
109
  "artifactId": "<artifact-id>"
110
110
  },
111
111
  "className": "net.minecraft.server.Main",
@@ -137,7 +137,7 @@ Start with these top-level workflow tools when possible. They cover the common w
137
137
  "tool": "get-class-members",
138
138
  "arguments": {
139
139
  "target": {
140
- "type": "artifact",
140
+ "kind": "artifact",
141
141
  "artifactId": "<artifact-id>"
142
142
  },
143
143
  "className": "net.minecraft.server.Main",
@@ -395,6 +395,30 @@ Start with these top-level workflow tools when possible. They cover the common w
395
395
 
396
396
  If the input JAR was already built with Mojang mappings, use `targetMapping: "mojang"` to get a copied output JAR and a `fromMapping: "mojang"` result.
397
397
 
398
+ ### Bridge a mod hit to a Minecraft-side check
399
+
400
+ When `search-mod-source` or `get-mod-class-source` surfaces a class or member that
401
+ looks like a Minecraft target (for example a Mixin `@Shadow`/`@Inject` target, or a
402
+ symbol from a crash stack), verify it against the target Minecraft version before
403
+ trusting it. Feed the owner/member into `verify-mixin-target`. When your owner/member
404
+ are in a readable namespace (e.g. `mojang`/`yarn`) but the resolved artifact is
405
+ obfuscated, set `autoRemap: true` to translate them automatically in one call:
406
+
407
+ ```json
408
+ {
409
+ "tool": "verify-mixin-target",
410
+ "arguments": {
411
+ "target": { "kind": "version", "value": "1.21.10" },
412
+ "owner": "net.minecraft.world.entity.LivingEntity",
413
+ "member": { "kind": "method", "name": "tickServer", "descriptor": "()V" },
414
+ "mapping": "mojang",
415
+ "autoRemap": true
416
+ }
417
+ }
418
+ ```
419
+
420
+ For a plain "does this symbol exist in this version?" question, use `analyze-symbol task="exists"` instead.
421
+
398
422
  ## Validation
399
423
 
400
424
  ### Validate Mixin source