@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/mapping-service.js
CHANGED
|
@@ -22,6 +22,12 @@ const SUPPORTED_MAPPINGS = new Set([
|
|
|
22
22
|
"intermediary",
|
|
23
23
|
"yarn"
|
|
24
24
|
]);
|
|
25
|
+
/**
|
|
26
|
+
* Cap on the declaring classes named in the owner-strict not_found warning. A real
|
|
27
|
+
* mapping can hold dozens of same-signature methods, and the warning is guidance, not
|
|
28
|
+
* an inventory — find-mapping is the tool that returns the full list.
|
|
29
|
+
*/
|
|
30
|
+
const MAX_REPORTED_DECLARING_OWNERS = 5;
|
|
25
31
|
/* parsers extracted to src/mapping/parsers/{symbol-records,normalize,proguard,tiny}.ts */
|
|
26
32
|
export class MappingService {
|
|
27
33
|
config;
|
|
@@ -388,39 +394,89 @@ export class MappingService {
|
|
|
388
394
|
const obfuscatedProjection = this.projectMethodDescriptorToTarget(graph, toObfuscatedPath, descriptor);
|
|
389
395
|
acceptedDescriptors.add(obfuscatedProjection.descriptor);
|
|
390
396
|
}
|
|
391
|
-
|
|
397
|
+
// "Strict" is the query's full advertised triple: owner + name + descriptor. The
|
|
398
|
+
// descriptor alone is not enough, because `lookupCandidates` also indexes methods
|
|
399
|
+
// under the OWNERLESS `<name><descriptor>` simple-name key — so a same-signature
|
|
400
|
+
// method on an unrelated class used to survive the filter and force an `ambiguous`
|
|
401
|
+
// verdict next to the one exact, confidence-1 candidate that answered the question.
|
|
402
|
+
const descriptorMatched = rawCandidates.filter((candidate) => candidate.descriptor !== undefined && acceptedDescriptors.has(candidate.descriptor));
|
|
403
|
+
const acceptedOwners = this.acceptedOwnersAlongPath(graph, path, owner);
|
|
404
|
+
const strictCandidates = descriptorMatched.filter((candidate) => candidate.owner !== undefined && acceptedOwners.has(candidate.owner));
|
|
405
|
+
// A candidate is attributed to the FIRST gate it failed, so the two counts partition
|
|
406
|
+
// the rejected set and never double-count a candidate that fails both.
|
|
407
|
+
const rejectedByDescriptor = rawCandidates.length - descriptorMatched.length;
|
|
408
|
+
const rejectedByOwner = descriptorMatched.length - strictCandidates.length;
|
|
392
409
|
if (strictCandidates.length === 1) {
|
|
393
410
|
const resolved = toResolutionCandidate(strictCandidates[0]);
|
|
411
|
+
// The verdict is computed from `strictCandidates`, so that is the set reported —
|
|
412
|
+
// the same rule the ambiguous branch follows. Returning the raw name-matched list
|
|
413
|
+
// here would publish candidates the strict filter had already rejected alongside
|
|
414
|
+
// `status: "resolved"`, with nothing to tell them apart from the answer.
|
|
415
|
+
const limitedStrictCandidates = limitResolutionCandidates([resolved], input.maxCandidates);
|
|
394
416
|
return {
|
|
395
417
|
querySymbol,
|
|
396
418
|
mappingContext,
|
|
397
419
|
resolved: true,
|
|
398
420
|
status: "resolved",
|
|
399
421
|
resolvedSymbol: resolved,
|
|
400
|
-
candidates:
|
|
401
|
-
candidateCount:
|
|
402
|
-
candidatesTruncated:
|
|
422
|
+
candidates: limitedStrictCandidates.candidates,
|
|
423
|
+
candidateCount: limitedStrictCandidates.candidateCount,
|
|
424
|
+
candidatesTruncated: limitedStrictCandidates.candidatesTruncated,
|
|
403
425
|
warnings,
|
|
404
426
|
provenance: this.provenanceForPath(graph, path)
|
|
405
427
|
};
|
|
406
428
|
}
|
|
407
429
|
if (strictCandidates.length > 1) {
|
|
430
|
+
// The verdict is computed from `strictCandidates`, so that is the set reported.
|
|
431
|
+
// Returning the raw name-matched list here put candidates the strict filter had
|
|
432
|
+
// already rejected — including full-confidence exact matches on other owners —
|
|
433
|
+
// next to `status: "ambiguous"`, with nothing to tell them apart from the two the
|
|
434
|
+
// tool actually could not choose between.
|
|
435
|
+
const strictResolutionCandidates = strictCandidates.map(toResolutionCandidate);
|
|
436
|
+
const limitedStrictCandidates = limitResolutionCandidates(strictResolutionCandidates, input.maxCandidates);
|
|
408
437
|
warnings.push("Exact method mapping is ambiguous for owner+method+descriptor.");
|
|
409
|
-
if (
|
|
438
|
+
if (limitedStrictCandidates.candidatesTruncated) {
|
|
410
439
|
warnings.push("Raise maxCandidates up to 200 to inspect the full candidate list, or narrow the lookup via find-mapping disambiguation hints.");
|
|
411
440
|
}
|
|
441
|
+
// Report the counts BY REASON. Most rejections here are by owner, not by
|
|
442
|
+
// descriptor, so a warning that names descriptor as the cause would be wrong.
|
|
443
|
+
const rejectedTotal = rejectedByOwner + rejectedByDescriptor;
|
|
444
|
+
if (rejectedTotal > 0) {
|
|
445
|
+
warnings.push(`${rejectedTotal} further name-matched candidate(s) were rejected before the verdict ` +
|
|
446
|
+
`(${rejectedByOwner} by owner, ${rejectedByDescriptor} by descriptor) and are not reported; ` +
|
|
447
|
+
`use find-mapping for the unfiltered list.`);
|
|
448
|
+
}
|
|
412
449
|
return {
|
|
413
450
|
querySymbol,
|
|
414
451
|
mappingContext,
|
|
415
452
|
resolved: false,
|
|
416
453
|
status: "ambiguous",
|
|
417
|
-
candidates:
|
|
418
|
-
candidateCount:
|
|
419
|
-
candidatesTruncated:
|
|
454
|
+
candidates: limitedStrictCandidates.candidates,
|
|
455
|
+
candidateCount: limitedStrictCandidates.candidateCount,
|
|
456
|
+
candidatesTruncated: limitedStrictCandidates.candidatesTruncated,
|
|
420
457
|
warnings,
|
|
421
|
-
provenance: this.provenanceForPath(graph, path)
|
|
458
|
+
provenance: this.provenanceForPath(graph, path),
|
|
459
|
+
ambiguityReasons: inferAmbiguityReasons(strictResolutionCandidates, pathUsesSource(graph.pairs, path, "mojang-client-mappings"))
|
|
422
460
|
};
|
|
423
461
|
}
|
|
462
|
+
// Owner-strictness has an accepted cost: a method the owner INHERITS rather than
|
|
463
|
+
// declares no longer resolves, because the mapping formats record declarations and
|
|
464
|
+
// carry no class hierarchy. Make that legible instead of returning a bare not_found.
|
|
465
|
+
if (rejectedByOwner > 0 && strictCandidates.length === 0) {
|
|
466
|
+
const declaringOwners = [
|
|
467
|
+
...new Set(descriptorMatched.map((candidate) => candidate.owner).filter(Boolean))
|
|
468
|
+
];
|
|
469
|
+
const shownOwners = declaringOwners.slice(0, MAX_REPORTED_DECLARING_OWNERS);
|
|
470
|
+
const ownerList = shownOwners.join(", ") +
|
|
471
|
+
(declaringOwners.length > shownOwners.length
|
|
472
|
+
? `, and ${declaringOwners.length - shownOwners.length} more`
|
|
473
|
+
: "");
|
|
474
|
+
warnings.push(`Exact method resolution is owner-strict: "${owner}" does not itself declare ` +
|
|
475
|
+
`${method}${descriptor}. ${rejectedByOwner} candidate(s) with that name and descriptor ` +
|
|
476
|
+
`are declared by other classes (${targetMapping}: ${ownerList}). An inherited or relocated ` +
|
|
477
|
+
`member cannot resolve here — use find-mapping for an owner-agnostic lookup, or query the ` +
|
|
478
|
+
`declaring class directly.`);
|
|
479
|
+
}
|
|
424
480
|
if (descriptorProjection.hadClassReferences && !descriptorProjection.complete) {
|
|
425
481
|
warnings.push(pathUsesSource(graph.pairs, path, "mojang-client-mappings")
|
|
426
482
|
? "Method descriptor could not be preserved through mojang-client-mappings and exact resolution is unavailable."
|
|
@@ -964,6 +1020,34 @@ export class MappingService {
|
|
|
964
1020
|
descriptor: item.record.descriptor
|
|
965
1021
|
}));
|
|
966
1022
|
}
|
|
1023
|
+
/**
|
|
1024
|
+
* The owner names that a query owner is allowed to appear under once candidates have
|
|
1025
|
+
* been projected along `path`.
|
|
1026
|
+
*
|
|
1027
|
+
* The query owner arrives in the SOURCE namespace while `mapCandidatesAlongPath`
|
|
1028
|
+
* hands back records whose `owner` is the declaring class in the TARGET namespace
|
|
1029
|
+
* (both parsers build member records with `createMethodSymbolRecord(<owner in that
|
|
1030
|
+
* namespace>, ...)`), so the two are only comparable after the owner itself has been
|
|
1031
|
+
* carried along the same path. Projecting the owner as a class record through
|
|
1032
|
+
* `mapCandidatesAlongPath` is that carriage, and it is the same mechanism
|
|
1033
|
+
* `projectMethodDescriptorToTarget` already uses for class references inside a
|
|
1034
|
+
* descriptor.
|
|
1035
|
+
*
|
|
1036
|
+
* `simple-name` class matches are excluded on purpose: they mean "some other class
|
|
1037
|
+
* that happens to share this simple name", which is exactly the foreign-owner noise
|
|
1038
|
+
* the strict filter exists to remove. The source-namespace name is kept as an
|
|
1039
|
+
* accepted alias so an owner that the mapping does not rename still matches itself.
|
|
1040
|
+
*/
|
|
1041
|
+
acceptedOwnersAlongPath(graph, path, owner) {
|
|
1042
|
+
const accepted = new Set([normalizeMappedSymbolOutput(owner)]);
|
|
1043
|
+
for (const candidate of this.mapCandidatesAlongPath(graph, path, createClassSymbolRecord(owner))) {
|
|
1044
|
+
if (candidate.kind !== "class" || candidate.matchKind === "simple-name") {
|
|
1045
|
+
continue;
|
|
1046
|
+
}
|
|
1047
|
+
accepted.add(normalizeMappedSymbolOutput(candidate.symbol));
|
|
1048
|
+
}
|
|
1049
|
+
return accepted;
|
|
1050
|
+
}
|
|
967
1051
|
projectMethodDescriptorToTarget(graph, path, descriptor) {
|
|
968
1052
|
let hadClassReferences = false;
|
|
969
1053
|
let complete = true;
|
package/dist/mcp-helpers.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CallToolResult, ReadResourceResult } from "@modelcontextprotocol/
|
|
1
|
+
import type { CallToolResult, ReadResourceResult, ServerContext } from "@modelcontextprotocol/server";
|
|
2
2
|
import { type ErrorCode } from "./errors.js";
|
|
3
3
|
type ObjectResultOptions = {
|
|
4
4
|
isError?: boolean;
|
|
@@ -6,9 +6,26 @@ type ObjectResultOptions = {
|
|
|
6
6
|
export declare function objectResult<T extends Record<string, unknown>>(data: T, options?: ObjectResultOptions): CallToolResult;
|
|
7
7
|
export declare function textResource(uri: string, value: string): ReadResourceResult;
|
|
8
8
|
export declare function objectResource(uri: string, data: Record<string, unknown>): ReadResourceResult;
|
|
9
|
+
/**
|
|
10
|
+
* Whether the request being handled carries the FULL shallow modern
|
|
11
|
+
* (2026-07-28) era signal in its lifted `_meta` envelope: a string
|
|
12
|
+
* protocol-version AND a plain-object clientCapabilities — the same shallow
|
|
13
|
+
* modern-signal check era-classifier's `classifyEraSignal` applies at
|
|
14
|
+
* supervisor admission (kept in lockstep; that module is the canonical
|
|
15
|
+
* definition). The version key ALONE is not enough: a claim-shaped-INVALID
|
|
16
|
+
* request (version present, capabilities missing/invalid) is
|
|
17
|
+
* legacy-permissively forwarded on a legacy-locked connection, the SDK's
|
|
18
|
+
* era-blind lift still surfaces the key at `ctx.mcpReq.envelope`, and the
|
|
19
|
+
* reply is encoded by the LEGACY codec — which serializes handler-returned
|
|
20
|
+
* fields verbatim. Shallow-VALID envelopes, by contrast, are answered by the
|
|
21
|
+
* 2026 codec (the SDK rejects envelope-less modern requests -32602
|
|
22
|
+
* pre-handler, and era-conflict admission keeps modern claims off legacy
|
|
23
|
+
* connections). Reads only public context surface (`ctx.mcpReq.envelope`).
|
|
24
|
+
*/
|
|
25
|
+
export declare function isModernEraRequest(ctx: Pick<ServerContext, "mcpReq"> | undefined): boolean;
|
|
9
26
|
export declare function errorResource(uri: string, error: string | {
|
|
10
27
|
message: string;
|
|
11
28
|
code?: ErrorCode;
|
|
12
29
|
details?: unknown;
|
|
13
|
-
}): ReadResourceResult;
|
|
30
|
+
}, ctx?: Pick<ServerContext, "mcpReq">): ReadResourceResult;
|
|
14
31
|
export {};
|
package/dist/mcp-helpers.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { ResourceNotFoundError } from "@modelcontextprotocol/server";
|
|
2
|
+
import { PROBLEM_DETAILS_READ_CACHE_FIELDS } from "./cache-policy.js";
|
|
3
|
+
import { CLIENT_CAPABILITIES_META_KEY, PROTOCOL_VERSION_META_KEY } from "./era-classifier.js";
|
|
1
4
|
import { ERROR_CODES } from "./errors.js";
|
|
2
5
|
import { retryClassForErrorCode, issueOriginForErrorCode, extractAllowlistedContext } from "./error-mapping.js";
|
|
3
6
|
import { toHints, extractValidatedSuggestionAndExamples, extractFieldErrorsFromDetails } from "./tool-guidance.js";
|
|
@@ -25,15 +28,18 @@ export function objectResource(uri, data) {
|
|
|
25
28
|
]
|
|
26
29
|
};
|
|
27
30
|
}
|
|
31
|
+
function isResourceNotFoundErrorCode(code) {
|
|
32
|
+
return (code === ERROR_CODES.FILE_NOT_FOUND ||
|
|
33
|
+
code === ERROR_CODES.SOURCE_NOT_FOUND ||
|
|
34
|
+
code === ERROR_CODES.CLASS_NOT_FOUND ||
|
|
35
|
+
code === ERROR_CODES.VERSION_NOT_FOUND ||
|
|
36
|
+
code === ERROR_CODES.JAR_NOT_FOUND);
|
|
37
|
+
}
|
|
28
38
|
function statusForResourceErrorCode(code) {
|
|
29
39
|
if (code === ERROR_CODES.INVALID_INPUT) {
|
|
30
40
|
return 400;
|
|
31
41
|
}
|
|
32
|
-
if (code
|
|
33
|
-
code === ERROR_CODES.SOURCE_NOT_FOUND ||
|
|
34
|
-
code === ERROR_CODES.CLASS_NOT_FOUND ||
|
|
35
|
-
code === ERROR_CODES.VERSION_NOT_FOUND ||
|
|
36
|
-
code === ERROR_CODES.JAR_NOT_FOUND) {
|
|
42
|
+
if (isResourceNotFoundErrorCode(code)) {
|
|
37
43
|
return 404;
|
|
38
44
|
}
|
|
39
45
|
if (code === ERROR_CODES.MAPPING_UNAVAILABLE ||
|
|
@@ -43,10 +49,38 @@ function statusForResourceErrorCode(code) {
|
|
|
43
49
|
}
|
|
44
50
|
return 500;
|
|
45
51
|
}
|
|
46
|
-
|
|
52
|
+
/** Shallow plain-object check, mirroring era-classifier's canonical helper. */
|
|
53
|
+
function isPlainObject(value) {
|
|
54
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Whether the request being handled carries the FULL shallow modern
|
|
58
|
+
* (2026-07-28) era signal in its lifted `_meta` envelope: a string
|
|
59
|
+
* protocol-version AND a plain-object clientCapabilities — the same shallow
|
|
60
|
+
* modern-signal check era-classifier's `classifyEraSignal` applies at
|
|
61
|
+
* supervisor admission (kept in lockstep; that module is the canonical
|
|
62
|
+
* definition). The version key ALONE is not enough: a claim-shaped-INVALID
|
|
63
|
+
* request (version present, capabilities missing/invalid) is
|
|
64
|
+
* legacy-permissively forwarded on a legacy-locked connection, the SDK's
|
|
65
|
+
* era-blind lift still surfaces the key at `ctx.mcpReq.envelope`, and the
|
|
66
|
+
* reply is encoded by the LEGACY codec — which serializes handler-returned
|
|
67
|
+
* fields verbatim. Shallow-VALID envelopes, by contrast, are answered by the
|
|
68
|
+
* 2026 codec (the SDK rejects envelope-less modern requests -32602
|
|
69
|
+
* pre-handler, and era-conflict admission keeps modern claims off legacy
|
|
70
|
+
* connections). Reads only public context surface (`ctx.mcpReq.envelope`).
|
|
71
|
+
*/
|
|
72
|
+
export function isModernEraRequest(ctx) {
|
|
73
|
+
const envelope = ctx?.mcpReq?.envelope;
|
|
74
|
+
return (typeof envelope?.[PROTOCOL_VERSION_META_KEY] === "string" &&
|
|
75
|
+
isPlainObject(envelope[CLIENT_CAPABILITIES_META_KEY]));
|
|
76
|
+
}
|
|
77
|
+
export function errorResource(uri, error, ctx) {
|
|
47
78
|
const isStr = typeof error === "string";
|
|
48
79
|
const detail = isStr ? error : error.message;
|
|
49
80
|
const code = isStr ? ERROR_CODES.INVALID_INPUT : error.code ?? ERROR_CODES.INTERNAL;
|
|
81
|
+
if (!isStr && isModernEraRequest(ctx) && isResourceNotFoundErrorCode(code)) {
|
|
82
|
+
throw new ResourceNotFoundError(uri, detail);
|
|
83
|
+
}
|
|
50
84
|
// Resource reads carry the same AppError as the equivalent tool call, so they
|
|
51
85
|
// get the same recovery metadata. Classifiers are always present; the rest is
|
|
52
86
|
// extracted from the AppError details (reusing the tool-error helpers as-is so
|
|
@@ -57,6 +91,14 @@ export function errorResource(uri, error) {
|
|
|
57
91
|
const fieldErrors = extractFieldErrorsFromDetails(details);
|
|
58
92
|
const context = extractAllowlistedContext(details);
|
|
59
93
|
return {
|
|
94
|
+
// ProblemDetails-read cache override (adopted policy): STRUCTURAL
|
|
95
|
+
// identification — this constructor IS the error-resource path, so the
|
|
96
|
+
// override never re-infers from serialized text. Emitted as
|
|
97
|
+
// handler-returned result fields (SDK precedence rank 1, beating the
|
|
98
|
+
// resource's configured class-row hint) and gated to modern-era requests
|
|
99
|
+
// because the 2025 codec serializes handler-returned fields verbatim —
|
|
100
|
+
// legacy replies must stay byte-identical to the frozen golden surfaces.
|
|
101
|
+
...(isModernEraRequest(ctx) ? PROBLEM_DETAILS_READ_CACHE_FIELDS : {}),
|
|
60
102
|
contents: [
|
|
61
103
|
{
|
|
62
104
|
uri,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Config } from "./types.js";
|
|
2
|
-
export type MappingNamespace = "obfuscated" | "mojang" | "yarn";
|
|
2
|
+
export type MappingNamespace = "obfuscated" | "mojang" | "intermediary" | "yarn";
|
|
3
3
|
type SignatureAccess = "public" | "all";
|
|
4
4
|
export interface ResponseContext {
|
|
5
5
|
minecraftVersion: string;
|
package/dist/mixin/types.d.ts
CHANGED
|
@@ -185,6 +185,10 @@ export type AccessWidenerValidationResult = {
|
|
|
185
185
|
invalid: number;
|
|
186
186
|
};
|
|
187
187
|
provenance?: RuntimeValidationProvenance<SourceMapping>;
|
|
188
|
+
/** True when the runtime jar used for evidence was not an exact match for the request. */
|
|
189
|
+
approximate?: boolean;
|
|
190
|
+
/** Why the verdict is approximate; present only alongside `approximate`. */
|
|
191
|
+
approximationReasons?: string[];
|
|
188
192
|
warnings: string[];
|
|
189
193
|
};
|
|
190
194
|
export type AccessTransformerValidationResult = {
|
|
@@ -204,5 +208,9 @@ export type AccessTransformerValidationResult = {
|
|
|
204
208
|
invalid: number;
|
|
205
209
|
};
|
|
206
210
|
provenance?: RuntimeValidationProvenance<AccessTransformerNamespace>;
|
|
211
|
+
/** True when the runtime jar used for evidence was not an exact match for the request. */
|
|
212
|
+
approximate?: boolean;
|
|
213
|
+
/** Why the verdict is approximate; present only alongside `approximate`. */
|
|
214
|
+
approximationReasons?: string[];
|
|
207
215
|
warnings: string[];
|
|
208
216
|
};
|
package/dist/mod-analyzer.js
CHANGED
|
@@ -26,13 +26,13 @@ const fabricModJsonSchema = z
|
|
|
26
26
|
name: z.string().optional(),
|
|
27
27
|
version: z.string().optional(),
|
|
28
28
|
description: z.string().optional(),
|
|
29
|
-
entrypoints: z.record(z.array(stringOrEntrypoint)).optional(),
|
|
29
|
+
entrypoints: z.record(z.string(), z.array(stringOrEntrypoint)).optional(),
|
|
30
30
|
mixins: z.array(stringOrMixinRef).optional(),
|
|
31
31
|
accessWidener: z.string().optional(),
|
|
32
|
-
depends: z.record(z.union([z.string(), z.array(z.string())])).optional(),
|
|
33
|
-
recommends: z.record(z.union([z.string(), z.array(z.string())])).optional(),
|
|
34
|
-
conflicts: z.record(z.union([z.string(), z.array(z.string())])).optional(),
|
|
35
|
-
suggests: z.record(z.union([z.string(), z.array(z.string())])).optional(),
|
|
32
|
+
depends: z.record(z.string(), z.union([z.string(), z.array(z.string())])).optional(),
|
|
33
|
+
recommends: z.record(z.string(), z.union([z.string(), z.array(z.string())])).optional(),
|
|
34
|
+
conflicts: z.record(z.string(), z.union([z.string(), z.array(z.string())])).optional(),
|
|
35
|
+
suggests: z.record(z.string(), z.union([z.string(), z.array(z.string())])).optional(),
|
|
36
36
|
jars: z.array(z.object({ file: z.string() }).passthrough()).optional()
|
|
37
37
|
})
|
|
38
38
|
.passthrough();
|
|
@@ -50,7 +50,7 @@ const quiltModJsonSchema = z
|
|
|
50
50
|
})
|
|
51
51
|
.passthrough()
|
|
52
52
|
.optional(),
|
|
53
|
-
entrypoints: z.record(z.array(stringOrEntrypoint)).optional(),
|
|
53
|
+
entrypoints: z.record(z.string(), z.array(stringOrEntrypoint)).optional(),
|
|
54
54
|
depends: z.array(z.unknown()).optional()
|
|
55
55
|
})
|
|
56
56
|
.passthrough()
|
|
@@ -72,7 +72,7 @@ const forgeModsTomlSchema = z
|
|
|
72
72
|
})
|
|
73
73
|
.passthrough())
|
|
74
74
|
.optional(),
|
|
75
|
-
dependencies: z.record(z.array(z.unknown())).optional(),
|
|
75
|
+
dependencies: z.record(z.string(), z.array(z.unknown())).optional(),
|
|
76
76
|
mixins: z.array(z.object({ config: z.string() }).passthrough()).optional(),
|
|
77
77
|
accessTransformers: z.array(z.object({ file: z.string().optional() }).passthrough()).optional()
|
|
78
78
|
})
|
|
@@ -193,6 +193,7 @@ export class ModDecompileService {
|
|
|
193
193
|
const decompileResult = await decompileBinaryJar(jarPath, this.config.cacheDir, {
|
|
194
194
|
vineflowerJarPath: vineflowerPath,
|
|
195
195
|
timeoutMs: DECOMPILE_TIMEOUT_MS,
|
|
196
|
+
maxMemoryMb: this.config.decompileMaxMemoryMb,
|
|
196
197
|
signature: cacheKey
|
|
197
198
|
});
|
|
198
199
|
const files = decompileResult.javaFiles.map((entry) => entry.filePath);
|
|
@@ -16,18 +16,28 @@ const TAG_ID_BY_NAME = {
|
|
|
16
16
|
longArray: 12
|
|
17
17
|
};
|
|
18
18
|
const TAG_NAME_BY_ID = Object.fromEntries(Object.entries(TAG_ID_BY_NAME).map(([name, id]) => [id, name]));
|
|
19
|
+
// Default repair guidance, mirroring the per-stage `nextAction` the size-limit path
|
|
20
|
+
// already sets. `toHints()` reads only `details.nextAction`, so without one these
|
|
21
|
+
// rejections reach the client with nothing actionable at all. A caller-supplied
|
|
22
|
+
// `nextAction` still wins: the spread comes last.
|
|
23
|
+
const PARSE_NEXT_ACTION = "The payload is not a well-formed Java NBT stream at the reported offset. Check that " +
|
|
24
|
+
"nbtBase64 holds the whole file and that compression matches how it was written " +
|
|
25
|
+
'(use compression "auto" to detect gzip).';
|
|
26
|
+
const ENCODE_NEXT_ACTION = "The typed document could not be written as Java NBT. Correct the node named by " +
|
|
27
|
+
"jsonPointer, then retry; round-tripping a real payload through nbt-to-json yields a " +
|
|
28
|
+
"document this encoder accepts.";
|
|
19
29
|
function parseError(message, details) {
|
|
20
30
|
return createError({
|
|
21
31
|
code: ERROR_CODES.NBT_PARSE_FAILED,
|
|
22
32
|
message,
|
|
23
|
-
details
|
|
33
|
+
details: { nextAction: PARSE_NEXT_ACTION, ...details }
|
|
24
34
|
});
|
|
25
35
|
}
|
|
26
36
|
function encodeError(message, details) {
|
|
27
37
|
return createError({
|
|
28
38
|
code: ERROR_CODES.NBT_ENCODE_FAILED,
|
|
29
39
|
message,
|
|
30
|
-
details
|
|
40
|
+
details: { nextAction: ENCODE_NEXT_ACTION, ...details }
|
|
31
41
|
});
|
|
32
42
|
}
|
|
33
43
|
function resolveTagName(tagId, pointer) {
|
package/dist/nbt/json-patch.js
CHANGED
|
@@ -7,25 +7,36 @@ function isRecord(value) {
|
|
|
7
7
|
!Array.isArray(value) &&
|
|
8
8
|
!ArrayBuffer.isView(value));
|
|
9
9
|
}
|
|
10
|
+
// Default repair guidance, mirroring the per-stage `nextAction` the NBT size-limit path
|
|
11
|
+
// already sets. `toHints()` reads only `details.nextAction`, so without one these
|
|
12
|
+
// rejections reach the client with nothing actionable. A caller-supplied `nextAction`
|
|
13
|
+
// still wins: the spread comes last.
|
|
14
|
+
const INVALID_PATCH_NEXT_ACTION = 'The patch must be an RFC6902 array of {"op": "add"|"remove"|"replace"|"test", "path": ' +
|
|
15
|
+
'"/json/pointer", "value": <typed-nbt-node>} objects. Paths address the typed document ' +
|
|
16
|
+
'(for example "/root/value/<key>"), and an added or replaced value is a typed node, not a bare scalar.';
|
|
17
|
+
const UNSUPPORTED_FEATURE_NEXT_ACTION = "This operation is not expressible in Java NBT. Rewrite it in terms the format supports " +
|
|
18
|
+
"(homogeneous lists, typed compound entries, decimal-string longs) and retry.";
|
|
19
|
+
const PATCH_CONFLICT_NEXT_ACTION = "The document did not hold what the patch expected at that path. Re-read the current " +
|
|
20
|
+
"document with nbt-to-json and rebuild the patch against it.";
|
|
10
21
|
function invalidPatch(message, details) {
|
|
11
22
|
throw createError({
|
|
12
23
|
code: ERROR_CODES.JSON_PATCH_INVALID,
|
|
13
24
|
message,
|
|
14
|
-
details
|
|
25
|
+
details: { nextAction: INVALID_PATCH_NEXT_ACTION, ...details }
|
|
15
26
|
});
|
|
16
27
|
}
|
|
17
28
|
function unsupportedFeature(message, details) {
|
|
18
29
|
throw createError({
|
|
19
30
|
code: ERROR_CODES.NBT_UNSUPPORTED_FEATURE,
|
|
20
31
|
message,
|
|
21
|
-
details
|
|
32
|
+
details: { nextAction: UNSUPPORTED_FEATURE_NEXT_ACTION, ...details }
|
|
22
33
|
});
|
|
23
34
|
}
|
|
24
35
|
function patchConflict(message, details) {
|
|
25
36
|
throw createError({
|
|
26
37
|
code: ERROR_CODES.JSON_PATCH_CONFLICT,
|
|
27
38
|
message,
|
|
28
|
-
details
|
|
39
|
+
details: { nextAction: PATCH_CONFLICT_NEXT_ACTION, ...details }
|
|
29
40
|
});
|
|
30
41
|
}
|
|
31
42
|
function parsePatchOperations(patch) {
|
package/dist/nbt/pipeline.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { gunzipSync, gzipSync } from "node:zlib";
|
|
2
|
-
import {
|
|
2
|
+
import { buildSuggestedCall } from "../build-suggested-call.js";
|
|
3
|
+
import { AppError, createError, ERROR_CODES } from "../errors.js";
|
|
3
4
|
import { decodeJavaNbt, encodeJavaNbt } from "./java-nbt-codec.js";
|
|
4
5
|
import { applyJsonPatch } from "./json-patch.js";
|
|
5
6
|
import { assertValidTypedNbtDocument } from "./typed-json.js";
|
|
@@ -46,6 +47,42 @@ function limitExceeded(stage, field, actual, limit) {
|
|
|
46
47
|
}
|
|
47
48
|
});
|
|
48
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* `assertValidTypedNbtDocument` has six call sites and knows nothing about tools, so the
|
|
52
|
+
* recovery suggestion is attached here — the only layer that knows which tool the caller
|
|
53
|
+
* invoked. The suggestion is a TEMPLATE (it needs the caller's own base64 payload), so it
|
|
54
|
+
* travels as `exampleCalls`; `params: undefined` means the primary-drop marker is never
|
|
55
|
+
* set and no "payload failed schema validation" hint is appended.
|
|
56
|
+
*/
|
|
57
|
+
function assertValidTypedNbtDocumentForTool(value, tool) {
|
|
58
|
+
try {
|
|
59
|
+
assertValidTypedNbtDocument(value);
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
if (!(error instanceof AppError) || error.code !== ERROR_CODES.NBT_INVALID_TYPED_JSON) {
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
throw createError({
|
|
66
|
+
code: error.code,
|
|
67
|
+
// Frozen: an approved-deviation ProblemDetails golden asserts this text verbatim.
|
|
68
|
+
message: error.message,
|
|
69
|
+
details: {
|
|
70
|
+
...error.details,
|
|
71
|
+
...buildSuggestedCall({
|
|
72
|
+
tool: "nbt-to-json",
|
|
73
|
+
params: undefined,
|
|
74
|
+
examples: [
|
|
75
|
+
{
|
|
76
|
+
params: { nbtBase64: "<base64-encoded-nbt-payload>" },
|
|
77
|
+
reason: `Decode a real NBT payload with nbt-to-json to obtain a well-formed typedJson ` +
|
|
78
|
+
`document, edit that, then retry ${tool} with it.`
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
49
86
|
function assertByteLimit(stage, field, actual, limit) {
|
|
50
87
|
if (actual > limit) {
|
|
51
88
|
limitExceeded(stage, field, actual, limit);
|
|
@@ -149,7 +186,7 @@ export function nbtBase64ToTypedJson(input, limits = DEFAULT_NBT_LIMITS) {
|
|
|
149
186
|
}
|
|
150
187
|
export function typedJsonToNbtBase64(input, limits = DEFAULT_NBT_LIMITS) {
|
|
151
188
|
const compression = parseEncodeCompression(input.compression);
|
|
152
|
-
|
|
189
|
+
assertValidTypedNbtDocumentForTool(input.typedJson, "json-to-nbt");
|
|
153
190
|
let output = encodeJavaNbt(input.typedJson);
|
|
154
191
|
if (compression === "gzip") {
|
|
155
192
|
output = gzipSync(output);
|
|
@@ -165,7 +202,7 @@ export function typedJsonToNbtBase64(input, limits = DEFAULT_NBT_LIMITS) {
|
|
|
165
202
|
};
|
|
166
203
|
}
|
|
167
204
|
export function applyNbtJsonPatch(input, limits = DEFAULT_NBT_LIMITS) {
|
|
168
|
-
|
|
205
|
+
assertValidTypedNbtDocumentForTool(input.typedJson, "nbt-apply-json-patch");
|
|
169
206
|
const patched = applyJsonPatch(input.typedJson, input.patch);
|
|
170
207
|
assertByteLimit("patch-output", "typedJson", jsonUtf8Bytes(patched.typedJson), limits.maxResponseBytes);
|
|
171
208
|
return patched;
|
package/dist/nbt/typed-json.js
CHANGED
|
@@ -205,13 +205,38 @@ export function validateTypedNbtDocument(value) {
|
|
|
205
205
|
}
|
|
206
206
|
return validateNode(value.root, "/root");
|
|
207
207
|
}
|
|
208
|
+
/**
|
|
209
|
+
* Prose form of the document contract. The `typedJson` argument is advertised on the
|
|
210
|
+
* wire as an empty JSON Schema (`{}`) and those bytes are frozen for legacy parity, so
|
|
211
|
+
* the rejection is the only place a caller can be told what the document must look
|
|
212
|
+
* like. Kept in sync with `docs/tool-reference.md` -> "Typed NBT document shape".
|
|
213
|
+
*/
|
|
214
|
+
const TYPED_NBT_DOCUMENT_SHAPE = 'A typed NBT document is {"rootName": <string>, "root": <node>}, where every node is ' +
|
|
215
|
+
'{"type": "byte|short|int|long|float|double|byteArray|string|list|compound|intArray|longArray", "value": ...}; ' +
|
|
216
|
+
'a "list" node also carries "elementType", and "long"/"longArray" values are decimal strings.';
|
|
208
217
|
export function assertValidTypedNbtDocument(value) {
|
|
209
218
|
const validation = validateTypedNbtDocument(value);
|
|
210
219
|
if (!validation.ok) {
|
|
220
|
+
const { jsonPointer, expectedType, actualType } = validation.issue;
|
|
221
|
+
// A root-level failure has an empty JSON pointer, which is useless as a field path;
|
|
222
|
+
// fall back to the argument name so the caller has something to act on.
|
|
223
|
+
const fieldPath = jsonPointer || "typedJson";
|
|
211
224
|
throw createError({
|
|
212
225
|
code: ERROR_CODES.NBT_INVALID_TYPED_JSON,
|
|
213
226
|
message: "Invalid typed NBT JSON document.",
|
|
214
|
-
details: {
|
|
227
|
+
details: {
|
|
228
|
+
// The raw issue triple stays in place: existing readers index it directly.
|
|
229
|
+
...validation.issue,
|
|
230
|
+
fieldErrors: [
|
|
231
|
+
{
|
|
232
|
+
path: fieldPath,
|
|
233
|
+
message: `expected ${expectedType}, received ${actualType}`,
|
|
234
|
+
code: "nbt_invalid_node"
|
|
235
|
+
}
|
|
236
|
+
],
|
|
237
|
+
nextAction: `Correct ${fieldPath}: expected ${expectedType}, received ${actualType}. ` +
|
|
238
|
+
TYPED_NBT_DOCUMENT_SHAPE
|
|
239
|
+
}
|
|
215
240
|
});
|
|
216
241
|
}
|
|
217
242
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { CallToolResult, McpServer, ServerContext, StandardSchemaWithJSON, ToolAnnotations } from "@modelcontextprotocol/server";
|
|
3
|
+
/**
|
|
4
|
+
* Validation-bypassing Standard Schema adapter for MCP SDK v2 tool
|
|
5
|
+
* registration.
|
|
6
|
+
*
|
|
7
|
+
* The v2 SDK validates tool arguments through `schema["~standard"].validate`
|
|
8
|
+
* before invoking the handler and returns generic InvalidParams text on
|
|
9
|
+
* failure. This adapter's identity `validate` passes ALL object args through
|
|
10
|
+
* raw so runTool() remains the single source of truth for validation and
|
|
11
|
+
* error envelopes (matching pre-migration behavior, where the equivalent v1
|
|
12
|
+
* layer was bypassed).
|
|
13
|
+
*
|
|
14
|
+
* `jsonSchema` is a Converter object whose `input()` serves the pinned
|
|
15
|
+
* pre-migration wire schema for the tool (V1_PARITY_SCHEMAS), advertised
|
|
16
|
+
* VERBATIM in tools/list. The zod fallback only applies to tools without a
|
|
17
|
+
* frozen fixture (none today).
|
|
18
|
+
*/
|
|
19
|
+
export declare function appValidated<S extends z.ZodObject>(name: string, schema: S): StandardSchemaWithJSON<unknown, z.output<S>>;
|
|
20
|
+
/**
|
|
21
|
+
* Tool handler shape used by all app registrations: raw (unvalidated) args
|
|
22
|
+
* plus the v2 ServerContext. Handlers delegate validation to runTool().
|
|
23
|
+
*/
|
|
24
|
+
export type AppToolHandler = (args: unknown, ctx: ServerContext) => Promise<CallToolResult>;
|
|
25
|
+
/**
|
|
26
|
+
* Registers a tool through the v2 `registerTool` API with the
|
|
27
|
+
* validation-bypassing adapter, preserving the v1 registration surface
|
|
28
|
+
* (name, description, zod raw shape, annotations, handler).
|
|
29
|
+
*/
|
|
30
|
+
export declare function registerAppTool(server: McpServer, name: string, description: string, shape: z.ZodRawShape, annotations: ToolAnnotations, handler: AppToolHandler): void;
|
|
31
|
+
export declare const EXPERT_TOOL_NOTE = " Expert tool: prefer the entry tools (inspect-minecraft, analyze-symbol, compare-minecraft, analyze-mod, validate-project) first.";
|
|
32
|
+
export declare function registerExpertTool(server: McpServer, name: string, description: string, shape: z.ZodRawShape, annotations: ToolAnnotations, handler: AppToolHandler): void;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { V1_PARITY_SCHEMAS } from "./v1-parity-schemas.js";
|
|
3
|
+
/**
|
|
4
|
+
* Validation-bypassing Standard Schema adapter for MCP SDK v2 tool
|
|
5
|
+
* registration.
|
|
6
|
+
*
|
|
7
|
+
* The v2 SDK validates tool arguments through `schema["~standard"].validate`
|
|
8
|
+
* before invoking the handler and returns generic InvalidParams text on
|
|
9
|
+
* failure. This adapter's identity `validate` passes ALL object args through
|
|
10
|
+
* raw so runTool() remains the single source of truth for validation and
|
|
11
|
+
* error envelopes (matching pre-migration behavior, where the equivalent v1
|
|
12
|
+
* layer was bypassed).
|
|
13
|
+
*
|
|
14
|
+
* `jsonSchema` is a Converter object whose `input()` serves the pinned
|
|
15
|
+
* pre-migration wire schema for the tool (V1_PARITY_SCHEMAS), advertised
|
|
16
|
+
* VERBATIM in tools/list. The zod fallback only applies to tools without a
|
|
17
|
+
* frozen fixture (none today).
|
|
18
|
+
*/
|
|
19
|
+
export function appValidated(name, schema) {
|
|
20
|
+
return {
|
|
21
|
+
"~standard": {
|
|
22
|
+
version: 1,
|
|
23
|
+
vendor: "app",
|
|
24
|
+
validate: (value) => ({ value: value }),
|
|
25
|
+
jsonSchema: {
|
|
26
|
+
input: () => V1_PARITY_SCHEMAS[name] ?? z.toJSONSchema(schema, { io: "input" })
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Registers a tool through the v2 `registerTool` API with the
|
|
33
|
+
* validation-bypassing adapter, preserving the v1 registration surface
|
|
34
|
+
* (name, description, zod raw shape, annotations, handler).
|
|
35
|
+
*/
|
|
36
|
+
export function registerAppTool(server, name, description, shape, annotations, handler) {
|
|
37
|
+
server.registerTool(name, {
|
|
38
|
+
description,
|
|
39
|
+
inputSchema: appValidated(name, z.object(shape)),
|
|
40
|
+
annotations
|
|
41
|
+
}, handler);
|
|
42
|
+
}
|
|
43
|
+
// Low-level/expert tools duplicate capability that the six entry tools expose
|
|
44
|
+
// in a single resolve+fetch call. registerExpertTool() registers them exactly
|
|
45
|
+
// like registerAppTool() but appends a note steering agents to the entry tools
|
|
46
|
+
// first. Entry tools, batch tools, and the NBT/runtime utilities (which have no
|
|
47
|
+
// entry equivalent) keep their plain descriptions via registerAppTool().
|
|
48
|
+
export const EXPERT_TOOL_NOTE = " Expert tool: prefer the entry tools (inspect-minecraft, analyze-symbol, compare-minecraft, analyze-mod, validate-project) first.";
|
|
49
|
+
export function registerExpertTool(server, name, description, shape, annotations, handler) {
|
|
50
|
+
registerAppTool(server, name, description + EXPERT_TOOL_NOTE, shape, annotations, handler);
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=registration-adapter.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type RequestContext = {
|
|
2
|
+
requestId: string;
|
|
3
|
+
deadlineAt?: number;
|
|
4
|
+
signal?: AbortSignal;
|
|
5
|
+
};
|
|
6
|
+
export declare function runWithRequestContext<T>(context: RequestContext, fn: () => T): T;
|
|
7
|
+
export declare function getRequestContext(): RequestContext | undefined;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
const requestContextStorage = new AsyncLocalStorage();
|
|
3
|
+
export function runWithRequestContext(context, fn) {
|
|
4
|
+
return requestContextStorage.run(context, fn);
|
|
5
|
+
}
|
|
6
|
+
export function getRequestContext() {
|
|
7
|
+
return requestContextStorage.getStore();
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=request-context.js.map
|
package/dist/resources.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type { McpServer } from "@modelcontextprotocol/
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/server";
|
|
2
2
|
import type { SourceService } from "./source-service.js";
|
|
3
3
|
export declare function registerResources(server: McpServer, sourceService: SourceService): void;
|