@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
@@ -0,0 +1,24 @@
1
+ export async function mapWithConcurrencyLimit(items, limit, mapper) {
2
+ if (!Number.isInteger(limit) || limit < 1) {
3
+ throw new Error("limit must be a positive integer");
4
+ }
5
+ if (items.length === 0) {
6
+ return [];
7
+ }
8
+ const results = new Array(items.length);
9
+ let nextIndex = 0;
10
+ const workerCount = Math.min(limit, items.length);
11
+ const worker = async () => {
12
+ while (true) {
13
+ const currentIndex = nextIndex;
14
+ nextIndex += 1;
15
+ if (currentIndex >= items.length) {
16
+ return;
17
+ }
18
+ results[currentIndex] = await mapper(items[currentIndex], currentIndex);
19
+ }
20
+ };
21
+ await Promise.all(Array.from({ length: workerCount }, async () => worker()));
22
+ return results;
23
+ }
24
+ //# sourceMappingURL=concurrency.js.map
package/dist/config.js CHANGED
@@ -52,6 +52,17 @@ function expandHome(pathValue) {
52
52
  const withoutTilde = pathValue.startsWith("~/") ? pathValue.slice(2) : pathValue.slice(1);
53
53
  return resolve(homedir(), withoutTilde);
54
54
  }
