toolcraft 0.0.119 → 0.0.121

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 (153) hide show
  1. package/composition.json +17 -2
  2. package/dist/composition.json +17 -2
  3. package/dist/http.compile-check.d.ts +1 -0
  4. package/dist/http.compile-check.js +31 -0
  5. package/dist/http.d.ts +19 -0
  6. package/dist/http.js +62 -0
  7. package/dist/mcp.js +26 -11
  8. package/node_modules/jose/LICENSE.md +21 -0
  9. package/node_modules/jose/README.md +153 -0
  10. package/node_modules/jose/dist/types/index.d.ts +55 -0
  11. package/node_modules/jose/dist/types/jwe/compact/decrypt.d.ts +31 -0
  12. package/node_modules/jose/dist/types/jwe/compact/encrypt.d.ts +65 -0
  13. package/node_modules/jose/dist/types/jwe/flattened/decrypt.d.ts +31 -0
  14. package/node_modules/jose/dist/types/jwe/flattened/encrypt.d.ts +83 -0
  15. package/node_modules/jose/dist/types/jwe/general/decrypt.d.ts +38 -0
  16. package/node_modules/jose/dist/types/jwe/general/encrypt.d.ts +74 -0
  17. package/node_modules/jose/dist/types/jwk/embedded.d.ts +17 -0
  18. package/node_modules/jose/dist/types/jwk/thumbprint.d.ts +32 -0
  19. package/node_modules/jose/dist/types/jwks/local.d.ts +29 -0
  20. package/node_modules/jose/dist/types/jwks/remote.d.ts +237 -0
  21. package/node_modules/jose/dist/types/jws/compact/sign.d.ts +36 -0
  22. package/node_modules/jose/dist/types/jws/compact/verify.d.ts +33 -0
  23. package/node_modules/jose/dist/types/jws/flattened/sign.d.ts +42 -0
  24. package/node_modules/jose/dist/types/jws/flattened/verify.d.ts +33 -0
  25. package/node_modules/jose/dist/types/jws/general/sign.d.ts +53 -0
  26. package/node_modules/jose/dist/types/jws/general/verify.d.ts +41 -0
  27. package/node_modules/jose/dist/types/jwt/decrypt.d.ts +35 -0
  28. package/node_modules/jose/dist/types/jwt/encrypt.d.ts +91 -0
  29. package/node_modules/jose/dist/types/jwt/sign.d.ts +43 -0
  30. package/node_modules/jose/dist/types/jwt/unsecured.d.ts +43 -0
  31. package/node_modules/jose/dist/types/jwt/verify.d.ts +37 -0
  32. package/node_modules/jose/dist/types/key/export.d.ts +33 -0
  33. package/node_modules/jose/dist/types/key/generate_key_pair.d.ts +47 -0
  34. package/node_modules/jose/dist/types/key/generate_secret.d.ts +35 -0
  35. package/node_modules/jose/dist/types/key/import.d.ts +83 -0
  36. package/node_modules/jose/dist/types/types.d.ts +852 -0
  37. package/node_modules/jose/dist/types/util/base64url.d.ts +9 -0
  38. package/node_modules/jose/dist/types/util/decode_jwt.d.ts +18 -0
  39. package/node_modules/jose/dist/types/util/decode_protected_header.d.ts +17 -0
  40. package/node_modules/jose/dist/types/util/errors.d.ts +213 -0
  41. package/node_modules/jose/dist/webapi/index.js +32 -0
  42. package/node_modules/jose/dist/webapi/jwe/compact/decrypt.js +27 -0
  43. package/node_modules/jose/dist/webapi/jwe/compact/encrypt.js +27 -0
  44. package/node_modules/jose/dist/webapi/jwe/flattened/decrypt.js +159 -0
  45. package/node_modules/jose/dist/webapi/jwe/flattened/encrypt.js +167 -0
  46. package/node_modules/jose/dist/webapi/jwe/general/decrypt.js +31 -0
  47. package/node_modules/jose/dist/webapi/jwe/general/encrypt.js +182 -0
  48. package/node_modules/jose/dist/webapi/jwk/embedded.js +17 -0
  49. package/node_modules/jose/dist/webapi/jwk/thumbprint.js +68 -0
  50. package/node_modules/jose/dist/webapi/jwks/local.js +119 -0
  51. package/node_modules/jose/dist/webapi/jwks/remote.js +179 -0
  52. package/node_modules/jose/dist/webapi/jws/compact/sign.js +18 -0
  53. package/node_modules/jose/dist/webapi/jws/compact/verify.js +21 -0
  54. package/node_modules/jose/dist/webapi/jws/flattened/sign.js +89 -0
  55. package/node_modules/jose/dist/webapi/jws/flattened/verify.js +110 -0
  56. package/node_modules/jose/dist/webapi/jws/general/sign.js +70 -0
  57. package/node_modules/jose/dist/webapi/jws/general/verify.js +24 -0
  58. package/node_modules/jose/dist/webapi/jwt/decrypt.js +23 -0
  59. package/node_modules/jose/dist/webapi/jwt/encrypt.js +101 -0
  60. package/node_modules/jose/dist/webapi/jwt/sign.js +52 -0
  61. package/node_modules/jose/dist/webapi/jwt/unsecured.js +63 -0
  62. package/node_modules/jose/dist/webapi/jwt/verify.js +15 -0
  63. package/node_modules/jose/dist/webapi/key/export.js +11 -0
  64. package/node_modules/jose/dist/webapi/key/generate_key_pair.js +97 -0
  65. package/node_modules/jose/dist/webapi/key/generate_secret.js +40 -0
  66. package/node_modules/jose/dist/webapi/key/import.js +57 -0
  67. package/node_modules/jose/dist/webapi/lib/aesgcmkw.js +15 -0
  68. package/node_modules/jose/dist/webapi/lib/aeskw.js +25 -0
  69. package/node_modules/jose/dist/webapi/lib/asn1.js +243 -0
  70. package/node_modules/jose/dist/webapi/lib/base64.js +22 -0
  71. package/node_modules/jose/dist/webapi/lib/buffer_utils.js +43 -0
  72. package/node_modules/jose/dist/webapi/lib/check_key_type.js +122 -0
  73. package/node_modules/jose/dist/webapi/lib/content_encryption.js +217 -0
  74. package/node_modules/jose/dist/webapi/lib/crypto_key.js +136 -0
  75. package/node_modules/jose/dist/webapi/lib/deflate.js +44 -0
  76. package/node_modules/jose/dist/webapi/lib/ecdhes.js +52 -0
  77. package/node_modules/jose/dist/webapi/lib/helpers.js +19 -0
  78. package/node_modules/jose/dist/webapi/lib/invalid_key_input.js +27 -0
  79. package/node_modules/jose/dist/webapi/lib/is_key_like.js +17 -0
  80. package/node_modules/jose/dist/webapi/lib/jwk_to_key.js +107 -0
  81. package/node_modules/jose/dist/webapi/lib/jwt_claims_set.js +238 -0
  82. package/node_modules/jose/dist/webapi/lib/key_management.js +186 -0
  83. package/node_modules/jose/dist/webapi/lib/key_to_jwk.js +31 -0
  84. package/node_modules/jose/dist/webapi/lib/normalize_key.js +166 -0
  85. package/node_modules/jose/dist/webapi/lib/pbes2kw.js +42 -0
  86. package/node_modules/jose/dist/webapi/lib/rsaes.js +24 -0
  87. package/node_modules/jose/dist/webapi/lib/signing.js +68 -0
  88. package/node_modules/jose/dist/webapi/lib/type_checks.js +40 -0
  89. package/node_modules/jose/dist/webapi/lib/validate_algorithms.js +10 -0
  90. package/node_modules/jose/dist/webapi/lib/validate_crit.js +33 -0
  91. package/node_modules/jose/dist/webapi/util/base64url.js +30 -0
  92. package/node_modules/jose/dist/webapi/util/decode_jwt.js +32 -0
  93. package/node_modules/jose/dist/webapi/util/decode_protected_header.js +34 -0
  94. package/node_modules/jose/dist/webapi/util/errors.js +99 -0
  95. package/node_modules/jose/package.json +200 -0
  96. package/node_modules/tiny-http-mcp-server/LICENSE +21 -0
  97. package/node_modules/tiny-http-mcp-server/README.md +778 -0
  98. package/node_modules/tiny-http-mcp-server/dist/auth.d.ts +69 -0
  99. package/node_modules/tiny-http-mcp-server/dist/auth.js +261 -0
  100. package/node_modules/tiny-http-mcp-server/dist/cli.d.ts +20 -0
  101. package/node_modules/tiny-http-mcp-server/dist/cli.js +465 -0
  102. package/node_modules/tiny-http-mcp-server/dist/composition.json +30 -0
  103. package/node_modules/tiny-http-mcp-server/dist/express-middleware.d.ts +18 -0
  104. package/node_modules/tiny-http-mcp-server/dist/express-middleware.js +90 -0
  105. package/node_modules/tiny-http-mcp-server/dist/http-server.d.ts +49 -0
  106. package/node_modules/tiny-http-mcp-server/dist/http-server.js +264 -0
  107. package/node_modules/tiny-http-mcp-server/dist/http-transport.d.ts +164 -0
  108. package/node_modules/tiny-http-mcp-server/dist/http-transport.js +897 -0
  109. package/node_modules/tiny-http-mcp-server/dist/index.d.ts +13 -0
  110. package/node_modules/tiny-http-mcp-server/dist/index.js +6 -0
  111. package/node_modules/tiny-http-mcp-server/dist/load-oauth-verifier.d.ts +6 -0
  112. package/node_modules/tiny-http-mcp-server/dist/load-oauth-verifier.js +43 -0
  113. package/node_modules/tiny-http-mcp-server/dist/parse-body.d.ts +22 -0
  114. package/node_modules/tiny-http-mcp-server/dist/parse-body.js +150 -0
  115. package/node_modules/tiny-http-mcp-server/dist/server.d.ts +9 -0
  116. package/node_modules/tiny-http-mcp-server/dist/server.js +4 -0
  117. package/node_modules/tiny-http-mcp-server/dist/session.d.ts +18 -0
  118. package/node_modules/tiny-http-mcp-server/dist/session.js +37 -0
  119. package/node_modules/tiny-http-mcp-server/dist/sse.d.ts +11 -0
  120. package/node_modules/tiny-http-mcp-server/dist/sse.js +22 -0
  121. package/node_modules/tiny-http-mcp-server/dist/test-support.d.ts +10 -0
  122. package/node_modules/tiny-http-mcp-server/dist/test-support.js +398 -0
  123. package/node_modules/tiny-http-mcp-server/dist/testing.d.ts +59 -0
  124. package/node_modules/tiny-http-mcp-server/dist/testing.js +191 -0
  125. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/LICENSE +21 -0
  126. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/README.md +70 -0
  127. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/auth-store-session-store.d.ts +14 -0
  128. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/auth-store-session-store.js +169 -0
  129. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/authorization-state.d.ts +8 -0
  130. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/authorization-state.js +47 -0
  131. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/default-oauth-client-provider.d.ts +3 -0
  132. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/default-oauth-client-provider.js +627 -0
  133. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/loopback-authorization.d.ts +20 -0
  134. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/loopback-authorization.js +207 -0
  135. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/pkce.d.ts +2 -0
  136. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/pkce.js +7 -0
  137. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/token-endpoint.d.ts +40 -0
  138. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/token-endpoint.js +164 -0
  139. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/types.d.ts +113 -0
  140. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/types.js +1 -0
  141. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/index.d.ts +10 -0
  142. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/index.js +7 -0
  143. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/resource-indicator.d.ts +1 -0
  144. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/resource-indicator.js +11 -0
  145. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/server/jwks-token-verifier.d.ts +32 -0
  146. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/server/jwks-token-verifier.js +388 -0
  147. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/types.compile-check.d.ts +1 -0
  148. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/types.compile-check.js +22 -0
  149. package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/package.json +28 -0
  150. package/node_modules/tiny-http-mcp-server/package.json +59 -0
  151. package/node_modules/tiny-stdio-mcp-server/dist/composition.json +1 -1
  152. package/node_modules/toolcraft-schema/package.json +1 -1
  153. package/package.json +11 -4
