skedyul 1.2.43 → 1.2.48

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.
Files changed (54) hide show
  1. package/dist/cli/commands/workflows.d.ts +1 -0
  2. package/dist/cli/index.js +3682 -9076
  3. package/dist/cli/utils/auth.d.ts +4 -1
  4. package/dist/cli/utils/auth.js +32 -8
  5. package/dist/cli/utils/config.d.ts +23 -0
  6. package/dist/cli/utils/env-sync.d.ts +46 -0
  7. package/dist/cli/utils/mcp-http-client.d.ts +74 -0
  8. package/dist/cli/utils/migration-approval.d.ts +39 -0
  9. package/dist/cli/utils/mock-context.d.ts +19 -9
  10. package/dist/cli/utils/sse.d.ts +33 -0
  11. package/dist/cli/utils.d.ts +5 -1
  12. package/dist/compiler/types.d.ts +11 -9
  13. package/dist/config/schema-loader.d.ts +15 -2
  14. package/dist/config/types/model.d.ts +2 -0
  15. package/dist/config/types/page.d.ts +9 -0
  16. package/dist/context/index.d.ts +2 -2
  17. package/dist/context/resolver.d.ts +29 -28
  18. package/dist/context/types.d.ts +195 -37
  19. package/dist/core/client.d.ts +189 -233
  20. package/dist/dedicated/server.js +264 -166
  21. package/dist/esm/index.mjs +1161 -7674
  22. package/dist/index.d.ts +11 -6
  23. package/dist/index.js +1206 -7672
  24. package/dist/scheduling/calculateWaitTime.d.ts +16 -0
  25. package/dist/scheduling/index.d.ts +11 -0
  26. package/dist/scheduling/index.js +334 -0
  27. package/dist/scheduling/index.mjs +305 -0
  28. package/dist/scheduling/isTimeInWindow.d.ts +15 -0
  29. package/dist/scheduling/types-workflow.d.ts +54 -0
  30. package/dist/scheduling/types.d.ts +166 -0
  31. package/dist/schemas/agent-schema-v3.d.ts +406 -60
  32. package/dist/schemas/agent-schema-v3.js +248 -75
  33. package/dist/schemas/agent-schema-v3.mjs +234 -73
  34. package/dist/schemas/agent-schema.js +3 -7295
  35. package/dist/schemas/agent-schema.mjs +3 -7323
  36. package/dist/schemas/crm-schema.d.ts +53 -19
  37. package/dist/schemas/index.d.ts +1 -1
  38. package/dist/schemas.d.ts +128 -40
  39. package/dist/server/route-handlers/handlers.d.ts +7 -0
  40. package/dist/server/utils/env.d.ts +9 -0
  41. package/dist/server/utils/index.d.ts +1 -0
  42. package/dist/server/utils/mcp-response.d.ts +11 -0
  43. package/dist/server.js +264 -166
  44. package/dist/serverless/server.mjs +264 -166
  45. package/dist/skills/index.d.ts +1 -1
  46. package/dist/skills/types.d.ts +34 -23
  47. package/dist/skills/types.js +8 -17
  48. package/dist/skills/types.mjs +7 -16
  49. package/dist/types/index.d.ts +3 -3
  50. package/dist/types/server.d.ts +1 -0
  51. package/dist/types/tool-context.d.ts +31 -4
  52. package/dist/types/tool-response.d.ts +2 -0
  53. package/dist/types/tool.d.ts +35 -1
  54. package/package.json +8 -1
package/dist/server.js CHANGED
@@ -149,10 +149,24 @@ function parseNumberEnv(value) {
149
149
  const parsed = Number.parseInt(value, 10);
150
150
  return Number.isNaN(parsed) ? null : parsed;
151
151
  }
152
+ function getBakedExecutableEnv() {
153
+ return {
154
+ ...parseJsonRecord(process.env.MCP_ENV_JSON),
155
+ ...parseJsonRecord(process.env.MCP_ENV)
156
+ };
157
+ }
158
+ function buildToolExecutionEnv(requestEnv = {}) {
159
+ const bakedEnv = getBakedExecutableEnv();
160
+ const merged = { ...bakedEnv };
161
+ for (const [key, value] of Object.entries(requestEnv)) {
162
+ if (value !== void 0) {
163
+ merged[key] = value;
164
+ }
165
+ }
166
+ return merged;
167
+ }
152
168
  function mergeRuntimeEnv() {
153
- const bakedEnv = parseJsonRecord(process.env.MCP_ENV_JSON);
154
- const runtimeEnv = parseJsonRecord(process.env.MCP_ENV);
155
- const merged = { ...bakedEnv, ...runtimeEnv };
169
+ const merged = getBakedExecutableEnv();
156
170
  Object.assign(process.env, merged);
157
171
  }
158
172
 
@@ -197,6 +211,48 @@ function getListeningPort(config) {
197
211
  return config.defaultPort ?? 3e3;
198
212
  }
199
213
 
