@adhisang/minecraft-modding-mcp 4.2.1 → 6.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 (144) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/README.md +5 -4
  3. package/dist/access-transformer-parser.d.ts +2 -1
  4. package/dist/access-transformer-parser.js +10 -1
  5. package/dist/access-widener-parser.d.ts +1 -0
  6. package/dist/access-widener-parser.js +6 -3
  7. package/dist/build-suggested-call.js +31 -10
  8. package/dist/cache-registry.d.ts +8 -0
  9. package/dist/cache-registry.js +50 -32
  10. package/dist/config.d.ts +1 -0
  11. package/dist/config.js +4 -0
  12. package/dist/decompiler/vineflower.js +8 -2
  13. package/dist/entry-tools/analyze-mod-service.d.ts +24 -24
  14. package/dist/entry-tools/analyze-mod-service.js +2 -1
  15. package/dist/entry-tools/analyze-symbol-service.d.ts +57 -22
  16. package/dist/entry-tools/analyze-symbol-service.js +139 -34
  17. package/dist/entry-tools/batch-class-members-service.d.ts +3 -1
  18. package/dist/entry-tools/batch-class-members-service.js +6 -6
  19. package/dist/entry-tools/batch-class-source-service.d.ts +3 -1
  20. package/dist/entry-tools/batch-class-source-service.js +5 -6
  21. package/dist/entry-tools/batch-mappings-service.d.ts +3 -1
  22. package/dist/entry-tools/batch-mappings-service.js +4 -5
  23. package/dist/entry-tools/batch-symbol-exists-service.d.ts +3 -1
  24. package/dist/entry-tools/batch-symbol-exists-service.js +4 -5
  25. package/dist/entry-tools/compare-minecraft-service.d.ts +10 -10
  26. package/dist/entry-tools/compare-minecraft-service.js +1 -1
  27. package/dist/entry-tools/entry-tool-schema.d.ts +1 -1
  28. package/dist/entry-tools/inspect-minecraft/handlers/artifact.js +1 -3
  29. package/dist/entry-tools/inspect-minecraft/handlers/class-members.js +4 -3
  30. package/dist/entry-tools/inspect-minecraft/handlers/class-overview.js +0 -5
  31. package/dist/entry-tools/inspect-minecraft/handlers/class-source.js +0 -1
  32. package/dist/entry-tools/inspect-minecraft/handlers/file.js +26 -2
  33. package/dist/entry-tools/inspect-minecraft/handlers/list-files.js +0 -1
  34. package/dist/entry-tools/inspect-minecraft/handlers/search.d.ts +8 -0
  35. package/dist/entry-tools/inspect-minecraft/handlers/search.js +13 -2
  36. package/dist/entry-tools/inspect-minecraft/handlers/versions.d.ts +1 -1
  37. package/dist/entry-tools/inspect-minecraft/handlers/versions.js +3 -2
  38. package/dist/entry-tools/inspect-minecraft/internal.d.ts +90 -89
  39. package/dist/entry-tools/inspect-minecraft/internal.js +32 -14
  40. package/dist/entry-tools/inspect-minecraft-service.d.ts +218 -218
  41. package/dist/entry-tools/inspect-minecraft-service.js +1 -1
  42. package/dist/entry-tools/manage-cache-service.d.ts +4 -4
  43. package/dist/entry-tools/response-contract.d.ts +3 -1
  44. package/dist/entry-tools/response-contract.js +3 -2
  45. package/dist/entry-tools/validate-project/cases/mixin.js +2 -1
  46. package/dist/entry-tools/validate-project/cases/project-summary.js +5 -3
  47. package/dist/entry-tools/validate-project-service.d.ts +42 -36
  48. package/dist/entry-tools/validate-project-service.js +3 -1
  49. package/dist/entry-tools/verify-mixin-target-service.d.ts +30 -0
  50. package/dist/entry-tools/verify-mixin-target-service.js +120 -29
  51. package/dist/error-mapping.d.ts +51 -0
  52. package/dist/error-mapping.js +157 -2
  53. package/dist/index.js +147 -76
  54. package/dist/java-process.js +1 -1
  55. package/dist/json-rpc-framing.d.ts +4 -0
  56. package/dist/json-rpc-framing.js +23 -1
  57. package/dist/mapping/internal-types.d.ts +24 -0
  58. package/dist/mapping/loaders/tiny-maven.js +46 -20
  59. package/dist/mapping/lookup.d.ts +13 -2
  60. package/dist/mapping/lookup.js +60 -2
  61. package/dist/mapping/types.d.ts +6 -0
  62. package/dist/mapping-service.d.ts +26 -2
  63. package/dist/mapping-service.js +212 -55
  64. package/dist/mcp-helpers.d.ts +1 -0
  65. package/dist/mcp-helpers.js +25 -3
  66. package/dist/minecraft-explorer-service.js +24 -11
  67. package/dist/mixin/access-validators.js +33 -1
  68. package/dist/mixin/annotation-validators.js +73 -9
  69. package/dist/mixin/parsed-validator.js +3 -2
  70. package/dist/mixin-parser.js +119 -7
  71. package/dist/mod-decompile-service.d.ts +2 -0
  72. package/dist/mod-decompile-service.js +21 -3
  73. package/dist/mod-remap-service.d.ts +20 -1
  74. package/dist/mod-remap-service.js +101 -45
  75. package/dist/mojang-tiny-mapping-service.js +5 -2
  76. package/dist/nbt/java-nbt-codec.js +46 -16
  77. package/dist/nbt/json-patch.js +51 -12
  78. package/dist/nbt/typed-json.d.ts +1 -0
  79. package/dist/nbt/typed-json.js +12 -2
  80. package/dist/nbt/types.d.ts +2 -2
  81. package/dist/observability.d.ts +6 -0
  82. package/dist/observability.js +10 -0
  83. package/dist/page-cursor.d.ts +17 -0
  84. package/dist/page-cursor.js +0 -0
  85. package/dist/path-converter.js +10 -3
  86. package/dist/registry-service.d.ts +2 -0
  87. package/dist/registry-service.js +41 -2
  88. package/dist/repo-downloader.js +4 -3
  89. package/dist/resources.js +45 -9
  90. package/dist/response-utils.d.ts +29 -4
  91. package/dist/response-utils.js +95 -5
  92. package/dist/source/access-validate.js +12 -2
  93. package/dist/source/artifact-resolver.js +2 -2
  94. package/dist/source/class-source/members-builder.d.ts +44 -1
  95. package/dist/source/class-source/members-builder.js +51 -4
  96. package/dist/source/class-source/snippet-builder.d.ts +13 -0
  97. package/dist/source/class-source/snippet-builder.js +54 -4
  98. package/dist/source/class-source-helpers.d.ts +4 -4
  99. package/dist/source/class-source-helpers.js +77 -20
  100. package/dist/source/class-source.d.ts +19 -0
  101. package/dist/source/class-source.js +144 -40
  102. package/dist/source/file-access.js +3 -2
  103. package/dist/source/indexer.js +29 -9
  104. package/dist/source/lifecycle/mapping-helpers.js +28 -3
  105. package/dist/source/lifecycle/runtime-check.js +20 -6
  106. package/dist/source/search.d.ts +1 -1
  107. package/dist/source/search.js +117 -30
  108. package/dist/source/validate-mixin/pipeline/resolve.js +23 -3
  109. package/dist/source/validate-mixin.d.ts +2 -1
  110. package/dist/source/validate-mixin.js +45 -4
  111. package/dist/source/workspace-target.js +17 -10
  112. package/dist/source-jar-reader.d.ts +54 -1
  113. package/dist/source-jar-reader.js +121 -10
  114. package/dist/source-resolver.js +2 -2
  115. package/dist/source-service.d.ts +28 -7
  116. package/dist/source-service.js +9 -1
  117. package/dist/stdio-supervisor.d.ts +2 -2
  118. package/dist/stdio-supervisor.js +29 -9
  119. package/dist/storage/db.js +24 -1
  120. package/dist/storage/files-repo.d.ts +15 -0
  121. package/dist/storage/files-repo.js +67 -37
  122. package/dist/storage/symbols-repo.d.ts +15 -0
  123. package/dist/storage/symbols-repo.js +53 -19
  124. package/dist/symbols/symbol-extractor.js +35 -6
  125. package/dist/text-truncate.d.ts +7 -0
  126. package/dist/text-truncate.js +21 -0
  127. package/dist/tiny-remapper-resolver.js +10 -0
  128. package/dist/tool-contract-manifest.js +2 -2
  129. package/dist/tool-execution-gate.js +12 -1
  130. package/dist/tool-guidance.d.ts +6 -2
  131. package/dist/tool-guidance.js +63 -13
  132. package/dist/tool-schemas.d.ts +561 -496
  133. package/dist/tool-schemas.js +175 -139
  134. package/dist/types.d.ts +1 -0
  135. package/dist/version-diff-service.js +5 -0
  136. package/dist/version-service.d.ts +3 -0
  137. package/dist/version-service.js +18 -2
  138. package/dist/warning-details.d.ts +30 -0
  139. package/dist/warning-details.js +80 -0
  140. package/dist/workspace-mapping-service.js +9 -0
  141. package/docs/README-ja.md +2 -2
  142. package/docs/examples.md +26 -2
  143. package/docs/tool-reference.md +22 -17
  144. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import { ZodError, z } from "zod";
