@adhisang/minecraft-modding-mcp 1.2.1 → 2.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.
- package/CHANGELOG.md +73 -0
- package/README.md +184 -64
- package/dist/cli.js +31 -4
- package/dist/compat-stdio-transport.d.ts +2 -7
- package/dist/compat-stdio-transport.js +12 -154
- package/dist/index.js +537 -202
- package/dist/json-rpc-framing.d.ts +22 -0
- package/dist/json-rpc-framing.js +168 -0
- package/dist/mapping-pipeline-service.d.ts +1 -1
- package/dist/mapping-pipeline-service.js +13 -5
- package/dist/mapping-service.d.ts +12 -4
- package/dist/mapping-service.js +222 -105
- package/dist/mcp-helpers.d.ts +10 -2
- package/dist/mcp-helpers.js +59 -5
- package/dist/minecraft-explorer-service.d.ts +1 -2
- package/dist/minecraft-explorer-service.js +120 -24
- package/dist/mixin-validator.d.ts +24 -2
- package/dist/mixin-validator.js +228 -103
- package/dist/mod-decompile-service.d.ts +5 -0
- package/dist/mod-decompile-service.js +40 -5
- package/dist/mod-remap-service.js +142 -30
- package/dist/mojang-tiny-mapping-service.js +26 -26
- package/dist/path-resolver.js +41 -4
- package/dist/registry-service.d.ts +10 -1
- package/dist/registry-service.js +154 -22
- package/dist/resources.js +7 -7
- package/dist/search-hit-accumulator.d.ts +0 -3
- package/dist/search-hit-accumulator.js +27 -6
- package/dist/source-jar-reader.js +16 -2
- package/dist/source-resolver.d.ts +1 -0
- package/dist/source-resolver.js +93 -2
- package/dist/source-service.d.ts +76 -47
- package/dist/source-service.js +1344 -763
- package/dist/stdio-supervisor.d.ts +46 -0
- package/dist/stdio-supervisor.js +349 -0
- package/dist/storage/files-repo.d.ts +3 -0
- package/dist/storage/files-repo.js +66 -1
- package/dist/storage/migrations.d.ts +1 -1
- package/dist/storage/migrations.js +6 -2
- package/dist/storage/schema.d.ts +1 -0
- package/dist/storage/schema.js +7 -0
- package/dist/symbols/symbol-extractor.js +6 -4
- package/dist/tool-execution-gate.d.ts +15 -0
- package/dist/tool-execution-gate.js +58 -0
- package/dist/tool-input.d.ts +6 -0
- package/dist/tool-input.js +64 -0
- package/dist/types.d.ts +1 -1
- package/dist/version-diff-service.js +10 -5
- package/dist/version-service.js +7 -2
- package/dist/workspace-mapping-service.js +12 -0
- package/package.json +4 -1
package/dist/index.js
CHANGED
|
@@ -3,18 +3,20 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
3
3
|
import { ZodError, z } from "zod";
|
|
4
4
|
import { CompatStdioServerTransport } from "./compat-stdio-transport.js";
|
|
5
5
|
import { objectResult } from "./mcp-helpers.js";
|
|
6
|
+
import { prepareToolInput } from "./tool-input.js";
|
|
6
7
|
import { loadConfig } from "./config.js";
|
|
7
|
-
import { ERROR_CODES, isAppError } from "./errors.js";
|
|
8
|
+
import { createError, ERROR_CODES, isAppError } from "./errors.js";
|
|
8
9
|
import { log } from "./logger.js";
|
|
9
10
|
import { applyNbtJsonPatch, nbtBase64ToTypedJson, typedJsonToNbtBase64 } from "./nbt/pipeline.js";
|
|
10
11
|
import { analyzeModJar } from "./mod-analyzer.js";
|
|
11
12
|
import { remapModJar } from "./mod-remap-service.js";
|
|
12
13
|
import { registerResources } from "./resources.js";
|
|
13
14
|
import { SourceService } from "./source-service.js";
|
|
15
|
+
import { ToolExecutionGate } from "./tool-execution-gate.js";
|
|
14
16
|
if (!process.env.NODE_ENV) {
|
|
15
17
|
process.env.NODE_ENV = "production";
|
|
16
18
|
}
|
|
17
|
-
const SOURCE_MAPPINGS = ["
|
|
19
|
+
const SOURCE_MAPPINGS = ["obfuscated", "mojang", "intermediary", "yarn"];
|
|
18
20
|
const SOURCE_PRIORITIES = ["loom-first", "maven-first"];
|
|
19
21
|
const TARGET_KINDS = ["version", "jar", "coordinate"];
|
|
20
22
|
const SEARCH_INTENTS = ["symbol", "text", "path"];
|
|
@@ -27,6 +29,16 @@ const SOURCE_MODES = ["metadata", "snippet", "full"];
|
|
|
27
29
|
const ARTIFACT_SCOPES = ["vanilla", "merged", "loader"];
|
|
28
30
|
const DECODE_COMPRESSIONS = ["none", "gzip", "auto"];
|
|
29
31
|
const ENCODE_COMPRESSIONS = ["none", "gzip"];
|
|
32
|
+
const HEAVY_TOOL_NAMES = new Set([
|
|
33
|
+
"trace-symbol-lifecycle",
|
|
34
|
+
"diff-class-signatures",
|
|
35
|
+
"compare-versions",
|
|
36
|
+
"find-mapping",
|
|
37
|
+
"resolve-method-mapping-exact",
|
|
38
|
+
"get-class-api-matrix",
|
|
39
|
+
"get-registry-data"
|
|
40
|
+
]);
|
|
41
|
+
const heavyToolExecutionGate = new ToolExecutionGate({ maxConcurrent: 1, maxQueue: 2 });
|
|
30
42
|
const nonEmptyString = z.string().trim().min(1);
|
|
31
43
|
const optionalNonEmptyString = z.string().trim().min(1).optional();
|
|
32
44
|
const optionalPositiveInt = z.number().int().positive().optional();
|
|
@@ -43,63 +55,53 @@ const sourceModeSchema = z.enum(SOURCE_MODES);
|
|
|
43
55
|
const artifactScopeSchema = z.enum(ARTIFACT_SCOPES);
|
|
44
56
|
const decodeCompressionSchema = z.enum(DECODE_COMPRESSIONS);
|
|
45
57
|
const encodeCompressionSchema = z.enum(ENCODE_COMPRESSIONS);
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
if (!hasArtifactId && !hasTargetKind) {
|
|
67
|
-
ctx.addIssue({
|
|
68
|
-
code: z.ZodIssueCode.custom,
|
|
69
|
-
message: "Either artifactId or targetKind+targetValue must be provided.",
|
|
70
|
-
path: ["artifactId"]
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
}
|
|
58
|
+
const resolveArtifactTargetSchema = z.object({
|
|
59
|
+
kind: targetKindSchema,
|
|
60
|
+
value: nonEmptyString
|
|
61
|
+
});
|
|
62
|
+
const sourceLookupTargetSchema = z.discriminatedUnion("type", [
|
|
63
|
+
z.object({
|
|
64
|
+
type: z.literal("artifact"),
|
|
65
|
+
artifactId: nonEmptyString
|
|
66
|
+
}),
|
|
67
|
+
z.object({
|
|
68
|
+
type: z.literal("resolve"),
|
|
69
|
+
kind: targetKindSchema,
|
|
70
|
+
value: nonEmptyString
|
|
71
|
+
})
|
|
72
|
+
]);
|
|
73
|
+
const RESOLVE_ARTIFACT_TARGET_DESCRIPTION = "Object with kind and value. Example: {\"kind\":\"version\",\"value\":\"1.21.10\"}. Must be an object, not a string.";
|
|
74
|
+
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.";
|
|
75
|
+
const SOURCE_SCOPE_DESCRIPTION = 'vanilla = Mojang client jar only; merged = Loom cache discovery (default); loader = currently behaves the same as "merged".';
|
|
74
76
|
const listVersionsShape = {
|
|
75
77
|
includeSnapshots: z.boolean().optional().describe("default false"),
|
|
76
78
|
limit: optionalPositiveInt.describe("default 20, max 200")
|
|
77
79
|
};
|
|
78
80
|
const listVersionsSchema = z.object(listVersionsShape);
|
|
79
81
|
const resolveArtifactShape = {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
82
|
+
target: z.object({
|
|
83
|
+
kind: targetKindSchema,
|
|
84
|
+
value: nonEmptyString
|
|
85
|
+
}).describe(RESOLVE_ARTIFACT_TARGET_DESCRIPTION),
|
|
86
|
+
mapping: sourceMappingSchema.optional().describe("obfuscated | mojang | intermediary | yarn"),
|
|
83
87
|
sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
|
|
84
88
|
allowDecompile: z.boolean().optional().describe("default true"),
|
|
85
89
|
projectPath: optionalNonEmptyString.describe("Optional workspace root path for Loom cache-assisted source resolution"),
|
|
86
|
-
scope: artifactScopeSchema.optional().describe(
|
|
87
|
-
preferProjectVersion: z.boolean().optional().describe("When true, detect MC version from gradle.properties and override
|
|
90
|
+
scope: artifactScopeSchema.optional().describe(SOURCE_SCOPE_DESCRIPTION),
|
|
91
|
+
preferProjectVersion: z.boolean().optional().describe("When true, detect MC version from gradle.properties and override target.value"),
|
|
88
92
|
strictVersion: z.boolean().optional().describe("When true, reject version-approximated results instead of returning them. Default false.")
|
|
89
93
|
};
|
|
90
94
|
const resolveArtifactSchema = z.object(resolveArtifactShape);
|
|
91
95
|
const getClassSourceShape = {
|
|
92
96
|
className: nonEmptyString,
|
|
93
97
|
mode: sourceModeSchema.optional().describe("metadata (default) = symbol outline only; snippet = source with default maxLines=200; full = entire source"),
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
targetValue: optionalNonEmptyString,
|
|
97
|
-
mapping: sourceMappingSchema.optional().describe("official | mojang | intermediary | yarn"),
|
|
98
|
+
target: sourceLookupTargetSchema.describe(SOURCE_LOOKUP_TARGET_DESCRIPTION),
|
|
99
|
+
mapping: sourceMappingSchema.optional().describe("obfuscated | mojang | intermediary | yarn"),
|
|
98
100
|
sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
|
|
99
101
|
allowDecompile: z.boolean().optional().describe("default true"),
|
|
100
102
|
projectPath: optionalNonEmptyString.describe("Optional workspace root path for Loom cache-assisted source resolution"),
|
|
101
|
-
scope: artifactScopeSchema.optional().describe(
|
|
102
|
-
preferProjectVersion: z.boolean().optional().describe("When true, detect MC version from gradle.properties and override
|
|
103
|
+
scope: artifactScopeSchema.optional().describe(SOURCE_SCOPE_DESCRIPTION),
|
|
104
|
+
preferProjectVersion: z.boolean().optional().describe("When true, detect MC version from gradle.properties and override target.value"),
|
|
103
105
|
strictVersion: z.boolean().optional().describe("When true, reject version-approximated results instead of returning them. Default false."),
|
|
104
106
|
startLine: optionalPositiveInt,
|
|
105
107
|
endLine: optionalPositiveInt,
|
|
@@ -110,11 +112,6 @@ const getClassSourceShape = {
|
|
|
110
112
|
const getClassSourceSchema = z
|
|
111
113
|
.object(getClassSourceShape)
|
|
112
114
|
.superRefine((value, ctx) => {
|
|
113
|
-
validateTargetPair({
|
|
114
|
-
artifactId: value.artifactId,
|
|
115
|
-
targetKind: value.targetKind,
|
|
116
|
-
targetValue: value.targetValue
|
|
117
|
-
}, ctx);
|
|
118
115
|
if (value.startLine !== undefined &&
|
|
119
116
|
value.endLine !== undefined &&
|
|
120
117
|
value.startLine > value.endLine) {
|
|
@@ -127,10 +124,8 @@ const getClassSourceSchema = z
|
|
|
127
124
|
});
|
|
128
125
|
const getClassMembersShape = {
|
|
129
126
|
className: nonEmptyString,
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
targetValue: optionalNonEmptyString,
|
|
133
|
-
mapping: sourceMappingSchema.optional().describe("official | mojang | intermediary | yarn (default official)"),
|
|
127
|
+
target: sourceLookupTargetSchema.describe(SOURCE_LOOKUP_TARGET_DESCRIPTION),
|
|
128
|
+
mapping: sourceMappingSchema.optional().describe("obfuscated | mojang | intermediary | yarn (default obfuscated)"),
|
|
134
129
|
sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
|
|
135
130
|
allowDecompile: z.boolean().optional().describe("default true"),
|
|
136
131
|
access: memberAccessSchema.optional().describe("public | all (default public)"),
|
|
@@ -139,19 +134,11 @@ const getClassMembersShape = {
|
|
|
139
134
|
memberPattern: optionalNonEmptyString,
|
|
140
135
|
maxMembers: optionalPositiveInt.describe("default 500, max 5000"),
|
|
141
136
|
projectPath: optionalNonEmptyString,
|
|
142
|
-
scope: artifactScopeSchema.optional().describe(
|
|
137
|
+
scope: artifactScopeSchema.optional().describe(SOURCE_SCOPE_DESCRIPTION),
|
|
143
138
|
preferProjectVersion: z.boolean().optional().describe("When true, detect MC version from gradle.properties and override version"),
|
|
144
139
|
strictVersion: z.boolean().optional().describe("When true, reject version-approximated results instead of returning them. Default false.")
|
|
145
140
|
};
|
|
146
|
-
const getClassMembersSchema = z
|
|
147
|
-
.object(getClassMembersShape)
|
|
148
|
-
.superRefine((value, ctx) => {
|
|
149
|
-
validateTargetPair({
|
|
150
|
-
artifactId: value.artifactId,
|
|
151
|
-
targetKind: value.targetKind,
|
|
152
|
-
targetValue: value.targetValue
|
|
153
|
-
}, ctx);
|
|
154
|
-
});
|
|
141
|
+
const getClassMembersSchema = z.object(getClassMembersShape);
|
|
155
142
|
const searchClassSourceShape = {
|
|
156
143
|
artifactId: nonEmptyString,
|
|
157
144
|
query: nonEmptyString,
|
|
@@ -160,14 +147,19 @@ const searchClassSourceShape = {
|
|
|
160
147
|
packagePrefix: optionalNonEmptyString,
|
|
161
148
|
fileGlob: optionalNonEmptyString,
|
|
162
149
|
symbolKind: searchSymbolKindSchema.optional().describe("class | interface | enum | record | method | field"),
|
|
163
|
-
snippetLines: optionalPositiveInt.describe("default 8, clamp 1..80"),
|
|
164
|
-
includeDefinition: z.boolean().optional().describe("default false"),
|
|
165
|
-
includeOneHop: z.boolean().optional().describe("default false"),
|
|
166
150
|
queryMode: z.enum(["auto", "token", "literal"]).optional().describe("auto (default): FTS5 with literal fallback for separator queries; token: FTS5 only; literal: substring scan only"),
|
|
167
151
|
limit: optionalPositiveInt.describe("default 20"),
|
|
168
152
|
cursor: optionalNonEmptyString
|
|
169
153
|
};
|
|
170
|
-
const searchClassSourceSchema = z.object(searchClassSourceShape)
|
|
154
|
+
const searchClassSourceSchema = z.object(searchClassSourceShape).superRefine((value, ctx) => {
|
|
155
|
+
if (value.symbolKind && value.intent && value.intent !== "symbol") {
|
|
156
|
+
ctx.addIssue({
|
|
157
|
+
code: z.ZodIssueCode.custom,
|
|
158
|
+
path: ["symbolKind"],
|
|
159
|
+
message: 'symbolKind filter is only supported when intent="symbol".'
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
});
|
|
171
163
|
const getArtifactFileShape = {
|
|
172
164
|
artifactId: nonEmptyString,
|
|
173
165
|
filePath: nonEmptyString,
|
|
@@ -186,7 +178,7 @@ const traceSymbolLifecycleShape = {
|
|
|
186
178
|
descriptor: optionalNonEmptyString.describe('optional JVM descriptor, e.g. "(I)V"'),
|
|
187
179
|
fromVersion: optionalNonEmptyString,
|
|
188
180
|
toVersion: optionalNonEmptyString,
|
|
189
|
-
mapping: sourceMappingSchema.optional().describe("
|
|
181
|
+
mapping: sourceMappingSchema.optional().describe("obfuscated | mojang | intermediary | yarn (default obfuscated)"),
|
|
190
182
|
sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
|
|
191
183
|
includeSnapshots: z.boolean().optional().describe("default false"),
|
|
192
184
|
maxVersions: optionalPositiveInt.describe("default 120, max 400"),
|
|
@@ -197,8 +189,9 @@ const diffClassSignaturesShape = {
|
|
|
197
189
|
className: nonEmptyString,
|
|
198
190
|
fromVersion: nonEmptyString,
|
|
199
191
|
toVersion: nonEmptyString,
|
|
200
|
-
mapping: sourceMappingSchema.optional().describe("
|
|
201
|
-
sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first")
|
|
192
|
+
mapping: sourceMappingSchema.optional().describe("obfuscated | mojang | intermediary | yarn (default obfuscated)"),
|
|
193
|
+
sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
|
|
194
|
+
includeFullDiff: z.boolean().optional().describe("When false, omit from/to snapshots from modified entries and keep only key+changed")
|
|
202
195
|
};
|
|
203
196
|
const diffClassSignaturesSchema = z.object(diffClassSignaturesShape);
|
|
204
197
|
const findMappingShape = {
|
|
@@ -207,8 +200,8 @@ const findMappingShape = {
|
|
|
207
200
|
name: nonEmptyString,
|
|
208
201
|
owner: optionalNonEmptyString,
|
|
209
202
|
descriptor: optionalNonEmptyString,
|
|
210
|
-
sourceMapping: sourceMappingSchema.describe("
|
|
211
|
-
targetMapping: sourceMappingSchema.describe("
|
|
203
|
+
sourceMapping: sourceMappingSchema.describe("obfuscated | mojang | intermediary | yarn"),
|
|
204
|
+
targetMapping: sourceMappingSchema.describe("obfuscated | mojang | intermediary | yarn"),
|
|
212
205
|
sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
|
|
213
206
|
disambiguation: z
|
|
214
207
|
.object({
|
|
@@ -216,7 +209,8 @@ const findMappingShape = {
|
|
|
216
209
|
descriptorHint: optionalNonEmptyString
|
|
217
210
|
})
|
|
218
211
|
.partial()
|
|
219
|
-
.optional()
|
|
212
|
+
.optional(),
|
|
213
|
+
maxCandidates: optionalPositiveInt.describe("Limit returned candidates (default 200, max 200)")
|
|
220
214
|
};
|
|
221
215
|
const findMappingSchema = z.object(findMappingShape).superRefine((value, ctx) => {
|
|
222
216
|
if (value.kind === "class") {
|
|
@@ -277,42 +271,21 @@ const findMappingSchema = z.object(findMappingShape).superRefine((value, ctx) =>
|
|
|
277
271
|
});
|
|
278
272
|
const resolveMethodMappingExactShape = {
|
|
279
273
|
version: nonEmptyString,
|
|
280
|
-
kind: workspaceSymbolKindSchema.describe("class | field | method"),
|
|
281
274
|
name: nonEmptyString,
|
|
282
|
-
owner:
|
|
283
|
-
descriptor:
|
|
284
|
-
sourceMapping: sourceMappingSchema.describe("
|
|
285
|
-
targetMapping: sourceMappingSchema.describe("
|
|
286
|
-
sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first")
|
|
275
|
+
owner: nonEmptyString,
|
|
276
|
+
descriptor: nonEmptyString.describe("required JVM descriptor"),
|
|
277
|
+
sourceMapping: sourceMappingSchema.describe("obfuscated | mojang | intermediary | yarn"),
|
|
278
|
+
targetMapping: sourceMappingSchema.describe("obfuscated | mojang | intermediary | yarn"),
|
|
279
|
+
sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
|
|
280
|
+
maxCandidates: optionalPositiveInt.describe("Limit returned candidates (default 200, max 200)")
|
|
287
281
|
};
|
|
288
282
|
const resolveMethodMappingExactSchema = z
|
|
289
283
|
.object(resolveMethodMappingExactShape)
|
|
290
284
|
.superRefine((value, ctx) => {
|
|
291
|
-
if (value.kind !== "method") {
|
|
292
|
-
ctx.addIssue({
|
|
293
|
-
code: z.ZodIssueCode.custom,
|
|
294
|
-
message: "resolve-method-mapping-exact requires kind=method.",
|
|
295
|
-
path: ["kind"]
|
|
296
|
-
});
|
|
297
|
-
}
|
|
298
|
-
if (!value.owner) {
|
|
299
|
-
ctx.addIssue({
|
|
300
|
-
code: z.ZodIssueCode.custom,
|
|
301
|
-
message: "owner is required when kind=method.",
|
|
302
|
-
path: ["owner"]
|
|
303
|
-
});
|
|
304
|
-
}
|
|
305
|
-
if (!value.descriptor) {
|
|
306
|
-
ctx.addIssue({
|
|
307
|
-
code: z.ZodIssueCode.custom,
|
|
308
|
-
message: "descriptor is required when kind=method.",
|
|
309
|
-
path: ["descriptor"]
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
285
|
if (/[\s./()]/.test(value.name)) {
|
|
313
286
|
ctx.addIssue({
|
|
314
287
|
code: z.ZodIssueCode.custom,
|
|
315
|
-
message: "name must be a simple method name
|
|
288
|
+
message: "name must be a simple method name.",
|
|
316
289
|
path: ["name"]
|
|
317
290
|
});
|
|
318
291
|
}
|
|
@@ -340,9 +313,10 @@ const classApiKindsSchema = z.string().superRefine((value, ctx) => {
|
|
|
340
313
|
const getClassApiMatrixShape = {
|
|
341
314
|
version: nonEmptyString,
|
|
342
315
|
className: nonEmptyString,
|
|
343
|
-
classNameMapping: sourceMappingSchema.describe("
|
|
316
|
+
classNameMapping: sourceMappingSchema.describe("obfuscated | mojang | intermediary | yarn"),
|
|
344
317
|
includeKinds: classApiKindsSchema.optional().describe("comma-separated: class,field,method"),
|
|
345
|
-
sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first")
|
|
318
|
+
sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
|
|
319
|
+
maxRows: optionalPositiveInt.describe("Limit returned rows (max 5000)")
|
|
346
320
|
};
|
|
347
321
|
const getClassApiMatrixSchema = z.object(getClassApiMatrixShape);
|
|
348
322
|
const resolveWorkspaceSymbolShape = {
|
|
@@ -352,8 +326,9 @@ const resolveWorkspaceSymbolShape = {
|
|
|
352
326
|
name: nonEmptyString,
|
|
353
327
|
owner: optionalNonEmptyString,
|
|
354
328
|
descriptor: optionalNonEmptyString,
|
|
355
|
-
sourceMapping: sourceMappingSchema.describe("
|
|
356
|
-
sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first")
|
|
329
|
+
sourceMapping: sourceMappingSchema.describe("obfuscated | mojang | intermediary | yarn"),
|
|
330
|
+
sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
|
|
331
|
+
maxCandidates: optionalPositiveInt.describe("Limit returned candidates for field/method lookups (default 200, max 200)")
|
|
357
332
|
};
|
|
358
333
|
const resolveWorkspaceSymbolSchema = z
|
|
359
334
|
.object(resolveWorkspaceSymbolShape)
|
|
@@ -420,11 +395,12 @@ const checkSymbolExistsShape = {
|
|
|
420
395
|
owner: optionalNonEmptyString,
|
|
421
396
|
name: nonEmptyString,
|
|
422
397
|
descriptor: optionalNonEmptyString.describe("required for kind=method unless signatureMode=name-only"),
|
|
423
|
-
sourceMapping: sourceMappingSchema.describe("
|
|
398
|
+
sourceMapping: sourceMappingSchema.describe("obfuscated | mojang | intermediary | yarn"),
|
|
424
399
|
sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
|
|
425
400
|
nameMode: classNameModeSchema.optional().describe("fqcn | auto (default fqcn)"),
|
|
426
401
|
signatureMode: z.enum(["exact", "name-only"]).optional()
|
|
427
|
-
.describe("exact (default): require descriptor for methods; name-only: match by owner+name only")
|
|
402
|
+
.describe("exact (default): require descriptor for methods; name-only: match by owner+name only"),
|
|
403
|
+
maxCandidates: optionalPositiveInt.describe("Limit returned candidates (default 200, max 200)")
|
|
428
404
|
};
|
|
429
405
|
const checkSymbolExistsSchema = z.object(checkSymbolExistsShape).superRefine((value, ctx) => {
|
|
430
406
|
if (value.kind === "class") {
|
|
@@ -511,17 +487,34 @@ const indexArtifactShape = {
|
|
|
511
487
|
};
|
|
512
488
|
const indexArtifactSchema = z.object(indexArtifactShape);
|
|
513
489
|
const validateMixinShape = {
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
490
|
+
input: z.discriminatedUnion("mode", [
|
|
491
|
+
z.object({
|
|
492
|
+
mode: z.literal("inline"),
|
|
493
|
+
source: nonEmptyString.describe("Mixin Java source text")
|
|
494
|
+
}),
|
|
495
|
+
z.object({
|
|
496
|
+
mode: z.literal("path"),
|
|
497
|
+
path: nonEmptyString.describe("Path to a Mixin .java file")
|
|
498
|
+
}),
|
|
499
|
+
z.object({
|
|
500
|
+
mode: z.literal("paths"),
|
|
501
|
+
paths: z.array(nonEmptyString).min(1).describe("Array of Mixin .java file paths for batch validation")
|
|
502
|
+
}),
|
|
503
|
+
z.object({
|
|
504
|
+
mode: z.literal("config"),
|
|
505
|
+
configPaths: z.array(nonEmptyString).min(1).describe("Path array to mixin config JSON files (e.g. modid.mixins.json)")
|
|
506
|
+
}),
|
|
507
|
+
z.object({
|
|
508
|
+
mode: z.literal("project"),
|
|
509
|
+
path: nonEmptyString.describe("Workspace root path used to discover *.mixins.json files automatically")
|
|
510
|
+
})
|
|
511
|
+
]).describe("One of { mode: 'inline', source }, { mode: 'path', path }, { mode: 'paths', paths[] }, { mode: 'config', configPaths[] }, or { mode: 'project', path }."),
|
|
519
512
|
sourceRoots: z.array(z.string().min(1)).optional()
|
|
520
513
|
.describe("Array of source roots for multi-module projects (e.g. ['common/src/main/java', 'neoforge/src/main/java'])"),
|
|
521
514
|
version: nonEmptyString.describe("Minecraft version"),
|
|
522
|
-
mapping: sourceMappingSchema.optional().describe("
|
|
515
|
+
mapping: sourceMappingSchema.optional().describe("obfuscated | mojang | intermediary | yarn"),
|
|
523
516
|
sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first"),
|
|
524
|
-
scope: artifactScopeSchema.optional().describe(
|
|
517
|
+
scope: artifactScopeSchema.optional().describe(SOURCE_SCOPE_DESCRIPTION),
|
|
525
518
|
projectPath: optionalNonEmptyString.describe("Optional workspace root path for Loom cache-assisted source resolution"),
|
|
526
519
|
preferProjectVersion: z.boolean().optional().describe("When true, detect MC version from gradle.properties and override version"),
|
|
527
520
|
minSeverity: z.enum(["error", "warning", "all"]).optional()
|
|
@@ -534,25 +527,20 @@ const validateMixinShape = {
|
|
|
534
527
|
.describe("'full'=all warnings (default), 'aggregated'=group warnings by category with counts and samples"),
|
|
535
528
|
preferProjectMapping: z.boolean().optional()
|
|
536
529
|
.describe("When true, auto-detect mapping from project config even if mapping is explicitly provided"),
|
|
537
|
-
reportMode: z.enum(["compact", "full"]).optional()
|
|
538
|
-
.describe("'compact' omits
|
|
530
|
+
reportMode: z.enum(["compact", "full", "summary-first"]).optional()
|
|
531
|
+
.describe("'compact' omits heavy per-result detail, 'summary-first' hoists shared provenance/warnings/incomplete reasons, 'full'=everything (default)"),
|
|
539
532
|
warningCategoryFilter: z.array(z.enum(["mapping", "configuration", "validation", "resolution", "parse"])).optional()
|
|
540
533
|
.describe("Only include warnings/issues matching these categories (default: all)"),
|
|
541
534
|
treatInfoAsWarning: z.boolean().optional()
|
|
542
|
-
.describe("When false, suppress info-severity structured warnings from output (default true)")
|
|
535
|
+
.describe("When false, suppress info-severity structured warnings from output (default true)"),
|
|
536
|
+
includeIssues: z.boolean().optional()
|
|
537
|
+
.describe("When false, keep summary fields but omit per-result issues[] payloads")
|
|
543
538
|
};
|
|
544
|
-
const validateMixinSchema = z.object(validateMixinShape)
|
|
545
|
-
const hasSource = d.source != null;
|
|
546
|
-
const hasSourcePath = d.sourcePath != null;
|
|
547
|
-
const hasSourcePaths = d.sourcePaths != null && d.sourcePaths.length > 0;
|
|
548
|
-
const hasMixinConfig = d.mixinConfigPath != null && (typeof d.mixinConfigPath === "string" || (Array.isArray(d.mixinConfigPath) && d.mixinConfigPath.length > 0));
|
|
549
|
-
// Exactly one of the four must be provided
|
|
550
|
-
return [hasSource, hasSourcePath, hasSourcePaths, hasMixinConfig].filter(Boolean).length === 1;
|
|
551
|
-
}, { message: "Exactly one of 'source', 'sourcePath', 'sourcePaths', or 'mixinConfigPath' must be provided." });
|
|
539
|
+
const validateMixinSchema = z.object(validateMixinShape);
|
|
552
540
|
const validateAccessWidenerShape = {
|
|
553
541
|
content: nonEmptyString.describe("Access Widener file content"),
|
|
554
542
|
version: nonEmptyString.describe("Minecraft version"),
|
|
555
|
-
mapping: sourceMappingSchema.optional().describe("
|
|
543
|
+
mapping: sourceMappingSchema.optional().describe("obfuscated | mojang | intermediary | yarn"),
|
|
556
544
|
sourcePriority: mappingSourcePrioritySchema.optional().describe("loom-first | maven-first")
|
|
557
545
|
};
|
|
558
546
|
const validateAccessWidenerSchema = z.object(validateAccessWidenerShape);
|
|
@@ -563,7 +551,9 @@ const analyzeModJarShape = {
|
|
|
563
551
|
const analyzeModJarSchema = z.object(analyzeModJarShape);
|
|
564
552
|
const getRegistryDataShape = {
|
|
565
553
|
version: nonEmptyString.describe("Minecraft version (e.g. 1.21)"),
|
|
566
|
-
registry: optionalNonEmptyString.describe('Optional registry name (e.g. "block", "item", "minecraft:biome"). Omit to list all registries.')
|
|
554
|
+
registry: optionalNonEmptyString.describe('Optional registry name (e.g. "block", "item", "minecraft:biome"). Omit to list all registries.'),
|
|
555
|
+
includeData: z.boolean().optional().describe("When false, return registry names/counts without full entry bodies"),
|
|
556
|
+
maxEntriesPerRegistry: optionalPositiveInt.describe("Limit returned entries per registry body")
|
|
567
557
|
};
|
|
568
558
|
const getRegistryDataSchema = z.object(getRegistryDataShape);
|
|
569
559
|
const COMPARE_VERSIONS_CATEGORIES = ["classes", "registry", "all"];
|
|
@@ -578,7 +568,9 @@ const compareVersionsShape = {
|
|
|
578
568
|
const compareVersionsSchema = z.object(compareVersionsShape);
|
|
579
569
|
const decompileModJarShape = {
|
|
580
570
|
jarPath: nonEmptyString.describe("Local path to the mod JAR file"),
|
|
581
|
-
className: optionalNonEmptyString.describe("Optional fully-qualified class name to view source. Omit to list all classes.")
|
|
571
|
+
className: optionalNonEmptyString.describe("Optional fully-qualified class name to view source. Omit to list all classes."),
|
|
572
|
+
includeFiles: z.boolean().optional().describe("When false, omit the full class list and return counts only"),
|
|
573
|
+
maxFiles: optionalPositiveInt.describe("Limit returned class names when files are included")
|
|
582
574
|
};
|
|
583
575
|
const decompileModJarSchema = z.object(decompileModJarShape);
|
|
584
576
|
const getModClassSourceShape = {
|
|
@@ -626,6 +618,9 @@ const server = new McpServer({
|
|
|
626
618
|
name: "@adhisang/minecraft-modding-mcp",
|
|
627
619
|
version: SERVER_VERSION
|
|
628
620
|
});
|
|
621
|
+
// The SDK validates tool args before invoking handlers and returns generic InvalidParams text.
|
|
622
|
+
// Bypass that layer so runTool() remains the single source of truth for validation and error envelopes.
|
|
623
|
+
server.validateToolInput = async (_tool, args) => args;
|
|
629
624
|
const config = loadConfig();
|
|
630
625
|
const nbtLimits = {
|
|
631
626
|
maxInputBytes: config.maxNbtInputBytes,
|
|
@@ -672,11 +667,16 @@ function attachProcessErrorHandlers() {
|
|
|
672
667
|
function buildRequestId() {
|
|
673
668
|
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
674
669
|
}
|
|
675
|
-
function
|
|
676
|
-
if (
|
|
677
|
-
return
|
|
670
|
+
function normalizeSourceLookupTarget(target) {
|
|
671
|
+
if (target.type === "artifact") {
|
|
672
|
+
return { artifactId: target.artifactId };
|
|
678
673
|
}
|
|
679
|
-
return {
|
|
674
|
+
return {
|
|
675
|
+
target: {
|
|
676
|
+
kind: target.kind,
|
|
677
|
+
value: target.value
|
|
678
|
+
}
|
|
679
|
+
};
|
|
680
680
|
}
|
|
681
681
|
function parseClassApiKinds(value) {
|
|
682
682
|
if (value == null) {
|
|
@@ -801,8 +801,314 @@ function extractFieldErrorsFromDetails(details) {
|
|
|
801
801
|
.filter((entry) => entry != null);
|
|
802
802
|
return normalized.length > 0 ? normalized : undefined;
|
|
803
803
|
}
|
|
804
|
-
function
|
|
804
|
+
function asObjectRecord(value) {
|
|
805
|
+
return typeof value === "object" && value != null && !Array.isArray(value)
|
|
806
|
+
? value
|
|
807
|
+
: undefined;
|
|
808
|
+
}
|
|
809
|
+
function asNonEmptyString(value) {
|
|
810
|
+
return typeof value === "string" && value.trim() ? value : undefined;
|
|
811
|
+
}
|
|
812
|
+
function asStringArray(value) {
|
|
813
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === "string" && entry.trim())
|
|
814
|
+
? value
|
|
815
|
+
: undefined;
|
|
816
|
+
}
|
|
817
|
+
function truncateSuggestionText(value, maxLength = 500) {
|
|
818
|
+
return value.length > maxLength
|
|
819
|
+
? `${value.slice(0, maxLength)}...`
|
|
820
|
+
: value;
|
|
821
|
+
}
|
|
822
|
+
function parseJsonObjectString(value) {
|
|
823
|
+
if (!value.trim().startsWith("{")) {
|
|
824
|
+
return undefined;
|
|
825
|
+
}
|
|
826
|
+
try {
|
|
827
|
+
const parsed = JSON.parse(value);
|
|
828
|
+
return asObjectRecord(parsed);
|
|
829
|
+
}
|
|
830
|
+
catch {
|
|
831
|
+
return undefined;
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
function inferTargetKindFromString(value) {
|
|
835
|
+
if (/[\\/]/.test(value) || /\.jar$/i.test(value)) {
|
|
836
|
+
return "jar";
|
|
837
|
+
}
|
|
838
|
+
if (value.split(":").length >= 3) {
|
|
839
|
+
return "coordinate";
|
|
840
|
+
}
|
|
841
|
+
return "version";
|
|
842
|
+
}
|
|
843
|
+
function copySourceLookupSuggestionFields(tool, source) {
|
|
844
|
+
const result = {};
|
|
845
|
+
const stringFields = tool === "get-class-source"
|
|
846
|
+
? ["className", "mode", "mapping", "sourcePriority", "projectPath", "scope", "outputFile"]
|
|
847
|
+
: ["className", "mapping", "sourcePriority", "projectPath", "scope", "access", "memberPattern"];
|
|
848
|
+
for (const field of stringFields) {
|
|
849
|
+
const value = source[field];
|
|
850
|
+
if (typeof value === "string" && value.trim()) {
|
|
851
|
+
result[field] = value;
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
const numericFields = tool === "get-class-source"
|
|
855
|
+
? ["startLine", "endLine", "maxLines", "maxChars"]
|
|
856
|
+
: ["maxMembers"];
|
|
857
|
+
for (const field of numericFields) {
|
|
858
|
+
const value = source[field];
|
|
859
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
860
|
+
result[field] = value;
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
const booleanFields = tool === "get-class-source"
|
|
864
|
+
? ["allowDecompile", "preferProjectVersion", "strictVersion"]
|
|
865
|
+
: ["allowDecompile", "preferProjectVersion", "strictVersion", "includeSynthetic", "includeInherited"];
|
|
866
|
+
for (const field of booleanFields) {
|
|
867
|
+
const value = source[field];
|
|
868
|
+
if (typeof value === "boolean") {
|
|
869
|
+
result[field] = value;
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
return result;
|
|
873
|
+
}
|
|
874
|
+
function copyValidateMixinSharedParams(source) {
|
|
875
|
+
const result = {};
|
|
876
|
+
const stringFields = [
|
|
877
|
+
"version",
|
|
878
|
+
"mapping",
|
|
879
|
+
"sourcePriority",
|
|
880
|
+
"scope",
|
|
881
|
+
"projectPath",
|
|
882
|
+
"minSeverity",
|
|
883
|
+
"warningMode",
|
|
884
|
+
"reportMode"
|
|
885
|
+
];
|
|
886
|
+
for (const field of stringFields) {
|
|
887
|
+
const value = source[field];
|
|
888
|
+
if (typeof value === "string" && value.trim()) {
|
|
889
|
+
result[field] = value;
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
const booleanFields = [
|
|
893
|
+
"preferProjectVersion",
|
|
894
|
+
"hideUncertain",
|
|
895
|
+
"explain",
|
|
896
|
+
"preferProjectMapping",
|
|
897
|
+
"treatInfoAsWarning",
|
|
898
|
+
"includeIssues"
|
|
899
|
+
];
|
|
900
|
+
for (const field of booleanFields) {
|
|
901
|
+
const value = source[field];
|
|
902
|
+
if (typeof value === "boolean") {
|
|
903
|
+
result[field] = value;
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
const sourceRoots = asStringArray(source.sourceRoots);
|
|
907
|
+
if (sourceRoots) {
|
|
908
|
+
result.sourceRoots = sourceRoots;
|
|
909
|
+
}
|
|
910
|
+
const warningCategoryFilter = asStringArray(source.warningCategoryFilter);
|
|
911
|
+
if (warningCategoryFilter) {
|
|
912
|
+
result.warningCategoryFilter = warningCategoryFilter;
|
|
913
|
+
}
|
|
914
|
+
return result;
|
|
915
|
+
}
|
|
916
|
+
function buildValidateMixinSuggestedParams(normalizedInput) {
|
|
917
|
+
const record = asObjectRecord(normalizedInput);
|
|
918
|
+
if (!record) {
|
|
919
|
+
return {
|
|
920
|
+
input: {
|
|
921
|
+
mode: "inline",
|
|
922
|
+
source: "<Mixin Java source>"
|
|
923
|
+
},
|
|
924
|
+
version: "<minecraft-version>"
|
|
925
|
+
};
|
|
926
|
+
}
|
|
927
|
+
const inputRecord = asObjectRecord(record.input);
|
|
928
|
+
const shared = copyValidateMixinSharedParams(record);
|
|
929
|
+
const version = asNonEmptyString(record.version) ?? "<minecraft-version>";
|
|
930
|
+
const inlineSource = asNonEmptyString(record.input) ??
|
|
931
|
+
asNonEmptyString(inputRecord?.source) ??
|
|
932
|
+
asNonEmptyString(record.source);
|
|
933
|
+
if (inlineSource) {
|
|
934
|
+
const parsedInlineObject = parseJsonObjectString(inlineSource);
|
|
935
|
+
if (parsedInlineObject && typeof parsedInlineObject.mode === "string") {
|
|
936
|
+
return {
|
|
937
|
+
...shared,
|
|
938
|
+
input: parsedInlineObject,
|
|
939
|
+
version
|
|
940
|
+
};
|
|
941
|
+
}
|
|
942
|
+
return {
|
|
943
|
+
...shared,
|
|
944
|
+
input: {
|
|
945
|
+
mode: "inline",
|
|
946
|
+
source: truncateSuggestionText(inlineSource)
|
|
947
|
+
},
|
|
948
|
+
version
|
|
949
|
+
};
|
|
950
|
+
}
|
|
951
|
+
const path = asNonEmptyString(inputRecord?.path) ??
|
|
952
|
+
asNonEmptyString(record.sourcePath);
|
|
953
|
+
if (path) {
|
|
954
|
+
return {
|
|
955
|
+
...shared,
|
|
956
|
+
input: {
|
|
957
|
+
mode: "path",
|
|
958
|
+
path
|
|
959
|
+
},
|
|
960
|
+
version
|
|
961
|
+
};
|
|
962
|
+
}
|
|
963
|
+
const paths = asStringArray(inputRecord?.paths) ??
|
|
964
|
+
asStringArray(record.sourcePaths);
|
|
965
|
+
if (paths) {
|
|
966
|
+
return {
|
|
967
|
+
...shared,
|
|
968
|
+
input: {
|
|
969
|
+
mode: "paths",
|
|
970
|
+
paths
|
|
971
|
+
},
|
|
972
|
+
version
|
|
973
|
+
};
|
|
974
|
+
}
|
|
975
|
+
const configPaths = asStringArray(inputRecord?.configPaths) ??
|
|
976
|
+
(asNonEmptyString(record.mixinConfigPath) ? [record.mixinConfigPath] : undefined);
|
|
977
|
+
if (configPaths) {
|
|
978
|
+
return {
|
|
979
|
+
...shared,
|
|
980
|
+
input: {
|
|
981
|
+
mode: "config",
|
|
982
|
+
configPaths
|
|
983
|
+
},
|
|
984
|
+
version
|
|
985
|
+
};
|
|
986
|
+
}
|
|
987
|
+
const projectPath = asNonEmptyString(record.projectPath) ??
|
|
988
|
+
(inputRecord?.mode === "project" ? asNonEmptyString(inputRecord.path) : undefined);
|
|
989
|
+
if (projectPath) {
|
|
990
|
+
return {
|
|
991
|
+
...shared,
|
|
992
|
+
input: {
|
|
993
|
+
mode: "project",
|
|
994
|
+
path: projectPath
|
|
995
|
+
},
|
|
996
|
+
version
|
|
997
|
+
};
|
|
998
|
+
}
|
|
999
|
+
return {
|
|
1000
|
+
...shared,
|
|
1001
|
+
input: {
|
|
1002
|
+
mode: "inline",
|
|
1003
|
+
source: "<Mixin Java source>"
|
|
1004
|
+
},
|
|
1005
|
+
version
|
|
1006
|
+
};
|
|
1007
|
+
}
|
|
1008
|
+
function buildResolveArtifactSuggestedParams(normalizedInput) {
|
|
1009
|
+
const record = asObjectRecord(normalizedInput);
|
|
1010
|
+
if (!record) {
|
|
1011
|
+
return {
|
|
1012
|
+
target: {
|
|
1013
|
+
kind: "version",
|
|
1014
|
+
value: "<minecraft-version>"
|
|
1015
|
+
}
|
|
1016
|
+
};
|
|
1017
|
+
}
|
|
1018
|
+
const targetValue = asNonEmptyString(record.target);
|
|
1019
|
+
const result = {
|
|
1020
|
+
target: targetValue
|
|
1021
|
+
? {
|
|
1022
|
+
kind: inferTargetKindFromString(targetValue),
|
|
1023
|
+
value: targetValue
|
|
1024
|
+
}
|
|
1025
|
+
: {
|
|
1026
|
+
kind: "version",
|
|
1027
|
+
value: "<minecraft-version>"
|
|
1028
|
+
}
|
|
1029
|
+
};
|
|
1030
|
+
const stringFields = ["mapping", "sourcePriority", "projectPath", "scope"];
|
|
1031
|
+
for (const field of stringFields) {
|
|
1032
|
+
const value = record[field];
|
|
1033
|
+
if (typeof value === "string" && value.trim()) {
|
|
1034
|
+
result[field] = value;
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
const booleanFields = ["allowDecompile", "preferProjectVersion", "strictVersion"];
|
|
1038
|
+
for (const field of booleanFields) {
|
|
1039
|
+
const value = record[field];
|
|
1040
|
+
if (typeof value === "boolean") {
|
|
1041
|
+
result[field] = value;
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
return result;
|
|
1045
|
+
}
|
|
1046
|
+
function buildSourceLookupSuggestedParams(tool, normalizedInput) {
|
|
1047
|
+
const record = asObjectRecord(normalizedInput);
|
|
1048
|
+
const result = record ? copySourceLookupSuggestionFields(tool, record) : {};
|
|
1049
|
+
const targetValue = asNonEmptyString(record?.target);
|
|
1050
|
+
result.target = targetValue
|
|
1051
|
+
? {
|
|
1052
|
+
type: "resolve",
|
|
1053
|
+
kind: inferTargetKindFromString(targetValue),
|
|
1054
|
+
value: targetValue
|
|
1055
|
+
}
|
|
1056
|
+
: {
|
|
1057
|
+
type: "resolve",
|
|
1058
|
+
kind: "version",
|
|
1059
|
+
value: "<minecraft-version>"
|
|
1060
|
+
};
|
|
1061
|
+
if (!asNonEmptyString(result.className)) {
|
|
1062
|
+
result.className = "<fully-qualified-class-name>";
|
|
1063
|
+
}
|
|
1064
|
+
return result;
|
|
1065
|
+
}
|
|
1066
|
+
function buildInvalidInputGuidance(tool, normalizedInput) {
|
|
1067
|
+
if (tool === "validate-mixin") {
|
|
1068
|
+
const hints = [
|
|
1069
|
+
"validate-mixin.input must be an object with input.mode = \"inline\" | \"path\" | \"paths\" | \"config\" | \"project\".",
|
|
1070
|
+
"Whole-project example: {\"input\":{\"mode\":\"project\",\"path\":\"/workspace\"},\"version\":\"1.21.10\",\"preferProjectVersion\":true,\"preferProjectMapping\":true}.",
|
|
1071
|
+
"Legacy top-level source/sourcePath/sourcePaths/mixinConfigPath fields are no longer accepted; wrap them under input.mode instead."
|
|
1072
|
+
];
|
|
1073
|
+
return {
|
|
1074
|
+
hints,
|
|
1075
|
+
suggestedCall: {
|
|
1076
|
+
tool,
|
|
1077
|
+
params: buildValidateMixinSuggestedParams(normalizedInput)
|
|
1078
|
+
}
|
|
1079
|
+
};
|
|
1080
|
+
}
|
|
1081
|
+
if (tool === "resolve-artifact") {
|
|
1082
|
+
return {
|
|
1083
|
+
hints: [
|
|
1084
|
+
"resolve-artifact.target must be an object: {\"kind\":\"version|jar|coordinate\",\"value\":\"...\"}.",
|
|
1085
|
+
"Bare string targets are not accepted; wrap the value under target.kind and target.value."
|
|
1086
|
+
],
|
|
1087
|
+
suggestedCall: {
|
|
1088
|
+
tool,
|
|
1089
|
+
params: buildResolveArtifactSuggestedParams(normalizedInput)
|
|
1090
|
+
}
|
|
1091
|
+
};
|
|
1092
|
+
}
|
|
1093
|
+
if (tool === "get-class-source" || tool === "get-class-members") {
|
|
1094
|
+
return {
|
|
1095
|
+
hints: [
|
|
1096
|
+
`${tool}.target must be an object: {"type":"resolve","kind":"version|jar|coordinate","value":"..."} or {"type":"artifact","artifactId":"..."}.`,
|
|
1097
|
+
"Bare string targets are not accepted; wrap the value under target.type/target.kind/target.value."
|
|
1098
|
+
],
|
|
1099
|
+
suggestedCall: {
|
|
1100
|
+
tool,
|
|
1101
|
+
params: buildSourceLookupSuggestedParams(tool, normalizedInput)
|
|
1102
|
+
}
|
|
1103
|
+
};
|
|
1104
|
+
}
|
|
1105
|
+
return undefined;
|
|
1106
|
+
}
|
|
1107
|
+
function mapErrorToProblem(caughtError, requestId, context) {
|
|
805
1108
|
if (caughtError instanceof ZodError) {
|
|
1109
|
+
const guidance = context?.tool
|
|
1110
|
+
? buildInvalidInputGuidance(context.tool, context.normalizedInput)
|
|
1111
|
+
: undefined;
|
|
806
1112
|
return {
|
|
807
1113
|
type: "https://minecraft-modding-mcp.dev/problems/invalid-input",
|
|
808
1114
|
title: "Invalid input",
|
|
@@ -811,7 +1117,8 @@ function mapErrorToProblem(caughtError, requestId) {
|
|
|
811
1117
|
code: ERROR_CODES.INVALID_INPUT,
|
|
812
1118
|
instance: requestId,
|
|
813
1119
|
fieldErrors: toFieldErrorsFromZod(caughtError),
|
|
814
|
-
hints: ["Check fieldErrors and submit a valid tool argument payload."]
|
|
1120
|
+
hints: guidance?.hints ?? ["Check fieldErrors and submit a valid tool argument payload."],
|
|
1121
|
+
...(guidance?.suggestedCall ? { suggestedCall: guidance.suggestedCall } : {})
|
|
815
1122
|
};
|
|
816
1123
|
}
|
|
817
1124
|
if (isAppError(caughtError)) {
|
|
@@ -856,9 +1163,35 @@ function splitWarnings(data) {
|
|
|
856
1163
|
async function runTool(tool, rawInput, schema, action) {
|
|
857
1164
|
const requestId = buildRequestId();
|
|
858
1165
|
const startedAt = Date.now();
|
|
1166
|
+
let normalizedInput = rawInput;
|
|
859
1167
|
try {
|
|
860
|
-
const
|
|
861
|
-
|
|
1168
|
+
const preparedInput = prepareToolInput(rawInput);
|
|
1169
|
+
normalizedInput = preparedInput.normalizedInput;
|
|
1170
|
+
const { removedOfficialPaths, suggestedReplacementInput } = preparedInput;
|
|
1171
|
+
if (removedOfficialPaths.length > 0) {
|
|
1172
|
+
throw createError({
|
|
1173
|
+
code: ERROR_CODES.INVALID_INPUT,
|
|
1174
|
+
message: `The "official" mapping namespace was removed. Use "obfuscated" instead.`,
|
|
1175
|
+
details: {
|
|
1176
|
+
fieldErrors: removedOfficialPaths.map((path) => ({
|
|
1177
|
+
path,
|
|
1178
|
+
message: `"official" is no longer supported for this field. Use "obfuscated".`,
|
|
1179
|
+
code: "invalid_enum_value"
|
|
1180
|
+
})),
|
|
1181
|
+
nextAction: `Replace "official" with "obfuscated" in mapping-related fields and retry.`,
|
|
1182
|
+
suggestedCall: suggestedReplacementInput
|
|
1183
|
+
? {
|
|
1184
|
+
tool,
|
|
1185
|
+
params: suggestedReplacementInput
|
|
1186
|
+
}
|
|
1187
|
+
: undefined
|
|
1188
|
+
}
|
|
1189
|
+
});
|
|
1190
|
+
}
|
|
1191
|
+
const parsedInput = schema.parse(normalizedInput);
|
|
1192
|
+
const payload = await (HEAVY_TOOL_NAMES.has(tool)
|
|
1193
|
+
? heavyToolExecutionGate.run(tool, () => action(parsedInput))
|
|
1194
|
+
: action(parsedInput));
|
|
862
1195
|
const { result, warnings } = splitWarnings(payload);
|
|
863
1196
|
return objectResult({
|
|
864
1197
|
result,
|
|
@@ -871,7 +1204,10 @@ async function runTool(tool, rawInput, schema, action) {
|
|
|
871
1204
|
});
|
|
872
1205
|
}
|
|
873
1206
|
catch (caughtError) {
|
|
874
|
-
const problem = mapErrorToProblem(caughtError, requestId
|
|
1207
|
+
const problem = mapErrorToProblem(caughtError, requestId, {
|
|
1208
|
+
tool,
|
|
1209
|
+
normalizedInput
|
|
1210
|
+
});
|
|
875
1211
|
if (isAppError(caughtError)) {
|
|
876
1212
|
const isSevere = caughtError.code === ERROR_CODES.DB_FAILURE ||
|
|
877
1213
|
caughtError.code === ERROR_CODES.REPO_FETCH_FAILED ||
|
|
@@ -910,18 +1246,15 @@ async function runTool(tool, rawInput, schema, action) {
|
|
|
910
1246
|
durationMs: Date.now() - startedAt,
|
|
911
1247
|
warnings: []
|
|
912
1248
|
}
|
|
913
|
-
});
|
|
1249
|
+
}, { isError: true });
|
|
914
1250
|
}
|
|
915
1251
|
}
|
|
916
1252
|
server.tool("list-versions", "List available Minecraft versions from Mojang manifest and locally cached version jars.", listVersionsShape, { readOnlyHint: true }, async (args) => runTool("list-versions", args, listVersionsSchema, async (input) => sourceService.listVersions({
|
|
917
1253
|
includeSnapshots: input.includeSnapshots,
|
|
918
1254
|
limit: input.limit
|
|
919
1255
|
})));
|
|
920
|
-
server.tool("resolve-artifact", "Resolve source artifact from
|
|
921
|
-
target:
|
|
922
|
-
kind: input.targetKind,
|
|
923
|
-
value: input.targetValue
|
|
924
|
-
},
|
|
1256
|
+
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({
|
|
1257
|
+
target: input.target,
|
|
925
1258
|
mapping: input.mapping,
|
|
926
1259
|
sourcePriority: input.sourcePriority,
|
|
927
1260
|
allowDecompile: input.allowDecompile,
|
|
@@ -941,42 +1274,48 @@ server.tool("find-class", "Resolve a simple or qualified class name to fully-qua
|
|
|
941
1274
|
artifactId: input.artifactId,
|
|
942
1275
|
limit: input.limit
|
|
943
1276
|
})));
|
|
944
|
-
server.tool("get-class-source", "Get Java source for a class by artifactId or
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
1277
|
+
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) => {
|
|
1278
|
+
const normalizedTarget = normalizeSourceLookupTarget(input.target);
|
|
1279
|
+
return sourceService.getClassSource({
|
|
1280
|
+
className: input.className,
|
|
1281
|
+
mode: input.mode,
|
|
1282
|
+
artifactId: normalizedTarget.artifactId,
|
|
1283
|
+
target: normalizedTarget.target,
|
|
1284
|
+
mapping: input.mapping,
|
|
1285
|
+
sourcePriority: input.sourcePriority,
|
|
1286
|
+
allowDecompile: input.allowDecompile,
|
|
1287
|
+
projectPath: input.projectPath,
|
|
1288
|
+
scope: input.scope,
|
|
1289
|
+
preferProjectVersion: input.preferProjectVersion,
|
|
1290
|
+
strictVersion: input.strictVersion,
|
|
1291
|
+
startLine: input.startLine,
|
|
1292
|
+
endLine: input.endLine,
|
|
1293
|
+
maxLines: input.maxLines,
|
|
1294
|
+
maxChars: input.maxChars,
|
|
1295
|
+
outputFile: input.outputFile
|
|
1296
|
+
});
|
|
1297
|
+
}));
|
|
1298
|
+
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) => {
|
|
1299
|
+
const normalizedTarget = normalizeSourceLookupTarget(input.target);
|
|
1300
|
+
return sourceService.getClassMembers({
|
|
1301
|
+
className: input.className,
|
|
1302
|
+
artifactId: normalizedTarget.artifactId,
|
|
1303
|
+
target: normalizedTarget.target,
|
|
1304
|
+
mapping: input.mapping,
|
|
1305
|
+
sourcePriority: input.sourcePriority,
|
|
1306
|
+
allowDecompile: input.allowDecompile,
|
|
1307
|
+
access: input.access,
|
|
1308
|
+
includeSynthetic: input.includeSynthetic,
|
|
1309
|
+
includeInherited: input.includeInherited,
|
|
1310
|
+
memberPattern: input.memberPattern,
|
|
1311
|
+
maxMembers: input.maxMembers,
|
|
1312
|
+
projectPath: input.projectPath,
|
|
1313
|
+
scope: input.scope,
|
|
1314
|
+
preferProjectVersion: input.preferProjectVersion,
|
|
1315
|
+
strictVersion: input.strictVersion
|
|
1316
|
+
});
|
|
1317
|
+
}));
|
|
1318
|
+
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) => {
|
|
980
1319
|
const scope = input.packagePrefix || input.fileGlob || input.symbolKind
|
|
981
1320
|
? {
|
|
982
1321
|
packagePrefix: input.packagePrefix,
|
|
@@ -984,22 +1323,12 @@ server.tool("search-class-source", "Search indexed class source files for one ar
|
|
|
984
1323
|
symbolKind: input.symbolKind
|
|
985
1324
|
}
|
|
986
1325
|
: undefined;
|
|
987
|
-
const include = input.snippetLines !== undefined ||
|
|
988
|
-
input.includeDefinition !== undefined ||
|
|
989
|
-
input.includeOneHop !== undefined
|
|
990
|
-
? {
|
|
991
|
-
snippetLines: input.snippetLines,
|
|
992
|
-
includeDefinition: input.includeDefinition,
|
|
993
|
-
includeOneHop: input.includeOneHop
|
|
994
|
-
}
|
|
995
|
-
: undefined;
|
|
996
1326
|
return sourceService.searchClassSource({
|
|
997
1327
|
artifactId: input.artifactId,
|
|
998
1328
|
query: input.query,
|
|
999
1329
|
intent: input.intent,
|
|
1000
1330
|
match: input.match,
|
|
1001
1331
|
scope: scope,
|
|
1002
|
-
include,
|
|
1003
1332
|
queryMode: input.queryMode,
|
|
1004
1333
|
limit: input.limit,
|
|
1005
1334
|
cursor: input.cursor
|
|
@@ -1027,7 +1356,8 @@ server.tool("diff-class-signatures", "Compare one class signature between two Mi
|
|
|
1027
1356
|
fromVersion: input.fromVersion,
|
|
1028
1357
|
toVersion: input.toVersion,
|
|
1029
1358
|
mapping: input.mapping,
|
|
1030
|
-
sourcePriority: input.sourcePriority
|
|
1359
|
+
sourcePriority: input.sourcePriority,
|
|
1360
|
+
includeFullDiff: input.includeFullDiff
|
|
1031
1361
|
})));
|
|
1032
1362
|
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({
|
|
1033
1363
|
version: input.version,
|
|
@@ -1038,24 +1368,26 @@ server.tool("find-mapping", "Find symbol mapping candidates between namespaces u
|
|
|
1038
1368
|
sourceMapping: input.sourceMapping,
|
|
1039
1369
|
targetMapping: input.targetMapping,
|
|
1040
1370
|
sourcePriority: input.sourcePriority,
|
|
1041
|
-
disambiguation: input.disambiguation
|
|
1371
|
+
disambiguation: input.disambiguation,
|
|
1372
|
+
maxCandidates: input.maxCandidates
|
|
1042
1373
|
})));
|
|
1043
1374
|
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({
|
|
1044
1375
|
version: input.version,
|
|
1045
|
-
kind: input.kind,
|
|
1046
1376
|
name: input.name,
|
|
1047
1377
|
owner: input.owner,
|
|
1048
1378
|
descriptor: input.descriptor,
|
|
1049
1379
|
sourceMapping: input.sourceMapping,
|
|
1050
1380
|
targetMapping: input.targetMapping,
|
|
1051
|
-
sourcePriority: input.sourcePriority
|
|
1381
|
+
sourcePriority: input.sourcePriority,
|
|
1382
|
+
maxCandidates: input.maxCandidates
|
|
1052
1383
|
})));
|
|
1053
|
-
server.tool("get-class-api-matrix", "List class/member API rows across
|
|
1384
|
+
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({
|
|
1054
1385
|
version: input.version,
|
|
1055
1386
|
className: input.className,
|
|
1056
1387
|
classNameMapping: input.classNameMapping,
|
|
1057
1388
|
includeKinds: parseClassApiKinds(input.includeKinds),
|
|
1058
|
-
sourcePriority: input.sourcePriority
|
|
1389
|
+
sourcePriority: input.sourcePriority,
|
|
1390
|
+
maxRows: input.maxRows
|
|
1059
1391
|
})));
|
|
1060
1392
|
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({
|
|
1061
1393
|
projectPath: input.projectPath,
|
|
@@ -1065,7 +1397,8 @@ server.tool("resolve-workspace-symbol", "Resolve class/field/method names as see
|
|
|
1065
1397
|
owner: input.owner,
|
|
1066
1398
|
descriptor: input.descriptor,
|
|
1067
1399
|
sourceMapping: input.sourceMapping,
|
|
1068
|
-
sourcePriority: input.sourcePriority
|
|
1400
|
+
sourcePriority: input.sourcePriority,
|
|
1401
|
+
maxCandidates: input.maxCandidates
|
|
1069
1402
|
})));
|
|
1070
1403
|
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({
|
|
1071
1404
|
version: input.version,
|
|
@@ -1076,7 +1409,8 @@ server.tool("check-symbol-exists", "Check whether a class/field/method symbol ex
|
|
|
1076
1409
|
sourceMapping: input.sourceMapping,
|
|
1077
1410
|
sourcePriority: input.sourcePriority,
|
|
1078
1411
|
nameMode: input.nameMode,
|
|
1079
|
-
signatureMode: input.signatureMode
|
|
1412
|
+
signatureMode: input.signatureMode,
|
|
1413
|
+
maxCandidates: input.maxCandidates
|
|
1080
1414
|
})));
|
|
1081
1415
|
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({
|
|
1082
1416
|
nbtBase64: input.nbtBase64,
|
|
@@ -1096,11 +1430,7 @@ server.tool("index-artifact", "Rebuild indexed files/symbols metadata for an exi
|
|
|
1096
1430
|
})));
|
|
1097
1431
|
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())));
|
|
1098
1432
|
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({
|
|
1099
|
-
|
|
1100
|
-
sourcePath: input.sourcePath,
|
|
1101
|
-
sourcePaths: input.sourcePaths,
|
|
1102
|
-
mixinConfigPath: input.mixinConfigPath,
|
|
1103
|
-
sourceRoot: input.sourceRoot,
|
|
1433
|
+
input: input.input,
|
|
1104
1434
|
sourceRoots: input.sourceRoots,
|
|
1105
1435
|
version: input.version,
|
|
1106
1436
|
mapping: input.mapping,
|
|
@@ -1115,7 +1445,8 @@ server.tool("validate-mixin", "Validate Mixin source against Minecraft bytecode
|
|
|
1115
1445
|
preferProjectMapping: input.preferProjectMapping,
|
|
1116
1446
|
reportMode: input.reportMode,
|
|
1117
1447
|
warningCategoryFilter: input.warningCategoryFilter,
|
|
1118
|
-
treatInfoAsWarning: input.treatInfoAsWarning
|
|
1448
|
+
treatInfoAsWarning: input.treatInfoAsWarning,
|
|
1449
|
+
includeIssues: input.includeIssues
|
|
1119
1450
|
})));
|
|
1120
1451
|
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({
|
|
1121
1452
|
content: input.content,
|
|
@@ -1131,7 +1462,9 @@ server.tool("analyze-mod-jar", "Analyze a Minecraft mod JAR to extract loader ty
|
|
|
1131
1462
|
}));
|
|
1132
1463
|
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({
|
|
1133
1464
|
version: input.version,
|
|
1134
|
-
registry: input.registry
|
|
1465
|
+
registry: input.registry,
|
|
1466
|
+
includeData: input.includeData,
|
|
1467
|
+
maxEntriesPerRegistry: input.maxEntriesPerRegistry
|
|
1135
1468
|
})));
|
|
1136
1469
|
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({
|
|
1137
1470
|
fromVersion: input.fromVersion,
|
|
@@ -1142,7 +1475,9 @@ server.tool("compare-versions", "Compare two Minecraft versions to find added/re
|
|
|
1142
1475
|
})));
|
|
1143
1476
|
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({
|
|
1144
1477
|
jarPath: input.jarPath,
|
|
1145
|
-
className: input.className
|
|
1478
|
+
className: input.className,
|
|
1479
|
+
includeFiles: input.includeFiles,
|
|
1480
|
+
maxFiles: input.maxFiles
|
|
1146
1481
|
})));
|
|
1147
1482
|
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({
|
|
1148
1483
|
jarPath: input.jarPath,
|