@forklaunch/core 0.9.22 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/http/index.js CHANGED
@@ -45,6 +45,7 @@ __export(http_exports, {
45
45
  discriminateResponseBodies: () => discriminateResponseBodies,
46
46
  enrichExpressLikeSend: () => enrichExpressLikeSend,
47
47
  evaluateTelemetryOptions: () => evaluateTelemetryOptions,
48
+ generateMcpServer: () => generateMcpServer,
48
49
  generateSwaggerDocument: () => generateSwaggerDocument,
49
50
  get: () => get,
50
51
  getCodeForStatus: () => getCodeForStatus,
@@ -90,6 +91,9 @@ function cors(corsOptions) {
90
91
  };
91
92
  }
92
93
 
94
+ // src/http/router/expressLikeRouter.ts
95
+ var import_common5 = require("@forklaunch/common");
96
+
93
97
  // src/http/guards/isForklaunchRouter.ts
94
98
  function isForklaunchRouter(maybeForklaunchRouter) {
95
99
  return maybeForklaunchRouter != null && typeof maybeForklaunchRouter === "object" && "basePath" in maybeForklaunchRouter && "routes" in maybeForklaunchRouter && Array.isArray(maybeForklaunchRouter.routes);
@@ -108,7 +112,7 @@ function isExpressLikeSchemaHandler(middleware2) {
108
112
  (argumentName) => argumentName.toLowerCase()
109
113
  )
110
114
  );
111
- return extractedArgumentNames && extractedArgumentNames.size <= 3;
115
+ return extractedArgumentNames && extractedArgumentNames.size <= 4;
112
116
  }
113
117
 
114
118
  // src/http/guards/isForklaunchExpressLikeRouter.ts
@@ -681,6 +685,12 @@ function discriminateBody(schemaValidator, body) {
681
685
  parserType: "text",
682
686
  schema: maybeTypedBody
683
687
  };
