skedyul 1.2.21 → 1.2.24

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/dist/cli/index.js CHANGED
@@ -2574,8 +2574,8 @@ function createRequestState(maxRequests, ttlExtendSeconds, runtimeLabel, toolNam
2574
2574
  };
2575
2575
  }
2576
2576
  function createCallToolHandler(registry, state, onMaxRequests) {
2577
- return async function callTool(nameRaw, argsRaw) {
2578
- const toolName = String(nameRaw);
2577
+ return async function callTool(toolNameInput, toolArgsInput) {
2578
+ const toolName = String(toolNameInput);
2579
2579
  const tool = registry[toolName];
2580
2580
  if (!tool) {
2581
2581
  throw new Error(`Tool "${toolName}" not found in registry`);
@@ -2584,7 +2584,7 @@ function createCallToolHandler(registry, state, onMaxRequests) {
2584
2584
  throw new Error(`Tool "${toolName}" handler is not a function`);
2585
2585
  }
2586
2586
  const fn = tool.handler;
2587
- const args2 = argsRaw ?? {};
2587
+ const args2 = toolArgsInput ?? {};
2588
2588
  const estimateMode = args2.estimate === true;
2589
2589
  if (!estimateMode) {
2590
2590
  state.incrementRequestCount();
@@ -2839,51 +2839,6 @@ async function handleCoreMethod(method, params) {
2839
2839
  };
2840
2840
  }
2841
2841
 
2842
- // src/server/handler-helpers.ts
2843
- function parseHandlerEnvelope(parsedBody) {
2844
- if (typeof parsedBody !== "object" || parsedBody === null || Array.isArray(parsedBody) || !("env" in parsedBody) || !("request" in parsedBody)) {
2845
- return null;
2846
- }
2847
- const envelope = parsedBody;
2848
- if (typeof envelope.env !== "object" || envelope.env === null || Array.isArray(envelope.env)) {
2849
- return null;
2850
- }
2851
- if (typeof envelope.request !== "object" || envelope.request === null || Array.isArray(envelope.request)) {
2852
- return null;
2853
- }
2854
- return {
2855
- env: envelope.env,
2856
- request: envelope.request,
2857
- context: envelope.context
2858
- };
2859
- }
2860
- function buildRequestFromRaw(raw) {
2861
- let parsedBody = raw.body;
2862
- const contentType = raw.headers["content-type"] ?? "";
2863
- if (contentType.includes("application/json")) {
2864
- try {
2865
- parsedBody = raw.body ? JSON.parse(raw.body) : {};
2866
- } catch {
2867
- parsedBody = raw.body;
2868
- }
2869
- }
2870
- return {
2871
- method: raw.method,
2872
- url: raw.url,
2873
- path: raw.path,
2874
- headers: raw.headers,
2875
- query: raw.query,
2876
- body: parsedBody,
2877
- rawBody: raw.body ? Buffer.from(raw.body, "utf-8") : void 0
2878
- };
2879
- }
2880
- function buildRequestScopedConfig(env) {
2881
- return {
2882
- baseUrl: env.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
2883
- apiToken: env.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
2884
- };
2885
- }
2886
-
2887
2842
  // src/server/startup-logger.ts
2888
2843
  function padEnd(str, length) {
2889
2844
  if (str.length >= length) {
@@ -2981,6 +2936,405 @@ function isProvisionConfig(value) {
2981
2936
  return value !== void 0 && value !== null && !(value instanceof Promise);
2982
2937
  }
2983
2938
 
2939
+ // src/server/handlers/install-handler.ts
2940
+ async function handleInstall(body, hooks) {
2941
+ if (!hooks?.install) {
2942
+ return {
2943
+ status: 404,
2944
+ body: { error: "Install handler not configured" }
2945
+ };
2946
+ }
2947
+ if (!body.context?.appInstallationId || !body.context?.workplace) {
2948
+ return {
2949
+ status: 400,
2950
+ body: {
2951
+ error: {
2952
+ code: -32602,
2953
+ message: "Missing context (appInstallationId and workplace required)"
2954
+ }
2955
+ }
2956
+ };
2957
+ }
2958
+ const installContext = {
2959
+ env: body.env ?? {},
2960
+ workplace: body.context.workplace,
2961
+ appInstallationId: body.context.appInstallationId,
2962
+ app: body.context.app,
2963
+ invocation: body.invocation,
2964
+ log: createContextLogger()
2965
+ };
2966
+ const requestConfig = {
2967
+ baseUrl: body.env?.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
2968
+ apiToken: body.env?.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
2969
+ };
2970
+ try {
2971
+ const installHook = hooks.install;
2972
+ const installHandler = typeof installHook === "function" ? installHook : installHook.handler;
2973
+ const result = await runWithLogContext({ invocation: body.invocation }, async () => {
2974
+ return await runWithConfig(requestConfig, async () => {
2975
+ return await installHandler(installContext);
2976
+ });
2977
+ });
2978
+ return {
2979
+ status: 200,
2980
+ body: { env: result.env ?? {}, redirect: result.redirect }
2981
+ };
2982
+ } catch (err) {
2983
+ if (err instanceof InstallError) {
2984
+ return {
2985
+ status: 400,
2986
+ body: {
2987
+ error: {
2988
+ code: err.code,
2989
+ message: err.message,
2990
+ field: err.field
2991
+ }
2992
+ }
2993
+ };
2994
+ }
2995
+ return {
2996
+ status: 500,
2997
+ body: {
2998
+ error: {
2999
+ code: -32603,
3000
+ message: err instanceof Error ? err.message : String(err ?? "")
3001
+ }
3002
+ }
3003
+ };
3004
+ }
3005
+ }
3006
+
3007
+ // src/server/handlers/uninstall-handler.ts
3008
+ async function handleUninstall(body, hooks) {
3009
+ if (!hooks?.uninstall) {
3010
+ return {
3011
+ status: 404,
3012
+ body: { error: "Uninstall handler not configured" }
3013
+ };
3014
+ }
3015
+ if (!body.context?.appInstallationId || !body.context?.workplace || !body.context?.app) {
3016
+ return {
3017
+ status: 400,
3018
+ body: {
3019
+ error: {
3020
+ code: -32602,
3021
+ message: "Missing context (appInstallationId, workplace and app required)"
3022
+ }
3023
+ }
3024
+ };
3025
+ }
3026
+ const uninstallContext = {
3027
+ env: body.env ?? {},
3028
+ workplace: body.context.workplace,
3029
+ appInstallationId: body.context.appInstallationId,
3030
+ app: body.context.app,
3031
+ invocation: body.invocation,
3032
+ log: createContextLogger()
3033
+ };
3034
+ const requestConfig = {
3035
+ baseUrl: body.env?.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
3036
+ apiToken: body.env?.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
3037
+ };
3038
+ try {
3039
+ const uninstallHook = hooks.uninstall;
3040
+ const uninstallHandlerFn = typeof uninstallHook === "function" ? uninstallHook : uninstallHook.handler;
3041
+ const result = await runWithLogContext({ invocation: body.invocation }, async () => {
3042
+ return await runWithConfig(requestConfig, async () => {
3043
+ return await uninstallHandlerFn(uninstallContext);
3044
+ });
3045
+ });
3046
+ return {
3047
+ status: 200,
3048
+ body: { cleanedWebhookIds: result.cleanedWebhookIds ?? [] }
3049
+ };
3050
+ } catch (err) {
3051
+ return {
3052
+ status: 500,
3053
+ body: {
3054
+ error: {
3055
+ code: -32603,
3056
+ message: err instanceof Error ? err.message : String(err ?? "")
3057
+ }
3058
+ }
3059
+ };
3060
+ }
3061
+ }
3062
+
3063
+ // src/server/handlers/provision-handler.ts
3064
+ async function handleProvision(body, hooks) {
3065
+ if (!hooks?.provision) {
3066
+ return {
3067
+ status: 404,
3068
+ body: { error: "Provision handler not configured" }
3069
+ };
3070
+ }
3071
+ if (!body.context?.app) {
3072
+ return {
3073
+ status: 400,
3074
+ body: {
3075
+ error: {
3076
+ code: -32602,
3077
+ message: "Missing context (app required)"
3078
+ }
3079
+ }
3080
+ };
3081
+ }
3082
+ const mergedEnv = {};
3083
+ for (const [key, value] of Object.entries(process.env)) {
3084
+ if (value !== void 0) {
3085
+ mergedEnv[key] = value;
3086
+ }
3087
+ }
3088
+ Object.assign(mergedEnv, body.env ?? {});
3089
+ const provisionContext = {
3090
+ env: mergedEnv,
3091
+ app: body.context.app,
3092
+ invocation: body.invocation,
3093
+ log: createContextLogger()
3094
+ };
3095
+ const requestConfig = {
3096
+ baseUrl: mergedEnv.SKEDYUL_API_URL ?? "",
3097
+ apiToken: mergedEnv.SKEDYUL_API_TOKEN ?? ""
3098
+ };
3099
+ try {
3100
+ const provisionHook = hooks.provision;
3101
+ const provisionHandler = typeof provisionHook === "function" ? provisionHook : provisionHook.handler;
3102
+ const result = await runWithLogContext({ invocation: body.invocation }, async () => {
3103
+ return await runWithConfig(requestConfig, async () => {
3104
+ return await provisionHandler(provisionContext);
3105
+ });
3106
+ });
3107
+ return {
3108
+ status: 200,
3109
+ body: result
3110
+ };
3111
+ } catch (err) {
3112
+ return {
3113
+ status: 500,
3114
+ body: {
3115
+ error: {
3116
+ code: -32603,
3117
+ message: err instanceof Error ? err.message : String(err ?? "")
3118
+ }
3119
+ }
3120
+ };
3121
+ }
3122
+ }
3123
+
3124
+ // src/server/handler-helpers.ts
3125
+ function parseHandlerEnvelope(parsedBody) {
3126
+ if (typeof parsedBody !== "object" || parsedBody === null || Array.isArray(parsedBody) || !("env" in parsedBody) || !("request" in parsedBody)) {
3127
+ return null;
3128
+ }
3129
+ const envelope = parsedBody;
3130
+ if (typeof envelope.env !== "object" || envelope.env === null || Array.isArray(envelope.env)) {
3131
+ return null;
3132
+ }
3133
+ if (typeof envelope.request !== "object" || envelope.request === null || Array.isArray(envelope.request)) {
3134
+ return null;
3135
+ }
3136
+ return {
3137
+ env: envelope.env,
3138
+ request: envelope.request,
3139
+ context: envelope.context
3140
+ };
3141
+ }
3142
+ function buildRequestFromRaw(raw) {
3143
+ let parsedBody = raw.body;
3144
+ const contentType = raw.headers["content-type"] ?? "";
3145
+ if (contentType.includes("application/json")) {
3146
+ try {
3147
+ parsedBody = raw.body ? JSON.parse(raw.body) : {};
3148
+ } catch {
3149
+ parsedBody = raw.body;
3150
+ }
3151
+ }
3152
+ return {
3153
+ method: raw.method,
3154
+ url: raw.url,
3155
+ path: raw.path,
3156
+ headers: raw.headers,
3157
+ query: raw.query,
3158
+ body: parsedBody,
3159
+ rawBody: raw.body ? Buffer.from(raw.body, "utf-8") : void 0
3160
+ };
3161
+ }
3162
+ function buildRequestScopedConfig(env) {
3163
+ return {
3164
+ baseUrl: env.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
3165
+ apiToken: env.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
3166
+ };
3167
+ }
3168
+
3169
+ // src/server/handlers/oauth-callback-handler.ts
3170
+ async function handleOAuthCallback(parsedBody, hooks) {
3171
+ if (!hooks?.oauth_callback) {
3172
+ return {
3173
+ status: 404,
3174
+ body: { error: "OAuth callback handler not configured" }
3175
+ };
3176
+ }
3177
+ const envelope = parseHandlerEnvelope(parsedBody);
3178
+ if (!envelope) {
3179
+ console.error("[OAuth Callback] Failed to parse envelope. Body:", JSON.stringify(parsedBody, null, 2));
3180
+ return {
3181
+ status: 400,
3182
+ body: {
3183
+ error: {
3184
+ code: -32602,
3185
+ message: "Missing envelope format: expected { env, request }"
3186
+ }
3187
+ }
3188
+ };
3189
+ }
3190
+ const invocation = parsedBody.invocation;
3191
+ const oauthRequest = buildRequestFromRaw(envelope.request);
3192
+ const requestConfig = buildRequestScopedConfig(envelope.env);
3193
+ const oauthCallbackContext = {
3194
+ request: oauthRequest,
3195
+ invocation,
3196
+ log: createContextLogger()
3197
+ };
3198
+ try {
3199
+ const oauthCallbackHook = hooks.oauth_callback;
3200
+ const oauthCallbackHandler = typeof oauthCallbackHook === "function" ? oauthCallbackHook : oauthCallbackHook.handler;
3201
+ const result = await runWithLogContext({ invocation }, async () => {
3202
+ return await runWithConfig(requestConfig, async () => {
3203
+ return await oauthCallbackHandler(oauthCallbackContext);
3204
+ });
3205
+ });
3206
+ return {
3207
+ status: 200,
3208
+ body: {
3209
+ appInstallationId: result.appInstallationId,
3210
+ env: result.env ?? {}
3211
+ }
3212
+ };
3213
+ } catch (err) {
3214
+ const errorMessage = err instanceof Error ? err.message : String(err ?? "Unknown error");
3215
+ return {
3216
+ status: 500,
3217
+ body: {
3218
+ error: {
3219
+ code: -32603,
3220
+ message: errorMessage
3221
+ }
3222
+ }
3223
+ };
3224
+ }
3225
+ }
3226
+
3227
+ // src/server/handlers/webhook-handler.ts
3228
+ function parseWebhookRequest(parsedBody, method, url, path14, headers, query, rawBody, appIdHeader, appVersionIdHeader) {
3229
+ const isEnvelope = typeof parsedBody === "object" && parsedBody !== null && "env" in parsedBody && "request" in parsedBody && "context" in parsedBody;
3230
+ if (isEnvelope) {
3231
+ const envelope = parsedBody;
3232
+ const requestEnv = envelope.env ?? {};
3233
+ const invocation = envelope.invocation;
3234
+ let originalParsedBody = envelope.request.body;
3235
+ const originalContentType = envelope.request.headers["content-type"] ?? "";
3236
+ if (originalContentType.includes("application/json")) {
3237
+ try {
3238
+ originalParsedBody = envelope.request.body ? JSON.parse(envelope.request.body) : {};
3239
+ } catch {
3240
+ }
3241
+ }
3242
+ const webhookRequest2 = {
3243
+ method: envelope.request.method,
3244
+ url: envelope.request.url,
3245
+ path: envelope.request.path,
3246
+ headers: envelope.request.headers,
3247
+ query: envelope.request.query,
3248
+ body: originalParsedBody,
3249
+ rawBody: envelope.request.body ? Buffer.from(envelope.request.body, "utf-8") : void 0
3250
+ };
3251
+ const envVars = { ...process.env, ...requestEnv };
3252
+ const app = envelope.context.app;
3253
+ let webhookContext2;
3254
+ if (envelope.context.appInstallationId && envelope.context.workplace) {
3255
+ webhookContext2 = {
3256
+ env: envVars,
3257
+ app,
3258
+ appInstallationId: envelope.context.appInstallationId,
3259
+ workplace: envelope.context.workplace,
3260
+ registration: envelope.context.registration ?? {},
3261
+ invocation,
3262
+ log: createContextLogger()
3263
+ };
3264
+ } else {
3265
+ webhookContext2 = {
3266
+ env: envVars,
3267
+ app,
3268
+ invocation,
3269
+ log: createContextLogger()
3270
+ };
3271
+ }
3272
+ return { webhookRequest: webhookRequest2, webhookContext: webhookContext2, requestEnv, invocation };
3273
+ }
3274
+ if (!appIdHeader || !appVersionIdHeader) {
3275
+ return {
3276
+ error: "Missing app info in webhook request (x-skedyul-app-id and x-skedyul-app-version-id headers required)"
3277
+ };
3278
+ }
3279
+ const webhookRequest = {
3280
+ method,
3281
+ url,
3282
+ path: path14,
3283
+ headers,
3284
+ query,
3285
+ body: parsedBody,
3286
+ rawBody: rawBody ? Buffer.from(rawBody, "utf-8") : void 0
3287
+ };
3288
+ const webhookContext = {
3289
+ env: process.env,
3290
+ app: { id: appIdHeader, versionId: appVersionIdHeader },
3291
+ log: createContextLogger()
3292
+ };
3293
+ return { webhookRequest, webhookContext, requestEnv: {} };
3294
+ }
3295
+ async function executeWebhookHandler(handle, webhookRegistry, data) {
3296
+ const webhookDef = webhookRegistry[handle];
3297
+ if (!webhookDef) {
3298
+ return {
3299
+ status: 404,
3300
+ body: { error: `Webhook handler '${handle}' not found` }
3301
+ };
3302
+ }
3303
+ const originalEnv = { ...process.env };
3304
+ Object.assign(process.env, data.requestEnv);
3305
+ const requestConfig = {
3306
+ baseUrl: data.requestEnv.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
3307
+ apiToken: data.requestEnv.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
3308
+ };
3309
+ let webhookResponse;
3310
+ try {
3311
+ webhookResponse = await runWithLogContext({ invocation: data.invocation }, async () => {
3312
+ return await runWithConfig(requestConfig, async () => {
3313
+ return await webhookDef.handler(data.webhookRequest, data.webhookContext);
3314
+ });
3315
+ });
3316
+ } catch (err) {
3317
+ console.error(`Webhook handler '${handle}' error:`, err);
3318
+ return {
3319
+ status: 500,
3320
+ body: { error: "Webhook handler error" }
3321
+ };
3322
+ } finally {
3323
+ process.env = originalEnv;
3324
+ }
3325
+ return {
3326
+ status: webhookResponse.status ?? 200,
3327
+ body: webhookResponse.body,
3328
+ headers: webhookResponse.headers
3329
+ };
3330
+ }
3331
+ function isMethodAllowed(webhookRegistry, handle, method) {
3332
+ const webhookDef = webhookRegistry[handle];
3333
+ if (!webhookDef) return false;
3334
+ const allowedMethods = webhookDef.methods ?? ["POST"];
3335
+ return allowedMethods.includes(method);
3336
+ }
3337
+
2984
3338
  // src/server/dedicated.ts
2985
3339
  function createDedicatedServerInstance(config, tools, callTool, state, mcpServer) {
2986
3340
  const port = getListeningPort(config);
@@ -3007,13 +3361,11 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
3007
3361
  }
3008
3362
  if (pathname.startsWith("/webhooks/") && webhookRegistry) {
3009
3363
  const handle = pathname.slice("/webhooks/".length);
3010
- const webhookDef = webhookRegistry[handle];
3011
- if (!webhookDef) {
3364
+ if (!webhookRegistry[handle]) {
3012
3365
  sendJSON(res, 404, { error: `Webhook handler '${handle}' not found` });
3013
3366
  return;
3014
3367
  }
3015
- const allowedMethods = webhookDef.methods ?? ["POST"];
3016
- if (!allowedMethods.includes(req.method)) {
3368
+ if (!isMethodAllowed(webhookRegistry, handle, req.method ?? "POST")) {
3017
3369
  sendJSON(res, 405, { error: `Method ${req.method} not allowed` });
3018
3370
  return;
3019
3371
  }
@@ -3035,87 +3387,34 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
3035
3387
  } else {
3036
3388
  parsedBody = rawBody;
3037
3389
  }
3038
- const envelope = parseHandlerEnvelope(parsedBody);
3039
- let webhookRequest;
3040
- let webhookContext;
3041
- let requestEnv = {};
3042
- let invocation;
3043
- if (envelope && "context" in envelope && envelope.context) {
3044
- const context = envelope.context;
3045
- requestEnv = envelope.env;
3046
- invocation = parsedBody.invocation;
3047
- webhookRequest = buildRequestFromRaw(envelope.request);
3048
- const envVars = { ...process.env, ...envelope.env };
3049
- const app = context.app;
3050
- if (context.appInstallationId && context.workplace) {
3051
- webhookContext = {
3052
- env: envVars,
3053
- app,
3054
- appInstallationId: context.appInstallationId,
3055
- workplace: context.workplace,
3056
- registration: context.registration ?? {},
3057
- invocation,
3058
- log: createContextLogger()
3059
- };
3060
- } else {
3061
- webhookContext = {
3062
- env: envVars,
3063
- app,
3064
- invocation,
3065
- log: createContextLogger()
3066
- };
3067
- }
3068
- } else {
3069
- const appId = req.headers["x-skedyul-app-id"];
3070
- const appVersionId = req.headers["x-skedyul-app-version-id"];
3071
- if (!appId || !appVersionId) {
3072
- throw new Error("Missing app info in webhook request (x-skedyul-app-id and x-skedyul-app-version-id headers required)");
3073
- }
3074
- webhookRequest = {
3075
- method: req.method ?? "POST",
3076
- url: url.toString(),
3077
- path: pathname,
3078
- headers: req.headers,
3079
- query: Object.fromEntries(url.searchParams.entries()),
3080
- body: parsedBody,
3081
- rawBody: rawBody ? Buffer.from(rawBody, "utf-8") : void 0
3082
- };
3083
- webhookContext = {
3084
- env: process.env,
3085
- app: { id: appId, versionId: appVersionId },
3086
- log: createContextLogger()
3087
- };
3088
- }
3089
- const originalEnv = { ...process.env };
3090
- Object.assign(process.env, requestEnv);
3091
- const requestConfig = buildRequestScopedConfig(requestEnv);
3092
- let webhookResponse;
3093
- try {
3094
- webhookResponse = await runWithLogContext({ invocation }, async () => {
3095
- return await runWithConfig(requestConfig, async () => {
3096
- return await webhookDef.handler(webhookRequest, webhookContext);
3097
- });
3098
- });
3099
- } catch (err) {
3100
- console.error(`Webhook handler '${handle}' error:`, err);
3101
- sendJSON(res, 500, { error: "Webhook handler error" });
3390
+ const parseResult = parseWebhookRequest(
3391
+ parsedBody,
3392
+ req.method ?? "POST",
3393
+ url.toString(),
3394
+ pathname,
3395
+ req.headers,
3396
+ Object.fromEntries(url.searchParams.entries()),
3397
+ rawBody,
3398
+ req.headers["x-skedyul-app-id"],
3399
+ req.headers["x-skedyul-app-version-id"]
3400
+ );
3401
+ if ("error" in parseResult) {
3402
+ sendJSON(res, 400, { error: parseResult.error });
3102
3403
  return;
3103
- } finally {
3104
- process.env = originalEnv;
3105
3404
  }
3106
- const status = webhookResponse.status ?? 200;
3405
+ const result = await executeWebhookHandler(handle, webhookRegistry, parseResult);
3107
3406
  const responseHeaders = {
3108
- ...webhookResponse.headers
3407
+ ...result.headers
3109
3408
  };
3110
3409
  if (!responseHeaders["Content-Type"] && !responseHeaders["content-type"]) {
3111
3410
  responseHeaders["Content-Type"] = "application/json";
3112
3411
  }
3113
- res.writeHead(status, responseHeaders);
3114
- if (webhookResponse.body !== void 0) {
3115
- if (typeof webhookResponse.body === "string") {
3116
- res.end(webhookResponse.body);
3412
+ res.writeHead(result.status, responseHeaders);
3413
+ if (result.body !== void 0) {
3414
+ if (typeof result.body === "string") {
3415
+ res.end(result.body);
3117
3416
  } else {
3118
- res.end(JSON.stringify(webhookResponse.body));
3417
+ res.end(JSON.stringify(result.body));
3119
3418
  }
3120
3419
  } else {
3121
3420
  res.end();
@@ -3154,10 +3453,6 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
3154
3453
  return;
3155
3454
  }
3156
3455
  if (pathname === "/oauth_callback" && req.method === "POST") {
3157
- if (!config.hooks?.oauth_callback) {
3158
- sendJSON(res, 404, { error: "OAuth callback handler not configured" });
3159
- return;
3160
- }
3161
3456
  let parsedBody;
3162
3457
  try {
3163
3458
  parsedBody = await parseJSONBody(req);
@@ -3168,50 +3463,11 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
3168
3463
  });
3169
3464
  return;
3170
3465
  }
3171
- const envelope = parseHandlerEnvelope(parsedBody);
3172
- if (!envelope) {
3173
- console.error("[OAuth Callback] Failed to parse envelope. Body:", JSON.stringify(parsedBody, null, 2));
3174
- sendJSON(res, 400, {
3175
- error: { code: -32602, message: "Missing envelope format: expected { env, request }" }
3176
- });
3177
- return;
3178
- }
3179
- const invocation = parsedBody.invocation;
3180
- const oauthRequest = buildRequestFromRaw(envelope.request);
3181
- const oauthCallbackRequestConfig = buildRequestScopedConfig(envelope.env);
3182
- const oauthCallbackContext = {
3183
- request: oauthRequest,
3184
- invocation,
3185
- log: createContextLogger()
3186
- };
3187
- try {
3188
- const oauthCallbackHook = config.hooks.oauth_callback;
3189
- const oauthCallbackHandler = typeof oauthCallbackHook === "function" ? oauthCallbackHook : oauthCallbackHook.handler;
3190
- const result = await runWithLogContext({ invocation }, async () => {
3191
- return await runWithConfig(oauthCallbackRequestConfig, async () => {
3192
- return await oauthCallbackHandler(oauthCallbackContext);
3193
- });
3194
- });
3195
- sendJSON(res, 200, {
3196
- appInstallationId: result.appInstallationId,
3197
- env: result.env ?? {}
3198
- });
3199
- } catch (err) {
3200
- const errorMessage = err instanceof Error ? err.message : String(err ?? "Unknown error");
3201
- sendJSON(res, 500, {
3202
- error: {
3203
- code: -32603,
3204
- message: errorMessage
3205
- }
3206
- });
3207
- }
3466
+ const result = await handleOAuthCallback(parsedBody, config.hooks);
3467
+ sendJSON(res, result.status, result.body);
3208
3468
  return;
3209
3469
  }
3210
3470
  if (pathname === "/install" && req.method === "POST") {
3211
- if (!config.hooks?.install) {
3212
- sendJSON(res, 404, { error: "Install handler not configured" });
3213
- return;
3214
- }
3215
3471
  let installBody;
3216
3472
  try {
3217
3473
  installBody = await parseJSONBody(req);
@@ -3221,61 +3477,11 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
3221
3477
  });
3222
3478
  return;
3223
3479
  }
3224
- if (!installBody.context?.appInstallationId || !installBody.context?.workplace) {
3225
- sendJSON(res, 400, {
3226
- error: { code: -32602, message: "Missing context (appInstallationId and workplace required)" }
3227
- });
3228
- return;
3229
- }
3230
- const installContext = {
3231
- env: installBody.env ?? {},
3232
- workplace: installBody.context.workplace,
3233
- appInstallationId: installBody.context.appInstallationId,
3234
- app: installBody.context.app,
3235
- invocation: installBody.invocation,
3236
- log: createContextLogger()
3237
- };
3238
- const installRequestConfig = {
3239
- baseUrl: installBody.env?.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
3240
- apiToken: installBody.env?.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
3241
- };
3242
- try {
3243
- const installHook = config.hooks.install;
3244
- const installHandler = typeof installHook === "function" ? installHook : installHook.handler;
3245
- const result = await runWithLogContext({ invocation: installBody.invocation }, async () => {
3246
- return await runWithConfig(installRequestConfig, async () => {
3247
- return await installHandler(installContext);
3248
- });
3249
- });
3250
- sendJSON(res, 200, {
3251
- env: result.env ?? {},
3252
- redirect: result.redirect
3253
- });
3254
- } catch (err) {
3255
- if (err instanceof InstallError) {
3256
- sendJSON(res, 400, {
3257
- error: {
3258
- code: err.code,
3259
- message: err.message,
3260
- field: err.field
3261
- }
3262
- });
3263
- } else {
3264
- sendJSON(res, 500, {
3265
- error: {
3266
- code: -32603,
3267
- message: err instanceof Error ? err.message : String(err ?? "")
3268
- }
3269
- });
3270
- }
3271
- }
3480
+ const result = await handleInstall(installBody, config.hooks);
3481
+ sendJSON(res, result.status, result.body);
3272
3482
  return;
3273
3483
  }
3274
3484
  if (pathname === "/uninstall" && req.method === "POST") {
3275
- if (!config.hooks?.uninstall) {
3276
- sendJSON(res, 404, { error: "Uninstall handler not configured" });
3277
- return;
3278
- }
3279
3485
  let uninstallBody;
3280
3486
  try {
3281
3487
  uninstallBody = await parseJSONBody(req);
@@ -3285,53 +3491,11 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
3285
3491
  });
3286
3492
  return;
3287
3493
  }
3288
- if (!uninstallBody.context?.appInstallationId || !uninstallBody.context?.workplace || !uninstallBody.context?.app) {
3289
- sendJSON(res, 400, {
3290
- error: {
3291
- code: -32602,
3292
- message: "Missing context (appInstallationId, workplace and app required)"
3293
- }
3294
- });
3295
- return;
3296
- }
3297
- const uninstallContext = {
3298
- env: uninstallBody.env ?? {},
3299
- workplace: uninstallBody.context.workplace,
3300
- appInstallationId: uninstallBody.context.appInstallationId,
3301
- app: uninstallBody.context.app,
3302
- invocation: uninstallBody.invocation,
3303
- log: createContextLogger()
3304
- };
3305
- const uninstallRequestConfig = {
3306
- baseUrl: uninstallBody.env?.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
3307
- apiToken: uninstallBody.env?.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
3308
- };
3309
- try {
3310
- const uninstallHook = config.hooks.uninstall;
3311
- const uninstallHandlerFn = typeof uninstallHook === "function" ? uninstallHook : uninstallHook.handler;
3312
- const result = await runWithLogContext({ invocation: uninstallBody.invocation }, async () => {
3313
- return await runWithConfig(uninstallRequestConfig, async () => {
3314
- return await uninstallHandlerFn(uninstallContext);
3315
- });
3316
- });
3317
- sendJSON(res, 200, {
3318
- cleanedWebhookIds: result.cleanedWebhookIds ?? []
3319
- });
3320
- } catch (err) {
3321
- sendJSON(res, 500, {
3322
- error: {
3323
- code: -32603,
3324
- message: err instanceof Error ? err.message : String(err ?? "")
3325
- }
3326
- });
3327
- }
3494
+ const result = await handleUninstall(uninstallBody, config.hooks);
3495
+ sendJSON(res, result.status, result.body);
3328
3496
  return;
3329
3497
  }
3330
3498
  if (pathname === "/provision" && req.method === "POST") {
3331
- if (!config.hooks?.provision) {
3332
- sendJSON(res, 404, { error: "Provision handler not configured" });
3333
- return;
3334
- }
3335
3499
  let provisionBody;
3336
3500
  try {
3337
3501
  provisionBody = await parseJSONBody(req);
@@ -3341,46 +3505,8 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
3341
3505
  });
3342
3506
  return;
3343
3507
  }
3344
- if (!provisionBody.context?.app) {
3345
- sendJSON(res, 400, {
3346
- error: { code: -32602, message: "Missing context (app required)" }
3347
- });
3348
- return;
3349
- }
3350
- const mergedEnv = {};
3351
- for (const [key, value] of Object.entries(process.env)) {
3352
- if (value !== void 0) {
3353
- mergedEnv[key] = value;
3354
- }
3355
- }
3356
- Object.assign(mergedEnv, provisionBody.env ?? {});
3357
- const provisionContext = {
3358
- env: mergedEnv,
3359
- app: provisionBody.context.app,
3360
- invocation: provisionBody.invocation,
3361
- log: createContextLogger()
3362
- };
3363
- const provisionRequestConfig = {
3364
- baseUrl: mergedEnv.SKEDYUL_API_URL ?? "",
3365
- apiToken: mergedEnv.SKEDYUL_API_TOKEN ?? ""
3366
- };
3367
- try {
3368
- const provisionHook = config.hooks.provision;
3369
- const provisionHandler = typeof provisionHook === "function" ? provisionHook : provisionHook.handler;
3370
- const result = await runWithLogContext({ invocation: provisionBody.invocation }, async () => {
3371
- return await runWithConfig(provisionRequestConfig, async () => {
3372
- return await provisionHandler(provisionContext);
3373
- });
3374
- });
3375
- sendJSON(res, 200, result);
3376
- } catch (err) {
3377
- sendJSON(res, 500, {
3378
- error: {
3379
- code: -32603,
3380
- message: err instanceof Error ? err.message : String(err ?? "")
3381
- }
3382
- });
3383
- }
3508
+ const result = await handleProvision(provisionBody, config.hooks);
3509
+ sendJSON(res, result.status, result.body);
3384
3510
  return;
3385
3511
  }
3386
3512
  if (pathname === "/core" && req.method === "POST") {
@@ -3561,12 +3687,10 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
3561
3687
  }
3562
3688
  if (path14.startsWith("/webhooks/") && webhookRegistry) {
3563
3689
  const handle = path14.slice("/webhooks/".length);
3564
- const webhookDef = webhookRegistry[handle];
3565
- if (!webhookDef) {
3690
+ if (!webhookRegistry[handle]) {
3566
3691
  return createResponse(404, { error: `Webhook handler '${handle}' not found` }, headers);
3567
3692
  }
3568
- const allowedMethods = webhookDef.methods ?? ["POST"];
3569
- if (!allowedMethods.includes(method)) {
3693
+ if (!isMethodAllowed(webhookRegistry, handle, method)) {
3570
3694
  return createResponse(405, { error: `Method ${method} not allowed` }, headers);
3571
3695
  }
3572
3696
  const rawBody = event.body ?? "";
@@ -3581,107 +3705,34 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
3581
3705
  } else {
3582
3706
  parsedBody = rawBody;
3583
3707
  }
3584
- const isEnvelope = typeof parsedBody === "object" && parsedBody !== null && "env" in parsedBody && "request" in parsedBody && "context" in parsedBody;
3585
- let webhookRequest;
3586
- let webhookContext;
3587
- let requestEnv = {};
3588
- let invocation;
3589
- if (isEnvelope) {
3590
- const envelope = parsedBody;
3591
- requestEnv = envelope.env ?? {};
3592
- invocation = envelope.invocation;
3593
- let originalParsedBody = envelope.request.body;
3594
- const originalContentType = envelope.request.headers["content-type"] ?? "";
3595
- if (originalContentType.includes("application/json")) {
3596
- try {
3597
- originalParsedBody = envelope.request.body ? JSON.parse(envelope.request.body) : {};
3598
- } catch {
3599
- }
3600
- }
3601
- webhookRequest = {
3602
- method: envelope.request.method,
3603
- url: envelope.request.url,
3604
- path: envelope.request.path,
3605
- headers: envelope.request.headers,
3606
- query: envelope.request.query,
3607
- body: originalParsedBody,
3608
- rawBody: envelope.request.body ? Buffer.from(envelope.request.body, "utf-8") : void 0
3609
- };
3610
- const envVars = { ...process.env, ...requestEnv };
3611
- const app = envelope.context.app;
3612
- if (envelope.context.appInstallationId && envelope.context.workplace) {
3613
- webhookContext = {
3614
- env: envVars,
3615
- app,
3616
- appInstallationId: envelope.context.appInstallationId,
3617
- workplace: envelope.context.workplace,
3618
- registration: envelope.context.registration ?? {},
3619
- invocation,
3620
- log: createContextLogger()
3621
- };
3622
- } else {
3623
- webhookContext = {
3624
- env: envVars,
3625
- app,
3626
- invocation,
3627
- log: createContextLogger()
3628
- };
3629
- }
3630
- } else {
3631
- const appId = event.headers?.["x-skedyul-app-id"] ?? event.headers?.["X-Skedyul-App-Id"];
3632
- const appVersionId = event.headers?.["x-skedyul-app-version-id"] ?? event.headers?.["X-Skedyul-App-Version-Id"];
3633
- if (!appId || !appVersionId) {
3634
- throw new Error("Missing app info in webhook request (x-skedyul-app-id and x-skedyul-app-version-id headers required)");
3635
- }
3636
- const forwardedProto = event.headers?.["x-forwarded-proto"] ?? event.headers?.["X-Forwarded-Proto"];
3637
- const protocol = forwardedProto ?? "https";
3638
- const host = event.headers?.host ?? event.headers?.Host ?? "localhost";
3639
- const queryString = event.queryStringParameters ? "?" + new URLSearchParams(event.queryStringParameters).toString() : "";
3640
- const webhookUrl = `${protocol}://${host}${path14}${queryString}`;
3641
- webhookRequest = {
3642
- method,
3643
- url: webhookUrl,
3644
- path: path14,
3645
- headers: event.headers,
3646
- query: event.queryStringParameters ?? {},
3647
- body: parsedBody,
3648
- rawBody: rawBody ? Buffer.from(rawBody, "utf-8") : void 0
3649
- };
3650
- webhookContext = {
3651
- env: process.env,
3652
- app: { id: appId, versionId: appVersionId },
3653
- log: createContextLogger()
3654
- };
3655
- }
3656
- const originalEnv = { ...process.env };
3657
- Object.assign(process.env, requestEnv);
3658
- const requestConfig = {
3659
- baseUrl: requestEnv.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
3660
- apiToken: requestEnv.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
3661
- };
3662
- let webhookResponse;
3663
- try {
3664
- webhookResponse = await runWithLogContext({ invocation }, async () => {
3665
- return await runWithConfig(requestConfig, async () => {
3666
- return await webhookDef.handler(webhookRequest, webhookContext);
3667
- });
3668
- });
3669
- } catch (err) {
3670
- console.error(`Webhook handler '${handle}' error:`, err);
3671
- return createResponse(500, { error: "Webhook handler error" }, headers);
3672
- } finally {
3673
- process.env = originalEnv;
3708
+ const forwardedProto = event.headers?.["x-forwarded-proto"] ?? event.headers?.["X-Forwarded-Proto"];
3709
+ const protocol = forwardedProto ?? "https";
3710
+ const host = event.headers?.host ?? event.headers?.Host ?? "localhost";
3711
+ const queryString = event.queryStringParameters ? "?" + new URLSearchParams(event.queryStringParameters).toString() : "";
3712
+ const webhookUrl = `${protocol}://${host}${path14}${queryString}`;
3713
+ const parseResult = parseWebhookRequest(
3714
+ parsedBody,
3715
+ method,
3716
+ webhookUrl,
3717
+ path14,
3718
+ event.headers,
3719
+ event.queryStringParameters ?? {},
3720
+ rawBody,
3721
+ event.headers?.["x-skedyul-app-id"] ?? event.headers?.["X-Skedyul-App-Id"],
3722
+ event.headers?.["x-skedyul-app-version-id"] ?? event.headers?.["X-Skedyul-App-Version-Id"]
3723
+ );
3724
+ if ("error" in parseResult) {
3725
+ return createResponse(400, { error: parseResult.error }, headers);
3674
3726
  }
3727
+ const result = await executeWebhookHandler(handle, webhookRegistry, parseResult);
3675
3728
  const responseHeaders = {
3676
3729
  ...headers,
3677
- ...webhookResponse.headers
3730
+ ...result.headers
3678
3731
  };
3679
- const status = webhookResponse.status ?? 200;
3680
- const body = webhookResponse.body;
3681
3732
  return {
3682
- statusCode: status,
3733
+ statusCode: result.status,
3683
3734
  headers: responseHeaders,
3684
- body: body !== void 0 ? typeof body === "string" ? body : JSON.stringify(body) : ""
3735
+ body: result.body !== void 0 ? typeof result.body === "string" ? result.body : JSON.stringify(result.body) : ""
3685
3736
  };
3686
3737
  }
3687
3738
  if (path14 === "/core" && method === "POST") {
@@ -3817,9 +3868,6 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
3817
3868
  }
3818
3869
  }
3819
3870
  if (path14 === "/install" && method === "POST") {
3820
- if (!config.hooks?.install) {
3821
- return createResponse(404, { error: "Install handler not configured" }, headers);
3822
- }
3823
3871
  let installBody;
3824
3872
  try {
3825
3873
  installBody = event.body ? JSON.parse(event.body) : {};
@@ -3830,68 +3878,10 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
3830
3878
  headers
3831
3879
  );
3832
3880
  }
3833
- if (!installBody.context?.appInstallationId || !installBody.context?.workplace) {
3834
- return createResponse(
3835
- 400,
3836
- { error: { code: -32602, message: "Missing context (appInstallationId and workplace required)" } },
3837
- headers
3838
- );
3839
- }
3840
- const installContext = {
3841
- env: installBody.env ?? {},
3842
- workplace: installBody.context.workplace,
3843
- appInstallationId: installBody.context.appInstallationId,
3844
- app: installBody.context.app,
3845
- invocation: installBody.invocation,
3846
- log: createContextLogger()
3847
- };
3848
- const installRequestConfig = {
3849
- baseUrl: installBody.env?.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
3850
- apiToken: installBody.env?.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
3851
- };
3852
- try {
3853
- const installHook = config.hooks.install;
3854
- const installHandler = typeof installHook === "function" ? installHook : installHook.handler;
3855
- const result = await runWithLogContext({ invocation: installBody.invocation }, async () => {
3856
- return await runWithConfig(installRequestConfig, async () => {
3857
- return await installHandler(installContext);
3858
- });
3859
- });
3860
- return createResponse(
3861
- 200,
3862
- { env: result.env ?? {}, redirect: result.redirect },
3863
- headers
3864
- );
3865
- } catch (err) {
3866
- if (err instanceof InstallError) {
3867
- return createResponse(
3868
- 400,
3869
- {
3870
- error: {
3871
- code: err.code,
3872
- message: err.message,
3873
- field: err.field
3874
- }
3875
- },
3876
- headers
3877
- );
3878
- }
3879
- return createResponse(
3880
- 500,
3881
- {
3882
- error: {
3883
- code: -32603,
3884
- message: err instanceof Error ? err.message : String(err ?? "")
3885
- }
3886
- },
3887
- headers
3888
- );
3889
- }
3881
+ const result = await handleInstall(installBody, config.hooks);
3882
+ return createResponse(result.status, result.body, headers);
3890
3883
  }
3891
3884
  if (path14 === "/uninstall" && method === "POST") {
3892
- if (!config.hooks?.uninstall) {
3893
- return createResponse(404, { error: "Uninstall handler not configured" }, headers);
3894
- }
3895
3885
  let uninstallBody;
3896
3886
  try {
3897
3887
  uninstallBody = event.body ? JSON.parse(event.body) : {};
@@ -3902,137 +3892,24 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
3902
3892
  headers
3903
3893
  );
3904
3894
  }
3905
- if (!uninstallBody.context?.appInstallationId || !uninstallBody.context?.workplace || !uninstallBody.context?.app) {
3906
- return createResponse(
3907
- 400,
3908
- {
3909
- error: {
3910
- code: -32602,
3911
- message: "Missing context (appInstallationId, workplace and app required)"
3912
- }
3913
- },
3914
- headers
3915
- );
3916
- }
3917
- const uninstallContext = {
3918
- env: uninstallBody.env ?? {},
3919
- workplace: uninstallBody.context.workplace,
3920
- appInstallationId: uninstallBody.context.appInstallationId,
3921
- app: uninstallBody.context.app,
3922
- invocation: uninstallBody.invocation,
3923
- log: createContextLogger()
3924
- };
3925
- const uninstallRequestConfig = {
3926
- baseUrl: uninstallBody.env?.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
3927
- apiToken: uninstallBody.env?.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
3928
- };
3929
- try {
3930
- const uninstallHook = config.hooks.uninstall;
3931
- const uninstallHandlerFn = typeof uninstallHook === "function" ? uninstallHook : uninstallHook.handler;
3932
- const result = await runWithLogContext({ invocation: uninstallBody.invocation }, async () => {
3933
- return await runWithConfig(uninstallRequestConfig, async () => {
3934
- return await uninstallHandlerFn(uninstallContext);
3935
- });
3936
- });
3937
- return createResponse(
3938
- 200,
3939
- { cleanedWebhookIds: result.cleanedWebhookIds ?? [] },
3940
- headers
3941
- );
3942
- } catch (err) {
3943
- return createResponse(
3944
- 500,
3945
- {
3946
- error: {
3947
- code: -32603,
3948
- message: err instanceof Error ? err.message : String(err ?? "")
3949
- }
3950
- },
3951
- headers
3952
- );
3953
- }
3895
+ const result = await handleUninstall(uninstallBody, config.hooks);
3896
+ return createResponse(result.status, result.body, headers);
3954
3897
  }
3955
3898
  if (path14 === "/provision" && method === "POST") {
3956
- console.log("[serverless] /provision endpoint called");
3957
- if (!config.hooks?.provision) {
3958
- console.log("[serverless] No provision handler configured");
3959
- return createResponse(404, { error: "Provision handler not configured" }, headers);
3960
- }
3961
3899
  let provisionBody;
3962
3900
  try {
3963
3901
  provisionBody = event.body ? JSON.parse(event.body) : {};
3964
- console.log("[serverless] Provision body parsed:", {
3965
- hasEnv: !!provisionBody.env,
3966
- hasContext: !!provisionBody.context,
3967
- appId: provisionBody.context?.app?.id,
3968
- versionId: provisionBody.context?.app?.versionId
3969
- });
3970
3902
  } catch {
3971
- console.log("[serverless] Failed to parse provision body");
3972
3903
  return createResponse(
3973
3904
  400,
3974
3905
  { error: { code: -32700, message: "Parse error" } },
3975
3906
  headers
3976
3907
  );
3977
3908
  }
3978
- if (!provisionBody.context?.app) {
3979
- console.log("[serverless] Missing app context in provision body");
3980
- return createResponse(
3981
- 400,
3982
- { error: { code: -32602, message: "Missing context (app required)" } },
3983
- headers
3984
- );
3985
- }
3986
- const mergedEnv = {};
3987
- for (const [key, value] of Object.entries(process.env)) {
3988
- if (value !== void 0) {
3989
- mergedEnv[key] = value;
3990
- }
3991
- }
3992
- Object.assign(mergedEnv, provisionBody.env ?? {});
3993
- const provisionContext = {
3994
- env: mergedEnv,
3995
- app: provisionBody.context.app,
3996
- invocation: provisionBody.invocation,
3997
- log: createContextLogger()
3998
- };
3999
- const provisionRequestConfig = {
4000
- baseUrl: mergedEnv.SKEDYUL_API_URL ?? "",
4001
- apiToken: mergedEnv.SKEDYUL_API_TOKEN ?? ""
4002
- };
4003
- console.log("[serverless] Calling provision handler...");
4004
- try {
4005
- const provisionHook = config.hooks.provision;
4006
- const provisionHandler = typeof provisionHook === "function" ? provisionHook : provisionHook.handler;
4007
- const result = await runWithLogContext({ invocation: provisionBody.invocation }, async () => {
4008
- return await runWithConfig(provisionRequestConfig, async () => {
4009
- return await provisionHandler(provisionContext);
4010
- });
4011
- });
4012
- console.log("[serverless] Provision handler completed successfully");
4013
- return createResponse(200, result, headers);
4014
- } catch (err) {
4015
- console.error("[serverless] Provision handler failed:", err instanceof Error ? err.message : String(err));
4016
- return createResponse(
4017
- 500,
4018
- {
4019
- error: {
4020
- code: -32603,
4021
- message: err instanceof Error ? err.message : String(err ?? "")
4022
- }
4023
- },
4024
- headers
4025
- );
4026
- }
3909
+ const result = await handleProvision(provisionBody, config.hooks);
3910
+ return createResponse(result.status, result.body, headers);
4027
3911
  }
4028
3912
  if (path14 === "/oauth_callback" && method === "POST") {
4029
- if (!config.hooks?.oauth_callback) {
4030
- return createResponse(
4031
- 404,
4032
- { error: "OAuth callback handler not configured" },
4033
- headers
4034
- );
4035
- }
4036
3913
  let parsedBody;
4037
3914
  try {
4038
3915
  parsedBody = event.body ? JSON.parse(event.body) : {};
@@ -4044,52 +3921,8 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
4044
3921
  headers
4045
3922
  );
4046
3923
  }
4047
- const envelope = parseHandlerEnvelope(parsedBody);
4048
- if (!envelope) {
4049
- console.error("[OAuth Callback] Failed to parse envelope. Body:", JSON.stringify(parsedBody, null, 2));
4050
- return createResponse(
4051
- 400,
4052
- { error: { code: -32602, message: "Missing envelope format: expected { env, request }" } },
4053
- headers
4054
- );
4055
- }
4056
- const invocation = parsedBody.invocation;
4057
- const oauthRequest = buildRequestFromRaw(envelope.request);
4058
- const oauthCallbackRequestConfig = buildRequestScopedConfig(envelope.env);
4059
- const oauthCallbackContext = {
4060
- request: oauthRequest,
4061
- invocation,
4062
- log: createContextLogger()
4063
- };
4064
- try {
4065
- const oauthCallbackHook = config.hooks.oauth_callback;
4066
- const oauthCallbackHandler = typeof oauthCallbackHook === "function" ? oauthCallbackHook : oauthCallbackHook.handler;
4067
- const result = await runWithLogContext({ invocation }, async () => {
4068
- return await runWithConfig(oauthCallbackRequestConfig, async () => {
4069
- return await oauthCallbackHandler(oauthCallbackContext);
4070
- });
4071
- });
4072
- return createResponse(
4073
- 200,
4074
- {
4075
- appInstallationId: result.appInstallationId,
4076
- env: result.env ?? {}
4077
- },
4078
- headers
4079
- );
4080
- } catch (err) {
4081
- const errorMessage = err instanceof Error ? err.message : String(err ?? "Unknown error");
4082
- return createResponse(
4083
- 500,
4084
- {
4085
- error: {
4086
- code: -32603,
4087
- message: errorMessage
4088
- }
4089
- },
4090
- headers
4091
- );
4092
- }
3924
+ const result = await handleOAuthCallback(parsedBody, config.hooks);
3925
+ return createResponse(result.status, result.body, headers);
4093
3926
  }
4094
3927
  if (path14 === "/health" && method === "GET") {
4095
3928
  return createResponse(200, state.getHealthStatus(), headers);