@frontmcp/sdk 1.9.0 → 1.9.1-rc.1

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 (40) hide show
  1. package/auth/flows/oauth.auth-ui.flow.d.ts +1 -1
  2. package/auth/flows/oauth.authorize.flow.d.ts +3 -3
  3. package/auth/flows/oauth.callback.flow.d.ts +2 -2
  4. package/auth/flows/oauth.connect.flow.d.ts +1 -1
  5. package/auth/flows/oauth.provider-callback.flow.d.ts +2 -2
  6. package/auth/flows/oauth.register.flow.d.ts +1 -1
  7. package/auth/flows/oauth.token.flow.d.ts +1 -1
  8. package/auth/flows/oauth.userinfo.flow.d.ts +1 -1
  9. package/auth/flows/well-known.jwks.flow.d.ts +3 -3
  10. package/auth/flows/well-known.oauth-authorization-server.flow.d.ts +2 -2
  11. package/auth/flows/well-known.prm.flow.d.ts +1 -1
  12. package/browser/index.cjs +77 -20
  13. package/browser/index.mjs +94 -38
  14. package/common/metadata/app.metadata.d.ts +2 -2
  15. package/common/metadata/front-mcp.metadata.d.ts +64 -64
  16. package/common/metadata/front-mcp.metadata.d.ts.map +1 -1
  17. package/common/metadata/output-policy.d.ts +1 -1
  18. package/common/metadata/tool.metadata.d.ts +1 -1
  19. package/common/schemas/http-output.schema.d.ts +24 -24
  20. package/common/types/options/skills-http/schema.d.ts +8 -8
  21. package/errors/index.d.ts +1 -1
  22. package/errors/index.d.ts.map +1 -1
  23. package/errors/registry.errors.d.ts +7 -0
  24. package/errors/registry.errors.d.ts.map +1 -1
  25. package/esm/index.mjs +94 -38
  26. package/front-mcp/static-startup.check.d.ts.map +1 -1
  27. package/index.d.ts +4 -4
  28. package/index.js +77 -20
  29. package/package.json +11 -11
  30. package/scope/scope.instance.d.ts.map +1 -1
  31. package/scope/server-entries.helper.d.ts +8 -0
  32. package/scope/server-entries.helper.d.ts.map +1 -0
  33. package/skill/cache/skill-http-cache.holder.d.ts.map +1 -1
  34. package/skill/flows/http/llm-full-txt.flow.d.ts +1 -1
  35. package/skill/flows/http/llm-txt.flow.d.ts +1 -1
  36. package/skill/flows/http/skills-api.flow.d.ts +1 -1
  37. package/tool/flows/call-tool.flow.d.ts.map +1 -1
  38. package/transport/flows/handle.streamable-http.flow.d.ts +1 -1
  39. package/utils/lineage.utils.d.ts +7 -0
  40. package/utils/lineage.utils.d.ts.map +1 -1
package/esm/index.mjs CHANGED
@@ -5423,7 +5423,7 @@ var init_normalization_errors = __esm({
5423
5423
  });
5424
5424
 
5425
5425
  // libs/sdk/src/errors/registry.errors.ts
