@adhisang/minecraft-modding-mcp 3.1.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
@@ -109,6 +109,10 @@ const SUGGESTED_CALL_DEFAULTS = {
109
109
  function isSuggestedCallDefault(field, value) {
110
110
  return value === SUGGESTED_CALL_DEFAULTS[field];
111
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"];
112
116
  const listVersionsShape = {
113
117
  includeSnapshots: z.boolean().default(false),
114
118
  limit: optionalPositiveInt.default(20).describe("max 200")
@@ -264,7 +268,7 @@ const findMappingSchema = z.object(findMappingShape).superRefine((value, ctx) =>
264
268
  path: ["descriptor"]
265
269
  });
266
270
  }
267
- if (!value.name.includes(".")) {
271
+ if (value.sourceMapping !== "obfuscated" && !value.name.includes(".")) {
268
272
  ctx.addIssue({
269
273
  code: z.ZodIssueCode.custom,
270
274
  message: "name must be fully-qualified class name when kind=class.",
@@ -680,6 +684,7 @@ const inspectMinecraftService = new InspectMinecraftService({
680
684
  listVersions: (input) => sourceService.listVersions(input),
681
685
  resolveArtifact: (input) => sourceService.resolveArtifact(input),
682
686
  findClass: (input) => Promise.resolve(sourceService.findClass(input)),
687
+ checkSymbolExists: (input) => sourceService.checkSymbolExists(input),
683
688
  getClassSource: (input) => sourceService.getClassSource(input),
684
689
  getClassMembers: (input) => sourceService.getClassMembers(input),
685
690
  searchClassSource: (input) => sourceService.searchClassSource(input),
@@ -1154,6 +1159,184 @@ function buildSourceLookupSuggestedParams(tool, normalizedInput) {
1154
1159
  }
1155
1160
  return result;
1156
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
+ }
1157
1340
  function buildInvalidInputGuidance(tool, normalizedInput) {
1158
1341
  if (tool === "validate-mixin") {
1159
1342
  const hints = [
@@ -1193,6 +1376,32 @@ function buildInvalidInputGuidance(tool, normalizedInput) {
1193
1376
  }
1194
1377
  };
1195
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
+ }
1196
1405
  return undefined;
1197
1406
  }
1198
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.
@@ -420,7 +420,7 @@ function parseClientMappings(text) {
420
420
  }
421
421
  function normalizeTinyNamespace(namespace) {
422
422
  const normalized = namespace.trim().toLowerCase();
423
- if (normalized === "obfuscated") {
423
+ if (normalized === "obfuscated" || normalized === "official") {
424
424
  return "obfuscated";
425
425
  }
426
426
  if (normalized === "mojang") {
@@ -590,6 +590,12 @@ function mappingSourceOrder(priority) {
590
590
  }
591
591
  return ["loom-cache", "maven"];
592
592
  }
593
+ function requiresOnlyObfuscatedMojangGraph(sourceMapping, targetMapping) {
594
+ return sourceMapping !== "intermediary" &&
595
+ sourceMapping !== "yarn" &&
596
+ targetMapping !== "intermediary" &&
597
+ targetMapping !== "yarn";
598
+ }
593
599
  function namespacePath(graph, sourceMapping, targetMapping) {
594
600
  if (sourceMapping === targetMapping) {
595
601
  return [sourceMapping];
@@ -709,7 +715,7 @@ function normalizeMethodDescriptor(descriptor) {
709
715
  }
710
716
  return normalized;
711
717
  }
712
- function normalizeQuerySymbol(input, signatureMode) {
718
+ function normalizeQuerySymbol(input, signatureMode, options) {
713
719
  if (input.kind !== "class" && input.kind !== "field" && input.kind !== "method") {
714
720
  throw invalidInputError('kind must be one of "class", "field", or "method".', {
715
721
  kind: input.kind
@@ -735,7 +741,7 @@ function normalizeQuerySymbol(input, signatureMode) {
735
741
  });
736
742
  }
737
743
  const className = normalizeMappedSymbolOutput(normalizedName);
738
- if (!className.includes(".")) {
744
+ if (!className.includes(".") && !options?.allowShortClassName) {
739
745
  throw invalidInputError("name must be a fully qualified class name when kind=class.", {
740
746
  name: input.name
741
747
  });
@@ -905,7 +911,9 @@ export class MappingService {
905
911
  }
906
912
  });
907
913
  }
908
- const { record: queryRecord, querySymbol } = normalizeQuerySymbol(input, input.signatureMode);
914
+ const { record: queryRecord, querySymbol } = normalizeQuerySymbol(input, input.signatureMode, {
915
+ allowShortClassName: input.kind === "class" && input.sourceMapping === "obfuscated"
916
+ });
909
917
  const sourceMapping = input.sourceMapping;
910
918
  const targetMapping = input.targetMapping;
911
919
  if (!SUPPORTED_MAPPINGS.has(sourceMapping) || !SUPPORTED_MAPPINGS.has(targetMapping)) {
@@ -945,7 +953,7 @@ export class MappingService {
945
953
  warnings: []
946
954
  };
947
955
  }
948
- const graph = await this.loadGraph(version, priority);
956
+ const graph = await this.loadGraph(version, priority, requiresOnlyObfuscatedMojangGraph(sourceMapping, targetMapping) ? "obfuscated-mojang-only" : "full");
949
957
  const path = namespacePath(graph, sourceMapping, targetMapping);
950
958
  if (!path) {
951
959
  return {
@@ -1029,7 +1037,7 @@ export class MappingService {
1029
1037
  warnings: []
1030
1038
  };
1031
1039
  }
1032
- const graph = await this.loadGraph(version, priority);
1040
+ const graph = await this.loadGraph(version, priority, requiresOnlyObfuscatedMojangGraph(sourceMapping, targetMapping) ? "obfuscated-mojang-only" : "full");
1033
1041
  const path = namespacePath(graph, sourceMapping, targetMapping);
1034
1042
  if (!path) {
1035
1043
  throw createError({
@@ -1113,7 +1121,7 @@ export class MappingService {
1113
1121
  warnings: []
1114
1122
  };
1115
1123
  }
1116
- const graph = await this.loadGraph(version, priority);
1124
+ const graph = await this.loadGraph(version, priority, requiresOnlyObfuscatedMojangGraph(sourceMapping, targetMapping) ? "obfuscated-mojang-only" : "full");
1117
1125
  const path = namespacePath(graph, sourceMapping, targetMapping);
1118
1126
  if (!path) {
1119
1127
  return {
@@ -1214,7 +1222,7 @@ export class MappingService {
1214
1222
  });
1215
1223
  }
1216
1224
  const priority = mappingPriorityFromInput(this.config.mappingSourcePriority, input.sourcePriority);
1217
- const graph = await this.loadGraph(version, priority);
1225
+ const graph = await this.loadGraph(version, priority, "full");
1218
1226
  const warnings = [...graph.warnings];
1219
1227
  const includeKinds = normalizeIncludedKinds(input.includeKinds);
1220
1228
  const pathCache = new Map();
@@ -1246,7 +1254,7 @@ export class MappingService {
1246
1254
  classByMapping[mapping] = mapped[0];
1247
1255
  }
1248
1256
  }
1249
- const baseMapping = classByMapping.obfuscated ? "obfuscated" : classNameMapping;
1257
+ const baseMapping = classNameMapping;
1250
1258
  const baseClass = classByMapping[baseMapping];
1251
1259
  if (!baseClass) {
1252
1260
  return {
@@ -1435,7 +1443,7 @@ export class MappingService {
1435
1443
  querySymbol
1436
1444
  };
1437
1445
  })();
1438
- const graph = await this.loadGraph(version, priority);
1446
+ const graph = await this.loadGraph(version, priority, sourceMapping === "mojang" ? "obfuscated-mojang-only" : "full");
1439
1447
  const warnings = [...graph.warnings];
1440
1448
  const records = collectTargetRecords(graph, sourceMapping);
1441
1449
  if (records.length === 0) {
@@ -1640,7 +1648,7 @@ export class MappingService {
1640
1648
  const degradations = [];
1641
1649
  let graph;
1642
1650
  try {
1643
- graph = await this.loadGraph(input.version, priority);
1651
+ graph = await this.loadGraph(input.version, priority, "full");
1644
1652
  }
1645
1653
  catch {
1646
1654
  return {
@@ -1684,8 +1692,8 @@ export class MappingService {
1684
1692
  degradations
1685
1693
  };
1686
1694
  }
1687
- async loadGraph(version, priority) {
1688
- const cacheKey = `${version}|${priority}`;
1695
+ async loadGraph(version, priority, mode) {
1696
+ const cacheKey = `${version}|${priority}|${mode}`;
1689
1697
  const cached = this.graphCache.get(cacheKey);
1690
1698
  if (cached) {
1691
1699
  this.graphCache.delete(cacheKey);
@@ -1696,7 +1704,7 @@ export class MappingService {
1696
1704
  if (existingLock) {
1697
1705
  return existingLock;
1698
1706
  }
1699
- const buildPromise = this.buildGraph(version, priority);
1707
+ const buildPromise = this.buildGraph(version, priority, mode);
1700
1708
  this.buildLocks.set(cacheKey, buildPromise);
1701
1709
  try {
1702
1710
  const built = await buildPromise;
@@ -1708,11 +1716,12 @@ export class MappingService {
1708
1716
  this.buildLocks.delete(cacheKey);
1709
1717
  }
1710
1718
  }
1711
- async buildGraph(version, priority) {
1719
+ async buildGraph(version, priority, mode) {
1712
1720
  if (isUnobfuscatedVersion(version)) {
1713
1721
  return {
1714
1722
  version,
1715
1723
  priority,
1724
+ mode,
1716
1725
  pairs: new Map(),
1717
1726
  adjacency: new Map(),
1718
1727
  pathCache: new Map(),
@@ -1725,6 +1734,7 @@ export class MappingService {
1725
1734
  const graph = {
1726
1735
  version,
1727
1736
  priority,
1737
+ mode,
1728
1738
  pairs: new Map(),
1729
1739
  adjacency: new Map(),
1730
1740
  pathCache: new Map(),
@@ -1734,27 +1744,29 @@ export class MappingService {
1734
1744
  const mojangLoad = await this.loadMojangPairs(version);
1735
1745
  graph.warnings.push(...mojangLoad.warnings);
1736
1746
  this.mergePairs(graph.pairs, mojangLoad.pairs, "mojang-client-mappings", mojangLoad.mappingArtifact);
1737
- let tinyLoaded = false;
1738
- const deferredTinyWarnings = [];
1739
- for (const source of mappingSourceOrder(priority)) {
1740
- const tinyLoad = source === "loom-cache"
1741
- ? await this.loadTinyPairsFromLoom(version)
1742
- : await this.loadTinyPairsFromMaven(version);
1743
- if (tinyLoad.pairs.size === 0) {
1744
- deferredTinyWarnings.push(...tinyLoad.warnings);
1745
- continue;
1747
+ if (mode === "full") {
1748
+ let tinyLoaded = false;
1749
+ const deferredTinyWarnings = [];
1750
+ for (const source of mappingSourceOrder(priority)) {
1751
+ const tinyLoad = source === "loom-cache"
1752
+ ? await this.loadTinyPairsFromLoom(version)
1753
+ : await this.loadTinyPairsFromMaven(version);
1754
+ if (tinyLoad.pairs.size === 0) {
1755
+ deferredTinyWarnings.push(...tinyLoad.warnings);
1756
+ continue;
1757
+ }
1758
+ tinyLoaded = true;
1759
+ this.mergePairs(graph.pairs, tinyLoad.pairs, source, tinyLoad.mappingArtifact);
1760
+ graph.warnings.push(...tinyLoad.warnings);
1761
+ if (deferredTinyWarnings.length > 0) {
1762
+ graph.warnings.push(`Used ${source === "maven" ? "Maven" : "Loom cache"} tiny mappings for "${version}" after an earlier source lookup returned no data.`);
1763
+ }
1764
+ break;
1746
1765
  }
1747
- tinyLoaded = true;
1748
- this.mergePairs(graph.pairs, tinyLoad.pairs, source, tinyLoad.mappingArtifact);
1749
- graph.warnings.push(...tinyLoad.warnings);
1750
- if (deferredTinyWarnings.length > 0) {
1751
- graph.warnings.push(`Used ${source === "maven" ? "Maven" : "Loom cache"} tiny mappings for "${version}" after an earlier source lookup returned no data.`);
1766
+ if (!tinyLoaded) {
1767
+ graph.warnings.push(...deferredTinyWarnings);
1768
+ graph.warnings.push("No intermediary/yarn tiny mappings were found for this version.");
1752
1769
  }
1753
- break;
1754
- }
1755
- if (!tinyLoaded) {
1756
- graph.warnings.push(...deferredTinyWarnings);
1757
- graph.warnings.push("No intermediary/yarn tiny mappings were found for this version.");
1758
1770
  }
1759
1771
  graph.adjacency = buildAdjacency(graph.pairs);
1760
1772
  graph.recordsByTarget = buildTargetRecordIndex(graph.pairs);
@@ -1990,6 +2002,15 @@ export class MappingService {
1990
2002
  this.graphCache.delete(oldestKey);
1991
2003
  }
1992
2004
  }
2005
+ releaseGraphCacheEntry(version, sourcePriority) {
2006
+ const normalizedVersion = version.trim();
2007
+ if (!normalizedVersion) {
2008
+ return;
2009
+ }
2010
+ const priority = mappingPriorityFromInput(this.config.mappingSourcePriority, sourcePriority);
2011
+ this.graphCache.delete(`${normalizedVersion}|${priority}|full`);
2012
+ this.graphCache.delete(`${normalizedVersion}|${priority}|obfuscated-mojang-only`);
2013
+ }
1993
2014
  }
1994
2015
  // ---------------------------------------------------------------------------
1995
2016
  // Standalone: Tiny v2 mapping file resolution for remapping
@@ -497,6 +497,8 @@ export declare class SourceService {
497
497
  private buildFallbackProvenance;
498
498
  private resolveClassNameForLookup;
499
499
  private buildClassSourceNotFoundError;
500
+ private rejectLifecycleClassLikeInput;
501
+ private releaseLifecycleMappingGraph;
500
502
  private resolveToObfuscatedClassName;
501
503
  private resolveToObfuscatedMemberName;
502
504
  private remapSignatureMembers;