@clipform/mcp-server 4.0.0 → 4.2.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.
@@ -29,7 +29,7 @@ import {
29
29
  union,
30
30
  unknown,
31
31
  url
32
- } from "./chunk-54RZGUSA.js";
32
+ } from "./chunk-5RUWQOYD.js";
33
33
  import {
34
34
  GUIDE_TYPES,
35
35
  QUIZ_VARIANTS,
@@ -38,13 +38,13 @@ import {
38
38
  getGuideUri,
39
39
  guideFallbackText,
40
40
  registerResources
41
- } from "./chunk-5MJGBBKA.js";
41
+ } from "./chunk-TXUWAYBZ.js";
42
42
  import {
43
43
  ANON_PREVIEW_NOTICE,
44
44
  FULL_CAPS,
45
45
  anonPreviewNotice,
46
46
  capsFromToolsets
47
- } from "./chunk-S6RYUVDM.js";
47
+ } from "./chunk-PDI6VJYQ.js";
48
48
  import {
49
49
  ACTIVE_NODE_TYPES,
50
50
  CONTACT_FIELDS,
@@ -66,7 +66,7 @@ import {
66
66
  resolveFormType,
67
67
  structuredResult,
68
68
  textResult
69
- } from "./chunk-JRASOR5T.js";
69
+ } from "./chunk-4IAURGG7.js";
70
70
  import {
71
71
  getMcpAuth,
72
72
  runWithMcpAuth,
@@ -132,6 +132,7 @@ function declaredDialect(schema, remedy) {
132
132
 
133
133
  // ../../node_modules/.pnpm/@modelcontextprotocol+core@2.3.1/node_modules/@modelcontextprotocol/core/dist/auth-BNDyLTqp.mjs
134
134
  var LATEST_PROTOCOL_VERSION = "2025-11-25";
135
+ var DEFAULT_NEGOTIATED_PROTOCOL_VERSION = "2025-03-26";
135
136
  var SUPPORTED_PROTOCOL_VERSIONS = [
136
137
  LATEST_PROTOCOL_VERSION,
137
138
  "2025-06-18",
@@ -1098,6 +1099,29 @@ function brandedHasInstance(cls, value) {
1098
1099
  }
1099
1100
  return Function.prototype[Symbol.hasInstance].call(cls, value);
1100
1101
  }
1102
+ var OAuthErrorCode = /* @__PURE__ */ (function(OAuthErrorCode$1) {
1103
+ OAuthErrorCode$1["InvalidRequest"] = "invalid_request";
1104
+ OAuthErrorCode$1["InvalidClient"] = "invalid_client";
1105
+ OAuthErrorCode$1["InvalidGrant"] = "invalid_grant";
1106
+ OAuthErrorCode$1["UnauthorizedClient"] = "unauthorized_client";
1107
+ OAuthErrorCode$1["UnsupportedGrantType"] = "unsupported_grant_type";
1108
+ OAuthErrorCode$1["InvalidScope"] = "invalid_scope";
1109
+ OAuthErrorCode$1["AccessDenied"] = "access_denied";
1110
+ OAuthErrorCode$1["ServerError"] = "server_error";
1111
+ OAuthErrorCode$1["TemporarilyUnavailable"] = "temporarily_unavailable";
1112
+ OAuthErrorCode$1["UnsupportedResponseType"] = "unsupported_response_type";
1113
+ OAuthErrorCode$1["UnsupportedTokenType"] = "unsupported_token_type";
1114
+ OAuthErrorCode$1["InvalidToken"] = "invalid_token";
1115
+ OAuthErrorCode$1["MethodNotAllowed"] = "method_not_allowed";
1116
+ OAuthErrorCode$1["TooManyRequests"] = "too_many_requests";
1117
+ OAuthErrorCode$1["InvalidClientMetadata"] = "invalid_client_metadata";
1118
+ OAuthErrorCode$1["InvalidRedirectUri"] = "invalid_redirect_uri";
1119
+ OAuthErrorCode$1["InsufficientScope"] = "insufficient_scope";
1120
+ OAuthErrorCode$1["InvalidTarget"] = "invalid_target";
1121
+ OAuthErrorCode$1["InvalidDpopProof"] = "invalid_dpop_proof";
1122
+ OAuthErrorCode$1["UseDpopNonce"] = "use_dpop_nonce";
1123
+ return OAuthErrorCode$1;
1124
+ })({});
1101
1125
  var OAuthError = class OAuthError2 extends Error {
1102
1126
  static {
1103
1127
  Object.defineProperty(this, "mcpBrand", { value: "mcp.OAuthError" });
@@ -1221,6 +1245,10 @@ var SdkHttpError = class extends SdkError {
1221
1245
  return this.data.statusText;
1222
1246
  }
1223
1247
  };
1248
+ var REQUIRED_CLIENT_CAPABILITIES_BY_METHOD = {};
1249
+ function requiredClientCapabilitiesForRequest(method) {
1250
+ return Object.hasOwn(REQUIRED_CLIENT_CAPABILITIES_BY_METHOD, method) ? REQUIRED_CLIENT_CAPABILITIES_BY_METHOD[method] : void 0;
1251
+ }
1224
1252
  function isPlainObject$7(value) {
1225
1253
  return value !== null && typeof value === "object" && !Array.isArray(value);
1226
1254
  }
@@ -1261,6 +1289,7 @@ function missingClientCapabilities(required, declared) {
1261
1289
  return Object.keys(missing).length > 0 ? missing : void 0;
1262
1290
  }
1263
1291
  var FIRST_MODERN_PROTOCOL_VERSION = "2026-07-28";
1292
+ var SUPPORTED_MODERN_PROTOCOL_VERSIONS = [FIRST_MODERN_PROTOCOL_VERSION];
1264
1293
  function isModernProtocolVersion(version) {
1265
1294
  return version >= FIRST_MODERN_PROTOCOL_VERSION;
1266
1295
  }
@@ -3826,6 +3855,25 @@ function isExtensionReusedRequestMethod(method) {
3826
3855
  return method === "tasks/get" || method === "tasks/cancel";
3827
3856
  }
3828
3857
  var ALL_CODECS = [rev2025Codec, rev2026Codec];
3858
+ function isPlainObject$3(value) {
3859
+ return value !== null && typeof value === "object" && !Array.isArray(value);
3860
+ }
3861
+ function requestMetaOf(params) {
3862
+ if (!isPlainObject$3(params)) return void 0;
3863
+ const meta = params["_meta"];
3864
+ return isPlainObject$3(meta) ? meta : void 0;
3865
+ }
3866
+ function hasEnvelopeClaim(params) {
3867
+ const meta = requestMetaOf(params);
3868
+ return meta !== void 0 && PROTOCOL_VERSION_META_KEY in meta;
3869
+ }
3870
+ function envelopeClaimVersion(params) {
3871
+ const value = requestMetaOf(params)?.[PROTOCOL_VERSION_META_KEY];
3872
+ return typeof value === "string" ? value : void 0;
3873
+ }
3874
+ function validateEnvelopeMeta(meta) {
3875
+ return codecForVersion(MODERN_WIRE_REVISION).validateEnvelopeMeta(meta);
3876
+ }
3829
3877
  var schemas_exports = /* @__PURE__ */ __exportAll({
3830
3878
  AnnotationsSchema: () => AnnotationsSchema,
3831
3879
  AudioContentSchema: () => AudioContentSchema,
@@ -3999,12 +4047,14 @@ var isJSONRPCNotification = (value) => JSONRPCNotificationSchema.safeParse(value
3999
4047
  var isJSONRPCResultResponse = (value) => JSONRPCResultResponseSchema.safeParse(value).success;
4000
4048
  var isJSONRPCErrorResponse = (value) => JSONRPCErrorResponseSchema.safeParse(value).success;
4001
4049
  var isInputRequiredResult = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && value.resultType === "input_required";
4050
+ var isInitializeRequest = (value) => InitializeRequestSchema.safeParse(value).success;
4002
4051
  function assertCompleteRequestPrompt(request) {
4003
4052
  if (request.params.ref.type !== "ref/prompt") throw new TypeError(`Expected CompleteRequestPrompt, but got ${request.params.ref.type}`);
4004
4053
  }
4005
4054
  function assertCompleteRequestResourceTemplate(request) {
4006
4055
  if (request.params.ref.type !== "ref/resource") throw new TypeError(`Expected CompleteRequestResourceTemplate, but got ${request.params.ref.type}`);
4007
4056
  }
4057
+ var MCP_PARAM_HEADER_PREFIX = "Mcp-Param-";
4008
4058
  var X_MCP_HEADER_KEY = "x-mcp-header";
4009
4059
  var RFC9110_TOKEN = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
4010
4060
  var PERMITTED_X_MCP_HEADER_TYPES = /* @__PURE__ */ new Set([
@@ -4089,6 +4139,72 @@ var OBJECT_VALUED_SUBSCHEMA_KEYWORDS = /* @__PURE__ */ new Set([
4089
4139
  function pathName(path) {
4090
4140
  return path.length === 0 ? "<root>" : path.join(".");
4091
4141
  }
4142
+ var BASE64_SENTINEL_PREFIX = "=?base64?";
4143
+ var BASE64_SENTINEL_SUFFIX = "?=";
4144
+ var BASE64_CANONICAL = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
4145
+ var CANONICAL_DECIMAL = /^-?\d+(\.\d+)?$/;
4146
+ function mcpParamPrimitiveToString(value) {
4147
+ if (typeof value === "string") return value;
4148
+ if (typeof value === "boolean") return value ? "true" : "false";
4149
+ if (typeof value === "number") {
4150
+ if (!Number.isFinite(value)) return void 0;
4151
+ if (Number.isInteger(value) && !Number.isSafeInteger(value)) return void 0;
4152
+ return String(value);
4153
+ }
4154
+ }
4155
+ function base64ToUtf8(b64) {
4156
+ const bin = atob(b64);
4157
+ const bytes = new Uint8Array(bin.length);
4158
+ for (let i = 0; i < bin.length; i++) bytes[i] = bin.codePointAt(i);
4159
+ return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
4160
+ }
4161
+ function decodeMcpParamValue(value) {
4162
+ if (!(value.startsWith(BASE64_SENTINEL_PREFIX) && value.endsWith(BASE64_SENTINEL_SUFFIX))) return value;
4163
+ const b64 = value.slice(9, value.length - 2);
4164
+ if (!BASE64_CANONICAL.test(b64)) return void 0;
4165
+ try {
4166
+ return base64ToUtf8(b64);
4167
+ } catch {
4168
+ return;
4169
+ }
4170
+ }
4171
+ function valueAtPath(root, path) {
4172
+ let node = root;
4173
+ for (const key of path) {
4174
+ if (node === null || typeof node !== "object") return void 0;
4175
+ node = node[key];
4176
+ }
4177
+ return node;
4178
+ }
4179
+ function validateMcpParamHeaders(declarations, args, headers) {
4180
+ for (const decl of declarations) {
4181
+ const headerKey = `${MCP_PARAM_HEADER_PREFIX}${decl.headerName}`;
4182
+ const headerValue = headers.get(headerKey);
4183
+ const bodyRaw = valueAtPath(args, decl.path);
4184
+ if (bodyRaw === void 0 || bodyRaw === null) continue;
4185
+ const bodyString = mcpParamPrimitiveToString(bodyRaw);
4186
+ if (bodyString === void 0) continue;
4187
+ if (headerValue === null) return paramHeaderMismatchRejection("param-header-missing", headerKey, `the body carries ${pathName(decl.path)}=${JSON.stringify(bodyRaw)} but the ${headerKey} header is absent`);
4188
+ const decoded = decodeMcpParamValue(headerValue);
4189
+ if (decoded === void 0) return paramHeaderMismatchRejection("param-header-invalid-encoding", headerKey, `the ${headerKey} header carries an invalid Base64 sentinel value`);
4190
+ if (!((decl.type === "integer" || decl.type === "number") && CANONICAL_DECIMAL.test(decoded) && typeof bodyRaw === "number" ? Number(decoded) === bodyRaw : decoded === bodyString)) return paramHeaderMismatchRejection("param-header-mismatch", headerKey, `the ${headerKey} header decodes to ${JSON.stringify(decoded)} but the body carries ${pathName(decl.path)}=${JSON.stringify(bodyRaw)}`);
4191
+ }
4192
+ }
4193
+ function paramHeaderMismatchRejection(cell, header, body) {
4194
+ return {
4195
+ kind: "reject",
4196
+ rung: "param-header-validation",
4197
+ cell,
4198
+ httpStatus: 400,
4199
+ code: HEADER_MISMATCH_ERROR_CODE,
4200
+ message: `Bad Request: the request headers and body disagree: ${body}`,
4201
+ data: { mismatch: {
4202
+ header,
4203
+ body
4204
+ } },
4205
+ settled: true
4206
+ };
4207
+ }
4092
4208
  var HEADER_MISMATCH_ERROR_CODE = -32020;
4093
4209
  var INBOUND_VALIDATION_LADDER = [
4094
4210
  {
@@ -4176,6 +4292,232 @@ var LADDER_ERROR_HTTP_STATUS = {
4176
4292
  [ProtocolErrorCode.MissingRequiredClientCapability]: 400,
4177
4293
  [HEADER_MISMATCH_ERROR_CODE]: 400
4178
4294
  };
4295
+ function httpStatusForErrorCode(code, origin) {
4296
+ if (origin === "in-band") return code === ProtocolErrorCode.MissingRequiredClientCapability ? 400 : 200;
4297
+ return LADDER_ERROR_HTTP_STATUS[code] ?? 400;
4298
+ }
4299
+ function rejection(rung, cell, httpStatus, error, settled) {
4300
+ return {
4301
+ kind: "reject",
4302
+ rung,
4303
+ cell,
4304
+ httpStatus,
4305
+ code: error.code,
4306
+ message: error.message,
4307
+ ...error.data !== void 0 && { data: error.data },
4308
+ settled
4309
+ };
4310
+ }
4311
+ function crossCheckMismatch(cell, header, body, rung = "era-classification") {
4312
+ return rejection(rung, cell, 400, new ProtocolError(HEADER_MISMATCH_ERROR_CODE, `Bad Request: the request headers and body disagree: ${body}`, { mismatch: {
4313
+ header,
4314
+ body
4315
+ } }), true);
4316
+ }
4317
+ var MCP_NAME_HEADER_SOURCE = {
4318
+ "tools/call": "name",
4319
+ "prompts/get": "name",
4320
+ "resources/read": "uri",
4321
+ "tasks/get": "taskId",
4322
+ "tasks/update": "taskId",
4323
+ "tasks/cancel": "taskId"
4324
+ };
4325
+ function mcpNameSource(method, params) {
4326
+ const field = Object.hasOwn(MCP_NAME_HEADER_SOURCE, method) ? MCP_NAME_HEADER_SOURCE[method] : void 0;
4327
+ if (field === void 0) return;
4328
+ const raw = isPlainObject$2(params) ? params[field] : void 0;
4329
+ return {
4330
+ field,
4331
+ value: typeof raw === "string" ? raw : void 0
4332
+ };
4333
+ }
4334
+ function stripHttpOws(value) {
4335
+ let start = 0;
4336
+ while (start < value.length) {
4337
+ const code = value.codePointAt(start);
4338
+ if (code !== 9 && code !== 32) break;
4339
+ start += 1;
4340
+ }
4341
+ let end = value.length;
4342
+ while (end > start) {
4343
+ const code = value.codePointAt(end - 1);
4344
+ if (code !== 9 && code !== 32) break;
4345
+ end -= 1;
4346
+ }
4347
+ return start === 0 && end === value.length ? value : value.slice(start, end);
4348
+ }
4349
+ function validateStandardRequestHeaders(request, route) {
4350
+ if (route.messageKind !== "request") return;
4351
+ const method = route.message.method;
4352
+ if (request.protocolVersionHeader === void 0) {
4353
+ const claimed = route.classification.revision;
4354
+ return crossCheckMismatch("version-header-missing", "(missing)", claimed === void 0 ? "the body carries a modern per-request envelope but the required MCP-Protocol-Version header is absent" : `the body envelope names protocol version ${claimed} but the required MCP-Protocol-Version header is absent`, "standard-header-validation");
4355
+ }
4356
+ if (request.mcpMethodHeader === void 0) return crossCheckMismatch("method-header-missing", "(missing)", `the body names method ${method} but the required Mcp-Method header is absent`, "standard-header-validation");
4357
+ const source = mcpNameSource(method, route.message.params);
4358
+ if (source === void 0) return;
4359
+ const { field: sourceField, value: bodyValue } = source;
4360
+ if (request.mcpNameHeader === void 0) {
4361
+ if (bodyValue === void 0) return;
4362
+ return crossCheckMismatch("name-header-missing", "(missing)", `the body carries params.${sourceField}="${bodyValue}" but the required Mcp-Name header is absent`, "standard-header-validation");
4363
+ }
4364
+ const normalizedNameHeader = stripHttpOws(request.mcpNameHeader);
4365
+ const decoded = decodeMcpParamValue(normalizedNameHeader);
4366
+ if (decoded === void 0) return crossCheckMismatch("name-header-invalid-encoding", normalizedNameHeader, "the Mcp-Name header carries an invalid Base64 sentinel value", "standard-header-validation");
4367
+ if (bodyValue !== void 0 && decoded !== bodyValue) return crossCheckMismatch("name-header-mismatch", normalizedNameHeader, `the body carries params.${sourceField}="${bodyValue}" but the Mcp-Name header names "${decoded}"`, "standard-header-validation");
4368
+ }
4369
+ function isPlainObject$2(value) {
4370
+ return value !== null && typeof value === "object" && !Array.isArray(value);
4371
+ }
4372
+ function classificationForClaim(claimedVersion) {
4373
+ if (claimedVersion === void 0) return { era: "modern" };
4374
+ return {
4375
+ era: isModernProtocolVersion(claimedVersion) ? "modern" : "legacy",
4376
+ revision: claimedVersion
4377
+ };
4378
+ }
4379
+ function carriesValidModernEnvelopeClaim(params) {
4380
+ if (!hasEnvelopeClaim(params)) return false;
4381
+ const claimedVersion = envelopeClaimVersion(params);
4382
+ if (claimedVersion === void 0 || !isModernProtocolVersion(claimedVersion)) return false;
4383
+ const meta = requestMetaOf(params);
4384
+ return meta !== void 0 && validateEnvelopeMeta(meta).length === 0;
4385
+ }
4386
+ function classifyBatch(body) {
4387
+ if (body.length === 0) return rejection("jsonrpc-shape", "empty-batch", 400, new ProtocolError(ProtocolErrorCode.InvalidRequest, "Bad Request: empty JSON-RPC batch"), true);
4388
+ for (const element of body) {
4389
+ if (hasEnvelopeClaim(isPlainObject$2(element) ? element["params"] : void 0)) return rejection("jsonrpc-shape", "batch-with-modern-element", 400, new ProtocolError(ProtocolErrorCode.InvalidRequest, "Bad Request: JSON-RPC batches may not contain requests for protocol revision 2026-07-28 or later"), true);
4390
+ if (!(isJSONRPCRequest(element) || isJSONRPCNotification(element) || isJSONRPCResultResponse(element) || isJSONRPCErrorResponse(element))) return rejection("jsonrpc-shape", "batch-with-invalid-element", 400, new ProtocolError(ProtocolErrorCode.InvalidRequest, "Bad Request: JSON-RPC batch contains an invalid message"), true);
4391
+ }
4392
+ return {
4393
+ kind: "legacy",
4394
+ reason: "batch"
4395
+ };
4396
+ }
4397
+ function classifyRequestBody(request, body) {
4398
+ const params = body.params;
4399
+ const method = body.method;
4400
+ const headerVersion = request.protocolVersionHeader;
4401
+ const headerNamesModern = headerVersion !== void 0 && isModernProtocolVersion(headerVersion);
4402
+ if (method === "initialize" && !carriesValidModernEnvelopeClaim(params)) {
4403
+ if (headerNamesModern) return crossCheckMismatch("initialize-with-modern-header", headerVersion, "an initialize request (legacy handshake) was sent with a modern MCP-Protocol-Version header");
4404
+ const requestedVersion = isPlainObject$2(params) && typeof params["protocolVersion"] === "string" ? params["protocolVersion"] : void 0;
4405
+ return {
4406
+ kind: "legacy",
4407
+ reason: "initialize",
4408
+ ...requestedVersion !== void 0 && { requestedVersion }
4409
+ };
4410
+ }
4411
+ if (hasEnvelopeClaim(params)) {
4412
+ const meta = requestMetaOf(params);
4413
+ const firstIssue = (meta === void 0 ? [] : validateEnvelopeMeta(meta))[0];
4414
+ if (firstIssue !== void 0) return rejection("envelope", "envelope-invalid", 400, new ProtocolError(ProtocolErrorCode.InvalidParams, `Invalid _meta envelope for protocol revision 2026-07-28: ${firstIssue.key}: ${firstIssue.problem}`, { envelope: firstIssue }), true);
4415
+ const claimedVersion = envelopeClaimVersion(params);
4416
+ if (headerVersion !== void 0 && claimedVersion !== void 0 && headerVersion !== claimedVersion) return crossCheckMismatch("header-body-version-mismatch", headerVersion, `the body envelope names protocol version ${claimedVersion} but the MCP-Protocol-Version header names ${headerVersion}`);
4417
+ if (request.mcpMethodHeader !== void 0 && request.mcpMethodHeader !== method) return crossCheckMismatch("method-header-mismatch", request.mcpMethodHeader, `the body names method ${method} but the Mcp-Method header names ${request.mcpMethodHeader}`);
4418
+ return {
4419
+ kind: "modern",
4420
+ messageKind: "request",
4421
+ message: body,
4422
+ classification: classificationForClaim(claimedVersion)
4423
+ };
4424
+ }
4425
+ if (headerNamesModern) {
4426
+ const meta = requestMetaOf(params);
4427
+ const missingFromEnvelope = validateEnvelopeMeta(meta ?? {}).filter((issue) => issue.problem === "missing").map((issue) => issue.key);
4428
+ const missing = meta === void 0 ? ["_meta"] : missingFromEnvelope.length > 0 ? missingFromEnvelope : [PROTOCOL_VERSION_META_KEY];
4429
+ return rejection("envelope", "modern-header-without-claim", 400, new ProtocolError(ProtocolErrorCode.InvalidParams, `Invalid params: the MCP-Protocol-Version header names protocol revision ${headerVersion}, but the request is missing the required per-request envelope key(s): ${missing.join(", ")}`, { envelope: { missing } }), true);
4430
+ }
4431
+ return {
4432
+ kind: "legacy",
4433
+ reason: "no-claim",
4434
+ ...headerVersion !== void 0 && { requestedVersion: headerVersion }
4435
+ };
4436
+ }
4437
+ function classifyNotificationBody(request, body) {
4438
+ const params = body.params;
4439
+ const method = body.method;
4440
+ const headerVersion = request.protocolVersionHeader;
4441
+ const headerNamesModern = headerVersion !== void 0 && isModernProtocolVersion(headerVersion);
4442
+ if (hasEnvelopeClaim(params)) {
4443
+ const claimedVersion = envelopeClaimVersion(params);
4444
+ if (claimedVersion === void 0) {
4445
+ const meta = requestMetaOf(params);
4446
+ const claimIssue = (meta === void 0 ? [] : validateEnvelopeMeta(meta)).find((issue) => issue.key === PROTOCOL_VERSION_META_KEY) ?? {
4447
+ key: PROTOCOL_VERSION_META_KEY,
4448
+ problem: "expected a protocol version string"
4449
+ };
4450
+ return rejection("envelope", "notification-envelope-invalid", 400, new ProtocolError(ProtocolErrorCode.InvalidParams, `Invalid _meta envelope for protocol revision 2026-07-28: ${claimIssue.key}: ${claimIssue.problem}`, { envelope: claimIssue }), true);
4451
+ }
4452
+ if (headerVersion !== void 0 && headerVersion !== claimedVersion) return crossCheckMismatch("notification-header-body-version-mismatch", headerVersion, `the notification envelope names protocol version ${claimedVersion} but the MCP-Protocol-Version header names ${headerVersion}`);
4453
+ const classification = classificationForClaim(claimedVersion);
4454
+ if (classification.era === "modern" && request.mcpMethodHeader !== void 0 && request.mcpMethodHeader !== method) return crossCheckMismatch("notification-method-header-mismatch", request.mcpMethodHeader, `the notification body names method ${method} but the Mcp-Method header names ${request.mcpMethodHeader}`);
4455
+ return {
4456
+ kind: "modern",
4457
+ messageKind: "notification",
4458
+ message: body,
4459
+ classification
4460
+ };
4461
+ }
4462
+ if (headerNamesModern) {
4463
+ if (request.mcpMethodHeader !== void 0 && request.mcpMethodHeader !== method) return crossCheckMismatch("notification-method-header-mismatch", request.mcpMethodHeader, `the notification body names method ${method} but the Mcp-Method header names ${request.mcpMethodHeader}`);
4464
+ return {
4465
+ kind: "modern",
4466
+ messageKind: "notification",
4467
+ message: body,
4468
+ classification: {
4469
+ era: "modern",
4470
+ revision: headerVersion
4471
+ }
4472
+ };
4473
+ }
4474
+ return {
4475
+ kind: "legacy",
4476
+ reason: "notification",
4477
+ ...headerVersion !== void 0 && { requestedVersion: headerVersion }
4478
+ };
4479
+ }
4480
+ function classifyInboundRequest(request) {
4481
+ request = {
4482
+ ...request,
4483
+ ...request.protocolVersionHeader !== void 0 && { protocolVersionHeader: stripHttpOws(request.protocolVersionHeader) },
4484
+ ...request.mcpMethodHeader !== void 0 && { mcpMethodHeader: stripHttpOws(request.mcpMethodHeader) },
4485
+ ...request.mcpNameHeader !== void 0 && { mcpNameHeader: stripHttpOws(request.mcpNameHeader) }
4486
+ };
4487
+ if (request.httpMethod.toUpperCase() !== "POST") return {
4488
+ kind: "legacy",
4489
+ reason: "http-method"
4490
+ };
4491
+ const body = request.body;
4492
+ if (Array.isArray(body)) return classifyBatch(body);
4493
+ if (isJSONRPCResultResponse(body) || isJSONRPCErrorResponse(body)) return {
4494
+ kind: "legacy",
4495
+ reason: "response"
4496
+ };
4497
+ if (isPlainObject$2(body) && isJSONRPCRequest(body)) return classifyRequestBody(request, body);
4498
+ if (isPlainObject$2(body) && isJSONRPCNotification(body)) return classifyNotificationBody(request, body);
4499
+ return rejection("jsonrpc-shape", "invalid-json-rpc-body", 400, new ProtocolError(ProtocolErrorCode.InvalidRequest, "Bad Request: the request body is not a valid JSON-RPC message"), true);
4500
+ }
4501
+ function modernOnlyStrictRejection(route, supportedVersions) {
4502
+ switch (route.reason) {
4503
+ case "http-method":
4504
+ return rejection("http-method", "modern-only-method-not-allowed", 405, new ProtocolError(-32e3, "Method not allowed."), true);
4505
+ case "batch":
4506
+ return rejection("jsonrpc-shape", "modern-only-batch-not-supported", 400, new ProtocolError(ProtocolErrorCode.InvalidRequest, "Bad Request: JSON-RPC batches are not supported by this endpoint"), true);
4507
+ case "response":
4508
+ return rejection("jsonrpc-shape", "modern-only-response-post", 400, new ProtocolError(ProtocolErrorCode.InvalidRequest, "Bad Request: JSON-RPC responses cannot be posted to this endpoint"), true);
4509
+ case "notification":
4510
+ return;
4511
+ case "initialize":
4512
+ case "no-claim": {
4513
+ const requested = route.requestedVersion;
4514
+ return rejection("era-classification", "modern-only-missing-envelope", 400, requested === void 0 ? new ProtocolError(ProtocolErrorCode.UnsupportedProtocolVersion, "Unsupported protocol version: the request did not name a protocol version", { supported: [...supportedVersions] }) : new UnsupportedProtocolVersionError({
4515
+ supported: [...supportedVersions],
4516
+ requested
4517
+ }), true);
4518
+ }
4519
+ }
4520
+ }
4179
4521
  function parseSchema(schema, data) {
4180
4522
  return safeParse(schema, data);
4181
4523
  }
@@ -4737,6 +5079,9 @@ function withRequestStateValue(ctx, value) {
4737
5079
  };
4738
5080
  }
4739
5081
  var writeNegotiatedProtocolVersion;
5082
+ function setNegotiatedProtocolVersion(instance, version) {
5083
+ writeNegotiatedProtocolVersion(instance, version);
5084
+ }
4740
5085
  var Protocol = class {
4741
5086
  _transport;
4742
5087
  _requestMessageId = 0;
@@ -5566,6 +5911,20 @@ var require_content_type = /* @__PURE__ */ __commonJSMin(((exports) => {
5566
5911
  }
5567
5912
  }));
5568
5913
  var import_content_type = /* @__PURE__ */ __toESM(require_content_type(), 1);
5914
+ function mediaTypeEssence(header) {
5915
+ if (!header) return;
5916
+ try {
5917
+ return import_content_type.parse(header).type;
5918
+ } catch {
5919
+ const essence = (header.split(";", 1)[0] ?? "").trim().toLowerCase();
5920
+ if (essence === "" || header.slice(essence.length).includes(",")) return;
5921
+ return essence;
5922
+ }
5923
+ }
5924
+ function isJsonContentType(header) {
5925
+ if (header === "application/json") return true;
5926
+ return mediaTypeEssence(header) === "application/json";
5927
+ }
5569
5928
  var STDIO_DEFAULT_MAX_BUFFER_SIZE = 10 * 1024 * 1024;
5570
5929
  var ReadBuffer = class {
5571
5930
  _buffer;
@@ -12689,10 +13048,317 @@ function isCompletable(schema) {
12689
13048
  function getCompleter(schema) {
12690
13049
  return schema[COMPLETABLE_SYMBOL]?.complete;
12691
13050
  }
13051
+ var DEFAULT_SSE_KEEP_ALIVE_MS = 15e3;
12692
13052
  var MAX_TIMER_DELAY_MS = 2 ** 31 - 1;
13053
+ function armSseKeepAlive(intervalMs, onTick) {
13054
+ if (!Number.isFinite(intervalMs) || intervalMs < 1) return;
13055
+ const timer = setInterval(onTick, Math.min(intervalMs, MAX_TIMER_DELAY_MS));
13056
+ timer.unref?.();
13057
+ return timer;
13058
+ }
13059
+ var InMemoryServerEventBus = class {
13060
+ _listeners = /* @__PURE__ */ new Set();
13061
+ /**
13062
+ * @param onerror - Optional callback for errors thrown by listeners
13063
+ * during dispatch.
13064
+ */
13065
+ constructor(onerror) {
13066
+ this.onerror = onerror;
13067
+ }
13068
+ publish(event) {
13069
+ for (const listener of this._listeners) try {
13070
+ listener(event);
13071
+ } catch (error) {
13072
+ this.onerror?.(error instanceof Error ? error : new Error(String(error)));
13073
+ }
13074
+ }
13075
+ subscribe(listener) {
13076
+ this._listeners.add(listener);
13077
+ let live = true;
13078
+ return () => {
13079
+ if (!live) return;
13080
+ live = false;
13081
+ this._listeners.delete(listener);
13082
+ };
13083
+ }
13084
+ /** The number of currently registered listeners (test/introspection only — the routers track capacity via their own open-subscription set). */
13085
+ get listenerCount() {
13086
+ return this._listeners.size;
13087
+ }
13088
+ };
13089
+ function createServerNotifier(bus) {
13090
+ return {
13091
+ toolsChanged: () => bus.publish({ kind: "tools_list_changed" }),
13092
+ promptsChanged: () => bus.publish({ kind: "prompts_list_changed" }),
13093
+ resourcesChanged: () => bus.publish({ kind: "resources_list_changed" }),
13094
+ resourceUpdated: (uri) => bus.publish({
13095
+ kind: "resource_updated",
13096
+ uri
13097
+ })
13098
+ };
13099
+ }
13100
+ function listenFilterAccepts(filter, event) {
13101
+ switch (event.kind) {
13102
+ case "tools_list_changed":
13103
+ return filter.toolsListChanged === true;
13104
+ case "prompts_list_changed":
13105
+ return filter.promptsListChanged === true;
13106
+ case "resources_list_changed":
13107
+ return filter.resourcesListChanged === true;
13108
+ case "resource_updated":
13109
+ return filter.resourceSubscriptions !== void 0 && filter.resourceSubscriptions.includes(event.uri);
13110
+ }
13111
+ }
13112
+ function honoredSubset(requested, capabilities) {
13113
+ const honored = {};
13114
+ const allow = (bit) => capabilities === void 0 || bit === true;
13115
+ if (requested.toolsListChanged === true && allow(capabilities?.tools?.listChanged)) honored.toolsListChanged = true;
13116
+ if (requested.promptsListChanged === true && allow(capabilities?.prompts?.listChanged)) honored.promptsListChanged = true;
13117
+ if (requested.resourcesListChanged === true && allow(capabilities?.resources?.listChanged)) honored.resourcesListChanged = true;
13118
+ if (requested.resourceSubscriptions !== void 0 && requested.resourceSubscriptions.length > 0 && allow(capabilities?.resources?.subscribe)) honored.resourceSubscriptions = [...requested.resourceSubscriptions];
13119
+ return honored;
13120
+ }
13121
+ function serverEventToNotification(event) {
13122
+ switch (event.kind) {
13123
+ case "tools_list_changed":
13124
+ return { method: "notifications/tools/list_changed" };
13125
+ case "prompts_list_changed":
13126
+ return { method: "notifications/prompts/list_changed" };
13127
+ case "resources_list_changed":
13128
+ return { method: "notifications/resources/list_changed" };
13129
+ case "resource_updated":
13130
+ return {
13131
+ method: "notifications/resources/updated",
13132
+ params: { uri: event.uri }
13133
+ };
13134
+ }
13135
+ }
13136
+ var DEFAULT_MAX_SUBSCRIPTIONS = 1024;
13137
+ function jsonRpcError(id, code, message) {
13138
+ return Response.json({
13139
+ jsonrpc: "2.0",
13140
+ error: {
13141
+ code,
13142
+ message
13143
+ },
13144
+ id
13145
+ }, { status: 200 });
13146
+ }
13147
+ function stampSubscriptionId(notification, subscriptionId) {
13148
+ return {
13149
+ method: notification.method,
13150
+ params: {
13151
+ ...notification.params,
13152
+ _meta: {
13153
+ ...notification.params?._meta,
13154
+ [SUBSCRIPTION_ID_META_KEY]: subscriptionId
13155
+ }
13156
+ }
13157
+ };
13158
+ }
13159
+ function parseListenFilter(message) {
13160
+ const outcome = codecForVersion(MODERN_WIRE_REVISION).validateRequest("subscriptions/listen", message);
13161
+ return outcome.ok ? outcome.value.params?.notifications : void 0;
13162
+ }
13163
+ function createListenRouter(options) {
13164
+ const { bus, onerror } = options;
13165
+ const maxSubscriptions = options.maxSubscriptions ?? DEFAULT_MAX_SUBSCRIPTIONS;
13166
+ const keepAliveMs = options.keepAliveMs ?? DEFAULT_SSE_KEEP_ALIVE_MS;
13167
+ const open = /* @__PURE__ */ new Set();
13168
+ function serve(message, signal, capabilities, serverInfo) {
13169
+ if (open.size >= maxSubscriptions) {
13170
+ onerror?.(/* @__PURE__ */ new Error(`subscriptions/listen refused: subscription limit reached (${maxSubscriptions})`));
13171
+ return jsonRpcError(message.id, -32603, "Subscription limit reached");
13172
+ }
13173
+ const filter = parseListenFilter(message);
13174
+ if (filter === void 0) return jsonRpcError(message.id, -32602, "Invalid params: 'notifications' is required and must be a valid SubscriptionFilter");
13175
+ const honored = honoredSubset(filter, capabilities);
13176
+ const subscriptionId = message.id;
13177
+ const encoder = new TextEncoder();
13178
+ let controller;
13179
+ let closed = false;
13180
+ let unsubscribe;
13181
+ let keepAliveTimer;
13182
+ let abortCleanup;
13183
+ const writeFrame = (frame) => {
13184
+ if (closed) return;
13185
+ try {
13186
+ controller.enqueue(encoder.encode(frame));
13187
+ } catch (error) {
13188
+ onerror?.(error instanceof Error ? error : new Error(String(error)));
13189
+ }
13190
+ };
13191
+ const writeNotification = (method, params) => {
13192
+ writeFrame(`event: message
13193
+ data: ${JSON.stringify({
13194
+ jsonrpc: "2.0",
13195
+ method,
13196
+ params
13197
+ })}
13198
+
13199
+ `);
13200
+ };
13201
+ const teardown = (graceful) => {
13202
+ if (closed) return;
13203
+ if (graceful) writeFrame(`event: message
13204
+ data: ${JSON.stringify({
13205
+ jsonrpc: "2.0",
13206
+ id: subscriptionId,
13207
+ result: {
13208
+ resultType: "complete",
13209
+ _meta: {
13210
+ [SUBSCRIPTION_ID_META_KEY]: subscriptionId,
13211
+ [SERVER_INFO_META_KEY]: serverInfo
13212
+ }
13213
+ }
13214
+ })}
13215
+
13216
+ `);
13217
+ closed = true;
13218
+ try {
13219
+ unsubscribe?.();
13220
+ } catch (error) {
13221
+ onerror?.(error instanceof Error ? error : new Error(String(error)));
13222
+ }
13223
+ if (keepAliveTimer !== void 0) clearInterval(keepAliveTimer);
13224
+ abortCleanup?.();
13225
+ open.delete(teardown);
13226
+ try {
13227
+ controller.close();
13228
+ } catch {
13229
+ }
13230
+ };
13231
+ const readable = new ReadableStream({
13232
+ start(streamController) {
13233
+ controller = streamController;
13234
+ const ack = stampSubscriptionId({
13235
+ method: "notifications/subscriptions/acknowledged",
13236
+ params: { notifications: honored }
13237
+ }, subscriptionId);
13238
+ writeNotification(ack.method, ack.params);
13239
+ if (Object.keys(honored).length === 0) {
13240
+ teardown(true);
13241
+ return;
13242
+ }
13243
+ unsubscribe = bus.subscribe((event) => {
13244
+ if (closed || !listenFilterAccepts(honored, event)) return;
13245
+ const note = stampSubscriptionId(serverEventToNotification(event), subscriptionId);
13246
+ writeNotification(note.method, note.params);
13247
+ });
13248
+ keepAliveTimer = armSseKeepAlive(keepAliveMs, () => writeFrame(": keepalive\n\n"));
13249
+ open.add(teardown);
13250
+ },
13251
+ cancel() {
13252
+ teardown(false);
13253
+ }
13254
+ });
13255
+ if (signal !== void 0) if (signal.aborted) teardown(false);
13256
+ else {
13257
+ const onAbort = () => teardown(false);
13258
+ signal.addEventListener("abort", onAbort, { once: true });
13259
+ abortCleanup = () => signal.removeEventListener("abort", onAbort);
13260
+ }
13261
+ return new Response(readable, {
13262
+ status: 200,
13263
+ headers: {
13264
+ "Content-Type": "text/event-stream",
13265
+ "Cache-Control": "no-cache, no-transform",
13266
+ Connection: "keep-alive",
13267
+ "X-Accel-Buffering": "no"
13268
+ }
13269
+ });
13270
+ }
13271
+ return {
13272
+ serve,
13273
+ closeAll() {
13274
+ for (const teardown of open) teardown(true);
13275
+ },
13276
+ get openCount() {
13277
+ return open.size;
13278
+ }
13279
+ };
13280
+ }
13281
+ function headerQuotedValue(value) {
13282
+ return value.replaceAll(/[\\"]/g, String.raw`\$&`).replaceAll(/[^\u0020-\u007E]/g, " ");
13283
+ }
13284
+ function buildWwwAuthenticateHeader(errorCode, description, requiredScopes, resourceMetadataUrl) {
13285
+ let header = `Bearer error="${headerQuotedValue(errorCode)}", error_description="${headerQuotedValue(description)}"`;
13286
+ if (requiredScopes.length > 0) header += `, scope="${headerQuotedValue(requiredScopes.join(" "))}"`;
13287
+ if (resourceMetadataUrl) header += `, resource_metadata="${headerQuotedValue(resourceMetadataUrl)}"`;
13288
+ return header;
13289
+ }
13290
+ function bearerAuthChallengeResponse(error, options) {
13291
+ const { requiredScopes = [], resourceMetadataUrl } = options ?? {};
13292
+ if (!(error instanceof OAuthError)) {
13293
+ const serverError = new OAuthError(OAuthErrorCode.ServerError, "Internal Server Error");
13294
+ return Response.json(serverError.toResponseObject(), { status: 500 });
13295
+ }
13296
+ switch (error.code) {
13297
+ case OAuthErrorCode.InvalidToken: {
13298
+ const challenge = buildWwwAuthenticateHeader(error.code, error.message, requiredScopes, resourceMetadataUrl);
13299
+ return Response.json(error.toResponseObject(), {
13300
+ status: 401,
13301
+ headers: { "WWW-Authenticate": challenge }
13302
+ });
13303
+ }
13304
+ case OAuthErrorCode.InsufficientScope: {
13305
+ const challenge = buildWwwAuthenticateHeader(error.code, error.message, requiredScopes, resourceMetadataUrl);
13306
+ return Response.json(error.toResponseObject(), {
13307
+ status: 403,
13308
+ headers: { "WWW-Authenticate": challenge }
13309
+ });
13310
+ }
13311
+ case OAuthErrorCode.ServerError:
13312
+ return Response.json(error.toResponseObject(), { status: 500 });
13313
+ default:
13314
+ return Response.json(error.toResponseObject(), { status: 400 });
13315
+ }
13316
+ }
13317
+ function getOAuthProtectedResourceMetadataUrl(serverUrl) {
13318
+ const metadataUrl = new URL(serverUrl);
13319
+ metadataUrl.pathname = protectedResourceMetadataPath(serverUrl);
13320
+ metadataUrl.hash = "";
13321
+ return metadataUrl.href;
13322
+ }
13323
+ function protectedResourceMetadataPath(resourceServerUrl) {
13324
+ const rsPath = stripTrailingSlash(resourceServerUrl.pathname);
13325
+ return `/.well-known/oauth-protected-resource${rsPath === "/" ? "" : rsPath}`;
13326
+ }
13327
+ function stripTrailingSlash(path) {
13328
+ return path.length > 1 && path.endsWith("/") ? path.slice(0, -1) : path;
13329
+ }
12693
13330
  function supportsScopeChallengeResolver(transport) {
12694
13331
  return typeof transport === "object" && transport !== null && "setScopeChallengeResolver" in transport && typeof transport.setScopeChallengeResolver === "function";
12695
13332
  }
13333
+ function assertScope(scope, location) {
13334
+ if (typeof scope !== "string" || !/^[\u0021\u0023-\u005B\u005D-\u007E]+$/.test(scope)) throw new TypeError(`${location} must satisfy the OAuth scope-token grammar`);
13335
+ }
13336
+ function validateScopeChallenge(challenge) {
13337
+ if (challenge === null || typeof challenge !== "object" || !Array.isArray(challenge.scopes) || challenge.scopes.length === 0) throw new TypeError("scope challenge must contain at least one scope");
13338
+ for (const [index, scope] of challenge.scopes.entries()) assertScope(scope, `scope challenge scopes[${index}]`);
13339
+ if (challenge.errorDescription !== void 0 && typeof challenge.errorDescription !== "string") throw new TypeError("scope challenge errorDescription must be a string");
13340
+ if (challenge.errorDescription !== void 0 && !/^[\u0020-\u0021\u0023-\u005B\u005D-\u007E]+$/.test(challenge.errorDescription)) throw new TypeError("scope challenge errorDescription must satisfy the RFC 6750 error-description grammar");
13341
+ return challenge;
13342
+ }
13343
+ async function findScopeChallenge(requests, authInfo, resolve) {
13344
+ for (const request of requests) {
13345
+ const challenge = await resolve({
13346
+ request,
13347
+ ...authInfo !== void 0 && { authInfo }
13348
+ });
13349
+ if (challenge !== void 0) return validateScopeChallenge(challenge);
13350
+ }
13351
+ }
13352
+ function scopeChallengeResourceMetadataUrl(authInfo) {
13353
+ if (authInfo?.resourceMetadataUrl !== void 0) return authInfo.resourceMetadataUrl;
13354
+ if (authInfo?.resource?.protocol === "https:" || authInfo?.resource?.protocol === "http:") return getOAuthProtectedResourceMetadataUrl(authInfo.resource);
13355
+ }
13356
+ function createScopeChallengeResponse(challenge, resourceMetadataUrl) {
13357
+ return bearerAuthChallengeResponse(new OAuthError(OAuthErrorCode.InsufficientScope, challenge.errorDescription ?? "Insufficient scope"), {
13358
+ requiredScopes: [...challenge.scopes],
13359
+ resourceMetadataUrl
13360
+ });
13361
+ }
12696
13362
  var DEFAULT_LEGACY_SHIM_MAX_ROUNDS = 8;
12697
13363
  var DEFAULT_LEGACY_SHIM_ROUND_TIMEOUT_MS = 6e5;
12698
13364
  function resolveLegacyShimOptions(options) {
@@ -12830,6 +13496,15 @@ var INPUT_REQUIRED_CAPABLE_METHODS = /* @__PURE__ */ new Set([
12830
13496
  var writeClientIdentity;
12831
13497
  var installDiscoverHandler;
12832
13498
  var readServerIdentity;
13499
+ function seedClientIdentityFromEnvelope(server, identity) {
13500
+ writeClientIdentity(server, identity);
13501
+ }
13502
+ function installModernOnlyHandlers(server, servedModernVersions) {
13503
+ installDiscoverHandler(server, servedModernVersions);
13504
+ }
13505
+ function serverIdentityOf(server) {
13506
+ return readServerIdentity(server);
13507
+ }
12833
13508
  var Server = class extends Protocol {
12834
13509
  _clientCapabilities;
12835
13510
  _clientVersion;
@@ -14090,26 +14765,1295 @@ function unwrapOptionalSchema(schema) {
14090
14765
  }
14091
14766
 
14092
14767
  // ../../node_modules/.pnpm/@modelcontextprotocol+server@2.3.1/node_modules/@modelcontextprotocol/server/dist/index.mjs
14093
- var DEFAULT_MAX_REQUEST_BODY_SIZE = 4 * 1024 * 1024;
14768
+ var PerRequestHTTPServerTransport = class {
14769
+ onclose;
14770
+ onerror;
14771
+ onmessage;
14772
+ _classification;
14773
+ _responseMode;
14774
+ _started = false;
14775
+ _used = false;
14776
+ _closed = false;
14777
+ _terminalDelivered = false;
14778
+ /**
14779
+ * `true` only while the inbound message is being delivered synchronously
14780
+ * to the connected protocol layer. The pre-handler gates (the era
14781
+ * registry gate, the edge→instance handoff check, the missing-handler
14782
+ * rejection) answer inside this window; request handlers always run
14783
+ * after it (the protocol layer defers them to a microtask). An error
14784
+ * sent inside the window is therefore ladder-originated, and an error
14785
+ * sent after it is handler-produced.
14786
+ */
14787
+ _dispatchWindowOpen = false;
14788
+ _requestId;
14789
+ _deferredResponse;
14790
+ _sse;
14791
+ _abortCleanup;
14792
+ _keepAliveMs;
14793
+ constructor(options) {
14794
+ this._classification = options.classification;
14795
+ this._responseMode = options.responseMode ?? "auto";
14796
+ this._keepAliveMs = options.keepAliveMs ?? DEFAULT_SSE_KEEP_ALIVE_MS;
14797
+ }
14798
+ async start() {
14799
+ if (this._started) throw new Error("PerRequestHTTPServerTransport is already started");
14800
+ this._started = true;
14801
+ }
14802
+ /**
14803
+ * Serves the single exchange: delivers the classified message to the
14804
+ * connected server instance and resolves with the HTTP response.
14805
+ *
14806
+ * Throws when called a second time (the transport is strictly
14807
+ * single-use), or before a server has been connected to the transport.
14808
+ * The returned promise rejects with a connection-closed error when the
14809
+ * transport is closed before a response was produced (for example because
14810
+ * the client disconnected).
14811
+ */
14812
+ async handleMessage(message, extra) {
14813
+ if (this._used) throw new Error("PerRequestHTTPServerTransport serves exactly one exchange; construct a new transport per request");
14814
+ if (!this._started || this.onmessage === void 0) throw new Error("PerRequestHTTPServerTransport is not connected: connect a server to this transport before handling a message");
14815
+ if (this._closed) throw new Error("PerRequestHTTPServerTransport is closed");
14816
+ this._used = true;
14817
+ const signal = extra?.request?.signal;
14818
+ if (signal?.aborted) {
14819
+ await this.close();
14820
+ throw new SdkError(SdkErrorCode.ConnectionClosed, "The request was aborted before it could be handled");
14821
+ }
14822
+ const messageExtra = {
14823
+ classification: this._classification,
14824
+ ...extra?.request !== void 0 && { request: extra.request },
14825
+ ...extra?.authInfo !== void 0 && { authInfo: extra.authInfo }
14826
+ };
14827
+ if (isJSONRPCRequest(message)) {
14828
+ this._requestId = message.id;
14829
+ let resolve;
14830
+ let reject;
14831
+ const promise = new Promise((promiseResolve, promiseReject) => {
14832
+ resolve = promiseResolve;
14833
+ reject = promiseReject;
14834
+ });
14835
+ this._deferredResponse = {
14836
+ promise,
14837
+ resolve,
14838
+ reject,
14839
+ settled: false
14840
+ };
14841
+ if (signal !== void 0) {
14842
+ const onAbort = () => void this.close();
14843
+ signal.addEventListener("abort", onAbort, { once: true });
14844
+ this._abortCleanup = () => signal.removeEventListener("abort", onAbort);
14845
+ }
14846
+ this._dispatchWindowOpen = true;
14847
+ try {
14848
+ this.onmessage(message, messageExtra);
14849
+ } finally {
14850
+ this._dispatchWindowOpen = false;
14851
+ }
14852
+ if (this._responseMode === "sse" && !this._closed && !this._deferredResponse.settled) this.upgradeToSse();
14853
+ return promise;
14854
+ }
14855
+ this.onmessage(message, messageExtra);
14856
+ return new Response(null, { status: 202 });
14857
+ }
14858
+ async send(message, options) {
14859
+ if (this._closed) return;
14860
+ const isResponse = isJSONRPCResultResponse(message) || isJSONRPCErrorResponse(message);
14861
+ const relatedId = isResponse ? message.id : options?.relatedRequestId;
14862
+ if (this._requestId === void 0 || relatedId === void 0 || relatedId !== this._requestId) {
14863
+ if (isResponse) this.onerror?.(/* @__PURE__ */ new Error(`Received a response for an unknown request id: ${String(message.id)}`));
14864
+ return;
14865
+ }
14866
+ if (isResponse) {
14867
+ if (this._terminalDelivered) return;
14868
+ this._terminalDelivered = true;
14869
+ const errorCode = isJSONRPCErrorResponse(message) ? message.error.code : void 0;
14870
+ const ladderStatus = errorCode !== void 0 && (this._dispatchWindowOpen || errorCode === ProtocolErrorCode.MissingRequiredClientCapability) ? LADDER_ERROR_HTTP_STATUS[errorCode] : void 0;
14871
+ if (ladderStatus !== void 0 && this._sse === void 0) {
14872
+ this.settleResponse(Response.json(message, {
14873
+ status: ladderStatus,
14874
+ headers: { "Content-Type": "application/json" }
14875
+ }));
14876
+ queueMicrotask(() => void this.close());
14877
+ return;
14878
+ }
14879
+ if (this._sse !== void 0 || this._responseMode === "sse") {
14880
+ if (this._sse === void 0) this.upgradeToSse();
14881
+ this.writeMessageFrame(message);
14882
+ this.finalizeStream();
14883
+ return;
14884
+ }
14885
+ this.settleResponse(Response.json(message, {
14886
+ status: 200,
14887
+ headers: { "Content-Type": "application/json" }
14888
+ }));
14889
+ queueMicrotask(() => void this.close());
14890
+ return;
14891
+ }
14892
+ if (this._responseMode === "json") return;
14893
+ if (this._sse === void 0) this.upgradeToSse();
14894
+ this.writeMessageFrame(message);
14895
+ }
14896
+ /**
14897
+ * Writes an SSE comment frame (a keep-alive heartbeat). Dropped when the
14898
+ * exchange is not currently streaming.
14899
+ */
14900
+ writeCommentFrame(comment) {
14901
+ if (this._closed || this._sse === void 0 || this._sse.closed) return;
14902
+ const frame = comment.split("\n").map((line) => `: ${line}`).join("\n");
14903
+ this.writeFrame(`${frame}
14094
14904
 
14095
- // src/server.ts
14096
- import { readFileSync } from "fs";
14097
- import { dirname, join } from "path";
14098
- import { fileURLToPath } from "url";
14905
+ `);
14906
+ }
14907
+ async close() {
14908
+ if (this._closed) return;
14909
+ this._closed = true;
14910
+ this._abortCleanup?.();
14911
+ this._abortCleanup = void 0;
14912
+ if (this._sse?.keepAliveTimer !== void 0) clearInterval(this._sse.keepAliveTimer);
14913
+ if (this._sse !== void 0 && !this._sse.closed) {
14914
+ this._sse.closed = true;
14915
+ try {
14916
+ this._sse.controller.close();
14917
+ } catch {
14918
+ }
14919
+ }
14920
+ if (this._deferredResponse !== void 0 && !this._deferredResponse.settled) {
14921
+ this._deferredResponse.settled = true;
14922
+ this._deferredResponse.reject(new SdkError(SdkErrorCode.ConnectionClosed, "Connection closed before a response was produced"));
14923
+ }
14924
+ this.onclose?.();
14925
+ }
14926
+ settleResponse(response) {
14927
+ if (this._deferredResponse === void 0 || this._deferredResponse.settled) return;
14928
+ this._deferredResponse.settled = true;
14929
+ this._deferredResponse.resolve(response);
14930
+ }
14931
+ upgradeToSse() {
14932
+ let controller;
14933
+ const readable = new ReadableStream({
14934
+ start: (streamController) => {
14935
+ controller = streamController;
14936
+ },
14937
+ cancel: () => {
14938
+ this.close();
14939
+ }
14940
+ });
14941
+ this._sse = {
14942
+ controller,
14943
+ encoder: new TextEncoder(),
14944
+ closed: false
14945
+ };
14946
+ this._sse.keepAliveTimer = armSseKeepAlive(this._keepAliveMs, () => this.writeCommentFrame("keepalive"));
14947
+ this.settleResponse(new Response(readable, {
14948
+ status: 200,
14949
+ headers: {
14950
+ "Content-Type": "text/event-stream",
14951
+ "Cache-Control": "no-cache, no-transform",
14952
+ Connection: "keep-alive",
14953
+ "X-Accel-Buffering": "no"
14954
+ }
14955
+ }));
14956
+ }
14957
+ finalizeStream() {
14958
+ if (this._sse?.keepAliveTimer !== void 0) clearInterval(this._sse.keepAliveTimer);
14959
+ if (this._sse !== void 0 && !this._sse.closed) {
14960
+ this._sse.closed = true;
14961
+ try {
14962
+ this._sse.controller.close();
14963
+ } catch {
14964
+ }
14965
+ }
14966
+ queueMicrotask(() => void this.close());
14967
+ }
14968
+ writeMessageFrame(message) {
14969
+ this.writeFrame(`event: message
14970
+ data: ${JSON.stringify(message)}
14099
14971
 
14100
- // src/lib/onboarding-copy.ts
14101
- var ONBOARDING_OFFER_CONTROL_VARIANT = "control";
14102
- var ONBOARDING_OFFER_COPY = {
14103
- // #4089 review: this cohort has already completed OAuth to connect - they
14104
- // have an account. Never claim "no account needed" or otherwise imply an
14105
- // anonymous/no-sign-up lane here; that framing contradicts what they just
14106
- // did and reads as broken. Acknowledge the connection instead, then offer
14107
- // to build. (The anonymous/no-auth entry point is a different surface,
14108
- // #4088 - not this cohort.)
14109
- [ONBOARDING_OFFER_CONTROL_VARIANT]: "When this is the start of a new conversation and the user has not yet said what they want to build, welcome them as someone who is now connected to Clipform and offer to build their first form together, then offer these starting points as plain text: lead-gen, feedback, quiz, or something else - ask which one fits, or invite them to describe what they need instead. If the user's first message already states what they want to build, even loosely, skip that offer entirely and go straight into the brief-driven flow below."
14972
+ `);
14973
+ }
14974
+ writeFrame(frame) {
14975
+ if (this._sse === void 0 || this._sse.closed) return;
14976
+ try {
14977
+ this._sse.controller.enqueue(this._sse.encoder.encode(frame));
14978
+ } catch (error) {
14979
+ this.onerror?.(/* @__PURE__ */ new Error(`Failed to write to the response stream: ${error}`));
14980
+ }
14981
+ }
14110
14982
  };
14111
- function buildOnboardingOffer(variant = ONBOARDING_OFFER_CONTROL_VARIANT) {
14112
- return ONBOARDING_OFFER_COPY[variant] ?? ONBOARDING_OFFER_COPY[ONBOARDING_OFFER_CONTROL_VARIANT];
14983
+ async function invoke(server, message, ctx) {
14984
+ const transport = new PerRequestHTTPServerTransport({
14985
+ classification: ctx.classification,
14986
+ ...ctx.responseMode !== void 0 && { responseMode: ctx.responseMode },
14987
+ ...ctx.keepAliveMs !== void 0 && { keepAliveMs: ctx.keepAliveMs }
14988
+ });
14989
+ await server.connect(transport);
14990
+ return transport.handleMessage(message, {
14991
+ ...ctx.request !== void 0 && { request: ctx.request },
14992
+ ...ctx.authInfo !== void 0 && { authInfo: ctx.authInfo }
14993
+ });
14994
+ }
14995
+ var DEFAULT_MAX_REQUEST_BODY_SIZE = 4 * 1024 * 1024;
14996
+ var MAX_BATCH_SIZE = 100;
14997
+ function requestBodyTooLargeMessage(maxBytes) {
14998
+ return `Payload Too Large: Request body must not exceed ${maxBytes} bytes`;
14999
+ }
15000
+ function resolveMaxRequestBodySize(value) {
15001
+ if (value === void 0) return DEFAULT_MAX_REQUEST_BODY_SIZE;
15002
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) throw new RangeError(`maxRequestBodySize must be a positive number of bytes, got ${String(value)}`);
15003
+ return value;
15004
+ }
15005
+ async function readRequestBody(request, maxBytes = DEFAULT_MAX_REQUEST_BODY_SIZE) {
15006
+ if (Number(request.headers.get("content-length")) > maxBytes) return { tooLarge: true };
15007
+ if (request.body === null) return {
15008
+ tooLarge: false,
15009
+ text: ""
15010
+ };
15011
+ const reader = request.body.getReader();
15012
+ const decoder = new TextDecoder();
15013
+ let received = 0;
15014
+ let text = "";
15015
+ try {
15016
+ for (; ; ) {
15017
+ const { done, value } = await reader.read();
15018
+ if (done) break;
15019
+ received += value.byteLength;
15020
+ if (received > maxBytes) return { tooLarge: true };
15021
+ text += decoder.decode(value, { stream: true });
15022
+ }
15023
+ } finally {
15024
+ reader.releaseLock();
15025
+ }
15026
+ return {
15027
+ tooLarge: false,
15028
+ text: text + decoder.decode()
15029
+ };
15030
+ }
15031
+ var WebStandardStreamableHTTPServerTransport = class {
15032
+ sessionIdGenerator;
15033
+ _started = false;
15034
+ _hasHandledRequest = false;
15035
+ _closed = false;
15036
+ _streamMapping = /* @__PURE__ */ new Map();
15037
+ _requestToStreamMapping = /* @__PURE__ */ new Map();
15038
+ _requestResponseMap = /* @__PURE__ */ new Map();
15039
+ _initialized = false;
15040
+ _enableJsonResponse = false;
15041
+ _standaloneSseStreamId = "_GET_stream";
15042
+ _eventStore;
15043
+ _onsessioninitialized;
15044
+ _onsessionclosed;
15045
+ _allowedHosts;
15046
+ _allowedOrigins;
15047
+ _enableDnsRebindingProtection;
15048
+ _retryInterval;
15049
+ _supportedProtocolVersions;
15050
+ _keepAliveMs;
15051
+ _maxRequestBodySize;
15052
+ _scopeChallengeResolver;
15053
+ sessionId;
15054
+ onclose;
15055
+ onerror;
15056
+ onmessage;
15057
+ constructor(options = {}) {
15058
+ this.sessionIdGenerator = options.sessionIdGenerator;
15059
+ this._enableJsonResponse = options.enableJsonResponse ?? false;
15060
+ this._eventStore = options.eventStore;
15061
+ this._onsessioninitialized = options.onsessioninitialized;
15062
+ this._onsessionclosed = options.onsessionclosed;
15063
+ this._allowedHosts = options.allowedHosts;
15064
+ this._allowedOrigins = options.allowedOrigins;
15065
+ this._enableDnsRebindingProtection = options.enableDnsRebindingProtection ?? false;
15066
+ this._retryInterval = options.retryInterval;
15067
+ this._supportedProtocolVersions = options.supportedProtocolVersions ?? SUPPORTED_PROTOCOL_VERSIONS;
15068
+ this._keepAliveMs = options.keepAliveMs ?? DEFAULT_SSE_KEEP_ALIVE_MS;
15069
+ this._maxRequestBodySize = resolveMaxRequestBodySize(options.maxRequestBodySize);
15070
+ }
15071
+ startKeepAlive(controller, encoder) {
15072
+ if (this._closed) return void 0;
15073
+ const timer = armSseKeepAlive(this._keepAliveMs, () => {
15074
+ try {
15075
+ controller.enqueue(encoder.encode(": keepalive\n\n"));
15076
+ } catch {
15077
+ if (timer !== void 0) clearInterval(timer);
15078
+ }
15079
+ });
15080
+ return timer;
15081
+ }
15082
+ /** Sets the scope challenge resolver for parsed JSON-RPC requests. */
15083
+ setScopeChallengeResolver(resolver) {
15084
+ this._scopeChallengeResolver = resolver;
15085
+ }
15086
+ /**
15087
+ * Starts the transport. This is required by the {@linkcode Transport} interface but is a no-op
15088
+ * for the Streamable HTTP transport as connections are managed per-request.
15089
+ */
15090
+ async start() {
15091
+ if (this._started) throw new Error("Transport already started");
15092
+ this._started = true;
15093
+ }
15094
+ /**
15095
+ * Sets the supported protocol versions for header validation.
15096
+ * Called by the server during {@linkcode server/server.Server.connect | connect()} to pass its supported versions.
15097
+ */
15098
+ setSupportedProtocolVersions(versions) {
15099
+ this._supportedProtocolVersions = versions;
15100
+ }
15101
+ /**
15102
+ * Helper to create a JSON error response
15103
+ */
15104
+ createJsonErrorResponse(status, code, message, options) {
15105
+ const error = {
15106
+ code,
15107
+ message
15108
+ };
15109
+ if (options?.data !== void 0) error.data = options.data;
15110
+ return Response.json({
15111
+ jsonrpc: "2.0",
15112
+ error,
15113
+ id: null
15114
+ }, {
15115
+ status,
15116
+ headers: {
15117
+ "Content-Type": "application/json",
15118
+ ...options?.headers
15119
+ }
15120
+ });
15121
+ }
15122
+ async _checkScopeChallenge(messages, authInfo) {
15123
+ if (!this._scopeChallengeResolver) return;
15124
+ const challenge = await findScopeChallenge(messages.filter((message) => isJSONRPCRequest(message)), authInfo, this._scopeChallengeResolver);
15125
+ return challenge === void 0 ? void 0 : createScopeChallengeResponse(challenge, scopeChallengeResourceMetadataUrl(authInfo));
15126
+ }
15127
+ /**
15128
+ * Validates request headers for DNS rebinding protection.
15129
+ * @returns Error response if validation fails, `undefined` if validation passes.
15130
+ */
15131
+ validateRequestHeaders(req) {
15132
+ if (!this._enableDnsRebindingProtection) return;
15133
+ if (this._allowedHosts && this._allowedHosts.length > 0) {
15134
+ const hostHeader = req.headers.get("host");
15135
+ if (!hostHeader || !this._allowedHosts.includes(hostHeader)) {
15136
+ const error = `Invalid Host header: ${hostHeader}`;
15137
+ this.onerror?.(new Error(error));
15138
+ return this.createJsonErrorResponse(403, -32e3, error);
15139
+ }
15140
+ }
15141
+ if (this._allowedOrigins && this._allowedOrigins.length > 0) {
15142
+ const originHeader = req.headers.get("origin");
15143
+ if (originHeader && !this._allowedOrigins.includes(originHeader)) {
15144
+ const error = `Invalid Origin header: ${originHeader}`;
15145
+ this.onerror?.(new Error(error));
15146
+ return this.createJsonErrorResponse(403, -32e3, error);
15147
+ }
15148
+ }
15149
+ }
15150
+ /**
15151
+ * Handles an incoming HTTP request, whether `GET`, `POST`, or `DELETE`
15152
+ * Returns a `Response` object (Web Standard)
15153
+ */
15154
+ async handleRequest(req, options) {
15155
+ if (this._closed) return this.createJsonErrorResponse(404, -32001, "Session not found");
15156
+ if (!this.sessionIdGenerator && this._hasHandledRequest) throw new Error("Stateless transport cannot be reused across requests. Create a new transport per request.");
15157
+ this._hasHandledRequest = true;
15158
+ const validationError = this.validateRequestHeaders(req);
15159
+ if (validationError) return validationError;
15160
+ switch (req.method) {
15161
+ case "POST":
15162
+ return this.handlePostRequest(req, options);
15163
+ case "GET":
15164
+ return this.handleGetRequest(req);
15165
+ case "DELETE":
15166
+ return this.handleDeleteRequest(req);
15167
+ default:
15168
+ return this.handleUnsupportedRequest();
15169
+ }
15170
+ }
15171
+ /**
15172
+ * Returns true if the client's protocol version supports empty SSE data in
15173
+ * priming events (the fix shipped with protocol version `2025-11-25`).
15174
+ *
15175
+ * The version is checked for membership in this transport instance's
15176
+ * supported protocol versions rather than with an open-ended
15177
+ * `>= '2025-11-25'` comparison: the value may come from an `initialize`
15178
+ * request body, which (unlike the `MCP-Protocol-Version` header) is not
15179
+ * validated against `supportedProtocolVersions` before reaching this
15180
+ * check. An unknown future version string must not silently enable
15181
+ * behavior reserved for versions this transport actually supports.
15182
+ */
15183
+ supportsEmptySSEData(protocolVersion) {
15184
+ return this._supportedProtocolVersions.includes(protocolVersion) && protocolVersion >= "2025-11-25";
15185
+ }
15186
+ /**
15187
+ * Writes a priming event to establish resumption capability.
15188
+ * Only sends if `eventStore` is configured (opt-in for resumability) and
15189
+ * the client's protocol version supports empty SSE data (a supported
15190
+ * version that is >= `2025-11-25`).
15191
+ */
15192
+ async writePrimingEvent(controller, encoder, streamId, protocolVersion) {
15193
+ if (!this._eventStore) return;
15194
+ if (!this.supportsEmptySSEData(protocolVersion)) return;
15195
+ const primingEventId = await this._eventStore.storeEvent(streamId, {});
15196
+ let primingEvent = `id: ${primingEventId}
15197
+ data:
15198
+
15199
+ `;
15200
+ if (this._retryInterval !== void 0) primingEvent = `id: ${primingEventId}
15201
+ retry: ${this._retryInterval}
15202
+ data:
15203
+
15204
+ `;
15205
+ controller.enqueue(encoder.encode(primingEvent));
15206
+ }
15207
+ /**
15208
+ * Handles `GET` requests for SSE stream
15209
+ */
15210
+ async handleGetRequest(req) {
15211
+ if (!req.headers.get("accept")?.includes("text/event-stream")) {
15212
+ this.onerror?.(/* @__PURE__ */ new Error("Not Acceptable: Client must accept text/event-stream"));
15213
+ return this.createJsonErrorResponse(406, -32e3, "Not Acceptable: Client must accept text/event-stream");
15214
+ }
15215
+ const sessionError = this.validateSession(req);
15216
+ if (sessionError) return sessionError;
15217
+ const protocolError = this.validateProtocolVersion(req);
15218
+ if (protocolError) return protocolError;
15219
+ if (this._eventStore) {
15220
+ const lastEventId = req.headers.get("last-event-id");
15221
+ if (lastEventId) return this.replayEvents(lastEventId);
15222
+ }
15223
+ if (this._streamMapping.get(this._standaloneSseStreamId) !== void 0) {
15224
+ this.onerror?.(/* @__PURE__ */ new Error("Conflict: Only one SSE stream is allowed per session"));
15225
+ return this.createJsonErrorResponse(409, -32e3, "Conflict: Only one SSE stream is allowed per session");
15226
+ }
15227
+ const encoder = new TextEncoder();
15228
+ let streamController;
15229
+ let keepAliveTimer;
15230
+ const readable = new ReadableStream({
15231
+ start: (controller) => {
15232
+ streamController = controller;
15233
+ },
15234
+ cancel: () => {
15235
+ if (keepAliveTimer !== void 0) clearInterval(keepAliveTimer);
15236
+ if (this._streamMapping.get(this._standaloneSseStreamId)?.controller === streamController) this._streamMapping.delete(this._standaloneSseStreamId);
15237
+ }
15238
+ });
15239
+ const headers = {
15240
+ "Content-Type": "text/event-stream",
15241
+ "Cache-Control": "no-cache, no-transform",
15242
+ Connection: "keep-alive",
15243
+ "X-Accel-Buffering": "no"
15244
+ };
15245
+ if (this.sessionId !== void 0) headers["mcp-session-id"] = this.sessionId;
15246
+ this._streamMapping.set(this._standaloneSseStreamId, {
15247
+ controller: streamController,
15248
+ encoder,
15249
+ cleanup: () => {
15250
+ if (keepAliveTimer !== void 0) clearInterval(keepAliveTimer);
15251
+ this._streamMapping.delete(this._standaloneSseStreamId);
15252
+ try {
15253
+ streamController.close();
15254
+ } catch {
15255
+ }
15256
+ }
15257
+ });
15258
+ keepAliveTimer = this.startKeepAlive(streamController, encoder);
15259
+ return new Response(readable, { headers });
15260
+ }
15261
+ /**
15262
+ * Replays events that would have been sent after the specified event ID
15263
+ * Only used when resumability is enabled
15264
+ */
15265
+ async replayEvents(lastEventId) {
15266
+ if (!this._eventStore) {
15267
+ this.onerror?.(/* @__PURE__ */ new Error("Event store not configured"));
15268
+ return this.createJsonErrorResponse(400, -32e3, "Event store not configured");
15269
+ }
15270
+ try {
15271
+ let streamId;
15272
+ if (this._eventStore.getStreamIdForEventId) {
15273
+ streamId = await this._eventStore.getStreamIdForEventId(lastEventId);
15274
+ if (!streamId) {
15275
+ this.onerror?.(/* @__PURE__ */ new Error("Invalid event ID format"));
15276
+ return this.createJsonErrorResponse(400, -32e3, "Invalid event ID format");
15277
+ }
15278
+ if (this._streamMapping.get(streamId) !== void 0) {
15279
+ this.onerror?.(/* @__PURE__ */ new Error("Conflict: Stream already has an active connection"));
15280
+ return this.createJsonErrorResponse(409, -32e3, "Conflict: Stream already has an active connection");
15281
+ }
15282
+ }
15283
+ const headers = {
15284
+ "Content-Type": "text/event-stream",
15285
+ "Cache-Control": "no-cache, no-transform",
15286
+ Connection: "keep-alive",
15287
+ "X-Accel-Buffering": "no"
15288
+ };
15289
+ if (this.sessionId !== void 0) headers["mcp-session-id"] = this.sessionId;
15290
+ const encoder = new TextEncoder();
15291
+ let streamController;
15292
+ let keepAliveTimer;
15293
+ let cancelled = false;
15294
+ let replayedStreamId;
15295
+ const readable = new ReadableStream({
15296
+ start: (controller) => {
15297
+ streamController = controller;
15298
+ },
15299
+ cancel: () => {
15300
+ cancelled = true;
15301
+ if (keepAliveTimer !== void 0) clearInterval(keepAliveTimer);
15302
+ if (replayedStreamId !== void 0 && this._streamMapping.get(replayedStreamId)?.controller === streamController) this._streamMapping.delete(replayedStreamId);
15303
+ }
15304
+ });
15305
+ const replayedEventIds = /* @__PURE__ */ new Set();
15306
+ replayedStreamId = await this._eventStore.replayEventsAfter(lastEventId, { send: async (eventId, message) => {
15307
+ replayedEventIds.add(eventId);
15308
+ if (!this.writeSSEEvent(streamController, encoder, message, eventId)) try {
15309
+ streamController.close();
15310
+ } catch {
15311
+ }
15312
+ } });
15313
+ if (this._closed || cancelled) {
15314
+ try {
15315
+ streamController.close();
15316
+ } catch {
15317
+ }
15318
+ return this.createJsonErrorResponse(404, -32001, "Session not found");
15319
+ }
15320
+ this._streamMapping.get(replayedStreamId)?.cleanup();
15321
+ this._streamMapping.set(replayedStreamId, {
15322
+ controller: streamController,
15323
+ encoder,
15324
+ replayedEventIds,
15325
+ cleanup: () => {
15326
+ if (keepAliveTimer !== void 0) clearInterval(keepAliveTimer);
15327
+ this._streamMapping.delete(replayedStreamId);
15328
+ try {
15329
+ streamController.close();
15330
+ } catch {
15331
+ }
15332
+ }
15333
+ });
15334
+ if (replayedStreamId !== this._standaloneSseStreamId) {
15335
+ if (![...this._requestToStreamMapping.values()].includes(replayedStreamId)) {
15336
+ this._streamMapping.delete(replayedStreamId);
15337
+ try {
15338
+ streamController.close();
15339
+ } catch {
15340
+ }
15341
+ }
15342
+ }
15343
+ if (this._streamMapping.get(replayedStreamId)?.controller === streamController) keepAliveTimer = this.startKeepAlive(streamController, encoder);
15344
+ return new Response(readable, { headers });
15345
+ } catch (error) {
15346
+ this.onerror?.(error);
15347
+ return this.createJsonErrorResponse(500, -32e3, "Error replaying events");
15348
+ }
15349
+ }
15350
+ /**
15351
+ * Writes an event to an SSE stream via controller with proper formatting
15352
+ */
15353
+ writeSSEEvent(controller, encoder, message, eventId) {
15354
+ try {
15355
+ let eventData = `event: message
15356
+ `;
15357
+ if (eventId) eventData += `id: ${eventId}
15358
+ `;
15359
+ eventData += `data: ${JSON.stringify(message)}
15360
+
15361
+ `;
15362
+ controller.enqueue(encoder.encode(eventData));
15363
+ return true;
15364
+ } catch (error) {
15365
+ this.onerror?.(error);
15366
+ return false;
15367
+ }
15368
+ }
15369
+ /**
15370
+ * Handles unsupported requests (`PUT`, `PATCH`, etc.)
15371
+ */
15372
+ handleUnsupportedRequest() {
15373
+ this.onerror?.(/* @__PURE__ */ new Error("Method not allowed."));
15374
+ return Response.json({
15375
+ jsonrpc: "2.0",
15376
+ error: {
15377
+ code: -32e3,
15378
+ message: "Method not allowed."
15379
+ },
15380
+ id: null
15381
+ }, {
15382
+ status: 405,
15383
+ headers: {
15384
+ Allow: "GET, POST, DELETE",
15385
+ "Content-Type": "application/json"
15386
+ }
15387
+ });
15388
+ }
15389
+ /**
15390
+ * Handles `POST` requests containing JSON-RPC messages
15391
+ */
15392
+ async handlePostRequest(req, options) {
15393
+ try {
15394
+ const acceptHeader = req.headers.get("accept");
15395
+ if (!acceptHeader?.includes("application/json") || !acceptHeader.includes("text/event-stream")) {
15396
+ this.onerror?.(/* @__PURE__ */ new Error("Not Acceptable: Client must accept both application/json and text/event-stream"));
15397
+ return this.createJsonErrorResponse(406, -32e3, "Not Acceptable: Client must accept both application/json and text/event-stream");
15398
+ }
15399
+ if (!isJsonContentType(req.headers.get("content-type"))) {
15400
+ this.onerror?.(/* @__PURE__ */ new Error("Unsupported Media Type: Content-Type must be application/json"));
15401
+ return this.createJsonErrorResponse(415, -32e3, "Unsupported Media Type: Content-Type must be application/json");
15402
+ }
15403
+ const request = req;
15404
+ let rawMessage;
15405
+ if (options?.parsedBody === void 0) try {
15406
+ const body = await readRequestBody(req, this._maxRequestBodySize);
15407
+ if (body.tooLarge) {
15408
+ const message = requestBodyTooLargeMessage(this._maxRequestBodySize);
15409
+ this.onerror?.(new Error(message));
15410
+ return this.createJsonErrorResponse(413, -32e3, message);
15411
+ }
15412
+ rawMessage = JSON.parse(body.text);
15413
+ } catch (error) {
15414
+ this.onerror?.(error);
15415
+ return this.createJsonErrorResponse(400, -32700, "Parse error: Invalid JSON");
15416
+ }
15417
+ else rawMessage = options.parsedBody;
15418
+ if (Array.isArray(rawMessage) && rawMessage.length > MAX_BATCH_SIZE) {
15419
+ this.onerror?.(/* @__PURE__ */ new Error(`Invalid Request: Batch must not exceed ${MAX_BATCH_SIZE} messages`));
15420
+ return this.createJsonErrorResponse(400, -32600, `Invalid Request: Batch must not exceed ${MAX_BATCH_SIZE} messages`);
15421
+ }
15422
+ let messages;
15423
+ try {
15424
+ messages = Array.isArray(rawMessage) ? rawMessage.map((msg) => JSONRPCMessageSchema.parse(msg)) : [JSONRPCMessageSchema.parse(rawMessage)];
15425
+ } catch (error) {
15426
+ this.onerror?.(error);
15427
+ return this.createJsonErrorResponse(400, -32700, "Parse error: Invalid JSON-RPC message");
15428
+ }
15429
+ if (this._closed) return this.createJsonErrorResponse(404, -32001, "Session not found");
15430
+ const isInitializationRequest = messages.some((element) => isInitializeRequest(element));
15431
+ if (isInitializationRequest) {
15432
+ if (this._initialized && this.sessionId !== void 0) {
15433
+ this.onerror?.(/* @__PURE__ */ new Error("Invalid Request: Server already initialized"));
15434
+ return this.createJsonErrorResponse(400, -32600, "Invalid Request: Server already initialized");
15435
+ }
15436
+ if (messages.length > 1) {
15437
+ this.onerror?.(/* @__PURE__ */ new Error("Invalid Request: Only one initialization request is allowed"));
15438
+ return this.createJsonErrorResponse(400, -32600, "Invalid Request: Only one initialization request is allowed");
15439
+ }
15440
+ this.sessionId = this.sessionIdGenerator?.();
15441
+ this._initialized = true;
15442
+ if (this.sessionId && this._onsessioninitialized) await Promise.resolve(this._onsessioninitialized(this.sessionId));
15443
+ }
15444
+ if (!isInitializationRequest) {
15445
+ const sessionError = this.validateSession(req);
15446
+ if (sessionError) return sessionError;
15447
+ const protocolError = this.validateProtocolVersion(req);
15448
+ if (protocolError) return protocolError;
15449
+ }
15450
+ if (this._closed) return this.createJsonErrorResponse(404, -32001, "Session not found");
15451
+ let scopeChallengeResponse;
15452
+ try {
15453
+ scopeChallengeResponse = await this._checkScopeChallenge(messages, options?.authInfo);
15454
+ } catch (error) {
15455
+ this.onerror?.(error);
15456
+ return this.createJsonErrorResponse(500, -32603, "Internal server error");
15457
+ }
15458
+ if (scopeChallengeResponse) return scopeChallengeResponse;
15459
+ if (!messages.some((element) => isJSONRPCRequest(element))) {
15460
+ for (const message of messages) this.onmessage?.(message, {
15461
+ authInfo: options?.authInfo,
15462
+ request
15463
+ });
15464
+ return new Response(null, { status: 202 });
15465
+ }
15466
+ const streamId = crypto.randomUUID();
15467
+ const initRequest = messages.find((m) => isInitializeRequest(m));
15468
+ const clientProtocolVersion = initRequest ? initRequest.params.protocolVersion : req.headers.get("mcp-protocol-version") ?? DEFAULT_NEGOTIATED_PROTOCOL_VERSION;
15469
+ if (this._enableJsonResponse) return new Promise((resolve) => {
15470
+ this._streamMapping.set(streamId, {
15471
+ resolveJson: resolve,
15472
+ cleanup: () => {
15473
+ this._streamMapping.delete(streamId);
15474
+ }
15475
+ });
15476
+ for (const message of messages) if (isJSONRPCRequest(message)) this._requestToStreamMapping.set(message.id, streamId);
15477
+ for (const message of messages) this.onmessage?.(message, {
15478
+ authInfo: options?.authInfo,
15479
+ request
15480
+ });
15481
+ });
15482
+ const encoder = new TextEncoder();
15483
+ let streamController;
15484
+ let keepAliveTimer;
15485
+ const readable = new ReadableStream({
15486
+ start: (controller) => {
15487
+ streamController = controller;
15488
+ },
15489
+ cancel: () => {
15490
+ if (keepAliveTimer !== void 0) clearInterval(keepAliveTimer);
15491
+ if (this._streamMapping.get(streamId)?.controller === streamController) this._streamMapping.delete(streamId);
15492
+ }
15493
+ });
15494
+ const headers = {
15495
+ "Content-Type": "text/event-stream",
15496
+ "Cache-Control": "no-cache, no-transform",
15497
+ Connection: "keep-alive",
15498
+ "X-Accel-Buffering": "no"
15499
+ };
15500
+ if (this.sessionId !== void 0) headers["mcp-session-id"] = this.sessionId;
15501
+ for (const message of messages) if (isJSONRPCRequest(message)) {
15502
+ this._streamMapping.set(streamId, {
15503
+ controller: streamController,
15504
+ encoder,
15505
+ cleanup: () => {
15506
+ if (keepAliveTimer !== void 0) clearInterval(keepAliveTimer);
15507
+ this._streamMapping.delete(streamId);
15508
+ try {
15509
+ streamController.close();
15510
+ } catch {
15511
+ }
15512
+ }
15513
+ });
15514
+ this._requestToStreamMapping.set(message.id, streamId);
15515
+ }
15516
+ await this.writePrimingEvent(streamController, encoder, streamId, clientProtocolVersion);
15517
+ for (const message of messages) {
15518
+ let closeSSEStream;
15519
+ let closeStandaloneSSEStream;
15520
+ if (isJSONRPCRequest(message) && this._eventStore && this.supportsEmptySSEData(clientProtocolVersion)) {
15521
+ closeSSEStream = () => {
15522
+ this.closeSSEStream(message.id);
15523
+ };
15524
+ closeStandaloneSSEStream = () => {
15525
+ this.closeStandaloneSSEStream();
15526
+ };
15527
+ }
15528
+ this.onmessage?.(message, {
15529
+ authInfo: options?.authInfo,
15530
+ request,
15531
+ closeSSEStream,
15532
+ closeStandaloneSSEStream
15533
+ });
15534
+ }
15535
+ if (this._streamMapping.get(streamId)?.controller === streamController) keepAliveTimer = this.startKeepAlive(streamController, encoder);
15536
+ return new Response(readable, {
15537
+ status: 200,
15538
+ headers
15539
+ });
15540
+ } catch (error) {
15541
+ this.onerror?.(error);
15542
+ return this.createJsonErrorResponse(400, -32700, "Parse error", { data: String(error) });
15543
+ }
15544
+ }
15545
+ /**
15546
+ * Handles `DELETE` requests to terminate sessions
15547
+ */
15548
+ async handleDeleteRequest(req) {
15549
+ const sessionError = this.validateSession(req);
15550
+ if (sessionError) return sessionError;
15551
+ const protocolError = this.validateProtocolVersion(req);
15552
+ if (protocolError) return protocolError;
15553
+ try {
15554
+ await Promise.resolve(this._onsessionclosed?.(this.sessionId));
15555
+ return new Response(null, { status: 200 });
15556
+ } finally {
15557
+ await this.close();
15558
+ }
15559
+ }
15560
+ /**
15561
+ * Validates session ID for non-initialization requests.
15562
+ * Returns `Response` error if invalid, `undefined` otherwise
15563
+ */
15564
+ validateSession(req) {
15565
+ if (this.sessionIdGenerator === void 0) return;
15566
+ if (!this._initialized) {
15567
+ this.onerror?.(/* @__PURE__ */ new Error("Bad Request: Server not initialized"));
15568
+ return this.createJsonErrorResponse(400, -32e3, "Bad Request: Server not initialized");
15569
+ }
15570
+ const sessionId = req.headers.get("mcp-session-id");
15571
+ if (!sessionId) {
15572
+ this.onerror?.(/* @__PURE__ */ new Error("Bad Request: Mcp-Session-Id header is required"));
15573
+ return this.createJsonErrorResponse(400, -32e3, "Bad Request: Mcp-Session-Id header is required");
15574
+ }
15575
+ if (sessionId !== this.sessionId) {
15576
+ this.onerror?.(/* @__PURE__ */ new Error("Session not found"));
15577
+ return this.createJsonErrorResponse(404, -32001, "Session not found");
15578
+ }
15579
+ }
15580
+ /**
15581
+ * Validates the `MCP-Protocol-Version` header on incoming requests.
15582
+ *
15583
+ * For initialization: Version negotiation handles unknown versions gracefully
15584
+ * (server responds with its supported version).
15585
+ *
15586
+ * For subsequent requests with `MCP-Protocol-Version` header:
15587
+ * - Accept if in supported list
15588
+ * - 400 if unsupported
15589
+ *
15590
+ * For HTTP requests without the `MCP-Protocol-Version` header:
15591
+ * - Accept and default to the version negotiated at initialization
15592
+ */
15593
+ validateProtocolVersion(req) {
15594
+ const protocolVersion = req.headers.get("mcp-protocol-version");
15595
+ if (protocolVersion !== null && !this._supportedProtocolVersions.includes(protocolVersion)) {
15596
+ const error = `Bad Request: Unsupported protocol version: ${protocolVersion} (supported versions: ${this._supportedProtocolVersions.join(", ")})`;
15597
+ this.onerror?.(new Error(error));
15598
+ return this.createJsonErrorResponse(400, -32e3, error);
15599
+ }
15600
+ }
15601
+ async close() {
15602
+ if (this._closed) return;
15603
+ this._closed = true;
15604
+ for (const { cleanup } of this._streamMapping.values()) cleanup();
15605
+ this._streamMapping.clear();
15606
+ this._requestResponseMap.clear();
15607
+ this.onclose?.();
15608
+ }
15609
+ /**
15610
+ * Close an SSE stream for a specific request, triggering client reconnection.
15611
+ * Use this to implement polling behavior during long-running operations -
15612
+ * client will reconnect after the retry interval specified in the priming event.
15613
+ */
15614
+ closeSSEStream(requestId) {
15615
+ const streamId = this._requestToStreamMapping.get(requestId);
15616
+ if (!streamId) return;
15617
+ const stream = this._streamMapping.get(streamId);
15618
+ if (stream) stream.cleanup();
15619
+ }
15620
+ /**
15621
+ * Close the standalone `GET` SSE stream, triggering client reconnection.
15622
+ * Use this to implement polling behavior for server-initiated notifications.
15623
+ */
15624
+ closeStandaloneSSEStream() {
15625
+ const stream = this._streamMapping.get(this._standaloneSseStreamId);
15626
+ if (stream) stream.cleanup();
15627
+ }
15628
+ async send(message, options) {
15629
+ let requestId = options?.relatedRequestId;
15630
+ if (isJSONRPCResultResponse(message) || isJSONRPCErrorResponse(message)) requestId = message.id;
15631
+ if (requestId === void 0) {
15632
+ if (isJSONRPCResultResponse(message) || isJSONRPCErrorResponse(message)) throw new Error("Cannot send a response on a standalone SSE stream unless resuming a previous client request");
15633
+ let eventId;
15634
+ if (this._eventStore) eventId = await this._eventStore.storeEvent(this._standaloneSseStreamId, message);
15635
+ const standaloneSse = this._streamMapping.get(this._standaloneSseStreamId);
15636
+ if (standaloneSse === void 0) return;
15637
+ if (standaloneSse.controller && standaloneSse.encoder && (eventId === void 0 || !standaloneSse.replayedEventIds?.has(eventId))) this.writeSSEEvent(standaloneSse.controller, standaloneSse.encoder, message, eventId);
15638
+ return;
15639
+ }
15640
+ const streamId = this._requestToStreamMapping.get(requestId);
15641
+ if (!streamId) throw new Error(`No connection established for request ID: ${String(requestId)}`);
15642
+ let stream = this._streamMapping.get(streamId);
15643
+ if (!this._enableJsonResponse) {
15644
+ let eventId;
15645
+ if (this._eventStore) {
15646
+ eventId = await this._eventStore.storeEvent(streamId, message);
15647
+ stream = this._streamMapping.get(streamId);
15648
+ }
15649
+ if (stream?.controller && stream?.encoder && (eventId === void 0 || !stream.replayedEventIds?.has(eventId))) this.writeSSEEvent(stream.controller, stream.encoder, message, eventId);
15650
+ }
15651
+ if (isJSONRPCResultResponse(message) || isJSONRPCErrorResponse(message)) {
15652
+ this._requestResponseMap.set(requestId, message);
15653
+ const relatedIds = [...this._requestToStreamMapping.entries()].filter(([_, sid]) => sid === streamId).map(([id]) => id);
15654
+ if (relatedIds.every((id) => this._requestResponseMap.has(id))) {
15655
+ if (!stream) {
15656
+ if (this._enableJsonResponse) throw new Error(`No connection established for request ID: ${String(requestId)}`);
15657
+ if (!this._eventStore) {
15658
+ this.onerror?.(/* @__PURE__ */ new Error(`Response for request ID ${String(requestId)} is undeliverable: per-request stream is disconnected and no eventStore is configured`));
15659
+ for (const id of relatedIds) {
15660
+ this._requestResponseMap.delete(id);
15661
+ this._requestToStreamMapping.delete(id);
15662
+ }
15663
+ return;
15664
+ }
15665
+ for (const id of relatedIds) {
15666
+ this._requestResponseMap.delete(id);
15667
+ this._requestToStreamMapping.delete(id);
15668
+ }
15669
+ return;
15670
+ }
15671
+ if (this._enableJsonResponse && stream.resolveJson) {
15672
+ const headers = { "Content-Type": "application/json" };
15673
+ if (this.sessionId !== void 0) headers["mcp-session-id"] = this.sessionId;
15674
+ const responses = relatedIds.map((id) => this._requestResponseMap.get(id));
15675
+ if (responses.length === 1) stream.resolveJson(Response.json(responses[0], {
15676
+ status: 200,
15677
+ headers
15678
+ }));
15679
+ else stream.resolveJson(Response.json(responses, {
15680
+ status: 200,
15681
+ headers
15682
+ }));
15683
+ stream.cleanup();
15684
+ } else stream.cleanup();
15685
+ for (const id of relatedIds) {
15686
+ this._requestResponseMap.delete(id);
15687
+ this._requestToStreamMapping.delete(id);
15688
+ }
15689
+ }
15690
+ }
15691
+ }
15692
+ };
15693
+ function echoableRequestId(body) {
15694
+ if (body === null || typeof body !== "object" || Array.isArray(body)) return null;
15695
+ const { method, id } = body;
15696
+ if (typeof method !== "string") return null;
15697
+ return typeof id === "string" || typeof id === "number" ? id : null;
15698
+ }
15699
+ function jsonRpcErrorResponse(httpStatus, code, message, data, id = null) {
15700
+ return Response.json({
15701
+ jsonrpc: "2.0",
15702
+ error: {
15703
+ code,
15704
+ message,
15705
+ ...data !== void 0 && { data }
15706
+ },
15707
+ id
15708
+ }, { status: httpStatus });
15709
+ }
15710
+ function rejectionResponse(rejection2, id = null) {
15711
+ return jsonRpcErrorResponse(rejection2.httpStatus, rejection2.code, rejection2.message, rejection2.data, id);
15712
+ }
15713
+ function toError(value) {
15714
+ return value instanceof Error ? value : new Error(String(value));
15715
+ }
15716
+ function internalServerErrorResponse(id = null) {
15717
+ return jsonRpcErrorResponse(500, -32603, "Internal server error", void 0, id);
15718
+ }
15719
+ function assertNotServing(server) {
15720
+ if (server.transport !== void 0) throw new Error("The factory returned an instance that is still serving another request. Return a new instance for each request.");
15721
+ }
15722
+ function createLegacyStatelessFallback(factory, onerror, keepAliveMs, maxRequestBodySize) {
15723
+ return async (request, options) => {
15724
+ if (request.method.toUpperCase() !== "POST") return jsonRpcErrorResponse(405, -32e3, "Method not allowed.");
15725
+ try {
15726
+ const product = await factory({
15727
+ era: "legacy",
15728
+ ...options?.authInfo !== void 0 && { authInfo: options.authInfo },
15729
+ requestInfo: request
15730
+ });
15731
+ const transport = new WebStandardStreamableHTTPServerTransport({
15732
+ sessionIdGenerator: void 0,
15733
+ ...keepAliveMs !== void 0 && { keepAliveMs },
15734
+ ...maxRequestBodySize !== void 0 && { maxRequestBodySize }
15735
+ });
15736
+ await product.connect(transport);
15737
+ const teardown = () => {
15738
+ transport.close().catch(() => {
15739
+ });
15740
+ product.close().catch(() => {
15741
+ });
15742
+ };
15743
+ request.signal?.addEventListener("abort", teardown, { once: true });
15744
+ const response = await transport.handleRequest(request, {
15745
+ ...options?.authInfo !== void 0 && { authInfo: options.authInfo },
15746
+ ...options?.parsedBody !== void 0 && { parsedBody: options.parsedBody }
15747
+ });
15748
+ if (response.body === null || mediaTypeEssence(response.headers.get("content-type")) !== "text/event-stream") {
15749
+ teardown();
15750
+ return response;
15751
+ }
15752
+ const reader = response.body.getReader();
15753
+ let toreDown = false;
15754
+ const completeExchange = () => {
15755
+ if (!toreDown) {
15756
+ toreDown = true;
15757
+ teardown();
15758
+ }
15759
+ };
15760
+ const monitoredBody = new ReadableStream({
15761
+ pull: async (controller) => {
15762
+ try {
15763
+ const { done, value } = await reader.read();
15764
+ if (done) {
15765
+ completeExchange();
15766
+ controller.close();
15767
+ return;
15768
+ }
15769
+ if (value !== void 0) controller.enqueue(value);
15770
+ } catch (error) {
15771
+ completeExchange();
15772
+ controller.error(error);
15773
+ }
15774
+ },
15775
+ cancel: (reason) => {
15776
+ completeExchange();
15777
+ return reader.cancel(reason).catch(() => {
15778
+ });
15779
+ }
15780
+ });
15781
+ return new Response(monitoredBody, {
15782
+ status: response.status,
15783
+ statusText: response.statusText,
15784
+ headers: response.headers
15785
+ });
15786
+ } catch (error) {
15787
+ try {
15788
+ onerror?.(toError(error));
15789
+ } catch {
15790
+ }
15791
+ return internalServerErrorResponse(echoableRequestId(options?.parsedBody));
15792
+ }
15793
+ };
15794
+ }
15795
+ function standardHeadersOf(request) {
15796
+ return {
15797
+ protocolVersionHeader: request.headers.get("mcp-protocol-version") ?? void 0,
15798
+ mcpMethodHeader: request.headers.get("mcp-method") ?? void 0,
15799
+ mcpNameHeader: request.headers.get("mcp-name") ?? void 0
15800
+ };
15801
+ }
15802
+ async function classifyEntryRequest(request, providedParsedBody, needsForward = true, maxRequestBodySize = DEFAULT_MAX_REQUEST_BODY_SIZE) {
15803
+ const httpMethod = request.method.toUpperCase();
15804
+ let body;
15805
+ let parsedBody = providedParsedBody;
15806
+ let forwardRequest = request;
15807
+ let unparseable = false;
15808
+ if (httpMethod === "POST") {
15809
+ if (parsedBody === void 0) {
15810
+ if (needsForward) forwardRequest = request.clone();
15811
+ let bodyText;
15812
+ try {
15813
+ const read = await readRequestBody(request, maxRequestBodySize);
15814
+ if (read.tooLarge) return { step: "body-too-large" };
15815
+ bodyText = read.text;
15816
+ } catch {
15817
+ return { step: "unreadable-body" };
15818
+ }
15819
+ try {
15820
+ body = bodyText.length === 0 ? void 0 : JSON.parse(bodyText);
15821
+ } catch {
15822
+ unparseable = true;
15823
+ }
15824
+ if (!unparseable && body !== void 0) parsedBody = body;
15825
+ } else body = parsedBody;
15826
+ if (unparseable || body === void 0) return {
15827
+ step: "no-json-body",
15828
+ forwardRequest
15829
+ };
15830
+ }
15831
+ return {
15832
+ step: "classified",
15833
+ outcome: classifyInboundRequest({
15834
+ httpMethod,
15835
+ ...standardHeadersOf(request),
15836
+ ...body !== void 0 && { body }
15837
+ }),
15838
+ body,
15839
+ parsedBody,
15840
+ forwardRequest
15841
+ };
15842
+ }
15843
+ async function isLegacyRequest(request, parsedBody, options) {
15844
+ const maxRequestBodySize = resolveMaxRequestBodySize(options?.maxRequestBodySize);
15845
+ const classified = await classifyEntryRequest(parsedBody === void 0 && request.method.toUpperCase() === "POST" ? request.clone() : request, parsedBody, false, maxRequestBodySize);
15846
+ return classified.step === "no-json-body" || classified.step === "classified" && classified.outcome.kind === "legacy";
15847
+ }
15848
+ function createMcpHandler(factory, options = {}) {
15849
+ const { legacy, onerror, responseMode } = options;
15850
+ const maxRequestBodySize = resolveMaxRequestBodySize(options.maxRequestBodySize);
15851
+ if (typeof legacy === "function") throw new TypeError("The 'legacy' option only accepts 'stateless' or 'reject', not a handler function. To serve 2025-era traffic with your own handler, route in user land with the exported isLegacyRequest(request) predicate in front of a strict (legacy: 'reject') handler.");
15852
+ const inflight = /* @__PURE__ */ new Set();
15853
+ let closed = false;
15854
+ const reportError = (error) => {
15855
+ try {
15856
+ onerror?.(error);
15857
+ } catch {
15858
+ }
15859
+ };
15860
+ const bus = options.bus ?? new InMemoryServerEventBus(reportError);
15861
+ const notify = createServerNotifier(bus);
15862
+ const listenRouter = createListenRouter({
15863
+ bus,
15864
+ maxSubscriptions: options.maxSubscriptions ?? DEFAULT_MAX_SUBSCRIPTIONS,
15865
+ keepAliveMs: options.keepAliveMs ?? DEFAULT_SSE_KEEP_ALIVE_MS,
15866
+ onerror: reportError
15867
+ });
15868
+ if (responseMode === "json") console.warn("responseMode: 'json' drops mid-call notifications. subscriptions/listen streams are always served over SSE regardless; other notifications emitted before a result are dropped.");
15869
+ const legacyHandler = legacy === "reject" ? void 0 : createLegacyStatelessFallback(factory, reportError, options.keepAliveMs, maxRequestBodySize);
15870
+ async function serveModern(route, request, authInfo) {
15871
+ const claimedRevision = route.classification.revision;
15872
+ if (claimedRevision === void 0 || !SUPPORTED_MODERN_PROTOCOL_VERSIONS.includes(claimedRevision)) {
15873
+ const error = new UnsupportedProtocolVersionError({
15874
+ supported: [...SUPPORTED_MODERN_PROTOCOL_VERSIONS],
15875
+ requested: claimedRevision ?? "unknown"
15876
+ });
15877
+ reportError(error);
15878
+ return jsonRpcErrorResponse(400, error.code, error.message, error.data, echoableRequestId(route.message));
15879
+ }
15880
+ const stdHeaderRejection = validateStandardRequestHeaders({
15881
+ httpMethod: request.method,
15882
+ ...standardHeadersOf(request)
15883
+ }, route);
15884
+ if (stdHeaderRejection !== void 0) {
15885
+ reportError(/* @__PURE__ */ new Error(`Rejected inbound request (${stdHeaderRejection.cell}): ${stdHeaderRejection.message}`));
15886
+ return rejectionResponse(stdHeaderRejection, echoableRequestId(route.message));
15887
+ }
15888
+ const meta = route.messageKind === "request" ? requestMetaOf(route.message.params) : void 0;
15889
+ const declaredClientCapabilities = meta?.[CLIENT_CAPABILITIES_META_KEY];
15890
+ if (route.messageKind === "request") {
15891
+ const required = requiredClientCapabilitiesForRequest(route.message.method);
15892
+ if (required !== void 0) {
15893
+ const missing = missingClientCapabilities(required, declaredClientCapabilities);
15894
+ if (missing !== void 0) {
15895
+ const error = new MissingRequiredClientCapabilityError({ requiredCapabilities: missing });
15896
+ reportError(error);
15897
+ return jsonRpcErrorResponse(httpStatusForErrorCode(error.code, "ladder"), error.code, error.message, error.data, route.message.id);
15898
+ }
15899
+ }
15900
+ }
15901
+ const product = await factory({
15902
+ era: "modern",
15903
+ ...authInfo !== void 0 && { authInfo },
15904
+ requestInfo: request
15905
+ });
15906
+ const server = product instanceof McpServer ? product.server : product;
15907
+ assertNotServing(server);
15908
+ if (route.messageKind === "request" && route.message.method === "subscriptions/listen") {
15909
+ const capabilities = server.getCapabilities();
15910
+ const serverInfo = serverIdentityOf(server);
15911
+ product.close().catch(reportError);
15912
+ return listenRouter.serve(route.message, request.signal, capabilities, serverInfo);
15913
+ }
15914
+ if (route.messageKind === "request" && route.message.method === "tools/call" && product instanceof McpServer) {
15915
+ const callParams = route.message.params;
15916
+ const toolName = typeof callParams?.name === "string" ? callParams.name : void 0;
15917
+ const inputSchema = toolName === void 0 ? void 0 : product.toolInputSchemaJson(toolName);
15918
+ if (inputSchema !== void 0) {
15919
+ const scan = scanXMcpHeaderDeclarations(inputSchema);
15920
+ if (scan.valid && scan.declarations.length > 0) {
15921
+ const rejection2 = validateMcpParamHeaders(scan.declarations, callParams?.arguments, request.headers);
15922
+ if (rejection2 !== void 0) {
15923
+ product.close().catch(reportError);
15924
+ reportError(/* @__PURE__ */ new Error(`Rejected inbound request (${rejection2.cell}): ${rejection2.message}`));
15925
+ return rejectionResponse(rejection2, route.message.id);
15926
+ }
15927
+ }
15928
+ }
15929
+ }
15930
+ if (route.messageKind === "request" && product instanceof McpServer) try {
15931
+ const challenge = await findScopeChallenge([route.message], authInfo, (context) => product.resolveScopeChallenge(context));
15932
+ if (challenge !== void 0) {
15933
+ product.close().catch(reportError);
15934
+ return createScopeChallengeResponse(challenge, scopeChallengeResourceMetadataUrl(authInfo));
15935
+ }
15936
+ } catch (error) {
15937
+ product.close().catch(reportError);
15938
+ reportError(toError(error));
15939
+ return internalServerErrorResponse(route.message.id);
15940
+ }
15941
+ assertNotServing(server);
15942
+ setNegotiatedProtocolVersion(server, claimedRevision);
15943
+ installModernOnlyHandlers(server, SUPPORTED_MODERN_PROTOCOL_VERSIONS);
15944
+ if (meta !== void 0) seedClientIdentityFromEnvelope(server, {
15945
+ clientInfo: meta[CLIENT_INFO_META_KEY],
15946
+ clientCapabilities: declaredClientCapabilities
15947
+ });
15948
+ const previousOnClose = server.onclose;
15949
+ inflight.add(server);
15950
+ const onExchangeClose = () => {
15951
+ inflight.delete(server);
15952
+ if (server.onclose === onExchangeClose) server.onclose = previousOnClose;
15953
+ previousOnClose?.();
15954
+ };
15955
+ server.onclose = onExchangeClose;
15956
+ try {
15957
+ const response = await invoke(product, route.message, {
15958
+ classification: route.classification,
15959
+ request,
15960
+ ...authInfo !== void 0 && { authInfo },
15961
+ ...responseMode !== void 0 && { responseMode },
15962
+ ...options.keepAliveMs !== void 0 && { keepAliveMs: options.keepAliveMs }
15963
+ });
15964
+ if (route.messageKind === "notification") queueMicrotask(() => void server.close().catch(() => {
15965
+ }));
15966
+ return response;
15967
+ } catch (error) {
15968
+ if (error instanceof SdkError && error.code === SdkErrorCode.ConnectionClosed) return new Response(null, { status: 499 });
15969
+ await server.close().catch(() => {
15970
+ });
15971
+ inflight.delete(server);
15972
+ reportError(toError(error));
15973
+ return internalServerErrorResponse(echoableRequestId(route.message));
15974
+ }
15975
+ }
15976
+ async function serveLegacyRoute(route, forwardRequest, authInfo, parsedBody) {
15977
+ if (legacyHandler !== void 0) return legacyHandler(forwardRequest, {
15978
+ ...authInfo !== void 0 && { authInfo },
15979
+ ...parsedBody !== void 0 && { parsedBody }
15980
+ });
15981
+ const strict = modernOnlyStrictRejection(route, SUPPORTED_MODERN_PROTOCOL_VERSIONS);
15982
+ if (strict === void 0) return new Response(null, { status: 202 });
15983
+ reportError(/* @__PURE__ */ new Error(`Rejected 2025-era request on a modern-only endpoint (${strict.cell}): ${strict.message}`));
15984
+ return rejectionResponse(strict, echoableRequestId(parsedBody));
15985
+ }
15986
+ async function handle(request, requestOptions) {
15987
+ const authInfo = requestOptions?.authInfo;
15988
+ if (request.method.toUpperCase() === "POST" && !isJsonContentType(request.headers.get("content-type"))) {
15989
+ reportError(/* @__PURE__ */ new Error("Unsupported Media Type: Content-Type must be application/json"));
15990
+ return jsonRpcErrorResponse(415, -32e3, "Unsupported Media Type: Content-Type must be application/json");
15991
+ }
15992
+ const classified = await classifyEntryRequest(request, requestOptions?.parsedBody, true, maxRequestBodySize);
15993
+ if (classified.step === "unreadable-body") return jsonRpcErrorResponse(400, -32700, "Parse error: the request body could not be read");
15994
+ if (classified.step === "body-too-large") return jsonRpcErrorResponse(413, -32e3, requestBodyTooLargeMessage(maxRequestBodySize));
15995
+ if (classified.step === "no-json-body") {
15996
+ if (legacyHandler !== void 0) return legacyHandler(classified.forwardRequest, { ...authInfo !== void 0 && { authInfo } });
15997
+ return jsonRpcErrorResponse(400, -32700, "Parse error: the request body is not valid JSON");
15998
+ }
15999
+ const { outcome, body, parsedBody, forwardRequest } = classified;
16000
+ try {
16001
+ switch (outcome.kind) {
16002
+ case "reject":
16003
+ reportError(/* @__PURE__ */ new Error(`Rejected inbound request (${outcome.cell}): ${outcome.message}`));
16004
+ return rejectionResponse(outcome, echoableRequestId(body));
16005
+ case "modern":
16006
+ return await serveModern(outcome, request, authInfo);
16007
+ case "legacy":
16008
+ return await serveLegacyRoute(outcome, forwardRequest, authInfo, parsedBody);
16009
+ }
16010
+ } catch (error) {
16011
+ reportError(toError(error));
16012
+ return internalServerErrorResponse(echoableRequestId(body));
16013
+ }
16014
+ }
16015
+ const fetchFace = async (request, requestOptions) => {
16016
+ if (closed) throw new Error("This MCP handler has been closed");
16017
+ try {
16018
+ return await handle(request, requestOptions);
16019
+ } catch (error) {
16020
+ reportError(toError(error));
16021
+ return internalServerErrorResponse(echoableRequestId(requestOptions?.parsedBody));
16022
+ }
16023
+ };
16024
+ return {
16025
+ fetch: fetchFace,
16026
+ notify,
16027
+ bus,
16028
+ close: async () => {
16029
+ closed = true;
16030
+ listenRouter.closeAll();
16031
+ const closing = [...inflight].map((server) => server.close().catch(() => {
16032
+ }));
16033
+ inflight.clear();
16034
+ await Promise.all(closing);
16035
+ }
16036
+ };
16037
+ }
16038
+
16039
+ // src/server.ts
16040
+ import { readFileSync } from "fs";
16041
+ import { dirname, join } from "path";
16042
+ import { fileURLToPath } from "url";
16043
+
16044
+ // src/lib/onboarding-copy.ts
16045
+ var ONBOARDING_OFFER_CONTROL_VARIANT = "control";
16046
+ var ONBOARDING_OFFER_COPY = {
16047
+ // #4089 review: this cohort has already completed OAuth to connect - they
16048
+ // have an account. Never claim "no account needed" or otherwise imply an
16049
+ // anonymous/no-sign-up lane here; that framing contradicts what they just
16050
+ // did and reads as broken. Acknowledge the connection instead, then offer
16051
+ // to build. (The anonymous/no-auth entry point is a different surface,
16052
+ // #4088 - not this cohort.)
16053
+ [ONBOARDING_OFFER_CONTROL_VARIANT]: "When this is the start of a new conversation and the user has not yet said what they want to build, welcome them as someone who is now connected to Clipform and offer to build their first form together, then offer these starting points as plain text: lead-gen, feedback, quiz, or something else - ask which one fits, or invite them to describe what they need instead. If the user's first message already states what they want to build, even loosely, skip that offer entirely and go straight into the brief-driven flow below."
16054
+ };
16055
+ function buildOnboardingOffer(variant = ONBOARDING_OFFER_CONTROL_VARIANT) {
16056
+ return ONBOARDING_OFFER_COPY[variant] ?? ONBOARDING_OFFER_COPY[ONBOARDING_OFFER_CONTROL_VARIANT];
14113
16057
  }
14114
16058
  var ONBOARDING_OFFER_TOOL_CUE = "If the user hasn't said what to build yet, offer to build their first form and suggest starting points: lead-gen, feedback, quiz, or something else.";
14115
16059
 
@@ -17907,7 +19851,7 @@ Quiz variants (optional): ${QUIZ_VARIANTS.join(", ")} - returns the variant-spec
17907
19851
  Optional args by type:
17908
19852
  - quiz: topic, question_count (8)
17909
19853
  - quiz (variant: personality): topic, categories, question_count (8)
17910
- - quiz (variant: comprehension): youtube_url, question_count (8), audience
19854
+ - quiz (variant: comprehension): question_count (8), audience
17911
19855
  - interview: purpose, response_format (all), needs_consent (true)
17912
19856
  - survey: topic, anonymous (true)
17913
19857
  - funnel: outcomes, criteria, needs_contact (true)
@@ -17922,7 +19866,6 @@ Optional args by type:
17922
19866
  topic: external_exports.string().optional().describe("Topic or theme (quiz, survey)"),
17923
19867
  question_count: external_exports.number().optional().describe("Number of questions, default 8 (quiz types)"),
17924
19868
  categories: external_exports.string().optional().describe("Comma-separated outcome categories (quiz variant: personality)"),
17925
- youtube_url: external_exports.string().optional().describe("YouTube video URL (quiz variant: comprehension)"),
17926
19869
  audience: external_exports.string().optional().describe("Target audience (quiz variant: comprehension)"),
17927
19870
  purpose: external_exports.string().optional().describe("What you're collecting (interview)"),
17928
19871
  response_format: external_exports.enum(["video", "audio", "text", "all"]).optional().describe("How respondents reply (interview)"),
@@ -17960,10 +19903,19 @@ Optional args by type:
17960
19903
  var __dirname = dirname(fileURLToPath(import.meta.url));
17961
19904
  var pkg = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8"));
17962
19905
  var MCP_VERSION = pkg.version;
19906
+ var serverJson = JSON.parse(readFileSync(join(__dirname, "..", "server.json"), "utf-8"));
17963
19907
  var MCP_SERVER_IDENTITY = Object.freeze({
17964
- name: "clipform-mcp-server",
19908
+ name: serverJson.name,
17965
19909
  version: MCP_VERSION
17966
19910
  });
19911
+ var MCP_SERVER_CARD_IDENTITY = Object.freeze({
19912
+ name: serverJson.name,
19913
+ title: serverJson.title,
19914
+ description: serverJson.description,
19915
+ websiteUrl: serverJson.websiteUrl,
19916
+ icons: serverJson.icons,
19917
+ repository: serverJson.repository
19918
+ });
17967
19919
  function buildInstructions(caps) {
17968
19920
  return [
17969
19921
  buildOnboardingOffer(),
@@ -18097,6 +20049,12 @@ function createServer(options) {
18097
20049
  };
18098
20050
  });
18099
20051
  }
20052
+ const declared = rawServer.getCapabilities();
20053
+ const noListChanged = {};
20054
+ for (const key of ["tools", "prompts", "resources"]) {
20055
+ if (declared[key]) noListChanged[key] = { listChanged: false };
20056
+ }
20057
+ rawServer.registerCapabilities(noListChanged);
18100
20058
  if (baseCallToolHandler) {
18101
20059
  nativeSetRequestHandler("tools/call", async (request, extra) => {
18102
20060
  try {
@@ -18118,11 +20076,15 @@ function createServer(options) {
18118
20076
  }
18119
20077
 
18120
20078
  export {
20079
+ SUPPORTED_PROTOCOL_VERSIONS,
18121
20080
  ReadBuffer,
18122
20081
  serializeMessage,
18123
20082
  process2 as process,
20083
+ isLegacyRequest,
20084
+ createMcpHandler,
18124
20085
  RENDER_TIMING,
18125
20086
  MCP_SERVER_IDENTITY,
20087
+ MCP_SERVER_CARD_IDENTITY,
18126
20088
  buildInstructions,
18127
20089
  createServer
18128
20090
  };
@@ -18135,4 +20097,4 @@ export {
18135
20097
  * MIT Licensed
18136
20098
  *)
18137
20099
  */
18138
- //# sourceMappingURL=chunk-MSLK3FRO.js.map
20100
+ //# sourceMappingURL=chunk-YKJV5LZQ.js.map