@adhisang/minecraft-modding-mcp 3.2.0 → 4.1.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 (194) hide show
  1. package/CHANGELOG.md +72 -0
  2. package/README.md +52 -32
  3. package/dist/build-suggested-call.d.ts +29 -0
  4. package/dist/build-suggested-call.js +58 -0
  5. package/dist/cache-registry.d.ts +3 -1
  6. package/dist/cache-registry.js +59 -7
  7. package/dist/config.d.ts +10 -1
  8. package/dist/config.js +52 -1
  9. package/dist/entry-tools/analyze-symbol-service.d.ts +18 -18
  10. package/dist/entry-tools/analyze-symbol-service.js +13 -2
  11. package/dist/entry-tools/batch-class-members-service.d.ts +34 -0
  12. package/dist/entry-tools/batch-class-members-service.js +97 -0
  13. package/dist/entry-tools/batch-class-source-service.d.ts +37 -0
  14. package/dist/entry-tools/batch-class-source-service.js +100 -0
  15. package/dist/entry-tools/batch-mappings-service.d.ts +36 -0
  16. package/dist/entry-tools/batch-mappings-service.js +66 -0
  17. package/dist/entry-tools/batch-runner.d.ts +72 -0
  18. package/dist/entry-tools/batch-runner.js +90 -0
  19. package/dist/entry-tools/batch-symbol-exists-service.d.ts +46 -0
  20. package/dist/entry-tools/batch-symbol-exists-service.js +113 -0
  21. package/dist/entry-tools/compare-minecraft-service.d.ts +6 -6
  22. package/dist/entry-tools/inspect-minecraft/handlers/artifact.d.ts +5 -0
  23. package/dist/entry-tools/inspect-minecraft/handlers/artifact.js +83 -0
  24. package/dist/entry-tools/inspect-minecraft/handlers/class-members.d.ts +6 -0
  25. package/dist/entry-tools/inspect-minecraft/handlers/class-members.js +80 -0
  26. package/dist/entry-tools/inspect-minecraft/handlers/class-overview.d.ts +5 -0
  27. package/dist/entry-tools/inspect-minecraft/handlers/class-overview.js +248 -0
  28. package/dist/entry-tools/inspect-minecraft/handlers/class-source.d.ts +5 -0
  29. package/dist/entry-tools/inspect-minecraft/handlers/class-source.js +60 -0
  30. package/dist/entry-tools/inspect-minecraft/handlers/file.d.ts +5 -0
  31. package/dist/entry-tools/inspect-minecraft/handlers/file.js +54 -0
  32. package/dist/entry-tools/inspect-minecraft/handlers/list-files.d.ts +5 -0
  33. package/dist/entry-tools/inspect-minecraft/handlers/list-files.js +100 -0
  34. package/dist/entry-tools/inspect-minecraft/handlers/search.d.ts +5 -0
  35. package/dist/entry-tools/inspect-minecraft/handlers/search.js +155 -0
  36. package/dist/entry-tools/inspect-minecraft/handlers/versions.d.ts +6 -0
  37. package/dist/entry-tools/inspect-minecraft/handlers/versions.js +49 -0
  38. package/dist/entry-tools/inspect-minecraft/internal.d.ts +1042 -0
  39. package/dist/entry-tools/inspect-minecraft/internal.js +448 -0
  40. package/dist/entry-tools/inspect-minecraft-service.d.ts +213 -328
  41. package/dist/entry-tools/inspect-minecraft-service.js +20 -1238
  42. package/dist/entry-tools/manage-cache-service.d.ts +16 -16
  43. package/dist/entry-tools/validate-project/cases/access-transformer.d.ts +6 -0
  44. package/dist/entry-tools/validate-project/cases/access-transformer.js +106 -0
  45. package/dist/entry-tools/validate-project/cases/access-widener.d.ts +6 -0
  46. package/dist/entry-tools/validate-project/cases/access-widener.js +86 -0
  47. package/dist/entry-tools/validate-project/cases/mixin.d.ts +6 -0
  48. package/dist/entry-tools/validate-project/cases/mixin.js +90 -0
  49. package/dist/entry-tools/validate-project/cases/project-summary.d.ts +97 -0
  50. package/dist/entry-tools/validate-project/cases/project-summary.js +346 -0
  51. package/dist/entry-tools/validate-project/internal.d.ts +135 -0
  52. package/dist/entry-tools/validate-project/internal.js +287 -0
  53. package/dist/entry-tools/validate-project-service.d.ts +63 -47
  54. package/dist/entry-tools/validate-project-service.js +12 -482
  55. package/dist/entry-tools/verify-mixin-target-service.d.ts +133 -0
  56. package/dist/entry-tools/verify-mixin-target-service.js +323 -0
  57. package/dist/error-mapping.d.ts +40 -0
  58. package/dist/error-mapping.js +139 -0
  59. package/dist/errors.d.ts +6 -0
  60. package/dist/errors.js +6 -0
  61. package/dist/index.d.ts +2 -0
  62. package/dist/index.js +170 -1314
  63. package/dist/lru-list.d.ts +31 -0
  64. package/dist/lru-list.js +102 -0
  65. package/dist/mapping/internal-types.d.ts +54 -0
  66. package/dist/mapping/internal-types.js +14 -0
  67. package/dist/mapping/loaders/mojang.d.ts +2 -0
  68. package/dist/mapping/loaders/mojang.js +64 -0
  69. package/dist/mapping/loaders/tiny-loom.d.ts +2 -0
  70. package/dist/mapping/loaders/tiny-loom.js +73 -0
  71. package/dist/mapping/loaders/tiny-maven.d.ts +2 -0
  72. package/dist/mapping/loaders/tiny-maven.js +104 -0
  73. package/dist/mapping/loaders/types.d.ts +14 -0
  74. package/dist/mapping/loaders/types.js +2 -0
  75. package/dist/mapping/lookup.d.ts +52 -0
  76. package/dist/mapping/lookup.js +496 -0
  77. package/dist/mapping/parsers/normalize.d.ts +10 -0
  78. package/dist/mapping/parsers/normalize.js +52 -0
  79. package/dist/mapping/parsers/proguard.d.ts +20 -0
  80. package/dist/mapping/parsers/proguard.js +138 -0
  81. package/dist/mapping/parsers/symbol-records.d.ts +27 -0
  82. package/dist/mapping/parsers/symbol-records.js +216 -0
  83. package/dist/mapping/parsers/tiny.d.ts +9 -0
  84. package/dist/mapping/parsers/tiny.js +96 -0
  85. package/dist/mapping/types.d.ts +147 -0
  86. package/dist/mapping/types.js +2 -0
  87. package/dist/mapping-pipeline-service.d.ts +10 -1
  88. package/dist/mapping-pipeline-service.js +16 -3
  89. package/dist/mapping-service.d.ts +15 -144
  90. package/dist/mapping-service.js +179 -1119
  91. package/dist/mixin/access-validators.d.ts +9 -0
  92. package/dist/mixin/access-validators.js +257 -0
  93. package/dist/mixin/annotation-validators.d.ts +5 -0
  94. package/dist/mixin/annotation-validators.js +162 -0
  95. package/dist/mixin/helpers.d.ts +28 -0
  96. package/dist/mixin/helpers.js +315 -0
  97. package/dist/mixin/parsed-validator.d.ts +8 -0
  98. package/dist/mixin/parsed-validator.js +337 -0
  99. package/dist/mixin/types.d.ts +208 -0
  100. package/dist/mixin/types.js +28 -0
  101. package/dist/mixin-validator.d.ts +9 -201
  102. package/dist/mixin-validator.js +8 -1005
  103. package/dist/observability.d.ts +18 -1
  104. package/dist/observability.js +44 -1
  105. package/dist/response-utils.d.ts +44 -10
  106. package/dist/response-utils.js +131 -17
  107. package/dist/source/access-validate.d.ts +4 -0
  108. package/dist/source/access-validate.js +254 -0
  109. package/dist/source/artifact-resolver.d.ts +110 -0
  110. package/dist/source/artifact-resolver.js +1174 -0
  111. package/dist/source/cache-metrics.d.ts +26 -0
  112. package/dist/source/cache-metrics.js +172 -0
  113. package/dist/source/class-source/members-builder.d.ts +34 -0
  114. package/dist/source/class-source/members-builder.js +46 -0
  115. package/dist/source/class-source/snippet-builder.d.ts +19 -0
  116. package/dist/source/class-source/snippet-builder.js +46 -0
  117. package/dist/source/class-source-helpers.d.ts +34 -0
  118. package/dist/source/class-source-helpers.js +140 -0
  119. package/dist/source/class-source.d.ts +42 -0
  120. package/dist/source/class-source.js +883 -0
  121. package/dist/source/descriptor-utils.d.ts +6 -0
  122. package/dist/source/descriptor-utils.js +37 -0
  123. package/dist/source/file-access.d.ts +4 -0
  124. package/dist/source/file-access.js +102 -0
  125. package/dist/source/indexer.d.ts +82 -0
  126. package/dist/source/indexer.js +505 -0
  127. package/dist/source/lifecycle/diff-utils.d.ts +9 -0
  128. package/dist/source/lifecycle/diff-utils.js +107 -0
  129. package/dist/source/lifecycle/diff.d.ts +2 -0
  130. package/dist/source/lifecycle/diff.js +265 -0
  131. package/dist/source/lifecycle/mapping-helpers.d.ts +22 -0
  132. package/dist/source/lifecycle/mapping-helpers.js +327 -0
  133. package/dist/source/lifecycle/runtime-check.d.ts +2 -0
  134. package/dist/source/lifecycle/runtime-check.js +142 -0
  135. package/dist/source/lifecycle/trace.d.ts +2 -0
  136. package/dist/source/lifecycle/trace.js +231 -0
  137. package/dist/source/lifecycle.d.ts +4 -0
  138. package/dist/source/lifecycle.js +5 -0
  139. package/dist/source/search.d.ts +51 -0
  140. package/dist/source/search.js +676 -0
  141. package/dist/source/shared-utils.d.ts +6 -0
  142. package/dist/source/shared-utils.js +55 -0
  143. package/dist/source/state.d.ts +21 -0
  144. package/dist/source/state.js +19 -0
  145. package/dist/source/symbol-resolver.d.ts +3 -0
  146. package/dist/source/symbol-resolver.js +212 -0
  147. package/dist/source/validate-mixin/pipeline/mapping-health.d.ts +3 -0
  148. package/dist/source/validate-mixin/pipeline/mapping-health.js +41 -0
  149. package/dist/source/validate-mixin/pipeline/parse.d.ts +2 -0
  150. package/dist/source/validate-mixin/pipeline/parse.js +10 -0
  151. package/dist/source/validate-mixin/pipeline/resolve.d.ts +3 -0
  152. package/dist/source/validate-mixin/pipeline/resolve.js +78 -0
  153. package/dist/source/validate-mixin/pipeline/target-lookup.d.ts +6 -0
  154. package/dist/source/validate-mixin/pipeline/target-lookup.js +260 -0
  155. package/dist/source/validate-mixin/pipeline-context.d.ts +72 -0
  156. package/dist/source/validate-mixin/pipeline-context.js +93 -0
  157. package/dist/source/validate-mixin.d.ts +22 -0
  158. package/dist/source/validate-mixin.js +799 -0
  159. package/dist/source/workspace-target.d.ts +18 -0
  160. package/dist/source/workspace-target.js +305 -0
  161. package/dist/source-resolver.d.ts +9 -1
  162. package/dist/source-resolver.js +14 -6
  163. package/dist/source-service.d.ts +178 -105
  164. package/dist/source-service.js +72 -5312
  165. package/dist/stage-emitter.d.ts +13 -0
  166. package/dist/stage-emitter.js +30 -0
  167. package/dist/stdio-supervisor.d.ts +61 -0
  168. package/dist/stdio-supervisor.js +326 -9
  169. package/dist/storage/artifacts-repo.d.ts +4 -1
  170. package/dist/storage/artifacts-repo.js +33 -5
  171. package/dist/storage/files-repo.d.ts +0 -2
  172. package/dist/storage/files-repo.js +0 -11
  173. package/dist/storage/migrations.d.ts +1 -1
  174. package/dist/storage/migrations.js +10 -2
  175. package/dist/storage/schema.d.ts +2 -0
  176. package/dist/storage/schema.js +25 -0
  177. package/dist/tool-contract-manifest.d.ts +1 -1
  178. package/dist/tool-contract-manifest.js +23 -6
  179. package/dist/tool-guidance.d.ts +82 -0
  180. package/dist/tool-guidance.js +734 -0
  181. package/dist/tool-schema-registry.d.ts +16 -0
  182. package/dist/tool-schema-registry.js +37 -0
  183. package/dist/tool-schemas.d.ts +3518 -0
  184. package/dist/tool-schemas.js +813 -0
  185. package/dist/types.d.ts +39 -0
  186. package/dist/version-service.js +7 -6
  187. package/dist/workspace-context-cache.d.ts +32 -0
  188. package/dist/workspace-context-cache.js +66 -0
  189. package/dist/workspace-mapping-service.d.ts +16 -0
  190. package/dist/workspace-mapping-service.js +173 -1
  191. package/docs/README-ja.md +414 -0
  192. package/docs/examples.md +483 -0
  193. package/docs/tool-reference.md +459 -0
  194. package/package.json +5 -2