@@ -0,0 +1,897 @@
1
+ import { validateHeaderValue } from "node:http";
2
+ import { JSON_RPC_ERROR_CODES } from "tiny-stdio-mcp-server";
3
+ import { formatErrorResponse, formatSuccessResponse } from "tiny-stdio-mcp-server/jsonrpc";
4
+ import { JsonRpcMessageError, readAndClassifyBody } from "./parse-body.js";
5
+ import { createSessionStore, defaultSessionIdGenerator } from "./session.js";
6
+ import { formatSseEvent, SSE_HEADERS } from "./sse.js";
7
+ const ALLOWED_METHODS = "POST, GET, DELETE, OPTIONS";
8
+ const MCP_SESSION_ID_HEADER = "Mcp-Session-Id";
9
+ const LOCAL_HOSTS = ["localhost", "127.0.0.1", "::1"];
10
+ const DEFAULT_ALLOWED_HEADERS = "Accept, Authorization, Content-Type, Mcp-Session-Id, MCP-Protocol-Version";
11
+ function validateOptionalIntegerOption(name, value, minimum) {
12
+ if (value === undefined) {
13
+ return undefined;
14
+ }
15
+ if (!Number.isInteger(value) || value < minimum) {
16
+ throw new Error(`${name} must be an integer greater than or equal to ${minimum}.`);
17
+ }
18
+ return value;
19
+ }
20
+ export class StreamableHttpTransport {
21
+ server;
22
+ runWithRequestContext;
23
+ sessionIdGenerator;
24
+ enableJsonResponse;
25
+ allowedOrigins;
26
+ allowedHosts;
27
+ maxRequestBytes;
28
+ maxBatchSize;
29
+ maxSessions;
30
+ sessionTtlMs;
31
+ maxStreamsPerSession;
32
+ maxStreamBufferBytes;
33
+ maxSseEventHistory;
34
+ sseKeepAliveMs;
35
+ maxConcurrentToolCalls;
36
+ sessionStore;
37
+ requestIdGenerator;
38
+ observability;
39
+ trustedProxy;
40
+ sessionMessages = new Map();
41
+ sseStreams = new Map();
42
+ sseExpiryTimers = new Map();
43
+ sseEventHistory = new Map();
44
+ responseRequestIds = new WeakMap();
45
+ responseOrigins = new WeakMap();
46
+ responseRejectionReasons = new WeakMap();
47
+ nextNotificationEventId = 1;
48
+ nextRequestId = 1;
49
+ activeToolCalls = 0;
50
+ closed = false;
51
+ sessionExpiryInterval;
52
+ sseKeepAliveInterval;
53
+ constructor(server, options = {}, runWithRequestContext = async (_req, callback) => callback()) {
54
+ this.server = server;
55
+ this.runWithRequestContext = runWithRequestContext;
56
+ this.sessionIdGenerator = hasOwnProperty(options, "sessionIdGenerator")
57
+ ? options.sessionIdGenerator
58
+ : defaultSessionIdGenerator;
59
+ this.enableJsonResponse = options.enableJsonResponse ?? false;
60
+ this.allowedOrigins = new Set(options.allowedOrigins ?? []);
61
+ this.allowedHosts = new Set((options.allowedHosts ?? LOCAL_HOSTS).map((host) => this.normalizeHost(host)));
62
+ this.maxRequestBytes = validateOptionalIntegerOption("maxRequestBytes", options.maxRequestBytes, 1);
63
+ this.maxBatchSize = validateOptionalIntegerOption("maxBatchSize", options.maxBatchSize, 1);
64
+ this.maxSessions = validateOptionalIntegerOption("maxSessions", options.maxSessions, 1);
65
+ this.sessionTtlMs = validateOptionalIntegerOption("sessionTtlMs", options.sessionTtlMs, 1);
66
+ this.maxStreamsPerSession =
67
+ validateOptionalIntegerOption("maxStreamsPerSession", options.maxStreamsPerSession, 1) ?? 1;
68
+ this.maxStreamBufferBytes =
69
+ validateOptionalIntegerOption("maxStreamBufferBytes", options.maxStreamBufferBytes, 0) ??
70
+ 1024 * 1024;
71
+ this.maxSseEventHistory =
72
+ validateOptionalIntegerOption("maxSseEventHistory", options.maxSseEventHistory, 0) ?? 100;
73
+ this.sseKeepAliveMs =
74
+ validateOptionalIntegerOption("sseKeepAliveMs", options.sseKeepAliveMs, 0) ?? 30_000;
75
+ this.maxConcurrentToolCalls = validateOptionalIntegerOption("maxConcurrentToolCalls", options.maxConcurrentToolCalls, 1);
76
+ this.sessionStore = options.sessionStore ?? createSessionStore();
77
+ this.requestIdGenerator = options.requestIdGenerator ?? (() => `req-${this.nextRequestId++}`);
78
+ this.observability = options.observability ?? {};
79
+ this.trustedProxy = options.trustedProxy ?? false;
80
+ if (this.sessionTtlMs !== undefined) {
81
+ this.sessionExpiryInterval = setInterval(() => {
82
+ try {
83
+ this.purgeExpiredSessions();
84
+ }
85
+ catch {
86
+ return;
87
+ }
88
+ }, Math.min(this.sessionTtlMs, 60_000));
89
+ this.sessionExpiryInterval.unref();
90
+ }
91
+ }
92
+ async handleRequest(req, res) {
93
+ const startedAt = Date.now();
94
+ const requestId = this.readRequestId(req) ?? this.requestIdGenerator();
95
+ this.responseRequestIds.set(res, requestId);
96
+ const origin = this.readOrigin(req);
97
+ if (origin !== undefined && this.acceptsOriginValue(req, origin)) {
98
+ this.responseOrigins.set(res, origin);
99
+ }
100
+ this.emit({
101
+ type: "request.start",
102
+ requestId,
103
+ method: req.method ?? "",
104
+ path: req.url ?? "",
105
+ sessionId: this.readSessionId(req)
106
+ });
107
+ try {
108
+ if (this.closed) {
109
+ this.respondWithRejection(res, 503, "transport_closed", "The transport is closed; create or use an active transport.");
110
+ return;
111
+ }
112
+ if (!this.acceptsHost(req)) {
113
+ this.respondWithRejection(res, 403, "host_not_allowed", `Host ${JSON.stringify(this.readRequestHost(req))} is not allowed; add it to allowedHosts.`);
114
+ return;
115
+ }
116
+ if (!this.acceptsOrigin(req)) {
117
+ this.respondWithRejection(res, 403, "origin_not_allowed", `Origin ${JSON.stringify(this.readOrigin(req) ?? "")} is not allowed; add it to allowedOrigins.`);
118
+ return;
119
+ }
120
+ switch (req.method) {
121
+ case "POST":
122
+ await this.handlePost(req, res);
123
+ return;
124
+ case "GET":
125
+ await this.handleGet(req, res);
126
+ return;
127
+ case "DELETE":
128
+ this.handleDelete(req, res);
129
+ return;
130
+ case "OPTIONS":
131
+ this.handleOptions(req, res);
132
+ return;
133
+ default:
134
+ this.respondWithStatus(res, 405, undefined, {
135
+ Allow: ALLOWED_METHODS
136
+ });
137
+ }
138
+ }
139
+ catch (error) {
140
+ this.emit({
141
+ type: "request.error",
142
+ requestId,
143
+ method: req.method ?? "",
144
+ durationMs: Date.now() - startedAt,
145
+ error,
146
+ sessionId: this.readSessionId(req)
147
+ });
148
+ if (!res.headersSent) {
149
+ this.respondWithStatus(res, 500);
150
+ }
151
+ else if (!res.writableEnded) {
152
+ res.end();
153
+ }
154
+ return;
155
+ }
156
+ finally {
157
+ this.emit({
158
+ type: "request.end",
159
+ requestId,
160
+ method: req.method ?? "",
161
+ statusCode: res.statusCode,
162
+ durationMs: Date.now() - startedAt,
163
+ sessionId: this.readSessionId(req),
164
+ ...(res.statusCode < 200 || res.statusCode >= 300
165
+ ? { reason: this.responseRejectionReasons.get(res) ?? "http_error" }
166
+ : {})
167
+ });
168
+ }
169
+ }
170
+ async close() {
171
+ this.closed = true;
172
+ if (this.sessionExpiryInterval !== undefined) {
173
+ clearInterval(this.sessionExpiryInterval);
174
+ this.sessionExpiryInterval = undefined;
175
+ }
176
+ this.stopSseKeepAlive();
177
+ for (const timer of this.sseExpiryTimers.values()) {
178
+ clearTimeout(timer);
179
+ }
180
+ this.sseExpiryTimers.clear();
181
+ for (const sessionId of [...this.sseStreams.keys()]) {
182
+ this.closeStreamsForSession(sessionId);
183
+ }
184
+ for (const sessionId of [...this.sessionMessages.keys()]) {
185
+ this.deleteSession(sessionId, "closed");
186
+ }
187
+ this.sessionMessages.clear();
188
+ }
189
+ async handlePost(req, res) {
190
+ if (!this.isJsonRequest(req)) {
191
+ this.respondWithJsonRpcError(res, 415, JSON_RPC_ERROR_CODES.INVALID_REQUEST, "Invalid Request");
192
+ return;
193
+ }
194
+ if (!this.acceptsConfiguredResponse(req)) {
195
+ const expectedType = this.enableJsonResponse ? "application/json" : "text/event-stream";
196
+ this.respondWithRejection(res, 406, "response_type_not_acceptable", `Accept must allow ${expectedType}.`);
197
+ return;
198
+ }
199
+ let classified;
200
+ try {
201
+ classified = await readAndClassifyBody(req, undefined, {
202
+ maxBytes: this.maxRequestBytes,
203
+ maxBatchSize: this.maxBatchSize
204
+ });
205
+ }
206
+ catch (error) {
207
+ const message = error instanceof Error ? error.message : "Invalid Request";
208
+ if (message === "Payload too large") {
209
+ this.respondWithJsonRpcError(res, 413, JSON_RPC_ERROR_CODES.INVALID_REQUEST, message);
210
+ return;
211
+ }
212
+ const code = error instanceof JsonRpcMessageError
213
+ ? error.code
214
+ : message === "Parse error"
215
+ ? JSON_RPC_ERROR_CODES.PARSE_ERROR
216
+ : JSON_RPC_ERROR_CODES.INVALID_REQUEST;
217
+ const id = error instanceof JsonRpcMessageError ? error.id : null;
218
+ this.respondWithJsonRpcError(res, 400, code, message, id);
219
+ return;
220
+ }
221
+ const initMessage = classified.messages.find((message) => this.isRequest(message) && message.method === "initialize");
222
+ let sessionId;
223
+ let activeSession;
224
+ if (this.sessionIdGenerator !== undefined) {
225
+ const headerSessionId = this.readSessionId(req);
226
+ if (headerSessionId === undefined) {
227
+ if (initMessage === undefined) {
228
+ this.respondWithRejection(res, 400, "session_id_required", "Mcp-Session-Id is required; initialize a session first.");
229
+ return;
230
+ }
231
+ if (this.maxSessions !== undefined && this.sessionCount() >= this.maxSessions) {
232
+ this.respondWithRejection(res, 503, "session_limit_reached", "The server has reached its session limit; close a session or retry later.");
233
+ return;
234
+ }
235
+ const newSessionId = this.sessionIdGenerator();
236
+ if (!this.isValidNewSessionId(newSessionId)) {
237
+ this.respondWithStatus(res, 500);
238
+ return;
239
+ }
240
+ sessionId = newSessionId;
241
+ activeSession = this.sessionStore.create(newSessionId);
242
+ const authSubject = this.readAuthSubject(req);
243
+ if (authSubject !== undefined) {
244
+ activeSession.authSubject = authSubject;
245
+ }
246
+ this.emit({ type: "session.created", sessionId: newSessionId });
247
+ this.createLocalMessageSession(newSessionId);
248
+ }
249
+ else {
250
+ activeSession = this.getActiveSession(headerSessionId, req);
251
+ if (activeSession === undefined) {
252
+ this.respondWithRejection(res, 404, "session_not_found", "Session was not found or has expired; reinitialize the session.");
253
+ return;
254
+ }
255
+ sessionId = headerSessionId;
256
+ this.touchSession(sessionId);
257
+ if (activeSession.protocolVersion !== undefined &&
258
+ !this.acceptsProtocolVersion(req, activeSession.protocolVersion)) {
259
+ this.respondWithRejection(res, 400, "protocol_version_mismatch", `MCP-Protocol-Version must match the session protocol version ${activeSession.protocolVersion}.`);
260
+ return;
261
+ }
262
+ await this.ensureLocalMessageSession(sessionId, activeSession);
263
+ }
264
+ }
265
+ const formattedResponses = await this.runWithRequestContext(req, async () => {
266
+ const responses = [];
267
+ for (const message of classified.entries) {
268
+ if (message === null) {
269
+ responses.push(formatErrorResponse(null, {
270
+ code: JSON_RPC_ERROR_CODES.INVALID_REQUEST,
271
+ message: "Invalid Request"
272
+ }));
273
+ continue;
274
+ }
275
+ if (!("method" in message)) {
276
+ continue;
277
+ }
278
+ const session = activeSession;
279
+ if (session !== undefined &&
280
+ message.method !== "initialize" &&
281
+ message.method !== "notifications/initialized" &&
282
+ message.method !== "ping" &&
283
+ !session.initialized) {
284
+ if (this.isRequest(message)) {
285
+ responses.push(formatErrorResponse(message.id, {
286
+ code: JSON_RPC_ERROR_CODES.INVALID_REQUEST,
287
+ message: "Session not initialized"
288
+ }));
289
+ }
290
+ continue;
291
+ }
292
+ const requestId = this.responseRequestIds.get(res) ?? "";
293
+ const toolName = this.readToolName(message);
294
+ if (message.method === "tools/call" &&
295
+ this.maxConcurrentToolCalls !== undefined &&
296
+ this.activeToolCalls >= this.maxConcurrentToolCalls) {
297
+ if (this.isRequest(message)) {
298
+ responses.push(formatErrorResponse(message.id, {
299
+ code: -32000,
300
+ message: "Too many concurrent tool calls"
301
+ }));
302
+ }
303
+ continue;
304
+ }
305
+ const messageHandler = sessionId === undefined
306
+ ? this.server.handleMessage
307
+ : (this.sessionMessages.get(sessionId)?.handleMessage ?? this.server.handleMessage);
308
+ const isToolCall = message.method === "tools/call";
309
+ const toolStartedAt = Date.now();
310
+ if (isToolCall) {
311
+ this.activeToolCalls += 1;
312
+ this.emit({
313
+ type: "tool.start",
314
+ requestId,
315
+ sessionId,
316
+ toolName
317
+ });
318
+ }
319
+ let handled;
320
+ try {
321
+ handled = await messageHandler(message.method, message.params);
322
+ }
323
+ finally {
324
+ if (isToolCall) {
325
+ this.activeToolCalls -= 1;
326
+ }
327
+ }
328
+ const { error, result } = handled;
329
+ if (isToolCall) {
330
+ this.emit({
331
+ type: "tool.end",
332
+ requestId,
333
+ sessionId,
334
+ toolName,
335
+ ok: this.isToolCallOk(error, result),
336
+ durationMs: Date.now() - toolStartedAt
337
+ });
338
+ }
339
+ if (session !== undefined && error === undefined) {
340
+ if (message.method === "initialize" && this.isRequest(message)) {
341
+ const initializeResult = result;
342
+ if (typeof initializeResult?.protocolVersion === "string") {
343
+ session.protocolVersion = initializeResult.protocolVersion;
344
+ }
345
+ }
346
+ else if (message.method === "notifications/initialized" &&
347
+ session.protocolVersion !== undefined) {
348
+ session.initialized = true;
349
+ }
350
+ }
351
+ if (!this.isRequest(message)) {
352
+ continue;
353
+ }
354
+ if (error !== undefined) {
355
+ responses.push(formatErrorResponse(message.id, error));
356
+ continue;
357
+ }
358
+ if (result !== undefined) {
359
+ responses.push(formatSuccessResponse(message.id, result));
360
+ }
361
+ }
362
+ return responses;
363
+ });
364
+ if (formattedResponses.length === 0) {
365
+ this.respondWithStatus(res, 202, sessionId);
366
+ return;
367
+ }
368
+ if (this.enableJsonResponse) {
369
+ const body = formattedResponses.length === 1
370
+ ? formattedResponses[0]
371
+ : JSON.stringify(formattedResponses.map((responseText) => JSON.parse(responseText)));
372
+ this.respondWithStatus(res, 200, sessionId, { "Content-Type": "application/json" }, body);
373
+ return;
374
+ }
375
+ res.writeHead(200, this.withSessionHeader(SSE_HEADERS, sessionId, res));
376
+ for (const formattedResponse of formattedResponses) {
377
+ res.write(formatSseEvent({ data: formattedResponse }));
378
+ }
379
+ res.end();
380
+ }
381
+ async handleGet(req, res) {
382
+ if (this.sessionIdGenerator === undefined) {
383
+ this.respondWithStatus(res, 405, undefined, {
384
+ Allow: ALLOWED_METHODS
385
+ });
386
+ return;
387
+ }
388
+ if (!this.acceptsResponseType(req, "text/event-stream")) {
389
+ this.respondWithRejection(res, 406, "response_type_not_acceptable", "Accept must allow text/event-stream.");
390
+ return;
391
+ }
392
+ const sessionId = this.readSessionId(req);
393
+ if (sessionId === undefined) {
394
+ this.respondWithRejection(res, 400, "session_id_required", "Mcp-Session-Id is required; initialize a session first.");
395
+ return;
396
+ }
397
+ const session = this.getActiveSession(sessionId, req);
398
+ if (session === undefined) {
399
+ this.respondWithRejection(res, 404, "session_not_found", "Session was not found or has expired; reinitialize the session.");
400
+ return;
401
+ }
402
+ this.touchSession(sessionId);
403
+ if (session.initialized &&
404
+ session.protocolVersion !== undefined &&
405
+ !this.acceptsProtocolVersion(req, session.protocolVersion)) {
406
+ this.respondWithRejection(res, 400, "protocol_version_mismatch", `MCP-Protocol-Version must match the session protocol version ${session.protocolVersion}.`);
407
+ return;
408
+ }
409
+ await this.ensureLocalMessageSession(sessionId, session);
410
+ const existingStreams = this.sseStreams.get(sessionId);
411
+ if ((existingStreams?.size ?? 0) >= this.maxStreamsPerSession) {
412
+ this.respondWithRejection(res, 409, "stream_limit_reached", "This session already has the maximum number of streams; close a stream and retry.");
413
+ return;
414
+ }
415
+ let streams = this.sseStreams.get(sessionId);
416
+ if (streams === undefined) {
417
+ streams = new Set();
418
+ this.sseStreams.set(sessionId, streams);
419
+ }
420
+ streams.add(res);
421
+ this.startSseKeepAlive();
422
+ this.emit({
423
+ type: "stream.opened",
424
+ sessionId,
425
+ streamCount: streams.size
426
+ });
427
+ const cleanup = () => {
428
+ this.clearSseExpiryTimer(res);
429
+ const activeStreams = this.sseStreams.get(sessionId);
430
+ if (activeStreams === undefined) {
431
+ return;
432
+ }
433
+ const deleted = activeStreams.delete(res);
434
+ if (deleted) {
435
+ this.emit({
436
+ type: "stream.closed",
437
+ sessionId,
438
+ streamCount: activeStreams.size
439
+ });
440
+ }
441
+ if (activeStreams.size === 0) {
442
+ this.sseStreams.delete(sessionId);
443
+ }
444
+ this.stopSseKeepAliveIfIdle();
445
+ };
446
+ req.on("close", cleanup);
447
+ res.on("close", cleanup);
448
+ res.on("finish", cleanup);
449
+ const expiresAt = req.auth?.expiresAt;
450
+ if (expiresAt !== undefined) {
451
+ this.scheduleSseExpiry(res, expiresAt);
452
+ }
453
+ res.writeHead(200, this.withSessionHeader(SSE_HEADERS, sessionId, res));
454
+ this.replaySseEvents(req, res, sessionId);
455
+ res.flushHeaders();
456
+ }
457
+ handleDelete(req, res) {
458
+ if (this.sessionIdGenerator === undefined) {
459
+ this.respondWithStatus(res, 405, undefined, {
460
+ Allow: ALLOWED_METHODS
461
+ });
462
+ return;
463
+ }
464
+ const sessionId = this.readSessionId(req);
465
+ if (sessionId === undefined) {
466
+ this.respondWithRejection(res, 400, "session_id_required", "Mcp-Session-Id is required; initialize a session first.");
467
+ return;
468
+ }
469
+ const session = this.getActiveSession(sessionId, req);
470
+ if (session === undefined) {
471
+ this.respondWithRejection(res, 404, "session_not_found", "Session was not found or has expired; reinitialize the session.");
472
+ return;
473
+ }
474
+ if (session.protocolVersion !== undefined &&
475
+ !this.acceptsProtocolVersion(req, session.protocolVersion)) {
476
+ this.respondWithRejection(res, 400, "protocol_version_mismatch", `MCP-Protocol-Version must match the session protocol version ${session.protocolVersion}.`);
477
+ return;
478
+ }
479
+ if (!this.deleteSession(sessionId, "client")) {
480
+ this.respondWithRejection(res, 404, "session_not_found", "Session was not found or has expired; reinitialize the session.");
481
+ return;
482
+ }
483
+ this.respondWithStatus(res, 204);
484
+ }
485
+ handleOptions(req, res) {
486
+ const requestedMethod = req.headers["access-control-request-method"];
487
+ const method = Array.isArray(requestedMethod) ? requestedMethod[0] : requestedMethod;
488
+ if (method !== undefined && !ALLOWED_METHODS.split(", ").includes(method)) {
489
+ this.respondWithStatus(res, 405, undefined, {
490
+ Allow: ALLOWED_METHODS
491
+ });
492
+ return;
493
+ }
494
+ const requestedHeaders = req.headers["access-control-request-headers"];
495
+ this.respondWithStatus(res, 204, undefined, {
496
+ Allow: ALLOWED_METHODS,
497
+ "Access-Control-Allow-Methods": ALLOWED_METHODS,
498
+ "Access-Control-Allow-Headers": Array.isArray(requestedHeaders)
499
+ ? requestedHeaders.join(", ")
500
+ : (requestedHeaders ?? DEFAULT_ALLOWED_HEADERS),
501
+ "Access-Control-Max-Age": "600"
502
+ });
503
+ }
504
+ readSessionId(req) {
505
+ const value = req.headers["mcp-session-id"];
506
+ const id = Array.isArray(value) ? value[0] : value;
507
+ return id !== undefined && id.length > 0 ? id : undefined;
508
+ }
509
+ acceptsProtocolVersion(req, protocolVersion) {
510
+ const value = req.headers["mcp-protocol-version"];
511
+ const header = Array.isArray(value) ? value[0] : value;
512
+ return header === undefined || header === protocolVersion;
513
+ }
514
+ readRequestId(req) {
515
+ const value = req.headers["x-request-id"];
516
+ const requestId = Array.isArray(value) ? value[0] : value;
517
+ return requestId !== undefined && requestId.length > 0 ? requestId : undefined;
518
+ }
519
+ readOrigin(req) {
520
+ const originHeader = req.headers.origin;
521
+ return Array.isArray(originHeader) ? originHeader[0] : originHeader;
522
+ }
523
+ readToolName(message) {
524
+ if (!("method" in message) || message.method !== "tools/call") {
525
+ return undefined;
526
+ }
527
+ const params = "params" in message ? message.params : undefined;
528
+ if (typeof params !== "object" || params === null || Array.isArray(params)) {
529
+ return undefined;
530
+ }
531
+ const name = params.name;
532
+ return typeof name === "string" ? name : undefined;
533
+ }
534
+ getActiveSession(sessionId, req) {
535
+ const session = this.sessionStore.get(sessionId);
536
+ if (session === undefined) {
537
+ return undefined;
538
+ }
539
+ if (this.isExpired(session)) {
540
+ this.deleteSession(sessionId, "expired");
541
+ return undefined;
542
+ }
543
+ if (session.authSubject !== undefined && session.authSubject !== this.readAuthSubject(req)) {
544
+ return undefined;
545
+ }
546
+ return session;
547
+ }
548
+ readAuthSubject(req) {
549
+ const auth = req.auth;
550
+ const authSubject = auth?.subject ?? auth?.clientId;
551
+ return authSubject !== undefined && authSubject.length > 0 ? authSubject : undefined;
552
+ }
553
+ touchSession(sessionId) {
554
+ const session = this.sessionStore.get(sessionId);
555
+ if (session === undefined) {
556
+ return;
557
+ }
558
+ if (this.sessionStore.touch !== undefined) {
559
+ this.sessionStore.touch(sessionId);
560
+ return;
561
+ }
562
+ session.lastSeenAt = new Date();
563
+ }
564
+ isExpired(session) {
565
+ if (this.sessionTtlMs === undefined) {
566
+ return false;
567
+ }
568
+ return Date.now() - session.lastSeenAt.getTime() > this.sessionTtlMs;
569
+ }
570
+ purgeExpiredSessions() {
571
+ if (this.sessionTtlMs === undefined || this.sessionStore.entries === undefined) {
572
+ return;
573
+ }
574
+ for (const session of [...this.sessionStore.entries()]) {
575
+ if (this.isExpired(session)) {
576
+ this.deleteSession(session.id, "expired");
577
+ }
578
+ }
579
+ }
580
+ sessionCount() {
581
+ if (this.sessionStore.entries !== undefined) {
582
+ return [...this.sessionStore.entries()].length;
583
+ }
584
+ return this.sessionMessages.size;
585
+ }
586
+ deleteSession(sessionId, reason) {
587
+ const deleted = this.sessionStore.delete(sessionId);
588
+ if (!deleted) {
589
+ return false;
590
+ }
591
+ this.sessionMessages.get(sessionId)?.close();
592
+ this.sessionMessages.delete(sessionId);
593
+ this.closeStreamsForSession(sessionId);
594
+ this.sseEventHistory.delete(sessionId);
595
+ this.emit({ type: "session.deleted", sessionId, reason });
596
+ return true;
597
+ }
598
+ createLocalMessageSession(sessionId) {
599
+ const messageSession = this.server.createMessageSession((notification) => {
600
+ this.sendNotificationToSession(sessionId, notification);
601
+ });
602
+ this.sessionMessages.set(sessionId, messageSession);
603
+ return messageSession;
604
+ }
605
+ async ensureLocalMessageSession(sessionId, session) {
606
+ const existing = this.sessionMessages.get(sessionId);
607
+ if (existing !== undefined) {
608
+ return existing;
609
+ }
610
+ const messageSession = this.createLocalMessageSession(sessionId);
611
+ if (session.protocolVersion !== undefined) {
612
+ await messageSession.handleMessage("initialize", {
613
+ protocolVersion: session.protocolVersion
614
+ });
615
+ if (session.initialized) {
616
+ await messageSession.handleMessage("notifications/initialized");
617
+ }
618
+ }
619
+ return messageSession;
620
+ }
621
+ closeStreamsForSession(sessionId) {
622
+ const streams = this.sseStreams.get(sessionId);
623
+ if (streams === undefined) {
624
+ return;
625
+ }
626
+ for (const response of streams) {
627
+ this.clearSseExpiryTimer(response);
628
+ if (!response.writableEnded) {
629
+ response.end();
630
+ }
631
+ }
632
+ this.sseStreams.delete(sessionId);
633
+ this.stopSseKeepAliveIfIdle();
634
+ }
635
+ scheduleSseExpiry(response, expiresAt) {
636
+ const remainingMs = Math.max(0, expiresAt * 1_000 - Date.now());
637
+ const timer = setTimeout(() => {
638
+ this.sseExpiryTimers.delete(response);
639
+ if (!response.writableEnded) {
640
+ response.end();
641
+ }
642
+ }, remainingMs);
643
+ timer.unref();
644
+ this.sseExpiryTimers.set(response, timer);
645
+ }
646
+ clearSseExpiryTimer(response) {
647
+ const timer = this.sseExpiryTimers.get(response);
648
+ if (timer === undefined) {
649
+ return;
650
+ }
651
+ clearTimeout(timer);
652
+ this.sseExpiryTimers.delete(response);
653
+ }
654
+ startSseKeepAlive() {
655
+ if (this.sseKeepAliveMs === 0 || this.sseKeepAliveInterval !== undefined) {
656
+ return;
657
+ }
658
+ this.sseKeepAliveInterval = setInterval(() => {
659
+ for (const streams of this.sseStreams.values()) {
660
+ for (const response of streams) {
661
+ if (!response.writableEnded) {
662
+ this.writeToLiveGetStream(response, ": keepalive\n\n");
663
+ }
664
+ }
665
+ }
666
+ }, this.sseKeepAliveMs);
667
+ this.sseKeepAliveInterval.unref();
668
+ }
669
+ stopSseKeepAliveIfIdle() {
670
+ if ([...this.sseStreams.values()].some((streams) => streams.size > 0)) {
671
+ return;
672
+ }
673
+ this.stopSseKeepAlive();
674
+ }
675
+ stopSseKeepAlive() {
676
+ if (this.sseKeepAliveInterval === undefined) {
677
+ return;
678
+ }
679
+ clearInterval(this.sseKeepAliveInterval);
680
+ this.sseKeepAliveInterval = undefined;
681
+ }
682
+ sendNotificationToSession(sessionId, notification) {
683
+ if (!this.sessionStore.get(sessionId)?.initialized) {
684
+ return;
685
+ }
686
+ const id = this.nextNotificationEventId++;
687
+ const data = JSON.stringify(notification);
688
+ this.recordSseEvent(sessionId, id, data);
689
+ const streams = this.sseStreams.get(sessionId);
690
+ if (streams === undefined) {
691
+ return;
692
+ }
693
+ const event = formatSseEvent({
694
+ id: String(id),
695
+ data
696
+ });
697
+ let latestResponse;
698
+ for (const response of streams) {
699
+ if (!response.writableEnded) {
700
+ latestResponse = response;
701
+ }
702
+ }
703
+ if (latestResponse !== undefined) {
704
+ this.writeToLiveGetStream(latestResponse, event);
705
+ }
706
+ }
707
+ writeToLiveGetStream(response, data) {
708
+ if ((response.writableLength ?? 0) > this.maxStreamBufferBytes) {
709
+ response.end();
710
+ return;
711
+ }
712
+ response.write(data);
713
+ }
714
+ recordSseEvent(sessionId, id, data) {
715
+ if (this.maxSseEventHistory <= 0) {
716
+ return;
717
+ }
718
+ const history = this.sseEventHistory.get(sessionId) ?? [];
719
+ history.push({ id, data });
720
+ while (history.length > this.maxSseEventHistory) {
721
+ history.shift();
722
+ }
723
+ this.sseEventHistory.set(sessionId, history);
724
+ }
725
+ replaySseEvents(req, res, sessionId) {
726
+ const value = req.headers["last-event-id"];
727
+ const header = Array.isArray(value) ? value[0] : value;
728
+ if (header === undefined) {
729
+ return;
730
+ }
731
+ const lastEventId = Number(header);
732
+ if (!Number.isSafeInteger(lastEventId)) {
733
+ return;
734
+ }
735
+ const history = this.sseEventHistory.get(sessionId) ?? [];
736
+ for (const event of history) {
737
+ if (event.id > lastEventId && !res.writableEnded) {
738
+ res.write(formatSseEvent({ id: String(event.id), data: event.data }));
739
+ }
740
+ }
741
+ }
742
+ isJsonRequest(req) {
743
+ const contentType = req.headers["content-type"];
744
+ if (contentType === undefined) {
745
+ return false;
746
+ }
747
+ const value = Array.isArray(contentType) ? contentType[0] : contentType;
748
+ const type = value.split(";")[0]?.trim().toLowerCase();
749
+ return type === "application/json";
750
+ }
751
+ acceptsOrigin(req) {
752
+ const origin = this.readOrigin(req);
753
+ if (origin === undefined) {
754
+ return true;
755
+ }
756
+ return this.acceptsOriginValue(req, origin);
757
+ }
758
+ acceptsOriginValue(req, origin) {
759
+ try {
760
+ const endpointOrigin = new URL(`${this.readRequestProtocol(req)}://${this.readRequestHost(req)}`).origin;
761
+ return origin === endpointOrigin || this.allowedOrigins.has(origin);
762
+ }
763
+ catch {
764
+ return false;
765
+ }
766
+ }
767
+ readRequestProtocol(req) {
768
+ if (this.trustedProxy) {
769
+ const forwardedProto = this.readForwardedHeader(req, "x-forwarded-proto")?.toLowerCase();
770
+ if (forwardedProto === "http" || forwardedProto === "https") {
771
+ return forwardedProto;
772
+ }
773
+ }
774
+ return "encrypted" in req.socket && req.socket.encrypted ? "https" : "http";
775
+ }
776
+ readRequestHost(req) {
777
+ if (this.trustedProxy) {
778
+ const forwardedHost = this.readForwardedHeader(req, "x-forwarded-host");
779
+ if (forwardedHost !== undefined && forwardedHost.length > 0) {
780
+ return forwardedHost;
781
+ }
782
+ }
783
+ const host = req.headers.host;
784
+ return Array.isArray(host) ? (host[0] ?? "127.0.0.1") : (host ?? "127.0.0.1");
785
+ }
786
+ readForwardedHeader(req, headerName) {
787
+ const value = req.headers[headerName];
788
+ const header = Array.isArray(value) ? value[0] : value;
789
+ return header?.split(",")[0]?.trim();
790
+ }
791
+ acceptsHost(req) {
792
+ const hostHeader = req.headers.host;
793
+ const host = Array.isArray(hostHeader) ? hostHeader[0] : hostHeader;
794
+ if (host === undefined || host.length === 0) {
795
+ return true;
796
+ }
797
+ return this.allowedHosts.has(this.normalizeHost(this.readRequestHost(req)));
798
+ }
799
+ normalizeHost(host) {
800
+ const normalized = host.trim().toLowerCase();
801
+ if (normalized.startsWith("[")) {
802
+ const closing = normalized.indexOf("]");
803
+ if (closing > 0) {
804
+ return normalized.slice(1, closing);
805
+ }
806
+ }
807
+ const colonCount = [...normalized].filter((character) => character === ":").length;
808
+ if (colonCount > 1) {
809
+ return normalized;
810
+ }
811
+ return normalized.includes(":") ? (normalized.split(":")[0] ?? normalized) : normalized;
812
+ }
813
+ acceptsConfiguredResponse(req) {
814
+ const expectedType = this.enableJsonResponse ? "application/json" : "text/event-stream";
815
+ return this.acceptsResponseType(req, expectedType);
816
+ }
817
+ acceptsResponseType(req, expectedType) {
818
+ const accept = req.headers.accept;
819
+ if (accept === undefined) {
820
+ return true;
821
+ }
822
+ const value = Array.isArray(accept) ? accept.join(",") : accept;
823
+ return value
824
+ .split(",")
825
+ .map((type) => type.split(";")[0]?.trim().toLowerCase())
826
+ .some((type) => type === "*/*" || type === expectedType);
827
+ }
828
+ isValidNewSessionId(sessionId) {
829
+ if (sessionId.length === 0 || this.sessionStore.has(sessionId)) {
830
+ return false;
831
+ }
832
+ try {
833
+ validateHeaderValue(MCP_SESSION_ID_HEADER, sessionId);
834
+ return true;
835
+ }
836
+ catch {
837
+ return false;
838
+ }
839
+ }
840
+ isRequest(message) {
841
+ return "method" in message && "id" in message;
842
+ }
843
+ isToolCallOk(error, result) {
844
+ if (error !== undefined) {
845
+ return false;
846
+ }
847
+ if (typeof result !== "object" || result === null || Array.isArray(result)) {
848
+ return true;
849
+ }
850
+ return !(hasOwnProperty(result, "isError") && result.isError === true);
851
+ }
852
+ respondWithJsonRpcError(res, statusCode, errorCode, message, id = null) {
853
+ this.responseRejectionReasons.set(res, "json_rpc_error");
854
+ this.respondWithStatus(res, statusCode, undefined, { "Content-Type": "application/json" }, formatErrorResponse(id, { code: errorCode, message }));
855
+ }
856
+ respondWithRejection(res, statusCode, reason, message) {
857
+ this.responseRejectionReasons.set(res, reason);
858
+ this.respondWithStatus(res, statusCode, undefined, { "Content-Type": "application/json" }, JSON.stringify({ error: reason, message }));
859
+ }
860
+ respondWithStatus(res, statusCode, sessionId, headers, body) {
861
+ res.writeHead(statusCode, this.withSessionHeader(headers, sessionId, res));
862
+ res.end(body);
863
+ }
864
+ withSessionHeader(headers, sessionId, res) {
865
+ const requestId = res === undefined ? undefined : this.responseRequestIds.get(res);
866
+ const origin = res === undefined ? undefined : this.responseOrigins.get(res);
867
+ const baseHeaders = {
868
+ "X-Content-Type-Options": "nosniff",
869
+ "Referrer-Policy": "no-referrer",
870
+ Vary: "Origin",
871
+ ...(requestId === undefined ? {} : { "X-Request-Id": requestId }),
872
+ ...(origin === undefined
873
+ ? {}
874
+ : {
875
+ "Access-Control-Allow-Origin": origin,
876
+ "Access-Control-Expose-Headers": "Mcp-Session-Id, X-Request-Id"
877
+ })
878
+ };
879
+ if (sessionId === undefined) {
880
+ return {
881
+ ...baseHeaders,
882
+ ...(headers ?? {})
883
+ };
884
+ }
885
+ return {
886
+ ...baseHeaders,
887
+ ...(headers ?? {}),
888
+ [MCP_SESSION_ID_HEADER]: sessionId
889
+ };
890
+ }
891
+ emit(event) {
892
+ this.observability.onEvent?.(event);
893
+ }
894
+ }
895
+ function hasOwnProperty(value, key) {
896
+ return Object.prototype.hasOwnProperty.call(value, key);
897
+ }