@adhisang/minecraft-modding-mcp 3.0.0 → 3.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -90,9 +90,32 @@ const sourceLookupTargetSchema = z.discriminatedUnion("type", [
90
90
  const RESOLVE_ARTIFACT_TARGET_DESCRIPTION = "Object with kind and value. Example: {\"kind\":\"version\",\"value\":\"1.21.10\"}. Must be an object, not a string.";
91
91
  const SOURCE_LOOKUP_TARGET_DESCRIPTION = "Object: {\"type\":\"resolve\",\"kind\":\"version\",\"value\":\"1.21.10\"} or {\"type\":\"artifact\",\"artifactId\":\"...\"}. Must be an object, not a string.";
92
92
  const SOURCE_SCOPE_DESCRIPTION = 'vanilla = Mojang client jar only; merged = Loom cache discovery (default); loader = currently behaves the same as "merged".';
93
+ const SUGGESTED_CALL_DEFAULTS = {
94
+ allowDecompile: true,
95
+ preferProjectVersion: false,
96
+ strictVersion: false,
97
+ mode: "metadata",
98
+ access: "public",
99
+ includeSynthetic: false,
100
+ includeInherited: false,
101
+ hideUncertain: false,
102
+ explain: false,
103
+ preferProjectMapping: false,
104
+ minSeverity: "all",
105
+ reportMode: "full",
106
+ treatInfoAsWarning: true,
107
+ includeIssues: true
108
+ };
109
+ function isSuggestedCallDefault(field, value) {
110
+ return value === SUGGESTED_CALL_DEFAULTS[field];
111
+ }
112
+ const ANALYZE_MOD_INCLUDE_GROUPS = ["warnings", "files", "source", "samples", "timings"];
113
+ const ANALYZE_MOD_LEGACY_METADATA_INCLUDES = ["metadata", "entrypoints", "mixins", "dependencies"];
114
+ const VALIDATE_PROJECT_INCLUDE_GROUPS = ["warnings", "issues", "workspace", "recovery"];
115
+ const VALIDATE_PROJECT_LEGACY_WORKSPACE_INCLUDES = ["detectedConfig", "mixins", "accessWideners"];
93
116
  const listVersionsShape = {
94
- includeSnapshots: z.boolean().optional().describe("default false"),
95
- limit: optionalPositiveInt.describe("default 20, max 200")
117
+ includeSnapshots: z.boolean().default(false),
118
+ limit: optionalPositiveInt.default(20).describe("max 200")
96
119
  };
97
120
  const listVersionsSchema = z.object(listVersionsShape);
98
121
  const resolveArtifactShape = {
@@ -102,7 +125,7 @@ const resolveArtifactShape = {
102
125
  }).describe(RESOLVE_ARTIFACT_TARGET_DESCRIPTION),
103
126
  mapping: sourceMappingSchema.optional().describe("obfuscated | mojang | intermediary | yarn"),
104
127
  sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
105
- allowDecompile: z.boolean().optional().describe("default true"),
128
+ allowDecompile: z.boolean().default(true),
106
129
  projectPath: optionalNonEmptyString.describe("Optional workspace root path for Loom cache-assisted source resolution"),
107
130
  scope: artifactScopeSchema.optional().describe(SOURCE_SCOPE_DESCRIPTION),
108
131
  preferProjectVersion: z.boolean().optional().describe("When true, detect MC version from gradle.properties and override target.value"),
@@ -111,11 +134,11 @@ const resolveArtifactShape = {
111
134
  const resolveArtifactSchema = z.object(resolveArtifactShape);
112
135
  const getClassSourceShape = {
113
136
  className: nonEmptyString,
114
- mode: sourceModeSchema.optional().describe("metadata (default) = symbol outline only; snippet = source with default maxLines=200; full = entire source"),
137
+ mode: sourceModeSchema.default("metadata").describe("metadata = symbol outline only; snippet = source with default maxLines=200; full = entire source"),
115
138
  target: sourceLookupTargetSchema.describe(SOURCE_LOOKUP_TARGET_DESCRIPTION),
116
139
  mapping: sourceMappingSchema.optional().describe("obfuscated | mojang | intermediary | yarn"),
117
140
  sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
118
- allowDecompile: z.boolean().optional().describe("default true"),
141
+ allowDecompile: z.boolean().default(true),
119
142
  projectPath: optionalNonEmptyString.describe("Optional workspace root path for Loom cache-assisted source resolution"),
120
143
  scope: artifactScopeSchema.optional().describe(SOURCE_SCOPE_DESCRIPTION),
121
144
  preferProjectVersion: z.boolean().optional().describe("When true, detect MC version from gradle.properties and override target.value"),
@@ -144,10 +167,10 @@ const getClassMembersShape = {
144
167
  target: sourceLookupTargetSchema.describe(SOURCE_LOOKUP_TARGET_DESCRIPTION),
145
168
  mapping: sourceMappingSchema.optional().describe("obfuscated | mojang | intermediary | yarn (default obfuscated)"),
146
169
  sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
147
- allowDecompile: z.boolean().optional().describe("default true"),
148
- access: memberAccessSchema.optional().describe("public | all (default public)"),
149
- includeSynthetic: z.boolean().optional().describe("default false"),
150
- includeInherited: z.boolean().optional().describe("default false"),
170
+ allowDecompile: z.boolean().default(true),
171
+ access: memberAccessSchema.default("public").describe("public | all"),
172
+ includeSynthetic: z.boolean().default(false),
173
+ includeInherited: z.boolean().default(false),
151
174
  memberPattern: optionalNonEmptyString,
152
175
  maxMembers: optionalPositiveInt.describe("default 500, max 5000"),
153
176
  projectPath: optionalNonEmptyString,
@@ -164,8 +187,8 @@ const searchClassSourceShape = {
164
187
  packagePrefix: optionalNonEmptyString,
165
188
  fileGlob: optionalNonEmptyString,
166
189
  symbolKind: searchSymbolKindSchema.optional().describe("class | interface | enum | record | method | field"),
167
- queryMode: z.enum(["auto", "token", "literal"]).optional().describe("auto (default): FTS5 with literal fallback for separator queries; token: FTS5 only; literal: substring scan only"),
168
- limit: optionalPositiveInt.describe("default 20"),
190
+ 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"),
191
+ limit: optionalPositiveInt.default(20),
169
192
  cursor: optionalNonEmptyString
170
193
  };
171
194
  const searchClassSourceSchema = z.object(searchClassSourceShape).superRefine((value, ctx) => {
@@ -197,9 +220,9 @@ const traceSymbolLifecycleShape = {
197
220
  toVersion: optionalNonEmptyString,
198
221
  mapping: sourceMappingSchema.optional().describe("obfuscated | mojang | intermediary | yarn (default obfuscated)"),
199
222
  sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
200
- includeSnapshots: z.boolean().optional().describe("default false"),
201
- maxVersions: optionalPositiveInt.describe("default 120, max 400"),
202
- includeTimeline: z.boolean().optional().describe("default false")
223
+ includeSnapshots: z.boolean().default(false),
224
+ maxVersions: optionalPositiveInt.default(120).describe("max 400"),
225
+ includeTimeline: z.boolean().default(false)
203
226
  };
204
227
  const traceSymbolLifecycleSchema = z.object(traceSymbolLifecycleShape);
205
228
  const diffClassSignaturesShape = {
@@ -208,7 +231,7 @@ const diffClassSignaturesShape = {
208
231
  toVersion: nonEmptyString,
209
232
  mapping: sourceMappingSchema.optional().describe("obfuscated | mojang | intermediary | yarn (default obfuscated)"),
210
233
  sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
211
- includeFullDiff: z.boolean().optional().describe("When false, omit from/to snapshots from modified entries and keep only key+changed")
234
+ includeFullDiff: z.boolean().default(true).describe("When false, omit from/to snapshots from modified entries and keep only key+changed")
212
235
  };
213
236
  const diffClassSignaturesSchema = z.object(diffClassSignaturesShape);
214
237
  const findMappingShape = {
@@ -227,7 +250,7 @@ const findMappingShape = {
227
250
  })
228
251
  .partial()
229
252
  .optional(),
230
- maxCandidates: optionalPositiveInt.describe("Limit returned candidates (default 200, max 200)")
253
+ maxCandidates: optionalPositiveInt.default(200).describe("Limit returned candidates (max 200)")
231
254
  };
232
255
  const findMappingSchema = z.object(findMappingShape).superRefine((value, ctx) => {
233
256
  if (value.kind === "class") {
@@ -245,7 +268,7 @@ const findMappingSchema = z.object(findMappingShape).superRefine((value, ctx) =>
245
268
  path: ["descriptor"]
246
269
  });
247
270
  }
248
- if (!value.name.includes(".")) {
271
+ if (value.sourceMapping !== "obfuscated" && !value.name.includes(".")) {
249
272
  ctx.addIssue({
250
273
  code: z.ZodIssueCode.custom,
251
274
  message: "name must be fully-qualified class name when kind=class.",
@@ -294,7 +317,7 @@ const resolveMethodMappingExactShape = {
294
317
  sourceMapping: sourceMappingSchema.describe("obfuscated | mojang | intermediary | yarn"),
295
318
  targetMapping: sourceMappingSchema.describe("obfuscated | mojang | intermediary | yarn"),
296
319
  sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
297
- maxCandidates: optionalPositiveInt.describe("Limit returned candidates (default 200, max 200)")
320
+ maxCandidates: optionalPositiveInt.default(200).describe("Limit returned candidates (max 200)")
298
321
  };
299
322
  const resolveMethodMappingExactSchema = z
300
323
  .object(resolveMethodMappingExactShape)
@@ -345,7 +368,7 @@ const resolveWorkspaceSymbolShape = {
345
368
  descriptor: optionalNonEmptyString,
346
369
  sourceMapping: sourceMappingSchema.describe("obfuscated | mojang | intermediary | yarn"),
347
370
  sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
348
- maxCandidates: optionalPositiveInt.describe("Limit returned candidates for field/method lookups (default 200, max 200)")
371
+ maxCandidates: optionalPositiveInt.default(200).describe("Limit returned candidates for field/method lookups (max 200)")
349
372
  };
350
373
  const resolveWorkspaceSymbolSchema = z
351
374
  .object(resolveWorkspaceSymbolShape)
@@ -414,10 +437,10 @@ const checkSymbolExistsShape = {
414
437
  descriptor: optionalNonEmptyString.describe("required for kind=method unless signatureMode=name-only"),
415
438
  sourceMapping: sourceMappingSchema.describe("obfuscated | mojang | intermediary | yarn"),
416
439
  sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
417
- nameMode: classNameModeSchema.optional().describe("fqcn | auto (default fqcn)"),
418
- signatureMode: z.enum(["exact", "name-only"]).optional()
419
- .describe("exact (default): require descriptor for methods; name-only: match by owner+name only"),
420
- maxCandidates: optionalPositiveInt.describe("Limit returned candidates (default 200, max 200)")
440
+ nameMode: classNameModeSchema.default("fqcn").describe("fqcn | auto"),
441
+ signatureMode: z.enum(["exact", "name-only"]).default("exact")
442
+ .describe("exact: require descriptor for methods; name-only: match by owner+name only"),
443
+ maxCandidates: optionalPositiveInt.default(200).describe("Limit returned candidates (max 200)")
421
444
  };
422
445
  const checkSymbolExistsSchema = z.object(checkSymbolExistsShape).superRefine((value, ctx) => {
423
446
  if (value.kind === "class") {
@@ -478,7 +501,7 @@ const checkSymbolExistsSchema = z.object(checkSymbolExistsShape).superRefine((va
478
501
  });
479
502
  const nbtToJsonShape = {
480
503
  nbtBase64: nonEmptyString,
481
- compression: decodeCompressionSchema.optional().describe("none | gzip | auto (default auto)")
504
+ compression: decodeCompressionSchema.default("auto").describe("none | gzip | auto")
482
505
  };
483
506
  const nbtToJsonSchema = z.object(nbtToJsonShape);
484
507
  const nbtPatchOperationSchema = z
@@ -495,12 +518,12 @@ const nbtApplyJsonPatchShape = {
495
518
  const nbtApplyJsonPatchSchema = z.object(nbtApplyJsonPatchShape);
496
519
  const jsonToNbtShape = {
497
520
  typedJson: z.unknown(),
498
- compression: encodeCompressionSchema.optional().describe("none | gzip (default none)")
521
+ compression: encodeCompressionSchema.default("none").describe("none | gzip")
499
522
  };
500
523
  const jsonToNbtSchema = z.object(jsonToNbtShape);
501
524
  const indexArtifactShape = {
502
525
  artifactId: nonEmptyString,
503
- force: z.boolean().optional().describe("default false")
526
+ force: z.boolean().default(false)
504
527
  };
505
528
  const indexArtifactSchema = z.object(indexArtifactShape);
506
529
  const validateMixinShape = {
@@ -534,23 +557,23 @@ const validateMixinShape = {
534
557
  scope: artifactScopeSchema.optional().describe(SOURCE_SCOPE_DESCRIPTION),
535
558
  projectPath: optionalNonEmptyString.describe("Optional workspace root path for Loom cache-assisted source resolution"),
536
559
  preferProjectVersion: z.boolean().optional().describe("When true, detect MC version from gradle.properties and override version"),
537
- minSeverity: z.enum(["error", "warning", "all"]).optional()
538
- .describe("'error'=errors only, 'warning'=errors+warnings, 'all'=everything (default 'all')"),
539
- hideUncertain: z.boolean().optional()
540
- .describe("Omit issues with confidence='uncertain' (default false)"),
541
- explain: z.boolean().optional()
542
- .describe("When true, enrich each issue with explanation and suggestedCall for agent recovery (default false)"),
560
+ minSeverity: z.enum(["error", "warning", "all"]).default("all")
561
+ .describe("'error'=errors only, 'warning'=errors+warnings, 'all'=everything"),
562
+ hideUncertain: z.boolean().default(false)
563
+ .describe("Omit issues with confidence='uncertain'"),
564
+ explain: z.boolean().default(false)
565
+ .describe("When true, enrich each issue with explanation and suggestedCall for agent recovery"),
543
566
  warningMode: z.enum(["full", "aggregated"]).optional()
544
- .describe("'full'=all warnings (default), 'aggregated'=group warnings by category with counts and samples"),
545
- preferProjectMapping: z.boolean().optional()
567
+ .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'"),
568
+ preferProjectMapping: z.boolean().default(false)
546
569
  .describe("When true, auto-detect mapping from project config even if mapping is explicitly provided"),
547
- reportMode: z.enum(["compact", "full", "summary-first"]).optional()
548
- .describe("'compact' omits heavy per-result detail, 'summary-first' hoists shared provenance/warnings/incomplete reasons, 'full'=everything (default)"),
570
+ reportMode: z.enum(["compact", "full", "summary-first"]).default("full")
571
+ .describe("'compact' omits heavy per-result detail, 'summary-first' hoists shared provenance/warnings/incomplete reasons, 'full'=everything"),
549
572
  warningCategoryFilter: z.array(z.enum(["mapping", "configuration", "validation", "resolution", "parse"])).optional()
550
573
  .describe("Only include warnings/issues matching these categories (default: all)"),
551
- treatInfoAsWarning: z.boolean().optional()
552
- .describe("When false, suppress info-severity structured warnings from output (default true)"),
553
- includeIssues: z.boolean().optional()
574
+ treatInfoAsWarning: z.boolean().default(true)
575
+ .describe("When false, suppress info-severity structured warnings from output"),
576
+ includeIssues: z.boolean().default(true)
554
577
  .describe("When false, keep summary fields but omit per-result issues[] payloads")
555
578
  };
556
579
  const validateMixinSchema = z.object(validateMixinShape);
@@ -563,13 +586,13 @@ const validateAccessWidenerShape = {
563
586
  const validateAccessWidenerSchema = z.object(validateAccessWidenerShape);
564
587
  const analyzeModJarShape = {
565
588
  jarPath: nonEmptyString.describe("Local path to the mod JAR file"),
566
- includeClasses: z.boolean().optional().describe("Include full class listing (default false)")
589
+ includeClasses: z.boolean().default(false).describe("Include full class listing")
567
590
  };
568
591
  const analyzeModJarSchema = z.object(analyzeModJarShape);
569
592
  const getRegistryDataShape = {
570
593
  version: nonEmptyString.describe("Minecraft version (e.g. 1.21)"),
571
594
  registry: optionalNonEmptyString.describe('Optional registry name (e.g. "block", "item", "minecraft:biome"). Omit to list all registries.'),
572
- includeData: z.boolean().optional().describe("When false, return registry names/counts without full entry bodies"),
595
+ includeData: z.boolean().default(true).describe("When false, return registry names/counts without full entry bodies"),
573
596
  maxEntriesPerRegistry: optionalPositiveInt.describe("Limit returned entries per registry body")
574
597
  };
575
598
  const getRegistryDataSchema = z.object(getRegistryDataShape);
@@ -578,15 +601,15 @@ const compareVersionsCategorySchema = z.enum(COMPARE_VERSIONS_CATEGORIES);
578
601
  const compareVersionsShape = {
579
602
  fromVersion: nonEmptyString.describe("Older Minecraft version (e.g. 1.20.4)"),
580
603
  toVersion: nonEmptyString.describe("Newer Minecraft version (e.g. 1.21)"),
581
- category: compareVersionsCategorySchema.optional().describe("classes | registry | all (default all)"),
604
+ category: compareVersionsCategorySchema.default("all").describe("classes | registry | all"),
582
605
  packageFilter: optionalNonEmptyString.describe("Filter classes to a package prefix (e.g. net.minecraft.world.item)"),
583
- maxClassResults: optionalPositiveInt.describe("Max class results per direction (default 500, max 5000)")
606
+ maxClassResults: optionalPositiveInt.default(500).describe("Max class results per direction (max 5000)")
584
607
  };
585
608
  const compareVersionsSchema = z.object(compareVersionsShape);
586
609
  const decompileModJarShape = {
587
610
  jarPath: nonEmptyString.describe("Local path to the mod JAR file"),
588
611
  className: optionalNonEmptyString.describe("Optional fully-qualified class name to view source. Omit to list all classes."),
589
- includeFiles: z.boolean().optional().describe("When false, omit the full class list and return counts only"),
612
+ includeFiles: z.boolean().default(true).describe("When false, omit the full class list and return counts only"),
590
613
  maxFiles: optionalPositiveInt.describe("Limit returned class names when files are included")
591
614
  };
592
615
  const decompileModJarSchema = z.object(decompileModJarShape);
@@ -603,8 +626,8 @@ const modSearchTypeSchema = z.enum(MOD_SEARCH_TYPES);
603
626
  const searchModSourceShape = {
604
627
  jarPath: nonEmptyString.describe("Local path to the mod JAR file"),
605
628
  query: nonEmptyString.describe("Search pattern (regex or literal string)"),
606
- searchType: modSearchTypeSchema.optional().describe("class | method | field | content | all (default all)"),
607
- limit: optionalPositiveInt.describe("Max results (default 50, max 200)")
629
+ searchType: modSearchTypeSchema.default("all").describe("class | method | field | content | all"),
630
+ limit: optionalPositiveInt.default(50).describe("Max results (max 200)")
608
631
  };
609
632
  const searchModSourceSchema = z.object(searchModSourceShape);
610
633
  const REMAP_TARGETS = ["yarn", "mojang"];
@@ -661,6 +684,7 @@ const inspectMinecraftService = new InspectMinecraftService({
661
684
  listVersions: (input) => sourceService.listVersions(input),
662
685
  resolveArtifact: (input) => sourceService.resolveArtifact(input),
663
686
  findClass: (input) => Promise.resolve(sourceService.findClass(input)),
687
+ checkSymbolExists: (input) => sourceService.checkSymbolExists(input),
664
688
  getClassSource: (input) => sourceService.getClassSource(input),
665
689
  getClassMembers: (input) => sourceService.getClassMembers(input),
666
690
  searchClassSource: (input) => sourceService.searchClassSource(input),
@@ -908,7 +932,10 @@ function copySourceLookupSuggestionFields(tool, source) {
908
932
  : ["className", "mapping", "sourcePriority", "projectPath", "scope", "access", "memberPattern"];
909
933
  for (const field of stringFields) {
910
934
  const value = source[field];
911
- if (typeof value === "string" && value.trim()) {
935
+ if (typeof value === "string" &&
936
+ value.trim() &&
937
+ (!Object.prototype.hasOwnProperty.call(SUGGESTED_CALL_DEFAULTS, field) ||
938
+ !isSuggestedCallDefault(field, value))) {
912
939
  result[field] = value;
913
940
  }
914
941
  }
@@ -926,7 +953,9 @@ function copySourceLookupSuggestionFields(tool, source) {
926
953
  : ["allowDecompile", "preferProjectVersion", "strictVersion", "includeSynthetic", "includeInherited"];
927
954
  for (const field of booleanFields) {
928
955
  const value = source[field];
929
- if (typeof value === "boolean") {
956
+ if (typeof value === "boolean" &&
957
+ (!Object.prototype.hasOwnProperty.call(SUGGESTED_CALL_DEFAULTS, field) ||
958
+ !isSuggestedCallDefault(field, value))) {
930
959
  result[field] = value;
931
960
  }
932
961
  }
@@ -946,7 +975,10 @@ function copyValidateMixinSharedParams(source) {
946
975
  ];
947
976
  for (const field of stringFields) {
948
977
  const value = source[field];
949
- if (typeof value === "string" && value.trim()) {
978
+ if (typeof value === "string" &&
979
+ value.trim() &&
980
+ (!Object.prototype.hasOwnProperty.call(SUGGESTED_CALL_DEFAULTS, field) ||
981
+ !isSuggestedCallDefault(field, value))) {
950
982
  result[field] = value;
951
983
  }
952
984
  }
@@ -960,7 +992,9 @@ function copyValidateMixinSharedParams(source) {
960
992
  ];
961
993
  for (const field of booleanFields) {
962
994
  const value = source[field];
963
- if (typeof value === "boolean") {
995
+ if (typeof value === "boolean" &&
996
+ (!Object.prototype.hasOwnProperty.call(SUGGESTED_CALL_DEFAULTS, field) ||
997
+ !isSuggestedCallDefault(field, value))) {
964
998
  result[field] = value;
965
999
  }
966
1000
  }
@@ -1098,7 +1132,8 @@ function buildResolveArtifactSuggestedParams(normalizedInput) {
1098
1132
  const booleanFields = ["allowDecompile", "preferProjectVersion", "strictVersion"];
1099
1133
  for (const field of booleanFields) {
1100
1134
  const value = record[field];
1101
- if (typeof value === "boolean") {
1135
+ if (typeof value === "boolean" &&
1136
+ !isSuggestedCallDefault(field, value)) {
1102
1137
  result[field] = value;
1103
1138
  }
1104
1139
  }
@@ -1124,6 +1159,184 @@ function buildSourceLookupSuggestedParams(tool, normalizedInput) {
1124
1159
  }
1125
1160
  return result;
1126
1161
  }
1162
+ function filterAllowedIncludeValues(values, allowed) {
1163
+ if (!values?.length) {
1164
+ return [];
1165
+ }
1166
+ const allowedSet = new Set(allowed);
1167
+ const filtered = values.filter((value) => allowedSet.has(value));
1168
+ return [...new Set(filtered)];
1169
+ }
1170
+ function buildAnalyzeModSuggestedParams(normalizedInput) {
1171
+ const record = asObjectRecord(normalizedInput);
1172
+ if (!record) {
1173
+ return {
1174
+ task: "summary",
1175
+ detail: "standard",
1176
+ subject: {
1177
+ kind: "jar",
1178
+ jarPath: "<mod-jar-path>"
1179
+ }
1180
+ };
1181
+ }
1182
+ const task = asNonEmptyString(record.task) ?? "summary";
1183
+ const result = { task };
1184
+ const subjectRecord = asObjectRecord(record.subject);
1185
+ const include = asStringArray(record.include);
1186
+ const canonicalInclude = filterAllowedIncludeValues(include, ANALYZE_MOD_INCLUDE_GROUPS);
1187
+ const wantsLegacyMetadata = include?.some((value) => ANALYZE_MOD_LEGACY_METADATA_INCLUDES.includes(value)) ?? false;
1188
+ const detail = asNonEmptyString(record.detail);
1189
+ if (task === "summary" && wantsLegacyMetadata) {
1190
+ result.detail = detail && detail !== "summary" ? detail : "standard";
1191
+ }
1192
+ else if (detail && detail !== "summary") {
1193
+ result.detail = detail;
1194
+ }
1195
+ if (canonicalInclude.length > 0) {
1196
+ result.include = canonicalInclude;
1197
+ }
1198
+ if (task === "class-source") {
1199
+ result.subject = {
1200
+ kind: "class",
1201
+ jarPath: asNonEmptyString(subjectRecord?.jarPath) ?? asNonEmptyString(record.subject) ?? "<mod-jar-path>",
1202
+ className: asNonEmptyString(subjectRecord?.className) ?? asNonEmptyString(record.className) ?? "<fully-qualified-class-name>"
1203
+ };
1204
+ }
1205
+ else {
1206
+ result.subject = {
1207
+ kind: "jar",
1208
+ jarPath: asNonEmptyString(subjectRecord?.jarPath) ?? asNonEmptyString(record.subject) ?? asNonEmptyString(record.jarPath) ?? "<mod-jar-path>"
1209
+ };
1210
+ }
1211
+ const stringFields = ["query", "searchType", "targetMapping", "outputJar", "executionMode"];
1212
+ for (const field of stringFields) {
1213
+ const value = record[field];
1214
+ if (typeof value === "string" && value.trim()) {
1215
+ result[field] = value;
1216
+ }
1217
+ }
1218
+ const booleanFields = ["includeFiles"];
1219
+ for (const field of booleanFields) {
1220
+ const value = record[field];
1221
+ if (typeof value === "boolean") {
1222
+ result[field] = value;
1223
+ }
1224
+ }
1225
+ const numericFields = ["limit", "maxFiles", "maxLines", "maxChars"];
1226
+ for (const field of numericFields) {
1227
+ const value = record[field];
1228
+ if (typeof value === "number" && Number.isFinite(value)) {
1229
+ result[field] = value;
1230
+ }
1231
+ }
1232
+ return result;
1233
+ }
1234
+ function buildValidateProjectSuggestedParams(normalizedInput) {
1235
+ const record = asObjectRecord(normalizedInput);
1236
+ if (!record) {
1237
+ return {
1238
+ task: "project-summary",
1239
+ subject: {
1240
+ kind: "workspace",
1241
+ projectPath: "<workspace-path>"
1242
+ },
1243
+ preferProjectVersion: true
1244
+ };
1245
+ }
1246
+ const task = asNonEmptyString(record.task) ?? "project-summary";
1247
+ const result = { task };
1248
+ const subjectRecord = asObjectRecord(record.subject);
1249
+ const include = asStringArray(record.include);
1250
+ const canonicalInclude = filterAllowedIncludeValues(include, VALIDATE_PROJECT_INCLUDE_GROUPS);
1251
+ const wantsWorkspaceInclude = include?.some((value) => VALIDATE_PROJECT_LEGACY_WORKSPACE_INCLUDES.includes(value)) ?? false;
1252
+ const detail = asNonEmptyString(record.detail);
1253
+ if (detail && detail !== "summary") {
1254
+ result.detail = detail;
1255
+ }
1256
+ const includeSuggestion = wantsWorkspaceInclude
1257
+ ? [...new Set([...canonicalInclude, "workspace"])]
1258
+ : canonicalInclude;
1259
+ if (includeSuggestion.length > 0) {
1260
+ result.include = includeSuggestion;
1261
+ }
1262
+ const stringFields = [
1263
+ "version",
1264
+ "mapping",
1265
+ "sourcePriority",
1266
+ "scope",
1267
+ "minSeverity",
1268
+ "warningMode"
1269
+ ];
1270
+ for (const field of stringFields) {
1271
+ const value = record[field];
1272
+ if (typeof value === "string" && value.trim()) {
1273
+ result[field] = value;
1274
+ }
1275
+ }
1276
+ const booleanFields = [
1277
+ "preferProjectVersion",
1278
+ "preferProjectMapping",
1279
+ "hideUncertain",
1280
+ "explain",
1281
+ "treatInfoAsWarning",
1282
+ "includeIssues"
1283
+ ];
1284
+ for (const field of booleanFields) {
1285
+ const value = record[field];
1286
+ if (typeof value === "boolean" &&
1287
+ (!Object.prototype.hasOwnProperty.call(SUGGESTED_CALL_DEFAULTS, field) ||
1288
+ !isSuggestedCallDefault(field, value))) {
1289
+ result[field] = value;
1290
+ }
1291
+ }
1292
+ const sourceRoots = asStringArray(record.sourceRoots);
1293
+ if (sourceRoots?.length) {
1294
+ result.sourceRoots = sourceRoots;
1295
+ }
1296
+ const configPaths = asStringArray(record.configPaths);
1297
+ if (configPaths?.length) {
1298
+ result.configPaths = configPaths;
1299
+ }
1300
+ const warningCategoryFilter = asStringArray(record.warningCategoryFilter);
1301
+ if (warningCategoryFilter?.length) {
1302
+ result.warningCategoryFilter = warningCategoryFilter;
1303
+ }
1304
+ if (task === "project-summary") {
1305
+ const subject = {
1306
+ kind: "workspace",
1307
+ projectPath: asNonEmptyString(subjectRecord?.projectPath) ??
1308
+ asNonEmptyString(record.subject) ??
1309
+ asNonEmptyString(record.projectPath) ??
1310
+ "<workspace-path>"
1311
+ };
1312
+ const discover = asStringArray(subjectRecord?.discover);
1313
+ if (discover?.length) {
1314
+ subject.discover = discover;
1315
+ }
1316
+ result.subject = subject;
1317
+ return result;
1318
+ }
1319
+ if (task === "mixin") {
1320
+ const inputRecord = asObjectRecord(subjectRecord?.input) ?? asObjectRecord(record.input);
1321
+ result.subject = {
1322
+ kind: "mixin",
1323
+ input: inputRecord ?? {
1324
+ mode: "inline",
1325
+ source: "<Mixin Java source>"
1326
+ }
1327
+ };
1328
+ return result;
1329
+ }
1330
+ const inputRecord = asObjectRecord(subjectRecord?.input) ?? asObjectRecord(record.input);
1331
+ result.subject = {
1332
+ kind: "access-widener",
1333
+ input: inputRecord ?? {
1334
+ mode: "inline",
1335
+ content: "<access widener contents>"
1336
+ }
1337
+ };
1338
+ return result;
1339
+ }
1127
1340
  function buildInvalidInputGuidance(tool, normalizedInput) {
1128
1341
  if (tool === "validate-mixin") {
1129
1342
  const hints = [
@@ -1163,6 +1376,32 @@ function buildInvalidInputGuidance(tool, normalizedInput) {
1163
1376
  }
1164
1377
  };
1165
1378
  }
1379
+ if (tool === "validate-project") {
1380
+ return {
1381
+ hints: [
1382
+ "validate-project.subject must be an object with subject.kind=workspace|mixin|access-widener.",
1383
+ "task=\"project-summary\" uses {\"subject\":{\"kind\":\"workspace\",\"projectPath\":\"/workspace\"}}.",
1384
+ "Legacy include names like projectSummary/detectedConfig/validationSummary are not accepted; use include:[\"workspace\"] only when you need discovery details."
1385
+ ],
1386
+ suggestedCall: {
1387
+ tool,
1388
+ params: buildValidateProjectSuggestedParams(normalizedInput)
1389
+ }
1390
+ };
1391
+ }
1392
+ if (tool === "analyze-mod") {
1393
+ return {
1394
+ hints: [
1395
+ "analyze-mod.subject must be an object with subject.kind=jar|class.",
1396
+ "task=\"summary\" uses {\"subject\":{\"kind\":\"jar\",\"jarPath\":\"/path/to/mod.jar\"}}.",
1397
+ "Legacy include names like metadata/entrypoints/mixins/dependencies are not accepted; use detail=\"standard\" to surface the metadata block, and canonical include groups only for warnings/files/source/samples/timings."
1398
+ ],
1399
+ suggestedCall: {
1400
+ tool,
1401
+ params: buildAnalyzeModSuggestedParams(normalizedInput)
1402
+ }
1403
+ };
1404
+ }
1166
1405
  return undefined;
1167
1406
  }
1168
1407
  function mapErrorToProblem(caughtError, requestId, context) {
@@ -177,6 +177,7 @@ export declare class MappingService {
177
177
  private parseTinyFromJar;
178
178
  private fetchYarnCoordinates;
179
179
  private trimGraphCache;
180
+ releaseGraphCacheEntry(version: string, sourcePriority?: MappingSourcePriority): void;
180
181
  }
181
182
  /**
182
183
  * Resolve and cache a Tiny v2 mapping file for the given Minecraft version.