214
+ // src/server/utils/mcp-response.ts
215
+ function serializeMcpContentText(value) {
216
+ return JSON.stringify(value ?? null);
217
+ }
218
+ function isToolCallFailure(result) {
219
+ const isNewShapeFailure = "success" in result && result.success === false;
220
+ const isLegacyErrorFailure = "error" in result && result.error != null;
221
+ const isLegacyMetaFailure = "meta" in result && result.meta != null && typeof result.meta === "object" && "success" in result.meta && result.meta.success === false;
222
+ return isNewShapeFailure || isLegacyErrorFailure || isLegacyMetaFailure;
223
+ }
224
+ function buildToolCallErrorOutput(result) {
225
+ const isNewShapeFailure = "success" in result && result.success === false;
226
+ const isLegacyErrorFailure = "error" in result && result.error != null;
227
+ const isLegacyMetaFailure = "meta" in result && result.meta != null && typeof result.meta === "object" && "success" in result.meta && result.meta.success === false;
228
+ if (isNewShapeFailure && "error" in result) {
229
+ return {
230
+ error: result.error,
231
+ retry: "retry" in result ? result.retry : void 0
232
+ };
233
+ }
234
+ if (isLegacyErrorFailure && "error" in result) {
235
+ return { error: result.error };
236
+ }
237
+ if (isLegacyMetaFailure && "meta" in result && result.meta) {
238
+ const meta = result.meta;
239
+ return {
240
+ error: {
241
+ code: "TOOL_FAILED",
242
+ message: meta.message ?? "Tool execution failed",
243
+ category: "internal"
244
+ }
245
+ };
246
+ }
247
+ return {
248
+ error: {
249
+ code: "TOOL_FAILED",
250
+ message: "Tool execution failed",
251
+ category: "internal"
252
+ }
253
+ };
254
+ }
255
+
200
256
  // src/core/client.ts
201
257
  var import_async_hooks = require("async_hooks");
202
258
  var import_v4 = require("zod/v4");
