mcp-ts-template 2.4.0 → 2.4.2
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/dist/index.js +846 -212
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -29010,7 +29010,7 @@ var require_gaxios = __commonJS((exports) => {
|
|
|
29010
29010
|
function hasFetch() {
|
|
29011
29011
|
return hasWindow() && !!window.fetch;
|
|
29012
29012
|
}
|
|
29013
|
-
function
|
|
29013
|
+
function hasBuffer2() {
|
|
29014
29014
|
return typeof Buffer !== "undefined";
|
|
29015
29015
|
}
|
|
29016
29016
|
function hasHeader(options, header) {
|
|
@@ -29236,7 +29236,7 @@ Content-Type: ${partContentType}\r
|
|
|
29236
29236
|
const isFormData = typeof FormData === "undefined" ? false : (opts === null || opts === undefined ? undefined : opts.data) instanceof FormData;
|
|
29237
29237
|
if (is_stream_1.default.readable(opts.data)) {
|
|
29238
29238
|
opts.body = opts.data;
|
|
29239
|
-
} else if (
|
|
29239
|
+
} else if (hasBuffer2() && Buffer.isBuffer(opts.data)) {
|
|
29240
29240
|
opts.body = opts.data;
|
|
29241
29241
|
if (!hasHeader(opts, "Content-Type")) {
|
|
29242
29242
|
opts.headers["Content-Type"] = "application/json";
|
|
@@ -117141,7 +117141,7 @@ var z = /* @__PURE__ */ Object.freeze({
|
|
|
117141
117141
|
// package.json
|
|
117142
117142
|
var package_default = {
|
|
117143
117143
|
name: "mcp-ts-template",
|
|
117144
|
-
version: "2.
|
|
117144
|
+
version: "2.4.1",
|
|
117145
117145
|
mcpName: "io.github.cyanheads/mcp-ts-template",
|
|
117146
117146
|
description: "The definitive, production-grade template for building powerful and scalable Model Context Protocol (MCP) servers with TypeScript, featuring built-in observability (OpenTelemetry), declarative tooling, robust error handling, and a modular, DI-driven architecture.",
|
|
117147
117147
|
main: "dist/index.js",
|
|
@@ -117645,81 +117645,175 @@ var config = parseConfig();
|
|
|
117645
117645
|
|
|
117646
117646
|
// src/utils/telemetry/instrumentation.ts
|
|
117647
117647
|
var import_api = __toESM(require_src(), 1);
|
|
117648
|
-
|
|
117649
|
-
|
|
117650
|
-
var
|
|
117651
|
-
|
|
117652
|
-
|
|
117653
|
-
|
|
117654
|
-
|
|
117655
|
-
|
|
117656
|
-
|
|
117648
|
+
|
|
117649
|
+
// src/utils/internal/runtime.ts
|
|
117650
|
+
var safeHas = (key) => {
|
|
117651
|
+
try {
|
|
117652
|
+
return typeof globalThis[key] !== "undefined";
|
|
117653
|
+
} catch {
|
|
117654
|
+
return false;
|
|
117655
|
+
}
|
|
117656
|
+
};
|
|
117657
|
+
var isNode = typeof process !== "undefined" && typeof process.versions?.node === "string";
|
|
117658
|
+
var hasProcess = typeof process !== "undefined";
|
|
117659
|
+
var hasBuffer = typeof Buffer !== "undefined";
|
|
117660
|
+
var hasTextEncoder = safeHas("TextEncoder");
|
|
117661
|
+
var hasPerformanceNow = typeof globalThis.performance?.now === "function";
|
|
117662
|
+
var isWorkerLike = !isNode && typeof globalThis.WorkerGlobalScope !== "undefined";
|
|
117663
|
+
var isBrowserLike = !isNode && !isWorkerLike && safeHas("window");
|
|
117664
|
+
var runtimeCaps = {
|
|
117665
|
+
isNode,
|
|
117666
|
+
isWorkerLike,
|
|
117667
|
+
isBrowserLike,
|
|
117668
|
+
hasProcess,
|
|
117669
|
+
hasBuffer,
|
|
117670
|
+
hasTextEncoder,
|
|
117671
|
+
hasPerformanceNow
|
|
117672
|
+
};
|
|
117673
|
+
|
|
117674
|
+
// src/utils/telemetry/instrumentation.ts
|
|
117657
117675
|
var sdk = null;
|
|
117658
117676
|
var isOtelInitialized = false;
|
|
117659
|
-
|
|
117660
|
-
|
|
117661
|
-
|
|
117662
|
-
|
|
117663
|
-
|
|
117664
|
-
|
|
117665
|
-
|
|
117666
|
-
|
|
117667
|
-
|
|
117668
|
-
|
|
117669
|
-
|
|
117670
|
-
|
|
117671
|
-
|
|
117672
|
-
|
|
117673
|
-
"
|
|
117674
|
-
}
|
|
117675
|
-
|
|
117676
|
-
|
|
117677
|
-
|
|
117678
|
-
|
|
117679
|
-
|
|
117680
|
-
|
|
117681
|
-
|
|
117682
|
-
}
|
|
117683
|
-
const metricReader = metricsEndpoint ? new import_sdk_metrics.PeriodicExportingMetricReader({
|
|
117684
|
-
exporter: new import_exporter_metrics_otlp_http.OTLPMetricExporter({ url: metricsEndpoint }),
|
|
117685
|
-
exportIntervalMillis: 15000
|
|
117686
|
-
}) : undefined;
|
|
117687
|
-
sdk = new import_sdk_node.NodeSDK({
|
|
117688
|
-
resource,
|
|
117689
|
-
spanProcessors,
|
|
117690
|
-
...metricReader && { metricReader },
|
|
117691
|
-
sampler: new import_sdk_trace_node.TraceIdRatioBasedSampler(config.openTelemetry.samplingRatio),
|
|
117692
|
-
instrumentations: [
|
|
117693
|
-
import_auto_instrumentations_node.getNodeAutoInstrumentations({
|
|
117694
|
-
"@opentelemetry/instrumentation-http": {
|
|
117695
|
-
enabled: true,
|
|
117696
|
-
ignoreIncomingRequestHook: (req) => req.url === "/healthz"
|
|
117697
|
-
},
|
|
117698
|
-
"@opentelemetry/instrumentation-fs": { enabled: false }
|
|
117699
|
-
}),
|
|
117700
|
-
new import_instrumentation_pino.PinoInstrumentation({
|
|
117701
|
-
logHook: (_span, record) => {
|
|
117702
|
-
record["trace_id"] = _span.spanContext().traceId;
|
|
117703
|
-
record["span_id"] = _span.spanContext().spanId;
|
|
117704
|
-
}
|
|
117705
|
-
})
|
|
117706
|
-
]
|
|
117707
|
-
});
|
|
117708
|
-
sdk.start();
|
|
117709
|
-
import_api.diag.info(`OpenTelemetry initialized for ${config.openTelemetry.serviceName} v${config.openTelemetry.serviceVersion}`);
|
|
117710
|
-
} catch (error) {
|
|
117711
|
-
import_api.diag.error("Error initializing OpenTelemetry", error);
|
|
117712
|
-
sdk = null;
|
|
117677
|
+
var initializationPromise = null;
|
|
117678
|
+
function canUseNodeSDK() {
|
|
117679
|
+
return runtimeCaps.isNode && typeof process?.versions?.node === "string" && typeof process.env === "object";
|
|
117680
|
+
}
|
|
117681
|
+
function detectCloudResource() {
|
|
117682
|
+
const attrs = {};
|
|
117683
|
+
if (runtimeCaps.isWorkerLike) {
|
|
117684
|
+
attrs["cloud.provider"] = "cloudflare";
|
|
117685
|
+
attrs["cloud.platform"] = "cloudflare_workers";
|
|
117686
|
+
}
|
|
117687
|
+
if (typeof process !== "undefined" && process.env?.AWS_LAMBDA_FUNCTION_NAME) {
|
|
117688
|
+
attrs["cloud.provider"] = "aws";
|
|
117689
|
+
attrs["cloud.platform"] = "aws_lambda";
|
|
117690
|
+
if (process.env.AWS_REGION) {
|
|
117691
|
+
attrs["cloud.region"] = process.env.AWS_REGION;
|
|
117692
|
+
}
|
|
117693
|
+
}
|
|
117694
|
+
if (typeof process !== "undefined" && (process.env?.FUNCTION_TARGET || process.env?.K_SERVICE)) {
|
|
117695
|
+
attrs["cloud.provider"] = "gcp";
|
|
117696
|
+
attrs["cloud.platform"] = process.env.FUNCTION_TARGET ? "gcp_cloud_functions" : "gcp_cloud_run";
|
|
117697
|
+
if (process.env.GCP_REGION) {
|
|
117698
|
+
attrs["cloud.region"] = process.env.GCP_REGION;
|
|
117699
|
+
}
|
|
117713
117700
|
}
|
|
117701
|
+
attrs["deployment.environment.name"] = config.environment;
|
|
117702
|
+
return attrs;
|
|
117714
117703
|
}
|
|
117715
|
-
async function
|
|
117716
|
-
if (
|
|
117704
|
+
async function initializeOpenTelemetry() {
|
|
117705
|
+
if (initializationPromise) {
|
|
117706
|
+
return initializationPromise;
|
|
117707
|
+
}
|
|
117708
|
+
if (isOtelInitialized) {
|
|
117709
|
+
return;
|
|
117710
|
+
}
|
|
117711
|
+
initializationPromise = (async () => {
|
|
117712
|
+
if (!config.openTelemetry.enabled) {
|
|
117713
|
+
import_api.diag.info("OpenTelemetry disabled via configuration.");
|
|
117714
|
+
isOtelInitialized = true;
|
|
117715
|
+
return;
|
|
117716
|
+
}
|
|
117717
|
+
if (!canUseNodeSDK()) {
|
|
117718
|
+
import_api.diag.info("NodeSDK unavailable in this runtime. Using lightweight telemetry mode.");
|
|
117719
|
+
isOtelInitialized = true;
|
|
117720
|
+
return;
|
|
117721
|
+
}
|
|
117722
|
+
isOtelInitialized = true;
|
|
117717
117723
|
try {
|
|
117718
|
-
|
|
117719
|
-
|
|
117724
|
+
const [
|
|
117725
|
+
{ getNodeAutoInstrumentations },
|
|
117726
|
+
{ OTLPMetricExporter },
|
|
117727
|
+
{ OTLPTraceExporter },
|
|
117728
|
+
{ PinoInstrumentation },
|
|
117729
|
+
{ resourceFromAttributes },
|
|
117730
|
+
{ PeriodicExportingMetricReader },
|
|
117731
|
+
{ NodeSDK },
|
|
117732
|
+
{ BatchSpanProcessor, TraceIdRatioBasedSampler },
|
|
117733
|
+
{ ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION }
|
|
117734
|
+
] = await Promise.all([
|
|
117735
|
+
Promise.resolve().then(() => __toESM(require_src58(), 1)),
|
|
117736
|
+
Promise.resolve().then(() => __toESM(require_src62(), 1)),
|
|
117737
|
+
Promise.resolve().then(() => __toESM(require_src63(), 1)),
|
|
117738
|
+
Promise.resolve().then(() => __toESM(require_src40(), 1)),
|
|
117739
|
+
Promise.resolve().then(() => __toESM(require_src52(), 1)),
|
|
117740
|
+
Promise.resolve().then(() => __toESM(require_src59(), 1)),
|
|
117741
|
+
Promise.resolve().then(() => __toESM(require_src83(), 1)),
|
|
117742
|
+
Promise.resolve().then(() => __toESM(require_src67(), 1)),
|
|
117743
|
+
Promise.resolve().then(() => __toESM(require_index_incubating(), 1))
|
|
117744
|
+
]);
|
|
117745
|
+
const otelLogLevelString = config.openTelemetry.logLevel.toUpperCase();
|
|
117746
|
+
const otelLogLevel = import_api.DiagLogLevel[otelLogLevelString] ?? import_api.DiagLogLevel.INFO;
|
|
117747
|
+
import_api.diag.setLogger(new import_api.DiagConsoleLogger, otelLogLevel);
|
|
117748
|
+
const tracesEndpoint = config.openTelemetry.tracesEndpoint;
|
|
117749
|
+
const metricsEndpoint = config.openTelemetry.metricsEndpoint;
|
|
117750
|
+
if (!tracesEndpoint && !metricsEndpoint) {
|
|
117751
|
+
import_api.diag.warn("OTEL_ENABLED is true, but no OTLP endpoint for traces or metrics is configured. OpenTelemetry will not export any telemetry.");
|
|
117752
|
+
}
|
|
117753
|
+
const resource = resourceFromAttributes({
|
|
117754
|
+
[ATTR_SERVICE_NAME]: config.openTelemetry.serviceName,
|
|
117755
|
+
[ATTR_SERVICE_VERSION]: config.openTelemetry.serviceVersion,
|
|
117756
|
+
...detectCloudResource()
|
|
117757
|
+
});
|
|
117758
|
+
const spanProcessors = [];
|
|
117759
|
+
if (tracesEndpoint) {
|
|
117760
|
+
import_api.diag.info(`Using OTLP exporter for traces, endpoint: ${tracesEndpoint}`);
|
|
117761
|
+
const traceExporter = new OTLPTraceExporter({ url: tracesEndpoint });
|
|
117762
|
+
spanProcessors.push(new BatchSpanProcessor(traceExporter));
|
|
117763
|
+
} else {
|
|
117764
|
+
import_api.diag.info("No OTLP traces endpoint configured. Traces will not be exported.");
|
|
117765
|
+
}
|
|
117766
|
+
const metricReader = metricsEndpoint ? new PeriodicExportingMetricReader({
|
|
117767
|
+
exporter: new OTLPMetricExporter({ url: metricsEndpoint }),
|
|
117768
|
+
exportIntervalMillis: 15000
|
|
117769
|
+
}) : undefined;
|
|
117770
|
+
sdk = new NodeSDK({
|
|
117771
|
+
resource,
|
|
117772
|
+
spanProcessors,
|
|
117773
|
+
...metricReader && { metricReader },
|
|
117774
|
+
sampler: new TraceIdRatioBasedSampler(config.openTelemetry.samplingRatio),
|
|
117775
|
+
instrumentations: [
|
|
117776
|
+
getNodeAutoInstrumentations({
|
|
117777
|
+
"@opentelemetry/instrumentation-http": {
|
|
117778
|
+
enabled: true,
|
|
117779
|
+
ignoreIncomingRequestHook: (req) => req.url === "/healthz"
|
|
117780
|
+
},
|
|
117781
|
+
"@opentelemetry/instrumentation-fs": { enabled: false }
|
|
117782
|
+
}),
|
|
117783
|
+
new PinoInstrumentation({
|
|
117784
|
+
logHook: (_span, record) => {
|
|
117785
|
+
record["trace_id"] = _span.spanContext().traceId;
|
|
117786
|
+
record["span_id"] = _span.spanContext().spanId;
|
|
117787
|
+
}
|
|
117788
|
+
})
|
|
117789
|
+
]
|
|
117790
|
+
});
|
|
117791
|
+
sdk.start();
|
|
117792
|
+
import_api.diag.info(`OpenTelemetry NodeSDK initialized for ${config.openTelemetry.serviceName} v${config.openTelemetry.serviceVersion}`);
|
|
117720
117793
|
} catch (error) {
|
|
117721
|
-
import_api.diag.error("Error
|
|
117794
|
+
import_api.diag.error("Error initializing OpenTelemetry", error);
|
|
117795
|
+
sdk = null;
|
|
117796
|
+
throw error;
|
|
117722
117797
|
}
|
|
117798
|
+
})();
|
|
117799
|
+
return initializationPromise;
|
|
117800
|
+
}
|
|
117801
|
+
async function shutdownOpenTelemetry(timeoutMs = 5000) {
|
|
117802
|
+
if (!sdk) {
|
|
117803
|
+
return;
|
|
117804
|
+
}
|
|
117805
|
+
try {
|
|
117806
|
+
const shutdownPromise = sdk.shutdown();
|
|
117807
|
+
const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error("OpenTelemetry SDK shutdown timeout")), timeoutMs));
|
|
117808
|
+
await Promise.race([shutdownPromise, timeoutPromise]);
|
|
117809
|
+
import_api.diag.info("OpenTelemetry SDK terminated successfully.");
|
|
117810
|
+
} catch (error) {
|
|
117811
|
+
import_api.diag.error("Error terminating OpenTelemetry SDK", error);
|
|
117812
|
+
throw error;
|
|
117813
|
+
} finally {
|
|
117814
|
+
sdk = null;
|
|
117815
|
+
isOtelInitialized = false;
|
|
117816
|
+
initializationPromise = null;
|
|
117723
117817
|
}
|
|
117724
117818
|
}
|
|
117725
117819
|
|
|
@@ -117789,6 +117883,26 @@ var requestContextServiceInstance = {
|
|
|
117789
117883
|
}
|
|
117790
117884
|
}
|
|
117791
117885
|
return context;
|
|
117886
|
+
},
|
|
117887
|
+
withAuthInfo(authInfo, parentContext) {
|
|
117888
|
+
const baseContext = this.createRequestContext({
|
|
117889
|
+
operation: "withAuthInfo",
|
|
117890
|
+
parentContext,
|
|
117891
|
+
additionalContext: {
|
|
117892
|
+
tenantId: authInfo.tenantId
|
|
117893
|
+
}
|
|
117894
|
+
});
|
|
117895
|
+
const authContext2 = {
|
|
117896
|
+
sub: authInfo.subject ?? authInfo.clientId,
|
|
117897
|
+
scopes: authInfo.scopes,
|
|
117898
|
+
clientId: authInfo.clientId,
|
|
117899
|
+
token: authInfo.token,
|
|
117900
|
+
...authInfo.tenantId ? { tenantId: authInfo.tenantId } : {}
|
|
117901
|
+
};
|
|
117902
|
+
return {
|
|
117903
|
+
...baseContext,
|
|
117904
|
+
auth: authContext2
|
|
117905
|
+
};
|
|
117792
117906
|
}
|
|
117793
117907
|
};
|
|
117794
117908
|
var requestContextService = requestContextServiceInstance;
|
|
@@ -118201,8 +118315,10 @@ class Logger {
|
|
|
118201
118315
|
}
|
|
118202
118316
|
async createPinoLogger(level) {
|
|
118203
118317
|
const pinoLevel = mcpToPinoLevel[level] || "info";
|
|
118318
|
+
const isTest = config.environment === "testing";
|
|
118319
|
+
const enableTestLogs = process.env.ENABLE_TEST_LOGS === "true";
|
|
118204
118320
|
const pinoOptions = {
|
|
118205
|
-
level: pinoLevel,
|
|
118321
|
+
level: isTest && !enableTestLogs ? "silent" : pinoLevel,
|
|
118206
118322
|
base: {
|
|
118207
118323
|
env: config.environment,
|
|
118208
118324
|
version: config.mcpServerVersion,
|
|
@@ -118220,7 +118336,6 @@ class Logger {
|
|
|
118220
118336
|
const { default: path } = await import("path");
|
|
118221
118337
|
const transports = [];
|
|
118222
118338
|
const isDevelopment = config.environment === "development";
|
|
118223
|
-
const isTest = config.environment === "testing";
|
|
118224
118339
|
if (isDevelopment && !isServerless2) {
|
|
118225
118340
|
try {
|
|
118226
118341
|
const { createRequire: createRequire2 } = await import("node:module");
|
|
@@ -118436,6 +118551,25 @@ class Logger {
|
|
|
118436
118551
|
var logger = Logger.getInstance();
|
|
118437
118552
|
|
|
118438
118553
|
// src/utils/internal/error-handler/mappings.ts
|
|
118554
|
+
var COMPILED_PATTERN_CACHE = new Map;
|
|
118555
|
+
function getCompiledPattern(pattern) {
|
|
118556
|
+
const cacheKey = pattern instanceof RegExp ? pattern.source + pattern.flags : pattern;
|
|
118557
|
+
if (COMPILED_PATTERN_CACHE.has(cacheKey)) {
|
|
118558
|
+
return COMPILED_PATTERN_CACHE.get(cacheKey);
|
|
118559
|
+
}
|
|
118560
|
+
let compiled;
|
|
118561
|
+
if (pattern instanceof RegExp) {
|
|
118562
|
+
let flags = pattern.flags.replace("g", "");
|
|
118563
|
+
if (!flags.includes("i")) {
|
|
118564
|
+
flags += "i";
|
|
118565
|
+
}
|
|
118566
|
+
compiled = new RegExp(pattern.source, flags);
|
|
118567
|
+
} else {
|
|
118568
|
+
compiled = new RegExp(pattern, "i");
|
|
118569
|
+
}
|
|
118570
|
+
COMPILED_PATTERN_CACHE.set(cacheKey, compiled);
|
|
118571
|
+
return compiled;
|
|
118572
|
+
}
|
|
118439
118573
|
var ERROR_TYPE_MAPPINGS = {
|
|
118440
118574
|
SyntaxError: -32007 /* ValidationError */,
|
|
118441
118575
|
TypeError: -32007 /* ValidationError */,
|
|
@@ -118487,17 +118621,76 @@ var COMMON_ERROR_PATTERNS = [
|
|
|
118487
118621
|
errorCode: -32007 /* ValidationError */
|
|
118488
118622
|
}
|
|
118489
118623
|
];
|
|
118624
|
+
var COMPILED_ERROR_PATTERNS = COMMON_ERROR_PATTERNS.map((mapping) => ({
|
|
118625
|
+
...mapping,
|
|
118626
|
+
compiledPattern: getCompiledPattern(mapping.pattern)
|
|
118627
|
+
}));
|
|
118628
|
+
var PROVIDER_ERROR_PATTERNS = [
|
|
118629
|
+
{
|
|
118630
|
+
pattern: /ThrottlingException|TooManyRequestsException/i,
|
|
118631
|
+
errorCode: -32003 /* RateLimited */
|
|
118632
|
+
},
|
|
118633
|
+
{
|
|
118634
|
+
pattern: /AccessDenied|UnauthorizedOperation/i,
|
|
118635
|
+
errorCode: -32005 /* Forbidden */
|
|
118636
|
+
},
|
|
118637
|
+
{
|
|
118638
|
+
pattern: /ResourceNotFoundException/i,
|
|
118639
|
+
errorCode: -32001 /* NotFound */
|
|
118640
|
+
},
|
|
118641
|
+
{ pattern: /status code 401/i, errorCode: -32006 /* Unauthorized */ },
|
|
118642
|
+
{ pattern: /status code 403/i, errorCode: -32005 /* Forbidden */ },
|
|
118643
|
+
{ pattern: /status code 404/i, errorCode: -32001 /* NotFound */ },
|
|
118644
|
+
{ pattern: /status code 409/i, errorCode: -32002 /* Conflict */ },
|
|
118645
|
+
{ pattern: /status code 429/i, errorCode: -32003 /* RateLimited */ },
|
|
118646
|
+
{
|
|
118647
|
+
pattern: /status code 5\d\d/i,
|
|
118648
|
+
errorCode: -32000 /* ServiceUnavailable */
|
|
118649
|
+
},
|
|
118650
|
+
{
|
|
118651
|
+
pattern: /ECONNREFUSED|connection refused/i,
|
|
118652
|
+
errorCode: -32000 /* ServiceUnavailable */
|
|
118653
|
+
},
|
|
118654
|
+
{
|
|
118655
|
+
pattern: /ETIMEDOUT|connection timeout/i,
|
|
118656
|
+
errorCode: -32004 /* Timeout */
|
|
118657
|
+
},
|
|
118658
|
+
{
|
|
118659
|
+
pattern: /unique constraint|duplicate key/i,
|
|
118660
|
+
errorCode: -32002 /* Conflict */
|
|
118661
|
+
},
|
|
118662
|
+
{
|
|
118663
|
+
pattern: /foreign key constraint/i,
|
|
118664
|
+
errorCode: -32007 /* ValidationError */
|
|
118665
|
+
},
|
|
118666
|
+
{ pattern: /JWT expired/i, errorCode: -32006 /* Unauthorized */ },
|
|
118667
|
+
{
|
|
118668
|
+
pattern: /row level security/i,
|
|
118669
|
+
errorCode: -32005 /* Forbidden */
|
|
118670
|
+
},
|
|
118671
|
+
{
|
|
118672
|
+
pattern: /insufficient_quota|quota exceeded/i,
|
|
118673
|
+
errorCode: -32003 /* RateLimited */
|
|
118674
|
+
},
|
|
118675
|
+
{ pattern: /model_not_found/i, errorCode: -32001 /* NotFound */ },
|
|
118676
|
+
{
|
|
118677
|
+
pattern: /context_length_exceeded/i,
|
|
118678
|
+
errorCode: -32007 /* ValidationError */
|
|
118679
|
+
},
|
|
118680
|
+
{ pattern: /ENOTFOUND|DNS/i, errorCode: -32000 /* ServiceUnavailable */ },
|
|
118681
|
+
{
|
|
118682
|
+
pattern: /ECONNRESET|connection reset/i,
|
|
118683
|
+
errorCode: -32000 /* ServiceUnavailable */
|
|
118684
|
+
}
|
|
118685
|
+
];
|
|
118686
|
+
var COMPILED_PROVIDER_PATTERNS = PROVIDER_ERROR_PATTERNS.map((mapping) => ({
|
|
118687
|
+
...mapping,
|
|
118688
|
+
compiledPattern: getCompiledPattern(mapping.pattern)
|
|
118689
|
+
}));
|
|
118490
118690
|
|
|
118491
118691
|
// src/utils/internal/error-handler/helpers.ts
|
|
118492
118692
|
function createSafeRegex(pattern) {
|
|
118493
|
-
|
|
118494
|
-
let flags = pattern.flags.replace("g", "");
|
|
118495
|
-
if (!flags.includes("i")) {
|
|
118496
|
-
flags += "i";
|
|
118497
|
-
}
|
|
118498
|
-
return new RegExp(pattern.source, flags);
|
|
118499
|
-
}
|
|
118500
|
-
return new RegExp(pattern, "i");
|
|
118693
|
+
return getCompiledPattern(pattern);
|
|
118501
118694
|
}
|
|
118502
118695
|
function getErrorName(error) {
|
|
118503
118696
|
if (error instanceof Error) {
|
|
@@ -118558,6 +118751,61 @@ function getErrorMessage(error) {
|
|
|
118558
118751
|
return `Error converting error to string: ${conversionError instanceof Error ? conversionError.message : "Unknown conversion error"}`;
|
|
118559
118752
|
}
|
|
118560
118753
|
}
|
|
118754
|
+
function extractErrorCauseChain(error, maxDepth = 20) {
|
|
118755
|
+
const chain = [];
|
|
118756
|
+
const seen = new WeakSet;
|
|
118757
|
+
let current = error;
|
|
118758
|
+
let depth = 0;
|
|
118759
|
+
while (current && depth < maxDepth) {
|
|
118760
|
+
if (typeof current === "object" && current !== null) {
|
|
118761
|
+
if (seen.has(current)) {
|
|
118762
|
+
chain.push({
|
|
118763
|
+
name: "CircularReference",
|
|
118764
|
+
message: "Circular reference detected in error cause chain",
|
|
118765
|
+
depth
|
|
118766
|
+
});
|
|
118767
|
+
break;
|
|
118768
|
+
}
|
|
118769
|
+
seen.add(current);
|
|
118770
|
+
}
|
|
118771
|
+
if (current instanceof Error) {
|
|
118772
|
+
const node = {
|
|
118773
|
+
name: current.name,
|
|
118774
|
+
message: current.message,
|
|
118775
|
+
depth,
|
|
118776
|
+
...current.stack !== undefined ? { stack: current.stack } : {}
|
|
118777
|
+
};
|
|
118778
|
+
if (current instanceof McpError && current.data) {
|
|
118779
|
+
node.data = current.data;
|
|
118780
|
+
}
|
|
118781
|
+
chain.push(node);
|
|
118782
|
+
current = current.cause;
|
|
118783
|
+
} else if (typeof current === "string") {
|
|
118784
|
+
chain.push({
|
|
118785
|
+
name: "StringError",
|
|
118786
|
+
message: current,
|
|
118787
|
+
depth
|
|
118788
|
+
});
|
|
118789
|
+
break;
|
|
118790
|
+
} else {
|
|
118791
|
+
chain.push({
|
|
118792
|
+
name: getErrorName(current),
|
|
118793
|
+
message: getErrorMessage(current),
|
|
118794
|
+
depth
|
|
118795
|
+
});
|
|
118796
|
+
break;
|
|
118797
|
+
}
|
|
118798
|
+
depth++;
|
|
118799
|
+
}
|
|
118800
|
+
if (depth >= maxDepth) {
|
|
118801
|
+
chain.push({
|
|
118802
|
+
name: "MaxDepthExceeded",
|
|
118803
|
+
message: `Error cause chain exceeded maximum depth of ${maxDepth}`,
|
|
118804
|
+
depth
|
|
118805
|
+
});
|
|
118806
|
+
}
|
|
118807
|
+
return chain;
|
|
118808
|
+
}
|
|
118561
118809
|
|
|
118562
118810
|
// src/utils/internal/error-handler/errorHandler.ts
|
|
118563
118811
|
class ErrorHandler {
|
|
@@ -118571,9 +118819,13 @@ class ErrorHandler {
|
|
|
118571
118819
|
if (mappedFromType) {
|
|
118572
118820
|
return mappedFromType;
|
|
118573
118821
|
}
|
|
118574
|
-
for (const mapping of
|
|
118575
|
-
|
|
118576
|
-
|
|
118822
|
+
for (const mapping of COMPILED_PROVIDER_PATTERNS) {
|
|
118823
|
+
if (mapping.compiledPattern.test(errorMessage) || mapping.compiledPattern.test(errorName)) {
|
|
118824
|
+
return mapping.errorCode;
|
|
118825
|
+
}
|
|
118826
|
+
}
|
|
118827
|
+
for (const mapping of COMPILED_ERROR_PATTERNS) {
|
|
118828
|
+
if (mapping.compiledPattern.test(errorMessage) || mapping.compiledPattern.test(errorName)) {
|
|
118577
118829
|
return mapping.errorCode;
|
|
118578
118830
|
}
|
|
118579
118831
|
}
|
|
@@ -118620,17 +118872,19 @@ class ErrorHandler {
|
|
|
118620
118872
|
consolidatedData.originalStack = originalStack;
|
|
118621
118873
|
}
|
|
118622
118874
|
const cause = error instanceof Error ? error : undefined;
|
|
118623
|
-
const
|
|
118624
|
-
|
|
118625
|
-
|
|
118626
|
-
|
|
118627
|
-
|
|
118628
|
-
|
|
118875
|
+
const causeChain = extractErrorCauseChain(error);
|
|
118876
|
+
if (causeChain.length > 0) {
|
|
118877
|
+
const rootCause = causeChain[causeChain.length - 1];
|
|
118878
|
+
if (rootCause) {
|
|
118879
|
+
consolidatedData["rootCause"] = {
|
|
118880
|
+
name: rootCause.name,
|
|
118881
|
+
message: rootCause.message
|
|
118882
|
+
};
|
|
118629
118883
|
}
|
|
118630
|
-
|
|
118631
|
-
}
|
|
118632
|
-
if (
|
|
118633
|
-
consolidatedData["
|
|
118884
|
+
consolidatedData["causeChain"] = causeChain;
|
|
118885
|
+
}
|
|
118886
|
+
if (context && "metadata" in context && context.metadata && typeof context.metadata === "object" && "breadcrumbs" in context.metadata) {
|
|
118887
|
+
consolidatedData["breadcrumbs"] = context.metadata.breadcrumbs;
|
|
118634
118888
|
}
|
|
118635
118889
|
if (error instanceof McpError) {
|
|
118636
118890
|
loggedErrorCode = error.code;
|
|
@@ -118714,32 +118968,119 @@ class ErrorHandler {
|
|
|
118714
118968
|
});
|
|
118715
118969
|
}
|
|
118716
118970
|
}
|
|
118717
|
-
|
|
118718
|
-
|
|
118719
|
-
|
|
118720
|
-
|
|
118721
|
-
|
|
118722
|
-
|
|
118723
|
-
|
|
118971
|
+
static async tryAsResult(fn, options) {
|
|
118972
|
+
try {
|
|
118973
|
+
const value = await Promise.resolve(fn());
|
|
118974
|
+
return { ok: true, value };
|
|
118975
|
+
} catch (caughtError) {
|
|
118976
|
+
const error = ErrorHandler.handleError(caughtError, {
|
|
118977
|
+
...options,
|
|
118978
|
+
rethrow: false
|
|
118979
|
+
});
|
|
118980
|
+
return { ok: false, error };
|
|
118981
|
+
}
|
|
118724
118982
|
}
|
|
118725
|
-
|
|
118726
|
-
|
|
118727
|
-
|
|
118728
|
-
|
|
118729
|
-
|
|
118730
|
-
|
|
118731
|
-
|
|
118732
|
-
|
|
118733
|
-
|
|
118734
|
-
|
|
118735
|
-
|
|
118736
|
-
|
|
118737
|
-
|
|
118738
|
-
|
|
118739
|
-
|
|
118740
|
-
|
|
118741
|
-
}
|
|
118742
|
-
|
|
118983
|
+
static mapResult(result, fn) {
|
|
118984
|
+
if (result.ok) {
|
|
118985
|
+
try {
|
|
118986
|
+
return { ok: true, value: fn(result.value) };
|
|
118987
|
+
} catch (error) {
|
|
118988
|
+
return {
|
|
118989
|
+
ok: false,
|
|
118990
|
+
error: new McpError(-32603 /* InternalError */, `Error mapping result: ${getErrorMessage(error)}`)
|
|
118991
|
+
};
|
|
118992
|
+
}
|
|
118993
|
+
}
|
|
118994
|
+
return result;
|
|
118995
|
+
}
|
|
118996
|
+
static flatMapResult(result, fn) {
|
|
118997
|
+
if (result.ok) {
|
|
118998
|
+
return fn(result.value);
|
|
118999
|
+
}
|
|
119000
|
+
return result;
|
|
119001
|
+
}
|
|
119002
|
+
static recoverResult(result, fallback) {
|
|
119003
|
+
if (result.ok) {
|
|
119004
|
+
return result.value;
|
|
119005
|
+
}
|
|
119006
|
+
return typeof fallback === "function" ? fallback(result.error) : fallback;
|
|
119007
|
+
}
|
|
119008
|
+
static addBreadcrumb(context, operation, additionalData) {
|
|
119009
|
+
const breadcrumbs = context.metadata?.breadcrumbs || [];
|
|
119010
|
+
breadcrumbs.push({
|
|
119011
|
+
timestamp: new Date().toISOString(),
|
|
119012
|
+
operation,
|
|
119013
|
+
...additionalData !== undefined ? { context: additionalData } : {}
|
|
119014
|
+
});
|
|
119015
|
+
return {
|
|
119016
|
+
...context,
|
|
119017
|
+
metadata: {
|
|
119018
|
+
...context.metadata,
|
|
119019
|
+
breadcrumbs
|
|
119020
|
+
}
|
|
119021
|
+
};
|
|
119022
|
+
}
|
|
119023
|
+
static async tryCatchWithRetry(fn, options, strategy) {
|
|
119024
|
+
let lastError;
|
|
119025
|
+
for (let attempt = 1;attempt <= strategy.maxAttempts; attempt++) {
|
|
119026
|
+
try {
|
|
119027
|
+
return await Promise.resolve(fn());
|
|
119028
|
+
} catch (caughtError) {
|
|
119029
|
+
lastError = caughtError instanceof Error ? caughtError : new Error(String(caughtError));
|
|
119030
|
+
if (attempt < strategy.maxAttempts && strategy.shouldRetry(lastError, attempt)) {
|
|
119031
|
+
const delay = strategy.getRetryDelay(attempt);
|
|
119032
|
+
const retryContext = {
|
|
119033
|
+
...options.context,
|
|
119034
|
+
requestId: options.context?.requestId || generateUUID(),
|
|
119035
|
+
timestamp: new Date().toISOString(),
|
|
119036
|
+
operation: options.operation,
|
|
119037
|
+
error: lastError.message,
|
|
119038
|
+
attempt
|
|
119039
|
+
};
|
|
119040
|
+
logger.warning(`Retry attempt ${attempt}/${strategy.maxAttempts} after ${delay}ms`, retryContext);
|
|
119041
|
+
strategy.onRetry?.(lastError, attempt);
|
|
119042
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
119043
|
+
} else {
|
|
119044
|
+
throw ErrorHandler.handleError(lastError, {
|
|
119045
|
+
...options,
|
|
119046
|
+
rethrow: true,
|
|
119047
|
+
context: {
|
|
119048
|
+
...options.context,
|
|
119049
|
+
retryAttempts: attempt,
|
|
119050
|
+
finalAttempt: true
|
|
119051
|
+
}
|
|
119052
|
+
});
|
|
119053
|
+
}
|
|
119054
|
+
}
|
|
119055
|
+
}
|
|
119056
|
+
throw ErrorHandler.handleError(lastError, {
|
|
119057
|
+
...options,
|
|
119058
|
+
rethrow: true
|
|
119059
|
+
});
|
|
119060
|
+
}
|
|
119061
|
+
static createExponentialBackoffStrategy(maxAttempts = 3, baseDelay = 1000, maxDelay = 30000) {
|
|
119062
|
+
return {
|
|
119063
|
+
maxAttempts,
|
|
119064
|
+
shouldRetry: (error) => {
|
|
119065
|
+
if (error instanceof McpError) {
|
|
119066
|
+
const nonRetryableCodes = [
|
|
119067
|
+
-32007 /* ValidationError */,
|
|
119068
|
+
-32006 /* Unauthorized */,
|
|
119069
|
+
-32005 /* Forbidden */,
|
|
119070
|
+
-32001 /* NotFound */
|
|
119071
|
+
];
|
|
119072
|
+
return !nonRetryableCodes.includes(error.code);
|
|
119073
|
+
}
|
|
119074
|
+
return true;
|
|
119075
|
+
},
|
|
119076
|
+
getRetryDelay: (attemptNumber) => {
|
|
119077
|
+
const exponentialDelay = baseDelay * Math.pow(2, attemptNumber - 1);
|
|
119078
|
+
const jitter = Math.random() * 0.3 * exponentialDelay;
|
|
119079
|
+
return Math.min(exponentialDelay + jitter, maxDelay);
|
|
119080
|
+
}
|
|
119081
|
+
};
|
|
119082
|
+
}
|
|
119083
|
+
}
|
|
118743
119084
|
// src/utils/internal/performance.ts
|
|
118744
119085
|
var import_api4 = __toESM(require_src(), 1);
|
|
118745
119086
|
|
|
@@ -125638,52 +125979,196 @@ class SpeechService2 {
|
|
|
125638
125979
|
}
|
|
125639
125980
|
// src/storage/core/StorageService.ts
|
|
125640
125981
|
var import_tsyringe5 = __toESM(require_cjs3(), 1);
|
|
125641
|
-
|
|
125642
|
-
|
|
125643
|
-
|
|
125644
|
-
|
|
125645
|
-
|
|
125646
|
-
|
|
125647
|
-
|
|
125982
|
+
|
|
125983
|
+
// src/utils/internal/encoding.ts
|
|
125984
|
+
function arrayBufferToBase64(buffer) {
|
|
125985
|
+
if (runtimeCaps.hasBuffer) {
|
|
125986
|
+
return Buffer.from(buffer).toString("base64");
|
|
125987
|
+
} else {
|
|
125988
|
+
let binary = "";
|
|
125989
|
+
const bytes = new Uint8Array(buffer);
|
|
125990
|
+
const len = bytes.byteLength;
|
|
125991
|
+
for (let i = 0;i < len; i++) {
|
|
125992
|
+
binary += String.fromCharCode(bytes[i]);
|
|
125993
|
+
}
|
|
125994
|
+
return btoa(binary);
|
|
125648
125995
|
}
|
|
125649
|
-
|
|
125650
|
-
|
|
125651
|
-
|
|
125652
|
-
|
|
125653
|
-
|
|
125654
|
-
|
|
125996
|
+
}
|
|
125997
|
+
function stringToBase64(str2) {
|
|
125998
|
+
if (runtimeCaps.hasBuffer) {
|
|
125999
|
+
return Buffer.from(str2, "utf-8").toString("base64");
|
|
126000
|
+
} else {
|
|
126001
|
+
const encoder = new TextEncoder;
|
|
126002
|
+
const bytes = encoder.encode(str2);
|
|
126003
|
+
return arrayBufferToBase64(bytes.buffer);
|
|
126004
|
+
}
|
|
126005
|
+
}
|
|
126006
|
+
function base64ToString(base642) {
|
|
126007
|
+
if (runtimeCaps.hasBuffer) {
|
|
126008
|
+
return Buffer.from(base642, "base64").toString("utf-8");
|
|
126009
|
+
} else {
|
|
126010
|
+
const decoded = atob(base642);
|
|
126011
|
+
const decoder = new TextDecoder;
|
|
126012
|
+
const bytes = new Uint8Array(decoded.split("").map((c) => c.charCodeAt(0)));
|
|
126013
|
+
return decoder.decode(bytes);
|
|
126014
|
+
}
|
|
126015
|
+
}
|
|
126016
|
+
|
|
126017
|
+
// src/storage/core/storageValidation.ts
|
|
126018
|
+
var MAX_TENANT_ID_LENGTH = 128;
|
|
126019
|
+
var MAX_KEY_LENGTH = 1024;
|
|
126020
|
+
var MAX_PREFIX_LENGTH = 512;
|
|
126021
|
+
var MAX_LIST_LIMIT = 1e4;
|
|
126022
|
+
var VALID_TENANT_ID_PATTERN = /^[a-zA-Z0-9]$|^[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9]$/;
|
|
126023
|
+
var VALID_KEY_PATTERN = /^[a-zA-Z0-9_.\-/]+$/;
|
|
126024
|
+
function validateTenantId(tenantId, context) {
|
|
126025
|
+
if (typeof tenantId !== "string") {
|
|
126026
|
+
throw new McpError(-32602 /* InvalidParams */, "Tenant ID must be a string.", { ...context, tenantId });
|
|
125655
126027
|
}
|
|
125656
126028
|
const trimmedTenantId = tenantId.trim();
|
|
125657
|
-
if (trimmedTenantId.length
|
|
125658
|
-
throw new McpError(-32602 /* InvalidParams */, "Tenant ID
|
|
125659
|
-
|
|
125660
|
-
|
|
125661
|
-
|
|
126029
|
+
if (trimmedTenantId.length === 0) {
|
|
126030
|
+
throw new McpError(-32602 /* InvalidParams */, "Tenant ID cannot be an empty string.", { ...context, tenantId });
|
|
126031
|
+
}
|
|
126032
|
+
if (trimmedTenantId.length > MAX_TENANT_ID_LENGTH) {
|
|
126033
|
+
throw new McpError(-32602 /* InvalidParams */, `Tenant ID exceeds maximum length of ${MAX_TENANT_ID_LENGTH} characters.`, { ...context, tenantIdLength: trimmedTenantId.length });
|
|
126034
|
+
}
|
|
126035
|
+
if (!VALID_TENANT_ID_PATTERN.test(trimmedTenantId)) {
|
|
126036
|
+
throw new McpError(-32602 /* InvalidParams */, "Tenant ID contains invalid characters. Only alphanumeric characters, hyphens, underscores, and dots are allowed. Must start and end with alphanumeric characters.", { ...context, tenantId: trimmedTenantId });
|
|
126037
|
+
}
|
|
126038
|
+
if (trimmedTenantId.includes("..")) {
|
|
126039
|
+
throw new McpError(-32602 /* InvalidParams */, "Tenant ID contains consecutive dots, which are not allowed.", { ...context, tenantId: trimmedTenantId });
|
|
126040
|
+
}
|
|
126041
|
+
if (trimmedTenantId.includes("../") || trimmedTenantId.includes("..\\")) {
|
|
126042
|
+
throw new McpError(-32602 /* InvalidParams */, "Tenant ID contains path traversal sequences, which are not allowed.", { ...context, tenantId: trimmedTenantId });
|
|
126043
|
+
}
|
|
126044
|
+
}
|
|
126045
|
+
function validateKey(key, context) {
|
|
126046
|
+
if (!key || typeof key !== "string") {
|
|
126047
|
+
throw new McpError(-32007 /* ValidationError */, "Key must be a non-empty string.", { ...context, key });
|
|
126048
|
+
}
|
|
126049
|
+
if (key.length > MAX_KEY_LENGTH) {
|
|
126050
|
+
throw new McpError(-32007 /* ValidationError */, `Key exceeds maximum length of ${MAX_KEY_LENGTH} characters.`, { ...context, key: key.substring(0, 50) + "..." });
|
|
126051
|
+
}
|
|
126052
|
+
if (!VALID_KEY_PATTERN.test(key)) {
|
|
126053
|
+
throw new McpError(-32007 /* ValidationError */, "Key contains invalid characters. Only alphanumeric, hyphens, underscores, dots, and slashes are allowed.", { ...context, key });
|
|
126054
|
+
}
|
|
126055
|
+
if (key.includes("..")) {
|
|
126056
|
+
throw new McpError(-32007 /* ValidationError */, 'Key must not contain ".." (path traversal attempt).', { ...context, key });
|
|
126057
|
+
}
|
|
126058
|
+
}
|
|
126059
|
+
function validatePrefix(prefix, context) {
|
|
126060
|
+
if (typeof prefix !== "string") {
|
|
126061
|
+
throw new McpError(-32007 /* ValidationError */, "Prefix must be a string.", { ...context, operation: "validatePrefix", prefix });
|
|
126062
|
+
}
|
|
126063
|
+
if (prefix === "") {
|
|
126064
|
+
return;
|
|
126065
|
+
}
|
|
126066
|
+
if (prefix.length > MAX_PREFIX_LENGTH) {
|
|
126067
|
+
throw new McpError(-32007 /* ValidationError */, `Prefix exceeds maximum length of ${MAX_PREFIX_LENGTH} characters.`, {
|
|
126068
|
+
...context,
|
|
126069
|
+
operation: "validatePrefix",
|
|
126070
|
+
prefix: prefix.substring(0, 50) + "..."
|
|
125662
126071
|
});
|
|
125663
126072
|
}
|
|
125664
|
-
|
|
125665
|
-
|
|
125666
|
-
|
|
125667
|
-
operation: "
|
|
125668
|
-
|
|
125669
|
-
tenantId: trimmedTenantId
|
|
126073
|
+
if (!VALID_KEY_PATTERN.test(prefix)) {
|
|
126074
|
+
throw new McpError(-32007 /* ValidationError */, "Prefix contains invalid characters. Only alphanumeric, hyphens, underscores, dots, and slashes are allowed.", {
|
|
126075
|
+
...context,
|
|
126076
|
+
operation: "validatePrefix",
|
|
126077
|
+
prefix: prefix.length > 50 ? prefix.substring(0, 50) + "..." : prefix
|
|
125670
126078
|
});
|
|
125671
126079
|
}
|
|
125672
|
-
if (
|
|
125673
|
-
throw new McpError(-
|
|
125674
|
-
|
|
125675
|
-
|
|
125676
|
-
|
|
126080
|
+
if (prefix.includes("..")) {
|
|
126081
|
+
throw new McpError(-32007 /* ValidationError */, 'Prefix must not contain ".." (path traversal attempt).', { ...context, operation: "validatePrefix", prefix });
|
|
126082
|
+
}
|
|
126083
|
+
}
|
|
126084
|
+
function validateStorageOptions(options, context) {
|
|
126085
|
+
if (!options) {
|
|
126086
|
+
return;
|
|
126087
|
+
}
|
|
126088
|
+
if (options.ttl !== undefined) {
|
|
126089
|
+
if (typeof options.ttl !== "number") {
|
|
126090
|
+
throw new McpError(-32007 /* ValidationError */, "TTL must be a number (seconds).", { ...context, ttl: options.ttl });
|
|
126091
|
+
}
|
|
126092
|
+
if (options.ttl < 0) {
|
|
126093
|
+
throw new McpError(-32007 /* ValidationError */, "TTL must be a non-negative number. Use 0 for immediate expiration.", { ...context, ttl: options.ttl });
|
|
126094
|
+
}
|
|
126095
|
+
if (!Number.isFinite(options.ttl)) {
|
|
126096
|
+
throw new McpError(-32007 /* ValidationError */, "TTL must be a finite number.", { ...context, ttl: options.ttl });
|
|
126097
|
+
}
|
|
126098
|
+
}
|
|
126099
|
+
}
|
|
126100
|
+
function validateListOptions(options, context) {
|
|
126101
|
+
if (!options) {
|
|
126102
|
+
return;
|
|
126103
|
+
}
|
|
126104
|
+
if (options.limit !== undefined) {
|
|
126105
|
+
if (typeof options.limit !== "number") {
|
|
126106
|
+
throw new McpError(-32007 /* ValidationError */, "List limit must be a number.", { ...context, operation: "validateListOptions", limit: options.limit });
|
|
126107
|
+
}
|
|
126108
|
+
if (!Number.isInteger(options.limit)) {
|
|
126109
|
+
throw new McpError(-32007 /* ValidationError */, "List limit must be an integer.", { ...context, operation: "validateListOptions", limit: options.limit });
|
|
126110
|
+
}
|
|
126111
|
+
if (options.limit < 1) {
|
|
126112
|
+
throw new McpError(-32007 /* ValidationError */, "List limit must be at least 1.", { ...context, operation: "validateListOptions", limit: options.limit });
|
|
126113
|
+
}
|
|
126114
|
+
if (options.limit > MAX_LIST_LIMIT) {
|
|
126115
|
+
throw new McpError(-32007 /* ValidationError */, `List limit exceeds maximum of ${MAX_LIST_LIMIT}.`, { ...context, operation: "validateListOptions", limit: options.limit });
|
|
126116
|
+
}
|
|
126117
|
+
if (!Number.isFinite(options.limit)) {
|
|
126118
|
+
throw new McpError(-32007 /* ValidationError */, "List limit must be a finite number.", { ...context, operation: "validateListOptions", limit: options.limit });
|
|
126119
|
+
}
|
|
126120
|
+
}
|
|
126121
|
+
if (options.cursor !== undefined) {
|
|
126122
|
+
if (typeof options.cursor !== "string") {
|
|
126123
|
+
throw new McpError(-32007 /* ValidationError */, "Cursor must be a string.", { ...context, operation: "validateListOptions" });
|
|
126124
|
+
}
|
|
126125
|
+
if (options.cursor.trim() === "") {
|
|
126126
|
+
throw new McpError(-32007 /* ValidationError */, "Cursor must not be an empty string.", { ...context, operation: "validateListOptions" });
|
|
126127
|
+
}
|
|
126128
|
+
if (!/^[A-Za-z0-9+/=]+$/.test(options.cursor)) {
|
|
126129
|
+
throw new McpError(-32007 /* ValidationError */, "Cursor contains invalid characters for base64.", { ...context, operation: "validateListOptions" });
|
|
126130
|
+
}
|
|
126131
|
+
}
|
|
126132
|
+
}
|
|
126133
|
+
function encodeCursor(lastKey, tenantId) {
|
|
126134
|
+
const data = { k: lastKey, t: tenantId };
|
|
126135
|
+
return stringToBase64(JSON.stringify(data));
|
|
126136
|
+
}
|
|
126137
|
+
function decodeCursor(cursor, tenantId, context) {
|
|
126138
|
+
try {
|
|
126139
|
+
const decoded = base64ToString(cursor);
|
|
126140
|
+
const data = JSON.parse(decoded);
|
|
126141
|
+
if (!data || typeof data !== "object" || !("k" in data) || !("t" in data)) {
|
|
126142
|
+
throw new McpError(-32602 /* InvalidParams */, "Invalid cursor format.", { ...context, operation: "decodeCursor" });
|
|
126143
|
+
}
|
|
126144
|
+
if (data.t !== tenantId) {
|
|
126145
|
+
throw new McpError(-32602 /* InvalidParams */, "Cursor tenant ID mismatch. Cursor may have been tampered with.", { ...context, operation: "decodeCursor" });
|
|
126146
|
+
}
|
|
126147
|
+
return data.k;
|
|
126148
|
+
} catch (error2) {
|
|
126149
|
+
if (error2 instanceof McpError) {
|
|
126150
|
+
throw error2;
|
|
126151
|
+
}
|
|
126152
|
+
throw new McpError(-32602 /* InvalidParams */, "Failed to decode cursor. Cursor may be corrupted or invalid.", {
|
|
126153
|
+
...context,
|
|
126154
|
+
operation: "decodeCursor",
|
|
126155
|
+
rawError: error2 instanceof Error ? error2.stack : String(error2)
|
|
125677
126156
|
});
|
|
125678
126157
|
}
|
|
125679
|
-
|
|
125680
|
-
|
|
125681
|
-
|
|
126158
|
+
}
|
|
126159
|
+
|
|
126160
|
+
// src/storage/core/StorageService.ts
|
|
126161
|
+
function requireTenantId(context) {
|
|
126162
|
+
const tenantId = context.tenantId;
|
|
126163
|
+
if (tenantId === undefined || tenantId === null) {
|
|
126164
|
+
throw new McpError(-32603 /* InternalError */, "Tenant ID is required for storage operations but was not found in the request context.", {
|
|
126165
|
+
operation: context.operation || "StorageService.requireTenantId",
|
|
125682
126166
|
requestId: context.requestId,
|
|
125683
|
-
|
|
126167
|
+
calledFrom: "StorageService"
|
|
125684
126168
|
});
|
|
125685
126169
|
}
|
|
125686
|
-
|
|
126170
|
+
validateTenantId(tenantId, context);
|
|
126171
|
+
return tenantId.trim();
|
|
125687
126172
|
}
|
|
125688
126173
|
|
|
125689
126174
|
class StorageService2 {
|
|
@@ -125693,34 +126178,103 @@ class StorageService2 {
|
|
|
125693
126178
|
}
|
|
125694
126179
|
get(key, context) {
|
|
125695
126180
|
const tenantId = requireTenantId(context);
|
|
126181
|
+
validateKey(key, context);
|
|
126182
|
+
logger.debug("[StorageService] get operation", {
|
|
126183
|
+
...context,
|
|
126184
|
+
operation: "StorageService.get",
|
|
126185
|
+
tenantId,
|
|
126186
|
+
key
|
|
126187
|
+
});
|
|
125696
126188
|
return this.provider.get(tenantId, key, context);
|
|
125697
126189
|
}
|
|
125698
126190
|
set(key, value, context, options) {
|
|
125699
126191
|
const tenantId = requireTenantId(context);
|
|
126192
|
+
validateKey(key, context);
|
|
126193
|
+
validateStorageOptions(options, context);
|
|
126194
|
+
logger.debug("[StorageService] set operation", {
|
|
126195
|
+
...context,
|
|
126196
|
+
operation: "StorageService.set",
|
|
126197
|
+
tenantId,
|
|
126198
|
+
key,
|
|
126199
|
+
hasTTL: options?.ttl !== undefined,
|
|
126200
|
+
ttl: options?.ttl
|
|
126201
|
+
});
|
|
125700
126202
|
return this.provider.set(tenantId, key, value, context, options);
|
|
125701
126203
|
}
|
|
125702
126204
|
delete(key, context) {
|
|
125703
126205
|
const tenantId = requireTenantId(context);
|
|
126206
|
+
validateKey(key, context);
|
|
126207
|
+
logger.debug("[StorageService] delete operation", {
|
|
126208
|
+
...context,
|
|
126209
|
+
operation: "StorageService.delete",
|
|
126210
|
+
tenantId,
|
|
126211
|
+
key
|
|
126212
|
+
});
|
|
125704
126213
|
return this.provider.delete(tenantId, key, context);
|
|
125705
126214
|
}
|
|
125706
126215
|
list(prefix, context, options) {
|
|
125707
126216
|
const tenantId = requireTenantId(context);
|
|
126217
|
+
validatePrefix(prefix, context);
|
|
126218
|
+
validateListOptions(options, context);
|
|
126219
|
+
logger.debug("[StorageService] list operation", {
|
|
126220
|
+
...context,
|
|
126221
|
+
operation: "StorageService.list",
|
|
126222
|
+
tenantId,
|
|
126223
|
+
prefix,
|
|
126224
|
+
limit: options?.limit,
|
|
126225
|
+
hasCursor: !!options?.cursor
|
|
126226
|
+
});
|
|
125708
126227
|
return this.provider.list(tenantId, prefix, context, options);
|
|
125709
126228
|
}
|
|
125710
126229
|
getMany(keys, context) {
|
|
125711
126230
|
const tenantId = requireTenantId(context);
|
|
126231
|
+
for (const key of keys) {
|
|
126232
|
+
validateKey(key, context);
|
|
126233
|
+
}
|
|
126234
|
+
logger.debug("[StorageService] getMany operation", {
|
|
126235
|
+
...context,
|
|
126236
|
+
operation: "StorageService.getMany",
|
|
126237
|
+
tenantId,
|
|
126238
|
+
keyCount: keys.length
|
|
126239
|
+
});
|
|
125712
126240
|
return this.provider.getMany(tenantId, keys, context);
|
|
125713
126241
|
}
|
|
125714
126242
|
setMany(entries, context, options) {
|
|
125715
126243
|
const tenantId = requireTenantId(context);
|
|
126244
|
+
validateStorageOptions(options, context);
|
|
126245
|
+
for (const key of entries.keys()) {
|
|
126246
|
+
validateKey(key, context);
|
|
126247
|
+
}
|
|
126248
|
+
logger.debug("[StorageService] setMany operation", {
|
|
126249
|
+
...context,
|
|
126250
|
+
operation: "StorageService.setMany",
|
|
126251
|
+
tenantId,
|
|
126252
|
+
entryCount: entries.size,
|
|
126253
|
+
hasTTL: options?.ttl !== undefined,
|
|
126254
|
+
ttl: options?.ttl
|
|
126255
|
+
});
|
|
125716
126256
|
return this.provider.setMany(tenantId, entries, context, options);
|
|
125717
126257
|
}
|
|
125718
126258
|
deleteMany(keys, context) {
|
|
125719
126259
|
const tenantId = requireTenantId(context);
|
|
126260
|
+
for (const key of keys) {
|
|
126261
|
+
validateKey(key, context);
|
|
126262
|
+
}
|
|
126263
|
+
logger.debug("[StorageService] deleteMany operation", {
|
|
126264
|
+
...context,
|
|
126265
|
+
operation: "StorageService.deleteMany",
|
|
126266
|
+
tenantId,
|
|
126267
|
+
keyCount: keys.length
|
|
126268
|
+
});
|
|
125720
126269
|
return this.provider.deleteMany(tenantId, keys, context);
|
|
125721
126270
|
}
|
|
125722
126271
|
clear(context) {
|
|
125723
126272
|
const tenantId = requireTenantId(context);
|
|
126273
|
+
logger.info("[StorageService] clear operation", {
|
|
126274
|
+
...context,
|
|
126275
|
+
operation: "StorageService.clear",
|
|
126276
|
+
tenantId
|
|
126277
|
+
});
|
|
125724
126278
|
return this.provider.clear(tenantId, context);
|
|
125725
126279
|
}
|
|
125726
126280
|
}
|
|
@@ -125779,9 +126333,12 @@ class FileSystemProvider {
|
|
|
125779
126333
|
return filePath;
|
|
125780
126334
|
}
|
|
125781
126335
|
buildEnvelope(value, options) {
|
|
125782
|
-
const expiresAt = options?.ttl ? Date.now() + options.ttl * 1000 : undefined;
|
|
126336
|
+
const expiresAt = options?.ttl !== undefined ? Date.now() + options.ttl * 1000 : undefined;
|
|
125783
126337
|
return {
|
|
125784
|
-
__mcp: {
|
|
126338
|
+
__mcp: {
|
|
126339
|
+
v: FILE_ENVELOPE_VERSION,
|
|
126340
|
+
...expiresAt !== undefined ? { expiresAt } : {}
|
|
126341
|
+
},
|
|
125785
126342
|
value
|
|
125786
126343
|
};
|
|
125787
126344
|
}
|
|
@@ -125889,13 +126446,14 @@ class FileSystemProvider {
|
|
|
125889
126446
|
const limit2 = options?.limit ?? DEFAULT_LIST_LIMIT;
|
|
125890
126447
|
let startIndex = 0;
|
|
125891
126448
|
if (options?.cursor) {
|
|
125892
|
-
const
|
|
126449
|
+
const lastKey = decodeCursor(options.cursor, tenantId, context);
|
|
126450
|
+
const cursorIndex = validKeys.indexOf(lastKey);
|
|
125893
126451
|
if (cursorIndex !== -1) {
|
|
125894
126452
|
startIndex = cursorIndex + 1;
|
|
125895
126453
|
}
|
|
125896
126454
|
}
|
|
125897
126455
|
const paginatedKeys = validKeys.slice(startIndex, startIndex + limit2);
|
|
125898
|
-
const nextCursor = startIndex + limit2 < validKeys.length ? paginatedKeys[paginatedKeys.length - 1] : undefined;
|
|
126456
|
+
const nextCursor = startIndex + limit2 < validKeys.length && paginatedKeys.length > 0 ? encodeCursor(paginatedKeys[paginatedKeys.length - 1], tenantId) : undefined;
|
|
125899
126457
|
return {
|
|
125900
126458
|
keys: paginatedKeys,
|
|
125901
126459
|
nextCursor
|
|
@@ -125908,13 +126466,18 @@ class FileSystemProvider {
|
|
|
125908
126466
|
}
|
|
125909
126467
|
async getMany(tenantId, keys, context) {
|
|
125910
126468
|
return ErrorHandler.tryCatch(async () => {
|
|
126469
|
+
if (keys.length === 0) {
|
|
126470
|
+
return new Map;
|
|
126471
|
+
}
|
|
126472
|
+
const promises = keys.map((key) => this.get(tenantId, key, context));
|
|
126473
|
+
const values2 = await Promise.all(promises);
|
|
125911
126474
|
const results = new Map;
|
|
125912
|
-
|
|
125913
|
-
const value =
|
|
126475
|
+
keys.forEach((key, i) => {
|
|
126476
|
+
const value = values2[i];
|
|
125914
126477
|
if (value !== null) {
|
|
125915
126478
|
results.set(key, value);
|
|
125916
126479
|
}
|
|
125917
|
-
}
|
|
126480
|
+
});
|
|
125918
126481
|
return results;
|
|
125919
126482
|
}, {
|
|
125920
126483
|
operation: "FileSystemProvider.getMany",
|
|
@@ -125924,9 +126487,11 @@ class FileSystemProvider {
|
|
|
125924
126487
|
}
|
|
125925
126488
|
async setMany(tenantId, entries, context, options) {
|
|
125926
126489
|
return ErrorHandler.tryCatch(async () => {
|
|
125927
|
-
|
|
125928
|
-
|
|
126490
|
+
if (entries.size === 0) {
|
|
126491
|
+
return;
|
|
125929
126492
|
}
|
|
126493
|
+
const promises = Array.from(entries.entries()).map(([key, value]) => this.set(tenantId, key, value, context, options));
|
|
126494
|
+
await Promise.all(promises);
|
|
125930
126495
|
}, {
|
|
125931
126496
|
operation: "FileSystemProvider.setMany",
|
|
125932
126497
|
context,
|
|
@@ -125935,14 +126500,12 @@ class FileSystemProvider {
|
|
|
125935
126500
|
}
|
|
125936
126501
|
async deleteMany(tenantId, keys, context) {
|
|
125937
126502
|
return ErrorHandler.tryCatch(async () => {
|
|
125938
|
-
|
|
125939
|
-
|
|
125940
|
-
const deleted = await this.delete(tenantId, key, context);
|
|
125941
|
-
if (deleted) {
|
|
125942
|
-
deletedCount++;
|
|
125943
|
-
}
|
|
126503
|
+
if (keys.length === 0) {
|
|
126504
|
+
return 0;
|
|
125944
126505
|
}
|
|
125945
|
-
|
|
126506
|
+
const promises = keys.map((key) => this.delete(tenantId, key, context));
|
|
126507
|
+
const results = await Promise.all(promises);
|
|
126508
|
+
return results.filter((deleted) => deleted).length;
|
|
125946
126509
|
}, {
|
|
125947
126510
|
operation: "FileSystemProvider.deleteMany",
|
|
125948
126511
|
context,
|
|
@@ -125999,10 +126562,10 @@ class InMemoryProvider {
|
|
|
125999
126562
|
set(tenantId, key, value, context, options) {
|
|
126000
126563
|
logger.debug(`[InMemoryProvider] Setting key: ${key} for tenant: ${tenantId}`, context);
|
|
126001
126564
|
const tenantStore = this.getTenantStore(tenantId);
|
|
126002
|
-
const expiresAt = options?.ttl ? Date.now() + options.ttl * 1000 : undefined;
|
|
126565
|
+
const expiresAt = options?.ttl !== undefined ? Date.now() + options.ttl * 1000 : undefined;
|
|
126003
126566
|
tenantStore.set(key, {
|
|
126004
126567
|
value,
|
|
126005
|
-
...expiresAt && { expiresAt }
|
|
126568
|
+
...expiresAt !== undefined && { expiresAt }
|
|
126006
126569
|
});
|
|
126007
126570
|
return Promise.resolve();
|
|
126008
126571
|
}
|
|
@@ -126029,44 +126592,54 @@ class InMemoryProvider {
|
|
|
126029
126592
|
const limit2 = options?.limit ?? DEFAULT_LIST_LIMIT2;
|
|
126030
126593
|
let startIndex = 0;
|
|
126031
126594
|
if (options?.cursor) {
|
|
126032
|
-
const
|
|
126595
|
+
const lastKey = decodeCursor(options.cursor, tenantId, context);
|
|
126596
|
+
const cursorIndex = allKeys.indexOf(lastKey);
|
|
126033
126597
|
if (cursorIndex !== -1) {
|
|
126034
126598
|
startIndex = cursorIndex + 1;
|
|
126035
126599
|
}
|
|
126036
126600
|
}
|
|
126037
126601
|
const paginatedKeys = allKeys.slice(startIndex, startIndex + limit2);
|
|
126038
|
-
const nextCursor = startIndex + limit2 < allKeys.length ? paginatedKeys[paginatedKeys.length - 1] : undefined;
|
|
126602
|
+
const nextCursor = startIndex + limit2 < allKeys.length && paginatedKeys.length > 0 ? encodeCursor(paginatedKeys[paginatedKeys.length - 1], tenantId) : undefined;
|
|
126039
126603
|
return Promise.resolve({
|
|
126040
126604
|
keys: paginatedKeys,
|
|
126041
126605
|
nextCursor
|
|
126042
126606
|
});
|
|
126043
126607
|
}
|
|
126044
126608
|
async getMany(tenantId, keys, context) {
|
|
126609
|
+
if (keys.length === 0) {
|
|
126610
|
+
return new Map;
|
|
126611
|
+
}
|
|
126045
126612
|
logger.debug(`[InMemoryProvider] Getting ${keys.length} keys for tenant: ${tenantId}`, context);
|
|
126613
|
+
const promises = keys.map((key) => this.get(tenantId, key, context));
|
|
126614
|
+
const values2 = await Promise.all(promises);
|
|
126046
126615
|
const results = new Map;
|
|
126047
|
-
|
|
126048
|
-
const value =
|
|
126616
|
+
keys.forEach((key, i) => {
|
|
126617
|
+
const value = values2[i];
|
|
126049
126618
|
if (value !== null) {
|
|
126050
126619
|
results.set(key, value);
|
|
126051
126620
|
}
|
|
126052
|
-
}
|
|
126621
|
+
});
|
|
126622
|
+
logger.debug(`[InMemoryProvider] Retrieved ${results.size}/${keys.length} keys for tenant: ${tenantId}`, context);
|
|
126053
126623
|
return results;
|
|
126054
126624
|
}
|
|
126055
126625
|
async setMany(tenantId, entries, context, options) {
|
|
126056
|
-
|
|
126057
|
-
|
|
126058
|
-
await this.set(tenantId, key, value, context, options);
|
|
126626
|
+
if (entries.size === 0) {
|
|
126627
|
+
return;
|
|
126059
126628
|
}
|
|
126629
|
+
logger.debug(`[InMemoryProvider] Setting ${entries.size} keys for tenant: ${tenantId}`, context);
|
|
126630
|
+
const promises = Array.from(entries.entries()).map(([key, value]) => this.set(tenantId, key, value, context, options));
|
|
126631
|
+
await Promise.all(promises);
|
|
126632
|
+
logger.debug(`[InMemoryProvider] Successfully set ${entries.size} keys for tenant: ${tenantId}`, context);
|
|
126060
126633
|
}
|
|
126061
126634
|
async deleteMany(tenantId, keys, context) {
|
|
126062
|
-
|
|
126063
|
-
|
|
126064
|
-
for (const key of keys) {
|
|
126065
|
-
const deleted = await this.delete(tenantId, key, context);
|
|
126066
|
-
if (deleted) {
|
|
126067
|
-
deletedCount++;
|
|
126068
|
-
}
|
|
126635
|
+
if (keys.length === 0) {
|
|
126636
|
+
return 0;
|
|
126069
126637
|
}
|
|
126638
|
+
logger.debug(`[InMemoryProvider] Deleting ${keys.length} keys for tenant: ${tenantId}`, context);
|
|
126639
|
+
const promises = keys.map((key) => this.delete(tenantId, key, context));
|
|
126640
|
+
const results = await Promise.all(promises);
|
|
126641
|
+
const deletedCount = results.filter((deleted) => deleted).length;
|
|
126642
|
+
logger.debug(`[InMemoryProvider] Deleted ${deletedCount}/${keys.length} keys for tenant: ${tenantId}`, context);
|
|
126070
126643
|
return deletedCount;
|
|
126071
126644
|
}
|
|
126072
126645
|
clear(tenantId, context) {
|
|
@@ -126116,7 +126689,7 @@ class SupabaseProvider {
|
|
|
126116
126689
|
}
|
|
126117
126690
|
async set(tenantId, key, value, context, options) {
|
|
126118
126691
|
return ErrorHandler.tryCatch(async () => {
|
|
126119
|
-
const expires_at = options?.ttl ? new Date(Date.now() + options.ttl * 1000).toISOString() : null;
|
|
126692
|
+
const expires_at = options?.ttl !== undefined ? new Date(Date.now() + options.ttl * 1000).toISOString() : null;
|
|
126120
126693
|
const { error: error2 } = await this.getClient().from(TABLE_NAME).upsert({
|
|
126121
126694
|
tenant_id: tenantId,
|
|
126122
126695
|
key,
|
|
@@ -126149,7 +126722,8 @@ class SupabaseProvider {
|
|
|
126149
126722
|
const limit2 = options?.limit ?? DEFAULT_LIST_LIMIT3;
|
|
126150
126723
|
let query = this.getClient().from(TABLE_NAME).select("key").eq("tenant_id", tenantId).like("key", `${prefix}%`).or(`expires_at.is.null,expires_at.gt.${now}`).order("key", { ascending: true }).limit(limit2 + 1);
|
|
126151
126724
|
if (options?.cursor) {
|
|
126152
|
-
|
|
126725
|
+
const lastKey = decodeCursor(options.cursor, tenantId, context);
|
|
126726
|
+
query = query.gt("key", lastKey);
|
|
126153
126727
|
}
|
|
126154
126728
|
const { data, error: error2 } = await query;
|
|
126155
126729
|
if (error2)
|
|
@@ -126157,7 +126731,7 @@ class SupabaseProvider {
|
|
|
126157
126731
|
const keys = data?.map((item) => item.key) ?? [];
|
|
126158
126732
|
const hasMore = keys.length > limit2;
|
|
126159
126733
|
const resultKeys = hasMore ? keys.slice(0, limit2) : keys;
|
|
126160
|
-
const nextCursor = hasMore ? resultKeys[resultKeys.length - 1] : undefined;
|
|
126734
|
+
const nextCursor = hasMore && resultKeys.length > 0 ? encodeCursor(resultKeys[resultKeys.length - 1], tenantId) : undefined;
|
|
126161
126735
|
return {
|
|
126162
126736
|
keys: resultKeys,
|
|
126163
126737
|
nextCursor
|
|
@@ -126196,7 +126770,7 @@ class SupabaseProvider {
|
|
|
126196
126770
|
if (entries.size === 0) {
|
|
126197
126771
|
return;
|
|
126198
126772
|
}
|
|
126199
|
-
const expires_at = options?.ttl ? new Date(Date.now() + options.ttl * 1000).toISOString() : null;
|
|
126773
|
+
const expires_at = options?.ttl !== undefined ? new Date(Date.now() + options.ttl * 1000).toISOString() : null;
|
|
126200
126774
|
const rows = Array.from(entries.entries()).map(([key, value]) => ({
|
|
126201
126775
|
tenant_id: tenantId,
|
|
126202
126776
|
key,
|
|
@@ -126265,9 +126839,12 @@ class R2Provider {
|
|
|
126265
126839
|
return `${tenantId}:${key}`;
|
|
126266
126840
|
}
|
|
126267
126841
|
buildEnvelope(value, options) {
|
|
126268
|
-
const expiresAt = options?.ttl ? Date.now() + options.ttl * 1000 : undefined;
|
|
126842
|
+
const expiresAt = options?.ttl !== undefined ? Date.now() + options.ttl * 1000 : undefined;
|
|
126269
126843
|
return {
|
|
126270
|
-
__mcp: {
|
|
126844
|
+
__mcp: {
|
|
126845
|
+
v: R2_ENVELOPE_VERSION,
|
|
126846
|
+
...expiresAt !== undefined ? { expiresAt } : {}
|
|
126847
|
+
},
|
|
126271
126848
|
value
|
|
126272
126849
|
};
|
|
126273
126850
|
}
|
|
@@ -126377,6 +126954,9 @@ class R2Provider {
|
|
|
126377
126954
|
}
|
|
126378
126955
|
async getMany(tenantId, keys, context) {
|
|
126379
126956
|
return ErrorHandler.tryCatch(async () => {
|
|
126957
|
+
if (keys.length === 0) {
|
|
126958
|
+
return new Map;
|
|
126959
|
+
}
|
|
126380
126960
|
const results = new Map;
|
|
126381
126961
|
for (const key of keys) {
|
|
126382
126962
|
const value = await this.get(tenantId, key, context);
|
|
@@ -126393,6 +126973,9 @@ class R2Provider {
|
|
|
126393
126973
|
}
|
|
126394
126974
|
async setMany(tenantId, entries, context, options) {
|
|
126395
126975
|
return ErrorHandler.tryCatch(async () => {
|
|
126976
|
+
if (entries.size === 0) {
|
|
126977
|
+
return;
|
|
126978
|
+
}
|
|
126396
126979
|
const promises = Array.from(entries.entries()).map(([key, value]) => this.set(tenantId, key, value, context, options));
|
|
126397
126980
|
await Promise.all(promises);
|
|
126398
126981
|
}, {
|
|
@@ -126403,6 +126986,9 @@ class R2Provider {
|
|
|
126403
126986
|
}
|
|
126404
126987
|
async deleteMany(tenantId, keys, context) {
|
|
126405
126988
|
return ErrorHandler.tryCatch(async () => {
|
|
126989
|
+
if (keys.length === 0) {
|
|
126990
|
+
return 0;
|
|
126991
|
+
}
|
|
126406
126992
|
const promises = keys.map((key) => this.delete(tenantId, key, context));
|
|
126407
126993
|
const results = await Promise.all(promises);
|
|
126408
126994
|
return results.filter((deleted) => deleted).length;
|
|
@@ -126482,7 +127068,7 @@ class KvProvider {
|
|
|
126482
127068
|
});
|
|
126483
127069
|
const valueToStore = JSON.stringify(value);
|
|
126484
127070
|
const putOptions = {};
|
|
126485
|
-
if (options?.ttl) {
|
|
127071
|
+
if (options?.ttl !== undefined) {
|
|
126486
127072
|
putOptions.expirationTtl = options.ttl;
|
|
126487
127073
|
}
|
|
126488
127074
|
await this.kv.put(kvKey, valueToStore, putOptions);
|
|
@@ -126543,6 +127129,9 @@ class KvProvider {
|
|
|
126543
127129
|
}
|
|
126544
127130
|
async getMany(tenantId, keys, context) {
|
|
126545
127131
|
return ErrorHandler.tryCatch(async () => {
|
|
127132
|
+
if (keys.length === 0) {
|
|
127133
|
+
return new Map;
|
|
127134
|
+
}
|
|
126546
127135
|
const results = new Map;
|
|
126547
127136
|
for (const key of keys) {
|
|
126548
127137
|
const value = await this.get(tenantId, key, context);
|
|
@@ -126559,6 +127148,9 @@ class KvProvider {
|
|
|
126559
127148
|
}
|
|
126560
127149
|
async setMany(tenantId, entries, context, options) {
|
|
126561
127150
|
return ErrorHandler.tryCatch(async () => {
|
|
127151
|
+
if (entries.size === 0) {
|
|
127152
|
+
return;
|
|
127153
|
+
}
|
|
126562
127154
|
const promises = Array.from(entries.entries()).map(([key, value]) => this.set(tenantId, key, value, context, options));
|
|
126563
127155
|
await Promise.all(promises);
|
|
126564
127156
|
}, {
|
|
@@ -126569,6 +127161,9 @@ class KvProvider {
|
|
|
126569
127161
|
}
|
|
126570
127162
|
async deleteMany(tenantId, keys, context) {
|
|
126571
127163
|
return ErrorHandler.tryCatch(async () => {
|
|
127164
|
+
if (keys.length === 0) {
|
|
127165
|
+
return 0;
|
|
127166
|
+
}
|
|
126572
127167
|
const promises = keys.map((key) => this.delete(tenantId, key, context));
|
|
126573
127168
|
const results = await Promise.all(promises);
|
|
126574
127169
|
return results.filter((deleted) => deleted).length;
|
|
@@ -130518,21 +131113,6 @@ var echoTool = {
|
|
|
130518
131113
|
responseFormatter: responseFormatter3
|
|
130519
131114
|
};
|
|
130520
131115
|
|
|
130521
|
-
// src/utils/internal/encoding.ts
|
|
130522
|
-
function arrayBufferToBase64(buffer) {
|
|
130523
|
-
if (runtimeCaps.hasBuffer) {
|
|
130524
|
-
return Buffer.from(buffer).toString("base64");
|
|
130525
|
-
} else {
|
|
130526
|
-
let binary = "";
|
|
130527
|
-
const bytes = new Uint8Array(buffer);
|
|
130528
|
-
const len = bytes.byteLength;
|
|
130529
|
-
for (let i = 0;i < len; i++) {
|
|
130530
|
-
binary += String.fromCharCode(bytes[i]);
|
|
130531
|
-
}
|
|
130532
|
-
return btoa(binary);
|
|
130533
|
-
}
|
|
130534
|
-
}
|
|
130535
|
-
|
|
130536
131116
|
// src/mcp-server/tools/definitions/template-image-test.tool.ts
|
|
130537
131117
|
var TOOL_NAME4 = "template_image_test";
|
|
130538
131118
|
var TOOL_TITLE4 = "Template Image Test";
|
|
@@ -133589,7 +134169,6 @@ var cors = (options) => {
|
|
|
133589
134169
|
|
|
133590
134170
|
// src/mcp-server/transports/http/httpTransport.ts
|
|
133591
134171
|
import http from "http";
|
|
133592
|
-
import { randomUUID } from "node:crypto";
|
|
133593
134172
|
// src/mcp-server/transports/auth/authFactory.ts
|
|
133594
134173
|
var import_tsyringe16 = __toESM(require_cjs3(), 1);
|
|
133595
134174
|
|
|
@@ -135373,6 +135952,7 @@ function createAuthStrategy() {
|
|
|
135373
135952
|
}
|
|
135374
135953
|
}
|
|
135375
135954
|
// src/mcp-server/transports/auth/authMiddleware.ts
|
|
135955
|
+
var import_api6 = __toESM(require_src(), 1);
|
|
135376
135956
|
function createAuthMiddleware(strategy) {
|
|
135377
135957
|
return async function authMiddleware(c, next) {
|
|
135378
135958
|
const context = requestContextService.createRequestContext({
|
|
@@ -135404,6 +135984,20 @@ function createAuthMiddleware(strategy) {
|
|
|
135404
135984
|
scopes: authInfo.scopes
|
|
135405
135985
|
};
|
|
135406
135986
|
logger.info("Authentication successful. Auth context populated.", authLogContext);
|
|
135987
|
+
const activeSpan = import_api6.trace.getActiveSpan();
|
|
135988
|
+
if (activeSpan) {
|
|
135989
|
+
activeSpan.setAttributes({
|
|
135990
|
+
"auth.client_id": authInfo.clientId,
|
|
135991
|
+
"auth.tenant_id": authInfo.tenantId ?? "none",
|
|
135992
|
+
"auth.scopes": authInfo.scopes.join(","),
|
|
135993
|
+
"auth.subject": authInfo.subject ?? "unknown",
|
|
135994
|
+
"auth.method": "bearer"
|
|
135995
|
+
});
|
|
135996
|
+
logger.debug("Added auth context to OpenTelemetry span", {
|
|
135997
|
+
...authLogContext,
|
|
135998
|
+
spanId: activeSpan.spanContext().spanId
|
|
135999
|
+
});
|
|
136000
|
+
}
|
|
135407
136001
|
await authContext.run({ authInfo }, next);
|
|
135408
136002
|
} catch (error2) {
|
|
135409
136003
|
logger.warning("Authentication verification failed.", {
|
|
@@ -135516,6 +136110,22 @@ var httpErrorHandler = async (err, c) => {
|
|
|
135516
136110
|
return c.json(errorResponse);
|
|
135517
136111
|
};
|
|
135518
136112
|
|
|
136113
|
+
// src/mcp-server/transports/http/sessionIdUtils.ts
|
|
136114
|
+
import { randomBytes as randomBytes2 } from "crypto";
|
|
136115
|
+
function generateSecureSessionId() {
|
|
136116
|
+
if (runtimeCaps.isNode && runtimeCaps.hasBuffer) {
|
|
136117
|
+
const bytes = randomBytes2(32);
|
|
136118
|
+
return bytes.toString("hex");
|
|
136119
|
+
} else {
|
|
136120
|
+
const bytes = new Uint8Array(32);
|
|
136121
|
+
crypto.getRandomValues(bytes);
|
|
136122
|
+
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
136123
|
+
}
|
|
136124
|
+
}
|
|
136125
|
+
function validateSessionIdFormat(sessionId) {
|
|
136126
|
+
return /^[a-f0-9]{64}$/.test(sessionId);
|
|
136127
|
+
}
|
|
136128
|
+
|
|
135519
136129
|
// src/mcp-server/transports/http/sessionStore.ts
|
|
135520
136130
|
class SessionStore {
|
|
135521
136131
|
sessions = new Map;
|
|
@@ -135525,6 +136135,14 @@ class SessionStore {
|
|
|
135525
136135
|
setInterval(() => this.cleanupStaleSessions(), 60000);
|
|
135526
136136
|
}
|
|
135527
136137
|
getOrCreate(sessionId, identity) {
|
|
136138
|
+
if (!validateSessionIdFormat(sessionId)) {
|
|
136139
|
+
const context = requestContextService.createRequestContext({
|
|
136140
|
+
operation: "SessionStore.getOrCreate",
|
|
136141
|
+
sessionIdPrefix: sessionId.substring(0, 16)
|
|
136142
|
+
});
|
|
136143
|
+
logger.warning("Invalid session ID format rejected", context);
|
|
136144
|
+
throw new McpError(-32602 /* InvalidParams */, "Invalid session ID format. Session IDs must be 64 hexadecimal characters.", context);
|
|
136145
|
+
}
|
|
135528
136146
|
let session = this.sessions.get(sessionId);
|
|
135529
136147
|
if (!session) {
|
|
135530
136148
|
const newSession = {
|
|
@@ -135697,15 +136315,26 @@ function createHttpApp(mcpServer, parentContext) {
|
|
|
135697
136315
|
app.get("/healthz", (c) => c.json({ status: "ok" }));
|
|
135698
136316
|
app.get("/.well-known/oauth-protected-resource", (c) => {
|
|
135699
136317
|
if (!config.oauthIssuerUrl) {
|
|
136318
|
+
logger.debug("OAuth Protected Resource Metadata requested but OAuth not configured", transportContext);
|
|
135700
136319
|
return c.json({ error: "OAuth not configured on this server" }, { status: 404 });
|
|
135701
136320
|
}
|
|
135702
|
-
|
|
135703
|
-
|
|
136321
|
+
const origin = new URL(c.req.url).origin;
|
|
136322
|
+
const resourceIdentifier = config.mcpServerResourceIdentifier ?? config.oauthAudience ?? `${origin}/mcp`;
|
|
136323
|
+
const metadata = {
|
|
136324
|
+
resource: resourceIdentifier,
|
|
135704
136325
|
authorization_servers: [config.oauthIssuerUrl],
|
|
135705
136326
|
bearer_methods_supported: ["header"],
|
|
135706
136327
|
resource_signing_alg_values_supported: ["RS256", "ES256", "PS256"],
|
|
136328
|
+
resource_documentation: `${origin}/docs`,
|
|
135707
136329
|
...config.oauthJwksUri && { jwks_uri: config.oauthJwksUri }
|
|
136330
|
+
};
|
|
136331
|
+
c.header("Cache-Control", "public, max-age=3600");
|
|
136332
|
+
c.header("Content-Type", "application/json");
|
|
136333
|
+
logger.debug("Serving OAuth Protected Resource Metadata", {
|
|
136334
|
+
...transportContext,
|
|
136335
|
+
resourceIdentifier
|
|
135708
136336
|
});
|
|
136337
|
+
return c.json(metadata);
|
|
135709
136338
|
});
|
|
135710
136339
|
app.get(config.mcpHttpEndpointPath, (c) => {
|
|
135711
136340
|
return c.json({
|
|
@@ -135770,7 +136399,7 @@ function createHttpApp(mcpServer, parentContext) {
|
|
|
135770
136399
|
}, 400);
|
|
135771
136400
|
}
|
|
135772
136401
|
const providedSessionId = c.req.header("mcp-session-id");
|
|
135773
|
-
const sessionId = providedSessionId ??
|
|
136402
|
+
const sessionId = providedSessionId ?? generateSecureSessionId();
|
|
135774
136403
|
const authStore = authContext.getStore();
|
|
135775
136404
|
let sessionIdentity;
|
|
135776
136405
|
if (authStore?.authInfo) {
|
|
@@ -136173,6 +136802,11 @@ var start = async () => {
|
|
|
136173
136802
|
await shutdownOpenTelemetry();
|
|
136174
136803
|
process.exit(1);
|
|
136175
136804
|
}
|
|
136805
|
+
try {
|
|
136806
|
+
await initializeOpenTelemetry();
|
|
136807
|
+
} catch (error2) {
|
|
136808
|
+
console.error("[Startup] Failed to initialize OpenTelemetry:", error2);
|
|
136809
|
+
}
|
|
136176
136810
|
await initializePerformance_Hrt();
|
|
136177
136811
|
const validMcpLogLevels = [
|
|
136178
136812
|
"debug",
|