package/dist/index.js CHANGED
@@ -1,10 +1,11 @@
1
1
  import { readFileSync } from "node:fs";
2
2
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { makeStageEmitter } from "./stage-emitter.js";
3
4
  import { ZodError, z } from "zod";
4
5
  import { CompatStdioServerTransport } from "./compat-stdio-transport.js";
5
6
  import { objectResult } from "./mcp-helpers.js";
6
7
  import { prepareToolInput } from "./tool-input.js";
7
- import { isCompactEnabled, COMPACT_MAPPING_TOOL_NAMES, compactResponse, compactArtifactResponse, compactMappingResponse } from "./response-utils.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
9
  import { loadConfig } from "./config.js";
9
10
  import { createError, ERROR_CODES, isAppError } from "./errors.js";
10
11
  import { log } from "./logger.js";
@@ -21,24 +22,22 @@ import { CompareMinecraftService, compareMinecraftSchema, compareMinecraftShape
21
22
  import { AnalyzeModService, analyzeModSchema, analyzeModShape } from "./entry-tools/analyze-mod-service.js";
22
23
  import { ValidateProjectService, validateProjectSchema, validateProjectShape, discoverWorkspaceAccessTransformers, discoverWorkspaceAccessWideners, discoverWorkspaceMixins } from "./entry-tools/validate-project-service.js";
23
24
  import { ManageCacheService, manageCacheSchema, manageCacheShape } from "./entry-tools/manage-cache-service.js";
25
+ import { VerifyMixinTargetService, VERIFY_MIXIN_TARGET_OFF } from "./entry-tools/verify-mixin-target-service.js";
26
+ import { BATCH_TOOLS_OFF } from "./entry-tools/batch-runner.js";
27
+ import { BatchClassSourceService } from "./entry-tools/batch-class-source-service.js";
28
+ import { BatchClassMembersService } from "./entry-tools/batch-class-members-service.js";
29
+ import { BatchSymbolExistsService } from "./entry-tools/batch-symbol-exists-service.js";
30
+ import { BatchMappingsService } from "./entry-tools/batch-mappings-service.js";
24
31
  import { createCacheRegistry } from "./cache-registry.js";
25
32
  import { buildEntryToolMeta } from "./entry-tools/response-contract.js";
33
+ import { registerToolSchema } from "./tool-schema-registry.js";
34
+ import { buildSuggestedCall } from "./build-suggested-call.js";
35
+ import { applyErrorMetaExtensions, mapErrorToProblem } from "./tool-guidance.js";
36
+ import { nonEmptyString, optionalPositiveInt, analyzeModJarSchema, analyzeModJarShape, batchClassMembersSchema, batchClassMembersShape, batchClassSourceSchema, batchClassSourceShape, batchMappingsSchema, batchMappingsShape, batchSymbolExistsSchema, batchSymbolExistsShape, checkSymbolExistsSchema, checkSymbolExistsShape, compareVersionsSchema, compareVersionsShape, decompileModJarSchema, decompileModJarShape, diffClassSignaturesSchema, diffClassSignaturesShape, emptySchema, findMappingSchema, findMappingShape, getArtifactFileSchema, getArtifactFileShape, getClassApiMatrixSchema, getClassApiMatrixShape, getClassMembersSchema, getClassMembersShape, getClassSourceSchema, getClassSourceShape, getModClassSourceSchema, getModClassSourceShape, getRegistryDataSchema, getRegistryDataShape, indexArtifactSchema, indexArtifactShape, jsonToNbtSchema, jsonToNbtShape, listArtifactFilesSchema, listArtifactFilesShape, listVersionsSchema, listVersionsShape, nbtApplyJsonPatchSchema, nbtApplyJsonPatchShape, nbtToJsonSchema, nbtToJsonShape, remapModJarSchema, remapModJarShape, resolveArtifactSchema, resolveArtifactShape, resolveMethodMappingExactSchema, resolveMethodMappingExactShape, resolveWorkspaceSymbolSchema, resolveWorkspaceSymbolShape, searchClassSourceSchema, searchClassSourceShape, searchModSourceSchema, searchModSourceShape, traceSymbolLifecycleSchema, traceSymbolLifecycleShape, validateAccessTransformerSchema, validateAccessTransformerShape, validateAccessWidenerSchema, validateAccessWidenerShape, validateMixinSchema, validateMixinShape, verifyMixinTargetSchema, verifyMixinTargetShape } from "./tool-schemas.js";
37
+ export { mapErrorToProblem, applyErrorMetaExtensions };
26
38
  if (!process.env.NODE_ENV) {
27
39
  process.env.NODE_ENV = "production";
28
40
  }
29
- const SOURCE_MAPPINGS = ["obfuscated", "mojang", "intermediary", "yarn"];
30
- const SOURCE_PRIORITIES = ["loom-first", "maven-first"];
31
- const TARGET_KINDS = ["version", "jar", "coordinate"];
32
- const SEARCH_INTENTS = ["symbol", "text", "path"];
33
- const SEARCH_MATCHES = ["exact", "prefix", "contains", "regex"];
34
- const SEARCH_SYMBOL_KINDS = ["class", "interface", "enum", "record", "method", "field"];
35
- const MEMBER_ACCESS = ["public", "all"];
36
- const WORKSPACE_SYMBOL_KINDS = ["class", "field", "method"];
37
- const CLASS_NAME_MODES = ["fqcn", "auto"];
38
- const SOURCE_MODES = ["metadata", "snippet", "full"];
39
- const ARTIFACT_SCOPES = ["vanilla", "merged", "loader"];
40
- const DECODE_COMPRESSIONS = ["none", "gzip", "auto"];
41
- const ENCODE_COMPRESSIONS = ["none", "gzip"];
42
41
  const HEAVY_TOOL_NAMES = new Set([
43
42
  "trace-symbol-lifecycle",
44
43
  "diff-class-signatures",
@@ -57,615 +56,6 @@ const ENTRY_TOOL_NAMES = new Set([
57
56
  "manage-cache"
58
57
  ]);
59
58
  const heavyToolExecutionGate = new ToolExecutionGate({ maxConcurrent: 1, maxQueue: 2 });
60
- const nonEmptyString = z.string().trim().min(1);
61
- const optionalNonEmptyString = z.string().trim().min(1).optional();
62
- const optionalPositiveInt = z.number().int().positive().optional();
63
- const sourceMappingSchema = z.enum(SOURCE_MAPPINGS);
64
- const mappingSourcePrioritySchema = z.enum(SOURCE_PRIORITIES);
65
- const targetKindSchema = z.enum(TARGET_KINDS);
66
- const searchIntentSchema = z.enum(SEARCH_INTENTS);
67
- const searchMatchSchema = z.enum(SEARCH_MATCHES);
68
- const searchSymbolKindSchema = z.enum(SEARCH_SYMBOL_KINDS);
69
- const memberAccessSchema = z.enum(MEMBER_ACCESS);
70
- const workspaceSymbolKindSchema = z.enum(WORKSPACE_SYMBOL_KINDS);
71
- const classNameModeSchema = z.enum(CLASS_NAME_MODES);
72
- const sourceModeSchema = z.enum(SOURCE_MODES);
73
- const artifactScopeSchema = z.enum(ARTIFACT_SCOPES);
74
- const decodeCompressionSchema = z.enum(DECODE_COMPRESSIONS);
75
- const encodeCompressionSchema = z.enum(ENCODE_COMPRESSIONS);
76
- const resolveArtifactTargetSchema = z.object({
77
- kind: targetKindSchema,
78
- value: nonEmptyString
79
- });
80
- const sourceLookupTargetSchema = z.discriminatedUnion("type", [
81
- z.object({
82
- type: z.literal("artifact"),
83
- artifactId: nonEmptyString
84
- }),
85
- z.object({
86
- type: z.literal("resolve"),
87
- kind: targetKindSchema,
88
- value: nonEmptyString
89
- })
90
- ]);
91
- const RESOLVE_ARTIFACT_TARGET_DESCRIPTION = "Object with kind and value. Example: {\"kind\":\"version\",\"value\":\"1.21.10\"}. Must be an object, not a string.";
92
- const SOURCE_LOOKUP_TARGET_DESCRIPTION = "Object: {\"type\":\"resolve\",\"kind\":\"version\",\"value\":\"1.21.10\"} or {\"type\":\"artifact\",\"artifactId\":\"...\"}. Must be an object, not a string.";
93
- const SOURCE_SCOPE_DESCRIPTION = "vanilla = Mojang client jar only; merged = source-oriented merged runtime discovery; loader = loader/runtime artifact discovery when the workspace exposes transformed runtime jars.";
94
- const SUGGESTED_CALL_DEFAULTS = {
95
- allowDecompile: true,
96
- preferProjectVersion: false,
97
- strictVersion: false,
98
- mode: "metadata",
99
- access: "public",
100
- includeSynthetic: false,
101
- includeInherited: false,
102
- hideUncertain: false,
103
- explain: false,
104
- preferProjectMapping: false,
105
- minSeverity: "all",
106
- reportMode: "full",
107
- treatInfoAsWarning: true,
108
- includeIssues: true
109
- };
110
- function isSuggestedCallDefault(field, value) {
111
- return value === SUGGESTED_CALL_DEFAULTS[field];
112
- }
113
- const ANALYZE_MOD_INCLUDE_GROUPS = ["warnings", "files", "source", "samples", "timings"];
114
- const ANALYZE_MOD_LEGACY_METADATA_INCLUDES = ["metadata", "entrypoints", "mixins", "dependencies"];
115
- const VALIDATE_PROJECT_INCLUDE_GROUPS = ["warnings", "issues", "workspace", "recovery"];
116
- const VALIDATE_PROJECT_LEGACY_WORKSPACE_INCLUDES = ["detectedConfig", "mixins", "accessWideners"];
117
- const listVersionsShape = {
118
- includeSnapshots: z.boolean().default(false),
119
- limit: optionalPositiveInt.default(20).describe("max 200")
120
- };
121
- const listVersionsSchema = z.object(listVersionsShape);
122
- const resolveArtifactShape = {
123
- target: z.object({
124
- kind: targetKindSchema,
125
- value: nonEmptyString
126
- }).describe(RESOLVE_ARTIFACT_TARGET_DESCRIPTION),
127
- mapping: sourceMappingSchema.optional().describe("obfuscated | mojang | intermediary | yarn"),
128
- sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
129
- allowDecompile: z.boolean().default(true),
130
- projectPath: optionalNonEmptyString.describe("Optional workspace root path for Loom cache-assisted source resolution"),
131
- scope: artifactScopeSchema.optional().describe(SOURCE_SCOPE_DESCRIPTION),
132
- preferProjectVersion: z.boolean().optional().describe("When true, detect MC version from gradle.properties and override target.value"),
133
- strictVersion: z.boolean().optional().describe("When true, reject version-approximated results instead of returning them. Default false."),
134
- compact: z.boolean().default(false).describe("When true, return minimal fields (artifactId, origin, isDecompiled, version, requestedMapping, mappingApplied, qualityFlags). "
135
- + "Omit provenance, artifactContents, sampleEntries, adjacentSourceCandidates, binaryJarPath, coordinate, repoUrl, resolvedSourceJarPath.")
136
- };
137
- const resolveArtifactSchema = z.object(resolveArtifactShape);
138
- const getClassSourceShape = {
139
- className: nonEmptyString,
140
- mode: sourceModeSchema.default("metadata").describe("metadata = symbol outline only; snippet = source with default maxLines=200; full = entire source"),
141
- target: sourceLookupTargetSchema.describe(SOURCE_LOOKUP_TARGET_DESCRIPTION),
142
- mapping: sourceMappingSchema.optional().describe("obfuscated | mojang | intermediary | yarn"),
143
- sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
144
- allowDecompile: z.boolean().default(true),
145
- projectPath: optionalNonEmptyString.describe("Optional workspace root path for Loom cache-assisted source resolution"),
146
- scope: artifactScopeSchema.optional().describe(SOURCE_SCOPE_DESCRIPTION),
147
- preferProjectVersion: z.boolean().optional().describe("When true, detect MC version from gradle.properties and override target.value"),
148
- strictVersion: z.boolean().optional().describe("When true, reject version-approximated results instead of returning them. Default false."),
149
- startLine: optionalPositiveInt,
150
- endLine: optionalPositiveInt,
151
- maxLines: optionalPositiveInt,
152
- maxChars: optionalPositiveInt.describe("Hard character limit on sourceText; truncates if exceeded"),
153
- outputFile: optionalNonEmptyString.describe("Write source to this file path and return metadata-only response")
154
- };
155
- const getClassSourceSchema = z
156
- .object(getClassSourceShape)
157
- .superRefine((value, ctx) => {
158
- if (value.startLine !== undefined &&
159
- value.endLine !== undefined &&
160
- value.startLine > value.endLine) {
161
- ctx.addIssue({
162
- code: z.ZodIssueCode.custom,
163
- message: "startLine must be less than or equal to endLine.",
164
- path: ["startLine"]
165
- });
166
- }
167
- });
168
- const getClassMembersShape = {
169
- className: nonEmptyString,
170
- target: sourceLookupTargetSchema.describe(SOURCE_LOOKUP_TARGET_DESCRIPTION),
171
- mapping: sourceMappingSchema.optional().describe("obfuscated | mojang | intermediary | yarn (default obfuscated)"),
172
- sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
173
- allowDecompile: z.boolean().default(true),
174
- access: memberAccessSchema.default("public").describe("public | all"),
175
- includeSynthetic: z.boolean().default(false),
176
- includeInherited: z.boolean().default(false),
177
- memberPattern: optionalNonEmptyString,
178
- maxMembers: optionalPositiveInt.describe("default 500, max 5000"),
179
- projectPath: optionalNonEmptyString,
180
- scope: artifactScopeSchema.optional().describe(SOURCE_SCOPE_DESCRIPTION),
181
- preferProjectVersion: z.boolean().optional().describe("When true, detect MC version from gradle.properties and override version"),
182
- strictVersion: z.boolean().optional().describe("When true, reject version-approximated results instead of returning them. Default false.")
183
- };
184
- const getClassMembersSchema = z.object(getClassMembersShape);
185
- const searchClassSourceShape = {
186
- artifactId: nonEmptyString,
187
- query: nonEmptyString,
188
- intent: searchIntentSchema.optional().describe("symbol | text | path"),
189
- match: searchMatchSchema.optional().describe("exact | prefix | contains | regex"),
190
- packagePrefix: optionalNonEmptyString,
191
- fileGlob: optionalNonEmptyString,
192
- symbolKind: searchSymbolKindSchema.optional().describe("class | interface | enum | record | method | field"),
193
- queryMode: z.enum(["auto", "token", "literal"]).default("auto").describe("auto: indexed search, including separator queries like foo.bar; token: indexed-only; literal: explicit substring scan only"),
194
- limit: optionalPositiveInt.default(20),
195
- cursor: optionalNonEmptyString
196
- };
197
- const searchClassSourceSchema = z.object(searchClassSourceShape).superRefine((value, ctx) => {
198
- if (value.symbolKind && value.intent && value.intent !== "symbol") {
199
- ctx.addIssue({
200
- code: z.ZodIssueCode.custom,
201
- path: ["symbolKind"],
202
- message: 'symbolKind filter is only supported when intent="symbol".'
203
- });
204
- }
205
- });
206
- const getArtifactFileShape = {
207
- artifactId: nonEmptyString,
208
- filePath: nonEmptyString,
209
- maxBytes: optionalPositiveInt
210
- };
211
- const getArtifactFileSchema = z.object(getArtifactFileShape);
212
- const listArtifactFilesShape = {
213
- artifactId: nonEmptyString,
214
- prefix: optionalNonEmptyString,
215
- limit: optionalPositiveInt,
216
- cursor: optionalNonEmptyString
217
- };
218
- const listArtifactFilesSchema = z.object(listArtifactFilesShape);
219
- const traceSymbolLifecycleShape = {
220
- symbol: nonEmptyString.describe("fully.qualified.Class.method"),
221
- descriptor: optionalNonEmptyString.describe('optional JVM descriptor, e.g. "(I)V"'),
222
- fromVersion: optionalNonEmptyString,
223
- toVersion: optionalNonEmptyString,
224
- mapping: sourceMappingSchema.optional().describe("obfuscated | mojang | intermediary | yarn (default obfuscated)"),
225
- sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
226
- includeSnapshots: z.boolean().default(false),
227
- maxVersions: optionalPositiveInt.default(120).describe("max 400"),
228
- includeTimeline: z.boolean().default(false)
229
- };
230
- const traceSymbolLifecycleSchema = z.object(traceSymbolLifecycleShape);
231
- const diffClassSignaturesShape = {
232
- className: nonEmptyString,
233
- fromVersion: nonEmptyString,
234
- toVersion: nonEmptyString,
235
- mapping: sourceMappingSchema.optional().describe("obfuscated | mojang | intermediary | yarn (default obfuscated)"),
236
- sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
237
- includeFullDiff: z.boolean().default(true).describe("When false, omit from/to snapshots from modified entries and keep only key+changed")
238
- };
239
- const diffClassSignaturesSchema = z.object(diffClassSignaturesShape);
240
- const findMappingShape = {
241
- version: nonEmptyString,
242
- kind: workspaceSymbolKindSchema.describe("class | field | method"),
243
- name: nonEmptyString,
244
- owner: optionalNonEmptyString,
245
- descriptor: optionalNonEmptyString,
246
- sourceMapping: sourceMappingSchema.describe("obfuscated | mojang | intermediary | yarn"),
247
- targetMapping: sourceMappingSchema.describe("obfuscated | mojang | intermediary | yarn"),
248
- sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
249
- disambiguation: z
250
- .object({
251
- ownerHint: optionalNonEmptyString,
252
- descriptorHint: optionalNonEmptyString
253
- })
254
- .partial()
255
- .optional(),
256
- maxCandidates: optionalPositiveInt.default(200).describe("Limit returned candidates (max 200)"),
257
- compact: z.boolean().default(false).describe("When true, omit top-level empty arrays, null/undefined values, and empty objects from the response. "
258
- + "Also omit redundant candidates array for single full-confidence exact-match resolutions.")
259
- };
260
- const findMappingSchema = z.object(findMappingShape).superRefine((value, ctx) => {
261
- if (value.kind === "class") {
262
- if (value.owner) {
263
- ctx.addIssue({
264
- code: z.ZodIssueCode.custom,
265
- message: "owner is not allowed when kind=class.",
266
- path: ["owner"]
267
- });
268
- }
269
- if (value.descriptor) {
270
- ctx.addIssue({
271
- code: z.ZodIssueCode.custom,
272
- message: "descriptor is not allowed when kind=class.",
273
- path: ["descriptor"]
274
- });
275
- }
276
- if (value.sourceMapping !== "obfuscated" && !value.name.includes(".")) {
277
- ctx.addIssue({
278
- code: z.ZodIssueCode.custom,
279
- message: "name must be fully-qualified class name when kind=class.",
280
- path: ["name"]
281
- });
282
- }
283
- return;
284
- }
285
- if (!value.owner) {
286
- ctx.addIssue({
287
- code: z.ZodIssueCode.custom,
288
- message: "owner is required when kind is field or method.",
289
- path: ["owner"]
290
- });
291
- }
292
- if (/[\s./()]/.test(value.name)) {
293
- ctx.addIssue({
294
- code: z.ZodIssueCode.custom,
295
- message: "name must be a simple member name when kind is field or method.",
296
- path: ["name"]
297
- });
298
- }
299
- if (value.kind === "field") {
300
- if (value.descriptor) {
301
- ctx.addIssue({
302
- code: z.ZodIssueCode.custom,
303
- message: "descriptor is not allowed when kind=field.",
304
- path: ["descriptor"]
305
- });
306
- }
307
- return;
308
- }
309
- if (!value.descriptor) {
310
- ctx.addIssue({
311
- code: z.ZodIssueCode.custom,
312
- message: "descriptor is required when kind=method.",
313
- path: ["descriptor"]
314
- });
315
- }
316
- });
317
- const resolveMethodMappingExactShape = {
318
- version: nonEmptyString,
319
- name: nonEmptyString,
320
- owner: nonEmptyString,
321
- descriptor: nonEmptyString.describe("required JVM descriptor"),
322
- sourceMapping: sourceMappingSchema.describe("obfuscated | mojang | intermediary | yarn"),
323
- targetMapping: sourceMappingSchema.describe("obfuscated | mojang | intermediary | yarn"),
324
- sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
325
- maxCandidates: optionalPositiveInt.default(200).describe("Limit returned candidates (max 200)"),
326
- compact: z.boolean().default(false).describe("When true, omit top-level empty arrays, null/undefined values, and empty objects from the response. "
327
- + "Also omit redundant candidates array for single full-confidence exact-match resolutions.")
328
- };
329
- const resolveMethodMappingExactSchema = z
330
- .object(resolveMethodMappingExactShape)
331
- .superRefine((value, ctx) => {
332
- if (/[\s./()]/.test(value.name)) {
333
- ctx.addIssue({
334
- code: z.ZodIssueCode.custom,
335
- message: "name must be a simple method name.",
336
- path: ["name"]
337
- });
338
- }
339
- });
340
- const classApiKindsSchema = z.string().superRefine((value, ctx) => {
341
- const tokens = value
342
- .split(",")
343
- .map((entry) => entry.trim().toLowerCase())
344
- .filter((entry) => entry.length > 0);
345
- if (tokens.length === 0) {
346
- ctx.addIssue({
347
- code: z.ZodIssueCode.custom,
348
- message: "includeKinds must include at least one of class, field, method."
349
- });
350
- return;
351
- }
352
- const invalidTokens = tokens.filter((entry) => entry !== "class" && entry !== "field" && entry !== "method");
353
- if (invalidTokens.length > 0) {
354
- ctx.addIssue({
355
- code: z.ZodIssueCode.custom,
356
- message: `includeKinds contains invalid values: ${invalidTokens.join(", ")}. Allowed values are class, field, method.`
357
- });
358
- }
359
- });
360
- const getClassApiMatrixShape = {
361
- version: nonEmptyString,
362
- className: nonEmptyString,
363
- classNameMapping: sourceMappingSchema.describe("obfuscated | mojang | intermediary | yarn"),
364
- includeKinds: classApiKindsSchema.optional().describe("comma-separated: class,field,method"),
365
- sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
366
- maxRows: optionalPositiveInt.describe("Limit returned rows (max 5000)")
367
- };
368
- const getClassApiMatrixSchema = z.object(getClassApiMatrixShape);
369
- const resolveWorkspaceSymbolShape = {
370
- projectPath: nonEmptyString,
371
- version: nonEmptyString,
372
- kind: workspaceSymbolKindSchema.describe("class | field | method"),
373
- name: nonEmptyString,
374
- owner: optionalNonEmptyString,
375
- descriptor: optionalNonEmptyString,
376
- sourceMapping: sourceMappingSchema.describe("obfuscated | mojang | intermediary | yarn"),
377
- sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
378
- maxCandidates: optionalPositiveInt.default(200).describe("Limit returned candidates for field/method lookups (max 200)"),
379
- compact: z.boolean().default(false).describe("When true, omit top-level empty arrays, null/undefined values, and empty objects from the response. "
380
- + "Also omit redundant candidates array for single full-confidence exact-match resolutions.")
381
- };
382
- const resolveWorkspaceSymbolSchema = z
383
- .object(resolveWorkspaceSymbolShape)
384
- .superRefine((value, ctx) => {
385
- if (value.kind === "class") {
386
- if (value.owner) {
387
- ctx.addIssue({
388
- code: z.ZodIssueCode.custom,
389
- message: "owner is not allowed when kind=class.",
390
- path: ["owner"]
391
- });
392
- }
393
- if (value.descriptor) {
394
- ctx.addIssue({
395
- code: z.ZodIssueCode.custom,
396
- message: "descriptor is not allowed when kind=class.",
397
- path: ["descriptor"]
398
- });
399
- }
400
- if (!value.name.includes(".")) {
401
- ctx.addIssue({
402
- code: z.ZodIssueCode.custom,
403
- message: "name must be fully-qualified class name when kind=class.",
404
- path: ["name"]
405
- });
406
- }
407
- return;
408
- }
409
- if (!value.owner) {
410
- ctx.addIssue({
411
- code: z.ZodIssueCode.custom,
412
- message: "owner is required when kind is field or method.",
413
- path: ["owner"]
414
- });
415
- }
416
- if (/[\s./()]/.test(value.name)) {
417
- ctx.addIssue({
418
- code: z.ZodIssueCode.custom,
419
- message: "name must be a simple member name when kind is field or method.",
420
- path: ["name"]
421
- });
422
- }
423
- if (value.kind === "field") {
424
- if (value.descriptor) {
425
- ctx.addIssue({
426
- code: z.ZodIssueCode.custom,
427
- message: "descriptor is not allowed when kind=field.",
428
- path: ["descriptor"]
429
- });
430
- }
431
- return;
432
- }
433
- if (!value.descriptor) {
434
- ctx.addIssue({
435
- code: z.ZodIssueCode.custom,
436
- message: "descriptor is required when kind=method.",
437
- path: ["descriptor"]
438
- });
439
- }
440
- });
441
- const checkSymbolExistsShape = {
442
- version: nonEmptyString,
443
- kind: workspaceSymbolKindSchema.describe("class | field | method"),
444
- owner: optionalNonEmptyString,
445
- name: nonEmptyString,
446
- descriptor: optionalNonEmptyString.describe("required for kind=method unless signatureMode=name-only"),
447
- sourceMapping: sourceMappingSchema.describe("obfuscated | mojang | intermediary | yarn"),
448
- sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
449
- nameMode: classNameModeSchema.default("fqcn").describe("fqcn | auto"),
450
- signatureMode: z.enum(["exact", "name-only"]).default("exact")
451
- .describe("exact: require descriptor for methods; name-only: match by owner+name only"),
452
- maxCandidates: optionalPositiveInt.default(200).describe("Limit returned candidates (max 200)"),
453
- compact: z.boolean().default(false).describe("When true, omit top-level empty arrays, null/undefined values, and empty objects from the response. "
454
- + "Also omit redundant candidates array for single full-confidence exact-match resolutions.")
455
- };
456
- const checkSymbolExistsSchema = z.object(checkSymbolExistsShape).superRefine((value, ctx) => {
457
- if (value.kind === "class") {
458
- if (value.owner) {
459
- ctx.addIssue({
460
- code: z.ZodIssueCode.custom,
461
- message: "owner is not allowed when kind=class.",
462
- path: ["owner"]
463
- });
464
- }
465
- if (value.descriptor) {
466
- ctx.addIssue({
467
- code: z.ZodIssueCode.custom,
468
- message: "descriptor is not allowed when kind=class.",
469
- path: ["descriptor"]
470
- });
471
- }
472
- if (value.nameMode !== "auto" && !value.name.includes(".")) {
473
- ctx.addIssue({
474
- code: z.ZodIssueCode.custom,
475
- message: "name must be fully-qualified class name when kind=class.",
476
- path: ["name"]
477
- });
478
- }
479
- return;
480
- }
481
- if (!value.owner) {
482
- ctx.addIssue({
483
- code: z.ZodIssueCode.custom,
484
- message: "owner is required when kind is field or method.",
485
- path: ["owner"]
486
- });
487
- }
488
- if (/[\s./()]/.test(value.name)) {
489
- ctx.addIssue({
490
- code: z.ZodIssueCode.custom,
491
- message: "name must be a simple member name when kind is field or method.",
492
- path: ["name"]
493
- });
494
- }
495
- if (value.kind === "field") {
496
- if (value.descriptor) {
497
- ctx.addIssue({
498
- code: z.ZodIssueCode.custom,
499
- message: "descriptor is not allowed when kind=field.",
500
- path: ["descriptor"]
501
- });
502
- }
503
- return;
504
- }
505
- if (!value.descriptor && value.signatureMode !== "name-only") {
506
- ctx.addIssue({
507
- code: z.ZodIssueCode.custom,
508
- message: "descriptor is required when kind=method (use signatureMode='name-only' to match by name only).",
509
- path: ["descriptor"]
510
- });
511
- }
512
- });
513
- const nbtToJsonShape = {
514
- nbtBase64: nonEmptyString,
515
- compression: decodeCompressionSchema.default("auto").describe("none | gzip | auto")
516
- };
517
- const nbtToJsonSchema = z.object(nbtToJsonShape);
518
- const nbtPatchOperationSchema = z
519
- .object({
520
- op: z.enum(["add", "remove", "replace", "test"]),
521
- path: nonEmptyString,
522
- value: z.unknown().optional()
523
- })
524
- .passthrough();
525
- const nbtApplyJsonPatchShape = {
526
- typedJson: z.unknown(),
527
- patch: z.array(nbtPatchOperationSchema).describe("RFC6902 operation array (add/remove/replace/test)")
528
- };
529
- const nbtApplyJsonPatchSchema = z.object(nbtApplyJsonPatchShape);
530
- const jsonToNbtShape = {
531
- typedJson: z.unknown(),
532
- compression: encodeCompressionSchema.default("none").describe("none | gzip")
533
- };
534
- const jsonToNbtSchema = z.object(jsonToNbtShape);
535
- const indexArtifactShape = {
536
- artifactId: nonEmptyString,
537
- force: z.boolean().default(false)
538
- };
539
- const indexArtifactSchema = z.object(indexArtifactShape);
540
- const validateMixinShape = {
541
- input: z.discriminatedUnion("mode", [
542
- z.object({
543
- mode: z.literal("inline"),
544
- source: nonEmptyString.describe("Mixin Java source text")
545
- }),
546
- z.object({
547
- mode: z.literal("path"),
548
- path: nonEmptyString.describe("Path to a Mixin .java file")
549
- }),
550
- z.object({
551
- mode: z.literal("paths"),
552
- paths: z.array(nonEmptyString).min(1).describe("Array of Mixin .java file paths for batch validation")
553
- }),
554
- z.object({
555
- mode: z.literal("config"),
556
- configPaths: z.array(nonEmptyString).min(1).describe("Path array to mixin config JSON files (e.g. modid.mixins.json)")
557
- }),
558
- z.object({
559
- mode: z.literal("project"),
560
- path: nonEmptyString.describe("Workspace root path used to discover *.mixins.json files automatically")
561
- })
562
- ]).describe("One of { mode: 'inline', source }, { mode: 'path', path }, { mode: 'paths', paths[] }, { mode: 'config', configPaths[] }, or { mode: 'project', path }."),
563
- sourceRoots: z.array(z.string().min(1)).optional()
564
- .describe("Array of source roots for multi-module projects (e.g. ['common/src/main/java', 'neoforge/src/main/java'])"),
565
- version: nonEmptyString.describe("Minecraft version"),
566
- mapping: sourceMappingSchema.optional().describe("obfuscated | mojang | intermediary | yarn"),
567
- sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
568
- scope: artifactScopeSchema.optional().describe(SOURCE_SCOPE_DESCRIPTION),
569
- projectPath: optionalNonEmptyString.describe("Optional workspace root path for Loom cache-assisted source resolution"),
570
- preferProjectVersion: z.boolean().optional().describe("When true, detect MC version from gradle.properties and override version"),
571
- minSeverity: z.enum(["error", "warning", "all"]).default("all")
572
- .describe("'error'=errors only, 'warning'=errors+warnings, 'all'=everything"),
573
- hideUncertain: z.boolean().default(false)
574
- .describe("Omit issues with confidence='uncertain'"),
575
- explain: z.boolean().default(false)
576
- .describe("When true, enrich each issue with explanation and suggestedCall for agent recovery"),
577
- warningMode: z.enum(["full", "aggregated"]).optional()
578
- .describe("'full'=all warnings; 'aggregated'=group warnings by category with counts and samples. Single validation uses the provided value as-is; batch validation defaults to 'aggregated'"),
579
- preferProjectMapping: z.boolean().default(false)
580
- .describe("When true, auto-detect mapping from project config even if mapping is explicitly provided"),
581
- reportMode: z.enum(["compact", "full", "summary-first"]).default("full")
582
- .describe("'compact' omits heavy per-result detail, 'summary-first' hoists shared provenance/warnings/incomplete reasons, 'full'=everything"),
583
- warningCategoryFilter: z.array(z.enum(["mapping", "configuration", "validation", "resolution", "parse"])).optional()
584
- .describe("Only include warnings/issues matching these categories (default: all)"),
585
- treatInfoAsWarning: z.boolean().default(true)
586
- .describe("When false, suppress info-severity structured warnings from output"),
587
- includeIssues: z.boolean().default(true)
588
- .describe("When false, keep summary fields but omit per-result issues[] payloads")
589
- };
590
- const validateMixinSchema = z.object(validateMixinShape);
591
- const validateAccessWidenerShape = {
592
- content: nonEmptyString.describe("Access Widener file content"),
593
- version: nonEmptyString.describe("Minecraft version"),
594
- mapping: sourceMappingSchema.optional().describe("obfuscated | mojang | intermediary | yarn"),
595
- sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
596
- projectPath: optionalNonEmptyString.describe("Optional workspace root path for Loom cache-assisted runtime validation"),
597
- scope: artifactScopeSchema.optional().describe(SOURCE_SCOPE_DESCRIPTION),
598
- preferProjectVersion: z.boolean().default(false)
599
- .describe("When true, detect MC version from gradle.properties and override version")
600
- };
601
- const validateAccessWidenerSchema = z.object(validateAccessWidenerShape);
602
- const validateAccessTransformerShape = {
603
- content: nonEmptyString.describe("Access Transformer file content"),
604
- version: nonEmptyString.describe("Minecraft version"),
605
- atNamespace: z.enum(["srg", "mojang", "obfuscated"]).optional().describe("srg | mojang | obfuscated"),
606
- sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
607
- projectPath: optionalNonEmptyString.describe("Optional workspace root path for Forge/NeoForge runtime validation"),
608
- scope: artifactScopeSchema.optional().describe(SOURCE_SCOPE_DESCRIPTION),
609
- preferProjectVersion: z.boolean().default(false)
610
- .describe("When true, detect MC version from gradle.properties and override version")
611
- };
612
- const validateAccessTransformerSchema = z.object(validateAccessTransformerShape);
613
- const analyzeModJarShape = {
614
- jarPath: nonEmptyString.describe("Local path to the mod JAR file"),
615
- includeClasses: z.boolean().default(false).describe("Include full class listing")
616
- };
617
- const analyzeModJarSchema = z.object(analyzeModJarShape);
618
- const getRegistryDataShape = {
619
- version: nonEmptyString.describe("Minecraft version (e.g. 1.21)"),
620
- registry: optionalNonEmptyString.describe('Optional registry name (e.g. "block", "item", "minecraft:biome"). Omit to list all registries.'),
621
- includeData: z.boolean().default(true).describe("When false, return registry names/counts without full entry bodies"),
622
- maxEntriesPerRegistry: optionalPositiveInt.describe("Limit returned entries per registry body")
623
- };
624
- const getRegistryDataSchema = z.object(getRegistryDataShape);
625
- const COMPARE_VERSIONS_CATEGORIES = ["classes", "registry", "all"];
626
- const compareVersionsCategorySchema = z.enum(COMPARE_VERSIONS_CATEGORIES);
627
- const compareVersionsShape = {
628
- fromVersion: nonEmptyString.describe("Older Minecraft version (e.g. 1.20.4)"),
629
- toVersion: nonEmptyString.describe("Newer Minecraft version (e.g. 1.21)"),
630
- category: compareVersionsCategorySchema.default("all").describe("classes | registry | all"),
631
- packageFilter: optionalNonEmptyString.describe("Filter classes to a package prefix (e.g. net.minecraft.world.item)"),
632
- maxClassResults: optionalPositiveInt.default(500).describe("Max class results per direction (max 5000)")
633
- };
634
- const compareVersionsSchema = z.object(compareVersionsShape);
635
- const decompileModJarShape = {
636
- jarPath: nonEmptyString.describe("Local path to the mod JAR file"),
637
- className: optionalNonEmptyString.describe("Optional fully-qualified class name to view source. Omit to list all classes."),
638
- includeFiles: z.boolean().default(true).describe("When false, omit the full class list and return counts only"),
639
- maxFiles: optionalPositiveInt.describe("Limit returned class names when files are included")
640
- };
641
- const decompileModJarSchema = z.object(decompileModJarShape);
642
- const getModClassSourceShape = {
643
- jarPath: nonEmptyString.describe("Local path to the mod JAR file"),
644
- className: nonEmptyString.describe("Fully-qualified class name (e.g. com.example.MyMixin)"),
645
- maxLines: optionalPositiveInt.describe("Max lines to return"),
646
- maxChars: optionalPositiveInt.describe("Hard character limit; truncates if exceeded"),
647
- outputFile: optionalNonEmptyString.describe("Write full source to file, return placeholder in content")
648
- };
649
- const getModClassSourceSchema = z.object(getModClassSourceShape);
650
- const MOD_SEARCH_TYPES = ["class", "method", "field", "content", "all"];
651
- const modSearchTypeSchema = z.enum(MOD_SEARCH_TYPES);
652
- const searchModSourceShape = {
653
- jarPath: nonEmptyString.describe("Local path to the mod JAR file"),
654
- query: nonEmptyString.describe("Search pattern (regex or literal string)"),
655
- searchType: modSearchTypeSchema.default("all").describe("class | method | field | content | all"),
656
- limit: optionalPositiveInt.default(50).describe("Max results (max 200)")
657
- };
658
- const searchModSourceSchema = z.object(searchModSourceShape);
659
- const REMAP_TARGETS = ["yarn", "mojang"];
660
- const remapTargetSchema = z.enum(REMAP_TARGETS);
661
- const remapModJarShape = {
662
- inputJar: nonEmptyString.describe("Path to the mod JAR file"),
663
- outputJar: optionalNonEmptyString.describe("Output path for remapped JAR (auto-generated if omitted)"),
664
- mcVersion: optionalNonEmptyString.describe("Minecraft version (auto-detected from mod metadata if omitted)"),
665
- targetMapping: remapTargetSchema.describe("yarn | mojang")
666
- };
667
- const remapModJarSchema = z.object(remapModJarShape);
668
- const emptySchema = z.object({}).passthrough();
669
59
  function getServerVersionFromPackageJson() {
670
60
  try {
671
61
  const packageJsonUrl = new URL("../package.json", import.meta.url);
@@ -745,7 +135,22 @@ const validateProjectService = new ValidateProjectService({
745
135
  discoverMixins: discoverWorkspaceMixins,
746
136
  discoverAccessWideners: discoverWorkspaceAccessWideners,
747
137
  discoverAccessTransformers: discoverWorkspaceAccessTransformers,
748
- detectProjectMinecraftVersion: (projectPath) => workspaceMappingService.detectProjectMinecraftVersion(projectPath)
138
+ detectProjectMinecraftVersion: (projectPath) => workspaceMappingService.detectProjectMinecraftVersion(projectPath),
139
+ resolveArtifact: async (input) => {
140
+ const output = await sourceService.resolveArtifact({
141
+ target: input.target,
142
+ mapping: input.mapping,
143
+ sourcePriority: input.sourcePriority,
144
+ projectPath: input.projectPath,
145
+ scope: input.scope,
146
+ preferProjectVersion: input.preferProjectVersion
147
+ });
148
+ return {
149
+ artifactId: output.artifactId,
150
+ mappingApplied: output.mappingApplied,
151
+ warnings: output.warnings
152
+ };
153
+ }
749
154
  });
750
155
  const manageCacheService = new ManageCacheService({
751
156
  registry: createCacheRegistry({
@@ -753,6 +158,42 @@ const manageCacheService = new ManageCacheService({
753
158
  sqlitePath: config.sqlitePath
754
159
  })
755
160
  });
161
+ const verifyMixinTargetService = new VerifyMixinTargetService({
162
+ resolveArtifact: async (input) => {
163
+ const output = await sourceService.resolveArtifact({
164
+ target: input.target,
165
+ mapping: input.mapping,
166
+ sourcePriority: input.sourcePriority,
167
+ projectPath: input.projectPath,
168
+ scope: input.scope,
169
+ preferProjectVersion: input.preferProjectVersion,
170
+ strictVersion: input.strictVersion
171
+ });
172
+ return {
173
+ artifactId: output.artifactId,
174
+ mappingApplied: output.mappingApplied,
175
+ binaryJarPath: output.binaryJarPath,
176
+ provenance: output.provenance,
177
+ warnings: output.warnings
178
+ };
179
+ },
180
+ getSignature: (input) => sourceService.explorerService.getSignature(input)
181
+ });
182
+ const batchClassSourceService = new BatchClassSourceService({
183
+ resolveArtifact: (input) => sourceService.resolveArtifact(input),
184
+ getClassSource: (input) => sourceService.getClassSource(input)
185
+ });
186
+ const batchClassMembersService = new BatchClassMembersService({
187
+ resolveArtifact: (input) => sourceService.resolveArtifact(input),
188
+ getClassMembers: (input) => sourceService.getClassMembers(input)
189
+ });
190
+ const batchSymbolExistsService = new BatchSymbolExistsService({
191
+ resolveArtifact: (input) => sourceService.resolveArtifact(input),
192
+ checkSymbolExists: (input) => sourceService.checkSymbolExists(input)
193
+ });
194
+ const batchMappingsService = new BatchMappingsService({
195
+ findMapping: (input) => sourceService.findMapping(input)
196
+ });
756
197
  registerResources(server, sourceService);
757
198
  let processHandlersAttached = false;
758
199
  let serverStarted = false;
@@ -785,12 +226,8 @@ function normalizeSourceLookupTarget(target) {
785
226
  if (target.type === "artifact") {
786
227
  return { artifactId: target.artifactId };
787
228
  }
788
- return {
789
- target: {
790
- kind: target.kind,
791
- value: target.value
792
- }
793
- };
229
+ const { type: _type, ...rest } = target;
230
+ return { target: rest };
794
231
  }
795
232
  function parseClassApiKinds(value) {
796
233
  if (value == null) {
@@ -805,674 +242,6 @@ function parseClassApiKinds(value) {
805
242
  }
806
243
  return [...new Set(normalized)];
807
244
  }
808
- function toFieldErrorsFromZod(error) {
809
- return error.issues.map((issue) => ({
810
- path: issue.path.join(".") || "$",
811
- message: issue.message,
812
- code: issue.code
813
- }));
814
- }
815
- function toHints(details) {
816
- if (typeof details !== "object" || details == null) {
817
- return undefined;
818
- }
819
- const hints = [];
820
- const maybeNextAction = details.nextAction;
821
- if (typeof maybeNextAction === "string" && maybeNextAction.trim()) {
822
- hints.push(maybeNextAction.trim());
823
- }
824
- if (hints.length === 0) {
825
- return undefined;
826
- }
827
- return hints;
828
- }
829
- function toSuggestedCall(details) {
830
- if (typeof details !== "object" || details == null) {
831
- return undefined;
832
- }
833
- const maybe = details.suggestedCall;
834
- if (typeof maybe !== "object" || maybe == null) {
835
- return undefined;
836
- }
837
- const call = maybe;
838
- if (typeof call.tool !== "string" || typeof call.params !== "object" || call.params == null) {
839
- return undefined;
840
- }
841
- return { tool: call.tool, params: call.params };
842
- }
843
- function statusForErrorCode(code) {
844
- if (code === ERROR_CODES.INVALID_INPUT ||
845
- code === ERROR_CODES.COORDINATE_PARSE_FAILED ||
846
- code === ERROR_CODES.INVALID_LINE_RANGE ||
847
- code === ERROR_CODES.NBT_PARSE_FAILED ||
848
- code === ERROR_CODES.NBT_INVALID_TYPED_JSON ||
849
- code === ERROR_CODES.JSON_PATCH_INVALID ||
850
- code === ERROR_CODES.NBT_ENCODE_FAILED ||
851
- code === ERROR_CODES.NBT_UNSUPPORTED_FEATURE) {
852
- return 400;
853
- }
854
- if (code === ERROR_CODES.JSON_PATCH_CONFLICT || code === ERROR_CODES.CONTEXT_UNRESOLVED) {
855
- return 409;
856
- }
857
- if (code === ERROR_CODES.SOURCE_NOT_FOUND ||
858
- code === ERROR_CODES.FILE_NOT_FOUND ||
859
- code === ERROR_CODES.JAR_NOT_FOUND ||
860
- code === ERROR_CODES.VERSION_NOT_FOUND ||
861
- code === ERROR_CODES.CLASS_NOT_FOUND) {
862
- return 404;
863
- }
864
- if (code === ERROR_CODES.MAPPING_NOT_APPLIED ||
865
- code === ERROR_CODES.MAPPING_UNAVAILABLE ||
866
- code === ERROR_CODES.NAMESPACE_MISMATCH ||
867
- code === ERROR_CODES.DECOMPILE_DISABLED ||
868
- code === ERROR_CODES.REMAP_FAILED) {
869
- return 422;
870
- }
871
- if (code === ERROR_CODES.REMAPPER_UNAVAILABLE ||
872
- code === ERROR_CODES.JAVA_PROCESS_FAILED) {
873
- return 503;
874
- }
875
- if (code === ERROR_CODES.LIMIT_EXCEEDED) {
876
- return 413;
877
- }
878
- if (code === ERROR_CODES.REPO_FETCH_FAILED) {
879
- return 502;
880
- }
881
- if (code === ERROR_CODES.DECOMPILER_UNAVAILABLE ||
882
- code === ERROR_CODES.DECOMPILER_FAILED ||
883
- code === ERROR_CODES.JAVA_UNAVAILABLE ||
884
- code === ERROR_CODES.REGISTRY_GENERATION_FAILED) {
885
- return 503;
886
- }
887
- return 500;
888
- }
889
- function extractFieldErrorsFromDetails(details) {
890
- if (typeof details !== "object" || details == null) {
891
- return undefined;
892
- }
893
- const maybeFieldErrors = details.fieldErrors;
894
- if (!Array.isArray(maybeFieldErrors)) {
895
- return undefined;
896
- }
897
- const normalized = maybeFieldErrors
898
- .map((entry) => {
899
- if (typeof entry !== "object" || entry == null) {
900
- return undefined;
901
- }
902
- const asRecord = entry;
903
- const path = asRecord.path;
904
- const message = asRecord.message;
905
- const code = asRecord.code;
906
- if (typeof path !== "string" || typeof message !== "string") {
907
- return undefined;
908
- }
909
- return {
910
- path,
911
- message,
912
- code: typeof code === "string" ? code : undefined
913
- };
914
- })
915
- .filter((entry) => entry != null);
916
- return normalized.length > 0 ? normalized : undefined;
917
- }
918
- function asObjectRecord(value) {
919
- return typeof value === "object" && value != null && !Array.isArray(value)
920
- ? value
921
- : undefined;
922
- }
923
- function asNonEmptyString(value) {
924
- return typeof value === "string" && value.trim() ? value : undefined;
925
- }
926
- function asStringArray(value) {
927
- return Array.isArray(value) && value.every((entry) => typeof entry === "string" && entry.trim())
928
- ? value
929
- : undefined;
930
- }
931
- function truncateSuggestionText(value, maxLength = 500) {
932
- return value.length > maxLength
933
- ? `${value.slice(0, maxLength)}...`
934
- : value;
935
- }
936
- function parseJsonObjectString(value) {
937
- if (!value.trim().startsWith("{")) {
938
- return undefined;
939
- }
940
- try {
941
- const parsed = JSON.parse(value);
942
- return asObjectRecord(parsed);
943
- }
944
- catch {
945
- return undefined;
946
- }
947
- }
948
- function inferTargetKindFromString(value) {
949
- if (/[\\/]/.test(value) || /\.jar$/i.test(value)) {
950
- return "jar";
951
- }
952
- if (value.split(":").length >= 3) {
953
- return "coordinate";
954
- }
955
- return "version";
956
- }
957
- function copySourceLookupSuggestionFields(tool, source) {
958
- const result = {};
959
- const stringFields = tool === "get-class-source"
960
- ? ["className", "mode", "mapping", "sourcePriority", "projectPath", "scope", "outputFile"]
961
- : ["className", "mapping", "sourcePriority", "projectPath", "scope", "access", "memberPattern"];
962
- for (const field of stringFields) {
963
- const value = source[field];
964
- if (typeof value === "string" &&
965
- value.trim() &&
966
- (!Object.prototype.hasOwnProperty.call(SUGGESTED_CALL_DEFAULTS, field) ||
967
- !isSuggestedCallDefault(field, value))) {
968
- result[field] = value;
969
- }
970
- }
971
- const numericFields = tool === "get-class-source"
972
- ? ["startLine", "endLine", "maxLines", "maxChars"]
973
- : ["maxMembers"];
974
- for (const field of numericFields) {
975
- const value = source[field];
976
- if (typeof value === "number" && Number.isFinite(value)) {
977
- result[field] = value;
978
- }
979
- }
980
- const booleanFields = tool === "get-class-source"
981
- ? ["allowDecompile", "preferProjectVersion", "strictVersion"]
982
- : ["allowDecompile", "preferProjectVersion", "strictVersion", "includeSynthetic", "includeInherited"];
983
- for (const field of booleanFields) {
984
- const value = source[field];
985
- if (typeof value === "boolean" &&
986
- (!Object.prototype.hasOwnProperty.call(SUGGESTED_CALL_DEFAULTS, field) ||
987
- !isSuggestedCallDefault(field, value))) {
988
- result[field] = value;
989
- }
990
- }
991
- return result;
992
- }
993
- function copyValidateMixinSharedParams(source) {
994
- const result = {};
995
- const stringFields = [
996
- "version",
997
- "mapping",
998
- "sourcePriority",
999
- "scope",
1000
- "projectPath",
1001
- "minSeverity",
1002
- "warningMode",
1003
- "reportMode"
1004
- ];
1005
- for (const field of stringFields) {
1006
- const value = source[field];
1007
- if (typeof value === "string" &&
1008
- value.trim() &&
1009
- (!Object.prototype.hasOwnProperty.call(SUGGESTED_CALL_DEFAULTS, field) ||
1010
- !isSuggestedCallDefault(field, value))) {
1011
- result[field] = value;
1012
- }
1013
- }
1014
- const booleanFields = [
1015
- "preferProjectVersion",
1016
- "hideUncertain",
1017
- "explain",
1018
- "preferProjectMapping",
1019
- "treatInfoAsWarning",
1020
- "includeIssues"
1021
- ];
1022
- for (const field of booleanFields) {
1023
- const value = source[field];
1024
- if (typeof value === "boolean" &&
1025
- (!Object.prototype.hasOwnProperty.call(SUGGESTED_CALL_DEFAULTS, field) ||
1026
- !isSuggestedCallDefault(field, value))) {
1027
- result[field] = value;
1028
- }
1029
- }
1030
- const sourceRoots = asStringArray(source.sourceRoots);
1031
- if (sourceRoots) {
1032
- result.sourceRoots = sourceRoots;
1033
- }
1034
- const warningCategoryFilter = asStringArray(source.warningCategoryFilter);
1035
- if (warningCategoryFilter) {
1036
- result.warningCategoryFilter = warningCategoryFilter;
1037
- }
1038
- return result;
1039
- }
1040
- function buildValidateMixinSuggestedParams(normalizedInput) {
1041
- const record = asObjectRecord(normalizedInput);
1042
- if (!record) {
1043
- return {
1044
- input: {
1045
- mode: "inline",
1046
- source: "<Mixin Java source>"
1047
- },
1048
- version: "<minecraft-version>"
1049
- };
1050
- }
1051
- const inputRecord = asObjectRecord(record.input);
1052
- const shared = copyValidateMixinSharedParams(record);
1053
- const version = asNonEmptyString(record.version) ?? "<minecraft-version>";
1054
- const inlineSource = asNonEmptyString(record.input) ??
1055
- asNonEmptyString(inputRecord?.source) ??
1056
- asNonEmptyString(record.source);
1057
- if (inlineSource) {
1058
- const parsedInlineObject = parseJsonObjectString(inlineSource);
1059
- if (parsedInlineObject && typeof parsedInlineObject.mode === "string") {
1060
- return {
1061
- ...shared,
1062
- input: parsedInlineObject,
1063
- version
1064
- };
1065
- }
1066
- return {
1067
- ...shared,
1068
- input: {
1069
- mode: "inline",
1070
- source: truncateSuggestionText(inlineSource)
1071
- },
1072
- version
1073
- };
1074
- }
1075
- const path = asNonEmptyString(inputRecord?.path) ??
1076
- asNonEmptyString(record.sourcePath);
1077
- if (path) {
1078
- return {
1079
- ...shared,
1080
- input: {
1081
- mode: "path",
1082
- path
1083
- },
1084
- version
1085
- };
1086
- }
1087
- const paths = asStringArray(inputRecord?.paths) ??
1088
- asStringArray(record.sourcePaths);
1089
- if (paths) {
1090
- return {
1091
- ...shared,
1092
- input: {
1093
- mode: "paths",
1094
- paths
1095
- },
1096
- version
1097
- };
1098
- }
1099
- const configPaths = asStringArray(inputRecord?.configPaths) ??
1100
- (asNonEmptyString(record.mixinConfigPath) ? [record.mixinConfigPath] : undefined);
1101
- if (configPaths) {
1102
- return {
1103
- ...shared,
1104
- input: {
1105
- mode: "config",
1106
- configPaths
1107
- },
1108
- version
1109
- };
1110
- }
1111
- const projectPath = asNonEmptyString(record.projectPath) ??
1112
- (inputRecord?.mode === "project" ? asNonEmptyString(inputRecord.path) : undefined);
1113
- if (projectPath) {
1114
- return {
1115
- ...shared,
1116
- input: {
1117
- mode: "project",
1118
- path: projectPath
1119
- },
1120
- version
1121
- };
1122
- }
1123
- return {
1124
- ...shared,
1125
- input: {
1126
- mode: "inline",
1127
- source: "<Mixin Java source>"
1128
- },
1129
- version
1130
- };
1131
- }
1132
- function buildResolveArtifactSuggestedParams(normalizedInput) {
1133
- const record = asObjectRecord(normalizedInput);
1134
- if (!record) {
1135
- return {
1136
- target: {
1137
- kind: "version",
1138
- value: "<minecraft-version>"
1139
- }
1140
- };
1141
- }
1142
- const targetValue = asNonEmptyString(record.target);
1143
- const result = {
1144
- target: targetValue
1145
- ? {
1146
- kind: inferTargetKindFromString(targetValue),
1147
- value: targetValue
1148
- }
1149
- : {
1150
- kind: "version",
1151
- value: "<minecraft-version>"
1152
- }
1153
- };
1154
- const stringFields = ["mapping", "sourcePriority", "projectPath", "scope"];
1155
- for (const field of stringFields) {
1156
- const value = record[field];
1157
- if (typeof value === "string" && value.trim()) {
1158
- result[field] = value;
1159
- }
1160
- }
1161
- const booleanFields = ["allowDecompile", "preferProjectVersion", "strictVersion"];
1162
- for (const field of booleanFields) {
1163
- const value = record[field];
1164
- if (typeof value === "boolean" &&
1165
- !isSuggestedCallDefault(field, value)) {
1166
- result[field] = value;
1167
- }
1168
- }
1169
- return result;
1170
- }
1171
- function buildSourceLookupSuggestedParams(tool, normalizedInput) {
1172
- const record = asObjectRecord(normalizedInput);
1173
- const result = record ? copySourceLookupSuggestionFields(tool, record) : {};
1174
- const targetValue = asNonEmptyString(record?.target);
1175
- result.target = targetValue
1176
- ? {
1177
- type: "resolve",
1178
- kind: inferTargetKindFromString(targetValue),
1179
- value: targetValue
1180
- }
1181
- : {
1182
- type: "resolve",
1183
- kind: "version",
1184
- value: "<minecraft-version>"
1185
- };
1186
- if (!asNonEmptyString(result.className)) {
1187
- result.className = "<fully-qualified-class-name>";
1188
- }
1189
- return result;
1190
- }
1191
- function filterAllowedIncludeValues(values, allowed) {
1192
- if (!values?.length) {
1193
- return [];
1194
- }
1195
- const allowedSet = new Set(allowed);
1196
- const filtered = values.filter((value) => allowedSet.has(value));
1197
- return [...new Set(filtered)];
1198
- }
1199
- function buildAnalyzeModSuggestedParams(normalizedInput) {
1200
- const record = asObjectRecord(normalizedInput);
1201
- if (!record) {
1202
- return {
1203
- task: "summary",
1204
- detail: "standard",
1205
- subject: {
1206
- kind: "jar",
1207
- jarPath: "<mod-jar-path>"
1208
- }
1209
- };
1210
- }
1211
- const task = asNonEmptyString(record.task) ?? "summary";
1212
- const result = { task };
1213
- const subjectRecord = asObjectRecord(record.subject);
1214
- const include = asStringArray(record.include);
1215
- const canonicalInclude = filterAllowedIncludeValues(include, ANALYZE_MOD_INCLUDE_GROUPS);
1216
- const wantsLegacyMetadata = include?.some((value) => ANALYZE_MOD_LEGACY_METADATA_INCLUDES.includes(value)) ?? false;
1217
- const detail = asNonEmptyString(record.detail);
1218
- if (task === "summary" && wantsLegacyMetadata) {
1219
- result.detail = detail && detail !== "summary" ? detail : "standard";
1220
- }
1221
- else if (detail && detail !== "summary") {
1222
- result.detail = detail;
1223
- }
1224
- if (canonicalInclude.length > 0) {
1225
- result.include = canonicalInclude;
1226
- }
1227
- if (task === "class-source") {
1228
- result.subject = {
1229
- kind: "class",
1230
- jarPath: asNonEmptyString(subjectRecord?.jarPath) ?? asNonEmptyString(record.subject) ?? "<mod-jar-path>",
1231
- className: asNonEmptyString(subjectRecord?.className) ?? asNonEmptyString(record.className) ?? "<fully-qualified-class-name>"
1232
- };
1233
- }
1234
- else {
1235
- result.subject = {
1236
- kind: "jar",
1237
- jarPath: asNonEmptyString(subjectRecord?.jarPath) ?? asNonEmptyString(record.subject) ?? asNonEmptyString(record.jarPath) ?? "<mod-jar-path>"
1238
- };
1239
- }
1240
- const stringFields = ["query", "searchType", "targetMapping", "outputJar", "executionMode"];
1241
- for (const field of stringFields) {
1242
- const value = record[field];
1243
- if (typeof value === "string" && value.trim()) {
1244
- result[field] = value;
1245
- }
1246
- }
1247
- const booleanFields = ["includeFiles"];
1248
- for (const field of booleanFields) {
1249
- const value = record[field];
1250
- if (typeof value === "boolean") {
1251
- result[field] = value;
1252
- }
1253
- }
1254
- const numericFields = ["limit", "maxFiles", "maxLines", "maxChars"];
1255
- for (const field of numericFields) {
1256
- const value = record[field];
1257
- if (typeof value === "number" && Number.isFinite(value)) {
1258
- result[field] = value;
1259
- }
1260
- }
1261
- return result;
1262
- }
1263
- function buildValidateProjectSuggestedParams(normalizedInput) {
1264
- const record = asObjectRecord(normalizedInput);
1265
- if (!record) {
1266
- return {
1267
- task: "project-summary",
1268
- subject: {
1269
- kind: "workspace",
1270
- projectPath: "<workspace-path>"
1271
- },
1272
- preferProjectVersion: true
1273
- };
1274
- }
1275
- const task = asNonEmptyString(record.task) ?? "project-summary";
1276
- const result = { task };
1277
- const subjectRecord = asObjectRecord(record.subject);
1278
- const include = asStringArray(record.include);
1279
- const canonicalInclude = filterAllowedIncludeValues(include, VALIDATE_PROJECT_INCLUDE_GROUPS);
1280
- const wantsWorkspaceInclude = include?.some((value) => VALIDATE_PROJECT_LEGACY_WORKSPACE_INCLUDES.includes(value)) ?? false;
1281
- const detail = asNonEmptyString(record.detail);
1282
- if (detail && detail !== "summary") {
1283
- result.detail = detail;
1284
- }
1285
- const includeSuggestion = wantsWorkspaceInclude
1286
- ? [...new Set([...canonicalInclude, "workspace"])]
1287
- : canonicalInclude;
1288
- if (includeSuggestion.length > 0) {
1289
- result.include = includeSuggestion;
1290
- }
1291
- const stringFields = [
1292
- "version",
1293
- "mapping",
1294
- "sourcePriority",
1295
- "scope",
1296
- "minSeverity",
1297
- "warningMode"
1298
- ];
1299
- for (const field of stringFields) {
1300
- const value = record[field];
1301
- if (typeof value === "string" && value.trim()) {
1302
- result[field] = value;
1303
- }
1304
- }
1305
- const booleanFields = [
1306
- "preferProjectVersion",
1307
- "preferProjectMapping",
1308
- "hideUncertain",
1309
- "explain",
1310
- "treatInfoAsWarning",
1311
- "includeIssues"
1312
- ];
1313
- for (const field of booleanFields) {
1314
- const value = record[field];
1315
- if (typeof value === "boolean" &&
1316
- (!Object.prototype.hasOwnProperty.call(SUGGESTED_CALL_DEFAULTS, field) ||
1317
- !isSuggestedCallDefault(field, value))) {
1318
- result[field] = value;
1319
- }
1320
- }
1321
- const sourceRoots = asStringArray(record.sourceRoots);
1322
- if (sourceRoots?.length) {
1323
- result.sourceRoots = sourceRoots;
1324
- }
1325
- const configPaths = asStringArray(record.configPaths);
1326
- if (configPaths?.length) {
1327
- result.configPaths = configPaths;
1328
- }
1329
- const warningCategoryFilter = asStringArray(record.warningCategoryFilter);
1330
- if (warningCategoryFilter?.length) {
1331
- result.warningCategoryFilter = warningCategoryFilter;
1332
- }
1333
- if (task === "project-summary") {
1334
- const subject = {
1335
- kind: "workspace",
1336
- projectPath: asNonEmptyString(subjectRecord?.projectPath) ??
1337
- asNonEmptyString(record.subject) ??
1338
- asNonEmptyString(record.projectPath) ??
1339
- "<workspace-path>"
1340
- };
1341
- const discover = asStringArray(subjectRecord?.discover);
1342
- if (discover?.length) {
1343
- subject.discover = discover;
1344
- }
1345
- result.subject = subject;
1346
- return result;
1347
- }
1348
- if (task === "mixin") {
1349
- const inputRecord = asObjectRecord(subjectRecord?.input) ?? asObjectRecord(record.input);
1350
- result.subject = {
1351
- kind: "mixin",
1352
- input: inputRecord ?? {
1353
- mode: "inline",
1354
- source: "<Mixin Java source>"
1355
- }
1356
- };
1357
- return result;
1358
- }
1359
- const inputRecord = asObjectRecord(subjectRecord?.input) ?? asObjectRecord(record.input);
1360
- result.subject = {
1361
- kind: "access-widener",
1362
- input: inputRecord ?? {
1363
- mode: "inline",
1364
- content: "<access widener contents>"
1365
- }
1366
- };
1367
- return result;
1368
- }
1369
- function buildInvalidInputGuidance(tool, normalizedInput) {
1370
- if (tool === "validate-mixin") {
1371
- const hints = [
1372
- "validate-mixin.input must be an object with input.mode = \"inline\" | \"path\" | \"paths\" | \"config\" | \"project\".",
1373
- "Whole-project example: {\"input\":{\"mode\":\"project\",\"path\":\"/workspace\"},\"version\":\"1.21.10\",\"preferProjectVersion\":true,\"preferProjectMapping\":true}.",
1374
- "Legacy top-level source/sourcePath/sourcePaths/mixinConfigPath fields are no longer accepted; wrap them under input.mode instead."
1375
- ];
1376
- return {
1377
- hints,
1378
- suggestedCall: {
1379
- tool,
1380
- params: buildValidateMixinSuggestedParams(normalizedInput)
1381
- }
1382
- };
1383
- }
1384
- if (tool === "resolve-artifact") {
1385
- return {
1386
- hints: [
1387
- "resolve-artifact.target must be an object: {\"kind\":\"version|jar|coordinate\",\"value\":\"...\"}.",
1388
- "Bare string targets are not accepted; wrap the value under target.kind and target.value."
1389
- ],
1390
- suggestedCall: {
1391
- tool,
1392
- params: buildResolveArtifactSuggestedParams(normalizedInput)
1393
- }
1394
- };
1395
- }
1396
- if (tool === "get-class-source" || tool === "get-class-members") {
1397
- return {
1398
- hints: [
1399
- `${tool}.target must be an object: {"type":"resolve","kind":"version|jar|coordinate","value":"..."} or {"type":"artifact","artifactId":"..."}.`,
1400
- "Bare string targets are not accepted; wrap the value under target.type/target.kind/target.value."
1401
- ],
1402
- suggestedCall: {
1403
- tool,
1404
- params: buildSourceLookupSuggestedParams(tool, normalizedInput)
1405
- }
1406
- };
1407
- }
1408
- if (tool === "validate-project") {
1409
- return {
1410
- hints: [
1411
- "validate-project.subject must be an object with subject.kind=workspace|mixin|access-widener|access-transformer.",
1412
- "task=\"project-summary\" uses {\"subject\":{\"kind\":\"workspace\",\"projectPath\":\"/workspace\"}}.",
1413
- "Legacy include names like projectSummary/detectedConfig/validationSummary are not accepted; use include:[\"workspace\"] only when you need discovery details."
1414
- ],
1415
- suggestedCall: {
1416
- tool,
1417
- params: buildValidateProjectSuggestedParams(normalizedInput)
1418
- }
1419
- };
1420
- }
1421
- if (tool === "analyze-mod") {
1422
- return {
1423
- hints: [
1424
- "analyze-mod.subject must be an object with subject.kind=jar|class.",
1425
- "task=\"summary\" uses {\"subject\":{\"kind\":\"jar\",\"jarPath\":\"/path/to/mod.jar\"}}.",
1426
- "Legacy include names like metadata/entrypoints/mixins/dependencies are not accepted; use detail=\"standard\" to surface the metadata block, and canonical include groups only for warnings/files/source/samples/timings."
1427
- ],
1428
- suggestedCall: {
1429
- tool,
1430
- params: buildAnalyzeModSuggestedParams(normalizedInput)
1431
- }
1432
- };
1433
- }
1434
- return undefined;
1435
- }
1436
- function mapErrorToProblem(caughtError, requestId, context) {
1437
- if (caughtError instanceof ZodError) {
1438
- const guidance = context?.tool
1439
- ? buildInvalidInputGuidance(context.tool, context.normalizedInput)
1440
- : undefined;
1441
- return {
1442
- type: "https://minecraft-modding-mcp.dev/problems/invalid-input",
1443
- title: "Invalid input",
1444
- detail: "Request validation failed.",
1445
- status: 400,
1446
- code: ERROR_CODES.INVALID_INPUT,
1447
- instance: requestId,
1448
- fieldErrors: toFieldErrorsFromZod(caughtError),
1449
- hints: guidance?.hints ?? ["Check fieldErrors and submit a valid tool argument payload."],
1450
- ...(guidance?.suggestedCall ? { suggestedCall: guidance.suggestedCall } : {})
1451
- };
1452
- }
1453
- if (isAppError(caughtError)) {
1454
- const suggestedCall = toSuggestedCall(caughtError.details);
1455
- return {
1456
- type: `https://minecraft-modding-mcp.dev/problems/${caughtError.code.toLowerCase()}`,
1457
- title: "Tool execution error",
1458
- detail: caughtError.message,
1459
- status: statusForErrorCode(caughtError.code),
1460
- code: caughtError.code,
1461
- instance: requestId,
1462
- fieldErrors: extractFieldErrorsFromDetails(caughtError.details),
1463
- hints: toHints(caughtError.details),
1464
- ...(suggestedCall ? { suggestedCall } : {})
1465
- };
1466
- }
1467
- return {
1468
- type: "https://minecraft-modding-mcp.dev/problems/internal",
1469
- title: "Internal server error",
1470
- detail: "Unexpected server error.",
1471
- status: 500,
1472
- code: ERROR_CODES.INTERNAL,
1473
- instance: requestId
1474
- };
1475
- }
1476
245
  function splitWarnings(data) {
1477
246
  const result = { ...data };
1478
247
  const warnings = [];
@@ -1517,12 +286,12 @@ async function runTool(tool, rawInput, schema, action) {
1517
286
  code: "invalid_enum_value"
1518
287
  })),
1519
288
  nextAction: `Replace "official" with "obfuscated" in mapping-related fields and retry.`,
1520
- suggestedCall: suggestedReplacementInput
1521
- ? {
289
+ ...(suggestedReplacementInput
290
+ ? buildSuggestedCall({
1522
291
  tool,
1523
292
  params: suggestedReplacementInput
1524
- }
1525
- : undefined
293
+ })
294
+ : {})
1526
295
  }
1527
296
  });
1528
297
  }
@@ -1540,7 +309,16 @@ async function runTool(tool, rawInput, schema, action) {
1540
309
  if (COMPACT_MAPPING_TOOL_NAMES.has(tool)) {
1541
310
  projectedResult = compactMappingResponse(projectedResult);
1542
311
  }
1543
- projectedResult = compactResponse(projectedResult);
312
+ if (COMPACT_SOURCE_TOOL_NAMES.has(tool)) {
313
+ projectedResult = compactSourceResponse(projectedResult);
314
+ }
315
+ if (COMPACT_MEMBERS_TOOL_NAMES.has(tool)) {
316
+ projectedResult = compactMembersResponse(projectedResult);
317
+ }
318
+ if (COMPACT_LIGHT_TOOL_NAMES.has(tool)) {
319
+ projectedResult = compactLightResponse(projectedResult);
320
+ }
321
+ projectedResult = compactResponse(projectedResult, TOOL_PRESERVE_PAYLOAD_KEYS[tool]);
1544
322
  }
1545
323
  const entryMeta = ENTRY_TOOL_NAMES.has(tool)
1546
324
  ? buildEntryToolMeta({
@@ -1558,6 +336,8 @@ async function runTool(tool, rawInput, schema, action) {
1558
336
  : undefined
1559
337
  })
1560
338
  : undefined;
339
+ const durationMs = Date.now() - startedAt;
340
+ sourceService.recordToolCall(tool, durationMs);
1561
341
  return objectResult({
1562
342
  result: projectedResult,
1563
343
  meta: {
@@ -1565,7 +345,7 @@ async function runTool(tool, rawInput, schema, action) {
1565
345
  ...resultMeta,
1566
346
  requestId,
1567
347
  tool,
1568
- durationMs: Date.now() - startedAt,
348
+ durationMs,
1569
349
  warnings
1570
350
  }
1571
351
  });
@@ -1605,14 +385,18 @@ async function runTool(tool, rawInput, schema, action) {
1605
385
  reason: caughtError instanceof Error ? caughtError.message : String(caughtError)
1606
386
  });
1607
387
  }
388
+ const errorDurationMs = Date.now() - startedAt;
389
+ sourceService.recordToolCall(tool, errorDurationMs);
390
+ const errorMeta = {
391
+ requestId,
392
+ tool,
393
+ durationMs: errorDurationMs,
394
+ warnings: []
395
+ };
396
+ applyErrorMetaExtensions(errorMeta, caughtError);
1608
397
  return objectResult({
1609
398
  error: problem,
1610
- meta: {
1611
- requestId,
1612
- tool,
1613
- durationMs: Date.now() - startedAt,
1614
- warnings: []
1615
- }
399
+ meta: errorMeta
1616
400
  }, { isError: true });
1617
401
  }
1618
402
  }
@@ -1620,12 +404,44 @@ server.tool("list-versions", "List available Minecraft versions from Mojang mani
1620
404
  includeSnapshots: input.includeSnapshots,
1621
405
  limit: input.limit
1622
406
  })));
