@adhisang/minecraft-modding-mcp 3.1.0 → 3.2.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 (44) hide show
  1. package/CHANGELOG.md +62 -34
  2. package/README.md +79 -100
  3. package/dist/access-transformer-parser.d.ts +17 -0
  4. package/dist/access-transformer-parser.js +97 -0
  5. package/dist/concurrency.d.ts +1 -0
  6. package/dist/concurrency.js +24 -0
  7. package/dist/config.js +19 -11
  8. package/dist/decompiler/vineflower.js +22 -21
  9. package/dist/entry-tools/analyze-mod-service.d.ts +4 -4
  10. package/dist/entry-tools/analyze-symbol-service.d.ts +22 -20
  11. package/dist/entry-tools/analyze-symbol-service.js +6 -3
  12. package/dist/entry-tools/inspect-minecraft-service.d.ts +166 -149
  13. package/dist/entry-tools/inspect-minecraft-service.js +318 -55
  14. package/dist/entry-tools/validate-project-service.d.ts +153 -16
  15. package/dist/entry-tools/validate-project-service.js +360 -23
  16. package/dist/gradle-paths.d.ts +4 -0
  17. package/dist/gradle-paths.js +57 -0
  18. package/dist/index.js +274 -13
  19. package/dist/mapping-pipeline-service.d.ts +3 -1
  20. package/dist/mapping-pipeline-service.js +16 -1
  21. package/dist/mapping-service.d.ts +5 -0
  22. package/dist/mapping-service.js +200 -84
  23. package/dist/minecraft-explorer-service.d.ts +13 -0
  24. package/dist/minecraft-explorer-service.js +8 -4
  25. package/dist/mixin-validator.d.ts +33 -2
  26. package/dist/mixin-validator.js +197 -11
  27. package/dist/mod-analyzer.d.ts +1 -0
  28. package/dist/mod-analyzer.js +17 -1
  29. package/dist/mod-decompile-service.js +4 -4
  30. package/dist/mod-remap-service.js +1 -54
  31. package/dist/mod-search-service.d.ts +1 -0
  32. package/dist/mod-search-service.js +84 -51
  33. package/dist/response-utils.d.ts +35 -0
  34. package/dist/response-utils.js +113 -0
  35. package/dist/source-jar-reader.d.ts +16 -0
  36. package/dist/source-jar-reader.js +103 -1
  37. package/dist/source-resolver.js +9 -10
  38. package/dist/source-service.d.ts +24 -2
  39. package/dist/source-service.js +1052 -139
  40. package/dist/tool-contract-manifest.js +74 -74
  41. package/dist/types.d.ts +17 -0
  42. package/dist/workspace-mapping-service.d.ts +13 -0
  43. package/dist/workspace-mapping-service.js +146 -14
  44. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@ import { ZodError, z } from "zod";
4
4
  import { CompatStdioServerTransport } from "./compat-stdio-transport.js";
5
5
  import { objectResult } from "./mcp-helpers.js";
6
6
  import { prepareToolInput } from "./tool-input.js";
7
+ import { isCompactEnabled, COMPACT_MAPPING_TOOL_NAMES, compactResponse, compactArtifactResponse, compactMappingResponse } from "./response-utils.js";
7
8
  import { loadConfig } from "./config.js";
8
9
  import { createError, ERROR_CODES, isAppError } from "./errors.js";
9
10
  import { log } from "./logger.js";