5
5
  import { CompatStdioServerTransport } from "./compat-stdio-transport.js";
6
6
  import { objectResult } from "./mcp-helpers.js";
7
7
  import { prepareToolInput } from "./tool-input.js";
8
- import { isCompactEnabled, COMPACT_MAPPING_TOOL_NAMES, COMPACT_SOURCE_TOOL_NAMES, COMPACT_MEMBERS_TOOL_NAMES, COMPACT_LIGHT_TOOL_NAMES, TOOL_PRESERVE_PAYLOAD_KEYS, compactResponse, compactArtifactResponse, compactMappingResponse, compactSourceResponse, compactMembersResponse, compactLightResponse } from "./response-utils.js";
8
+ import { DETAIL_ENABLED_TOOL_NAMES, DEFAULT_DETAIL_BY_TOOL, projectByDetail } from "./response-utils.js";
9
9
  import { loadConfig } from "./config.js";
10
10
  import { createError, ERROR_CODES, isAppError } from "./errors.js";
11
11
  import { log } from "./logger.js";
@@ -15,6 +15,7 @@ import { remapModJar } from "./mod-remap-service.js";
15
15
  import { registerResources } from "./resources.js";
16
16
  import { SourceService } from "./source-service.js";
17
17
  import { ToolExecutionGate } from "./tool-execution-gate.js";
18
+ import { capWarningDetailsForSummary, classifyWarnings } from "./warning-details.js";
18
19
  import { WorkspaceMappingService } from "./workspace-mapping-service.js";
19
20
  import { InspectMinecraftService, inspectMinecraftSchema, inspectMinecraftShape } from "./entry-tools/inspect-minecraft-service.js";
20
21
  import { AnalyzeSymbolService, analyzeSymbolSchema, analyzeSymbolShape } from "./entry-tools/analyze-symbol-service.js";
@@ -55,6 +56,13 @@ const ENTRY_TOOL_NAMES = new Set([
55
56
  "validate-project",
56
57
  "manage-cache"
57
58
  ]);
59
+ // Batch tools self-project per entry but report the applied detail at the top level.
60
+ const BATCH_DETAIL_TOOL_NAMES = new Set([
61
+ "batch-class-source",
62
+ "batch-class-members",
63
+ "batch-symbol-exists",
64
+ "batch-mappings"
65
+ ]);
58
66
  const heavyToolExecutionGate = new ToolExecutionGate({ maxConcurrent: 1, maxQueue: 2 });
