mcp-ts-template 2.1.7 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +58 -49
  2. package/dist/index.js +336 -124
  3. package/package.json +5 -3
  4. package/dist/worker.js +0 -60707
package/dist/index.js CHANGED
@@ -117229,7 +117229,7 @@ var z = /* @__PURE__ */ Object.freeze({
117229
117229
  // package.json
117230
117230
  var package_default = {
117231
117231
  name: "mcp-ts-template",
117232
- version: "2.1.6",
117232
+ version: "2.1.8",
117233
117233
  mcpName: "io.github.cyanheads/mcp-ts-template",
117234
117234
  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.",
117235
117235
  main: "dist/index.js",
@@ -117284,8 +117284,8 @@ var package_default = {
117284
117284
  prepare: "[ -d .husky ] && husky || true",
117285
117285
  inspector: "bunx mcp-inspector --config mcp.json --server mcp-ts-template",
117286
117286
  "db:duckdb-example": "MCP_LOG_LEVEL=debug tsc && node dist/storage/duckdbExample.js",
117287
- test: "bun test",
117288
- "test:coverage": "bun test --coverage",
117287
+ test: "bun test --config vitest.config.ts",
117288
+ "test:coverage": "bun test --coverage --config vitest.config.ts",
117289
117289
  audit: "bun audit",
117290
117290
  "audit:fix": "bun audit --fix",
117291
117291
  "publish-mcp": "bun scripts/validate-mcp-publish-schema.ts"
@@ -117307,7 +117307,7 @@ var package_default = {
117307
117307
  "chrono-node": "^2.9.0",
117308
117308
  dotenv: "^17.2.2",
117309
117309
  "fast-xml-parser": "^5.2.5",
117310
- hono: "^4.9.8",
117310
+ hono: "^4.9.9",
117311
117311
  ignore: "^7.0.5",
117312
117312
  jose: "^6.1.0",
117313
117313
  "js-yaml": "^4.1.0",
@@ -117319,6 +117319,7 @@ var package_default = {
117319
117319
  "reflect-metadata": "^0.2.2",
117320
117320
  repomix: "^1.6.0",
117321
117321
  "sanitize-html": "^2.17.0",
117322
+ "@apidevtools/swagger-parser": "^12.0.0",
117322
117323
  tslib: "^2.8.1",
117323
117324
  tsyringe: "^4.10.0",
117324
117325
  validator: "13.15.15",
@@ -117749,6 +117750,7 @@ var JsonRpcErrorCode;
117749
117750
  JsonRpcErrorCode2[JsonRpcErrorCode2["ConfigurationError"] = -32008] = "ConfigurationError";
117750
117751
  JsonRpcErrorCode2[JsonRpcErrorCode2["InitializationFailed"] = -32009] = "InitializationFailed";
117751
117752
  JsonRpcErrorCode2[JsonRpcErrorCode2["DatabaseError"] = -32010] = "DatabaseError";
117753
+ JsonRpcErrorCode2[JsonRpcErrorCode2["SerializationError"] = -32070] = "SerializationError";
117752
117754
  JsonRpcErrorCode2[JsonRpcErrorCode2["UnknownError"] = -32099] = "UnknownError";
117753
117755
  })(JsonRpcErrorCode ||= {});
117754
117756
 
@@ -124912,6 +124914,8 @@ var import_tsyringe5 = __toESM(require_cjs3(), 1);
124912
124914
  import { existsSync, mkdirSync } from "fs";
124913
124915
  import { readFile, readdir, rm, writeFile } from "fs/promises";
124914
124916
  import path2 from "path";
124917
+ var FILE_ENVELOPE_VERSION = 1;
124918
+
124915
124919
  class FileSystemProvider {
124916
124920
  storagePath;
124917
124921
  constructor(storagePath) {
@@ -124948,12 +124952,38 @@ class FileSystemProvider {
124948
124952
  }
124949
124953
  return filePath;
124950
124954
  }
124955
+ buildEnvelope(value, options) {
124956
+ const expiresAt = options?.ttl ? Date.now() + options.ttl * 1000 : undefined;
124957
+ return {
124958
+ __mcp: { v: FILE_ENVELOPE_VERSION, ...expiresAt ? { expiresAt } : {} },
124959
+ value
124960
+ };
124961
+ }
124962
+ async parseAndValidate(raw, tenantId, key, filePath, context) {
124963
+ try {
124964
+ const parsed = JSON.parse(raw);
124965
+ if (parsed && typeof parsed === "object" && "__mcp" in parsed) {
124966
+ const env2 = parsed;
124967
+ const expiresAt = env2.__mcp?.expiresAt;
124968
+ if (expiresAt && Date.now() > expiresAt) {
124969
+ try {
124970
+ await rm(filePath);
124971
+ } catch {}
124972
+ return null;
124973
+ }
124974
+ return env2.value;
124975
+ }
124976
+ return parsed;
124977
+ } catch (error2) {
124978
+ throw new McpError(-32070 /* SerializationError */, `Failed to parse stored JSON for key "${key}" (tenant "${tenantId}").`, { ...context, error: error2 });
124979
+ }
124980
+ }
124951
124981
  async get(tenantId, key, context) {
124952
124982
  const filePath = this.getFilePath(tenantId, key);
124953
124983
  return ErrorHandler.tryCatch(async () => {
124954
124984
  try {
124955
124985
  const data = await readFile(filePath, "utf-8");
124956
- return JSON.parse(data);
124986
+ return this.parseAndValidate(data, tenantId, key, filePath, context);
124957
124987
  } catch (error2) {
124958
124988
  if (error2 instanceof Error && "code" in error2 && error2.code === "ENOENT") {
124959
124989
  return null;
@@ -124966,10 +124996,11 @@ class FileSystemProvider {
124966
124996
  input: { tenantId, key }
124967
124997
  });
124968
124998
  }
124969
- async set(tenantId, key, value, context) {
124999
+ async set(tenantId, key, value, context, options) {
124970
125000
  const filePath = this.getFilePath(tenantId, key);
124971
125001
  return ErrorHandler.tryCatch(async () => {
124972
- const content = typeof value === "string" ? value : JSON.stringify(value, null, 2);
125002
+ const envelope = this.buildEnvelope(value, options);
125003
+ const content = JSON.stringify(envelope, null, 2);
124973
125004
  mkdirSync(path2.dirname(filePath), { recursive: true });
124974
125005
  await writeFile(filePath, content, "utf-8");
124975
125006
  }, {
@@ -124996,11 +125027,39 @@ class FileSystemProvider {
124996
125027
  input: { tenantId, key }
124997
125028
  });
124998
125029
  }
125030
+ async listFilesRecursively(dir, baseDir) {
125031
+ const entries = await readdir(dir, { withFileTypes: true });
125032
+ const results = [];
125033
+ for (const entry of entries) {
125034
+ const fullPath = path2.join(dir, entry.name);
125035
+ if (entry.isDirectory()) {
125036
+ results.push(...await this.listFilesRecursively(fullPath, baseDir));
125037
+ } else if (entry.isFile()) {
125038
+ const rel = path2.relative(baseDir, fullPath);
125039
+ results.push(rel.split(path2.sep).join("/"));
125040
+ }
125041
+ }
125042
+ return results;
125043
+ }
124999
125044
  async list(tenantId, prefix, context) {
125000
125045
  return ErrorHandler.tryCatch(async () => {
125001
125046
  const tenantPath = this.getTenantPath(tenantId);
125002
- const files = await readdir(tenantPath);
125003
- return files.filter((file) => file.startsWith(prefix));
125047
+ const allKeys = await this.listFilesRecursively(tenantPath, tenantPath);
125048
+ const candidateKeys = allKeys.filter((k) => k.startsWith(prefix));
125049
+ const validKeys = [];
125050
+ for (const k of candidateKeys) {
125051
+ const filePath = this.getFilePath(tenantId, k);
125052
+ try {
125053
+ const raw = await readFile(filePath, "utf-8");
125054
+ const value = await this.parseAndValidate(raw, tenantId, k, filePath, context);
125055
+ if (value !== null) {
125056
+ validKeys.push(k);
125057
+ }
125058
+ } catch (_e) {
125059
+ continue;
125060
+ }
125061
+ }
125062
+ return validKeys;
125004
125063
  }, {
125005
125064
  operation: "FileSystemProvider.list",
125006
125065
  context,
@@ -125153,6 +125212,8 @@ SupabaseProvider = __legacyDecorateClassTS([
125153
125212
  ], SupabaseProvider);
125154
125213
 
125155
125214
  // src/storage/providers/cloudflare/r2Provider.ts
125215
+ var R2_ENVELOPE_VERSION = 1;
125216
+
125156
125217
  class R2Provider {
125157
125218
  bucket;
125158
125219
  constructor(bucket) {
@@ -125161,58 +125222,99 @@ class R2Provider {
125161
125222
  getR2Key(tenantId, key) {
125162
125223
  return `${tenantId}:${key}`;
125163
125224
  }
125164
- async get(tenantId, key, context) {
125165
- const r2Key = this.getR2Key(tenantId, key);
125166
- logger.debug(`[R2Provider] Getting key: ${r2Key}`, context);
125167
- const object = await this.bucket.get(r2Key);
125168
- if (object === null) {
125169
- logger.debug(`[R2Provider] Key not found: ${r2Key}`, context);
125170
- return null;
125171
- }
125225
+ buildEnvelope(value, options) {
125226
+ const expiresAt = options?.ttl ? Date.now() + options.ttl * 1000 : undefined;
125227
+ return {
125228
+ __mcp: { v: R2_ENVELOPE_VERSION, ...expiresAt ? { expiresAt } : {} },
125229
+ value
125230
+ };
125231
+ }
125232
+ parseAndValidate(raw, tenantId, key, context) {
125172
125233
  try {
125173
- return await object.json();
125234
+ const parsed = JSON.parse(raw);
125235
+ if (parsed && typeof parsed === "object" && "__mcp" in parsed) {
125236
+ const env2 = parsed;
125237
+ const expiresAt = env2.__mcp?.expiresAt;
125238
+ if (expiresAt && Date.now() > expiresAt) {
125239
+ return null;
125240
+ }
125241
+ return env2.value;
125242
+ }
125243
+ return parsed;
125174
125244
  } catch (error2) {
125175
- logger.error(`[R2Provider] Failed to parse JSON for key: ${r2Key}`, {
125176
- ...context,
125177
- error: error2
125178
- });
125179
- return null;
125245
+ throw new McpError(-32070 /* SerializationError */, `[R2Provider] Failed to parse JSON for key: ${this.getR2Key(tenantId, key)}`, { ...context, error: error2 });
125180
125246
  }
125181
125247
  }
125248
+ async get(tenantId, key, context) {
125249
+ const r2Key = this.getR2Key(tenantId, key);
125250
+ return ErrorHandler.tryCatch(async () => {
125251
+ logger.debug(`[R2Provider] Getting key: ${r2Key}`, context);
125252
+ const object = await this.bucket.get(r2Key);
125253
+ if (object === null) {
125254
+ return null;
125255
+ }
125256
+ const text = await object.text();
125257
+ const value = this.parseAndValidate(text, tenantId, key, context);
125258
+ if (value === null) {
125259
+ await this.bucket.delete(r2Key).catch(() => {});
125260
+ logger.debug(`[R2Provider] Key expired and removed: ${r2Key}`, context);
125261
+ }
125262
+ return value;
125263
+ }, {
125264
+ operation: "R2Provider.get",
125265
+ context,
125266
+ input: { tenantId, key }
125267
+ });
125268
+ }
125182
125269
  async set(tenantId, key, value, context, options) {
125183
125270
  const r2Key = this.getR2Key(tenantId, key);
125184
- logger.debug(`[R2Provider] Setting key: ${r2Key}`, { ...context, options });
125185
- const valueToStore = JSON.stringify(value);
125186
- const putOptions = {};
125187
- if (options?.ttl) {
125188
- logger.warning(`[R2Provider] TTL is not natively supported by R2. The 'ttl' option for key '${r2Key}' will be ignored.`, context);
125189
- }
125190
- await this.bucket.put(r2Key, valueToStore, putOptions);
125191
- logger.debug(`[R2Provider] Successfully set key: ${r2Key}`, context);
125271
+ return ErrorHandler.tryCatch(async () => {
125272
+ logger.debug(`[R2Provider] Setting key: ${r2Key}`, {
125273
+ ...context,
125274
+ options
125275
+ });
125276
+ const envelope = this.buildEnvelope(value, options);
125277
+ const body = JSON.stringify(envelope);
125278
+ await this.bucket.put(r2Key, body);
125279
+ logger.debug(`[R2Provider] Successfully set key: ${r2Key}`, context);
125280
+ }, {
125281
+ operation: "R2Provider.set",
125282
+ context,
125283
+ input: { tenantId, key }
125284
+ });
125192
125285
  }
125193
125286
  async delete(tenantId, key, context) {
125194
125287
  const r2Key = this.getR2Key(tenantId, key);
125195
- logger.debug(`[R2Provider] Deleting key: ${r2Key}`, context);
125196
- const head = await this.bucket.head(r2Key);
125197
- if (head === null) {
125198
- logger.debug(`[R2Provider] Key to delete not found: ${r2Key}`, context);
125199
- return false;
125200
- }
125201
- await this.bucket.delete(r2Key);
125202
- logger.debug(`[R2Provider] Successfully deleted key: ${r2Key}`, context);
125203
- return true;
125288
+ return ErrorHandler.tryCatch(async () => {
125289
+ logger.debug(`[R2Provider] Deleting key: ${r2Key}`, context);
125290
+ const head = await this.bucket.head(r2Key);
125291
+ if (head === null) {
125292
+ logger.debug(`[R2Provider] Key to delete not found: ${r2Key}`, context);
125293
+ return false;
125294
+ }
125295
+ await this.bucket.delete(r2Key);
125296
+ logger.debug(`[R2Provider] Successfully deleted key: ${r2Key}`, context);
125297
+ return true;
125298
+ }, {
125299
+ operation: "R2Provider.delete",
125300
+ context,
125301
+ input: { tenantId, key }
125302
+ });
125204
125303
  }
125205
125304
  async list(tenantId, prefix, context) {
125206
125305
  const r2Prefix = this.getR2Key(tenantId, prefix);
125207
- logger.debug(`[R2Provider] Listing keys with prefix: ${r2Prefix}`, context);
125208
- const listOptions = {
125209
- prefix: r2Prefix
125210
- };
125211
- const listed = await this.bucket.list(listOptions);
125212
- const tenantPrefix = `${tenantId}:`;
125213
- const keys = listed.objects.map((obj) => obj.key.startsWith(tenantPrefix) ? obj.key.substring(tenantPrefix.length) : obj.key);
125214
- logger.debug(`[R2Provider] Found ${keys.length} keys with prefix: ${r2Prefix}`, context);
125215
- return keys;
125306
+ return ErrorHandler.tryCatch(async () => {
125307
+ logger.debug(`[R2Provider] Listing keys with prefix: ${r2Prefix}`, context);
125308
+ const listed = await this.bucket.list({ prefix: r2Prefix });
125309
+ const tenantPrefix = `${tenantId}:`;
125310
+ const keys = listed.objects.map((obj) => obj.key.startsWith(tenantPrefix) ? obj.key.substring(tenantPrefix.length) : obj.key);
125311
+ logger.debug(`[R2Provider] Found ${keys.length} keys with prefix: ${r2Prefix}`, context);
125312
+ return keys;
125313
+ }, {
125314
+ operation: "R2Provider.list",
125315
+ context,
125316
+ input: { tenantId, prefix }
125317
+ });
125216
125318
  }
125217
125319
  }
125218
125320
 
@@ -125227,59 +125329,78 @@ class KvProvider {
125227
125329
  }
125228
125330
  async get(tenantId, key, context) {
125229
125331
  const kvKey = this.getKvKey(tenantId, key);
125230
- logger.debug(`[KvProvider] Getting key: ${kvKey}`, context);
125231
- try {
125232
- const result = await this.kv.get(kvKey, "json");
125233
- if (result === null) {
125234
- logger.debug(`[KvProvider] Key not found: ${kvKey}`, context);
125235
- return null;
125332
+ return ErrorHandler.tryCatch(async () => {
125333
+ logger.debug(`[KvProvider] Getting key: ${kvKey}`, context);
125334
+ try {
125335
+ const result = await this.kv.get(kvKey, "json");
125336
+ return result;
125337
+ } catch (error2) {
125338
+ throw new McpError(-32070 /* SerializationError */, `[KvProvider] Failed to parse JSON for key: ${kvKey}`, { ...context, error: error2 });
125236
125339
  }
125237
- return result;
125238
- } catch (error2) {
125239
- logger.error(`[KvProvider] Failed to get or parse key: ${kvKey}`, {
125240
- ...context,
125241
- error: error2
125242
- });
125243
- return null;
125244
- }
125340
+ }, {
125341
+ operation: "KvProvider.get",
125342
+ context,
125343
+ input: { tenantId, key }
125344
+ });
125245
125345
  }
125246
125346
  async set(tenantId, key, value, context, options) {
125247
125347
  const kvKey = this.getKvKey(tenantId, key);
125248
- logger.debug(`[KvProvider] Setting key: ${kvKey}`, { ...context, options });
125249
- const valueToStore = JSON.stringify(value);
125250
- const putOptions = {};
125251
- if (options?.ttl) {
125252
- putOptions.expirationTtl = options.ttl;
125253
- }
125254
- await this.kv.put(kvKey, valueToStore, putOptions);
125255
- logger.debug(`[KvProvider] Successfully set key: ${kvKey}`, context);
125348
+ return ErrorHandler.tryCatch(async () => {
125349
+ logger.debug(`[KvProvider] Setting key: ${kvKey}`, {
125350
+ ...context,
125351
+ options
125352
+ });
125353
+ const valueToStore = JSON.stringify(value);
125354
+ const putOptions = {};
125355
+ if (options?.ttl) {
125356
+ putOptions.expirationTtl = options.ttl;
125357
+ }
125358
+ await this.kv.put(kvKey, valueToStore, putOptions);
125359
+ logger.debug(`[KvProvider] Successfully set key: ${kvKey}`, context);
125360
+ }, {
125361
+ operation: "KvProvider.set",
125362
+ context,
125363
+ input: { tenantId, key }
125364
+ });
125256
125365
  }
125257
125366
  async delete(tenantId, key, context) {
125258
125367
  const kvKey = this.getKvKey(tenantId, key);
125259
- logger.debug(`[KvProvider] Deleting key: ${kvKey}`, context);
125260
- const value = await this.kv.get(kvKey);
125261
- if (value === null) {
125262
- logger.debug(`[KvProvider] Key to delete not found: ${kvKey}`, context);
125263
- return false;
125264
- }
125265
- await this.kv.delete(kvKey);
125266
- logger.debug(`[KvProvider] Successfully deleted key: ${kvKey}`, context);
125267
- return true;
125368
+ return ErrorHandler.tryCatch(async () => {
125369
+ logger.debug(`[KvProvider] Deleting key: ${kvKey}`, context);
125370
+ const value = await this.kv.get(kvKey);
125371
+ if (value === null) {
125372
+ logger.debug(`[KvProvider] Key to delete not found: ${kvKey}`, context);
125373
+ return false;
125374
+ }
125375
+ await this.kv.delete(kvKey);
125376
+ logger.debug(`[KvProvider] Successfully deleted key: ${kvKey}`, context);
125377
+ return true;
125378
+ }, {
125379
+ operation: "KvProvider.delete",
125380
+ context,
125381
+ input: { tenantId, key }
125382
+ });
125268
125383
  }
125269
125384
  async list(tenantId, prefix, context) {
125270
125385
  const kvPrefix = this.getKvKey(tenantId, prefix);
125271
- logger.debug(`[KvProvider] Listing keys with prefix: ${kvPrefix}`, context);
125272
- const listed = await this.kv.list({ prefix: kvPrefix });
125273
- const tenantPrefix = `${tenantId}:`;
125274
- const keys = listed.keys.map((keyInfo) => keyInfo.name.startsWith(tenantPrefix) ? keyInfo.name.substring(tenantPrefix.length) : keyInfo.name);
125275
- logger.debug(`[KvProvider] Found ${keys.length} keys with prefix: ${kvPrefix}`, context);
125276
- return keys;
125386
+ return ErrorHandler.tryCatch(async () => {
125387
+ logger.debug(`[KvProvider] Listing keys with prefix: ${kvPrefix}`, context);
125388
+ const listed = await this.kv.list({ prefix: kvPrefix });
125389
+ const tenantPrefix = `${tenantId}:`;
125390
+ const keys = listed.keys.map((keyInfo) => keyInfo.name.startsWith(tenantPrefix) ? keyInfo.name.substring(tenantPrefix.length) : keyInfo.name);
125391
+ logger.debug(`[KvProvider] Found ${keys.length} keys with prefix: ${kvPrefix}`, context);
125392
+ return keys;
125393
+ }, {
125394
+ operation: "KvProvider.list",
125395
+ context,
125396
+ input: { tenantId, prefix }
125397
+ });
125277
125398
  }
125278
125399
  }
125279
125400
 
125280
125401
  // src/storage/core/storageFactory.ts
125281
125402
  var isServerless3 = typeof process === "undefined" || process.env.IS_SERVERLESS === "true";
125282
- function createStorageProvider(config2) {
125403
+ function createStorageProvider(config2, deps = {}) {
125283
125404
  const context = requestContextService.createRequestContext({
125284
125405
  operation: "createStorageProvider"
125285
125406
  });
@@ -125301,16 +125422,19 @@ function createStorageProvider(config2) {
125301
125422
  if (!config2.supabase?.url || !config2.supabase?.serviceRoleKey) {
125302
125423
  throw new McpError(-32008 /* ConfigurationError */, "SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY must be set for the supabase storage provider.", context);
125303
125424
  }
125425
+ if (deps.supabaseClient) {
125426
+ return new SupabaseProvider(deps.supabaseClient);
125427
+ }
125304
125428
  return import_tsyringe5.container.resolve(SupabaseProvider);
125305
125429
  case "cloudflare-r2":
125306
125430
  if (isServerless3) {
125307
- const bucket = globalThis.R2_BUCKET;
125431
+ const bucket = deps.r2Bucket ?? globalThis.R2_BUCKET;
125308
125432
  return new R2Provider(bucket);
125309
125433
  }
125310
125434
  throw new McpError(-32008 /* ConfigurationError */, "Cloudflare R2 storage is only available in a Cloudflare Worker environment.", context);
125311
125435
  case "cloudflare-kv":
125312
125436
  if (isServerless3) {
125313
- const kv = globalThis.KV_NAMESPACE;
125437
+ const kv = deps.kvNamespace ?? globalThis.KV_NAMESPACE;
125314
125438
  return new KvProvider(kv);
125315
125439
  }
125316
125440
  throw new McpError(-32008 /* ConfigurationError */, "Cloudflare KV storage is only available in a Cloudflare Worker environment.", context);
@@ -125386,9 +125510,9 @@ function withRequiredScopes(requiredScopes) {
125386
125510
 
125387
125511
  // src/mcp-server/transports/auth/lib/withAuth.ts
125388
125512
  function withToolAuth(requiredScopes, logicFn) {
125389
- return async (input, context) => {
125513
+ return async (input, context, sdkContext) => {
125390
125514
  withRequiredScopes(requiredScopes);
125391
- return Promise.resolve(logicFn(input, context));
125515
+ return Promise.resolve(logicFn(input, context, sdkContext));
125392
125516
  };
125393
125517
  }
125394
125518
  function withResourceAuth(requiredScopes, logicFn) {
@@ -128692,7 +128816,7 @@ async function registerResource(server, def) {
128692
128816
  description: def.description,
128693
128817
  mimeType,
128694
128818
  ...def.examples && { examples: def.examples }
128695
- }, (uri, params, callContext) => {
128819
+ }, async (uri, params, callContext) => {
128696
128820
  const sessionId = typeof callContext?.sessionId === "string" ? callContext.sessionId : undefined;
128697
128821
  const handlerContext = requestContextService.createRequestContext({
128698
128822
  parentContext: callContext,
@@ -128705,7 +128829,7 @@ async function registerResource(server, def) {
128705
128829
  });
128706
128830
  try {
128707
128831
  const parsedParams = def.paramsSchema.parse(params);
128708
- const responseData = def.logic(uri, parsedParams, handlerContext);
128832
+ const responseData = await def.logic(uri, parsedParams, handlerContext);
128709
128833
  const rawContents = formatter(responseData, {
128710
128834
  uri,
128711
128835
  mimeType
@@ -128767,7 +128891,7 @@ var import_tsyringe8 = __toESM(require_cjs3(), 1);
128767
128891
 
128768
128892
  // src/mcp-server/tools/definitions/template-cat-fact.tool.ts
128769
128893
  var TOOL_NAME = "template_cat_fact";
128770
- var TOOL_TITLE = "template_cat_fact";
128894
+ var TOOL_TITLE = "Template Cat Fact";
128771
128895
  var TOOL_DESCRIPTION = "Fetches a random cat fact from a public API with an optional maximum length.";
128772
128896
  var TOOL_ANNOTATIONS = {
128773
128897
  readOnlyHint: true,
@@ -128789,20 +128913,20 @@ var OutputSchema2 = z.object({
128789
128913
  requestedMaxLength: z.number().int().optional().describe("The maximum length that was requested for the fact."),
128790
128914
  timestamp: z.string().datetime().describe("ISO 8601 timestamp of when the response was generated.")
128791
128915
  }).describe("Cat fact tool response payload.");
128792
- async function catFactToolLogic(input, context) {
128916
+ async function catFactToolLogic(input, appContext, _sdkContext) {
128793
128917
  logger.debug("Processing template_cat_fact logic.", {
128794
- ...context,
128918
+ ...appContext,
128795
128919
  toolInput: input
128796
128920
  });
128797
128921
  const url = input.maxLength !== undefined ? `${CAT_FACT_API_URL}?max_length=${input.maxLength}` : CAT_FACT_API_URL;
128798
- logger.info(`Fetching random cat fact from: ${url}`, context);
128799
- const response = await fetchWithTimeout(url, CAT_FACT_API_TIMEOUT_MS, context);
128922
+ logger.info(`Fetching random cat fact from: ${url}`, appContext);
128923
+ const response = await fetchWithTimeout(url, CAT_FACT_API_TIMEOUT_MS, appContext);
128800
128924
  if (!response.ok) {
128801
128925
  const errorText = await response.text().catch(() => {
128802
128926
  return;
128803
128927
  });
128804
128928
  throw new McpError(-32000 /* ServiceUnavailable */, `Cat Fact API request failed: ${response.status} ${response.statusText}`, {
128805
- requestId: context.requestId,
128929
+ requestId: appContext.requestId,
128806
128930
  httpStatusCode: response.status,
128807
128931
  responseBody: errorText
128808
128932
  });
@@ -128811,12 +128935,12 @@ async function catFactToolLogic(input, context) {
128811
128935
  const parsed = CatFactApiSchema.safeParse(rawData);
128812
128936
  if (!parsed.success) {
128813
128937
  logger.error("Cat Fact API response validation failed", {
128814
- ...context,
128938
+ ...appContext,
128815
128939
  receivedData: rawData,
128816
128940
  issues: parsed.error.issues
128817
128941
  });
128818
128942
  throw new McpError(-32000 /* ServiceUnavailable */, "Cat Fact API returned unexpected data format.", {
128819
- requestId: context.requestId,
128943
+ requestId: appContext.requestId,
128820
128944
  issues: parsed.error.issues
128821
128945
  });
128822
128946
  }
@@ -128828,7 +128952,7 @@ async function catFactToolLogic(input, context) {
128828
128952
  timestamp: new Date().toISOString()
128829
128953
  };
128830
128954
  logger.notice("Random cat fact fetched and processed successfully.", {
128831
- ...context,
128955
+ ...appContext,
128832
128956
  factLength: toolResponse.length
128833
128957
  });
128834
128958
  return toolResponse;
@@ -128854,7 +128978,7 @@ var catFactTool = {
128854
128978
 
128855
128979
  // src/mcp-server/tools/definitions/template-echo-message.tool.ts
128856
128980
  var TOOL_NAME2 = "template_echo_message";
128857
- var TOOL_TITLE2 = "Echo Message";
128981
+ var TOOL_TITLE2 = "Template Echo Message";
128858
128982
  var TOOL_DESCRIPTION2 = "Echoes a message back with optional formatting and repetition.";
128859
128983
  var TOOL_ANNOTATIONS2 = {
128860
128984
  readOnlyHint: true,
@@ -128880,17 +129004,17 @@ var OutputSchema3 = z.object({
128880
129004
  repeatCount: z.number().int().min(1).describe("The number of times the message was repeated."),
128881
129005
  timestamp: z.string().datetime().optional().describe("Optional ISO 8601 timestamp of when the response was generated.")
128882
129006
  }).describe("Echo tool response payload.");
128883
- async function echoToolLogic(input, context) {
129007
+ async function echoToolLogic(input, appContext, _sdkContext) {
128884
129008
  logger.debug("Processing echo message logic.", {
128885
- ...context,
129009
+ ...appContext,
128886
129010
  toolInput: input
128887
129011
  });
128888
129012
  if (input.message === TEST_ERROR_TRIGGER_MESSAGE) {
128889
129013
  const errorData = {
128890
- requestId: context.requestId
129014
+ requestId: appContext.requestId
128891
129015
  };
128892
- if (typeof context.traceId === "string") {
128893
- errorData.traceId = context.traceId;
129016
+ if (typeof appContext.traceId === "string") {
129017
+ errorData.traceId = appContext.traceId;
128894
129018
  }
128895
129019
  throw new McpError(-32007 /* ValidationError */, "Deliberate failure triggered.", errorData);
128896
129020
  }
@@ -128949,7 +129073,7 @@ function arrayBufferToBase64(buffer) {
128949
129073
 
128950
129074
  // src/mcp-server/tools/definitions/template-image-test.tool.ts
128951
129075
  var TOOL_NAME3 = "template_image_test";
128952
- var TOOL_TITLE3 = "Fetch Image Test";
129076
+ var TOOL_TITLE3 = "Template Image Test";
128953
129077
  var TOOL_DESCRIPTION3 = "Fetches a random cat image and returns it base64-encoded with the MIME type. Useful for testing image handling.";
128954
129078
  var TOOL_ANNOTATIONS3 = {
128955
129079
  readOnlyHint: true,
@@ -128964,25 +129088,25 @@ var OutputSchema4 = z.object({
128964
129088
  data: z.string().describe("Base64 encoded image data."),
128965
129089
  mimeType: z.string().describe("The MIME type of the image (e.g., 'image/jpeg').")
128966
129090
  }).describe("Image tool response payload.");
128967
- async function imageTestToolLogic(input, context) {
129091
+ async function imageTestToolLogic(input, appContext, _sdkContext) {
128968
129092
  logger.debug("Processing template_image_test logic.", {
128969
- ...context,
129093
+ ...appContext,
128970
129094
  toolInput: input
128971
129095
  });
128972
- const response = await fetchWithTimeout(CAT_API_URL, API_TIMEOUT_MS, context);
129096
+ const response = await fetchWithTimeout(CAT_API_URL, API_TIMEOUT_MS, appContext);
128973
129097
  if (!response.ok) {
128974
129098
  const errorText = await response.text().catch(() => {
128975
129099
  return;
128976
129100
  });
128977
129101
  throw new McpError(-32000 /* ServiceUnavailable */, `Image API request failed: ${response.status} ${response.statusText}`, {
128978
- requestId: context.requestId,
129102
+ requestId: appContext.requestId,
128979
129103
  httpStatusCode: response.status,
128980
129104
  responseBody: errorText
128981
129105
  });
128982
129106
  }
128983
129107
  const arrayBuf = await response.arrayBuffer();
128984
129108
  if (arrayBuf.byteLength === 0) {
128985
- throw new McpError(-32000 /* ServiceUnavailable */, "Image API returned an empty payload.", { requestId: context.requestId });
129109
+ throw new McpError(-32000 /* ServiceUnavailable */, "Image API returned an empty payload.", { requestId: appContext.requestId });
128986
129110
  }
128987
129111
  const mimeType = response.headers.get("content-type") || "image/jpeg";
128988
129112
  const result = {
@@ -128990,7 +129114,7 @@ async function imageTestToolLogic(input, context) {
128990
129114
  mimeType
128991
129115
  };
128992
129116
  logger.notice("Image fetched and encoded successfully.", {
128993
- ...context,
129117
+ ...appContext,
128994
129118
  mimeType,
128995
129119
  byteLength: arrayBuf.byteLength
128996
129120
  });
@@ -129016,8 +129140,91 @@ var imageTestTool = {
129016
129140
  responseFormatter: responseFormatter3
129017
129141
  };
129018
129142
 
129143
+ // src/mcp-server/tools/definitions/template_madlibs_elicitation.tool.ts
129144
+ var TOOL_NAME4 = "template_madlibs_elicitation";
129145
+ var TOOL_TITLE4 = "Mad Libs Elicitation Game";
129146
+ var TOOL_DESCRIPTION4 = "Plays a game of Mad Libs. If any parts of speech (noun, verb, adjective) are missing, it will use elicitation to ask the user for them.";
129147
+ var TOOL_ANNOTATIONS4 = {
129148
+ readOnlyHint: true,
129149
+ idempotentHint: false,
129150
+ openWorldHint: false
129151
+ };
129152
+ var InputSchema4 = z.object({
129153
+ noun: z.string().optional().describe("A noun for the story."),
129154
+ verb: z.string().optional().describe("A verb (past tense) for the story."),
129155
+ adjective: z.string().optional().describe("An adjective for the story.")
129156
+ }).describe("Inputs for the Mad Libs game. Any missing fields will be elicited.");
129157
+ var OutputSchema5 = z.object({
129158
+ story: z.string().describe("The final, generated Mad Libs story."),
129159
+ noun: z.string().describe("The noun used in the story."),
129160
+ verb: z.string().describe("The verb used in the story."),
129161
+ adjective: z.string().describe("The adjective used in the story.")
129162
+ }).describe("The completed Mad Libs story and the words used.");
129163
+ function hasElicitInput(ctx) {
129164
+ return typeof ctx?.elicitInput === "function";
129165
+ }
129166
+ async function elicitAndValidate(partOfSpeech, sdkContext) {
129167
+ if (!hasElicitInput(sdkContext)) {
129168
+ throw new McpError(-32600 /* InvalidRequest */, "Elicitation is not available in the current context.", { requestId: sdkContext.requestId, operation: "madlibs.elicit" });
129169
+ }
129170
+ const elicitedUnknown = await sdkContext.elicitInput({
129171
+ message: `I need a ${partOfSpeech}.`,
129172
+ schema: { type: "string" }
129173
+ });
129174
+ const validation = z.string().min(1).safeParse(elicitedUnknown);
129175
+ if (!validation.success) {
129176
+ throw new McpError(-32602 /* InvalidParams */, `Invalid ${partOfSpeech} received from user.`, { provided: elicitedUnknown });
129177
+ }
129178
+ return validation.data;
129179
+ }
129180
+ async function madlibsToolLogic(input, appContext, sdkContext) {
129181
+ logger.debug("Processing Mad Libs logic.", { ...appContext, toolInput: input });
129182
+ const noun = input.noun ?? await elicitAndValidate("noun", sdkContext);
129183
+ const verb = input.verb ?? await elicitAndValidate("verb", sdkContext);
129184
+ const adjective = input.adjective ?? await elicitAndValidate("adjective", sdkContext);
129185
+ const story = `The ${adjective} ${noun} ${verb} over the lazy dog.`;
129186
+ const response = {
129187
+ story,
129188
+ noun,
129189
+ verb,
129190
+ adjective
129191
+ };
129192
+ return Promise.resolve(response);
129193
+ }
129194
+ function responseFormatter4(result) {
129195
+ return [
129196
+ {
129197
+ type: "text",
129198
+ text: result.story
129199
+ },
129200
+ {
129201
+ type: "text",
129202
+ text: JSON.stringify({
129203
+ noun: result.noun,
129204
+ verb: result.verb,
129205
+ adjective: result.adjective
129206
+ }, null, 2)
129207
+ }
129208
+ ];
129209
+ }
129210
+ var madlibsElicitationTool = {
129211
+ name: TOOL_NAME4,
129212
+ title: TOOL_TITLE4,
129213
+ description: TOOL_DESCRIPTION4,
129214
+ inputSchema: InputSchema4,
129215
+ outputSchema: OutputSchema5,
129216
+ annotations: TOOL_ANNOTATIONS4,
129217
+ logic: withToolAuth(["tool:madlibs:play"], madlibsToolLogic),
129218
+ responseFormatter: responseFormatter4
129219
+ };
129220
+
129019
129221
  // src/mcp-server/tools/definitions/index.ts
129020
- var allToolDefinitions = [catFactTool, echoTool, imageTestTool];
129222
+ var allToolDefinitions = [
129223
+ catFactTool,
129224
+ echoTool,
129225
+ imageTestTool,
129226
+ madlibsElicitationTool
129227
+ ];
129021
129228
 
129022
129229
  // src/mcp-server/tools/utils/toolHandlerFactory.ts
129023
129230
  var defaultResponseFormatter2 = (result) => [
@@ -129026,25 +129233,29 @@ var defaultResponseFormatter2 = (result) => [
129026
129233
  function createMcpToolHandler({
129027
129234
  toolName,
129028
129235
  logic,
129029
- responseFormatter: responseFormatter4 = defaultResponseFormatter2
129236
+ responseFormatter: responseFormatter5 = defaultResponseFormatter2
129030
129237
  }) {
129031
129238
  return async (input, callContext) => {
129032
- const sessionId = typeof callContext?.sessionId === "string" ? callContext.sessionId : undefined;
129033
- const handlerContext = requestContextService.createRequestContext({
129034
- parentContext: callContext,
129239
+ const sdkContext = callContext;
129240
+ const sessionId = typeof sdkContext?.sessionId === "string" ? sdkContext.sessionId : undefined;
129241
+ const appContext = requestContextService.createRequestContext({
129242
+ parentContext: sdkContext,
129035
129243
  operation: "HandleToolRequest",
129036
129244
  additionalContext: { toolName, sessionId, input }
129037
129245
  });
129246
+ if ("elicitInput" in sdkContext && typeof sdkContext.elicitInput === "function") {
129247
+ appContext.elicitInput = sdkContext.elicitInput;
129248
+ }
129038
129249
  try {
129039
- const result = await measureToolExecution(() => logic(input, handlerContext), { ...handlerContext, toolName }, input);
129250
+ const result = await measureToolExecution(() => logic(input, appContext, sdkContext), { ...appContext, toolName }, input);
129040
129251
  return {
129041
129252
  structuredContent: result,
129042
- content: responseFormatter4(result)
129253
+ content: responseFormatter5(result)
129043
129254
  };
129044
129255
  } catch (error2) {
129045
129256
  const mcpError = ErrorHandler.handleError(error2, {
129046
129257
  operation: `tool:${toolName}`,
129047
- context: handlerContext,
129258
+ context: appContext,
129048
129259
  input
129049
129260
  });
129050
129261
  return {
@@ -133919,7 +134130,8 @@ async function createMcpServerInstance() {
133919
134130
  capabilities: {
133920
134131
  logging: {},
133921
134132
  resources: { listChanged: true },
133922
- tools: { listChanged: true }
134133
+ tools: { listChanged: true },
134134
+ elicitation: {}
133923
134135
  }
133924
134136
  });
133925
134137
  try {