mcp-ts-template 2.2.3 → 2.2.5

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 (2) hide show
  1. package/dist/index.js +816 -108
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -117536,7 +117536,7 @@ var ErrorSchema = z.object({
117536
117536
  // package.json
117537
117537
  var package_default = {
117538
117538
  name: "mcp-ts-template",
117539
- version: "2.2.2",
117539
+ version: "2.2.4",
117540
117540
  mcpName: "io.github.cyanheads/mcp-ts-template",
117541
117541
  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.",
117542
117542
  main: "dist/index.js",
@@ -117782,6 +117782,7 @@ var ConfigSchema = z.object({
117782
117782
  oauthAudience: z.string().optional(),
117783
117783
  oauthJwksCooldownMs: z.coerce.number().default(300000),
117784
117784
  oauthJwksTimeoutMs: z.coerce.number().default(5000),
117785
+ mcpServerResourceIdentifier: z.string().url().optional(),
117785
117786
  devMcpClientId: z.string().optional(),
117786
117787
  devMcpScopes: z.array(z.string()).optional(),
117787
117788
  openrouterAppUrl: z.string().default("http://localhost:3000"),
@@ -117876,6 +117877,7 @@ var parseConfig = () => {
117876
117877
  oauthAudience: env.OAUTH_AUDIENCE,
117877
117878
  oauthJwksCooldownMs: env.OAUTH_JWKS_COOLDOWN_MS,
117878
117879
  oauthJwksTimeoutMs: env.OAUTH_JWKS_TIMEOUT_MS,
117880
+ mcpServerResourceIdentifier: env.MCP_SERVER_RESOURCE_IDENTIFIER,
117879
117881
  devMcpClientId: env.DEV_MCP_CLIENT_ID,
117880
117882
  devMcpScopes: env.DEV_MCP_SCOPES?.split(",").map((s) => s.trim()),
117881
117883
  openrouterAppUrl: env.OPENROUTER_APP_URL,
@@ -119508,7 +119510,7 @@ async function fetchWithTimeout(url, timeoutMs, context, options) {
119508
119510
 
119509
119511
  // src/container/index.ts
119510
119512
  var import_reflect_metadata = __toESM(require_Reflect(), 1);
119511
- var import_tsyringe15 = __toESM(require_cjs3(), 1);
119513
+ var import_tsyringe17 = __toESM(require_cjs3(), 1);
119512
119514
 
119513
119515
  // src/container/registrations/core.ts
119514
119516
  var import_tsyringe6 = __toESM(require_cjs3(), 1);
@@ -125172,9 +125174,25 @@ class StorageService2 {
125172
125174
  const tenantId = requireTenantId(context);
125173
125175
  return this.provider.delete(tenantId, key, context);
125174
125176
  }
125175
- list(prefix, context) {
125177
+ list(prefix, context, options) {
125178
+ const tenantId = requireTenantId(context);
125179
+ return this.provider.list(tenantId, prefix, context, options);
125180
+ }
125181
+ getMany(keys, context) {
125182
+ const tenantId = requireTenantId(context);
125183
+ return this.provider.getMany(tenantId, keys, context);
125184
+ }
125185
+ setMany(entries, context, options) {
125186
+ const tenantId = requireTenantId(context);
125187
+ return this.provider.setMany(tenantId, entries, context, options);
125188
+ }
125189
+ deleteMany(keys, context) {
125190
+ const tenantId = requireTenantId(context);
125191
+ return this.provider.deleteMany(tenantId, keys, context);
125192
+ }
125193
+ clear(context) {
125176
125194
  const tenantId = requireTenantId(context);
125177
- return this.provider.list(tenantId, prefix, context);
125195
+ return this.provider.clear(tenantId, context);
125178
125196
  }
125179
125197
  }
125180
125198
  StorageService2 = __legacyDecorateClassTS([
@@ -125192,6 +125210,7 @@ var import_tsyringe5 = __toESM(require_cjs3(), 1);
125192
125210
  import { existsSync, mkdirSync } from "fs";
125193
125211
  import { readFile, readdir, rm, writeFile } from "fs/promises";
125194
125212
  import path2 from "path";
125213
+ var DEFAULT_LIST_LIMIT = 1000;
125195
125214
  var FILE_ENVELOPE_VERSION = 1;
125196
125215
 
125197
125216
  class FileSystemProvider {
@@ -125319,7 +125338,7 @@ class FileSystemProvider {
125319
125338
  }
125320
125339
  return results;
125321
125340
  }
125322
- async list(tenantId, prefix, context) {
125341
+ async list(tenantId, prefix, context, options) {
125323
125342
  return ErrorHandler.tryCatch(async () => {
125324
125343
  const tenantPath = this.getTenantPath(tenantId);
125325
125344
  const allKeys = await this.listFilesRecursively(tenantPath, tenantPath);
@@ -125337,16 +125356,93 @@ class FileSystemProvider {
125337
125356
  continue;
125338
125357
  }
125339
125358
  }
125340
- return validKeys;
125359
+ validKeys.sort();
125360
+ const limit2 = options?.limit ?? DEFAULT_LIST_LIMIT;
125361
+ let startIndex = 0;
125362
+ if (options?.cursor) {
125363
+ const cursorIndex = validKeys.indexOf(options.cursor);
125364
+ if (cursorIndex !== -1) {
125365
+ startIndex = cursorIndex + 1;
125366
+ }
125367
+ }
125368
+ const paginatedKeys = validKeys.slice(startIndex, startIndex + limit2);
125369
+ const nextCursor = startIndex + limit2 < validKeys.length ? paginatedKeys[paginatedKeys.length - 1] : undefined;
125370
+ return {
125371
+ keys: paginatedKeys,
125372
+ nextCursor
125373
+ };
125341
125374
  }, {
125342
125375
  operation: "FileSystemProvider.list",
125343
125376
  context,
125344
125377
  input: { tenantId, prefix }
125345
125378
  });
125346
125379
  }
125380
+ async getMany(tenantId, keys, context) {
125381
+ return ErrorHandler.tryCatch(async () => {
125382
+ const results = new Map;
125383
+ for (const key of keys) {
125384
+ const value = await this.get(tenantId, key, context);
125385
+ if (value !== null) {
125386
+ results.set(key, value);
125387
+ }
125388
+ }
125389
+ return results;
125390
+ }, {
125391
+ operation: "FileSystemProvider.getMany",
125392
+ context,
125393
+ input: { tenantId, keyCount: keys.length }
125394
+ });
125395
+ }
125396
+ async setMany(tenantId, entries, context, options) {
125397
+ return ErrorHandler.tryCatch(async () => {
125398
+ for (const [key, value] of entries.entries()) {
125399
+ await this.set(tenantId, key, value, context, options);
125400
+ }
125401
+ }, {
125402
+ operation: "FileSystemProvider.setMany",
125403
+ context,
125404
+ input: { tenantId, entryCount: entries.size }
125405
+ });
125406
+ }
125407
+ async deleteMany(tenantId, keys, context) {
125408
+ return ErrorHandler.tryCatch(async () => {
125409
+ let deletedCount = 0;
125410
+ for (const key of keys) {
125411
+ const deleted = await this.delete(tenantId, key, context);
125412
+ if (deleted) {
125413
+ deletedCount++;
125414
+ }
125415
+ }
125416
+ return deletedCount;
125417
+ }, {
125418
+ operation: "FileSystemProvider.deleteMany",
125419
+ context,
125420
+ input: { tenantId, keyCount: keys.length }
125421
+ });
125422
+ }
125423
+ async clear(tenantId, context) {
125424
+ return ErrorHandler.tryCatch(async () => {
125425
+ const tenantPath = this.getTenantPath(tenantId);
125426
+ const allKeys = await this.listFilesRecursively(tenantPath, tenantPath);
125427
+ let deletedCount = 0;
125428
+ for (const key of allKeys) {
125429
+ const deleted = await this.delete(tenantId, key, context);
125430
+ if (deleted) {
125431
+ deletedCount++;
125432
+ }
125433
+ }
125434
+ return deletedCount;
125435
+ }, {
125436
+ operation: "FileSystemProvider.clear",
125437
+ context,
125438
+ input: { tenantId }
125439
+ });
125440
+ }
125347
125441
  }
125348
125442
 
125349
125443
  // src/storage/providers/inMemory/inMemoryProvider.ts
125444
+ var DEFAULT_LIST_LIMIT2 = 1000;
125445
+
125350
125446
  class InMemoryProvider {
125351
125447
  store = new Map;
125352
125448
  getTenantStore(tenantId) {
@@ -125386,21 +125482,71 @@ class InMemoryProvider {
125386
125482
  const tenantStore = this.getTenantStore(tenantId);
125387
125483
  return Promise.resolve(tenantStore.delete(key));
125388
125484
  }
125389
- list(tenantId, prefix, context) {
125390
- logger.debug(`[InMemoryProvider] Listing keys with prefix: ${prefix} for tenant: ${tenantId}`, context);
125485
+ list(tenantId, prefix, context, options) {
125486
+ logger.debug(`[InMemoryProvider] Listing keys with prefix: ${prefix} for tenant: ${tenantId}`, { ...context, options });
125391
125487
  const tenantStore = this.getTenantStore(tenantId);
125392
125488
  const now = Date.now();
125393
- const keys = [];
125489
+ const allKeys = [];
125394
125490
  for (const [key, entry] of tenantStore.entries()) {
125395
125491
  if (key.startsWith(prefix)) {
125396
125492
  if (entry.expiresAt && now > entry.expiresAt) {
125397
125493
  tenantStore.delete(key);
125398
125494
  } else {
125399
- keys.push(key);
125495
+ allKeys.push(key);
125400
125496
  }
125401
125497
  }
125402
125498
  }
125403
- return Promise.resolve(keys);
125499
+ allKeys.sort();
125500
+ const limit2 = options?.limit ?? DEFAULT_LIST_LIMIT2;
125501
+ let startIndex = 0;
125502
+ if (options?.cursor) {
125503
+ const cursorIndex = allKeys.indexOf(options.cursor);
125504
+ if (cursorIndex !== -1) {
125505
+ startIndex = cursorIndex + 1;
125506
+ }
125507
+ }
125508
+ const paginatedKeys = allKeys.slice(startIndex, startIndex + limit2);
125509
+ const nextCursor = startIndex + limit2 < allKeys.length ? paginatedKeys[paginatedKeys.length - 1] : undefined;
125510
+ return Promise.resolve({
125511
+ keys: paginatedKeys,
125512
+ nextCursor
125513
+ });
125514
+ }
125515
+ async getMany(tenantId, keys, context) {
125516
+ logger.debug(`[InMemoryProvider] Getting ${keys.length} keys for tenant: ${tenantId}`, context);
125517
+ const results = new Map;
125518
+ for (const key of keys) {
125519
+ const value = await this.get(tenantId, key, context);
125520
+ if (value !== null) {
125521
+ results.set(key, value);
125522
+ }
125523
+ }
125524
+ return results;
125525
+ }
125526
+ async setMany(tenantId, entries, context, options) {
125527
+ logger.debug(`[InMemoryProvider] Setting ${entries.size} keys for tenant: ${tenantId}`, context);
125528
+ for (const [key, value] of entries.entries()) {
125529
+ await this.set(tenantId, key, value, context, options);
125530
+ }
125531
+ }
125532
+ async deleteMany(tenantId, keys, context) {
125533
+ logger.debug(`[InMemoryProvider] Deleting ${keys.length} keys for tenant: ${tenantId}`, context);
125534
+ let deletedCount = 0;
125535
+ for (const key of keys) {
125536
+ const deleted = await this.delete(tenantId, key, context);
125537
+ if (deleted) {
125538
+ deletedCount++;
125539
+ }
125540
+ }
125541
+ return deletedCount;
125542
+ }
125543
+ clear(tenantId, context) {
125544
+ logger.debug(`[InMemoryProvider] Clearing all keys for tenant: ${tenantId}`, context);
125545
+ const tenantStore = this.getTenantStore(tenantId);
125546
+ const count = tenantStore.size;
125547
+ tenantStore.clear();
125548
+ logger.info(`[InMemoryProvider] Cleared ${count} keys for tenant: ${tenantId}`, context);
125549
+ return Promise.resolve(count);
125404
125550
  }
125405
125551
  }
125406
125552
 
@@ -125408,6 +125554,7 @@ class InMemoryProvider {
125408
125554
  var import_tsyringe4 = __toESM(require_cjs3(), 1);
125409
125555
  var import_supabase_js = __toESM(require_main6(), 1);
125410
125556
  var TABLE_NAME = "kv_store";
125557
+ var DEFAULT_LIST_LIMIT3 = 1000;
125411
125558
 
125412
125559
  class SupabaseProvider {
125413
125560
  client;
@@ -125467,17 +125614,101 @@ class SupabaseProvider {
125467
125614
  input: { tenantId, key }
125468
125615
  });
125469
125616
  }
125470
- async list(tenantId, prefix, context) {
125617
+ async list(tenantId, prefix, context, options) {
125471
125618
  return ErrorHandler.tryCatch(async () => {
125472
125619
  const now = new Date().toISOString();
125473
- const { data, error: error2 } = await this.getClient().from(TABLE_NAME).select("key").eq("tenant_id", tenantId).like("key", `${prefix}%`).or(`expires_at.is.null,expires_at.gt.${now}`);
125620
+ const limit2 = options?.limit ?? DEFAULT_LIST_LIMIT3;
125621
+ 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);
125622
+ if (options?.cursor) {
125623
+ query = query.gt("key", options.cursor);
125624
+ }
125625
+ const { data, error: error2 } = await query;
125474
125626
  if (error2)
125475
125627
  throw error2;
125476
- return data?.map((item) => item.key) ?? [];
125628
+ const keys = data?.map((item) => item.key) ?? [];
125629
+ const hasMore = keys.length > limit2;
125630
+ const resultKeys = hasMore ? keys.slice(0, limit2) : keys;
125631
+ const nextCursor = hasMore ? resultKeys[resultKeys.length - 1] : undefined;
125632
+ return {
125633
+ keys: resultKeys,
125634
+ nextCursor
125635
+ };
125477
125636
  }, {
125478
125637
  operation: "SupabaseProvider.list",
125479
125638
  context,
125480
- input: { tenantId, prefix }
125639
+ input: { tenantId, prefix, options }
125640
+ });
125641
+ }
125642
+ async getMany(tenantId, keys, context) {
125643
+ return ErrorHandler.tryCatch(async () => {
125644
+ if (keys.length === 0) {
125645
+ return new Map;
125646
+ }
125647
+ const { data, error: error2 } = await this.getClient().from(TABLE_NAME).select("key, value, expires_at").eq("tenant_id", tenantId).in("key", keys);
125648
+ if (error2)
125649
+ throw error2;
125650
+ const results = new Map;
125651
+ for (const row of data ?? []) {
125652
+ if (!row.expires_at || new Date(row.expires_at).getTime() >= Date.now()) {
125653
+ results.set(row.key, row.value);
125654
+ } else {
125655
+ await this.delete(tenantId, row.key, context);
125656
+ }
125657
+ }
125658
+ return results;
125659
+ }, {
125660
+ operation: "SupabaseProvider.getMany",
125661
+ context,
125662
+ input: { tenantId, keyCount: keys.length }
125663
+ });
125664
+ }
125665
+ async setMany(tenantId, entries, context, options) {
125666
+ return ErrorHandler.tryCatch(async () => {
125667
+ if (entries.size === 0) {
125668
+ return;
125669
+ }
125670
+ const expires_at = options?.ttl ? new Date(Date.now() + options.ttl * 1000).toISOString() : null;
125671
+ const rows = Array.from(entries.entries()).map(([key, value]) => ({
125672
+ tenant_id: tenantId,
125673
+ key,
125674
+ value,
125675
+ expires_at
125676
+ }));
125677
+ const { error: error2 } = await this.getClient().from(TABLE_NAME).upsert(rows);
125678
+ if (error2)
125679
+ throw error2;
125680
+ }, {
125681
+ operation: "SupabaseProvider.setMany",
125682
+ context,
125683
+ input: { tenantId, entryCount: entries.size }
125684
+ });
125685
+ }
125686
+ async deleteMany(tenantId, keys, context) {
125687
+ return ErrorHandler.tryCatch(async () => {
125688
+ if (keys.length === 0) {
125689
+ return 0;
125690
+ }
125691
+ const { error: error2, count } = await this.getClient().from(TABLE_NAME).delete({ count: "exact" }).eq("tenant_id", tenantId).in("key", keys);
125692
+ if (error2)
125693
+ throw error2;
125694
+ return count ?? 0;
125695
+ }, {
125696
+ operation: "SupabaseProvider.deleteMany",
125697
+ context,
125698
+ input: { tenantId, keyCount: keys.length }
125699
+ });
125700
+ }
125701
+ async clear(tenantId, context) {
125702
+ return ErrorHandler.tryCatch(async () => {
125703
+ const { error: error2, count } = await this.getClient().from(TABLE_NAME).delete({ count: "exact" }).eq("tenant_id", tenantId);
125704
+ if (error2)
125705
+ throw error2;
125706
+ logger.info(`[SupabaseProvider] Cleared ${count ?? 0} keys for tenant: ${tenantId}`, context);
125707
+ return count ?? 0;
125708
+ }, {
125709
+ operation: "SupabaseProvider.clear",
125710
+ context,
125711
+ input: { tenantId }
125481
125712
  });
125482
125713
  }
125483
125714
  }
@@ -125491,10 +125722,14 @@ SupabaseProvider = __legacyDecorateClassTS([
125491
125722
 
125492
125723
  // src/storage/providers/cloudflare/r2Provider.ts
125493
125724
  var R2_ENVELOPE_VERSION = 1;
125725
+ var DEFAULT_LIST_LIMIT4 = 1000;
125494
125726
 
125495
125727
  class R2Provider {
125496
125728
  bucket;
125497
125729
  constructor(bucket) {
125730
+ if (!bucket) {
125731
+ throw new McpError(-32008 /* ConfigurationError */, "R2Provider requires a valid R2Bucket instance.");
125732
+ }
125498
125733
  this.bucket = bucket;
125499
125734
  }
125500
125735
  getR2Key(tenantId, key) {
@@ -125579,27 +125814,115 @@ class R2Provider {
125579
125814
  input: { tenantId, key }
125580
125815
  });
125581
125816
  }
125582
- async list(tenantId, prefix, context) {
125817
+ async list(tenantId, prefix, context, options) {
125583
125818
  const r2Prefix = this.getR2Key(tenantId, prefix);
125584
125819
  return ErrorHandler.tryCatch(async () => {
125585
- logger.debug(`[R2Provider] Listing keys with prefix: ${r2Prefix}`, context);
125586
- const listed = await this.bucket.list({ prefix: r2Prefix });
125820
+ logger.debug(`[R2Provider] Listing keys with prefix: ${r2Prefix}`, {
125821
+ ...context,
125822
+ options
125823
+ });
125824
+ const limit2 = options?.limit ?? DEFAULT_LIST_LIMIT4;
125825
+ const listOptions = {
125826
+ prefix: r2Prefix,
125827
+ limit: limit2 + 1
125828
+ };
125829
+ if (options?.cursor) {
125830
+ listOptions.cursor = options.cursor;
125831
+ }
125832
+ const listed = await this.bucket.list(listOptions);
125587
125833
  const tenantPrefix = `${tenantId}:`;
125588
125834
  const keys = listed.objects.map((obj) => obj.key.startsWith(tenantPrefix) ? obj.key.substring(tenantPrefix.length) : obj.key);
125589
- logger.debug(`[R2Provider] Found ${keys.length} keys with prefix: ${r2Prefix}`, context);
125590
- return keys;
125835
+ const hasMore = keys.length > limit2;
125836
+ const resultKeys = hasMore ? keys.slice(0, limit2) : keys;
125837
+ const nextCursor = "cursor" in listed && listed.truncated ? listed.cursor : undefined;
125838
+ logger.debug(`[R2Provider] Found ${resultKeys.length} keys with prefix: ${r2Prefix}`, context);
125839
+ return {
125840
+ keys: resultKeys,
125841
+ nextCursor
125842
+ };
125591
125843
  }, {
125592
125844
  operation: "R2Provider.list",
125593
125845
  context,
125594
- input: { tenantId, prefix }
125846
+ input: { tenantId, prefix, options }
125847
+ });
125848
+ }
125849
+ async getMany(tenantId, keys, context) {
125850
+ return ErrorHandler.tryCatch(async () => {
125851
+ const results = new Map;
125852
+ for (const key of keys) {
125853
+ const value = await this.get(tenantId, key, context);
125854
+ if (value !== null) {
125855
+ results.set(key, value);
125856
+ }
125857
+ }
125858
+ return results;
125859
+ }, {
125860
+ operation: "R2Provider.getMany",
125861
+ context,
125862
+ input: { tenantId, keyCount: keys.length }
125863
+ });
125864
+ }
125865
+ async setMany(tenantId, entries, context, options) {
125866
+ return ErrorHandler.tryCatch(async () => {
125867
+ const promises = Array.from(entries.entries()).map(([key, value]) => this.set(tenantId, key, value, context, options));
125868
+ await Promise.all(promises);
125869
+ }, {
125870
+ operation: "R2Provider.setMany",
125871
+ context,
125872
+ input: { tenantId, entryCount: entries.size }
125873
+ });
125874
+ }
125875
+ async deleteMany(tenantId, keys, context) {
125876
+ return ErrorHandler.tryCatch(async () => {
125877
+ const promises = keys.map((key) => this.delete(tenantId, key, context));
125878
+ const results = await Promise.all(promises);
125879
+ return results.filter((deleted) => deleted).length;
125880
+ }, {
125881
+ operation: "R2Provider.deleteMany",
125882
+ context,
125883
+ input: { tenantId, keyCount: keys.length }
125884
+ });
125885
+ }
125886
+ async clear(tenantId, context) {
125887
+ return ErrorHandler.tryCatch(async () => {
125888
+ const r2Prefix = `${tenantId}:`;
125889
+ let deletedCount = 0;
125890
+ let cursor;
125891
+ let hasMore = true;
125892
+ while (hasMore) {
125893
+ const listOpts = {
125894
+ prefix: r2Prefix,
125895
+ limit: 1000
125896
+ };
125897
+ if (cursor) {
125898
+ listOpts.cursor = cursor;
125899
+ }
125900
+ const listed = await this.bucket.list(listOpts);
125901
+ const deletePromises = listed.objects.map((obj) => this.bucket.delete(obj.key));
125902
+ await Promise.all(deletePromises);
125903
+ deletedCount += listed.objects.length;
125904
+ hasMore = listed.truncated;
125905
+ cursor = "cursor" in listed ? listed.cursor : undefined;
125906
+ }
125907
+ logger.info(`[R2Provider] Cleared ${deletedCount} keys for tenant: ${tenantId}`, context);
125908
+ return deletedCount;
125909
+ }, {
125910
+ operation: "R2Provider.clear",
125911
+ context,
125912
+ input: { tenantId }
125595
125913
  });
125596
125914
  }
125597
125915
  }
125598
125916
 
125599
125917
  // src/storage/providers/cloudflare/kvProvider.ts
125918
+ var DEFAULT_LIST_LIMIT5 = 1000;
125919
+
125600
125920
  class KvProvider {
125601
125921
  kv;
125602
125922
  constructor(kv) {
125923
+ if (!kv) {
125924
+ throw new McpError(-32008 /* ConfigurationError */, "KvProvider requires a valid KVNamespace instance.");
125925
+ }
125603
125926
  this.kv = kv;
125604
125927
  }
125605
125928
  getKvKey(tenantId, key) {
@@ -125659,19 +125982,100 @@ class KvProvider {
125659
125982
  input: { tenantId, key }
125660
125983
  });
125661
125984
  }
125662
- async list(tenantId, prefix, context) {
125985
+ async list(tenantId, prefix, context, options) {
125663
125986
  const kvPrefix = this.getKvKey(tenantId, prefix);
125664
125987
  return ErrorHandler.tryCatch(async () => {
125665
- logger.debug(`[KvProvider] Listing keys with prefix: ${kvPrefix}`, context);
125666
- const listed = await this.kv.list({ prefix: kvPrefix });
125988
+ logger.debug(`[KvProvider] Listing keys with prefix: ${kvPrefix}`, {
125989
+ ...context,
125990
+ options
125991
+ });
125992
+ const limit2 = options?.limit ?? DEFAULT_LIST_LIMIT5;
125993
+ const listOptions = {
125994
+ prefix: kvPrefix,
125995
+ limit: limit2
125996
+ };
125997
+ if (options?.cursor) {
125998
+ listOptions.cursor = options.cursor;
125999
+ }
126000
+ const listed = await this.kv.list(listOptions);
125667
126001
  const tenantPrefix = `${tenantId}:`;
125668
126002
  const keys = listed.keys.map((keyInfo) => keyInfo.name.startsWith(tenantPrefix) ? keyInfo.name.substring(tenantPrefix.length) : keyInfo.name);
126003
+ const nextCursor = "cursor" in listed && !listed.list_complete ? listed.cursor : undefined;
125669
126004
  logger.debug(`[KvProvider] Found ${keys.length} keys with prefix: ${kvPrefix}`, context);
125670
- return keys;
126005
+ return {
126006
+ keys,
126007
+ nextCursor
126008
+ };
125671
126009
  }, {
125672
126010
  operation: "KvProvider.list",
125673
126011
  context,
125674
- input: { tenantId, prefix }
126012
+ input: { tenantId, prefix, options }
126013
+ });
126014
+ }
126015
+ async getMany(tenantId, keys, context) {
126016
+ return ErrorHandler.tryCatch(async () => {
126017
+ const results = new Map;
126018
+ for (const key of keys) {
126019
+ const value = await this.get(tenantId, key, context);
126020
+ if (value !== null) {
126021
+ results.set(key, value);
126022
+ }
126023
+ }
126024
+ return results;
126025
+ }, {
126026
+ operation: "KvProvider.getMany",
126027
+ context,
126028
+ input: { tenantId, keyCount: keys.length }
126029
+ });
126030
+ }
126031
+ async setMany(tenantId, entries, context, options) {
126032
+ return ErrorHandler.tryCatch(async () => {
126033
+ const promises = Array.from(entries.entries()).map(([key, value]) => this.set(tenantId, key, value, context, options));
126034
+ await Promise.all(promises);
126035
+ }, {
126036
+ operation: "KvProvider.setMany",
126037
+ context,
126038
+ input: { tenantId, entryCount: entries.size }
126039
+ });
126040
+ }
126041
+ async deleteMany(tenantId, keys, context) {
126042
+ return ErrorHandler.tryCatch(async () => {
126043
+ const promises = keys.map((key) => this.delete(tenantId, key, context));
126044
+ const results = await Promise.all(promises);
126045
+ return results.filter((deleted) => deleted).length;
126046
+ }, {
126047
+ operation: "KvProvider.deleteMany",
126048
+ context,
126049
+ input: { tenantId, keyCount: keys.length }
126050
+ });
126051
+ }
126052
+ async clear(tenantId, context) {
126053
+ return ErrorHandler.tryCatch(async () => {
126054
+ const kvPrefix = `${tenantId}:`;
126055
+ let deletedCount = 0;
126056
+ let cursor;
126057
+ let listComplete = false;
126058
+ while (!listComplete) {
126059
+ const listOpts = {
126060
+ prefix: kvPrefix,
126061
+ limit: 1000
126062
+ };
126063
+ if (cursor) {
126064
+ listOpts.cursor = cursor;
126065
+ }
126066
+ const listed = await this.kv.list(listOpts);
126067
+ const deletePromises = listed.keys.map((keyInfo) => this.kv.delete(keyInfo.name));
126068
+ await Promise.all(deletePromises);
126069
+ deletedCount += listed.keys.length;
126070
+ listComplete = listed.list_complete;
126071
+ cursor = "cursor" in listed ? listed.cursor : undefined;
126072
+ }
126073
+ logger.info(`[KvProvider] Cleared ${deletedCount} keys for tenant: ${tenantId}`, context);
126074
+ return deletedCount;
126075
+ }, {
126076
+ operation: "KvProvider.clear",
126077
+ context,
126078
+ input: { tenantId }
125675
126079
  });
125676
126080
  }
125677
126081
  }
@@ -125751,7 +126155,7 @@ var registerCoreServices = () => {
125751
126155
  };
125752
126156
 
125753
126157
  // src/container/registrations/mcp.ts
125754
- var import_tsyringe14 = __toESM(require_cjs3(), 1);
126158
+ var import_tsyringe16 = __toESM(require_cjs3(), 1);
125755
126159
 
125756
126160
  // src/mcp-server/resources/resource-registration.ts
125757
126161
  var import_tsyringe7 = __toESM(require_cjs3(), 1);
@@ -129162,10 +129566,133 @@ var registerResources = (container3) => {
129162
129566
  };
129163
129567
 
129164
129568
  // src/mcp-server/server.ts
129569
+ var import_tsyringe11 = __toESM(require_cjs3(), 1);
129570
+
129571
+ // src/mcp-server/prompts/prompt-registration.ts
129572
+ var import_tsyringe8 = __toESM(require_cjs3(), 1);
129573
+
129574
+ // src/mcp-server/prompts/definitions/code-review.prompt.ts
129575
+ var PROMPT_NAME = "code_review";
129576
+ var PROMPT_DESCRIPTION = "Generates a structured code review prompt for analyzing code quality, security, and performance.";
129577
+ var ArgumentsSchema = z.object({
129578
+ language: z.string().optional().describe("Programming language of the code to review."),
129579
+ focus: z.string().optional().describe("The primary focus area for the code review ('security' | 'performance' | 'style' | 'general'). Defaults to 'general'."),
129580
+ includeExamples: z.string().optional().describe("Whether to include example improvements in the review ('true' | 'false'). Defaults to 'false'.")
129581
+ });
129582
+ var codeReviewPrompt = {
129583
+ name: PROMPT_NAME,
129584
+ description: PROMPT_DESCRIPTION,
129585
+ argumentsSchema: ArgumentsSchema,
129586
+ generate: (args) => {
129587
+ const focus = args.focus || "general";
129588
+ const includeExamples = args.includeExamples === "true";
129589
+ const focusGuidance = {
129590
+ security: `- Security vulnerabilities and potential exploits
129591
+ - Input validation and sanitization
129592
+ - Authentication and authorization issues
129593
+ - Data exposure risks`,
129594
+ performance: `- Algorithmic complexity and bottlenecks
129595
+ - Memory usage and leaks
129596
+ - Database query optimization
129597
+ - Caching opportunities`,
129598
+ style: `- Code readability and clarity
129599
+ - Naming conventions
129600
+ - Code organization and structure
129601
+ - Documentation completeness`,
129602
+ general: `- Overall code quality
129603
+ - Security considerations
129604
+ - Performance implications
129605
+ - Maintainability and readability`
129606
+ };
129607
+ const examplesSection = includeExamples ? `
129608
+
129609
+ For each significant finding, provide a concrete example of how to improve the code.` : "";
129610
+ return [
129611
+ {
129612
+ role: "user",
129613
+ content: {
129614
+ type: "text",
129615
+ text: `You are an expert code reviewer${args.language ? ` specializing in ${args.language}` : ""}. Please conduct a thorough code review with a focus on ${focus}.
129616
+
129617
+ Review the code for:
129618
+ ${focusGuidance[focus] || focusGuidance["general"]}
129619
+
129620
+ Structure your review as follows:
129621
+ 1. **Summary**: 2-3 sentence overview of the code's quality
129622
+ 2. **Key Findings**: Bullet-point list of important observations
129623
+ 3. **Critical Issues**: Any must-fix problems (if found)
129624
+ 4. **Recommendations**: Suggested improvements prioritized by impact${examplesSection}
129625
+
129626
+ Be constructive, specific, and actionable in your feedback.`
129627
+ }
129628
+ }
129629
+ ];
129630
+ }
129631
+ };
129632
+
129633
+ // src/mcp-server/prompts/definitions/index.ts
129634
+ var allPromptDefinitions = [codeReviewPrompt];
129635
+
129636
+ // src/mcp-server/prompts/prompt-registration.ts
129637
+ class PromptRegistry {
129638
+ logger;
129639
+ constructor(logger2) {
129640
+ this.logger = logger2;
129641
+ }
129642
+ registerAll(server) {
129643
+ const context = requestContextService.createRequestContext({
129644
+ operation: "PromptRegistry.registerAll"
129645
+ });
129646
+ this.logger.debug(`Registering ${allPromptDefinitions.length} prompts...`, context);
129647
+ for (const promptDef of allPromptDefinitions) {
129648
+ this.logger.debug(`Registering prompt: ${promptDef.name}`, context);
129649
+ server.registerPrompt(promptDef.name, {
129650
+ description: promptDef.description,
129651
+ ...promptDef.argumentsSchema && {
129652
+ argsSchema: promptDef.argumentsSchema.shape
129653
+ }
129654
+ }, async (args) => {
129655
+ const messages = await promptDef.generate(args);
129656
+ return { messages };
129657
+ });
129658
+ this.logger.info(`Registered prompt: ${promptDef.name}`, context);
129659
+ }
129660
+ this.logger.info(`Successfully registered ${allPromptDefinitions.length} prompts`, context);
129661
+ }
129662
+ }
129663
+ PromptRegistry = __legacyDecorateClassTS([
129664
+ import_tsyringe8.injectable(),
129665
+ __legacyDecorateParamTS(0, import_tsyringe8.inject(Logger2)),
129666
+ __legacyMetadataTS("design:paramtypes", [
129667
+ Object
129668
+ ])
129669
+ ], PromptRegistry);
129670
+
129671
+ // src/mcp-server/roots/roots-registration.ts
129165
129672
  var import_tsyringe9 = __toESM(require_cjs3(), 1);
129673
+ class RootsRegistry {
129674
+ logger;
129675
+ constructor(logger2) {
129676
+ this.logger = logger2;
129677
+ }
129678
+ registerAll(_server) {
129679
+ const context = requestContextService.createRequestContext({
129680
+ operation: "RootsRegistry.registerAll"
129681
+ });
129682
+ this.logger.debug("Roots capability enabled (client-provided roots)", context);
129683
+ this.logger.info("Roots capability registered successfully", context);
129684
+ }
129685
+ }
129686
+ RootsRegistry = __legacyDecorateClassTS([
129687
+ import_tsyringe9.injectable(),
129688
+ __legacyDecorateParamTS(0, import_tsyringe9.inject(Logger2)),
129689
+ __legacyMetadataTS("design:paramtypes", [
129690
+ Object
129691
+ ])
129692
+ ], RootsRegistry);
129166
129693
 
129167
129694
  // src/mcp-server/tools/tool-registration.ts
129168
- var import_tsyringe8 = __toESM(require_cjs3(), 1);
129695
+ var import_tsyringe10 = __toESM(require_cjs3(), 1);
129169
129696
 
129170
129697
  // src/mcp-server/tools/definitions/template-cat-fact.tool.ts
129171
129698
  var TOOL_NAME = "template_cat_fact";
@@ -129254,11 +129781,136 @@ var catFactTool = {
129254
129781
  responseFormatter
129255
129782
  };
129256
129783
 
129257
- // src/mcp-server/tools/definitions/template-echo-message.tool.ts
129258
- var TOOL_NAME2 = "template_echo_message";
129259
- var TOOL_TITLE2 = "Template Echo Message";
129260
- var TOOL_DESCRIPTION2 = "Echoes a message back with optional formatting and repetition.";
129784
+ // src/mcp-server/tools/definitions/template-code-review-sampling.tool.ts
129785
+ var TOOL_NAME2 = "template_code_review_sampling";
129786
+ var TOOL_TITLE2 = "Code Review with Sampling";
129787
+ var TOOL_DESCRIPTION2 = "Demonstrates MCP sampling by requesting an LLM to review code snippets. The tool uses the client's LLM to generate a code review summary.";
129261
129788
  var TOOL_ANNOTATIONS2 = {
129789
+ readOnlyHint: true,
129790
+ idempotentHint: false,
129791
+ openWorldHint: true
129792
+ };
129793
+ var InputSchema2 = z.object({
129794
+ code: z.string().min(1, "Code snippet cannot be empty.").max(1e4, "Code snippet too large (max 10000 characters).").describe("The code snippet to review."),
129795
+ language: z.string().optional().describe('Programming language of the code (e.g., "typescript", "python").'),
129796
+ focus: z.enum(["security", "performance", "style", "general"]).default("general").describe("The focus area for the code review."),
129797
+ maxTokens: z.number().int().min(100).max(2000).default(500).describe("Maximum tokens for the LLM response.")
129798
+ }).describe("Request an LLM-powered code review via sampling.");
129799
+ var OutputSchema3 = z.object({
129800
+ code: z.string().describe("The original code snippet."),
129801
+ language: z.string().optional().describe("The programming language."),
129802
+ focus: z.string().describe("The review focus area."),
129803
+ review: z.string().describe("The LLM-generated code review summary."),
129804
+ tokenUsage: z.object({
129805
+ requested: z.number().describe("Requested max tokens."),
129806
+ actual: z.number().optional().describe("Actual tokens used (if available).")
129807
+ }).optional().describe("Token usage information.")
129808
+ }).describe("Code review tool response payload.");
129809
+ function hasSamplingCapability(ctx) {
129810
+ return typeof ctx?.createMessage === "function";
129811
+ }
129812
+ async function codeReviewToolLogic(input, appContext, sdkContext) {
129813
+ logger.debug("Processing code review with sampling.", {
129814
+ ...appContext,
129815
+ toolInput: { ...input, code: `${input.code.substring(0, 100)}...` }
129816
+ });
129817
+ if (!hasSamplingCapability(sdkContext)) {
129818
+ throw new McpError(-32600 /* InvalidRequest */, "Sampling capability is not available. The client does not support MCP sampling.", { requestId: appContext.requestId, operation: "codeReview.sample" });
129819
+ }
129820
+ const focusInstructions = {
129821
+ security: "Focus on security vulnerabilities, input validation, and potential exploits.",
129822
+ performance: "Focus on performance bottlenecks, algorithmic complexity, and optimization opportunities.",
129823
+ style: "Focus on code style, readability, naming conventions, and best practices.",
129824
+ general: "Provide a comprehensive review covering security, performance, and code quality."
129825
+ };
129826
+ const prompt = `You are an expert code reviewer. Please review the following ${input.language || "code"} snippet.
129827
+
129828
+ ${focusInstructions[input.focus]}
129829
+
129830
+ Provide a concise, structured review with:
129831
+ 1. Summary (2-3 sentences)
129832
+ 2. Key findings (bullet points)
129833
+ 3. Recommendations (if applicable)
129834
+
129835
+ Code to review:
129836
+ \`\`\`${input.language || ""}
129837
+ ${input.code}
129838
+ \`\`\`
129839
+
129840
+ Your review:`;
129841
+ logger.debug("Requesting LLM completion via sampling...", {
129842
+ ...appContext,
129843
+ maxTokens: input.maxTokens,
129844
+ focus: input.focus
129845
+ });
129846
+ try {
129847
+ const samplingResult = await sdkContext.createMessage({
129848
+ messages: [
129849
+ {
129850
+ role: "user",
129851
+ content: {
129852
+ type: "text",
129853
+ text: prompt
129854
+ }
129855
+ }
129856
+ ],
129857
+ maxTokens: input.maxTokens,
129858
+ temperature: 0.3,
129859
+ modelPreferences: {
129860
+ hints: [{ name: "claude-3-5-sonnet-20241022" }],
129861
+ intelligencePriority: 0.8,
129862
+ speedPriority: 0.2
129863
+ }
129864
+ });
129865
+ logger.info("Sampling completed successfully.", {
129866
+ ...appContext,
129867
+ model: samplingResult.model,
129868
+ stopReason: samplingResult.stopReason
129869
+ });
129870
+ const response = {
129871
+ code: input.code,
129872
+ language: input.language,
129873
+ focus: input.focus,
129874
+ review: samplingResult.content.text,
129875
+ tokenUsage: {
129876
+ requested: input.maxTokens
129877
+ }
129878
+ };
129879
+ return response;
129880
+ } catch (error2) {
129881
+ logger.error("Sampling request failed.", {
129882
+ ...appContext,
129883
+ error: error2 instanceof Error ? error2.message : String(error2)
129884
+ });
129885
+ throw new McpError(-32603 /* InternalError */, `Failed to complete sampling request: ${error2 instanceof Error ? error2.message : "Unknown error"}`, { requestId: appContext.requestId, operation: "codeReview.sample" });
129886
+ }
129887
+ }
129888
+ function responseFormatter2(result) {
129889
+ return [
129890
+ {
129891
+ type: "text",
129892
+ text: `# Code Review (${result.focus})
129893
+
129894
+ ${result.review}`
129895
+ }
129896
+ ];
129897
+ }
129898
+ var codeReviewSamplingTool = {
129899
+ name: TOOL_NAME2,
129900
+ title: TOOL_TITLE2,
129901
+ description: TOOL_DESCRIPTION2,
129902
+ inputSchema: InputSchema2,
129903
+ outputSchema: OutputSchema3,
129904
+ annotations: TOOL_ANNOTATIONS2,
129905
+ logic: withToolAuth(["tool:code-review:use"], codeReviewToolLogic),
129906
+ responseFormatter: responseFormatter2
129907
+ };
129908
+
129909
+ // src/mcp-server/tools/definitions/template-echo-message.tool.ts
129910
+ var TOOL_NAME3 = "template_echo_message";
129911
+ var TOOL_TITLE3 = "Template Echo Message";
129912
+ var TOOL_DESCRIPTION3 = "Echoes a message back with optional formatting and repetition.";
129913
+ var TOOL_ANNOTATIONS3 = {
129262
129914
  readOnlyHint: true,
129263
129915
  idempotentHint: true,
129264
129916
  openWorldHint: false
@@ -129268,13 +129920,13 @@ var DEFAULT_MODE = "standard";
129268
129920
  var DEFAULT_REPEAT = 1;
129269
129921
  var DEFAULT_INCLUDE_TIMESTAMP = false;
129270
129922
  var TEST_ERROR_TRIGGER_MESSAGE = "TRIGGER_ERROR";
129271
- var InputSchema2 = z.object({
129923
+ var InputSchema3 = z.object({
129272
129924
  message: z.string().min(1, "Message cannot be empty.").max(1000, "Message cannot exceed 1000 characters.").describe(`The message to echo back. To trigger a test error, provide '${TEST_ERROR_TRIGGER_MESSAGE}'.`),
129273
129925
  mode: z.enum(ECHO_MODES).default(DEFAULT_MODE).describe("How to format the message ('standard' | 'uppercase' | 'lowercase')."),
129274
129926
  repeat: z.number().int().min(1).max(5).default(DEFAULT_REPEAT).describe("Number of times to repeat the formatted message."),
129275
129927
  includeTimestamp: z.boolean().default(DEFAULT_INCLUDE_TIMESTAMP).describe("Whether to include an ISO 8601 timestamp in the response.")
129276
129928
  }).describe("Echo a message with optional formatting and repetition.");
129277
- var OutputSchema3 = z.object({
129929
+ var OutputSchema4 = z.object({
129278
129930
  originalMessage: z.string().describe("The original message provided in the input."),
129279
129931
  formattedMessage: z.string().describe("The message after applying the specified formatting."),
129280
129932
  repeatedMessage: z.string().describe("The final message repeated the requested number of times."),
@@ -129308,7 +129960,7 @@ async function echoToolLogic(input, appContext, _sdkContext) {
129308
129960
  };
129309
129961
  return Promise.resolve(response);
129310
129962
  }
129311
- function responseFormatter2(result) {
129963
+ function responseFormatter3(result) {
129312
129964
  const preview = result.repeatedMessage.length > 200 ? `${result.repeatedMessage.slice(0, 197)}…` : result.repeatedMessage;
129313
129965
  const lines = [
129314
129966
  `Echo (mode=${result.mode}, repeat=${result.repeatCount})`,
@@ -129324,14 +129976,14 @@ function responseFormatter2(result) {
129324
129976
  ];
129325
129977
  }
129326
129978
  var echoTool = {
129327
- name: TOOL_NAME2,
129328
- title: TOOL_TITLE2,
129329
- description: TOOL_DESCRIPTION2,
129330
- inputSchema: InputSchema2,
129331
- outputSchema: OutputSchema3,
129332
- annotations: TOOL_ANNOTATIONS2,
129979
+ name: TOOL_NAME3,
129980
+ title: TOOL_TITLE3,
129981
+ description: TOOL_DESCRIPTION3,
129982
+ inputSchema: InputSchema3,
129983
+ outputSchema: OutputSchema4,
129984
+ annotations: TOOL_ANNOTATIONS3,
129333
129985
  logic: withToolAuth(["tool:echo:read"], echoToolLogic),
129334
- responseFormatter: responseFormatter2
129986
+ responseFormatter: responseFormatter3
129335
129987
  };
129336
129988
 
129337
129989
  // src/utils/internal/encoding.ts
@@ -129350,19 +130002,19 @@ function arrayBufferToBase64(buffer) {
129350
130002
  }
129351
130003
 
129352
130004
  // src/mcp-server/tools/definitions/template-image-test.tool.ts
129353
- var TOOL_NAME3 = "template_image_test";
129354
- var TOOL_TITLE3 = "Template Image Test";
129355
- var TOOL_DESCRIPTION3 = "Fetches a random cat image and returns it base64-encoded with the MIME type. Useful for testing image handling.";
129356
- var TOOL_ANNOTATIONS3 = {
130005
+ var TOOL_NAME4 = "template_image_test";
130006
+ var TOOL_TITLE4 = "Template Image Test";
130007
+ var TOOL_DESCRIPTION4 = "Fetches a random cat image and returns it base64-encoded with the MIME type. Useful for testing image handling.";
130008
+ var TOOL_ANNOTATIONS4 = {
129357
130009
  readOnlyHint: true,
129358
130010
  openWorldHint: true
129359
130011
  };
129360
130012
  var CAT_API_URL = "https://cataas.com/cat";
129361
130013
  var API_TIMEOUT_MS = 5000;
129362
- var InputSchema3 = z.object({
130014
+ var InputSchema4 = z.object({
129363
130015
  trigger: z.boolean().optional().default(true).describe("A trigger to invoke the tool and fetch a new cat image.")
129364
130016
  }).describe("Parameters for fetching a random image.");
129365
- var OutputSchema4 = z.object({
130017
+ var OutputSchema5 = z.object({
129366
130018
  data: z.string().describe("Base64 encoded image data."),
129367
130019
  mimeType: z.string().describe("The MIME type of the image (e.g., 'image/jpeg').")
129368
130020
  }).describe("Image tool response payload.");
@@ -129398,7 +130050,7 @@ async function imageTestToolLogic(input, appContext, _sdkContext) {
129398
130050
  });
129399
130051
  return result;
129400
130052
  }
129401
- function responseFormatter3(result) {
130053
+ function responseFormatter4(result) {
129402
130054
  return [
129403
130055
  {
129404
130056
  type: "image",
@@ -129408,31 +130060,31 @@ function responseFormatter3(result) {
129408
130060
  ];
129409
130061
  }
129410
130062
  var imageTestTool = {
129411
- name: TOOL_NAME3,
129412
- title: TOOL_TITLE3,
129413
- description: TOOL_DESCRIPTION3,
129414
- inputSchema: InputSchema3,
129415
- outputSchema: OutputSchema4,
129416
- annotations: TOOL_ANNOTATIONS3,
130063
+ name: TOOL_NAME4,
130064
+ title: TOOL_TITLE4,
130065
+ description: TOOL_DESCRIPTION4,
130066
+ inputSchema: InputSchema4,
130067
+ outputSchema: OutputSchema5,
130068
+ annotations: TOOL_ANNOTATIONS4,
129417
130069
  logic: withToolAuth(["tool:image_test:read"], imageTestToolLogic),
129418
- responseFormatter: responseFormatter3
130070
+ responseFormatter: responseFormatter4
129419
130071
  };
129420
130072
 
129421
130073
  // src/mcp-server/tools/definitions/template-madlibs-elicitation.tool.ts
129422
- var TOOL_NAME4 = "template_madlibs_elicitation";
129423
- var TOOL_TITLE4 = "Mad Libs Elicitation Game";
129424
- 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.";
129425
- var TOOL_ANNOTATIONS4 = {
130074
+ var TOOL_NAME5 = "template_madlibs_elicitation";
130075
+ var TOOL_TITLE5 = "Mad Libs Elicitation Game";
130076
+ var TOOL_DESCRIPTION5 = "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.";
130077
+ var TOOL_ANNOTATIONS5 = {
129426
130078
  readOnlyHint: true,
129427
130079
  idempotentHint: false,
129428
130080
  openWorldHint: false
129429
130081
  };
129430
- var InputSchema4 = z.object({
130082
+ var InputSchema5 = z.object({
129431
130083
  noun: z.string().optional().describe("A noun for the story."),
129432
130084
  verb: z.string().optional().describe("A verb (past tense) for the story."),
129433
130085
  adjective: z.string().optional().describe("An adjective for the story.")
129434
130086
  }).describe("Inputs for the Mad Libs game. Any missing fields will be elicited.");
129435
- var OutputSchema5 = z.object({
130087
+ var OutputSchema6 = z.object({
129436
130088
  story: z.string().describe("The final, generated Mad Libs story."),
129437
130089
  noun: z.string().describe("The noun used in the story."),
129438
130090
  verb: z.string().describe("The verb used in the story."),
@@ -129472,7 +130124,7 @@ async function madlibsToolLogic(input, appContext, sdkContext) {
129472
130124
  };
129473
130125
  return Promise.resolve(response);
129474
130126
  }
129475
- function responseFormatter4(result) {
130127
+ function responseFormatter5(result) {
129476
130128
  return [
129477
130129
  {
129478
130130
  type: "text",
@@ -129489,19 +130141,20 @@ function responseFormatter4(result) {
129489
130141
  ];
129490
130142
  }
129491
130143
  var madlibsElicitationTool = {
129492
- name: TOOL_NAME4,
129493
- title: TOOL_TITLE4,
129494
- description: TOOL_DESCRIPTION4,
129495
- inputSchema: InputSchema4,
129496
- outputSchema: OutputSchema5,
129497
- annotations: TOOL_ANNOTATIONS4,
130144
+ name: TOOL_NAME5,
130145
+ title: TOOL_TITLE5,
130146
+ description: TOOL_DESCRIPTION5,
130147
+ inputSchema: InputSchema5,
130148
+ outputSchema: OutputSchema6,
130149
+ annotations: TOOL_ANNOTATIONS5,
129498
130150
  logic: withToolAuth(["tool:madlibs:play"], madlibsToolLogic),
129499
- responseFormatter: responseFormatter4
130151
+ responseFormatter: responseFormatter5
129500
130152
  };
129501
130153
 
129502
130154
  // src/mcp-server/tools/definitions/index.ts
129503
130155
  var allToolDefinitions = [
129504
130156
  catFactTool,
130157
+ codeReviewSamplingTool,
129505
130158
  echoTool,
129506
130159
  imageTestTool,
129507
130160
  madlibsElicitationTool
@@ -129514,7 +130167,7 @@ var defaultResponseFormatter2 = (result) => [
129514
130167
  function createMcpToolHandler({
129515
130168
  toolName,
129516
130169
  logic,
129517
- responseFormatter: responseFormatter5 = defaultResponseFormatter2
130170
+ responseFormatter: responseFormatter6 = defaultResponseFormatter2
129518
130171
  }) {
129519
130172
  return async (input, callContext) => {
129520
130173
  const sdkContext = callContext;
@@ -129531,7 +130184,7 @@ function createMcpToolHandler({
129531
130184
  const result = await measureToolExecution(() => logic(input, appContext, sdkContext), { ...appContext, toolName }, input);
129532
130185
  return {
129533
130186
  structuredContent: result,
129534
- content: responseFormatter5(result)
130187
+ content: responseFormatter6(result)
129535
130188
  };
129536
130189
  } catch (error2) {
129537
130190
  const mcpError = ErrorHandler.handleError(error2, {
@@ -129602,8 +130255,8 @@ class ToolRegistry {
129602
130255
  }
129603
130256
  }
129604
130257
  ToolRegistry = __legacyDecorateClassTS([
129605
- import_tsyringe8.injectable(),
129606
- __legacyDecorateParamTS(0, import_tsyringe8.injectAll(ToolDefinitions)),
130258
+ import_tsyringe10.injectable(),
130259
+ __legacyDecorateParamTS(0, import_tsyringe10.injectAll(ToolDefinitions)),
129607
130260
  __legacyMetadataTS("design:paramtypes", [
129608
130261
  Array
129609
130262
  ])
@@ -129634,18 +130287,25 @@ async function createMcpServerInstance() {
129634
130287
  logging: {},
129635
130288
  resources: { listChanged: true },
129636
130289
  tools: { listChanged: true },
129637
- elicitation: {}
130290
+ elicitation: {},
130291
+ sampling: {},
130292
+ prompts: { listChanged: true },
130293
+ roots: { listChanged: true }
129638
130294
  }
129639
130295
  });
129640
130296
  try {
129641
- logger.debug("Registering resources and tools via registries...", context);
129642
- const toolRegistry = import_tsyringe9.container.resolve(ToolRegistry);
130297
+ logger.debug("Registering all MCP capabilities via registries...", context);
130298
+ const toolRegistry = import_tsyringe11.container.resolve(ToolRegistry);
129643
130299
  await toolRegistry.registerAll(server);
129644
- const resourceRegistry = import_tsyringe9.container.resolve(ResourceRegistry);
130300
+ const resourceRegistry = import_tsyringe11.container.resolve(ResourceRegistry);
129645
130301
  await resourceRegistry.registerAll(server);
129646
- logger.info("Resources and tools registered successfully", context);
130302
+ const promptRegistry = import_tsyringe11.container.resolve(PromptRegistry);
130303
+ promptRegistry.registerAll(server);
130304
+ const rootsRegistry = import_tsyringe11.container.resolve(RootsRegistry);
130305
+ rootsRegistry.registerAll(server);
130306
+ logger.info("All MCP capabilities registered successfully", context);
129647
130307
  } catch (err) {
129648
- logger.error("Failed to register resources/tools", {
130308
+ logger.error("Failed to register MCP capabilities", {
129649
130309
  ...context,
129650
130310
  error: err instanceof Error ? err.message : String(err),
129651
130311
  stack: err instanceof Error ? err.stack : undefined
@@ -129656,7 +130316,7 @@ async function createMcpServerInstance() {
129656
130316
  }
129657
130317
 
129658
130318
  // src/mcp-server/transports/manager.ts
129659
- var import_tsyringe13 = __toESM(require_cjs3(), 1);
130319
+ var import_tsyringe15 = __toESM(require_cjs3(), 1);
129660
130320
 
129661
130321
  // node_modules/hono/dist/http-exception.js
129662
130322
  var HTTPException = class extends Error {
@@ -132324,7 +132984,7 @@ var cors = (options) => {
132324
132984
  import http from "http";
132325
132985
  import { randomUUID } from "node:crypto";
132326
132986
  // src/mcp-server/transports/auth/authFactory.ts
132327
- var import_tsyringe12 = __toESM(require_cjs3(), 1);
132987
+ var import_tsyringe14 = __toESM(require_cjs3(), 1);
132328
132988
 
132329
132989
  // node_modules/jose/dist/webapi/lib/buffer_utils.js
132330
132990
  var encoder = new TextEncoder;
@@ -133842,7 +134502,7 @@ function createRemoteJWKSet(url, options) {
133842
134502
  return remoteJWKSet;
133843
134503
  }
133844
134504
  // src/mcp-server/transports/auth/strategies/jwtStrategy.ts
133845
- var import_tsyringe10 = __toESM(require_cjs3(), 1);
134505
+ var import_tsyringe12 = __toESM(require_cjs3(), 1);
133846
134506
  class JwtStrategy {
133847
134507
  config;
133848
134508
  logger;
@@ -133945,9 +134605,9 @@ class JwtStrategy {
133945
134605
  }
133946
134606
  }
133947
134607
  JwtStrategy = __legacyDecorateClassTS([
133948
- import_tsyringe10.injectable(),
133949
- __legacyDecorateParamTS(0, import_tsyringe10.inject(AppConfig)),
133950
- __legacyDecorateParamTS(1, import_tsyringe10.inject(Logger2)),
134608
+ import_tsyringe12.injectable(),
134609
+ __legacyDecorateParamTS(0, import_tsyringe12.inject(AppConfig)),
134610
+ __legacyDecorateParamTS(1, import_tsyringe12.inject(Logger2)),
133951
134611
  __legacyMetadataTS("design:paramtypes", [
133952
134612
  Object,
133953
134613
  Object
@@ -133955,7 +134615,7 @@ JwtStrategy = __legacyDecorateClassTS([
133955
134615
  ], JwtStrategy);
133956
134616
 
133957
134617
  // src/mcp-server/transports/auth/strategies/oauthStrategy.ts
133958
- var import_tsyringe11 = __toESM(require_cjs3(), 1);
134618
+ var import_tsyringe13 = __toESM(require_cjs3(), 1);
133959
134619
  class OauthStrategy {
133960
134620
  config;
133961
134621
  logger;
@@ -134006,6 +134666,26 @@ class OauthStrategy {
134006
134666
  ...context,
134007
134667
  claims: payload
134008
134668
  });
134669
+ if (this.config.mcpServerResourceIdentifier) {
134670
+ const resourceClaim = payload.resource || payload.aud;
134671
+ const expectedResource = this.config.mcpServerResourceIdentifier;
134672
+ const isResourceValid = Array.isArray(resourceClaim) && resourceClaim.includes(expectedResource) || resourceClaim === expectedResource;
134673
+ if (!isResourceValid) {
134674
+ this.logger.warning("Token resource indicator mismatch. Token was not issued for this MCP server.", {
134675
+ ...context,
134676
+ expected: expectedResource,
134677
+ received: resourceClaim
134678
+ });
134679
+ throw new McpError(-32005 /* Forbidden */, "Token was not issued for this MCP server. Resource indicator mismatch.", {
134680
+ expected: expectedResource,
134681
+ received: resourceClaim
134682
+ });
134683
+ }
134684
+ this.logger.debug("RFC 8707 resource indicator validated successfully.", {
134685
+ ...context,
134686
+ resource: expectedResource
134687
+ });
134688
+ }
134009
134689
  const scopes = typeof payload.scope === "string" ? payload.scope.split(" ") : [];
134010
134690
  if (scopes.length === 0) {
134011
134691
  this.logger.warning("Invalid token: missing or empty 'scope' claim.", context);
@@ -134052,9 +134732,9 @@ class OauthStrategy {
134052
134732
  }
134053
134733
  }
134054
134734
  OauthStrategy = __legacyDecorateClassTS([
134055
- import_tsyringe11.injectable(),
134056
- __legacyDecorateParamTS(0, import_tsyringe11.inject(AppConfig)),
134057
- __legacyDecorateParamTS(1, import_tsyringe11.inject(Logger2)),
134735
+ import_tsyringe13.injectable(),
134736
+ __legacyDecorateParamTS(0, import_tsyringe13.inject(AppConfig)),
134737
+ __legacyDecorateParamTS(1, import_tsyringe13.inject(Logger2)),
134058
134738
  __legacyMetadataTS("design:paramtypes", [
134059
134739
  Object,
134060
134740
  Object
@@ -134062,8 +134742,8 @@ OauthStrategy = __legacyDecorateClassTS([
134062
134742
  ], OauthStrategy);
134063
134743
 
134064
134744
  // src/mcp-server/transports/auth/authFactory.ts
134065
- import_tsyringe12.container.register(JwtStrategy, { useClass: JwtStrategy });
134066
- import_tsyringe12.container.register(OauthStrategy, { useClass: OauthStrategy });
134745
+ import_tsyringe14.container.register(JwtStrategy, { useClass: JwtStrategy });
134746
+ import_tsyringe14.container.register(OauthStrategy, { useClass: OauthStrategy });
134067
134747
  function createAuthStrategy() {
134068
134748
  const context = requestContextService.createRequestContext({
134069
134749
  operation: "createAuthStrategy",
@@ -134073,10 +134753,10 @@ function createAuthStrategy() {
134073
134753
  switch (config.mcpAuthMode) {
134074
134754
  case "jwt":
134075
134755
  logger.debug("Resolving JWT strategy from container.", context);
134076
- return import_tsyringe12.container.resolve(JwtStrategy);
134756
+ return import_tsyringe14.container.resolve(JwtStrategy);
134077
134757
  case "oauth":
134078
134758
  logger.debug("Resolving OAuth strategy from container.", context);
134079
- return import_tsyringe12.container.resolve(OauthStrategy);
134759
+ return import_tsyringe14.container.resolve(OauthStrategy);
134080
134760
  case "none":
134081
134761
  logger.info("Authentication is disabled ('none' mode).", context);
134082
134762
  return null;
@@ -134245,6 +134925,18 @@ function createHttpApp(mcpServer, parentContext) {
134245
134925
  }));
134246
134926
  app.onError(httpErrorHandler);
134247
134927
  app.get("/healthz", (c) => c.json({ status: "ok" }));
134928
+ app.get("/.well-known/oauth-protected-resource", (c) => {
134929
+ if (!config.oauthIssuerUrl) {
134930
+ return c.json({ error: "OAuth not configured on this server" }, { status: 404 });
134931
+ }
134932
+ return c.json({
134933
+ resource: config.mcpServerResourceIdentifier || config.oauthAudience,
134934
+ authorization_servers: [config.oauthIssuerUrl],
134935
+ bearer_methods_supported: ["header"],
134936
+ resource_signing_alg_values_supported: ["RS256", "ES256", "PS256"],
134937
+ ...config.oauthJwksUri && { jwks_uri: config.oauthJwksUri }
134938
+ });
134939
+ });
134248
134940
  app.get(config.mcpHttpEndpointPath, (c) => {
134249
134941
  return c.json({
134250
134942
  status: "ok",
@@ -134267,11 +134959,21 @@ function createHttpApp(mcpServer, parentContext) {
134267
134959
  logger.info("Authentication is disabled; MCP endpoint is unprotected.", transportContext);
134268
134960
  }
134269
134961
  app.all(config.mcpHttpEndpointPath, async (c) => {
134962
+ const protocolVersion = c.req.header("mcp-protocol-version") ?? "2025-03-26";
134270
134963
  logger.debug("Handling MCP request.", {
134271
134964
  ...transportContext,
134272
134965
  path: c.req.path,
134273
- method: c.req.method
134966
+ method: c.req.method,
134967
+ protocolVersion
134274
134968
  });
134969
+ const supportedVersions = ["2025-03-26", "2025-06-18"];
134970
+ if (!supportedVersions.includes(protocolVersion)) {
134971
+ logger.warning("Unsupported MCP protocol version requested.", {
134972
+ ...transportContext,
134973
+ protocolVersion,
134974
+ supportedVersions
134975
+ });
134976
+ }
134275
134977
  const sessionId = c.req.header("mcp-session-id") ?? randomUUID();
134276
134978
  const transport = new McpSessionTransport(sessionId);
134277
134979
  const handleRpc = async () => {
@@ -134289,7 +134991,13 @@ function createHttpApp(mcpServer, parentContext) {
134289
134991
  }
134290
134992
  return await handleRpc();
134291
134993
  } catch (err) {
134292
- await transport.close?.();
134994
+ await transport.close?.().catch((closeErr) => {
134995
+ logger.warning("Failed to close transport after error", {
134996
+ ...transportContext,
134997
+ sessionId,
134998
+ error: closeErr instanceof Error ? closeErr.message : String(closeErr)
134999
+ });
135000
+ });
134293
135001
  throw err instanceof Error ? err : new Error(String(err));
134294
135002
  }
134295
135003
  });
@@ -134488,7 +135196,7 @@ async function startStdioTransport(server, parentContext) {
134488
135196
  logger.info("MCP Server connected and listening via stdio transport.", operationContext);
134489
135197
  logStartupBanner(`
134490
135198
  \uD83D\uDE80 MCP Server running in STDIO mode.
134491
- (MCP Spec: 2025-03-26 Stdio Transport)
135199
+ (MCP Spec: 2025-06-18 Stdio Transport)
134492
135200
  `);
134493
135201
  return server;
134494
135202
  } catch (err) {
@@ -134562,10 +135270,10 @@ class TransportManager {
134562
135270
  }
134563
135271
  }
134564
135272
  TransportManager = __legacyDecorateClassTS([
134565
- import_tsyringe13.injectable(),
134566
- __legacyDecorateParamTS(0, import_tsyringe13.inject(AppConfig)),
134567
- __legacyDecorateParamTS(1, import_tsyringe13.inject(Logger2)),
134568
- __legacyDecorateParamTS(2, import_tsyringe13.inject(CreateMcpServerInstance)),
135273
+ import_tsyringe15.injectable(),
135274
+ __legacyDecorateParamTS(0, import_tsyringe15.inject(AppConfig)),
135275
+ __legacyDecorateParamTS(1, import_tsyringe15.inject(Logger2)),
135276
+ __legacyDecorateParamTS(2, import_tsyringe15.inject(CreateMcpServerInstance)),
134569
135277
  __legacyMetadataTS("design:paramtypes", [
134570
135278
  typeof AppConfigType === "undefined" ? Object : AppConfigType,
134571
135279
  Object,
@@ -134575,14 +135283,14 @@ TransportManager = __legacyDecorateClassTS([
134575
135283
 
134576
135284
  // src/container/registrations/mcp.ts
134577
135285
  var registerMcpServices = () => {
134578
- import_tsyringe14.container.registerSingleton(ToolRegistry);
134579
- import_tsyringe14.container.registerSingleton(ResourceRegistry);
134580
- registerTools(import_tsyringe14.container);
134581
- registerResources(import_tsyringe14.container);
134582
- import_tsyringe14.container.register(CreateMcpServerInstance, {
135286
+ import_tsyringe16.container.registerSingleton(ToolRegistry);
135287
+ import_tsyringe16.container.registerSingleton(ResourceRegistry);
135288
+ registerTools(import_tsyringe16.container);
135289
+ registerResources(import_tsyringe16.container);
135290
+ import_tsyringe16.container.register(CreateMcpServerInstance, {
134583
135291
  useValue: createMcpServerInstance
134584
135292
  });
134585
- import_tsyringe14.container.registerSingleton(TransportManagerToken, TransportManager);
135293
+ import_tsyringe16.container.registerSingleton(TransportManagerToken, TransportManager);
134586
135294
  logger.info("MCP services and factories registered with the DI container.");
134587
135295
  };
134588
135296
 
@@ -134596,7 +135304,7 @@ function composeContainer() {
134596
135304
  registerMcpServices();
134597
135305
  isContainerComposed = true;
134598
135306
  }
134599
- var container_default = import_tsyringe15.container;
135307
+ var container_default = import_tsyringe17.container;
134600
135308
 
134601
135309
  // src/index.ts
134602
135310
  var config2;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-ts-template",
3
- "version": "2.2.3",
3
+ "version": "2.2.5",
4
4
  "mcpName": "io.github.cyanheads/mcp-ts-template",
5
5
  "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.",
6
6
  "main": "dist/index.js",