@adhisang/minecraft-modding-mcp 2.0.0 → 3.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 (57) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/README.md +139 -30
  3. package/dist/cache-registry.d.ts +95 -0
  4. package/dist/cache-registry.js +541 -0
  5. package/dist/cli.js +31 -4
  6. package/dist/compat-stdio-transport.d.ts +2 -7
  7. package/dist/compat-stdio-transport.js +12 -154
  8. package/dist/entry-tools/analyze-mod-service.d.ts +207 -0
  9. package/dist/entry-tools/analyze-mod-service.js +253 -0
  10. package/dist/entry-tools/analyze-symbol-service.d.ts +209 -0
  11. package/dist/entry-tools/analyze-symbol-service.js +304 -0
  12. package/dist/entry-tools/compare-minecraft-service.d.ts +210 -0
  13. package/dist/entry-tools/compare-minecraft-service.js +397 -0
  14. package/dist/entry-tools/entry-tool-schema.d.ts +6 -0
  15. package/dist/entry-tools/entry-tool-schema.js +10 -0
  16. package/dist/entry-tools/inspect-minecraft-service.d.ts +1953 -0
  17. package/dist/entry-tools/inspect-minecraft-service.js +876 -0
  18. package/dist/entry-tools/manage-cache-service.d.ts +130 -0
  19. package/dist/entry-tools/manage-cache-service.js +229 -0
  20. package/dist/entry-tools/request-normalizers.d.ts +10 -0
  21. package/dist/entry-tools/request-normalizers.js +36 -0
  22. package/dist/entry-tools/response-contract.d.ts +44 -0
  23. package/dist/entry-tools/response-contract.js +96 -0
  24. package/dist/entry-tools/validate-project-service.d.ts +543 -0
  25. package/dist/entry-tools/validate-project-service.js +381 -0
  26. package/dist/index.js +495 -42
  27. package/dist/json-rpc-framing.d.ts +22 -0
  28. package/dist/json-rpc-framing.js +168 -0
  29. package/dist/mapping-pipeline-service.js +9 -1
  30. package/dist/mapping-service.d.ts +9 -0
  31. package/dist/mapping-service.js +183 -60
  32. package/dist/minecraft-explorer-service.d.ts +0 -1
  33. package/dist/minecraft-explorer-service.js +119 -23
  34. package/dist/mixin-validator.d.ts +24 -2
  35. package/dist/mixin-validator.js +223 -98
  36. package/dist/mod-decompile-service.d.ts +5 -0
  37. package/dist/mod-decompile-service.js +40 -5
  38. package/dist/mod-remap-service.js +142 -30
  39. package/dist/path-resolver.js +41 -4
  40. package/dist/registry-service.d.ts +10 -1
  41. package/dist/registry-service.js +154 -22
  42. package/dist/search-hit-accumulator.js +23 -2
  43. package/dist/source-jar-reader.js +16 -2
  44. package/dist/source-resolver.js +6 -7
  45. package/dist/source-service.d.ts +42 -4
  46. package/dist/source-service.js +781 -127
  47. package/dist/stdio-supervisor.d.ts +46 -0
  48. package/dist/stdio-supervisor.js +349 -0
  49. package/dist/storage/files-repo.d.ts +3 -9
  50. package/dist/storage/files-repo.js +66 -43
  51. package/dist/symbols/symbol-extractor.js +6 -4
  52. package/dist/tool-execution-gate.d.ts +15 -0
  53. package/dist/tool-execution-gate.js +58 -0
  54. package/dist/version-diff-service.js +10 -5
  55. package/dist/version-service.js +7 -2
  56. package/dist/workspace-mapping-service.js +12 -0
  57. package/package.json +1 -1
@@ -68,7 +68,7 @@ function modifierPrefix(flags, category) {
68
68
  }
69
69
  return parts.join(" ");
70
70
  }
