skedyul 1.2.21 → 1.2.23

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.
@@ -30,32 +30,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/server.ts
31
31
  var server_exports = {};
32
32
  __export(server_exports, {
33
- buildRequestFromRaw: () => buildRequestFromRaw,
34
- buildRequestScopedConfig: () => buildRequestScopedConfig,
35
- buildToolMetadata: () => buildToolMetadata,
36
- createCallToolHandler: () => createCallToolHandler,
37
- createDedicatedServerInstance: () => createDedicatedServerInstance,
38
- createRequestState: () => createRequestState,
39
- createResponse: () => createResponse,
40
- createServerlessInstance: () => createServerlessInstance,
41
33
  createSkedyulServer: () => createSkedyulServer,
42
- getDefaultHeaders: () => getDefaultHeaders,
43
34
  getJsonSchemaFromToolSchema: () => getJsonSchemaFromToolSchema,
44
- getListeningPort: () => getListeningPort,
45
35
  getZodSchema: () => getZodSchema,
46
- handleCoreMethod: () => handleCoreMethod,
47
36
  isToolSchemaWithJson: () => isToolSchemaWithJson,
48
- mergeRuntimeEnv: () => mergeRuntimeEnv,
49
- normalizeBilling: () => normalizeBilling,
50
- padEnd: () => padEnd,
51
- parseHandlerEnvelope: () => parseHandlerEnvelope,
52
- parseJSONBody: () => parseJSONBody,
53
- parseJsonRecord: () => parseJsonRecord,
54
- parseNumberEnv: () => parseNumberEnv,
55
- printStartupLog: () => printStartupLog,
56
- readRawRequestBody: () => readRawRequestBody,
57
- sendHTML: () => sendHTML,
58
- sendJSON: () => sendJSON,
59
37
  server: () => server,
60
38
  toJsonSchema: () => toJsonSchema
61
39
  });
@@ -203,10 +181,6 @@ function sendJSON(res, statusCode, data) {
203
181
  res.writeHead(statusCode, { "Content-Type": "application/json" });
204
182
  res.end(JSON.stringify(data));
205
183
  }
