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.
@@ -2078,8 +2078,8 @@ function createRequestState(maxRequests, ttlExtendSeconds, runtimeLabel, toolNam
2078
2078
  };
2079
2079
  }
2080
2080
  function createCallToolHandler(registry, state, onMaxRequests) {
2081
- return async function callTool(nameRaw, argsRaw) {
2082
- const toolName = String(nameRaw);
2081
+ return async function callTool(toolNameInput, toolArgsInput) {
2082
+ const toolName = String(toolNameInput);
2083
2083
  const tool = registry[toolName];
2084
2084
  if (!tool) {
2085
2085
  throw new Error(`Tool "${toolName}" not found in registry`);
@@ -2088,7 +2088,7 @@ function createCallToolHandler(registry, state, onMaxRequests) {
2088
2088
  throw new Error(`Tool "${toolName}" handler is not a function`);
2089
2089
  }
2090
2090
  const fn = tool.handler;
2091
- const args = argsRaw ?? {};
2091
+ const args = toolArgsInput ?? {};
2092
2092
  const estimateMode = args.estimate === true;
2093
2093
  if (!estimateMode) {
2094
2094
  state.incrementRequestCount();
@@ -2343,51 +2343,6 @@ async function handleCoreMethod(method, params) {
2343
2343
  };
2344
2344
  }
2345
2345
 
2346
- // src/server/handler-helpers.ts
2347
- function parseHandlerEnvelope(parsedBody) {
2348
- if (typeof parsedBody !== "object" || parsedBody === null || Array.isArray(parsedBody) || !("env" in parsedBody) || !("request" in parsedBody)) {
2349
- return null;
2350
- }
2351
- const envelope = parsedBody;
2352
- if (typeof envelope.env !== "object" || envelope.env === null || Array.isArray(envelope.env)) {
2353
- return null;
2354
- }
2355
- if (typeof envelope.request !== "object" || envelope.request === null || Array.isArray(envelope.request)) {
2356
- return null;
2357
- }
2358
- return {
2359
- env: envelope.env,
2360
- request: envelope.request,
2361
- context: envelope.context
2362
- };
2363
- }
2364
- function buildRequestFromRaw(raw) {
2365
- let parsedBody = raw.body;
2366
- const contentType = raw.headers["content-type"] ?? "";
2367
- if (contentType.includes("application/json")) {
2368
- try {
2369
- parsedBody = raw.body ? JSON.parse(raw.body) : {};
2370
- } catch {
2371
- parsedBody = raw.body;
2372
- }
2373
- }
2374
- return {
2375
- method: raw.method,
2376
- url: raw.url,
2377
- path: raw.path,
2378
- headers: raw.headers,
2379
- query: raw.query,
2380
- body: parsedBody,
2381
- rawBody: raw.body ? Buffer.from(raw.body, "utf-8") : void 0
2382
- };
2383
- }
2384
- function buildRequestScopedConfig(env) {
2385
- return {
2386
- baseUrl: env.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
2387
- apiToken: env.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
2388
- };
2389
- }
2390
-
2391
2346
  // src/server/startup-logger.ts
2392
2347
  function padEnd(str, length) {
2393
2348
  if (str.length >= length) {
@@ -2485,6 +2440,405 @@ function isProvisionConfig(value) {
2485
2440
  return value !== void 0 && value !== null && !(value instanceof Promise);
2486
2441
  }
2487
2442
 
2443
+ // src/server/handlers/install-handler.ts
2444
+ async function handleInstall(body, hooks) {
2445
+ if (!hooks?.install) {
2446
+ return {
2447
+ status: 404,
2448
+ body: { error: "Install handler not configured" }
2449
+ };
2450
+ }
2451
+ if (!body.context?.appInstallationId || !body.context?.workplace) {
2452
+ return {
2453
+ status: 400,
2454
+ body: {
2455
+ error: {
2456
+ code: -32602,
2457
+ message: "Missing context (appInstallationId and workplace required)"
2458
+ }
2459
+ }
2460
+ };
2461
+ }
2462
+ const installContext = {
2463
+ env: body.env ?? {},
2464
+ workplace: body.context.workplace,
2465
+ appInstallationId: body.context.appInstallationId,
2466
+ app: body.context.app,
2467
+ invocation: body.invocation,
2468
+ log: createContextLogger()
2469
+ };
2470
+ const requestConfig = {
2471
+ baseUrl: body.env?.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
2472
+ apiToken: body.env?.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
2473
+ };
2474
+ try {
2475
+ const installHook = hooks.install;
2476
+ const installHandler = typeof installHook === "function" ? installHook : installHook.handler;
2477
+ const result = await runWithLogContext({ invocation: body.invocation }, async () => {
2478
+ return await runWithConfig(requestConfig, async () => {
2479
+ return await installHandler(installContext);
2480
+ });
2481
+ });
2482
+ return {
2483
+ status: 200,
2484
+ body: { env: result.env ?? {}, redirect: result.redirect }
2485
+ };
2486
+ } catch (err) {
2487
+ if (err instanceof InstallError) {
2488
+ return {
2489
+ status: 400,
2490
+ body: {
2491
+ error: {
2492
+ code: err.code,
2493
+ message: err.message,
2494
+ field: err.field
2495
+ }
2496
+ }
2497
+ };
2498
+ }
2499
+ return {
2500
+ status: 500,
2501
+ body: {
2502
+ error: {
2503
+ code: -32603,
2504
+ message: err instanceof Error ? err.message : String(err ?? "")
2505
+ }
2506
+ }
2507
+ };
2508
+ }
2509
+ }
2510
+
2511
+ // src/server/handlers/uninstall-handler.ts
2512
+ async function handleUninstall(body, hooks) {
2513
+ if (!hooks?.uninstall) {
2514
+ return {
2515
+ status: 404,
2516
+ body: { error: "Uninstall handler not configured" }
2517
+ };
2518
+ }
2519
+ if (!body.context?.appInstallationId || !body.context?.workplace || !body.context?.app) {
2520
+ return {
2521
+ status: 400,
2522
+ body: {
2523
+ error: {
2524
+ code: -32602,
2525
+ message: "Missing context (appInstallationId, workplace and app required)"
2526
+ }
2527
+ }
2528
+ };
2529
+ }
2530
+ const uninstallContext = {
2531
+ env: body.env ?? {},
2532
+ workplace: body.context.workplace,
2533
+ appInstallationId: body.context.appInstallationId,
2534
+ app: body.context.app,
2535
+ invocation: body.invocation,
2536
+ log: createContextLogger()
2537
+ };
2538
+ const requestConfig = {
2539
+ baseUrl: body.env?.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
2540
+ apiToken: body.env?.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
2541
+ };
2542
+ try {
2543
+ const uninstallHook = hooks.uninstall;
2544
+ const uninstallHandlerFn = typeof uninstallHook === "function" ? uninstallHook : uninstallHook.handler;
2545
+ const result = await runWithLogContext({ invocation: body.invocation }, async () => {
2546
+ return await runWithConfig(requestConfig, async () => {
2547
+ return await uninstallHandlerFn(uninstallContext);
2548
+ });
2549
+ });
2550
+ return {
2551
+ status: 200,
2552
+ body: { cleanedWebhookIds: result.cleanedWebhookIds ?? [] }
2553
+ };
2554
+ } catch (err) {
2555
+ return {
2556
+ status: 500,
2557
+ body: {
2558
+ error: {
2559
+ code: -32603,
2560
+ message: err instanceof Error ? err.message : String(err ?? "")
2561
+ }
2562
+ }
2563
+ };
2564
+ }
2565
+ }
2566
+
2567
+ // src/server/handlers/provision-handler.ts
2568
+ async function handleProvision(body, hooks) {
2569
+ if (!hooks?.provision) {
2570
+ return {
2571
+ status: 404,
2572
+ body: { error: "Provision handler not configured" }
2573
+ };
2574
+ }
2575
+ if (!body.context?.app) {
2576
+ return {
2577
+ status: 400,
2578
+ body: {
2579
+ error: {
2580
+ code: -32602,
2581
+ message: "Missing context (app required)"
2582
+ }
2583
+ }
2584
+ };
2585
+ }
2586
+ const mergedEnv = {};
2587
+ for (const [key, value] of Object.entries(process.env)) {
2588
+ if (value !== void 0) {
2589
+ mergedEnv[key] = value;
2590
+ }
2591
+ }
2592
+ Object.assign(mergedEnv, body.env ?? {});
2593
+ const provisionContext = {
2594
+ env: mergedEnv,
2595
+ app: body.context.app,
2596
+ invocation: body.invocation,
2597
+ log: createContextLogger()
2598
+ };
2599
+ const requestConfig = {
2600
+ baseUrl: mergedEnv.SKEDYUL_API_URL ?? "",
2601
+ apiToken: mergedEnv.SKEDYUL_API_TOKEN ?? ""
2602
+ };
2603
+ try {
2604
+ const provisionHook = hooks.provision;
2605
+ const provisionHandler = typeof provisionHook === "function" ? provisionHook : provisionHook.handler;
2606
+ const result = await runWithLogContext({ invocation: body.invocation }, async () => {
2607
+ return await runWithConfig(requestConfig, async () => {
2608
+ return await provisionHandler(provisionContext);
2609
+ });
2610
+ });
2611
+ return {
2612
+ status: 200,
2613
+ body: result
2614
+ };
2615
+ } catch (err) {
2616
+ return {
2617
+ status: 500,
2618
+ body: {
2619
+ error: {
2620
+ code: -32603,
2621
+ message: err instanceof Error ? err.message : String(err ?? "")
2622
+ }
2623
+ }
2624
+ };
2625
+ }
2626
+ }
2627
+
2628
+ // src/server/handler-helpers.ts
2629
+ function parseHandlerEnvelope(parsedBody) {
2630
+ if (typeof parsedBody !== "object" || parsedBody === null || Array.isArray(parsedBody) || !("env" in parsedBody) || !("request" in parsedBody)) {
2631
+ return null;
2632
+ }
2633
+ const envelope = parsedBody;
2634
+ if (typeof envelope.env !== "object" || envelope.env === null || Array.isArray(envelope.env)) {
2635
+ return null;
2636
+ }
2637
+ if (typeof envelope.request !== "object" || envelope.request === null || Array.isArray(envelope.request)) {
2638
+ return null;
2639
+ }
2640
+ return {
2641
+ env: envelope.env,
2642
+ request: envelope.request,
2643
+ context: envelope.context
2644
+ };
2645
+ }
2646
+ function buildRequestFromRaw(raw) {
2647
+ let parsedBody = raw.body;
2648
+ const contentType = raw.headers["content-type"] ?? "";
2649
+ if (contentType.includes("application/json")) {
2650
+ try {
2651
+ parsedBody = raw.body ? JSON.parse(raw.body) : {};
2652
+ } catch {
2653
+ parsedBody = raw.body;
2654
+ }
2655
+ }
2656
+ return {
2657
+ method: raw.method,
2658
+ url: raw.url,
2659
+ path: raw.path,
2660
+ headers: raw.headers,
2661
+ query: raw.query,
2662
+ body: parsedBody,
2663
+ rawBody: raw.body ? Buffer.from(raw.body, "utf-8") : void 0
2664
+ };
2665
+ }
2666
+ function buildRequestScopedConfig(env) {
2667
+ return {
2668
+ baseUrl: env.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
2669
+ apiToken: env.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
2670
+ };
2671
+ }
2672
+
2673
+ // src/server/handlers/oauth-callback-handler.ts
2674
+ async function handleOAuthCallback(parsedBody, hooks) {
2675
+ if (!hooks?.oauth_callback) {
2676
+ return {
2677
+ status: 404,
2678
+ body: { error: "OAuth callback handler not configured" }
2679
+ };
2680
+ }
2681
+ const envelope = parseHandlerEnvelope(parsedBody);
2682
+ if (!envelope) {
2683
+ console.error("[OAuth Callback] Failed to parse envelope. Body:", JSON.stringify(parsedBody, null, 2));
2684
+ return {
2685
+ status: 400,
2686
+ body: {
2687
+ error: {
2688
+ code: -32602,
2689
+ message: "Missing envelope format: expected { env, request }"
2690
+ }
2691
+ }
2692
+ };
2693
+ }
2694
+ const invocation = parsedBody.invocation;
2695
+ const oauthRequest = buildRequestFromRaw(envelope.request);
2696
+ const requestConfig = buildRequestScopedConfig(envelope.env);
2697
+ const oauthCallbackContext = {
2698
+ request: oauthRequest,
2699
+ invocation,
2700
+ log: createContextLogger()
2701
+ };
2702
+ try {
2703
+ const oauthCallbackHook = hooks.oauth_callback;
2704
+ const oauthCallbackHandler = typeof oauthCallbackHook === "function" ? oauthCallbackHook : oauthCallbackHook.handler;
2705
+ const result = await runWithLogContext({ invocation }, async () => {
2706
+ return await runWithConfig(requestConfig, async () => {
2707
+ return await oauthCallbackHandler(oauthCallbackContext);
2708
+ });
2709
+ });
2710
+ return {
2711
+ status: 200,
2712
+ body: {
2713
+ appInstallationId: result.appInstallationId,
2714
+ env: result.env ?? {}
2715
+ }
2716
+ };
2717
+ } catch (err) {
2718
+ const errorMessage = err instanceof Error ? err.message : String(err ?? "Unknown error");
2719
+ return {
2720
+ status: 500,
2721
+ body: {
2722
+ error: {
2723
+ code: -32603,
2724
+ message: errorMessage
2725
+ }
2726
+ }
2727
+ };
2728
+ }
2729
+ }
2730
+
2731
+ // src/server/handlers/webhook-handler.ts
2732
+ function parseWebhookRequest(parsedBody, method, url, path2, headers, query, rawBody, appIdHeader, appVersionIdHeader) {
2733
+ const isEnvelope = typeof parsedBody === "object" && parsedBody !== null && "env" in parsedBody && "request" in parsedBody && "context" in parsedBody;
2734
+ if (isEnvelope) {
2735
+ const envelope = parsedBody;
2736
+ const requestEnv = envelope.env ?? {};
2737
+ const invocation = envelope.invocation;
2738
+ let originalParsedBody = envelope.request.body;
2739
+ const originalContentType = envelope.request.headers["content-type"] ?? "";
2740
+ if (originalContentType.includes("application/json")) {
2741
+ try {
2742
+ originalParsedBody = envelope.request.body ? JSON.parse(envelope.request.body) : {};
2743
+ } catch {
2744
+ }
2745
+ }
2746
+ const webhookRequest2 = {
2747
+ method: envelope.request.method,
2748
+ url: envelope.request.url,
2749
+ path: envelope.request.path,
2750
+ headers: envelope.request.headers,
2751
+ query: envelope.request.query,
2752
+ body: originalParsedBody,
2753
+ rawBody: envelope.request.body ? Buffer.from(envelope.request.body, "utf-8") : void 0
2754
+ };
2755
+ const envVars = { ...process.env, ...requestEnv };
2756
+ const app = envelope.context.app;
2757
+ let webhookContext2;
2758
+ if (envelope.context.appInstallationId && envelope.context.workplace) {
2759
+ webhookContext2 = {
2760
+ env: envVars,
2761
+ app,
2762
+ appInstallationId: envelope.context.appInstallationId,
2763
+ workplace: envelope.context.workplace,
2764
+ registration: envelope.context.registration ?? {},
2765
+ invocation,
2766
+ log: createContextLogger()
2767
+ };
2768
+ } else {
2769
+ webhookContext2 = {
2770
+ env: envVars,
2771
+ app,
2772
+ invocation,
2773
+ log: createContextLogger()
2774
+ };
2775
+ }
2776
+ return { webhookRequest: webhookRequest2, webhookContext: webhookContext2, requestEnv, invocation };
2777
+ }
2778
+ if (!appIdHeader || !appVersionIdHeader) {
2779
+ return {
2780
+ error: "Missing app info in webhook request (x-skedyul-app-id and x-skedyul-app-version-id headers required)"
2781
+ };
2782
+ }
2783
+ const webhookRequest = {
2784
+ method,
2785
+ url,
2786
+ path: path2,
2787
+ headers,
2788
+ query,
2789
+ body: parsedBody,
2790
+ rawBody: rawBody ? Buffer.from(rawBody, "utf-8") : void 0
2791
+ };
2792
+ const webhookContext = {
2793
+ env: process.env,
2794
+ app: { id: appIdHeader, versionId: appVersionIdHeader },
2795
+ log: createContextLogger()
2796
+ };
2797
+ return { webhookRequest, webhookContext, requestEnv: {} };
2798
+ }
2799
+ async function executeWebhookHandler(handle, webhookRegistry, data) {
2800
+ const webhookDef = webhookRegistry[handle];
2801
+ if (!webhookDef) {
2802
+ return {
2803
+ status: 404,
2804
+ body: { error: `Webhook handler '${handle}' not found` }
2805
+ };
2806
+ }
2807
+ const originalEnv = { ...process.env };
2808
+ Object.assign(process.env, data.requestEnv);
2809
+ const requestConfig = {
2810
+ baseUrl: data.requestEnv.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
2811
+ apiToken: data.requestEnv.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
2812
+ };
2813
+ let webhookResponse;
2814
+ try {
2815
+ webhookResponse = await runWithLogContext({ invocation: data.invocation }, async () => {
2816
+ return await runWithConfig(requestConfig, async () => {
2817
+ return await webhookDef.handler(data.webhookRequest, data.webhookContext);
2818
+ });
2819
+ });
2820
+ } catch (err) {
2821
+ console.error(`Webhook handler '${handle}' error:`, err);
2822
+ return {
2823
+ status: 500,
2824
+ body: { error: "Webhook handler error" }
2825
+ };
2826
+ } finally {
2827
+ process.env = originalEnv;
2828
+ }
2829
+ return {
2830
+ status: webhookResponse.status ?? 200,
2831
+ body: webhookResponse.body,
2832
+ headers: webhookResponse.headers
2833
+ };
2834
+ }
2835
+ function isMethodAllowed(webhookRegistry, handle, method) {
2836
+ const webhookDef = webhookRegistry[handle];
2837
+ if (!webhookDef) return false;
2838
+ const allowedMethods = webhookDef.methods ?? ["POST"];
2839
+ return allowedMethods.includes(method);
2840
+ }
2841
+
2488
2842
  // src/server/dedicated.ts
2489
2843
  function createDedicatedServerInstance(config, tools, callTool, state, mcpServer) {
2490
2844
  const port = getListeningPort(config);
@@ -2511,13 +2865,11 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
2511
2865
  }
2512
2866
  if (pathname.startsWith("/webhooks/") && webhookRegistry) {
2513
2867
  const handle = pathname.slice("/webhooks/".length);
2514
- const webhookDef = webhookRegistry[handle];
2515
- if (!webhookDef) {
2868
+ if (!webhookRegistry[handle]) {
2516
2869
  sendJSON(res, 404, { error: `Webhook handler '${handle}' not found` });
2517
2870
  return;
2518
2871
  }
2519
- const allowedMethods = webhookDef.methods ?? ["POST"];
2520
- if (!allowedMethods.includes(req.method)) {
2872
+ if (!isMethodAllowed(webhookRegistry, handle, req.method ?? "POST")) {
2521
2873
  sendJSON(res, 405, { error: `Method ${req.method} not allowed` });
2522
2874
  return;
2523
2875
  }
@@ -2539,87 +2891,34 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
2539
2891
  } else {
2540
2892
  parsedBody = rawBody;
2541
2893
  }
2542
- const envelope = parseHandlerEnvelope(parsedBody);
2543
- let webhookRequest;
2544
- let webhookContext;
2545
- let requestEnv = {};
2546
- let invocation;
2547
- if (envelope && "context" in envelope && envelope.context) {
2548
- const context = envelope.context;
2549
- requestEnv = envelope.env;
2550
- invocation = parsedBody.invocation;
2551
- webhookRequest = buildRequestFromRaw(envelope.request);
2552
- const envVars = { ...process.env, ...envelope.env };
2553
- const app = context.app;
2554
- if (context.appInstallationId && context.workplace) {
2555
- webhookContext = {
2556
- env: envVars,
2557
- app,
2558
- appInstallationId: context.appInstallationId,
2559
- workplace: context.workplace,
2560
- registration: context.registration ?? {},
2561
- invocation,
2562
- log: createContextLogger()
2563
- };
2564
- } else {
2565
- webhookContext = {
2566
- env: envVars,
2567
- app,
2568
- invocation,
2569
- log: createContextLogger()
2570
- };
2571
- }
2572
- } else {
2573
- const appId = req.headers["x-skedyul-app-id"];
2574
- const appVersionId = req.headers["x-skedyul-app-version-id"];
2575
- if (!appId || !appVersionId) {
2576
- throw new Error("Missing app info in webhook request (x-skedyul-app-id and x-skedyul-app-version-id headers required)");
2577
- }
2578
- webhookRequest = {
2579
- method: req.method ?? "POST",
2580
- url: url.toString(),
2581
- path: pathname,
2582
- headers: req.headers,
2583
- query: Object.fromEntries(url.searchParams.entries()),
2584
- body: parsedBody,
2585
- rawBody: rawBody ? Buffer.from(rawBody, "utf-8") : void 0
2586
- };
2587
- webhookContext = {
2588
- env: process.env,
2589
- app: { id: appId, versionId: appVersionId },
2590
- log: createContextLogger()
2591
- };
2592
- }
2593
- const originalEnv = { ...process.env };
2594
- Object.assign(process.env, requestEnv);
2595
- const requestConfig = buildRequestScopedConfig(requestEnv);
2596
- let webhookResponse;
2597
- try {
2598
- webhookResponse = await runWithLogContext({ invocation }, async () => {
2599
- return await runWithConfig(requestConfig, async () => {
2600
- return await webhookDef.handler(webhookRequest, webhookContext);
2601
- });
2602
- });
2603
- } catch (err) {
2604
- console.error(`Webhook handler '${handle}' error:`, err);
2605
- sendJSON(res, 500, { error: "Webhook handler error" });
2894
+ const parseResult = parseWebhookRequest(
2895
+ parsedBody,
2896
+ req.method ?? "POST",
2897
+ url.toString(),
2898
+ pathname,
2899
+ req.headers,
2900
+ Object.fromEntries(url.searchParams.entries()),
2901
+ rawBody,
2902
+ req.headers["x-skedyul-app-id"],
2903
+ req.headers["x-skedyul-app-version-id"]
2904
+ );
2905
+ if ("error" in parseResult) {
2906
+ sendJSON(res, 400, { error: parseResult.error });
2606
2907
  return;
2607
- } finally {
2608
- process.env = originalEnv;
2609
2908
  }
2610
- const status = webhookResponse.status ?? 200;
2909
+ const result = await executeWebhookHandler(handle, webhookRegistry, parseResult);
2611
2910
  const responseHeaders = {
2612
- ...webhookResponse.headers
2911
+ ...result.headers
2613
2912
  };
2614
2913
  if (!responseHeaders["Content-Type"] && !responseHeaders["content-type"]) {
2615
2914
  responseHeaders["Content-Type"] = "application/json";
2616
2915
  }
2617
- res.writeHead(status, responseHeaders);
2618
- if (webhookResponse.body !== void 0) {
2619
- if (typeof webhookResponse.body === "string") {
2620
- res.end(webhookResponse.body);
2916
+ res.writeHead(result.status, responseHeaders);
2917
+ if (result.body !== void 0) {
2918
+ if (typeof result.body === "string") {
2919
+ res.end(result.body);
2621
2920
  } else {
2622
- res.end(JSON.stringify(webhookResponse.body));
2921
+ res.end(JSON.stringify(result.body));
2623
2922
  }
2624
2923
  } else {
2625
2924
  res.end();
@@ -2658,10 +2957,6 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
2658
2957
  return;
2659
2958
  }
2660
2959
  if (pathname === "/oauth_callback" && req.method === "POST") {
2661
- if (!config.hooks?.oauth_callback) {
2662
- sendJSON(res, 404, { error: "OAuth callback handler not configured" });
2663
- return;
2664
- }
2665
2960
  let parsedBody;
2666
2961
  try {
2667
2962
  parsedBody = await parseJSONBody(req);
@@ -2672,50 +2967,11 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
2672
2967
  });
2673
2968
  return;
2674
2969
  }
2675
- const envelope = parseHandlerEnvelope(parsedBody);
2676
- if (!envelope) {
2677
- console.error("[OAuth Callback] Failed to parse envelope. Body:", JSON.stringify(parsedBody, null, 2));
2678
- sendJSON(res, 400, {
2679
- error: { code: -32602, message: "Missing envelope format: expected { env, request }" }
2680
- });
2681
- return;
2682
- }
2683
- const invocation = parsedBody.invocation;
2684
- const oauthRequest = buildRequestFromRaw(envelope.request);
2685
- const oauthCallbackRequestConfig = buildRequestScopedConfig(envelope.env);
2686
- const oauthCallbackContext = {
2687
- request: oauthRequest,
2688
- invocation,
2689
- log: createContextLogger()
2690
- };
2691
- try {
2692
- const oauthCallbackHook = config.hooks.oauth_callback;
2693
- const oauthCallbackHandler = typeof oauthCallbackHook === "function" ? oauthCallbackHook : oauthCallbackHook.handler;
2694
- const result = await runWithLogContext({ invocation }, async () => {
2695
- return await runWithConfig(oauthCallbackRequestConfig, async () => {
2696
- return await oauthCallbackHandler(oauthCallbackContext);
2697
- });
2698
- });
2699
- sendJSON(res, 200, {
2700
- appInstallationId: result.appInstallationId,
2701
- env: result.env ?? {}
2702
- });
2703
- } catch (err) {
2704
- const errorMessage = err instanceof Error ? err.message : String(err ?? "Unknown error");
2705
- sendJSON(res, 500, {
2706
- error: {
2707
- code: -32603,
2708
- message: errorMessage
2709
- }
2710
- });
2711
- }
2970
+ const result = await handleOAuthCallback(parsedBody, config.hooks);
2971
+ sendJSON(res, result.status, result.body);
2712
2972
  return;
2713
2973
  }
2714
2974
  if (pathname === "/install" && req.method === "POST") {
2715
- if (!config.hooks?.install) {
2716
- sendJSON(res, 404, { error: "Install handler not configured" });
2717
- return;
2718
- }
2719
2975
  let installBody;
2720
2976
  try {
2721
2977
  installBody = await parseJSONBody(req);
@@ -2725,61 +2981,11 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
2725
2981
  });
2726
2982
  return;
2727
2983
  }
2728
- if (!installBody.context?.appInstallationId || !installBody.context?.workplace) {
2729
- sendJSON(res, 400, {
2730
- error: { code: -32602, message: "Missing context (appInstallationId and workplace required)" }
2731
- });
2732
- return;
2733
- }
2734
- const installContext = {
2735
- env: installBody.env ?? {},
2736
- workplace: installBody.context.workplace,
2737
- appInstallationId: installBody.context.appInstallationId,
2738
- app: installBody.context.app,
2739
- invocation: installBody.invocation,
2740
- log: createContextLogger()
2741
- };
2742
- const installRequestConfig = {
2743
- baseUrl: installBody.env?.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
2744
- apiToken: installBody.env?.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
2745
- };
2746
- try {
2747
- const installHook = config.hooks.install;
2748
- const installHandler = typeof installHook === "function" ? installHook : installHook.handler;
2749
- const result = await runWithLogContext({ invocation: installBody.invocation }, async () => {
2750
- return await runWithConfig(installRequestConfig, async () => {
2751
- return await installHandler(installContext);
2752
- });
2753
- });
2754
- sendJSON(res, 200, {
2755
- env: result.env ?? {},
2756
- redirect: result.redirect
2757
- });
2758
- } catch (err) {
2759
- if (err instanceof InstallError) {
2760
- sendJSON(res, 400, {
2761
- error: {
2762
- code: err.code,
2763
- message: err.message,
2764
- field: err.field
2765
- }
2766
- });
2767
- } else {
2768
- sendJSON(res, 500, {
2769
- error: {
2770
- code: -32603,
2771
- message: err instanceof Error ? err.message : String(err ?? "")
2772
- }
2773
- });
2774
- }
2775
- }
2984
+ const result = await handleInstall(installBody, config.hooks);
2985
+ sendJSON(res, result.status, result.body);
2776
2986
  return;
2777
2987
  }
2778
2988
  if (pathname === "/uninstall" && req.method === "POST") {
2779
- if (!config.hooks?.uninstall) {
2780
- sendJSON(res, 404, { error: "Uninstall handler not configured" });
2781
- return;
2782
- }
2783
2989
  let uninstallBody;
2784
2990
  try {
2785
2991
  uninstallBody = await parseJSONBody(req);
@@ -2789,53 +2995,11 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
2789
2995
  });
2790
2996
  return;
2791
2997
  }
2792
- if (!uninstallBody.context?.appInstallationId || !uninstallBody.context?.workplace || !uninstallBody.context?.app) {
2793
- sendJSON(res, 400, {
2794
- error: {
2795
- code: -32602,
2796
- message: "Missing context (appInstallationId, workplace and app required)"
2797
- }
2798
- });
2799
- return;
2800
- }
2801
- const uninstallContext = {
2802
- env: uninstallBody.env ?? {},
2803
- workplace: uninstallBody.context.workplace,
2804
- appInstallationId: uninstallBody.context.appInstallationId,
2805
- app: uninstallBody.context.app,
2806
- invocation: uninstallBody.invocation,
2807
- log: createContextLogger()
2808
- };
2809
- const uninstallRequestConfig = {
2810
- baseUrl: uninstallBody.env?.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
2811
- apiToken: uninstallBody.env?.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
2812
- };
2813
- try {
2814
- const uninstallHook = config.hooks.uninstall;
2815
- const uninstallHandlerFn = typeof uninstallHook === "function" ? uninstallHook : uninstallHook.handler;
2816
- const result = await runWithLogContext({ invocation: uninstallBody.invocation }, async () => {
2817
- return await runWithConfig(uninstallRequestConfig, async () => {
2818
- return await uninstallHandlerFn(uninstallContext);
2819
- });
2820
- });
2821
- sendJSON(res, 200, {
2822
- cleanedWebhookIds: result.cleanedWebhookIds ?? []
2823
- });
2824
- } catch (err) {
2825
- sendJSON(res, 500, {
2826
- error: {
2827
- code: -32603,
2828
- message: err instanceof Error ? err.message : String(err ?? "")
2829
- }
2830
- });
2831
- }
2998
+ const result = await handleUninstall(uninstallBody, config.hooks);
2999
+ sendJSON(res, result.status, result.body);
2832
3000
  return;
2833
3001
  }
2834
3002
  if (pathname === "/provision" && req.method === "POST") {
2835
- if (!config.hooks?.provision) {
2836
- sendJSON(res, 404, { error: "Provision handler not configured" });
2837
- return;
2838
- }
2839
3003
  let provisionBody;
2840
3004
  try {
2841
3005
  provisionBody = await parseJSONBody(req);
@@ -2845,46 +3009,8 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
2845
3009
  });
2846
3010
  return;
2847
3011
  }
2848
- if (!provisionBody.context?.app) {
2849
- sendJSON(res, 400, {
2850
- error: { code: -32602, message: "Missing context (app required)" }
2851
- });
2852
- return;
2853
- }
2854
- const mergedEnv = {};
2855
- for (const [key, value] of Object.entries(process.env)) {
2856
- if (value !== void 0) {
2857
- mergedEnv[key] = value;
2858
- }
2859
- }
2860
- Object.assign(mergedEnv, provisionBody.env ?? {});
2861
- const provisionContext = {
2862
- env: mergedEnv,
2863
- app: provisionBody.context.app,
2864
- invocation: provisionBody.invocation,
2865
- log: createContextLogger()
2866
- };
2867
- const provisionRequestConfig = {
2868
- baseUrl: mergedEnv.SKEDYUL_API_URL ?? "",
2869
- apiToken: mergedEnv.SKEDYUL_API_TOKEN ?? ""
2870
- };
2871
- try {
2872
- const provisionHook = config.hooks.provision;
2873
- const provisionHandler = typeof provisionHook === "function" ? provisionHook : provisionHook.handler;
2874
- const result = await runWithLogContext({ invocation: provisionBody.invocation }, async () => {
2875
- return await runWithConfig(provisionRequestConfig, async () => {
2876
- return await provisionHandler(provisionContext);
2877
- });
2878
- });
2879
- sendJSON(res, 200, result);
2880
- } catch (err) {
2881
- sendJSON(res, 500, {
2882
- error: {
2883
- code: -32603,
2884
- message: err instanceof Error ? err.message : String(err ?? "")
2885
- }
2886
- });
2887
- }
3012
+ const result = await handleProvision(provisionBody, config.hooks);
3013
+ sendJSON(res, result.status, result.body);
2888
3014
  return;
2889
3015
  }
2890
3016
  if (pathname === "/core" && req.method === "POST") {
@@ -3065,12 +3191,10 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
3065
3191
  }
3066
3192
  if (path2.startsWith("/webhooks/") && webhookRegistry) {
3067
3193
  const handle = path2.slice("/webhooks/".length);
3068
- const webhookDef = webhookRegistry[handle];
3069
- if (!webhookDef) {
3194
+ if (!webhookRegistry[handle]) {
3070
3195
  return createResponse(404, { error: `Webhook handler '${handle}' not found` }, headers);
3071
3196
  }
3072
- const allowedMethods = webhookDef.methods ?? ["POST"];
3073
- if (!allowedMethods.includes(method)) {
3197
+ if (!isMethodAllowed(webhookRegistry, handle, method)) {
3074
3198
  return createResponse(405, { error: `Method ${method} not allowed` }, headers);
3075
3199
  }
3076
3200
  const rawBody = event.body ?? "";
@@ -3085,107 +3209,34 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
3085
3209
  } else {
3086
3210
  parsedBody = rawBody;
3087
3211
  }
3088
- const isEnvelope = typeof parsedBody === "object" && parsedBody !== null && "env" in parsedBody && "request" in parsedBody && "context" in parsedBody;
3089
- let webhookRequest;
3090
- let webhookContext;
3091
- let requestEnv = {};
3092
- let invocation;
3093
- if (isEnvelope) {
3094
- const envelope = parsedBody;
3095
- requestEnv = envelope.env ?? {};
3096
- invocation = envelope.invocation;
3097
- let originalParsedBody = envelope.request.body;
3098
- const originalContentType = envelope.request.headers["content-type"] ?? "";
3099
- if (originalContentType.includes("application/json")) {
3100
- try {
3101
- originalParsedBody = envelope.request.body ? JSON.parse(envelope.request.body) : {};
3102
- } catch {
3103
- }
3104
- }
3105
- webhookRequest = {
3106
- method: envelope.request.method,
3107
- url: envelope.request.url,
3108
- path: envelope.request.path,
3109
- headers: envelope.request.headers,
3110
- query: envelope.request.query,
3111
- body: originalParsedBody,
3112
- rawBody: envelope.request.body ? Buffer.from(envelope.request.body, "utf-8") : void 0
3113
- };
3114
- const envVars = { ...process.env, ...requestEnv };
3115
- const app = envelope.context.app;
3116
- if (envelope.context.appInstallationId && envelope.context.workplace) {
3117
- webhookContext = {
3118
- env: envVars,
3119
- app,
3120
- appInstallationId: envelope.context.appInstallationId,
3121
- workplace: envelope.context.workplace,
3122
- registration: envelope.context.registration ?? {},
3123
- invocation,
3124
- log: createContextLogger()
3125
- };
3126
- } else {
3127
- webhookContext = {
3128
- env: envVars,
3129
- app,
3130
- invocation,
3131
- log: createContextLogger()
3132
- };
3133
- }
3134
- } else {
3135
- const appId = event.headers?.["x-skedyul-app-id"] ?? event.headers?.["X-Skedyul-App-Id"];
3136
- const appVersionId = event.headers?.["x-skedyul-app-version-id"] ?? event.headers?.["X-Skedyul-App-Version-Id"];
3137
- if (!appId || !appVersionId) {
3138
- throw new Error("Missing app info in webhook request (x-skedyul-app-id and x-skedyul-app-version-id headers required)");
3139
- }
3140
- const forwardedProto = event.headers?.["x-forwarded-proto"] ?? event.headers?.["X-Forwarded-Proto"];
3141
- const protocol = forwardedProto ?? "https";
3142
- const host = event.headers?.host ?? event.headers?.Host ?? "localhost";
3143
- const queryString = event.queryStringParameters ? "?" + new URLSearchParams(event.queryStringParameters).toString() : "";
3144
- const webhookUrl = `${protocol}://${host}${path2}${queryString}`;
3145
- webhookRequest = {
3146
- method,
3147
- url: webhookUrl,
3148
- path: path2,
3149
- headers: event.headers,
3150
- query: event.queryStringParameters ?? {},
3151
- body: parsedBody,
3152
- rawBody: rawBody ? Buffer.from(rawBody, "utf-8") : void 0
3153
- };
3154
- webhookContext = {
3155
- env: process.env,
3156
- app: { id: appId, versionId: appVersionId },
3157
- log: createContextLogger()
3158
- };
3159
- }
3160
- const originalEnv = { ...process.env };
3161
- Object.assign(process.env, requestEnv);
3162
- const requestConfig = {
3163
- baseUrl: requestEnv.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
3164
- apiToken: requestEnv.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
3165
- };
3166
- let webhookResponse;
3167
- try {
3168
- webhookResponse = await runWithLogContext({ invocation }, async () => {
3169
- return await runWithConfig(requestConfig, async () => {
3170
- return await webhookDef.handler(webhookRequest, webhookContext);
3171
- });
3172
- });
3173
- } catch (err) {
3174
- console.error(`Webhook handler '${handle}' error:`, err);
3175
- return createResponse(500, { error: "Webhook handler error" }, headers);
3176
- } finally {
3177
- process.env = originalEnv;
3212
+ const forwardedProto = event.headers?.["x-forwarded-proto"] ?? event.headers?.["X-Forwarded-Proto"];
3213
+ const protocol = forwardedProto ?? "https";
3214
+ const host = event.headers?.host ?? event.headers?.Host ?? "localhost";
3215
+ const queryString = event.queryStringParameters ? "?" + new URLSearchParams(event.queryStringParameters).toString() : "";
3216
+ const webhookUrl = `${protocol}://${host}${path2}${queryString}`;
3217
+ const parseResult = parseWebhookRequest(
3218
+ parsedBody,
3219
+ method,
3220
+ webhookUrl,
3221
+ path2,
3222
+ event.headers,
3223
+ event.queryStringParameters ?? {},
3224
+ rawBody,
3225
+ event.headers?.["x-skedyul-app-id"] ?? event.headers?.["X-Skedyul-App-Id"],
3226
+ event.headers?.["x-skedyul-app-version-id"] ?? event.headers?.["X-Skedyul-App-Version-Id"]
3227
+ );
3228
+ if ("error" in parseResult) {
3229
+ return createResponse(400, { error: parseResult.error }, headers);
3178
3230
  }
3231
+ const result = await executeWebhookHandler(handle, webhookRegistry, parseResult);
3179
3232
  const responseHeaders = {
3180
3233
  ...headers,
3181
- ...webhookResponse.headers
3234
+ ...result.headers
3182
3235
  };
3183
- const status = webhookResponse.status ?? 200;
3184
- const body = webhookResponse.body;
3185
3236
  return {
3186
- statusCode: status,
3237
+ statusCode: result.status,
3187
3238
  headers: responseHeaders,
3188
- body: body !== void 0 ? typeof body === "string" ? body : JSON.stringify(body) : ""
3239
+ body: result.body !== void 0 ? typeof result.body === "string" ? result.body : JSON.stringify(result.body) : ""
3189
3240
  };
3190
3241
  }
3191
3242
  if (path2 === "/core" && method === "POST") {
@@ -3321,9 +3372,6 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
3321
3372
  }
3322
3373
  }
3323
3374
  if (path2 === "/install" && method === "POST") {
3324
- if (!config.hooks?.install) {
3325
- return createResponse(404, { error: "Install handler not configured" }, headers);
3326
- }
3327
3375
  let installBody;
3328
3376
  try {
3329
3377
  installBody = event.body ? JSON.parse(event.body) : {};
@@ -3334,68 +3382,10 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
3334
3382
  headers
3335
3383
  );
3336
3384
  }
3337
- if (!installBody.context?.appInstallationId || !installBody.context?.workplace) {
3338
- return createResponse(
3339
- 400,
3340
- { error: { code: -32602, message: "Missing context (appInstallationId and workplace required)" } },
3341
- headers
3342
- );
3343
- }
3344
- const installContext = {
3345
- env: installBody.env ?? {},
3346
- workplace: installBody.context.workplace,
3347
- appInstallationId: installBody.context.appInstallationId,
3348
- app: installBody.context.app,
3349
- invocation: installBody.invocation,
3350
- log: createContextLogger()
3351
- };
3352
- const installRequestConfig = {
3353
- baseUrl: installBody.env?.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
3354
- apiToken: installBody.env?.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
3355
- };
3356
- try {
3357
- const installHook = config.hooks.install;
3358
- const installHandler = typeof installHook === "function" ? installHook : installHook.handler;
3359
- const result = await runWithLogContext({ invocation: installBody.invocation }, async () => {
3360
- return await runWithConfig(installRequestConfig, async () => {
3361
- return await installHandler(installContext);
3362
- });
3363
- });
3364
- return createResponse(
3365
- 200,
3366
- { env: result.env ?? {}, redirect: result.redirect },
3367
- headers
3368
- );
3369
- } catch (err) {
3370
- if (err instanceof InstallError) {
3371
- return createResponse(
3372
- 400,
3373
- {
3374
- error: {
3375
- code: err.code,
3376
- message: err.message,
3377
- field: err.field
3378
- }
3379
- },
3380
- headers
3381
- );
3382
- }
3383
- return createResponse(
3384
- 500,
3385
- {
3386
- error: {
3387
- code: -32603,
3388
- message: err instanceof Error ? err.message : String(err ?? "")
3389
- }
3390
- },
3391
- headers
3392
- );
3393
- }
3385
+ const result = await handleInstall(installBody, config.hooks);
3386
+ return createResponse(result.status, result.body, headers);
3394
3387
  }
3395
3388
  if (path2 === "/uninstall" && method === "POST") {
3396
- if (!config.hooks?.uninstall) {
3397
- return createResponse(404, { error: "Uninstall handler not configured" }, headers);
3398
- }
3399
3389
  let uninstallBody;
3400
3390
  try {
3401
3391
  uninstallBody = event.body ? JSON.parse(event.body) : {};
@@ -3406,137 +3396,24 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
3406
3396
  headers
3407
3397
  );
3408
3398
  }
3409
- if (!uninstallBody.context?.appInstallationId || !uninstallBody.context?.workplace || !uninstallBody.context?.app) {
3410
- return createResponse(
3411
- 400,
3412
- {
3413
- error: {
3414
- code: -32602,
3415
- message: "Missing context (appInstallationId, workplace and app required)"
3416
- }
3417
- },
3418
- headers
3419
- );
3420
- }
3421
- const uninstallContext = {
3422
- env: uninstallBody.env ?? {},
3423
- workplace: uninstallBody.context.workplace,
3424
- appInstallationId: uninstallBody.context.appInstallationId,
3425
- app: uninstallBody.context.app,
3426
- invocation: uninstallBody.invocation,
3427
- log: createContextLogger()
3428
- };
3429
- const uninstallRequestConfig = {
3430
- baseUrl: uninstallBody.env?.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
3431
- apiToken: uninstallBody.env?.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
3432
- };
3433
- try {
3434
- const uninstallHook = config.hooks.uninstall;
3435
- const uninstallHandlerFn = typeof uninstallHook === "function" ? uninstallHook : uninstallHook.handler;
3436
- const result = await runWithLogContext({ invocation: uninstallBody.invocation }, async () => {
3437
- return await runWithConfig(uninstallRequestConfig, async () => {
3438
- return await uninstallHandlerFn(uninstallContext);
3439
- });
3440
- });
3441
- return createResponse(
3442
- 200,
3443
- { cleanedWebhookIds: result.cleanedWebhookIds ?? [] },
3444
- headers
3445
- );
3446
- } catch (err) {
3447
- return createResponse(
3448
- 500,
3449
- {
3450
- error: {
3451
- code: -32603,
3452
- message: err instanceof Error ? err.message : String(err ?? "")
3453
- }
3454
- },
3455
- headers
3456
- );
3457
- }
3399
+ const result = await handleUninstall(uninstallBody, config.hooks);
3400
+ return createResponse(result.status, result.body, headers);
3458
3401
  }
3459
3402
  if (path2 === "/provision" && method === "POST") {
3460
- console.log("[serverless] /provision endpoint called");
3461
- if (!config.hooks?.provision) {
3462
- console.log("[serverless] No provision handler configured");
3463
- return createResponse(404, { error: "Provision handler not configured" }, headers);
3464
- }
3465
3403
  let provisionBody;
3466
3404
  try {
3467
3405
  provisionBody = event.body ? JSON.parse(event.body) : {};
3468
- console.log("[serverless] Provision body parsed:", {
3469
- hasEnv: !!provisionBody.env,
3470
- hasContext: !!provisionBody.context,
3471
- appId: provisionBody.context?.app?.id,
3472
- versionId: provisionBody.context?.app?.versionId
3473
- });
3474
3406
  } catch {
3475
- console.log("[serverless] Failed to parse provision body");
3476
3407
  return createResponse(
3477
3408
  400,
3478
3409
  { error: { code: -32700, message: "Parse error" } },
3479
3410
  headers
3480
3411
  );
3481
3412
  }
3482
- if (!provisionBody.context?.app) {
3483
- console.log("[serverless] Missing app context in provision body");
3484
- return createResponse(
3485
- 400,
3486
- { error: { code: -32602, message: "Missing context (app required)" } },
3487
- headers
3488
- );
3489
- }
3490
- const mergedEnv = {};
3491
- for (const [key, value] of Object.entries(process.env)) {
3492
- if (value !== void 0) {
3493
- mergedEnv[key] = value;
3494
- }
3495
- }
3496
- Object.assign(mergedEnv, provisionBody.env ?? {});
3497
- const provisionContext = {
3498
- env: mergedEnv,
3499
- app: provisionBody.context.app,
3500
- invocation: provisionBody.invocation,
3501
- log: createContextLogger()
3502
- };
3503
- const provisionRequestConfig = {
3504
- baseUrl: mergedEnv.SKEDYUL_API_URL ?? "",
3505
- apiToken: mergedEnv.SKEDYUL_API_TOKEN ?? ""
3506
- };
3507
- console.log("[serverless] Calling provision handler...");
3508
- try {
3509
- const provisionHook = config.hooks.provision;
3510
- const provisionHandler = typeof provisionHook === "function" ? provisionHook : provisionHook.handler;
3511
- const result = await runWithLogContext({ invocation: provisionBody.invocation }, async () => {
3512
- return await runWithConfig(provisionRequestConfig, async () => {
3513
- return await provisionHandler(provisionContext);
3514
- });
3515
- });
3516
- console.log("[serverless] Provision handler completed successfully");
3517
- return createResponse(200, result, headers);
3518
- } catch (err) {
3519
- console.error("[serverless] Provision handler failed:", err instanceof Error ? err.message : String(err));
3520
- return createResponse(
3521
- 500,
3522
- {
3523
- error: {
3524
- code: -32603,
3525
- message: err instanceof Error ? err.message : String(err ?? "")
3526
- }
3527
- },
3528
- headers
3529
- );
3530
- }
3413
+ const result = await handleProvision(provisionBody, config.hooks);
3414
+ return createResponse(result.status, result.body, headers);
3531
3415
  }
3532
3416
  if (path2 === "/oauth_callback" && method === "POST") {
3533
- if (!config.hooks?.oauth_callback) {
3534
- return createResponse(
3535
- 404,
3536
- { error: "OAuth callback handler not configured" },
3537
- headers
3538
- );
3539
- }
3540
3417
  let parsedBody;
3541
3418
  try {
3542
3419
  parsedBody = event.body ? JSON.parse(event.body) : {};
@@ -3548,52 +3425,8 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
3548
3425
  headers
3549
3426
  );
3550
3427
  }
3551
- const envelope = parseHandlerEnvelope(parsedBody);
3552
- if (!envelope) {
3553
- console.error("[OAuth Callback] Failed to parse envelope. Body:", JSON.stringify(parsedBody, null, 2));
3554
- return createResponse(
3555
- 400,
3556
- { error: { code: -32602, message: "Missing envelope format: expected { env, request }" } },
3557
- headers
3558
- );
3559
- }
3560
- const invocation = parsedBody.invocation;
3561
- const oauthRequest = buildRequestFromRaw(envelope.request);
3562
- const oauthCallbackRequestConfig = buildRequestScopedConfig(envelope.env);
3563
- const oauthCallbackContext = {
3564
- request: oauthRequest,
3565
- invocation,
3566
- log: createContextLogger()
3567
- };
3568
- try {
3569
- const oauthCallbackHook = config.hooks.oauth_callback;
3570
- const oauthCallbackHandler = typeof oauthCallbackHook === "function" ? oauthCallbackHook : oauthCallbackHook.handler;
3571
- const result = await runWithLogContext({ invocation }, async () => {
3572
- return await runWithConfig(oauthCallbackRequestConfig, async () => {
3573
- return await oauthCallbackHandler(oauthCallbackContext);
3574
- });
3575
- });
3576
- return createResponse(
3577
- 200,
3578
- {
3579
- appInstallationId: result.appInstallationId,
3580
- env: result.env ?? {}
3581
- },
3582
- headers
3583
- );
3584
- } catch (err) {
3585
- const errorMessage = err instanceof Error ? err.message : String(err ?? "Unknown error");
3586
- return createResponse(
3587
- 500,
3588
- {
3589
- error: {
3590
- code: -32603,
3591
- message: errorMessage
3592
- }
3593
- },
3594
- headers
3595
- );
3596
- }
3428
+ const result = await handleOAuthCallback(parsedBody, config.hooks);
3429
+ return createResponse(result.status, result.body, headers);
3597
3430
  }
3598
3431
  if (path2 === "/health" && method === "GET") {
3599
3432
  return createResponse(200, state.getHealthStatus(), headers);