@soulbatical/tetra-core 0.10.5 → 0.11.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 (245) hide show
  1. package/README.md +32 -6
  2. package/dist/core/createApp.d.ts +1 -1
  3. package/dist/core/createApp.d.ts.map +1 -1
  4. package/dist/generators/rls-checker.d.ts +1 -1
  5. package/dist/generators/rls-checker.js +1 -1
  6. package/dist/generators/rls-exec-sql.d.ts +1 -1
  7. package/dist/generators/rls-exec-sql.js +1 -1
  8. package/dist/generators/rpc/index.d.ts +1 -1
  9. package/dist/generators/rpc/index.js +1 -1
  10. package/dist/index.d.ts +0 -6
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +3 -9
  13. package/dist/index.js.map +1 -1
  14. package/dist/middleware/securityMiddleware.d.ts +1 -1
  15. package/dist/middleware/securityMiddleware.d.ts.map +1 -1
  16. package/dist/utils/logger.d.ts.map +1 -1
  17. package/dist/utils/logger.js +12 -1
  18. package/dist/utils/logger.js.map +1 -1
  19. package/package.json +30 -44
  20. package/dist/affiliate.d.ts +0 -11
  21. package/dist/affiliate.d.ts.map +0 -1
  22. package/dist/affiliate.js +0 -10
  23. package/dist/affiliate.js.map +0 -1
  24. package/dist/billing.d.ts +0 -8
  25. package/dist/billing.d.ts.map +0 -1
  26. package/dist/billing.js +0 -7
  27. package/dist/billing.js.map +0 -1
  28. package/dist/email.d.ts +0 -9
  29. package/dist/email.d.ts.map +0 -1
  30. package/dist/email.js +0 -8
  31. package/dist/email.js.map +0 -1
  32. package/dist/generators/rls-exec-sql.sql +0 -57
  33. package/dist/generators.d.ts +0 -9
  34. package/dist/generators.d.ts.map +0 -1
  35. package/dist/generators.js +0 -8
  36. package/dist/generators.js.map +0 -1
  37. package/dist/mcp.d.ts +0 -8
  38. package/dist/mcp.d.ts.map +0 -1
  39. package/dist/mcp.js +0 -7
  40. package/dist/mcp.js.map +0 -1
  41. package/dist/planner.d.ts +0 -8
  42. package/dist/planner.d.ts.map +0 -1
  43. package/dist/planner.js +0 -7
  44. package/dist/planner.js.map +0 -1
  45. package/dist/shared/affiliate/AffiliateAttributionService.d.ts +0 -47
  46. package/dist/shared/affiliate/AffiliateAttributionService.d.ts.map +0 -1
  47. package/dist/shared/affiliate/AffiliateAttributionService.js +0 -308
  48. package/dist/shared/affiliate/AffiliateAttributionService.js.map +0 -1
  49. package/dist/shared/affiliate/AffiliateClickService.d.ts +0 -35
  50. package/dist/shared/affiliate/AffiliateClickService.d.ts.map +0 -1
  51. package/dist/shared/affiliate/AffiliateClickService.js +0 -87
  52. package/dist/shared/affiliate/AffiliateClickService.js.map +0 -1
  53. package/dist/shared/affiliate/affiliateFeatureConfig.d.ts +0 -11
  54. package/dist/shared/affiliate/affiliateFeatureConfig.d.ts.map +0 -1
  55. package/dist/shared/affiliate/affiliateFeatureConfig.js +0 -242
  56. package/dist/shared/affiliate/affiliateFeatureConfig.js.map +0 -1
  57. package/dist/shared/affiliate/index.d.ts +0 -11
  58. package/dist/shared/affiliate/index.d.ts.map +0 -1
  59. package/dist/shared/affiliate/index.js +0 -13
  60. package/dist/shared/affiliate/index.js.map +0 -1
  61. package/dist/shared/affiliate/routes.d.ts +0 -87
  62. package/dist/shared/affiliate/routes.d.ts.map +0 -1
  63. package/dist/shared/affiliate/routes.js +0 -404
  64. package/dist/shared/affiliate/routes.js.map +0 -1
  65. package/dist/shared/affiliate/types.d.ts +0 -170
  66. package/dist/shared/affiliate/types.d.ts.map +0 -1
  67. package/dist/shared/affiliate/types.js +0 -11
  68. package/dist/shared/affiliate/types.js.map +0 -1
  69. package/dist/shared/billing/BillingService.d.ts +0 -56
  70. package/dist/shared/billing/BillingService.d.ts.map +0 -1
  71. package/dist/shared/billing/BillingService.js +0 -588
  72. package/dist/shared/billing/BillingService.js.map +0 -1
  73. package/dist/shared/billing/SeatBillingService.d.ts +0 -106
  74. package/dist/shared/billing/SeatBillingService.d.ts.map +0 -1
  75. package/dist/shared/billing/SeatBillingService.js +0 -292
  76. package/dist/shared/billing/SeatBillingService.js.map +0 -1
  77. package/dist/shared/billing/index.d.ts +0 -30
  78. package/dist/shared/billing/index.d.ts.map +0 -1
  79. package/dist/shared/billing/index.js +0 -27
  80. package/dist/shared/billing/index.js.map +0 -1
  81. package/dist/shared/billing/routes.d.ts +0 -45
  82. package/dist/shared/billing/routes.d.ts.map +0 -1
  83. package/dist/shared/billing/routes.js +0 -184
  84. package/dist/shared/billing/routes.js.map +0 -1
  85. package/dist/shared/billing/seat-pricing.d.ts +0 -53
  86. package/dist/shared/billing/seat-pricing.d.ts.map +0 -1
  87. package/dist/shared/billing/seat-pricing.js +0 -81
  88. package/dist/shared/billing/seat-pricing.js.map +0 -1
  89. package/dist/shared/billing/types.d.ts +0 -109
  90. package/dist/shared/billing/types.d.ts.map +0 -1
  91. package/dist/shared/billing/types.js +0 -8
  92. package/dist/shared/billing/types.js.map +0 -1
  93. package/dist/shared/email/EmailService.d.ts +0 -64
  94. package/dist/shared/email/EmailService.d.ts.map +0 -1
  95. package/dist/shared/email/EmailService.js +0 -300
  96. package/dist/shared/email/EmailService.js.map +0 -1
  97. package/dist/shared/email/adminRoutes.d.ts +0 -30
  98. package/dist/shared/email/adminRoutes.d.ts.map +0 -1
  99. package/dist/shared/email/adminRoutes.js +0 -227
  100. package/dist/shared/email/adminRoutes.js.map +0 -1
  101. package/dist/shared/email/gmail.d.ts +0 -208
  102. package/dist/shared/email/gmail.d.ts.map +0 -1
  103. package/dist/shared/email/gmail.js +0 -626
  104. package/dist/shared/email/gmail.js.map +0 -1
  105. package/dist/shared/email/index.d.ts +0 -15
  106. package/dist/shared/email/index.d.ts.map +0 -1
  107. package/dist/shared/email/index.js +0 -18
  108. package/dist/shared/email/index.js.map +0 -1
  109. package/dist/shared/email/mailgun.d.ts +0 -18
  110. package/dist/shared/email/mailgun.d.ts.map +0 -1
  111. package/dist/shared/email/mailgun.js +0 -76
  112. package/dist/shared/email/mailgun.js.map +0 -1
  113. package/dist/shared/email/sanitize.d.ts +0 -25
  114. package/dist/shared/email/sanitize.d.ts.map +0 -1
  115. package/dist/shared/email/sanitize.js +0 -39
  116. package/dist/shared/email/sanitize.js.map +0 -1
  117. package/dist/shared/email/smtp.d.ts +0 -20
  118. package/dist/shared/email/smtp.d.ts.map +0 -1
  119. package/dist/shared/email/smtp.js +0 -53
  120. package/dist/shared/email/smtp.js.map +0 -1
  121. package/dist/shared/email/types.d.ts +0 -113
  122. package/dist/shared/email/types.d.ts.map +0 -1
  123. package/dist/shared/email/types.js +0 -7
  124. package/dist/shared/email/types.js.map +0 -1
  125. package/dist/shared/email/webhookRoutes.d.ts +0 -29
  126. package/dist/shared/email/webhookRoutes.d.ts.map +0 -1
  127. package/dist/shared/email/webhookRoutes.js +0 -125
  128. package/dist/shared/email/webhookRoutes.js.map +0 -1
  129. package/dist/shared/mcp/index.d.ts +0 -51
  130. package/dist/shared/mcp/index.d.ts.map +0 -1
  131. package/dist/shared/mcp/index.js +0 -51
  132. package/dist/shared/mcp/index.js.map +0 -1
  133. package/dist/shared/mcp/mcp-auth-routes.d.ts +0 -26
  134. package/dist/shared/mcp/mcp-auth-routes.d.ts.map +0 -1
  135. package/dist/shared/mcp/mcp-auth-routes.js +0 -141
  136. package/dist/shared/mcp/mcp-auth-routes.js.map +0 -1
  137. package/dist/shared/mcp/mcp-db.d.ts +0 -99
  138. package/dist/shared/mcp/mcp-db.d.ts.map +0 -1
  139. package/dist/shared/mcp/mcp-db.js +0 -106
  140. package/dist/shared/mcp/mcp-db.js.map +0 -1
  141. package/dist/shared/mcp/mcp-routes.d.ts +0 -29
  142. package/dist/shared/mcp/mcp-routes.d.ts.map +0 -1
  143. package/dist/shared/mcp/mcp-routes.js +0 -171
  144. package/dist/shared/mcp/mcp-routes.js.map +0 -1
  145. package/dist/shared/mcp/mcp-tokens-routes.d.ts +0 -35
  146. package/dist/shared/mcp/mcp-tokens-routes.d.ts.map +0 -1
  147. package/dist/shared/mcp/mcp-tokens-routes.js +0 -94
  148. package/dist/shared/mcp/mcp-tokens-routes.js.map +0 -1
  149. package/dist/shared/mcp/mcp-usage-routes.d.ts +0 -17
  150. package/dist/shared/mcp/mcp-usage-routes.d.ts.map +0 -1
  151. package/dist/shared/mcp/mcp-usage-routes.js +0 -81
  152. package/dist/shared/mcp/mcp-usage-routes.js.map +0 -1
  153. package/dist/shared/mcp/tenant-context.d.ts +0 -59
  154. package/dist/shared/mcp/tenant-context.d.ts.map +0 -1
  155. package/dist/shared/mcp/tenant-context.js +0 -136
  156. package/dist/shared/mcp/tenant-context.js.map +0 -1
  157. package/dist/shared/mcp/types.d.ts +0 -74
  158. package/dist/shared/mcp/types.d.ts.map +0 -1
  159. package/dist/shared/mcp/types.js +0 -7
  160. package/dist/shared/mcp/types.js.map +0 -1
  161. package/dist/shared/planner/GoogleCalendarService.d.ts +0 -137
  162. package/dist/shared/planner/GoogleCalendarService.d.ts.map +0 -1
  163. package/dist/shared/planner/GoogleCalendarService.js +0 -525
  164. package/dist/shared/planner/GoogleCalendarService.js.map +0 -1
  165. package/dist/shared/planner/PlannerService.d.ts +0 -264
  166. package/dist/shared/planner/PlannerService.d.ts.map +0 -1
  167. package/dist/shared/planner/PlannerService.js +0 -1393
  168. package/dist/shared/planner/PlannerService.js.map +0 -1
  169. package/dist/shared/planner/index.d.ts +0 -37
  170. package/dist/shared/planner/index.d.ts.map +0 -1
  171. package/dist/shared/planner/index.js +0 -35
  172. package/dist/shared/planner/index.js.map +0 -1
  173. package/dist/shared/planner/intervals.d.ts +0 -60
  174. package/dist/shared/planner/intervals.d.ts.map +0 -1
  175. package/dist/shared/planner/intervals.js +0 -141
  176. package/dist/shared/planner/intervals.js.map +0 -1
  177. package/dist/shared/planner/routes.d.ts +0 -69
  178. package/dist/shared/planner/routes.d.ts.map +0 -1
  179. package/dist/shared/planner/routes.js +0 -770
  180. package/dist/shared/planner/routes.js.map +0 -1
  181. package/dist/shared/planner/types.d.ts +0 -328
  182. package/dist/shared/planner/types.d.ts.map +0 -1
  183. package/dist/shared/planner/types.js +0 -9
  184. package/dist/shared/planner/types.js.map +0 -1
  185. package/dist/shared/storage/ImageProcessingService.d.ts +0 -32
  186. package/dist/shared/storage/ImageProcessingService.d.ts.map +0 -1
  187. package/dist/shared/storage/ImageProcessingService.js +0 -127
  188. package/dist/shared/storage/ImageProcessingService.js.map +0 -1
  189. package/dist/shared/storage/StorageProxyService.d.ts +0 -47
  190. package/dist/shared/storage/StorageProxyService.d.ts.map +0 -1
  191. package/dist/shared/storage/StorageProxyService.js +0 -196
  192. package/dist/shared/storage/StorageProxyService.js.map +0 -1
  193. package/dist/shared/storage/StorageUploadService.d.ts +0 -126
  194. package/dist/shared/storage/StorageUploadService.d.ts.map +0 -1
  195. package/dist/shared/storage/StorageUploadService.js +0 -206
  196. package/dist/shared/storage/StorageUploadService.js.map +0 -1
  197. package/dist/shared/storage/creative-urls.d.ts +0 -14
  198. package/dist/shared/storage/creative-urls.d.ts.map +0 -1
  199. package/dist/shared/storage/creative-urls.js +0 -30
  200. package/dist/shared/storage/creative-urls.js.map +0 -1
  201. package/dist/shared/storage/index.d.ts +0 -28
  202. package/dist/shared/storage/index.d.ts.map +0 -1
  203. package/dist/shared/storage/index.js +0 -27
  204. package/dist/shared/storage/index.js.map +0 -1
  205. package/dist/shared/storage/routes.d.ts +0 -42
  206. package/dist/shared/storage/routes.d.ts.map +0 -1
  207. package/dist/shared/storage/routes.js +0 -160
  208. package/dist/shared/storage/routes.js.map +0 -1
  209. package/dist/shared/storage/types.d.ts +0 -53
  210. package/dist/shared/storage/types.d.ts.map +0 -1
  211. package/dist/shared/storage/types.js +0 -2
  212. package/dist/shared/storage/types.js.map +0 -1
  213. package/dist/shared/telegram/index.d.ts +0 -4
  214. package/dist/shared/telegram/index.d.ts.map +0 -1
  215. package/dist/shared/telegram/index.js +0 -3
  216. package/dist/shared/telegram/index.js.map +0 -1
  217. package/dist/shared/telegram/routes.d.ts +0 -48
  218. package/dist/shared/telegram/routes.d.ts.map +0 -1
  219. package/dist/shared/telegram/routes.js +0 -918
  220. package/dist/shared/telegram/routes.js.map +0 -1
  221. package/dist/shared/telegram/types.d.ts +0 -186
  222. package/dist/shared/telegram/types.d.ts.map +0 -1
  223. package/dist/shared/telegram/types.js +0 -7
  224. package/dist/shared/telegram/types.js.map +0 -1
  225. package/dist/shared/telegram/utils.d.ts +0 -44
  226. package/dist/shared/telegram/utils.d.ts.map +0 -1
  227. package/dist/shared/telegram/utils.js +0 -121
  228. package/dist/shared/telegram/utils.js.map +0 -1
  229. package/dist/storage.d.ts +0 -9
  230. package/dist/storage.d.ts.map +0 -1
  231. package/dist/storage.js +0 -8
  232. package/dist/storage.js.map +0 -1
  233. package/dist/telemetry.d.ts +0 -9
  234. package/dist/telemetry.d.ts.map +0 -1
  235. package/dist/telemetry.js +0 -8
  236. package/dist/telemetry.js.map +0 -1
  237. package/scripts/postinstall.js +0 -79
  238. package/src/shared/affiliate/migrations/001_create_affiliates.sql +0 -49
  239. package/src/shared/affiliate/migrations/002_create_affiliate_commissions.sql +0 -31
  240. package/src/shared/affiliate/migrations/003_create_affiliate_clicks.sql +0 -26
  241. package/src/shared/affiliate/migrations/004_create_affiliate_payments.sql +0 -34
  242. package/src/shared/affiliate/migrations/005_create_affiliate_tier_history.sql +0 -19
  243. package/src/shared/affiliate/migrations/006_create_affiliate_rpc_functions.sql +0 -209
  244. package/src/shared/affiliate/migrations/007_create_affiliate_rls_policies.sql +0 -123
  245. package/src/shared/billing/migrations/00000000000001_billing.sql +0 -114
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/shared/mcp/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAG1D,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC5I,YAAY,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AAGtE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACjD,YAAY,EAAE,WAAW,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG/E,YAAY,EAAE,eAAe,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,aAAa,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
@@ -1,51 +0,0 @@
1
- /**
2
- * MCP Online Module
3
- *
4
- * Provides everything needed to expose MCP tools over HTTP:
5
- * - StreamableHTTP transport with Bearer token auth
6
- * - Browser-based login flow for token acquisition
7
- * - Token CRUD management
8
- * - Usage stats / audit logging
9
- * - Tenant isolation via AsyncLocalStorage
10
- *
11
- * Quick start:
12
- * ```typescript
13
- * import { addMcpRoutes, addMcpAuthRoutes, addMcpTokenRoutes, addMcpUsageRoutes } from '@soulbatical/tetra-core';
14
- * import { Router } from 'express';
15
- *
16
- * const tokenPrefix = 'myapp_';
17
- *
18
- * // MCP endpoint (Bearer API token auth)
19
- * const mcpRouter = Router();
20
- * addMcpRoutes(mcpRouter, { tools, handlers, tokenPrefix });
21
- * app.use('/api/mcp', mcpRouter);
22
- *
23
- * // Browser auth flow (public + JWT)
24
- * const mcpAuthRouter = Router();
25
- * addMcpAuthRoutes(mcpAuthRouter, { tokenPrefix });
26
- * app.use('/api/mcp-auth', mcpAuthRouter);
27
- *
28
- * // Token management (JWT auth)
29
- * const mcpTokensRouter = Router();
30
- * addMcpTokenRoutes(mcpTokensRouter, { tokenPrefix });
31
- * app.use('/api/mcp-tokens', mcpTokensRouter);
32
- *
33
- * // Usage stats (JWT auth)
34
- * const mcpUsageRouter = Router();
35
- * addMcpUsageRoutes(mcpUsageRouter);
36
- * app.use('/api/mcp-usage', mcpUsageRouter);
37
- * ```
38
- *
39
- * Database: requires `mcp_api_tokens` and `mcp_audit_log` tables.
40
- * See migrations/ directory for SQL.
41
- */
42
- // Routes
43
- export { addMcpRoutes } from './mcp-routes.js';
44
- export { addMcpAuthRoutes } from './mcp-auth-routes.js';
45
- export { addMcpTokenRoutes } from './mcp-tokens-routes.js';
46
- export { addMcpUsageRoutes } from './mcp-usage-routes.js';
47
- // Tenant context
48
- export { getMcpOrganizationId, getMcpTenantContext, runWithMcpTenant, validateMcpApiToken, generateMcpApiToken } from './tenant-context.js';
49
- // Database client for MCP tool handlers
50
- export { mcpDB, mcpScopedDB } from './mcp-db.js';
51
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/shared/mcp/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,SAAS;AACT,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE1D,iBAAiB;AACjB,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAG5I,wCAAwC;AACxC,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
@@ -1,26 +0,0 @@
1
- /**
2
- * MCP Auth Routes — Factory
3
- *
4
- * Browser-based login flow for MCP token acquisition.
5
- *
6
- * Flow:
7
- * 1. MCP client POST /request → gets request_id + login URL
8
- * 2. User opens browser, logs in
9
- * 3. Frontend calls POST /approve/:requestId (authenticated)
10
- * 4. MCP client polls GET /poll/:requestId → receives API token
11
- *
12
- * Usage:
13
- * ```typescript
14
- * import { addMcpAuthRoutes } from '@soulbatical/tetra-core';
15
- *
16
- * const mcpAuthRouter = Router();
17
- * addMcpAuthRoutes(mcpAuthRouter, {
18
- * tokenPrefix: 'vincifox_',
19
- * });
20
- * app.use('/api/mcp-auth', mcpAuthRouter);
21
- * ```
22
- */
23
- import type { Router } from 'express';
24
- import type { McpAuthRoutesConfig } from './types.js';
25
- export declare function addMcpAuthRoutes(router: Router, config: McpAuthRoutesConfig): void;
26
- //# sourceMappingURL=mcp-auth-routes.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"mcp-auth-routes.d.ts","sourceRoot":"","sources":["../../../src/shared/mcp/mcp-auth-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAqB,MAAM,SAAS,CAAC;AAKzD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAiCtD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,mBAAmB,GAAG,IAAI,CA2HlF"}
@@ -1,141 +0,0 @@
1
- /**
2
- * MCP Auth Routes — Factory
3
- *
4
- * Browser-based login flow for MCP token acquisition.
5
- *
6
- * Flow:
7
- * 1. MCP client POST /request → gets request_id + login URL
8
- * 2. User opens browser, logs in
9
- * 3. Frontend calls POST /approve/:requestId (authenticated)
10
- * 4. MCP client polls GET /poll/:requestId → receives API token
11
- *
12
- * Usage:
13
- * ```typescript
14
- * import { addMcpAuthRoutes } from '@soulbatical/tetra-core';
15
- *
16
- * const mcpAuthRouter = Router();
17
- * addMcpAuthRoutes(mcpAuthRouter, {
18
- * tokenPrefix: 'vincifox_',
19
- * });
20
- * app.use('/api/mcp-auth', mcpAuthRouter);
21
- * ```
22
- */
23
- import { randomUUID } from 'node:crypto';
24
- import { authenticateToken } from '../../middleware/authMiddleware.js';
25
- import { systemDB } from '../../core/systemDb.js';
26
- import { generateMcpApiToken } from './tenant-context.js';
27
- // In-memory store (short-lived, <5min)
28
- const pendingRequests = new Map();
29
- // Cleanup expired every 30s
30
- setInterval(() => {
31
- const now = Date.now();
32
- for (const [id, req] of pendingRequests) {
33
- if (now - req.createdAt > 600_000) { // 10min hard limit
34
- pendingRequests.delete(id);
35
- }
36
- }
37
- }, 30_000).unref();
38
- function getOrgId(req) {
39
- const user = req.user;
40
- return user?.activeOrganizationId || user?.active_organization_id || user?.organizationId || null;
41
- }
42
- function paramStr(value) {
43
- return Array.isArray(value) ? value[0] : value || '';
44
- }
45
- export function addMcpAuthRoutes(router, config) {
46
- const { tokenPrefix, frontendApprovalPath = '/app/settings/api-tokens', defaultDeviceName = 'Claude Code', expirySeconds = 300, } = config;
47
- const frontendUrl = config.frontendUrl || process.env.FRONTEND_URL || 'http://localhost:3000';
48
- // Lazy DB: only create client when first request arrives (not at mount time)
49
- let _db = null;
50
- const db = () => { if (!_db)
51
- _db = systemDB('mcp-auth'); return _db; };
52
- // POST /request — Create pending auth request
53
- router.post('/request', (req, res) => {
54
- const { device_name } = req.body || {};
55
- const requestId = randomUUID();
56
- pendingRequests.set(requestId, {
57
- requestId,
58
- deviceName: device_name || defaultDeviceName,
59
- createdAt: Date.now(),
60
- });
61
- res.json({
62
- request_id: requestId,
63
- login_url: `${frontendUrl}${frontendApprovalPath}?mcp_auth=${requestId}`,
64
- expires_in: expirySeconds,
65
- });
66
- });
67
- // GET /status/:requestId — Check if request is valid (for frontend)
68
- router.get('/status/:requestId', (req, res) => {
69
- const requestId = paramStr(req.params.requestId);
70
- const pending = pendingRequests.get(requestId);
71
- if (!pending) {
72
- res.status(404).json({ error: 'Auth request not found or expired' });
73
- return;
74
- }
75
- const expiryMs = expirySeconds * 1000;
76
- res.json({
77
- status: pending.approved ? 'approved' : 'pending',
78
- device_name: pending.deviceName,
79
- expires_in: Math.max(0, Math.ceil((pending.createdAt + expiryMs - Date.now()) / 1000)),
80
- });
81
- });
82
- // POST /approve/:requestId — Approve and generate token (requires JWT)
83
- router.post('/approve/:requestId', authenticateToken, async (req, res) => {
84
- try {
85
- const requestId = paramStr(req.params.requestId);
86
- const user = req.user;
87
- const pending = pendingRequests.get(requestId);
88
- if (!pending) {
89
- res.status(404).json({ error: 'Auth request not found or expired' });
90
- return;
91
- }
92
- if (pending.approved) {
93
- res.status(409).json({ error: 'Already approved' });
94
- return;
95
- }
96
- const organizationId = getOrgId(req);
97
- if (!organizationId) {
98
- res.status(400).json({ error: 'No active organization' });
99
- return;
100
- }
101
- // Generate API token
102
- const tokenName = `${pending.deviceName} (${new Date().toISOString().slice(0, 10)})`;
103
- const token = await generateMcpApiToken(organizationId, tokenName, tokenPrefix, user.id);
104
- // Get org name for display
105
- const { data: org } = await db()
106
- .from('organizations')
107
- .select('name')
108
- .eq('id', organizationId)
109
- .single();
110
- pending.apiToken = token;
111
- pending.organizationName = org?.name || 'Unknown';
112
- pending.approved = true;
113
- res.json({ success: true, organization: org?.name });
114
- }
115
- catch (err) {
116
- res.status(500).json({ error: 'Internal server error' });
117
- }
118
- });
119
- // GET /poll/:requestId — Poll for token (MCP client side)
120
- router.get('/poll/:requestId', (req, res) => {
121
- const requestId = paramStr(req.params.requestId);
122
- const pending = pendingRequests.get(requestId);
123
- if (!pending) {
124
- res.status(404).json({ error: 'Auth request not found or expired' });
125
- return;
126
- }
127
- if (!pending.approved) {
128
- res.json({ status: 'pending' });
129
- return;
130
- }
131
- // Return token and clean up (one-time retrieval)
132
- const result = {
133
- status: 'approved',
134
- api_token: pending.apiToken,
135
- organization: pending.organizationName,
136
- };
137
- pendingRequests.delete(requestId);
138
- res.json(result);
139
- });
140
- }
141
- //# sourceMappingURL=mcp-auth-routes.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"mcp-auth-routes.js","sourceRoot":"","sources":["../../../src/shared/mcp/mcp-auth-routes.ts"],"names":[],"mappings":"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"}
@@ -1,99 +0,0 @@
1
- /**
2
- * MCP Database Client
3
- *
4
- * Provides a tenant-scoped Supabase client for MCP tool handlers.
5
- * Replaces the `createAdminClient()` workaround used in projects like AgentRook.
6
- *
7
- * How it works:
8
- * 1. Reads the TenantContext from AsyncLocalStorage (set by addMcpRoutes)
9
- * 2. Returns a service_role client (bypasses RLS — MCP tools filter by org_id themselves)
10
- * 3. Includes the organization_id so tools can scope their queries
11
- *
12
- * Usage in MCP tool handlers:
13
- * ```typescript
14
- * import { mcpDB } from '@soulbatical/tetra-core/mcp';
15
- *
16
- * async function handleGenerateImage(args: Record<string, unknown>) {
17
- * const { client, organizationId } = mcpDB();
18
- *
19
- * // All queries are scoped to this org
20
- * const { data } = await client
21
- * .from('campaigns')
22
- * .select('*')
23
- * .eq('organization_id', organizationId);
24
- *
25
- * // ...
26
- * }
27
- * ```
28
- *
29
- * For local/stdio MCP (no tenant context), use `mcpDB({ allowLocal: true })`
30
- * which returns a client without org scoping. The tool is responsible for
31
- * handling the missing org context.
32
- */
33
- import { type SupabaseClient } from '@supabase/supabase-js';
34
- export interface McpDBResult {
35
- /** Supabase client (service_role, bypasses RLS) */
36
- client: SupabaseClient;
37
- /** Organization ID from the MCP API token */
38
- organizationId: string;
39
- /** Token ID for audit logging */
40
- tokenId: string;
41
- }
42
- export interface McpDBLocalResult {
43
- /** Supabase client (service_role, bypasses RLS) */
44
- client: SupabaseClient;
45
- /** Organization ID — undefined in local/stdio mode */
46
- organizationId: string | undefined;
47
- /** Token ID — undefined in local/stdio mode */
48
- tokenId: string | undefined;
49
- }
50
- export interface McpDBOptions {
51
- /**
52
- * Allow usage without tenant context (local/stdio mode).
53
- * When true, returns a client without org scoping.
54
- * Default: false
55
- */
56
- allowLocal?: boolean;
57
- }
58
- /**
59
- * Get a tenant-scoped database client for MCP tool handlers.
60
- *
61
- * Must be called within a `runWithMcpTenant()` context (set automatically
62
- * by `addMcpRoutes`). Throws if no tenant context is found and
63
- * `allowLocal` is not set.
64
- *
65
- * @example
66
- * ```typescript
67
- * // Standard usage (online MCP with API key auth)
68
- * const { client, organizationId } = mcpDB();
69
- * const { data } = await client.from('items').select('*').eq('organization_id', organizationId);
70
- *
71
- * // Local/stdio mode (development, no API key)
72
- * const { client, organizationId } = mcpDB({ allowLocal: true });
73
- * if (!organizationId) {
74
- * // Handle local mode — maybe use a default org or skip org filtering
75
- * }
76
- * ```
77
- */
78
- export declare function mcpDB(): McpDBResult;
79
- export declare function mcpDB(options: {
80
- allowLocal: true;
81
- }): McpDBLocalResult;
82
- /**
83
- * Convenience wrapper that creates a scoped query builder.
84
- * Automatically adds `.eq('organization_id', orgId)` to every query.
85
- *
86
- * @example
87
- * ```typescript
88
- * const db = mcpScopedDB();
89
- * const { data } = await db.from('campaigns').select('*');
90
- * // Automatically filtered by organization_id
91
- * ```
92
- */
93
- export declare function mcpScopedDB(): {
94
- from: (table: string) => ReturnType<SupabaseClient['from']>;
95
- organizationId: string;
96
- tokenId: string;
97
- client: SupabaseClient;
98
- };
99
- //# sourceMappingURL=mcp-db.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"mcp-db.d.ts","sourceRoot":"","sources":["../../../src/shared/mcp/mcp-db.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAQ1E,MAAM,WAAW,WAAW;IAC1B,mDAAmD;IACnD,MAAM,EAAE,cAAc,CAAC;IACvB,6CAA6C;IAC7C,cAAc,EAAE,MAAM,CAAC;IACvB,iCAAiC;IACjC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,mDAAmD;IACnD,MAAM,EAAE,cAAc,CAAC;IACvB,sDAAsD;IACtD,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,+CAA+C;IAC/C,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B;AAED,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAsBD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,KAAK,IAAI,WAAW,CAAC;AACrC,wBAAgB,KAAK,CAAC,OAAO,EAAE;IAAE,UAAU,EAAE,IAAI,CAAA;CAAE,GAAG,gBAAgB,CAAC;AAgCvE;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,IAAI;IAC7B,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,UAAU,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5D,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,cAAc,CAAC;CACxB,CAqBA"}
@@ -1,106 +0,0 @@
1
- /**
2
- * MCP Database Client
3
- *
4
- * Provides a tenant-scoped Supabase client for MCP tool handlers.
5
- * Replaces the `createAdminClient()` workaround used in projects like AgentRook.
6
- *
7
- * How it works:
8
- * 1. Reads the TenantContext from AsyncLocalStorage (set by addMcpRoutes)
9
- * 2. Returns a service_role client (bypasses RLS — MCP tools filter by org_id themselves)
10
- * 3. Includes the organization_id so tools can scope their queries
11
- *
12
- * Usage in MCP tool handlers:
13
- * ```typescript
14
- * import { mcpDB } from '@soulbatical/tetra-core/mcp';
15
- *
16
- * async function handleGenerateImage(args: Record<string, unknown>) {
17
- * const { client, organizationId } = mcpDB();
18
- *
19
- * // All queries are scoped to this org
20
- * const { data } = await client
21
- * .from('campaigns')
22
- * .select('*')
23
- * .eq('organization_id', organizationId);
24
- *
25
- * // ...
26
- * }
27
- * ```
28
- *
29
- * For local/stdio MCP (no tenant context), use `mcpDB({ allowLocal: true })`
30
- * which returns a client without org scoping. The tool is responsible for
31
- * handling the missing org context.
32
- */
33
- import { createClient } from '@supabase/supabase-js';
34
- import { getMcpTenantContext } from './tenant-context.js';
35
- import { createLogger } from '../../utils/logger.js';
36
- const logger = createLogger('mcp:db');
37
- // ── Singleton client ───────────────────────────────────
38
- let _client = null;
39
- function getServiceClient() {
40
- if (!_client) {
41
- const url = process.env.SUPABASE_URL;
42
- const key = process.env.SUPABASE_SERVICE_ROLE_KEY;
43
- if (!url || !key) {
44
- throw new Error('mcpDB requires SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY');
45
- }
46
- _client = createClient(url, key, {
47
- auth: { autoRefreshToken: false, persistSession: false },
48
- });
49
- }
50
- return _client;
51
- }
52
- export function mcpDB(options) {
53
- const ctx = getMcpTenantContext();
54
- const client = getServiceClient();
55
- if (!ctx) {
56
- if (options?.allowLocal) {
57
- logger.debug('mcpDB: no tenant context (local/stdio mode)');
58
- return {
59
- client,
60
- organizationId: undefined,
61
- tokenId: undefined,
62
- };
63
- }
64
- throw new Error('mcpDB: no tenant context found. ' +
65
- 'Ensure this is called within an MCP tool handler (inside runWithMcpTenant). ' +
66
- 'For local/stdio mode, use mcpDB({ allowLocal: true }).');
67
- }
68
- logger.debug({ organizationId: ctx.organizationId }, 'mcpDB: tenant-scoped access');
69
- return {
70
- client,
71
- organizationId: ctx.organizationId,
72
- tokenId: ctx.tokenId,
73
- };
74
- }
75
- // ── Convenience: scoped query helper ───────────────────
76
- /**
77
- * Convenience wrapper that creates a scoped query builder.
78
- * Automatically adds `.eq('organization_id', orgId)` to every query.
79
- *
80
- * @example
81
- * ```typescript
82
- * const db = mcpScopedDB();
83
- * const { data } = await db.from('campaigns').select('*');
84
- * // Automatically filtered by organization_id
85
- * ```
86
- */
87
- export function mcpScopedDB() {
88
- const { client, organizationId, tokenId } = mcpDB();
89
- return {
90
- from: (table) => {
91
- const query = client.from(table);
92
- // Patch select/insert/update/delete to auto-scope
93
- const original = query.select.bind(query);
94
- const scopedQuery = Object.create(query);
95
- // For SELECT queries, auto-add org filter
96
- scopedQuery.select = (...args) => {
97
- return original(...args).eq('organization_id', organizationId);
98
- };
99
- return scopedQuery;
100
- },
101
- organizationId,
102
- tokenId,
103
- client,
104
- };
105
- }
106
- //# sourceMappingURL=mcp-db.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"mcp-db.js","sourceRoot":"","sources":["../../../src/shared/mcp/mcp-db.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,EAAE,YAAY,EAAuB,MAAM,uBAAuB,CAAC;AAC1E,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAErD,MAAM,MAAM,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;AA+BtC,0DAA0D;AAE1D,IAAI,OAAO,GAA0B,IAAI,CAAC;AAE1C,SAAS,gBAAgB;IACvB,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;QACrC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC;QAClD,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC;QAC/E,CAAC;QACD,OAAO,GAAG,YAAY,CAAC,GAAG,EAAE,GAAG,EAAE;YAC/B,IAAI,EAAE,EAAE,gBAAgB,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE;SACzD,CAAC,CAAC;IACL,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AA0BD,MAAM,UAAU,KAAK,CAAC,OAAsB;IAC1C,MAAM,GAAG,GAAG,mBAAmB,EAAE,CAAC;IAClC,MAAM,MAAM,GAAG,gBAAgB,EAAE,CAAC;IAElC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,IAAI,OAAO,EAAE,UAAU,EAAE,CAAC;YACxB,MAAM,CAAC,KAAK,CAAC,6CAA6C,CAAC,CAAC;YAC5D,OAAO;gBACL,MAAM;gBACN,cAAc,EAAE,SAAS;gBACzB,OAAO,EAAE,SAAS;aACnB,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,KAAK,CACb,kCAAkC;YAClC,8EAA8E;YAC9E,wDAAwD,CACzD,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,EAAE,cAAc,EAAE,GAAG,CAAC,cAAc,EAAE,EAAE,6BAA6B,CAAC,CAAC;IAEpF,OAAO;QACL,MAAM;QACN,cAAc,EAAE,GAAG,CAAC,cAAc;QAClC,OAAO,EAAE,GAAG,CAAC,OAAO;KACrB,CAAC;AACJ,CAAC;AAED,0DAA0D;AAE1D;;;;;;;;;;GAUG;AACH,MAAM,UAAU,WAAW;IAMzB,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,GAAG,KAAK,EAAE,CAAC;IAEpD,OAAO;QACL,IAAI,EAAE,CAAC,KAAa,EAAE,EAAE;YACtB,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACjC,kDAAkD;YAClD,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC1C,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAEzC,0CAA0C;YAC1C,WAAW,CAAC,MAAM,GAAG,CAAC,GAAG,IAAqC,EAAE,EAAE;gBAChE,OAAO,QAAQ,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC,iBAAiB,EAAE,cAAc,CAAC,CAAC;YACjE,CAAC,CAAC;YAEF,OAAO,WAAW,CAAC;QACrB,CAAC;QACD,cAAc;QACd,OAAO;QACP,MAAM;KACP,CAAC;AACJ,CAAC"}
@@ -1,29 +0,0 @@
1
- /**
2
- * MCP StreamableHTTP Routes — Factory
3
- *
4
- * Creates an Express router that exposes MCP tools over HTTP
5
- * with Bearer token authentication, rate limiting, and audit logging.
6
- *
7
- * Usage:
8
- * ```typescript
9
- * import { addMcpRoutes } from '@soulbatical/tetra-core';
10
- *
11
- * const mcpRouter = Router();
12
- * addMcpRoutes(mcpRouter, {
13
- * tools: myToolDefinitions,
14
- * handlers: myToolHandlers,
15
- * tokenPrefix: 'vincifox_',
16
- * });
17
- * app.use('/', mcpRouter); // or app.use('/api/mcp', ...) if behind a backend proxy
18
- * ```
19
- */
20
- import type { Router } from 'express';
21
- import type { McpRoutesConfig } from './types.js';
22
- /**
23
- * Add MCP StreamableHTTP routes to a router.
24
- *
25
- * The router handles POST (JSON-RPC requests), GET (SSE), and DELETE (cleanup).
26
- * Authentication is via Bearer token in the Authorization header.
27
- */
28
- export declare function addMcpRoutes(router: Router, config: McpRoutesConfig): void;
29
- //# sourceMappingURL=mcp-routes.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"mcp-routes.d.ts","sourceRoot":"","sources":["../../../src/shared/mcp/mcp-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAqB,MAAM,SAAS,CAAC;AAIzD,OAAO,KAAK,EAAE,eAAe,EAAiB,MAAM,YAAY,CAAC;AA8CjE;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CA2I1E"}
@@ -1,171 +0,0 @@
1
- /**
2
- * MCP StreamableHTTP Routes — Factory
3
- *
4
- * Creates an Express router that exposes MCP tools over HTTP
5
- * with Bearer token authentication, rate limiting, and audit logging.
6
- *
7
- * Usage:
8
- * ```typescript
9
- * import { addMcpRoutes } from '@soulbatical/tetra-core';
10
- *
11
- * const mcpRouter = Router();
12
- * addMcpRoutes(mcpRouter, {
13
- * tools: myToolDefinitions,
14
- * handlers: myToolHandlers,
15
- * tokenPrefix: 'vincifox_',
16
- * });
17
- * app.use('/', mcpRouter); // or app.use('/api/mcp', ...) if behind a backend proxy
18
- * ```
19
- */
20
- import { createLogger } from '../../utils/logger.js';
21
- import { systemDB } from '../../core/systemDb.js';
22
- import { validateMcpApiToken, runWithMcpTenant } from './tenant-context.js';
23
- const logger = createLogger('mcp:routes');
24
- const rateBuckets = new Map();
25
- // Clean up expired buckets every 5 min
26
- setInterval(() => {
27
- const now = Date.now();
28
- for (const [key, bucket] of rateBuckets) {
29
- if (now > bucket.resetAt) {
30
- rateBuckets.delete(key);
31
- }
32
- }
33
- }, 5 * 60 * 1000).unref();
34
- function checkRateLimit(tokenId, max, windowMs) {
35
- const now = Date.now();
36
- let bucket = rateBuckets.get(tokenId);
37
- if (!bucket || now > bucket.resetAt) {
38
- bucket = { count: 0, resetAt: now + windowMs };
39
- rateBuckets.set(tokenId, bucket);
40
- }
41
- bucket.count++;
42
- const remaining = Math.max(0, max - bucket.count);
43
- return {
44
- allowed: bucket.count <= max,
45
- remaining,
46
- resetAt: bucket.resetAt,
47
- };
48
- }
49
- /**
50
- * Add MCP StreamableHTTP routes to a router.
51
- *
52
- * The router handles POST (JSON-RPC requests), GET (SSE), and DELETE (cleanup).
53
- * Authentication is via Bearer token in the Authorization header.
54
- */
55
- export function addMcpRoutes(router, config) {
56
- const { tools, handlers, tokenPrefix, rateLimitMax = 100, rateLimitWindowMs = 15 * 60 * 1000, enableAuditLog = true, onAuthenticate, } = config;
57
- const db = enableAuditLog ? systemDB('mcp-audit') : null;
58
- // Audit logging (fire-and-forget)
59
- function logToolCall(tokenId, organizationId, toolName) {
60
- if (!db)
61
- return;
62
- db.from('mcp_audit_log')
63
- .insert({ token_id: tokenId, organization_id: organizationId, tool_name: toolName })
64
- .then(() => { });
65
- }
66
- // Auth helper — uses custom onAuthenticate if provided, else default mcp_api_tokens lookup
67
- async function authenticateRequest(req, res) {
68
- const authHeader = req.headers.authorization;
69
- if (!authHeader || !authHeader.startsWith('Bearer ')) {
70
- res.status(401).json({ error: 'Missing or invalid Authorization header' });
71
- return null;
72
- }
73
- const token = authHeader.substring(7);
74
- try {
75
- if (onAuthenticate) {
76
- return await onAuthenticate(token);
77
- }
78
- return await validateMcpApiToken(token, tokenPrefix);
79
- }
80
- catch (error) {
81
- const message = error instanceof Error ? error.message : 'Authentication failed';
82
- res.status(401).json({ error: message });
83
- return null;
84
- }
85
- }
86
- // Lazy import MCP SDK (peer dependency)
87
- let mcpSdkLoaded = false;
88
- let Server;
89
- let StreamableHTTPServerTransport;
90
- let CallToolRequestSchema;
91
- let ListToolsRequestSchema;
92
- async function ensureMcpSdk() {
93
- if (mcpSdkLoaded)
94
- return;
95
- const serverMod = await import('@modelcontextprotocol/sdk/server/index.js');
96
- const transportMod = await import('@modelcontextprotocol/sdk/server/streamableHttp.js');
97
- const typesMod = await import('@modelcontextprotocol/sdk/types.js');
98
- Server = serverMod.Server;
99
- StreamableHTTPServerTransport = transportMod.StreamableHTTPServerTransport;
100
- CallToolRequestSchema = typesMod.CallToolRequestSchema;
101
- ListToolsRequestSchema = typesMod.ListToolsRequestSchema;
102
- mcpSdkLoaded = true;
103
- }
104
- // Create MCP server instance
105
- function createMcpServer(tenant) {
106
- const server = new Server({ name: 'tetra-mcp', version: '1.0.0' }, { capabilities: { tools: {} } });
107
- server.setRequestHandler(ListToolsRequestSchema, async () => ({
108
- tools,
109
- }));
110
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
111
- const { name, arguments: args = {} } = request.params;
112
- const handler = handlers[name];
113
- if (!handler) {
114
- throw new Error(`Unknown tool: ${name}`);
115
- }
116
- logToolCall(tenant.tokenId, tenant.organizationId, name);
117
- return handler(args);
118
- });
119
- return server;
120
- }
121
- // Main endpoint
122
- router.all('/', async (req, res) => {
123
- const tenant = await authenticateRequest(req, res);
124
- if (!tenant)
125
- return;
126
- // Rate limit
127
- const rateResult = checkRateLimit(tenant.tokenId, rateLimitMax, rateLimitWindowMs);
128
- res.setHeader('X-RateLimit-Limit', rateLimitMax);
129
- res.setHeader('X-RateLimit-Remaining', rateResult.remaining);
130
- res.setHeader('X-RateLimit-Reset', Math.ceil(rateResult.resetAt / 1000));
131
- if (!rateResult.allowed) {
132
- res.status(429).json({
133
- error: 'Rate limit exceeded',
134
- retry_after: Math.ceil((rateResult.resetAt - Date.now()) / 1000),
135
- });
136
- return;
137
- }
138
- // Lazy-load MCP SDK
139
- try {
140
- await ensureMcpSdk();
141
- }
142
- catch (err) {
143
- logger.error({ err }, 'Failed to load @modelcontextprotocol/sdk — is it installed?');
144
- res.status(500).json({ error: 'MCP SDK not available' });
145
- return;
146
- }
147
- const transport = new StreamableHTTPServerTransport({
148
- sessionIdGenerator: undefined,
149
- });
150
- const server = createMcpServer(tenant);
151
- try {
152
- await server.connect(transport);
153
- await runWithMcpTenant(tenant, async () => {
154
- await transport.handleRequest(req, res, req.body);
155
- });
156
- }
157
- catch (error) {
158
- if (!res.headersSent) {
159
- res.status(500).json({
160
- error: 'MCP request failed',
161
- details: error instanceof Error ? error.message : 'Unknown error',
162
- });
163
- }
164
- }
165
- finally {
166
- await transport.close().catch(() => { });
167
- await server.close().catch(() => { });
168
- }
169
- });
170
- }
171
- //# sourceMappingURL=mcp-routes.js.map