407
+ registerToolSchema("list-versions", listVersionsSchema);
1623
408
  server.tool("inspect-minecraft", "High-level v3 entry 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)));
409
+ registerToolSchema("inspect-minecraft", inspectMinecraftSchema);
1624
410
  server.tool("analyze-symbol", "High-level v3 entry 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)));
411
+ registerToolSchema("analyze-symbol", analyzeSymbolSchema);
1625
412
  server.tool("compare-minecraft", "High-level v3 entry 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)));
413
+ registerToolSchema("compare-minecraft", compareMinecraftSchema);
1626
414
  server.tool("analyze-mod", "High-level v3 entry tool for mod metadata inspection, decompile/search flows, class source, and safe remap previews/applies.", analyzeModShape, { readOnlyHint: false }, async (args) => runTool("analyze-mod", args, analyzeModSchema, async (input) => analyzeModService.execute(input)));
415
+ registerToolSchema("analyze-mod", analyzeModSchema);
1627
416
  server.tool("validate-project", "High-level v3 entry tool for project summary, direct mixin validation, and access widener/access transformer validation.", validateProjectShape, { readOnlyHint: true }, async (args) => runTool("validate-project", args, validateProjectSchema, async (input) => validateProjectService.execute(input)));
417
+ registerToolSchema("validate-project", validateProjectSchema);
1628
418
  server.tool("manage-cache", "High-level v3 entry tool for cache summaries, listing, verification, previewed mutation, and explicit apply operations.", manageCacheShape, { readOnlyHint: false }, async (args) => runTool("manage-cache", args, manageCacheSchema, async (input) => manageCacheService.execute(input)));
419
+ registerToolSchema("manage-cache", manageCacheSchema);
420
+ if (!VERIFY_MIXIN_TARGET_OFF) {
421
+ server.tool("verify-mixin-target", "Single-call probe: does this target owner / member exist and which @Shadow / @Accessor / @Invoker should the mixin use? Reuses target.kind workspace/version/coordinate/dependency/jar.", verifyMixinTargetShape, { readOnlyHint: true }, async (args) => runTool("verify-mixin-target", args, verifyMixinTargetSchema, async (input) => verifyMixinTargetService.execute({
422
+ owner: input.owner,
423
+ member: input.member,
424
+ mixinMemberName: input.mixinMemberName,
425
+ mapping: input.mapping,
426
+ sourcePriority: input.sourcePriority,
427
+ projectPath: input.projectPath,
428
+ target: input.target,
429
+ scope: input.scope,
430
+ preferProjectVersion: input.preferProjectVersion,
431
+ strictVersion: input.strictVersion
432
+ })));
433
+ registerToolSchema("verify-mixin-target", verifyMixinTargetSchema);
434
+ }
435
+ if (!BATCH_TOOLS_OFF) {
436
+ 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)));
437
+ registerToolSchema("batch-class-source", batchClassSourceSchema);
438
+ 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)));
439
+ registerToolSchema("batch-class-members", batchClassMembersSchema);
440
+ 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)));
441
+ registerToolSchema("batch-symbol-exists", batchSymbolExistsSchema);
442
+ 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)));
443
+ registerToolSchema("batch-mappings", batchMappingsSchema);
444
+ }
1629
445
  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({
1630
446
  target: input.target,
1631
447
  mapping: input.mapping,
@@ -1634,8 +450,10 @@ server.tool("resolve-artifact", "Resolve source artifact from a target object ({
1634
450
  projectPath: input.projectPath,
1635
451
  scope: input.scope,
1636
452
  preferProjectVersion: input.preferProjectVersion,
1637
- strictVersion: input.strictVersion
453
+ strictVersion: input.strictVersion,
454
+ compact: input.compact
1638
455
  })));
