@soulbatical/tetra-core 0.1.24 → 0.1.26

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 (35) hide show
  1. package/dist/index.d.ts +2 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +2 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/shared/mcp/index.d.ts +48 -0
  6. package/dist/shared/mcp/index.d.ts.map +1 -0
  7. package/dist/shared/mcp/index.js +49 -0
  8. package/dist/shared/mcp/index.js.map +1 -0
  9. package/dist/shared/mcp/mcp-auth-routes.d.ts +26 -0
  10. package/dist/shared/mcp/mcp-auth-routes.d.ts.map +1 -0
  11. package/dist/shared/mcp/mcp-auth-routes.js +138 -0
  12. package/dist/shared/mcp/mcp-auth-routes.js.map +1 -0
  13. package/dist/shared/mcp/mcp-routes.d.ts +29 -0
  14. package/dist/shared/mcp/mcp-routes.d.ts.map +1 -0
  15. package/dist/shared/mcp/mcp-routes.js +168 -0
  16. package/dist/shared/mcp/mcp-routes.js.map +1 -0
  17. package/dist/shared/mcp/mcp-tokens-routes.d.ts +21 -0
  18. package/dist/shared/mcp/mcp-tokens-routes.d.ts.map +1 -0
  19. package/dist/shared/mcp/mcp-tokens-routes.js +82 -0
  20. package/dist/shared/mcp/mcp-tokens-routes.js.map +1 -0
  21. package/dist/shared/mcp/mcp-usage-routes.d.ts +17 -0
  22. package/dist/shared/mcp/mcp-usage-routes.d.ts.map +1 -0
  23. package/dist/shared/mcp/mcp-usage-routes.js +78 -0
  24. package/dist/shared/mcp/mcp-usage-routes.js.map +1 -0
  25. package/dist/shared/mcp/tenant-context.d.ts +44 -0
  26. package/dist/shared/mcp/tenant-context.d.ts.map +1 -0
  27. package/dist/shared/mcp/tenant-context.js +117 -0
  28. package/dist/shared/mcp/tenant-context.js.map +1 -0
  29. package/dist/shared/mcp/types.d.ts +58 -0
  30. package/dist/shared/mcp/types.d.ts.map +1 -0
  31. package/dist/shared/mcp/types.js +7 -0
  32. package/dist/shared/mcp/types.js.map +1 -0
  33. package/package.json +8 -2
  34. package/src/shared/mcp/migrations/001_mcp_api_tokens.sql +21 -0
  35. package/src/shared/mcp/migrations/002_mcp_audit_log.sql +16 -0