206
- function sendHTML(res, statusCode, html) {
207
- res.writeHead(statusCode, { "Content-Type": "text/html; charset=utf-8" });
208
- res.end(html);
209
- }
210
184
  function getDefaultHeaders(options) {
211
185
  return {
212
186
  "Content-Type": "application/json",
@@ -392,8 +366,8 @@ function createRequestState(maxRequests, ttlExtendSeconds, runtimeLabel, toolNam
392
366
  };
393
367
  }
394
368
  function createCallToolHandler(registry, state, onMaxRequests) {
395
- return async function callTool(nameRaw, argsRaw) {
396
- const toolName = String(nameRaw);
369
+ return async function callTool(toolNameInput, toolArgsInput) {
370
+ const toolName = String(toolNameInput);
397
371
  const tool = registry[toolName];
398
372
  if (!tool) {
399
373
  throw new Error(`Tool "${toolName}" not found in registry`);
@@ -402,7 +376,7 @@ function createCallToolHandler(registry, state, onMaxRequests) {
402
376
  throw new Error(`Tool "${toolName}" handler is not a function`);
403
377
  }
404
378
  const fn = tool.handler;
405
- const args = argsRaw ?? {};
379
+ const args = toolArgsInput ?? {};
406
380
  const estimateMode = args.estimate === true;
407
381
  if (!estimateMode) {
408
382
  state.incrementRequestCount();
@@ -657,51 +631,6 @@ async function handleCoreMethod(method, params) {
657
631
  };
658
632
  }
659
633
 
660
- // src/server/handler-helpers.ts
661
- function parseHandlerEnvelope(parsedBody) {
662
- if (typeof parsedBody !== "object" || parsedBody === null || Array.isArray(parsedBody) || !("env" in parsedBody) || !("request" in parsedBody)) {
663
- return null;
664
- }
665
- const envelope = parsedBody;
666
- if (typeof envelope.env !== "object" || envelope.env === null || Array.isArray(envelope.env)) {
667
- return null;
668
- }
669
- if (typeof envelope.request !== "object" || envelope.request === null || Array.isArray(envelope.request)) {
670
- return null;
671
- }
672
- return {
673
- env: envelope.env,
674
- request: envelope.request,
675
- context: envelope.context
676
- };
677
- }
678
- function buildRequestFromRaw(raw) {
679
- let parsedBody = raw.body;
680
- const contentType = raw.headers["content-type"] ?? "";
681
- if (contentType.includes("application/json")) {
682
- try {
683
- parsedBody = raw.body ? JSON.parse(raw.body) : {};
684
- } catch {
685
- parsedBody = raw.body;
686
- }
687
- }
688
- return {
689
- method: raw.method,
690
- url: raw.url,
691
- path: raw.path,
692
- headers: raw.headers,
693
- query: raw.query,
694
- body: parsedBody,
695
- rawBody: raw.body ? Buffer.from(raw.body, "utf-8") : void 0
696
- };
697
- }
698
- function buildRequestScopedConfig(env) {
699
- return {
700
- baseUrl: env.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
701
- apiToken: env.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
702
- };
703
- }
704
-
705
634
  // src/server/startup-logger.ts
706
635
  function padEnd(str, length) {
707
636
  if (str.length >= length) {
@@ -799,6 +728,405 @@ function isProvisionConfig(value) {
799
728
  return value !== void 0 && value !== null && !(value instanceof Promise);
800
729
  }
801
730
 
731
+ // src/server/handlers/install-handler.ts
732
+ async function handleInstall(body, hooks) {
733
+ if (!hooks?.install) {
734
+ return {
735
+ status: 404,
736
+ body: { error: "Install handler not configured" }
737
+ };
738
+ }
739
+ if (!body.context?.appInstallationId || !body.context?.workplace) {
740
+ return {
741
+ status: 400,
742
+ body: {
743
+ error: {
744
+ code: -32602,
745
+ message: "Missing context (appInstallationId and workplace required)"
746
+ }
747
+ }
748
+ };
749
+ }
750
+ const installContext = {
751
+ env: body.env ?? {},
752
+ workplace: body.context.workplace,
753
+ appInstallationId: body.context.appInstallationId,
754
+ app: body.context.app,
755
+ invocation: body.invocation,
756
+ log: createContextLogger()
757
+ };
758
+ const requestConfig = {
759
+ baseUrl: body.env?.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
760
+ apiToken: body.env?.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
761
+ };
762
+ try {
763
+ const installHook = hooks.install;
764
+ const installHandler = typeof installHook === "function" ? installHook : installHook.handler;
765
+ const result = await runWithLogContext({ invocation: body.invocation }, async () => {
766
+ return await runWithConfig(requestConfig, async () => {
767
+ return await installHandler(installContext);
768
+ });
769
+ });
770
+ return {
771
+ status: 200,
772
+ body: { env: result.env ?? {}, redirect: result.redirect }
773
+ };
774
+ } catch (err) {
775
+ if (err instanceof InstallError) {
776
+ return {
777
+ status: 400,
778
+ body: {
779
+ error: {
780
+ code: err.code,
781
+ message: err.message,
782
+ field: err.field
783
+ }
784
+ }
785
+ };
786
+ }
787
+ return {
788
+ status: 500,
789
+ body: {
790
+ error: {
791
+ code: -32603,
792
+ message: err instanceof Error ? err.message : String(err ?? "")
793
+ }
794
+ }
795
+ };
796
+ }
797
+ }
798
+
799
+ // src/server/handlers/uninstall-handler.ts
800
+ async function handleUninstall(body, hooks) {
801
+ if (!hooks?.uninstall) {
802
+ return {
803
+ status: 404,
804
+ body: { error: "Uninstall handler not configured" }
805
+ };
806
+ }
807
+ if (!body.context?.appInstallationId || !body.context?.workplace || !body.context?.app) {
808
+ return {
809
+ status: 400,
810
+ body: {
811
+ error: {
812
+ code: -32602,
813
+ message: "Missing context (appInstallationId, workplace and app required)"
814
+ }
815
+ }
816
+ };
817
+ }
818
+ const uninstallContext = {
819
+ env: body.env ?? {},
820
+ workplace: body.context.workplace,
821
+ appInstallationId: body.context.appInstallationId,
822
+ app: body.context.app,
823
+ invocation: body.invocation,
824
+ log: createContextLogger()
825
+ };
826
+ const requestConfig = {
827
+ baseUrl: body.env?.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
828
+ apiToken: body.env?.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
829
+ };
830
+ try {
831
+ const uninstallHook = hooks.uninstall;
832
+ const uninstallHandlerFn = typeof uninstallHook === "function" ? uninstallHook : uninstallHook.handler;
833
+ const result = await runWithLogContext({ invocation: body.invocation }, async () => {
834
+ return await runWithConfig(requestConfig, async () => {
835
+ return await uninstallHandlerFn(uninstallContext);
836
+ });
837
+ });
838
+ return {
839
+ status: 200,
840
+ body: { cleanedWebhookIds: result.cleanedWebhookIds ?? [] }
841
+ };
842
+ } catch (err) {
843
+ return {
844
+ status: 500,
845
+ body: {
846
+ error: {
847
+ code: -32603,
848
+ message: err instanceof Error ? err.message : String(err ?? "")
849
+ }
850
+ }
851
+ };
852
+ }
853
+ }
854
+
855
+ // src/server/handlers/provision-handler.ts
856
+ async function handleProvision(body, hooks) {
857
+ if (!hooks?.provision) {
858
+ return {
859
+ status: 404,
860
+ body: { error: "Provision handler not configured" }
861
+ };
862
+ }
863
+ if (!body.context?.app) {
864
+ return {
865
+ status: 400,
866
+ body: {
867
+ error: {
868
+ code: -32602,
869
+ message: "Missing context (app required)"
870
+ }
871
+ }
872
+ };
873
+ }
874
+ const mergedEnv = {};
875
+ for (const [key, value] of Object.entries(process.env)) {
876
+ if (value !== void 0) {
877
+ mergedEnv[key] = value;
878
+ }
879
+ }
880
+ Object.assign(mergedEnv, body.env ?? {});
881
+ const provisionContext = {
882
+ env: mergedEnv,
883
+ app: body.context.app,
884
+ invocation: body.invocation,
885
+ log: createContextLogger()
886
+ };
887
+ const requestConfig = {
888
+ baseUrl: mergedEnv.SKEDYUL_API_URL ?? "",
889
+ apiToken: mergedEnv.SKEDYUL_API_TOKEN ?? ""
890
+ };
891
+ try {
892
+ const provisionHook = hooks.provision;
893
+ const provisionHandler = typeof provisionHook === "function" ? provisionHook : provisionHook.handler;
894
+ const result = await runWithLogContext({ invocation: body.invocation }, async () => {
895
+ return await runWithConfig(requestConfig, async () => {
896
+ return await provisionHandler(provisionContext);
897
+ });
898
+ });
899
+ return {
900
+ status: 200,
901
+ body: result
902
+ };
903
+ } catch (err) {
904
+ return {
905
+ status: 500,
906
+ body: {
907
+ error: {
908
+ code: -32603,
909
+ message: err instanceof Error ? err.message : String(err ?? "")
910
+ }
911
+ }
912
+ };
913
+ }
914
+ }
915
+
916
+ // src/server/handler-helpers.ts
917
+ function parseHandlerEnvelope(parsedBody) {
918
+ if (typeof parsedBody !== "object" || parsedBody === null || Array.isArray(parsedBody) || !("env" in parsedBody) || !("request" in parsedBody)) {
919
+ return null;
920
+ }
921
+ const envelope = parsedBody;
922
+ if (typeof envelope.env !== "object" || envelope.env === null || Array.isArray(envelope.env)) {
923
+ return null;
924
+ }
925
+ if (typeof envelope.request !== "object" || envelope.request === null || Array.isArray(envelope.request)) {
926
+ return null;
927
+ }
928
+ return {
929
+ env: envelope.env,
930
+ request: envelope.request,
931
+ context: envelope.context
932
+ };
933
+ }
934
+ function buildRequestFromRaw(raw) {
935
+ let parsedBody = raw.body;
936
+ const contentType = raw.headers["content-type"] ?? "";
937
+ if (contentType.includes("application/json")) {
938
+ try {
939
+ parsedBody = raw.body ? JSON.parse(raw.body) : {};
940
+ } catch {
941
+ parsedBody = raw.body;
942
+ }
943
+ }
944
+ return {
945
+ method: raw.method,
946
+ url: raw.url,
947
+ path: raw.path,
948
+ headers: raw.headers,
949
+ query: raw.query,
950
+ body: parsedBody,
951
+ rawBody: raw.body ? Buffer.from(raw.body, "utf-8") : void 0
952
+ };
953
+ }
954
+ function buildRequestScopedConfig(env) {
955
+ return {
956
+ baseUrl: env.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
957
+ apiToken: env.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
958
+ };
959
+ }
960
+
961
+ // src/server/handlers/oauth-callback-handler.ts
962
+ async function handleOAuthCallback(parsedBody, hooks) {
963
+ if (!hooks?.oauth_callback) {
964
+ return {
965
+ status: 404,
966
+ body: { error: "OAuth callback handler not configured" }
967
+ };
968
+ }
969
+ const envelope = parseHandlerEnvelope(parsedBody);
970
+ if (!envelope) {
971
+ console.error("[OAuth Callback] Failed to parse envelope. Body:", JSON.stringify(parsedBody, null, 2));
972
+ return {
973
+ status: 400,
974
+ body: {
975
+ error: {
976
+ code: -32602,
977
+ message: "Missing envelope format: expected { env, request }"
978
+ }
979
+ }
980
+ };
981
+ }
982
+ const invocation = parsedBody.invocation;
983
+ const oauthRequest = buildRequestFromRaw(envelope.request);
984
+ const requestConfig = buildRequestScopedConfig(envelope.env);
985
+ const oauthCallbackContext = {
986
+ request: oauthRequest,
987
+ invocation,
988
+ log: createContextLogger()
989
+ };
990
+ try {
991
+ const oauthCallbackHook = hooks.oauth_callback;
992
+ const oauthCallbackHandler = typeof oauthCallbackHook === "function" ? oauthCallbackHook : oauthCallbackHook.handler;
993
+ const result = await runWithLogContext({ invocation }, async () => {
994
+ return await runWithConfig(requestConfig, async () => {
995
+ return await oauthCallbackHandler(oauthCallbackContext);
996
+ });
997
+ });
998
+ return {
999
+ status: 200,
1000
+ body: {
1001
+ appInstallationId: result.appInstallationId,
1002
+ env: result.env ?? {}
1003
+ }
1004
+ };
1005
+ } catch (err) {
1006
+ const errorMessage = err instanceof Error ? err.message : String(err ?? "Unknown error");
1007
+ return {
1008
+ status: 500,
1009
+ body: {
1010
+ error: {
1011
+ code: -32603,
1012
+ message: errorMessage
1013
+ }
1014
+ }
1015
+ };
1016
+ }
1017
+ }
1018
+
1019
+ // src/server/handlers/webhook-handler.ts
1020
+ function parseWebhookRequest(parsedBody, method, url, path, headers, query, rawBody, appIdHeader, appVersionIdHeader) {
1021
+ const isEnvelope = typeof parsedBody === "object" && parsedBody !== null && "env" in parsedBody && "request" in parsedBody && "context" in parsedBody;
1022
+ if (isEnvelope) {
1023
+ const envelope = parsedBody;
1024
+ const requestEnv = envelope.env ?? {};
1025
+ const invocation = envelope.invocation;
1026
+ let originalParsedBody = envelope.request.body;
1027
+ const originalContentType = envelope.request.headers["content-type"] ?? "";
1028
+ if (originalContentType.includes("application/json")) {
1029
+ try {
1030
+ originalParsedBody = envelope.request.body ? JSON.parse(envelope.request.body) : {};
1031
+ } catch {
1032
+ }
1033
+ }
1034
+ const webhookRequest2 = {
1035
+ method: envelope.request.method,
1036
+ url: envelope.request.url,
1037
+ path: envelope.request.path,
1038
+ headers: envelope.request.headers,
1039
+ query: envelope.request.query,
1040
+ body: originalParsedBody,
1041
+ rawBody: envelope.request.body ? Buffer.from(envelope.request.body, "utf-8") : void 0
1042
+ };
1043
+ const envVars = { ...process.env, ...requestEnv };
1044
+ const app = envelope.context.app;
1045
+ let webhookContext2;
1046
+ if (envelope.context.appInstallationId && envelope.context.workplace) {
1047
+ webhookContext2 = {
1048
+ env: envVars,
1049
+ app,
1050
+ appInstallationId: envelope.context.appInstallationId,
1051
+ workplace: envelope.context.workplace,
1052
+ registration: envelope.context.registration ?? {},
1053
+ invocation,
1054
+ log: createContextLogger()
1055
+ };
1056
+ } else {
1057
+ webhookContext2 = {
1058
+ env: envVars,
1059
+ app,
1060
+ invocation,
1061
+ log: createContextLogger()
1062
+ };
1063
+ }
1064
+ return { webhookRequest: webhookRequest2, webhookContext: webhookContext2, requestEnv, invocation };
1065
+ }
1066
+ if (!appIdHeader || !appVersionIdHeader) {
1067
+ return {
1068
+ error: "Missing app info in webhook request (x-skedyul-app-id and x-skedyul-app-version-id headers required)"
1069
+ };
1070
+ }
1071
+ const webhookRequest = {
1072
+ method,
1073
+ url,
1074
+ path,
1075
+ headers,
1076
+ query,
1077
+ body: parsedBody,
1078
+ rawBody: rawBody ? Buffer.from(rawBody, "utf-8") : void 0
1079
+ };
1080
+ const webhookContext = {
1081
+ env: process.env,
1082
+ app: { id: appIdHeader, versionId: appVersionIdHeader },
1083
+ log: createContextLogger()
1084
+ };
1085
+ return { webhookRequest, webhookContext, requestEnv: {} };
1086
+ }
1087
+ async function executeWebhookHandler(handle, webhookRegistry, data) {
1088
+ const webhookDef = webhookRegistry[handle];
1089
+ if (!webhookDef) {
1090
+ return {
1091
+ status: 404,
1092
+ body: { error: `Webhook handler '${handle}' not found` }
1093
+ };
1094
+ }
1095
+ const originalEnv = { ...process.env };
1096
+ Object.assign(process.env, data.requestEnv);
1097
+ const requestConfig = {
1098
+ baseUrl: data.requestEnv.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
1099
+ apiToken: data.requestEnv.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
1100
+ };
1101
+ let webhookResponse;
1102
+ try {
1103
+ webhookResponse = await runWithLogContext({ invocation: data.invocation }, async () => {
1104
+ return await runWithConfig(requestConfig, async () => {
1105
+ return await webhookDef.handler(data.webhookRequest, data.webhookContext);
1106
+ });
1107
+ });
1108
+ } catch (err) {
1109
+ console.error(`Webhook handler '${handle}' error:`, err);
1110
+ return {
1111
+ status: 500,
1112
+ body: { error: "Webhook handler error" }
1113
+ };
1114
+ } finally {
1115
+ process.env = originalEnv;
1116
+ }
1117
+ return {
1118
+ status: webhookResponse.status ?? 200,
1119
+ body: webhookResponse.body,
1120
+ headers: webhookResponse.headers
1121
+ };
1122
+ }
1123
+ function isMethodAllowed(webhookRegistry, handle, method) {
1124
+ const webhookDef = webhookRegistry[handle];
1125
+ if (!webhookDef) return false;
1126
+ const allowedMethods = webhookDef.methods ?? ["POST"];
1127
+ return allowedMethods.includes(method);
1128
+ }
1129
+
802
1130
  // src/server/dedicated.ts
803
1131
  function createDedicatedServerInstance(config, tools, callTool, state, mcpServer) {
804
1132
  const port = getListeningPort(config);
@@ -825,13 +1153,11 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
825
1153
  }
826
1154
  if (pathname.startsWith("/webhooks/") && webhookRegistry) {
827
1155
  const handle = pathname.slice("/webhooks/".length);
828
- const webhookDef = webhookRegistry[handle];
829
- if (!webhookDef) {
1156
+ if (!webhookRegistry[handle]) {
830
1157
  sendJSON(res, 404, { error: `Webhook handler '${handle}' not found` });
831
1158
  return;
832
1159
  }
833
- const allowedMethods = webhookDef.methods ?? ["POST"];
834
- if (!allowedMethods.includes(req.method)) {
1160
+ if (!isMethodAllowed(webhookRegistry, handle, req.method ?? "POST")) {
835
1161
  sendJSON(res, 405, { error: `Method ${req.method} not allowed` });
836
1162
  return;
837
1163
  }
@@ -853,87 +1179,34 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
853
1179
  } else {
854
1180
  parsedBody = rawBody;
855
1181
  }
856
- const envelope = parseHandlerEnvelope(parsedBody);
857
- let webhookRequest;
858
- let webhookContext;
859
- let requestEnv = {};
860
- let invocation;
861
- if (envelope && "context" in envelope && envelope.context) {
862
- const context = envelope.context;
863
- requestEnv = envelope.env;
864
- invocation = parsedBody.invocation;
865
- webhookRequest = buildRequestFromRaw(envelope.request);
866
- const envVars = { ...process.env, ...envelope.env };
867
- const app = context.app;
868
- if (context.appInstallationId && context.workplace) {
869
- webhookContext = {
870
- env: envVars,
871
- app,
872
- appInstallationId: context.appInstallationId,
873
- workplace: context.workplace,
874
- registration: context.registration ?? {},
875
- invocation,
876
- log: createContextLogger()
877
- };
878
- } else {
879
- webhookContext = {
880
- env: envVars,
881
- app,
882
- invocation,
883
- log: createContextLogger()
884
- };
885
- }
886
- } else {
887
- const appId = req.headers["x-skedyul-app-id"];
888
- const appVersionId = req.headers["x-skedyul-app-version-id"];
889
- if (!appId || !appVersionId) {
890
- throw new Error("Missing app info in webhook request (x-skedyul-app-id and x-skedyul-app-version-id headers required)");
891
- }
892
- webhookRequest = {
893
- method: req.method ?? "POST",
894
- url: url.toString(),
895
- path: pathname,
896
- headers: req.headers,
897
- query: Object.fromEntries(url.searchParams.entries()),
898
- body: parsedBody,
899
- rawBody: rawBody ? Buffer.from(rawBody, "utf-8") : void 0
900
- };
901
- webhookContext = {
902
- env: process.env,
903
- app: { id: appId, versionId: appVersionId },
904
- log: createContextLogger()
905
- };
906
- }
907
- const originalEnv = { ...process.env };
908
- Object.assign(process.env, requestEnv);
909
- const requestConfig = buildRequestScopedConfig(requestEnv);
910
- let webhookResponse;
911
- try {
912
- webhookResponse = await runWithLogContext({ invocation }, async () => {
913
- return await runWithConfig(requestConfig, async () => {
914
- return await webhookDef.handler(webhookRequest, webhookContext);
915
- });
916
- });
917
- } catch (err) {
918
- console.error(`Webhook handler '${handle}' error:`, err);
919
- sendJSON(res, 500, { error: "Webhook handler error" });
1182
+ const parseResult = parseWebhookRequest(
1183
+ parsedBody,
1184
+ req.method ?? "POST",
1185
+ url.toString(),
1186
+ pathname,
1187
+ req.headers,
1188
+ Object.fromEntries(url.searchParams.entries()),
1189
+ rawBody,
1190
+ req.headers["x-skedyul-app-id"],
1191
+ req.headers["x-skedyul-app-version-id"]
1192
+ );
1193
+ if ("error" in parseResult) {
1194
+ sendJSON(res, 400, { error: parseResult.error });
920
1195
  return;
921
- } finally {
922
- process.env = originalEnv;
923
1196
  }
924
- const status = webhookResponse.status ?? 200;
1197
+ const result = await executeWebhookHandler(handle, webhookRegistry, parseResult);
925
1198
  const responseHeaders = {
926
- ...webhookResponse.headers
1199
+ ...result.headers
927
1200
  };
928
1201
  if (!responseHeaders["Content-Type"] && !responseHeaders["content-type"]) {
929
1202
  responseHeaders["Content-Type"] = "application/json";
930
1203
  }
931
- res.writeHead(status, responseHeaders);
932
- if (webhookResponse.body !== void 0) {
933
- if (typeof webhookResponse.body === "string") {
934
- res.end(webhookResponse.body);
1204
+ res.writeHead(result.status, responseHeaders);
1205
+ if (result.body !== void 0) {
1206
+ if (typeof result.body === "string") {
1207
+ res.end(result.body);
935
1208
  } else {
936
- res.end(JSON.stringify(webhookResponse.body));
1209
+ res.end(JSON.stringify(result.body));
937
1210
  }
938
1211
  } else {
939
1212
  res.end();
@@ -972,10 +1245,6 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
972
1245
  return;
973
1246
  }
974
1247
  if (pathname === "/oauth_callback" && req.method === "POST") {
975
- if (!config.hooks?.oauth_callback) {
976
- sendJSON(res, 404, { error: "OAuth callback handler not configured" });
977
- return;
978
- }
979
1248
  let parsedBody;
980
1249
  try {
981
1250
  parsedBody = await parseJSONBody(req);
@@ -986,50 +1255,11 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
986
1255
  });
987
1256
  return;
988
1257
  }
989
- const envelope = parseHandlerEnvelope(parsedBody);
990
- if (!envelope) {
991
- console.error("[OAuth Callback] Failed to parse envelope. Body:", JSON.stringify(parsedBody, null, 2));
992
- sendJSON(res, 400, {
993
- error: { code: -32602, message: "Missing envelope format: expected { env, request }" }
994
- });
995
- return;
996
- }
997
- const invocation = parsedBody.invocation;
998
- const oauthRequest = buildRequestFromRaw(envelope.request);
999
- const oauthCallbackRequestConfig = buildRequestScopedConfig(envelope.env);
1000
- const oauthCallbackContext = {
1001
- request: oauthRequest,
1002
- invocation,
1003
- log: createContextLogger()
1004
- };
1005
- try {
1006
- const oauthCallbackHook = config.hooks.oauth_callback;
1007
- const oauthCallbackHandler = typeof oauthCallbackHook === "function" ? oauthCallbackHook : oauthCallbackHook.handler;
1008
- const result = await runWithLogContext({ invocation }, async () => {
1009
- return await runWithConfig(oauthCallbackRequestConfig, async () => {
1010
- return await oauthCallbackHandler(oauthCallbackContext);
1011
- });
1012
- });
1013
- sendJSON(res, 200, {
1014
- appInstallationId: result.appInstallationId,
1015
- env: result.env ?? {}
1016
- });
1017
- } catch (err) {
1018
- const errorMessage = err instanceof Error ? err.message : String(err ?? "Unknown error");
1019
- sendJSON(res, 500, {
1020
- error: {
1021
- code: -32603,
1022
- message: errorMessage
1023
- }
1024
- });
1025
- }
1258
+ const result = await handleOAuthCallback(parsedBody, config.hooks);
1259
+ sendJSON(res, result.status, result.body);
1026
1260
  return;
1027
1261
  }
1028
1262
  if (pathname === "/install" && req.method === "POST") {
1029
- if (!config.hooks?.install) {
1030
- sendJSON(res, 404, { error: "Install handler not configured" });
1031
- return;
1032
- }
1033
1263
  let installBody;
1034
1264
  try {
1035
1265
  installBody = await parseJSONBody(req);
@@ -1039,61 +1269,11 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
1039
1269
  });
1040
1270
  return;
1041
1271
  }
1042
- if (!installBody.context?.appInstallationId || !installBody.context?.workplace) {
1043
- sendJSON(res, 400, {
1044
- error: { code: -32602, message: "Missing context (appInstallationId and workplace required)" }
1045
- });
1046
- return;
1047
- }
1048
- const installContext = {
1049
- env: installBody.env ?? {},
1050
- workplace: installBody.context.workplace,
1051
- appInstallationId: installBody.context.appInstallationId,
1052
- app: installBody.context.app,
1053
- invocation: installBody.invocation,
1054
- log: createContextLogger()
1055
- };
1056
- const installRequestConfig = {
1057
- baseUrl: installBody.env?.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
1058
- apiToken: installBody.env?.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
1059
- };
1060
- try {
1061
- const installHook = config.hooks.install;
1062
- const installHandler = typeof installHook === "function" ? installHook : installHook.handler;
1063
- const result = await runWithLogContext({ invocation: installBody.invocation }, async () => {
1064
- return await runWithConfig(installRequestConfig, async () => {
1065
- return await installHandler(installContext);
1066
- });
1067
- });
1068
- sendJSON(res, 200, {
1069
- env: result.env ?? {},
1070
- redirect: result.redirect
1071
- });
1072
- } catch (err) {
1073
- if (err instanceof InstallError) {
1074
- sendJSON(res, 400, {
1075
- error: {
1076
- code: err.code,
1077
- message: err.message,
1078
- field: err.field
1079
- }
1080
- });
1081
- } else {
1082
- sendJSON(res, 500, {
1083
- error: {
1084
- code: -32603,
1085
- message: err instanceof Error ? err.message : String(err ?? "")
1086
- }
1087
- });
1088
- }
1089
- }
1272
+ const result = await handleInstall(installBody, config.hooks);
1273
+ sendJSON(res, result.status, result.body);
1090
1274
  return;
1091
1275
  }
1092
1276
  if (pathname === "/uninstall" && req.method === "POST") {
1093
- if (!config.hooks?.uninstall) {
1094
- sendJSON(res, 404, { error: "Uninstall handler not configured" });
1095
- return;
1096
- }
1097
1277
  let uninstallBody;
1098
1278
  try {
1099
1279
  uninstallBody = await parseJSONBody(req);
@@ -1103,53 +1283,11 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
1103
1283
  });
1104
1284
  return;
1105
1285
  }
1106
- if (!uninstallBody.context?.appInstallationId || !uninstallBody.context?.workplace || !uninstallBody.context?.app) {
1107
- sendJSON(res, 400, {
1108
- error: {
1109
- code: -32602,
1110
- message: "Missing context (appInstallationId, workplace and app required)"
1111
- }
1112
- });
1113
- return;
1114
- }
1115
- const uninstallContext = {
1116
- env: uninstallBody.env ?? {},
1117
- workplace: uninstallBody.context.workplace,
1118
- appInstallationId: uninstallBody.context.appInstallationId,
1119
- app: uninstallBody.context.app,
1120
- invocation: uninstallBody.invocation,
1121
- log: createContextLogger()
1122
- };
1123
- const uninstallRequestConfig = {
1124
- baseUrl: uninstallBody.env?.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
1125
- apiToken: uninstallBody.env?.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
1126
- };
1127
- try {
1128
- const uninstallHook = config.hooks.uninstall;
1129
- const uninstallHandlerFn = typeof uninstallHook === "function" ? uninstallHook : uninstallHook.handler;
1130
- const result = await runWithLogContext({ invocation: uninstallBody.invocation }, async () => {
1131
- return await runWithConfig(uninstallRequestConfig, async () => {
1132
- return await uninstallHandlerFn(uninstallContext);
1133
- });
1134
- });
1135
- sendJSON(res, 200, {
1136
- cleanedWebhookIds: result.cleanedWebhookIds ?? []
1137
- });
1138
- } catch (err) {
1139
- sendJSON(res, 500, {
1140
- error: {
1141
- code: -32603,
1142
- message: err instanceof Error ? err.message : String(err ?? "")
1143
- }
1144
- });
1145
- }
1286
+ const result = await handleUninstall(uninstallBody, config.hooks);
1287
+ sendJSON(res, result.status, result.body);
1146
1288
  return;
1147
1289
  }
1148
1290
  if (pathname === "/provision" && req.method === "POST") {
1149
- if (!config.hooks?.provision) {
1150
- sendJSON(res, 404, { error: "Provision handler not configured" });
1151
- return;
1152
- }
1153
1291
  let provisionBody;
1154
1292
  try {
1155
1293
  provisionBody = await parseJSONBody(req);
@@ -1159,46 +1297,8 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
1159
1297
  });
1160
1298
  return;
1161
1299
  }
1162
- if (!provisionBody.context?.app) {
1163
- sendJSON(res, 400, {
1164
- error: { code: -32602, message: "Missing context (app required)" }
1165
- });
1166
- return;
1167
- }
1168
- const mergedEnv = {};
1169
- for (const [key, value] of Object.entries(process.env)) {
1170
- if (value !== void 0) {
1171
- mergedEnv[key] = value;
1172
- }
1173
- }
1174
- Object.assign(mergedEnv, provisionBody.env ?? {});
1175
- const provisionContext = {
1176
- env: mergedEnv,
1177
- app: provisionBody.context.app,
1178
- invocation: provisionBody.invocation,
1179
- log: createContextLogger()
1180
- };
1181
- const provisionRequestConfig = {
1182
- baseUrl: mergedEnv.SKEDYUL_API_URL ?? "",
1183
- apiToken: mergedEnv.SKEDYUL_API_TOKEN ?? ""
1184
- };
1185
- try {
1186
- const provisionHook = config.hooks.provision;
1187
- const provisionHandler = typeof provisionHook === "function" ? provisionHook : provisionHook.handler;
1188
- const result = await runWithLogContext({ invocation: provisionBody.invocation }, async () => {
1189
- return await runWithConfig(provisionRequestConfig, async () => {
1190
- return await provisionHandler(provisionContext);
1191
- });
1192
- });
1193
- sendJSON(res, 200, result);
1194
- } catch (err) {
1195
- sendJSON(res, 500, {
1196
- error: {
1197
- code: -32603,
1198
- message: err instanceof Error ? err.message : String(err ?? "")
1199
- }
1200
- });
1201
- }
1300
+ const result = await handleProvision(provisionBody, config.hooks);
1301
+ sendJSON(res, result.status, result.body);
1202
1302
  return;
1203
1303
  }
1204
1304
  if (pathname === "/core" && req.method === "POST") {
@@ -1379,12 +1479,10 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
1379
1479
  }
1380
1480
  if (path.startsWith("/webhooks/") && webhookRegistry) {
1381
1481
  const handle = path.slice("/webhooks/".length);
1382
- const webhookDef = webhookRegistry[handle];
1383
- if (!webhookDef) {
1482
+ if (!webhookRegistry[handle]) {
1384
1483
  return createResponse(404, { error: `Webhook handler '${handle}' not found` }, headers);
1385
1484
  }
1386
- const allowedMethods = webhookDef.methods ?? ["POST"];
1387
- if (!allowedMethods.includes(method)) {
1485
+ if (!isMethodAllowed(webhookRegistry, handle, method)) {
1388
1486
  return createResponse(405, { error: `Method ${method} not allowed` }, headers);
1389
1487
  }
1390
1488
  const rawBody = event.body ?? "";
@@ -1399,107 +1497,34 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
1399
1497
  } else {
1400
1498
  parsedBody = rawBody;
1401
1499
  }
1402
- const isEnvelope = typeof parsedBody === "object" && parsedBody !== null && "env" in parsedBody && "request" in parsedBody && "context" in parsedBody;
1403
- let webhookRequest;
1404
- let webhookContext;
1405
- let requestEnv = {};
1406
- let invocation;
1407
- if (isEnvelope) {
1408
- const envelope = parsedBody;
1409
- requestEnv = envelope.env ?? {};
1410
- invocation = envelope.invocation;
1411
- let originalParsedBody = envelope.request.body;
1412
- const originalContentType = envelope.request.headers["content-type"] ?? "";
1413
- if (originalContentType.includes("application/json")) {
1414
- try {
1415
- originalParsedBody = envelope.request.body ? JSON.parse(envelope.request.body) : {};
1416
- } catch {
1417
- }
1418
- }
1419
- webhookRequest = {
1420
- method: envelope.request.method,
1421
- url: envelope.request.url,
1422
- path: envelope.request.path,
1423
- headers: envelope.request.headers,
1424
- query: envelope.request.query,
1425
- body: originalParsedBody,
1426
- rawBody: envelope.request.body ? Buffer.from(envelope.request.body, "utf-8") : void 0
1427
- };
1428
- const envVars = { ...process.env, ...requestEnv };
1429
- const app = envelope.context.app;
1430
- if (envelope.context.appInstallationId && envelope.context.workplace) {
1431
- webhookContext = {
1432
- env: envVars,
1433
- app,
1434
- appInstallationId: envelope.context.appInstallationId,
1435
- workplace: envelope.context.workplace,
1436
- registration: envelope.context.registration ?? {},
1437
- invocation,
1438
- log: createContextLogger()
1439
- };
1440
- } else {
1441
- webhookContext = {
1442
- env: envVars,
1443
- app,
1444
- invocation,
1445
- log: createContextLogger()
1446
- };
1447
- }
1448
- } else {
1449
- const appId = event.headers?.["x-skedyul-app-id"] ?? event.headers?.["X-Skedyul-App-Id"];
1450
- const appVersionId = event.headers?.["x-skedyul-app-version-id"] ?? event.headers?.["X-Skedyul-App-Version-Id"];
1451
- if (!appId || !appVersionId) {
1452
- throw new Error("Missing app info in webhook request (x-skedyul-app-id and x-skedyul-app-version-id headers required)");
1453
- }
1454
- const forwardedProto = event.headers?.["x-forwarded-proto"] ?? event.headers?.["X-Forwarded-Proto"];
1455
- const protocol = forwardedProto ?? "https";
1456
- const host = event.headers?.host ?? event.headers?.Host ?? "localhost";
1457
- const queryString = event.queryStringParameters ? "?" + new URLSearchParams(event.queryStringParameters).toString() : "";
1458
- const webhookUrl = `${protocol}://${host}${path}${queryString}`;
1459
- webhookRequest = {
1460
- method,
1461
- url: webhookUrl,
1462
- path,
1463
- headers: event.headers,
1464
- query: event.queryStringParameters ?? {},
1465
- body: parsedBody,
1466
- rawBody: rawBody ? Buffer.from(rawBody, "utf-8") : void 0
1467
- };
1468
- webhookContext = {
1469
- env: process.env,
1470
- app: { id: appId, versionId: appVersionId },
1471
- log: createContextLogger()
1472
- };
1473
- }
1474
- const originalEnv = { ...process.env };
1475
- Object.assign(process.env, requestEnv);
1476
- const requestConfig = {
1477
- baseUrl: requestEnv.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
1478
- apiToken: requestEnv.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
1479
- };
1480
- let webhookResponse;
1481
- try {
1482
- webhookResponse = await runWithLogContext({ invocation }, async () => {
1483
- return await runWithConfig(requestConfig, async () => {
1484
- return await webhookDef.handler(webhookRequest, webhookContext);
1485
- });
1486
- });
1487
- } catch (err) {
1488
- console.error(`Webhook handler '${handle}' error:`, err);
1489
- return createResponse(500, { error: "Webhook handler error" }, headers);
1490
- } finally {
1491
- process.env = originalEnv;
1500
+ const forwardedProto = event.headers?.["x-forwarded-proto"] ?? event.headers?.["X-Forwarded-Proto"];
1501
+ const protocol = forwardedProto ?? "https";
1502
+ const host = event.headers?.host ?? event.headers?.Host ?? "localhost";
1503
+ const queryString = event.queryStringParameters ? "?" + new URLSearchParams(event.queryStringParameters).toString() : "";
1504
+ const webhookUrl = `${protocol}://${host}${path}${queryString}`;
1505
+ const parseResult = parseWebhookRequest(
1506
+ parsedBody,
1507
+ method,
1508
+ webhookUrl,
1509
+ path,
1510
+ event.headers,
1511
+ event.queryStringParameters ?? {},
1512
+ rawBody,
1513
+ event.headers?.["x-skedyul-app-id"] ?? event.headers?.["X-Skedyul-App-Id"],
1514
+ event.headers?.["x-skedyul-app-version-id"] ?? event.headers?.["X-Skedyul-App-Version-Id"]
1515
+ );
1516
+ if ("error" in parseResult) {
1517
+ return createResponse(400, { error: parseResult.error }, headers);
1492
1518
  }
1519
+ const result = await executeWebhookHandler(handle, webhookRegistry, parseResult);
1493
1520
  const responseHeaders = {
1494
1521
  ...headers,
1495
- ...webhookResponse.headers
1522
+ ...result.headers
1496
1523
  };
1497
- const status = webhookResponse.status ?? 200;
1498
- const body = webhookResponse.body;
1499
1524
  return {
1500
- statusCode: status,
1525
+ statusCode: result.status,
1501
1526
  headers: responseHeaders,
1502
- body: body !== void 0 ? typeof body === "string" ? body : JSON.stringify(body) : ""
1527
+ body: result.body !== void 0 ? typeof result.body === "string" ? result.body : JSON.stringify(result.body) : ""
1503
1528
  };
1504
1529
  }
1505
1530
  if (path === "/core" && method === "POST") {
@@ -1635,9 +1660,6 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
1635
1660
  }
1636
1661
  }
1637
1662
  if (path === "/install" && method === "POST") {
1638
- if (!config.hooks?.install) {
1639
- return createResponse(404, { error: "Install handler not configured" }, headers);
1640
- }
1641
1663
  let installBody;
1642
1664
  try {
1643
1665
  installBody = event.body ? JSON.parse(event.body) : {};
@@ -1648,68 +1670,10 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
1648
1670
  headers
1649
1671
  );
1650
1672
  }
1651
- if (!installBody.context?.appInstallationId || !installBody.context?.workplace) {
1652
- return createResponse(
1653
- 400,
1654
- { error: { code: -32602, message: "Missing context (appInstallationId and workplace required)" } },
1655
- headers
1656
- );
1657
- }
1658
- const installContext = {
1659
- env: installBody.env ?? {},
1660
- workplace: installBody.context.workplace,
1661
- appInstallationId: installBody.context.appInstallationId,
1662
- app: installBody.context.app,
1663
- invocation: installBody.invocation,
1664
- log: createContextLogger()
1665
- };
1666
- const installRequestConfig = {
1667
- baseUrl: installBody.env?.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
1668
- apiToken: installBody.env?.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
1669
- };
1670
- try {
1671
- const installHook = config.hooks.install;
1672
- const installHandler = typeof installHook === "function" ? installHook : installHook.handler;
1673
- const result = await runWithLogContext({ invocation: installBody.invocation }, async () => {
1674
- return await runWithConfig(installRequestConfig, async () => {
1675
- return await installHandler(installContext);
1676
- });
1677
- });
1678
- return createResponse(
1679
- 200,
1680
- { env: result.env ?? {}, redirect: result.redirect },
1681
- headers
1682
- );
1683
- } catch (err) {
1684
- if (err instanceof InstallError) {
1685
- return createResponse(
1686
- 400,
1687
- {
1688
- error: {
1689
- code: err.code,
1690
- message: err.message,
1691
- field: err.field
1692
- }
1693
- },
1694
- headers
1695
- );
1696
- }
1697
- return createResponse(
1698
- 500,
1699
- {
1700
- error: {
1701
- code: -32603,
1702
- message: err instanceof Error ? err.message : String(err ?? "")
1703
- }
1704
- },
1705
- headers
1706
- );
1707
- }
1673
+ const result = await handleInstall(installBody, config.hooks);
1674
+ return createResponse(result.status, result.body, headers);
1708
1675
  }
1709
1676
  if (path === "/uninstall" && method === "POST") {
1710
- if (!config.hooks?.uninstall) {
1711
- return createResponse(404, { error: "Uninstall handler not configured" }, headers);
1712
- }
1713
1677
  let uninstallBody;
1714
1678
  try {
1715
1679
  uninstallBody = event.body ? JSON.parse(event.body) : {};
@@ -1720,137 +1684,24 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
1720
1684
  headers
1721
1685
  );
1722
1686
  }
1723
- if (!uninstallBody.context?.appInstallationId || !uninstallBody.context?.workplace || !uninstallBody.context?.app) {
1724
- return createResponse(
1725
- 400,
1726
- {
1727
- error: {
1728
- code: -32602,
1729
- message: "Missing context (appInstallationId, workplace and app required)"
1730
- }
1731
- },
1732
- headers
1733
- );
1734
- }
1735
- const uninstallContext = {
1736
- env: uninstallBody.env ?? {},
1737
- workplace: uninstallBody.context.workplace,
1738
- appInstallationId: uninstallBody.context.appInstallationId,
1739
- app: uninstallBody.context.app,
1740
- invocation: uninstallBody.invocation,
1741
- log: createContextLogger()
1742
- };
1743
- const uninstallRequestConfig = {
1744
- baseUrl: uninstallBody.env?.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
1745
- apiToken: uninstallBody.env?.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
1746
- };
1747
- try {
1748
- const uninstallHook = config.hooks.uninstall;
1749
- const uninstallHandlerFn = typeof uninstallHook === "function" ? uninstallHook : uninstallHook.handler;
1750
- const result = await runWithLogContext({ invocation: uninstallBody.invocation }, async () => {
1751
- return await runWithConfig(uninstallRequestConfig, async () => {
1752
- return await uninstallHandlerFn(uninstallContext);
1753
- });
1754
- });
1755
- return createResponse(
1756
- 200,
1757
- { cleanedWebhookIds: result.cleanedWebhookIds ?? [] },
1758
- headers
1759
- );
1760
- } catch (err) {
1761
- return createResponse(
1762
- 500,
1763
- {
1764
- error: {
1765
- code: -32603,
1766
- message: err instanceof Error ? err.message : String(err ?? "")
1767
- }
1768
- },
1769
- headers
1770
- );
1771
- }
1687
+ const result = await handleUninstall(uninstallBody, config.hooks);
1688
+ return createResponse(result.status, result.body, headers);
1772
1689
  }
1773
1690
  if (path === "/provision" && method === "POST") {
1774
- console.log("[serverless] /provision endpoint called");
1775
- if (!config.hooks?.provision) {
1776
- console.log("[serverless] No provision handler configured");
1777
- return createResponse(404, { error: "Provision handler not configured" }, headers);
1778
- }
1779
1691
  let provisionBody;
1780
1692
  try {
1781
1693
  provisionBody = event.body ? JSON.parse(event.body) : {};
1782
- console.log("[serverless] Provision body parsed:", {
1783
- hasEnv: !!provisionBody.env,
1784
- hasContext: !!provisionBody.context,
1785
- appId: provisionBody.context?.app?.id,
1786
- versionId: provisionBody.context?.app?.versionId
1787
- });
1788
1694
  } catch {
1789
- console.log("[serverless] Failed to parse provision body");
1790
1695
  return createResponse(
1791
1696
  400,
1792
1697
  { error: { code: -32700, message: "Parse error" } },
1793
1698
  headers
1794
1699
  );
1795
1700
  }
1796
- if (!provisionBody.context?.app) {
1797
- console.log("[serverless] Missing app context in provision body");
1798
- return createResponse(
1799
- 400,
1800
- { error: { code: -32602, message: "Missing context (app required)" } },
1801
- headers
1802
- );
1803
- }
1804
- const mergedEnv = {};
1805
- for (const [key, value] of Object.entries(process.env)) {
1806
- if (value !== void 0) {
1807
- mergedEnv[key] = value;
1808
- }
1809
- }
1810
- Object.assign(mergedEnv, provisionBody.env ?? {});
1811
- const provisionContext = {
1812
- env: mergedEnv,
1813
- app: provisionBody.context.app,
1814
- invocation: provisionBody.invocation,
1815
- log: createContextLogger()
1816
- };
1817
- const provisionRequestConfig = {
1818
- baseUrl: mergedEnv.SKEDYUL_API_URL ?? "",
1819
- apiToken: mergedEnv.SKEDYUL_API_TOKEN ?? ""
1820
- };
1821
- console.log("[serverless] Calling provision handler...");
1822
- try {
1823
- const provisionHook = config.hooks.provision;
1824
- const provisionHandler = typeof provisionHook === "function" ? provisionHook : provisionHook.handler;
1825
- const result = await runWithLogContext({ invocation: provisionBody.invocation }, async () => {
1826
- return await runWithConfig(provisionRequestConfig, async () => {
1827
- return await provisionHandler(provisionContext);
1828
- });
1829
- });
1830
- console.log("[serverless] Provision handler completed successfully");
1831
- return createResponse(200, result, headers);
1832
- } catch (err) {
1833
- console.error("[serverless] Provision handler failed:", err instanceof Error ? err.message : String(err));
1834
- return createResponse(
1835
- 500,
1836
- {
1837
- error: {
1838
- code: -32603,
1839
- message: err instanceof Error ? err.message : String(err ?? "")
1840
- }
1841
- },
1842
- headers
1843
- );
1844
- }
1701
+ const result = await handleProvision(provisionBody, config.hooks);
1702
+ return createResponse(result.status, result.body, headers);
1845
1703
  }
1846
1704
  if (path === "/oauth_callback" && method === "POST") {
1847
- if (!config.hooks?.oauth_callback) {
1848
- return createResponse(
1849
- 404,
1850
- { error: "OAuth callback handler not configured" },
1851
- headers
1852
- );
1853
- }
1854
1705
  let parsedBody;
1855
1706
  try {
1856
1707
  parsedBody = event.body ? JSON.parse(event.body) : {};
@@ -1862,52 +1713,8 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
1862
1713
  headers
1863
1714
  );
1864
1715
  }
1865
- const envelope = parseHandlerEnvelope(parsedBody);
1866
- if (!envelope) {
1867
- console.error("[OAuth Callback] Failed to parse envelope. Body:", JSON.stringify(parsedBody, null, 2));
1868
- return createResponse(
1869
- 400,
1870
- { error: { code: -32602, message: "Missing envelope format: expected { env, request }" } },
1871
- headers
1872
- );
1873
- }
1874
- const invocation = parsedBody.invocation;
1875
- const oauthRequest = buildRequestFromRaw(envelope.request);
1876
- const oauthCallbackRequestConfig = buildRequestScopedConfig(envelope.env);
1877
- const oauthCallbackContext = {
1878
- request: oauthRequest,
1879
- invocation,
1880
- log: createContextLogger()
1881
- };
1882
- try {
1883
- const oauthCallbackHook = config.hooks.oauth_callback;
1884
- const oauthCallbackHandler = typeof oauthCallbackHook === "function" ? oauthCallbackHook : oauthCallbackHook.handler;
1885
- const result = await runWithLogContext({ invocation }, async () => {
1886
- return await runWithConfig(oauthCallbackRequestConfig, async () => {
1887
- return await oauthCallbackHandler(oauthCallbackContext);
1888
- });
1889
- });
1890
- return createResponse(
1891
- 200,
1892
- {
1893
- appInstallationId: result.appInstallationId,
1894
- env: result.env ?? {}
1895
- },
1896
- headers
1897
- );
1898
- } catch (err) {
1899
- const errorMessage = err instanceof Error ? err.message : String(err ?? "Unknown error");
1900
- return createResponse(
1901
- 500,
1902
- {
1903
- error: {
1904
- code: -32603,
1905
- message: errorMessage
1906
- }
1907
- },
1908
- headers
1909
- );
1910
- }
1716
+ const result = await handleOAuthCallback(parsedBody, config.hooks);
1717
+ return createResponse(result.status, result.body, headers);
1911
1718
  }
1912
1719
  if (path === "/health" && method === "GET") {
1913
1720
  return createResponse(200, state.getHealthStatus(), headers);
@@ -2302,32 +2109,10 @@ var server = {
2302
2109
  };
2303
2110
  // Annotate the CommonJS export names for ESM import in node:
2304
2111
  0 && (module.exports = {
2305
- buildRequestFromRaw,
2306
- buildRequestScopedConfig,
2307
- buildToolMetadata,
2308
- createCallToolHandler,
2309
- createDedicatedServerInstance,
2310
- createRequestState,
2311
- createResponse,
2312
- createServerlessInstance,
2313
2112
  createSkedyulServer,
2314
- getDefaultHeaders,
2315
2113
  getJsonSchemaFromToolSchema,
2316
- getListeningPort,
2317
2114
  getZodSchema,
2318
- handleCoreMethod,
2319
2115
  isToolSchemaWithJson,
2320
- mergeRuntimeEnv,
2321
- normalizeBilling,
2322
- padEnd,
2323
- parseHandlerEnvelope,
2324
- parseJSONBody,
2325
- parseJsonRecord,
2326
- parseNumberEnv,
2327
- printStartupLog,
2328
- readRawRequestBody,
2329
- sendHTML,
2330
- sendJSON,
2331
2116
  server,
2332
2117
  toJsonSchema
2333
2118
  });