71
- function parseFieldType(descriptor, position = 0) {
71
+ function parseFieldType(descriptor, position = 0, options = {}) {
72
72
  if (position >= descriptor.length) {
73
73
  throw createError({
74
74
  code: ERROR_CODES.INVALID_INPUT,
@@ -95,6 +95,13 @@ function parseFieldType(descriptor, position = 0) {
95
95
  case "Z":
96
96
  return { type: "boolean", next: position + 1 };
97
97
  case "V":
98
+ if (!options.allowVoid) {
99
+ throw createError({
100
+ code: ERROR_CODES.INVALID_INPUT,
101
+ message: options.invalidVoidMessage ?? `Invalid field descriptor "${descriptor}".`,
102
+ details: { descriptor, position }
103
+ });
104
+ }
98
105
  return { type: "void", next: position + 1 };
99
106
  case "L": {
100
107
  const end = descriptor.indexOf(";", position);
@@ -109,7 +116,7 @@ function parseFieldType(descriptor, position = 0) {
109
116
  return { type, next: end + 1 };
110
117
  }
111
118
  case "[": {
112
- const inner = parseFieldType(descriptor, position + 1);
119
+ const inner = parseFieldType(descriptor, position + 1, options);
113
120
  return { type: `${inner.type}[]`, next: inner.next };
114
121
  }
115
122
  default:
@@ -131,7 +138,10 @@ function parseMethodDescriptor(descriptor) {
131
138
  const args = [];
132
139
  let cursor = 1;
133
140
  while (cursor < descriptor.length && descriptor[cursor] !== ")") {
134
- const parsed = parseFieldType(descriptor, cursor);
141
+ const parsed = parseFieldType(descriptor, cursor, {
142
+ allowVoid: false,
143
+ invalidVoidMessage: `Invalid method descriptor "${descriptor}": void is not allowed in this position.`
144
+ });
135
145
  args.push(parsed.type);
136
146
  cursor = parsed.next;
137
147
  }
@@ -142,7 +152,15 @@ function parseMethodDescriptor(descriptor) {
142
152
  details: { descriptor, cursor }
143
153
  });
144
154
  }
145
- const returnType = parseFieldType(descriptor, cursor + 1).type;
155
+ const parsedReturn = parseFieldType(descriptor, cursor + 1, { allowVoid: true });
156
+ if (parsedReturn.next !== descriptor.length) {
157
+ throw createError({
158
+ code: ERROR_CODES.INVALID_INPUT,
159
+ message: `Invalid method descriptor "${descriptor}".`,
160
+ details: { descriptor, cursor: parsedReturn.next }
161
+ });
162
+ }
163
+ const returnType = parsedReturn.type;
146
164
  return { args, returnType };
147
165
  }
148
166
  function hasPublicVisibility(flags) {
@@ -154,6 +172,80 @@ function toInternalName(fqn) {
154
172
  function extractVersionFromPath(inputPath) {
155
173
  return inputPath.match(/(\d+\.\d+(?:\.\d+)?)/)?.[1];
156
174
  }
175
+ class SignatureCacheStore {
176
+ maxEntries;
177
+ nodes = new Map();
178
+ oldest;
179
+ newest;
180
+ constructor(maxEntries) {
181
+ this.maxEntries = maxEntries;
182
+ }
183
+ get(key) {
184
+ const node = this.nodes.get(key);
185
+ if (!node) {
186
+ return undefined;
187
+ }
188
+ this.promote(node);
189
+ return node.value;
190
+ }
191
+ set(key, value) {
192
+ const existing = this.nodes.get(key);
193
+ if (existing) {
194
+ existing.value = value;
195
+ this.promote(existing);
196
+ return;
197
+ }
198
+ const node = { key, value, older: this.newest };
199
+ if (this.newest) {
200
+ this.newest.newer = node;
201
+ }
202
+ else {
203
+ this.oldest = node;
204
+ }
205
+ this.newest = node;
206
+ this.nodes.set(key, node);
207
+ while (this.nodes.size > this.maxEntries) {
208
+ this.evictOldest();
209
+ }
210
+ }
211
+ promote(node) {
212
+ if (this.newest === node) {
213
+ return;
214
+ }
215
+ if (node.older) {
216
+ node.older.newer = node.newer;
217
+ }
218
+ else {
219
+ this.oldest = node.newer;
220
+ }
221
+ if (node.newer) {
222
+ node.newer.older = node.older;
223
+ }
224
+ node.older = this.newest;
225
+ node.newer = undefined;
226
+ if (this.newest) {
227
+ this.newest.newer = node;
228
+ }
229
+ else {
230
+ this.oldest = node;
231
+ }
232
+ this.newest = node;
233
+ }
234
+ evictOldest() {
235
+ const node = this.oldest;
236
+ if (!node) {
237
+ return;
238
+ }
239
+ this.oldest = node.newer;
240
+ if (this.oldest) {
241
+ this.oldest.older = undefined;
242
+ }
243
+ else {
244
+ this.newest = undefined;
245
+ }
246
+ this.nodes.delete(node.key);
247
+ }
248
+ }
157
249
  class ByteReader {
158
250
  offset = 0;
159
251
  buffer;
@@ -321,9 +413,10 @@ function parseClassFile(buffer) {
321
413
  }
322
414
  export class MinecraftExplorerService {
323
415
  config;
324
- signatureCache = new Map();
416
+ signatureCache;
325
417
  constructor(explicitConfig) {
326
418
  this.config = explicitConfig ?? loadConfig();
419
+ this.signatureCache = new SignatureCacheStore(Math.max(1, this.config.maxSignatureCache ?? 2_000));
327
420
  }
328
421
  async getSignature(input) {
329
422
  const jarPath = this.normalizeJarPathOrThrow(input.jarPath);
@@ -348,10 +441,11 @@ export class MinecraftExplorerService {
348
441
  ].join("|");
349
442
  const cached = this.signatureCache.get(cacheKey);
350
443
  if (cached) {
351
- this.signatureCache.delete(cacheKey);
352
- this.signatureCache.set(cacheKey, cached);
353
444
  return {
354
- ...cached,
445
+ constructors: cached.constructors,
446
+ methods: cached.methods,
447
+ fields: cached.fields,
448
+ warnings: cached.warnings,
355
449
  context: this.contextForJar(jarPath)
356
450
  };
357
451
  }
@@ -446,7 +540,15 @@ export class MinecraftExplorerService {
446
540
  }
447
541
  const toSignatureMember = (ownerFqn, ownerSimpleClassName, member, category) => {
448
542
  if (category === "field") {
449
- const fieldType = parseFieldType(member.descriptor).type;
543
+ const parsedField = parseFieldType(member.descriptor, 0, { allowVoid: false });
544
+ if (parsedField.next !== member.descriptor.length) {
545
+ throw createError({
546
+ code: ERROR_CODES.INVALID_INPUT,
547
+ message: `Invalid field descriptor "${member.descriptor}".`,
548
+ details: { descriptor: member.descriptor, position: parsedField.next }
549
+ });
550
+ }
551
+ const fieldType = parsedField.type;
450
552
  const modifiers = modifierPrefix(member.accessFlags, "field");
451
553
  return {
452
554
  ownerFqn,
@@ -520,12 +622,16 @@ export class MinecraftExplorerService {
520
622
  constructors,
521
623
  methods,
522
624
  fields,
523
- warnings,
524
- context: this.contextForJar(jarPath)
625
+ warnings
525
626
  };
526
627
  this.signatureCache.set(cacheKey, output);
527
- this.trimSignatureCache();
528
- return output;
628
+ return {
629
+ constructors: output.constructors,
630
+ methods: output.methods,
631
+ fields: output.fields,
632
+ warnings: output.warnings,
633
+ context: this.contextForJar(jarPath)
634
+ };
529
635
  }
530
636
  contextForJar(jarPath) {
531
637
  return {
@@ -548,15 +654,5 @@ export class MinecraftExplorerService {
548
654
  });
549
655
  }
550
656
  }
551
- trimSignatureCache() {
552
- const maxEntries = Math.max(1, this.config.maxSignatureCache ?? 2_000);
553
- while (this.signatureCache.size > maxEntries) {
554
- const oldest = this.signatureCache.keys().next().value;
555
- if (!oldest) {
556
- return;
557
- }
558
- this.signatureCache.delete(oldest);
559
- }
560
- }
561
657
  }
562
658
  //# sourceMappingURL=minecraft-explorer-service.js.map
@@ -16,10 +16,11 @@ export type MappingHealthReport = {
16
16
  degradations: string[];
17
17
  };
18
18
  export type IssueConfidence = "definite" | "likely" | "uncertain";
19
+ export type ValidationStatus = "full" | "partial" | "invalid";
19
20
  export type ResolutionPath = "member-remap-failed" | "target-mapping-failed" | "target-class-missing" | "source-signature-unavailable";
20
21
  export type ValidationIssue = {
21
22
  severity: "error" | "warning";
22
- kind: "target-not-found" | "target-mapping-failed" | "method-not-found" | "field-not-found" | "descriptor-mismatch" | "access-mismatch" | "unknown-annotation";
23
+ kind: "target-not-found" | "validation-incomplete" | "target-mapping-failed" | "method-not-found" | "field-not-found" | "descriptor-mismatch" | "access-mismatch" | "unknown-annotation";
23
24
  annotation: string;
24
25
  target: string;
25
26
  message: string;
@@ -42,6 +43,9 @@ export type ValidationSummary = {
42
43
  shadows: number;
43
44
  accessors: number;
44
45
  total: number;
46
+ membersValidated: number;
47
+ membersSkipped: number;
48
+ membersMissing: number;
45
49
  errors: number;
46
50
  warnings: number;
47
51
  definiteErrors: number;
@@ -54,8 +58,12 @@ export type MixinValidationProvenance = {
54
58
  jarPath: string;
55
59
  requestedMapping: SourceMapping;
56
60
  mappingApplied: SourceMapping;
61
+ requestedScope?: "vanilla" | "merged" | "loader";
62
+ appliedScope?: "vanilla" | "merged" | "loader";
63
+ requestedSourcePriority?: "loom-first" | "maven-first";
64
+ appliedSourcePriority?: "loom-first" | "maven-first";
57
65
  resolutionNotes?: string[];
58
- jarType?: "vanilla-client" | "merged" | "unknown";
66
+ jarType?: "vanilla-client" | "merged" | "loader" | "unknown";
59
67
  mappingChain?: string[];
60
68
  remapFailures?: number;
61
69
  mappingAutoDetected?: boolean;
@@ -91,11 +99,23 @@ export type AggregatedWarningGroup = {
91
99
  count: number;
92
100
  samples: string[];
93
101
  };
102
+ export type ConfidencePenalty = {
103
+ reason: string;
104
+ points: number;
105
+ };
106
+ export type ConfidenceBreakdown = {
107
+ baseScore: number;
108
+ score: number;
109
+ penalties: ConfidencePenalty[];
110
+ };
94
111
  export type MixinValidationResult = {
95
112
  className: string;
96
113
  targets: string[];
97
114
  priority?: number;
115
+ /** Legacy coarse pass/fail flag. Prefer validationStatus for the primary outcome. */
98
116
  valid: boolean;
117
+ /** full = fully validated, partial = tool-limited/incomplete, invalid = definite validation errors. */
118
+ validationStatus: ValidationStatus;
99
119
  issues: ValidationIssue[];
100
120
  summary: ValidationSummary;
101
121
  unfilteredSummary?: ValidationSummary;
@@ -106,6 +126,7 @@ export type MixinValidationResult = {
106
126
  resolvedMembers?: ResolvedMember[];
107
127
  toolHealth?: MappingHealthReport;
108
128
  confidenceScore?: number;
129
+ confidenceBreakdown?: ConfidenceBreakdown;
109
130
  quickSummary?: string;
110
131
  };
111
132
  export type ResolvedTargetMembers = {
@@ -141,6 +162,7 @@ export declare function extractMethodName(ref: string): string;
141
162
  * "tick" → undefined
142
163
  */
143
164
  export declare function extractMethodDescriptor(ref: string): string | undefined;
165
+ export declare function refreshMixinValidationOutcome(result: MixinValidationResult): MixinValidationResult;
144
166
  export declare function validateParsedMixin(parsed: ParsedMixin, targetMembers: Map<string, ResolvedTargetMembers>, warnings: string[], provenance?: MixinValidationProvenance, confidence?: IssueConfidence, mappingFailedTargets?: Set<string>, explain?: boolean, remapFailedMembers?: Map<string, Set<string>>, signatureFailedTargets?: Set<string>, suggestedCallContext?: {
145
167
  scope?: string;
146
168
  sourcePriority?: string;