mcp-macos 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +360 -0
  3. package/bin/dev.cjs +10 -0
  4. package/bin/run.cjs +15 -0
  5. package/dist/config/index.d.ts +36 -0
  6. package/dist/config/index.js +127 -0
  7. package/dist/config/index.js.map +1 -0
  8. package/dist/config/schema.d.ts +161 -0
  9. package/dist/config/schema.js +45 -0
  10. package/dist/config/schema.js.map +1 -0
  11. package/dist/index.d.ts +11 -0
  12. package/dist/index.js +75 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/server/handlers.d.ts +10 -0
  15. package/dist/server/handlers.js +43 -0
  16. package/dist/server/handlers.js.map +1 -0
  17. package/dist/server/promptAbstractions.d.ts +74 -0
  18. package/dist/server/promptAbstractions.js +150 -0
  19. package/dist/server/promptAbstractions.js.map +1 -0
  20. package/dist/server/prompts.d.ts +8 -0
  21. package/dist/server/prompts.js +480 -0
  22. package/dist/server/prompts.js.map +1 -0
  23. package/dist/server/server.d.ts +29 -0
  24. package/dist/server/server.js +52 -0
  25. package/dist/server/server.js.map +1 -0
  26. package/dist/server/transports/http/auth.d.ts +34 -0
  27. package/dist/server/transports/http/auth.js +148 -0
  28. package/dist/server/transports/http/auth.js.map +1 -0
  29. package/dist/server/transports/http/health.d.ts +35 -0
  30. package/dist/server/transports/http/health.js +93 -0
  31. package/dist/server/transports/http/health.js.map +1 -0
  32. package/dist/server/transports/http/index.d.ts +43 -0
  33. package/dist/server/transports/http/index.js +141 -0
  34. package/dist/server/transports/http/index.js.map +1 -0
  35. package/dist/server/transports/http/middleware.d.ts +53 -0
  36. package/dist/server/transports/http/middleware.js +133 -0
  37. package/dist/server/transports/http/middleware.js.map +1 -0
  38. package/dist/tools/definitions.d.ts +10 -0
  39. package/dist/tools/definitions.js +633 -0
  40. package/dist/tools/definitions.js.map +1 -0
  41. package/dist/tools/handlers/calendarHandlers.d.ts +11 -0
  42. package/dist/tools/handlers/calendarHandlers.js +123 -0
  43. package/dist/tools/handlers/calendarHandlers.js.map +1 -0
  44. package/dist/tools/handlers/contactsHandlers.d.ts +17 -0
  45. package/dist/tools/handlers/contactsHandlers.js +397 -0
  46. package/dist/tools/handlers/contactsHandlers.js.map +1 -0
  47. package/dist/tools/handlers/index.d.ts +11 -0
  48. package/dist/tools/handlers/index.js +12 -0
  49. package/dist/tools/handlers/index.js.map +1 -0
  50. package/dist/tools/handlers/listHandlers.d.ts +10 -0
  51. package/dist/tools/handlers/listHandlers.js +40 -0
  52. package/dist/tools/handlers/listHandlers.js.map +1 -0
  53. package/dist/tools/handlers/mailHandlers.d.ts +11 -0
  54. package/dist/tools/handlers/mailHandlers.js +301 -0
  55. package/dist/tools/handlers/mailHandlers.js.map +1 -0
  56. package/dist/tools/handlers/messagesHandlers.d.ts +17 -0
  57. package/dist/tools/handlers/messagesHandlers.js +350 -0
  58. package/dist/tools/handlers/messagesHandlers.js.map +1 -0
  59. package/dist/tools/handlers/notesHandlers.d.ts +12 -0
  60. package/dist/tools/handlers/notesHandlers.js +305 -0
  61. package/dist/tools/handlers/notesHandlers.js.map +1 -0
  62. package/dist/tools/handlers/reminderHandlers.d.ts +10 -0
  63. package/dist/tools/handlers/reminderHandlers.js +96 -0
  64. package/dist/tools/handlers/reminderHandlers.js.map +1 -0
  65. package/dist/tools/handlers/shared.d.ts +51 -0
  66. package/dist/tools/handlers/shared.js +107 -0
  67. package/dist/tools/handlers/shared.js.map +1 -0
  68. package/dist/tools/index.d.ts +10 -0
  69. package/dist/tools/index.js +125 -0
  70. package/dist/tools/index.js.map +1 -0
  71. package/dist/types/index.d.ts +265 -0
  72. package/dist/types/index.js +67 -0
  73. package/dist/types/index.js.map +1 -0
  74. package/dist/types/prompts.d.ts +84 -0
  75. package/dist/types/prompts.js +6 -0
  76. package/dist/types/prompts.js.map +1 -0
  77. package/dist/types/repository.d.ts +102 -0
  78. package/dist/types/repository.js +6 -0
  79. package/dist/types/repository.js.map +1 -0
  80. package/dist/utils/binaryValidator.d.ts +52 -0
  81. package/dist/utils/binaryValidator.js +152 -0
  82. package/dist/utils/binaryValidator.js.map +1 -0
  83. package/dist/utils/calendarRepository.d.ts +25 -0
  84. package/dist/utils/calendarRepository.js +100 -0
  85. package/dist/utils/calendarRepository.js.map +1 -0
  86. package/dist/utils/cliExecutor.d.ts +28 -0
  87. package/dist/utils/cliExecutor.js +196 -0
  88. package/dist/utils/cliExecutor.js.map +1 -0
  89. package/dist/utils/constants.d.ts +96 -0
  90. package/dist/utils/constants.js +97 -0
  91. package/dist/utils/constants.js.map +1 -0
  92. package/dist/utils/contactResolver.d.ts +142 -0
  93. package/dist/utils/contactResolver.js +386 -0
  94. package/dist/utils/contactResolver.js.map +1 -0
  95. package/dist/utils/dateFiltering.d.ts +22 -0
  96. package/dist/utils/dateFiltering.js +72 -0
  97. package/dist/utils/dateFiltering.js.map +1 -0
  98. package/dist/utils/dateUtils.d.ts +20 -0
  99. package/dist/utils/dateUtils.js +36 -0
  100. package/dist/utils/dateUtils.js.map +1 -0
  101. package/dist/utils/errorHandling.d.ts +30 -0
  102. package/dist/utils/errorHandling.js +101 -0
  103. package/dist/utils/errorHandling.js.map +1 -0
  104. package/dist/utils/helpers.d.ts +35 -0
  105. package/dist/utils/helpers.js +59 -0
  106. package/dist/utils/helpers.js.map +1 -0
  107. package/dist/utils/jxaExecutor.d.ts +47 -0
  108. package/dist/utils/jxaExecutor.js +194 -0
  109. package/dist/utils/jxaExecutor.js.map +1 -0
  110. package/dist/utils/logging.d.ts +31 -0
  111. package/dist/utils/logging.js +98 -0
  112. package/dist/utils/logging.js.map +1 -0
  113. package/dist/utils/permissionPrompt.d.ts +16 -0
  114. package/dist/utils/permissionPrompt.js +42 -0
  115. package/dist/utils/permissionPrompt.js.map +1 -0
  116. package/dist/utils/preflight.d.ts +30 -0
  117. package/dist/utils/preflight.js +196 -0
  118. package/dist/utils/preflight.js.map +1 -0
  119. package/dist/utils/projectUtils.d.ts +11 -0
  120. package/dist/utils/projectUtils.js +76 -0
  121. package/dist/utils/projectUtils.js.map +1 -0
  122. package/dist/utils/reminderDateParser.d.ts +8 -0
  123. package/dist/utils/reminderDateParser.js +77 -0
  124. package/dist/utils/reminderDateParser.js.map +1 -0
  125. package/dist/utils/reminderRepository.d.ts +23 -0
  126. package/dist/utils/reminderRepository.js +91 -0
  127. package/dist/utils/reminderRepository.js.map +1 -0
  128. package/dist/utils/sqliteContactReader.d.ts +51 -0
  129. package/dist/utils/sqliteContactReader.js +216 -0
  130. package/dist/utils/sqliteContactReader.js.map +1 -0
  131. package/dist/utils/sqliteMailReader.d.ts +97 -0
  132. package/dist/utils/sqliteMailReader.js +310 -0
  133. package/dist/utils/sqliteMailReader.js.map +1 -0
  134. package/dist/utils/sqliteMessageReader.d.ts +71 -0
  135. package/dist/utils/sqliteMessageReader.js +400 -0
  136. package/dist/utils/sqliteMessageReader.js.map +1 -0
  137. package/dist/utils/timeHelpers.d.ts +40 -0
  138. package/dist/utils/timeHelpers.js +136 -0
  139. package/dist/utils/timeHelpers.js.map +1 -0
  140. package/dist/utils/timezone.d.ts +24 -0
  141. package/dist/utils/timezone.js +39 -0
  142. package/dist/utils/timezone.js.map +1 -0
  143. package/dist/validation/schemas.d.ts +610 -0
  144. package/dist/validation/schemas.js +354 -0
  145. package/dist/validation/schemas.js.map +1 -0
  146. package/package.json +97 -0
  147. package/scripts/build-swift.mjs +86 -0
  148. package/src/swift/EventKitCLI.swift +778 -0
  149. package/src/swift/Info.plist +38 -0
