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.
- package/dist/cli/commands/workflows.d.ts +1 -0
- package/dist/cli/index.js +3682 -9076
- package/dist/cli/utils/auth.d.ts +4 -1
- package/dist/cli/utils/auth.js +32 -8
- package/dist/cli/utils/config.d.ts +23 -0
- package/dist/cli/utils/env-sync.d.ts +46 -0
- package/dist/cli/utils/mcp-http-client.d.ts +74 -0
- package/dist/cli/utils/migration-approval.d.ts +39 -0
- package/dist/cli/utils/mock-context.d.ts +19 -9
- package/dist/cli/utils/sse.d.ts +33 -0
- package/dist/cli/utils.d.ts +5 -1
- package/dist/compiler/types.d.ts +11 -9
- package/dist/config/schema-loader.d.ts +15 -2
- package/dist/config/types/model.d.ts +2 -0
- package/dist/config/types/page.d.ts +9 -0
- package/dist/context/index.d.ts +2 -2
- package/dist/context/resolver.d.ts +29 -28
- package/dist/context/types.d.ts +195 -37
- package/dist/core/client.d.ts +189 -233
- package/dist/dedicated/server.js +264 -166
- package/dist/esm/index.mjs +1161 -7674
- package/dist/index.d.ts +11 -6
- package/dist/index.js +1206 -7672
- package/dist/scheduling/calculateWaitTime.d.ts +16 -0
- package/dist/scheduling/index.d.ts +11 -0
- package/dist/scheduling/index.js +334 -0
- package/dist/scheduling/index.mjs +305 -0
- package/dist/scheduling/isTimeInWindow.d.ts +15 -0
- package/dist/scheduling/types-workflow.d.ts +54 -0
- package/dist/scheduling/types.d.ts +166 -0
- package/dist/schemas/agent-schema-v3.d.ts +406 -60
- package/dist/schemas/agent-schema-v3.js +248 -75
- package/dist/schemas/agent-schema-v3.mjs +234 -73
- package/dist/schemas/agent-schema.js +3 -7295
- package/dist/schemas/agent-schema.mjs +3 -7323
- package/dist/schemas/crm-schema.d.ts +53 -19
- package/dist/schemas/index.d.ts +1 -1
- package/dist/schemas.d.ts +128 -40
- package/dist/server/route-handlers/handlers.d.ts +7 -0
- package/dist/server/utils/env.d.ts +9 -0
- package/dist/server/utils/index.d.ts +1 -0
- package/dist/server/utils/mcp-response.d.ts +11 -0
- package/dist/server.js +264 -166
- package/dist/serverless/server.mjs +264 -166
- package/dist/skills/index.d.ts +1 -1
- package/dist/skills/types.d.ts +34 -23
- package/dist/skills/types.js +8 -17
- package/dist/skills/types.mjs +7 -16
- package/dist/types/index.d.ts +3 -3
- package/dist/types/server.d.ts +1 -0
- package/dist/types/tool-context.d.ts +31 -4
- package/dist/types/tool-response.d.ts +2 -0
- package/dist/types/tool.d.ts +35 -1
- 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
|
|
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
|
|
324
|
-
const
|
|
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,
|
|
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:
|
|
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 =
|
|
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:
|
|
428
|
-
apiToken:
|
|
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
|
|
808
|
-
|
|
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
|
-
|
|
1319
|
-
|
|
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
|
-
|
|
1514
|
-
|
|
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
|
|
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
|
-
|
|
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:
|
|
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
|
|
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:
|
|
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
|
|
2033
|
-
const errorOutput =
|
|
2127
|
+
if (isToolCallFailure(result)) {
|
|
2128
|
+
const errorOutput = buildToolCallErrorOutput(result);
|
|
2034
2129
|
return {
|
|
2035
|
-
content: [{ type: "text", text:
|
|
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
|
|
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 = {
|
|
2047
|
-
|
|
2048
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|