@frontmcp/sdk 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (226) hide show
  1. package/README.md +3 -3
  2. package/package.json +8 -19
  3. package/src/adapter/adapter.instance.js +5 -0
  4. package/src/adapter/adapter.instance.js.map +1 -1
  5. package/src/auth/authorization/authorization.class.d.ts +1 -4
  6. package/src/auth/authorization/authorization.class.js +6 -13
  7. package/src/auth/authorization/authorization.class.js.map +1 -1
  8. package/src/auth/flows/session.verify.flow.d.ts +1 -0
  9. package/src/auth/flows/session.verify.flow.js +11 -1
  10. package/src/auth/flows/session.verify.flow.js.map +1 -1
  11. package/src/auth/flows/well-known.jwks.flow.js +2 -2
  12. package/src/auth/flows/well-known.jwks.flow.js.map +1 -1
  13. package/src/auth/jwks/dev-key-persistence.d.ts +63 -0
  14. package/src/auth/jwks/dev-key-persistence.js +219 -0
  15. package/src/auth/jwks/dev-key-persistence.js.map +1 -0
  16. package/src/auth/jwks/index.d.ts +1 -0
  17. package/src/auth/jwks/index.js +1 -0
  18. package/src/auth/jwks/index.js.map +1 -1
  19. package/src/auth/jwks/jwks.service.d.ts +7 -4
  20. package/src/auth/jwks/jwks.service.js +81 -12
  21. package/src/auth/jwks/jwks.service.js.map +1 -1
  22. package/src/auth/jwks/jwks.types.d.ts +7 -0
  23. package/src/auth/jwks/jwks.types.js.map +1 -1
  24. package/src/auth/machine-id.d.ts +5 -0
  25. package/src/auth/machine-id.js +32 -0
  26. package/src/auth/machine-id.js.map +1 -0
  27. package/src/auth/session/index.d.ts +1 -0
  28. package/src/auth/session/index.js +3 -1
  29. package/src/auth/session/index.js.map +1 -1
  30. package/src/auth/session/record/session.base.js +5 -3
  31. package/src/auth/session/record/session.base.js.map +1 -1
  32. package/src/auth/session/record/session.stateless.d.ts +2 -2
  33. package/src/auth/session/record/session.stateless.js +5 -3
  34. package/src/auth/session/record/session.stateless.js.map +1 -1
  35. package/src/auth/session/redis-session.store.d.ts +64 -0
  36. package/src/auth/session/redis-session.store.js +204 -0
  37. package/src/auth/session/redis-session.store.js.map +1 -0
  38. package/src/auth/session/session.service.d.ts +0 -2
  39. package/src/auth/session/session.service.js +1 -7
  40. package/src/auth/session/session.service.js.map +1 -1
  41. package/src/auth/session/transport-session.manager.js +3 -5
  42. package/src/auth/session/transport-session.manager.js.map +1 -1
  43. package/src/auth/session/transport-session.types.d.ts +4 -0
  44. package/src/auth/session/transport-session.types.js +4 -3
  45. package/src/auth/session/transport-session.types.js.map +1 -1
  46. package/src/auth/session/utils/session-id.utils.d.ts +12 -1
  47. package/src/auth/session/utils/session-id.utils.js +48 -9
  48. package/src/auth/session/utils/session-id.utils.js.map +1 -1
  49. package/src/auth/ui/base-layout.d.ts +0 -8
  50. package/src/auth/ui/base-layout.js +1 -14
  51. package/src/auth/ui/base-layout.js.map +1 -1
  52. package/src/auth/ui/index.d.ts +3 -4
  53. package/src/auth/ui/index.js +10 -11
  54. package/src/auth/ui/index.js.map +1 -1
  55. package/src/auth/ui/{htmx-templates.d.ts → templates.d.ts} +5 -6
  56. package/src/auth/ui/{htmx-templates.js → templates.js} +8 -15
  57. package/src/auth/ui/templates.js.map +1 -0
  58. package/src/common/decorators/decorator-utils.js.map +1 -1
  59. package/src/common/decorators/front-mcp.decorator.js +28 -2
  60. package/src/common/decorators/front-mcp.decorator.js.map +1 -1
  61. package/src/common/index.d.ts +0 -1
  62. package/src/common/index.js +0 -1
  63. package/src/common/index.js.map +1 -1
  64. package/src/common/interfaces/adapter.interface.d.ts +6 -0
  65. package/src/common/interfaces/adapter.interface.js.map +1 -1
  66. package/src/common/interfaces/execution-context.interface.d.ts +52 -3
  67. package/src/common/interfaces/execution-context.interface.js +88 -3
  68. package/src/common/interfaces/execution-context.interface.js.map +1 -1
  69. package/src/common/interfaces/flow.interface.d.ts +13 -0
  70. package/src/common/interfaces/flow.interface.js +24 -0
  71. package/src/common/interfaces/flow.interface.js.map +1 -1
  72. package/src/common/interfaces/server.interface.d.ts +9 -0
  73. package/src/common/interfaces/server.interface.js.map +1 -1
  74. package/src/common/metadata/app.metadata.d.ts +108 -0
  75. package/src/common/metadata/front-mcp.metadata.d.ts +659 -2
  76. package/src/common/metadata/front-mcp.metadata.js +3 -1
  77. package/src/common/metadata/front-mcp.metadata.js.map +1 -1
  78. package/src/common/metadata/provider.metadata.d.ts +14 -0
  79. package/src/common/metadata/provider.metadata.js +18 -2
  80. package/src/common/metadata/provider.metadata.js.map +1 -1
  81. package/src/common/metadata/tool.metadata.d.ts +33 -1
  82. package/src/common/metadata/tool.metadata.js.map +1 -1
  83. package/src/common/migrate/auth-transport.migrate.d.ts +62 -0
  84. package/src/common/migrate/auth-transport.migrate.js +140 -0
  85. package/src/common/migrate/auth-transport.migrate.js.map +1 -0
  86. package/src/common/migrate/index.d.ts +1 -0
  87. package/src/common/migrate/index.js +6 -0
  88. package/src/common/migrate/index.js.map +1 -0
  89. package/src/common/schemas/http-output.schema.d.ts +10 -2
  90. package/src/common/schemas/index.d.ts +1 -0
  91. package/src/common/schemas/index.js +1 -0
  92. package/src/common/schemas/index.js.map +1 -1
  93. package/src/common/schemas/session-header.schema.d.ts +16 -0
  94. package/src/common/schemas/session-header.schema.js +42 -0
  95. package/src/common/schemas/session-header.schema.js.map +1 -0
  96. package/src/common/tokens/front-mcp.tokens.js +3 -1
  97. package/src/common/tokens/front-mcp.tokens.js.map +1 -1
  98. package/src/common/types/options/auth.options.d.ts +233 -3
  99. package/src/common/types/options/auth.options.js +29 -40
  100. package/src/common/types/options/auth.options.js.map +1 -1
  101. package/src/common/types/options/index.d.ts +2 -0
  102. package/src/common/types/options/index.js +2 -0
  103. package/src/common/types/options/index.js.map +1 -1
  104. package/src/common/types/options/redis.options.d.ts +22 -0
  105. package/src/common/types/options/redis.options.js +45 -0
  106. package/src/common/types/options/redis.options.js.map +1 -0
  107. package/src/common/types/options/transport.options.d.ts +84 -0
  108. package/src/common/types/options/transport.options.js +121 -0
  109. package/src/common/types/options/transport.options.js.map +1 -0
  110. package/src/completion/flows/complete.flow.d.ts +17 -2
  111. package/src/context/frontmcp-context-storage.d.ts +94 -0
  112. package/src/context/frontmcp-context-storage.js +183 -0
  113. package/src/context/frontmcp-context-storage.js.map +1 -0
  114. package/src/context/frontmcp-context.d.ts +269 -0
  115. package/src/context/frontmcp-context.js +360 -0
  116. package/src/context/frontmcp-context.js.map +1 -0
  117. package/src/context/frontmcp-context.provider.d.ts +43 -0
  118. package/src/context/frontmcp-context.provider.js +61 -0
  119. package/src/context/frontmcp-context.provider.js.map +1 -0
  120. package/src/context/index.d.ts +34 -0
  121. package/src/context/index.js +64 -0
  122. package/src/context/index.js.map +1 -0
  123. package/src/context/request-context-storage.d.ts +89 -0
  124. package/src/context/request-context-storage.js +183 -0
  125. package/src/context/request-context-storage.js.map +1 -0
  126. package/src/context/request-context.d.ts +184 -0
  127. package/src/context/request-context.js +209 -0
  128. package/src/context/request-context.js.map +1 -0
  129. package/src/context/request-context.provider.d.ts +37 -0
  130. package/src/context/request-context.provider.js +51 -0
  131. package/src/context/request-context.provider.js.map +1 -0
  132. package/src/context/session-key.provider.d.ts +45 -0
  133. package/src/context/session-key.provider.js +65 -0
  134. package/src/context/session-key.provider.js.map +1 -0
  135. package/src/context/trace-context.d.ts +43 -0
  136. package/src/context/trace-context.js +142 -0
  137. package/src/context/trace-context.js.map +1 -0
  138. package/src/errors/index.d.ts +1 -1
  139. package/src/errors/index.js +3 -1
  140. package/src/errors/index.js.map +1 -1
  141. package/src/errors/mcp.error.d.ts +7 -0
  142. package/src/errors/mcp.error.js +11 -1
  143. package/src/errors/mcp.error.js.map +1 -1
  144. package/src/flows/flow.instance.d.ts +16 -0
  145. package/src/flows/flow.instance.js +166 -80
  146. package/src/flows/flow.instance.js.map +1 -1
  147. package/src/flows/flow.registry.d.ts +5 -0
  148. package/src/flows/flow.registry.js +45 -3
  149. package/src/flows/flow.registry.js.map +1 -1
  150. package/src/front-mcp/front-mcp.d.ts +12 -0
  151. package/src/front-mcp/front-mcp.js +22 -3
  152. package/src/front-mcp/front-mcp.js.map +1 -1
  153. package/src/front-mcp/front-mcp.providers.d.ts +266 -1
  154. package/src/front-mcp/front-mcp.providers.js +2 -1
  155. package/src/front-mcp/front-mcp.providers.js.map +1 -1
  156. package/src/front-mcp/serverless-handler.d.ts +28 -0
  157. package/src/front-mcp/serverless-handler.js +61 -0
  158. package/src/front-mcp/serverless-handler.js.map +1 -0
  159. package/src/hooks/hooks.utils.d.ts +1 -1
  160. package/src/hooks/hooks.utils.js +10 -3
  161. package/src/hooks/hooks.utils.js.map +1 -1
  162. package/src/index.d.ts +8 -4
  163. package/src/index.js +20 -1
  164. package/src/index.js.map +1 -1
  165. package/src/logger/instances/instance.logger.js +0 -1
  166. package/src/logger/instances/instance.logger.js.map +1 -1
  167. package/src/logging/flows/set-level.flow.d.ts +17 -2
  168. package/src/notification/notification.service.js +5 -1
  169. package/src/notification/notification.service.js.map +1 -1
  170. package/src/prompt/flows/get-prompt.flow.d.ts +97 -2
  171. package/src/prompt/flows/prompts-list.flow.d.ts +12 -1
  172. package/src/provider/provider.registry.d.ts +97 -5
  173. package/src/provider/provider.registry.js +306 -9
  174. package/src/provider/provider.registry.js.map +1 -1
  175. package/src/provider/provider.types.d.ts +21 -3
  176. package/src/provider/provider.types.js.map +1 -1
  177. package/src/resource/flows/read-resource.flow.d.ts +22 -3
  178. package/src/resource/flows/resource-templates-list.flow.d.ts +20 -1
  179. package/src/resource/flows/resources-list.flow.d.ts +20 -1
  180. package/src/resource/flows/subscribe-resource.flow.d.ts +17 -2
  181. package/src/resource/flows/unsubscribe-resource.flow.d.ts +17 -2
  182. package/src/scope/flows/http.request.flow.js +43 -7
  183. package/src/scope/flows/http.request.flow.js.map +1 -1
  184. package/src/scope/scope.instance.js +12 -5
  185. package/src/scope/scope.instance.js.map +1 -1
  186. package/src/server/adapters/base.host.adapter.d.ts +9 -0
  187. package/src/server/adapters/base.host.adapter.js.map +1 -1
  188. package/src/server/adapters/express.host.adapter.d.ts +12 -0
  189. package/src/server/adapters/express.host.adapter.js +21 -1
  190. package/src/server/adapters/express.host.adapter.js.map +1 -1
  191. package/src/server/server.instance.d.ts +3 -0
  192. package/src/server/server.instance.js +14 -7
  193. package/src/server/server.instance.js.map +1 -1
  194. package/src/tool/flows/call-tool.flow.d.ts +118 -13
  195. package/src/tool/flows/call-tool.flow.js +240 -194
  196. package/src/tool/flows/call-tool.flow.js.map +1 -1
  197. package/src/tool/flows/tools-list.flow.d.ts +25 -11
  198. package/src/tool/flows/tools-list.flow.js +82 -31
  199. package/src/tool/flows/tools-list.flow.js.map +1 -1
  200. package/src/tool/tool.instance.d.ts +1 -4
  201. package/src/transport/adapters/transport.streamable-http.adapter.js +1 -0
  202. package/src/transport/adapters/transport.streamable-http.adapter.js.map +1 -1
  203. package/src/transport/flows/handle.sse.flow.js +9 -2
  204. package/src/transport/flows/handle.sse.flow.js.map +1 -1
  205. package/src/transport/flows/handle.streamable-http.flow.js +63 -6
  206. package/src/transport/flows/handle.streamable-http.flow.js.map +1 -1
  207. package/src/transport/mcp-handlers/complete-request.handler.d.ts +27 -1
  208. package/src/transport/mcp-handlers/get-prompt-request.handler.d.ts +52 -1
  209. package/src/transport/mcp-handlers/index.d.ts +413 -7
  210. package/src/transport/mcp-handlers/initialize-request.handler.js +12 -2
  211. package/src/transport/mcp-handlers/initialize-request.handler.js.map +1 -1
  212. package/src/transport/mcp-handlers/list-prompts-request.handler.d.ts +27 -1
  213. package/src/transport/mcp-handlers/list-resource-templates-request.handler.d.ts +32 -1
  214. package/src/transport/mcp-handlers/list-resources-request.handler.d.ts +32 -1
  215. package/src/transport/mcp-handlers/list-tools-request.handler.d.ts +30 -1
  216. package/src/transport/mcp-handlers/logging-set-level-request.handler.d.ts +20 -0
  217. package/src/transport/mcp-handlers/read-resource-request.handler.d.ts +27 -1
  218. package/src/transport/mcp-handlers/subscribe-request.handler.d.ts +20 -0
  219. package/src/transport/mcp-handlers/unsubscribe-request.handler.d.ts +20 -0
  220. package/src/transport/transport.registry.d.ts +68 -4
  221. package/src/transport/transport.registry.js +313 -11
  222. package/src/transport/transport.registry.js.map +1 -1
  223. package/src/auth/ui/htmx-templates.js.map +0 -1
  224. package/src/common/providers/session.provider.d.ts +0 -13
  225. package/src/common/providers/session.provider.js +0 -27
  226. package/src/common/providers/session.provider.js.map +0 -1
