@adhisang/minecraft-modding-mcp 2.1.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,397 @@
1
+ import { z } from "zod";
2
+ import { createError, ERROR_CODES } from "../errors.js";
3
+ import { buildIncludeSchema, detailSchema, positiveIntSchema } from "./entry-tool-schema.js";
4
+ import { buildEntryToolMeta, buildEntryToolResult, createNextAction, createTruncationMeta } from "./response-contract.js";
5
+ import { capArray, resolveDetail, resolveInclude } from "./request-normalizers.js";
6
+ const nonEmptyString = z.string().trim().min(1);
7
+ const INCLUDE_GROUPS = ["warnings", "classes", "registry", "diff", "samples", "timings"];
8
+ const subjectSchema = z.discriminatedUnion("kind", [
9
+ z.object({
10
+ kind: z.literal("version-pair"),
11
+ fromVersion: nonEmptyString,
12
+ toVersion: nonEmptyString,
13
+ packageFilter: nonEmptyString.optional()
14
+ }),
15
+ z.object({
16
+ kind: z.literal("class"),
17
+ className: nonEmptyString,
18
+ fromVersion: nonEmptyString,
19
+ toVersion: nonEmptyString,
20
+ mapping: z.enum(["obfuscated", "mojang", "intermediary", "yarn"]).optional(),
21
+ sourcePriority: z.enum(["loom-first", "maven-first"]).optional()
22
+ }),
23
+ z.object({
24
+ kind: z.literal("registry"),
25
+ fromVersion: nonEmptyString,
26
+ toVersion: nonEmptyString,
27
+ registry: nonEmptyString.optional()
28
+ })
29
+ ]);
30
+ export const compareMinecraftShape = {
31
+ task: z.enum(["auto", "versions", "class-diff", "registry-diff", "migration-overview"]).optional(),
32
+ subject: subjectSchema,
33
+ detail: detailSchema.optional(),
34
+ include: buildIncludeSchema(INCLUDE_GROUPS),
35
+ limit: positiveIntSchema.optional(),
36
+ maxClassResults: positiveIntSchema.optional(),
37
+ maxEntriesPerRegistry: positiveIntSchema.optional(),
38
+ includeFullDiff: z.boolean().optional()
39
+ };
40
+ export const compareMinecraftSchema = z.object(compareMinecraftShape);
41
+ function compareStatusFromCounts(changedCount) {
42
+ return changedCount > 0 ? "changed" : "unchanged";
43
+ }
44
+ export class CompareMinecraftService {
45
+ deps;
46
+ constructor(deps) {
47
+ this.deps = deps;
48
+ }
49
+ async execute(input) {
50
+ const task = input.task && input.task !== "auto"
51
+ ? input.task
52
+ : input.subject.kind === "class"
53
+ ? "class-diff"
54
+ : input.subject.kind === "registry"
55
+ ? "registry-diff"
56
+ : "versions";
57
+ const detail = resolveDetail(input.detail);
58
+ const include = resolveInclude(input.include);
59
+ switch (task) {
60
+ case "versions": {
61
+ const subject = input.subject.kind === "version-pair"
62
+ ? input.subject
63
+ : {
64
+ fromVersion: input.subject.fromVersion,
65
+ toVersion: input.subject.toVersion,
66
+ packageFilter: input.subject.kind === "class" ? undefined : input.subject.registry
67
+ };
68
+ const output = await this.deps.compareVersions({
69
+ fromVersion: subject.fromVersion,
70
+ toVersion: subject.toVersion,
71
+ category: "all",
72
+ packageFilter: subject.packageFilter,
73
+ maxClassResults: input.maxClassResults
74
+ });
75
+ const changedCount = (output.classes?.addedCount ?? 0) +
76
+ (output.classes?.removedCount ?? 0) +
77
+ (output.registry?.summary.registriesChanged ?? 0);
78
+ const classSamples = capArray(output.classes?.added ?? [], 5);
79
+ const newRegistries = capArray(output.registry?.newRegistries ?? [], 5);
80
+ const removedRegistries = capArray(output.registry?.removedRegistries ?? [], 5);
81
+ const truncatedGroups = [
82
+ ...(classSamples.truncated ? ["classes"] : []),
83
+ ...(newRegistries.truncated || removedRegistries.truncated ? ["registry"] : [])
84
+ ];
85
+ const summaryTruncatedGroups = detail === "summary"
86
+ ? truncatedGroups.filter((group) => !include.includes(group))
87
+ : [];
88
+ return {
89
+ ...buildEntryToolResult({
90
+ task: "versions",
91
+ detail,
92
+ include,
93
+ summary: {
94
+ status: compareStatusFromCounts(changedCount),
95
+ headline: `Compared ${output.fromVersion} to ${output.toVersion}.`,
96
+ counts: {
97
+ addedClasses: output.classes?.addedCount ?? 0,
98
+ removedClasses: output.classes?.removedCount ?? 0,
99
+ changedRegistries: output.registry?.summary.registriesChanged ?? 0
100
+ }
101
+ },
102
+ blocks: {
103
+ comparison: {
104
+ fromVersion: output.fromVersion,
105
+ toVersion: output.toVersion
106
+ },
107
+ classes: include.includes("classes") || detail !== "summary"
108
+ ? {
109
+ addedCount: output.classes?.addedCount ?? 0,
110
+ removedCount: output.classes?.removedCount ?? 0,
111
+ unchanged: output.classes?.unchanged ?? 0,
112
+ added: output.classes?.added ?? [],
113
+ removed: output.classes?.removed ?? []
114
+ }
115
+ : {
116
+ addedCount: output.classes?.addedCount ?? 0,
117
+ removedCount: output.classes?.removedCount ?? 0,
118
+ unchanged: output.classes?.unchanged ?? 0,
119
+ sampleAdded: classSamples.items
120
+ },
121
+ registry: output.registry
122
+ ? {
123
+ summary: output.registry.summary,
124
+ newRegistries: include.includes("registry") || detail !== "summary"
125
+ ? output.registry.newRegistries
126
+ : newRegistries.items,
127
+ removedRegistries: include.includes("registry") || detail !== "summary"
128
+ ? output.registry.removedRegistries
129
+ : removedRegistries.items
130
+ }
131
+ : undefined
132
+ }
133
+ }),
134
+ warnings: output.warnings,
135
+ ...(summaryTruncatedGroups.length > 0
136
+ ? {
137
+ meta: buildEntryToolMeta({
138
+ detail,
139
+ include,
140
+ warnings: output.warnings,
141
+ truncated: createTruncationMeta({
142
+ omittedGroups: summaryTruncatedGroups,
143
+ nextActions: [
144
+ createNextAction("compare-minecraft", {
145
+ task: "versions",
146
+ detail: "standard",
147
+ include: resolveInclude([...include, ...summaryTruncatedGroups]),
148
+ subject: input.subject
149
+ })
150
+ ]
151
+ })
152
+ })
153
+ }
154
+ : {})
155
+ };
156
+ }
157
+ case "class-diff": {
158
+ if (input.subject.kind !== "class") {
159
+ throw createError({
160
+ code: ERROR_CODES.INVALID_INPUT,
161
+ message: "task=class-diff requires subject.kind=class."
162
+ });
163
+ }
164
+ const output = await this.deps.diffClassSignatures({
165
+ className: input.subject.className,
166
+ fromVersion: input.subject.fromVersion,
167
+ toVersion: input.subject.toVersion,
168
+ mapping: input.subject.mapping,
169
+ sourcePriority: input.subject.sourcePriority,
170
+ includeFullDiff: input.includeFullDiff
171
+ });
172
+ const changedCount = output.summary.total.added +
173
+ output.summary.total.removed +
174
+ output.summary.total.modified;
175
+ return {
176
+ ...buildEntryToolResult({
177
+ task: "class-diff",
178
+ detail,
179
+ include,
180
+ summary: {
181
+ status: compareStatusFromCounts(changedCount),
182
+ headline: `Compared ${output.query.className} between ${output.range.fromVersion} and ${output.range.toVersion}.`,
183
+ counts: output.summary.total
184
+ },
185
+ blocks: {
186
+ comparison: output.query,
187
+ classDiff: include.includes("diff") || detail !== "summary"
188
+ ? {
189
+ classChange: output.classChange,
190
+ summary: output.summary,
191
+ constructors: output.constructors,
192
+ methods: output.methods,
193
+ fields: output.fields
194
+ }
195
+ : {
196
+ classChange: output.classChange,
197
+ summary: output.summary
198
+ }
199
+ }
200
+ }),
201
+ warnings: output.warnings
202
+ };
203
+ }
204
+ case "registry-diff": {
205
+ const subject = input.subject.kind === "registry"
206
+ ? input.subject
207
+ : {
208
+ fromVersion: input.subject.fromVersion,
209
+ toVersion: input.subject.toVersion,
210
+ registry: undefined
211
+ };
212
+ const compare = await this.deps.compareVersions({
213
+ fromVersion: subject.fromVersion,
214
+ toVersion: subject.toVersion,
215
+ category: "registry"
216
+ });
217
+ const warnings = [...compare.warnings];
218
+ const registrySummary = compare.registry?.summary ?? {
219
+ registriesChanged: 0,
220
+ totalAdded: 0,
221
+ totalRemoved: 0
222
+ };
223
+ let entries;
224
+ if ((include.includes("registry") || detail === "full") && subject.registry) {
225
+ const [fromData, toData] = await Promise.allSettled([
226
+ this.deps.getRegistryData({
227
+ version: subject.fromVersion,
228
+ registry: subject.registry,
229
+ includeData: true,
230
+ maxEntriesPerRegistry: input.maxEntriesPerRegistry
231
+ }),
232
+ this.deps.getRegistryData({
233
+ version: subject.toVersion,
234
+ registry: subject.registry,
235
+ includeData: true,
236
+ maxEntriesPerRegistry: input.maxEntriesPerRegistry
237
+ })
238
+ ]);
239
+ entries = {
240
+ from: fromData.status === "fulfilled" ? fromData.value : undefined,
241
+ to: toData.status === "fulfilled" ? toData.value : undefined
242
+ };
243
+ if (fromData.status === "fulfilled") {
244
+ warnings.push(...fromData.value.warnings);
245
+ }
246
+ else {
247
+ warnings.push(`Could not load ${subject.registry} registry details for ${subject.fromVersion}: ${fromData.reason instanceof Error ? fromData.reason.message : String(fromData.reason)}`);
248
+ }
249
+ if (toData.status === "fulfilled") {
250
+ warnings.push(...toData.value.warnings);
251
+ }
252
+ else {
253
+ warnings.push(`Could not load ${subject.registry} registry details for ${subject.toVersion}: ${toData.reason instanceof Error ? toData.reason.message : String(toData.reason)}`);
254
+ }
255
+ }
256
+ const partialDetail = Boolean(subject.registry) &&
257
+ (include.includes("registry") || detail === "full") &&
258
+ entries !== undefined &&
259
+ (!entries.from || !entries.to);
260
+ const missingFromDetail = partialDetail && entries != null && !entries.from;
261
+ const missingToDetail = partialDetail && entries != null && !entries.to;
262
+ const summary = {
263
+ status: partialDetail ? "partial" : compareStatusFromCounts(registrySummary.registriesChanged),
264
+ headline: partialDetail
265
+ ? `Compared registry changes between ${subject.fromVersion} and ${subject.toVersion} with partial detail.`
266
+ : `Compared registry changes between ${subject.fromVersion} and ${subject.toVersion}.`,
267
+ counts: {
268
+ changedRegistries: registrySummary.registriesChanged,
269
+ addedEntries: registrySummary.totalAdded,
270
+ removedEntries: registrySummary.totalRemoved
271
+ },
272
+ ...(partialDetail
273
+ ? {
274
+ nextActions: [
275
+ ...(missingFromDetail
276
+ ? [{
277
+ tool: "get-registry-data",
278
+ params: {
279
+ version: subject.fromVersion,
280
+ registry: subject.registry,
281
+ includeData: true,
282
+ maxEntriesPerRegistry: input.maxEntriesPerRegistry
283
+ }
284
+ }]
285
+ : []),
286
+ ...(missingToDetail
287
+ ? [{
288
+ tool: "get-registry-data",
289
+ params: {
290
+ version: subject.toVersion,
291
+ registry: subject.registry,
292
+ includeData: true,
293
+ maxEntriesPerRegistry: input.maxEntriesPerRegistry
294
+ }
295
+ }]
296
+ : [])
297
+ ]
298
+ }
299
+ : {})
300
+ };
301
+ return {
302
+ ...buildEntryToolResult({
303
+ task: "registry-diff",
304
+ detail,
305
+ include,
306
+ summary,
307
+ blocks: {
308
+ comparison: {
309
+ fromVersion: subject.fromVersion,
310
+ toVersion: subject.toVersion,
311
+ registry: subject.registry
312
+ },
313
+ registry: {
314
+ summary: registrySummary,
315
+ entries
316
+ }
317
+ }
318
+ }),
319
+ warnings
320
+ };
321
+ }
322
+ case "migration-overview": {
323
+ const subject = input.subject.kind === "version-pair"
324
+ ? input.subject
325
+ : {
326
+ fromVersion: input.subject.fromVersion,
327
+ toVersion: input.subject.toVersion
328
+ };
329
+ const compare = await this.deps.compareVersions({
330
+ fromVersion: subject.fromVersion,
331
+ toVersion: subject.toVersion,
332
+ category: "all",
333
+ maxClassResults: input.maxClassResults
334
+ });
335
+ const classSignals = (compare.classes?.addedCount ?? 0) + (compare.classes?.removedCount ?? 0);
336
+ const registrySignals = compare.registry?.summary.registriesChanged ?? 0;
337
+ const status = compareStatusFromCounts(classSignals + registrySignals);
338
+ return {
339
+ ...buildEntryToolResult({
340
+ task: "migration-overview",
341
+ detail,
342
+ include,
343
+ summary: {
344
+ status,
345
+ headline: `Summarized migration impact from ${subject.fromVersion} to ${subject.toVersion}.`,
346
+ counts: {
347
+ classSignals,
348
+ registrySignals
349
+ }
350
+ },
351
+ blocks: {
352
+ comparison: subject,
353
+ migration: {
354
+ impact: classSignals > 0 && registrySignals > 0
355
+ ? "classes-and-registry"
356
+ : classSignals > 0
357
+ ? "classes"
358
+ : registrySignals > 0
359
+ ? "registry"
360
+ : "minimal",
361
+ nextActions: [
362
+ {
363
+ tool: classSignals > 0 ? "compare-minecraft" : "inspect-minecraft",
364
+ params: classSignals > 0
365
+ ? {
366
+ task: "class-diff",
367
+ subject: {
368
+ kind: "class",
369
+ className: "net.minecraft.world.item.ItemStack",
370
+ fromVersion: subject.fromVersion,
371
+ toVersion: subject.toVersion
372
+ }
373
+ }
374
+ : {
375
+ task: "artifact",
376
+ subject: {
377
+ kind: "version",
378
+ version: subject.toVersion
379
+ }
380
+ }
381
+ }
382
+ ]
383
+ }
384
+ }
385
+ }),
386
+ warnings: compare.warnings
387
+ };
388
+ }
389
+ default:
390
+ throw createError({
391
+ code: ERROR_CODES.INVALID_INPUT,
392
+ message: `Unsupported compare-minecraft task "${task}".`
393
+ });
394
+ }
395
+ }
396
+ }
397
+ //# sourceMappingURL=compare-minecraft-service.js.map
@@ -0,0 +1,6 @@
1
+ import { z } from "zod";
2
+ export declare const detailSchema: z.ZodEnum<["summary", "standard", "full"]>;
3
+ export declare const includeGroupSchema: z.ZodEnum<["warnings", "provenance", "candidates", "members", "source", "files", "samples", "diff", "issues", "timeline", "matrix", "entries", "workspace", "health", "recovery", "paths", "owners", "preview", "cacheEntries", "timings", "artifact", "classes", "registry"]>;
4
+ export declare const executionModeSchema: z.ZodEnum<["preview", "apply"]>;
5
+ export declare const positiveIntSchema: z.ZodNumber;
6
+ export declare function buildIncludeSchema(allowed: readonly string[]): z.ZodOptional<z.ZodArray<z.ZodEnum<[string, ...string[]]>, "many">>;
@@ -0,0 +1,10 @@
1
+ import { z } from "zod";
2
+ import { CANONICAL_INCLUDE_GROUPS, DETAIL_LEVELS } from "./response-contract.js";
3
+ export const detailSchema = z.enum(DETAIL_LEVELS);
4
+ export const includeGroupSchema = z.enum(CANONICAL_INCLUDE_GROUPS);
5
+ export const executionModeSchema = z.enum(["preview", "apply"]);
6
+ export const positiveIntSchema = z.number().int().positive();
7
+ export function buildIncludeSchema(allowed) {
8
+ return z.array(z.enum(allowed)).optional();
9
+ }
10
+ //# sourceMappingURL=entry-tool-schema.js.map