@@ -18,7 +19,7 @@ import { InspectMinecraftService, inspectMinecraftSchema, inspectMinecraftShape
18
19
  import { AnalyzeSymbolService, analyzeSymbolSchema, analyzeSymbolShape } from "./entry-tools/analyze-symbol-service.js";
19
20
  import { CompareMinecraftService, compareMinecraftSchema, compareMinecraftShape } from "./entry-tools/compare-minecraft-service.js";
20
21
  import { AnalyzeModService, analyzeModSchema, analyzeModShape } from "./entry-tools/analyze-mod-service.js";
21
- import { ValidateProjectService, validateProjectSchema, validateProjectShape, discoverWorkspaceAccessWideners, discoverWorkspaceMixins } from "./entry-tools/validate-project-service.js";
22
+ import { ValidateProjectService, validateProjectSchema, validateProjectShape, discoverWorkspaceAccessTransformers, discoverWorkspaceAccessWideners, discoverWorkspaceMixins } from "./entry-tools/validate-project-service.js";
22
23
  import { ManageCacheService, manageCacheSchema, manageCacheShape } from "./entry-tools/manage-cache-service.js";
23
24
  import { createCacheRegistry } from "./cache-registry.js";
24
25
  import { buildEntryToolMeta } from "./entry-tools/response-contract.js";
@@ -89,7 +90,7 @@ const sourceLookupTargetSchema = z.discriminatedUnion("type", [
89
90
  ]);
90
91
  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
92
  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
- const SOURCE_SCOPE_DESCRIPTION = 'vanilla = Mojang client jar only; merged = Loom cache discovery (default); loader = currently behaves the same as "merged".';
93
+ 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.";
93
94
  const SUGGESTED_CALL_DEFAULTS = {
94
95
  allowDecompile: true,
95
96
  preferProjectVersion: false,
@@ -109,6 +110,10 @@ const SUGGESTED_CALL_DEFAULTS = {
109
110
  function isSuggestedCallDefault(field, value) {
110
111
  return value === SUGGESTED_CALL_DEFAULTS[field];
111
112
  }
113
+ const ANALYZE_MOD_INCLUDE_GROUPS = ["warnings", "files", "source", "samples", "timings"];
114
+ const ANALYZE_MOD_LEGACY_METADATA_INCLUDES = ["metadata", "entrypoints", "mixins", "dependencies"];
115
+ const VALIDATE_PROJECT_INCLUDE_GROUPS = ["warnings", "issues", "workspace", "recovery"];
116
+ const VALIDATE_PROJECT_LEGACY_WORKSPACE_INCLUDES = ["detectedConfig", "mixins", "accessWideners"];
112
117
  const listVersionsShape = {
113
118
  includeSnapshots: z.boolean().default(false),
114
119
  limit: optionalPositiveInt.default(20).describe("max 200")
@@ -125,7 +130,9 @@ const resolveArtifactShape = {
125
130
  projectPath: optionalNonEmptyString.describe("Optional workspace root path for Loom cache-assisted source resolution"),
126
131
  scope: artifactScopeSchema.optional().describe(SOURCE_SCOPE_DESCRIPTION),
127
132
  preferProjectVersion: z.boolean().optional().describe("When true, detect MC version from gradle.properties and override target.value"),
128
- strictVersion: z.boolean().optional().describe("When true, reject version-approximated results instead of returning them. Default false.")
133
+ strictVersion: z.boolean().optional().describe("When true, reject version-approximated results instead of returning them. Default false."),
134
+ compact: z.boolean().default(false).describe("When true, return minimal fields (artifactId, origin, isDecompiled, version, requestedMapping, mappingApplied, qualityFlags). "
135
+ + "Omit provenance, artifactContents, sampleEntries, adjacentSourceCandidates, binaryJarPath, coordinate, repoUrl, resolvedSourceJarPath.")
129
136
  };
130
137
  const resolveArtifactSchema = z.object(resolveArtifactShape);
131
138
  const getClassSourceShape = {
@@ -246,7 +253,9 @@ const findMappingShape = {
246
253
  })
247
254
  .partial()
248
255
  .optional(),
249
- maxCandidates: optionalPositiveInt.default(200).describe("Limit returned candidates (max 200)")
256
+ maxCandidates: optionalPositiveInt.default(200).describe("Limit returned candidates (max 200)"),
257
+ compact: z.boolean().default(false).describe("When true, omit top-level empty arrays, null/undefined values, and empty objects from the response. "
258
+ + "Also omit redundant candidates array for single full-confidence exact-match resolutions.")
250
259
  };
251
260
  const findMappingSchema = z.object(findMappingShape).superRefine((value, ctx) => {
252
261
  if (value.kind === "class") {
@@ -264,7 +273,7 @@ const findMappingSchema = z.object(findMappingShape).superRefine((value, ctx) =>
264
273
  path: ["descriptor"]
265
274
  });
266
275
  }
267
- if (!value.name.includes(".")) {
276
+ if (value.sourceMapping !== "obfuscated" && !value.name.includes(".")) {
268
277
  ctx.addIssue({
269
278
  code: z.ZodIssueCode.custom,
270
279
  message: "name must be fully-qualified class name when kind=class.",
@@ -313,7 +322,9 @@ const resolveMethodMappingExactShape = {
313
322
  sourceMapping: sourceMappingSchema.describe("obfuscated | mojang | intermediary | yarn"),
314
323
  targetMapping: sourceMappingSchema.describe("obfuscated | mojang | intermediary | yarn"),
315
324
  sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
316
- maxCandidates: optionalPositiveInt.default(200).describe("Limit returned candidates (max 200)")
325
+ maxCandidates: optionalPositiveInt.default(200).describe("Limit returned candidates (max 200)"),
326
+ compact: z.boolean().default(false).describe("When true, omit top-level empty arrays, null/undefined values, and empty objects from the response. "
327
+ + "Also omit redundant candidates array for single full-confidence exact-match resolutions.")
317
328
  };
318
329
  const resolveMethodMappingExactSchema = z
319
330
  .object(resolveMethodMappingExactShape)
@@ -364,7 +375,9 @@ const resolveWorkspaceSymbolShape = {
364
375
  descriptor: optionalNonEmptyString,
365
376
  sourceMapping: sourceMappingSchema.describe("obfuscated | mojang | intermediary | yarn"),
366
377
  sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
367
- maxCandidates: optionalPositiveInt.default(200).describe("Limit returned candidates for field/method lookups (max 200)")
378
+ maxCandidates: optionalPositiveInt.default(200).describe("Limit returned candidates for field/method lookups (max 200)"),
379
+ compact: z.boolean().default(false).describe("When true, omit top-level empty arrays, null/undefined values, and empty objects from the response. "
380
+ + "Also omit redundant candidates array for single full-confidence exact-match resolutions.")
368
381
  };
369
382
  const resolveWorkspaceSymbolSchema = z
370
383
  .object(resolveWorkspaceSymbolShape)
@@ -436,7 +449,9 @@ const checkSymbolExistsShape = {
436
449
  nameMode: classNameModeSchema.default("fqcn").describe("fqcn | auto"),
437
450
  signatureMode: z.enum(["exact", "name-only"]).default("exact")
438
451
  .describe("exact: require descriptor for methods; name-only: match by owner+name only"),
439
- maxCandidates: optionalPositiveInt.default(200).describe("Limit returned candidates (max 200)")
452
+ maxCandidates: optionalPositiveInt.default(200).describe("Limit returned candidates (max 200)"),
453
+ compact: z.boolean().default(false).describe("When true, omit top-level empty arrays, null/undefined values, and empty objects from the response. "
454
+ + "Also omit redundant candidates array for single full-confidence exact-match resolutions.")
440
455
  };
441
456
  const checkSymbolExistsSchema = z.object(checkSymbolExistsShape).superRefine((value, ctx) => {
442
457
  if (value.kind === "class") {
@@ -577,9 +592,24 @@ const validateAccessWidenerShape = {
577
592
  content: nonEmptyString.describe("Access Widener file content"),
578
593
  version: nonEmptyString.describe("Minecraft version"),
579
594
  mapping: sourceMappingSchema.optional().describe("obfuscated | mojang | intermediary | yarn"),
580
- sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first")
595
+ sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
596
+ projectPath: optionalNonEmptyString.describe("Optional workspace root path for Loom cache-assisted runtime validation"),
597
+ scope: artifactScopeSchema.optional().describe(SOURCE_SCOPE_DESCRIPTION),
598
+ preferProjectVersion: z.boolean().default(false)
599
+ .describe("When true, detect MC version from gradle.properties and override version")
581
600
  };
582
601
  const validateAccessWidenerSchema = z.object(validateAccessWidenerShape);
602
+ const validateAccessTransformerShape = {
603
+ content: nonEmptyString.describe("Access Transformer file content"),
604
+ version: nonEmptyString.describe("Minecraft version"),
605
+ atNamespace: z.enum(["srg", "mojang", "obfuscated"]).optional().describe("srg | mojang | obfuscated"),
606
+ sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
607
+ projectPath: optionalNonEmptyString.describe("Optional workspace root path for Forge/NeoForge runtime validation"),
608
+ scope: artifactScopeSchema.optional().describe(SOURCE_SCOPE_DESCRIPTION),
609
+ preferProjectVersion: z.boolean().default(false)
610
+ .describe("When true, detect MC version from gradle.properties and override version")
611
+ };
612
+ const validateAccessTransformerSchema = z.object(validateAccessTransformerShape);
583
613
  const analyzeModJarShape = {
584
614
  jarPath: nonEmptyString.describe("Local path to the mod JAR file"),
585
615
  includeClasses: z.boolean().default(false).describe("Include full class listing")
@@ -680,6 +710,7 @@ const inspectMinecraftService = new InspectMinecraftService({
680
710
  listVersions: (input) => sourceService.listVersions(input),
681
711
  resolveArtifact: (input) => sourceService.resolveArtifact(input),
682
712
  findClass: (input) => Promise.resolve(sourceService.findClass(input)),
713
+ checkSymbolExists: (input) => sourceService.checkSymbolExists(input),
683
714
  getClassSource: (input) => sourceService.getClassSource(input),
684
715
  getClassMembers: (input) => sourceService.getClassMembers(input),
685
716
  searchClassSource: (input) => sourceService.searchClassSource(input),
@@ -710,8 +741,11 @@ const analyzeModService = new AnalyzeModService({
710
741
  const validateProjectService = new ValidateProjectService({
711
742
  validateMixin: (input) => sourceService.validateMixin(input),
712
743
  validateAccessWidener: (input) => sourceService.validateAccessWidener(input),
744
+ validateAccessTransformer: (input) => sourceService.validateAccessTransformer(input),
713
745
  discoverMixins: discoverWorkspaceMixins,
714
- discoverAccessWideners: discoverWorkspaceAccessWideners
746
+ discoverAccessWideners: discoverWorkspaceAccessWideners,
747
+ discoverAccessTransformers: discoverWorkspaceAccessTransformers,
748
+ detectProjectMinecraftVersion: (projectPath) => workspaceMappingService.detectProjectMinecraftVersion(projectPath)
715
749
  });
716
750
  const manageCacheService = new ManageCacheService({
717
751
  registry: createCacheRegistry({
@@ -1154,6 +1188,184 @@ function buildSourceLookupSuggestedParams(tool, normalizedInput) {
1154
1188
  }
1155
1189
  return result;
1156
1190
  }
1191
+ function filterAllowedIncludeValues(values, allowed) {
1192
+ if (!values?.length) {
1193
+ return [];
1194
+ }
1195
+ const allowedSet = new Set(allowed);
1196
+ const filtered = values.filter((value) => allowedSet.has(value));
1197
+ return [...new Set(filtered)];
1198
+ }
1199
+ function buildAnalyzeModSuggestedParams(normalizedInput) {
1200
+ const record = asObjectRecord(normalizedInput);
1201
+ if (!record) {
1202
+ return {
1203
+ task: "summary",
1204
+ detail: "standard",
1205
+ subject: {
1206
+ kind: "jar",
1207
+ jarPath: "<mod-jar-path>"
1208
+ }
1209
+ };
1210
+ }
1211
+ const task = asNonEmptyString(record.task) ?? "summary";
1212
+ const result = { task };
1213
+ const subjectRecord = asObjectRecord(record.subject);
1214
+ const include = asStringArray(record.include);
1215
+ const canonicalInclude = filterAllowedIncludeValues(include, ANALYZE_MOD_INCLUDE_GROUPS);
1216
+ const wantsLegacyMetadata = include?.some((value) => ANALYZE_MOD_LEGACY_METADATA_INCLUDES.includes(value)) ?? false;
1217
+ const detail = asNonEmptyString(record.detail);
1218
+ if (task === "summary" && wantsLegacyMetadata) {
1219
+ result.detail = detail && detail !== "summary" ? detail : "standard";
1220
+ }
1221
+ else if (detail && detail !== "summary") {
1222
+ result.detail = detail;
1223
+ }
1224
+ if (canonicalInclude.length > 0) {
1225
+ result.include = canonicalInclude;
1226
+ }
1227
+ if (task === "class-source") {
1228
+ result.subject = {
1229
+ kind: "class",
1230
+ jarPath: asNonEmptyString(subjectRecord?.jarPath) ?? asNonEmptyString(record.subject) ?? "<mod-jar-path>",
1231
+ className: asNonEmptyString(subjectRecord?.className) ?? asNonEmptyString(record.className) ?? "<fully-qualified-class-name>"
1232
+ };
1233
+ }
1234
+ else {
1235
+ result.subject = {
1236
+ kind: "jar",
1237
+ jarPath: asNonEmptyString(subjectRecord?.jarPath) ?? asNonEmptyString(record.subject) ?? asNonEmptyString(record.jarPath) ?? "<mod-jar-path>"
1238
+ };
1239
+ }
1240
+ const stringFields = ["query", "searchType", "targetMapping", "outputJar", "executionMode"];
1241
+ for (const field of stringFields) {
1242
+ const value = record[field];
1243
+ if (typeof value === "string" && value.trim()) {
1244
+ result[field] = value;
1245
+ }
1246
+ }
1247
+ const booleanFields = ["includeFiles"];
1248
+ for (const field of booleanFields) {
1249
+ const value = record[field];
1250
+ if (typeof value === "boolean") {
1251
+ result[field] = value;
1252
+ }
1253
+ }
1254
+ const numericFields = ["limit", "maxFiles", "maxLines", "maxChars"];
1255
+ for (const field of numericFields) {
1256
+ const value = record[field];
1257
+ if (typeof value === "number" && Number.isFinite(value)) {
1258
+ result[field] = value;
1259
+ }
1260
+ }
1261
+ return result;
1262
+ }
1263
+ function buildValidateProjectSuggestedParams(normalizedInput) {
1264
+ const record = asObjectRecord(normalizedInput);
1265
+ if (!record) {
1266
+ return {
1267
+ task: "project-summary",
1268
+ subject: {
1269
+ kind: "workspace",
1270
+ projectPath: "<workspace-path>"
1271
+ },
1272
+ preferProjectVersion: true
1273
+ };
1274
+ }
1275
+ const task = asNonEmptyString(record.task) ?? "project-summary";
1276
+ const result = { task };
1277
+ const subjectRecord = asObjectRecord(record.subject);
1278
+ const include = asStringArray(record.include);
1279
+ const canonicalInclude = filterAllowedIncludeValues(include, VALIDATE_PROJECT_INCLUDE_GROUPS);
1280
+ const wantsWorkspaceInclude = include?.some((value) => VALIDATE_PROJECT_LEGACY_WORKSPACE_INCLUDES.includes(value)) ?? false;
1281
+ const detail = asNonEmptyString(record.detail);
1282
+ if (detail && detail !== "summary") {
1283
+ result.detail = detail;
1284
+ }
1285
+ const includeSuggestion = wantsWorkspaceInclude
1286
+ ? [...new Set([...canonicalInclude, "workspace"])]
1287
+ : canonicalInclude;
1288
+ if (includeSuggestion.length > 0) {
1289
+ result.include = includeSuggestion;
1290
+ }
1291
+ const stringFields = [
1292
+ "version",
1293
+ "mapping",
1294
+ "sourcePriority",
1295
+ "scope",
1296
+ "minSeverity",
1297
+ "warningMode"
1298
+ ];
1299
+ for (const field of stringFields) {
1300
+ const value = record[field];
1301
+ if (typeof value === "string" && value.trim()) {
1302
+ result[field] = value;
1303
+ }
1304
+ }
1305
+ const booleanFields = [
1306
+ "preferProjectVersion",
1307
+ "preferProjectMapping",
1308
+ "hideUncertain",
1309
+ "explain",
1310
+ "treatInfoAsWarning",
1311
+ "includeIssues"
1312
+ ];
1313
+ for (const field of booleanFields) {
1314
+ const value = record[field];
1315
+ if (typeof value === "boolean" &&
1316
+ (!Object.prototype.hasOwnProperty.call(SUGGESTED_CALL_DEFAULTS, field) ||
1317
+ !isSuggestedCallDefault(field, value))) {
1318
+ result[field] = value;
1319
+ }
1320
+ }
1321
+ const sourceRoots = asStringArray(record.sourceRoots);
1322
+ if (sourceRoots?.length) {
1323
+ result.sourceRoots = sourceRoots;
1324
+ }
1325
+ const configPaths = asStringArray(record.configPaths);
1326
+ if (configPaths?.length) {
1327
+ result.configPaths = configPaths;
1328
+ }
1329
+ const warningCategoryFilter = asStringArray(record.warningCategoryFilter);
1330
+ if (warningCategoryFilter?.length) {
1331
+ result.warningCategoryFilter = warningCategoryFilter;
1332
+ }
1333
+ if (task === "project-summary") {
1334
+ const subject = {
1335
+ kind: "workspace",
1336
+ projectPath: asNonEmptyString(subjectRecord?.projectPath) ??
1337
+ asNonEmptyString(record.subject) ??
1338
+ asNonEmptyString(record.projectPath) ??
1339
+ "<workspace-path>"
1340
+ };
1341
+ const discover = asStringArray(subjectRecord?.discover);
1342
+ if (discover?.length) {
1343
+ subject.discover = discover;
1344
+ }
1345
+ result.subject = subject;
1346
+ return result;
1347
+ }
1348
+ if (task === "mixin") {
1349
+ const inputRecord = asObjectRecord(subjectRecord?.input) ?? asObjectRecord(record.input);
1350
+ result.subject = {
1351
+ kind: "mixin",
1352
+ input: inputRecord ?? {
1353
+ mode: "inline",
1354
+ source: "<Mixin Java source>"
1355
+ }
1356
+ };
1357
+ return result;
1358
+ }
1359
+ const inputRecord = asObjectRecord(subjectRecord?.input) ?? asObjectRecord(record.input);
1360
+ result.subject = {
1361
+ kind: "access-widener",
1362
+ input: inputRecord ?? {
1363
+ mode: "inline",
1364
+ content: "<access widener contents>"
1365
+ }
1366
+ };
1367
+ return result;
1368
+ }
1157
1369
  function buildInvalidInputGuidance(tool, normalizedInput) {
1158
1370
  if (tool === "validate-mixin") {
1159
1371
  const hints = [
@@ -1193,6 +1405,32 @@ function buildInvalidInputGuidance(tool, normalizedInput) {
1193
1405
  }
1194
1406
  };
1195
1407
  }
1408
+ if (tool === "validate-project") {
1409
+ return {
1410
+ hints: [
1411
+ "validate-project.subject must be an object with subject.kind=workspace|mixin|access-widener|access-transformer.",
1412
+ "task=\"project-summary\" uses {\"subject\":{\"kind\":\"workspace\",\"projectPath\":\"/workspace\"}}.",
1413
+ "Legacy include names like projectSummary/detectedConfig/validationSummary are not accepted; use include:[\"workspace\"] only when you need discovery details."
1414
+ ],
1415
+ suggestedCall: {
1416
+ tool,
1417
+ params: buildValidateProjectSuggestedParams(normalizedInput)
1418
+ }
1419
+ };
1420
+ }
1421
+ if (tool === "analyze-mod") {
1422
+ return {
1423
+ hints: [
1424
+ "analyze-mod.subject must be an object with subject.kind=jar|class.",
1425
+ "task=\"summary\" uses {\"subject\":{\"kind\":\"jar\",\"jarPath\":\"/path/to/mod.jar\"}}.",
1426
+ "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."
1427
+ ],
1428
+ suggestedCall: {
1429
+ tool,
1430
+ params: buildAnalyzeModSuggestedParams(normalizedInput)
1431
+ }
1432
+ };
1433
+ }
1196
1434
  return undefined;
1197
1435
  }
1198
1436
  function mapErrorToProblem(caughtError, requestId, context) {
@@ -1293,6 +1531,17 @@ async function runTool(tool, rawInput, schema, action) {
1293
1531
  ? heavyToolExecutionGate.run(tool, () => action(parsedInput))
1294
1532
  : action(parsedInput));
1295
1533
  const { result, warnings, meta: resultMeta } = splitWarnings(payload);
1534
+ const isCompact = isCompactEnabled(tool, parsedInput);
1535
+ let projectedResult = result;
1536
+ if (isCompact) {
1537
+ if (tool === "resolve-artifact") {
1538
+ projectedResult = compactArtifactResponse(projectedResult);
1539
+ }
1540
+ if (COMPACT_MAPPING_TOOL_NAMES.has(tool)) {
1541
+ projectedResult = compactMappingResponse(projectedResult);
1542
+ }
1543
+ projectedResult = compactResponse(projectedResult);
1544
+ }
1296
1545
  const entryMeta = ENTRY_TOOL_NAMES.has(tool)
1297
1546
  ? buildEntryToolMeta({
1298
1547
  detail: normalizedInput &&
@@ -1310,7 +1559,7 @@ async function runTool(tool, rawInput, schema, action) {
1310
1559
  })
1311
1560
  : undefined;
1312
1561
  return objectResult({
1313
- result,
1562
+ result: projectedResult,
1314
1563
  meta: {
1315
1564
  ...(entryMeta ?? {}),
1316
1565
  ...resultMeta,
@@ -1375,7 +1624,7 @@ server.tool("inspect-minecraft", "High-level v3 entry tool for version discovery
1375
1624
  server.tool("analyze-symbol", "High-level v3 entry tool for symbol existence, mapping, lifecycle, workspace analysis, and API overview.", analyzeSymbolShape, { readOnlyHint: true }, async (args) => runTool("analyze-symbol", args, analyzeSymbolSchema, async (input) => analyzeSymbolService.execute(input)));
1376
1625
  server.tool("compare-minecraft", "High-level v3 entry tool for version comparisons, class diffs, registry diffs, and migration overviews.", compareMinecraftShape, { readOnlyHint: true }, async (args) => runTool("compare-minecraft", args, compareMinecraftSchema, async (input) => compareMinecraftService.execute(input)));
1377
1626
  server.tool("analyze-mod", "High-level v3 entry tool for mod metadata inspection, decompile/search flows, class source, and safe remap previews/applies.", analyzeModShape, { readOnlyHint: false }, async (args) => runTool("analyze-mod", args, analyzeModSchema, async (input) => analyzeModService.execute(input)));
1378
- server.tool("validate-project", "High-level v3 entry tool for project summary, direct mixin validation, and access widener validation.", validateProjectShape, { readOnlyHint: true }, async (args) => runTool("validate-project", args, validateProjectSchema, async (input) => validateProjectService.execute(input)));
1627
+ server.tool("validate-project", "High-level v3 entry tool for project summary, direct mixin validation, and access widener/access transformer validation.", validateProjectShape, { readOnlyHint: true }, async (args) => runTool("validate-project", args, validateProjectSchema, async (input) => validateProjectService.execute(input)));
1379
1628
  server.tool("manage-cache", "High-level v3 entry tool for cache summaries, listing, verification, previewed mutation, and explicit apply operations.", manageCacheShape, { readOnlyHint: false }, async (args) => runTool("manage-cache", args, manageCacheSchema, async (input) => manageCacheService.execute(input)));
1380
1629
  server.tool("resolve-artifact", "Resolve source artifact from a target object ({ kind, value }) and return artifact metadata. For target.kind=jar, only <basename>-sources.jar is auto-adopted; other adjacent *-sources.jar files are informational.", resolveArtifactShape, { readOnlyHint: true }, async (args) => runTool("resolve-artifact", args, resolveArtifactSchema, async (input) => sourceService.resolveArtifact({
1381
1630
  target: input.target,
@@ -1576,7 +1825,19 @@ server.tool("validate-access-widener", "Validate Access Widener file entries aga
1576
1825
  content: input.content,
1577
1826
  version: input.version,
1578
1827
  mapping: input.mapping,
1579
- sourcePriority: input.sourcePriority
1828
+ sourcePriority: input.sourcePriority,
1829
+ projectPath: input.projectPath,
1830
+ scope: input.scope,
1831
+ preferProjectVersion: input.preferProjectVersion
1832
+ })));
1833
+ server.tool("validate-access-transformer", "Validate Access Transformer file entries against Minecraft bytecode signatures for a given version.", validateAccessTransformerShape, { readOnlyHint: true }, async (args) => runTool("validate-access-transformer", args, validateAccessTransformerSchema, async (input) => sourceService.validateAccessTransformer({
1834
+ content: input.content,
1835
+ version: input.version,
1836
+ atNamespace: input.atNamespace,
1837
+ sourcePriority: input.sourcePriority,
1838
+ projectPath: input.projectPath,
1839
+ scope: input.scope,
1840
+ preferProjectVersion: input.preferProjectVersion
1580
1841
  })));
1581
1842
  server.tool("analyze-mod-jar", "Analyze a Minecraft mod JAR to extract loader type, metadata, entrypoints, mixins, and dependencies.", analyzeModJarShape, { readOnlyHint: true }, async (args) => runTool("analyze-mod-jar", args, analyzeModJarSchema, async (input) => {
1582
1843
  const result = await analyzeModJar(input.jarPath, {
@@ -3,6 +3,7 @@ export interface MappingPipelineInput {
3
3
  requestedMapping: SourceMapping;
4
4
  target: SourceTargetInput;
5
5
  resolved: ResolvedSourceArtifact;
6
+ runtimeNamesUnobfuscated?: boolean;
6
7
  }
7
8
  export interface MappingPipelineResult {
8
9
  mappingApplied: SourceMapping;
@@ -13,6 +14,7 @@ export interface MappingPipelineResult {
13
14
  * Mapping pipeline for v0.3.
14
15
  * Current implementation enforces explicit guarantees:
15
16
  * - obfuscated: always pass-through
16
- * - mojang: requires source-backed artifact; decompile-only artifacts are rejected
17
+ * - mojang: requires source-backed artifacts on legacy obfuscated versions,
18
+ * but unobfuscated runtime jars can pass through directly
17
19
  */
18
20
  export declare function applyMappingPipeline(input: MappingPipelineInput): MappingPipelineResult;
@@ -3,7 +3,8 @@ import { createError, ERROR_CODES } from "./errors.js";
3
3
  * Mapping pipeline for v0.3.
4
4
  * Current implementation enforces explicit guarantees:
5
5
  * - obfuscated: always pass-through
6
- * - mojang: requires source-backed artifact; decompile-only artifacts are rejected
6
+ * - mojang: requires source-backed artifacts on legacy obfuscated versions,
7
+ * but unobfuscated runtime jars can pass through directly
7
8
  */
8
9
  export function applyMappingPipeline(input) {
9
10
  const transformChain = [];
@@ -22,6 +23,20 @@ export function applyMappingPipeline(input) {
22
23
  transformChain
23
24
  };
24
25
  }
26
+ if (input.requestedMapping === "mojang" && input.runtimeNamesUnobfuscated) {
27
+ transformChain.push("mapping:mojang-runtime-unobfuscated");
28
+ if (input.resolved.isDecompiled) {
29
+ qualityFlags.push("decompiled");
30
+ }
31
+ else {
32
+ qualityFlags.push("source-backed");
33
+ }
34
+ return {
35
+ mappingApplied: "mojang",
36
+ qualityFlags,
37
+ transformChain
38
+ };
39
+ }
25
40
  if (input.requestedMapping !== "mojang" &&
26
41
  input.requestedMapping !== "intermediary" &&
27
42
  input.requestedMapping !== "yarn") {
@@ -62,6 +62,7 @@ export type FindMappingInput = {
62
62
  sourceMapping: SourceMapping;
63
63
  targetMapping: SourceMapping;
64
64
  sourcePriority?: MappingSourcePriority;
65
+ projectPath?: string;
65
66
  disambiguation?: {
66
67
  ownerHint?: string;
67
68
  descriptorHint?: string;
@@ -74,6 +75,7 @@ export type EnsureMappingAvailableInput = {
74
75
  sourceMapping: SourceMapping;
75
76
  targetMapping: SourceMapping;
76
77
  sourcePriority?: MappingSourcePriority;
78
+ projectPath?: string;
77
79
  };
78
80
  export type EnsureMappingAvailableOutput = {
79
81
  transformChain: string[];
@@ -88,6 +90,7 @@ export type ResolveMethodMappingExactInput = {
88
90
  sourceMapping: SourceMapping;
89
91
  targetMapping: SourceMapping;
90
92
  sourcePriority?: MappingSourcePriority;
93
+ projectPath?: string;
91
94
  maxCandidates?: number;
92
95
  };
93
96
  export type ResolveMethodMappingExactOutput = SymbolResolutionOutput;
@@ -153,6 +156,7 @@ export declare class MappingService {
153
156
  checkSymbolExists(input: SymbolExistenceInput): Promise<SymbolExistenceOutput>;
154
157
  private mapRecordBetweenMappings;
155
158
  private mapCandidatesAlongPath;
159
+ private projectMethodDescriptorToTarget;
156
160
  private provenanceForPath;
157
161
  /**
158
162
  * Probe the mapping graph health for a given version.
@@ -177,6 +181,7 @@ export declare class MappingService {
177
181
  private parseTinyFromJar;
178
182
  private fetchYarnCoordinates;
179
183
  private trimGraphCache;
184
+ releaseGraphCacheEntry(version: string, sourcePriority?: MappingSourcePriority): void;
180
185
  }
181
186
  /**
182
187
  * Resolve and cache a Tiny v2 mapping file for the given Minecraft version.