package/dist/index.d.ts CHANGED
@@ -96,6 +96,8 @@ export { RPCGenerator, DetailRPCGenerator, validateConfig as validateRPCConfig,
96
96
  export type { GeneratedSQL, FilterDefinition, RPCGeneratorOptions, DetailRPCGeneratorOptions, ValidationResult as RPCValidationResult, ValidationError as RPCValidationError, AccessLevel, CreatorVisibilityConfig as RPCCreatorVisibilityConfig } from './generators/rpc/index.js';
97
97
  export { EmailService, sendMailgunEmail, escapeHtml } from './shared/email/index.js';
98
98
  export type { EmailConfig, SendEmailOpts, EmailTemplate, EmailLogEntry, MailgunResponse } from './shared/email/index.js';
99
+ export { addMcpRoutes, addMcpAuthRoutes, addMcpTokenRoutes, addMcpUsageRoutes, getMcpOrganizationId, getMcpTenantContext, runWithMcpTenant, validateMcpApiToken, generateMcpApiToken } from './shared/mcp/index.js';
100
+ export type { McpRoutesConfig, McpAuthRoutesConfig, McpToolDefinition, McpToolResult, McpToolHandler, TenantContext as McpTenantContext } from './shared/mcp/index.js';
99
101
  export { createApp } from './core/createApp.js';
100
102
  export type { CreateAppConfig } from './core/createApp.js';
101
103
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -90,6 +90,8 @@ export { generateExecSQL, checkExecSQLExists } from './generators/rls-exec-sql.j
90
90
  export { RPCGenerator, DetailRPCGenerator, validateConfig as validateRPCConfig, generateAuthCheck, generateAuthWhereClause, generateAuthDeclarations, generateTimestamp, getTableAlias, escapeIdentifier } from './generators/rpc/index.js';
91
91
  // ─── Email Module ───────────────────────────────────────────
92
92
  export { EmailService, sendMailgunEmail, escapeHtml } from './shared/email/index.js';
93
+ // ─── MCP Online Module ──────────────────────────────────────
94
+ export { addMcpRoutes, addMcpAuthRoutes, addMcpTokenRoutes, addMcpUsageRoutes, getMcpOrganizationId, getMcpTenantContext, runWithMcpTenant, validateMcpApiToken, generateMcpApiToken } from './shared/mcp/index.js';
93
95
  // ─── App Bootstrap ──────────────────────────────────────────
94
96
  export { createApp } from './core/createApp.js';
95
97
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
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
+ export { addMcpRoutes } from './mcp-routes.js';
43
+ export { addMcpAuthRoutes } from './mcp-auth-routes.js';
44
+ export { addMcpTokenRoutes } from './mcp-tokens-routes.js';
45
+ export { addMcpUsageRoutes } from './mcp-usage-routes.js';
46
+ export { getMcpOrganizationId, getMcpTenantContext, runWithMcpTenant, validateMcpApiToken, generateMcpApiToken } from './tenant-context.js';
47
+ export type { McpRoutesConfig, McpAuthRoutesConfig, McpToolDefinition, McpToolResult, McpToolHandler, TenantContext } from './types.js';
48
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
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;AAG5I,YAAY,EAAE,eAAe,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,aAAa,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,49 @@
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
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
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"}
@@ -0,0 +1,26 @@
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
@@ -0,0 +1 @@
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,CAwHlF"}
@@ -0,0 +1,138 @@
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
+ const db = systemDB('mcp-auth');
49
+ // POST /request — Create pending auth request
50
+ router.post('/request', (req, res) => {
51
+ const { device_name } = req.body || {};
52
+ const requestId = randomUUID();
53
+ pendingRequests.set(requestId, {
54
+ requestId,
55
+ deviceName: device_name || defaultDeviceName,
56
+ createdAt: Date.now(),
57
+ });
58
+ res.json({
59
+ request_id: requestId,
60
+ login_url: `${frontendUrl}${frontendApprovalPath}?mcp_auth=${requestId}`,
61
+ expires_in: expirySeconds,
62
+ });
63
+ });
64
+ // GET /status/:requestId — Check if request is valid (for frontend)
65
+ router.get('/status/:requestId', (req, res) => {
66
+ const requestId = paramStr(req.params.requestId);
67
+ const pending = pendingRequests.get(requestId);
68
+ if (!pending) {
69
+ res.status(404).json({ error: 'Auth request not found or expired' });
70
+ return;
71
+ }
72
+ const expiryMs = expirySeconds * 1000;
73
+ res.json({
74
+ status: pending.approved ? 'approved' : 'pending',
75
+ device_name: pending.deviceName,
76
+ expires_in: Math.max(0, Math.ceil((pending.createdAt + expiryMs - Date.now()) / 1000)),
77
+ });
78
+ });
79
+ // POST /approve/:requestId — Approve and generate token (requires JWT)
80
+ router.post('/approve/:requestId', authenticateToken, async (req, res) => {
81
+ try {
82
+ const requestId = paramStr(req.params.requestId);
83
+ const user = req.user;
84
+ const pending = pendingRequests.get(requestId);
85
+ if (!pending) {
86
+ res.status(404).json({ error: 'Auth request not found or expired' });
87
+ return;
88
+ }
89
+ if (pending.approved) {
90
+ res.status(409).json({ error: 'Already approved' });
91
+ return;
92
+ }
93
+ const organizationId = getOrgId(req);
94
+ if (!organizationId) {
95
+ res.status(400).json({ error: 'No active organization' });
96
+ return;
97
+ }
98
+ // Generate API token
99
+ const tokenName = `${pending.deviceName} (${new Date().toISOString().slice(0, 10)})`;
100
+ const token = await generateMcpApiToken(organizationId, tokenName, tokenPrefix, user.id);
101
+ // Get org name for display
102
+ const { data: org } = await db
103
+ .from('organizations')
104
+ .select('name')
105
+ .eq('id', organizationId)
106
+ .single();
107
+ pending.apiToken = token;
108
+ pending.organizationName = org?.name || 'Unknown';
109
+ pending.approved = true;
110
+ res.json({ success: true, organization: org?.name });
111
+ }
112
+ catch (err) {
113
+ res.status(500).json({ error: 'Internal server error' });
114
+ }
115
+ });
116
+ // GET /poll/:requestId — Poll for token (MCP client side)
117
+ router.get('/poll/:requestId', (req, res) => {
118
+ const requestId = paramStr(req.params.requestId);
119
+ const pending = pendingRequests.get(requestId);
120
+ if (!pending) {
121
+ res.status(404).json({ error: 'Auth request not found or expired' });
122
+ return;
123
+ }
124
+ if (!pending.approved) {
125
+ res.json({ status: 'pending' });
126
+ return;
127
+ }
128
+ // Return token and clean up (one-time retrieval)
129
+ const result = {
130
+ status: 'approved',
131
+ api_token: pending.apiToken,
132
+ organization: pending.organizationName,
133
+ };
134
+ pendingRequests.delete(requestId);
135
+ res.json(result);
136
+ });
137
+ }
138
+ //# sourceMappingURL=mcp-auth-routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-auth-routes.js","sourceRoot":"","sources":["../../../src/shared/mcp/mcp-auth-routes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
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('/api/mcp', mcpRouter);
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
@@ -0,0 +1 @@
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,CAuI1E"}
@@ -0,0 +1,168 @@
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('/api/mcp', mcpRouter);
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, } = 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
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
+ return await validateMcpApiToken(token, tokenPrefix);
76
+ }
77
+ catch (error) {
78
+ const message = error instanceof Error ? error.message : 'Authentication failed';
79
+ res.status(401).json({ error: message });
80
+ return null;
81
+ }
82
+ }
83
+ // Lazy import MCP SDK (peer dependency)
84
+ let mcpSdkLoaded = false;
85
+ let Server;
86
+ let StreamableHTTPServerTransport;
87
+ let CallToolRequestSchema;
88
+ let ListToolsRequestSchema;
89
+ async function ensureMcpSdk() {
90
+ if (mcpSdkLoaded)
91
+ return;
92
+ const serverMod = await import('@modelcontextprotocol/sdk/server/index.js');
93
+ const transportMod = await import('@modelcontextprotocol/sdk/server/streamableHttp.js');
94
+ const typesMod = await import('@modelcontextprotocol/sdk/types.js');
95
+ Server = serverMod.Server;
96
+ StreamableHTTPServerTransport = transportMod.StreamableHTTPServerTransport;
97
+ CallToolRequestSchema = typesMod.CallToolRequestSchema;
98
+ ListToolsRequestSchema = typesMod.ListToolsRequestSchema;
99
+ mcpSdkLoaded = true;
100
+ }
101
+ // Create MCP server instance
102
+ function createMcpServer(tenant) {
103
+ const server = new Server({ name: 'tetra-mcp', version: '1.0.0' }, { capabilities: { tools: {} } });
104
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
105
+ tools,
106
+ }));
107
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
108
+ const { name, arguments: args = {} } = request.params;
109
+ const handler = handlers[name];
110
+ if (!handler) {
111
+ throw new Error(`Unknown tool: ${name}`);
112
+ }
113
+ logToolCall(tenant.tokenId, tenant.organizationId, name);
114
+ return handler(args);
115
+ });
116
+ return server;
117
+ }
118
+ // Main endpoint
119
+ router.all('/', async (req, res) => {
120
+ const tenant = await authenticateRequest(req, res);
121
+ if (!tenant)
122
+ return;
123
+ // Rate limit
124
+ const rateResult = checkRateLimit(tenant.tokenId, rateLimitMax, rateLimitWindowMs);
125
+ res.setHeader('X-RateLimit-Limit', rateLimitMax);
126
+ res.setHeader('X-RateLimit-Remaining', rateResult.remaining);
127
+ res.setHeader('X-RateLimit-Reset', Math.ceil(rateResult.resetAt / 1000));
128
+ if (!rateResult.allowed) {
129
+ res.status(429).json({
130
+ error: 'Rate limit exceeded',
131
+ retry_after: Math.ceil((rateResult.resetAt - Date.now()) / 1000),
132
+ });
133
+ return;
134
+ }
135
+ // Lazy-load MCP SDK
136
+ try {
137
+ await ensureMcpSdk();
138
+ }
139
+ catch (err) {
140
+ logger.error({ err }, 'Failed to load @modelcontextprotocol/sdk — is it installed?');
141
+ res.status(500).json({ error: 'MCP SDK not available' });
142
+ return;
143
+ }
144
+ const transport = new StreamableHTTPServerTransport({
145
+ sessionIdGenerator: undefined,
146
+ });
147
+ const server = createMcpServer(tenant);
148
+ try {
149
+ await server.connect(transport);
150
+ await runWithMcpTenant(tenant, async () => {
151
+ await transport.handleRequest(req, res, req.body);
152
+ });
153
+ }
154
+ catch (error) {
155
+ if (!res.headersSent) {
156
+ res.status(500).json({
157
+ error: 'MCP request failed',
158
+ details: error instanceof Error ? error.message : 'Unknown error',
159
+ });
160
+ }
161
+ }
162
+ finally {
163
+ await transport.close().catch(() => { });
164
+ await server.close().catch(() => { });
165
+ }
166
+ });
167
+ }
168
+ //# sourceMappingURL=mcp-routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-routes.js","sourceRoot":"","sources":["../../../src/shared/mcp/mcp-routes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * MCP Token Management Routes — Factory
3
+ *
4
+ * CRUD for API tokens. All endpoints require authenticated user (Supabase JWT).
5
+ * Tokens are scoped to the user's active organization.
6
+ *
7
+ * Usage:
8
+ * ```typescript
9
+ * import { addMcpTokenRoutes } from '@soulbatical/tetra-core';
10
+ *
11
+ * const mcpTokensRouter = Router();
12
+ * addMcpTokenRoutes(mcpTokensRouter, { tokenPrefix: 'vincifox_' });
13
+ * app.use('/api/mcp-tokens', mcpTokensRouter);
14
+ * ```
15
+ */
16
+ import type { Router } from 'express';
17
+ export interface McpTokenRoutesConfig {
18
+ tokenPrefix: string;
19
+ }
20
+ export declare function addMcpTokenRoutes(router: Router, config: McpTokenRoutesConfig): void;
21
+ //# sourceMappingURL=mcp-tokens-routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-tokens-routes.d.ts","sourceRoot":"","sources":["../../../src/shared/mcp/mcp-tokens-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAqB,MAAM,SAAS,CAAC;AAKzD,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;CACrB;AAOD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,GAAG,IAAI,CAqEpF"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * MCP Token Management Routes — Factory
3
+ *
4
+ * CRUD for API tokens. All endpoints require authenticated user (Supabase JWT).
5
+ * Tokens are scoped to the user's active organization.
6
+ *
7
+ * Usage:
8
+ * ```typescript
9
+ * import { addMcpTokenRoutes } from '@soulbatical/tetra-core';
10
+ *
11
+ * const mcpTokensRouter = Router();
12
+ * addMcpTokenRoutes(mcpTokensRouter, { tokenPrefix: 'vincifox_' });
13
+ * app.use('/api/mcp-tokens', mcpTokensRouter);
14
+ * ```
15
+ */
16
+ import { authenticateToken } from '../../middleware/authMiddleware.js';
17
+ import { systemDB } from '../../core/systemDb.js';
18
+ import { generateMcpApiToken } from './tenant-context.js';
19
+ function getOrgId(req) {
20
+ const user = req.user;
21
+ return user?.activeOrganizationId || user?.active_organization_id || user?.organizationId || null;
22
+ }
23
+ export function addMcpTokenRoutes(router, config) {
24
+ const { tokenPrefix } = config;
25
+ const db = systemDB('mcp-tokens');
26
+ // GET / — List all tokens for the active organization
27
+ router.get('/', authenticateToken, async (req, res) => {
28
+ const orgId = getOrgId(req);
29
+ if (!orgId) {
30
+ res.status(400).json({ error: 'No active organization' });
31
+ return;
32
+ }
33
+ const { data, error } = await db
34
+ .from('mcp_api_tokens')
35
+ .select('id, name, is_active, last_used_at, created_at, revoked_at, created_by')
36
+ .eq('organization_id', orgId)
37
+ .order('created_at', { ascending: false });
38
+ if (error) {
39
+ res.status(500).json({ error: 'Failed to list tokens' });
40
+ return;
41
+ }
42
+ res.json({ tokens: data || [] });
43
+ });
44
+ // POST / — Generate a new API token
45
+ router.post('/', authenticateToken, async (req, res) => {
46
+ const orgId = getOrgId(req);
47
+ const user = req.user;
48
+ if (!orgId) {
49
+ res.status(400).json({ error: 'No active organization' });
50
+ return;
51
+ }
52
+ const { name } = req.body || {};
53
+ const tokenName = name || `API Token (${new Date().toISOString().slice(0, 10)})`;
54
+ try {
55
+ const token = await generateMcpApiToken(orgId, tokenName, tokenPrefix, user.id);
56
+ res.json({ token, name: tokenName });
57
+ }
58
+ catch (err) {
59
+ res.status(500).json({ error: 'Failed to generate token' });
60
+ }
61
+ });
62
+ // DELETE /:tokenId — Revoke a token
63
+ router.delete('/:tokenId', authenticateToken, async (req, res) => {
64
+ const orgId = getOrgId(req);
65
+ if (!orgId) {
66
+ res.status(400).json({ error: 'No active organization' });
67
+ return;
68
+ }
69
+ const tokenId = Array.isArray(req.params.tokenId) ? req.params.tokenId[0] : req.params.tokenId;
70
+ const { error } = await db
71
+ .from('mcp_api_tokens')
72
+ .update({ is_active: false, revoked_at: new Date().toISOString() })
73
+ .eq('id', tokenId)
74
+ .eq('organization_id', orgId);
75
+ if (error) {
76
+ res.status(500).json({ error: 'Failed to revoke token' });
77
+ return;
78
+ }
79
+ res.json({ success: true });
80
+ });
81
+ }
82
+ //# sourceMappingURL=mcp-tokens-routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-tokens-routes.js","sourceRoot":"","sources":["../../../src/shared/mcp/mcp-tokens-routes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * MCP Usage Routes — Factory
3
+ *
4
+ * Returns audit log stats for the active organization.
5
+ *
6
+ * Usage:
7
+ * ```typescript
8
+ * import { addMcpUsageRoutes } from '@soulbatical/tetra-core';
9
+ *
10
+ * const mcpUsageRouter = Router();
11
+ * addMcpUsageRoutes(mcpUsageRouter);
12
+ * app.use('/api/mcp-usage', mcpUsageRouter);
13
+ * ```
14
+ */
15
+ import type { Router } from 'express';
16
+ export declare function addMcpUsageRoutes(router: Router): void;
17
+ //# sourceMappingURL=mcp-usage-routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-usage-routes.d.ts","sourceRoot":"","sources":["../../../src/shared/mcp/mcp-usage-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAqB,MAAM,SAAS,CAAC;AASzD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CA8DtD"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * MCP Usage Routes — Factory
3
+ *
4
+ * Returns audit log stats for the active organization.
5
+ *
6
+ * Usage:
7
+ * ```typescript
8
+ * import { addMcpUsageRoutes } from '@soulbatical/tetra-core';
9
+ *
10
+ * const mcpUsageRouter = Router();
11
+ * addMcpUsageRoutes(mcpUsageRouter);
12
+ * app.use('/api/mcp-usage', mcpUsageRouter);
13
+ * ```
14
+ */
15
+ import { authenticateToken } from '../../middleware/authMiddleware.js';
16
+ import { systemDB } from '../../core/systemDb.js';
17
+ function getOrgId(req) {
18
+ const user = req.user;
19
+ return user?.activeOrganizationId || user?.active_organization_id || user?.organizationId || null;
20
+ }
21
+ export function addMcpUsageRoutes(router) {
22
+ const db = systemDB('mcp-usage');
23
+ // GET / — Usage stats (total calls, by tool, daily trend)
24
+ // Query: ?period=7d|30d|90d
25
+ router.get('/', authenticateToken, async (req, res) => {
26
+ const orgId = getOrgId(req);
27
+ if (!orgId) {
28
+ res.status(400).json({ error: 'No active organization' });
29
+ return;
30
+ }
31
+ const period = req.query.period || '30d';
32
+ const days = period === '7d' ? 7 : period === '90d' ? 90 : 30;
33
+ const since = new Date();
34
+ since.setDate(since.getDate() - days);
35
+ try {
36
+ const { count: totalCalls } = await db
37
+ .from('mcp_audit_log')
38
+ .select('id', { count: 'exact', head: true })
39
+ .eq('organization_id', orgId)
40
+ .gte('created_at', since.toISOString());
41
+ const { data: logs } = await db
42
+ .from('mcp_audit_log')
43
+ .select('tool_name, created_at')
44
+ .eq('organization_id', orgId)
45
+ .gte('created_at', since.toISOString())
46
+ .order('created_at', { ascending: true });
47
+ // Aggregate
48
+ const byTool = {};
49
+ const byDay = {};
50
+ for (const log of logs || []) {
51
+ byTool[log.tool_name] = (byTool[log.tool_name] || 0) + 1;
52
+ const day = log.created_at.slice(0, 10);
53
+ byDay[day] = (byDay[day] || 0) + 1;
54
+ }
55
+ // Fill day gaps
56
+ const trend = [];
57
+ const current = new Date(since);
58
+ const now = new Date();
59
+ while (current <= now) {
60
+ const day = current.toISOString().slice(0, 10);
61
+ trend.push({ date: day, count: byDay[day] || 0 });
62
+ current.setDate(current.getDate() + 1);
63
+ }
64
+ res.json({
65
+ period,
66
+ total_calls: totalCalls || 0,
67
+ by_tool: Object.entries(byTool)
68
+ .map(([tool, count]) => ({ tool, count }))
69
+ .sort((a, b) => b.count - a.count),
70
+ trend,
71
+ });
72
+ }
73
+ catch (err) {
74
+ res.status(500).json({ error: 'Failed to load usage stats' });
75
+ }
76
+ });
77
+ }
78
+ //# sourceMappingURL=mcp-usage-routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-usage-routes.js","sourceRoot":"","sources":["../../../src/shared/mcp/mcp-usage-routes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * MCP Tenant Context
3
+ *
4
+ * Provides:
5
+ * - Token validation (Bearer token → organization_id)
6
+ * - Request-scoped tenant context via AsyncLocalStorage
7
+ * - Token generation for admin use
8
+ *
9
+ * Works with any project that has the mcp_api_tokens table.
10
+ */
11
+ import type { TenantContext } from './types.js';
12
+ /**
13
+ * Get the current tenant's organization_id.
14
+ * Returns undefined when running in local stdio mode (no tenant context).
15
+ */
16
+ export declare function getMcpOrganizationId(): string | undefined;
17
+ /**
18
+ * Get the full tenant context (org + token).
19
+ */
20
+ export declare function getMcpTenantContext(): TenantContext | undefined;
21
+ /**
22
+ * Run a function within a tenant context.
23
+ * The context is propagated through async calls via AsyncLocalStorage.
24
+ */
25
+ export declare function runWithMcpTenant<T>(ctx: TenantContext, fn: () => T): T;
26
+ /**
27
+ * Validate a Bearer token and return the tenant context.
28
+ * Throws if token is invalid, revoked, or inactive.
29
+ *
30
+ * @param token - The raw API token (e.g. "vincifox_abc123...")
31
+ * @param expectedPrefix - Expected token prefix (e.g. "vincifox_")
32
+ */
33
+ export declare function validateMcpApiToken(token: string, expectedPrefix: string): Promise<TenantContext>;
34
+ /**
35
+ * Generate a new API token for an organization.
36
+ * Returns the plaintext token (only shown once — stored as SHA-256 hash).
37
+ *
38
+ * @param organizationId - The organization UUID
39
+ * @param name - Human-readable token name
40
+ * @param prefix - Token prefix (e.g. "vincifox_")
41
+ * @param createdBy - Optional user UUID who created the token
42
+ */
43
+ export declare function generateMcpApiToken(organizationId: string, name: string, prefix: string, createdBy?: string): Promise<string>;
44
+ //# sourceMappingURL=tenant-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenant-context.d.ts","sourceRoot":"","sources":["../../../src/shared/mcp/tenant-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAMhD;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,GAAG,SAAS,CAEzD;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,aAAa,GAAG,SAAS,CAE/D;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,GAAG,EAAE,aAAa,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAEtE;AA4BD;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,MAAM,EACb,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,aAAa,CAAC,CAiCxB;AAID;;;;;;;;GAQG;AACH,wBAAsB,mBAAmB,CACvC,cAAc,EAAE,MAAM,EACtB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CAkBjB"}
@@ -0,0 +1,117 @@
1
+ /**
2
+ * MCP Tenant Context
3
+ *
4
+ * Provides:
5
+ * - Token validation (Bearer token → organization_id)
6
+ * - Request-scoped tenant context via AsyncLocalStorage
7
+ * - Token generation for admin use
8
+ *
9
+ * Works with any project that has the mcp_api_tokens table.
10
+ */
11
+ import { createHash, randomBytes } from 'node:crypto';
12
+ import { AsyncLocalStorage } from 'node:async_hooks';
13
+ import { createClient } from '@supabase/supabase-js';
14
+ // ── AsyncLocalStorage for request-scoped context ──
15
+ const tenantStorage = new AsyncLocalStorage();
16
+ /**
17
+ * Get the current tenant's organization_id.
18
+ * Returns undefined when running in local stdio mode (no tenant context).
19
+ */
20
+ export function getMcpOrganizationId() {
21
+ return tenantStorage.getStore()?.organizationId;
22
+ }
23
+ /**
24
+ * Get the full tenant context (org + token).
25
+ */
26
+ export function getMcpTenantContext() {
27
+ return tenantStorage.getStore();
28
+ }
29
+ /**
30
+ * Run a function within a tenant context.
31
+ * The context is propagated through async calls via AsyncLocalStorage.
32
+ */
33
+ export function runWithMcpTenant(ctx, fn) {
34
+ return tenantStorage.run(ctx, fn);
35
+ }
36
+ // ── DB client ──
37
+ let db = null;
38
+ function getDb() {
39
+ if (!db) {
40
+ const url = process.env.SUPABASE_URL;
41
+ const key = process.env.SUPABASE_SERVICE_ROLE_KEY;
42
+ if (!url || !key) {
43
+ throw new Error('SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY required for MCP tenant auth');
44
+ }
45
+ db = createClient(url, key, {
46
+ auth: { autoRefreshToken: false, persistSession: false },
47
+ });
48
+ }
49
+ return db;
50
+ }
51
+ // ── Token hashing ──
52
+ function hashToken(token) {
53
+ return createHash('sha256').update(token).digest('hex');
54
+ }
55
+ // ── Token validation ──
56
+ /**
57
+ * Validate a Bearer token and return the tenant context.
58
+ * Throws if token is invalid, revoked, or inactive.
59
+ *
60
+ * @param token - The raw API token (e.g. "vincifox_abc123...")
61
+ * @param expectedPrefix - Expected token prefix (e.g. "vincifox_")
62
+ */
63
+ export async function validateMcpApiToken(token, expectedPrefix) {
64
+ if (!token.startsWith(expectedPrefix)) {
65
+ throw new Error('Invalid token format');
66
+ }
67
+ const hash = hashToken(token);
68
+ const supabase = getDb();
69
+ const { data, error } = await supabase
70
+ .from('mcp_api_tokens')
71
+ .select('id, organization_id, is_active, revoked_at')
72
+ .eq('token_hash', hash)
73
+ .single();
74
+ if (error || !data) {
75
+ throw new Error('Invalid API token');
76
+ }
77
+ if (!data.is_active || data.revoked_at) {
78
+ throw new Error('Token has been revoked');
79
+ }
80
+ // Update last_used_at (fire and forget)
81
+ supabase
82
+ .from('mcp_api_tokens')
83
+ .update({ last_used_at: new Date().toISOString() })
84
+ .eq('id', data.id)
85
+ .then(() => { });
86
+ return {
87
+ organizationId: data.organization_id,
88
+ tokenId: data.id,
89
+ };
90
+ }
91
+ // ── Token generation ──
92
+ /**
93
+ * Generate a new API token for an organization.
94
+ * Returns the plaintext token (only shown once — stored as SHA-256 hash).
95
+ *
96
+ * @param organizationId - The organization UUID
97
+ * @param name - Human-readable token name
98
+ * @param prefix - Token prefix (e.g. "vincifox_")
99
+ * @param createdBy - Optional user UUID who created the token
100
+ */
101
+ export async function generateMcpApiToken(organizationId, name, prefix, createdBy) {
102
+ const hex = randomBytes(32).toString('hex');
103
+ const token = `${prefix}${hex}`;
104
+ const hash = hashToken(token);
105
+ const supabase = getDb();
106
+ const { error } = await supabase.from('mcp_api_tokens').insert({
107
+ organization_id: organizationId,
108
+ token_hash: hash,
109
+ name,
110
+ created_by: createdBy || null,
111
+ });
112
+ if (error) {
113
+ throw new Error(`Failed to create token: ${error.message}`);
114
+ }
115
+ return token;
116
+ }
117
+ //# sourceMappingURL=tenant-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenant-context.js","sourceRoot":"","sources":["../../../src/shared/mcp/tenant-context.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * MCP Online Module — Types
3
+ *
4
+ * Shared types for MCP StreamableHTTP endpoints.
5
+ */
6
+ export interface McpToolDefinition {
7
+ name: string;
8
+ description: string;
9
+ inputSchema: {
10
+ type: 'object';
11
+ properties?: Record<string, unknown>;
12
+ required?: string[];
13
+ };
14
+ }
15
+ export interface McpToolResult {
16
+ content: Array<{
17
+ type: 'text';
18
+ text: string;
19
+ }>;
20
+ isError?: boolean;
21
+ [key: string]: unknown;
22
+ }
23
+ export type McpToolHandler = (args: Record<string, unknown>) => Promise<McpToolResult>;
24
+ export interface TenantContext {
25
+ organizationId: string;
26
+ tokenId: string;
27
+ }
28
+ export interface McpRoutesConfig {
29
+ /** Tool definitions to expose via the MCP endpoint */
30
+ tools: McpToolDefinition[];
31
+ /** Map of tool name → handler function */
32
+ handlers: Record<string, McpToolHandler>;
33
+ /** Token prefix for validation, e.g. "vincifox_" or "snelstart_" */
34
+ tokenPrefix: string;
35
+ /** Rate limit: max requests per window (default: 100) */
36
+ rateLimitMax?: number;
37
+ /** Rate limit: window in ms (default: 15 minutes) */
38
+ rateLimitWindowMs?: number;
39
+ /** Enable audit logging to mcp_audit_log table (default: true) */
40
+ enableAuditLog?: boolean;
41
+ }
42
+ export interface McpAuthRoutesConfig {
43
+ /** Token prefix for generated tokens, e.g. "vincifox_" */
44
+ tokenPrefix: string;
45
+ /** Frontend URL for login redirect (falls back to FRONTEND_URL env or localhost:3000) */
46
+ frontendUrl?: string;
47
+ /** Path on the frontend where the approval page lives (default: "/app/settings/api-tokens") */
48
+ frontendApprovalPath?: string;
49
+ /** Default device name when not provided (default: "Claude Code") */
50
+ defaultDeviceName?: string;
51
+ /** Request expiry in seconds (default: 300 = 5 minutes) */
52
+ expirySeconds?: number;
53
+ }
54
+ export interface McpTokenRoutesConfig {
55
+ }
56
+ export interface McpUsageRoutesConfig {
57
+ }
58
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/shared/mcp/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ,CAAC;QACf,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACrC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;KACrB,CAAC;CACH;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,MAAM,cAAc,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;AAIvF,MAAM,WAAW,aAAa;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;CACjB;AAID,MAAM,WAAW,eAAe;IAC9B,sDAAsD;IACtD,KAAK,EAAE,iBAAiB,EAAE,CAAC;IAC3B,0CAA0C;IAC1C,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACzC,oEAAoE;IACpE,WAAW,EAAE,MAAM,CAAC;IACpB,yDAAyD;IACzD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qDAAqD;IACrD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,kEAAkE;IAClE,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,mBAAmB;IAClC,0DAA0D;IAC1D,WAAW,EAAE,MAAM,CAAC;IACpB,yFAAyF;IACzF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+FAA+F;IAC/F,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,qEAAqE;IACrE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,2DAA2D;IAC3D,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,oBAAoB;CAEpC;AAED,MAAM,WAAW,oBAAoB;CAEpC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * MCP Online Module — Types
3
+ *
4
+ * Shared types for MCP StreamableHTTP endpoints.
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/shared/mcp/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soulbatical/tetra-core",
3
- "version": "0.1.24",
3
+ "version": "0.1.26",
4
4
  "publishConfig": {
5
5
  "access": "restricted"
6
6
  },