@@ -0,0 +1,148 @@
1
+ /**
2
+ * @fileoverview Cloudflare Access JWT verification for HTTP transport
3
+ * @module server/transports/http/auth
4
+ * @description Verifies JWTs from Cloudflare Access for defense-in-depth security
5
+ */
6
+ import * as jose from 'jose';
7
+ /** JWKS cache TTL in milliseconds (1 hour) */
8
+ const JWKS_CACHE_TTL = 60 * 60 * 1000;
9
+ /** JWKS cache keyed by team domain */
10
+ const jwksCache = new Map();
11
+ /**
12
+ * Gets the JWKS URL for a Cloudflare Access team domain
13
+ * @param teamDomain - Cloudflare Access team domain
14
+ * @returns JWKS URL
15
+ */
16
+ function getJwksUrl(teamDomain) {
17
+ // Normalize domain - remove protocol if present and ensure .cloudflareaccess.com suffix
18
+ let domain = teamDomain.replace(/^https?:\/\//, '');
19
+ // If just the team name is provided, add the full domain
20
+ if (!domain.includes('.')) {
21
+ domain = `${domain}.cloudflareaccess.com`;
22
+ }
23
+ return `https://${domain}/cdn-cgi/access/certs`;
24
+ }
25
+ /**
26
+ * Gets or creates a cached JWKS getter for the team domain
27
+ * @param teamDomain - Cloudflare Access team domain
28
+ * @returns JWKS getter function
29
+ */
30
+ async function getJwks(teamDomain) {
31
+ const cached = jwksCache.get(teamDomain);
32
+ if (cached && cached.expiresAt > Date.now()) {
33
+ return cached.jwks;
34
+ }
35
+ const jwksUrl = getJwksUrl(teamDomain);
36
+ const jwks = jose.createRemoteJWKSet(new URL(jwksUrl));
37
+ jwksCache.set(teamDomain, {
38
+ jwks,
39
+ expiresAt: Date.now() + JWKS_CACHE_TTL,
40
+ });
41
+ return jwks;
42
+ }
43
+ /**
44
+ * Verifies a Cloudflare Access JWT
45
+ *
46
+ * @param token - JWT from Cf-Access-Jwt-Assertion header
47
+ * @param config - Cloudflare Access configuration
48
+ * @returns Verification result
49
+ */
50
+ export async function verifyCloudflareAccessJwt(token, config) {
51
+ try {
52
+ const jwks = await getJwks(config.teamDomain);
53
+ // Normalize team domain for issuer validation
54
+ let normalizedDomain = config.teamDomain.replace(/^https?:\/\//, '');
55
+ if (!normalizedDomain.includes('.')) {
56
+ normalizedDomain = `${normalizedDomain}.cloudflareaccess.com`;
57
+ }
58
+ const expectedIssuer = `https://${normalizedDomain}`;
59
+ const { payload } = await jose.jwtVerify(token, jwks, {
60
+ audience: config.policyAUD,
61
+ issuer: expectedIssuer,
62
+ });
63
+ // Extract email from payload
64
+ const email = typeof payload.email === 'string' ? payload.email : undefined;
65
+ // Validate email against allowed list if configured
66
+ if (config.allowedEmails && config.allowedEmails.length > 0) {
67
+ if (!email) {
68
+ return {
69
+ valid: false,
70
+ error: 'JWT missing email claim',
71
+ };
72
+ }
73
+ if (!config.allowedEmails.includes(email)) {
74
+ return {
75
+ valid: false,
76
+ error: `Email ${email} not in allowed list`,
77
+ };
78
+ }
79
+ }
80
+ return {
81
+ valid: true,
82
+ email,
83
+ };
84
+ }
85
+ catch (error) {
86
+ if (error instanceof jose.errors.JWTExpired) {
87
+ return {
88
+ valid: false,
89
+ error: 'JWT has expired',
90
+ };
91
+ }
92
+ if (error instanceof jose.errors.JWTClaimValidationFailed) {
93
+ return {
94
+ valid: false,
95
+ error: `JWT claim validation failed: ${error.message}`,
96
+ };
97
+ }
98
+ if (error instanceof jose.errors.JWSSignatureVerificationFailed) {
99
+ return {
100
+ valid: false,
101
+ error: 'JWT signature verification failed',
102
+ };
103
+ }
104
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
105
+ return {
106
+ valid: false,
107
+ error: `JWT verification failed: ${errorMessage}`,
108
+ };
109
+ }
110
+ }
111
+ /**
112
+ * Express middleware that verifies Cloudflare Access JWTs
113
+ * Returns 401 if no valid JWT is present
114
+ *
115
+ * @param config - Cloudflare Access configuration
116
+ * @returns Express middleware function
117
+ */
118
+ export function createAuthMiddleware(config) {
119
+ return async (req, res, next) => {
120
+ // Get JWT from Cloudflare Access header
121
+ const token = req.headers['cf-access-jwt-assertion'];
122
+ if (!token || typeof token !== 'string') {
123
+ res.status(401).json({
124
+ error: 'Unauthorized',
125
+ message: 'Missing Cf-Access-Jwt-Assertion header',
126
+ });
127
+ return;
128
+ }
129
+ const result = await verifyCloudflareAccessJwt(token, config);
130
+ if (!result.valid) {
131
+ res.status(401).json({
132
+ error: 'Unauthorized',
133
+ message: result.error ?? 'JWT verification failed',
134
+ });
135
+ return;
136
+ }
137
+ // Attach verified email to request for logging
138
+ req.cfAccessEmail = result.email;
139
+ next();
140
+ };
141
+ }
142
+ /**
143
+ * Clears the JWKS cache (useful for testing)
144
+ */
145
+ export function clearJwksCache() {
146
+ jwksCache.clear();
147
+ }
148
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../../src/server/transports/http/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * @fileoverview Health check endpoints for HTTP transport
3
+ * @module server/transports/http/health
4
+ * @description Provides /health and /health/ready endpoints for monitoring
5
+ */
6
+ import type { Request, Response, Router } from 'express';
7
+ import type { FullServerConfig } from '../../../config/index.js';
8
+ /**
9
+ * Marks the server as started (call when HTTP server begins listening)
10
+ */
11
+ export declare function markServerStarted(): void;
12
+ /**
13
+ * Basic health check handler
14
+ * Returns 200 if server is running
15
+ *
16
+ * @param config - Server configuration
17
+ * @returns Express request handler
18
+ */
19
+ export declare function createHealthHandler(config: FullServerConfig): (req: Request, res: Response) => void;
20
+ /**
21
+ * Readiness check handler
22
+ * Returns 200 with subsystem status if server is ready to accept requests
23
+ *
24
+ * @param config - Server configuration
25
+ * @returns Express request handler
26
+ */
27
+ export declare function createReadinessHandler(config: FullServerConfig): (req: Request, res: Response) => void;
28
+ /**
29
+ * Registers health check routes on the provided router
30
+ * These endpoints do NOT require authentication
31
+ *
32
+ * @param router - Express router to register routes on
33
+ * @param config - Server configuration
34
+ */
35
+ export declare function registerHealthRoutes(router: Router, config: FullServerConfig): void;
@@ -0,0 +1,93 @@
1
+ /**
2
+ * @fileoverview Health check endpoints for HTTP transport
3
+ * @module server/transports/http/health
4
+ * @description Provides /health and /health/ready endpoints for monitoring
5
+ */
6
+ /** Server start time for uptime calculation */
7
+ let serverStartTime = null;
8
+ /**
9
+ * Marks the server as started (call when HTTP server begins listening)
10
+ */
11
+ export function markServerStarted() {
12
+ serverStartTime = Date.now();
13
+ }
14
+ /**
15
+ * Gets the server uptime in seconds
16
+ * @returns Uptime in seconds, or 0 if not started
17
+ */
18
+ function getUptime() {
19
+ if (serverStartTime === null) {
20
+ return 0;
21
+ }
22
+ return Math.floor((Date.now() - serverStartTime) / 1000);
23
+ }
24
+ /**
25
+ * Basic health check handler
26
+ * Returns 200 if server is running
27
+ *
28
+ * @param config - Server configuration
29
+ * @returns Express request handler
30
+ */
31
+ export function createHealthHandler(config) {
32
+ return (_req, res) => {
33
+ const response = {
34
+ status: 'healthy',
35
+ service: config.name,
36
+ version: config.version,
37
+ timestamp: new Date().toISOString(),
38
+ uptime: getUptime(),
39
+ };
40
+ res.status(200).json(response);
41
+ };
42
+ }
43
+ /**
44
+ * Readiness check handler
45
+ * Returns 200 with subsystem status if server is ready to accept requests
46
+ *
47
+ * @param config - Server configuration
48
+ * @returns Express request handler
49
+ */
50
+ export function createReadinessHandler(config) {
51
+ return (_req, res) => {
52
+ const subsystems = [
53
+ {
54
+ name: 'mcp-server',
55
+ status: 'healthy',
56
+ message: 'MCP server is accepting connections',
57
+ },
58
+ {
59
+ name: 'http-transport',
60
+ status: 'healthy',
61
+ message: 'HTTP transport is operational',
62
+ },
63
+ ];
64
+ // Determine overall status based on subsystems
65
+ const overallStatus = subsystems.every((s) => s.status === 'healthy')
66
+ ? 'healthy'
67
+ : subsystems.some((s) => s.status === 'unhealthy')
68
+ ? 'unhealthy'
69
+ : 'degraded';
70
+ const response = {
71
+ status: overallStatus,
72
+ service: config.name,
73
+ version: config.version,
74
+ timestamp: new Date().toISOString(),
75
+ uptime: getUptime(),
76
+ subsystems,
77
+ };
78
+ const statusCode = overallStatus === 'healthy' ? 200 : 503;
79
+ res.status(statusCode).json(response);
80
+ };
81
+ }
82
+ /**
83
+ * Registers health check routes on the provided router
84
+ * These endpoints do NOT require authentication
85
+ *
86
+ * @param router - Express router to register routes on
87
+ * @param config - Server configuration
88
+ */
89
+ export function registerHealthRoutes(router, config) {
90
+ router.get('/health', createHealthHandler(config));
91
+ router.get('/health/ready', createReadinessHandler(config));
92
+ }
93
+ //# sourceMappingURL=health.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health.js","sourceRoot":"","sources":["../../../../src/server/transports/http/health.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA0BH,+CAA+C;AAC/C,IAAI,eAAe,GAAkB,IAAI,CAAC;AAE1C;;GAEG;AACH,MAAM,UAAU,iBAAiB;IAC/B,eAAe,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;AAC/B,CAAC;AAED;;;GAGG;AACH,SAAS,SAAS;IAChB,IAAI,eAAe,KAAK,IAAI,EAAE,CAAC;QAC7B,OAAO,CAAC,CAAC;IACX,CAAC;IACD,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,eAAe,CAAC,GAAG,IAAI,CAAC,CAAC;AAC3D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CACjC,MAAwB;IAExB,OAAO,CAAC,IAAa,EAAE,GAAa,EAAQ,EAAE;QAC5C,MAAM,QAAQ,GAAmB;YAC/B,MAAM,EAAE,SAAS;YACjB,OAAO,EAAE,MAAM,CAAC,IAAI;YACpB,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,MAAM,EAAE,SAAS,EAAE;SACpB,CAAC;QAEF,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACjC,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CACpC,MAAwB;IAExB,OAAO,CAAC,IAAa,EAAE,GAAa,EAAQ,EAAE;QAC5C,MAAM,UAAU,GAAsB;YACpC;gBACE,IAAI,EAAE,YAAY;gBAClB,MAAM,EAAE,SAAS;gBACjB,OAAO,EAAE,qCAAqC;aAC/C;YACD;gBACE,IAAI,EAAE,gBAAgB;gBACtB,MAAM,EAAE,SAAS;gBACjB,OAAO,EAAE,+BAA+B;aACzC;SACF,CAAC;QAEF,+CAA+C;QAC/C,MAAM,aAAa,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC;YACnE,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,WAAW,CAAC;gBAChD,CAAC,CAAC,WAAW;gBACb,CAAC,CAAC,UAAU,CAAC;QAEjB,MAAM,QAAQ,GAAmB;YAC/B,MAAM,EAAE,aAAa;YACrB,OAAO,EAAE,MAAM,CAAC,IAAI;YACpB,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,MAAM,EAAE,SAAS,EAAE;YACnB,UAAU;SACX,CAAC;QAEF,MAAM,UAAU,GAAG,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;QAC3D,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACxC,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAClC,MAAc,EACd,MAAwB;IAExB,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,mBAAmB,CAAC,MAAM,CAAC,CAAC,CAAC;IACnD,MAAM,CAAC,GAAG,CAAC,eAAe,EAAE,sBAAsB,CAAC,MAAM,CAAC,CAAC,CAAC;AAC9D,CAAC"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * @fileoverview HTTP transport layer using MCP SDK's StreamableHTTPServerTransport
3
+ * @module server/transports/http
4
+ * @description Express server providing HTTP transport for MCP protocol messages
5
+ */
6
+ import type { Server as McpServer } from '@modelcontextprotocol/sdk/server/index.js';
7
+ import { type Express } from 'express';
8
+ import type { FullServerConfig, HttpConfig } from '../../../config/index.js';
9
+ /**
10
+ * HTTP transport instance
11
+ */
12
+ export interface HttpTransportInstance {
13
+ /** Express application */
14
+ app: Express;
15
+ /** Start listening on configured host:port */
16
+ start: () => Promise<void>;
17
+ /** Stop the HTTP server */
18
+ stop: () => Promise<void>;
19
+ }
20
+ /**
21
+ * Creates an HTTP transport for the MCP server
22
+ *
23
+ * SDK 1.26.0 requires stateless transports to be created fresh per request.
24
+ * Each POST creates a new MCP Server + StreamableHTTPServerTransport pair,
25
+ * handles the request, then cleans up on response close.
26
+ *
27
+ * @param _mcpServer - Unused (kept for API compatibility). Per-request servers are created internally.
28
+ * @param config - Full server configuration
29
+ * @param httpConfig - HTTP-specific configuration
30
+ * @returns HTTP transport instance
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * const config = loadConfig();
35
+ * const mcpServer = createServer(config);
36
+ * const httpTransport = createHttpTransport(mcpServer, config, config.http!);
37
+ * await httpTransport.start();
38
+ * ```
39
+ */
40
+ export declare function createHttpTransport(_mcpServer: McpServer, config: FullServerConfig, httpConfig: HttpConfig): HttpTransportInstance;
41
+ export { createAuthMiddleware, verifyCloudflareAccessJwt } from './auth.js';
42
+ export { createHealthHandler, createReadinessHandler, registerHealthRoutes, } from './health.js';
43
+ export { corsMiddleware, createRateLimiter, errorHandler, requestLogging, requestTiming, } from './middleware.js';
@@ -0,0 +1,141 @@
1
+ /**
2
+ * @fileoverview HTTP transport layer using MCP SDK's StreamableHTTPServerTransport
3
+ * @module server/transports/http
4
+ * @description Express server providing HTTP transport for MCP protocol messages
5
+ */
6
+ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
7
+ import express, { Router, } from 'express';
8
+ import { createServer } from '../../server.js';
9
+ import { createAuthMiddleware } from './auth.js';
10
+ import { markServerStarted, registerHealthRoutes } from './health.js';
11
+ import { corsMiddleware, createRateLimiter, errorHandler, requestLogging, requestTiming, } from './middleware.js';
12
+ /**
13
+ * Creates an HTTP transport for the MCP server
14
+ *
15
+ * SDK 1.26.0 requires stateless transports to be created fresh per request.
16
+ * Each POST creates a new MCP Server + StreamableHTTPServerTransport pair,
17
+ * handles the request, then cleans up on response close.
18
+ *
19
+ * @param _mcpServer - Unused (kept for API compatibility). Per-request servers are created internally.
20
+ * @param config - Full server configuration
21
+ * @param httpConfig - HTTP-specific configuration
22
+ * @returns HTTP transport instance
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * const config = loadConfig();
27
+ * const mcpServer = createServer(config);
28
+ * const httpTransport = createHttpTransport(mcpServer, config, config.http!);
29
+ * await httpTransport.start();
30
+ * ```
31
+ */
32
+ export function createHttpTransport(_mcpServer, config, httpConfig) {
33
+ const app = express();
34
+ // Trust proxy for rate limiting behind Cloudflare Tunnel
35
+ app.set('trust proxy', true);
36
+ // Apply global middleware
37
+ // express.json() is required to pre-parse the body for the SDK's parsedBody parameter.
38
+ // SDK 1.26.0 explicitly supports this pattern: transport.handleRequest(req, res, req.body)
39
+ app.use(express.json());
40
+ app.use(corsMiddleware());
41
+ app.use(requestTiming());
42
+ app.use(requestLogging());
43
+ app.use(createRateLimiter());
44
+ // Register health endpoints (no auth required)
45
+ const healthRouter = Router();
46
+ registerHealthRoutes(healthRouter, config);
47
+ app.use(healthRouter);
48
+ // Apply auth middleware to MCP endpoint if Cloudflare Access is configured
49
+ if (httpConfig.cloudflareAccess) {
50
+ app.use('/mcp', createAuthMiddleware(httpConfig.cloudflareAccess));
51
+ }
52
+ // MCP endpoint handler — creates a fresh server + transport per request
53
+ // SDK 1.26.0 stateless mode throws "Stateless transport cannot be reused across requests"
54
+ // if a transport with sessionIdGenerator=undefined handles more than one request.
55
+ // The official SDK pattern (simpleStatelessStreamableHttp.ts) creates new instances per request.
56
+ const mcpHandler = async (req, res) => {
57
+ const perRequestServer = createServer(config);
58
+ const transport = new StreamableHTTPServerTransport({
59
+ sessionIdGenerator: undefined,
60
+ enableJsonResponse: true,
61
+ });
62
+ // Log transport-level errors to stderr for visibility
63
+ transport.onerror = (error) => {
64
+ process.stderr.write(`${JSON.stringify({ timestamp: new Date().toISOString(), error: 'MCP transport error', message: error.message, stack: error.stack })}\n`);
65
+ };
66
+ try {
67
+ await perRequestServer.connect(transport);
68
+ await transport.handleRequest(req, res, req.body);
69
+ // Clean up when the response closes
70
+ res.on('close', () => {
71
+ transport.close().catch(() => { });
72
+ perRequestServer.close().catch(() => { });
73
+ });
74
+ }
75
+ catch (error) {
76
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
77
+ process.stderr.write(`${JSON.stringify({ timestamp: new Date().toISOString(), error: 'MCP request failed', message: errorMessage })}\n`);
78
+ if (!res.headersSent) {
79
+ res.status(500).json({
80
+ error: 'Internal Server Error',
81
+ message: 'MCP request processing failed',
82
+ });
83
+ }
84
+ }
85
+ };
86
+ // Register MCP routes - handle all methods for session management
87
+ // Support both /mcp (explicit) and root / (Claude.ai expects this)
88
+ app.all('/mcp', mcpHandler);
89
+ app.all('/', mcpHandler);
90
+ // Error handler must be last
91
+ app.use(errorHandler());
92
+ // HTTP server reference for cleanup
93
+ let server = null;
94
+ return {
95
+ app,
96
+ async start() {
97
+ return new Promise((resolve, reject) => {
98
+ try {
99
+ server = app.listen(httpConfig.port, httpConfig.host, () => {
100
+ markServerStarted();
101
+ process.stderr.write(`${JSON.stringify({
102
+ timestamp: new Date().toISOString(),
103
+ event: 'http_server_started',
104
+ host: httpConfig.host,
105
+ port: httpConfig.port,
106
+ authEnabled: !!httpConfig.cloudflareAccess,
107
+ })}\n`);
108
+ resolve();
109
+ });
110
+ server.on('error', (error) => {
111
+ process.stderr.write(`${JSON.stringify({ timestamp: new Date().toISOString(), error: 'HTTP server error', message: error.message })}\n`);
112
+ reject(error);
113
+ });
114
+ }
115
+ catch (error) {
116
+ reject(error);
117
+ }
118
+ });
119
+ },
120
+ async stop() {
121
+ if (server) {
122
+ return new Promise((resolve, reject) => {
123
+ server?.close((error) => {
124
+ if (error) {
125
+ reject(error);
126
+ }
127
+ else {
128
+ process.stderr.write(`${JSON.stringify({ timestamp: new Date().toISOString(), event: 'http_server_stopped' })}\n`);
129
+ resolve();
130
+ }
131
+ });
132
+ });
133
+ }
134
+ },
135
+ };
136
+ }
137
+ // Re-export components for testing and extensibility
138
+ export { createAuthMiddleware, verifyCloudflareAccessJwt } from './auth.js';
139
+ export { createHealthHandler, createReadinessHandler, registerHealthRoutes, } from './health.js';
140
+ export { corsMiddleware, createRateLimiter, errorHandler, requestLogging, requestTiming, } from './middleware.js';
141
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/server/transports/http/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * @fileoverview HTTP middleware for rate limiting, logging, and CORS
3
+ * @module server/transports/http/middleware
4
+ * @description Express middleware stack for the HTTP transport
5
+ */
6
+ import type { NextFunction, Request, Response } from 'express';
7
+ import rateLimit from 'express-rate-limit';
8
+ /**
9
+ * Extended request type with timing and auth info
10
+ */
11
+ export interface TimedRequest extends Request {
12
+ /** Request start time for duration calculation */
13
+ startTime?: number;
14
+ /** Verified Cloudflare Access email */
15
+ cfAccessEmail?: string;
16
+ }
17
+ /**
18
+ * Creates a rate limiter middleware
19
+ * Default: 100 requests per minute per IP
20
+ *
21
+ * @param maxRequests - Maximum requests per window (default: 100)
22
+ * @param windowMs - Window size in milliseconds (default: 60000 = 1 minute)
23
+ * @returns Express rate limiting middleware
24
+ */
25
+ export declare function createRateLimiter(maxRequests?: number, windowMs?: number): ReturnType<typeof rateLimit>;
26
+ /**
27
+ * Request timing middleware
28
+ * Adds startTime to request for duration calculation
29
+ *
30
+ * @returns Express middleware function
31
+ */
32
+ export declare function requestTiming(): (req: TimedRequest, res: Response, next: NextFunction) => void;
33
+ /**
34
+ * Request logging middleware
35
+ * Logs request details after response is sent
36
+ *
37
+ * @returns Express middleware function
38
+ */
39
+ export declare function requestLogging(): (req: TimedRequest, res: Response, next: NextFunction) => void;
40
+ /**
41
+ * CORS middleware for handling preflight requests
42
+ * Configured for Cloudflare Tunnel / Access
43
+ *
44
+ * @returns Express middleware function
45
+ */
46
+ export declare function corsMiddleware(): (req: Request, res: Response, next: NextFunction) => void;
47
+ /**
48
+ * Error handling middleware
49
+ * Catches unhandled errors and returns appropriate response
50
+ *
51
+ * @returns Express error handling middleware
52
+ */
53
+ export declare function errorHandler(): (err: Error, req: Request, res: Response, next: NextFunction) => void;
@@ -0,0 +1,133 @@
1
+ /**
2
+ * @fileoverview HTTP middleware for rate limiting, logging, and CORS
3
+ * @module server/transports/http/middleware
4
+ * @description Express middleware stack for the HTTP transport
5
+ */
6
+ import rateLimit from 'express-rate-limit';
7
+ /**
8
+ * Creates a rate limiter middleware
9
+ * Default: 100 requests per minute per IP
10
+ *
11
+ * @param maxRequests - Maximum requests per window (default: 100)
12
+ * @param windowMs - Window size in milliseconds (default: 60000 = 1 minute)
13
+ * @returns Express rate limiting middleware
14
+ */
15
+ export function createRateLimiter(maxRequests = 100, windowMs = 60000) {
16
+ return rateLimit({
17
+ windowMs,
18
+ max: maxRequests,
19
+ // Suppress ERR_ERL_KEY_GEN_IPV6 ValidationError — we're behind Cloudflare
20
+ // Tunnel which always provides X-Forwarded-For, so IPv6 validation is irrelevant
21
+ validate: { keyGeneratorIpFallback: false },
22
+ message: {
23
+ error: 'Too Many Requests',
24
+ message: `Rate limit exceeded. Maximum ${maxRequests} requests per ${windowMs / 1000} seconds.`,
25
+ },
26
+ standardHeaders: true,
27
+ legacyHeaders: false,
28
+ // Use X-Forwarded-For header if behind proxy (Cloudflare Tunnel)
29
+ keyGenerator: (req) => {
30
+ const forwarded = req.headers['x-forwarded-for'];
31
+ if (typeof forwarded === 'string') {
32
+ // Take the first IP if there are multiple
33
+ return forwarded.split(',')[0].trim();
34
+ }
35
+ return req.ip ?? req.socket.remoteAddress ?? 'unknown';
36
+ },
37
+ // Skip rate limiting for health checks
38
+ skip: (req) => {
39
+ return req.path === '/health' || req.path === '/health/ready';
40
+ },
41
+ });
42
+ }
43
+ /**
44
+ * Request timing middleware
45
+ * Adds startTime to request for duration calculation
46
+ *
47
+ * @returns Express middleware function
48
+ */
49
+ export function requestTiming() {
50
+ return (req, _res, next) => {
51
+ req.startTime = Date.now();
52
+ next();
53
+ };
54
+ }
55
+ /**
56
+ * Request logging middleware
57
+ * Logs request details after response is sent
58
+ *
59
+ * @returns Express middleware function
60
+ */
61
+ export function requestLogging() {
62
+ return (req, res, next) => {
63
+ // Log after response is finished
64
+ res.on('finish', () => {
65
+ const duration = req.startTime ? Date.now() - req.startTime : 0;
66
+ const logEntry = {
67
+ timestamp: new Date().toISOString(),
68
+ method: req.method,
69
+ path: req.path,
70
+ status: res.statusCode,
71
+ duration,
72
+ ip: (typeof req.headers['x-forwarded-for'] === 'string'
73
+ ? req.headers['x-forwarded-for'].split(',')[0].trim()
74
+ : undefined) ??
75
+ req.ip ??
76
+ req.socket.remoteAddress ??
77
+ 'unknown',
78
+ };
79
+ // Add user if available from Cloudflare Access
80
+ if (req.cfAccessEmail) {
81
+ logEntry.user = req.cfAccessEmail;
82
+ }
83
+ // Log to stderr to avoid interfering with stdio transport
84
+ // Use structured JSON for easy parsing
85
+ process.stderr.write(`${JSON.stringify(logEntry)}\n`);
86
+ });
87
+ next();
88
+ };
89
+ }
90
+ /**
91
+ * CORS middleware for handling preflight requests
92
+ * Configured for Cloudflare Tunnel / Access
93
+ *
94
+ * @returns Express middleware function
95
+ */
96
+ export function corsMiddleware() {
97
+ return (req, res, next) => {
98
+ // Allow requests from same origin or Cloudflare
99
+ res.setHeader('Access-Control-Allow-Origin', '*');
100
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS');
101
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Cf-Access-Jwt-Assertion, Mcp-Session-Id, Last-Event-Id');
102
+ res.setHeader('Access-Control-Expose-Headers', 'Mcp-Session-Id');
103
+ res.setHeader('Access-Control-Max-Age', '86400');
104
+ // Handle preflight requests
105
+ if (req.method === 'OPTIONS') {
106
+ res.status(204).end();
107
+ return;
108
+ }
109
+ next();
110
+ };
111
+ }
112
+ /**
113
+ * Error handling middleware
114
+ * Catches unhandled errors and returns appropriate response
115
+ *
116
+ * @returns Express error handling middleware
117
+ */
118
+ export function errorHandler() {
119
+ return (err, _req, res, _next) => {
120
+ // Log error to stderr
121
+ process.stderr.write(`${JSON.stringify({ timestamp: new Date().toISOString(), error: err.message, stack: err.stack })}\n`);
122
+ // Send error response if not already sent
123
+ if (!res.headersSent) {
124
+ res.status(500).json({
125
+ error: 'Internal Server Error',
126
+ message: process.env.NODE_ENV === 'production'
127
+ ? 'An unexpected error occurred'
128
+ : err.message,
129
+ });
130
+ }
131
+ };
132
+ }
133
+ //# sourceMappingURL=middleware.js.map