@adhisang/minecraft-modding-mcp 1.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 (106) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +21 -0
  3. package/README.md +765 -0
  4. package/dist/access-widener-parser.d.ts +24 -0
  5. package/dist/access-widener-parser.js +77 -0
  6. package/dist/cli.d.ts +2 -0
  7. package/dist/cli.js +4 -0
  8. package/dist/config.d.ts +27 -0
  9. package/dist/config.js +178 -0
  10. package/dist/decompiler/vineflower.d.ts +15 -0
  11. package/dist/decompiler/vineflower.js +185 -0
  12. package/dist/errors.d.ts +50 -0
  13. package/dist/errors.js +49 -0
  14. package/dist/hash.d.ts +1 -0
  15. package/dist/hash.js +12 -0
  16. package/dist/index.d.ts +7 -0
  17. package/dist/index.js +1447 -0
  18. package/dist/java-process.d.ts +16 -0
  19. package/dist/java-process.js +120 -0
  20. package/dist/logger.d.ts +3 -0
  21. package/dist/logger.js +21 -0
  22. package/dist/mapping-pipeline-service.d.ts +18 -0
  23. package/dist/mapping-pipeline-service.js +60 -0
  24. package/dist/mapping-service.d.ts +161 -0
  25. package/dist/mapping-service.js +1706 -0
  26. package/dist/maven-resolver.d.ts +22 -0
  27. package/dist/maven-resolver.js +122 -0
  28. package/dist/minecraft-explorer-service.d.ts +43 -0
  29. package/dist/minecraft-explorer-service.js +562 -0
  30. package/dist/mixin-parser.d.ts +34 -0
  31. package/dist/mixin-parser.js +194 -0
  32. package/dist/mixin-validator.d.ts +59 -0
  33. package/dist/mixin-validator.js +274 -0
  34. package/dist/mod-analyzer.d.ts +23 -0
  35. package/dist/mod-analyzer.js +346 -0
  36. package/dist/mod-decompile-service.d.ts +39 -0
  37. package/dist/mod-decompile-service.js +136 -0
  38. package/dist/mod-remap-service.d.ts +17 -0
  39. package/dist/mod-remap-service.js +186 -0
  40. package/dist/mod-search-service.d.ts +28 -0
  41. package/dist/mod-search-service.js +174 -0
  42. package/dist/mojang-tiny-mapping-service.d.ts +13 -0
  43. package/dist/mojang-tiny-mapping-service.js +351 -0
  44. package/dist/nbt/java-nbt-codec.d.ts +3 -0
  45. package/dist/nbt/java-nbt-codec.js +385 -0
  46. package/dist/nbt/json-patch.d.ts +3 -0
  47. package/dist/nbt/json-patch.js +352 -0
  48. package/dist/nbt/pipeline.d.ts +39 -0
  49. package/dist/nbt/pipeline.js +173 -0
  50. package/dist/nbt/typed-json.d.ts +10 -0
  51. package/dist/nbt/typed-json.js +205 -0
  52. package/dist/nbt/types.d.ts +66 -0
  53. package/dist/nbt/types.js +2 -0
  54. package/dist/observability.d.ts +88 -0
  55. package/dist/observability.js +165 -0
  56. package/dist/path-converter.d.ts +12 -0
  57. package/dist/path-converter.js +161 -0
  58. package/dist/path-resolver.d.ts +19 -0
  59. package/dist/path-resolver.js +78 -0
  60. package/dist/registry-service.d.ts +29 -0
  61. package/dist/registry-service.js +214 -0
  62. package/dist/repo-downloader.d.ts +15 -0
  63. package/dist/repo-downloader.js +111 -0
  64. package/dist/resources.d.ts +3 -0
  65. package/dist/resources.js +154 -0
  66. package/dist/search-hit-accumulator.d.ts +38 -0
  67. package/dist/search-hit-accumulator.js +153 -0
  68. package/dist/source-jar-reader.d.ts +13 -0
  69. package/dist/source-jar-reader.js +216 -0
  70. package/dist/source-resolver.d.ts +14 -0
  71. package/dist/source-resolver.js +274 -0
  72. package/dist/source-service.d.ts +404 -0
  73. package/dist/source-service.js +2881 -0
  74. package/dist/storage/artifacts-repo.d.ts +45 -0
  75. package/dist/storage/artifacts-repo.js +209 -0
  76. package/dist/storage/db.d.ts +14 -0
  77. package/dist/storage/db.js +132 -0
  78. package/dist/storage/files-repo.d.ts +78 -0
  79. package/dist/storage/files-repo.js +437 -0
  80. package/dist/storage/index-meta-repo.d.ts +35 -0
  81. package/dist/storage/index-meta-repo.js +97 -0
  82. package/dist/storage/migrations.d.ts +11 -0
  83. package/dist/storage/migrations.js +71 -0
  84. package/dist/storage/schema.d.ts +1 -0
  85. package/dist/storage/schema.js +160 -0
  86. package/dist/storage/sqlite.d.ts +20 -0
  87. package/dist/storage/sqlite.js +111 -0
  88. package/dist/storage/symbols-repo.d.ts +63 -0
  89. package/dist/storage/symbols-repo.js +401 -0
  90. package/dist/symbols/symbol-extractor.d.ts +7 -0
  91. package/dist/symbols/symbol-extractor.js +64 -0
  92. package/dist/tiny-remapper-resolver.d.ts +1 -0
  93. package/dist/tiny-remapper-resolver.js +62 -0
  94. package/dist/tiny-remapper-service.d.ts +16 -0
  95. package/dist/tiny-remapper-service.js +73 -0
  96. package/dist/types.d.ts +120 -0
  97. package/dist/types.js +2 -0
  98. package/dist/version-diff-service.d.ts +41 -0
  99. package/dist/version-diff-service.js +222 -0
  100. package/dist/version-service.d.ts +70 -0
  101. package/dist/version-service.js +411 -0
  102. package/dist/vineflower-resolver.d.ts +1 -0
  103. package/dist/vineflower-resolver.js +62 -0
  104. package/dist/workspace-mapping-service.d.ts +18 -0
  105. package/dist/workspace-mapping-service.js +89 -0
  106. package/package.json +61 -0
