@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.
- package/README.md +78 -38
- package/dist/core/createApp.d.ts +1 -1
- package/dist/core/createApp.d.ts.map +1 -1
- package/dist/core/createApp.js +77 -2
- package/dist/core/createApp.js.map +1 -1
- package/dist/core/dualWriteProxy.d.ts +7 -2
- package/dist/core/dualWriteProxy.d.ts.map +1 -1
- package/dist/core/dualWriteProxy.js +16 -5
- package/dist/core/dualWriteProxy.js.map +1 -1
- package/dist/core/routeContext.d.ts +24 -0
- package/dist/core/routeContext.d.ts.map +1 -1
- package/dist/core/routeContext.js +31 -4
- package/dist/core/routeContext.js.map +1 -1
- package/dist/core/systemDb.d.ts +2 -2
- package/dist/core/systemDb.js +2 -2
- package/dist/generators/rls-checker.d.ts +1 -1
- package/dist/generators/rls-checker.js +1 -1
- package/dist/generators/rls-exec-sql.d.ts +1 -1
- package/dist/generators/rls-exec-sql.js +1 -1
- package/dist/generators/rpc/index.d.ts +1 -1
- package/dist/generators/rpc/index.js +1 -1
- package/dist/index.d.ts +3 -31
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -32
- package/dist/index.js.map +1 -1
- package/dist/middleware/securityMiddleware.d.ts +1 -1
- package/dist/middleware/securityMiddleware.d.ts.map +1 -1
- package/dist/middleware/validateBody.d.ts.map +1 -1
- package/dist/middleware/validateBody.js +51 -8
- package/dist/middleware/validateBody.js.map +1 -1
- package/dist/shared/rfc7807ErrorResponse.d.ts +7 -0
- package/dist/shared/rfc7807ErrorResponse.d.ts.map +1 -1
- package/dist/shared/rfc7807ErrorResponse.js +19 -5
- package/dist/shared/rfc7807ErrorResponse.js.map +1 -1
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +16 -1
- package/dist/utils/logger.js.map +1 -1
- package/package.json +33 -77
- package/dist/affiliate.d.ts +0 -11
- package/dist/affiliate.d.ts.map +0 -1
- package/dist/affiliate.js +0 -10
- package/dist/affiliate.js.map +0 -1
- package/dist/billing.d.ts +0 -8
- package/dist/billing.d.ts.map +0 -1
- package/dist/billing.js +0 -7
- package/dist/billing.js.map +0 -1
- package/dist/email.d.ts +0 -9
- package/dist/email.d.ts.map +0 -1
- package/dist/email.js +0 -8
- package/dist/email.js.map +0 -1
- package/dist/generators/rls-exec-sql.sql +0 -57
- package/dist/generators.d.ts +0 -15
- package/dist/generators.d.ts.map +0 -1
- package/dist/generators.js +0 -12
- package/dist/generators.js.map +0 -1
- package/dist/mcp.d.ts +0 -8
- package/dist/mcp.d.ts.map +0 -1
- package/dist/mcp.js +0 -7
- package/dist/mcp.js.map +0 -1
- package/dist/planner.d.ts +0 -8
- package/dist/planner.d.ts.map +0 -1
- package/dist/planner.js +0 -7
- package/dist/planner.js.map +0 -1
- package/dist/shared/affiliate/AffiliateAttributionService.d.ts +0 -47
- package/dist/shared/affiliate/AffiliateAttributionService.d.ts.map +0 -1
- package/dist/shared/affiliate/AffiliateAttributionService.js +0 -308
- package/dist/shared/affiliate/AffiliateAttributionService.js.map +0 -1
- package/dist/shared/affiliate/AffiliateClickService.d.ts +0 -35
- package/dist/shared/affiliate/AffiliateClickService.d.ts.map +0 -1
- package/dist/shared/affiliate/AffiliateClickService.js +0 -87
- package/dist/shared/affiliate/AffiliateClickService.js.map +0 -1
- package/dist/shared/affiliate/affiliateFeatureConfig.d.ts +0 -11
- package/dist/shared/affiliate/affiliateFeatureConfig.d.ts.map +0 -1
- package/dist/shared/affiliate/affiliateFeatureConfig.js +0 -242
- package/dist/shared/affiliate/affiliateFeatureConfig.js.map +0 -1
- package/dist/shared/affiliate/index.d.ts +0 -11
- package/dist/shared/affiliate/index.d.ts.map +0 -1
- package/dist/shared/affiliate/index.js +0 -13
- package/dist/shared/affiliate/index.js.map +0 -1
- package/dist/shared/affiliate/routes.d.ts +0 -87
- package/dist/shared/affiliate/routes.d.ts.map +0 -1
- package/dist/shared/affiliate/routes.js +0 -404
- package/dist/shared/affiliate/routes.js.map +0 -1
- package/dist/shared/affiliate/types.d.ts +0 -170
- package/dist/shared/affiliate/types.d.ts.map +0 -1
- package/dist/shared/affiliate/types.js +0 -11
- package/dist/shared/affiliate/types.js.map +0 -1
- package/dist/shared/billing/BillingService.d.ts +0 -56
- package/dist/shared/billing/BillingService.d.ts.map +0 -1
- package/dist/shared/billing/BillingService.js +0 -588
- package/dist/shared/billing/BillingService.js.map +0 -1
- package/dist/shared/billing/SeatBillingService.d.ts +0 -106
- package/dist/shared/billing/SeatBillingService.d.ts.map +0 -1
- package/dist/shared/billing/SeatBillingService.js +0 -292
- package/dist/shared/billing/SeatBillingService.js.map +0 -1
- package/dist/shared/billing/index.d.ts +0 -30
- package/dist/shared/billing/index.d.ts.map +0 -1
- package/dist/shared/billing/index.js +0 -27
- package/dist/shared/billing/index.js.map +0 -1
- package/dist/shared/billing/routes.d.ts +0 -45
- package/dist/shared/billing/routes.d.ts.map +0 -1
- package/dist/shared/billing/routes.js +0 -184
- package/dist/shared/billing/routes.js.map +0 -1
- package/dist/shared/billing/seat-pricing.d.ts +0 -53
- package/dist/shared/billing/seat-pricing.d.ts.map +0 -1
- package/dist/shared/billing/seat-pricing.js +0 -81
- package/dist/shared/billing/seat-pricing.js.map +0 -1
- package/dist/shared/billing/types.d.ts +0 -109
- package/dist/shared/billing/types.d.ts.map +0 -1
- package/dist/shared/billing/types.js +0 -8
- package/dist/shared/billing/types.js.map +0 -1
- package/dist/shared/email/EmailService.d.ts +0 -64
- package/dist/shared/email/EmailService.d.ts.map +0 -1
- package/dist/shared/email/EmailService.js +0 -300
- package/dist/shared/email/EmailService.js.map +0 -1
- package/dist/shared/email/adminRoutes.d.ts +0 -30
- package/dist/shared/email/adminRoutes.d.ts.map +0 -1
- package/dist/shared/email/adminRoutes.js +0 -227
- package/dist/shared/email/adminRoutes.js.map +0 -1
- package/dist/shared/email/gmail.d.ts +0 -208
- package/dist/shared/email/gmail.d.ts.map +0 -1
- package/dist/shared/email/gmail.js +0 -626
- package/dist/shared/email/gmail.js.map +0 -1
- package/dist/shared/email/index.d.ts +0 -15
- package/dist/shared/email/index.d.ts.map +0 -1
- package/dist/shared/email/index.js +0 -18
- package/dist/shared/email/index.js.map +0 -1
- package/dist/shared/email/mailgun.d.ts +0 -18
- package/dist/shared/email/mailgun.d.ts.map +0 -1
- package/dist/shared/email/mailgun.js +0 -76
- package/dist/shared/email/mailgun.js.map +0 -1
- package/dist/shared/email/sanitize.d.ts +0 -25
- package/dist/shared/email/sanitize.d.ts.map +0 -1
- package/dist/shared/email/sanitize.js +0 -39
- package/dist/shared/email/sanitize.js.map +0 -1
- package/dist/shared/email/smtp.d.ts +0 -20
- package/dist/shared/email/smtp.d.ts.map +0 -1
- package/dist/shared/email/smtp.js +0 -53
- package/dist/shared/email/smtp.js.map +0 -1
- package/dist/shared/email/types.d.ts +0 -113
- package/dist/shared/email/types.d.ts.map +0 -1
- package/dist/shared/email/types.js +0 -7
- package/dist/shared/email/types.js.map +0 -1
- package/dist/shared/email/webhookRoutes.d.ts +0 -29
- package/dist/shared/email/webhookRoutes.d.ts.map +0 -1
- package/dist/shared/email/webhookRoutes.js +0 -125
- package/dist/shared/email/webhookRoutes.js.map +0 -1
- package/dist/shared/mcp/index.d.ts +0 -51
- package/dist/shared/mcp/index.d.ts.map +0 -1
- package/dist/shared/mcp/index.js +0 -51
- package/dist/shared/mcp/index.js.map +0 -1
- package/dist/shared/mcp/mcp-auth-routes.d.ts +0 -26
- package/dist/shared/mcp/mcp-auth-routes.d.ts.map +0 -1
- package/dist/shared/mcp/mcp-auth-routes.js +0 -141
- package/dist/shared/mcp/mcp-auth-routes.js.map +0 -1
- package/dist/shared/mcp/mcp-db.d.ts +0 -99
- package/dist/shared/mcp/mcp-db.d.ts.map +0 -1
- package/dist/shared/mcp/mcp-db.js +0 -106
- package/dist/shared/mcp/mcp-db.js.map +0 -1
- package/dist/shared/mcp/mcp-routes.d.ts +0 -29
- package/dist/shared/mcp/mcp-routes.d.ts.map +0 -1
- package/dist/shared/mcp/mcp-routes.js +0 -171
- package/dist/shared/mcp/mcp-routes.js.map +0 -1
- package/dist/shared/mcp/mcp-tokens-routes.d.ts +0 -35
- package/dist/shared/mcp/mcp-tokens-routes.d.ts.map +0 -1
- package/dist/shared/mcp/mcp-tokens-routes.js +0 -94
- package/dist/shared/mcp/mcp-tokens-routes.js.map +0 -1
- package/dist/shared/mcp/mcp-usage-routes.d.ts +0 -17
- package/dist/shared/mcp/mcp-usage-routes.d.ts.map +0 -1
- package/dist/shared/mcp/mcp-usage-routes.js +0 -81
- package/dist/shared/mcp/mcp-usage-routes.js.map +0 -1
- package/dist/shared/mcp/tenant-context.d.ts +0 -59
- package/dist/shared/mcp/tenant-context.d.ts.map +0 -1
- package/dist/shared/mcp/tenant-context.js +0 -136
- package/dist/shared/mcp/tenant-context.js.map +0 -1
- package/dist/shared/mcp/types.d.ts +0 -74
- package/dist/shared/mcp/types.d.ts.map +0 -1
- package/dist/shared/mcp/types.js +0 -7
- package/dist/shared/mcp/types.js.map +0 -1
- package/dist/shared/planner/GoogleCalendarService.d.ts +0 -137
- package/dist/shared/planner/GoogleCalendarService.d.ts.map +0 -1
- package/dist/shared/planner/GoogleCalendarService.js +0 -525
- package/dist/shared/planner/GoogleCalendarService.js.map +0 -1
- package/dist/shared/planner/PlannerService.d.ts +0 -264
- package/dist/shared/planner/PlannerService.d.ts.map +0 -1
- package/dist/shared/planner/PlannerService.js +0 -1393
- package/dist/shared/planner/PlannerService.js.map +0 -1
- package/dist/shared/planner/index.d.ts +0 -37
- package/dist/shared/planner/index.d.ts.map +0 -1
- package/dist/shared/planner/index.js +0 -35
- package/dist/shared/planner/index.js.map +0 -1
- package/dist/shared/planner/intervals.d.ts +0 -60
- package/dist/shared/planner/intervals.d.ts.map +0 -1
- package/dist/shared/planner/intervals.js +0 -141
- package/dist/shared/planner/intervals.js.map +0 -1
- package/dist/shared/planner/routes.d.ts +0 -69
- package/dist/shared/planner/routes.d.ts.map +0 -1
- package/dist/shared/planner/routes.js +0 -770
- package/dist/shared/planner/routes.js.map +0 -1
- package/dist/shared/planner/types.d.ts +0 -328
- package/dist/shared/planner/types.d.ts.map +0 -1
- package/dist/shared/planner/types.js +0 -9
- package/dist/shared/planner/types.js.map +0 -1
- package/dist/shared/storage/ImageProcessingService.d.ts +0 -32
- package/dist/shared/storage/ImageProcessingService.d.ts.map +0 -1
- package/dist/shared/storage/ImageProcessingService.js +0 -127
- package/dist/shared/storage/ImageProcessingService.js.map +0 -1
- package/dist/shared/storage/StorageProxyService.d.ts +0 -47
- package/dist/shared/storage/StorageProxyService.d.ts.map +0 -1
- package/dist/shared/storage/StorageProxyService.js +0 -196
- package/dist/shared/storage/StorageProxyService.js.map +0 -1
- package/dist/shared/storage/StorageUploadService.d.ts +0 -126
- package/dist/shared/storage/StorageUploadService.d.ts.map +0 -1
- package/dist/shared/storage/StorageUploadService.js +0 -206
- package/dist/shared/storage/StorageUploadService.js.map +0 -1
- package/dist/shared/storage/creative-urls.d.ts +0 -14
- package/dist/shared/storage/creative-urls.d.ts.map +0 -1
- package/dist/shared/storage/creative-urls.js +0 -30
- package/dist/shared/storage/creative-urls.js.map +0 -1
- package/dist/shared/storage/index.d.ts +0 -28
- package/dist/shared/storage/index.d.ts.map +0 -1
- package/dist/shared/storage/index.js +0 -27
- package/dist/shared/storage/index.js.map +0 -1
- package/dist/shared/storage/routes.d.ts +0 -42
- package/dist/shared/storage/routes.d.ts.map +0 -1
- package/dist/shared/storage/routes.js +0 -160
- package/dist/shared/storage/routes.js.map +0 -1
- package/dist/shared/storage/types.d.ts +0 -53
- package/dist/shared/storage/types.d.ts.map +0 -1
- package/dist/shared/storage/types.js +0 -2
- package/dist/shared/storage/types.js.map +0 -1
- package/dist/shared/telegram/index.d.ts +0 -4
- package/dist/shared/telegram/index.d.ts.map +0 -1
- package/dist/shared/telegram/index.js +0 -3
- package/dist/shared/telegram/index.js.map +0 -1
- package/dist/shared/telegram/routes.d.ts +0 -43
- package/dist/shared/telegram/routes.d.ts.map +0 -1
- package/dist/shared/telegram/routes.js +0 -868
- package/dist/shared/telegram/routes.js.map +0 -1
- package/dist/shared/telegram/types.d.ts +0 -168
- package/dist/shared/telegram/types.d.ts.map +0 -1
- package/dist/shared/telegram/types.js +0 -7
- package/dist/shared/telegram/types.js.map +0 -1
- package/dist/shared/telegram/utils.d.ts +0 -44
- package/dist/shared/telegram/utils.d.ts.map +0 -1
- package/dist/shared/telegram/utils.js +0 -121
- package/dist/shared/telegram/utils.js.map +0 -1
- package/dist/storage.d.ts +0 -9
- package/dist/storage.d.ts.map +0 -1
- package/dist/storage.js +0 -8
- package/dist/storage.js.map +0 -1
- package/dist/telemetry.d.ts +0 -9
- package/dist/telemetry.d.ts.map +0 -1
- package/dist/telemetry.js +0 -8
- package/dist/telemetry.js.map +0 -1
- package/scripts/postinstall.js +0 -79
- package/src/shared/affiliate/migrations/001_create_affiliates.sql +0 -49
- package/src/shared/affiliate/migrations/002_create_affiliate_commissions.sql +0 -31
- package/src/shared/affiliate/migrations/003_create_affiliate_clicks.sql +0 -26
- package/src/shared/affiliate/migrations/004_create_affiliate_payments.sql +0 -34
- package/src/shared/affiliate/migrations/005_create_affiliate_tier_history.sql +0 -19
- package/src/shared/affiliate/migrations/006_create_affiliate_rpc_functions.sql +0 -209
- package/src/shared/affiliate/migrations/007_create_affiliate_rls_policies.sql +0 -123
- package/src/shared/billing/migrations/00000000000001_billing.sql +0 -114
- package/src/shared/email/migrations/000_create_email_logs.sql +0 -27
- package/src/shared/email/migrations/001_create_email_templates.sql +0 -27
- package/src/shared/email/migrations/002_add_rls_baseline_policies.sql +0 -37
- package/src/shared/email/migrations/003_create_gmail_accounts.sql +0 -82
- package/src/shared/email/migrations/004_add_email_logs_tracking_columns.sql +0 -15
- package/src/shared/mcp/migrations/001_mcp_api_tokens.sql +0 -21
- 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"}
|
package/dist/shared/mcp/index.js
DELETED
|
@@ -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
|