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/index.js CHANGED
@@ -2244,8 +2244,8 @@ function createRequestState(maxRequests, ttlExtendSeconds, runtimeLabel, toolNam
2244
2244
  };
2245
2245
  }
2246
2246
  function createCallToolHandler(registry, state, onMaxRequests) {
2247
- return async function callTool(nameRaw, argsRaw) {
2248
- const toolName = String(nameRaw);
2247
+ return async function callTool(toolNameInput, toolArgsInput) {
2248
+ const toolName = String(toolNameInput);
2249
2249
  const tool = registry[toolName];
2250
2250
  if (!tool) {
2251
2251
  throw new Error(`Tool "${toolName}" not found in registry`);
@@ -2254,7 +2254,7 @@ function createCallToolHandler(registry, state, onMaxRequests) {
2254
2254
  throw new Error(`Tool "${toolName}" handler is not a function`);
2255
2255
  }
2256
2256
  const fn = tool.handler;
2257
- const args = argsRaw ?? {};
2257
+ const args = toolArgsInput ?? {};
2258
2258
  const estimateMode = args.estimate === true;
2259
2259
  if (!estimateMode) {
2260
2260
  state.incrementRequestCount();
@@ -2509,51 +2509,6 @@ async function handleCoreMethod(method, params) {
2509
2509
  };
2510
2510
  }
2511
2511
 
2512
- // src/server/handler-helpers.ts
2513
- function parseHandlerEnvelope(parsedBody) {
2514
- if (typeof parsedBody !== "object" || parsedBody === null || Array.isArray(parsedBody) || !("env" in parsedBody) || !("request" in parsedBody)) {
2515
- return null;
2516
- }
2517
- const envelope = parsedBody;
2518
- if (typeof envelope.env !== "object" || envelope.env === null || Array.isArray(envelope.env)) {
2519
- return null;
2520
- }
2521
- if (typeof envelope.request !== "object" || envelope.request === null || Array.isArray(envelope.request)) {
2522
- return null;
2523
- }
2524
- return {
2525
- env: envelope.env,
2526
- request: envelope.request,
2527
- context: envelope.context
2528
- };
2529
- }
2530
- function buildRequestFromRaw(raw) {
2531
- let parsedBody = raw.body;
2532
- const contentType = raw.headers["content-type"] ?? "";
2533
- if (contentType.includes("application/json")) {
2534
- try {
2535
- parsedBody = raw.body ? JSON.parse(raw.body) : {};
2536
- } catch {
2537
- parsedBody = raw.body;
2538
- }
2539
- }
2540
- return {
2541
- method: raw.method,
2542
- url: raw.url,
2543
- path: raw.path,
2544
- headers: raw.headers,
2545
- query: raw.query,
2546
- body: parsedBody,
2547
- rawBody: raw.body ? Buffer.from(raw.body, "utf-8") : void 0
2548
- };
2549
- }
2550
- function buildRequestScopedConfig(env) {
2551
- return {
2552
- baseUrl: env.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
2553
- apiToken: env.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
2554
- };
2555
- }
2556
-
2557
2512
  // src/server/startup-logger.ts
2558
2513
  function padEnd(str, length) {
2559
2514
  if (str.length >= length) {
@@ -2651,6 +2606,405 @@ function isProvisionConfig(value) {
2651
2606
  return value !== void 0 && value !== null && !(value instanceof Promise);
2652
2607
  }
2653
2608
 
2609
+ // src/server/handlers/install-handler.ts
2610
+ async function handleInstall(body, hooks) {
2611
+ if (!hooks?.install) {
2612
+ return {
2613
+ status: 404,
2614
+ body: { error: "Install handler not configured" }
2615
+ };
2616
+ }
2617
+ if (!body.context?.appInstallationId || !body.context?.workplace) {
2618
+ return {
2619
+ status: 400,
2620
+ body: {
2621
+ error: {
2622
+ code: -32602,
2623
+ message: "Missing context (appInstallationId and workplace required)"
2624
+ }
2625
+ }
2626
+ };
2627
+ }
2628
+ const installContext = {
2629
+ env: body.env ?? {},
2630
+ workplace: body.context.workplace,
2631
+ appInstallationId: body.context.appInstallationId,
2632
+ app: body.context.app,
2633
+ invocation: body.invocation,
2634
+ log: createContextLogger()
2635
+ };
2636
+ const requestConfig = {
2637
+ baseUrl: body.env?.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
2638
+ apiToken: body.env?.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
2639
+ };
2640
+ try {
2641
+ const installHook = hooks.install;
2642
+ const installHandler = typeof installHook === "function" ? installHook : installHook.handler;
2643
+ const result = await runWithLogContext({ invocation: body.invocation }, async () => {
2644
+ return await runWithConfig(requestConfig, async () => {
2645
+ return await installHandler(installContext);
2646
+ });
2647
+ });
2648
+ return {
2649
+ status: 200,
2650
+ body: { env: result.env ?? {}, redirect: result.redirect }
2651
+ };
2652
+ } catch (err) {
2653
+ if (err instanceof InstallError) {
2654
+ return {
2655
+ status: 400,
2656
+ body: {
2657
+ error: {
2658
+ code: err.code,
2659
+ message: err.message,
2660
+ field: err.field
2661
+ }
2662
+ }
2663
+ };
2664
+ }
2665
+ return {
2666
+ status: 500,
2667
+ body: {
2668
+ error: {
2669
+ code: -32603,
2670
+ message: err instanceof Error ? err.message : String(err ?? "")
2671
+ }
2672
+ }
2673
+ };
2674
+ }
2675
+ }
2676
+
2677
+ // src/server/handlers/uninstall-handler.ts
2678
+ async function handleUninstall(body, hooks) {
2679
+ if (!hooks?.uninstall) {
2680
+ return {
2681
+ status: 404,
2682
+ body: { error: "Uninstall handler not configured" }
2683
+ };
2684
+ }
2685
+ if (!body.context?.appInstallationId || !body.context?.workplace || !body.context?.app) {
2686
+ return {
2687
+ status: 400,
2688
+ body: {
2689
+ error: {
2690
+ code: -32602,
2691
+ message: "Missing context (appInstallationId, workplace and app required)"
2692
+ }
2693
+ }
2694
+ };
2695
+ }
2696
+ const uninstallContext = {
2697
+ env: body.env ?? {},
2698
+ workplace: body.context.workplace,
2699
+ appInstallationId: body.context.appInstallationId,
2700
+ app: body.context.app,
2701
+ invocation: body.invocation,
2702
+ log: createContextLogger()
2703
+ };
2704
+ const requestConfig = {
2705
+ baseUrl: body.env?.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
2706
+ apiToken: body.env?.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
2707
+ };
2708
+ try {
2709
+ const uninstallHook = hooks.uninstall;
2710
+ const uninstallHandlerFn = typeof uninstallHook === "function" ? uninstallHook : uninstallHook.handler;
2711
+ const result = await runWithLogContext({ invocation: body.invocation }, async () => {
2712
+ return await runWithConfig(requestConfig, async () => {
2713
+ return await uninstallHandlerFn(uninstallContext);
2714
+ });
2715
+ });
2716
+ return {
2717
+ status: 200,
2718
+ body: { cleanedWebhookIds: result.cleanedWebhookIds ?? [] }
2719
+ };
2720
+ } catch (err) {
2721
+ return {
2722
+ status: 500,
2723
+ body: {
2724
+ error: {
2725
+ code: -32603,
2726
+ message: err instanceof Error ? err.message : String(err ?? "")
2727
+ }
2728
+ }
2729
+ };
2730
+ }
2731
+ }
2732
+
2733
+ // src/server/handlers/provision-handler.ts
2734
+ async function handleProvision(body, hooks) {
2735
+ if (!hooks?.provision) {
2736
+ return {
2737
+ status: 404,
2738
+ body: { error: "Provision handler not configured" }
2739
+ };
2740
+ }
2741
+ if (!body.context?.app) {
2742
+ return {
2743
+ status: 400,
2744
+ body: {
2745
+ error: {
2746
+ code: -32602,
2747
+ message: "Missing context (app required)"
2748
+ }
2749
+ }
2750
+ };
2751
+ }
2752
+ const mergedEnv = {};
2753
+ for (const [key, value] of Object.entries(process.env)) {
2754
+ if (value !== void 0) {
2755
+ mergedEnv[key] = value;
2756
+ }
2757
+ }
2758
+ Object.assign(mergedEnv, body.env ?? {});
2759
+ const provisionContext = {
2760
+ env: mergedEnv,
2761
+ app: body.context.app,
2762
+ invocation: body.invocation,
2763
+ log: createContextLogger()
2764
+ };
2765
+ const requestConfig = {
2766
+ baseUrl: mergedEnv.SKEDYUL_API_URL ?? "",
2767
+ apiToken: mergedEnv.SKEDYUL_API_TOKEN ?? ""
2768
+ };
2769
+ try {
2770
+ const provisionHook = hooks.provision;
2771
+ const provisionHandler = typeof provisionHook === "function" ? provisionHook : provisionHook.handler;
2772
+ const result = await runWithLogContext({ invocation: body.invocation }, async () => {
2773
+ return await runWithConfig(requestConfig, async () => {
2774
+ return await provisionHandler(provisionContext);
2775
+ });
2776
+ });
2777
+ return {
2778
+ status: 200,
2779
+ body: result
2780
+ };
2781
+ } catch (err) {
2782
+ return {
2783
+ status: 500,
2784
+ body: {
2785
+ error: {
2786
+ code: -32603,
2787
+ message: err instanceof Error ? err.message : String(err ?? "")
2788
+ }
2789
+ }
2790
+ };
2791
+ }
2792
+ }
2793
+
2794
+ // src/server/handler-helpers.ts
2795
+ function parseHandlerEnvelope(parsedBody) {
2796
+ if (typeof parsedBody !== "object" || parsedBody === null || Array.isArray(parsedBody) || !("env" in parsedBody) || !("request" in parsedBody)) {
2797
+ return null;
2798
+ }
2799
+ const envelope = parsedBody;
2800
+ if (typeof envelope.env !== "object" || envelope.env === null || Array.isArray(envelope.env)) {
2801
+ return null;
2802
+ }
2803
+ if (typeof envelope.request !== "object" || envelope.request === null || Array.isArray(envelope.request)) {
2804
+ return null;
2805
+ }
2806
+ return {
2807
+ env: envelope.env,
2808
+ request: envelope.request,
2809
+ context: envelope.context
2810
+ };
2811
+ }
2812
+ function buildRequestFromRaw(raw) {
2813
+ let parsedBody = raw.body;
2814
+ const contentType = raw.headers["content-type"] ?? "";
2815
+ if (contentType.includes("application/json")) {
2816
+ try {
2817
+ parsedBody = raw.body ? JSON.parse(raw.body) : {};
2818
+ } catch {
2819
+ parsedBody = raw.body;
2820
+ }
2821
+ }
2822
+ return {
2823
+ method: raw.method,
2824
+ url: raw.url,
2825
+ path: raw.path,
2826
+ headers: raw.headers,
2827
+ query: raw.query,
2828
+ body: parsedBody,
2829
+ rawBody: raw.body ? Buffer.from(raw.body, "utf-8") : void 0
2830
+ };
2831
+ }
2832
+ function buildRequestScopedConfig(env) {
2833
+ return {
2834
+ baseUrl: env.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
2835
+ apiToken: env.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
2836
+ };
2837
+ }
2838
+
2839
+ // src/server/handlers/oauth-callback-handler.ts
2840
+ async function handleOAuthCallback(parsedBody, hooks) {
2841
+ if (!hooks?.oauth_callback) {
2842
+ return {
2843
+ status: 404,
2844
+ body: { error: "OAuth callback handler not configured" }
2845
+ };
2846
+ }
2847
+ const envelope = parseHandlerEnvelope(parsedBody);
2848
+ if (!envelope) {
2849
+ console.error("[OAuth Callback] Failed to parse envelope. Body:", JSON.stringify(parsedBody, null, 2));
2850
+ return {
2851
+ status: 400,
2852
+ body: {
2853
+ error: {
2854
+ code: -32602,
2855
+ message: "Missing envelope format: expected { env, request }"
2856
+ }
2857
+ }
2858
+ };
2859
+ }
2860
+ const invocation = parsedBody.invocation;
2861
+ const oauthRequest = buildRequestFromRaw(envelope.request);
2862
+ const requestConfig = buildRequestScopedConfig(envelope.env);
2863
+ const oauthCallbackContext = {
2864
+ request: oauthRequest,
2865
+ invocation,
2866
+ log: createContextLogger()
2867
+ };
2868
+ try {
2869
+ const oauthCallbackHook = hooks.oauth_callback;
2870
+ const oauthCallbackHandler = typeof oauthCallbackHook === "function" ? oauthCallbackHook : oauthCallbackHook.handler;
2871
+ const result = await runWithLogContext({ invocation }, async () => {
2872
+ return await runWithConfig(requestConfig, async () => {
2873
+ return await oauthCallbackHandler(oauthCallbackContext);
2874
+ });
2875
+ });
2876
+ return {
2877
+ status: 200,
2878
+ body: {
2879
+ appInstallationId: result.appInstallationId,
2880
+ env: result.env ?? {}
2881
+ }
2882
+ };
2883
+ } catch (err) {
2884
+ const errorMessage = err instanceof Error ? err.message : String(err ?? "Unknown error");
2885
+ return {
2886
+ status: 500,
2887
+ body: {
2888
+ error: {
2889
+ code: -32603,
2890
+ message: errorMessage
2891
+ }
2892
+ }
2893
+ };
2894
+ }
2895
+ }
2896
+
2897
+ // src/server/handlers/webhook-handler.ts
2898
+ function parseWebhookRequest(parsedBody, method, url, path2, headers, query, rawBody, appIdHeader, appVersionIdHeader) {
2899
+ const isEnvelope = typeof parsedBody === "object" && parsedBody !== null && "env" in parsedBody && "request" in parsedBody && "context" in parsedBody;
2900
+ if (isEnvelope) {
2901
+ const envelope = parsedBody;
2902
+ const requestEnv = envelope.env ?? {};
2903
+ const invocation = envelope.invocation;
2904
+ let originalParsedBody = envelope.request.body;
2905
+ const originalContentType = envelope.request.headers["content-type"] ?? "";
2906
+ if (originalContentType.includes("application/json")) {
2907
+ try {
2908
+ originalParsedBody = envelope.request.body ? JSON.parse(envelope.request.body) : {};
2909
+ } catch {
2910
+ }
2911
+ }
2912
+ const webhookRequest2 = {
2913
+ method: envelope.request.method,
2914
+ url: envelope.request.url,
2915
+ path: envelope.request.path,
2916
+ headers: envelope.request.headers,
2917
+ query: envelope.request.query,
2918
+ body: originalParsedBody,
2919
+ rawBody: envelope.request.body ? Buffer.from(envelope.request.body, "utf-8") : void 0
2920
+ };
2921
+ const envVars = { ...process.env, ...requestEnv };
2922
+ const app = envelope.context.app;
2923
+ let webhookContext2;
2924
+ if (envelope.context.appInstallationId && envelope.context.workplace) {
2925
+ webhookContext2 = {
2926
+ env: envVars,
2927
+ app,
2928
+ appInstallationId: envelope.context.appInstallationId,
2929
+ workplace: envelope.context.workplace,
2930
+ registration: envelope.context.registration ?? {},
2931
+ invocation,
2932
+ log: createContextLogger()
2933
+ };
2934
+ } else {
2935
+ webhookContext2 = {
2936
+ env: envVars,
2937
+ app,
2938
+ invocation,
2939
+ log: createContextLogger()
2940
+ };
2941
+ }
2942
+ return { webhookRequest: webhookRequest2, webhookContext: webhookContext2, requestEnv, invocation };
2943
+ }
2944
+ if (!appIdHeader || !appVersionIdHeader) {
2945
+ return {
2946
+ error: "Missing app info in webhook request (x-skedyul-app-id and x-skedyul-app-version-id headers required)"
2947
+ };
2948
+ }
2949
+ const webhookRequest = {
2950
+ method,
2951
+ url,
2952
+ path: path2,
2953
+ headers,
2954
+ query,
2955
+ body: parsedBody,
2956
+ rawBody: rawBody ? Buffer.from(rawBody, "utf-8") : void 0
2957
+ };
2958
+ const webhookContext = {
2959
+ env: process.env,
2960
+ app: { id: appIdHeader, versionId: appVersionIdHeader },
2961
+ log: createContextLogger()
2962
+ };
2963
+ return { webhookRequest, webhookContext, requestEnv: {} };
2964
+ }
2965
+ async function executeWebhookHandler(handle, webhookRegistry, data) {
2966
+ const webhookDef = webhookRegistry[handle];
2967
+ if (!webhookDef) {
2968
+ return {
2969
+ status: 404,
2970
+ body: { error: `Webhook handler '${handle}' not found` }
2971
+ };
2972
+ }
2973
+ const originalEnv = { ...process.env };
2974
+ Object.assign(process.env, data.requestEnv);
2975
+ const requestConfig = {
2976
+ baseUrl: data.requestEnv.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
2977
+ apiToken: data.requestEnv.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
2978
+ };
2979
+ let webhookResponse;
2980
+ try {
2981
+ webhookResponse = await runWithLogContext({ invocation: data.invocation }, async () => {
2982
+ return await runWithConfig(requestConfig, async () => {
2983
+ return await webhookDef.handler(data.webhookRequest, data.webhookContext);
2984
+ });
2985
+ });
2986
+ } catch (err) {
2987
+ console.error(`Webhook handler '${handle}' error:`, err);
2988
+ return {
2989
+ status: 500,
2990
+ body: { error: "Webhook handler error" }
2991
+ };
2992
+ } finally {
2993
+ process.env = originalEnv;
2994
+ }
2995
+ return {
2996
+ status: webhookResponse.status ?? 200,
2997
+ body: webhookResponse.body,
2998
+ headers: webhookResponse.headers
2999
+ };
3000
+ }
3001
+ function isMethodAllowed(webhookRegistry, handle, method) {
3002
+ const webhookDef = webhookRegistry[handle];
3003
+ if (!webhookDef) return false;
3004
+ const allowedMethods = webhookDef.methods ?? ["POST"];
3005
+ return allowedMethods.includes(method);
3006
+ }
3007
+
2654
3008
  // src/server/dedicated.ts
2655
3009
  function createDedicatedServerInstance(config, tools, callTool, state, mcpServer) {
2656
3010
  const port = getListeningPort(config);
@@ -2677,13 +3031,11 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
2677
3031
  }
2678
3032
  if (pathname.startsWith("/webhooks/") && webhookRegistry) {
2679
3033
  const handle = pathname.slice("/webhooks/".length);
2680
- const webhookDef = webhookRegistry[handle];
2681
- if (!webhookDef) {
3034
+ if (!webhookRegistry[handle]) {
2682
3035
  sendJSON(res, 404, { error: `Webhook handler '${handle}' not found` });
2683
3036
  return;
2684
3037
  }
2685
- const allowedMethods = webhookDef.methods ?? ["POST"];
2686
- if (!allowedMethods.includes(req.method)) {
3038
+ if (!isMethodAllowed(webhookRegistry, handle, req.method ?? "POST")) {
2687
3039
  sendJSON(res, 405, { error: `Method ${req.method} not allowed` });
2688
3040
  return;
2689
3041
  }
@@ -2705,87 +3057,34 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
2705
3057
  } else {
2706
3058
  parsedBody = rawBody;
2707
3059
  }
2708
- const envelope = parseHandlerEnvelope(parsedBody);
2709
- let webhookRequest;
2710
- let webhookContext;
2711
- let requestEnv = {};
2712
- let invocation;
2713
- if (envelope && "context" in envelope && envelope.context) {
2714
- const context = envelope.context;
2715
- requestEnv = envelope.env;
2716
- invocation = parsedBody.invocation;
2717
- webhookRequest = buildRequestFromRaw(envelope.request);
2718
- const envVars = { ...process.env, ...envelope.env };
2719
- const app = context.app;
2720
- if (context.appInstallationId && context.workplace) {
2721
- webhookContext = {
2722
- env: envVars,
2723
- app,
2724
- appInstallationId: context.appInstallationId,
2725
- workplace: context.workplace,
2726
- registration: context.registration ?? {},
2727
- invocation,
2728
- log: createContextLogger()
2729
- };
2730
- } else {
2731
- webhookContext = {
2732
- env: envVars,
2733
- app,
2734
- invocation,
2735
- log: createContextLogger()
2736
- };
2737
- }
2738
- } else {
2739
- const appId = req.headers["x-skedyul-app-id"];
2740
- const appVersionId = req.headers["x-skedyul-app-version-id"];
2741
- if (!appId || !appVersionId) {
2742
- throw new Error("Missing app info in webhook request (x-skedyul-app-id and x-skedyul-app-version-id headers required)");
2743
- }
2744
- webhookRequest = {
2745
- method: req.method ?? "POST",
2746
- url: url.toString(),
2747
- path: pathname,
2748
- headers: req.headers,
2749
- query: Object.fromEntries(url.searchParams.entries()),
2750
- body: parsedBody,
2751
- rawBody: rawBody ? Buffer.from(rawBody, "utf-8") : void 0
2752
- };
2753
- webhookContext = {
2754
- env: process.env,
2755
- app: { id: appId, versionId: appVersionId },
2756
- log: createContextLogger()
2757
- };
2758
- }
2759
- const originalEnv = { ...process.env };
2760
- Object.assign(process.env, requestEnv);
2761
- const requestConfig = buildRequestScopedConfig(requestEnv);
2762
- let webhookResponse;
2763
- try {
2764
- webhookResponse = await runWithLogContext({ invocation }, async () => {
2765
- return await runWithConfig(requestConfig, async () => {
2766
- return await webhookDef.handler(webhookRequest, webhookContext);
2767
- });
2768
- });
2769
- } catch (err) {
2770
- console.error(`Webhook handler '${handle}' error:`, err);
2771
- sendJSON(res, 500, { error: "Webhook handler error" });
3060
+ const parseResult = parseWebhookRequest(
3061
+ parsedBody,
3062
+ req.method ?? "POST",
3063
+ url.toString(),
3064
+ pathname,
3065
+ req.headers,
3066
+ Object.fromEntries(url.searchParams.entries()),
3067
+ rawBody,
3068
+ req.headers["x-skedyul-app-id"],
3069
+ req.headers["x-skedyul-app-version-id"]
3070
+ );
3071
+ if ("error" in parseResult) {
3072
+ sendJSON(res, 400, { error: parseResult.error });
2772
3073
  return;
2773
- } finally {
2774
- process.env = originalEnv;
2775
3074
  }
2776
- const status = webhookResponse.status ?? 200;
3075
+ const result = await executeWebhookHandler(handle, webhookRegistry, parseResult);
2777
3076
  const responseHeaders = {
2778
- ...webhookResponse.headers
3077
+ ...result.headers
2779
3078
  };
2780
3079
  if (!responseHeaders["Content-Type"] && !responseHeaders["content-type"]) {
2781
3080
  responseHeaders["Content-Type"] = "application/json";
2782
3081
  }
2783
- res.writeHead(status, responseHeaders);
2784
- if (webhookResponse.body !== void 0) {
2785
- if (typeof webhookResponse.body === "string") {
2786
- res.end(webhookResponse.body);
3082
+ res.writeHead(result.status, responseHeaders);
3083
+ if (result.body !== void 0) {
3084
+ if (typeof result.body === "string") {
3085
+ res.end(result.body);
2787
3086
  } else {
2788
- res.end(JSON.stringify(webhookResponse.body));
3087
+ res.end(JSON.stringify(result.body));
2789
3088
  }
2790
3089
  } else {
2791
3090
  res.end();
@@ -2824,10 +3123,6 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
2824
3123
  return;
2825
3124
  }
2826
3125
  if (pathname === "/oauth_callback" && req.method === "POST") {
2827
- if (!config.hooks?.oauth_callback) {
2828
- sendJSON(res, 404, { error: "OAuth callback handler not configured" });
2829
- return;
2830
- }
2831
3126
  let parsedBody;
2832
3127
  try {
2833
3128
  parsedBody = await parseJSONBody(req);
@@ -2838,50 +3133,11 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
2838
3133
  });
2839
3134
  return;
2840
3135
  }
2841
- const envelope = parseHandlerEnvelope(parsedBody);
2842
- if (!envelope) {
2843
- console.error("[OAuth Callback] Failed to parse envelope. Body:", JSON.stringify(parsedBody, null, 2));
2844
- sendJSON(res, 400, {
2845
- error: { code: -32602, message: "Missing envelope format: expected { env, request }" }
2846
- });
2847
- return;
2848
- }
2849
- const invocation = parsedBody.invocation;
2850
- const oauthRequest = buildRequestFromRaw(envelope.request);
2851
- const oauthCallbackRequestConfig = buildRequestScopedConfig(envelope.env);
2852
- const oauthCallbackContext = {
2853
- request: oauthRequest,
2854
- invocation,
2855
- log: createContextLogger()
2856
- };
2857
- try {
2858
- const oauthCallbackHook = config.hooks.oauth_callback;
2859
- const oauthCallbackHandler = typeof oauthCallbackHook === "function" ? oauthCallbackHook : oauthCallbackHook.handler;
2860
- const result = await runWithLogContext({ invocation }, async () => {
2861
- return await runWithConfig(oauthCallbackRequestConfig, async () => {
2862
- return await oauthCallbackHandler(oauthCallbackContext);
2863
- });
2864
- });
2865
- sendJSON(res, 200, {
2866
- appInstallationId: result.appInstallationId,
2867
- env: result.env ?? {}
2868
- });
2869
- } catch (err) {
2870
- const errorMessage = err instanceof Error ? err.message : String(err ?? "Unknown error");
2871
- sendJSON(res, 500, {
2872
- error: {
2873
- code: -32603,
2874
- message: errorMessage
2875
- }
2876
- });
2877
- }
3136
+ const result = await handleOAuthCallback(parsedBody, config.hooks);
3137
+ sendJSON(res, result.status, result.body);
2878
3138
  return;
2879
3139
  }
2880
3140
  if (pathname === "/install" && req.method === "POST") {
2881
- if (!config.hooks?.install) {
2882
- sendJSON(res, 404, { error: "Install handler not configured" });
2883
- return;
2884
- }
2885
3141
  let installBody;
2886
3142
  try {
2887
3143
  installBody = await parseJSONBody(req);
@@ -2891,61 +3147,11 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
2891
3147
  });
2892
3148
  return;
2893
3149
  }
2894
- if (!installBody.context?.appInstallationId || !installBody.context?.workplace) {
2895
- sendJSON(res, 400, {
2896
- error: { code: -32602, message: "Missing context (appInstallationId and workplace required)" }
2897
- });
2898
- return;
2899
- }
2900
- const installContext = {
2901
- env: installBody.env ?? {},
2902
- workplace: installBody.context.workplace,
2903
- appInstallationId: installBody.context.appInstallationId,
2904
- app: installBody.context.app,
2905
- invocation: installBody.invocation,
2906
- log: createContextLogger()
2907
- };
2908
- const installRequestConfig = {
2909
- baseUrl: installBody.env?.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
2910
- apiToken: installBody.env?.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
2911
- };
2912
- try {
2913
- const installHook = config.hooks.install;
2914
- const installHandler = typeof installHook === "function" ? installHook : installHook.handler;
2915
- const result = await runWithLogContext({ invocation: installBody.invocation }, async () => {
2916
- return await runWithConfig(installRequestConfig, async () => {
2917
- return await installHandler(installContext);
2918
- });
2919
- });
2920
- sendJSON(res, 200, {
2921
- env: result.env ?? {},
2922
- redirect: result.redirect
2923
- });
2924
- } catch (err) {
2925
- if (err instanceof InstallError) {
2926
- sendJSON(res, 400, {
2927
- error: {
2928
- code: err.code,
2929
- message: err.message,
2930
- field: err.field
2931
- }
2932
- });
2933
- } else {
2934
- sendJSON(res, 500, {
2935
- error: {
2936
- code: -32603,
2937
- message: err instanceof Error ? err.message : String(err ?? "")
2938
- }
2939
- });
2940
- }
2941
- }
3150
+ const result = await handleInstall(installBody, config.hooks);
3151
+ sendJSON(res, result.status, result.body);
2942
3152
  return;
2943
3153
  }
2944
3154
  if (pathname === "/uninstall" && req.method === "POST") {
2945
- if (!config.hooks?.uninstall) {
2946
- sendJSON(res, 404, { error: "Uninstall handler not configured" });
2947
- return;
2948
- }
2949
3155
  let uninstallBody;
2950
3156
  try {
2951
3157
  uninstallBody = await parseJSONBody(req);
@@ -2955,53 +3161,11 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
2955
3161
  });
2956
3162
  return;
2957
3163
  }
2958
- if (!uninstallBody.context?.appInstallationId || !uninstallBody.context?.workplace || !uninstallBody.context?.app) {
2959
- sendJSON(res, 400, {
2960
- error: {
2961
- code: -32602,
2962
- message: "Missing context (appInstallationId, workplace and app required)"
2963
- }
2964
- });
2965
- return;
2966
- }
2967
- const uninstallContext = {
2968
- env: uninstallBody.env ?? {},
2969
- workplace: uninstallBody.context.workplace,
2970
- appInstallationId: uninstallBody.context.appInstallationId,
2971
- app: uninstallBody.context.app,
2972
- invocation: uninstallBody.invocation,
2973
- log: createContextLogger()
2974
- };
2975
- const uninstallRequestConfig = {
2976
- baseUrl: uninstallBody.env?.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
2977
- apiToken: uninstallBody.env?.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
2978
- };
2979
- try {
2980
- const uninstallHook = config.hooks.uninstall;
2981
- const uninstallHandlerFn = typeof uninstallHook === "function" ? uninstallHook : uninstallHook.handler;
2982
- const result = await runWithLogContext({ invocation: uninstallBody.invocation }, async () => {
2983
- return await runWithConfig(uninstallRequestConfig, async () => {
2984
- return await uninstallHandlerFn(uninstallContext);
2985
- });
2986
- });
2987
- sendJSON(res, 200, {
2988
- cleanedWebhookIds: result.cleanedWebhookIds ?? []
2989
- });
2990
- } catch (err) {
2991
- sendJSON(res, 500, {
2992
- error: {
2993
- code: -32603,
2994
- message: err instanceof Error ? err.message : String(err ?? "")
2995
- }
2996
- });
2997
- }
3164
+ const result = await handleUninstall(uninstallBody, config.hooks);
3165
+ sendJSON(res, result.status, result.body);
2998
3166
  return;
2999
3167
  }
3000
3168
  if (pathname === "/provision" && req.method === "POST") {
3001
- if (!config.hooks?.provision) {
3002
- sendJSON(res, 404, { error: "Provision handler not configured" });
3003
- return;
3004
- }
3005
3169
  let provisionBody;
3006
3170
  try {
3007
3171
  provisionBody = await parseJSONBody(req);
@@ -3011,46 +3175,8 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
3011
3175
  });
3012
3176
  return;
3013
3177
  }
3014
- if (!provisionBody.context?.app) {
3015
- sendJSON(res, 400, {
3016
- error: { code: -32602, message: "Missing context (app required)" }
3017
- });
3018
- return;
3019
- }
3020
- const mergedEnv = {};
3021
- for (const [key, value] of Object.entries(process.env)) {
3022
- if (value !== void 0) {
3023
- mergedEnv[key] = value;
3024
- }
3025
- }
3026
- Object.assign(mergedEnv, provisionBody.env ?? {});
3027
- const provisionContext = {
3028
- env: mergedEnv,
3029
- app: provisionBody.context.app,
3030
- invocation: provisionBody.invocation,
3031
- log: createContextLogger()
3032
- };
3033
- const provisionRequestConfig = {
3034
- baseUrl: mergedEnv.SKEDYUL_API_URL ?? "",
3035
- apiToken: mergedEnv.SKEDYUL_API_TOKEN ?? ""
3036
- };
3037
- try {
3038
- const provisionHook = config.hooks.provision;
3039
- const provisionHandler = typeof provisionHook === "function" ? provisionHook : provisionHook.handler;
3040
- const result = await runWithLogContext({ invocation: provisionBody.invocation }, async () => {
3041
- return await runWithConfig(provisionRequestConfig, async () => {
3042
- return await provisionHandler(provisionContext);
3043
- });
3044
- });
3045
- sendJSON(res, 200, result);
3046
- } catch (err) {
3047
- sendJSON(res, 500, {
3048
- error: {
3049
- code: -32603,
3050
- message: err instanceof Error ? err.message : String(err ?? "")
3051
- }
3052
- });
3053
- }
3178
+ const result = await handleProvision(provisionBody, config.hooks);
3179
+ sendJSON(res, result.status, result.body);
3054
3180
  return;
3055
3181
  }
3056
3182
  if (pathname === "/core" && req.method === "POST") {
@@ -3231,12 +3357,10 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
3231
3357
  }
3232
3358
  if (path2.startsWith("/webhooks/") && webhookRegistry) {
3233
3359
  const handle = path2.slice("/webhooks/".length);
3234
- const webhookDef = webhookRegistry[handle];
3235
- if (!webhookDef) {
3360
+ if (!webhookRegistry[handle]) {
3236
3361
  return createResponse(404, { error: `Webhook handler '${handle}' not found` }, headers);
3237
3362
  }
3238
- const allowedMethods = webhookDef.methods ?? ["POST"];
3239
- if (!allowedMethods.includes(method)) {
3363
+ if (!isMethodAllowed(webhookRegistry, handle, method)) {
3240
3364
  return createResponse(405, { error: `Method ${method} not allowed` }, headers);
3241
3365
  }
3242
3366
  const rawBody = event.body ?? "";
@@ -3251,107 +3375,34 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
3251
3375
  } else {
3252
3376
  parsedBody = rawBody;
3253
3377
  }
3254
- const isEnvelope = typeof parsedBody === "object" && parsedBody !== null && "env" in parsedBody && "request" in parsedBody && "context" in parsedBody;
3255
- let webhookRequest;
3256
- let webhookContext;
3257
- let requestEnv = {};
3258
- let invocation;
3259
- if (isEnvelope) {
3260
- const envelope = parsedBody;
3261
- requestEnv = envelope.env ?? {};
3262
- invocation = envelope.invocation;
3263
- let originalParsedBody = envelope.request.body;
3264
- const originalContentType = envelope.request.headers["content-type"] ?? "";
3265
- if (originalContentType.includes("application/json")) {
3266
- try {
3267
- originalParsedBody = envelope.request.body ? JSON.parse(envelope.request.body) : {};
3268
- } catch {
3269
- }
3270
- }
3271
- webhookRequest = {
3272
- method: envelope.request.method,
3273
- url: envelope.request.url,
3274
- path: envelope.request.path,
3275
- headers: envelope.request.headers,
3276
- query: envelope.request.query,
3277
- body: originalParsedBody,
3278
- rawBody: envelope.request.body ? Buffer.from(envelope.request.body, "utf-8") : void 0
3279
- };
3280
- const envVars = { ...process.env, ...requestEnv };
3281
- const app = envelope.context.app;
3282
- if (envelope.context.appInstallationId && envelope.context.workplace) {
3283
- webhookContext = {
3284
- env: envVars,
3285
- app,
3286
- appInstallationId: envelope.context.appInstallationId,
3287
- workplace: envelope.context.workplace,
3288
- registration: envelope.context.registration ?? {},
3289
- invocation,
3290
- log: createContextLogger()
3291
- };
3292
- } else {
3293
- webhookContext = {
3294
- env: envVars,
3295
- app,
3296
- invocation,
3297
- log: createContextLogger()
3298
- };
3299
- }
3300
- } else {
3301
- const appId = event.headers?.["x-skedyul-app-id"] ?? event.headers?.["X-Skedyul-App-Id"];
3302
- const appVersionId = event.headers?.["x-skedyul-app-version-id"] ?? event.headers?.["X-Skedyul-App-Version-Id"];
3303
- if (!appId || !appVersionId) {
3304
- throw new Error("Missing app info in webhook request (x-skedyul-app-id and x-skedyul-app-version-id headers required)");
3305
- }
3306
- const forwardedProto = event.headers?.["x-forwarded-proto"] ?? event.headers?.["X-Forwarded-Proto"];
3307
- const protocol = forwardedProto ?? "https";
3308
- const host = event.headers?.host ?? event.headers?.Host ?? "localhost";
3309
- const queryString = event.queryStringParameters ? "?" + new URLSearchParams(event.queryStringParameters).toString() : "";
3310
- const webhookUrl = `${protocol}://${host}${path2}${queryString}`;
3311
- webhookRequest = {
3312
- method,
3313
- url: webhookUrl,
3314
- path: path2,
3315
- headers: event.headers,
3316
- query: event.queryStringParameters ?? {},
3317
- body: parsedBody,
3318
- rawBody: rawBody ? Buffer.from(rawBody, "utf-8") : void 0
3319
- };
3320
- webhookContext = {
3321
- env: process.env,
3322
- app: { id: appId, versionId: appVersionId },
3323
- log: createContextLogger()
3324
- };
3325
- }
3326
- const originalEnv = { ...process.env };
3327
- Object.assign(process.env, requestEnv);
3328
- const requestConfig = {
3329
- baseUrl: requestEnv.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
3330
- apiToken: requestEnv.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
3331
- };
3332
- let webhookResponse;
3333
- try {
3334
- webhookResponse = await runWithLogContext({ invocation }, async () => {
3335
- return await runWithConfig(requestConfig, async () => {
3336
- return await webhookDef.handler(webhookRequest, webhookContext);
3337
- });
3338
- });
3339
- } catch (err) {
3340
- console.error(`Webhook handler '${handle}' error:`, err);
3341
- return createResponse(500, { error: "Webhook handler error" }, headers);
3342
- } finally {
3343
- process.env = originalEnv;
3378
+ const forwardedProto = event.headers?.["x-forwarded-proto"] ?? event.headers?.["X-Forwarded-Proto"];
3379
+ const protocol = forwardedProto ?? "https";
3380
+ const host = event.headers?.host ?? event.headers?.Host ?? "localhost";
3381
+ const queryString = event.queryStringParameters ? "?" + new URLSearchParams(event.queryStringParameters).toString() : "";
3382
+ const webhookUrl = `${protocol}://${host}${path2}${queryString}`;
3383
+ const parseResult = parseWebhookRequest(
3384
+ parsedBody,
3385
+ method,
3386
+ webhookUrl,
3387
+ path2,
3388
+ event.headers,
3389
+ event.queryStringParameters ?? {},
3390
+ rawBody,
3391
+ event.headers?.["x-skedyul-app-id"] ?? event.headers?.["X-Skedyul-App-Id"],
3392
+ event.headers?.["x-skedyul-app-version-id"] ?? event.headers?.["X-Skedyul-App-Version-Id"]
3393
+ );
3394
+ if ("error" in parseResult) {
3395
+ return createResponse(400, { error: parseResult.error }, headers);
3344
3396
  }
3397
+ const result = await executeWebhookHandler(handle, webhookRegistry, parseResult);
3345
3398
  const responseHeaders = {
3346
3399
  ...headers,
3347
- ...webhookResponse.headers
3400
+ ...result.headers
3348
3401
  };
3349
- const status = webhookResponse.status ?? 200;
3350
- const body = webhookResponse.body;
3351
3402
  return {
3352
- statusCode: status,
3403
+ statusCode: result.status,
3353
3404
  headers: responseHeaders,
3354
- body: body !== void 0 ? typeof body === "string" ? body : JSON.stringify(body) : ""
3405
+ body: result.body !== void 0 ? typeof result.body === "string" ? result.body : JSON.stringify(result.body) : ""
3355
3406
  };
3356
3407
  }
3357
3408
  if (path2 === "/core" && method === "POST") {
@@ -3487,9 +3538,6 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
3487
3538
  }
3488
3539
  }
3489
3540
  if (path2 === "/install" && method === "POST") {
3490
- if (!config.hooks?.install) {
3491
- return createResponse(404, { error: "Install handler not configured" }, headers);
3492
- }
3493
3541
  let installBody;
3494
3542
  try {
3495
3543
  installBody = event.body ? JSON.parse(event.body) : {};
@@ -3500,68 +3548,10 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
3500
3548
  headers
3501
3549
  );
3502
3550
  }
3503
- if (!installBody.context?.appInstallationId || !installBody.context?.workplace) {
3504
- return createResponse(
3505
- 400,
3506
- { error: { code: -32602, message: "Missing context (appInstallationId and workplace required)" } },
3507
- headers
3508
- );
3509
- }
3510
- const installContext = {
3511
- env: installBody.env ?? {},
3512
- workplace: installBody.context.workplace,
3513
- appInstallationId: installBody.context.appInstallationId,
3514
- app: installBody.context.app,
3515
- invocation: installBody.invocation,
3516
- log: createContextLogger()
3517
- };
3518
- const installRequestConfig = {
3519
- baseUrl: installBody.env?.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
3520
- apiToken: installBody.env?.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
3521
- };
3522
- try {
3523
- const installHook = config.hooks.install;
3524
- const installHandler = typeof installHook === "function" ? installHook : installHook.handler;
3525
- const result = await runWithLogContext({ invocation: installBody.invocation }, async () => {
3526
- return await runWithConfig(installRequestConfig, async () => {
3527
- return await installHandler(installContext);
3528
- });
3529
- });
3530
- return createResponse(
3531
- 200,
3532
- { env: result.env ?? {}, redirect: result.redirect },
3533
- headers
3534
- );
3535
- } catch (err) {
3536
- if (err instanceof InstallError) {
3537
- return createResponse(
3538
- 400,
3539
- {
3540
- error: {
3541
- code: err.code,
3542
- message: err.message,
3543
- field: err.field
3544
- }
3545
- },
3546
- headers
3547
- );
3548
- }
3549
- return createResponse(
3550
- 500,
3551
- {
3552
- error: {
3553
- code: -32603,
3554
- message: err instanceof Error ? err.message : String(err ?? "")
3555
- }
3556
- },
3557
- headers
3558
- );
3559
- }
3551
+ const result = await handleInstall(installBody, config.hooks);
3552
+ return createResponse(result.status, result.body, headers);
3560
3553
  }
3561
3554
  if (path2 === "/uninstall" && method === "POST") {
3562
- if (!config.hooks?.uninstall) {
3563
- return createResponse(404, { error: "Uninstall handler not configured" }, headers);
3564
- }
3565
3555
  let uninstallBody;
3566
3556
  try {
3567
3557
  uninstallBody = event.body ? JSON.parse(event.body) : {};
@@ -3572,137 +3562,24 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
3572
3562
  headers
3573
3563
  );
3574
3564
  }
3575
- if (!uninstallBody.context?.appInstallationId || !uninstallBody.context?.workplace || !uninstallBody.context?.app) {
3576
- return createResponse(
3577
- 400,
3578
- {
3579
- error: {
3580
- code: -32602,
3581
- message: "Missing context (appInstallationId, workplace and app required)"
3582
- }
3583
- },
3584
- headers
3585
- );
3586
- }
3587
- const uninstallContext = {
3588
- env: uninstallBody.env ?? {},
3589
- workplace: uninstallBody.context.workplace,
3590
- appInstallationId: uninstallBody.context.appInstallationId,
3591
- app: uninstallBody.context.app,
3592
- invocation: uninstallBody.invocation,
3593
- log: createContextLogger()
3594
- };
3595
- const uninstallRequestConfig = {
3596
- baseUrl: uninstallBody.env?.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
3597
- apiToken: uninstallBody.env?.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
3598
- };
3599
- try {
3600
- const uninstallHook = config.hooks.uninstall;
3601
- const uninstallHandlerFn = typeof uninstallHook === "function" ? uninstallHook : uninstallHook.handler;
3602
- const result = await runWithLogContext({ invocation: uninstallBody.invocation }, async () => {
3603
- return await runWithConfig(uninstallRequestConfig, async () => {
3604
- return await uninstallHandlerFn(uninstallContext);
3605
- });
3606
- });
3607
- return createResponse(
3608
- 200,
3609
- { cleanedWebhookIds: result.cleanedWebhookIds ?? [] },
3610
- headers
3611
- );
3612
- } catch (err) {
3613
- return createResponse(
3614
- 500,
3615
- {
3616
- error: {
3617
- code: -32603,
3618
- message: err instanceof Error ? err.message : String(err ?? "")
3619
- }
3620
- },
3621
- headers
3622
- );
3623
- }
3565
+ const result = await handleUninstall(uninstallBody, config.hooks);
3566
+ return createResponse(result.status, result.body, headers);
3624
3567
  }
3625
3568
  if (path2 === "/provision" && method === "POST") {
3626
- console.log("[serverless] /provision endpoint called");
3627
- if (!config.hooks?.provision) {
3628
- console.log("[serverless] No provision handler configured");
3629
- return createResponse(404, { error: "Provision handler not configured" }, headers);
3630
- }
3631
3569
  let provisionBody;
3632
3570
  try {
3633
3571
  provisionBody = event.body ? JSON.parse(event.body) : {};
3634
- console.log("[serverless] Provision body parsed:", {
3635
- hasEnv: !!provisionBody.env,
3636
- hasContext: !!provisionBody.context,
3637
- appId: provisionBody.context?.app?.id,
3638
- versionId: provisionBody.context?.app?.versionId
3639
- });
3640
3572
  } catch {
3641
- console.log("[serverless] Failed to parse provision body");
3642
3573
  return createResponse(
3643
3574
  400,
3644
3575
  { error: { code: -32700, message: "Parse error" } },
3645
3576
  headers
3646
3577
  );
3647
3578
  }
3648
- if (!provisionBody.context?.app) {
3649
- console.log("[serverless] Missing app context in provision body");
3650
- return createResponse(
3651
- 400,
3652
- { error: { code: -32602, message: "Missing context (app required)" } },
3653
- headers
3654
- );
3655
- }
3656
- const mergedEnv = {};
3657
- for (const [key, value] of Object.entries(process.env)) {
3658
- if (value !== void 0) {
3659
- mergedEnv[key] = value;
3660
- }
3661
- }
3662
- Object.assign(mergedEnv, provisionBody.env ?? {});
3663
- const provisionContext = {
3664
- env: mergedEnv,
3665
- app: provisionBody.context.app,
3666
- invocation: provisionBody.invocation,
3667
- log: createContextLogger()
3668
- };
3669
- const provisionRequestConfig = {
3670
- baseUrl: mergedEnv.SKEDYUL_API_URL ?? "",
3671
- apiToken: mergedEnv.SKEDYUL_API_TOKEN ?? ""
3672
- };
3673
- console.log("[serverless] Calling provision handler...");
3674
- try {
3675
- const provisionHook = config.hooks.provision;
3676
- const provisionHandler = typeof provisionHook === "function" ? provisionHook : provisionHook.handler;
3677
- const result = await runWithLogContext({ invocation: provisionBody.invocation }, async () => {
3678
- return await runWithConfig(provisionRequestConfig, async () => {
3679
- return await provisionHandler(provisionContext);
3680
- });
3681
- });
3682
- console.log("[serverless] Provision handler completed successfully");
3683
- return createResponse(200, result, headers);
3684
- } catch (err) {
3685
- console.error("[serverless] Provision handler failed:", err instanceof Error ? err.message : String(err));
3686
- return createResponse(
3687
- 500,
3688
- {
3689
- error: {
3690
- code: -32603,
3691
- message: err instanceof Error ? err.message : String(err ?? "")
3692
- }
3693
- },
3694
- headers
3695
- );
3696
- }
3579
+ const result = await handleProvision(provisionBody, config.hooks);
3580
+ return createResponse(result.status, result.body, headers);
3697
3581
  }
3698
3582
  if (path2 === "/oauth_callback" && method === "POST") {
3699
- if (!config.hooks?.oauth_callback) {
3700
- return createResponse(
3701
- 404,
3702
- { error: "OAuth callback handler not configured" },
3703
- headers
3704
- );
3705
- }
3706
3583
  let parsedBody;
3707
3584
  try {
3708
3585
  parsedBody = event.body ? JSON.parse(event.body) : {};
@@ -3714,52 +3591,8 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
3714
3591
  headers
3715
3592
  );
3716
3593
  }
3717
- const envelope = parseHandlerEnvelope(parsedBody);
3718
- if (!envelope) {
3719
- console.error("[OAuth Callback] Failed to parse envelope. Body:", JSON.stringify(parsedBody, null, 2));
3720
- return createResponse(
3721
- 400,
3722
- { error: { code: -32602, message: "Missing envelope format: expected { env, request }" } },
3723
- headers
3724
- );
3725
- }
3726
- const invocation = parsedBody.invocation;
3727
- const oauthRequest = buildRequestFromRaw(envelope.request);
3728
- const oauthCallbackRequestConfig = buildRequestScopedConfig(envelope.env);
3729
- const oauthCallbackContext = {
3730
- request: oauthRequest,
3731
- invocation,
3732
- log: createContextLogger()
3733
- };
3734
- try {
3735
- const oauthCallbackHook = config.hooks.oauth_callback;
3736
- const oauthCallbackHandler = typeof oauthCallbackHook === "function" ? oauthCallbackHook : oauthCallbackHook.handler;
3737
- const result = await runWithLogContext({ invocation }, async () => {
3738
- return await runWithConfig(oauthCallbackRequestConfig, async () => {
3739
- return await oauthCallbackHandler(oauthCallbackContext);
3740
- });
3741
- });
3742
- return createResponse(
3743
- 200,
3744
- {
3745
- appInstallationId: result.appInstallationId,
3746
- env: result.env ?? {}
3747
- },
3748
- headers
3749
- );
3750
- } catch (err) {
3751
- const errorMessage = err instanceof Error ? err.message : String(err ?? "Unknown error");
3752
- return createResponse(
3753
- 500,
3754
- {
3755
- error: {
3756
- code: -32603,
3757
- message: errorMessage
3758
- }
3759
- },
3760
- headers
3761
- );
3762
- }
3594
+ const result = await handleOAuthCallback(parsedBody, config.hooks);
3595
+ return createResponse(result.status, result.body, headers);
3763
3596
  }
3764
3597
  if (path2 === "/health" && method === "GET") {
3765
3598
  return createResponse(200, state.getHealthStatus(), headers);