688
+ } else if (schemaValidator.openapi(maybeTypedBody).format === "binary") {
689
+ return {
690
+ contentType: "application/octet-stream",
691
+ parserType: "file",
692
+ schema: maybeTypedBody
693
+ };
684
694
  } else {
685
695
  return {
686
696
  contentType: "application/json",
@@ -732,6 +742,12 @@ function discriminateResponseBodies(schemaValidator, responses) {
732
742
  parserType: "text",
733
743
  schema: response
734
744
  };
745
+ } else if (schemaValidator.openapi(response).format === "binary") {
746
+ discriminatedResponses[Number(statusCode)] = {
747
+ contentType: "application/octet-stream",
748
+ parserType: "file",
749
+ schema: response
750
+ };
735
751
  } else {
736
752
  discriminatedResponses[Number(statusCode)] = {
737
753
  contentType: "application/json",
@@ -751,37 +767,38 @@ function discriminateResponseBodies(schemaValidator, responses) {
751
767
  }
752
768
 
753
769
  // src/http/router/expressLikeRouter.ts
754
- var ForklaunchExpressLikeRouter = class {
755
- constructor(basePath, schemaValidator, internal, postEnrichMiddleware, openTelemetryCollector) {
770
+ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
771
+ constructor(basePath, schemaValidator, internal, postEnrichMiddleware, openTelemetryCollector, sdkName) {
772
+ this.basePath = basePath;
756
773
  this.schemaValidator = schemaValidator;
757
774
  this.internal = internal;
758
775
  this.postEnrichMiddleware = postEnrichMiddleware;
759
776
  this.openTelemetryCollector = openTelemetryCollector;
760
- this.basePath = basePath;
761
- process.on("uncaughtException", (err) => {
762
- this.openTelemetryCollector.log("error", `Uncaught exception: ${err}`);
763
- process.exit(1);
764
- });
765
- process.on("unhandledRejection", (reason) => {
766
- this.openTelemetryCollector.log(
767
- "error",
768
- `Unhandled rejection: ${reason}`
769
- );
770
- process.exit(1);
771
- });
772
- process.on("exit", () => {
773
- this.openTelemetryCollector.log("info", "Shutting down application");
774
- });
775
- process.on("SIGINT", () => {
776
- this.openTelemetryCollector.log("info", "Shutting down application");
777
- process.exit(0);
778
- });
777
+ this.sdkName = sdkName;
778
+ if (process.env.NODE_ENV !== "test" && !process.env.VITEST) {
779
+ process.on("uncaughtException", (err) => {
780
+ this.openTelemetryCollector.error(`Uncaught exception: ${err}`);
781
+ process.exit(1);
782
+ });
783
+ process.on("unhandledRejection", (reason) => {
784
+ this.openTelemetryCollector.error(`Unhandled rejection: ${reason}`);
785
+ process.exit(1);
786
+ });
787
+ process.on("exit", () => {
788
+ this.openTelemetryCollector.info("Shutting down application");
789
+ });
790
+ process.on("SIGINT", () => {
791
+ this.openTelemetryCollector.info("Shutting down application");
792
+ process.exit(0);
793
+ });
794
+ }
779
795
  this.internal.use(createContext(this.schemaValidator));
780
796
  }
781
797
  requestHandler;
782
798
  routers = [];
783
799
  routes = [];
784
- basePath;
800
+ fetchMap = {};
801
+ sdk = {};
785
802
  /**
786
803
  * Resolves middlewares based on the contract details.
787
804
  *
@@ -900,6 +917,20 @@ var ForklaunchExpressLikeRouter = class {
900
917
  responseSchemas
901
918
  };
902
919
  }
920
+ /**
921
+ * Fetches a route from the route map and executes it with the given parameters.
922
+ *
923
+ * @template Path - The path type that extends keyof fetchMap and string.
924
+ * @param {Path} path - The route path
925
+ * @param {Parameters<fetchMap[Path]>[1]} [requestInit] - Optional request initialization parameters.
926
+ * @returns {Promise<ReturnType<fetchMap[Path]>>} - The result of executing the route handler.
927
+ */
928
+ fetch = async (path, ...reqInit) => {
929
+ return this.fetchMap[path](
930
+ path,
931
+ reqInit[0]
932
+ );
933
+ };
903
934
  /**
904
935
  * Executes request locally, applying parameters
905
936
  *
@@ -919,24 +950,6 @@ var ForklaunchExpressLikeRouter = class {
919
950
  body: discriminateBody(this.schemaValidator, request?.body)?.schema ?? {},
920
951
  path: route
921
952
  };
922
- function remapFileBody(body) {
923
- if (body instanceof File) {
924
- return (name, contentType) => {
925
- return new File([body], name, { type: contentType });
926
- };
927
- }
928
- Object.entries(body).forEach(([key, value]) => {
929
- if (value instanceof File) {
930
- body[key] = (name, contentType) => {
931
- return new File([value], name, { type: contentType });
932
- };
933
- } else if (typeof value === "object") {
934
- body[key] = remapFileBody(value);
935
- }
936
- });
937
- return body;
938
- }
939
- req.body = remapFileBody(req.body);
940
953
  const res = {
941
954
  status: (code) => {
942
955
  statusCode = code;
@@ -990,24 +1003,20 @@ var ForklaunchExpressLikeRouter = class {
990
1003
  registerRoute(method, path, registrationMethod, contractDetailsOrMiddlewareOrTypedHandler, ...middlewareOrMiddlewareAndTypedHandler) {
991
1004
  if (isTypedHandler(contractDetailsOrMiddlewareOrTypedHandler)) {
992
1005
  const { contractDetails, handlers } = contractDetailsOrMiddlewareOrTypedHandler;
993
- return this.registerRoute(
994
- method,
995
- path,
996
- registrationMethod,
997
- contractDetails,
998
- ...handlers
999
- );
1006
+ this.registerRoute(method, path, registrationMethod, contractDetails, ...handlers);
1007
+ return this;
1000
1008
  } else {
1001
1009
  const maybeTypedHandler = middlewareOrMiddlewareAndTypedHandler[middlewareOrMiddlewareAndTypedHandler.length - 1];
1002
1010
  if (isTypedHandler(maybeTypedHandler)) {
1003
1011
  const { contractDetails, handlers } = maybeTypedHandler;
1004
- return this.registerRoute(
1012
+ this.registerRoute(
1005
1013
  method,
1006
1014
  path,
1007
1015
  registrationMethod,
1008
1016
  contractDetails,
1009
1017
  ...middlewareOrMiddlewareAndTypedHandler.concat(handlers)
1010
1018
  );
1019
+ return this;
1011
1020
  } else {
1012
1021
  if (isExpressLikeSchemaHandler(contractDetailsOrMiddlewareOrTypedHandler) || isTypedHandler(contractDetailsOrMiddlewareOrTypedHandler)) {
1013
1022
  throw new Error("Contract details are not defined");
@@ -1039,10 +1048,13 @@ var ForklaunchExpressLikeRouter = class {
1039
1048
  ).concat(handlers),
1040
1049
  this.#parseAndRunControllerHandler(controllerHandler)
1041
1050
  );
1042
- return this.#localParamRequest(
1051
+ const localParamRequest = this.#localParamRequest(
1043
1052
  handlers,
1044
1053
  controllerHandler
1045
1054
  );
1055
+ (0, import_common5.toRecord)(this.fetchMap)[(0, import_common5.sanitizePathSlashes)(`${this.basePath}${path}`)] = localParamRequest;
1056
+ (0, import_common5.toRecord)(this.sdk)[(0, import_common5.toPrettyCamelCase)(contractDetails.name ?? this.basePath)] = (req) => localParamRequest(`${this.basePath}${path}`, req);
1057
+ return this;
1046
1058
  }
1047
1059
  }
1048
1060
  }
@@ -1066,10 +1078,13 @@ var ForklaunchExpressLikeRouter = class {
1066
1078
  return this.#extractHandlers(handlers);
1067
1079
  }
1068
1080
  #extractNestableMiddlewareFromEnrichedTypedHandlerArray(handlers) {
1069
- return this.#extractHandlers(
1070
- handlers,
1071
- (handler) => isForklaunchExpressLikeRouter(handler) ? handler.internal : handler
1072
- );
1081
+ return this.#extractHandlers(handlers, (handler) => {
1082
+ if (isForklaunchExpressLikeRouter(handler)) {
1083
+ this.addRouterToSdk(handler);
1084
+ return handler.internal;
1085
+ }
1086
+ return handler;
1087
+ });
1073
1088
  }
1074
1089
  #processTypedHandlerOrMiddleware(handler, middleware2) {
1075
1090
  if (isTypedHandler(handler)) {
@@ -1093,6 +1108,7 @@ var ForklaunchExpressLikeRouter = class {
1093
1108
  middleware2
1094
1109
  );
1095
1110
  if (isForklaunchExpressLikeRouter(contractDetailsOrMiddlewareOrTypedHandler)) {
1111
+ this.addRouterToSdk(contractDetailsOrMiddlewareOrTypedHandler);
1096
1112
  middleware2.push(contractDetailsOrMiddlewareOrTypedHandler.internal);
1097
1113
  }
1098
1114
  middleware2.push(
@@ -1122,6 +1138,16 @@ var ForklaunchExpressLikeRouter = class {
1122
1138
  }
1123
1139
  return this;
1124
1140
  }
1141
+ addRouterToSdk(router) {
1142
+ Object.entries(router.fetchMap).map(
1143
+ ([key, value]) => (0, import_common5.toRecord)(this.fetchMap)[(0, import_common5.sanitizePathSlashes)(`${this.basePath}${key}`)] = value
1144
+ );
1145
+ const existingSdk = this.sdk[router.sdkName ?? (0, import_common5.toPrettyCamelCase)(router.basePath)];
1146
+ (0, import_common5.toRecord)(this.sdk)[router.sdkName ?? (0, import_common5.toPrettyCamelCase)(router.basePath)] = {
1147
+ ...typeof existingSdk === "object" ? existingSdk : {},
1148
+ ...router.sdk
1149
+ };
1150
+ }
1125
1151
  registerNestableMiddlewareHandler(registrationMethod, pathOrContractDetailsOrMiddlewareOrTypedHandler, contractDetailsOrMiddlewareOrTypedHandler, ...middlewareOrMiddlewareWithTypedHandler) {
1126
1152
  const middleware2 = [];
1127
1153
  let path;
@@ -1137,7 +1163,9 @@ var ForklaunchExpressLikeRouter = class {
1137
1163
  if (isConstrainedForklaunchRouter(
1138
1164
  pathOrContractDetailsOrMiddlewareOrTypedHandler
1139
1165
  )) {
1140
- path = pathOrContractDetailsOrMiddlewareOrTypedHandler.basePath;
1166
+ const router = pathOrContractDetailsOrMiddlewareOrTypedHandler;
1167
+ this.addRouterToSdk(router);
1168
+ path = router.basePath;
1141
1169
  }
1142
1170
  middleware2.push(
1143
1171
  ...this.#extractNestableMiddlewareAsRouterHandlers(
@@ -1148,6 +1176,11 @@ var ForklaunchExpressLikeRouter = class {
1148
1176
  )
1149
1177
  );
1150
1178
  }
1179
+ if (!path) {
1180
+ path = middleware2.filter(
1181
+ (m) => isForklaunchExpressLikeRouter(m)
1182
+ )[0]?.basePath;
1183
+ }
1151
1184
  if (path) {
1152
1185
  registrationMethod.bind(this.internal)(path, ...middleware2);
1153
1186
  } else {
@@ -1202,15 +1235,13 @@ var ForklaunchExpressLikeRouter = class {
1202
1235
  * @returns {ExpressRouter} - The Express router.
1203
1236
  */
1204
1237
  get = (path, contractDetailsOrMiddlewareOrTypedHandler, ...middlewareOrMiddlewareWithTypedHandler) => {
1205
- return {
1206
- get: this.registerRoute(
1207
- "get",
1208
- path,
1209
- this.internal.get,
1210
- contractDetailsOrMiddlewareOrTypedHandler,
1211
- ...middlewareOrMiddlewareWithTypedHandler
1212
- )
1213
- };
1238
+ return this.registerRoute(
1239
+ "get",
1240
+ path,
1241
+ this.internal.get,
1242
+ contractDetailsOrMiddlewareOrTypedHandler,
1243
+ ...middlewareOrMiddlewareWithTypedHandler
1244
+ );
1214
1245
  };
1215
1246
  /**
1216
1247
  * Registers a POST route with the specified contract details and handler handlers.
@@ -1226,15 +1257,13 @@ var ForklaunchExpressLikeRouter = class {
1226
1257
  * @returns {ExpressRouter} - The Expxwress router.
1227
1258
  */
1228
1259
  post = (path, contractDetailsOrMiddlewareOrTypedHandler, ...middlewareOrMiddlewareWithTypedHandler) => {
1229
- return {
1230
- post: this.registerRoute(
1231
- "post",
1232
- path,
1233
- this.internal.post,
1234
- contractDetailsOrMiddlewareOrTypedHandler,
1235
- ...middlewareOrMiddlewareWithTypedHandler
1236
- )
1237
- };
1260
+ return this.registerRoute(
1261
+ "post",
1262
+ path,
1263
+ this.internal.post,
1264
+ contractDetailsOrMiddlewareOrTypedHandler,
1265
+ ...middlewareOrMiddlewareWithTypedHandler
1266
+ );
1238
1267
  };
1239
1268
  /**
1240
1269
  * Registers a PUT route with the specified contract details and handler handlers.
@@ -1250,15 +1279,13 @@ var ForklaunchExpressLikeRouter = class {
1250
1279
  * @returns {ExpressRouter} - The Express router.
1251
1280
  */
1252
1281
  put = (path, contractDetailsOrMiddlewareOrTypedHandler, ...middlewareOrMiddlewareWithTypedHandler) => {
1253
- return {
1254
- put: this.registerRoute(
1255
- "put",
1256
- path,
1257
- this.internal.put,
1258
- contractDetailsOrMiddlewareOrTypedHandler,
1259
- ...middlewareOrMiddlewareWithTypedHandler
1260
- )
1261
- };
1282
+ return this.registerRoute(
1283
+ "put",
1284
+ path,
1285
+ this.internal.put,
1286
+ contractDetailsOrMiddlewareOrTypedHandler,
1287
+ ...middlewareOrMiddlewareWithTypedHandler
1288
+ );
1262
1289
  };
1263
1290
  /**
1264
1291
  * Registers a PATCH route with the specified contract details and handler handlers.
@@ -1274,15 +1301,13 @@ var ForklaunchExpressLikeRouter = class {
1274
1301
  * @returns {ExpressRouter} - The Express router.
1275
1302
  */
1276
1303
  patch = (path, contractDetailsOrMiddlewareOrTypedHandler, ...middlewareOrMiddlewareWithTypedHandler) => {
1277
- return {
1278
- patch: this.registerRoute(
1279
- "patch",
1280
- path,
1281
- this.internal.patch,
1282
- contractDetailsOrMiddlewareOrTypedHandler,
1283
- ...middlewareOrMiddlewareWithTypedHandler
1284
- )
1285
- };
1304
+ return this.registerRoute(
1305
+ "patch",
1306
+ path,
1307
+ this.internal.patch,
1308
+ contractDetailsOrMiddlewareOrTypedHandler,
1309
+ ...middlewareOrMiddlewareWithTypedHandler
1310
+ );
1286
1311
  };
1287
1312
  /**
1288
1313
  * Registers a DELETE route with the specified contract details and handler handlers.
@@ -1298,15 +1323,13 @@ var ForklaunchExpressLikeRouter = class {
1298
1323
  * @returns {ExpressRouter} - The Express router.
1299
1324
  */
1300
1325
  delete = (path, contractDetailsOrMiddlewareOrTypedHandler, ...middlewareOrMiddlewareWithTypedHandler) => {
1301
- return {
1302
- delete: this.registerRoute(
1303
- "delete",
1304
- path,
1305
- this.internal.delete,
1306
- contractDetailsOrMiddlewareOrTypedHandler,
1307
- ...middlewareOrMiddlewareWithTypedHandler
1308
- )
1309
- };
1326
+ return this.registerRoute(
1327
+ "delete",
1328
+ path,
1329
+ this.internal.delete,
1330
+ contractDetailsOrMiddlewareOrTypedHandler,
1331
+ ...middlewareOrMiddlewareWithTypedHandler
1332
+ );
1310
1333
  };
1311
1334
  /**
1312
1335
  * Registers a OPTIONS route with the specified contract details and handler handlers.
@@ -1322,15 +1345,13 @@ var ForklaunchExpressLikeRouter = class {
1322
1345
  * @returns {ExpressRouter} - The Express router.
1323
1346
  */
1324
1347
  options = (path, contractDetailsOrMiddlewareOrTypedHandler, ...middlewareOrMiddlewareWithTypedHandler) => {
1325
- return {
1326
- options: this.registerRoute(
1327
- "options",
1328
- path,
1329
- this.internal.options,
1330
- contractDetailsOrMiddlewareOrTypedHandler,
1331
- ...middlewareOrMiddlewareWithTypedHandler
1332
- )
1333
- };
1348
+ return this.registerRoute(
1349
+ "options",
1350
+ path,
1351
+ this.internal.options,
1352
+ contractDetailsOrMiddlewareOrTypedHandler,
1353
+ ...middlewareOrMiddlewareWithTypedHandler
1354
+ );
1334
1355
  };
1335
1356
  /**
1336
1357
  * Registers a HEAD route with the specified contract details and handler handlers.
@@ -1346,15 +1367,13 @@ var ForklaunchExpressLikeRouter = class {
1346
1367
  * @returns {ExpressRouter} - The Express router.
1347
1368
  */
1348
1369
  head = (path, contractDetailsOrMiddlewareOrTypedHandler, ...middlewareOrMiddlewareWithTypedHandler) => {
1349
- return {
1350
- head: this.registerRoute(
1351
- "head",
1352
- path,
1353
- this.internal.head,
1354
- contractDetailsOrMiddlewareOrTypedHandler,
1355
- ...middlewareOrMiddlewareWithTypedHandler
1356
- )
1357
- };
1370
+ return this.registerRoute(
1371
+ "head",
1372
+ path,
1373
+ this.internal.head,
1374
+ contractDetailsOrMiddlewareOrTypedHandler,
1375
+ ...middlewareOrMiddlewareWithTypedHandler
1376
+ );
1358
1377
  };
1359
1378
  /**
1360
1379
  * Registers a TRACE route with the specified contract details and handler handlers.
@@ -1370,16 +1389,32 @@ var ForklaunchExpressLikeRouter = class {
1370
1389
  * @returns {ExpressRouter} - The Express router.
1371
1390
  */
1372
1391
  trace = (path, contractDetailsOrMiddlewareOrTypedHandler, ...middlewareOrMiddlewareWithTypedHandler) => {
1373
- return {
1374
- trace: this.registerRoute(
1375
- "trace",
1376
- path,
1377
- this.internal.trace,
1378
- contractDetailsOrMiddlewareOrTypedHandler,
1379
- ...middlewareOrMiddlewareWithTypedHandler
1380
- )
1381
- };
1392
+ return this.registerRoute(
1393
+ "trace",
1394
+ path,
1395
+ this.internal.trace,
1396
+ contractDetailsOrMiddlewareOrTypedHandler,
1397
+ ...middlewareOrMiddlewareWithTypedHandler
1398
+ );
1382
1399
  };
1400
+ cloneInternals(clone) {
1401
+ clone.routers = [...this.routers];
1402
+ clone.routes = [...this.routes];
1403
+ clone.fetchMap = { ...this.fetchMap };
1404
+ clone.sdk = { ...this.sdk };
1405
+ }
1406
+ clone() {
1407
+ const clone = new _ForklaunchExpressLikeRouter(
1408
+ this.basePath,
1409
+ this.schemaValidator,
1410
+ this.internal,
1411
+ this.postEnrichMiddleware,
1412
+ this.openTelemetryCollector,
1413
+ this.sdkName
1414
+ );
1415
+ this.cloneInternals(clone);
1416
+ return clone;
1417
+ }
1383
1418
  };
1384
1419
 
1385
1420
  // src/http/application/expressLikeApplication.ts
@@ -2466,6 +2501,218 @@ var getCodeForStatus = (status) => {
2466
2501
  };
2467
2502
  var httpStatusCodes_default = HTTPStatuses;
2468
2503
 
2504
+ // src/http/mcpGenerator/mcpGenerator.ts
2505
+ var import_common7 = require("@forklaunch/common");
2506
+ var import_zod = require("@forklaunch/validator/zod");
2507
+ var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
2508
+
2509
+ // src/http/router/unpackRouters.ts
2510
+ var import_common6 = require("@forklaunch/common");
2511
+ function unpackRouters(routers, recursiveBasePath = [], recursiveSdkPath = []) {
2512
+ return routers.reduce((acc, router) => {
2513
+ acc.push({
2514
+ fullPath: [...recursiveBasePath, router.basePath].join(""),
2515
+ sdkPath: [
2516
+ ...recursiveSdkPath,
2517
+ (0, import_common6.toPrettyCamelCase)(router.sdkName ?? router.basePath)
2518
+ ].join("."),
2519
+ router
2520
+ });
2521
+ acc.push(
2522
+ ...unpackRouters(
2523
+ router.routers,
2524
+ [...recursiveBasePath, router.basePath],
2525
+ [
2526
+ ...recursiveSdkPath,
2527
+ (0, import_common6.toPrettyCamelCase)(router.sdkName ?? router.basePath)
2528
+ ]
2529
+ )
2530
+ );
2531
+ return acc;
2532
+ }, []);
2533
+ }
2534
+
2535
+ // src/http/mcpGenerator/mcpGenerator.ts
2536
+ function generateMcpServer(schemaValidator, protocol, host, port, version, routers, contentTypeMap) {
2537
+ if (!(schemaValidator instanceof import_zod.ZodSchemaValidator)) {
2538
+ throw new Error(
2539
+ "Schema validator must be an instance of ZodSchemaValidator"
2540
+ );
2541
+ }
2542
+ const mcpServer = new import_mcp.McpServer({
2543
+ name: "example-server",
2544
+ version
2545
+ });
2546
+ unpackRouters(routers).forEach(({ fullPath, router }) => {
2547
+ router.routes.forEach((route) => {
2548
+ let discriminatedBody;
2549
+ if ("body" in route.contractDetails) {
2550
+ discriminatedBody = discriminateBody(
2551
+ schemaValidator,
2552
+ route.contractDetails.body
2553
+ );
2554
+ }
2555
+ const inputSchema = {
2556
+ ...discriminatedBody && "body" in route.contractDetails ? {
2557
+ ..."contentType" in route.contractDetails.body ? { contentType: route.contractDetails.body.contentType } : {},
2558
+ body: schemaValidator.schemify(discriminatedBody.schema)
2559
+ } : {},
2560
+ ...route.contractDetails.params ? { params: schemaValidator.schemify(route.contractDetails.params) } : {},
2561
+ ...route.contractDetails.query ? { query: schemaValidator.schemify(route.contractDetails.query) } : {},
2562
+ ...route.contractDetails.requestHeaders ? {
2563
+ headers: schemaValidator.schemify(
2564
+ route.contractDetails.requestHeaders
2565
+ )
2566
+ } : {}
2567
+ // TODO: support auth
2568
+ // ...(route.contractDetails.auth
2569
+ // ? { auth: route.contractDetails.auth }
2570
+ // : {})
2571
+ };
2572
+ mcpServer.tool(
2573
+ route.contractDetails.name,
2574
+ route.contractDetails.summary,
2575
+ inputSchema,
2576
+ async (args) => {
2577
+ const { contentType, body, params, query, headers } = args;
2578
+ let url = `${protocol}://${host}:${port}${fullPath}${route.path}`;
2579
+ if (params) {
2580
+ for (const key in params) {
2581
+ url = url.replace(
2582
+ `:${key}`,
2583
+ encodeURIComponent(params[key])
2584
+ );
2585
+ }
2586
+ }
2587
+ let parsedBody;
2588
+ if (discriminatedBody) {
2589
+ switch (discriminatedBody.parserType) {
2590
+ case "json": {
2591
+ parsedBody = (0, import_common7.safeStringify)(body);
2592
+ break;
2593
+ }
2594
+ case "text": {
2595
+ parsedBody = body;
2596
+ break;
2597
+ }
2598
+ case "file": {
2599
+ parsedBody = body;
2600
+ break;
2601
+ }
2602
+ case "multipart": {
2603
+ const formData = new FormData();
2604
+ if ((0, import_common7.isRecord)(body)) {
2605
+ for (const key in body) {
2606
+ if (typeof body[key] === "string" || body[key] instanceof Blob) {
2607
+ formData.append(key, body[key]);
2608
+ } else {
2609
+ throw new Error("Body is not a valid multipart object");
2610
+ }
2611
+ }
2612
+ } else {
2613
+ throw new Error("Body is not a valid multipart object");
2614
+ }
2615
+ parsedBody = formData;
2616
+ break;
2617
+ }
2618
+ case "urlEncoded": {
2619
+ if ((0, import_common7.isRecord)(body)) {
2620
+ parsedBody = new URLSearchParams(
2621
+ Object.entries(body).map(([key, value]) => [
2622
+ key,
2623
+ (0, import_common7.safeStringify)(value)
2624
+ ])
2625
+ );
2626
+ } else {
2627
+ throw new Error("Body is not a valid url encoded object");
2628
+ }
2629
+ break;
2630
+ }
2631
+ default: {
2632
+ (0, import_common7.isNever)(discriminatedBody.parserType);
2633
+ parsedBody = (0, import_common7.safeStringify)(body);
2634
+ break;
2635
+ }
2636
+ }
2637
+ }
2638
+ if (query) {
2639
+ const queryString = new URLSearchParams(
2640
+ Object.entries(query).map(([key, value]) => [
2641
+ key,
2642
+ (0, import_common7.safeStringify)(value)
2643
+ ])
2644
+ ).toString();
2645
+ url += queryString ? `?${queryString}` : "";
2646
+ }
2647
+ const response = await fetch(encodeURI(url), {
2648
+ method: route.method.toUpperCase(),
2649
+ headers: {
2650
+ ...headers,
2651
+ ...discriminatedBody?.contentType != "multipart/form-data" ? {
2652
+ "Content-Type": contentType ?? discriminatedBody?.contentType
2653
+ } : {}
2654
+ },
2655
+ body: parsedBody
2656
+ });
2657
+ if (response.status >= 300) {
2658
+ throw new Error(
2659
+ `Error received while proxying request to ${url}: ${await response.text()}`
2660
+ );
2661
+ }
2662
+ const contractContentType = discriminateResponseBodies(
2663
+ schemaValidator,
2664
+ route.contractDetails.responses
2665
+ )[response.status].contentType;
2666
+ switch (contentTypeMap && contentTypeMap[contractContentType] ? contentTypeMap[contractContentType] : contractContentType) {
2667
+ case "application/json":
2668
+ return {
2669
+ content: [
2670
+ {
2671
+ type: "text",
2672
+ text: (0, import_common7.safeStringify)(await response.json())
2673
+ }
2674
+ ]
2675
+ };
2676
+ case "text/plain":
2677
+ return {
2678
+ content: [
2679
+ { type: "text", text: await response.text() }
2680
+ ]
2681
+ };
2682
+ case "application/octet-stream":
2683
+ return {
2684
+ content: [
2685
+ {
2686
+ type: "resource",
2687
+ resource: {
2688
+ uri: response.url,
2689
+ blob: Buffer.from(
2690
+ await (await response.blob()).arrayBuffer()
2691
+ ).toString("base64")
2692
+ }
2693
+ }
2694
+ ]
2695
+ };
2696
+ case "text/event-stream":
2697
+ return {
2698
+ content: [
2699
+ { type: "text", text: await response.text() }
2700
+ ]
2701
+ };
2702
+ default:
2703
+ return {
2704
+ content: [
2705
+ { type: "text", text: await response.text() }
2706
+ ]
2707
+ };
2708
+ }
2709
+ }
2710
+ );
2711
+ });
2712
+ });
2713
+ return mcpServer;
2714
+ }
2715
+
2469
2716
  // src/http/middleware/response/parse.middleware.ts
2470
2717
  var import_validator2 = require("@forklaunch/validator");
2471
2718
  function parse2(req, res, next) {
@@ -2528,18 +2775,18 @@ ${parseErrors.join("\n\n")}`
2528
2775
  }
2529
2776
 
2530
2777
  // src/http/middleware/response/enrichExpressLikeSend.middleware.ts
2531
- var import_common6 = require("@forklaunch/common");
2778
+ var import_common9 = require("@forklaunch/common");
2532
2779
  var import_stream = require("stream");
2533
2780
 
2534
2781
  // src/http/telemetry/recordMetric.ts
2535
- var import_common5 = require("@forklaunch/common");
2782
+ var import_common8 = require("@forklaunch/common");
2536
2783
  var import_semantic_conventions3 = require("@opentelemetry/semantic-conventions");
2537
2784
  function recordMetric(req, res) {
2538
2785
  if (res.metricRecorded) {
2539
2786
  return;
2540
2787
  }
2541
2788
  httpRequestsTotalCounter.add(1, {
2542
- [import_semantic_conventions3.ATTR_SERVICE_NAME]: (0, import_common5.getEnvVar)("OTEL_SERVICE_NAME"),
2789
+ [import_semantic_conventions3.ATTR_SERVICE_NAME]: (0, import_common8.getEnvVar)("OTEL_SERVICE_NAME"),
2543
2790
  [ATTR_API_NAME]: req.contractDetails?.name,
2544
2791
  [ATTR_CORRELATION_ID]: req.context.correlationId,
2545
2792
  [import_semantic_conventions3.ATTR_HTTP_REQUEST_METHOD]: req.method,
@@ -2559,7 +2806,7 @@ function enrichExpressLikeSend(instance, req, res, originalOperation, originalSe
2559
2806
  if (res.statusCode === 404) {
2560
2807
  res.type("text/plain");
2561
2808
  res.status(404);
2562
- logger("error").error("Not Found");
2809
+ req.openTelemetryCollector.error("Not Found");
2563
2810
  originalSend.call(instance, "Not Found");
2564
2811
  errorSent = true;
2565
2812
  }
@@ -2577,8 +2824,8 @@ function enrichExpressLikeSend(instance, req, res, originalOperation, originalSe
2577
2824
  `attachment; filename="${data.name}"`
2578
2825
  );
2579
2826
  }
2580
- if ((0, import_common6.isNodeJsWriteableStream)(res)) {
2581
- import_stream.Readable.from((0, import_common6.readableStreamToAsyncIterable)(data.stream())).pipe(
2827
+ if ((0, import_common9.isNodeJsWriteableStream)(res)) {
2828
+ import_stream.Readable.from((0, import_common9.readableStreamToAsyncIterable)(data.stream())).pipe(
2582
2829
  res
2583
2830
  );
2584
2831
  } else {
@@ -2587,7 +2834,7 @@ function enrichExpressLikeSend(instance, req, res, originalOperation, originalSe
2587
2834
  originalSend.call(instance, "Not a NodeJS WritableStream");
2588
2835
  errorSent = true;
2589
2836
  }
2590
- } else if ((0, import_common6.isAsyncGenerator)(data)) {
2837
+ } else if ((0, import_common9.isAsyncGenerator)(data)) {
2591
2838
  let firstPass = true;
2592
2839
  const transformer = new import_stream.Transform({
2593
2840
  objectMode: true,
@@ -2602,7 +2849,7 @@ function enrichExpressLikeSend(instance, req, res, originalOperation, originalSe
2602
2849
  ------------------
2603
2850
  ${res.locals.errorMessage}`;
2604
2851
  }
2605
- logger("error").error(errorString);
2852
+ req.openTelemetryCollector.error(errorString);
2606
2853
  res.type("text/plain");
2607
2854
  res.status(500);
2608
2855
  originalSend.call(instance, errorString);
@@ -2615,7 +2862,7 @@ ${res.locals.errorMessage}`;
2615
2862
  if (!errorSent) {
2616
2863
  let data2 = "";
2617
2864
  for (const [key, value] of Object.entries(chunk)) {
2618
- data2 += `${key}: ${typeof value === "string" ? value : (0, import_common6.safeStringify)(value)}
2865
+ data2 += `${key}: ${typeof value === "string" ? value : (0, import_common9.safeStringify)(value)}
2619
2866
  `;
2620
2867
  }
2621
2868
  data2 += "\n";
@@ -2623,7 +2870,7 @@ ${res.locals.errorMessage}`;
2623
2870
  }
2624
2871
  }
2625
2872
  });
2626
- if ((0, import_common6.isNodeJsWriteableStream)(res)) {
2873
+ if ((0, import_common9.isNodeJsWriteableStream)(res)) {
2627
2874
  import_stream.Readable.from(data).pipe(transformer).pipe(res);
2628
2875
  } else {
2629
2876
  res.type("text/plain");
@@ -2634,7 +2881,7 @@ ${res.locals.errorMessage}`;
2634
2881
  } else {
2635
2882
  const parserType = responseBodies?.[Number(res.statusCode)]?.parserType;
2636
2883
  res.bodyData = data;
2637
- if ((0, import_common6.isRecord)(data)) {
2884
+ if ((0, import_common9.isRecord)(data)) {
2638
2885
  switch (parserType) {
2639
2886
  case "json":
2640
2887
  res.bodyData = "json" in data ? data.json : data;
@@ -2655,7 +2902,7 @@ ${res.locals.errorMessage}`;
2655
2902
  res.bodyData = data;
2656
2903
  break;
2657
2904
  default:
2658
- (0, import_common6.isNever)(parserType);
2905
+ (0, import_common9.isNever)(parserType);
2659
2906
  res.bodyData = data;
2660
2907
  break;
2661
2908
  }
@@ -2668,7 +2915,7 @@ ${res.locals.errorMessage}`;
2668
2915
  ------------------
2669
2916
  ${res.locals.errorMessage}`;
2670
2917
  }
2671
- logger("error").error(errorString);
2918
+ req.openTelemetryCollector.error(errorString);
2672
2919
  res.type("text/plain");
2673
2920
  res.status(500);
2674
2921
  originalSend.call(instance, errorString);
@@ -2691,16 +2938,17 @@ ${res.locals.errorMessage}`;
2691
2938
  }
2692
2939
 
2693
2940
  // src/http/openApiV3Generator/openApiV3Generator.ts
2941
+ var import_common10 = require("@forklaunch/common");
2694
2942
  function toUpperCase(str) {
2695
2943
  return str.charAt(0).toUpperCase() + str.slice(1);
2696
2944
  }
2697
2945
  function transformBasePath(basePath) {
2698
2946
  if (basePath.startsWith("/")) {
2699
- return toUpperCase(basePath.slice(1));
2947
+ return basePath.slice(1);
2700
2948
  }
2701
2949
  return `/${basePath}`;
2702
2950
  }
2703
- function generateOpenApiDocument(port, tags, paths) {
2951
+ function generateOpenApiDocument(protocol, host, port, tags, paths, otherServers) {
2704
2952
  return {
2705
2953
  openapi: "3.1.0",
2706
2954
  info: {
@@ -2719,8 +2967,10 @@ function generateOpenApiDocument(port, tags, paths) {
2719
2967
  tags,
2720
2968
  servers: [
2721
2969
  {
2722
- url: `http://localhost:${port}`
2723
- }
2970
+ url: `${protocol}://${host}:${port}`,
2971
+ description: "Main Server"
2972
+ },
2973
+ ...otherServers || []
2724
2974
  ],
2725
2975
  paths
2726
2976
  };
@@ -2741,19 +2991,21 @@ function contentResolver(schemaValidator, body, contentType) {
2741
2991
  }
2742
2992
  };
2743
2993
  }
2744
- function generateSwaggerDocument(schemaValidator, port, routers) {
2994
+ function generateSwaggerDocument(schemaValidator, protocol, host, port, routers, otherServers) {
2745
2995
  const tags = [];
2746
2996
  const paths = {};
2747
- routers.flat(Infinity).forEach((router) => {
2748
- const controllerName = transformBasePath(router.basePath);
2997
+ unpackRouters(routers).forEach(({ fullPath, router, sdkPath }) => {
2998
+ const controllerName = transformBasePath(fullPath);
2749
2999
  tags.push({
2750
3000
  name: controllerName,
2751
- description: `${controllerName} Operations`
3001
+ description: `${toUpperCase(controllerName)} Operations`
2752
3002
  });
2753
3003
  router.routes.forEach((route) => {
2754
- const fullPath = `${router.basePath}${route.path === "/" ? "" : route.path}`.replace(/:(\w+)/g, "{$1}");
2755
- if (!paths[fullPath]) {
2756
- paths[fullPath] = {};
3004
+ const openApiPath = (0, import_common10.openApiCompliantPath)(
3005
+ `${fullPath}${route.path === "/" ? "" : route.path}`
3006
+ );
3007
+ if (!paths[openApiPath]) {
3008
+ paths[openApiPath] = {};
2757
3009
  }
2758
3010
  const { name, summary, query, requestHeaders } = route.contractDetails;
2759
3011
  const responses = {};
@@ -2780,15 +3032,16 @@ function generateSwaggerDocument(schemaValidator, port, routers) {
2780
3032
  };
2781
3033
  }
2782
3034
  }
2783
- const pathItemObject = {
3035
+ const operationObject = {
2784
3036
  tags: [controllerName],
2785
3037
  summary: `${name}: ${summary}`,
2786
3038
  parameters: [],
2787
- responses
3039
+ responses,
3040
+ operationId: `${sdkPath}.${(0, import_common10.toPrettyCamelCase)(name)}`
2788
3041
  };
2789
3042
  if (route.contractDetails.params) {
2790
3043
  for (const key in route.contractDetails.params) {
2791
- pathItemObject.parameters?.push({
3044
+ operationObject.parameters?.push({
2792
3045
  name: key,
2793
3046
  in: "path",
2794
3047
  schema: schemaValidator.openapi(
@@ -2799,7 +3052,7 @@ function generateSwaggerDocument(schemaValidator, port, routers) {
2799
3052
  }
2800
3053
  const discriminatedBodyResult = "body" in route.contractDetails ? discriminateBody(schemaValidator, route.contractDetails.body) : null;
2801
3054
  if (discriminatedBodyResult) {
2802
- pathItemObject.requestBody = {
3055
+ operationObject.requestBody = {
2803
3056
  required: true,
2804
3057
  content: contentResolver(
2805
3058
  schemaValidator,
@@ -2810,7 +3063,7 @@ function generateSwaggerDocument(schemaValidator, port, routers) {
2810
3063
  }
2811
3064
  if (requestHeaders) {
2812
3065
  for (const key in requestHeaders) {
2813
- pathItemObject.parameters?.push({
3066
+ operationObject.parameters?.push({
2814
3067
  name: key,
2815
3068
  in: "header",
2816
3069
  schema: schemaValidator.openapi(
@@ -2821,7 +3074,7 @@ function generateSwaggerDocument(schemaValidator, port, routers) {
2821
3074
  }
2822
3075
  if (query) {
2823
3076
  for (const key in query) {
2824
- pathItemObject.parameters?.push({
3077
+ operationObject.parameters?.push({
2825
3078
  name: key,
2826
3079
  in: "query",
2827
3080
  schema: schemaValidator.openapi(query[key])
@@ -2838,7 +3091,7 @@ function generateSwaggerDocument(schemaValidator, port, routers) {
2838
3091
  content: contentResolver(schemaValidator, schemaValidator.string)
2839
3092
  };
2840
3093
  if (route.contractDetails.auth.method === "jwt") {
2841
- pathItemObject.security = [
3094
+ operationObject.security = [
2842
3095
  {
2843
3096
  bearer: Array.from(
2844
3097
  route.contractDetails.auth.allowedPermissions?.values() || []
@@ -2848,11 +3101,18 @@ function generateSwaggerDocument(schemaValidator, port, routers) {
2848
3101
  }
2849
3102
  }
2850
3103
  if (route.method !== "middleware") {
2851
- paths[fullPath][route.method] = pathItemObject;
3104
+ paths[openApiPath][route.method] = operationObject;
2852
3105
  }
2853
3106
  });
2854
3107
  });
2855
- return generateOpenApiDocument(port, tags, paths);
3108
+ return generateOpenApiDocument(
3109
+ protocol,
3110
+ host,
3111
+ port,
3112
+ tags,
3113
+ paths,
3114
+ otherServers
3115
+ );
2856
3116
  }
2857
3117
 
2858
3118
  // src/http/telemetry/evaluateTelemetryOptions.ts
@@ -2892,6 +3152,7 @@ function metricsDefinitions(metrics2) {
2892
3152
  discriminateResponseBodies,
2893
3153
  enrichExpressLikeSend,
2894
3154
  evaluateTelemetryOptions,
3155
+ generateMcpServer,
2895
3156
  generateSwaggerDocument,
2896
3157
  get,
2897
3158
  getCodeForStatus,