59
67
  function getServerVersionFromPackageJson() {
60
68
  try {
@@ -77,6 +85,13 @@ const server = new McpServer({
77
85
  // The SDK validates tool args before invoking handlers and returns generic InvalidParams text.
78
86
  // Bypass that layer so runTool() remains the single source of truth for validation and error envelopes.
79
87
  server.validateToolInput = async (_tool, args) => args;
88
+ // Low-level/expert tools duplicate capability that the six entry tools expose
89
+ // in a single resolve+fetch call. expertTool() registers them exactly like
90
+ // server.tool() but appends a note steering agents to the entry tools first.
91
+ // Entry tools, batch tools, and the NBT/runtime utilities (which have no entry
92
+ // equivalent) keep their plain descriptions via server.tool().
93
+ const EXPERT_TOOL_NOTE = " Expert tool: prefer the entry tools (inspect-minecraft, analyze-symbol, compare-minecraft, analyze-mod, validate-project) first.";
94
+ const expertTool = ((name, description, ...rest) => server.tool(name, description + EXPERT_TOOL_NOTE, ...rest));
80
95
  const config = loadConfig();
81
96
  const nbtLimits = {
82
97
  maxInputBytes: config.maxNbtInputBytes,
@@ -176,10 +191,32 @@ const verifyMixinTargetService = new VerifyMixinTargetService({
176
191
  artifactId: output.artifactId,
177
192
  mappingApplied: output.mappingApplied,
178
193
  binaryJarPath: output.binaryJarPath,
194
+ version: output.version,
179
195
  provenance: output.provenance,
180
196
  warnings: output.warnings
181
197
  };
182
198
  },
199
+ findMapping: async (input) => {
200
+ const output = await sourceService.findMapping({
201
+ version: input.version,
202
+ kind: input.kind,
203
+ name: input.name,
204
+ owner: input.owner,
205
+ descriptor: input.descriptor,
206
+ sourceMapping: input.sourceMapping,
207
+ targetMapping: input.targetMapping
208
+ });
209
+ return {
210
+ resolved: output.resolved,
211
+ resolvedSymbol: output.resolvedSymbol
212
+ ? {
213
+ name: output.resolvedSymbol.name,
214
+ owner: output.resolvedSymbol.owner,
215
+ descriptor: output.resolvedSymbol.descriptor
216
+ }
217
+ : undefined
218
+ };
219
+ },
183
220
  getSignature: (input) => sourceService.explorerService.getSignature(input)
184
221
  });
185
222
  const batchClassSourceService = new BatchClassSourceService({
@@ -226,11 +263,10 @@ function buildRequestId() {
226
263
  return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
227
264
  }
228
265
  function normalizeSourceLookupTarget(target) {
229
- if (target.type === "artifact") {
266
+ if (target.kind === "artifact") {
230
267
  return { artifactId: target.artifactId };
231
268
  }
232
- const { type: _type, ...rest } = target;
233
- return { target: rest };
269
+ return { target: target };
234
270
  }
235
271
  function parseClassApiKinds(value) {
236
272
  if (value == null) {
@@ -270,6 +306,33 @@ function splitWarnings(data) {
270
306
  meta
271
307
  };
272
308
  }
309
+ /**
310
+ * Read the detail/include response-shape controls from a parsed tool input.
311
+ * Folds the legacy includeProvenance/includeDescriptors flags into the include
312
+ * set as aliases so the migration off `compact` preserves Phase-4 behavior.
313
+ */
314
+ function readResponseShapeInput(parsedInput) {
315
+ const include = new Set();
316
+ if (parsedInput === null || typeof parsedInput !== "object" || Array.isArray(parsedInput)) {
317
+ return { include };
318
+ }
319
+ const record = parsedInput;
320
+ if (Array.isArray(record.include)) {
321
+ for (const group of record.include) {
322
+ if (typeof group === "string")
323
+ include.add(group);
324
+ }
325
+ }
326
+ if (record.includeProvenance === true)
327
+ include.add("provenance");
328
+ if (record.includeDescriptors === true)
329
+ include.add("descriptors");
330
+ const detail = typeof record.detail === "string" &&
331
+ (record.detail === "summary" || record.detail === "standard" || record.detail === "full")
332
+ ? record.detail
333
+ : undefined;
334
+ return { detail, include };
335
+ }
273
336
  async function runTool(tool, rawInput, schema, action) {
274
337
  const requestId = buildRequestId();
275
338
  const startedAt = Date.now();
@@ -289,6 +352,11 @@ async function runTool(tool, rawInput, schema, action) {
289
352
  code: "invalid_enum_value"
290
353
  })),
291
354
  nextAction: `Replace "official" with "obfuscated" in mapping-related fields and retry.`,
355
+ // Route construction through buildSuggestedCall to satisfy the D12
356
+ // invariant (tests/suggested-call-invariant.test.ts): every emitted
357
+ // suggestedCall must be built by the helper. The downstream
358
+ // mapErrorToProblem gate re-validates on emission; that second pass is
359
+ // the intentional, cheap cost of the two complementary safety gates.
292
360
  ...(suggestedReplacementInput
293
361
  ? buildSuggestedCall({
294
362
  tool,
@@ -303,44 +371,37 @@ async function runTool(tool, rawInput, schema, action) {
303
371
  ? heavyToolExecutionGate.run(tool, () => action(parsedInput))
304
372
  : action(parsedInput));
305
373
  const { result, warnings, meta: resultMeta } = splitWarnings(payload);
306
- const isCompact = isCompactEnabled(tool, parsedInput);
374
+ // Expert tools share the entry-tool detail/include response contract (the old
375
+ // per-tool `compact` boolean is gone). projectByDetail reproduces the previous
376
+ // compact defaults byte-identically: resolution tools default detail=summary
377
+ // (old compact:true), source/file tools default detail=standard (old compact:false).
378
+ // The legacy includeProvenance/includeDescriptors flags are folded into the
379
+ // include set as aliases so Phase-4 behavior is preserved.
380
+ const shapeInput = readResponseShapeInput(parsedInput);
381
+ const effectiveDetail = ENTRY_TOOL_NAMES.has(tool)
382
+ ? (shapeInput.detail ?? "summary")
383
+ : (shapeInput.detail ?? DEFAULT_DETAIL_BY_TOOL[tool] ?? "summary");
307
384
  let projectedResult = result;
308
- if (isCompact) {
309
- if (tool === "resolve-artifact") {
310
- projectedResult = compactArtifactResponse(projectedResult);
311
- }
312
- if (COMPACT_MAPPING_TOOL_NAMES.has(tool)) {
313
- projectedResult = compactMappingResponse(projectedResult);
314
- }
315
- if (COMPACT_SOURCE_TOOL_NAMES.has(tool)) {
316
- projectedResult = compactSourceResponse(projectedResult);
317
- }
318
- if (COMPACT_MEMBERS_TOOL_NAMES.has(tool)) {
319
- projectedResult = compactMembersResponse(projectedResult);
320
- }
321
- if (COMPACT_LIGHT_TOOL_NAMES.has(tool)) {
322
- projectedResult = compactLightResponse(projectedResult);
323
- }
324
- projectedResult = compactResponse(projectedResult, TOOL_PRESERVE_PAYLOAD_KEYS[tool]);
385
+ if (DETAIL_ENABLED_TOOL_NAMES.has(tool)) {
386
+ projectedResult = projectByDetail(tool, projectedResult, effectiveDetail, shapeInput.include);
325
387
  }
326
- const entryMeta = ENTRY_TOOL_NAMES.has(tool)
388
+ // Entry, expert, and batch tools report the applied detail/include shape in meta.
389
+ // detailApplied is omitted when it matches the tool's default to keep responses lean.
390
+ const defaultDetail = ENTRY_TOOL_NAMES.has(tool)
391
+ ? "summary"
392
+ : DEFAULT_DETAIL_BY_TOOL[tool] ?? "summary";
393
+ const entryMeta = ENTRY_TOOL_NAMES.has(tool) ||
394
+ DETAIL_ENABLED_TOOL_NAMES.has(tool) ||
395
+ BATCH_DETAIL_TOOL_NAMES.has(tool)
327
396
  ? buildEntryToolMeta({
328
- detail: normalizedInput &&
329
- typeof normalizedInput === "object" &&
330
- !Array.isArray(normalizedInput) &&
331
- typeof normalizedInput.detail === "string"
332
- ? normalizedInput.detail ?? "summary"
333
- : "summary",
334
- include: normalizedInput &&
335
- typeof normalizedInput === "object" &&
336
- !Array.isArray(normalizedInput) &&
337
- Array.isArray(normalizedInput.include)
338
- ? normalizedInput.include
339
- : undefined
397
+ detail: effectiveDetail,
398
+ defaultDetail,
399
+ include: shapeInput.include.size > 0 ? [...shapeInput.include] : undefined
340
400
  })
341
401
  : undefined;
342
402
  const durationMs = Date.now() - startedAt;
343
403
  sourceService.recordToolCall(tool, durationMs);
404
+ const warningDetails = capWarningDetailsForSummary(classifyWarnings(warnings), effectiveDetail === "summary");
344
405
  return objectResult({
345
406
  result: projectedResult,
346
407
  meta: {
@@ -349,7 +410,8 @@ async function runTool(tool, rawInput, schema, action) {
349
410
  requestId,
350
411
  tool,
351
412
  durationMs,
352
- warnings
413
+ ...(warnings.length > 0 ? { warnings } : {}),
414
+ ...(warningDetails.length > 0 ? { warningDetails } : {})
353
415
  }
354
416
  });
355
417
  }
@@ -393,8 +455,7 @@ async function runTool(tool, rawInput, schema, action) {
393
455
  const errorMeta = {
394
456
  requestId,
395
457
  tool,
396
- durationMs: errorDurationMs,
397
- warnings: []
458
+ durationMs: errorDurationMs
398
459
  };
399
460
  applyErrorMetaExtensions(errorMeta, caughtError);
400
461
  return objectResult({
@@ -403,14 +464,14 @@ async function runTool(tool, rawInput, schema, action) {
403
464
  }, { isError: true });
404
465
  }
405
466
  }
406
- server.tool("list-versions", "List available Minecraft versions from Mojang manifest and locally cached version jars.", listVersionsShape, { readOnlyHint: true }, async (args) => runTool("list-versions", args, listVersionsSchema, async (input) => sourceService.listVersions({
467
+ expertTool("list-versions", "List available Minecraft versions from Mojang manifest and locally cached version jars.", listVersionsShape, { readOnlyHint: true }, async (args) => runTool("list-versions", args, listVersionsSchema, async (input) => sourceService.listVersions({
407
468
  includeSnapshots: input.includeSnapshots,
408
469
  limit: input.limit
409
470
  })));
410
471
  registerToolSchema("list-versions", listVersionsSchema);
411
472
  server.tool("inspect-minecraft", "Top-level workflow tool for version discovery, artifact resolution, class inspection, source search, file reads, and file listings.", inspectMinecraftShape, { readOnlyHint: true }, async (args) => runTool("inspect-minecraft", args, inspectMinecraftSchema, async (input) => inspectMinecraftService.execute(input)));
412
473
  registerToolSchema("inspect-minecraft", inspectMinecraftSchema);
413
- server.tool("analyze-symbol", "Top-level workflow tool for symbol existence, mapping, lifecycle, workspace analysis, and API overview.", analyzeSymbolShape, { readOnlyHint: true }, async (args) => runTool("analyze-symbol", args, analyzeSymbolSchema, async (input) => analyzeSymbolService.execute(input)));
474
+ server.tool("analyze-symbol", "Top-level workflow tool for symbol existence, mapping, lifecycle, workspace analysis, and API overview. subject.kind='symbol' auto-detects class/field/method from the selector.", analyzeSymbolShape, { readOnlyHint: true }, async (args) => runTool("analyze-symbol", args, analyzeSymbolSchema, async (input) => analyzeSymbolService.execute(input)));
414
475
  registerToolSchema("analyze-symbol", analyzeSymbolSchema);
415
476
  server.tool("compare-minecraft", "Top-level workflow tool for version comparisons, class diffs, registry diffs, and migration overviews.", compareMinecraftShape, { readOnlyHint: true }, async (args) => runTool("compare-minecraft", args, compareMinecraftSchema, async (input) => compareMinecraftService.execute(input)));
416
477
  registerToolSchema("compare-minecraft", compareMinecraftSchema);
@@ -428,6 +489,7 @@ if (!VERIFY_MIXIN_TARGET_OFF) {
428
489
  member: input.member,
429
490
  mixinMemberName: input.mixinMemberName,
430
491
  mapping: input.mapping,
492
+ autoRemap: input.autoRemap,
431
493
  sourcePriority: input.sourcePriority,
432
494
  projectPath: input.projectPath,
433
495
  gradleUserHome: input.gradleUserHome,
@@ -439,16 +501,16 @@ if (!VERIFY_MIXIN_TARGET_OFF) {
439
501
  registerToolSchema("verify-mixin-target", verifyMixinTargetSchema);
440
502
  }
441
503
  if (!BATCH_TOOLS_OFF) {
442
- server.tool("batch-class-source", "Batch lookup: read source for many classes in one call, sharing a single resolved artifact. Returns per-entry { status, result?, error? } plus aggregate summary. Per-entry retry suggestions point at get-class-source.", batchClassSourceShape, { readOnlyHint: true }, async (args) => runTool("batch-class-source", args, batchClassSourceSchema, async (input) => batchClassSourceService.execute(input)));
504
+ server.tool("batch-class-source", "Batch get-class-source for many classes sharing one resolved artifact; returns per-entry status plus aggregate summary. Not read-only: per-entry outputFile writes to disk.", batchClassSourceShape, { readOnlyHint: false }, async (args) => runTool("batch-class-source", args, batchClassSourceSchema, async (input) => batchClassSourceService.execute(input)));
443
505
  registerToolSchema("batch-class-source", batchClassSourceSchema);
444
- server.tool("batch-class-members", "Batch lookup: list members for many classes in one call, sharing a single resolved artifact. Returns per-entry { status, result?, error? } plus aggregate summary. Per-entry retry suggestions point at get-class-members.", batchClassMembersShape, { readOnlyHint: true }, async (args) => runTool("batch-class-members", args, batchClassMembersSchema, async (input) => batchClassMembersService.execute(input)));
506
+ server.tool("batch-class-members", "Batch get-class-members for many classes sharing one resolved artifact; returns per-entry status plus aggregate summary.", batchClassMembersShape, { readOnlyHint: true }, async (args) => runTool("batch-class-members", args, batchClassMembersSchema, async (input) => batchClassMembersService.execute(input)));
445
507
  registerToolSchema("batch-class-members", batchClassMembersSchema);
446
- server.tool("batch-symbol-exists", "Batch existence/mapping query: probe many symbols in one call against a shared Minecraft-version artifact. Accepts target.kind=workspace or version only (other kinds carry library versions, not Minecraft versions). Per-entry retry suggestions point at check-symbol-exists.", batchSymbolExistsShape, { readOnlyHint: true }, async (args) => runTool("batch-symbol-exists", args, batchSymbolExistsSchema, async (input) => batchSymbolExistsService.execute(input)));
508
+ server.tool("batch-symbol-exists", "Batch check-symbol-exists for many symbols against one shared Minecraft version (target.kind=version or workspace only).", batchSymbolExistsShape, { readOnlyHint: true }, async (args) => runTool("batch-symbol-exists", args, batchSymbolExistsSchema, async (input) => batchSymbolExistsService.execute(input)));
447
509
  registerToolSchema("batch-symbol-exists", batchSymbolExistsSchema);
448
- server.tool("batch-mappings", "Batch mapping translation: resolve many symbols across mapping namespaces with one shared Minecraft version. Per-entry retry suggestions point at find-mapping.", batchMappingsShape, { readOnlyHint: true }, async (args) => runTool("batch-mappings", args, batchMappingsSchema, async (input) => batchMappingsService.execute(input)));
510
+ server.tool("batch-mappings", "Batch find-mapping: resolve many symbols across mapping namespaces with one shared Minecraft version.", batchMappingsShape, { readOnlyHint: true }, async (args) => runTool("batch-mappings", args, batchMappingsSchema, async (input) => batchMappingsService.execute(input)));
449
511
  registerToolSchema("batch-mappings", batchMappingsSchema);
450
512
  }
451
- server.tool("resolve-artifact", "Resolve source artifact from a target object ({ kind, value }) and return artifact metadata. For target.kind=jar, only <basename>-sources.jar is auto-adopted; other adjacent *-sources.jar files are informational.", resolveArtifactShape, { readOnlyHint: true }, async (args) => runTool("resolve-artifact", args, resolveArtifactSchema, async (input) => sourceService.resolveArtifact({
513
+ expertTool("resolve-artifact", "Resolve a source artifact and return artifact metadata. For target.kind=jar, only <basename>-sources.jar is auto-adopted.", resolveArtifactShape, { readOnlyHint: true }, async (args) => runTool("resolve-artifact", args, resolveArtifactSchema, async (input) => sourceService.resolveArtifact({
452
514
  target: input.target,
453
515
  mapping: input.mapping,
454
516
  sourcePriority: input.sourcePriority,
@@ -458,7 +520,8 @@ server.tool("resolve-artifact", "Resolve source artifact from a target object ({
458
520
  scope: input.scope,
459
521
  preferProjectVersion: input.preferProjectVersion,
460
522
  strictVersion: input.strictVersion,
461
- compact: input.compact
523
+ includeSampleEntries: input.detail === "full" ||
524
+ (Array.isArray(input.include) && input.include.includes("samples"))
462
525
  })));
463
526
  registerToolSchema("resolve-artifact", resolveArtifactSchema);
464
527
  const findClassShape = {
@@ -467,13 +530,13 @@ const findClassShape = {
467
530
  limit: optionalPositiveInt.describe("default 20, max 200")
468
531
  };
469
532
  const findClassSchema = z.object(findClassShape);
470
- server.tool("find-class", "Resolve a simple or qualified class name to fully-qualified class names within an artifact. Use this before get-class-source when you only have a simple name.", findClassShape, { readOnlyHint: true }, async (args) => runTool("find-class", args, findClassSchema, async (input) => sourceService.findClass({
533
+ expertTool("find-class", "Resolve a simple or qualified class name to fully-qualified class names within an artifact. Use this before get-class-source when you only have a simple name.", findClassShape, { readOnlyHint: true }, async (args) => runTool("find-class", args, findClassSchema, async (input) => sourceService.findClass({
471
534
  className: input.className,
472
535
  artifactId: input.artifactId,
473
536
  limit: input.limit
474
537
  })));
475
538
  registerToolSchema("find-class", findClassSchema);
476
- server.tool("get-class-source", "Get Java source for a class by target ({ type: 'artifact', artifactId } or { type: 'resolve', kind, value }). Default mode=metadata returns symbol outline only; use mode=snippet for bounded excerpts or mode=full for entire source.", getClassSourceShape, { readOnlyHint: true }, async (args) => runTool("get-class-source", args, getClassSourceSchema, async (input) => {
539
+ expertTool("get-class-source", "Get Java source for a class. Default mode=metadata returns a symbol outline only; pass mode=snippet or mode=full to read source text. Not read-only: outputFile writes to disk.", getClassSourceShape, { readOnlyHint: false }, async (args) => runTool("get-class-source", args, getClassSourceSchema, async (input) => {
477
540
  const normalizedTarget = normalizeSourceLookupTarget(input.target);
478
541
  return sourceService.getClassSource({
479
542
  className: input.className,
@@ -496,7 +559,7 @@ server.tool("get-class-source", "Get Java source for a class by target ({ type:
496
559
  });
497
560
  }));
498
561
  registerToolSchema("get-class-source", getClassSourceSchema);
499
- server.tool("get-class-members", "Get fields/methods/constructors for one class from binary bytecode by target ({ type: 'artifact', artifactId } or { type: 'resolve', kind, value }).", getClassMembersShape, { readOnlyHint: true }, async (args) => runTool("get-class-members", args, getClassMembersSchema, async (input) => {
562
+ expertTool("get-class-members", "Get fields/methods/constructors for one class from binary bytecode.", getClassMembersShape, { readOnlyHint: true }, async (args) => runTool("get-class-members", args, getClassMembersSchema, async (input) => {
500
563
  const normalizedTarget = normalizeSourceLookupTarget(input.target);
501
564
  return sourceService.getClassMembers({
502
565
  className: input.className,
@@ -510,15 +573,20 @@ server.tool("get-class-members", "Get fields/methods/constructors for one class
510
573
  includeInherited: input.includeInherited,
511
574
  memberPattern: input.memberPattern,
512
575
  maxMembers: input.maxMembers,
576
+ cursor: input.cursor,
513
577
  projectPath: input.projectPath,
514
578
  gradleUserHome: input.gradleUserHome,
515
579
  scope: input.scope,
516
580
  preferProjectVersion: input.preferProjectVersion,
517
- strictVersion: input.strictVersion
581
+ strictVersion: input.strictVersion,
582
+ // includeDescriptors is the documented alias for include:["descriptors"]; honor both
583
+ // (mirror batch-class-members-service / inspect-minecraft class-members handler).
584
+ includeDescriptors: input.includeDescriptors === true ||
585
+ (Array.isArray(input.include) && input.include.includes("descriptors"))
518
586
  });
519
587
  }));
520
588
  registerToolSchema("get-class-members", getClassMembersSchema);
521
- server.tool("search-class-source", "Search indexed class source files for one artifact with symbol/text/path intent and compact hit output.", searchClassSourceShape, { readOnlyHint: true }, async (args) => runTool("search-class-source", args, searchClassSourceSchema, async (input) => {
589
+ expertTool("search-class-source", "Search indexed class source files for one artifact with symbol/text/path intent and compact hit output.", searchClassSourceShape, { readOnlyHint: true }, async (args) => runTool("search-class-source", args, searchClassSourceSchema, async (input) => {
522
590
  const scope = input.packagePrefix || input.fileGlob || input.symbolKind
523
591
  ? {
524
592
  packagePrefix: input.packagePrefix,
@@ -541,20 +609,20 @@ server.tool("search-class-source", "Search indexed class source files for one ar
541
609
  });
542
610
  }));
543
611
  registerToolSchema("search-class-source", searchClassSourceSchema);
544
- server.tool("get-artifact-file", "Get full source file content by artifactId and file path.", getArtifactFileShape, { readOnlyHint: true }, async (args) => runTool("get-artifact-file", args, getArtifactFileSchema, async (input) => sourceService.getArtifactFile({
612
+ expertTool("get-artifact-file", "Get full source file content by artifactId and file path.", getArtifactFileShape, { readOnlyHint: true }, async (args) => runTool("get-artifact-file", args, getArtifactFileSchema, async (input) => sourceService.getArtifactFile({
545
613
  artifactId: input.artifactId,
546
614
  filePath: input.filePath,
547
615
  maxBytes: input.maxBytes
548
616
  })));
549
617
  registerToolSchema("get-artifact-file", getArtifactFileSchema);
550
- server.tool("list-artifact-files", "List source file paths in an artifact with optional prefix filter and cursor-based pagination.", listArtifactFilesShape, { readOnlyHint: true }, async (args) => runTool("list-artifact-files", args, listArtifactFilesSchema, async (input) => sourceService.listArtifactFiles({
618
+ expertTool("list-artifact-files", "List source file paths in an artifact with optional prefix filter and cursor-based pagination.", listArtifactFilesShape, { readOnlyHint: true }, async (args) => runTool("list-artifact-files", args, listArtifactFilesSchema, async (input) => sourceService.listArtifactFiles({
551
619
  artifactId: input.artifactId,
552
620
  prefix: input.prefix,
553
621
  limit: input.limit,
554
622
  cursor: input.cursor
555
623
  })));
556
624
  registerToolSchema("list-artifact-files", listArtifactFilesSchema);
557
- server.tool("trace-symbol-lifecycle", "Trace which Minecraft versions contain a specific class method and report first/last seen versions.", traceSymbolLifecycleShape, { readOnlyHint: true }, async (args) => runTool("trace-symbol-lifecycle", args, traceSymbolLifecycleSchema, async (input) => sourceService.traceSymbolLifecycle({
625
+ expertTool("trace-symbol-lifecycle", "Trace which Minecraft versions contain a specific class method and report first/last seen versions.", traceSymbolLifecycleShape, { readOnlyHint: true }, async (args) => runTool("trace-symbol-lifecycle", args, traceSymbolLifecycleSchema, async (input) => sourceService.traceSymbolLifecycle({
558
626
  symbol: input.symbol,
559
627
  descriptor: input.descriptor,
560
628
  fromVersion: input.fromVersion,
@@ -567,7 +635,7 @@ server.tool("trace-symbol-lifecycle", "Trace which Minecraft versions contain a
567
635
  includeTimeline: input.includeTimeline
568
636
  })));
569
637
  registerToolSchema("trace-symbol-lifecycle", traceSymbolLifecycleSchema);
570
- server.tool("diff-class-signatures", "Compare one class signature between two Minecraft versions and report added/removed/modified constructors, methods, and fields.", diffClassSignaturesShape, { readOnlyHint: true }, async (args) => runTool("diff-class-signatures", args, diffClassSignaturesSchema, async (input) => sourceService.diffClassSignatures({
638
+ expertTool("diff-class-signatures", "Compare one class signature between two Minecraft versions and report added/removed/modified constructors, methods, and fields.", diffClassSignaturesShape, { readOnlyHint: true }, async (args) => runTool("diff-class-signatures", args, diffClassSignaturesSchema, async (input) => sourceService.diffClassSignatures({
571
639
  className: input.className,
572
640
  fromVersion: input.fromVersion,
573
641
  toVersion: input.toVersion,
@@ -577,7 +645,7 @@ server.tool("diff-class-signatures", "Compare one class signature between two Mi
577
645
  includeFullDiff: input.includeFullDiff
578
646
  })));
579
647
  registerToolSchema("diff-class-signatures", diffClassSignaturesSchema);
580
- server.tool("find-mapping", "Find symbol mapping candidates between namespaces using structured symbol inputs for a specific Minecraft version.", findMappingShape, { readOnlyHint: true }, async (args) => runTool("find-mapping", args, findMappingSchema, async (input) => sourceService.findMapping({
648
+ expertTool("find-mapping", "Find symbol mapping candidates between namespaces using structured symbol inputs for a specific Minecraft version.", findMappingShape, { readOnlyHint: true }, async (args) => runTool("find-mapping", args, findMappingSchema, async (input) => sourceService.findMapping({
581
649
  version: input.version,
582
650
  kind: input.kind,
583
651
  name: input.name,
@@ -587,12 +655,13 @@ server.tool("find-mapping", "Find symbol mapping candidates between namespaces u
587
655
  targetMapping: input.targetMapping,
588
656
  sourcePriority: input.sourcePriority,
589
657
  gradleUserHome: input.gradleUserHome,
658
+ nameMode: input.nameMode,
590
659
  signatureMode: input.signatureMode,
591
660
  disambiguation: input.disambiguation,
592
661
  maxCandidates: input.maxCandidates
593
662
  })));
594
663
  registerToolSchema("find-mapping", findMappingSchema);
595
- server.tool("resolve-method-mapping-exact", "Resolve one method mapping exactly by owner+name+descriptor between namespaces and report resolved/not_found/ambiguous.", resolveMethodMappingExactShape, { readOnlyHint: true }, async (args) => runTool("resolve-method-mapping-exact", args, resolveMethodMappingExactSchema, async (input) => sourceService.resolveMethodMappingExact({
664
+ expertTool("resolve-method-mapping-exact", "Strict variant of find-mapping(kind=method, signatureMode=exact): requires a COMPLETE descriptor projection and returns mapping_unavailable when any descriptor class reference cannot be projected. Prefer find-mapping unless you need that guarantee.", resolveMethodMappingExactShape, { readOnlyHint: true }, async (args) => runTool("resolve-method-mapping-exact", args, resolveMethodMappingExactSchema, async (input) => sourceService.resolveMethodMappingExact({
596
665
  version: input.version,
597
666
  name: input.name,
598
667
  owner: input.owner,
@@ -604,17 +673,18 @@ server.tool("resolve-method-mapping-exact", "Resolve one method mapping exactly
604
673
  maxCandidates: input.maxCandidates
605
674
  })));
606
675
  registerToolSchema("resolve-method-mapping-exact", resolveMethodMappingExactSchema);
607
- server.tool("get-class-api-matrix", "List class/member API rows across obfuscated/mojang/intermediary/yarn mappings for one class and Minecraft version.", getClassApiMatrixShape, { readOnlyHint: true }, async (args) => runTool("get-class-api-matrix", args, getClassApiMatrixSchema, async (input) => sourceService.getClassApiMatrix({
676
+ expertTool("get-class-api-matrix", "List class/member API rows across obfuscated/mojang/intermediary/yarn mappings for one class and Minecraft version.", getClassApiMatrixShape, { readOnlyHint: true }, async (args) => runTool("get-class-api-matrix", args, getClassApiMatrixSchema, async (input) => sourceService.getClassApiMatrix({
608
677
  version: input.version,
609
678
  className: input.className,
610
679
  classNameMapping: input.classNameMapping,
611
680
  includeKinds: parseClassApiKinds(input.includeKinds),
612
681
  sourcePriority: input.sourcePriority,
613
682
  gradleUserHome: input.gradleUserHome,
614
- maxRows: input.maxRows
683
+ maxRows: input.maxRows,
684
+ cursor: input.cursor
615
685
  })));
616
686
  registerToolSchema("get-class-api-matrix", getClassApiMatrixSchema);
617
- server.tool("resolve-workspace-symbol", "Resolve class/field/method names as seen at compile time for a workspace by reading Gradle Loom mapping settings.", resolveWorkspaceSymbolShape, { readOnlyHint: true }, async (args) => runTool("resolve-workspace-symbol", args, resolveWorkspaceSymbolSchema, async (input) => sourceService.resolveWorkspaceSymbol({
687
+ expertTool("resolve-workspace-symbol", "Resolve class/field/method names as seen at compile time for a workspace by reading Gradle Loom mapping settings.", resolveWorkspaceSymbolShape, { readOnlyHint: true }, async (args) => runTool("resolve-workspace-symbol", args, resolveWorkspaceSymbolSchema, async (input) => sourceService.resolveWorkspaceSymbol({
618
688
  projectPath: input.projectPath,
619
689
  version: input.version,
620
690
  kind: input.kind,
@@ -627,7 +697,7 @@ server.tool("resolve-workspace-symbol", "Resolve class/field/method names as see
627
697
  maxCandidates: input.maxCandidates
628
698
  })));
629
699
  registerToolSchema("resolve-workspace-symbol", resolveWorkspaceSymbolSchema);
630
- server.tool("check-symbol-exists", "Check whether a class/field/method symbol exists in a specific mapping namespace for one Minecraft version.", checkSymbolExistsShape, { readOnlyHint: true }, async (args) => runTool("check-symbol-exists", args, checkSymbolExistsSchema, async (input) => sourceService.checkSymbolExists({
700
+ expertTool("check-symbol-exists", "Check whether a class/field/method symbol exists in a specific mapping namespace for one Minecraft version.", checkSymbolExistsShape, { readOnlyHint: true }, async (args) => runTool("check-symbol-exists", args, checkSymbolExistsSchema, async (input) => sourceService.checkSymbolExists({
631
701
  version: input.version,
632
702
  kind: input.kind,
633
703
  owner: input.owner,
@@ -641,29 +711,29 @@ server.tool("check-symbol-exists", "Check whether a class/field/method symbol ex
641
711
  maxCandidates: input.maxCandidates
642
712
  })));
643
713
  registerToolSchema("check-symbol-exists", checkSymbolExistsSchema);
644
- server.tool("nbt-to-json", "Decode Java Edition NBT binary payload (base64) into typed JSON.", nbtToJsonShape, { readOnlyHint: true }, async (args) => runTool("nbt-to-json", args, nbtToJsonSchema, async (input) => Promise.resolve(nbtBase64ToTypedJson({
714
+ server.tool("nbt-to-json", "Decode Java Edition NBT binary payload (base64) into typed JSON.", nbtToJsonShape, { readOnlyHint: true, openWorldHint: false }, async (args) => runTool("nbt-to-json", args, nbtToJsonSchema, async (input) => Promise.resolve(nbtBase64ToTypedJson({
645
715
  nbtBase64: input.nbtBase64,
646
716
  compression: input.compression
647
717
  }, nbtLimits))));
648
718
  registerToolSchema("nbt-to-json", nbtToJsonSchema);
649
- server.tool("nbt-apply-json-patch", "Apply RFC6902 add/remove/replace/test operations to typed NBT JSON.", nbtApplyJsonPatchShape, { readOnlyHint: true }, async (args) => runTool("nbt-apply-json-patch", args, nbtApplyJsonPatchSchema, async (input) => Promise.resolve(applyNbtJsonPatch({
719
+ server.tool("nbt-apply-json-patch", "Apply RFC6902 add/remove/replace/test operations to typed NBT JSON.", nbtApplyJsonPatchShape, { readOnlyHint: true, openWorldHint: false }, async (args) => runTool("nbt-apply-json-patch", args, nbtApplyJsonPatchSchema, async (input) => Promise.resolve(applyNbtJsonPatch({
650
720
  typedJson: input.typedJson,
651
721
  patch: input.patch
652
722
  }, nbtLimits))));
653
723
  registerToolSchema("nbt-apply-json-patch", nbtApplyJsonPatchSchema);
654
- server.tool("json-to-nbt", "Encode typed NBT JSON to Java Edition NBT binary payload (base64).", jsonToNbtShape, { readOnlyHint: true }, async (args) => runTool("json-to-nbt", args, jsonToNbtSchema, async (input) => Promise.resolve(typedJsonToNbtBase64({
724
+ server.tool("json-to-nbt", "Encode typed NBT JSON to Java Edition NBT binary payload (base64).", jsonToNbtShape, { readOnlyHint: true, openWorldHint: false }, async (args) => runTool("json-to-nbt", args, jsonToNbtSchema, async (input) => Promise.resolve(typedJsonToNbtBase64({
655
725
  typedJson: input.typedJson,
656
726
  compression: input.compression
657
727
  }, nbtLimits))));
658
728
  registerToolSchema("json-to-nbt", jsonToNbtSchema);
659
- server.tool("index-artifact", "Rebuild indexed files/symbols metadata for an existing artifactId. Does not resolve new artifacts.", indexArtifactShape, async (args) => runTool("index-artifact", args, indexArtifactSchema, async (input) => sourceService.indexArtifact({
729
+ expertTool("index-artifact", "Rebuild indexed files/symbols metadata for an existing artifactId. Does not resolve new artifacts.", indexArtifactShape, { readOnlyHint: false, idempotentHint: true }, async (args) => runTool("index-artifact", args, indexArtifactSchema, async (input) => sourceService.indexArtifact({
660
730
  artifactId: input.artifactId,
661
731
  force: input.force
662
732
  })));
663
733
  registerToolSchema("index-artifact", indexArtifactSchema);
664
- server.tool("get-runtime-metrics", "Get runtime service counters and latency snapshots for cache/search/index diagnostics.", { readOnlyHint: true }, async (args) => runTool("get-runtime-metrics", args, emptySchema, async () => Promise.resolve(sourceService.getRuntimeMetrics())));
734
+ server.tool("get-runtime-metrics", "Get runtime service counters and latency snapshots for cache/search/index diagnostics.", { readOnlyHint: true, openWorldHint: false }, async (args) => runTool("get-runtime-metrics", args, emptySchema, async () => Promise.resolve(sourceService.getRuntimeMetrics())));
665
735
  registerToolSchema("get-runtime-metrics", emptySchema);
666
- server.tool("validate-mixin", "Validate Mixin source against Minecraft bytecode signatures for a given version.", validateMixinShape, { readOnlyHint: true }, async (args, extra) => runTool("validate-mixin", args, validateMixinSchema, async (input) => sourceService.validateMixin({
736
+ expertTool("validate-mixin", "Validate Mixin source against Minecraft bytecode signatures for a given version.", validateMixinShape, { readOnlyHint: true }, async (args, extra) => runTool("validate-mixin", args, validateMixinSchema, async (input) => sourceService.validateMixin({
667
737
  input: input.input,
668
738
  sourceRoots: input.sourceRoots,
669
739
  version: input.version,
@@ -686,7 +756,7 @@ server.tool("validate-mixin", "Validate Mixin source against Minecraft bytecode
686
756
  stageEmitter: makeStageEmitter(extra)
687
757
  })));
688
758
  registerToolSchema("validate-mixin", validateMixinSchema);
689
- server.tool("validate-access-widener", "Validate Access Widener file entries against Minecraft bytecode signatures for a given version.", validateAccessWidenerShape, { readOnlyHint: true }, async (args) => runTool("validate-access-widener", args, validateAccessWidenerSchema, async (input) => sourceService.validateAccessWidener({
759
+ expertTool("validate-access-widener", "Validate Access Widener file entries against Minecraft bytecode signatures for a given version.", validateAccessWidenerShape, { readOnlyHint: true }, async (args) => runTool("validate-access-widener", args, validateAccessWidenerSchema, async (input) => sourceService.validateAccessWidener({
690
760
  content: input.content,
691
761
  version: input.version,
692
762
  mapping: input.mapping,
@@ -697,7 +767,7 @@ server.tool("validate-access-widener", "Validate Access Widener file entries aga
697
767
  preferProjectVersion: input.preferProjectVersion
698
768
  })));
699
769
  registerToolSchema("validate-access-widener", validateAccessWidenerSchema);
700
- server.tool("validate-access-transformer", "Validate Access Transformer file entries against Minecraft bytecode signatures for a given version.", validateAccessTransformerShape, { readOnlyHint: true }, async (args) => runTool("validate-access-transformer", args, validateAccessTransformerSchema, async (input) => sourceService.validateAccessTransformer({
770
+ expertTool("validate-access-transformer", "Validate Access Transformer file entries against Minecraft bytecode signatures for a given version.", validateAccessTransformerShape, { readOnlyHint: true }, async (args) => runTool("validate-access-transformer", args, validateAccessTransformerSchema, async (input) => sourceService.validateAccessTransformer({
701
771
  content: input.content,
702
772
  version: input.version,
703
773
  atNamespace: input.atNamespace,
@@ -708,7 +778,7 @@ server.tool("validate-access-transformer", "Validate Access Transformer file ent
708
778
  preferProjectVersion: input.preferProjectVersion
709
779
  })));
710
780
  registerToolSchema("validate-access-transformer", validateAccessTransformerSchema);
711
- server.tool("analyze-mod-jar", "Analyze a Minecraft mod JAR to extract loader type, metadata, entrypoints, mixins, and dependencies.", analyzeModJarShape, { readOnlyHint: true }, async (args) => runTool("analyze-mod-jar", args, analyzeModJarSchema, async (input) => {
781
+ expertTool("analyze-mod-jar", "Analyze a Minecraft mod JAR to extract loader type, metadata, entrypoints, mixins, and dependencies.", analyzeModJarShape, { readOnlyHint: true }, async (args) => runTool("analyze-mod-jar", args, analyzeModJarSchema, async (input) => {
712
782
  const result = await analyzeModJar(input.jarPath, {
713
783
  includeClasses: input.includeClasses ?? false
714
784
  });
@@ -722,7 +792,7 @@ server.tool("get-registry-data", "Get Minecraft registry data (blocks, items, bi
722
792
  maxEntriesPerRegistry: input.maxEntriesPerRegistry
723
793
  })));
724
794
  registerToolSchema("get-registry-data", getRegistryDataSchema);
725
- server.tool("compare-versions", "Compare two Minecraft versions to find added/removed classes and registry entry changes. Useful for understanding what changed between versions during mod migration.", compareVersionsShape, { readOnlyHint: true }, async (args) => runTool("compare-versions", args, compareVersionsSchema, async (input) => sourceService.compareVersions({
795
+ expertTool("compare-versions", "Compare two Minecraft versions to find added/removed classes and registry entry changes.", compareVersionsShape, { readOnlyHint: true }, async (args) => runTool("compare-versions", args, compareVersionsSchema, async (input) => sourceService.compareVersions({
726
796
  fromVersion: input.fromVersion,
727
797
  toVersion: input.toVersion,
728
798
  category: input.category,
@@ -730,14 +800,14 @@ server.tool("compare-versions", "Compare two Minecraft versions to find added/re
730
800
  maxClassResults: input.maxClassResults
731
801
  })));
732
802
  registerToolSchema("compare-versions", compareVersionsSchema);
733
- server.tool("decompile-mod-jar", "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.", decompileModJarShape, { readOnlyHint: true }, async (args) => runTool("decompile-mod-jar", args, decompileModJarSchema, async (input) => sourceService.decompileModJar({
803
+ expertTool("decompile-mod-jar", "Decompile a Minecraft mod JAR using Vineflower and list available classes, or view a specific class source. Builds on analyze-mod-jar.", decompileModJarShape, { readOnlyHint: true }, async (args) => runTool("decompile-mod-jar", args, decompileModJarSchema, async (input) => sourceService.decompileModJar({
734
804
  jarPath: input.jarPath,
735
805
  className: input.className,
736
806
  includeFiles: input.includeFiles,
737
807
  maxFiles: input.maxFiles
738
808
  })));
739
809
  registerToolSchema("decompile-mod-jar", decompileModJarSchema);
740
- server.tool("get-mod-class-source", "Get decompiled source code for a specific class in a mod JAR. The mod JAR will be decompiled if not already cached.", getModClassSourceShape, { readOnlyHint: true }, async (args) => runTool("get-mod-class-source", args, getModClassSourceSchema, async (input) => sourceService.getModClassSource({
810
+ expertTool("get-mod-class-source", "Get decompiled source code for a specific class in a mod JAR. The mod JAR will be decompiled if not already cached. Not read-only: outputFile writes the source to disk.", getModClassSourceShape, { readOnlyHint: false }, async (args) => runTool("get-mod-class-source", args, getModClassSourceSchema, async (input) => sourceService.getModClassSource({
741
811
  jarPath: input.jarPath,
742
812
  className: input.className,
743
813
  maxLines: input.maxLines,
@@ -745,19 +815,20 @@ server.tool("get-mod-class-source", "Get decompiled source code for a specific c
745
815
  outputFile: input.outputFile
746
816
  })));
747
817
  registerToolSchema("get-mod-class-source", getModClassSourceSchema);
748
- server.tool("search-mod-source", "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.", searchModSourceShape, { readOnlyHint: true }, async (args) => runTool("search-mod-source", args, searchModSourceSchema, async (input) => sourceService.searchModSource({
818
+ expertTool("search-mod-source", "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.", searchModSourceShape, { readOnlyHint: true }, async (args) => runTool("search-mod-source", args, searchModSourceSchema, async (input) => sourceService.searchModSource({
749
819
  jarPath: input.jarPath,
750
820
  query: input.query,
751
821
  searchType: input.searchType,
752
822
  limit: input.limit
753
823
  })));
754
824
  registerToolSchema("search-mod-source", searchModSourceSchema);
755
- server.tool("remap-mod-jar", "Remap a Fabric mod JAR from intermediary to yarn/mojang names. Requires Java to be installed.", remapModJarShape, { readOnlyHint: false }, async (args) => runTool("remap-mod-jar", args, remapModJarSchema, async (input) => {
825
+ expertTool("remap-mod-jar", "Remap a Fabric mod JAR from intermediary to yarn/mojang names. Requires Java to be installed.", remapModJarShape, { readOnlyHint: false }, async (args) => runTool("remap-mod-jar", args, remapModJarSchema, async (input) => {
756
826
  const result = await remapModJar({
757
827
  inputJar: input.inputJar,
758
828
  outputJar: input.outputJar,
759
829
  mcVersion: input.mcVersion,
760
- targetMapping: input.targetMapping
830
+ targetMapping: input.targetMapping,
831
+ forceRemap: input.forceRemap
761
832
  }, config);
762
833
  return result;
763
834
  }));
@@ -107,7 +107,7 @@ export function runJavaProcess(options) {
107
107
  }
108
108
  }));
109
109
  });
110
- proc.once("exit", (code) => {
110
+ proc.once("close", (code) => {
111
111
  clearTimeout(timer);
112
112
  resolve({
113
113
  exitCode: code ?? -1,
@@ -9,6 +9,9 @@ export declare function encodeJsonRpcMessage(message: JSONRPCMessage, mode: Conc
9
9
  export declare class JsonRpcFrameReader {
10
10
  private mode;
11
11
  private buffer;
12
+ private pendingChunks;
13
+ private pendingBytes;
14
+ private awaitedFrameEnd;
12
15
  get currentMode(): FramingMode;
13
16
  reset(): void;
14
17
  clear(): void;
@@ -16,6 +19,7 @@ export declare class JsonRpcFrameReader {
16
19
  onFrame: (frame: ParsedJsonRpcFrame) => void;
17
20
  onError: (error: Error) => void;
18
21
  }): void;
22
+ private canCompleteFrame;
19
23
  private detectMode;
20
24
  private readLineDelimitedMessage;
21
25
  private readContentLengthMessage;
@@ -25,21 +25,35 @@ export function encodeJsonRpcMessage(message, mode) {
25
25
  export class JsonRpcFrameReader {
26
26
  mode = "unknown";
27
27
  buffer = Buffer.alloc(0);
28
+ pendingChunks = [];
29
+ pendingBytes = 0;
30
+ awaitedFrameEnd = -1;
28
31
  get currentMode() {
29
32
  return this.mode;
30
33
  }
31
34
  reset() {
32
35
  this.mode = "unknown";
36
+ this.awaitedFrameEnd = -1;
33
37
  }
34
38
  clear() {
35
39
  this.mode = "unknown";
36
40
  this.buffer = Buffer.alloc(0);
41
+ this.pendingChunks = [];
42
+ this.pendingBytes = 0;
43
+ this.awaitedFrameEnd = -1;
37
44
  }
38
45
  processChunk(chunk, handlers) {
39
46
  if (chunk.length === 0) {
40
47
  return;
41
48
  }
42
- this.buffer = Buffer.concat([this.buffer, chunk]);
49
+ this.pendingChunks.push(chunk);
50
+ this.pendingBytes += chunk.length;
51
+ if (!this.canCompleteFrame(chunk)) {
52
+ return;
53
+ }
54
+ this.buffer = Buffer.concat([this.buffer, ...this.pendingChunks]);
55
+ this.pendingChunks = [];
56
+ this.pendingBytes = 0;
43
57
  while (true) {
44
58
  try {
45
59
  if (this.mode === "unknown") {
@@ -71,6 +85,12 @@ export class JsonRpcFrameReader {
71
85
  }
72
86
  }
73
87
  }
88
+ canCompleteFrame(chunk) {
89
+ if (this.mode === "content-length" && this.awaitedFrameEnd >= 0) {
90
+ return this.buffer.length + this.pendingBytes >= this.awaitedFrameEnd;
91
+ }
92
+ return chunk.includes(0x0a);
93
+ }
74
94
  detectMode() {
75
95
  while (this.buffer.length > 0) {
76
96
  if (this.buffer[0] === 0x0a) {
@@ -124,6 +144,7 @@ export class JsonRpcFrameReader {
124
144
  }
125
145
  }
126
146
  readContentLengthMessage() {
147
+ this.awaitedFrameEnd = -1;
127
148
  const headerBoundary = findHeaderBoundary(this.buffer);
128
149
  if (!headerBoundary) {
129
150
  return undefined;
@@ -158,6 +179,7 @@ export class JsonRpcFrameReader {
158
179
  const messageStart = headerBoundary.index + headerBoundary.delimiterBytes;
159
180
  const frameEnd = messageStart + contentLength;
160
181
  if (this.buffer.length < frameEnd) {
182
+ this.awaitedFrameEnd = frameEnd;
161
183
  return undefined;
162
184
  }
163
185
  const body = this.buffer.subarray(messageStart, frameEnd).toString("utf8");