@adhisang/minecraft-modding-mcp 6.2.0 → 7.0.0-rc.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 +71 -0
- package/README.md +21 -5
- package/dist/cache-policy.d.ts +71 -0
- package/dist/cache-policy.js +83 -0
- package/dist/cache-registry.js +6 -6
- package/dist/cli.js +74 -3
- package/dist/compat-stdio-transport.d.ts +1 -1
- package/dist/compat-stdio-transport.js +13 -1
- package/dist/config.d.ts +3 -0
- package/dist/config.js +8 -2
- package/dist/decompiler/vineflower.d.ts +1 -0
- package/dist/decompiler/vineflower.js +8 -5
- package/dist/entry-tools/analyze-mod-service.d.ts +70 -136
- package/dist/entry-tools/analyze-symbol-service.d.ts +112 -150
- package/dist/entry-tools/compare-minecraft-service.d.ts +59 -145
- package/dist/entry-tools/entry-tool-schema.d.ts +38 -4
- package/dist/entry-tools/entry-tool-schema.js +4 -1
- package/dist/entry-tools/inspect-minecraft/internal.d.ts +235 -799
- package/dist/entry-tools/inspect-minecraft/internal.js +65 -28
- package/dist/entry-tools/inspect-minecraft-service.d.ts +372 -1736
- package/dist/entry-tools/inspect-minecraft-service.js +1 -1
- package/dist/entry-tools/manage-cache-service.d.ts +81 -91
- package/dist/entry-tools/validate-project/cases/project-summary.d.ts +7 -7
- package/dist/entry-tools/validate-project-service.d.ts +164 -592
- package/dist/entry-tools/verify-mixin-target-service.d.ts +3 -19
- package/dist/era-classifier.d.ts +161 -0
- package/dist/era-classifier.js +292 -0
- package/dist/error-mapping.js +9 -2
- package/dist/index.d.ts +42 -4
- package/dist/index.js +637 -475
- package/dist/java-process.d.ts +2 -0
- package/dist/java-process.js +22 -2
- package/dist/json-rpc-framing.d.ts +77 -1
- package/dist/json-rpc-framing.js +249 -13
- package/dist/mapping/loaders/tiny-loom-selection.d.ts +88 -0
- package/dist/mapping/loaders/tiny-loom-selection.js +223 -0
- package/dist/mapping/loaders/tiny-loom.js +45 -33
- package/dist/mapping/loaders/tiny-maven.js +6 -11
- package/dist/mapping/parsers/tiny.d.ts +57 -0
- package/dist/mapping/parsers/tiny.js +99 -22
- package/dist/mapping-service.d.ts +19 -0
- package/dist/mapping-service.js +93 -9
- package/dist/mcp-helpers.d.ts +19 -2
- package/dist/mcp-helpers.js +48 -6
- package/dist/minecraft-explorer-service.d.ts +1 -1
- package/dist/mixin/types.d.ts +8 -0
- package/dist/mod-analyzer.js +7 -7
- package/dist/mod-decompile-service.js +1 -0
- package/dist/nbt/java-nbt-codec.js +12 -2
- package/dist/nbt/json-patch.js +14 -3
- package/dist/nbt/pipeline.js +40 -3
- package/dist/nbt/typed-json.js +26 -1
- package/dist/registration-adapter.d.ts +32 -0
- package/dist/registration-adapter.js +52 -0
- package/dist/request-context.d.ts +7 -0
- package/dist/request-context.js +9 -0
- package/dist/resources.d.ts +1 -1
- package/dist/resources.js +25 -19
- package/dist/server-identity.d.ts +27 -0
- package/dist/server-identity.js +26 -0
- package/dist/source/access-validate.js +53 -0
- package/dist/source/artifact-resolver.d.ts +69 -1
- package/dist/source/artifact-resolver.js +215 -14
- package/dist/source/class-source.d.ts +22 -0
- package/dist/source/class-source.js +162 -29
- package/dist/source/did-you-mean.d.ts +12 -1
- package/dist/source/did-you-mean.js +6 -2
- package/dist/source/file-access.js +150 -46
- package/dist/source/indexer.js +1 -0
- package/dist/source/nested-jars.d.ts +19 -0
- package/dist/source/nested-jars.js +90 -21
- package/dist/source/shared-utils.d.ts +21 -0
- package/dist/source/shared-utils.js +23 -0
- package/dist/source-service.d.ts +12 -0
- package/dist/source-service.js +3 -0
- package/dist/stdio-supervisor.d.ts +357 -2
- package/dist/stdio-supervisor.js +1031 -80
- package/dist/storage/db.d.ts +2 -1
- package/dist/storage/db.js +15 -8
- package/dist/synthetic-decorator.d.ts +24 -0
- package/dist/synthetic-decorator.js +48 -0
- package/dist/tool-contract-manifest.js +1 -1
- package/dist/tool-guidance.d.ts +17 -1
- package/dist/tool-guidance.js +417 -13
- package/dist/tool-schema-registry.d.ts +2 -0
- package/dist/tool-schema-registry.js +4 -0
- package/dist/tool-schemas.d.ts +2214 -3915
- package/dist/tool-schemas.js +34 -7
- package/dist/types.d.ts +35 -0
- package/dist/v1-parity-schemas.d.ts +7 -0
- package/dist/v1-parity-schemas.js +5584 -0
- package/dist/version-diff-service.d.ts +33 -0
- package/dist/version-diff-service.js +148 -3
- package/dist/version-service.js +36 -14
- package/dist/warning-details.js +18 -1
- package/docs/README-ja.md +9 -3
- package/docs/tool-reference.md +267 -22
- package/package.json +12 -9
package/dist/index.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/server";
|
|
2
|
+
import { serveStdio } from "@modelcontextprotocol/server/stdio";
|
|
3
3
|
import { makeStageEmitter } from "./stage-emitter.js";
|
|
4
4
|
import { ZodError } from "zod";
|
|
5
|
+
import { RESOURCE_LISTS_CACHE_HINT } from "./cache-policy.js";
|
|
5
6
|
import { CompatStdioServerTransport } from "./compat-stdio-transport.js";
|
|
7
|
+
import { registerAppTool as registerAppToolDirect, registerExpertTool as registerExpertToolDirect } from "./registration-adapter.js";
|
|
6
8
|
import { objectResult } from "./mcp-helpers.js";
|
|
9
|
+
import { runWithRequestContext } from "./request-context.js";
|
|
7
10
|
import { prepareToolInput } from "./tool-input.js";
|
|
8
11
|
import { DETAIL_ENABLED_TOOL_NAMES, DEFAULT_DETAIL_BY_TOOL, projectByDetail } from "./response-utils.js";
|
|
9
12
|
import { loadConfig } from "./config.js";
|
|
@@ -15,6 +18,8 @@ import { remapModJar } from "./mod-remap-service.js";
|
|
|
15
18
|
import { registerResources } from "./resources.js";
|
|
16
19
|
import { SourceService } from "./source-service.js";
|
|
17
20
|
import { ToolExecutionGate } from "./tool-execution-gate.js";
|
|
21
|
+
import { SERVER_IDENTITY, SERVER_VERSION } from "./server-identity.js";
|
|
22
|
+
import { STDIO_WORKER_MODE_ENV } from "./stdio-supervisor.js";
|
|
18
23
|
import { capWarningDetailsForSummary, classifyWarnings } from "./warning-details.js";
|
|
19
24
|
import { WorkspaceMappingService } from "./workspace-mapping-service.js";
|
|
20
25
|
import { InspectMinecraftService, inspectMinecraftSchema, inspectMinecraftShape } from "./entry-tools/inspect-minecraft-service.js";
|
|
@@ -31,7 +36,7 @@ import { BatchSymbolExistsService } from "./entry-tools/batch-symbol-exists-serv
|
|
|
31
36
|
import { BatchMappingsService } from "./entry-tools/batch-mappings-service.js";
|
|
32
37
|
import { createCacheRegistry } from "./cache-registry.js";
|
|
33
38
|
import { buildEntryToolMeta } from "./entry-tools/response-contract.js";
|
|
34
|
-
import { registerToolSchema } from "./tool-schema-registry.js";
|
|
39
|
+
import { getToolSchema, registerToolSchema as registerToolSchemaInRegistry } from "./tool-schema-registry.js";
|
|
35
40
|
import { buildSuggestedCall } from "./build-suggested-call.js";
|
|
36
41
|
import { applyErrorMetaExtensions, mapErrorToProblem } from "./tool-guidance.js";
|
|
37
42
|
import { analyzeModJarSchema, analyzeModJarShape, batchClassMembersSchema, batchClassMembersShape, batchClassSourceSchema, batchClassSourceShape, batchMappingsSchema, batchMappingsShape, batchSymbolExistsSchema, batchSymbolExistsShape, checkSymbolExistsSchema, checkSymbolExistsShape, compareVersionsSchema, compareVersionsShape, decompileModJarSchema, decompileModJarShape, diffClassSignaturesSchema, diffClassSignaturesShape, emptySchema, findMappingSchema, findMappingShape, getArtifactFileSchema, getArtifactFileShape, getClassApiMatrixSchema, getClassApiMatrixShape, getClassMembersSchema, getClassMembersShape, getClassSourceSchema, getClassSourceShape, getModClassSourceSchema, getModClassSourceShape, getRegistryDataSchema, getRegistryDataShape, indexArtifactSchema, indexArtifactShape, jsonToNbtSchema, jsonToNbtShape, listArtifactFilesSchema, listArtifactFilesShape, listVersionsSchema, listVersionsShape, nbtApplyJsonPatchSchema, nbtApplyJsonPatchShape, nbtToJsonSchema, nbtToJsonShape, remapModJarSchema, remapModJarShape, resolveArtifactSchema, resolveArtifactShape, resolveMethodMappingExactSchema, resolveMethodMappingExactShape, resolveWorkspaceSymbolSchema, resolveWorkspaceSymbolShape, searchClassSourceSchema, searchClassSourceShape, searchModSourceSchema, searchModSourceShape, traceSymbolLifecycleSchema, traceSymbolLifecycleShape, validateAccessTransformerSchema, validateAccessTransformerShape, validateAccessWidenerSchema, validateAccessWidenerShape, validateMixinSchema, validateMixinShape, verifyMixinTargetSchema, verifyMixinTargetShape, findClassShape, findClassSchema } from "./tool-schemas.js";
|
|
@@ -64,34 +69,18 @@ const BATCH_DETAIL_TOOL_NAMES = new Set([
|
|
|
64
69
|
"batch-mappings"
|
|
65
70
|
]);
|
|
66
71
|
const heavyToolExecutionGate = new ToolExecutionGate({ maxConcurrent: 1, maxQueue: 2 });
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
const SERVER_VERSION = getServerVersionFromPackageJson();
|
|
81
|
-
const server = new McpServer({
|
|
82
|
-
name: "@adhisang/minecraft-modding-mcp",
|
|
83
|
-
version: SERVER_VERSION
|
|
84
|
-
});
|
|
85
|
-
// The SDK validates tool args before invoking handlers and returns generic InvalidParams text.
|
|
86
|
-
// Bypass that layer so runTool() remains the single source of truth for validation and error envelopes.
|
|
87
|
-
server.validateToolInput = async (_tool, args) => args;
|
|
88
|
-
// Low-level/expert tools duplicate capability that the six entry tools expose
|
|
89
|
-
// in a single resolve+fetch call. expertTool() registers them exactly like
|
|
90
|
-
// server.tool() but appends a note steering agents to the entry tools first.
|
|
91
|
-
// Entry tools, batch tools, and the NBT/runtime utilities (which have no entry
|
|
92
|
-
// equivalent) keep their plain descriptions via server.tool().
|
|
93
|
-
const EXPERT_TOOL_NOTE = " Expert tool: prefer the entry tools (inspect-minecraft, analyze-symbol, compare-minecraft, analyze-mod, validate-project) first.";
|
|
94
|
-
const expertTool = ((name, description, ...rest) => server.tool(name, description + EXPERT_TOOL_NOTE, ...rest));
|
|
72
|
+
// Server identity ({name, version}) comes from the canonical module shared
|
|
73
|
+
// with the supervisor's synthetic decorator (src/server-identity.ts): the SDK
|
|
74
|
+
// stamps the Implementation passed to McpServer verbatim as
|
|
75
|
+
// `_meta[SERVER_INFO_META_KEY]` on every modern result, so identity equality
|
|
76
|
+
// with supervisor-synthesized results holds by construction.
|
|
77
|
+
// The McpServer instance is constructed per serveStdio factory call inside
|
|
78
|
+
// buildServer() below; module scope keeps only the shared services/config.
|
|
79
|
+
// Tool registration goes through src/registration-adapter.ts: a
|
|
80
|
+
// validation-bypassing Standard Schema adapter keeps runTool() the single
|
|
81
|
+
// source of truth for validation and error envelopes, and serves the frozen
|
|
82
|
+
// pre-migration inputSchema bytes (src/v1-parity-schemas.ts) in tools/list.
|
|
83
|
+
// registerExpertTool() appends the expert-tool note to the description.
|
|
95
84
|
const config = loadConfig();
|
|
96
85
|
const nbtLimits = {
|
|
97
86
|
maxInputBytes: config.maxNbtInputBytes,
|
|
@@ -110,11 +99,22 @@ const sourceService = new Proxy({}, {
|
|
|
110
99
|
return typeof value === "function" ? value.bind(service) : value;
|
|
111
100
|
}
|
|
112
101
|
});
|
|
102
|
+
function recordToolCallBestEffort(tool, durationMs) {
|
|
103
|
+
try {
|
|
104
|
+
sourceService.recordToolCall(tool, durationMs);
|
|
105
|
+
}
|
|
106
|
+
catch (caughtError) {
|
|
107
|
+
log("warn", "tool.metrics.record.failed", {
|
|
108
|
+
tool,
|
|
109
|
+
reason: caughtError instanceof Error ? caughtError.message : String(caughtError)
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
113
|
const workspaceMappingService = new WorkspaceMappingService();
|
|
114
114
|
const inspectMinecraftService = new InspectMinecraftService({
|
|
115
115
|
listVersions: (input) => sourceService.listVersions(input),
|
|
116
116
|
resolveArtifact: (input) => sourceService.resolveArtifact(input),
|
|
117
|
-
findClass: (input) =>
|
|
117
|
+
findClass: (input) => sourceService.findClassIncludingNested(input),
|
|
118
118
|
checkSymbolExists: (input) => sourceService.checkSymbolExists(input),
|
|
119
119
|
getClassSource: (input) => sourceService.getClassSource(input),
|
|
120
120
|
getClassMembers: (input) => sourceService.getClassMembers(input),
|
|
@@ -237,7 +237,6 @@ const batchSymbolExistsService = new BatchSymbolExistsService({
|
|
|
237
237
|
const batchMappingsService = new BatchMappingsService({
|
|
238
238
|
findMapping: (input) => sourceService.findMapping(input)
|
|
239
239
|
});
|
|
240
|
-
registerResources(server, sourceService);
|
|
241
240
|
let processHandlersAttached = false;
|
|
242
241
|
let serverStarted = false;
|
|
243
242
|
function attachProcessErrorHandlers() {
|
|
@@ -251,6 +250,9 @@ function attachProcessErrorHandlers() {
|
|
|
251
250
|
message: error.message,
|
|
252
251
|
stack: error.stack
|
|
253
252
|
});
|
|
253
|
+
if (process.env[STDIO_WORKER_MODE_ENV] === "1") {
|
|
254
|
+
process.exit(1);
|
|
255
|
+
}
|
|
254
256
|
process.exitCode = 1;
|
|
255
257
|
});
|
|
256
258
|
process.on("unhandledRejection", (reason) => {
|
|
@@ -259,6 +261,9 @@ function attachProcessErrorHandlers() {
|
|
|
259
261
|
message: error.message,
|
|
260
262
|
stack: error.stack
|
|
261
263
|
});
|
|
264
|
+
if (process.env[STDIO_WORKER_MODE_ENV] === "1") {
|
|
265
|
+
process.exit(1);
|
|
266
|
+
}
|
|
262
267
|
process.exitCode = 1;
|
|
263
268
|
});
|
|
264
269
|
}
|
|
@@ -341,81 +346,83 @@ async function runTool(tool, rawInput, schema, action) {
|
|
|
341
346
|
const startedAt = Date.now();
|
|
342
347
|
let normalizedInput = rawInput;
|
|
343
348
|
try {
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
path
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
349
|
+
return await runWithRequestContext({ requestId }, async () => {
|
|
350
|
+
const preparedInput = prepareToolInput(rawInput);
|
|
351
|
+
normalizedInput = preparedInput.normalizedInput;
|
|
352
|
+
const { removedOfficialPaths, suggestedReplacementInput } = preparedInput;
|
|
353
|
+
if (removedOfficialPaths.length > 0) {
|
|
354
|
+
throw createError({
|
|
355
|
+
code: ERROR_CODES.INVALID_INPUT,
|
|
356
|
+
message: `The "official" mapping namespace was removed. Use "obfuscated" instead.`,
|
|
357
|
+
details: {
|
|
358
|
+
fieldErrors: removedOfficialPaths.map((path) => ({
|
|
359
|
+
path,
|
|
360
|
+
message: `"official" is no longer supported for this field. Use "obfuscated".`,
|
|
361
|
+
code: "invalid_enum_value"
|
|
362
|
+
})),
|
|
363
|
+
nextAction: `Replace "official" with "obfuscated" in mapping-related fields and retry.`,
|
|
364
|
+
// Route construction through buildSuggestedCall to satisfy the D12
|
|
365
|
+
// invariant (tests/suggested-call-invariant.test.ts): every emitted
|
|
366
|
+
// suggestedCall must be built by the helper. The downstream
|
|
367
|
+
// mapErrorToProblem gate re-validates on emission; that second pass is
|
|
368
|
+
// the intentional, cheap cost of the two complementary safety gates.
|
|
369
|
+
...(suggestedReplacementInput
|
|
370
|
+
? buildSuggestedCall({
|
|
371
|
+
tool,
|
|
372
|
+
params: suggestedReplacementInput
|
|
373
|
+
})
|
|
374
|
+
: {})
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
const parsedInput = schema.parse(normalizedInput);
|
|
379
|
+
const payload = await (HEAVY_TOOL_NAMES.has(tool)
|
|
380
|
+
? heavyToolExecutionGate.run(tool, () => action(parsedInput))
|
|
381
|
+
: action(parsedInput));
|
|
382
|
+
const { result, warnings, meta: resultMeta } = splitWarnings(payload);
|
|
383
|
+
// Expert tools share the entry-tool detail/include response contract (the old
|
|
384
|
+
// per-tool `compact` boolean is gone). projectByDetail reproduces the previous
|
|
385
|
+
// compact defaults byte-identically: resolution tools default detail=summary
|
|
386
|
+
// (old compact:true), source/file tools default detail=standard (old compact:false).
|
|
387
|
+
// The legacy includeProvenance/includeDescriptors flags are folded into the
|
|
388
|
+
// include set as aliases so Phase-4 behavior is preserved.
|
|
389
|
+
const shapeInput = readResponseShapeInput(parsedInput);
|
|
390
|
+
const effectiveDetail = ENTRY_TOOL_NAMES.has(tool)
|
|
391
|
+
? (shapeInput.detail ?? "summary")
|
|
392
|
+
: (shapeInput.detail ?? DEFAULT_DETAIL_BY_TOOL[tool] ?? "summary");
|
|
393
|
+
let projectedResult = result;
|
|
394
|
+
if (DETAIL_ENABLED_TOOL_NAMES.has(tool)) {
|
|
395
|
+
projectedResult = projectByDetail(tool, projectedResult, effectiveDetail, shapeInput.include);
|
|
396
|
+
}
|
|
397
|
+
// Entry, expert, and batch tools report the applied detail/include shape in meta.
|
|
398
|
+
// detailApplied is omitted when it matches the tool's default to keep responses lean.
|
|
399
|
+
const defaultDetail = ENTRY_TOOL_NAMES.has(tool)
|
|
400
|
+
? "summary"
|
|
401
|
+
: DEFAULT_DETAIL_BY_TOOL[tool] ?? "summary";
|
|
402
|
+
const entryMeta = ENTRY_TOOL_NAMES.has(tool) ||
|
|
403
|
+
DETAIL_ENABLED_TOOL_NAMES.has(tool) ||
|
|
404
|
+
BATCH_DETAIL_TOOL_NAMES.has(tool)
|
|
405
|
+
? buildEntryToolMeta({
|
|
406
|
+
detail: effectiveDetail,
|
|
407
|
+
defaultDetail,
|
|
408
|
+
include: shapeInput.include.size > 0 ? [...shapeInput.include] : undefined
|
|
409
|
+
})
|
|
410
|
+
: undefined;
|
|
411
|
+
const durationMs = Date.now() - startedAt;
|
|
412
|
+
recordToolCallBestEffort(tool, durationMs);
|
|
413
|
+
const warningDetails = capWarningDetailsForSummary(classifyWarnings(warnings), effectiveDetail === "summary");
|
|
414
|
+
return objectResult({
|
|
415
|
+
result: projectedResult,
|
|
416
|
+
meta: {
|
|
417
|
+
...(entryMeta ?? {}),
|
|
418
|
+
...resultMeta,
|
|
419
|
+
requestId,
|
|
420
|
+
tool,
|
|
421
|
+
durationMs,
|
|
422
|
+
...(warnings.length > 0 ? { warnings } : {}),
|
|
423
|
+
...(warningDetails.length > 0 ? { warningDetails } : {})
|
|
369
424
|
}
|
|
370
425
|
});
|
|
371
|
-
}
|
|
372
|
-
const parsedInput = schema.parse(normalizedInput);
|
|
373
|
-
const payload = await (HEAVY_TOOL_NAMES.has(tool)
|
|
374
|
-
? heavyToolExecutionGate.run(tool, () => action(parsedInput))
|
|
375
|
-
: action(parsedInput));
|
|
376
|
-
const { result, warnings, meta: resultMeta } = splitWarnings(payload);
|
|
377
|
-
// Expert tools share the entry-tool detail/include response contract (the old
|
|
378
|
-
// per-tool `compact` boolean is gone). projectByDetail reproduces the previous
|
|
379
|
-
// compact defaults byte-identically: resolution tools default detail=summary
|
|
380
|
-
// (old compact:true), source/file tools default detail=standard (old compact:false).
|
|
381
|
-
// The legacy includeProvenance/includeDescriptors flags are folded into the
|
|
382
|
-
// include set as aliases so Phase-4 behavior is preserved.
|
|
383
|
-
const shapeInput = readResponseShapeInput(parsedInput);
|
|
384
|
-
const effectiveDetail = ENTRY_TOOL_NAMES.has(tool)
|
|
385
|
-
? (shapeInput.detail ?? "summary")
|
|
386
|
-
: (shapeInput.detail ?? DEFAULT_DETAIL_BY_TOOL[tool] ?? "summary");
|
|
387
|
-
let projectedResult = result;
|
|
388
|
-
if (DETAIL_ENABLED_TOOL_NAMES.has(tool)) {
|
|
389
|
-
projectedResult = projectByDetail(tool, projectedResult, effectiveDetail, shapeInput.include);
|
|
390
|
-
}
|
|
391
|
-
// Entry, expert, and batch tools report the applied detail/include shape in meta.
|
|
392
|
-
// detailApplied is omitted when it matches the tool's default to keep responses lean.
|
|
393
|
-
const defaultDetail = ENTRY_TOOL_NAMES.has(tool)
|
|
394
|
-
? "summary"
|
|
395
|
-
: DEFAULT_DETAIL_BY_TOOL[tool] ?? "summary";
|
|
396
|
-
const entryMeta = ENTRY_TOOL_NAMES.has(tool) ||
|
|
397
|
-
DETAIL_ENABLED_TOOL_NAMES.has(tool) ||
|
|
398
|
-
BATCH_DETAIL_TOOL_NAMES.has(tool)
|
|
399
|
-
? buildEntryToolMeta({
|
|
400
|
-
detail: effectiveDetail,
|
|
401
|
-
defaultDetail,
|
|
402
|
-
include: shapeInput.include.size > 0 ? [...shapeInput.include] : undefined
|
|
403
|
-
})
|
|
404
|
-
: undefined;
|
|
405
|
-
const durationMs = Date.now() - startedAt;
|
|
406
|
-
sourceService.recordToolCall(tool, durationMs);
|
|
407
|
-
const warningDetails = capWarningDetailsForSummary(classifyWarnings(warnings), effectiveDetail === "summary");
|
|
408
|
-
return objectResult({
|
|
409
|
-
result: projectedResult,
|
|
410
|
-
meta: {
|
|
411
|
-
...(entryMeta ?? {}),
|
|
412
|
-
...resultMeta,
|
|
413
|
-
requestId,
|
|
414
|
-
tool,
|
|
415
|
-
durationMs,
|
|
416
|
-
...(warnings.length > 0 ? { warnings } : {}),
|
|
417
|
-
...(warningDetails.length > 0 ? { warningDetails } : {})
|
|
418
|
-
}
|
|
419
426
|
});
|
|
420
427
|
}
|
|
421
428
|
catch (caughtError) {
|
|
@@ -454,7 +461,7 @@ async function runTool(tool, rawInput, schema, action) {
|
|
|
454
461
|
});
|
|
455
462
|
}
|
|
456
463
|
const errorDurationMs = Date.now() - startedAt;
|
|
457
|
-
|
|
464
|
+
recordToolCallBestEffort(tool, errorDurationMs);
|
|
458
465
|
const errorMeta = {
|
|
459
466
|
requestId,
|
|
460
467
|
tool,
|
|
@@ -467,393 +474,530 @@ async function runTool(tool, rawInput, schema, action) {
|
|
|
467
474
|
}, { isError: true });
|
|
468
475
|
}
|
|
469
476
|
}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
477
|
+
/**
|
|
478
|
+
* Builds a fully-registered McpServer instance. serveStdio calls its factory
|
|
479
|
+
* PER INSTANCE, not per connection: a modern `server/discover` opening builds
|
|
480
|
+
* a probe instance which a following legacy `initialize` DISCARDS
|
|
481
|
+
* (`product.close()`) before calling the factory again — and the probe path
|
|
482
|
+
* MUTATES the instance (installModernOnlyHandlers adds "2026-07-28" support
|
|
483
|
+
* and a server/discover handler). Every call therefore constructs a FRESH
|
|
484
|
+
* McpServer so probe-instance mutations can never leak onto the re-pinned
|
|
485
|
+
* legacy instance (frozen negotiate-down contract). Module-scope services,
|
|
486
|
+
* config, and helpers stay shared; only the McpServer and its registrations
|
|
487
|
+
* are per-instance.
|
|
488
|
+
*
|
|
489
|
+
* Tool registration ORDER: the CALL-SITE order inside this function is the
|
|
490
|
+
* frozen legacy tools/list order — do not reorder the call sites. The calls
|
|
491
|
+
* are captured as deferred thunks and executed at the bottom of the
|
|
492
|
+
* function: in call-site order for legacy/ctx-less instances (byte-frozen
|
|
493
|
+
* golden contract), and in raw tool-name-ascending order when serveStdio
|
|
494
|
+
* constructs a MODERN-era instance (`ctx.era === "modern"` — the SDK's
|
|
495
|
+
* documented era-parameterized factory seam; the v2 SDK itself emits
|
|
496
|
+
* registration order and never sorts). Each instance serves exactly one era,
|
|
497
|
+
* so the two orders can never mix on one connection. (Adopted ordering
|
|
498
|
+
* policy.)
|
|
499
|
+
*
|
|
500
|
+
* Cache hints (adopted policy, src/cache-policy.ts): the constructor options
|
|
501
|
+
* configure the non-zero resources/list + resources/templates/list rows;
|
|
502
|
+
* per-resource rows live in registerResources(); all hints ride the SDK's
|
|
503
|
+
* never-serialized carrier, so 2025-era responses are unaffected.
|
|
504
|
+
*
|
|
505
|
+
* NOTE: the function body below intentionally keeps the original module-scope
|
|
506
|
+
* indentation of the registration block to preserve a reviewable minimal diff.
|
|
507
|
+
*/
|
|
508
|
+
// eslint-disable-next-line func-style
|
|
509
|
+
function buildServer(ctx) {
|
|
510
|
+
const server = new McpServer({
|
|
511
|
+
name: SERVER_IDENTITY.name,
|
|
512
|
+
version: SERVER_IDENTITY.version
|
|
513
|
+
}, {
|
|
514
|
+
// Advertised capabilities (adopted policy). The SDK defaults BOTH
|
|
515
|
+
// listChanged flags to `true` the first time a tool/resource is registered
|
|
516
|
+
// (`... ?? true`), so an explicit `false` passed here is the only public way
|
|
517
|
+
// to suppress them — the key cannot be omitted through any public API, and
|
|
518
|
+
// per the revision an explicit `false` and an absent flag are equivalent.
|
|
519
|
+
// The suppression is deliberate and is NOT era-gated: this server never
|
|
520
|
+
// emits `notifications/tools/list_changed` or
|
|
521
|
+
// `notifications/resources/list_changed` (the tool and resource surface is
|
|
522
|
+
// fixed at process start by environment flags), and `subscriptions/listen`
|
|
523
|
+
// answers -32601 in both eras, so advertising `true` would promise a stream
|
|
524
|
+
// no client can subscribe to or ever receive.
|
|
525
|
+
// KEY ORDER IS LOAD-BEARING: `resources` before `tools` reproduces the
|
|
526
|
+
// frozen wire order of the legacy `initialize` and modern `server/discover`
|
|
527
|
+
// capability payloads. Pinned by
|
|
528
|
+
// tests/stdio/stdio-dependency-method-inventory.test.ts (legacy) and
|
|
529
|
+
// tests/stdio/stdio-modern-discover-contents.test.ts (modern).
|
|
530
|
+
capabilities: {
|
|
531
|
+
resources: { listChanged: false },
|
|
532
|
+
tools: { listChanged: false }
|
|
533
|
+
},
|
|
534
|
+
cacheHints: {
|
|
535
|
+
"resources/list": RESOURCE_LISTS_CACHE_HINT,
|
|
536
|
+
"resources/templates/list": RESOURCE_LISTS_CACHE_HINT
|
|
537
|
+
}
|
|
538
|
+
});
|
|
539
|
+
registerResources(server, sourceService);
|
|
540
|
+
// Deferred tool-registration capture: the adapter imports are RENAMED to
|
|
541
|
+
// registerAppToolDirect/registerExpertToolDirect, and these local wrapper
|
|
542
|
+
// functions take the original names, so every call site below records
|
|
543
|
+
// {name, thunk} in call-site order; the executor at the bottom of this
|
|
544
|
+
// function replays them in the era-appropriate order.
|
|
545
|
+
const pendingToolRegistrations = [];
|
|
546
|
+
const registerAppTool = (srv, name, description, shape, annotations, handler) => {
|
|
547
|
+
pendingToolRegistrations.push({
|
|
548
|
+
name,
|
|
549
|
+
register: () => registerAppToolDirect(srv, name, description, shape, annotations, handler)
|
|
550
|
+
});
|
|
551
|
+
};
|
|
552
|
+
const registerExpertTool = (srv, name, description, shape, annotations, handler) => {
|
|
553
|
+
pendingToolRegistrations.push({
|
|
554
|
+
name,
|
|
555
|
+
register: () => registerExpertToolDirect(srv, name, description, shape, annotations, handler)
|
|
556
|
+
});
|
|
557
|
+
};
|
|
558
|
+
// The app-level schema registry (tool-schema-registry) is register-once and
|
|
559
|
+
// module-scoped: the eager `buildServer()` call at the bottom of this module
|
|
560
|
+
// populates it at load time; re-builds (one per serveStdio instance) skip
|
|
561
|
+
// re-registration so the registry never sees a duplicate.
|
|
562
|
+
const registerToolSchema = (name, schema) => {
|
|
563
|
+
if (getToolSchema(name) === undefined)
|
|
564
|
+
registerToolSchemaInRegistry(name, schema);
|
|
565
|
+
};
|
|
566
|
+
registerExpertTool(server, "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({
|
|
567
|
+
includeSnapshots: input.includeSnapshots,
|
|
568
|
+
limit: input.limit
|
|
569
|
+
})));
|
|
570
|
+
registerToolSchema("list-versions", listVersionsSchema);
|
|
571
|
+
registerAppTool(server, "inspect-minecraft", "Top-level workflow tool for version discovery, artifact resolution, class inspection, source search, file reads, and file listings. Workspace subject.focus is a structured class/file/search object, never a string; task=auto dispatches from subject.kind and focus.kind.", inspectMinecraftShape, { readOnlyHint: true }, async (args) => runTool("inspect-minecraft", args, inspectMinecraftSchema, async (input) => inspectMinecraftService.execute(input)));
|
|
572
|
+
registerToolSchema("inspect-minecraft", inspectMinecraftSchema);
|
|
573
|
+
registerAppTool(server, "analyze-symbol", "Top-level workflow tool for symbol existence, mapping, lifecycle, workspace analysis, and API overview. subject.kind='symbol' auto-detects class/field/method from the selector.", analyzeSymbolShape, { readOnlyHint: true }, async (args) => runTool("analyze-symbol", args, analyzeSymbolSchema, async (input) => analyzeSymbolService.execute(input)));
|
|
574
|
+
registerToolSchema("analyze-symbol", analyzeSymbolSchema);
|
|
575
|
+
registerAppTool(server, "compare-minecraft", "Top-level workflow tool for version comparisons, class diffs, registry diffs, and migration overviews.", compareMinecraftShape, { readOnlyHint: true }, async (args) => runTool("compare-minecraft", args, compareMinecraftSchema, async (input) => compareMinecraftService.execute(input)));
|
|
576
|
+
registerToolSchema("compare-minecraft", compareMinecraftSchema);
|
|
577
|
+
registerAppTool(server, "analyze-mod", "Top-level workflow tool for mod metadata inspection, decompile/search flows, class source, bytecode-only class member reads, and safe remap previews/applies.", analyzeModShape, { readOnlyHint: false }, async (args) => runTool("analyze-mod", args, analyzeModSchema, async (input) => analyzeModService.execute(input)));
|
|
578
|
+
registerToolSchema("analyze-mod", analyzeModSchema);
|
|
579
|
+
registerAppTool(server, "validate-project", "Top-level workflow tool for project summary, direct mixin validation, and access widener/access transformer validation.", validateProjectShape, { readOnlyHint: true }, async (args, ctx) => runTool("validate-project", args, validateProjectSchema, async (input) => validateProjectService.execute(input, {
|
|
580
|
+
stageEmitter: makeStageEmitter(stageEmitterExtraFromCtx(ctx))
|
|
581
|
+
})));
|
|
582
|
+
registerToolSchema("validate-project", validateProjectSchema);
|
|
583
|
+
registerAppTool(server, "manage-cache", "Top-level workflow tool for cache summaries, listing, verification, previewed mutation, and explicit apply operations.", manageCacheShape, { readOnlyHint: false }, async (args) => runTool("manage-cache", args, manageCacheSchema, async (input) => manageCacheService.execute(input)));
|
|
584
|
+
registerToolSchema("manage-cache", manageCacheSchema);
|
|
585
|
+
if (!VERIFY_MIXIN_TARGET_OFF) {
|
|
586
|
+
registerAppTool(server, "verify-mixin-target", "Single-call probe: does this target owner / member exist and which @Shadow / @Accessor / @Invoker should the mixin use? Reuses target.kind workspace/version/coordinate/dependency/jar.", verifyMixinTargetShape, { readOnlyHint: true }, async (args) => runTool("verify-mixin-target", args, verifyMixinTargetSchema, async (input) => verifyMixinTargetService.execute({
|
|
587
|
+
owner: input.owner,
|
|
588
|
+
member: input.member,
|
|
589
|
+
mixinMemberName: input.mixinMemberName,
|
|
590
|
+
mapping: input.mapping,
|
|
591
|
+
autoRemap: input.autoRemap,
|
|
592
|
+
sourcePriority: input.sourcePriority,
|
|
593
|
+
projectPath: input.projectPath,
|
|
594
|
+
gradleUserHome: input.gradleUserHome,
|
|
595
|
+
target: input.target,
|
|
596
|
+
scope: input.scope,
|
|
597
|
+
preferProjectVersion: input.preferProjectVersion,
|
|
598
|
+
strictVersion: input.strictVersion
|
|
599
|
+
})));
|
|
600
|
+
registerToolSchema("verify-mixin-target", verifyMixinTargetSchema);
|
|
601
|
+
}
|
|
602
|
+
if (!BATCH_TOOLS_OFF) {
|
|
603
|
+
registerAppTool(server, "batch-class-source", "Batch get-class-source for many classes sharing one resolved artifact; returns per-entry status plus aggregate summary. Not read-only: per-entry outputFile writes to disk.", batchClassSourceShape, { readOnlyHint: false }, async (args) => runTool("batch-class-source", args, batchClassSourceSchema, async (input) => batchClassSourceService.execute(input)));
|
|
604
|
+
registerToolSchema("batch-class-source", batchClassSourceSchema);
|
|
605
|
+
registerAppTool(server, "batch-class-members", "Batch get-class-members for many classes sharing one resolved artifact; returns per-entry status plus aggregate summary.", batchClassMembersShape, { readOnlyHint: true }, async (args) => runTool("batch-class-members", args, batchClassMembersSchema, async (input) => batchClassMembersService.execute(input)));
|
|
606
|
+
registerToolSchema("batch-class-members", batchClassMembersSchema);
|
|
607
|
+
registerAppTool(server, "batch-symbol-exists", "Batch check-symbol-exists for many symbols against one shared Minecraft version (target.kind=version or workspace only).", batchSymbolExistsShape, { readOnlyHint: true }, async (args) => runTool("batch-symbol-exists", args, batchSymbolExistsSchema, async (input) => batchSymbolExistsService.execute(input)));
|
|
608
|
+
registerToolSchema("batch-symbol-exists", batchSymbolExistsSchema);
|
|
609
|
+
registerAppTool(server, "batch-mappings", "Batch find-mapping: resolve many symbols across mapping namespaces with one shared Minecraft version.", batchMappingsShape, { readOnlyHint: true }, async (args) => runTool("batch-mappings", args, batchMappingsSchema, async (input) => batchMappingsService.execute(input)));
|
|
610
|
+
registerToolSchema("batch-mappings", batchMappingsSchema);
|
|
611
|
+
}
|
|
612
|
+
registerExpertTool(server, "resolve-artifact", "Resolve a source artifact and return artifact metadata. For target.kind=jar, only <basename>-sources.jar is auto-adopted.", resolveArtifactShape, { readOnlyHint: true }, async (args) => runTool("resolve-artifact", args, resolveArtifactSchema, async (input) => sourceService.resolveArtifact({
|
|
613
|
+
target: input.target,
|
|
494
614
|
mapping: input.mapping,
|
|
495
|
-
autoRemap: input.autoRemap,
|
|
496
615
|
sourcePriority: input.sourcePriority,
|
|
616
|
+
allowDecompile: input.allowDecompile,
|
|
497
617
|
projectPath: input.projectPath,
|
|
498
618
|
gradleUserHome: input.gradleUserHome,
|
|
499
|
-
target: input.target,
|
|
500
619
|
scope: input.scope,
|
|
501
620
|
preferProjectVersion: input.preferProjectVersion,
|
|
502
|
-
strictVersion: input.strictVersion
|
|
621
|
+
strictVersion: input.strictVersion,
|
|
622
|
+
includeSampleEntries: input.detail === "full" ||
|
|
623
|
+
(Array.isArray(input.include) && input.include.includes("samples"))
|
|
503
624
|
})));
|
|
504
|
-
registerToolSchema("
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
includeSampleEntries: input.detail === "full" ||
|
|
527
|
-
(Array.isArray(input.include) && input.include.includes("samples"))
|
|
528
|
-
})));
|
|
529
|
-
registerToolSchema("resolve-artifact", resolveArtifactSchema);
|
|
530
|
-
// Flat tools accept `target` as an additive alternative to `artifactId`
|
|
531
|
-
// (mutually exclusive, enforced by each schema). A target is resolved to its
|
|
532
|
-
// artifactId here before the underlying artifactId-based service call.
|
|
533
|
-
async function resolveFlatArtifactId(input) {
|
|
534
|
-
if (input.artifactId) {
|
|
535
|
-
return input.artifactId;
|
|
536
|
-
}
|
|
537
|
-
const target = input.target;
|
|
538
|
-
// {kind:"artifact"} reuses an already-resolved artifact without a resolve
|
|
539
|
-
// round-trip. Workspace-context kinds carry their own projectPath when the
|
|
540
|
-
// target shape provides one; kinds that require workspace context beyond
|
|
541
|
-
// that fail with the resolver's own projectPath guidance.
|
|
542
|
-
if (target?.kind === "artifact" && target.artifactId) {
|
|
543
|
-
return target.artifactId;
|
|
625
|
+
registerToolSchema("resolve-artifact", resolveArtifactSchema);
|
|
626
|
+
// Flat tools accept `target` as an additive alternative to `artifactId`
|
|
627
|
+
// (mutually exclusive, enforced by each schema). A target is resolved to its
|
|
628
|
+
// artifactId here before the underlying artifactId-based service call.
|
|
629
|
+
async function resolveFlatArtifactId(input) {
|
|
630
|
+
if (input.artifactId) {
|
|
631
|
+
return input.artifactId;
|
|
632
|
+
}
|
|
633
|
+
const target = input.target;
|
|
634
|
+
// {kind:"artifact"} reuses an already-resolved artifact without a resolve
|
|
635
|
+
// round-trip. Workspace-context kinds carry their own projectPath when the
|
|
636
|
+
// target shape provides one; kinds that require workspace context beyond
|
|
637
|
+
// that fail with the resolver's own projectPath guidance.
|
|
638
|
+
if (target?.kind === "artifact" && target.artifactId) {
|
|
639
|
+
return target.artifactId;
|
|
640
|
+
}
|
|
641
|
+
const projectPath = input.projectPath ?? target?.projectPath;
|
|
642
|
+
const resolved = await sourceService.resolveArtifact({
|
|
643
|
+
target: target,
|
|
644
|
+
...(projectPath ? { projectPath } : {})
|
|
645
|
+
});
|
|
646
|
+
return resolved.artifactId;
|
|
544
647
|
}
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
648
|
+
registerExpertTool(server, "find-class", "Resolve a simple or qualified class name to fully-qualified class names within an artifact. Use this before get-class-source when you only have a simple name.", findClassShape, { readOnlyHint: true }, async (args) => runTool("find-class", args, findClassSchema, async (input) => sourceService.findClassIncludingNested({
|
|
649
|
+
className: input.className,
|
|
650
|
+
artifactId: await resolveFlatArtifactId(input),
|
|
651
|
+
limit: input.limit
|
|
652
|
+
})));
|
|
653
|
+
registerToolSchema("find-class", findClassSchema);
|
|
654
|
+
registerExpertTool(server, "get-class-source", "Get Java source for a class. Default mode=metadata returns a symbol outline only; pass mode=snippet or mode=full to read source text. Not read-only: outputFile writes to disk.", getClassSourceShape, { readOnlyHint: false }, async (args) => runTool("get-class-source", args, getClassSourceSchema, async (input) => {
|
|
655
|
+
const normalizedTarget = normalizeSourceLookupTarget(input.target);
|
|
656
|
+
return sourceService.getClassSource({
|
|
657
|
+
className: input.className,
|
|
658
|
+
mode: input.mode,
|
|
659
|
+
artifactId: normalizedTarget.artifactId,
|
|
660
|
+
target: normalizedTarget.target,
|
|
661
|
+
mapping: input.mapping,
|
|
662
|
+
sourcePriority: input.sourcePriority,
|
|
663
|
+
allowDecompile: input.allowDecompile,
|
|
664
|
+
projectPath: input.projectPath,
|
|
665
|
+
gradleUserHome: input.gradleUserHome,
|
|
666
|
+
scope: input.scope,
|
|
667
|
+
preferProjectVersion: input.preferProjectVersion,
|
|
668
|
+
strictVersion: input.strictVersion,
|
|
669
|
+
startLine: input.startLine,
|
|
670
|
+
endLine: input.endLine,
|
|
671
|
+
maxLines: input.maxLines,
|
|
672
|
+
maxChars: input.maxChars,
|
|
673
|
+
outputFile: input.outputFile
|
|
674
|
+
});
|
|
675
|
+
}));
|
|
676
|
+
registerToolSchema("get-class-source", getClassSourceSchema);
|
|
677
|
+
registerExpertTool(server, "get-class-members", "Get fields/methods/constructors for one class from binary bytecode.", getClassMembersShape, { readOnlyHint: true }, async (args) => runTool("get-class-members", args, getClassMembersSchema, async (input) => {
|
|
678
|
+
const normalizedTarget = normalizeSourceLookupTarget(input.target);
|
|
679
|
+
return sourceService.getClassMembers({
|
|
680
|
+
className: input.className,
|
|
681
|
+
artifactId: normalizedTarget.artifactId,
|
|
682
|
+
target: normalizedTarget.target,
|
|
683
|
+
mapping: input.mapping,
|
|
684
|
+
sourcePriority: input.sourcePriority,
|
|
685
|
+
allowDecompile: input.allowDecompile,
|
|
686
|
+
access: input.access,
|
|
687
|
+
includeSynthetic: input.includeSynthetic,
|
|
688
|
+
includeInherited: input.includeInherited,
|
|
689
|
+
memberPattern: input.memberPattern,
|
|
690
|
+
maxMembers: input.maxMembers,
|
|
691
|
+
projection: input.projection,
|
|
692
|
+
cursor: input.cursor,
|
|
693
|
+
projectPath: input.projectPath,
|
|
694
|
+
gradleUserHome: input.gradleUserHome,
|
|
695
|
+
scope: input.scope,
|
|
696
|
+
preferProjectVersion: input.preferProjectVersion,
|
|
697
|
+
strictVersion: input.strictVersion,
|
|
698
|
+
// includeDescriptors is the documented alias for include:["descriptors"]; honor both
|
|
699
|
+
// (mirror batch-class-members-service / inspect-minecraft class-members handler).
|
|
700
|
+
includeDescriptors: input.includeDescriptors === true ||
|
|
701
|
+
(Array.isArray(input.include) && input.include.includes("descriptors"))
|
|
702
|
+
});
|
|
703
|
+
}));
|
|
704
|
+
registerToolSchema("get-class-members", getClassMembersSchema);
|
|
705
|
+
registerExpertTool(server, "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) => {
|
|
706
|
+
const scope = input.packagePrefix || input.fileGlob || input.symbolKind
|
|
707
|
+
? {
|
|
708
|
+
packagePrefix: input.packagePrefix,
|
|
709
|
+
fileGlob: input.fileGlob,
|
|
710
|
+
symbolKind: input.symbolKind
|
|
711
|
+
}
|
|
712
|
+
: undefined;
|
|
713
|
+
return sourceService.searchClassSource({
|
|
714
|
+
artifactId: await resolveFlatArtifactId(input),
|
|
715
|
+
query: input.query,
|
|
716
|
+
intent: input.intent,
|
|
717
|
+
match: input.match,
|
|
718
|
+
scope: scope,
|
|
719
|
+
queryMode: input.queryMode,
|
|
720
|
+
limit: input.limit,
|
|
721
|
+
cursor: input.cursor,
|
|
722
|
+
queryNamespace: input.queryNamespace,
|
|
723
|
+
sourcePriority: input.sourcePriority,
|
|
724
|
+
gradleUserHome: input.gradleUserHome
|
|
725
|
+
});
|
|
726
|
+
}));
|
|
727
|
+
registerToolSchema("search-class-source", searchClassSourceSchema);
|
|
728
|
+
registerExpertTool(server, "get-artifact-file", "Get full source file content by artifactId and file path.", getArtifactFileShape, { readOnlyHint: true }, async (args) => runTool("get-artifact-file", args, getArtifactFileSchema, async (input) => sourceService.getArtifactFile({
|
|
729
|
+
artifactId: await resolveFlatArtifactId(input),
|
|
730
|
+
filePath: input.filePath,
|
|
731
|
+
maxBytes: input.maxBytes
|
|
732
|
+
})));
|
|
733
|
+
registerToolSchema("get-artifact-file", getArtifactFileSchema);
|
|
734
|
+
registerExpertTool(server, "list-artifact-files", "List source file paths in an artifact with optional prefix filter and cursor-based pagination.", listArtifactFilesShape, { readOnlyHint: true }, async (args) => runTool("list-artifact-files", args, listArtifactFilesSchema, async (input) => sourceService.listArtifactFiles({
|
|
735
|
+
artifactId: await resolveFlatArtifactId(input),
|
|
736
|
+
prefix: input.prefix,
|
|
737
|
+
limit: input.limit,
|
|
738
|
+
cursor: input.cursor
|
|
739
|
+
})));
|
|
740
|
+
registerToolSchema("list-artifact-files", listArtifactFilesSchema);
|
|
741
|
+
registerExpertTool(server, "trace-symbol-lifecycle", "Trace which Minecraft versions contain a specific class method and report first/last seen versions.", traceSymbolLifecycleShape, { readOnlyHint: true }, async (args) => runTool("trace-symbol-lifecycle", args, traceSymbolLifecycleSchema, async (input) => sourceService.traceSymbolLifecycle({
|
|
742
|
+
symbol: input.symbol,
|
|
743
|
+
descriptor: input.descriptor,
|
|
744
|
+
fromVersion: input.fromVersion,
|
|
745
|
+
toVersion: input.toVersion,
|
|
746
|
+
mapping: input.mapping,
|
|
747
|
+
sourcePriority: input.sourcePriority,
|
|
748
|
+
gradleUserHome: input.gradleUserHome,
|
|
749
|
+
includeSnapshots: input.includeSnapshots,
|
|
750
|
+
maxVersions: input.maxVersions,
|
|
751
|
+
includeTimeline: input.includeTimeline
|
|
752
|
+
})));
|
|
753
|
+
registerToolSchema("trace-symbol-lifecycle", traceSymbolLifecycleSchema);
|
|
754
|
+
registerExpertTool(server, "diff-class-signatures", "Compare one class signature between two Minecraft versions and report added/removed/modified constructors, methods, and fields.", diffClassSignaturesShape, { readOnlyHint: true }, async (args) => runTool("diff-class-signatures", args, diffClassSignaturesSchema, async (input) => sourceService.diffClassSignatures({
|
|
562
755
|
className: input.className,
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
target: normalizedTarget.target,
|
|
756
|
+
fromVersion: input.fromVersion,
|
|
757
|
+
toVersion: input.toVersion,
|
|
566
758
|
mapping: input.mapping,
|
|
567
759
|
sourcePriority: input.sourcePriority,
|
|
568
|
-
|
|
760
|
+
gradleUserHome: input.gradleUserHome,
|
|
761
|
+
includeFullDiff: input.includeFullDiff
|
|
762
|
+
})));
|
|
763
|
+
registerToolSchema("diff-class-signatures", diffClassSignaturesSchema);
|
|
764
|
+
registerExpertTool(server, "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({
|
|
765
|
+
version: input.version,
|
|
766
|
+
kind: input.kind,
|
|
767
|
+
name: input.name,
|
|
768
|
+
owner: input.owner,
|
|
769
|
+
descriptor: input.descriptor,
|
|
770
|
+
sourceMapping: input.sourceMapping,
|
|
771
|
+
targetMapping: input.targetMapping,
|
|
772
|
+
sourcePriority: input.sourcePriority,
|
|
773
|
+
gradleUserHome: input.gradleUserHome,
|
|
774
|
+
nameMode: input.nameMode,
|
|
775
|
+
signatureMode: input.signatureMode,
|
|
776
|
+
disambiguation: input.disambiguation,
|
|
777
|
+
maxCandidates: input.maxCandidates
|
|
778
|
+
})));
|
|
779
|
+
registerToolSchema("find-mapping", findMappingSchema);
|
|
780
|
+
registerExpertTool(server, "resolve-method-mapping-exact", "Strict variant of find-mapping(kind=method, signatureMode=exact): requires a COMPLETE descriptor projection and returns mapping_unavailable when any descriptor class reference cannot be projected. Prefer find-mapping unless you need that guarantee.", resolveMethodMappingExactShape, { readOnlyHint: true }, async (args) => runTool("resolve-method-mapping-exact", args, resolveMethodMappingExactSchema, async (input) => sourceService.resolveMethodMappingExact({
|
|
781
|
+
version: input.version,
|
|
782
|
+
name: input.name,
|
|
783
|
+
owner: input.owner,
|
|
784
|
+
descriptor: input.descriptor,
|
|
785
|
+
sourceMapping: input.sourceMapping,
|
|
786
|
+
targetMapping: input.targetMapping,
|
|
787
|
+
sourcePriority: input.sourcePriority,
|
|
788
|
+
gradleUserHome: input.gradleUserHome,
|
|
789
|
+
maxCandidates: input.maxCandidates
|
|
790
|
+
})));
|
|
791
|
+
registerToolSchema("resolve-method-mapping-exact", resolveMethodMappingExactSchema);
|
|
792
|
+
registerExpertTool(server, "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({
|
|
793
|
+
version: input.version,
|
|
794
|
+
className: input.className,
|
|
795
|
+
classNameMapping: input.classNameMapping,
|
|
796
|
+
includeKinds: parseClassApiKinds(input.includeKinds),
|
|
797
|
+
sourcePriority: input.sourcePriority,
|
|
798
|
+
gradleUserHome: input.gradleUserHome,
|
|
799
|
+
maxRows: input.maxRows,
|
|
800
|
+
cursor: input.cursor
|
|
801
|
+
})));
|
|
802
|
+
registerToolSchema("get-class-api-matrix", getClassApiMatrixSchema);
|
|
803
|
+
registerExpertTool(server, "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({
|
|
569
804
|
projectPath: input.projectPath,
|
|
805
|
+
version: input.version,
|
|
806
|
+
kind: input.kind,
|
|
807
|
+
name: input.name,
|
|
808
|
+
owner: input.owner,
|
|
809
|
+
descriptor: input.descriptor,
|
|
810
|
+
sourceMapping: input.sourceMapping,
|
|
811
|
+
sourcePriority: input.sourcePriority,
|
|
570
812
|
gradleUserHome: input.gradleUserHome,
|
|
813
|
+
maxCandidates: input.maxCandidates
|
|
814
|
+
})));
|
|
815
|
+
registerToolSchema("resolve-workspace-symbol", resolveWorkspaceSymbolSchema);
|
|
816
|
+
registerExpertTool(server, "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({
|
|
817
|
+
version: input.version,
|
|
818
|
+
kind: input.kind,
|
|
819
|
+
owner: input.owner,
|
|
820
|
+
name: input.name,
|
|
821
|
+
descriptor: input.descriptor,
|
|
822
|
+
sourceMapping: input.sourceMapping,
|
|
823
|
+
sourcePriority: input.sourcePriority,
|
|
824
|
+
gradleUserHome: input.gradleUserHome,
|
|
825
|
+
nameMode: input.nameMode,
|
|
826
|
+
signatureMode: input.signatureMode,
|
|
827
|
+
maxCandidates: input.maxCandidates
|
|
828
|
+
})));
|
|
829
|
+
registerToolSchema("check-symbol-exists", checkSymbolExistsSchema);
|
|
830
|
+
registerAppTool(server, "nbt-to-json", "Decode Java Edition NBT binary payload (base64) into typed JSON.", nbtToJsonShape, { readOnlyHint: true, openWorldHint: false }, async (args) => runTool("nbt-to-json", args, nbtToJsonSchema, async (input) => Promise.resolve(nbtBase64ToTypedJson({
|
|
831
|
+
nbtBase64: input.nbtBase64,
|
|
832
|
+
compression: input.compression
|
|
833
|
+
}, nbtLimits))));
|
|
834
|
+
registerToolSchema("nbt-to-json", nbtToJsonSchema);
|
|
835
|
+
registerAppTool(server, "nbt-apply-json-patch", "Apply RFC6902 add/remove/replace/test operations to typed NBT JSON.", nbtApplyJsonPatchShape, { readOnlyHint: true, openWorldHint: false }, async (args) => runTool("nbt-apply-json-patch", args, nbtApplyJsonPatchSchema, async (input) => Promise.resolve(applyNbtJsonPatch({
|
|
836
|
+
typedJson: input.typedJson,
|
|
837
|
+
patch: input.patch
|
|
838
|
+
}, nbtLimits))));
|
|
839
|
+
registerToolSchema("nbt-apply-json-patch", nbtApplyJsonPatchSchema);
|
|
840
|
+
registerAppTool(server, "json-to-nbt", "Encode typed NBT JSON to Java Edition NBT binary payload (base64).", jsonToNbtShape, { readOnlyHint: true, openWorldHint: false }, async (args) => runTool("json-to-nbt", args, jsonToNbtSchema, async (input) => Promise.resolve(typedJsonToNbtBase64({
|
|
841
|
+
typedJson: input.typedJson,
|
|
842
|
+
compression: input.compression
|
|
843
|
+
}, nbtLimits))));
|
|
844
|
+
registerToolSchema("json-to-nbt", jsonToNbtSchema);
|
|
845
|
+
registerExpertTool(server, "index-artifact", "Rebuild indexed files/symbols metadata for an artifact addressed by artifactId or target. A new target is resolved and ingested first; force rebuilds an existing index.", indexArtifactShape, { readOnlyHint: false, idempotentHint: true }, async (args) => runTool("index-artifact", args, indexArtifactSchema, async (input) => sourceService.indexArtifact({
|
|
846
|
+
artifactId: await resolveFlatArtifactId(input),
|
|
847
|
+
force: input.force
|
|
848
|
+
})));
|
|
849
|
+
registerToolSchema("index-artifact", indexArtifactSchema);
|
|
850
|
+
registerAppTool(server, "get-runtime-metrics", "Get runtime service counters and latency snapshots for cache/search/index diagnostics.", {}, { readOnlyHint: true, openWorldHint: false }, async (args) => runTool("get-runtime-metrics", args, emptySchema, async () => Promise.resolve(sourceService.getRuntimeMetrics())));
|
|
851
|
+
registerToolSchema("get-runtime-metrics", emptySchema);
|
|
852
|
+
registerExpertTool(server, "validate-mixin", "Validate Mixin source against Minecraft bytecode signatures for a given version.", validateMixinShape, { readOnlyHint: true }, async (args, ctx) => runTool("validate-mixin", args, validateMixinSchema, async (input) => sourceService.validateMixin({
|
|
853
|
+
input: input.input,
|
|
854
|
+
sourceRoots: input.sourceRoots,
|
|
855
|
+
version: input.version,
|
|
856
|
+
mapping: input.mapping,
|
|
857
|
+
sourcePriority: input.sourcePriority,
|
|
571
858
|
scope: input.scope,
|
|
859
|
+
projectPath: input.projectPath,
|
|
860
|
+
gradleUserHome: input.gradleUserHome,
|
|
572
861
|
preferProjectVersion: input.preferProjectVersion,
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
862
|
+
minSeverity: input.minSeverity,
|
|
863
|
+
hideUncertain: input.hideUncertain,
|
|
864
|
+
explain: input.explain,
|
|
865
|
+
warningMode: input.warningMode,
|
|
866
|
+
preferProjectMapping: input.preferProjectMapping,
|
|
867
|
+
reportMode: input.reportMode,
|
|
868
|
+
warningCategoryFilter: input.warningCategoryFilter,
|
|
869
|
+
treatInfoAsWarning: input.treatInfoAsWarning,
|
|
870
|
+
includeIssues: input.includeIssues
|
|
871
|
+
}, {
|
|
872
|
+
stageEmitter: makeStageEmitter(stageEmitterExtraFromCtx(ctx))
|
|
873
|
+
})));
|
|
874
|
+
registerToolSchema("validate-mixin", validateMixinSchema);
|
|
875
|
+
registerExpertTool(server, "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({
|
|
876
|
+
content: input.content,
|
|
877
|
+
version: input.version,
|
|
588
878
|
mapping: input.mapping,
|
|
589
879
|
sourcePriority: input.sourcePriority,
|
|
590
|
-
allowDecompile: input.allowDecompile,
|
|
591
|
-
access: input.access,
|
|
592
|
-
includeSynthetic: input.includeSynthetic,
|
|
593
|
-
includeInherited: input.includeInherited,
|
|
594
|
-
memberPattern: input.memberPattern,
|
|
595
|
-
maxMembers: input.maxMembers,
|
|
596
|
-
projection: input.projection,
|
|
597
|
-
cursor: input.cursor,
|
|
598
880
|
projectPath: input.projectPath,
|
|
599
881
|
gradleUserHome: input.gradleUserHome,
|
|
600
882
|
scope: input.scope,
|
|
601
|
-
preferProjectVersion: input.preferProjectVersion
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
}));
|
|
609
|
-
registerToolSchema("get-class-members", getClassMembersSchema);
|
|
610
|
-
expertTool("search-class-source", "Search indexed class source files for one artifact with symbol/text/path intent and compact hit output.", searchClassSourceShape, { readOnlyHint: true }, async (args) => runTool("search-class-source", args, searchClassSourceSchema, async (input) => {
|
|
611
|
-
const scope = input.packagePrefix || input.fileGlob || input.symbolKind
|
|
612
|
-
? {
|
|
613
|
-
packagePrefix: input.packagePrefix,
|
|
614
|
-
fileGlob: input.fileGlob,
|
|
615
|
-
symbolKind: input.symbolKind
|
|
616
|
-
}
|
|
617
|
-
: undefined;
|
|
618
|
-
return sourceService.searchClassSource({
|
|
619
|
-
artifactId: await resolveFlatArtifactId(input),
|
|
620
|
-
query: input.query,
|
|
621
|
-
intent: input.intent,
|
|
622
|
-
match: input.match,
|
|
623
|
-
scope: scope,
|
|
624
|
-
queryMode: input.queryMode,
|
|
625
|
-
limit: input.limit,
|
|
626
|
-
cursor: input.cursor,
|
|
627
|
-
queryNamespace: input.queryNamespace,
|
|
883
|
+
preferProjectVersion: input.preferProjectVersion
|
|
884
|
+
})));
|
|
885
|
+
registerToolSchema("validate-access-widener", validateAccessWidenerSchema);
|
|
886
|
+
registerExpertTool(server, "validate-access-transformer", "Validate Access Transformer file entries against Minecraft bytecode signatures for a given version.", validateAccessTransformerShape, { readOnlyHint: true }, async (args) => runTool("validate-access-transformer", args, validateAccessTransformerSchema, async (input) => sourceService.validateAccessTransformer({
|
|
887
|
+
content: input.content,
|
|
888
|
+
version: input.version,
|
|
889
|
+
atNamespace: input.atNamespace,
|
|
628
890
|
sourcePriority: input.sourcePriority,
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
}))
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
name: input.
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
}
|
|
739
|
-
registerToolSchema("nbt-to-json", nbtToJsonSchema);
|
|
740
|
-
server.tool("nbt-apply-json-patch", "Apply RFC6902 add/remove/replace/test operations to typed NBT JSON.", nbtApplyJsonPatchShape, { readOnlyHint: true, openWorldHint: false }, async (args) => runTool("nbt-apply-json-patch", args, nbtApplyJsonPatchSchema, async (input) => Promise.resolve(applyNbtJsonPatch({
|
|
741
|
-
typedJson: input.typedJson,
|
|
742
|
-
patch: input.patch
|
|
743
|
-
}, nbtLimits))));
|
|
744
|
-
registerToolSchema("nbt-apply-json-patch", nbtApplyJsonPatchSchema);
|
|
745
|
-
server.tool("json-to-nbt", "Encode typed NBT JSON to Java Edition NBT binary payload (base64).", jsonToNbtShape, { readOnlyHint: true, openWorldHint: false }, async (args) => runTool("json-to-nbt", args, jsonToNbtSchema, async (input) => Promise.resolve(typedJsonToNbtBase64({
|
|
746
|
-
typedJson: input.typedJson,
|
|
747
|
-
compression: input.compression
|
|
748
|
-
}, nbtLimits))));
|
|
749
|
-
registerToolSchema("json-to-nbt", jsonToNbtSchema);
|
|
750
|
-
expertTool("index-artifact", "Rebuild indexed files/symbols metadata for an artifact addressed by artifactId or target. A new target is resolved and ingested first; force rebuilds an existing index.", indexArtifactShape, { readOnlyHint: false, idempotentHint: true }, async (args) => runTool("index-artifact", args, indexArtifactSchema, async (input) => sourceService.indexArtifact({
|
|
751
|
-
artifactId: await resolveFlatArtifactId(input),
|
|
752
|
-
force: input.force
|
|
753
|
-
})));
|
|
754
|
-
registerToolSchema("index-artifact", indexArtifactSchema);
|
|
755
|
-
server.tool("get-runtime-metrics", "Get runtime service counters and latency snapshots for cache/search/index diagnostics.", { readOnlyHint: true, openWorldHint: false }, async (args) => runTool("get-runtime-metrics", args, emptySchema, async () => Promise.resolve(sourceService.getRuntimeMetrics())));
|
|
756
|
-
registerToolSchema("get-runtime-metrics", emptySchema);
|
|
757
|
-
expertTool("validate-mixin", "Validate Mixin source against Minecraft bytecode signatures for a given version.", validateMixinShape, { readOnlyHint: true }, async (args, extra) => runTool("validate-mixin", args, validateMixinSchema, async (input) => sourceService.validateMixin({
|
|
758
|
-
input: input.input,
|
|
759
|
-
sourceRoots: input.sourceRoots,
|
|
760
|
-
version: input.version,
|
|
761
|
-
mapping: input.mapping,
|
|
762
|
-
sourcePriority: input.sourcePriority,
|
|
763
|
-
scope: input.scope,
|
|
764
|
-
projectPath: input.projectPath,
|
|
765
|
-
gradleUserHome: input.gradleUserHome,
|
|
766
|
-
preferProjectVersion: input.preferProjectVersion,
|
|
767
|
-
minSeverity: input.minSeverity,
|
|
768
|
-
hideUncertain: input.hideUncertain,
|
|
769
|
-
explain: input.explain,
|
|
770
|
-
warningMode: input.warningMode,
|
|
771
|
-
preferProjectMapping: input.preferProjectMapping,
|
|
772
|
-
reportMode: input.reportMode,
|
|
773
|
-
warningCategoryFilter: input.warningCategoryFilter,
|
|
774
|
-
treatInfoAsWarning: input.treatInfoAsWarning,
|
|
775
|
-
includeIssues: input.includeIssues
|
|
776
|
-
}, {
|
|
777
|
-
stageEmitter: makeStageEmitter(extra)
|
|
778
|
-
})));
|
|
779
|
-
registerToolSchema("validate-mixin", validateMixinSchema);
|
|
780
|
-
expertTool("validate-access-widener", "Validate Access Widener file entries against Minecraft bytecode signatures for a given version.", validateAccessWidenerShape, { readOnlyHint: true }, async (args) => runTool("validate-access-widener", args, validateAccessWidenerSchema, async (input) => sourceService.validateAccessWidener({
|
|
781
|
-
content: input.content,
|
|
782
|
-
version: input.version,
|
|
783
|
-
mapping: input.mapping,
|
|
784
|
-
sourcePriority: input.sourcePriority,
|
|
785
|
-
projectPath: input.projectPath,
|
|
786
|
-
gradleUserHome: input.gradleUserHome,
|
|
787
|
-
scope: input.scope,
|
|
788
|
-
preferProjectVersion: input.preferProjectVersion
|
|
789
|
-
})));
|
|
790
|
-
registerToolSchema("validate-access-widener", validateAccessWidenerSchema);
|
|
791
|
-
expertTool("validate-access-transformer", "Validate Access Transformer file entries against Minecraft bytecode signatures for a given version.", validateAccessTransformerShape, { readOnlyHint: true }, async (args) => runTool("validate-access-transformer", args, validateAccessTransformerSchema, async (input) => sourceService.validateAccessTransformer({
|
|
792
|
-
content: input.content,
|
|
793
|
-
version: input.version,
|
|
794
|
-
atNamespace: input.atNamespace,
|
|
795
|
-
sourcePriority: input.sourcePriority,
|
|
796
|
-
projectPath: input.projectPath,
|
|
797
|
-
gradleUserHome: input.gradleUserHome,
|
|
798
|
-
scope: input.scope,
|
|
799
|
-
preferProjectVersion: input.preferProjectVersion
|
|
800
|
-
})));
|
|
801
|
-
registerToolSchema("validate-access-transformer", validateAccessTransformerSchema);
|
|
802
|
-
expertTool("analyze-mod-jar", "Analyze a Minecraft mod JAR to extract loader type, metadata, entrypoints, mixins, and dependencies.", analyzeModJarShape, { readOnlyHint: true }, async (args) => runTool("analyze-mod-jar", args, analyzeModJarSchema, async (input) => {
|
|
803
|
-
const result = await analyzeModJar(input.jarPath, {
|
|
804
|
-
includeClasses: input.includeClasses ?? false
|
|
805
|
-
});
|
|
806
|
-
return result;
|
|
807
|
-
}));
|
|
808
|
-
registerToolSchema("analyze-mod-jar", analyzeModJarSchema);
|
|
809
|
-
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({
|
|
810
|
-
version: input.version,
|
|
811
|
-
registry: input.registry,
|
|
812
|
-
includeData: input.includeData,
|
|
813
|
-
maxEntriesPerRegistry: input.maxEntriesPerRegistry
|
|
814
|
-
})));
|
|
815
|
-
registerToolSchema("get-registry-data", getRegistryDataSchema);
|
|
816
|
-
expertTool("compare-versions", "Compare two Minecraft versions to find added/removed classes and registry entry changes.", compareVersionsShape, { readOnlyHint: true }, async (args) => runTool("compare-versions", args, compareVersionsSchema, async (input) => sourceService.compareVersions({
|
|
817
|
-
fromVersion: input.fromVersion,
|
|
818
|
-
toVersion: input.toVersion,
|
|
819
|
-
category: input.category,
|
|
820
|
-
packageFilter: input.packageFilter,
|
|
821
|
-
maxClassResults: input.maxClassResults
|
|
822
|
-
})));
|
|
823
|
-
registerToolSchema("compare-versions", compareVersionsSchema);
|
|
824
|
-
expertTool("decompile-mod-jar", "Decompile a Minecraft mod JAR using Vineflower and list available classes, or view a specific class source. Builds on analyze-mod-jar.", decompileModJarShape, { readOnlyHint: true }, async (args) => runTool("decompile-mod-jar", args, decompileModJarSchema, async (input) => sourceService.decompileModJar({
|
|
825
|
-
jarPath: input.jarPath,
|
|
826
|
-
className: input.className,
|
|
827
|
-
includeFiles: input.includeFiles,
|
|
828
|
-
maxFiles: input.maxFiles
|
|
829
|
-
})));
|
|
830
|
-
registerToolSchema("decompile-mod-jar", decompileModJarSchema);
|
|
831
|
-
expertTool("get-mod-class-source", "Get decompiled source code for a specific class in a mod JAR. The mod JAR will be decompiled if not already cached. Not read-only: outputFile writes the source to disk.", getModClassSourceShape, { readOnlyHint: false }, async (args) => runTool("get-mod-class-source", args, getModClassSourceSchema, async (input) => sourceService.getModClassSource({
|
|
832
|
-
jarPath: input.jarPath,
|
|
833
|
-
className: input.className,
|
|
834
|
-
maxLines: input.maxLines,
|
|
835
|
-
maxChars: input.maxChars,
|
|
836
|
-
outputFile: input.outputFile
|
|
837
|
-
})));
|
|
838
|
-
registerToolSchema("get-mod-class-source", getModClassSourceSchema);
|
|
839
|
-
expertTool("search-mod-source", "Search through decompiled mod JAR source code by class name, method, field, or content pattern. The mod JAR will be decompiled automatically if not already cached.", searchModSourceShape, { readOnlyHint: true }, async (args) => runTool("search-mod-source", args, searchModSourceSchema, async (input) => sourceService.searchModSource({
|
|
840
|
-
jarPath: input.jarPath,
|
|
841
|
-
query: input.query,
|
|
842
|
-
searchType: input.searchType,
|
|
843
|
-
limit: input.limit
|
|
844
|
-
})));
|
|
845
|
-
registerToolSchema("search-mod-source", searchModSourceSchema);
|
|
846
|
-
expertTool("remap-mod-jar", "Remap a Fabric mod JAR from intermediary to yarn/mojang names. Requires Java to be installed.", remapModJarShape, { readOnlyHint: false }, async (args) => runTool("remap-mod-jar", args, remapModJarSchema, async (input) => {
|
|
847
|
-
const result = await remapModJar({
|
|
848
|
-
inputJar: input.inputJar,
|
|
849
|
-
outputJar: input.outputJar,
|
|
850
|
-
mcVersion: input.mcVersion,
|
|
851
|
-
targetMapping: input.targetMapping,
|
|
852
|
-
forceRemap: input.forceRemap
|
|
853
|
-
}, config);
|
|
854
|
-
return result;
|
|
855
|
-
}));
|
|
856
|
-
registerToolSchema("remap-mod-jar", remapModJarSchema);
|
|
891
|
+
projectPath: input.projectPath,
|
|
892
|
+
gradleUserHome: input.gradleUserHome,
|
|
893
|
+
scope: input.scope,
|
|
894
|
+
preferProjectVersion: input.preferProjectVersion
|
|
895
|
+
})));
|
|
896
|
+
registerToolSchema("validate-access-transformer", validateAccessTransformerSchema);
|
|
897
|
+
registerExpertTool(server, "analyze-mod-jar", "Analyze a Minecraft mod JAR to extract loader type, metadata, entrypoints, mixins, and dependencies.", analyzeModJarShape, { readOnlyHint: true }, async (args) => runTool("analyze-mod-jar", args, analyzeModJarSchema, async (input) => {
|
|
898
|
+
const result = await analyzeModJar(input.jarPath, {
|
|
899
|
+
includeClasses: input.includeClasses ?? false
|
|
900
|
+
});
|
|
901
|
+
return result;
|
|
902
|
+
}));
|
|
903
|
+
registerToolSchema("analyze-mod-jar", analyzeModJarSchema);
|
|
904
|
+
registerAppTool(server, "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({
|
|
905
|
+
version: input.version,
|
|
906
|
+
registry: input.registry,
|
|
907
|
+
includeData: input.includeData,
|
|
908
|
+
maxEntriesPerRegistry: input.maxEntriesPerRegistry
|
|
909
|
+
})));
|
|
910
|
+
registerToolSchema("get-registry-data", getRegistryDataSchema);
|
|
911
|
+
registerExpertTool(server, "compare-versions", "Compare two Minecraft versions to find added/removed classes and registry entry changes.", compareVersionsShape, { readOnlyHint: true }, async (args) => runTool("compare-versions", args, compareVersionsSchema, async (input) => sourceService.compareVersions({
|
|
912
|
+
fromVersion: input.fromVersion,
|
|
913
|
+
toVersion: input.toVersion,
|
|
914
|
+
category: input.category,
|
|
915
|
+
packageFilter: input.packageFilter,
|
|
916
|
+
maxClassResults: input.maxClassResults
|
|
917
|
+
})));
|
|
918
|
+
registerToolSchema("compare-versions", compareVersionsSchema);
|
|
919
|
+
registerExpertTool(server, "decompile-mod-jar", "Decompile a Minecraft mod JAR using Vineflower and list available classes, or view a specific class source. Builds on analyze-mod-jar.", decompileModJarShape, { readOnlyHint: true }, async (args) => runTool("decompile-mod-jar", args, decompileModJarSchema, async (input) => sourceService.decompileModJar({
|
|
920
|
+
jarPath: input.jarPath,
|
|
921
|
+
className: input.className,
|
|
922
|
+
includeFiles: input.includeFiles,
|
|
923
|
+
maxFiles: input.maxFiles
|
|
924
|
+
})));
|
|
925
|
+
registerToolSchema("decompile-mod-jar", decompileModJarSchema);
|
|
926
|
+
registerExpertTool(server, "get-mod-class-source", "Get decompiled source code for a specific class in a mod JAR. The mod JAR will be decompiled if not already cached. Not read-only: outputFile writes the source to disk.", getModClassSourceShape, { readOnlyHint: false }, async (args) => runTool("get-mod-class-source", args, getModClassSourceSchema, async (input) => sourceService.getModClassSource({
|
|
927
|
+
jarPath: input.jarPath,
|
|
928
|
+
className: input.className,
|
|
929
|
+
maxLines: input.maxLines,
|
|
930
|
+
maxChars: input.maxChars,
|
|
931
|
+
outputFile: input.outputFile
|
|
932
|
+
})));
|
|
933
|
+
registerToolSchema("get-mod-class-source", getModClassSourceSchema);
|
|
934
|
+
registerExpertTool(server, "search-mod-source", "Search through decompiled mod JAR source code by class name, method, field, or content pattern. The mod JAR will be decompiled automatically if not already cached.", searchModSourceShape, { readOnlyHint: true }, async (args) => runTool("search-mod-source", args, searchModSourceSchema, async (input) => sourceService.searchModSource({
|
|
935
|
+
jarPath: input.jarPath,
|
|
936
|
+
query: input.query,
|
|
937
|
+
searchType: input.searchType,
|
|
938
|
+
limit: input.limit
|
|
939
|
+
})));
|
|
940
|
+
registerToolSchema("search-mod-source", searchModSourceSchema);
|
|
941
|
+
registerExpertTool(server, "remap-mod-jar", "Remap a Fabric mod JAR from intermediary to yarn/mojang names. Requires Java to be installed.", remapModJarShape, { readOnlyHint: false }, async (args) => runTool("remap-mod-jar", args, remapModJarSchema, async (input) => {
|
|
942
|
+
const result = await remapModJar({
|
|
943
|
+
inputJar: input.inputJar,
|
|
944
|
+
outputJar: input.outputJar,
|
|
945
|
+
mcVersion: input.mcVersion,
|
|
946
|
+
targetMapping: input.targetMapping,
|
|
947
|
+
forceRemap: input.forceRemap
|
|
948
|
+
}, config);
|
|
949
|
+
return result;
|
|
950
|
+
}));
|
|
951
|
+
registerToolSchema("remap-mod-jar", remapModJarSchema);
|
|
952
|
+
// Deferred-registration executor: legacy (and the ctx-less module singleton)
|
|
953
|
+
// keeps the frozen call-site order; a modern-era instance registers in raw
|
|
954
|
+
// tool-name-ascending order (plain code-unit comparison — tool names are
|
|
955
|
+
// ASCII, so this is byte order). The advertised tools/list order follows
|
|
956
|
+
// registration order in the SDK, which is exactly what the two golden/pinned
|
|
957
|
+
// contracts observe.
|
|
958
|
+
const toolRegistrationOrder = ctx?.era === "modern"
|
|
959
|
+
? [...pendingToolRegistrations].sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0))
|
|
960
|
+
: pendingToolRegistrations;
|
|
961
|
+
for (const pending of toolRegistrationOrder)
|
|
962
|
+
pending.register();
|
|
963
|
+
return server;
|
|
964
|
+
}
|
|
965
|
+
/**
|
|
966
|
+
* Module-scope singleton: populates the register-once tool-schema registry at
|
|
967
|
+
* load time and serves in-process consumers (tests drive its request handlers
|
|
968
|
+
* directly). The stdio wire path does NOT serve this instance — serveStdio's
|
|
969
|
+
* factory builds a fresh one per pinned/probe instance (see buildServer).
|
|
970
|
+
*/
|
|
971
|
+
const server = buildServer();
|
|
972
|
+
/**
|
|
973
|
+
* The worker's wire transport, kept module-scoped so per-request stage
|
|
974
|
+
* emitters can write `$/stageUpdate` notifications directly onto the wire
|
|
975
|
+
* (the v2 ServerContext exposes no raw sendNotification). Set once by
|
|
976
|
+
* startServer(); undefined outside stdio worker mode, in which case
|
|
977
|
+
* makeStageEmitter() NOOPs exactly as before.
|
|
978
|
+
*/
|
|
979
|
+
let workerTransport;
|
|
980
|
+
/**
|
|
981
|
+
* Builds the makeStageEmitter() argument from a v2 ServerContext: the
|
|
982
|
+
* JSON-RPC request id comes from ctx.mcpReq.id and delivery goes through the
|
|
983
|
+
* module-scoped worker transport. Returns undefined (NOOP emitter) when the
|
|
984
|
+
* transport is not up or the id is not a wire-correlatable string/number.
|
|
985
|
+
*/
|
|
986
|
+
function stageEmitterExtraFromCtx(ctx) {
|
|
987
|
+
const transport = workerTransport;
|
|
988
|
+
const requestId = ctx.mcpReq?.id;
|
|
989
|
+
if (!transport || (typeof requestId !== "string" && typeof requestId !== "number")) {
|
|
990
|
+
return undefined;
|
|
991
|
+
}
|
|
992
|
+
return {
|
|
993
|
+
requestId,
|
|
994
|
+
sendNotification: (notification) => transport.send({
|
|
995
|
+
jsonrpc: "2.0",
|
|
996
|
+
method: notification.method,
|
|
997
|
+
...(notification.params !== undefined ? { params: notification.params } : {})
|
|
998
|
+
})
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
857
1001
|
export async function startServer() {
|
|
858
1002
|
if (serverStarted) {
|
|
859
1003
|
return;
|
|
@@ -866,10 +1010,28 @@ export async function startServer() {
|
|
|
866
1010
|
sourceRepos: config.sourceRepos.length
|
|
867
1011
|
});
|
|
868
1012
|
const transport = new CompatStdioServerTransport();
|
|
869
|
-
|
|
1013
|
+
workerTransport = transport;
|
|
1014
|
+
// serveStdio owns the connection and calls the factory PER INSTANCE: a
|
|
1015
|
+
// modern server/discover opening builds a probe instance that a following
|
|
1016
|
+
// legacy initialize discards and replaces via a second factory call. The
|
|
1017
|
+
// probe path mutates its instance (modern-only handlers + protocol
|
|
1018
|
+
// versions), so the factory MUST build a fresh McpServer each time — never
|
|
1019
|
+
// the module-scope singleton — or probe mutations would leak onto the
|
|
1020
|
+
// re-pinned legacy instance and break the negotiate-down contract. The
|
|
1021
|
+
// transport is started synchronously inside serveStdio, so resolving here
|
|
1022
|
+
// keeps the READY marker contract: the worker is listening once
|
|
1023
|
+
// startServer() returns.
|
|
1024
|
+
// maxSubscriptions: 0 is defense in depth for the supervisor's admission
|
|
1025
|
+
// rejection of subscriptions/listen (docs/tool-reference.md, "Absent modern
|
|
1026
|
+
// surfaces"). The SDK stdio entry auto-provides that method with zero
|
|
1027
|
+
// registration and its default cap is 1024, so without this a frame that
|
|
1028
|
+
// somehow bypassed admission would be ACCEPTED — answered with an id-less
|
|
1029
|
+
// acknowledgment notification that never settles the request id — instead of
|
|
1030
|
+
// being refused.
|
|
1031
|
+
serveStdio(buildServer, { transport, maxSubscriptions: 0 });
|
|
870
1032
|
// In stdio mode, explicitly resume stdin so JSON-RPC lines are consumed.
|
|
871
1033
|
process.stdin.resume();
|
|
872
1034
|
serverStarted = true;
|
|
873
1035
|
}
|
|
874
|
-
export { server, sourceService, config, SERVER_VERSION };
|
|
1036
|
+
export { server, sourceService, config, SERVER_VERSION, buildServer };
|
|
875
1037
|
//# sourceMappingURL=index.js.map
|