@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
@@ -0,0 +1,323 @@
1
+ import { z } from "zod";
2
+ import { buildSuggestedCall } from "../build-suggested-call.js";
3
+ import { createError, ERROR_CODES, isAppError } from "../errors.js";
4
+ import { suggestSimilar } from "../mixin-validator.js";
5
+ export const VERIFY_MIXIN_TARGET_OFF = process.env.VERIFY_MIXIN_TARGET_OFF === "1";
6
+ const ACCESSOR_NAME_RE = /^(get|set|is)([A-Z]\w*)$/;
7
+ const INVOKER_NAME_RE = /^(invoke|call)([A-Z]\w*)$/;
8
+ function decodeAccessFlags(flags) {
9
+ const labels = [];
10
+ if (flags == null) {
11
+ return labels;
12
+ }
13
+ if ((flags & 0x0001) !== 0)
14
+ labels.push("public");
15
+ if ((flags & 0x0002) !== 0)
16
+ labels.push("private");
17
+ if ((flags & 0x0004) !== 0)
18
+ labels.push("protected");
19
+ if ((flags & 0x0008) !== 0)
20
+ labels.push("static");
21
+ if ((flags & 0x0010) !== 0)
22
+ labels.push("final");
23
+ if ((flags & 0x0020) !== 0)
24
+ labels.push("synchronized");
25
+ if ((flags & 0x0040) !== 0)
26
+ labels.push("volatile");
27
+ if ((flags & 0x0080) !== 0)
28
+ labels.push("transient");
29
+ if ((flags & 0x0100) !== 0)
30
+ labels.push("native");
31
+ if ((flags & 0x0400) !== 0)
32
+ labels.push("abstract");
33
+ if ((flags & 0x0800) !== 0)
34
+ labels.push("strictfp");
35
+ if (labels.length === 0) {
36
+ labels.push("package-private");
37
+ }
38
+ return labels;
39
+ }
40
+ function visibilityFromFlags(flags) {
41
+ if (flags == null)
42
+ return "package-private";
43
+ if ((flags & 0x0001) !== 0)
44
+ return "public";
45
+ if ((flags & 0x0004) !== 0)
46
+ return "protected";
47
+ if ((flags & 0x0002) !== 0)
48
+ return "private";
49
+ return "package-private";
50
+ }
51
+ function buildExampleSnippet(annotation, member, match, mixinMemberName) {
52
+ const accessFlags = match?.accessFlags ?? [];
53
+ const isFinal = accessFlags.includes("final");
54
+ const targetName = member.name;
55
+ if (annotation === "@Inject-only") {
56
+ return `// "${targetName}" is already visible to the mixin (target access: ${accessFlags.filter((f) => f === "public" || f === "protected" || f === "private" || f === "package-private")[0] ?? "unknown"}); use @Inject directly without @Shadow.`;
57
+ }
58
+ if (annotation === "@Shadow") {
59
+ if (member.kind === "field") {
60
+ const finalTag = isFinal ? "@Shadow @Final\n" : "@Shadow\n";
61
+ return `${finalTag}private <type> ${targetName};`;
62
+ }
63
+ return `@Shadow\nprivate <returnType> ${targetName}(<params>);`;
64
+ }
65
+ if (annotation === "@Accessor") {
66
+ const accessorName = mixinMemberName ?? `get${targetName.charAt(0).toUpperCase()}${targetName.slice(1)}`;
67
+ const isSetter = mixinMemberName !== undefined && /^set[A-Z]/.test(mixinMemberName);
68
+ if (isSetter) {
69
+ return `@Accessor("${targetName}")\nvoid ${accessorName}(<type> value);`;
70
+ }
71
+ return `@Accessor("${targetName}")\n<returnType> ${accessorName}();`;
72
+ }
73
+ const invokerName = mixinMemberName ?? `invoke${targetName.charAt(0).toUpperCase()}${targetName.slice(1)}`;
74
+ return `@Invoker("${targetName}")\n<returnType> ${invokerName}(<params>);`;
75
+ }
76
+ function inferAnnotation(member, match, mixinMemberName) {
77
+ const visibility = match ? deriveVisibility(match.accessFlags) : "private";
78
+ if (visibility === "public" || visibility === "protected") {
79
+ return {
80
+ suggestedAnnotation: "@Inject-only",
81
+ reasoning: `Target "${member.name}" is ${visibility}; it is already visible to the mixin without @Shadow. Use @Inject directly.`,
82
+ exampleSnippet: buildExampleSnippet("@Inject-only", member, match, mixinMemberName)
83
+ };
84
+ }
85
+ if (mixinMemberName) {
86
+ if (member.kind === "field" && ACCESSOR_NAME_RE.test(mixinMemberName)) {
87
+ return {
88
+ suggestedAnnotation: "@Accessor",
89
+ reasoning: `mixin member "${mixinMemberName}" follows the get/set/is convention — inferred field "${member.name}" via prefix removal.`,
90
+ exampleSnippet: buildExampleSnippet("@Accessor", member, match, mixinMemberName)
91
+ };
92
+ }
93
+ if (member.kind === "method" && INVOKER_NAME_RE.test(mixinMemberName)) {
94
+ return {
95
+ suggestedAnnotation: "@Invoker",
96
+ reasoning: `mixin member "${mixinMemberName}" follows the invoke/call convention — inferred method "${member.name}" via prefix removal.`,
97
+ exampleSnippet: buildExampleSnippet("@Invoker", member, match, mixinMemberName)
98
+ };
99
+ }
100
+ return {
101
+ suggestedAnnotation: "@Shadow",
102
+ reasoning: `Target "${member.name}" is private. mixin member "${mixinMemberName}" does not match accessor/invoker prefix conventions; @Shadow is appropriate.`,
103
+ exampleSnippet: buildExampleSnippet("@Shadow", member, match, mixinMemberName)
104
+ };
105
+ }
106
+ if (member.kind === "field") {
107
+ return {
108
+ suggestedAnnotation: "@Shadow",
109
+ reasoning: `Target "${member.name}" is private; use @Shadow to access it from the mixin.`,
110
+ exampleSnippet: buildExampleSnippet("@Shadow", member, match, mixinMemberName)
111
+ };
112
+ }
113
+ return {
114
+ suggestedAnnotation: null,
115
+ reasoning: `Target method "${member.name}" is private and no mixinMemberName was supplied; cannot decide between @Shadow and @Invoker. Choose @Shadow when the mixin overrides the method, @Invoker when it only needs to call it.`,
116
+ exampleSnippet: "// Provide mixinMemberName to receive a concrete @Shadow or @Invoker template.",
117
+ candidates: [
118
+ {
119
+ annotation: "@Shadow",
120
+ reasoning: "Use @Shadow to override or read the private method body from the mixin.",
121
+ exampleSnippet: buildExampleSnippet("@Shadow", member, match, mixinMemberName)
122
+ },
123
+ {
124
+ annotation: "@Invoker",
125
+ reasoning: "Use @Invoker when the mixin only needs to invoke the private method without redeclaring the body.",
126
+ exampleSnippet: buildExampleSnippet("@Invoker", member, match, mixinMemberName)
127
+ }
128
+ ]
129
+ };
130
+ }
131
+ function deriveVisibility(accessFlags) {
132
+ if (accessFlags.includes("public"))
133
+ return "public";
134
+ if (accessFlags.includes("protected"))
135
+ return "protected";
136
+ if (accessFlags.includes("private"))
137
+ return "private";
138
+ return "package-private";
139
+ }
140
+ function buildMatch(member) {
141
+ return {
142
+ name: member.name,
143
+ descriptor: member.jvmDescriptor,
144
+ accessFlags: decodeAccessFlags(member.accessFlags),
145
+ javaSignature: member.javaSignature,
146
+ ...(member.sourceLine ? { sourceLine: member.sourceLine } : {})
147
+ };
148
+ }
149
+ export class VerifyMixinTargetService {
150
+ deps;
151
+ constructor(deps) {
152
+ this.deps = deps;
153
+ }
154
+ async execute(input) {
155
+ if (VERIFY_MIXIN_TARGET_OFF) {
156
+ throw createError({
157
+ code: ERROR_CODES.INVALID_INPUT,
158
+ message: "verify-mixin-target is disabled by VERIFY_MIXIN_TARGET_OFF=1.",
159
+ details: { fieldErrors: [{ path: "tool", message: "verify-mixin-target is disabled." }] }
160
+ });
161
+ }
162
+ if (!input.target) {
163
+ throw createError({
164
+ code: ERROR_CODES.INVALID_INPUT,
165
+ message: "verify-mixin-target requires a target.",
166
+ details: { fieldErrors: [{ path: "target", message: "target is required" }] }
167
+ });
168
+ }
169
+ const owner = input.owner.trim();
170
+ if (!owner) {
171
+ throw createError({
172
+ code: ERROR_CODES.INVALID_INPUT,
173
+ message: "owner must be non-empty."
174
+ });
175
+ }
176
+ const memberName = input.member.name.trim();
177
+ if (!memberName) {
178
+ throw createError({
179
+ code: ERROR_CODES.INVALID_INPUT,
180
+ message: "member.name must be non-empty."
181
+ });
182
+ }
183
+ const resolved = await this.deps.resolveArtifact({
184
+ target: input.target,
185
+ mapping: input.mapping,
186
+ sourcePriority: input.sourcePriority,
187
+ projectPath: input.projectPath,
188
+ scope: input.scope,
189
+ preferProjectVersion: input.preferProjectVersion,
190
+ strictVersion: input.strictVersion
191
+ });
192
+ const warnings = [...(resolved.warnings ?? [])];
193
+ if (input.mapping && input.mapping !== resolved.mappingApplied) {
194
+ throw createError({
195
+ code: ERROR_CODES.NAMESPACE_MISMATCH,
196
+ message: `verify-mixin-target requires owner and member.name to be in the artifact's namespace `
197
+ + `("${resolved.mappingApplied}"), but the request supplied mapping="${input.mapping}". `
198
+ + `Automatic name/descriptor translation is not yet supported; supply owner+member in the artifact namespace, `
199
+ + `or omit the mapping argument so the resolver picks the namespace automatically.`,
200
+ details: {
201
+ owner,
202
+ requestedMapping: input.mapping,
203
+ mappingApplied: resolved.mappingApplied,
204
+ artifactId: resolved.artifactId,
205
+ nextAction: `Retry with mapping="${resolved.mappingApplied}" and owner/member translated to that namespace, `
206
+ + `or use find-mapping to translate before calling verify-mixin-target.`
207
+ }
208
+ });
209
+ }
210
+ if (!resolved.binaryJarPath) {
211
+ throw createError({
212
+ code: ERROR_CODES.CONTEXT_UNRESOLVED,
213
+ message: `verify-mixin-target requires a binary jar but artifact "${resolved.artifactId}" has none.`,
214
+ details: { artifactId: resolved.artifactId, owner }
215
+ });
216
+ }
217
+ let signature;
218
+ try {
219
+ signature = await this.deps.getSignature({
220
+ fqn: owner,
221
+ jarPath: resolved.binaryJarPath,
222
+ access: "all",
223
+ includeSynthetic: false,
224
+ includeInherited: false
225
+ });
226
+ }
227
+ catch (error) {
228
+ if (isAppError(error) && error.code === ERROR_CODES.CLASS_NOT_FOUND) {
229
+ throw createError({
230
+ code: ERROR_CODES.CLASS_NOT_FOUND,
231
+ message: error.message,
232
+ details: {
233
+ ...(error.details ?? {}),
234
+ owner,
235
+ artifactId: resolved.artifactId,
236
+ nextAction: `Use find-class to locate the canonical FQCN for "${owner}" before retrying verify-mixin-target.`,
237
+ ...buildSuggestedCall({
238
+ tool: "find-class",
239
+ params: {
240
+ artifactId: resolved.artifactId,
241
+ className: owner.includes(".") ? owner.slice(owner.lastIndexOf(".") + 1) : owner
242
+ }
243
+ })
244
+ }
245
+ });
246
+ }
247
+ throw error;
248
+ }
249
+ warnings.push(...(signature.warnings ?? []));
250
+ const memberPool = input.member.kind === "method"
251
+ ? [...signature.constructors, ...signature.methods]
252
+ : [...signature.fields];
253
+ const allMemberNames = memberPool.map((m) => m.name);
254
+ const exactNameHits = memberPool.filter((m) => m.name === memberName);
255
+ let matches;
256
+ let candidates = [];
257
+ if (input.member.descriptor) {
258
+ const descriptorHits = exactNameHits.filter((m) => m.jvmDescriptor === input.member.descriptor);
259
+ if (descriptorHits.length > 0) {
260
+ matches = descriptorHits.map(buildMatch);
261
+ }
262
+ else {
263
+ matches = [];
264
+ candidates = exactNameHits.map((m) => ({
265
+ name: m.name,
266
+ descriptor: m.jvmDescriptor,
267
+ reason: `name match, descriptor ${m.jvmDescriptor} differs from requested ${input.member.descriptor}`
268
+ }));
269
+ }
270
+ }
271
+ else {
272
+ matches = exactNameHits.map(buildMatch);
273
+ }
274
+ if (matches.length === 0 && candidates.length === 0) {
275
+ const suggestions = suggestSimilar(memberName, allMemberNames);
276
+ candidates = suggestions.flatMap((suggestion) => memberPool
277
+ .filter((m) => m.name === suggestion)
278
+ .map((m) => ({
279
+ name: m.name,
280
+ descriptor: m.jvmDescriptor,
281
+ reason: `name "${m.name}" is similar to requested "${memberName}"`
282
+ })));
283
+ }
284
+ const exists = matches.length > 0;
285
+ let accessorAdvice;
286
+ if (matches.length === 1) {
287
+ accessorAdvice = inferAnnotation(input.member, matches[0], input.mixinMemberName);
288
+ }
289
+ return {
290
+ exists,
291
+ resolvedOwner: { className: owner, mapping: resolved.mappingApplied },
292
+ matches,
293
+ candidates,
294
+ ...(accessorAdvice ? { accessorAdvice } : {}),
295
+ warnings,
296
+ provenance: {
297
+ artifactId: resolved.artifactId,
298
+ mappingNamespace: resolved.mappingApplied,
299
+ ...(resolved.provenance?.workspaceResolution
300
+ ? { workspaceResolution: resolved.provenance.workspaceResolution }
301
+ : {}),
302
+ ...(resolved.provenance?.dependencyResolution
303
+ ? { dependencyResolution: resolved.provenance.dependencyResolution }
304
+ : {})
305
+ }
306
+ };
307
+ }
308
+ }
309
+ const memberDescriptorSchema = z.string().trim().min(1).optional();
310
+ const verifyMixinTargetMemberSchema = z.discriminatedUnion("kind", [
311
+ z.object({
312
+ kind: z.literal("method"),
313
+ name: z.string().trim().min(1),
314
+ descriptor: memberDescriptorSchema
315
+ }),
316
+ z.object({
317
+ kind: z.literal("field"),
318
+ name: z.string().trim().min(1),
319
+ descriptor: memberDescriptorSchema
320
+ })
321
+ ]);
322
+ export const verifyMixinTargetMemberSchemaExport = verifyMixinTargetMemberSchema;
323
+ //# sourceMappingURL=verify-mixin-target-service.js.map
@@ -0,0 +1,40 @@
1
+ import { type ErrorCode } from "./errors.js";
2
+ export type ProblemFieldError = {
3
+ path: string;
4
+ message: string;
5
+ code?: string;
6
+ };
7
+ export type SuggestedCall = {
8
+ tool: string;
9
+ params: Record<string, unknown>;
10
+ };
11
+ export type ExampleCall = {
12
+ tool: string;
13
+ params: Record<string, unknown>;
14
+ reason: string;
15
+ };
16
+ export type ProblemDetails = {
17
+ type: string;
18
+ title: string;
19
+ detail: string;
20
+ status: number;
21
+ code: string;
22
+ instance: string;
23
+ fieldErrors?: ProblemFieldError[];
24
+ hints?: string[];
25
+ suggestedCall?: SuggestedCall;
26
+ exampleCalls?: ExampleCall[];
27
+ failedStage?: string;
28
+ };
29
+ export declare function statusForErrorCode(code: string): number;
30
+ /**
31
+ * Per-entry subset of `mapErrorToProblem` for batch tools. ZodErrors are not
32
+ * handled here (per-entry input has already cleared the batch tool's schema
33
+ * gate); the optional `suggestedCall` is attached as-is for the caller's
34
+ * synthesized retry payload.
35
+ */
36
+ export declare function errorToBatchEntryProblem(caughtError: unknown, instance: string, options?: {
37
+ suggestedCall?: SuggestedCall;
38
+ }): ProblemDetails;
39
+ export declare function buildBatchAbortedProblem(instance: string): ProblemDetails;
40
+ export type { ErrorCode };
@@ -0,0 +1,139 @@
1
+ import { ERROR_CODES, isAppError } from "./errors.js";
2
+ import { log } from "./logger.js";
3
+ export function statusForErrorCode(code) {
4
+ if (code === ERROR_CODES.BATCH_ABORTED) {
5
+ return 412;
6
+ }
7
+ if (code === ERROR_CODES.STAGE_BUDGET_PRE_PARSE) {
8
+ // 408 (Request Timeout): caller-recoverable stage-budget exhaustion,
9
+ // not an internal server failure.
10
+ return 408;
11
+ }
12
+ if (code === ERROR_CODES.INVALID_INPUT ||
13
+ code === ERROR_CODES.COORDINATE_PARSE_FAILED ||
14
+ code === ERROR_CODES.INVALID_LINE_RANGE ||
15
+ code === ERROR_CODES.NBT_PARSE_FAILED ||
16
+ code === ERROR_CODES.NBT_INVALID_TYPED_JSON ||
17
+ code === ERROR_CODES.JSON_PATCH_INVALID ||
18
+ code === ERROR_CODES.NBT_ENCODE_FAILED ||
19
+ code === ERROR_CODES.NBT_UNSUPPORTED_FEATURE) {
20
+ return 400;
21
+ }
22
+ if (code === ERROR_CODES.JSON_PATCH_CONFLICT || code === ERROR_CODES.CONTEXT_UNRESOLVED) {
23
+ return 409;
24
+ }
25
+ if (code === ERROR_CODES.SOURCE_NOT_FOUND ||
26
+ code === ERROR_CODES.FILE_NOT_FOUND ||
27
+ code === ERROR_CODES.JAR_NOT_FOUND ||
28
+ code === ERROR_CODES.VERSION_NOT_FOUND ||
29
+ code === ERROR_CODES.CLASS_NOT_FOUND) {
30
+ return 404;
31
+ }
32
+ if (code === ERROR_CODES.MAPPING_NOT_APPLIED ||
33
+ code === ERROR_CODES.MAPPING_UNAVAILABLE ||
34
+ code === ERROR_CODES.NAMESPACE_MISMATCH ||
35
+ code === ERROR_CODES.DECOMPILE_DISABLED ||
36
+ code === ERROR_CODES.REMAP_FAILED ||
37
+ code === ERROR_CODES.WORKSPACE_VERSION_UNRESOLVED ||
38
+ code === ERROR_CODES.DEPENDENCY_VERSION_UNRESOLVED) {
39
+ return 422;
40
+ }
41
+ if (code === ERROR_CODES.REMAPPER_UNAVAILABLE ||
42
+ code === ERROR_CODES.JAVA_PROCESS_FAILED) {
43
+ return 503;
44
+ }
45
+ if (code === ERROR_CODES.LIMIT_EXCEEDED) {
46
+ return 413;
47
+ }
48
+ if (code === ERROR_CODES.REPO_FETCH_FAILED) {
49
+ return 502;
50
+ }
51
+ if (code === ERROR_CODES.DECOMPILER_UNAVAILABLE ||
52
+ code === ERROR_CODES.DECOMPILER_FAILED ||
53
+ code === ERROR_CODES.JAVA_UNAVAILABLE ||
54
+ code === ERROR_CODES.REGISTRY_GENERATION_FAILED) {
55
+ return 503;
56
+ }
57
+ return 500;
58
+ }
59
+ function extractFieldErrors(details) {
60
+ if (typeof details !== "object" || details == null)
61
+ return undefined;
62
+ const raw = details.fieldErrors;
63
+ if (!Array.isArray(raw))
64
+ return undefined;
65
+ const out = [];
66
+ for (const entry of raw) {
67
+ if (typeof entry !== "object" || entry == null)
68
+ continue;
69
+ const rec = entry;
70
+ if (typeof rec.path !== "string" || typeof rec.message !== "string")
71
+ continue;
72
+ out.push({
73
+ path: rec.path,
74
+ message: rec.message,
75
+ ...(typeof rec.code === "string" ? { code: rec.code } : {})
76
+ });
77
+ }
78
+ return out.length > 0 ? out : undefined;
79
+ }
80
+ function extractHints(details) {
81
+ if (typeof details !== "object" || details == null)
82
+ return undefined;
83
+ const next = details.nextAction;
84
+ if (typeof next !== "string" || !next.trim())
85
+ return undefined;
86
+ return [next.trim()];
87
+ }
88
+ /**
89
+ * Per-entry subset of `mapErrorToProblem` for batch tools. ZodErrors are not
90
+ * handled here (per-entry input has already cleared the batch tool's schema
91
+ * gate); the optional `suggestedCall` is attached as-is for the caller's
92
+ * synthesized retry payload.
93
+ */
94
+ export function errorToBatchEntryProblem(caughtError, instance, options) {
95
+ if (isAppError(caughtError)) {
96
+ const baseHints = extractHints(caughtError.details);
97
+ const fieldErrors = extractFieldErrors(caughtError.details);
98
+ return {
99
+ type: `https://minecraft-modding-mcp.dev/problems/${caughtError.code.toLowerCase()}`,
100
+ title: "Tool execution error",
101
+ detail: caughtError.message,
102
+ status: statusForErrorCode(caughtError.code),
103
+ code: caughtError.code,
104
+ instance,
105
+ ...(fieldErrors ? { fieldErrors } : {}),
106
+ ...(baseHints ? { hints: baseHints } : {}),
107
+ ...(options?.suggestedCall ? { suggestedCall: options.suggestedCall } : {})
108
+ };
109
+ }
110
+ // Generic-error sanitization: fixed public detail, raw message logged
111
+ // server-side keyed by `instance`. Mirrors `mapErrorToProblem` so the
112
+ // public envelope cannot leak filesystem paths, parser internals, or
113
+ // assertion text on a non-AppError path.
114
+ const rawMessage = caughtError instanceof Error ? caughtError.message : String(caughtError);
115
+ log("error", "batch.entry.unhandled", {
116
+ instance,
117
+ reason: rawMessage
118
+ });
119
+ return {
120
+ type: "https://minecraft-modding-mcp.dev/problems/internal",
121
+ title: "Internal server error",
122
+ detail: "Unexpected server error.",
123
+ status: 500,
124
+ code: ERROR_CODES.INTERNAL,
125
+ instance,
126
+ ...(options?.suggestedCall ? { suggestedCall: options.suggestedCall } : {})
127
+ };
128
+ }
129
+ export function buildBatchAbortedProblem(instance) {
130
+ return {
131
+ type: `https://minecraft-modding-mcp.dev/problems/${ERROR_CODES.BATCH_ABORTED.toLowerCase()}`,
132
+ title: "Batch aborted",
133
+ detail: "Earlier entry failed and failFast=true.",
134
+ status: 412,
135
+ code: ERROR_CODES.BATCH_ABORTED,
136
+ instance
137
+ };
138
+ }
139
+ //# sourceMappingURL=error-mapping.js.map
package/dist/errors.d.ts CHANGED
@@ -30,6 +30,12 @@ export declare const ERROR_CODES: {
30
30
  readonly JAVA_PROCESS_FAILED: "ERR_JAVA_PROCESS_FAILED";
31
31
  readonly REMAP_FAILED: "ERR_REMAP_FAILED";
32
32
  readonly REMAPPER_UNAVAILABLE: "ERR_REMAPPER_UNAVAILABLE";
33
+ readonly WORKER_RESTART: "ERR_WORKER_RESTART";
34
+ readonly MIXIN_PARSE_FAILED: "ERR_MIXIN_PARSE_FAILED";
35
+ readonly STAGE_BUDGET_PRE_PARSE: "ERR_STAGE_BUDGET_PRE_PARSE";
36
+ readonly WORKSPACE_VERSION_UNRESOLVED: "ERR_WORKSPACE_VERSION_UNRESOLVED";
37
+ readonly DEPENDENCY_VERSION_UNRESOLVED: "ERR_DEPENDENCY_VERSION_UNRESOLVED";
38
+ readonly BATCH_ABORTED: "ERR_BATCH_ABORTED";
33
39
  readonly INTERNAL: "ERR_INTERNAL";
34
40
  };