package/dist/index.js ADDED
@@ -0,0 +1,1447 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { MCPServer, object } from "mcp-use/server";
3
+ import { ZodError, z } from "zod";
4
+ import { loadConfig } from "./config.js";
5
+ import { ERROR_CODES, isAppError } from "./errors.js";
6
+ import { log } from "./logger.js";
7
+ import { applyNbtJsonPatch, nbtBase64ToTypedJson, typedJsonToNbtBase64 } from "./nbt/pipeline.js";
8
+ import { analyzeModJar } from "./mod-analyzer.js";
9
+ import { remapModJar } from "./mod-remap-service.js";
10
+ import { registerResources } from "./resources.js";
11
+ import { SourceService } from "./source-service.js";
12
+ if (!process.env.NODE_ENV) {
13
+ process.env.NODE_ENV = "production";
14
+ }
15
+ const SOURCE_MAPPINGS = ["official", "mojang", "intermediary", "yarn"];
16
+ const SOURCE_PRIORITIES = ["loom-first", "maven-first"];
17
+ const TARGET_KINDS = ["version", "jar", "coordinate"];
18
+ const SEARCH_INTENTS = ["symbol", "text", "path"];
19
+ const SEARCH_MATCHES = ["exact", "prefix", "contains", "regex"];
20
+ const SEARCH_SYMBOL_KINDS = ["class", "interface", "enum", "record", "method", "field"];
21
+ const MEMBER_ACCESS = ["public", "all"];
22
+ const WORKSPACE_SYMBOL_KINDS = ["class", "field", "method"];
23
+ const DECODE_COMPRESSIONS = ["none", "gzip", "auto"];
24
+ const ENCODE_COMPRESSIONS = ["none", "gzip"];
25
+ const nonEmptyString = z.string().trim().min(1);
26
+ const optionalNonEmptyString = z.string().trim().min(1).optional();
27
+ const optionalPositiveInt = z.number().int().positive().optional();
28
+ const sourceMappingSchema = z.enum(SOURCE_MAPPINGS);
29
+ const mappingSourcePrioritySchema = z.enum(SOURCE_PRIORITIES);
30
+ const targetKindSchema = z.enum(TARGET_KINDS);
31
+ const searchIntentSchema = z.enum(SEARCH_INTENTS);
32
+ const searchMatchSchema = z.enum(SEARCH_MATCHES);
33
+ const searchSymbolKindSchema = z.enum(SEARCH_SYMBOL_KINDS);
34
+ const memberAccessSchema = z.enum(MEMBER_ACCESS);
35
+ const workspaceSymbolKindSchema = z.enum(WORKSPACE_SYMBOL_KINDS);
36
+ const decodeCompressionSchema = z.enum(DECODE_COMPRESSIONS);
37
+ const encodeCompressionSchema = z.enum(ENCODE_COMPRESSIONS);
38
+ function validateTargetPair(value, ctx) {
39
+ const hasArtifactId = Boolean(value.artifactId);
40
+ const hasTargetKind = value.targetKind !== undefined;
41
+ const hasTargetValue = value.targetValue !== undefined;
42
+ if (hasArtifactId && (hasTargetKind || hasTargetValue)) {
43
+ ctx.addIssue({
44
+ code: z.ZodIssueCode.custom,
45
+ message: "artifactId and targetKind/targetValue are mutually exclusive.",
46
+ path: ["artifactId"]
47
+ });
48
+ return;
49
+ }
50
+ if (hasTargetKind !== hasTargetValue) {
51
+ ctx.addIssue({
52
+ code: z.ZodIssueCode.custom,
53
+ message: "targetKind and targetValue must be provided together.",
54
+ path: [hasTargetKind ? "targetValue" : "targetKind"]
55
+ });
56
+ return;
57
+ }
58
+ if (!hasArtifactId && !hasTargetKind) {
59
+ ctx.addIssue({
60
+ code: z.ZodIssueCode.custom,
61
+ message: "Either artifactId or targetKind+targetValue must be provided.",
62
+ path: ["artifactId"]
63
+ });
64
+ }
65
+ }
66
+ const listVersionsSchema = z.object({
67
+ includeSnapshots: z.boolean().optional(),
68
+ limit: optionalPositiveInt
69
+ });
70
+ const resolveArtifactSchema = z.object({
71
+ targetKind: targetKindSchema,
72
+ targetValue: nonEmptyString,
73
+ mapping: sourceMappingSchema.optional(),
74
+ sourcePriority: mappingSourcePrioritySchema.optional(),
75
+ allowDecompile: z.boolean().optional()
76
+ });
77
+ const getClassSourceSchema = z
78
+ .object({
79
+ className: nonEmptyString,
80
+ artifactId: optionalNonEmptyString,
81
+ targetKind: targetKindSchema.optional(),
82
+ targetValue: optionalNonEmptyString,
83
+ mapping: sourceMappingSchema.optional(),
84
+ sourcePriority: mappingSourcePrioritySchema.optional(),
85
+ allowDecompile: z.boolean().optional(),
86
+ startLine: optionalPositiveInt,
87
+ endLine: optionalPositiveInt,
88
+ maxLines: optionalPositiveInt
89
+ })
90
+ .superRefine((value, ctx) => {
91
+ validateTargetPair({
92
+ artifactId: value.artifactId,
93
+ targetKind: value.targetKind,
94
+ targetValue: value.targetValue
95
+ }, ctx);
96
+ if (value.startLine !== undefined &&
97
+ value.endLine !== undefined &&
98
+ value.startLine > value.endLine) {
99
+ ctx.addIssue({
100
+ code: z.ZodIssueCode.custom,
101
+ message: "startLine must be less than or equal to endLine.",
102
+ path: ["startLine"]
103
+ });
104
+ }
105
+ });
106
+ const getClassMembersSchema = z
107
+ .object({
108
+ className: nonEmptyString,
109
+ artifactId: optionalNonEmptyString,
110
+ targetKind: targetKindSchema.optional(),
111
+ targetValue: optionalNonEmptyString,
112
+ mapping: sourceMappingSchema.optional(),
113
+ sourcePriority: mappingSourcePrioritySchema.optional(),
114
+ allowDecompile: z.boolean().optional(),
115
+ access: memberAccessSchema.optional(),
116
+ includeSynthetic: z.boolean().optional(),
117
+ includeInherited: z.boolean().optional(),
118
+ memberPattern: optionalNonEmptyString,
119
+ maxMembers: optionalPositiveInt
120
+ })
121
+ .superRefine((value, ctx) => {
122
+ validateTargetPair({
123
+ artifactId: value.artifactId,
124
+ targetKind: value.targetKind,
125
+ targetValue: value.targetValue
126
+ }, ctx);
127
+ });
128
+ const searchClassSourceSchema = z.object({
129
+ artifactId: nonEmptyString,
130
+ query: nonEmptyString,
131
+ intent: searchIntentSchema.optional(),
132
+ match: searchMatchSchema.optional(),
133
+ packagePrefix: optionalNonEmptyString,
134
+ fileGlob: optionalNonEmptyString,
135
+ symbolKind: searchSymbolKindSchema.optional(),
136
+ snippetLines: optionalPositiveInt,
137
+ includeDefinition: z.boolean().optional(),
138
+ includeOneHop: z.boolean().optional(),
139
+ limit: optionalPositiveInt,
140
+ cursor: optionalNonEmptyString
141
+ });
142
+ const getArtifactFileSchema = z.object({
143
+ artifactId: nonEmptyString,
144
+ filePath: nonEmptyString,
145
+ maxBytes: optionalPositiveInt
146
+ });
147
+ const listArtifactFilesSchema = z.object({
148
+ artifactId: nonEmptyString,
149
+ prefix: optionalNonEmptyString,
150
+ limit: optionalPositiveInt,
151
+ cursor: optionalNonEmptyString
152
+ });
153
+ const traceSymbolLifecycleSchema = z.object({
154
+ symbol: nonEmptyString,
155
+ descriptor: optionalNonEmptyString,
156
+ fromVersion: optionalNonEmptyString,
157
+ toVersion: optionalNonEmptyString,
158
+ mapping: sourceMappingSchema.optional(),
159
+ sourcePriority: mappingSourcePrioritySchema.optional(),
160
+ includeSnapshots: z.boolean().optional(),
161
+ maxVersions: optionalPositiveInt,
162
+ includeTimeline: z.boolean().optional()
163
+ });
164
+ const diffClassSignaturesSchema = z.object({
165
+ className: nonEmptyString,
166
+ fromVersion: nonEmptyString,
167
+ toVersion: nonEmptyString,
168
+ mapping: sourceMappingSchema.optional(),
169
+ sourcePriority: mappingSourcePrioritySchema.optional()
170
+ });
171
+ const findMappingSchema = z.object({
172
+ version: nonEmptyString,
173
+ kind: workspaceSymbolKindSchema,
174
+ name: nonEmptyString,
175
+ owner: optionalNonEmptyString,
176
+ descriptor: optionalNonEmptyString,
177
+ sourceMapping: sourceMappingSchema,
178
+ targetMapping: sourceMappingSchema,
179
+ sourcePriority: mappingSourcePrioritySchema.optional()
180
+ }).superRefine((value, ctx) => {
181
+ if (value.kind === "class") {
182
+ if (value.owner) {
183
+ ctx.addIssue({
184
+ code: z.ZodIssueCode.custom,
185
+ message: "owner is not allowed when kind=class.",
186
+ path: ["owner"]
187
+ });
188
+ }
189
+ if (value.descriptor) {
190
+ ctx.addIssue({
191
+ code: z.ZodIssueCode.custom,
192
+ message: "descriptor is not allowed when kind=class.",
193
+ path: ["descriptor"]
194
+ });
195
+ }
196
+ if (!value.name.includes(".")) {
197
+ ctx.addIssue({
198
+ code: z.ZodIssueCode.custom,
199
+ message: "name must be fully-qualified class name when kind=class.",
200
+ path: ["name"]
201
+ });
202
+ }
203
+ return;
204
+ }
205
+ if (!value.owner) {
206
+ ctx.addIssue({
207
+ code: z.ZodIssueCode.custom,
208
+ message: "owner is required when kind is field or method.",
209
+ path: ["owner"]
210
+ });
211
+ }
212
+ if (/[\s./()]/.test(value.name)) {
213
+ ctx.addIssue({
214
+ code: z.ZodIssueCode.custom,
215
+ message: "name must be a simple member name when kind is field or method.",
216
+ path: ["name"]
217
+ });
218
+ }
219
+ if (value.kind === "field") {
220
+ if (value.descriptor) {
221
+ ctx.addIssue({
222
+ code: z.ZodIssueCode.custom,
223
+ message: "descriptor is not allowed when kind=field.",
224
+ path: ["descriptor"]
225
+ });
226
+ }
227
+ return;
228
+ }
229
+ if (!value.descriptor) {
230
+ ctx.addIssue({
231
+ code: z.ZodIssueCode.custom,
232
+ message: "descriptor is required when kind=method.",
233
+ path: ["descriptor"]
234
+ });
235
+ }
236
+ });
237
+ const resolveMethodMappingExactSchema = z
238
+ .object({
239
+ version: nonEmptyString,
240
+ kind: workspaceSymbolKindSchema,
241
+ name: nonEmptyString,
242
+ owner: optionalNonEmptyString,
243
+ descriptor: optionalNonEmptyString,
244
+ sourceMapping: sourceMappingSchema,
245
+ targetMapping: sourceMappingSchema,
246
+ sourcePriority: mappingSourcePrioritySchema.optional()
247
+ })
248
+ .superRefine((value, ctx) => {
249
+ if (value.kind !== "method") {
250
+ ctx.addIssue({
251
+ code: z.ZodIssueCode.custom,
252
+ message: "resolve-method-mapping-exact requires kind=method.",
253
+ path: ["kind"]
254
+ });
255
+ }
256
+ if (!value.owner) {
257
+ ctx.addIssue({
258
+ code: z.ZodIssueCode.custom,
259
+ message: "owner is required when kind=method.",
260
+ path: ["owner"]
261
+ });
262
+ }
263
+ if (!value.descriptor) {
264
+ ctx.addIssue({
265
+ code: z.ZodIssueCode.custom,
266
+ message: "descriptor is required when kind=method.",
267
+ path: ["descriptor"]
268
+ });
269
+ }
270
+ if (/[\s./()]/.test(value.name)) {
271
+ ctx.addIssue({
272
+ code: z.ZodIssueCode.custom,
273
+ message: "name must be a simple method name when kind=method.",
274
+ path: ["name"]
275
+ });
276
+ }
277
+ });
278
+ const classApiKindsSchema = z.string().superRefine((value, ctx) => {
279
+ const tokens = value
280
+ .split(",")
281
+ .map((entry) => entry.trim().toLowerCase())
282
+ .filter((entry) => entry.length > 0);
283
+ if (tokens.length === 0) {
284
+ ctx.addIssue({
285
+ code: z.ZodIssueCode.custom,
286
+ message: "includeKinds must include at least one of class, field, method."
287
+ });
288
+ return;
289
+ }
290
+ const invalidTokens = tokens.filter((entry) => entry !== "class" && entry !== "field" && entry !== "method");
291
+ if (invalidTokens.length > 0) {
292
+ ctx.addIssue({
293
+ code: z.ZodIssueCode.custom,
294
+ message: `includeKinds contains invalid values: ${invalidTokens.join(", ")}. Allowed values are class, field, method.`
295
+ });
296
+ }
297
+ });
298
+ const getClassApiMatrixSchema = z.object({
299
+ version: nonEmptyString,
300
+ className: nonEmptyString,
301
+ classNameMapping: sourceMappingSchema,
302
+ includeKinds: classApiKindsSchema.optional(),
303
+ sourcePriority: mappingSourcePrioritySchema.optional()
304
+ });
305
+ const resolveWorkspaceSymbolSchema = z
306
+ .object({
307
+ projectPath: nonEmptyString,
308
+ version: nonEmptyString,
309
+ kind: workspaceSymbolKindSchema,
310
+ name: nonEmptyString,
311
+ owner: optionalNonEmptyString,
312
+ descriptor: optionalNonEmptyString,
313
+ sourceMapping: sourceMappingSchema,
314
+ sourcePriority: mappingSourcePrioritySchema.optional()
315
+ })
316
+ .superRefine((value, ctx) => {
317
+ if (value.kind === "class") {
318
+ if (value.owner) {
319
+ ctx.addIssue({
320
+ code: z.ZodIssueCode.custom,
321
+ message: "owner is not allowed when kind=class.",
322
+ path: ["owner"]
323
+ });
324
+ }
325
+ if (value.descriptor) {
326
+ ctx.addIssue({
327
+ code: z.ZodIssueCode.custom,
328
+ message: "descriptor is not allowed when kind=class.",
329
+ path: ["descriptor"]
330
+ });
331
+ }
332
+ if (!value.name.includes(".")) {
333
+ ctx.addIssue({
334
+ code: z.ZodIssueCode.custom,
335
+ message: "name must be fully-qualified class name when kind=class.",
336
+ path: ["name"]
337
+ });
338
+ }
339
+ return;
340
+ }
341
+ if (!value.owner) {
342
+ ctx.addIssue({
343
+ code: z.ZodIssueCode.custom,
344
+ message: "owner is required when kind is field or method.",
345
+ path: ["owner"]
346
+ });
347
+ }
348
+ if (/[\s./()]/.test(value.name)) {
349
+ ctx.addIssue({
350
+ code: z.ZodIssueCode.custom,
351
+ message: "name must be a simple member name when kind is field or method.",
352
+ path: ["name"]
353
+ });
354
+ }
355
+ if (value.kind === "field") {
356
+ if (value.descriptor) {
357
+ ctx.addIssue({
358
+ code: z.ZodIssueCode.custom,
359
+ message: "descriptor is not allowed when kind=field.",
360
+ path: ["descriptor"]
361
+ });
362
+ }
363
+ return;
364
+ }
365
+ if (!value.descriptor) {
366
+ ctx.addIssue({
367
+ code: z.ZodIssueCode.custom,
368
+ message: "descriptor is required when kind=method.",
369
+ path: ["descriptor"]
370
+ });
371
+ }
372
+ });
373
+ const checkSymbolExistsSchema = z.object({
374
+ version: nonEmptyString,
375
+ kind: workspaceSymbolKindSchema,
376
+ owner: optionalNonEmptyString,
377
+ name: nonEmptyString,
378
+ descriptor: optionalNonEmptyString,
379
+ sourceMapping: sourceMappingSchema,
380
+ sourcePriority: mappingSourcePrioritySchema.optional()
381
+ }).superRefine((value, ctx) => {
382
+ if (value.kind === "class") {
383
+ if (value.owner) {
384
+ ctx.addIssue({
385
+ code: z.ZodIssueCode.custom,
386
+ message: "owner is not allowed when kind=class.",
387
+ path: ["owner"]
388
+ });
389
+ }
390
+ if (value.descriptor) {
391
+ ctx.addIssue({
392
+ code: z.ZodIssueCode.custom,
393
+ message: "descriptor is not allowed when kind=class.",
394
+ path: ["descriptor"]
395
+ });
396
+ }
397
+ if (!value.name.includes(".")) {
398
+ ctx.addIssue({
399
+ code: z.ZodIssueCode.custom,
400
+ message: "name must be fully-qualified class name when kind=class.",
401
+ path: ["name"]
402
+ });
403
+ }
404
+ return;
405
+ }
406
+ if (!value.owner) {
407
+ ctx.addIssue({
408
+ code: z.ZodIssueCode.custom,
409
+ message: "owner is required when kind is field or method.",
410
+ path: ["owner"]
411
+ });
412
+ }
413
+ if (/[\s./()]/.test(value.name)) {
414
+ ctx.addIssue({
415
+ code: z.ZodIssueCode.custom,
416
+ message: "name must be a simple member name when kind is field or method.",
417
+ path: ["name"]
418
+ });
419
+ }
420
+ if (value.kind === "field") {
421
+ if (value.descriptor) {
422
+ ctx.addIssue({
423
+ code: z.ZodIssueCode.custom,
424
+ message: "descriptor is not allowed when kind=field.",
425
+ path: ["descriptor"]
426
+ });
427
+ }
428
+ return;
429
+ }
430
+ if (!value.descriptor) {
431
+ ctx.addIssue({
432
+ code: z.ZodIssueCode.custom,
433
+ message: "descriptor is required when kind=method.",
434
+ path: ["descriptor"]
435
+ });
436
+ }
437
+ });
438
+ const nbtToJsonSchema = z.object({
439
+ nbtBase64: nonEmptyString,
440
+ compression: decodeCompressionSchema.optional()
441
+ });
442
+ const nbtPatchOperationSchema = z
443
+ .object({
444
+ op: z.enum(["add", "remove", "replace", "test"]),
445
+ path: nonEmptyString,
446
+ value: z.unknown().optional()
447
+ })
448
+ .passthrough();
449
+ const nbtApplyJsonPatchSchema = z.object({
450
+ typedJson: z.unknown(),
451
+ patch: z.array(nbtPatchOperationSchema)
452
+ });
453
+ const jsonToNbtSchema = z.object({
454
+ typedJson: z.unknown(),
455
+ compression: encodeCompressionSchema.optional()
456
+ });
457
+ const indexArtifactSchema = z.object({
458
+ artifactId: nonEmptyString,
459
+ force: z.boolean().optional()
460
+ });
461
+ const validateMixinSchema = z.object({
462
+ source: nonEmptyString,
463
+ version: nonEmptyString,
464
+ mapping: sourceMappingSchema.optional(),
465
+ sourcePriority: mappingSourcePrioritySchema.optional()
466
+ });
467
+ const validateAccessWidenerSchema = z.object({
468
+ content: nonEmptyString,
469
+ version: nonEmptyString,
470
+ mapping: sourceMappingSchema.optional(),
471
+ sourcePriority: mappingSourcePrioritySchema.optional()
472
+ });
473
+ const analyzeModJarSchema = z.object({
474
+ jarPath: nonEmptyString,
475
+ includeClasses: z.boolean().optional()
476
+ });
477
+ const getRegistryDataSchema = z.object({
478
+ version: nonEmptyString,
479
+ registry: optionalNonEmptyString
480
+ });
481
+ const COMPARE_VERSIONS_CATEGORIES = ["classes", "registry", "all"];
482
+ const compareVersionsCategorySchema = z.enum(COMPARE_VERSIONS_CATEGORIES);
483
+ const compareVersionsSchema = z.object({
484
+ fromVersion: nonEmptyString,
485
+ toVersion: nonEmptyString,
486
+ category: compareVersionsCategorySchema.optional(),
487
+ packageFilter: optionalNonEmptyString,
488
+ maxClassResults: optionalPositiveInt
489
+ });
490
+ const decompileModJarSchema = z.object({
491
+ jarPath: nonEmptyString,
492
+ className: optionalNonEmptyString
493
+ });
494
+ const getModClassSourceSchema = z.object({
495
+ jarPath: nonEmptyString,
496
+ className: nonEmptyString
497
+ });
498
+ const MOD_SEARCH_TYPES = ["class", "method", "field", "content", "all"];
499
+ const modSearchTypeSchema = z.enum(MOD_SEARCH_TYPES);
500
+ const searchModSourceSchema = z.object({
501
+ jarPath: nonEmptyString,
502
+ query: nonEmptyString,
503
+ searchType: modSearchTypeSchema.optional(),
504
+ limit: optionalPositiveInt
505
+ });
506
+ const REMAP_TARGETS = ["yarn", "mojang"];
507
+ const remapTargetSchema = z.enum(REMAP_TARGETS);
508
+ const remapModJarSchema = z.object({
509
+ inputJar: nonEmptyString,
510
+ outputJar: optionalNonEmptyString,
511
+ mcVersion: optionalNonEmptyString,
512
+ targetMapping: remapTargetSchema
513
+ });
514
+ const emptySchema = z.object({}).passthrough();
515
+ function getServerVersionFromPackageJson() {
516
+ try {
517
+ const packageJsonUrl = new URL("../package.json", import.meta.url);
518
+ const packageJson = JSON.parse(readFileSync(packageJsonUrl, "utf8"));
519
+ if (typeof packageJson.version === "string" && packageJson.version.trim()) {
520
+ return packageJson.version.trim();
521
+ }
522
+ }
523
+ catch {
524
+ // ignore and fallback
525
+ }
526
+ return "0.3.0";
527
+ }
528
+ const SERVER_VERSION = getServerVersionFromPackageJson();
529
+ const server = new MCPServer({
530
+ name: "@adhisang/minecraft-modding-mcp",
531
+ version: SERVER_VERSION
532
+ });
533
+ const config = loadConfig();
534
+ const nbtLimits = {
535
+ maxInputBytes: config.maxNbtInputBytes,
536
+ maxInflatedBytes: config.maxNbtInflatedBytes,
537
+ maxResponseBytes: config.maxNbtResponseBytes
538
+ };
539
+ const sourceService = new SourceService(config);
540
+ registerResources(server, sourceService);
541
+ let processHandlersAttached = false;
542
+ let serverStarted = false;
543
+ function attachProcessErrorHandlers() {
544
+ if (processHandlersAttached) {
545
+ return;
546
+ }
547
+ processHandlersAttached = true;
548
+ process.on("uncaughtException", (caughtError) => {
549
+ const error = caughtError instanceof Error ? caughtError : new Error(String(caughtError));
550
+ log("error", "process.uncaught_exception", {
551
+ message: error.message,
552
+ stack: error.stack
553
+ });
554
+ process.exitCode = 1;
555
+ });
556
+ process.on("unhandledRejection", (reason) => {
557
+ const error = reason instanceof Error ? reason : new Error(String(reason));
558
+ log("error", "process.unhandled_rejection", {
559
+ message: error.message,
560
+ stack: error.stack
561
+ });
562
+ process.exitCode = 1;
563
+ });
564
+ }
565
+ function buildRequestId() {
566
+ return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
567
+ }
568
+ function buildTarget(kind, value) {
569
+ if (!kind || !value) {
570
+ return undefined;
571
+ }
572
+ return { kind, value };
573
+ }
574
+ function parseClassApiKinds(value) {
575
+ if (value == null) {
576
+ return undefined;
577
+ }
578
+ const normalized = value
579
+ .split(",")
580
+ .map((entry) => entry.trim().toLowerCase())
581
+ .filter((entry) => entry === "class" || entry === "field" || entry === "method");
582
+ if (normalized.length === 0) {
583
+ return undefined;
584
+ }
585
+ return [...new Set(normalized)];
586
+ }
587
+ function toFieldErrorsFromZod(error) {
588
+ return error.issues.map((issue) => ({
589
+ path: issue.path.join(".") || "$",
590
+ message: issue.message,
591
+ code: issue.code
592
+ }));
593
+ }
594
+ function toHints(details) {
595
+ if (typeof details !== "object" || details == null) {
596
+ return undefined;
597
+ }
598
+ const hints = [];
599
+ const maybeNextAction = details.nextAction;
600
+ if (typeof maybeNextAction === "string" && maybeNextAction.trim()) {
601
+ hints.push(maybeNextAction.trim());
602
+ }
603
+ if (hints.length === 0) {
604
+ return undefined;
605
+ }
606
+ return hints;
607
+ }
608
+ function statusForErrorCode(code) {
609
+ if (code === ERROR_CODES.INVALID_INPUT ||
610
+ code === ERROR_CODES.COORDINATE_PARSE_FAILED ||
611
+ code === ERROR_CODES.INVALID_LINE_RANGE ||
612
+ code === ERROR_CODES.NBT_PARSE_FAILED ||
613
+ code === ERROR_CODES.NBT_INVALID_TYPED_JSON ||
614
+ code === ERROR_CODES.JSON_PATCH_INVALID ||
615
+ code === ERROR_CODES.NBT_ENCODE_FAILED ||
616
+ code === ERROR_CODES.NBT_UNSUPPORTED_FEATURE) {
617
+ return 400;
618
+ }
619
+ if (code === ERROR_CODES.JSON_PATCH_CONFLICT || code === ERROR_CODES.CONTEXT_UNRESOLVED) {
620
+ return 409;
621
+ }
622
+ if (code === ERROR_CODES.SOURCE_NOT_FOUND ||
623
+ code === ERROR_CODES.FILE_NOT_FOUND ||
624
+ code === ERROR_CODES.JAR_NOT_FOUND ||
625
+ code === ERROR_CODES.VERSION_NOT_FOUND ||
626
+ code === ERROR_CODES.CLASS_NOT_FOUND) {
627
+ return 404;
628
+ }
629
+ if (code === ERROR_CODES.MAPPING_NOT_APPLIED ||
630
+ code === ERROR_CODES.MAPPING_UNAVAILABLE ||
631
+ code === ERROR_CODES.NAMESPACE_MISMATCH ||
632
+ code === ERROR_CODES.DECOMPILE_DISABLED ||
633
+ code === ERROR_CODES.REMAP_FAILED) {
634
+ return 422;
635
+ }
636
+ if (code === ERROR_CODES.REMAPPER_UNAVAILABLE ||
637
+ code === ERROR_CODES.JAVA_PROCESS_FAILED) {
638
+ return 503;
639
+ }
640
+ if (code === ERROR_CODES.LIMIT_EXCEEDED) {
641
+ return 413;
642
+ }
643
+ if (code === ERROR_CODES.REPO_FETCH_FAILED) {
644
+ return 502;
645
+ }
646
+ if (code === ERROR_CODES.DECOMPILER_UNAVAILABLE ||
647
+ code === ERROR_CODES.DECOMPILER_FAILED ||
648
+ code === ERROR_CODES.JAVA_UNAVAILABLE ||
649
+ code === ERROR_CODES.REGISTRY_GENERATION_FAILED) {
650
+ return 503;
651
+ }
652
+ return 500;
653
+ }
654
+ function extractFieldErrorsFromDetails(details) {
655
+ if (typeof details !== "object" || details == null) {
656
+ return undefined;
657
+ }
658
+ const maybeFieldErrors = details.fieldErrors;
659
+ if (!Array.isArray(maybeFieldErrors)) {
660
+ return undefined;
661
+ }
662
+ const normalized = maybeFieldErrors
663
+ .map((entry) => {
664
+ if (typeof entry !== "object" || entry == null) {
665
+ return undefined;
666
+ }
667
+ const asRecord = entry;
668
+ const path = asRecord.path;
669
+ const message = asRecord.message;
670
+ const code = asRecord.code;
671
+ if (typeof path !== "string" || typeof message !== "string") {
672
+ return undefined;
673
+ }
674
+ return {
675
+ path,
676
+ message,
677
+ code: typeof code === "string" ? code : undefined
678
+ };
679
+ })
680
+ .filter((entry) => entry != null);
681
+ return normalized.length > 0 ? normalized : undefined;
682
+ }
683
+ function mapErrorToProblem(caughtError, requestId) {
684
+ if (caughtError instanceof ZodError) {
685
+ return {
686
+ type: "https://minecraft-modding-mcp.dev/problems/invalid-input",
687
+ title: "Invalid input",
688
+ detail: "Request validation failed.",
689
+ status: 400,
690
+ code: ERROR_CODES.INVALID_INPUT,
691
+ instance: requestId,
692
+ fieldErrors: toFieldErrorsFromZod(caughtError),
693
+ hints: ["Check fieldErrors and submit a valid tool argument payload."]
694
+ };
695
+ }
696
+ if (isAppError(caughtError)) {
697
+ return {
698
+ type: `https://minecraft-modding-mcp.dev/problems/${caughtError.code.toLowerCase()}`,
699
+ title: "Tool execution error",
700
+ detail: caughtError.message,
701
+ status: statusForErrorCode(caughtError.code),
702
+ code: caughtError.code,
703
+ instance: requestId,
704
+ fieldErrors: extractFieldErrorsFromDetails(caughtError.details),
705
+ hints: toHints(caughtError.details)
706
+ };
707
+ }
708
+ return {
709
+ type: "https://minecraft-modding-mcp.dev/problems/internal",
710
+ title: "Internal server error",
711
+ detail: "Unexpected server error.",
712
+ status: 500,
713
+ code: ERROR_CODES.INTERNAL,
714
+ instance: requestId
715
+ };
716
+ }
717
+ function splitWarnings(data) {
718
+ const result = { ...data };
719
+ const maybeWarnings = result.warnings;
720
+ if (!Array.isArray(maybeWarnings)) {
721
+ return {
722
+ result,
723
+ warnings: []
724
+ };
725
+ }
726
+ const warnings = maybeWarnings.filter((entry) => typeof entry === "string");
727
+ delete result.warnings;
728
+ return {
729
+ result,
730
+ warnings
731
+ };
732
+ }
733
+ async function runTool(tool, rawInput, schema, action) {
734
+ const requestId = buildRequestId();
735
+ const startedAt = Date.now();
736
+ try {
737
+ const parsedInput = schema.parse(rawInput);
738
+ const payload = await action(parsedInput);
739
+ const { result, warnings } = splitWarnings(payload);
740
+ return object({
741
+ result,
742
+ meta: {
743
+ requestId,
744
+ tool,
745
+ durationMs: Date.now() - startedAt,
746
+ warnings
747
+ }
748
+ });
749
+ }
750
+ catch (caughtError) {
751
+ const problem = mapErrorToProblem(caughtError, requestId);
752
+ if (isAppError(caughtError)) {
753
+ const isSevere = caughtError.code === ERROR_CODES.DB_FAILURE ||
754
+ caughtError.code === ERROR_CODES.REPO_FETCH_FAILED ||
755
+ caughtError.code === ERROR_CODES.REGISTRY_GENERATION_FAILED ||
756
+ caughtError.code === ERROR_CODES.JAVA_UNAVAILABLE ||
757
+ caughtError.code.startsWith("ERR_DECOMPILER");
758
+ if (isSevere) {
759
+ log("error", "tool.call.failed", {
760
+ requestId,
761
+ tool,
762
+ code: caughtError.code,
763
+ message: caughtError.message
764
+ });
765
+ }
766
+ else {
767
+ log("warn", "tool.call.warning", {
768
+ requestId,
769
+ tool,
770
+ code: caughtError.code,
771
+ message: caughtError.message
772
+ });
773
+ }
774
+ }
775
+ else if (!(caughtError instanceof ZodError)) {
776
+ log("error", "tool.call.unhandled", {
777
+ requestId,
778
+ tool,
779
+ reason: caughtError instanceof Error ? caughtError.message : String(caughtError)
780
+ });
781
+ }
782
+ return object({
783
+ error: problem,
784
+ meta: {
785
+ requestId,
786
+ tool,
787
+ durationMs: Date.now() - startedAt,
788
+ warnings: []
789
+ }
790
+ });
791
+ }
792
+ }
793
+ server.tool({
794
+ name: "list-versions",
795
+ description: "List available Minecraft versions from Mojang manifest and locally cached version jars.",
796
+ inputs: [
797
+ { name: "includeSnapshots", type: "boolean", description: "default false" },
798
+ { name: "limit", type: "number", description: "default 20, max 200" }
799
+ ],
800
+ annotations: {
801
+ readOnlyHint: true
802
+ }
803
+ }, async (rawInput) => {
804
+ return runTool("list-versions", rawInput, listVersionsSchema, async (input) => sourceService.listVersions({
805
+ includeSnapshots: input.includeSnapshots,
806
+ limit: input.limit
807
+ }));
808
+ });
809
+ server.tool({
810
+ name: "resolve-artifact",
811
+ description: "Resolve source artifact from version, jar path, or Maven coordinate and return artifact metadata. For targetKind=jar, only <basename>-sources.jar is auto-adopted; other adjacent *-sources.jar files are informational.",
812
+ inputs: [
813
+ { name: "targetKind", type: "string", required: true, description: "version | jar | coordinate" },
814
+ { name: "targetValue", type: "string", required: true },
815
+ { name: "mapping", type: "string", description: "official | mojang | intermediary | yarn" },
816
+ { name: "sourcePriority", type: "string", description: "loom-first | maven-first" },
817
+ { name: "allowDecompile", type: "boolean", description: "default true" }
818
+ ],
819
+ annotations: {
820
+ readOnlyHint: true
821
+ }
822
+ }, async (rawInput) => {
823
+ return runTool("resolve-artifact", rawInput, resolveArtifactSchema, async (input) => sourceService.resolveArtifact({
824
+ target: {
825
+ kind: input.targetKind,
826
+ value: input.targetValue
827
+ },
828
+ mapping: input.mapping,
829
+ sourcePriority: input.sourcePriority,
830
+ allowDecompile: input.allowDecompile
831
+ }));
832
+ });
833
+ server.tool({
834
+ name: "get-class-source",
835
+ description: "Get Java source for a class by artifactId or by resolving target (version/jar/coordinate), with optional line-range filtering.",
836
+ inputs: [
837
+ { name: "className", type: "string", required: true },
838
+ { name: "artifactId", type: "string" },
839
+ { name: "targetKind", type: "string", description: "version | jar | coordinate" },
840
+ { name: "targetValue", type: "string" },
841
+ { name: "mapping", type: "string", description: "official | mojang | intermediary | yarn" },
842
+ { name: "sourcePriority", type: "string", description: "loom-first | maven-first" },
843
+ { name: "allowDecompile", type: "boolean", description: "default true" },
844
+ { name: "startLine", type: "number" },
845
+ { name: "endLine", type: "number" },
846
+ { name: "maxLines", type: "number" }
847
+ ],
848
+ annotations: {
849
+ readOnlyHint: true
850
+ }
851
+ }, async (rawInput) => {
852
+ return runTool("get-class-source", rawInput, getClassSourceSchema, async (input) => sourceService.getClassSource({
853
+ className: input.className,
854
+ artifactId: input.artifactId,
855
+ target: buildTarget(input.targetKind, input.targetValue),
856
+ mapping: input.mapping,
857
+ sourcePriority: input.sourcePriority,
858
+ allowDecompile: input.allowDecompile,
859
+ startLine: input.startLine,
860
+ endLine: input.endLine,
861
+ maxLines: input.maxLines
862
+ }));
863
+ });
864
+ server.tool({
865
+ name: "get-class-members",
866
+ description: "Get fields/methods/constructors for one class from binary bytecode by artifactId or by resolving target (version/jar/coordinate).",
867
+ inputs: [
868
+ { name: "className", type: "string", required: true },
869
+ { name: "artifactId", type: "string" },
870
+ { name: "targetKind", type: "string", description: "version | jar | coordinate" },
871
+ { name: "targetValue", type: "string" },
872
+ { name: "mapping", type: "string", description: "official | mojang | intermediary | yarn (default official)" },
873
+ { name: "sourcePriority", type: "string", description: "loom-first | maven-first" },
874
+ { name: "allowDecompile", type: "boolean", description: "default true" },
875
+ { name: "access", type: "string", description: "public | all (default public)" },
876
+ { name: "includeSynthetic", type: "boolean", description: "default false" },
877
+ { name: "includeInherited", type: "boolean", description: "default false" },
878
+ { name: "memberPattern", type: "string" },
879
+ { name: "maxMembers", type: "number", description: "default 500, max 5000" }
880
+ ],
881
+ annotations: {
882
+ readOnlyHint: true
883
+ }
884
+ }, async (rawInput) => {
885
+ return runTool("get-class-members", rawInput, getClassMembersSchema, async (input) => sourceService.getClassMembers({
886
+ className: input.className,
887
+ artifactId: input.artifactId,
888
+ target: buildTarget(input.targetKind, input.targetValue),
889
+ mapping: input.mapping,
890
+ sourcePriority: input.sourcePriority,
891
+ allowDecompile: input.allowDecompile,
892
+ access: input.access,
893
+ includeSynthetic: input.includeSynthetic,
894
+ includeInherited: input.includeInherited,
895
+ memberPattern: input.memberPattern,
896
+ maxMembers: input.maxMembers
897
+ }));
898
+ });
899
+ server.tool({
900
+ name: "search-class-source",
901
+ description: "Search indexed class source files for one artifact with symbol/text/path intent and optional one-hop relation expansion.",
902
+ inputs: [
903
+ { name: "artifactId", type: "string", required: true },
904
+ { name: "query", type: "string", required: true },
905
+ { name: "intent", type: "string", description: "symbol | text | path" },
906
+ { name: "match", type: "string", description: "exact | prefix | contains | regex" },
907
+ { name: "packagePrefix", type: "string" },
908
+ { name: "fileGlob", type: "string" },
909
+ { name: "symbolKind", type: "string", description: "class | interface | enum | record | method | field" },
910
+ { name: "snippetLines", type: "number", description: "default 8, clamp 1..80" },
911
+ { name: "includeDefinition", type: "boolean", description: "default false" },
912
+ { name: "includeOneHop", type: "boolean", description: "default false" },
913
+ { name: "limit", type: "number", description: "default 20" },
914
+ { name: "cursor", type: "string" }
915
+ ],
916
+ annotations: {
917
+ readOnlyHint: true
918
+ }
919
+ }, async (rawInput) => {
920
+ return runTool("search-class-source", rawInput, searchClassSourceSchema, async (input) => {
921
+ const scope = input.packagePrefix || input.fileGlob || input.symbolKind
922
+ ? {
923
+ packagePrefix: input.packagePrefix,
924
+ fileGlob: input.fileGlob,
925
+ symbolKind: input.symbolKind
926
+ }
927
+ : undefined;
928
+ const include = input.snippetLines !== undefined ||
929
+ input.includeDefinition !== undefined ||
930
+ input.includeOneHop !== undefined
931
+ ? {
932
+ snippetLines: input.snippetLines,
933
+ includeDefinition: input.includeDefinition,
934
+ includeOneHop: input.includeOneHop
935
+ }
936
+ : undefined;
937
+ return sourceService.searchClassSource({
938
+ artifactId: input.artifactId,
939
+ query: input.query,
940
+ intent: input.intent,
941
+ match: input.match,
942
+ scope: scope,
943
+ include,
944
+ limit: input.limit,
945
+ cursor: input.cursor
946
+ });
947
+ });
948
+ });
949
+ server.tool({
950
+ name: "get-artifact-file",
951
+ description: "Get full source file content by artifactId and file path.",
952
+ inputs: [
953
+ { name: "artifactId", type: "string", required: true },
954
+ { name: "filePath", type: "string", required: true },
955
+ { name: "maxBytes", type: "number" }
956
+ ],
957
+ annotations: {
958
+ readOnlyHint: true
959
+ }
960
+ }, async (rawInput) => {
961
+ return runTool("get-artifact-file", rawInput, getArtifactFileSchema, async (input) => sourceService.getArtifactFile({
962
+ artifactId: input.artifactId,
963
+ filePath: input.filePath,
964
+ maxBytes: input.maxBytes
965
+ }));
966
+ });
967
+ server.tool({
968
+ name: "list-artifact-files",
969
+ description: "List source file paths in an artifact with optional prefix filter and cursor-based pagination.",
970
+ inputs: [
971
+ { name: "artifactId", type: "string", required: true },
972
+ { name: "prefix", type: "string" },
973
+ { name: "limit", type: "number" },
974
+ { name: "cursor", type: "string" }
975
+ ],
976
+ annotations: {
977
+ readOnlyHint: true
978
+ }
979
+ }, async (rawInput) => {
980
+ return runTool("list-artifact-files", rawInput, listArtifactFilesSchema, async (input) => sourceService.listArtifactFiles(input));
981
+ });
982
+ server.tool({
983
+ name: "trace-symbol-lifecycle",
984
+ description: "Trace which Minecraft versions contain a specific class method and report first/last seen versions.",
985
+ inputs: [
986
+ { name: "symbol", type: "string", required: true, description: "fully.qualified.Class.method" },
987
+ { name: "descriptor", type: "string", description: 'optional JVM descriptor, e.g. "(I)V"' },
988
+ { name: "fromVersion", type: "string" },
989
+ { name: "toVersion", type: "string" },
990
+ { name: "mapping", type: "string", description: "official | mojang | intermediary | yarn (default official)" },
991
+ { name: "sourcePriority", type: "string", description: "loom-first | maven-first" },
992
+ { name: "includeSnapshots", type: "boolean", description: "default false" },
993
+ { name: "maxVersions", type: "number", description: "default 120, max 400" },
994
+ { name: "includeTimeline", type: "boolean", description: "default false" }
995
+ ],
996
+ annotations: {
997
+ readOnlyHint: true
998
+ }
999
+ }, async (rawInput) => {
1000
+ return runTool("trace-symbol-lifecycle", rawInput, traceSymbolLifecycleSchema, async (input) => sourceService.traceSymbolLifecycle({
1001
+ symbol: input.symbol,
1002
+ descriptor: input.descriptor,
1003
+ fromVersion: input.fromVersion,
1004
+ toVersion: input.toVersion,
1005
+ mapping: input.mapping,
1006
+ sourcePriority: input.sourcePriority,
1007
+ includeSnapshots: input.includeSnapshots,
1008
+ maxVersions: input.maxVersions,
1009
+ includeTimeline: input.includeTimeline
1010
+ }));
1011
+ });
1012
+ server.tool({
1013
+ name: "diff-class-signatures",
1014
+ description: "Compare one class signature between two Minecraft versions and report added/removed/modified constructors, methods, and fields.",
1015
+ inputs: [
1016
+ { name: "className", type: "string", required: true },
1017
+ { name: "fromVersion", type: "string", required: true },
1018
+ { name: "toVersion", type: "string", required: true },
1019
+ { name: "mapping", type: "string", description: "official | mojang | intermediary | yarn (default official)" },
1020
+ { name: "sourcePriority", type: "string", description: "loom-first | maven-first" }
1021
+ ],
1022
+ annotations: {
1023
+ readOnlyHint: true
1024
+ }
1025
+ }, async (rawInput) => {
1026
+ return runTool("diff-class-signatures", rawInput, diffClassSignaturesSchema, async (input) => sourceService.diffClassSignatures({
1027
+ className: input.className,
1028
+ fromVersion: input.fromVersion,
1029
+ toVersion: input.toVersion,
1030
+ mapping: input.mapping,
1031
+ sourcePriority: input.sourcePriority
1032
+ }));
1033
+ });
1034
+ server.tool({
1035
+ name: "find-mapping",
1036
+ description: "Find symbol mapping candidates between namespaces using structured symbol inputs for a specific Minecraft version.",
1037
+ inputs: [
1038
+ { name: "version", type: "string", required: true },
1039
+ { name: "kind", type: "string", required: true, description: "class | field | method" },
1040
+ { name: "name", type: "string", required: true },
1041
+ { name: "owner", type: "string" },
1042
+ { name: "descriptor", type: "string" },
1043
+ { name: "sourceMapping", type: "string", required: true, description: "official | mojang | intermediary | yarn" },
1044
+ { name: "targetMapping", type: "string", required: true, description: "official | mojang | intermediary | yarn" },
1045
+ { name: "sourcePriority", type: "string", description: "loom-first | maven-first" }
1046
+ ],
1047
+ annotations: {
1048
+ readOnlyHint: true
1049
+ }
1050
+ }, async (rawInput) => {
1051
+ return runTool("find-mapping", rawInput, findMappingSchema, async (input) => sourceService.findMapping({
1052
+ version: input.version,
1053
+ kind: input.kind,
1054
+ name: input.name,
1055
+ owner: input.owner,
1056
+ descriptor: input.descriptor,
1057
+ sourceMapping: input.sourceMapping,
1058
+ targetMapping: input.targetMapping,
1059
+ sourcePriority: input.sourcePriority
1060
+ }));
1061
+ });
1062
+ server.tool({
1063
+ name: "resolve-method-mapping-exact",
1064
+ description: "Resolve one method mapping exactly by owner+name+descriptor between namespaces and report resolved/not_found/ambiguous.",
1065
+ inputs: [
1066
+ { name: "version", type: "string", required: true },
1067
+ { name: "kind", type: "string", required: true, description: "class | field | method" },
1068
+ { name: "name", type: "string", required: true },
1069
+ { name: "owner", type: "string" },
1070
+ { name: "descriptor", type: "string", description: "required for kind=method" },
1071
+ { name: "sourceMapping", type: "string", required: true, description: "official | mojang | intermediary | yarn" },
1072
+ { name: "targetMapping", type: "string", required: true, description: "official | mojang | intermediary | yarn" },
1073
+ { name: "sourcePriority", type: "string", description: "loom-first | maven-first" }
1074
+ ],
1075
+ annotations: {
1076
+ readOnlyHint: true
1077
+ }
1078
+ }, async (rawInput) => {
1079
+ return runTool("resolve-method-mapping-exact", rawInput, resolveMethodMappingExactSchema, async (input) => sourceService.resolveMethodMappingExact({
1080
+ version: input.version,
1081
+ kind: input.kind,
1082
+ name: input.name,
1083
+ owner: input.owner,
1084
+ descriptor: input.descriptor,
1085
+ sourceMapping: input.sourceMapping,
1086
+ targetMapping: input.targetMapping,
1087
+ sourcePriority: input.sourcePriority
1088
+ }));
1089
+ });
1090
+ server.tool({
1091
+ name: "get-class-api-matrix",
1092
+ description: "List class/member API rows across official/mojang/intermediary/yarn mappings for one class and Minecraft version.",
1093
+ inputs: [
1094
+ { name: "version", type: "string", required: true },
1095
+ { name: "className", type: "string", required: true },
1096
+ { name: "classNameMapping", type: "string", required: true, description: "official | mojang | intermediary | yarn" },
1097
+ { name: "includeKinds", type: "string", description: "comma-separated: class,field,method" },
1098
+ { name: "sourcePriority", type: "string", description: "loom-first | maven-first" }
1099
+ ],
1100
+ annotations: {
1101
+ readOnlyHint: true
1102
+ }
1103
+ }, async (rawInput) => {
1104
+ return runTool("get-class-api-matrix", rawInput, getClassApiMatrixSchema, async (input) => sourceService.getClassApiMatrix({
1105
+ version: input.version,
1106
+ className: input.className,
1107
+ classNameMapping: input.classNameMapping,
1108
+ includeKinds: parseClassApiKinds(input.includeKinds),
1109
+ sourcePriority: input.sourcePriority
1110
+ }));
1111
+ });
1112
+ server.tool({
1113
+ name: "resolve-workspace-symbol",
1114
+ description: "Resolve class/field/method names as seen at compile time for a workspace by reading Gradle Loom mapping settings.",
1115
+ inputs: [
1116
+ { name: "projectPath", type: "string", required: true },
1117
+ { name: "version", type: "string", required: true },
1118
+ { name: "kind", type: "string", required: true, description: "class | field | method" },
1119
+ { name: "name", type: "string", required: true },
1120
+ { name: "owner", type: "string" },
1121
+ { name: "descriptor", type: "string" },
1122
+ { name: "sourceMapping", type: "string", required: true, description: "official | mojang | intermediary | yarn" },
1123
+ { name: "sourcePriority", type: "string", description: "loom-first | maven-first" }
1124
+ ],
1125
+ annotations: {
1126
+ readOnlyHint: true
1127
+ }
1128
+ }, async (rawInput) => {
1129
+ return runTool("resolve-workspace-symbol", rawInput, resolveWorkspaceSymbolSchema, async (input) => sourceService.resolveWorkspaceSymbol({
1130
+ projectPath: input.projectPath,
1131
+ version: input.version,
1132
+ kind: input.kind,
1133
+ name: input.name,
1134
+ owner: input.owner,
1135
+ descriptor: input.descriptor,
1136
+ sourceMapping: input.sourceMapping,
1137
+ sourcePriority: input.sourcePriority
1138
+ }));
1139
+ });
1140
+ server.tool({
1141
+ name: "check-symbol-exists",
1142
+ description: "Check whether a class/field/method symbol exists in a specific mapping namespace for one Minecraft version.",
1143
+ inputs: [
1144
+ { name: "version", type: "string", required: true },
1145
+ { name: "kind", type: "string", required: true, description: "class | field | method" },
1146
+ { name: "owner", type: "string" },
1147
+ { name: "name", type: "string", required: true },
1148
+ { name: "descriptor", type: "string", description: "required for kind=method" },
1149
+ { name: "sourceMapping", type: "string", required: true, description: "official | mojang | intermediary | yarn" },
1150
+ { name: "sourcePriority", type: "string", description: "loom-first | maven-first" }
1151
+ ],
1152
+ annotations: {
1153
+ readOnlyHint: true
1154
+ }
1155
+ }, async (rawInput) => {
1156
+ return runTool("check-symbol-exists", rawInput, checkSymbolExistsSchema, async (input) => sourceService.checkSymbolExists({
1157
+ version: input.version,
1158
+ kind: input.kind,
1159
+ owner: input.owner,
1160
+ name: input.name,
1161
+ descriptor: input.descriptor,
1162
+ sourceMapping: input.sourceMapping,
1163
+ sourcePriority: input.sourcePriority
1164
+ }));
1165
+ });
1166
+ server.tool({
1167
+ name: "nbt-to-json",
1168
+ description: "Decode Java Edition NBT binary payload (base64) into typed JSON.",
1169
+ inputs: [
1170
+ { name: "nbtBase64", type: "string", required: true },
1171
+ { name: "compression", type: "string", description: "none | gzip | auto (default auto)" }
1172
+ ],
1173
+ annotations: {
1174
+ readOnlyHint: true
1175
+ }
1176
+ }, async (rawInput) => {
1177
+ return runTool("nbt-to-json", rawInput, nbtToJsonSchema, async (input) => Promise.resolve(nbtBase64ToTypedJson({
1178
+ nbtBase64: input.nbtBase64,
1179
+ compression: input.compression
1180
+ }, nbtLimits)));
1181
+ });
1182
+ server.tool({
1183
+ name: "nbt-apply-json-patch",
1184
+ description: "Apply RFC6902 add/remove/replace/test operations to typed NBT JSON.",
1185
+ inputs: [
1186
+ { name: "typedJson", type: "object", required: true },
1187
+ {
1188
+ name: "patch",
1189
+ type: "array",
1190
+ required: true,
1191
+ description: "RFC6902 operation array (add/remove/replace/test)"
1192
+ }
1193
+ ],
1194
+ annotations: {
1195
+ readOnlyHint: true
1196
+ }
1197
+ }, async (rawInput) => {
1198
+ return runTool("nbt-apply-json-patch", rawInput, nbtApplyJsonPatchSchema, async (input) => Promise.resolve(applyNbtJsonPatch({
1199
+ typedJson: input.typedJson,
1200
+ patch: input.patch
1201
+ }, nbtLimits)));
1202
+ });
1203
+ server.tool({
1204
+ name: "json-to-nbt",
1205
+ description: "Encode typed NBT JSON to Java Edition NBT binary payload (base64).",
1206
+ inputs: [
1207
+ { name: "typedJson", type: "object", required: true },
1208
+ { name: "compression", type: "string", description: "none | gzip (default none)" }
1209
+ ],
1210
+ annotations: {
1211
+ readOnlyHint: true
1212
+ }
1213
+ }, async (rawInput) => {
1214
+ return runTool("json-to-nbt", rawInput, jsonToNbtSchema, async (input) => Promise.resolve(typedJsonToNbtBase64({
1215
+ typedJson: input.typedJson,
1216
+ compression: input.compression
1217
+ }, nbtLimits)));
1218
+ });
1219
+ server.tool({
1220
+ name: "index-artifact",
1221
+ description: "Rebuild indexed files/symbols metadata for an existing artifactId. Does not resolve new artifacts.",
1222
+ inputs: [
1223
+ { name: "artifactId", type: "string", required: true },
1224
+ { name: "force", type: "boolean", description: "default false" }
1225
+ ]
1226
+ }, async (rawInput) => {
1227
+ return runTool("index-artifact", rawInput, indexArtifactSchema, async (input) => sourceService.indexArtifact({
1228
+ artifactId: input.artifactId,
1229
+ force: input.force
1230
+ }));
1231
+ });
1232
+ server.tool({
1233
+ name: "get-runtime-metrics",
1234
+ description: "Get runtime service counters and latency snapshots for cache/search/index diagnostics.",
1235
+ annotations: {
1236
+ readOnlyHint: true
1237
+ }
1238
+ }, async (rawInput) => {
1239
+ return runTool("get-runtime-metrics", rawInput, emptySchema, async () => Promise.resolve(sourceService.getRuntimeMetrics()));
1240
+ });
1241
+ server.tool({
1242
+ name: "validate-mixin",
1243
+ description: "Validate Mixin source against Minecraft bytecode signatures for a given version.",
1244
+ inputs: [
1245
+ { name: "source", type: "string", required: true, description: "Mixin Java source text" },
1246
+ { name: "version", type: "string", required: true, description: "Minecraft version" },
1247
+ { name: "mapping", type: "string", description: "official | mojang | intermediary | yarn" },
1248
+ { name: "sourcePriority", type: "string", description: "loom-first | maven-first" }
1249
+ ],
1250
+ annotations: {
1251
+ readOnlyHint: true
1252
+ }
1253
+ }, async (rawInput) => {
1254
+ return runTool("validate-mixin", rawInput, validateMixinSchema, async (input) => sourceService.validateMixin({
1255
+ source: input.source,
1256
+ version: input.version,
1257
+ mapping: input.mapping,
1258
+ sourcePriority: input.sourcePriority
1259
+ }));
1260
+ });
1261
+ server.tool({
1262
+ name: "validate-access-widener",
1263
+ description: "Validate Access Widener file entries against Minecraft bytecode signatures for a given version.",
1264
+ inputs: [
1265
+ { name: "content", type: "string", required: true, description: "Access Widener file content" },
1266
+ { name: "version", type: "string", required: true, description: "Minecraft version" },
1267
+ { name: "mapping", type: "string", description: "official | mojang | intermediary | yarn" },
1268
+ { name: "sourcePriority", type: "string", description: "loom-first | maven-first" }
1269
+ ],
1270
+ annotations: {
1271
+ readOnlyHint: true
1272
+ }
1273
+ }, async (rawInput) => {
1274
+ return runTool("validate-access-widener", rawInput, validateAccessWidenerSchema, async (input) => sourceService.validateAccessWidener({
1275
+ content: input.content,
1276
+ version: input.version,
1277
+ mapping: input.mapping,
1278
+ sourcePriority: input.sourcePriority
1279
+ }));
1280
+ });
1281
+ server.tool({
1282
+ name: "analyze-mod-jar",
1283
+ description: "Analyze a Minecraft mod JAR to extract loader type, metadata, entrypoints, mixins, and dependencies.",
1284
+ inputs: [
1285
+ { name: "jarPath", type: "string", required: true, description: "Local path to the mod JAR file" },
1286
+ { name: "includeClasses", type: "boolean", description: "Include full class listing (default false)" }
1287
+ ],
1288
+ annotations: {
1289
+ readOnlyHint: true
1290
+ }
1291
+ }, async (rawInput) => {
1292
+ return runTool("analyze-mod-jar", rawInput, analyzeModJarSchema, async (input) => {
1293
+ const result = await analyzeModJar(input.jarPath, {
1294
+ includeClasses: input.includeClasses ?? false
1295
+ });
1296
+ return result;
1297
+ });
1298
+ });
1299
+ server.tool({
1300
+ name: "get-registry-data",
1301
+ description: "Get Minecraft registry data (blocks, items, biomes, etc.) for a specific version by running the server data generator.",
1302
+ inputs: [
1303
+ { name: "version", type: "string", required: true, description: "Minecraft version (e.g. 1.21)" },
1304
+ {
1305
+ name: "registry",
1306
+ type: "string",
1307
+ description: 'Optional registry name (e.g. "block", "item", "minecraft:biome"). Omit to list all registries.'
1308
+ }
1309
+ ],
1310
+ annotations: {
1311
+ readOnlyHint: true
1312
+ }
1313
+ }, async (rawInput) => {
1314
+ return runTool("get-registry-data", rawInput, getRegistryDataSchema, async (input) => sourceService.getRegistryData({
1315
+ version: input.version,
1316
+ registry: input.registry
1317
+ }));
1318
+ });
1319
+ server.tool({
1320
+ name: "compare-versions",
1321
+ description: "Compare two Minecraft versions to find added/removed classes and registry entry changes. Useful for understanding what changed between versions during mod migration.",
1322
+ inputs: [
1323
+ { name: "fromVersion", type: "string", required: true, description: "Older Minecraft version (e.g. 1.20.4)" },
1324
+ { name: "toVersion", type: "string", required: true, description: "Newer Minecraft version (e.g. 1.21)" },
1325
+ {
1326
+ name: "category",
1327
+ type: "string",
1328
+ description: "classes | registry | all (default all)"
1329
+ },
1330
+ { name: "packageFilter", type: "string", description: "Filter classes to a package prefix (e.g. net.minecraft.world.item)" },
1331
+ { name: "maxClassResults", type: "number", description: "Max class results per direction (default 500, max 5000)" }
1332
+ ],
1333
+ annotations: {
1334
+ readOnlyHint: true
1335
+ }
1336
+ }, async (rawInput) => {
1337
+ return runTool("compare-versions", rawInput, compareVersionsSchema, async (input) => sourceService.compareVersions({
1338
+ fromVersion: input.fromVersion,
1339
+ toVersion: input.toVersion,
1340
+ category: input.category,
1341
+ packageFilter: input.packageFilter,
1342
+ maxClassResults: input.maxClassResults
1343
+ }));
1344
+ });
1345
+ server.tool({
1346
+ name: "decompile-mod-jar",
1347
+ description: "Decompile a Minecraft mod JAR using Vineflower and list available classes, or view a specific class source. Builds on analyze-mod-jar by exposing the actual source code.",
1348
+ inputs: [
1349
+ { name: "jarPath", type: "string", required: true, description: "Local path to the mod JAR file" },
1350
+ {
1351
+ name: "className",
1352
+ type: "string",
1353
+ description: "Optional fully-qualified class name to view source. Omit to list all classes."
1354
+ }
1355
+ ],
1356
+ annotations: {
1357
+ readOnlyHint: true
1358
+ }
1359
+ }, async (rawInput) => {
1360
+ return runTool("decompile-mod-jar", rawInput, decompileModJarSchema, async (input) => sourceService.decompileModJar({
1361
+ jarPath: input.jarPath,
1362
+ className: input.className
1363
+ }));
1364
+ });
1365
+ server.tool({
1366
+ name: "get-mod-class-source",
1367
+ description: "Get decompiled source code for a specific class in a mod JAR. The mod JAR will be decompiled if not already cached.",
1368
+ inputs: [
1369
+ { name: "jarPath", type: "string", required: true, description: "Local path to the mod JAR file" },
1370
+ { name: "className", type: "string", required: true, description: "Fully-qualified class name (e.g. com.example.MyMixin)" }
1371
+ ],
1372
+ annotations: {
1373
+ readOnlyHint: true
1374
+ }
1375
+ }, async (rawInput) => {
1376
+ return runTool("get-mod-class-source", rawInput, getModClassSourceSchema, async (input) => sourceService.getModClassSource({
1377
+ jarPath: input.jarPath,
1378
+ className: input.className
1379
+ }));
1380
+ });
1381
+ server.tool({
1382
+ name: "search-mod-source",
1383
+ description: "Search through decompiled mod JAR source code by class name, method, field, or content pattern. The mod JAR will be decompiled automatically if not already cached.",
1384
+ inputs: [
1385
+ { name: "jarPath", type: "string", required: true, description: "Local path to the mod JAR file" },
1386
+ { name: "query", type: "string", required: true, description: "Search pattern (regex or literal string)" },
1387
+ {
1388
+ name: "searchType",
1389
+ type: "string",
1390
+ description: "class | method | field | content | all (default all)"
1391
+ },
1392
+ { name: "limit", type: "number", description: "Max results (default 50, max 200)" }
1393
+ ],
1394
+ annotations: {
1395
+ readOnlyHint: true
1396
+ }
1397
+ }, async (rawInput) => {
1398
+ return runTool("search-mod-source", rawInput, searchModSourceSchema, async (input) => sourceService.searchModSource({
1399
+ jarPath: input.jarPath,
1400
+ query: input.query,
1401
+ searchType: input.searchType,
1402
+ limit: input.limit
1403
+ }));
1404
+ });
1405
+ server.tool({
1406
+ name: "remap-mod-jar",
1407
+ description: "Remap a Fabric mod JAR from intermediary to yarn/mojang names. Requires Java to be installed.",
1408
+ inputs: [
1409
+ { name: "inputJar", type: "string", required: true, description: "Path to the mod JAR file" },
1410
+ { name: "outputJar", type: "string", description: "Output path for remapped JAR (auto-generated if omitted)" },
1411
+ {
1412
+ name: "mcVersion",
1413
+ type: "string",
1414
+ description: "Minecraft version (auto-detected from mod metadata if omitted)"
1415
+ },
1416
+ { name: "targetMapping", type: "string", required: true, description: "yarn | mojang" }
1417
+ ],
1418
+ annotations: {
1419
+ readOnlyHint: false
1420
+ }
1421
+ }, async (rawInput) => {
1422
+ return runTool("remap-mod-jar", rawInput, remapModJarSchema, async (input) => {
1423
+ const result = await remapModJar({
1424
+ inputJar: input.inputJar,
1425
+ outputJar: input.outputJar,
1426
+ mcVersion: input.mcVersion,
1427
+ targetMapping: input.targetMapping
1428
+ }, config);
1429
+ return result;
1430
+ });
1431
+ });
1432
+ export function startServer() {
1433
+ if (serverStarted) {
1434
+ return;
1435
+ }
1436
+ attachProcessErrorHandlers();
1437
+ log("info", "server.start", {
1438
+ version: SERVER_VERSION,
1439
+ cacheDir: config.cacheDir,
1440
+ sqlitePath: config.sqlitePath,
1441
+ sourceRepos: config.sourceRepos.length
1442
+ });
1443
+ server.listen();
1444
+ serverStarted = true;
1445
+ }
1446
+ export { server, sourceService, config, SERVER_VERSION };
1447
+ //# sourceMappingURL=index.js.map