skedyul 0.3.3 → 0.3.4

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/.build-stamp CHANGED
@@ -1 +1 @@
1
- 1771565387388
1
+ 1771566817944
@@ -0,0 +1,23 @@
1
+ import type { InvocationContext } from '../types';
2
+ interface LogContext {
3
+ invocation?: InvocationContext;
4
+ }
5
+ /**
6
+ * Runs a function with invocation context that will be automatically
7
+ * injected into all console.log/info/warn/error calls within that scope.
8
+ */
9
+ export declare function runWithLogContext<T>(context: LogContext, fn: () => T | Promise<T>): T | Promise<T>;
10
+ /**
11
+ * Gets the current log context from AsyncLocalStorage
12
+ */
13
+ export declare function getLogContext(): LogContext | undefined;
14
+ /**
15
+ * Installs the context-aware logger by patching console methods.
16
+ * This should be called once at server startup.
17
+ */
18
+ export declare function installContextLogger(): void;
19
+ /**
20
+ * Restores original console methods (useful for testing)
21
+ */
22
+ export declare function uninstallContextLogger(): void;
23
+ export {};
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runWithLogContext = runWithLogContext;
4
+ exports.getLogContext = getLogContext;
5
+ exports.installContextLogger = installContextLogger;
6
+ exports.uninstallContextLogger = uninstallContextLogger;
7
+ const async_hooks_1 = require("async_hooks");
8
+ const logContextStorage = new async_hooks_1.AsyncLocalStorage();
9
+ /**
10
+ * Runs a function with invocation context that will be automatically
11
+ * injected into all console.log/info/warn/error calls within that scope.
12
+ */
13
+ function runWithLogContext(context, fn) {
14
+ return logContextStorage.run(context, fn);
15
+ }
16
+ /**
17
+ * Gets the current log context from AsyncLocalStorage
18
+ */
19
+ function getLogContext() {
20
+ return logContextStorage.getStore();
21
+ }
22
+ /**
23
+ * Formats a log message with invocation context prepended as JSON
24
+ */
25
+ function formatLogWithContext(args) {
26
+ const context = getLogContext();
27
+ if (!context?.invocation) {
28
+ return args;
29
+ }
30
+ // Create a context prefix that includes key invocation fields
31
+ const contextPrefix = {
32
+ invocationType: context.invocation.invocationType,
33
+ ...(context.invocation.toolHandle && { toolHandle: context.invocation.toolHandle }),
34
+ ...(context.invocation.serverHookHandle && { serverHookHandle: context.invocation.serverHookHandle }),
35
+ ...(context.invocation.appInstallationId && { appInstallationId: context.invocation.appInstallationId }),
36
+ ...(context.invocation.toolCallId && { toolCallId: context.invocation.toolCallId }),
37
+ ...(context.invocation.workflowId && { workflowId: context.invocation.workflowId }),
38
+ ...(context.invocation.workflowRunId && { workflowRunId: context.invocation.workflowRunId }),
39
+ };
40
+ // If the first argument is a string, prepend context
41
+ if (typeof args[0] === 'string') {
42
+ return [`[${JSON.stringify(contextPrefix)}] ${args[0]}`, ...args.slice(1)];
43
+ }
44
+ // If the first argument is an object, merge context into it
45
+ if (args[0] && typeof args[0] === 'object' && !Array.isArray(args[0])) {
46
+ return [{ ...contextPrefix, ...args[0] }, ...args.slice(1)];
47
+ }
48
+ // Otherwise, prepend context as first argument
49
+ return [contextPrefix, ...args];
50
+ }
51
+ // Store original console methods
52
+ const originalConsole = {
53
+ log: console.log.bind(console),
54
+ info: console.info.bind(console),
55
+ warn: console.warn.bind(console),
56
+ error: console.error.bind(console),
57
+ debug: console.debug.bind(console),
58
+ };
59
+ /**
60
+ * Installs the context-aware logger by patching console methods.
61
+ * This should be called once at server startup.
62
+ */
63
+ function installContextLogger() {
64
+ console.log = (...args) => {
65
+ originalConsole.log(...formatLogWithContext(args));
66
+ };
67
+ console.info = (...args) => {
68
+ originalConsole.info(...formatLogWithContext(args));
69
+ };
70
+ console.warn = (...args) => {
71
+ originalConsole.warn(...formatLogWithContext(args));
72
+ };
73
+ console.error = (...args) => {
74
+ originalConsole.error(...formatLogWithContext(args));
75
+ };
76
+ console.debug = (...args) => {
77
+ originalConsole.debug(...formatLogWithContext(args));
78
+ };
79
+ }
80
+ /**
81
+ * Restores original console methods (useful for testing)
82
+ */
83
+ function uninstallContextLogger() {
84
+ console.log = originalConsole.log;
85
+ console.info = originalConsole.info;
86
+ console.warn = originalConsole.warn;
87
+ console.error = originalConsole.error;
88
+ console.debug = originalConsole.debug;
89
+ }
@@ -12,6 +12,7 @@ const errors_1 = require("../errors");
12
12
  const core_api_handler_1 = require("./core-api-handler");