5426
- var RegistryDefinitionNotFoundError, RegistryGraphEntryNotFoundError, RegistryDependencyNotRegisteredError, InvalidRegistryKindError, NameDisambiguationError, EntryValidationError, ToolNameConflictError, FlowNotRegisteredError, UnsupportedHookOwnerKindError;
5426
+ var RegistryDefinitionNotFoundError, RegistryGraphEntryNotFoundError, RegistryDependencyNotRegisteredError, InvalidRegistryKindError, NameDisambiguationError, EntryValidationError, ToolNameConflictError, ReservedAppIdError, FlowNotRegisteredError, UnsupportedHookOwnerKindError;
5427
5427
  var init_registry_errors = __esm({
5428
5428
  "libs/sdk/src/errors/registry.errors.ts"() {
5429
5429
  "use strict";
@@ -5466,6 +5466,14 @@ var init_registry_errors = __esm({
5466
5466
  super(`A tool named "${toolName}" is already registered`, "TOOL_NAME_CONFLICT");
5467
5467
  }
5468
5468
  };
5469
+ ReservedAppIdError = class extends InternalMcpError {
5470
+ constructor(appId) {
5471
+ super(
5472
+ `App id "${appId}" is reserved while @FrontMcp declares tools or resources: a server-level entry named like an app's is listed as "${appId}:<name>". Give the app another id.`,
5473
+ "RESERVED_APP_ID"
5474
+ );
5475
+ }
5476
+ };
5469
5477
  FlowNotRegisteredError = class extends InternalMcpError {
5470
5478
  constructor(flowName) {
5471
5479
  super(`Flow "${flowName}" is not registered`, "FLOW_NOT_REGISTERED");
@@ -6115,6 +6123,7 @@ __export(errors_exports, {
6115
6123
  RemoteUnsupportedTransportError: () => RemoteUnsupportedTransportError,
6116
6124
  RequestContextNotAvailableError: () => RequestContextNotAvailableError,
6117
6125
  RequiredConfigUndefinedError: () => RequiredConfigUndefinedError,
6126
+ ReservedAppIdError: () => ReservedAppIdError,
6118
6127
  ResourceNotFoundError: () => ResourceNotFoundError,
6119
6128
  ResourceReadError: () => ResourceReadError,
6120
6129
  SamplingNotAvailableError: () => SamplingNotAvailableError,
@@ -13944,7 +13953,19 @@ var init_flow_context_providers = __esm({
13944
13953
  }
13945
13954
  });
13946
13955
 
13956
+ // libs/sdk/src/front-mcp/front-mcp.tokens.ts
13957
+ var FrontMcpConfig;
13958
+ var init_front_mcp_tokens2 = __esm({
13959
+ "libs/sdk/src/front-mcp/front-mcp.tokens.ts"() {
13960
+ "use strict";
13961
+ FrontMcpConfig = /* @__PURE__ */ Symbol("FrontMcpConfig");
13962
+ }
13963
+ });
13964
+
13947
13965
  // libs/sdk/src/utils/lineage.utils.ts
13966
+ function isServerEntryOwner(owner) {
13967
+ return owner?.kind === serverEntryOwner.kind && owner.id === serverEntryOwner.id && owner.ref === serverEntryOwner.ref;
13968
+ }
13948
13969
  function ownerKeyOf(lineage) {
13949
13970
  return lineage.map((o) => `${o.kind}:${o.id}`).join("/");
13950
13971
  }
@@ -13959,10 +13980,12 @@ function appOwnerIdOf(lineage, owner) {
13959
13980
  const owners2 = owner ? [...lineage, owner] : lineage;
13960
13981
  return owners2.find((candidate) => candidate.kind === "app")?.id;
13961
13982
  }
13962
- var EntryLineageIndex;
13983
+ var serverEntryOwner, EntryLineageIndex;
13963
13984
  var init_lineage_utils = __esm({
13964
13985
  "libs/sdk/src/utils/lineage.utils.ts"() {
13965
13986
  "use strict";
13987
+ init_front_mcp_tokens2();
13988
+ serverEntryOwner = { kind: "scope", id: "server", ref: FrontMcpConfig };
13966
13989
  EntryLineageIndex = class {
13967
13990
  lineages = /* @__PURE__ */ new Map();
13968
13991
  /** Replace the index with these rows; an entry held by several rows keeps the first row's lineage. */
@@ -14411,9 +14434,9 @@ var init_call_tool_flow = __esm({
14411
14434
  "checkToolAuthorization: incrementalAuth enabled but the verified token carries no authorized_apps claim \u2014 failing closed"
14412
14435
  );
14413
14436
  }
14414
- const appId = tool?.owner?.id;
14437
+ const appId = isServerEntryOwner(tool?.owner) ? void 0 : tool?.owner?.id;
14415
14438
  if (!appId) {
14416
- this.logger.verbose("checkToolAuthorization:skip (no owner)");
14439
+ this.logger.verbose("checkToolAuthorization:skip (no app owner)");
14417
14440
  return;
14418
14441
  }
14419
14442
  const isAppAuthorized = (claimAuthorizedApps?.has(appId) ?? false) || (legacyAuthorization?.authorizedAppIds?.includes(appId) ?? false) || appId in (legacyAuthorization?.authorizedApps || {});
@@ -16523,15 +16546,6 @@ var init_instance_file_logger = __esm({
16523
16546
  }
16524
16547
  });
16525
16548
 
16526
- // libs/sdk/src/front-mcp/front-mcp.tokens.ts
16527
- var FrontMcpConfig;
16528
- var init_front_mcp_tokens2 = __esm({
16529
- "libs/sdk/src/front-mcp/front-mcp.tokens.ts"() {
16530
- "use strict";
16531
- FrontMcpConfig = /* @__PURE__ */ Symbol("FrontMcpConfig");
16532
- }
16533
- });
16534
-
16535
16549
  // libs/sdk/src/regsitry/registry.base.ts
16536
16550
  var RegistryAbstract;
16537
16551
  var init_registry_base = __esm({
@@ -53967,7 +53981,7 @@ var init_skill_http_cache = __esm({
53967
53981
  });
53968
53982
 
53969
53983
  // libs/sdk/src/skill/cache/skill-http-cache.factory.ts
53970
- import { attachRedisErrorListener as attachRedisErrorListener3 } from "@frontmcp/utils";
53984
+ import { attachRedisErrorListener as attachRedisErrorListener3, getEnv as getEnv11, StorageConfigError } from "@frontmcp/utils";
53971
53985
  function hasRedisProvider(redis) {
53972
53986
  if (!redis?.provider) return false;
53973
53987
  const provider = redis.provider;
@@ -53995,9 +54009,15 @@ async function createSkillHttpCache(options = {}) {
53995
54009
  async function createRedisCache(redis, keyPrefix, ttlMs, logger) {
53996
54010
  const provider = redis.provider;
53997
54011
  if (provider === "vercel-kv" || provider === "@vercel/kv") {
53998
- const { kv } = __require("@vercel/kv");
54012
+ const url = getEnv11("KV_REST_API_URL");
54013
+ const token = getEnv11("KV_REST_API_TOKEN");
54014
+ if (!url || !token) {
54015
+ throw new StorageConfigError("vercel-kv", "KV_REST_API_URL and KV_REST_API_TOKEN must be set.");
54016
+ }
54017
+ const { createClient } = __require("@vercel/kv");
54018
+ const client2 = createClient({ url, token, automaticDeserialization: false });
53999
54019
  return new RedisSkillHttpCache({
54000
- getClient: async () => kv,
54020
+ getClient: async () => client2,
54001
54021
  keyPrefix,
54002
54022
  ttlMs
54003
54023
  });
@@ -54043,7 +54063,7 @@ async function getSkillHttpCache(scope) {
54043
54063
  if (pending) {
54044
54064
  return pending;
54045
54065
  }
54046
- const creationPromise = createCacheForScope(scopeId, cacheConfig);
54066
+ const creationPromise = createCacheForScope(scopeId, cacheConfig, scope.logger);
54047
54067
  pendingCreation.set(scopeId, creationPromise);
54048
54068
  try {
54049
54069
  const cache2 = await creationPromise;
@@ -54053,12 +54073,13 @@ async function getSkillHttpCache(scope) {
54053
54073
  pendingCreation.delete(scopeId);
54054
54074
  }
54055
54075
  }
54056
- async function createCacheForScope(scopeId, cacheConfig) {
54076
+ async function createCacheForScope(scopeId, cacheConfig, logger) {
54057
54077
  try {
54058
54078
  const { cache: cache2 } = await createSkillHttpCache({
54059
54079
  redis: cacheConfig.redis,
54060
54080
  ttlMs: cacheConfig.ttlMs,
54061
- keyPrefix: cacheConfig.keyPrefix ?? `frontmcp:skills:${scopeId}:cache:`
54081
+ keyPrefix: cacheConfig.keyPrefix ?? `frontmcp:skills:${scopeId}:cache:`,
54082
+ logger
54062
54083
  });
54063
54084
  return cache2;
54064
54085
  } catch (error) {
@@ -59712,7 +59733,7 @@ var init_handle_mcp_20260728_flow = __esm({
59712
59733
  });
59713
59734
 
59714
59735
  // libs/sdk/src/ha/ha-headers.ts
59715
- import { buildSetCookie, getEnv as getEnv11, getMachineId as getMachineId3, getRuntimeContext as getRuntimeContext13 } from "@frontmcp/utils";
59736
+ import { buildSetCookie, getEnv as getEnv12, getMachineId as getMachineId3, getRuntimeContext as getRuntimeContext13 } from "@frontmcp/utils";
59716
59737
  function machineIdHeader() {
59717
59738
  if (getRuntimeContext13().deployment !== "distributed") return void 0;
59718
59739
  return [DEFAULT_FRONTMCP_MACHINE_ID_HEADER, getMachineId3()];
@@ -59724,9 +59745,9 @@ function applyMachineIdHeader(response) {
59724
59745
  return true;
59725
59746
  }
59726
59747
  function affinityCookieOptions() {
59727
- const name44 = getEnv11("FRONTMCP_AFFINITY_COOKIE")?.trim() || DEFAULT_FRONTMCP_NODE_COOKIE;
59728
- const domain = getEnv11("FRONTMCP_AFFINITY_COOKIE_DOMAIN")?.trim() || void 0;
59729
- const rawSameSite = getEnv11("FRONTMCP_AFFINITY_COOKIE_SAMESITE")?.trim().toLowerCase();
59748
+ const name44 = getEnv12("FRONTMCP_AFFINITY_COOKIE")?.trim() || DEFAULT_FRONTMCP_NODE_COOKIE;
59749
+ const domain = getEnv12("FRONTMCP_AFFINITY_COOKIE_DOMAIN")?.trim() || void 0;
59750
+ const rawSameSite = getEnv12("FRONTMCP_AFFINITY_COOKIE_SAMESITE")?.trim().toLowerCase();
59730
59751
  const sameSite = rawSameSite === "strict" ? "Strict" : rawSameSite === "lax" ? "Lax" : rawSameSite === "none" ? "None" : void 0;
59731
59752
  return { name: name44, ...domain ? { domain } : {}, ...sameSite ? { sameSite } : {} };
59732
59753
  }
@@ -64494,6 +64515,29 @@ var init_http_request_flow = __esm({
64494
64515
  }
64495
64516
  });
64496
64517
 
64518
+ // libs/sdk/src/scope/server-entries.helper.ts
64519
+ async function registerServerEntries(providers, entries, appIds) {
64520
+ if (!entries.tools?.length && !entries.resources?.length) return;
64521
+ if (appIds.includes(serverEntryOwner.id)) throw new ReservedAppIdError(serverEntryOwner.id);
64522
+ const registries = [];
64523
+ if (entries.tools?.length) {
64524
+ registries.push(new ToolRegistry(providers, entries.tools, serverEntryOwner, { adopt: false }).ready);
64525
+ }
64526
+ if (entries.resources?.length) {
64527
+ registries.push(new ResourceRegistry(providers, entries.resources, serverEntryOwner, { adopt: false }).ready);
64528
+ }
64529
+ await Promise.all(registries);
64530
+ }
64531
+ var init_server_entries_helper = __esm({
64532
+ "libs/sdk/src/scope/server-entries.helper.ts"() {
64533
+ "use strict";
64534
+ init_errors();
64535
+ init_resource_registry();
64536
+ init_tool_registry();
64537
+ init_lineage_utils();
64538
+ }
64539
+ });
64540
+
64497
64541
  // libs/sdk/src/scope/scope.instance.ts
64498
64542
  import "reflect-metadata";
64499
64543
  import {
@@ -64560,6 +64604,7 @@ var init_scope_instance = __esm({
64560
64604
  init_http_ip_filter_flow();
64561
64605
  init_http_request_flow();
64562
64606
  init_optional_dependency_util();
64607
+ init_server_entries_helper();
64563
64608
  SCOPE_NON_DISPATCHABLE_FLOWS = /* @__PURE__ */ new Set(["http:request"]);
64564
64609
  Scope = class _Scope extends ScopeEntry {
64565
64610
  id;
@@ -64991,6 +65036,11 @@ var init_scope_instance = __esm({
64991
65036
  await scopeAdapters.ready;
64992
65037
  this.onDispose(() => scopeAdapters.dispose());
64993
65038
  }
65039
+ await registerServerEntries(
65040
+ this.scopeProviders,
65041
+ this.metadata,
65042
+ this.scopeApps.getApps().map((app) => app.id)
65043
+ );
64994
65044
  this.initAuthoritiesFromConfig();
64995
65045
  this.scopeTools = new ToolRegistry(this.scopeProviders, [], scopeRef);
64996
65046
  this.scopeResources = new ResourceRegistry(this.scopeProviders, [], scopeRef);
@@ -67174,9 +67224,9 @@ var init_ipc_server_transport = __esm({
67174
67224
  });
67175
67225
 
67176
67226
  // libs/sdk/src/server/middleware/cors-env.ts
67177
- import { getEnv as getEnv12 } from "@frontmcp/utils";
67227
+ import { getEnv as getEnv13 } from "@frontmcp/utils";
67178
67228
  function readCorsFromEnv() {
67179
- const rawOrigins = getEnv12("FRONTMCP_CORS_ORIGINS");
67229
+ const rawOrigins = getEnv13("FRONTMCP_CORS_ORIGINS");
67180
67230
  if (!rawOrigins) return void 0;
67181
67231
  let origins;
67182
67232
  try {
@@ -67187,9 +67237,9 @@ function readCorsFromEnv() {
67187
67237
  const list = (Array.isArray(origins) ? origins : [origins]).filter((origin) => typeof origin === "string").map((origin) => origin.trim()).filter((origin) => origin.length > 0);
67188
67238
  if (list.length === 0) return void 0;
67189
67239
  const cors2 = { origin: list.includes("*") ? true : list };
67190
- const credentials = getEnv12("FRONTMCP_CORS_CREDENTIALS");
67240
+ const credentials = getEnv13("FRONTMCP_CORS_CREDENTIALS");
67191
67241
  if (credentials !== void 0) cors2.credentials = credentials === "true" || credentials === "1";
67192
- const maxAge = Number(getEnv12("FRONTMCP_CORS_MAX_AGE"));
67242
+ const maxAge = Number(getEnv13("FRONTMCP_CORS_MAX_AGE"));
67193
67243
  if (Number.isFinite(maxAge) && maxAge > 0) cors2.maxAge = maxAge;
67194
67244
  return cors2;
67195
67245
  }
@@ -67203,10 +67253,10 @@ var init_cors_env = __esm({
67203
67253
  });
67204
67254
 
67205
67255
  // libs/sdk/src/server/middleware/csp.middleware.ts
67206
- import { getEnv as getEnv13, getEnvFlag as getEnvFlag2 } from "@frontmcp/utils";
67256
+ import { getEnv as getEnv14, getEnvFlag as getEnvFlag2 } from "@frontmcp/utils";
67207
67257
  function readCspFromEnv() {
67208
67258
  if (!getEnvFlag2("FRONTMCP_CSP_ENABLED")) return void 0;
67209
- const rawDirectives = getEnv13("FRONTMCP_CSP_DIRECTIVES") ?? "";
67259
+ const rawDirectives = getEnv14("FRONTMCP_CSP_DIRECTIVES") ?? "";
67210
67260
  const directives = {};
67211
67261
  for (const part of rawDirectives.split(";")) {
67212
67262
  const trimmed = part.trim();
@@ -67221,7 +67271,7 @@ function readCspFromEnv() {
67221
67271
  return {
67222
67272
  enabled: true,
67223
67273
  directives,
67224
- reportUri: getEnv13("FRONTMCP_CSP_REPORT_URI"),
67274
+ reportUri: getEnv14("FRONTMCP_CSP_REPORT_URI"),
67225
67275
  reportOnly: getEnvFlag2("FRONTMCP_CSP_REPORT_ONLY")
67226
67276
  };
67227
67277
  }
@@ -67247,12 +67297,12 @@ function readSecurityHeadersFromEnv() {
67247
67297
  };
67248
67298
  }
67249
67299
  function readHeaderEnv(name44, fallback) {
67250
- const value = getEnv13(name44);
67300
+ const value = getEnv14(name44);
67251
67301
  if (value === void 0 || value === "") return fallback;
67252
67302
  return ["off", "false", "none"].includes(value.trim().toLowerCase()) ? void 0 : value;
67253
67303
  }
67254
67304
  function readCustomHeadersFromEnv() {
67255
- const raw = getEnv13("FRONTMCP_HEADERS_CUSTOM");
67305
+ const raw = getEnv14("FRONTMCP_HEADERS_CUSTOM");
67256
67306
  if (!raw) return void 0;
67257
67307
  try {
67258
67308
  const parsed = JSON.parse(raw);
@@ -68432,7 +68482,7 @@ var init_server_instance = __esm({
68432
68482
 
68433
68483
  // libs/sdk/src/front-mcp/front-mcp.providers.ts
68434
68484
  import { ProviderScope as ProviderScope9 } from "@frontmcp/di";
68435
- import { getEnv as getEnv14 } from "@frontmcp/utils";
68485
+ import { getEnv as getEnv15 } from "@frontmcp/utils";
68436
68486
  function createMcpGlobalProviders(metadata) {
68437
68487
  const isCli = !!metadata["__cliMode"];
68438
68488
  const isNoServe = metadata.serve === false;
@@ -68456,7 +68506,7 @@ var init_front_mcp_providers = __esm({
68456
68506
  useValue: metadata
68457
68507
  })
68458
68508
  };
68459
- defaultHttpOptions = () => ({ port: Number(getEnv14("PORT")) || 3e3, entryPath: "/mcp", bodyLimit: "4mb" });
68509
+ defaultHttpOptions = () => ({ port: Number(getEnv15("PORT")) || 3e3, entryPath: "/mcp", bodyLimit: "4mb" });
68460
68510
  frontMcpServer = AsyncProvider({
68461
68511
  name: "frontmcp:server",
68462
68512
  scope: ProviderScope9.GLOBAL,
@@ -68549,9 +68599,10 @@ function collectStaticEntries(config) {
68549
68599
  };
68550
68600
  const visitEntries = (owner, app) => {
68551
68601
  const reach = app ? { app } : "every-plugin";
68602
+ const toolReach = app ? { app } : "every-app";
68552
68603
  for (const item of owner.tools ?? []) {
68553
68604
  const record = tryNormalize(() => normalizeTool(item));
68554
- if (record) add(`Tool "${record.metadata.id || record.metadata.name}"`, record.metadata, reach);
68605
+ if (record) add(`Tool "${record.metadata.id || record.metadata.name}"`, record.metadata, toolReach);
68555
68606
  }
68556
68607
  for (const item of owner.resources ?? []) {
68557
68608
  if (tryNormalize(() => isResourceTemplate(item))) {
@@ -68584,7 +68635,10 @@ function collectStaticEntries(config) {
68584
68635
  visitPlugins(owner.plugins, app);
68585
68636
  };
68586
68637
  const server = config;
68587
- visitEntries({ skills: server.skills, plugins: server.plugins }, void 0);
68638
+ visitEntries(
68639
+ { tools: server.tools, resources: server.resources, skills: server.skills, plugins: server.plugins },
68640
+ void 0
68641
+ );
68588
68642
  for (const app of server.apps ?? []) {
68589
68643
  const record = tryNormalize(() => normalizeApp(app));
68590
68644
  if (record?.kind === "LOCAL_CLASS" /* LOCAL_CLASS */) visitEntries(record.metadata, record);
@@ -68592,6 +68646,7 @@ function collectStaticEntries(config) {
68592
68646
  const everyPluginKeys = /* @__PURE__ */ new Set([...everyAppKeys, ...[...ownAppKeys.values()].flatMap((keys) => [...keys])]);
68593
68647
  const keysReaching = (reach) => {
68594
68648
  if (reach === "every-plugin") return everyPluginKeys;
68649
+ if (reach === "every-app") return everyAppKeys;
68595
68650
  if ("agentKeys" in reach) return reach.agentKeys;
68596
68651
  return /* @__PURE__ */ new Set([...everyAppKeys, ...ownAppKeys.get(reach.app) ?? []]);
68597
68652
  };
@@ -68640,7 +68695,7 @@ var front_mcp_exports = {};
68640
68695
  __export(front_mcp_exports, {
68641
68696
  FrontMcpInstance: () => FrontMcpInstance
68642
68697
  });
68643
- import { fileExists as fileExists7, getEnv as getEnv15, isEdgeRuntime as isEdgeRuntime2, randomUUID as randomUUID29, unlink as unlink2 } from "@frontmcp/utils";
68698
+ import { fileExists as fileExists7, getEnv as getEnv16, isEdgeRuntime as isEdgeRuntime2, randomUUID as randomUUID29, unlink as unlink2 } from "@frontmcp/utils";
68644
68699
  function resolveConfigInput(optionsOrClass) {
68645
68700
  if (typeof optionsOrClass === "function") {
68646
68701
  const stored = getDecoratorConfig(optionsOrClass);
@@ -68708,7 +68763,7 @@ var init_front_mcp = __esm({
68708
68763
  for (const scope of this.getScopes()) {
68709
68764
  await scope.emitServerStarted();
68710
68765
  }
68711
- if (getEnv15("FRONTMCP_DEV_BOOTSTRAP_SENTINEL") === "1") {
68766
+ if (getEnv16("FRONTMCP_DEV_BOOTSTRAP_SENTINEL") === "1") {
68712
68767
  process.stderr.write(`__FRONTMCP_BOOTSTRAP_COMPLETE__ ${JSON.stringify(this.devListenerInfo(server))}
68713
68768
  `);
68714
68769
  }
@@ -71980,6 +72035,7 @@ export {
71980
72035
  RemoveWorkflowTool,
71981
72036
  RequestContextNotAvailableError,
71982
72037
  RequiredConfigUndefinedError,
72038
+ ReservedAppIdError,
71983
72039
  Resource,
71984
72040
  ResourceContext,
71985
72041
  ResourceEntry,
@@ -1 +1 @@
1
- {"version":3,"file":"static-startup.check.d.ts","sourceRoot":"","sources":["../../src/front-mcp/static-startup.check.ts"],"names":[],"mappings":"AA+BA,OAAO,EAGL,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EAGxB,MAAM,WAAW,CAAC;AA0LnB;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,mBAAmB,GAAG,kBAAkB,GAAG,IAAI,CA6BhG"}
1
+ {"version":3,"file":"static-startup.check.d.ts","sourceRoot":"","sources":["../../src/front-mcp/static-startup.check.ts"],"names":[],"mappings":"AA+BA,OAAO,EAGL,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EAGxB,MAAM,WAAW,CAAC;AA+LnB;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,mBAAmB,GAAG,kBAAkB,GAAG,IAAI,CA6BhG"}
package/index.d.ts CHANGED
@@ -50,10 +50,10 @@ export declare const ListToolsHook: {
50
50
  Around: (stage: "parseInput" | "ensureRemoteCapabilities" | "filterByAuthorities" | "resolveConflicts" | "findTools" | "parseTools", opts?: import("./common").HookOptions<import("./tool/flows/tools-list.flow").default>) => MethodDecorator;
51
51
  };
52
52
  export declare const HttpHook: {
53
- Stage: (stage: "metrics" | "finalize" | "acquireQuota" | "acquireSemaphore" | "releaseSemaphore" | "releaseQuota" | "checkIpFilter" | "router" | "traceRequest" | "relayToSessionOwner" | "checkAuthorization" | "acquireIdentityQuota" | "checkPersistentSessionOwner" | "handleMcp2026" | "handleWebFetch" | "handleLegacySse" | "handleSse" | "handleStreamableHttp" | "handleStatefulHttp" | "handleStatelessHttp" | "handleDeleteSession" | "audit", opts?: import("./common").HookOptions<import("./scope/flows/http.request.flow").default>) => MethodDecorator;
54
- Will: (stage: "metrics" | "finalize" | "acquireQuota" | "acquireSemaphore" | "releaseSemaphore" | "releaseQuota" | "checkIpFilter" | "router" | "traceRequest" | "relayToSessionOwner" | "checkAuthorization" | "acquireIdentityQuota" | "checkPersistentSessionOwner" | "handleMcp2026" | "handleWebFetch" | "handleLegacySse" | "handleSse" | "handleStreamableHttp" | "handleStatefulHttp" | "handleStatelessHttp" | "handleDeleteSession" | "audit", opts?: import("./common").HookOptions<import("./scope/flows/http.request.flow").default>) => MethodDecorator;
55
- Did: (stage: "metrics" | "finalize" | "acquireQuota" | "acquireSemaphore" | "releaseSemaphore" | "releaseQuota" | "checkIpFilter" | "router" | "traceRequest" | "relayToSessionOwner" | "checkAuthorization" | "acquireIdentityQuota" | "checkPersistentSessionOwner" | "handleMcp2026" | "handleWebFetch" | "handleLegacySse" | "handleSse" | "handleStreamableHttp" | "handleStatefulHttp" | "handleStatelessHttp" | "handleDeleteSession" | "audit", opts?: import("./common").HookOptions<import("./scope/flows/http.request.flow").default>) => MethodDecorator;
56
- Around: (stage: "metrics" | "finalize" | "acquireQuota" | "acquireSemaphore" | "releaseSemaphore" | "releaseQuota" | "checkIpFilter" | "router" | "traceRequest" | "relayToSessionOwner" | "checkAuthorization" | "acquireIdentityQuota" | "checkPersistentSessionOwner" | "handleMcp2026" | "handleWebFetch" | "handleLegacySse" | "handleSse" | "handleStreamableHttp" | "handleStatefulHttp" | "handleStatelessHttp" | "handleDeleteSession" | "audit", opts?: import("./common").HookOptions<import("./scope/flows/http.request.flow").default>) => MethodDecorator;
53
+ Stage: (stage: "metrics" | "audit" | "finalize" | "acquireQuota" | "acquireSemaphore" | "releaseSemaphore" | "releaseQuota" | "checkIpFilter" | "router" | "traceRequest" | "relayToSessionOwner" | "checkAuthorization" | "acquireIdentityQuota" | "checkPersistentSessionOwner" | "handleMcp2026" | "handleWebFetch" | "handleLegacySse" | "handleSse" | "handleStreamableHttp" | "handleStatefulHttp" | "handleStatelessHttp" | "handleDeleteSession", opts?: import("./common").HookOptions<import("./scope/flows/http.request.flow").default>) => MethodDecorator;
54
+ Will: (stage: "metrics" | "audit" | "finalize" | "acquireQuota" | "acquireSemaphore" | "releaseSemaphore" | "releaseQuota" | "checkIpFilter" | "router" | "traceRequest" | "relayToSessionOwner" | "checkAuthorization" | "acquireIdentityQuota" | "checkPersistentSessionOwner" | "handleMcp2026" | "handleWebFetch" | "handleLegacySse" | "handleSse" | "handleStreamableHttp" | "handleStatefulHttp" | "handleStatelessHttp" | "handleDeleteSession", opts?: import("./common").HookOptions<import("./scope/flows/http.request.flow").default>) => MethodDecorator;
55
+ Did: (stage: "metrics" | "audit" | "finalize" | "acquireQuota" | "acquireSemaphore" | "releaseSemaphore" | "releaseQuota" | "checkIpFilter" | "router" | "traceRequest" | "relayToSessionOwner" | "checkAuthorization" | "acquireIdentityQuota" | "checkPersistentSessionOwner" | "handleMcp2026" | "handleWebFetch" | "handleLegacySse" | "handleSse" | "handleStreamableHttp" | "handleStatefulHttp" | "handleStatelessHttp" | "handleDeleteSession", opts?: import("./common").HookOptions<import("./scope/flows/http.request.flow").default>) => MethodDecorator;
56
+ Around: (stage: "metrics" | "audit" | "finalize" | "acquireQuota" | "acquireSemaphore" | "releaseSemaphore" | "releaseQuota" | "checkIpFilter" | "router" | "traceRequest" | "relayToSessionOwner" | "checkAuthorization" | "acquireIdentityQuota" | "checkPersistentSessionOwner" | "handleMcp2026" | "handleWebFetch" | "handleLegacySse" | "handleSse" | "handleStreamableHttp" | "handleStatefulHttp" | "handleStatelessHttp" | "handleDeleteSession", opts?: import("./common").HookOptions<import("./scope/flows/http.request.flow").default>) => MethodDecorator;
57
57
  };
58
58
  export declare const OAuthAuthorizeHook: {
59
59
  Stage: (stage: "parseInput" | "validateOutput" | "validateInput" | "checkIpFilter" | "checkIfAuthorized" | "prepareAuthorizationRequest" | "buildAuthorizeOutput", opts?: import("./common").HookOptions<import("./auth/flows/oauth.authorize.flow").default>) => MethodDecorator;
package/index.js CHANGED
@@ -5378,7 +5378,7 @@ var init_normalization_errors = __esm({
5378
5378
  });
5379
5379
 
5380
5380
  // libs/sdk/src/errors/registry.errors.ts
5381
- var RegistryDefinitionNotFoundError, RegistryGraphEntryNotFoundError, RegistryDependencyNotRegisteredError, InvalidRegistryKindError, NameDisambiguationError, EntryValidationError, ToolNameConflictError, FlowNotRegisteredError, UnsupportedHookOwnerKindError;
5381
+ var RegistryDefinitionNotFoundError, RegistryGraphEntryNotFoundError, RegistryDependencyNotRegisteredError, InvalidRegistryKindError, NameDisambiguationError, EntryValidationError, ToolNameConflictError, ReservedAppIdError, FlowNotRegisteredError, UnsupportedHookOwnerKindError;
5382
5382
  var init_registry_errors = __esm({
5383
5383
  "libs/sdk/src/errors/registry.errors.ts"() {
5384
5384
  "use strict";
@@ -5421,6 +5421,14 @@ var init_registry_errors = __esm({
5421
5421
  super(`A tool named "${toolName}" is already registered`, "TOOL_NAME_CONFLICT");
5422
5422
  }
5423
5423
  };
5424
+ ReservedAppIdError = class extends InternalMcpError {
5425
+ constructor(appId) {
5426
+ super(
5427
+ `App id "${appId}" is reserved while @FrontMcp declares tools or resources: a server-level entry named like an app's is listed as "${appId}:<name>". Give the app another id.`,
5428
+ "RESERVED_APP_ID"
5429
+ );
5430
+ }
5431
+ };
5424
5432
  FlowNotRegisteredError = class extends InternalMcpError {
5425
5433
  constructor(flowName) {
5426
5434
  super(`Flow "${flowName}" is not registered`, "FLOW_NOT_REGISTERED");
@@ -6051,6 +6059,7 @@ __export(errors_exports, {
6051
6059
  RemoteUnsupportedTransportError: () => RemoteUnsupportedTransportError,
6052
6060
  RequestContextNotAvailableError: () => RequestContextNotAvailableError,
6053
6061
  RequiredConfigUndefinedError: () => RequiredConfigUndefinedError,
6062
+ ReservedAppIdError: () => ReservedAppIdError,
6054
6063
  ResourceNotFoundError: () => ResourceNotFoundError,
6055
6064
  ResourceReadError: () => ResourceReadError,
6056
6065
  SamplingNotAvailableError: () => SamplingNotAvailableError,
@@ -13885,7 +13894,19 @@ var init_flow_context_providers = __esm({
13885
13894
  }
13886
13895
  });
13887
13896
 
13897
+ // libs/sdk/src/front-mcp/front-mcp.tokens.ts
13898
+ var FrontMcpConfig;
13899
+ var init_front_mcp_tokens2 = __esm({
13900
+ "libs/sdk/src/front-mcp/front-mcp.tokens.ts"() {
13901
+ "use strict";
13902
+ FrontMcpConfig = /* @__PURE__ */ Symbol("FrontMcpConfig");
13903
+ }
13904
+ });
13905
+
13888
13906
  // libs/sdk/src/utils/lineage.utils.ts
13907
+ function isServerEntryOwner(owner) {
13908
+ return owner?.kind === serverEntryOwner.kind && owner.id === serverEntryOwner.id && owner.ref === serverEntryOwner.ref;
13909
+ }
13889
13910
  function ownerKeyOf(lineage) {
13890
13911
  return lineage.map((o) => `${o.kind}:${o.id}`).join("/");
13891
13912
  }
@@ -13900,10 +13921,12 @@ function appOwnerIdOf(lineage, owner) {
13900
13921
  const owners2 = owner ? [...lineage, owner] : lineage;
13901
13922
  return owners2.find((candidate) => candidate.kind === "app")?.id;
13902
13923
  }
13903
- var EntryLineageIndex;
13924
+ var serverEntryOwner, EntryLineageIndex;
13904
13925
  var init_lineage_utils = __esm({
13905
13926
  "libs/sdk/src/utils/lineage.utils.ts"() {
13906
13927
  "use strict";
13928
+ init_front_mcp_tokens2();
13929
+ serverEntryOwner = { kind: "scope", id: "server", ref: FrontMcpConfig };
13907
13930
  EntryLineageIndex = class {
13908
13931
  lineages = /* @__PURE__ */ new Map();
13909
13932
  /** Replace the index with these rows; an entry held by several rows keeps the first row's lineage. */
@@ -14341,9 +14364,9 @@ var init_call_tool_flow = __esm({
14341
14364
  "checkToolAuthorization: incrementalAuth enabled but the verified token carries no authorized_apps claim \u2014 failing closed"
14342
14365
  );
14343
14366
  }
14344
- const appId = tool?.owner?.id;
14367
+ const appId = isServerEntryOwner(tool?.owner) ? void 0 : tool?.owner?.id;
14345
14368
  if (!appId) {
14346
- this.logger.verbose("checkToolAuthorization:skip (no owner)");
14369
+ this.logger.verbose("checkToolAuthorization:skip (no app owner)");
14347
14370
  return;
14348
14371
  }
14349
14372
  const isAppAuthorized = (claimAuthorizedApps?.has(appId) ?? false) || (legacyAuthorization?.authorizedAppIds?.includes(appId) ?? false) || appId in (legacyAuthorization?.authorizedApps || {});
@@ -16456,15 +16479,6 @@ var init_instance_file_logger = __esm({
16456
16479
  }
16457
16480
  });
16458
16481
 
16459
- // libs/sdk/src/front-mcp/front-mcp.tokens.ts
16460
- var FrontMcpConfig;
16461
- var init_front_mcp_tokens2 = __esm({
16462
- "libs/sdk/src/front-mcp/front-mcp.tokens.ts"() {
16463
- "use strict";
16464
- FrontMcpConfig = /* @__PURE__ */ Symbol("FrontMcpConfig");
16465
- }
16466
- });
16467
-
16468
16482
  // libs/sdk/src/regsitry/registry.base.ts
16469
16483
  var RegistryAbstract;
16470
16484
  var init_registry_base = __esm({
@@ -53798,9 +53812,15 @@ async function createSkillHttpCache(options = {}) {
53798
53812
  async function createRedisCache(redis, keyPrefix, ttlMs, logger) {
53799
53813
  const provider = redis.provider;
53800
53814
  if (provider === "vercel-kv" || provider === "@vercel/kv") {
53801
- const { kv } = require("@vercel/kv");
53815
+ const url = (0, import_utils97.getEnv)("KV_REST_API_URL");
53816
+ const token = (0, import_utils97.getEnv)("KV_REST_API_TOKEN");
53817
+ if (!url || !token) {
53818
+ throw new import_utils97.StorageConfigError("vercel-kv", "KV_REST_API_URL and KV_REST_API_TOKEN must be set.");
53819
+ }
53820
+ const { createClient } = require("@vercel/kv");
53821
+ const client2 = createClient({ url, token, automaticDeserialization: false });
53802
53822
  return new RedisSkillHttpCache({
53803
- getClient: async () => kv,
53823
+ getClient: async () => client2,
53804
53824
  keyPrefix,
53805
53825
  ttlMs
53806
53826
  });
@@ -53848,7 +53868,7 @@ async function getSkillHttpCache(scope) {
53848
53868
  if (pending) {
53849
53869
  return pending;
53850
53870
  }
53851
- const creationPromise = createCacheForScope(scopeId, cacheConfig);
53871
+ const creationPromise = createCacheForScope(scopeId, cacheConfig, scope.logger);
53852
53872
  pendingCreation.set(scopeId, creationPromise);
53853
53873
  try {
53854
53874
  const cache2 = await creationPromise;
@@ -53858,12 +53878,13 @@ async function getSkillHttpCache(scope) {
53858
53878
  pendingCreation.delete(scopeId);
53859
53879
  }
53860
53880
  }
53861
- async function createCacheForScope(scopeId, cacheConfig) {
53881
+ async function createCacheForScope(scopeId, cacheConfig, logger) {
53862
53882
  try {
53863
53883
  const { cache: cache2 } = await createSkillHttpCache({
53864
53884
  redis: cacheConfig.redis,
53865
53885
  ttlMs: cacheConfig.ttlMs,
53866
- keyPrefix: cacheConfig.keyPrefix ?? `frontmcp:skills:${scopeId}:cache:`
53886
+ keyPrefix: cacheConfig.keyPrefix ?? `frontmcp:skills:${scopeId}:cache:`,
53887
+ logger
53867
53888
  });
53868
53889
  return cache2;
53869
53890
  } catch (error) {
@@ -64291,6 +64312,29 @@ var init_http_request_flow = __esm({
64291
64312
  }
64292
64313
  });
64293
64314
 
64315
+ // libs/sdk/src/scope/server-entries.helper.ts
64316
+ async function registerServerEntries(providers, entries, appIds) {
64317
+ if (!entries.tools?.length && !entries.resources?.length) return;
64318
+ if (appIds.includes(serverEntryOwner.id)) throw new ReservedAppIdError(serverEntryOwner.id);
64319
+ const registries = [];
64320
+ if (entries.tools?.length) {
64321
+ registries.push(new ToolRegistry(providers, entries.tools, serverEntryOwner, { adopt: false }).ready);
64322
+ }
64323
+ if (entries.resources?.length) {
64324
+ registries.push(new ResourceRegistry(providers, entries.resources, serverEntryOwner, { adopt: false }).ready);
64325
+ }
64326
+ await Promise.all(registries);
64327
+ }
64328
+ var init_server_entries_helper = __esm({
64329
+ "libs/sdk/src/scope/server-entries.helper.ts"() {
64330
+ "use strict";
64331
+ init_errors();
64332
+ init_resource_registry();
64333
+ init_tool_registry();
64334
+ init_lineage_utils();
64335
+ }
64336
+ });
64337
+
64294
64338
  // libs/sdk/src/scope/scope.instance.ts
64295
64339
  var import_reflect_metadata25, import_auth60, import_guard9, import_utils115, SCOPE_NON_DISPATCHABLE_FLOWS, Scope;
64296
64340
  var init_scope_instance = __esm({
@@ -64352,6 +64396,7 @@ var init_scope_instance = __esm({
64352
64396
  init_http_ip_filter_flow();
64353
64397
  init_http_request_flow();
64354
64398
  init_optional_dependency_util();
64399
+ init_server_entries_helper();
64355
64400
  SCOPE_NON_DISPATCHABLE_FLOWS = /* @__PURE__ */ new Set(["http:request"]);
64356
64401
  Scope = class _Scope extends ScopeEntry {
64357
64402
  id;
@@ -64783,6 +64828,11 @@ var init_scope_instance = __esm({
64783
64828
  await scopeAdapters.ready;
64784
64829
  this.onDispose(() => scopeAdapters.dispose());
64785
64830
  }
64831
+ await registerServerEntries(
64832
+ this.scopeProviders,
64833
+ this.metadata,
64834
+ this.scopeApps.getApps().map((app) => app.id)
64835
+ );
64786
64836
  this.initAuthoritiesFromConfig();
64787
64837
  this.scopeTools = new ToolRegistry(this.scopeProviders, [], scopeRef);
64788
64838
  this.scopeResources = new ResourceRegistry(this.scopeProviders, [], scopeRef);
@@ -68336,9 +68386,10 @@ function collectStaticEntries(config) {
68336
68386
  };
68337
68387
  const visitEntries = (owner, app) => {
68338
68388
  const reach = app ? { app } : "every-plugin";
68389
+ const toolReach = app ? { app } : "every-app";
68339
68390
  for (const item of owner.tools ?? []) {
68340
68391
  const record = tryNormalize(() => normalizeTool(item));
68341
- if (record) add(`Tool "${record.metadata.id || record.metadata.name}"`, record.metadata, reach);
68392
+ if (record) add(`Tool "${record.metadata.id || record.metadata.name}"`, record.metadata, toolReach);
68342
68393
  }
68343
68394
  for (const item of owner.resources ?? []) {
68344
68395
  if (tryNormalize(() => isResourceTemplate(item))) {
@@ -68371,7 +68422,10 @@ function collectStaticEntries(config) {
68371
68422
  visitPlugins(owner.plugins, app);
68372
68423
  };
68373
68424
  const server = config;
68374
- visitEntries({ skills: server.skills, plugins: server.plugins }, void 0);
68425
+ visitEntries(
68426
+ { tools: server.tools, resources: server.resources, skills: server.skills, plugins: server.plugins },
68427
+ void 0
68428
+ );
68375
68429
  for (const app of server.apps ?? []) {
68376
68430
  const record = tryNormalize(() => normalizeApp(app));
68377
68431
  if (record?.kind === "LOCAL_CLASS" /* LOCAL_CLASS */) visitEntries(record.metadata, record);
@@ -68379,6 +68433,7 @@ function collectStaticEntries(config) {
68379
68433
  const everyPluginKeys = /* @__PURE__ */ new Set([...everyAppKeys, ...[...ownAppKeys.values()].flatMap((keys) => [...keys])]);
68380
68434
  const keysReaching = (reach) => {
68381
68435
  if (reach === "every-plugin") return everyPluginKeys;
68436
+ if (reach === "every-app") return everyAppKeys;
68382
68437
  if ("agentKeys" in reach) return reach.agentKeys;
68383
68438
  return /* @__PURE__ */ new Set([...everyAppKeys, ...ownAppKeys.get(reach.app) ?? []]);
68384
68439
  };
@@ -71354,6 +71409,7 @@ __export(index_exports, {
71354
71409
  RemoveWorkflowTool: () => RemoveWorkflowTool,
71355
71410
  RequestContextNotAvailableError: () => RequestContextNotAvailableError,
71356
71411
  RequiredConfigUndefinedError: () => RequiredConfigUndefinedError,
71412
+ ReservedAppIdError: () => ReservedAppIdError,
71357
71413
  Resource: () => Resource,
71358
71414
  ResourceContext: () => ResourceContext,
71359
71415
  ResourceEntry: () => ResourceEntry,
@@ -72611,6 +72667,7 @@ var ChannelListHook = FlowHooksOf("channels:list");
72611
72667
  RemoveWorkflowTool,
72612
72668
  RequestContextNotAvailableError,
72613
72669
  RequiredConfigUndefinedError,
72670
+ ReservedAppIdError,
72614
72671
  Resource,
72615
72672
  ResourceContext,
72616
72673
  ResourceEntry,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frontmcp/sdk",
3
- "version": "1.9.0",
3
+ "version": "1.9.1-rc.1",
4
4
  "description": "FrontMCP SDK",
5
5
  "author": "AgentFront <info@agentfront.dev>",
6
6
  "homepage": "https://docs.agentfront.dev",
@@ -95,9 +95,9 @@
95
95
  },
96
96
  "peerDependencies": {
97
97
  "@anthropic-ai/sdk": "^0.30.0 || ^0.78.0",
98
- "@enclave-vm/core": "^2.15.3",
99
- "@frontmcp/observability": "1.9.0",
100
- "@frontmcp/storage-sqlite": "1.9.0",
98
+ "@enclave-vm/core": "^2.16.0",
99
+ "@frontmcp/observability": "1.9.1-rc.1",
100
+ "@frontmcp/storage-sqlite": "1.9.1-rc.1",
101
101
  "@opentelemetry/api": "^1.9.0",
102
102
  "@opentelemetry/sdk-trace-base": "^2.8.0",
103
103
  "@vercel/kv": "^3.0.0",
@@ -138,13 +138,13 @@
138
138
  }
139
139
  },
140
140
  "dependencies": {
141
- "@frontmcp/auth": "1.9.0",
142
- "@frontmcp/di": "1.9.0",
143
- "@frontmcp/guard": "1.9.0",
144
- "@frontmcp/lazy-zod": "1.9.0",
145
- "@frontmcp/protocol": "1.9.0",
146
- "@frontmcp/uipack": "1.9.0",
147
- "@frontmcp/utils": "1.9.0",
141
+ "@frontmcp/auth": "1.9.1-rc.1",
142
+ "@frontmcp/di": "1.9.1-rc.1",
143
+ "@frontmcp/guard": "1.9.1-rc.1",
144
+ "@frontmcp/lazy-zod": "1.9.1-rc.1",
145
+ "@frontmcp/protocol": "1.9.1-rc.1",
146
+ "@frontmcp/uipack": "1.9.1-rc.1",
147
+ "@frontmcp/utils": "1.9.1-rc.1",
148
148
  "@types/cors": "^2.8.17",
149
149
  "ioredis": "^5.8.0",
150
150
  "jose": "^6.1.3",