@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.
- package/CHANGELOG.md +11 -0
- package/LICENSE +21 -0
- package/README.md +765 -0
- package/dist/access-widener-parser.d.ts +24 -0
- package/dist/access-widener-parser.js +77 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +4 -0
- package/dist/config.d.ts +27 -0
- package/dist/config.js +178 -0
- package/dist/decompiler/vineflower.d.ts +15 -0
- package/dist/decompiler/vineflower.js +185 -0
- package/dist/errors.d.ts +50 -0
- package/dist/errors.js +49 -0
- package/dist/hash.d.ts +1 -0
- package/dist/hash.js +12 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +1447 -0
- package/dist/java-process.d.ts +16 -0
- package/dist/java-process.js +120 -0
- package/dist/logger.d.ts +3 -0
- package/dist/logger.js +21 -0
- package/dist/mapping-pipeline-service.d.ts +18 -0
- package/dist/mapping-pipeline-service.js +60 -0
- package/dist/mapping-service.d.ts +161 -0
- package/dist/mapping-service.js +1706 -0
- package/dist/maven-resolver.d.ts +22 -0
- package/dist/maven-resolver.js +122 -0
- package/dist/minecraft-explorer-service.d.ts +43 -0
- package/dist/minecraft-explorer-service.js +562 -0
- package/dist/mixin-parser.d.ts +34 -0
- package/dist/mixin-parser.js +194 -0
- package/dist/mixin-validator.d.ts +59 -0
- package/dist/mixin-validator.js +274 -0
- package/dist/mod-analyzer.d.ts +23 -0
- package/dist/mod-analyzer.js +346 -0
- package/dist/mod-decompile-service.d.ts +39 -0
- package/dist/mod-decompile-service.js +136 -0
- package/dist/mod-remap-service.d.ts +17 -0
- package/dist/mod-remap-service.js +186 -0
- package/dist/mod-search-service.d.ts +28 -0
- package/dist/mod-search-service.js +174 -0
- package/dist/mojang-tiny-mapping-service.d.ts +13 -0
- package/dist/mojang-tiny-mapping-service.js +351 -0
- package/dist/nbt/java-nbt-codec.d.ts +3 -0
- package/dist/nbt/java-nbt-codec.js +385 -0
- package/dist/nbt/json-patch.d.ts +3 -0
- package/dist/nbt/json-patch.js +352 -0
- package/dist/nbt/pipeline.d.ts +39 -0
- package/dist/nbt/pipeline.js +173 -0
- package/dist/nbt/typed-json.d.ts +10 -0
- package/dist/nbt/typed-json.js +205 -0
- package/dist/nbt/types.d.ts +66 -0
- package/dist/nbt/types.js +2 -0
- package/dist/observability.d.ts +88 -0
- package/dist/observability.js +165 -0
- package/dist/path-converter.d.ts +12 -0
- package/dist/path-converter.js +161 -0
- package/dist/path-resolver.d.ts +19 -0
- package/dist/path-resolver.js +78 -0
- package/dist/registry-service.d.ts +29 -0
- package/dist/registry-service.js +214 -0
- package/dist/repo-downloader.d.ts +15 -0
- package/dist/repo-downloader.js +111 -0
- package/dist/resources.d.ts +3 -0
- package/dist/resources.js +154 -0
- package/dist/search-hit-accumulator.d.ts +38 -0
- package/dist/search-hit-accumulator.js +153 -0
- package/dist/source-jar-reader.d.ts +13 -0
- package/dist/source-jar-reader.js +216 -0
- package/dist/source-resolver.d.ts +14 -0
- package/dist/source-resolver.js +274 -0
- package/dist/source-service.d.ts +404 -0
- package/dist/source-service.js +2881 -0
- package/dist/storage/artifacts-repo.d.ts +45 -0
- package/dist/storage/artifacts-repo.js +209 -0
- package/dist/storage/db.d.ts +14 -0
- package/dist/storage/db.js +132 -0
- package/dist/storage/files-repo.d.ts +78 -0
- package/dist/storage/files-repo.js +437 -0
- package/dist/storage/index-meta-repo.d.ts +35 -0
- package/dist/storage/index-meta-repo.js +97 -0
- package/dist/storage/migrations.d.ts +11 -0
- package/dist/storage/migrations.js +71 -0
- package/dist/storage/schema.d.ts +1 -0
- package/dist/storage/schema.js +160 -0
- package/dist/storage/sqlite.d.ts +20 -0
- package/dist/storage/sqlite.js +111 -0
- package/dist/storage/symbols-repo.d.ts +63 -0
- package/dist/storage/symbols-repo.js +401 -0
- package/dist/symbols/symbol-extractor.d.ts +7 -0
- package/dist/symbols/symbol-extractor.js +64 -0
- package/dist/tiny-remapper-resolver.d.ts +1 -0
- package/dist/tiny-remapper-resolver.js +62 -0
- package/dist/tiny-remapper-service.d.ts +16 -0
- package/dist/tiny-remapper-service.js +73 -0
- package/dist/types.d.ts +120 -0
- package/dist/types.js +2 -0
- package/dist/version-diff-service.d.ts +41 -0
- package/dist/version-diff-service.js +222 -0
- package/dist/version-service.d.ts +70 -0
- package/dist/version-service.js +411 -0
- package/dist/vineflower-resolver.d.ts +1 -0
- package/dist/vineflower-resolver.js +62 -0
- package/dist/workspace-mapping-service.d.ts +18 -0
- package/dist/workspace-mapping-service.js +89 -0
- 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
|