@@ -1,12 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.InMemorySessionStore = exports.TransportSessionManager = void 0;
3
+ exports.RedisSessionStore = exports.InMemorySessionStore = exports.TransportSessionManager = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  // Transport session architecture
6
6
  tslib_1.__exportStar(require("./transport-session.types"), exports);
7
7
  var transport_session_manager_1 = require("./transport-session.manager");
8
8
  Object.defineProperty(exports, "TransportSessionManager", { enumerable: true, get: function () { return transport_session_manager_1.TransportSessionManager; } });
9
9
  Object.defineProperty(exports, "InMemorySessionStore", { enumerable: true, get: function () { return transport_session_manager_1.InMemorySessionStore; } });
10
+ var redis_session_store_1 = require("./redis-session.store");
11
+ Object.defineProperty(exports, "RedisSessionStore", { enumerable: true, get: function () { return redis_session_store_1.RedisSessionStore; } });
10
12
  // Authorization store for OAuth flows
11
13
  tslib_1.__exportStar(require("./authorization.store"), exports);
12
14
  // Authorization vault for stateful sessions
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/auth/session/index.ts"],"names":[],"mappings":";;;;AAAA,iCAAiC;AACjC,oEAA0C;AAC1C,yEAA4F;AAAnF,oIAAA,uBAAuB,OAAA;AAAE,iIAAA,oBAAoB,OAAA;AAEtD,sCAAsC;AACtC,gEAAsC;AAEtC,4CAA4C;AAC5C,gEAAsC","sourcesContent":["// Transport session architecture\nexport * from './transport-session.types';\nexport { TransportSessionManager, InMemorySessionStore } from './transport-session.manager';\n\n// Authorization store for OAuth flows\nexport * from './authorization.store';\n\n// Authorization vault for stateful sessions\nexport * from './authorization-vault';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/auth/session/index.ts"],"names":[],"mappings":";;;;AAAA,iCAAiC;AACjC,oEAA0C;AAC1C,yEAA4F;AAAnF,oIAAA,uBAAuB,OAAA;AAAE,iIAAA,oBAAoB,OAAA;AACtD,6DAA0D;AAAjD,wHAAA,iBAAiB,OAAA;AAE1B,sCAAsC;AACtC,gEAAsC;AAEtC,4CAA4C;AAC5C,gEAAsC","sourcesContent":["// Transport session architecture\nexport * from './transport-session.types';\nexport { TransportSessionManager, InMemorySessionStore } from './transport-session.manager';\nexport { RedisSessionStore } from './redis-session.store';\n\n// Authorization store for OAuth flows\nexport * from './authorization.store';\n\n// Authorization vault for stateful sessions\nexport * from './authorization-vault';\n"]}
@@ -36,7 +36,7 @@ class Session {
36
36
  this.user = ctx.user;
37
37
  this.claims = ctx.claims;
38
38
  // derive token expiration from JWT claims if present (exp in seconds)
39
- const exp = (ctx.claims && typeof ctx.claims['exp'] === 'number') ? Number(ctx.claims['exp']) : undefined;
39
+ const exp = ctx.claims && typeof ctx.claims['exp'] === 'number' ? Number(ctx.claims['exp']) : undefined;
40
40
  if (exp) {
41
41
  this.expiresAt = exp > 1e12 ? exp : exp * 1000;
42
42
  }
@@ -68,12 +68,14 @@ class Session {
68
68
  async getTransportSessionId() {
69
69
  if (this.#activeTransportId)
70
70
  return this.#activeTransportId;
71
- const mode = this.scope.metadata.session?.transportIdMode ?? 'uuid';
71
+ const mode = this.scope.metadata.transport?.transportIdMode ?? 'uuid';
72
72
  if (typeof mode === 'string') {
73
73
  return session_transport_1.TransportIdGenerator.createId(mode);
74
74
  }
75
75
  else {
76
- const modeResult = await mode(this.issuer);
76
+ // Cast to proper function type since Zod's z.function() type is too generic
77
+ const modeFn = mode;
78
+ const modeResult = await modeFn(this.issuer);
77
79
  return session_transport_1.TransportIdGenerator.createId(modeResult);
78
80
  }
79
81
  }
@@ -1 +1 @@
1
- {"version":3,"file":"session.base.js","sourceRoot":"","sources":["../../../../../src/auth/session/record/session.base.ts"],"names":[],"mappings":"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","sourcesContent":["// auth/session/record/session.base.ts\n\nimport type { ProviderSnapshot, SessionMode } from '../session.types';\nimport { TransportIdGenerator } from '../session.transport';\nimport { Scope } from '../../../scope';\n\nexport interface BaseCreateCtx {\n id: string;\n sessionId?: string;\n scope: Scope;\n issuer: string;\n token: string;\n user: SessionUser;\n claims?: SessionClaims;\n createdAt?: number;\n // optional precomputed authorization projections\n authorizedProviders?: Record<string, ProviderSnapshot>;\n authorizedProviderIds?: string[];\n authorizedApps?: Record<string, { id: string; toolIds: string[] }>;\n authorizedAppIds?: string[];\n authorizedResources?: string[];\n scopes?: string[];\n // Scoped tools/prompts maps\n authorizedTools?: Record<string, { executionPath: [string, string]; details?: Record<string, any> }>;\n authorizedToolIds?: string[];\n authorizedPrompts?: Record<string, { executionPath: [string, string]; details?: Record<string, any> }>;\n authorizedPromptIds?: string[];\n}\n\n// TODO: can be extended\nexport interface SessionUser {\n sub?: string;\n name?: string;\n email?: string;\n picture?: string;\n}\n\n// TODO: can be extended\nexport interface SessionClaims {\n [key: string]: any;\n}\n\nexport abstract class Session {\n // ---------------- public immutable data ----------------\n readonly id: string;\n abstract readonly mode: SessionMode;\n readonly createdAt: number;\n readonly scopeId: string;\n readonly user: SessionUser;\n readonly claims?: Record<string, unknown>;\n /** Epoch millis when the bearer token expires (if available). */\n readonly expiresAt?: number;\n\n readonly authorizedProviders: Record<string, ProviderSnapshot>;\n readonly authorizedProviderIds: string[];\n readonly authorizedApps: Record<string, { id: string; toolIds: string[] }>;\n readonly authorizedAppIds: string[];\n readonly authorizedResources: string[];\n readonly scopes?: string[];\n readonly authorizedTools?: Record<string, { executionPath: [string, string]; details?: Record<string, any> }>;\n readonly authorizedToolIds?: string[];\n readonly authorizedPrompts?: Record<string, { executionPath: [string, string]; details?: Record<string, any> }>;\n readonly authorizedPromptIds?: string[];\n\n // ---------------- private/shared ----------------\n #scope: Scope;\n #issuer: string;\n protected token: string;\n\n #activeTransportId?: string;\n\n protected constructor(ctx: BaseCreateCtx) {\n this.id = ctx.id;\n this.createdAt = ctx.createdAt || Date.now();\n this.#scope = ctx.scope;\n this.#issuer = ctx.issuer;\n this.scopeId = ctx.scope.id;\n this.user = ctx.user;\n this.claims = ctx.claims;\n // derive token expiration from JWT claims if present (exp in seconds)\n const exp = (ctx.claims && typeof (ctx.claims as any)['exp'] === 'number') ? Number((ctx.claims as any)['exp']) : undefined;\n if (exp) {\n this.expiresAt = exp > 1e12 ? exp : exp * 1000;\n }\n // project authorized fields (defaults to empty)\n this.authorizedProviders = ctx.authorizedProviders ?? {};\n this.authorizedProviderIds = ctx.authorizedProviderIds ?? [];\n this.authorizedApps = ctx.authorizedApps ?? {};\n this.authorizedAppIds = ctx.authorizedAppIds ?? [];\n this.authorizedResources = ctx.authorizedResources ?? [];\n this.authorizedTools = ctx.authorizedTools ?? {};\n this.authorizedToolIds = ctx.authorizedToolIds ?? [];\n this.authorizedPrompts = ctx.authorizedPrompts ?? {};\n this.authorizedPromptIds = ctx.authorizedPromptIds ?? [];\n this.token = ctx.token;\n this.#activeTransportId = ctx.sessionId;\n }\n\n /**\n * Get the scope associated with this session.\n * Can be used by subclasses to implement custom scope handling.\n * @protected\n */\n protected get scope(): Scope {\n return this.#scope;\n }\n // ---------------- accessors ----------------\n\n get issuer(): string {\n return this.#issuer;\n }\n\n async getTransportSessionId(): Promise<string> {\n if (this.#activeTransportId) return this.#activeTransportId;\n const mode = this.scope.metadata.session?.transportIdMode ?? 'uuid';\n if (typeof mode === 'string') {\n return TransportIdGenerator.createId(mode);\n } else {\n const modeResult = await mode(this.issuer);\n return TransportIdGenerator.createId(modeResult);\n }\n }\n\n /**\n * Get the access token for a given provider.\n * Must be implemented in subclasses based on session topology.\n * @protected\n * @param providerId\n */\n abstract getToken(providerId?: string): Promise<string> | string;\n\n // ---------------- scoped view ----------------\n scoped(allowed: string | string[] | ((id: string) => boolean)) {\n const fn =\n typeof allowed === 'function'\n ? allowed\n : Array.isArray(allowed)\n ? (id: string) => allowed.includes(id)\n : (id: string) => id === allowed;\n return new SessionView(this, fn);\n }\n}\n\nexport class SessionView {\n constructor(private readonly parent: Session, private readonly allow: (id: string) => boolean) {}\n\n get id() {\n return this.parent.id;\n }\n get mode() {\n return this.parent.mode;\n }\n get user() {\n return this.parent.user;\n }\n get claims() {\n return this.parent.claims;\n }\n get authorizedApps() {\n return this.parent.authorizedApps;\n }\n\n async getToken(providerId: string) {\n if (!this.allow(providerId)) throw new Error(`scoped_denied:${providerId}`);\n return this.parent.getToken(providerId);\n }\n get transportId() {\n return this.parent.getTransportSessionId;\n }\n}\n"]}
1
+ {"version":3,"file":"session.base.js","sourceRoot":"","sources":["../../../../../src/auth/session/record/session.base.ts"],"names":[],"mappings":"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","sourcesContent":["// auth/session/record/session.base.ts\n\nimport type { ProviderSnapshot, SessionMode } from '../session.types';\nimport type { TransportIdMode } from '../../../common';\nimport { TransportIdGenerator } from '../session.transport';\nimport { Scope } from '../../../scope';\n\nexport interface BaseCreateCtx {\n id: string;\n sessionId?: string;\n scope: Scope;\n issuer: string;\n token: string;\n user: SessionUser;\n claims?: SessionClaims;\n createdAt?: number;\n // optional precomputed authorization projections\n authorizedProviders?: Record<string, ProviderSnapshot>;\n authorizedProviderIds?: string[];\n authorizedApps?: Record<string, { id: string; toolIds: string[] }>;\n authorizedAppIds?: string[];\n authorizedResources?: string[];\n scopes?: string[];\n // Scoped tools/prompts maps\n authorizedTools?: Record<string, { executionPath: [string, string]; details?: Record<string, any> }>;\n authorizedToolIds?: string[];\n authorizedPrompts?: Record<string, { executionPath: [string, string]; details?: Record<string, any> }>;\n authorizedPromptIds?: string[];\n}\n\n// TODO: can be extended\nexport interface SessionUser {\n sub?: string;\n name?: string;\n email?: string;\n picture?: string;\n}\n\n// TODO: can be extended\nexport interface SessionClaims {\n [key: string]: any;\n}\n\nexport abstract class Session {\n // ---------------- public immutable data ----------------\n readonly id: string;\n abstract readonly mode: SessionMode;\n readonly createdAt: number;\n readonly scopeId: string;\n readonly user: SessionUser;\n readonly claims?: Record<string, unknown>;\n /** Epoch millis when the bearer token expires (if available). */\n readonly expiresAt?: number;\n\n readonly authorizedProviders: Record<string, ProviderSnapshot>;\n readonly authorizedProviderIds: string[];\n readonly authorizedApps: Record<string, { id: string; toolIds: string[] }>;\n readonly authorizedAppIds: string[];\n readonly authorizedResources: string[];\n readonly scopes?: string[];\n readonly authorizedTools?: Record<string, { executionPath: [string, string]; details?: Record<string, any> }>;\n readonly authorizedToolIds?: string[];\n readonly authorizedPrompts?: Record<string, { executionPath: [string, string]; details?: Record<string, any> }>;\n readonly authorizedPromptIds?: string[];\n\n // ---------------- private/shared ----------------\n #scope: Scope;\n #issuer: string;\n protected token: string;\n\n #activeTransportId?: string;\n\n protected constructor(ctx: BaseCreateCtx) {\n this.id = ctx.id;\n this.createdAt = ctx.createdAt || Date.now();\n this.#scope = ctx.scope;\n this.#issuer = ctx.issuer;\n this.scopeId = ctx.scope.id;\n this.user = ctx.user;\n this.claims = ctx.claims;\n // derive token expiration from JWT claims if present (exp in seconds)\n const exp =\n ctx.claims && typeof (ctx.claims as any)['exp'] === 'number' ? Number((ctx.claims as any)['exp']) : undefined;\n if (exp) {\n this.expiresAt = exp > 1e12 ? exp : exp * 1000;\n }\n // project authorized fields (defaults to empty)\n this.authorizedProviders = ctx.authorizedProviders ?? {};\n this.authorizedProviderIds = ctx.authorizedProviderIds ?? [];\n this.authorizedApps = ctx.authorizedApps ?? {};\n this.authorizedAppIds = ctx.authorizedAppIds ?? [];\n this.authorizedResources = ctx.authorizedResources ?? [];\n this.authorizedTools = ctx.authorizedTools ?? {};\n this.authorizedToolIds = ctx.authorizedToolIds ?? [];\n this.authorizedPrompts = ctx.authorizedPrompts ?? {};\n this.authorizedPromptIds = ctx.authorizedPromptIds ?? [];\n this.token = ctx.token;\n this.#activeTransportId = ctx.sessionId;\n }\n\n /**\n * Get the scope associated with this session.\n * Can be used by subclasses to implement custom scope handling.\n * @protected\n */\n protected get scope(): Scope {\n return this.#scope;\n }\n // ---------------- accessors ----------------\n\n get issuer(): string {\n return this.#issuer;\n }\n\n async getTransportSessionId(): Promise<string> {\n if (this.#activeTransportId) return this.#activeTransportId;\n const mode = this.scope.metadata.transport?.transportIdMode ?? 'uuid';\n if (typeof mode === 'string') {\n return TransportIdGenerator.createId(mode as TransportIdMode);\n } else {\n // Cast to proper function type since Zod's z.function() type is too generic\n const modeFn = mode as (issuer: string) => Promise<TransportIdMode> | TransportIdMode;\n const modeResult = await modeFn(this.issuer);\n return TransportIdGenerator.createId(modeResult);\n }\n }\n\n /**\n * Get the access token for a given provider.\n * Must be implemented in subclasses based on session topology.\n * @protected\n * @param providerId\n */\n abstract getToken(providerId?: string): Promise<string> | string;\n\n // ---------------- scoped view ----------------\n scoped(allowed: string | string[] | ((id: string) => boolean)) {\n const fn =\n typeof allowed === 'function'\n ? allowed\n : Array.isArray(allowed)\n ? (id: string) => allowed.includes(id)\n : (id: string) => id === allowed;\n return new SessionView(this, fn);\n }\n}\n\nexport class SessionView {\n constructor(private readonly parent: Session, private readonly allow: (id: string) => boolean) {}\n\n get id() {\n return this.parent.id;\n }\n get mode() {\n return this.parent.mode;\n }\n get user() {\n return this.parent.user;\n }\n get claims() {\n return this.parent.claims;\n }\n get authorizedApps() {\n return this.parent.authorizedApps;\n }\n\n async getToken(providerId: string) {\n if (!this.allow(providerId)) throw new Error(`scoped_denied:${providerId}`);\n return this.parent.getToken(providerId);\n }\n get transportId() {\n return this.parent.getTransportSessionId;\n }\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import { Session, type BaseCreateCtx } from './session.base';
2
- export type StatefulCreateCtx = BaseCreateCtx & {};
2
+ export type StatefulCreateCtx = BaseCreateCtx & Record<string, never>;
3
3
  /**
4
4
  * Represents a **stateful session (non-refreshable)** where nested OAuth
5
5
  * tokens cannot be refreshed server-side. When a nested provider token
@@ -13,5 +13,5 @@ export declare class StatelessSession extends Session {
13
13
  #private;
14
14
  readonly mode = "stateless";
15
15
  constructor(ctx: StatefulCreateCtx);
16
- getToken(providerId?: string): Promise<string> | string;
16
+ getToken(_providerId?: string): Promise<string> | string;
17
17
  }
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.StatelessSession = void 0;
4
4
  const session_base_1 = require("./session.base");
5
+ const mcp_error_1 = require("../../../errors/mcp.error");
5
6
  /**
6
7
  * Represents a **stateful session (non-refreshable)** where nested OAuth
7
8
  * tokens cannot be refreshed server-side. When a nested provider token
@@ -17,13 +18,14 @@ class StatelessSession extends session_base_1.Session {
17
18
  * Used to encrypt/decrypt nested provider tokens in #store.
18
19
  * @private
19
20
  */
21
+ // eslint-disable-next-line no-unused-private-class-members
20
22
  #vault;
21
23
  constructor(ctx) {
22
24
  super(ctx);
23
- throw new Error('Method not implemented.');
25
+ throw new mcp_error_1.InternalMcpError('StatelessSession not yet implemented', 'NOT_IMPLEMENTED');
24
26
  }
25
- getToken(providerId) {
26
- throw new Error('Method not implemented.');
27
+ getToken(_providerId) {
28
+ throw new mcp_error_1.InternalMcpError('Token refresh not supported in stateless mode', 'NOT_IMPLEMENTED');
27
29
  }
28
30
  }
29
31
  exports.StatelessSession = StatelessSession;
@@ -1 +1 @@
1
- {"version":3,"file":"session.stateless.js","sourceRoot":"","sources":["../../../../../src/auth/session/record/session.stateless.ts"],"names":[],"mappings":";;;AAAA,iDAA6D;AAK7D;;;;;;;;GAQG;AACH,MAAa,gBAAiB,SAAQ,sBAAO;IAClC,IAAI,GAAG,WAAW,CAAC;IAC5B;;;OAGG;IACH,MAAM,CAAa;IACnB,YAAY,GAAsB;QAChC,KAAK,CAAC,GAAU,CAAC,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IACQ,QAAQ,CAAC,UAAmB;QACnC,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;CACF;AAdD,4CAcC","sourcesContent":["import { Session, type BaseCreateCtx } from './session.base';\nimport { TokenVault } from '../token.vault';\n\nexport type StatefulCreateCtx = BaseCreateCtx & {};\n\n/**\n * Represents a **stateful session (non-refreshable)** where nested OAuth\n * tokens cannot be refreshed server-side. When a nested provider token\n * expires, the user must re-authorize to obtain new credentials.\n *\n * Notes:\n * - Simpler flow, but degrades UX when tokens are short-lived.\n * - Prefer the refreshable stateful session for multi-app environments.\n */\nexport class StatelessSession extends Session {\n readonly mode = 'stateless';\n /**\n * Used to encrypt/decrypt nested provider tokens in #store.\n * @private\n */\n #vault: TokenVault;\n constructor(ctx: StatefulCreateCtx) {\n super(ctx as any);\n throw new Error('Method not implemented.');\n }\n override getToken(providerId?: string): Promise<string> | string {\n throw new Error('Method not implemented.');\n }\n}\n"]}
1
+ {"version":3,"file":"session.stateless.js","sourceRoot":"","sources":["../../../../../src/auth/session/record/session.stateless.ts"],"names":[],"mappings":";;;AAAA,iDAA6D;AAE7D,yDAA6D;AAI7D;;;;;;;;GAQG;AACH,MAAa,gBAAiB,SAAQ,sBAAO;IAClC,IAAI,GAAG,WAAW,CAAC;IAC5B;;;OAGG;IACH,2DAA2D;IAC3D,MAAM,CAAa;IACnB,YAAY,GAAsB;QAChC,KAAK,CAAC,GAAoB,CAAC,CAAC;QAC5B,MAAM,IAAI,4BAAgB,CAAC,sCAAsC,EAAE,iBAAiB,CAAC,CAAC;IACxF,CAAC;IACQ,QAAQ,CAAC,WAAoB;QACpC,MAAM,IAAI,4BAAgB,CAAC,+CAA+C,EAAE,iBAAiB,CAAC,CAAC;IACjG,CAAC;CACF;AAfD,4CAeC","sourcesContent":["import { Session, type BaseCreateCtx } from './session.base';\nimport { TokenVault } from '../token.vault';\nimport { InternalMcpError } from '../../../errors/mcp.error';\n\nexport type StatefulCreateCtx = BaseCreateCtx & Record<string, never>;\n\n/**\n * Represents a **stateful session (non-refreshable)** where nested OAuth\n * tokens cannot be refreshed server-side. When a nested provider token\n * expires, the user must re-authorize to obtain new credentials.\n *\n * Notes:\n * - Simpler flow, but degrades UX when tokens are short-lived.\n * - Prefer the refreshable stateful session for multi-app environments.\n */\nexport class StatelessSession extends Session {\n readonly mode = 'stateless';\n /**\n * Used to encrypt/decrypt nested provider tokens in #store.\n * @private\n */\n // eslint-disable-next-line no-unused-private-class-members\n #vault: TokenVault;\n constructor(ctx: StatefulCreateCtx) {\n super(ctx as BaseCreateCtx);\n throw new InternalMcpError('StatelessSession not yet implemented', 'NOT_IMPLEMENTED');\n }\n override getToken(_providerId?: string): Promise<string> | string {\n throw new InternalMcpError('Token refresh not supported in stateless mode', 'NOT_IMPLEMENTED');\n }\n}\n"]}
@@ -0,0 +1,64 @@
1
+ import { Redis } from 'ioredis';
2
+ import { SessionStore, StoredSession, RedisConfig } from './transport-session.types';
3
+ import { FrontMcpLogger } from '../../common/interfaces/logger.interface';
4
+ /**
5
+ * Redis-backed session store implementation
6
+ *
7
+ * Provides persistent session storage for distributed deployments.
8
+ * Sessions are stored as JSON with optional TTL.
9
+ */
10
+ export declare class RedisSessionStore implements SessionStore {
11
+ private readonly redis;
12
+ private readonly keyPrefix;
13
+ private readonly defaultTtlMs;
14
+ private readonly logger?;
15
+ private externalInstance;
16
+ constructor(config: RedisConfig | {
17
+ redis: Redis;
18
+ keyPrefix?: string;
19
+ defaultTtlMs?: number;
20
+ }, logger?: FrontMcpLogger);
21
+ /**
22
+ * Get the full Redis key for a session ID
23
+ * @throws Error if sessionId is empty
24
+ */
25
+ private key;
26
+ /**
27
+ * Get a stored session by ID
28
+ *
29
+ * Note: Uses atomic GETEX to extend TTL while reading, preventing race conditions
30
+ * where concurrent readers might resurrect expired sessions.
31
+ */
32
+ get(sessionId: string): Promise<StoredSession | null>;
33
+ /**
34
+ * Store a session with optional TTL
35
+ */
36
+ set(sessionId: string, session: StoredSession, ttlMs?: number): Promise<void>;
37
+ /**
38
+ * Delete a session
39
+ */
40
+ delete(sessionId: string): Promise<void>;
41
+ /**
42
+ * Check if a session exists
43
+ */
44
+ exists(sessionId: string): Promise<boolean>;
45
+ /**
46
+ * Allocate a new session ID
47
+ */
48
+ allocId(): string;
49
+ /**
50
+ * Disconnect from Redis (only if we created the connection)
51
+ */
52
+ disconnect(): Promise<void>;
53
+ /**
54
+ * Get the underlying Redis client (for advanced use cases)
55
+ */
56
+ getRedisClient(): Redis;
57
+ /**
58
+ * Test Redis connection by sending a PING command.
59
+ * Useful for validating connection on startup.
60
+ *
61
+ * @returns true if connection is healthy, false otherwise
62
+ */
63
+ ping(): Promise<boolean>;
64
+ }
@@ -0,0 +1,204 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RedisSessionStore = void 0;
4
+ const tslib_1 = require("tslib");
5
+ // auth/session/redis-session.store.ts
6
+ const ioredis_1 = tslib_1.__importDefault(require("ioredis"));
7
+ const crypto_1 = require("crypto");
8
+ const transport_session_types_1 = require("./transport-session.types");
9
+ /**
10
+ * Redis-backed session store implementation
11
+ *
12
+ * Provides persistent session storage for distributed deployments.
13
+ * Sessions are stored as JSON with optional TTL.
14
+ */
15
+ class RedisSessionStore {
16
+ redis;
17
+ keyPrefix;
18
+ defaultTtlMs;
19
+ logger;
20
+ externalInstance = false;
21
+ constructor(config, logger) {
22
+ // Default TTL of 1 hour for session extension on access
23
+ this.defaultTtlMs = ('defaultTtlMs' in config ? config.defaultTtlMs : undefined) ?? 3600000;
24
+ this.logger = logger;
25
+ if ('redis' in config && config.redis) {
26
+ // Use provided Redis instance
27
+ this.redis = config.redis;
28
+ this.keyPrefix = config.keyPrefix ?? 'mcp:session:';
29
+ this.externalInstance = true;
30
+ }
31
+ else {
32
+ // Create new Redis connection from config
33
+ const redisConfig = config;
34
+ const options = {
35
+ host: redisConfig.host,
36
+ port: redisConfig.port ?? 6379,
37
+ password: redisConfig.password,
38
+ db: redisConfig.db ?? 0,
39
+ };
40
+ if (redisConfig.tls) {
41
+ options.tls = {};
42
+ }
43
+ this.redis = new ioredis_1.default(options);
44
+ this.keyPrefix = redisConfig.keyPrefix ?? 'mcp:session:';
45
+ }
46
+ }
47
+ /**
48
+ * Get the full Redis key for a session ID
49
+ * @throws Error if sessionId is empty
50
+ */
51
+ key(sessionId) {
52
+ if (!sessionId || sessionId.trim() === '') {
53
+ throw new Error('[RedisSessionStore] sessionId cannot be empty');
54
+ }
55
+ return `${this.keyPrefix}${sessionId}`;
56
+ }
57
+ /**
58
+ * Get a stored session by ID
59
+ *
60
+ * Note: Uses atomic GETEX to extend TTL while reading, preventing race conditions
61
+ * where concurrent readers might resurrect expired sessions.
62
+ */
63
+ async get(sessionId) {
64
+ const key = this.key(sessionId);
65
+ // Use GETEX to atomically get and extend TTL in a single operation
66
+ // This prevents the race where one request deletes expired session
67
+ // while another is trying to extend it
68
+ let raw;
69
+ try {
70
+ // GETEX with EXAT/PXAT is atomic - no race condition possible
71
+ raw = await this.redis.getex(key, 'PX', this.defaultTtlMs);
72
+ }
73
+ catch {
74
+ // Fallback for older Redis versions that don't support GETEX
75
+ raw = await this.redis.get(key);
76
+ }
77
+ if (!raw)
78
+ return null;
79
+ try {
80
+ const parsed = JSON.parse(raw);
81
+ const result = transport_session_types_1.storedSessionSchema.safeParse(parsed);
82
+ if (!result.success) {
83
+ this.logger?.warn('[RedisSessionStore] Invalid session format', {
84
+ sessionId: sessionId.slice(0, 20),
85
+ errors: result.error.issues.slice(0, 3).map((i) => ({ path: i.path, message: i.message })),
86
+ });
87
+ // Delete invalid session data
88
+ this.delete(sessionId).catch(() => void 0);
89
+ return null;
90
+ }
91
+ const session = result.data;
92
+ // Check application-level expiration (separate from Redis TTL)
93
+ if (session.session.expiresAt && session.session.expiresAt < Date.now()) {
94
+ // Session is logically expired - delete it
95
+ // Note: We await the delete to ensure it completes before returning
96
+ // This prevents race conditions where another read might get the expired session
97
+ await this.delete(sessionId);
98
+ return null;
99
+ }
100
+ // Bound Redis TTL by session.expiresAt to avoid keeping expired sessions in Redis
101
+ // GETEX may have extended TTL beyond expiresAt, so we shorten it if needed
102
+ if (session.session.expiresAt) {
103
+ const ttlMs = Math.min(this.defaultTtlMs, session.session.expiresAt - Date.now());
104
+ if (ttlMs > 0 && ttlMs < this.defaultTtlMs) {
105
+ // Fire-and-forget - we're only optimizing cache eviction timing
106
+ this.redis.pexpire(key, ttlMs).catch(() => void 0);
107
+ }
108
+ }
109
+ // Update last accessed timestamp (in the returned object)
110
+ // Note: We don't fire-and-forget a set() here because:
111
+ // 1. GETEX already extended the Redis TTL
112
+ // 2. Fire-and-forget can cause race conditions with deletion
113
+ const updatedSession = {
114
+ ...session,
115
+ lastAccessedAt: Date.now(),
116
+ };
117
+ return updatedSession;
118
+ }
119
+ catch (error) {
120
+ this.logger?.warn('[RedisSessionStore] Failed to parse session', {
121
+ sessionId: sessionId.slice(0, 20),
122
+ error: error.message,
123
+ });
124
+ // Delete corrupted session payloads to prevent repeated failures
125
+ this.delete(sessionId).catch(() => void 0);
126
+ return null;
127
+ }
128
+ }
129
+ /**
130
+ * Store a session with optional TTL
131
+ */
132
+ async set(sessionId, session, ttlMs) {
133
+ const key = this.key(sessionId);
134
+ const value = JSON.stringify(session);
135
+ if (ttlMs && ttlMs > 0) {
136
+ // Use PX for millisecond precision
137
+ await this.redis.set(key, value, 'PX', ttlMs);
138
+ }
139
+ else if (session.session.expiresAt) {
140
+ // Use session's expiration if available
141
+ const ttl = session.session.expiresAt - Date.now();
142
+ if (ttl > 0) {
143
+ await this.redis.set(key, value, 'PX', ttl);
144
+ }
145
+ else {
146
+ // Already expired, but store anyway (will be cleaned up on next access)
147
+ await this.redis.set(key, value);
148
+ }
149
+ }
150
+ else {
151
+ // No TTL - session persists until explicitly deleted
152
+ await this.redis.set(key, value);
153
+ }
154
+ }
155
+ /**
156
+ * Delete a session
157
+ */
158
+ async delete(sessionId) {
159
+ await this.redis.del(this.key(sessionId));
160
+ }
161
+ /**
162
+ * Check if a session exists
163
+ */
164
+ async exists(sessionId) {
165
+ return (await this.redis.exists(this.key(sessionId))) === 1;
166
+ }
167
+ /**
168
+ * Allocate a new session ID
169
+ */
170
+ allocId() {
171
+ return (0, crypto_1.randomUUID)();
172
+ }
173
+ /**
174
+ * Disconnect from Redis (only if we created the connection)
175
+ */
176
+ async disconnect() {
177
+ if (!this.externalInstance) {
178
+ await this.redis.quit();
179
+ }
180
+ }
181
+ /**
182
+ * Get the underlying Redis client (for advanced use cases)
183
+ */
184
+ getRedisClient() {
185
+ return this.redis;
186
+ }
187
+ /**
188
+ * Test Redis connection by sending a PING command.
189
+ * Useful for validating connection on startup.
190
+ *
191
+ * @returns true if connection is healthy, false otherwise
192
+ */
193
+ async ping() {
194
+ try {
195
+ const result = await this.redis.ping();
196
+ return result === 'PONG';
197
+ }
198
+ catch {
199
+ return false;
200
+ }
201
+ }
202
+ }
203
+ exports.RedisSessionStore = RedisSessionStore;
204
+ //# sourceMappingURL=redis-session.store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis-session.store.js","sourceRoot":"","sources":["../../../../src/auth/session/redis-session.store.ts"],"names":[],"mappings":"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","sourcesContent":["// auth/session/redis-session.store.ts\nimport IoRedis, { Redis, RedisOptions } from 'ioredis';\nimport { randomUUID } from 'crypto';\nimport { SessionStore, StoredSession, RedisConfig, storedSessionSchema } from './transport-session.types';\nimport { FrontMcpLogger } from '../../common/interfaces/logger.interface';\n\n/**\n * Redis-backed session store implementation\n *\n * Provides persistent session storage for distributed deployments.\n * Sessions are stored as JSON with optional TTL.\n */\nexport class RedisSessionStore implements SessionStore {\n private readonly redis: Redis;\n private readonly keyPrefix: string;\n private readonly defaultTtlMs: number;\n private readonly logger?: FrontMcpLogger;\n private externalInstance = false;\n\n constructor(\n config: RedisConfig | { redis: Redis; keyPrefix?: string; defaultTtlMs?: number },\n logger?: FrontMcpLogger,\n ) {\n // Default TTL of 1 hour for session extension on access\n this.defaultTtlMs = ('defaultTtlMs' in config ? config.defaultTtlMs : undefined) ?? 3600000;\n this.logger = logger;\n\n if ('redis' in config && config.redis) {\n // Use provided Redis instance\n this.redis = config.redis;\n this.keyPrefix = config.keyPrefix ?? 'mcp:session:';\n this.externalInstance = true;\n } else {\n // Create new Redis connection from config\n const redisConfig = config as RedisConfig;\n const options: RedisOptions = {\n host: redisConfig.host,\n port: redisConfig.port ?? 6379,\n password: redisConfig.password,\n db: redisConfig.db ?? 0,\n };\n\n if (redisConfig.tls) {\n options.tls = {};\n }\n\n this.redis = new IoRedis(options);\n this.keyPrefix = redisConfig.keyPrefix ?? 'mcp:session:';\n }\n }\n\n /**\n * Get the full Redis key for a session ID\n * @throws Error if sessionId is empty\n */\n private key(sessionId: string): string {\n if (!sessionId || sessionId.trim() === '') {\n throw new Error('[RedisSessionStore] sessionId cannot be empty');\n }\n return `${this.keyPrefix}${sessionId}`;\n }\n\n /**\n * Get a stored session by ID\n *\n * Note: Uses atomic GETEX to extend TTL while reading, preventing race conditions\n * where concurrent readers might resurrect expired sessions.\n */\n async get(sessionId: string): Promise<StoredSession | null> {\n const key = this.key(sessionId);\n\n // Use GETEX to atomically get and extend TTL in a single operation\n // This prevents the race where one request deletes expired session\n // while another is trying to extend it\n let raw: string | null;\n try {\n // GETEX with EXAT/PXAT is atomic - no race condition possible\n raw = await this.redis.getex(key, 'PX', this.defaultTtlMs);\n } catch {\n // Fallback for older Redis versions that don't support GETEX\n raw = await this.redis.get(key);\n }\n\n if (!raw) return null;\n\n try {\n const parsed = JSON.parse(raw);\n const result = storedSessionSchema.safeParse(parsed);\n\n if (!result.success) {\n this.logger?.warn('[RedisSessionStore] Invalid session format', {\n sessionId: sessionId.slice(0, 20),\n errors: result.error.issues.slice(0, 3).map((i) => ({ path: i.path, message: i.message })),\n });\n // Delete invalid session data\n this.delete(sessionId).catch(() => void 0);\n return null;\n }\n\n const session = result.data;\n\n // Check application-level expiration (separate from Redis TTL)\n if (session.session.expiresAt && session.session.expiresAt < Date.now()) {\n // Session is logically expired - delete it\n // Note: We await the delete to ensure it completes before returning\n // This prevents race conditions where another read might get the expired session\n await this.delete(sessionId);\n return null;\n }\n\n // Bound Redis TTL by session.expiresAt to avoid keeping expired sessions in Redis\n // GETEX may have extended TTL beyond expiresAt, so we shorten it if needed\n if (session.session.expiresAt) {\n const ttlMs = Math.min(this.defaultTtlMs, session.session.expiresAt - Date.now());\n if (ttlMs > 0 && ttlMs < this.defaultTtlMs) {\n // Fire-and-forget - we're only optimizing cache eviction timing\n this.redis.pexpire(key, ttlMs).catch(() => void 0);\n }\n }\n\n // Update last accessed timestamp (in the returned object)\n // Note: We don't fire-and-forget a set() here because:\n // 1. GETEX already extended the Redis TTL\n // 2. Fire-and-forget can cause race conditions with deletion\n const updatedSession: StoredSession = {\n ...session,\n lastAccessedAt: Date.now(),\n };\n\n return updatedSession;\n } catch (error) {\n this.logger?.warn('[RedisSessionStore] Failed to parse session', {\n sessionId: sessionId.slice(0, 20),\n error: (error as Error).message,\n });\n // Delete corrupted session payloads to prevent repeated failures\n this.delete(sessionId).catch(() => void 0);\n return null;\n }\n }\n\n /**\n * Store a session with optional TTL\n */\n async set(sessionId: string, session: StoredSession, ttlMs?: number): Promise<void> {\n const key = this.key(sessionId);\n const value = JSON.stringify(session);\n\n if (ttlMs && ttlMs > 0) {\n // Use PX for millisecond precision\n await this.redis.set(key, value, 'PX', ttlMs);\n } else if (session.session.expiresAt) {\n // Use session's expiration if available\n const ttl = session.session.expiresAt - Date.now();\n if (ttl > 0) {\n await this.redis.set(key, value, 'PX', ttl);\n } else {\n // Already expired, but store anyway (will be cleaned up on next access)\n await this.redis.set(key, value);\n }\n } else {\n // No TTL - session persists until explicitly deleted\n await this.redis.set(key, value);\n }\n }\n\n /**\n * Delete a session\n */\n async delete(sessionId: string): Promise<void> {\n await this.redis.del(this.key(sessionId));\n }\n\n /**\n * Check if a session exists\n */\n async exists(sessionId: string): Promise<boolean> {\n return (await this.redis.exists(this.key(sessionId))) === 1;\n }\n\n /**\n * Allocate a new session ID\n */\n allocId(): string {\n return randomUUID();\n }\n\n /**\n * Disconnect from Redis (only if we created the connection)\n */\n async disconnect(): Promise<void> {\n if (!this.externalInstance) {\n await this.redis.quit();\n }\n }\n\n /**\n * Get the underlying Redis client (for advanced use cases)\n */\n getRedisClient(): Redis {\n return this.redis;\n }\n\n /**\n * Test Redis connection by sending a PING command.\n * Useful for validating connection on startup.\n *\n * @returns true if connection is healthy, false otherwise\n */\n async ping(): Promise<boolean> {\n try {\n const result = await this.redis.ping();\n return result === 'PONG';\n } catch {\n return false;\n }\n }\n}\n"]}
@@ -3,10 +3,8 @@ import { StatefulSession } from './record/session.stateful';
3
3
  import { Scope } from '../../scope';
4
4
  import { CreateSessionArgs } from './session.types';
5
5
  import { TransparentSession } from './record/session.transparent';
6
- import { Authorization } from '../../common';
7
6
  export declare class SessionService {
8
7
  private store;
9
- keyOf(authorization: Authorization): Promise<void>;
10
8
  /**
11
9
  * Create and persist a new Session from verified auth data.
12
10
  * The returned Session exposes async token helpers, scoped view, and transport JWT helpers.
@@ -6,14 +6,8 @@ const session_stateless_1 = require("./record/session.stateless");
6
6
  const session_stateful_1 = require("./record/session.stateful");
7
7
  const session_transparent_1 = require("./record/session.transparent");
8
8
  const store_1 = require("../../store");
9
- const session_id_utils_1 = require("./utils/session-id.utils");
10
9
  class SessionService {
11
10
  store = new store_1.ScopedInMemoryStore();
12
- async keyOf(authorization) {
13
- const sessionKey = (0, session_id_utils_1.encryptJson)({ token: authorization.token });
14
- if (authorization.session) {
15
- }
16
- }
17
11
  /**
18
12
  * Create and persist a new Session from verified auth data.
19
13
  * The returned Session exposes async token helpers, scoped view, and transport JWT helpers.
@@ -27,7 +21,7 @@ class SessionService {
27
21
  }
28
22
  }
29
23
  createOrchestratedSession(scope, args) {
30
- const stateless = scope.metadata.session?.sessionMode === 'stateless';
24
+ const stateless = scope.metadata.transport?.sessionMode === 'stateless';
31
25
  if (stateless) {
32
26
  return new session_stateless_1.StatelessSession(args);
33
27
  }
@@ -1 +1 @@
1
- {"version":3,"file":"session.service.js","sourceRoot":"","sources":["../../../../src/auth/session/session.service.ts"],"names":[],"mappings":"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","sourcesContent":["// auth/session/session.service.ts\nimport { StatelessSession } from './record/session.stateless';\nimport { StatefulSession } from './record/session.stateful';\nimport { Scope } from '../../scope';\nimport { CreateSessionArgs } from './session.types';\nimport { TransparentSession } from './record/session.transparent';\nimport { Authorization } from '../../common';\nimport { ScopedInMemoryStore } from '../../store';\nimport { encryptJson } from './utils/session-id.utils';\n\nexport class SessionService {\n private store = new ScopedInMemoryStore()\n\n\n async keyOf(authorization: Authorization) {\n const sessionKey = encryptJson({token:authorization.token});\n if(authorization.session){\n\n }\n }\n\n /**\n * Create and persist a new Session from verified auth data.\n * The returned Session exposes async token helpers, scoped view, and transport JWT helpers.\n */\n async createSession(scope: Scope, args: CreateSessionArgs) {\n if (scope.orchestrated) {\n return this.createOrchestratedSession(scope, args);\n } else {\n return this.createTransparentSession(scope, args);\n }\n }\n\n private createOrchestratedSession(scope: Scope, args: CreateSessionArgs) {\n const stateless = scope.metadata.session?.sessionMode === 'stateless';\n if (stateless) {\n return new StatelessSession(args as any);\n } else {\n return new StatefulSession(args as any);\n }\n }\n\n private createTransparentSession(scope: Scope, args: CreateSessionArgs) {\n const primary = scope.auth;\n\n const apps = scope.apps.getApps();\n const appIds = apps.map((app) => app.id);\n\n // Prefer precomputed projections when provided\n let authorizedApps: Record<string, { id: string; toolIds: string[] }> = args.authorizedApps ?? {};\n if (!args.authorizedApps) {\n authorizedApps = {};\n for (const app of apps) {\n try {\n const toolNames = app.tools.getTools().map((t) => String(t.metadata.name));\n authorizedApps[app.id] = { id: app.id, toolIds: toolNames };\n } catch {\n authorizedApps[app.id] = { id: app.id, toolIds: [] };\n }\n }\n }\n\n // TODO: the authorized resources should be computed from the oauth-protected-resource flow\n // let authorizedResources: string[] = args.authorizedResources ?? [];\n // if (!args.authorizedResources) {\n // authorizedResources = [];\n // }\n\n // Providers snapshot\n let authorizedProviders = args.authorizedProviders;\n let authorizedProviderIds = args.authorizedProviderIds;\n if (!authorizedProviders || !authorizedProviderIds) {\n const expClaim =\n args.claims && typeof (args.claims as any)['exp'] === 'number'\n ? Number((args.claims as any)['exp'])\n : undefined;\n const providerSnapshot = {\n id: primary.id,\n exp: expClaim,\n payload: args.claims ?? {},\n apps: appIds.map((id) => ({ id, toolIds: authorizedApps[id]?.toolIds ?? [] })),\n embedMode: 'plain' as const,\n };\n authorizedProviders = { [primary.id]: providerSnapshot } as any;\n authorizedProviderIds = [primary.id];\n }\n\n // resolve granted scopes from token claims (scope or scp)\n let scopes: string[] = args.scopes ?? [];\n if (!args.scopes) {\n const rawScope = (args.claims && ((args.claims as any)['scope'] ?? (args.claims as any)['scp'])) as unknown;\n scopes = Array.isArray(rawScope)\n ? rawScope.map(String)\n : typeof rawScope === 'string'\n ? rawScope.split(/[\\s,]+/).filter(Boolean)\n : [];\n }\n\n return new TransparentSession({\n apps: appIds,\n id: args.token,\n sessionId: args.sessionId,\n scope,\n user: args.user,\n issuer: primary.issuer,\n token: args.token,\n claims: args.claims,\n authorizedProviders: authorizedProviders as any,\n authorizedProviderIds: authorizedProviderIds as any,\n authorizedApps,\n authorizedAppIds: appIds,\n authorizedResources: [], // TODO: fix\n scopes,\n authorizedTools: args.authorizedTools,\n authorizedToolIds: args.authorizedToolIds,\n authorizedPrompts: args.authorizedPrompts,\n authorizedPromptIds: args.authorizedPromptIds,\n } as any);\n }\n}\n"]}
1
+ {"version":3,"file":"session.service.js","sourceRoot":"","sources":["../../../../src/auth/session/session.service.ts"],"names":[],"mappings":"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","sourcesContent":["// auth/session/session.service.ts\nimport { StatelessSession } from './record/session.stateless';\nimport { StatefulSession } from './record/session.stateful';\nimport { Scope } from '../../scope';\nimport { CreateSessionArgs } from './session.types';\nimport { TransparentSession } from './record/session.transparent';\nimport { ScopedInMemoryStore } from '../../store';\n\nexport class SessionService {\n private store = new ScopedInMemoryStore();\n\n /**\n * Create and persist a new Session from verified auth data.\n * The returned Session exposes async token helpers, scoped view, and transport JWT helpers.\n */\n async createSession(scope: Scope, args: CreateSessionArgs) {\n if (scope.orchestrated) {\n return this.createOrchestratedSession(scope, args);\n } else {\n return this.createTransparentSession(scope, args);\n }\n }\n\n private createOrchestratedSession(scope: Scope, args: CreateSessionArgs) {\n const stateless = scope.metadata.transport?.sessionMode === 'stateless';\n if (stateless) {\n return new StatelessSession(args as any);\n } else {\n return new StatefulSession(args as any);\n }\n }\n\n private createTransparentSession(scope: Scope, args: CreateSessionArgs) {\n const primary = scope.auth;\n\n const apps = scope.apps.getApps();\n const appIds = apps.map((app) => app.id);\n\n // Prefer precomputed projections when provided\n let authorizedApps: Record<string, { id: string; toolIds: string[] }> = args.authorizedApps ?? {};\n if (!args.authorizedApps) {\n authorizedApps = {};\n for (const app of apps) {\n try {\n const toolNames = app.tools.getTools().map((t) => String(t.metadata.name));\n authorizedApps[app.id] = { id: app.id, toolIds: toolNames };\n } catch {\n authorizedApps[app.id] = { id: app.id, toolIds: [] };\n }\n }\n }\n\n // TODO: the authorized resources should be computed from the oauth-protected-resource flow\n // let authorizedResources: string[] = args.authorizedResources ?? [];\n // if (!args.authorizedResources) {\n // authorizedResources = [];\n // }\n\n // Providers snapshot\n let authorizedProviders = args.authorizedProviders;\n let authorizedProviderIds = args.authorizedProviderIds;\n if (!authorizedProviders || !authorizedProviderIds) {\n const expClaim =\n args.claims && typeof (args.claims as any)['exp'] === 'number'\n ? Number((args.claims as any)['exp'])\n : undefined;\n const providerSnapshot = {\n id: primary.id,\n exp: expClaim,\n payload: args.claims ?? {},\n apps: appIds.map((id) => ({ id, toolIds: authorizedApps[id]?.toolIds ?? [] })),\n embedMode: 'plain' as const,\n };\n authorizedProviders = { [primary.id]: providerSnapshot } as any;\n authorizedProviderIds = [primary.id];\n }\n\n // resolve granted scopes from token claims (scope or scp)\n let scopes: string[] = args.scopes ?? [];\n if (!args.scopes) {\n const rawScope = (args.claims && ((args.claims as any)['scope'] ?? (args.claims as any)['scp'])) as unknown;\n scopes = Array.isArray(rawScope)\n ? rawScope.map(String)\n : typeof rawScope === 'string'\n ? rawScope.split(/[\\s,]+/).filter(Boolean)\n : [];\n }\n\n return new TransparentSession({\n apps: appIds,\n id: args.token,\n sessionId: args.sessionId,\n scope,\n user: args.user,\n issuer: primary.issuer,\n token: args.token,\n claims: args.claims,\n authorizedProviders: authorizedProviders as any,\n authorizedProviderIds: authorizedProviderIds as any,\n authorizedApps,\n authorizedAppIds: appIds,\n authorizedResources: [], // TODO: fix\n scopes,\n authorizedTools: args.authorizedTools,\n authorizedToolIds: args.authorizedToolIds,\n authorizedPrompts: args.authorizedPrompts,\n authorizedPromptIds: args.authorizedPromptIds,\n } as any);\n }\n}\n"]}
@@ -6,6 +6,7 @@ const crypto_1 = require("crypto");
6
6
  const session_id_utils_1 = require("./utils/session-id.utils");
7
7
  const session_crypto_1 = require("./session.crypto");
8
8
  const authorization_class_1 = require("../authorization/authorization.class");
9
+ const redis_session_store_1 = require("./redis-session.store");
9
10
  /**
10
11
  * In-memory session store implementation
11
12
  */
@@ -93,11 +94,8 @@ class TransportSessionManager {
93
94
  this.store = new InMemorySessionStore();
94
95
  }
95
96
  else if (config.store === 'redis') {
96
- // Redis store would be instantiated here
97
- // For now, fall back to in-memory
98
- // TODO: Implement RedisSessionStore
99
- console.warn('[TransportSessionManager] Redis store requested but not implemented - falling back to in-memory');
100
- this.store = new InMemorySessionStore();
97
+ // Instantiate Redis session store
98
+ this.store = new redis_session_store_1.RedisSessionStore(config.config);
101
99
  }
102
100
  else {
103
101
  this.store = new InMemorySessionStore();