13
13
  const handler_helpers_1 = require("./handler-helpers");
14
14
  const startup_logger_1 = require("./startup-logger");
15
+ const context_logger_1 = require("./context-logger");
15
16
  const utils_1 = require("./utils");
16
17
  /**
17
18
  * Creates a dedicated (long-running HTTP) server instance
@@ -67,15 +68,18 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
67
68
  parsedBody = rawBody;
68
69
  }
69
70
  // Check if this is an envelope format from the platform
70
- // Envelope format: { env: {...}, request: {...}, context: {...} }
71
+ // Envelope format: { env: {...}, request: {...}, context: {...}, invocation?: {...} }
71
72
  const envelope = (0, handler_helpers_1.parseHandlerEnvelope)(parsedBody);
72
73
  let webhookRequest;
73
74
  let webhookContext;
74
75
  let requestEnv = {};
76
+ let invocation;
75
77
  if (envelope && 'context' in envelope && envelope.context) {
76
78
  // Platform envelope format - use shared helpers
77
79
  const context = envelope.context;
78
80
  requestEnv = envelope.env;
81
+ // Extract invocation context from parsed body
82
+ invocation = parsedBody.invocation;
79
83
  // Convert raw request to rich request using shared helper
80
84
  webhookRequest = (0, handler_helpers_1.buildRequestFromRaw)(envelope.request);
81
85
  const envVars = { ...process.env, ...envelope.env };
@@ -89,6 +93,7 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
89
93
  appInstallationId: context.appInstallationId,
90
94
  workplace: context.workplace,
91
95
  registration: context.registration ?? {},
96
+ invocation,
92
97
  };
93
98
  }
94
99
  else {
@@ -96,6 +101,7 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
96
101
  webhookContext = {
97
102
  env: envVars,
98
103
  app,
104
+ invocation,
99
105
  };
100
106
  }
101
107
  }
@@ -128,11 +134,13 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
128
134
  // Build request-scoped config for the skedyul client
129
135
  // This uses AsyncLocalStorage to override the global config (same pattern as tools)
130
136
  const requestConfig = (0, handler_helpers_1.buildRequestScopedConfig)(requestEnv);
131
- // Invoke the handler with request-scoped config
137
+ // Invoke the handler with request-scoped config and log context
132
138
  let webhookResponse;
133
139
  try {
134
- webhookResponse = await (0, client_1.runWithConfig)(requestConfig, async () => {
135
- return await webhookDef.handler(webhookRequest, webhookContext);
140
+ webhookResponse = await (0, context_logger_1.runWithLogContext)({ invocation }, async () => {
141
+ return await (0, client_1.runWithConfig)(requestConfig, async () => {
142
+ return await webhookDef.handler(webhookRequest, webhookContext);
143
+ });
136
144
  });
137
145
  }
138
146
  catch (err) {
@@ -226,20 +234,25 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
226
234
  });
227
235
  return;
228
236
  }
237
+ // Extract invocation context from envelope
238
+ const invocation = parsedBody.invocation;
229
239
  // Convert raw request to rich request using shared helper
230
240
  const oauthRequest = (0, handler_helpers_1.buildRequestFromRaw)(envelope.request);
231
241
  // Build request-scoped config using shared helper
232
242
  const oauthCallbackRequestConfig = (0, handler_helpers_1.buildRequestScopedConfig)(envelope.env);
233
243
  const oauthCallbackContext = {
234
244
  request: oauthRequest,
245
+ invocation,
235
246
  };
236
247
  try {
237
248
  const oauthCallbackHook = config.hooks.oauth_callback;
238
249
  const oauthCallbackHandler = typeof oauthCallbackHook === 'function'
239
250
  ? oauthCallbackHook
240
251
  : oauthCallbackHook.handler;
241
- const result = await (0, client_1.runWithConfig)(oauthCallbackRequestConfig, async () => {
242
- return await oauthCallbackHandler(oauthCallbackContext);
252
+ const result = await (0, context_logger_1.runWithLogContext)({ invocation }, async () => {
253
+ return await (0, client_1.runWithConfig)(oauthCallbackRequestConfig, async () => {
254
+ return await oauthCallbackHandler(oauthCallbackContext);
255
+ });
243
256
  });
244
257
  (0, utils_1.sendJSON)(res, 200, {
245
258
  appInstallationId: result.appInstallationId,
@@ -284,6 +297,7 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
284
297
  workplace: installBody.context.workplace,
285
298
  appInstallationId: installBody.context.appInstallationId,
286
299
  app: installBody.context.app,
300
+ invocation: installBody.invocation,
287
301
  };
288
302
  // Build request-scoped config for SDK access
289
303
  // Use env from request body (contains generated token from workflow)
@@ -300,8 +314,10 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
300
314
  const installHandler = typeof installHook === 'function'
301
315
  ? installHook
302
316
  : installHook.handler;
303
- const result = await (0, client_1.runWithConfig)(installRequestConfig, async () => {
304
- return await installHandler(installContext);
317
+ const result = await (0, context_logger_1.runWithLogContext)({ invocation: installBody.invocation }, async () => {
318
+ return await (0, client_1.runWithConfig)(installRequestConfig, async () => {
319
+ return await installHandler(installContext);
320
+ });
305
321
  });
306
322
  (0, utils_1.sendJSON)(res, 200, {
307
323
  env: result.env ?? {},
@@ -362,6 +378,7 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
362
378
  workplace: uninstallBody.context.workplace,
363
379
  appInstallationId: uninstallBody.context.appInstallationId,
364
380
  app: uninstallBody.context.app,
381
+ invocation: uninstallBody.invocation,
365
382
  };
366
383
  const uninstallRequestConfig = {
367
384
  baseUrl: uninstallBody.env?.SKEDYUL_API_URL ??
@@ -374,8 +391,10 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
374
391
  try {
375
392
  const uninstallHook = config.hooks.uninstall;
376
393
  const uninstallHandlerFn = typeof uninstallHook === 'function' ? uninstallHook : uninstallHook.handler;
377
- const result = await (0, client_1.runWithConfig)(uninstallRequestConfig, async () => {
378
- return await uninstallHandlerFn(uninstallContext);
394
+ const result = await (0, context_logger_1.runWithLogContext)({ invocation: uninstallBody.invocation }, async () => {
395
+ return await (0, client_1.runWithConfig)(uninstallRequestConfig, async () => {
396
+ return await uninstallHandlerFn(uninstallContext);
397
+ });
379
398
  });
380
399
  (0, utils_1.sendJSON)(res, 200, {
381
400
  cleanedWebhookIds: result.cleanedWebhookIds ?? [],
@@ -427,6 +446,7 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
427
446
  const provisionContext = {
428
447
  env: mergedEnv,
429
448
  app: provisionBody.context.app,
449
+ invocation: provisionBody.invocation,
430
450
  };
431
451
  // Build request-scoped config for SDK access
432
452
  // Use merged env for consistency
@@ -439,8 +459,10 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
439
459
  const provisionHandler = typeof provisionHook === 'function'
440
460
  ? provisionHook
441
461
  : provisionHook.handler;
442
- const result = await (0, client_1.runWithConfig)(provisionRequestConfig, async () => {
443
- return await provisionHandler(provisionContext);
462
+ const result = await (0, context_logger_1.runWithLogContext)({ invocation: provisionBody.invocation }, async () => {
463
+ return await (0, client_1.runWithConfig)(provisionRequestConfig, async () => {
464
+ return await provisionHandler(provisionContext);
465
+ });
444
466
  });
445
467
  (0, utils_1.sendJSON)(res, 200, result);
446
468
  }
@@ -7,6 +7,7 @@ export { parseHandlerEnvelope, buildRequestFromRaw, buildRequestScopedConfig } f
7
7
  export { printStartupLog, padEnd } from './startup-logger';
8
8
  export { createDedicatedServerInstance } from './dedicated';
9
9
  export { createServerlessInstance } from './serverless';
10
+ export { runWithLogContext, getLogContext, installContextLogger, uninstallContextLogger } from './context-logger';
10
11
  export declare function createSkedyulServer(config: SkedyulServerConfig & {
11
12
  computeLayer: 'dedicated';
12
13
  }, registry: ToolRegistry, webhookRegistry?: WebhookRegistry): DedicatedServerInstance;
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.server = exports.createServerlessInstance = exports.createDedicatedServerInstance = exports.padEnd = exports.printStartupLog = exports.buildRequestScopedConfig = exports.buildRequestFromRaw = exports.parseHandlerEnvelope = exports.createCallToolHandler = exports.createRequestState = exports.buildToolMetadata = exports.handleCoreMethod = exports.getListeningPort = exports.createResponse = exports.getDefaultHeaders = exports.sendHTML = exports.sendJSON = exports.parseJSONBody = exports.readRawRequestBody = exports.mergeRuntimeEnv = exports.parseNumberEnv = exports.parseJsonRecord = exports.getJsonSchemaFromToolSchema = exports.getZodSchema = exports.isToolSchemaWithJson = exports.toJsonSchema = exports.normalizeBilling = void 0;
36
+ exports.server = exports.uninstallContextLogger = exports.installContextLogger = exports.getLogContext = exports.runWithLogContext = exports.createServerlessInstance = exports.createDedicatedServerInstance = exports.padEnd = exports.printStartupLog = exports.buildRequestScopedConfig = exports.buildRequestFromRaw = exports.parseHandlerEnvelope = exports.createCallToolHandler = exports.createRequestState = exports.buildToolMetadata = exports.handleCoreMethod = exports.getListeningPort = exports.createResponse = exports.getDefaultHeaders = exports.sendHTML = exports.sendJSON = exports.parseJSONBody = exports.readRawRequestBody = exports.mergeRuntimeEnv = exports.parseNumberEnv = exports.parseJsonRecord = exports.getJsonSchemaFromToolSchema = exports.getZodSchema = exports.isToolSchemaWithJson = exports.toJsonSchema = exports.normalizeBilling = void 0;
37
37
  exports.createSkedyulServer = createSkedyulServer;
38
38
  const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
39
39
  const z = __importStar(require("zod"));
@@ -42,6 +42,9 @@ const tool_handler_1 = require("./tool-handler");
42
42
  const dedicated_1 = require("./dedicated");
43
43
  const serverless_1 = require("./serverless");
44
44
  const utils_1 = require("./utils");
45
+ const context_logger_1 = require("./context-logger");
46
+ // Install context-aware logger at module load time
47
+ (0, context_logger_1.installContextLogger)();
45
48
  // Re-export utilities
46
49
  var utils_2 = require("./utils");
47
50
  Object.defineProperty(exports, "normalizeBilling", { enumerable: true, get: function () { return utils_2.normalizeBilling; } });
@@ -77,6 +80,11 @@ var dedicated_2 = require("./dedicated");
77
80
  Object.defineProperty(exports, "createDedicatedServerInstance", { enumerable: true, get: function () { return dedicated_2.createDedicatedServerInstance; } });
78
81
  var serverless_2 = require("./serverless");
79
82
  Object.defineProperty(exports, "createServerlessInstance", { enumerable: true, get: function () { return serverless_2.createServerlessInstance; } });
83
+ var context_logger_2 = require("./context-logger");
84
+ Object.defineProperty(exports, "runWithLogContext", { enumerable: true, get: function () { return context_logger_2.runWithLogContext; } });
85
+ Object.defineProperty(exports, "getLogContext", { enumerable: true, get: function () { return context_logger_2.getLogContext; } });
86
+ Object.defineProperty(exports, "installContextLogger", { enumerable: true, get: function () { return context_logger_2.installContextLogger; } });
87
+ Object.defineProperty(exports, "uninstallContextLogger", { enumerable: true, get: function () { return context_logger_2.uninstallContextLogger; } });
80
88
  function createSkedyulServer(config, registry, webhookRegistry) {
81
89
  (0, utils_1.mergeRuntimeEnv)();
82
90
  if (config.coreApi?.service) {
@@ -7,6 +7,7 @@ const errors_1 = require("../errors");
7
7
  const core_api_handler_1 = require("./core-api-handler");
8
8
  const handler_helpers_1 = require("./handler-helpers");
9
9
  const startup_logger_1 = require("./startup-logger");
10
+ const context_logger_1 = require("./context-logger");
10
11
  const utils_1 = require("./utils");
11
12
  /**
12
13
  * Creates a serverless (Lambda-style) server instance
@@ -57,7 +58,7 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer, reg
57
58
  parsedBody = rawBody;
58
59
  }
59
60
  // Check if this is an envelope format from the platform
60
- // Envelope format: { env: {...}, request: {...}, context: {...} }
61
+ // Envelope format: { env: {...}, request: {...}, context: {...}, invocation?: {...} }
61
62
  const isEnvelope = (typeof parsedBody === 'object' &&
62
63
  parsedBody !== null &&
63
64
  'env' in parsedBody &&
@@ -66,10 +67,12 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer, reg
66
67
  let webhookRequest;
67
68
  let webhookContext;
68
69
  let requestEnv = {};
70
+ let invocation;
69
71
  if (isEnvelope) {
70
72
  // Platform envelope format - extract env, request, and context
71
73
  const envelope = parsedBody;
72
74
  requestEnv = envelope.env ?? {};
75
+ invocation = envelope.invocation;
73
76
  // Parse the original request body
74
77
  let originalParsedBody = envelope.request.body;
75
78
  const originalContentType = envelope.request.headers['content-type'] ?? '';
@@ -101,6 +104,7 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer, reg
101
104
  appInstallationId: envelope.context.appInstallationId,
102
105
  workplace: envelope.context.workplace,
103
106
  registration: envelope.context.registration ?? {},
107
+ invocation,
104
108
  };
105
109
  }
106
110
  else {
@@ -108,6 +112,7 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer, reg
108
112
  webhookContext = {
109
113
  env: envVars,
110
114
  app,
115
+ invocation,
111
116
  };
112
117
  }
113
118
  }
@@ -151,11 +156,13 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer, reg
151
156
  baseUrl: requestEnv.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? '',
152
157
  apiToken: requestEnv.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? '',
153
158
  };
154
- // Invoke the handler with request-scoped config
159
+ // Invoke the handler with request-scoped config and log context
155
160
  let webhookResponse;
156
161
  try {
157
- webhookResponse = await (0, client_1.runWithConfig)(requestConfig, async () => {
158
- return await webhookDef.handler(webhookRequest, webhookContext);
162
+ webhookResponse = await (0, context_logger_1.runWithLogContext)({ invocation }, async () => {
163
+ return await (0, client_1.runWithConfig)(requestConfig, async () => {
164
+ return await webhookDef.handler(webhookRequest, webhookContext);
165
+ });
159
166
  });
160
167
  }
161
168
  catch (err) {
@@ -308,6 +315,7 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer, reg
308
315
  workplace: installBody.context.workplace,
309
316
  appInstallationId: installBody.context.appInstallationId,
310
317
  app: installBody.context.app,
318
+ invocation: installBody.invocation,
311
319
  };
312
320
  // Build request-scoped config for SDK access
313
321
  // Use env from request body (contains generated token from workflow)
@@ -324,8 +332,10 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer, reg
324
332
  const installHandler = typeof installHook === 'function'
325
333
  ? installHook
326
334
  : installHook.handler;
327
- const result = await (0, client_1.runWithConfig)(installRequestConfig, async () => {
328
- return await installHandler(installContext);
335
+ const result = await (0, context_logger_1.runWithLogContext)({ invocation: installBody.invocation }, async () => {
336
+ return await (0, client_1.runWithConfig)(installRequestConfig, async () => {
337
+ return await installHandler(installContext);
338
+ });
329
339
  });
330
340
  return (0, utils_1.createResponse)(200, { env: result.env ?? {}, redirect: result.redirect }, headers);
331
341
  }
@@ -375,6 +385,7 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer, reg
375
385
  workplace: uninstallBody.context.workplace,
376
386
  appInstallationId: uninstallBody.context.appInstallationId,
377
387
  app: uninstallBody.context.app,
388
+ invocation: uninstallBody.invocation,
378
389
  };
379
390
  const uninstallRequestConfig = {
380
391
  baseUrl: uninstallBody.env?.SKEDYUL_API_URL ??
@@ -387,8 +398,10 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer, reg
387
398
  try {
388
399
  const uninstallHook = config.hooks.uninstall;
389
400
  const uninstallHandlerFn = typeof uninstallHook === 'function' ? uninstallHook : uninstallHook.handler;
390
- const result = await (0, client_1.runWithConfig)(uninstallRequestConfig, async () => {
391
- return await uninstallHandlerFn(uninstallContext);
401
+ const result = await (0, context_logger_1.runWithLogContext)({ invocation: uninstallBody.invocation }, async () => {
402
+ return await (0, client_1.runWithConfig)(uninstallRequestConfig, async () => {
403
+ return await uninstallHandlerFn(uninstallContext);
404
+ });
392
405
  });
393
406
  return (0, utils_1.createResponse)(200, { cleanedWebhookIds: result.cleanedWebhookIds ?? [] }, headers);
394
407
  }
@@ -420,20 +433,25 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer, reg
420
433
  console.error('[OAuth Callback] Failed to parse envelope. Body:', JSON.stringify(parsedBody, null, 2));
421
434
  return (0, utils_1.createResponse)(400, { error: { code: -32602, message: 'Missing envelope format: expected { env, request }' } }, headers);
422
435
  }
436
+ // Extract invocation context from parsed body
437
+ const invocation = parsedBody.invocation;
423
438
  // Convert raw request to rich request using shared helper
424
439
  const oauthRequest = (0, handler_helpers_1.buildRequestFromRaw)(envelope.request);
425
440
  // Build request-scoped config using shared helper
426
441
  const oauthCallbackRequestConfig = (0, handler_helpers_1.buildRequestScopedConfig)(envelope.env);
427
442
  const oauthCallbackContext = {
428
443
  request: oauthRequest,
444
+ invocation,
429
445
  };
430
446
  try {
431
447
  const oauthCallbackHook = config.hooks.oauth_callback;
432
448
  const oauthCallbackHandler = typeof oauthCallbackHook === 'function'
433
449
  ? oauthCallbackHook
434
450
  : oauthCallbackHook.handler;
435
- const result = await (0, client_1.runWithConfig)(oauthCallbackRequestConfig, async () => {
436
- return await oauthCallbackHandler(oauthCallbackContext);
451
+ const result = await (0, context_logger_1.runWithLogContext)({ invocation }, async () => {
452
+ return await (0, client_1.runWithConfig)(oauthCallbackRequestConfig, async () => {
453
+ return await oauthCallbackHandler(oauthCallbackContext);
454
+ });
437
455
  });
438
456
  return (0, utils_1.createResponse)(200, {
439
457
  appInstallationId: result.appInstallationId,
@@ -6,6 +6,7 @@ exports.createCallToolHandler = createCallToolHandler;
6
6
  const utils_1 = require("./utils");
7
7
  const client_1 = require("../core/client");
8
8
  const errors_1 = require("../errors");
9
+ const context_logger_1 = require("./context-logger");
9
10
  /**
10
11
  * Builds tool metadata array from a tool registry
11
12
  */