456
+ registerToolSchema("resolve-artifact", resolveArtifactSchema);
1639
457
  const findClassShape = {
1640
458
  className: nonEmptyString.describe("Simple name (e.g. Blocks) or fully-qualified name (e.g. net.minecraft.world.level.block.Blocks)"),
1641
459
  artifactId: nonEmptyString,
@@ -1647,6 +465,7 @@ server.tool("find-class", "Resolve a simple or qualified class name to fully-qua
1647
465
  artifactId: input.artifactId,
1648
466
  limit: input.limit
1649
467
  })));
468
+ registerToolSchema("find-class", findClassSchema);
1650
469
  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) => {
1651
470
  const normalizedTarget = normalizeSourceLookupTarget(input.target);
1652
471
  return sourceService.getClassSource({
@@ -1668,6 +487,7 @@ server.tool("get-class-source", "Get Java source for a class by target ({ type:
1668
487
  outputFile: input.outputFile
1669
488
  });
1670
489
  }));
490
+ registerToolSchema("get-class-source", getClassSourceSchema);
1671
491
  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) => {
1672
492
  const normalizedTarget = normalizeSourceLookupTarget(input.target);
1673
493
  return sourceService.getClassMembers({
@@ -1688,6 +508,7 @@ server.tool("get-class-members", "Get fields/methods/constructors for one class
1688
508
  strictVersion: input.strictVersion
1689
509
  });
1690
510
  }));
