@soulbatical/tetra-core 0.10.4 → 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 (271) hide show
  1. package/README.md +78 -38
  2. package/dist/core/createApp.d.ts +1 -1
  3. package/dist/core/createApp.d.ts.map +1 -1
  4. package/dist/core/createApp.js +77 -2
  5. package/dist/core/createApp.js.map +1 -1
  6. package/dist/core/dualWriteProxy.d.ts +7 -2
  7. package/dist/core/dualWriteProxy.d.ts.map +1 -1
  8. package/dist/core/dualWriteProxy.js +16 -5
  9. package/dist/core/dualWriteProxy.js.map +1 -1
  10. package/dist/core/routeContext.d.ts +24 -0
  11. package/dist/core/routeContext.d.ts.map +1 -1
  12. package/dist/core/routeContext.js +31 -4
  13. package/dist/core/routeContext.js.map +1 -1
  14. package/dist/core/systemDb.d.ts +2 -2
  15. package/dist/core/systemDb.js +2 -2
  16. package/dist/generators/rls-checker.d.ts +1 -1
  17. package/dist/generators/rls-checker.js +1 -1
  18. package/dist/generators/rls-exec-sql.d.ts +1 -1
  19. package/dist/generators/rls-exec-sql.js +1 -1
  20. package/dist/generators/rpc/index.d.ts +1 -1
  21. package/dist/generators/rpc/index.js +1 -1
  22. package/dist/index.d.ts +3 -31
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +4 -32
  25. package/dist/index.js.map +1 -1
  26. package/dist/middleware/securityMiddleware.d.ts +1 -1
  27. package/dist/middleware/securityMiddleware.d.ts.map +1 -1
  28. package/dist/middleware/validateBody.d.ts.map +1 -1
  29. package/dist/middleware/validateBody.js +51 -8
  30. package/dist/middleware/validateBody.js.map +1 -1
  31. package/dist/shared/rfc7807ErrorResponse.d.ts +7 -0
  32. package/dist/shared/rfc7807ErrorResponse.d.ts.map +1 -1
  33. package/dist/shared/rfc7807ErrorResponse.js +19 -5
  34. package/dist/shared/rfc7807ErrorResponse.js.map +1 -1
  35. package/dist/utils/logger.d.ts.map +1 -1
  36. package/dist/utils/logger.js +16 -1
  37. package/dist/utils/logger.js.map +1 -1
  38. package/package.json +33 -77
  39. package/dist/affiliate.d.ts +0 -11
  40. package/dist/affiliate.d.ts.map +0 -1
  41. package/dist/affiliate.js +0 -10
  42. package/dist/affiliate.js.map +0 -1
  43. package/dist/billing.d.ts +0 -8
  44. package/dist/billing.d.ts.map +0 -1
  45. package/dist/billing.js +0 -7
  46. package/dist/billing.js.map +0 -1
  47. package/dist/email.d.ts +0 -9
  48. package/dist/email.d.ts.map +0 -1
  49. package/dist/email.js +0 -8
  50. package/dist/email.js.map +0 -1
  51. package/dist/generators/rls-exec-sql.sql +0 -57
  52. package/dist/generators.d.ts +0 -15
  53. package/dist/generators.d.ts.map +0 -1
  54. package/dist/generators.js +0 -12
  55. package/dist/generators.js.map +0 -1
  56. package/dist/mcp.d.ts +0 -8
  57. package/dist/mcp.d.ts.map +0 -1
  58. package/dist/mcp.js +0 -7
  59. package/dist/mcp.js.map +0 -1
  60. package/dist/planner.d.ts +0 -8
  61. package/dist/planner.d.ts.map +0 -1
  62. package/dist/planner.js +0 -7
  63. package/dist/planner.js.map +0 -1
  64. package/dist/shared/affiliate/AffiliateAttributionService.d.ts +0 -47
  65. package/dist/shared/affiliate/AffiliateAttributionService.d.ts.map +0 -1
  66. package/dist/shared/affiliate/AffiliateAttributionService.js +0 -308
  67. package/dist/shared/affiliate/AffiliateAttributionService.js.map +0 -1
  68. package/dist/shared/affiliate/AffiliateClickService.d.ts +0 -35
  69. package/dist/shared/affiliate/AffiliateClickService.d.ts.map +0 -1
  70. package/dist/shared/affiliate/AffiliateClickService.js +0 -87
  71. package/dist/shared/affiliate/AffiliateClickService.js.map +0 -1
  72. package/dist/shared/affiliate/affiliateFeatureConfig.d.ts +0 -11
  73. package/dist/shared/affiliate/affiliateFeatureConfig.d.ts.map +0 -1
  74. package/dist/shared/affiliate/affiliateFeatureConfig.js +0 -242
  75. package/dist/shared/affiliate/affiliateFeatureConfig.js.map +0 -1
  76. package/dist/shared/affiliate/index.d.ts +0 -11
  77. package/dist/shared/affiliate/index.d.ts.map +0 -1
  78. package/dist/shared/affiliate/index.js +0 -13
  79. package/dist/shared/affiliate/index.js.map +0 -1
  80. package/dist/shared/affiliate/routes.d.ts +0 -87
  81. package/dist/shared/affiliate/routes.d.ts.map +0 -1
  82. package/dist/shared/affiliate/routes.js +0 -404
  83. package/dist/shared/affiliate/routes.js.map +0 -1
  84. package/dist/shared/affiliate/types.d.ts +0 -170
  85. package/dist/shared/affiliate/types.d.ts.map +0 -1
  86. package/dist/shared/affiliate/types.js +0 -11
  87. package/dist/shared/affiliate/types.js.map +0 -1
  88. package/dist/shared/billing/BillingService.d.ts +0 -56
  89. package/dist/shared/billing/BillingService.d.ts.map +0 -1
  90. package/dist/shared/billing/BillingService.js +0 -588
  91. package/dist/shared/billing/BillingService.js.map +0 -1
  92. package/dist/shared/billing/SeatBillingService.d.ts +0 -106
  93. package/dist/shared/billing/SeatBillingService.d.ts.map +0 -1
  94. package/dist/shared/billing/SeatBillingService.js +0 -292
  95. package/dist/shared/billing/SeatBillingService.js.map +0 -1
  96. package/dist/shared/billing/index.d.ts +0 -30
  97. package/dist/shared/billing/index.d.ts.map +0 -1
  98. package/dist/shared/billing/index.js +0 -27
  99. package/dist/shared/billing/index.js.map +0 -1
  100. package/dist/shared/billing/routes.d.ts +0 -45
  101. package/dist/shared/billing/routes.d.ts.map +0 -1
  102. package/dist/shared/billing/routes.js +0 -184
  103. package/dist/shared/billing/routes.js.map +0 -1
  104. package/dist/shared/billing/seat-pricing.d.ts +0 -53
  105. package/dist/shared/billing/seat-pricing.d.ts.map +0 -1
  106. package/dist/shared/billing/seat-pricing.js +0 -81
  107. package/dist/shared/billing/seat-pricing.js.map +0 -1
  108. package/dist/shared/billing/types.d.ts +0 -109
  109. package/dist/shared/billing/types.d.ts.map +0 -1
  110. package/dist/shared/billing/types.js +0 -8
  111. package/dist/shared/billing/types.js.map +0 -1
  112. package/dist/shared/email/EmailService.d.ts +0 -64
  113. package/dist/shared/email/EmailService.d.ts.map +0 -1
  114. package/dist/shared/email/EmailService.js +0 -300
  115. package/dist/shared/email/EmailService.js.map +0 -1
  116. package/dist/shared/email/adminRoutes.d.ts +0 -30
  117. package/dist/shared/email/adminRoutes.d.ts.map +0 -1
  118. package/dist/shared/email/adminRoutes.js +0 -227
  119. package/dist/shared/email/adminRoutes.js.map +0 -1
  120. package/dist/shared/email/gmail.d.ts +0 -208
  121. package/dist/shared/email/gmail.d.ts.map +0 -1
  122. package/dist/shared/email/gmail.js +0 -626
  123. package/dist/shared/email/gmail.js.map +0 -1
  124. package/dist/shared/email/index.d.ts +0 -15
  125. package/dist/shared/email/index.d.ts.map +0 -1
  126. package/dist/shared/email/index.js +0 -18
  127. package/dist/shared/email/index.js.map +0 -1
  128. package/dist/shared/email/mailgun.d.ts +0 -18
  129. package/dist/shared/email/mailgun.d.ts.map +0 -1
  130. package/dist/shared/email/mailgun.js +0 -76
  131. package/dist/shared/email/mailgun.js.map +0 -1
  132. package/dist/shared/email/sanitize.d.ts +0 -25
  133. package/dist/shared/email/sanitize.d.ts.map +0 -1
  134. package/dist/shared/email/sanitize.js +0 -39
  135. package/dist/shared/email/sanitize.js.map +0 -1
  136. package/dist/shared/email/smtp.d.ts +0 -20
  137. package/dist/shared/email/smtp.d.ts.map +0 -1
  138. package/dist/shared/email/smtp.js +0 -53
  139. package/dist/shared/email/smtp.js.map +0 -1
  140. package/dist/shared/email/types.d.ts +0 -113
  141. package/dist/shared/email/types.d.ts.map +0 -1
  142. package/dist/shared/email/types.js +0 -7
  143. package/dist/shared/email/types.js.map +0 -1
  144. package/dist/shared/email/webhookRoutes.d.ts +0 -29
  145. package/dist/shared/email/webhookRoutes.d.ts.map +0 -1
  146. package/dist/shared/email/webhookRoutes.js +0 -125
  147. package/dist/shared/email/webhookRoutes.js.map +0 -1
  148. package/dist/shared/mcp/index.d.ts +0 -51
  149. package/dist/shared/mcp/index.d.ts.map +0 -1
  150. package/dist/shared/mcp/index.js +0 -51
  151. package/dist/shared/mcp/index.js.map +0 -1
  152. package/dist/shared/mcp/mcp-auth-routes.d.ts +0 -26
  153. package/dist/shared/mcp/mcp-auth-routes.d.ts.map +0 -1
  154. package/dist/shared/mcp/mcp-auth-routes.js +0 -141
  155. package/dist/shared/mcp/mcp-auth-routes.js.map +0 -1
  156. package/dist/shared/mcp/mcp-db.d.ts +0 -99
  157. package/dist/shared/mcp/mcp-db.d.ts.map +0 -1
  158. package/dist/shared/mcp/mcp-db.js +0 -106
  159. package/dist/shared/mcp/mcp-db.js.map +0 -1
  160. package/dist/shared/mcp/mcp-routes.d.ts +0 -29
  161. package/dist/shared/mcp/mcp-routes.d.ts.map +0 -1
  162. package/dist/shared/mcp/mcp-routes.js +0 -171
  163. package/dist/shared/mcp/mcp-routes.js.map +0 -1
  164. package/dist/shared/mcp/mcp-tokens-routes.d.ts +0 -35
  165. package/dist/shared/mcp/mcp-tokens-routes.d.ts.map +0 -1
  166. package/dist/shared/mcp/mcp-tokens-routes.js +0 -94
  167. package/dist/shared/mcp/mcp-tokens-routes.js.map +0 -1
  168. package/dist/shared/mcp/mcp-usage-routes.d.ts +0 -17
  169. package/dist/shared/mcp/mcp-usage-routes.d.ts.map +0 -1
  170. package/dist/shared/mcp/mcp-usage-routes.js +0 -81
  171. package/dist/shared/mcp/mcp-usage-routes.js.map +0 -1
  172. package/dist/shared/mcp/tenant-context.d.ts +0 -59
  173. package/dist/shared/mcp/tenant-context.d.ts.map +0 -1
  174. package/dist/shared/mcp/tenant-context.js +0 -136
  175. package/dist/shared/mcp/tenant-context.js.map +0 -1
  176. package/dist/shared/mcp/types.d.ts +0 -74
  177. package/dist/shared/mcp/types.d.ts.map +0 -1
  178. package/dist/shared/mcp/types.js +0 -7
  179. package/dist/shared/mcp/types.js.map +0 -1
  180. package/dist/shared/planner/GoogleCalendarService.d.ts +0 -137
  181. package/dist/shared/planner/GoogleCalendarService.d.ts.map +0 -1
  182. package/dist/shared/planner/GoogleCalendarService.js +0 -525
  183. package/dist/shared/planner/GoogleCalendarService.js.map +0 -1
  184. package/dist/shared/planner/PlannerService.d.ts +0 -264
  185. package/dist/shared/planner/PlannerService.d.ts.map +0 -1
  186. package/dist/shared/planner/PlannerService.js +0 -1393
  187. package/dist/shared/planner/PlannerService.js.map +0 -1
  188. package/dist/shared/planner/index.d.ts +0 -37
  189. package/dist/shared/planner/index.d.ts.map +0 -1
  190. package/dist/shared/planner/index.js +0 -35
  191. package/dist/shared/planner/index.js.map +0 -1
  192. package/dist/shared/planner/intervals.d.ts +0 -60
  193. package/dist/shared/planner/intervals.d.ts.map +0 -1
  194. package/dist/shared/planner/intervals.js +0 -141
  195. package/dist/shared/planner/intervals.js.map +0 -1
  196. package/dist/shared/planner/routes.d.ts +0 -69
  197. package/dist/shared/planner/routes.d.ts.map +0 -1
  198. package/dist/shared/planner/routes.js +0 -770
  199. package/dist/shared/planner/routes.js.map +0 -1
  200. package/dist/shared/planner/types.d.ts +0 -328
  201. package/dist/shared/planner/types.d.ts.map +0 -1
  202. package/dist/shared/planner/types.js +0 -9
  203. package/dist/shared/planner/types.js.map +0 -1
  204. package/dist/shared/storage/ImageProcessingService.d.ts +0 -32
  205. package/dist/shared/storage/ImageProcessingService.d.ts.map +0 -1
  206. package/dist/shared/storage/ImageProcessingService.js +0 -127
  207. package/dist/shared/storage/ImageProcessingService.js.map +0 -1
  208. package/dist/shared/storage/StorageProxyService.d.ts +0 -47
  209. package/dist/shared/storage/StorageProxyService.d.ts.map +0 -1
  210. package/dist/shared/storage/StorageProxyService.js +0 -196
  211. package/dist/shared/storage/StorageProxyService.js.map +0 -1
  212. package/dist/shared/storage/StorageUploadService.d.ts +0 -126
  213. package/dist/shared/storage/StorageUploadService.d.ts.map +0 -1
  214. package/dist/shared/storage/StorageUploadService.js +0 -206
  215. package/dist/shared/storage/StorageUploadService.js.map +0 -1
  216. package/dist/shared/storage/creative-urls.d.ts +0 -14
  217. package/dist/shared/storage/creative-urls.d.ts.map +0 -1
  218. package/dist/shared/storage/creative-urls.js +0 -30
  219. package/dist/shared/storage/creative-urls.js.map +0 -1
  220. package/dist/shared/storage/index.d.ts +0 -28
  221. package/dist/shared/storage/index.d.ts.map +0 -1
  222. package/dist/shared/storage/index.js +0 -27
  223. package/dist/shared/storage/index.js.map +0 -1
  224. package/dist/shared/storage/routes.d.ts +0 -42
  225. package/dist/shared/storage/routes.d.ts.map +0 -1
  226. package/dist/shared/storage/routes.js +0 -160
  227. package/dist/shared/storage/routes.js.map +0 -1
  228. package/dist/shared/storage/types.d.ts +0 -53
  229. package/dist/shared/storage/types.d.ts.map +0 -1
  230. package/dist/shared/storage/types.js +0 -2
  231. package/dist/shared/storage/types.js.map +0 -1
  232. package/dist/shared/telegram/index.d.ts +0 -4
  233. package/dist/shared/telegram/index.d.ts.map +0 -1
  234. package/dist/shared/telegram/index.js +0 -3
  235. package/dist/shared/telegram/index.js.map +0 -1
  236. package/dist/shared/telegram/routes.d.ts +0 -43
  237. package/dist/shared/telegram/routes.d.ts.map +0 -1
  238. package/dist/shared/telegram/routes.js +0 -868
  239. package/dist/shared/telegram/routes.js.map +0 -1
  240. package/dist/shared/telegram/types.d.ts +0 -168
  241. package/dist/shared/telegram/types.d.ts.map +0 -1
  242. package/dist/shared/telegram/types.js +0 -7
  243. package/dist/shared/telegram/types.js.map +0 -1
  244. package/dist/shared/telegram/utils.d.ts +0 -44
  245. package/dist/shared/telegram/utils.d.ts.map +0 -1
  246. package/dist/shared/telegram/utils.js +0 -121
  247. package/dist/shared/telegram/utils.js.map +0 -1
  248. package/dist/storage.d.ts +0 -9
  249. package/dist/storage.d.ts.map +0 -1
  250. package/dist/storage.js +0 -8
  251. package/dist/storage.js.map +0 -1
  252. package/dist/telemetry.d.ts +0 -9
  253. package/dist/telemetry.d.ts.map +0 -1
  254. package/dist/telemetry.js +0 -8
  255. package/dist/telemetry.js.map +0 -1
  256. package/scripts/postinstall.js +0 -79
  257. package/src/shared/affiliate/migrations/001_create_affiliates.sql +0 -49
  258. package/src/shared/affiliate/migrations/002_create_affiliate_commissions.sql +0 -31
  259. package/src/shared/affiliate/migrations/003_create_affiliate_clicks.sql +0 -26
  260. package/src/shared/affiliate/migrations/004_create_affiliate_payments.sql +0 -34
  261. package/src/shared/affiliate/migrations/005_create_affiliate_tier_history.sql +0 -19
  262. package/src/shared/affiliate/migrations/006_create_affiliate_rpc_functions.sql +0 -209
  263. package/src/shared/affiliate/migrations/007_create_affiliate_rls_policies.sql +0 -123
  264. package/src/shared/billing/migrations/00000000000001_billing.sql +0 -114
  265. package/src/shared/email/migrations/000_create_email_logs.sql +0 -27
  266. package/src/shared/email/migrations/001_create_email_templates.sql +0 -27
  267. package/src/shared/email/migrations/002_add_rls_baseline_policies.sql +0 -37
  268. package/src/shared/email/migrations/003_create_gmail_accounts.sql +0 -82
  269. package/src/shared/email/migrations/004_add_email_logs_tracking_columns.sql +0 -15
  270. package/src/shared/mcp/migrations/001_mcp_api_tokens.sql +0 -21
  271. package/src/shared/mcp/migrations/002_mcp_audit_log.sql +0 -16
@@ -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