@@ -30,7 +30,8 @@
30
30
  "scripts",
31
31
  "src/shared/auth/migrations",
32
32
  "src/shared/affiliate/migrations",
33
- "src/shared/email/migrations"
33
+ "src/shared/email/migrations",
34
+ "src/shared/mcp/migrations"
34
35
  ],
35
36
  "scripts": {
36
37
  "build": "tsc",
@@ -46,6 +47,7 @@
46
47
  "zod": "^3.22.4"
47
48
  },
48
49
  "peerDependencies": {
50
+ "@modelcontextprotocol/sdk": "^1.0.0",
49
51
  "@supabase/supabase-js": "2.93.3",
50
52
  "@tanstack/react-table": "^8.0.0",
51
53
  "express": "^5.0.0",
@@ -61,9 +63,13 @@
61
63
  },
62
64
  "@tanstack/react-table": {
63
65
  "optional": true
66
+ },
67
+ "@modelcontextprotocol/sdk": {
68
+ "optional": true
64
69
  }
65
70
  },
66
71
  "devDependencies": {
72
+ "@modelcontextprotocol/sdk": "^1.27.1",
67
73
  "@supabase/supabase-js": "2.93.3",
68
74
  "@tanstack/react-table": "^8.21.3",
69
75
  "@types/express": "^5.0.0",
@@ -0,0 +1,21 @@
1
+ -- MCP API tokens for online MCP access
2
+ -- Each token is linked to an organization for multi-tenant isolation
3
+ -- Token is stored as SHA-256 hash, never plaintext
4
+
5
+ CREATE TABLE IF NOT EXISTS public.mcp_api_tokens (
6
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
7
+ organization_id UUID NOT NULL REFERENCES organizations(id),
8
+ token_hash TEXT NOT NULL UNIQUE,
9
+ name TEXT NOT NULL DEFAULT 'Default',
10
+ created_by UUID REFERENCES auth.users(id),
11
+ is_active BOOLEAN NOT NULL DEFAULT true,
12
+ last_used_at TIMESTAMPTZ,
13
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
14
+ revoked_at TIMESTAMPTZ
15
+ );
16
+
17
+ ALTER TABLE public.mcp_api_tokens ENABLE ROW LEVEL SECURITY;
18
+ CREATE POLICY "Service role full access" ON public.mcp_api_tokens
19
+ FOR ALL TO service_role USING (true) WITH CHECK (true);
20
+ CREATE INDEX IF NOT EXISTS idx_mcp_api_tokens_hash ON public.mcp_api_tokens(token_hash);
21
+ CREATE INDEX IF NOT EXISTS idx_mcp_api_tokens_org ON public.mcp_api_tokens(organization_id);
@@ -0,0 +1,16 @@
1
+ -- MCP audit log for tracking tool usage per token/organization
2
+
3
+ CREATE TABLE IF NOT EXISTS public.mcp_audit_log (
4
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
5
+ token_id UUID REFERENCES mcp_api_tokens(id),
6
+ organization_id UUID NOT NULL REFERENCES organizations(id),
7
+ tool_name TEXT NOT NULL,
8
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now()
9
+ );
10
+
11
+ ALTER TABLE public.mcp_audit_log ENABLE ROW LEVEL SECURITY;
12
+ CREATE POLICY "Service role full access" ON public.mcp_audit_log
13
+ FOR ALL TO service_role USING (true) WITH CHECK (true);
14
+ CREATE INDEX IF NOT EXISTS idx_mcp_audit_log_org ON public.mcp_audit_log(organization_id);
15
+ CREATE INDEX IF NOT EXISTS idx_mcp_audit_log_token ON public.mcp_audit_log(token_id);
16
+ CREATE INDEX IF NOT EXISTS idx_mcp_audit_log_created ON public.mcp_audit_log(created_at DESC);