511
+ registerToolSchema("get-class-members", getClassMembersSchema);
1691
512
  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) => {
1692
513
  const scope = input.packagePrefix || input.fileGlob || input.symbolKind
1693
514
  ? {
@@ -1704,15 +525,25 @@ server.tool("search-class-source", "Search indexed class source files for one ar
1704
525
  scope: scope,
1705
526
  queryMode: input.queryMode,
1706
527
  limit: input.limit,
1707
- cursor: input.cursor
528
+ cursor: input.cursor,
529
+ queryNamespace: input.queryNamespace,
530
+ sourcePriority: input.sourcePriority
1708
531
  });
1709
532
  }));
533
+ registerToolSchema("search-class-source", searchClassSourceSchema);
1710
534
  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({
1711
535
  artifactId: input.artifactId,
1712
536
  filePath: input.filePath,
1713
537
  maxBytes: input.maxBytes
1714
538
  })));
1715
- 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(input)));
539
+ registerToolSchema("get-artifact-file", getArtifactFileSchema);
540
+ 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({
541
+ artifactId: input.artifactId,
542
+ prefix: input.prefix,
543
+ limit: input.limit,
544
+ cursor: input.cursor
545
+ })));
546
+ registerToolSchema("list-artifact-files", listArtifactFilesSchema);
1716
547
  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({
1717
548
  symbol: input.symbol,
1718
549
  descriptor: input.descriptor,
@@ -1724,6 +555,7 @@ server.tool("trace-symbol-lifecycle", "Trace which Minecraft versions contain a
1724
555
  maxVersions: input.maxVersions,
1725
556
  includeTimeline: input.includeTimeline
1726
557
  })));