@@ -320,14 +376,19 @@ function createContextLogger() {
320
376
  function buildToolMetadata(registry) {
321
377
  return Object.values(registry).map((tool) => {
322
378
  const toolConfig = tool.config ?? {};
323
- const timeout = typeof toolConfig.timeout === "number" && toolConfig.timeout > 0 ? toolConfig.timeout : 1e4;
324
- const retries = typeof toolConfig.retries === "number" && toolConfig.retries >= 1 ? toolConfig.retries : 1;
379
+ const rawTimeout = tool.timeout ?? toolConfig.timeout;
380
+ const rawRetries = tool.retries ?? toolConfig.retries;
381
+ const timeout = typeof rawTimeout === "number" && rawTimeout > 0 ? rawTimeout : 1e4;
382
+ const retries = typeof rawRetries === "number" && rawRetries >= 1 ? rawRetries : 1;
325
383
  return {
326
384
  name: tool.name,
327
385
  displayName: tool.label || tool.name,
328
386
  description: tool.description,
329
387
  inputSchema: getJsonSchemaFromToolSchema(tool.inputSchema),
330
388
  outputSchema: getJsonSchemaFromToolSchema(tool.outputSchema),
389
+ // Include timeout/retries at top-level for tools/list response (used by syncExecutableTools)
390
+ timeout,
391
+ retries,
331
392
  config: {
332
393
  timeout,
333
394
  retries,
@@ -381,8 +442,9 @@ function createCallToolHandler(registry, state, onMaxRequests) {
381
442
  }
382
443
  }
383
444
  const requestEnv = args.env ?? {};
445
+ const toolEnv = buildToolExecutionEnv(requestEnv);
384
446
  const originalEnv = { ...process.env };
385
- Object.assign(process.env, requestEnv);
447
+ Object.assign(process.env, toolEnv);
386
448
  const invocation = args.invocation;
387
449
  try {
388
450
  const inputs = args.inputs ?? {};
@@ -395,7 +457,16 @@ function createCallToolHandler(registry, state, onMaxRequests) {
395
457
  executionContext = {
396
458
  trigger: "provision",
397
459
  app,
398
- env: process.env,
460
+ env: toolEnv,
461
+ mode: estimateMode ? "estimate" : "execute",
462
+ invocation,
463
+ log
464
+ };
465
+ } else if (trigger === "developer_page_action" || trigger === "developer_form_submit") {
466
+ executionContext = {
467
+ trigger,
468
+ app,
469
+ env: toolEnv,
399
470
  mode: estimateMode ? "estimate" : "execute",
400
471
  invocation,
401
472
  log
@@ -404,7 +475,7 @@ function createCallToolHandler(registry, state, onMaxRequests) {
404
475
  const workplace = rawContext.workplace;
405
476
  const request = rawContext.request;
406
477
  const appInstallationId = rawContext.appInstallationId;
407
- const envVars = process.env;
478
+ const envVars = toolEnv;
408
479
  const modeValue = estimateMode ? "estimate" : "execute";
409
480
  if (trigger === "field_change") {
410
481
  const field = rawContext.field;
@@ -424,8 +495,8 @@ function createCallToolHandler(registry, state, onMaxRequests) {
424
495
  }
425
496
  }
426
497
  const requestConfig = {
427
- baseUrl: requestEnv.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
428
- apiToken: requestEnv.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
498
+ baseUrl: toolEnv.SKEDYUL_API_URL ?? "",
499
+ apiToken: toolEnv.SKEDYUL_API_TOKEN ?? ""
429
500
  };
430
501
  const functionResult = await runWithConfig(requestConfig, async () => {
431
502
  return await runWithLogContext({ invocation }, async () => {
@@ -433,7 +504,17 @@ function createCallToolHandler(registry, state, onMaxRequests) {
433
504
  });
434
505
  });
435
506
  const billing = normalizeBilling(functionResult.billing);
507
+ if ("success" in functionResult && functionResult.success === false) {
508
+ return {
509
+ success: false,
510
+ output: null,
511
+ billing,
512
+ error: "error" in functionResult ? functionResult.error : void 0,
513
+ effect: "effect" in functionResult ? functionResult.effect : void 0
514
+ };
515
+ }
436
516
  return {
517
+ success: true,
437
518
  output: functionResult.output,
438
519
  billing,
439
520
  meta: functionResult.meta ?? {
@@ -441,7 +522,9 @@ function createCallToolHandler(registry, state, onMaxRequests) {
441
522
  message: "OK",
442
523
  toolName
443
524
  },
444
- effect: functionResult.effect
525
+ effect: functionResult.effect,
526
+ dataBlocks: functionResult.dataBlocks,
527
+ cursor: functionResult.cursor
445
528
  };
446
529
  } catch (error) {
447
530
  if (error instanceof AppAuthInvalidError) {
@@ -804,8 +887,9 @@ function serializeConfig(config) {
804
887
  tools: registry ? Object.entries(registry).map(([key, tool]) => ({
805
888
  name: tool.name || key,
806
889
  description: tool.description,
807
- timeout: tool.config?.timeout,
808
- retries: tool.config?.retries
890
+ // Read timeout/retries from top-level first, then fallback to config
891
+ timeout: tool.timeout ?? tool.config?.timeout,
892
+ retries: tool.retries ?? tool.config?.retries
809
893
  })) : [],
810
894
  webhooks: webhookRegistry ? Object.values(webhookRegistry).map((w) => ({
811
895
  name: w.name,
@@ -1224,6 +1308,34 @@ function isMethodAllowed(webhookRegistry, handle, method) {
1224
1308
  function getConfigFilePath() {
1225
1309
  return process.env.LAMBDA_TASK_ROOT ? path.join(process.env.LAMBDA_TASK_ROOT, ".skedyul", "config.json") : ".skedyul/config.json";
1226
1310
  }
1311
+ function findToolInRegistry(registry, toolName) {
1312
+ for (const [key, t] of Object.entries(registry)) {
1313
+ if (t.name === toolName || key === toolName) {
1314
+ return { toolKey: key, tool: t };
1315
+ }
1316
+ }
1317
+ const inputParts = toolName.split(":");
1318
+ const inputShortName = inputParts[inputParts.length - 1];
1319
+ if (inputParts.length > 1) {
1320
+ for (const [key, t] of Object.entries(registry)) {
1321
+ if (t.name === inputShortName || key === inputShortName) {
1322
+ return { toolKey: key, tool: t };
1323
+ }
1324
+ }
1325
+ }
1326
+ const matches = [];
1327
+ for (const [key, t] of Object.entries(registry)) {
1328
+ const registryParts = t.name.split(":");
1329
+ const registryShortName = registryParts[registryParts.length - 1];
1330
+ if (registryShortName === toolName) {
1331
+ matches.push({ toolKey: key, tool: t });
1332
+ }
1333
+ }
1334
+ if (matches.length === 1) {
1335
+ return matches[0];
1336
+ }
1337
+ return null;
1338
+ }
1227
1339
  function handleHealthRoute(ctx) {
1228
1340
  return {
1229
1341
  status: 200,
@@ -1233,34 +1345,14 @@ function handleHealthRoute(ctx) {
1233
1345
  function handleConfigRoute(ctx) {
1234
1346
  const configFilePath = getConfigFilePath();
1235
1347
  try {
1236
- console.log(`[/config] Checking for config file at: ${configFilePath}`);
1237
1348
  if (fs.existsSync(configFilePath)) {
1238
1349
  const fileConfig = JSON.parse(fs.readFileSync(configFilePath, "utf-8"));
1239
- console.log(
1240
- `[/config] Loaded config from file: tools=${fileConfig.tools?.length ?? 0}, webhooks=${fileConfig.webhooks?.length ?? 0}`
1241
- );
1242
- console.log(
1243
- `[/config] SENDING config with keys: ${Object.keys(fileConfig).join(", ")}`
1244
- );
1245
- console.log(
1246
- `[/config] SENDING full config: ${JSON.stringify(fileConfig).substring(0, 2e3)}...`
1247
- );
1248
1350
  return { status: 200, body: fileConfig };
1249
1351
  }
1250
- console.log("[/config] Config file not found, falling back to runtime serialization");
1251
1352
  } catch (err) {
1252
1353
  console.warn("[/config] Failed to read config file, falling back to runtime serialization:", err);
1253
1354
  }
1254
1355
  const serialized = serializeConfig(ctx.config);
1255
- console.log(
1256
- `[/config] Runtime serialization: tools=${serialized.tools?.length ?? 0}, webhooks=${serialized.webhooks?.length ?? 0}`
1257
- );
1258
- console.log(
1259
- `[/config] SENDING serialized config with keys: ${Object.keys(serialized).join(", ")}`
1260
- );
1261
- console.log(
1262
- `[/config] SENDING full serialized config: ${JSON.stringify(serialized).substring(0, 2e3)}...`
1263
- );
1264
1356
  return { status: 200, body: serialized };
1265
1357
  }
1266
1358
  async function handleCoreRoute(req, ctx) {
@@ -1315,16 +1407,8 @@ async function handleEstimateRoute(req, ctx) {
1315
1407
  try {
1316
1408
  const toolName = estimateBody.name;
1317
1409
  const toolArgs = estimateBody.inputs ?? {};
1318
- let toolKey = null;
1319
- let tool = null;
1320
- for (const [key, t] of Object.entries(ctx.registry)) {
1321
- if (t.name === toolName || key === toolName) {
1322
- toolKey = key;
1323
- tool = t;
1324
- break;
1325
- }
1326
- }
1327
- if (!tool || !toolKey) {
1410
+ const found = findToolInRegistry(ctx.registry, toolName);
1411
+ if (!found) {
1328
1412
  return {
1329
1413
  status: 400,
1330
1414
  body: {
@@ -1335,6 +1419,7 @@ async function handleEstimateRoute(req, ctx) {
1335
1419
  }
1336
1420
  };
1337
1421
  }
1422
+ const { toolKey, tool } = found;
1338
1423
  const inputSchema = getZodSchema(tool.inputSchema);
1339
1424
  const validatedArgs = inputSchema ? inputSchema.parse(toolArgs) : toolArgs;
1340
1425
  const estimateResponse = await ctx.callTool(toolKey, {
@@ -1497,35 +1582,13 @@ async function handleMcpRoute(req, ctx) {
1497
1582
  async function handleMcpToolsCall(params, id, ctx) {
1498
1583
  const toolName = params?.name;
1499
1584
  const rawArgs = params?.arguments ?? {};
1500
- console.log("[route-handlers /mcp] Received tools/call request:", JSON.stringify({
1501
- toolName,
1502
- hasArguments: !!params?.arguments,
1503
- argumentKeys: rawArgs ? Object.keys(rawArgs) : [],
1504
- hasEnv: "env" in rawArgs,
1505
- envKeys: rawArgs.env ? Object.keys(rawArgs.env) : [],
1506
- hasApiToken: !!rawArgs.env?.SKEDYUL_API_TOKEN
1507
- }, null, 2));
1508
1585
  const hasSkedyulFormat = "inputs" in rawArgs || "env" in rawArgs || "context" in rawArgs || "invocation" in rawArgs;
1509
1586
  const toolInputs = hasSkedyulFormat ? rawArgs.inputs ?? {} : rawArgs;
1510
1587
  const toolContext = hasSkedyulFormat ? rawArgs.context : void 0;
1511
1588
  const toolEnv = hasSkedyulFormat ? rawArgs.env : void 0;
1512
1589
  const toolInvocation = hasSkedyulFormat ? rawArgs.invocation : void 0;
1513
- console.log("[route-handlers /mcp] Extracted env:", JSON.stringify({
1514
- hasSkedyulFormat,
1515
- hasToolEnv: !!toolEnv,
1516
- toolEnvKeys: toolEnv ? Object.keys(toolEnv) : [],
1517
- hasApiToken: toolEnv?.SKEDYUL_API_TOKEN ? `yes (${toolEnv.SKEDYUL_API_TOKEN.length} chars)` : "no"
1518
- }, null, 2));
1519
- let toolKey = null;
1520
- let tool = null;
1521
- for (const [key, t] of Object.entries(ctx.registry)) {
1522
- if (t.name === toolName || key === toolName) {
1523
- toolKey = key;
1524
- tool = t;
1525
- break;
1526
- }
1527
- }
1528
- if (!tool || !toolKey) {
1590
+ const found = findToolInRegistry(ctx.registry, toolName);
1591
+ if (!found) {
1529
1592
  return {
1530
1593
  status: 200,
1531
1594
  body: {
@@ -1538,6 +1601,7 @@ async function handleMcpToolsCall(params, id, ctx) {
1538
1601
  }
1539
1602
  };
1540
1603
  }
1604
+ const { toolKey, tool } = found;
1541
1605
  try {
1542
1606
  const inputSchema = getZodSchema(tool.inputSchema);
1543
1607
  const outputSchema = getZodSchema(tool.outputSchema);
@@ -1550,68 +1614,45 @@ async function handleMcpToolsCall(params, id, ctx) {
1550
1614
  invocation: toolInvocation
1551
1615
  });
1552
1616
  let result;
1553
- const isNewShapeFailure = "success" in toolResult && toolResult.success === false;
1554
- const isLegacyErrorFailure = "error" in toolResult && toolResult.error != null;
1555
- const isLegacyMetaFailure = "meta" in toolResult && toolResult.meta != null && typeof toolResult.meta === "object" && "success" in toolResult.meta && toolResult.meta.success === false;
1556
- const isFailure = isNewShapeFailure || isLegacyErrorFailure || isLegacyMetaFailure;
1617
+ const isFailure = isToolCallFailure(toolResult);
1557
1618
  if (isFailure) {
1558
- let errorOutput;
1559
- if (isNewShapeFailure && "error" in toolResult) {
1560
- errorOutput = {
1561
- error: toolResult.error,
1562
- retry: "retry" in toolResult ? toolResult.retry : void 0
1563
- };
1564
- } else if (isLegacyErrorFailure && "error" in toolResult) {
1565
- errorOutput = { error: toolResult.error };
1566
- } else if (isLegacyMetaFailure && "meta" in toolResult && toolResult.meta) {
1567
- const meta = toolResult.meta;
1568
- errorOutput = {
1569
- error: {
1570
- code: "TOOL_FAILED",
1571
- message: meta.message ?? "Tool execution failed",
1572
- category: "internal"
1573
- }
1574
- };
1575
- } else {
1576
- errorOutput = {
1577
- error: {
1578
- code: "TOOL_FAILED",
1579
- message: "Tool execution failed",
1580
- category: "internal"
1581
- }
1582
- };
1583
- }
1619
+ const errorOutput = buildToolCallErrorOutput(toolResult);
1584
1620
  result = {
1585
- content: [{ type: "text", text: JSON.stringify(errorOutput) }],
1621
+ content: [{ type: "text", text: serializeMcpContentText(errorOutput) }],
1586
1622
  structuredContent: hasOutputSchema ? void 0 : errorOutput,
1587
1623
  isError: true,
1588
1624
  billing: "billing" in toolResult ? toolResult.billing : void 0
1589
1625
  };
1590
1626
  } else {
1591
- const outputData = "output" in toolResult ? toolResult.output : null;
1627
+ const rawOutput = "output" in toolResult ? toolResult.output : null;
1628
+ const outputData = rawOutput ?? null;
1592
1629
  const effect = "effect" in toolResult ? toolResult.effect : void 0;
1593
1630
  const warnings = "warnings" in toolResult ? toolResult.warnings : void 0;
1594
1631
  const pagination = "pagination" in toolResult ? toolResult.pagination : void 0;
1632
+ const cursor = "cursor" in toolResult ? toolResult.cursor : void 0;
1595
1633
  let structuredContent;
1596
1634
  if (outputData) {
1597
1635
  structuredContent = {
1598
1636
  ...outputData,
1599
1637
  __effect: effect,
1600
1638
  __warnings: warnings,
1601
- __pagination: pagination
1639
+ __pagination: pagination,
1640
+ __cursor: cursor
1602
1641
  };
1603
- } else if (effect || warnings || pagination) {
1642
+ } else if (effect || warnings || pagination || cursor) {
1604
1643
  structuredContent = {
1605
1644
  __effect: effect,
1606
1645
  __warnings: warnings,
1607
- __pagination: pagination
1646
+ __pagination: pagination,
1647
+ __cursor: cursor
1608
1648
  };
1609
1649
  } else if (hasOutputSchema) {
1610
1650
  structuredContent = {};
1611
1651
  }
1612
1652
  result = {
1613
- content: [{ type: "text", text: JSON.stringify(outputData) }],
1653
+ content: [{ type: "text", text: serializeMcpContentText(outputData) }],
1614
1654
  structuredContent,
1655
+ cursor,
1615
1656
  billing: "billing" in toolResult ? toolResult.billing : void 0
1616
1657
  };
1617
1658
  }
@@ -1637,6 +1678,94 @@ async function handleMcpToolsCall(params, id, ctx) {
1637
1678
  };
1638
1679
  }
1639
1680
  }
1681
+ async function handleMcpBatchRoute(req, ctx) {
1682
+ const parseResult = parseJsonBody(req);
1683
+ if (!parseResult.success) {
1684
+ return parseResult.error;
1685
+ }
1686
+ const { calls, deadline, env: batchEnv, context: batchContext } = parseResult.data;
1687
+ if (!calls || !Array.isArray(calls) || calls.length === 0) {
1688
+ return {
1689
+ status: 400,
1690
+ body: {
1691
+ error: {
1692
+ code: -32602,
1693
+ message: "Missing or invalid calls array"
1694
+ }
1695
+ }
1696
+ };
1697
+ }
1698
+ const perCallTimeoutMs = deadline ? Math.min(deadline, 25e3) : 25e3;
1699
+ const executeWithTimeout = async (call, timeoutMs) => {
1700
+ return new Promise(async (resolve) => {
1701
+ const timeoutHandle = setTimeout(() => {
1702
+ resolve({
1703
+ id: call.id,
1704
+ success: false,
1705
+ error: "Timeout",
1706
+ timedOut: true
1707
+ });
1708
+ }, timeoutMs);
1709
+ try {
1710
+ const toolName = call.name;
1711
+ const toolArgs = call.arguments ?? {};
1712
+ const found = findToolInRegistry(ctx.registry, toolName);
1713
+ if (!found) {
1714
+ clearTimeout(timeoutHandle);
1715
+ resolve({
1716
+ id: call.id,
1717
+ success: false,
1718
+ error: `Tool "${toolName}" not found`
1719
+ });
1720
+ return;
1721
+ }
1722
+ const { toolKey, tool } = found;
1723
+ const inputSchema = getZodSchema(tool.inputSchema);
1724
+ const validatedInputs = inputSchema ? inputSchema.parse(toolArgs) : toolArgs;
1725
+ const toolResult = await ctx.callTool(toolKey, {
1726
+ inputs: validatedInputs,
1727
+ context: batchContext,
1728
+ env: batchEnv
1729
+ });
1730
+ clearTimeout(timeoutHandle);
1731
+ const isFailure = "success" in toolResult && toolResult.success === false || "error" in toolResult && toolResult.error != null;
1732
+ if (isFailure) {
1733
+ resolve({
1734
+ id: call.id,
1735
+ success: false,
1736
+ error: "error" in toolResult && toolResult.error ? typeof toolResult.error === "string" ? toolResult.error : JSON.stringify(toolResult.error) : "Tool execution failed"
1737
+ });
1738
+ } else {
1739
+ resolve({
1740
+ id: call.id,
1741
+ success: true,
1742
+ result: "output" in toolResult ? toolResult.output : toolResult
1743
+ });
1744
+ }
1745
+ } catch (err) {
1746
+ clearTimeout(timeoutHandle);
1747
+ resolve({
1748
+ id: call.id,
1749
+ success: false,
1750
+ error: err instanceof Error ? err.message : String(err)
1751
+ });
1752
+ }
1753
+ });
1754
+ };
1755
+ const results = await Promise.all(
1756
+ calls.map((call) => executeWithTimeout(call, perCallTimeoutMs))
1757
+ );
1758
+ return {
1759
+ status: 200,
1760
+ body: {
1761
+ results,
1762
+ totalCalls: calls.length,
1763
+ successCount: results.filter((r) => r.success).length,
1764
+ failedCount: results.filter((r) => !r.success).length,
1765
+ timedOutCount: results.filter((r) => r.timedOut).length
1766
+ }
1767
+ };
1768
+ }
1640
1769
  function createNotFoundResponse() {
1641
1770
  return {
1642
1771
  status: 404,
@@ -1710,6 +1839,9 @@ async function routeRequest(req, ctx) {
1710
1839
  if (req.path === "/mcp" && req.method === "POST") {
1711
1840
  return handleMcpRoute(req, ctx);
1712
1841
  }
1842
+ if (req.path === "/mcp/batch" && req.method === "POST") {
1843
+ return handleMcpBatchRoute(req, ctx);
1844
+ }
1713
1845
  return createNotFoundResponse();
1714
1846
  } catch (err) {
1715
1847
  return createErrorResponse(err);
@@ -1806,30 +1938,22 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
1806
1938
  httpServer.once("error", reject);
1807
1939
  });
1808
1940
  },
1941
+ async close() {
1942
+ return new Promise((resolve, reject) => {
1943
+ const closable = httpServer;
1944
+ closable.closeAllConnections?.();
1945
+ httpServer.close((err) => {
1946
+ if (err) reject(err);
1947
+ else resolve();
1948
+ });
1949
+ });
1950
+ },
1809
1951
  getHealthStatus: () => state.getHealthStatus()
1810
1952
  };
1811
1953
  }
1812
1954
  async function handleMcpWithSdkTransport(req, res, url, ctx, mcpServer) {
1813
1955
  try {
1814
1956
  const body = await parseJSONBody(req);
1815
- if (body?.method === "tools/call") {
1816
- console.log(
1817
- "[dedicated.ts /mcp] Received tools/call request:",
1818
- JSON.stringify(
1819
- {
1820
- method: body.method,
1821
- toolName: body.params?.name,
1822
- hasArguments: !!body.params?.arguments,
1823
- argumentKeys: body.params?.arguments ? Object.keys(body.params.arguments) : [],
1824
- hasEnv: !!body.params?.arguments?.env,
1825
- envKeys: body.params?.arguments?.env ? Object.keys(body.params.arguments.env) : [],
1826
- hasApiToken: !!body.params?.arguments?.env?.SKEDYUL_API_TOKEN
1827
- },
1828
- null,
1829
- 2
1830
- )
1831
- );
1832
- }
1833
1957
  if (body?.method === "tools/list") {
1834
1958
  sendJSON(res, 200, {
1835
1959
  jsonrpc: "2.0",
@@ -1904,65 +2028,46 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
1904
2028
  }
1905
2029
 
1906
2030
  // src/server/index.ts
1907
- console.log("[skedyul-node/server] Module loading - imports done");
1908
- console.log("[skedyul-node/server] All imports complete");
1909
- console.log("[skedyul-node/server] Installing context logger...");
1910
2031
  installContextLogger();
1911
- console.log("[skedyul-node/server] Context logger installed");
1912
2032
  function createSkedyulServer(config) {
1913
- console.log("[createSkedyulServer] Step 1: mergeRuntimeEnv()");
1914
2033
  mergeRuntimeEnv();
1915
2034
  const registry = config.tools;
1916
2035
  const webhookRegistry = config.webhooks;
1917
- console.log("[createSkedyulServer] Step 2: coreApi setup");
1918
2036
  if (config.coreApi?.service) {
1919
2037
  coreApiService.register(config.coreApi.service);
1920
2038
  if (config.coreApi.webhookHandler) {
1921
2039
  coreApiService.setWebhookHandler(config.coreApi.webhookHandler);
1922
2040
  }
1923
2041
  }
1924
- console.log("[createSkedyulServer] Step 3: buildToolMetadata()");
1925
2042
  const tools = buildToolMetadata(registry);
1926
- console.log("[createSkedyulServer] Step 3 done, tools:", tools.length);
1927
2043
  const toolNames = Object.values(registry).map((tool) => tool.name);
1928
2044
  const runtimeLabel = config.computeLayer ?? "serverless";
1929
2045
  const maxRequests = config.maxRequests ?? parseNumberEnv(process.env.MCP_MAX_REQUESTS) ?? null;
1930
2046
  const ttlExtendSeconds = config.ttlExtendSeconds ?? parseNumberEnv(process.env.MCP_TTL_EXTEND) ?? 3600;
1931
- console.log("[createSkedyulServer] Step 4: createRequestState()");
1932
2047
  const state = createRequestState(
1933
2048
  maxRequests,
1934
2049
  ttlExtendSeconds,
1935
2050
  runtimeLabel,
1936
2051
  toolNames
1937
2052
  );
1938
- console.log("[createSkedyulServer] Step 4 done");
1939
- console.log("[createSkedyulServer] Step 5: new McpServer()");
1940
2053
  const mcpServer = new import_mcp.McpServer({
1941
2054
  name: config.name,
1942
2055
  version: config.version ?? "0.0.0"
1943
2056
  });
1944
- console.log("[createSkedyulServer] Step 5 done");
1945
2057
  const dedicatedShutdown = () => {
1946
2058
  console.log("Max requests reached, shutting down...");
1947
2059
  setTimeout(() => process.exit(0), 1e3);
1948
2060
  };
1949
- console.log("[createSkedyulServer] Step 6: createCallToolHandler()");
1950
2061
  const callTool = createCallToolHandler(
1951
2062
  registry,
1952
2063
  state,
1953
2064
  config.computeLayer === "dedicated" ? dedicatedShutdown : void 0
1954
2065
  );
1955
- console.log("[createSkedyulServer] Step 6 done");
1956
- console.log("[createSkedyulServer] Step 7: Registering tools...");
1957
2066
  for (const [toolKey, tool] of Object.entries(registry)) {
1958
- console.log(`[createSkedyulServer] Registering tool: ${toolKey}`);
1959
2067
  const toolName = tool.name || toolKey;
1960
2068
  const toolDisplayName = tool.label || toolName;
1961
- console.log(`[createSkedyulServer] Getting input schema for ${toolKey}`);
1962
2069
  const inputZodSchema = getZodSchema(tool.inputSchema);
1963
- console.log(`[createSkedyulServer] Getting output schema for ${toolKey}`);
1964
2070
  const outputZodSchema = getZodSchema(tool.outputSchema);
1965
- console.log(`[createSkedyulServer] Creating wrapped schema for ${toolKey}`);
1966
2071
  const wrappedInputSchema = z3.object({
1967
2072
  inputs: inputZodSchema ?? z3.record(z3.string(), z3.unknown()).optional(),
1968
2073
  context: z3.record(z3.string(), z3.unknown()).optional(),
@@ -1970,7 +2075,6 @@ function createSkedyulServer(config) {
1970
2075
  invocation: z3.record(z3.string(), z3.unknown()).optional(),
1971
2076
  estimate: z3.boolean().optional()
1972
2077
  }).passthrough();
1973
- console.log(`[createSkedyulServer] Calling mcpServer.registerTool for ${toolKey}`);
1974
2078
  mcpServer.registerTool(
1975
2079
  toolName,
1976
2080
  {
@@ -1982,20 +2086,11 @@ function createSkedyulServer(config) {
1982
2086
  // outputSchema: outputZodSchema,
1983
2087
  },
1984
2088
  async (args) => {
1985
- console.log(`[mcpServer.registerTool] Tool ${toolName} received raw args:`, JSON.stringify(args, null, 2));
1986
2089
  const rawArgs = args;
1987
2090
  const toolInputs = rawArgs.inputs ?? {};
1988
2091
  const toolContext = rawArgs.context;
1989
2092
  const toolEnv = rawArgs.env;
1990
2093
  const toolInvocation = rawArgs.invocation;
1991
- console.log(`[mcpServer.registerTool] Tool ${toolName} extracted:`, {
1992
- hasInputs: !!rawArgs.inputs,
1993
- hasContext: !!rawArgs.context,
1994
- hasEnv: !!rawArgs.env,
1995
- hasInvocation: !!rawArgs.invocation,
1996
- envKeys: toolEnv ? Object.keys(toolEnv) : [],
1997
- hasApiToken: toolEnv?.SKEDYUL_API_TOKEN ? `yes (${toolEnv.SKEDYUL_API_TOKEN.length} chars)` : "no"
1998
- });
1999
2094
  let validatedInputs = toolInputs;
2000
2095
  if (inputZodSchema) {
2001
2096
  try {
@@ -2029,10 +2124,10 @@ function createSkedyulServer(config) {
2029
2124
  invocation: toolInvocation
2030
2125
  });
2031
2126
  const hasOutputSchema = Boolean(outputZodSchema);
2032
- if (result.error) {
2033
- const errorOutput = { error: result.error };
2127
+ if (isToolCallFailure(result)) {
2128
+ const errorOutput = buildToolCallErrorOutput(result);
2034
2129
  return {
2035
- content: [{ type: "text", text: JSON.stringify(errorOutput) }],
2130
+ content: [{ type: "text", text: serializeMcpContentText(errorOutput) }],
2036
2131
  // Don't provide structuredContent for error responses when tool has outputSchema
2037
2132
  // because the error response won't match the success schema and MCP SDK validates it
2038
2133
  structuredContent: hasOutputSchema ? void 0 : errorOutput,
@@ -2040,28 +2135,34 @@ function createSkedyulServer(config) {
2040
2135
  billing: result.billing
2041
2136
  };
2042
2137
  }
2043
- const outputData = result.output;
2138
+ const rawOutput = "output" in result ? result.output : null;
2139
+ const outputData = rawOutput ?? null;
2140
+ const dataBlocks = result.dataBlocks;
2044
2141
  let structuredContent;
2045
2142
  if (outputData) {
2046
- structuredContent = { ...outputData, __effect: result.effect };
2047
- } else if (result.effect) {
2048
- structuredContent = { __effect: result.effect };
2143
+ structuredContent = {
2144
+ ...outputData,
2145
+ __effect: result.effect,
2146
+ __dataBlocks: dataBlocks
2147
+ };
2148
+ } else if (result.effect || dataBlocks) {
2149
+ structuredContent = {
2150
+ __effect: result.effect,
2151
+ __dataBlocks: dataBlocks
2152
+ };
2049
2153
  } else if (hasOutputSchema) {
2050
2154
  structuredContent = {};
2051
2155
  }
2052
2156
  return {
2053
- content: [{ type: "text", text: JSON.stringify(result.output) }],
2157
+ // Ensure text is always a string - JSON.stringify(undefined) returns undefined, not a string
2158
+ content: [{ type: "text", text: serializeMcpContentText(outputData) }],
2054
2159
  structuredContent,
2055
2160
  billing: result.billing
2056
2161
  };
2057
2162
  }
2058
2163
  );
2059
- console.log(`[createSkedyulServer] Tool ${toolKey} registered successfully`);
2060
2164
  }
2061
- console.log("[createSkedyulServer] Step 7 done - all tools registered");
2062
- console.log("[createSkedyulServer] Step 8: Creating server instance");
2063
2165
  if (config.computeLayer === "dedicated") {
2064
- console.log("[createSkedyulServer] Creating dedicated instance");
2065
2166
  return createDedicatedServerInstance(
2066
2167
  config,
2067
2168
  tools,
@@ -2070,10 +2171,7 @@ function createSkedyulServer(config) {
2070
2171
  mcpServer
2071
2172
  );
2072
2173
  }
2073
- console.log("[createSkedyulServer] Creating serverless instance");
2074
- const serverlessInstance = createServerlessInstance(config, tools, callTool, state, mcpServer);
2075
- console.log("[createSkedyulServer] Serverless instance created successfully");
2076
- return serverlessInstance;
2174
+ return createServerlessInstance(config, tools, callTool, state, mcpServer);
2077
2175
  }
2078
2176
  var server = {
2079
2177
  create: createSkedyulServer