@@ -75,6 +76,8 @@ function createCallToolHandler(registry, state, onMaxRequests) {
75
76
  const requestEnv = args.env ?? {};
76
77
  const originalEnv = { ...process.env };
77
78
  Object.assign(process.env, requestEnv);
79
+ // Extract invocation context from args (passed from workflow)
80
+ const invocation = args.invocation;
78
81
  try {
79
82
  // Get tool inputs (clean, no context)
80
83
  const inputs = (args.inputs ?? {});
@@ -93,6 +96,7 @@ function createCallToolHandler(registry, state, onMaxRequests) {
93
96
  app,
94
97
  env: process.env,
95
98
  mode: estimateMode ? 'estimate' : 'execute',
99
+ invocation,
96
100
  };
97
101
  }
98
102
  else {
@@ -104,26 +108,26 @@ function createCallToolHandler(registry, state, onMaxRequests) {
104
108
  const modeValue = estimateMode ? 'estimate' : 'execute';
105
109
  if (trigger === 'field_change') {
106
110
  const field = rawContext.field;
107
- executionContext = { trigger: 'field_change', app, appInstallationId, workplace, request, env: envVars, mode: modeValue, field };
111
+ executionContext = { trigger: 'field_change', app, appInstallationId, workplace, request, env: envVars, mode: modeValue, field, invocation };
108
112
  }
109
113
  else if (trigger === 'page_action') {
110
114
  const page = rawContext.page;
111
- executionContext = { trigger: 'page_action', app, appInstallationId, workplace, request, env: envVars, mode: modeValue, page };
115
+ executionContext = { trigger: 'page_action', app, appInstallationId, workplace, request, env: envVars, mode: modeValue, page, invocation };
112
116
  }
113
117
  else if (trigger === 'form_submit') {
114
118
  const form = rawContext.form;
115
- executionContext = { trigger: 'form_submit', app, appInstallationId, workplace, request, env: envVars, mode: modeValue, form };
119
+ executionContext = { trigger: 'form_submit', app, appInstallationId, workplace, request, env: envVars, mode: modeValue, form, invocation };
116
120
  }
117
121
  else if (trigger === 'workflow') {
118
- executionContext = { trigger: 'workflow', app, appInstallationId, workplace, request, env: envVars, mode: modeValue };
122
+ executionContext = { trigger: 'workflow', app, appInstallationId, workplace, request, env: envVars, mode: modeValue, invocation };
119
123
  }
120
124
  else if (trigger === 'page_context') {
121
125
  // Page context trigger - similar to agent but for page context resolution
122
- executionContext = { trigger: 'agent', app, appInstallationId, workplace, request, env: envVars, mode: modeValue };
126
+ executionContext = { trigger: 'agent', app, appInstallationId, workplace, request, env: envVars, mode: modeValue, invocation };
123
127
  }
124
128
  else {
125
129
  // Default to agent
126
- executionContext = { trigger: 'agent', app, appInstallationId, workplace, request, env: envVars, mode: modeValue };
130
+ executionContext = { trigger: 'agent', app, appInstallationId, workplace, request, env: envVars, mode: modeValue, invocation };
127
131
  }
128
132
  }
129
133
  // Build request-scoped config from env passed in MCP call
@@ -133,8 +137,11 @@ function createCallToolHandler(registry, state, onMaxRequests) {
133
137
  };
134
138
  // Call handler with two arguments: (input, context)
135
139
  // Wrap in runWithConfig for request-scoped SDK configuration
136
- const functionResult = await (0, client_1.runWithConfig)(requestConfig, async () => {
137
- return await fn(inputs, executionContext);
140
+ // Also wrap in runWithLogContext to inject invocation context into all logs
141
+ const functionResult = await (0, context_logger_1.runWithLogContext)({ invocation }, async () => {
142
+ return await (0, client_1.runWithConfig)(requestConfig, async () => {
143
+ return await fn(inputs, executionContext);
144
+ });
138
145
  });
139
146
  const billing = (0, utils_1.normalizeBilling)(functionResult.billing);
140
147
  return {
@@ -1,4 +1,4 @@
1
- import type { HealthStatus } from '../types';
1
+ import type { HealthStatus, InvocationContext } from '../types';
2
2
  /**
3
3
  * Arguments passed to tool call handlers
4
4
  */
@@ -7,6 +7,7 @@ export type ToolCallArgs = {
7
7
  inputs?: Record<string, unknown>;
8
8
  context?: Record<string, unknown>;
9
9
  estimate?: boolean;
10
+ invocation?: InvocationContext;
10
11
  };
11
12
  /**
12
13
  * Interface for tracking request state and health status
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skedyul",
3
- "version": "0.3.3",
3
+ "version": "0.3.4",
4
4
  "description": "The Skedyul SDK for Node.js",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",