558
+ registerToolSchema("trace-symbol-lifecycle", traceSymbolLifecycleSchema);
1727
559
  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({
1728
560
  className: input.className,
1729
561
  fromVersion: input.fromVersion,
@@ -1732,6 +564,7 @@ server.tool("diff-class-signatures", "Compare one class signature between two Mi
1732
564
  sourcePriority: input.sourcePriority,
1733
565
  includeFullDiff: input.includeFullDiff
1734
566
  })));
567
+ registerToolSchema("diff-class-signatures", diffClassSignaturesSchema);
1735
568
  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({
1736
569
  version: input.version,
1737
570
  kind: input.kind,
@@ -1741,9 +574,11 @@ server.tool("find-mapping", "Find symbol mapping candidates between namespaces u
1741
574
  sourceMapping: input.sourceMapping,
1742
575
  targetMapping: input.targetMapping,
1743
576
  sourcePriority: input.sourcePriority,
577
+ signatureMode: input.signatureMode,
1744
578
  disambiguation: input.disambiguation,
1745
579
  maxCandidates: input.maxCandidates
1746
580
  })));
581
+ registerToolSchema("find-mapping", findMappingSchema);
1747
582
  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({
1748
583
  version: input.version,
1749
584
  name: input.name,
@@ -1754,6 +589,7 @@ server.tool("resolve-method-mapping-exact", "Resolve one method mapping exactly
1754
589
  sourcePriority: input.sourcePriority,
1755
590
  maxCandidates: input.maxCandidates
1756
591
  })));