35
41
  export type ErrorCode = (typeof ERROR_CODES)[keyof typeof ERROR_CODES];
package/dist/errors.js CHANGED
@@ -30,6 +30,12 @@ export const ERROR_CODES = {
30
30
  JAVA_PROCESS_FAILED: "ERR_JAVA_PROCESS_FAILED",
31
31
  REMAP_FAILED: "ERR_REMAP_FAILED",
32
32
  REMAPPER_UNAVAILABLE: "ERR_REMAPPER_UNAVAILABLE",
33
+ WORKER_RESTART: "ERR_WORKER_RESTART",
34
+ MIXIN_PARSE_FAILED: "ERR_MIXIN_PARSE_FAILED",
35
+ STAGE_BUDGET_PRE_PARSE: "ERR_STAGE_BUDGET_PRE_PARSE",
36
+ WORKSPACE_VERSION_UNRESOLVED: "ERR_WORKSPACE_VERSION_UNRESOLVED",
37
+ DEPENDENCY_VERSION_UNRESOLVED: "ERR_DEPENDENCY_VERSION_UNRESOLVED",
38
+ BATCH_ABORTED: "ERR_BATCH_ABORTED",
33
39
  INTERNAL: "ERR_INTERNAL"
34
40
  };
35
41
  export class AppError extends Error {
package/dist/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import { SourceService } from "./source-service.js";
3
+ import { applyErrorMetaExtensions, mapErrorToProblem } from "./tool-guidance.js";
4
+ export { mapErrorToProblem, applyErrorMetaExtensions };
3
5
  declare const SERVER_VERSION: string;
4
6
  declare const server: McpServer;
5
7
  declare const config: import("./types.js").Config;