55
+ function normalizeOptionalPathEnvValue(pathValue) {
56
+ if (!pathValue) {
57
+ return undefined;
58
+ }
59
+ const trimmed = pathValue.trim();
60
+ if (!trimmed) {
61
+ return undefined;
62
+ }
63
+ const normalized = trimmed.toLowerCase();
64
+ return normalized === "undefined" || normalized === "null" ? undefined : trimmed;
65
+ }
55
66
  function normalizePath(pathValue, field) {
56
67
  const expanded = expandHome(pathValue.trim());
57
68
  const normalizedForHost = normalizePathForHost(expanded, undefined, field);
@@ -120,14 +131,11 @@ function parseMappingSourcePriority(envValue) {
120
131
  return DEFAULTS.mappingSourcePriority;
121
132
  }
122
133
  function parseOptionalJarPath(envValue, field) {
123
- if (!envValue) {
124
- return undefined;
125
- }
126
- const trimmed = envValue.trim();
127
- if (!trimmed) {
128
- return undefined;
129
- }
130
- return normalizePath(trimmed, field);
134
+ const trimmed = normalizeOptionalPathEnvValue(envValue);
135
+ return trimmed ? normalizePath(trimmed, field) : undefined;
136
+ }
137
+ function parseRequiredPath(envValue, fallback, field) {
138
+ return normalizePath(normalizeOptionalPathEnvValue(envValue) ?? fallback, field);
131
139
  }
132
140
  function parseVineflowerPath(envValue) {
133
141
  return parseOptionalJarPath(envValue, "MCP_VINEFLOWER_JAR_PATH");
@@ -137,12 +145,12 @@ function buildArtifactsDirectory(cacheDir) {
137
145
  return normalizePath(input, "MCP_SQLITE_PATH");
138
146
  }
139
147
  export function loadConfig() {
140
- const cacheDir = normalizePath(process.env.MCP_CACHE_DIR ?? DEFAULTS.cacheDir, "MCP_CACHE_DIR");
141
- const localM2Path = normalizePath(process.env.MCP_LOCAL_M2 ?? DEFAULTS.localM2Path, "MCP_LOCAL_M2");
148
+ const cacheDir = parseRequiredPath(process.env.MCP_CACHE_DIR, DEFAULTS.cacheDir, "MCP_CACHE_DIR");
149
+ const localM2Path = parseRequiredPath(process.env.MCP_LOCAL_M2, DEFAULTS.localM2Path, "MCP_LOCAL_M2");
142
150
  const sourceRepos = parseRepos(process.env.MCP_SOURCE_REPOS);
143
151
  return {
144
152
  cacheDir,
145
- sqlitePath: normalizePath(process.env.MCP_SQLITE_PATH ?? buildArtifactsDirectory(cacheDir), "MCP_SQLITE_PATH"),
153
+ sqlitePath: parseRequiredPath(process.env.MCP_SQLITE_PATH, buildArtifactsDirectory(cacheDir), "MCP_SQLITE_PATH"),
146
154
  sourceRepos,
147
155
  localM2Path,
148
156
  vineflowerJarPath: parseVineflowerPath(process.env.MCP_VINEFLOWER_JAR_PATH),
@@ -1,11 +1,13 @@
1
- import { access, constants } from "node:fs/promises";
2
- import { mkdirSync, readdirSync, rmSync, statSync } from "node:fs";
1
+ import { access, constants, mkdir, readFile, readdir, stat } from "node:fs/promises";
2
+ import { mkdirSync, rmSync } from "node:fs";
3
3
  import { createHash } from "node:crypto";
4
4
  import { basename, join, relative, sep } from "node:path";
5
+ import { mapWithConcurrencyLimit } from "../concurrency.js";
5
6
  import { createError, ERROR_CODES, isAppError } from "../errors.js";
6
7
  import { assertJavaAvailable, runJavaProcess } from "../java-process.js";
7
8
  import { log } from "../logger.js";
8
9
  const DEFAULT_TIMEOUT_MS = 120_000;
10
+ const DECOMPILED_JAVA_READ_CONCURRENCY = 8;
9
11
  const VINEFLOWER_FLAG_PROFILES = [
10
12
  { label: "default", flags: ["-din=1", "-rbr=1", "-dgs=1"] },
11
13
  { label: "relaxed", flags: ["-din=1", "-rbr=0", "-dgs=0"] },
@@ -47,14 +49,14 @@ async function assertVineflowerAvailable(vineflowerJarPath) {
47
49
  });
48
50
  }
49
51
  }
50
- function collectJavaFilesSync(baseDir, currentDir = "") {
52
+ async function collectJavaFilesRecursive(baseDir, currentDir = "") {
51
53
  const absoluteBase = currentDir ? join(baseDir, currentDir) : baseDir;
52
- const entries = readdirSync(absoluteBase, { withFileTypes: true });
54
+ const entries = await readdir(absoluteBase, { withFileTypes: true });
53
55
  const result = [];
54
56
  for (const entry of entries) {
55
57
  const next = currentDir ? join(currentDir, entry.name) : entry.name;
56
58
  if (entry.isDirectory()) {
57
- result.push(...collectJavaFilesSync(baseDir, next));
59
+ result.push(...await collectJavaFilesRecursive(baseDir, next));
58
60
  continue;
59
61
  }
60
62
  if (entry.isFile() && entry.name.endsWith(".java")) {
@@ -66,23 +68,21 @@ function collectJavaFilesSync(baseDir, currentDir = "") {
66
68
  async function collectJavaFiles(baseDir) {
67
69
  try {
68
70
  const fastGlobModule = (await import("fast-glob"));
69
- const sync = fastGlobModule.default?.sync;
70
- if (typeof sync === "function") {
71
- return sync("**/*.java", { cwd: baseDir, onlyFiles: true });
71
+ const glob = fastGlobModule.default?.glob;
72
+ if (typeof glob === "function") {
73
+ return (await glob("**/*.java", { cwd: baseDir, onlyFiles: true }))
74
+ .sort((left, right) => left.localeCompare(right));
72
75
  }
73
76
  }
74
77
  catch {
75
78
  // optional dependency: fallback to recursive traversal
76
79
  }
77
- return collectJavaFilesSync(baseDir).map((candidate) => candidate.split(sep).join("/"));
80
+ return (await collectJavaFilesRecursive(baseDir))
81
+ .map((candidate) => candidate.split(sep).join("/"))
82
+ .sort((left, right) => left.localeCompare(right));
78
83
  }
79
84
  function readFileTreeText(filePath) {
80
- return new Promise((resolve, reject) => {
81
- import("node:fs/promises")
82
- .then((fs) => fs.readFile(filePath, "utf8"))
83
- .then(resolve)
84
- .catch(reject);
85
- });
85
+ return readFile(filePath, "utf8");
86
86
  }
87
87
  function decompileOutputDir(cacheDir, binaryJarPath, signature) {
88
88
  const digest = createHash("sha256").update(binaryJarPath).update(signature).digest("hex");
@@ -130,17 +130,18 @@ export async function decompileBinaryJar(binaryJarPath, cacheDir, options) {
130
130
  const signature = options.signature ?? basename(normalizedBinaryJarPath);
131
131
  const outputDir = decompileOutputDir(cacheDir, normalizedBinaryJarPath, signature).replace(/[/\\]$/, "");
132
132
  try {
133
- mkdirSync(outputDir, { recursive: true });
134
- if (statSync(outputDir, { throwIfNoEntry: false })) {
133
+ await mkdir(outputDir, { recursive: true });
134
+ const outputDirStats = await stat(outputDir).catch(() => undefined);
135
+ if (outputDirStats) {
135
136
  const existingJavaFiles = await collectJavaFiles(outputDir);
136
137
  if (existingJavaFiles.length > 0) {
137
- const results = await Promise.all(existingJavaFiles.map(async (candidate) => {
138
+ const results = await mapWithConcurrencyLimit(existingJavaFiles, DECOMPILED_JAVA_READ_CONCURRENCY, async (candidate) => {
138
139
  const abs = join(outputDir, candidate);
139
140
  return {
140
141
  filePath: normalizeOutputPath(outputDir, abs),
141
142
  content: await readFileTreeText(abs)
142
143
  };
143
- }));
144
+ });
144
145
  emitDecompileLog("decompile.done", {
145
146
  durationMs: Date.now() - startedAt,
146
147
  artifactIdCandidate: options.artifactIdCandidate,
@@ -181,13 +182,13 @@ export async function decompileBinaryJar(binaryJarPath, cacheDir, options) {
181
182
  }
182
183
  });
183
184
  }
184
- const javaFiles = await Promise.all(javaFileNames.map(async (candidate) => {
185
+ const javaFiles = await mapWithConcurrencyLimit(javaFileNames, DECOMPILED_JAVA_READ_CONCURRENCY, async (candidate) => {
185
186
  const abs = join(outputDir, candidate);
186
187
  return {
187
188
  filePath: normalizeOutputPath(outputDir, abs),
188
189
  content: await readFileTreeText(abs)
189
190
  };
190
- }));
191
+ });
191
192
  emitDecompileLog("decompile.done", {
192
193
  durationMs: Date.now() - startedAt,
193
194
  artifactIdCandidate: options.artifactIdCandidate,
@@ -75,7 +75,7 @@ export declare const analyzeModSchema: z.ZodEffects<z.ZodObject<{
75
75
  include: z.ZodOptional<z.ZodArray<z.ZodEnum<[string, ...string[]]>, "many">>;
76
76
  }, "strip", z.ZodTypeAny, {
77
77
  limit: number;
78
- searchType: "class" | "method" | "field" | "all" | "content";
78
+ searchType: "class" | "field" | "method" | "all" | "content";
79
79
  task: "search" | "remap" | "summary" | "class-source" | "decompile";
80
80
  subject: {
81
81
  kind: "jar";
@@ -111,7 +111,7 @@ export declare const analyzeModSchema: z.ZodEffects<z.ZodObject<{
111
111
  targetMapping?: "mojang" | "yarn" | undefined;
112
112
  outputJar?: string | undefined;
113
113
  query?: string | undefined;
114
- searchType?: "class" | "method" | "field" | "all" | "content" | undefined;
114
+ searchType?: "class" | "field" | "method" | "all" | "content" | undefined;
115
115
  detail?: "full" | "summary" | "standard" | undefined;
116
116
  include?: string[] | undefined;
117
117
  includeFiles?: boolean | undefined;
@@ -119,7 +119,7 @@ export declare const analyzeModSchema: z.ZodEffects<z.ZodObject<{
119
119
  executionMode?: "preview" | "apply" | undefined;
120
120
  }>, {
121
121
  limit: number;
122
- searchType: "class" | "method" | "field" | "all" | "content";
122
+ searchType: "class" | "field" | "method" | "all" | "content";
123
123
  task: "search" | "remap" | "summary" | "class-source" | "decompile";
124
124
  subject: {
125
125
  kind: "jar";
@@ -155,7 +155,7 @@ export declare const analyzeModSchema: z.ZodEffects<z.ZodObject<{
155
155
  targetMapping?: "mojang" | "yarn" | undefined;
156
156
  outputJar?: string | undefined;
157
157
  query?: string | undefined;
158
- searchType?: "class" | "method" | "field" | "all" | "content" | undefined;
158
+ searchType?: "class" | "field" | "method" | "all" | "content" | undefined;
159
159
  detail?: "full" | "summary" | "standard" | undefined;
160
160
  include?: string[] | undefined;
161
161
  includeFiles?: boolean | undefined;
@@ -8,15 +8,15 @@ export declare const analyzeSymbolShape: {
8
8
  owner: z.ZodOptional<z.ZodString>;
9
9
  descriptor: z.ZodOptional<z.ZodString>;
10
10
  }, "strip", z.ZodTypeAny, {
11
- kind: "symbol" | "class" | "method" | "field";
12
11
  name: string;
13
- owner?: string | undefined;
12
+ kind: "symbol" | "class" | "field" | "method";
14
13
  descriptor?: string | undefined;
14
+ owner?: string | undefined;
15
15
  }, {
16
- kind: "symbol" | "class" | "method" | "field";
17
16
  name: string;
18
- owner?: string | undefined;
17
+ kind: "symbol" | "class" | "field" | "method";
19
18
  descriptor?: string | undefined;
19
+ owner?: string | undefined;
20
20
  }>;
21
21
  version: z.ZodOptional<z.ZodString>;
22
22
  sourceMapping: z.ZodOptional<z.ZodEnum<["obfuscated", "mojang", "intermediary", "yarn"]>>;
@@ -39,15 +39,15 @@ export declare const analyzeSymbolSchema: z.ZodEffects<z.ZodObject<{
39
39
  owner: z.ZodOptional<z.ZodString>;
40
40
  descriptor: z.ZodOptional<z.ZodString>;
41
41
  }, "strip", z.ZodTypeAny, {
42
- kind: "symbol" | "class" | "method" | "field";
43
42
  name: string;
44
- owner?: string | undefined;
43
+ kind: "symbol" | "class" | "field" | "method";
45
44
  descriptor?: string | undefined;
45
+ owner?: string | undefined;
46
46
  }, {
47
- kind: "symbol" | "class" | "method" | "field";
48
47
  name: string;
49
- owner?: string | undefined;
48
+ kind: "symbol" | "class" | "field" | "method";
50
49
  descriptor?: string | undefined;
50
+ owner?: string | undefined;
51
51
  }>;
52
52
  version: z.ZodOptional<z.ZodString>;
53
53
  sourceMapping: z.ZodOptional<z.ZodEnum<["obfuscated", "mojang", "intermediary", "yarn"]>>;
@@ -65,10 +65,10 @@ export declare const analyzeSymbolSchema: z.ZodEffects<z.ZodObject<{
65
65
  nameMode: "auto" | "fqcn";
66
66
  task: "map" | "workspace" | "exists" | "exact-map" | "lifecycle" | "api-overview";
67
67
  subject: {
68
- kind: "symbol" | "class" | "method" | "field";
69
68
  name: string;
70
- owner?: string | undefined;
69
+ kind: "symbol" | "class" | "field" | "method";
71
70
  descriptor?: string | undefined;
71
+ owner?: string | undefined;
72
72
  };
73
73
  signatureMode: "exact" | "name-only";
74
74
  maxCandidates: number;
@@ -79,15 +79,15 @@ export declare const analyzeSymbolSchema: z.ZodEffects<z.ZodObject<{
79
79
  classNameMapping?: "intermediary" | "mojang" | "yarn" | "obfuscated" | undefined;
80
80
  detail?: "full" | "summary" | "standard" | undefined;
81
81
  include?: string[] | undefined;
82
- includeKinds?: ("class" | "method" | "field")[] | undefined;
82
+ includeKinds?: ("class" | "field" | "method")[] | undefined;
83
83
  maxRows?: number | undefined;
84
84
  }, {
85
85
  task: "map" | "workspace" | "exists" | "exact-map" | "lifecycle" | "api-overview";
86
86
  subject: {
87
- kind: "symbol" | "class" | "method" | "field";
88
87
  name: string;
89
- owner?: string | undefined;
88
+ kind: "symbol" | "class" | "field" | "method";
90
89
  descriptor?: string | undefined;
90
+ owner?: string | undefined;
91
91
  };
92
92
  projectPath?: string | undefined;
93
93
  version?: string | undefined;
@@ -98,17 +98,17 @@ export declare const analyzeSymbolSchema: z.ZodEffects<z.ZodObject<{
98
98
  detail?: "full" | "summary" | "standard" | undefined;
99
99
  include?: string[] | undefined;
100
100
  signatureMode?: "exact" | "name-only" | undefined;
101
- includeKinds?: ("class" | "method" | "field")[] | undefined;
101
+ includeKinds?: ("class" | "field" | "method")[] | undefined;
102
102
  maxRows?: number | undefined;
103
103
  maxCandidates?: number | undefined;
104
104
  }>, {
105
105
  nameMode: "auto" | "fqcn";
106
106
  task: "map" | "workspace" | "exists" | "exact-map" | "lifecycle" | "api-overview";
107
107
  subject: {
108
- kind: "symbol" | "class" | "method" | "field";
109
108
  name: string;
110
- owner?: string | undefined;
109
+ kind: "symbol" | "class" | "field" | "method";
111
110
  descriptor?: string | undefined;
111
+ owner?: string | undefined;
112
112
  };
113
113
  signatureMode: "exact" | "name-only";
114
114
  maxCandidates: number;
@@ -119,15 +119,15 @@ export declare const analyzeSymbolSchema: z.ZodEffects<z.ZodObject<{
119
119
  classNameMapping?: "intermediary" | "mojang" | "yarn" | "obfuscated" | undefined;
120
120
  detail?: "full" | "summary" | "standard" | undefined;
121
121
  include?: string[] | undefined;
122
- includeKinds?: ("class" | "method" | "field")[] | undefined;
122
+ includeKinds?: ("class" | "field" | "method")[] | undefined;
123
123
  maxRows?: number | undefined;
124
124
  }, {
125
125
  task: "map" | "workspace" | "exists" | "exact-map" | "lifecycle" | "api-overview";
126
126
  subject: {
127
- kind: "symbol" | "class" | "method" | "field";
128
127
  name: string;
129
- owner?: string | undefined;
128
+ kind: "symbol" | "class" | "field" | "method";
130
129
  descriptor?: string | undefined;
130
+ owner?: string | undefined;
131
131
  };
132
132
  projectPath?: string | undefined;
133
133
  version?: string | undefined;
@@ -138,7 +138,7 @@ export declare const analyzeSymbolSchema: z.ZodEffects<z.ZodObject<{
138
138
  detail?: "full" | "summary" | "standard" | undefined;
139
139
  include?: string[] | undefined;
140
140
  signatureMode?: "exact" | "name-only" | undefined;
141
- includeKinds?: ("class" | "method" | "field")[] | undefined;
141
+ includeKinds?: ("class" | "field" | "method")[] | undefined;
142
142
  maxRows?: number | undefined;
143
143
  maxCandidates?: number | undefined;
144
144
  }>;
@@ -180,6 +180,8 @@ type AnalyzeSymbolDeps = {
180
180
  symbol: string;
181
181
  descriptor?: string;
182
182
  mapping?: "obfuscated" | "mojang" | "intermediary" | "yarn";
183
+ toVersion?: string;
184
+ maxVersions?: number;
183
185
  }) => Promise<TraceSymbolLifecycleOutput>;
184
186
  resolveWorkspaceSymbol: (input: {
185
187
  projectPath: string;
@@ -231,7 +231,9 @@ export class AnalyzeSymbolService {
231
231
  ? `${input.subject.owner}.${input.subject.name}`
232
232
  : input.subject.name,
233
233
  descriptor: input.subject.descriptor,
234
- mapping: input.sourceMapping
234
+ mapping: input.sourceMapping,
235
+ toVersion: input.version,
236
+ maxVersions: 5
235
237
  });
236
238
  return {
237
239
  ...buildEntryToolResult({
@@ -309,10 +311,11 @@ export class AnalyzeSymbolService {
309
311
  };
310
312
  }
311
313
  case "api-overview": {
314
+ const classNameMapping = input.classNameMapping ?? input.sourceMapping ?? "obfuscated";
312
315
  const output = await this.deps.getClassApiMatrix({
313
316
  version: input.version,
314
317
  className: input.subject.name,
315
- classNameMapping: input.classNameMapping ?? "obfuscated",
318
+ classNameMapping,
316
319
  includeKinds: input.includeKinds,
317
320
  maxRows: input.maxRows
318
321
  });
@@ -329,7 +332,7 @@ export class AnalyzeSymbolService {
329
332
  kind: input.subject.kind,
330
333
  name: input.subject.name,
331
334
  version: input.version,
332
- classNameMapping: input.classNameMapping ?? "obfuscated"
335
+ classNameMapping
333
336
  }),
334
337
  counts: {
335
338
  rows: output.rowCount,