592
+ registerToolSchema("resolve-method-mapping-exact", resolveMethodMappingExactSchema);
1757
593
  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({
1758
594
  version: input.version,
1759
595
  className: input.className,
@@ -1762,6 +598,7 @@ server.tool("get-class-api-matrix", "List class/member API rows across obfuscate
1762
598
  sourcePriority: input.sourcePriority,
1763
599
  maxRows: input.maxRows
1764
600
  })));
601
+ registerToolSchema("get-class-api-matrix", getClassApiMatrixSchema);
1765
602
  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({
1766
603
  projectPath: input.projectPath,
1767
604
  version: input.version,
@@ -1773,6 +610,7 @@ server.tool("resolve-workspace-symbol", "Resolve class/field/method names as see
1773
610
  sourcePriority: input.sourcePriority,
1774
611
  maxCandidates: input.maxCandidates
1775
612
  })));
613
+ registerToolSchema("resolve-workspace-symbol", resolveWorkspaceSymbolSchema);
1776
614
  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({
1777
615
  version: input.version,
1778
616
  kind: input.kind,
@@ -1785,24 +623,30 @@ server.tool("check-symbol-exists", "Check whether a class/field/method symbol ex
1785
623
  signatureMode: input.signatureMode,
1786
624
  maxCandidates: input.maxCandidates
1787
625
  })));
626
+ registerToolSchema("check-symbol-exists", checkSymbolExistsSchema);
1788
627
  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({
1789
628
  nbtBase64: input.nbtBase64,
1790
629
  compression: input.compression
1791
630
  }, nbtLimits))));
631
+ registerToolSchema("nbt-to-json", nbtToJsonSchema);
1792
632
  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({
1793
633
  typedJson: input.typedJson,
1794
634
  patch: input.patch
1795
635
  }, nbtLimits))));
636
+ registerToolSchema("nbt-apply-json-patch", nbtApplyJsonPatchSchema);
1796
637
  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({
1797
638
  typedJson: input.typedJson,
1798
639
  compression: input.compression
1799
640
  }, nbtLimits))));
641
+ registerToolSchema("json-to-nbt", jsonToNbtSchema);
1800
642
  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({
1801
643
  artifactId: input.artifactId,
1802
644
  force: input.force
1803
645
  })));
646
+ registerToolSchema("index-artifact", indexArtifactSchema);
1804
647
  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())));
1805
- server.tool("validate-mixin", "Validate Mixin source against Minecraft bytecode signatures for a given version.", validateMixinShape, { readOnlyHint: true }, async (args) => runTool("validate-mixin", args, validateMixinSchema, async (input) => sourceService.validateMixin({
648
+ registerToolSchema("get-runtime-metrics", emptySchema);
649
+ 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({
1806
650
  input: input.input,
1807
651
  sourceRoots: input.sourceRoots,
1808
652
  version: input.version,
@@ -1820,7 +664,10 @@ server.tool("validate-mixin", "Validate Mixin source against Minecraft bytecode
1820
664
  warningCategoryFilter: input.warningCategoryFilter,
1821
665
  treatInfoAsWarning: input.treatInfoAsWarning,
1822
666
  includeIssues: input.includeIssues
667
+ }, {
668
+ stageEmitter: makeStageEmitter(extra)
1823
669
  })));
670
+ registerToolSchema("validate-mixin", validateMixinSchema);
1824
671
  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({
1825
672
  content: input.content,
1826
673
  version: input.version,
@@ -1830,6 +677,7 @@ server.tool("validate-access-widener", "Validate Access Widener file entries aga
1830
677
  scope: input.scope,
1831
678
  preferProjectVersion: input.preferProjectVersion
1832
679
  })));
680
+ registerToolSchema("validate-access-widener", validateAccessWidenerSchema);
1833
681
  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({
1834
682
  content: input.content,
1835
683
  version: input.version,
@@ -1839,18 +687,21 @@ server.tool("validate-access-transformer", "Validate Access Transformer file ent
1839
687
  scope: input.scope,
1840
688
  preferProjectVersion: input.preferProjectVersion
1841
689
  })));
690
+ registerToolSchema("validate-access-transformer", validateAccessTransformerSchema);
1842
691
  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) => {
1843
692
  const result = await analyzeModJar(input.jarPath, {
1844
693
  includeClasses: input.includeClasses ?? false
1845
694
  });
1846
695
  return result;
1847
696
  }));
697
+ registerToolSchema("analyze-mod-jar", analyzeModJarSchema);
1848
698
  server.tool("get-registry-data", "Get Minecraft registry data (blocks, items, biomes, etc.) for a specific version by running the server data generator.", getRegistryDataShape, { readOnlyHint: true }, async (args) => runTool("get-registry-data", args, getRegistryDataSchema, async (input) => sourceService.getRegistryData({
1849
699
  version: input.version,
1850
700
  registry: input.registry,
1851
701
  includeData: input.includeData,
1852
702
  maxEntriesPerRegistry: input.maxEntriesPerRegistry
1853
703
  })));
704
+ registerToolSchema("get-registry-data", getRegistryDataSchema);
1854
705
  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({
1855
706
  fromVersion: input.fromVersion,
1856
707
  toVersion: input.toVersion,
@@ -1858,12 +709,14 @@ server.tool("compare-versions", "Compare two Minecraft versions to find added/re
1858
709
  packageFilter: input.packageFilter,
1859
710
  maxClassResults: input.maxClassResults
1860
711
  })));
712
+ registerToolSchema("compare-versions", compareVersionsSchema);
1861
713
  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({
1862
714
  jarPath: input.jarPath,
1863
715
  className: input.className,
1864
716
  includeFiles: input.includeFiles,
1865
717
  maxFiles: input.maxFiles
1866
718
  })));
719
+ registerToolSchema("decompile-mod-jar", decompileModJarSchema);
1867
720
  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({
1868
721
  jarPath: input.jarPath,
1869
722
  className: input.className,
@@ -1871,12 +724,14 @@ server.tool("get-mod-class-source", "Get decompiled source code for a specific c
1871
724
  maxChars: input.maxChars,
1872
725
  outputFile: input.outputFile
1873
726
  })));
727
+ registerToolSchema("get-mod-class-source", getModClassSourceSchema);
1874
728
  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({
1875
729
  jarPath: input.jarPath,
1876
730
  query: input.query,
1877
731
  searchType: input.searchType,
1878
732
  limit: input.limit
1879
733
  })));
734
+ registerToolSchema("search-mod-source", searchModSourceSchema);
1880
735
  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) => {
1881
736
  const result = await remapModJar({
1882
737
  inputJar: input.inputJar,
@@ -1886,6 +741,7 @@ server.tool("remap-mod-jar", "Remap a Fabric mod JAR from intermediary to yarn/m
1886
741
  }, config);
1887
742
  return result;
1888
743
  }));
744
+ registerToolSchema("remap-mod-jar", remapModJarSchema);
1889
745
  export async function startServer() {
1890
746
  if (serverStarted) {
1891
747
  return;