skedyul 1.2.44 → 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 +3670 -9073
- 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 +252 -163
- package/dist/esm/index.mjs +1149 -7671
- package/dist/index.d.ts +11 -6
- package/dist/index.js +1194 -7669
- 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 +252 -163
- package/dist/serverless/server.mjs +252 -163
- 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 +33 -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 ?? {
|
|
@@ -442,7 +523,8 @@ function createCallToolHandler(registry, state, onMaxRequests) {
|
|
|
442
523
|
toolName
|
|
443
524
|
},
|
|
444
525
|
effect: functionResult.effect,
|
|
445
|
-
dataBlocks: functionResult.dataBlocks
|
|
526
|
+
dataBlocks: functionResult.dataBlocks,
|
|
527
|
+
cursor: functionResult.cursor
|
|
446
528
|
};
|
|
447
529
|
} catch (error) {
|
|
448
530
|
if (error instanceof AppAuthInvalidError) {
|
|
@@ -805,8 +887,9 @@ function serializeConfig(config) {
|
|
|
805
887
|
tools: registry ? Object.entries(registry).map(([key, tool]) => ({
|
|
806
888
|
name: tool.name || key,
|
|
807
889
|
description: tool.description,
|
|
808
|
-
timeout
|
|
809
|
-
|
|
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
|
|
810
893
|
})) : [],
|
|
811
894
|
webhooks: webhookRegistry ? Object.values(webhookRegistry).map((w) => ({
|
|
812
895
|
name: w.name,
|
|
@@ -1225,6 +1308,34 @@ function isMethodAllowed(webhookRegistry, handle, method) {
|
|
|
1225
1308
|
function getConfigFilePath() {
|
|
1226
1309
|
return process.env.LAMBDA_TASK_ROOT ? path.join(process.env.LAMBDA_TASK_ROOT, ".skedyul", "config.json") : ".skedyul/config.json";
|
|
1227
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
|
+
}
|
|
1228
1339
|
function handleHealthRoute(ctx) {
|
|
1229
1340
|
return {
|
|
1230
1341
|
status: 200,
|
|
@@ -1234,34 +1345,14 @@ function handleHealthRoute(ctx) {
|
|
|
1234
1345
|
function handleConfigRoute(ctx) {
|
|
1235
1346
|
const configFilePath = getConfigFilePath();
|
|
1236
1347
|
try {
|
|
1237
|
-
console.log(`[/config] Checking for config file at: ${configFilePath}`);
|
|
1238
1348
|
if (fs.existsSync(configFilePath)) {
|
|
1239
1349
|
const fileConfig = JSON.parse(fs.readFileSync(configFilePath, "utf-8"));
|
|
1240
|
-
console.log(
|
|
1241
|
-
`[/config] Loaded config from file: tools=${fileConfig.tools?.length ?? 0}, webhooks=${fileConfig.webhooks?.length ?? 0}`
|
|
1242
|
-
);
|
|
1243
|
-
console.log(
|
|
1244
|
-
`[/config] SENDING config with keys: ${Object.keys(fileConfig).join(", ")}`
|
|
1245
|
-
);
|
|
1246
|
-
console.log(
|
|
1247
|
-
`[/config] SENDING full config: ${JSON.stringify(fileConfig).substring(0, 2e3)}...`
|
|
1248
|
-
);
|
|
1249
1350
|
return { status: 200, body: fileConfig };
|
|
1250
1351
|
}
|
|
1251
|
-
console.log("[/config] Config file not found, falling back to runtime serialization");
|
|
1252
1352
|
} catch (err) {
|
|
1253
1353
|
console.warn("[/config] Failed to read config file, falling back to runtime serialization:", err);
|
|
1254
1354
|
}
|
|
1255
1355
|
const serialized = serializeConfig(ctx.config);
|
|
1256
|
-
console.log(
|
|
1257
|
-
`[/config] Runtime serialization: tools=${serialized.tools?.length ?? 0}, webhooks=${serialized.webhooks?.length ?? 0}`
|
|
1258
|
-
);
|
|
1259
|
-
console.log(
|
|
1260
|
-
`[/config] SENDING serialized config with keys: ${Object.keys(serialized).join(", ")}`
|
|
1261
|
-
);
|
|
1262
|
-
console.log(
|
|
1263
|
-
`[/config] SENDING full serialized config: ${JSON.stringify(serialized).substring(0, 2e3)}...`
|
|
1264
|
-
);
|
|
1265
1356
|
return { status: 200, body: serialized };
|
|
1266
1357
|
}
|
|
1267
1358
|
async function handleCoreRoute(req, ctx) {
|
|
@@ -1316,16 +1407,8 @@ async function handleEstimateRoute(req, ctx) {
|
|
|
1316
1407
|
try {
|
|
1317
1408
|
const toolName = estimateBody.name;
|
|
1318
1409
|
const toolArgs = estimateBody.inputs ?? {};
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
for (const [key, t] of Object.entries(ctx.registry)) {
|
|
1322
|
-
if (t.name === toolName || key === toolName) {
|
|
1323
|
-
toolKey = key;
|
|
1324
|
-
tool = t;
|
|
1325
|
-
break;
|
|
1326
|
-
}
|
|
1327
|
-
}
|
|
1328
|
-
if (!tool || !toolKey) {
|
|
1410
|
+
const found = findToolInRegistry(ctx.registry, toolName);
|
|
1411
|
+
if (!found) {
|
|
1329
1412
|
return {
|
|
1330
1413
|
status: 400,
|
|
1331
1414
|
body: {
|
|
@@ -1336,6 +1419,7 @@ async function handleEstimateRoute(req, ctx) {
|
|
|
1336
1419
|
}
|
|
1337
1420
|
};
|
|
1338
1421
|
}
|
|
1422
|
+
const { toolKey, tool } = found;
|
|
1339
1423
|
const inputSchema = getZodSchema(tool.inputSchema);
|
|
1340
1424
|
const validatedArgs = inputSchema ? inputSchema.parse(toolArgs) : toolArgs;
|
|
1341
1425
|
const estimateResponse = await ctx.callTool(toolKey, {
|
|
@@ -1498,35 +1582,13 @@ async function handleMcpRoute(req, ctx) {
|
|
|
1498
1582
|
async function handleMcpToolsCall(params, id, ctx) {
|
|
1499
1583
|
const toolName = params?.name;
|
|
1500
1584
|
const rawArgs = params?.arguments ?? {};
|
|
1501
|
-
console.log("[route-handlers /mcp] Received tools/call request:", JSON.stringify({
|
|
1502
|
-
toolName,
|
|
1503
|
-
hasArguments: !!params?.arguments,
|
|
1504
|
-
argumentKeys: rawArgs ? Object.keys(rawArgs) : [],
|
|
1505
|
-
hasEnv: "env" in rawArgs,
|
|
1506
|
-
envKeys: rawArgs.env ? Object.keys(rawArgs.env) : [],
|
|
1507
|
-
hasApiToken: !!rawArgs.env?.SKEDYUL_API_TOKEN
|
|
1508
|
-
}, null, 2));
|
|
1509
1585
|
const hasSkedyulFormat = "inputs" in rawArgs || "env" in rawArgs || "context" in rawArgs || "invocation" in rawArgs;
|
|
1510
1586
|
const toolInputs = hasSkedyulFormat ? rawArgs.inputs ?? {} : rawArgs;
|
|
1511
1587
|
const toolContext = hasSkedyulFormat ? rawArgs.context : void 0;
|
|
1512
1588
|
const toolEnv = hasSkedyulFormat ? rawArgs.env : void 0;
|
|
1513
1589
|
const toolInvocation = hasSkedyulFormat ? rawArgs.invocation : void 0;
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
hasToolEnv: !!toolEnv,
|
|
1517
|
-
toolEnvKeys: toolEnv ? Object.keys(toolEnv) : [],
|
|
1518
|
-
hasApiToken: toolEnv?.SKEDYUL_API_TOKEN ? `yes (${toolEnv.SKEDYUL_API_TOKEN.length} chars)` : "no"
|
|
1519
|
-
}, null, 2));
|
|
1520
|
-
let toolKey = null;
|
|
1521
|
-
let tool = null;
|
|
1522
|
-
for (const [key, t] of Object.entries(ctx.registry)) {
|
|
1523
|
-
if (t.name === toolName || key === toolName) {
|
|
1524
|
-
toolKey = key;
|
|
1525
|
-
tool = t;
|
|
1526
|
-
break;
|
|
1527
|
-
}
|
|
1528
|
-
}
|
|
1529
|
-
if (!tool || !toolKey) {
|
|
1590
|
+
const found = findToolInRegistry(ctx.registry, toolName);
|
|
1591
|
+
if (!found) {
|
|
1530
1592
|
return {
|
|
1531
1593
|
status: 200,
|
|
1532
1594
|
body: {
|
|
@@ -1539,6 +1601,7 @@ async function handleMcpToolsCall(params, id, ctx) {
|
|
|
1539
1601
|
}
|
|
1540
1602
|
};
|
|
1541
1603
|
}
|
|
1604
|
+
const { toolKey, tool } = found;
|
|
1542
1605
|
try {
|
|
1543
1606
|
const inputSchema = getZodSchema(tool.inputSchema);
|
|
1544
1607
|
const outputSchema = getZodSchema(tool.outputSchema);
|
|
@@ -1551,68 +1614,45 @@ async function handleMcpToolsCall(params, id, ctx) {
|
|
|
1551
1614
|
invocation: toolInvocation
|
|
1552
1615
|
});
|
|
1553
1616
|
let result;
|
|
1554
|
-
const
|
|
1555
|
-
const isLegacyErrorFailure = "error" in toolResult && toolResult.error != null;
|
|
1556
|
-
const isLegacyMetaFailure = "meta" in toolResult && toolResult.meta != null && typeof toolResult.meta === "object" && "success" in toolResult.meta && toolResult.meta.success === false;
|
|
1557
|
-
const isFailure = isNewShapeFailure || isLegacyErrorFailure || isLegacyMetaFailure;
|
|
1617
|
+
const isFailure = isToolCallFailure(toolResult);
|
|
1558
1618
|
if (isFailure) {
|
|
1559
|
-
|
|
1560
|
-
if (isNewShapeFailure && "error" in toolResult) {
|
|
1561
|
-
errorOutput = {
|
|
1562
|
-
error: toolResult.error,
|
|
1563
|
-
retry: "retry" in toolResult ? toolResult.retry : void 0
|
|
1564
|
-
};
|
|
1565
|
-
} else if (isLegacyErrorFailure && "error" in toolResult) {
|
|
1566
|
-
errorOutput = { error: toolResult.error };
|
|
1567
|
-
} else if (isLegacyMetaFailure && "meta" in toolResult && toolResult.meta) {
|
|
1568
|
-
const meta = toolResult.meta;
|
|
1569
|
-
errorOutput = {
|
|
1570
|
-
error: {
|
|
1571
|
-
code: "TOOL_FAILED",
|
|
1572
|
-
message: meta.message ?? "Tool execution failed",
|
|
1573
|
-
category: "internal"
|
|
1574
|
-
}
|
|
1575
|
-
};
|
|
1576
|
-
} else {
|
|
1577
|
-
errorOutput = {
|
|
1578
|
-
error: {
|
|
1579
|
-
code: "TOOL_FAILED",
|
|
1580
|
-
message: "Tool execution failed",
|
|
1581
|
-
category: "internal"
|
|
1582
|
-
}
|
|
1583
|
-
};
|
|
1584
|
-
}
|
|
1619
|
+
const errorOutput = buildToolCallErrorOutput(toolResult);
|
|
1585
1620
|
result = {
|
|
1586
|
-
content: [{ type: "text", text:
|
|
1621
|
+
content: [{ type: "text", text: serializeMcpContentText(errorOutput) }],
|
|
1587
1622
|
structuredContent: hasOutputSchema ? void 0 : errorOutput,
|
|
1588
1623
|
isError: true,
|
|
1589
1624
|
billing: "billing" in toolResult ? toolResult.billing : void 0
|
|
1590
1625
|
};
|
|
1591
1626
|
} else {
|
|
1592
|
-
const
|
|
1627
|
+
const rawOutput = "output" in toolResult ? toolResult.output : null;
|
|
1628
|
+
const outputData = rawOutput ?? null;
|
|
1593
1629
|
const effect = "effect" in toolResult ? toolResult.effect : void 0;
|
|
1594
1630
|
const warnings = "warnings" in toolResult ? toolResult.warnings : void 0;
|
|
1595
1631
|
const pagination = "pagination" in toolResult ? toolResult.pagination : void 0;
|
|
1632
|
+
const cursor = "cursor" in toolResult ? toolResult.cursor : void 0;
|
|
1596
1633
|
let structuredContent;
|
|
1597
1634
|
if (outputData) {
|
|
1598
1635
|
structuredContent = {
|
|
1599
1636
|
...outputData,
|
|
1600
1637
|
__effect: effect,
|
|
1601
1638
|
__warnings: warnings,
|
|
1602
|
-
__pagination: pagination
|
|
1639
|
+
__pagination: pagination,
|
|
1640
|
+
__cursor: cursor
|
|
1603
1641
|
};
|
|
1604
|
-
} else if (effect || warnings || pagination) {
|
|
1642
|
+
} else if (effect || warnings || pagination || cursor) {
|
|
1605
1643
|
structuredContent = {
|
|
1606
1644
|
__effect: effect,
|
|
1607
1645
|
__warnings: warnings,
|
|
1608
|
-
__pagination: pagination
|
|
1646
|
+
__pagination: pagination,
|
|
1647
|
+
__cursor: cursor
|
|
1609
1648
|
};
|
|
1610
1649
|
} else if (hasOutputSchema) {
|
|
1611
1650
|
structuredContent = {};
|
|
1612
1651
|
}
|
|
1613
1652
|
result = {
|
|
1614
|
-
content: [{ type: "text", text:
|
|
1653
|
+
content: [{ type: "text", text: serializeMcpContentText(outputData) }],
|
|
1615
1654
|
structuredContent,
|
|
1655
|
+
cursor,
|
|
1616
1656
|
billing: "billing" in toolResult ? toolResult.billing : void 0
|
|
1617
1657
|
};
|
|
1618
1658
|
}
|
|
@@ -1638,6 +1678,94 @@ async function handleMcpToolsCall(params, id, ctx) {
|
|
|
1638
1678
|
};
|
|
1639
1679
|
}
|
|
1640
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
|
+
}
|
|
1641
1769
|
function createNotFoundResponse() {
|
|
1642
1770
|
return {
|
|
1643
1771
|
status: 404,
|
|
@@ -1711,6 +1839,9 @@ async function routeRequest(req, ctx) {
|
|
|
1711
1839
|
if (req.path === "/mcp" && req.method === "POST") {
|
|
1712
1840
|
return handleMcpRoute(req, ctx);
|
|
1713
1841
|
}
|
|
1842
|
+
if (req.path === "/mcp/batch" && req.method === "POST") {
|
|
1843
|
+
return handleMcpBatchRoute(req, ctx);
|
|
1844
|
+
}
|
|
1714
1845
|
return createNotFoundResponse();
|
|
1715
1846
|
} catch (err) {
|
|
1716
1847
|
return createErrorResponse(err);
|
|
@@ -1807,30 +1938,22 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
|
|
|
1807
1938
|
httpServer.once("error", reject);
|
|
1808
1939
|
});
|
|
1809
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
|
+
},
|
|
1810
1951
|
getHealthStatus: () => state.getHealthStatus()
|
|
1811
1952
|
};
|
|
1812
1953
|
}
|
|
1813
1954
|
async function handleMcpWithSdkTransport(req, res, url, ctx, mcpServer) {
|
|
1814
1955
|
try {
|
|
1815
1956
|
const body = await parseJSONBody(req);
|
|
1816
|
-
if (body?.method === "tools/call") {
|
|
1817
|
-
console.log(
|
|
1818
|
-
"[dedicated.ts /mcp] Received tools/call request:",
|
|
1819
|
-
JSON.stringify(
|
|
1820
|
-
{
|
|
1821
|
-
method: body.method,
|
|
1822
|
-
toolName: body.params?.name,
|
|
1823
|
-
hasArguments: !!body.params?.arguments,
|
|
1824
|
-
argumentKeys: body.params?.arguments ? Object.keys(body.params.arguments) : [],
|
|
1825
|
-
hasEnv: !!body.params?.arguments?.env,
|
|
1826
|
-
envKeys: body.params?.arguments?.env ? Object.keys(body.params.arguments.env) : [],
|
|
1827
|
-
hasApiToken: !!body.params?.arguments?.env?.SKEDYUL_API_TOKEN
|
|
1828
|
-
},
|
|
1829
|
-
null,
|
|
1830
|
-
2
|
|
1831
|
-
)
|
|
1832
|
-
);
|
|
1833
|
-
}
|
|
1834
1957
|
if (body?.method === "tools/list") {
|
|
1835
1958
|
sendJSON(res, 200, {
|
|
1836
1959
|
jsonrpc: "2.0",
|
|
@@ -1905,65 +2028,46 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
|
|
|
1905
2028
|
}
|
|
1906
2029
|
|
|
1907
2030
|
// src/server/index.ts
|
|
1908
|
-
console.log("[skedyul-node/server] Module loading - imports done");
|
|
1909
|
-
console.log("[skedyul-node/server] All imports complete");
|
|
1910
|
-
console.log("[skedyul-node/server] Installing context logger...");
|
|
1911
2031
|
installContextLogger();
|
|
1912
|
-
console.log("[skedyul-node/server] Context logger installed");
|
|
1913
2032
|
function createSkedyulServer(config) {
|
|
1914
|
-
console.log("[createSkedyulServer] Step 1: mergeRuntimeEnv()");
|
|
1915
2033
|
mergeRuntimeEnv();
|
|
1916
2034
|
const registry = config.tools;
|
|
1917
2035
|
const webhookRegistry = config.webhooks;
|
|
1918
|
-
console.log("[createSkedyulServer] Step 2: coreApi setup");
|
|
1919
2036
|
if (config.coreApi?.service) {
|
|
1920
2037
|
coreApiService.register(config.coreApi.service);
|
|
1921
2038
|
if (config.coreApi.webhookHandler) {
|
|
1922
2039
|
coreApiService.setWebhookHandler(config.coreApi.webhookHandler);
|
|
1923
2040
|
}
|
|
1924
2041
|
}
|
|
1925
|
-
console.log("[createSkedyulServer] Step 3: buildToolMetadata()");
|
|
1926
2042
|
const tools = buildToolMetadata(registry);
|
|
1927
|
-
console.log("[createSkedyulServer] Step 3 done, tools:", tools.length);
|
|
1928
2043
|
const toolNames = Object.values(registry).map((tool) => tool.name);
|
|
1929
2044
|
const runtimeLabel = config.computeLayer ?? "serverless";
|
|
1930
2045
|
const maxRequests = config.maxRequests ?? parseNumberEnv(process.env.MCP_MAX_REQUESTS) ?? null;
|
|
1931
2046
|
const ttlExtendSeconds = config.ttlExtendSeconds ?? parseNumberEnv(process.env.MCP_TTL_EXTEND) ?? 3600;
|
|
1932
|
-
console.log("[createSkedyulServer] Step 4: createRequestState()");
|
|
1933
2047
|
const state = createRequestState(
|
|
1934
2048
|
maxRequests,
|
|
1935
2049
|
ttlExtendSeconds,
|
|
1936
2050
|
runtimeLabel,
|
|
1937
2051
|
toolNames
|
|
1938
2052
|
);
|
|
1939
|
-
console.log("[createSkedyulServer] Step 4 done");
|
|
1940
|
-
console.log("[createSkedyulServer] Step 5: new McpServer()");
|
|
1941
2053
|
const mcpServer = new import_mcp.McpServer({
|
|
1942
2054
|
name: config.name,
|
|
1943
2055
|
version: config.version ?? "0.0.0"
|
|
1944
2056
|
});
|
|
1945
|
-
console.log("[createSkedyulServer] Step 5 done");
|
|
1946
2057
|
const dedicatedShutdown = () => {
|
|
1947
2058
|
console.log("Max requests reached, shutting down...");
|
|
1948
2059
|
setTimeout(() => process.exit(0), 1e3);
|
|
1949
2060
|
};
|
|
1950
|
-
console.log("[createSkedyulServer] Step 6: createCallToolHandler()");
|
|
1951
2061
|
const callTool = createCallToolHandler(
|
|
1952
2062
|
registry,
|
|
1953
2063
|
state,
|
|
1954
2064
|
config.computeLayer === "dedicated" ? dedicatedShutdown : void 0
|
|
1955
2065
|
);
|
|
1956
|
-
console.log("[createSkedyulServer] Step 6 done");
|
|
1957
|
-
console.log("[createSkedyulServer] Step 7: Registering tools...");
|
|
1958
2066
|
for (const [toolKey, tool] of Object.entries(registry)) {
|
|
1959
|
-
console.log(`[createSkedyulServer] Registering tool: ${toolKey}`);
|
|
1960
2067
|
const toolName = tool.name || toolKey;
|
|
1961
2068
|
const toolDisplayName = tool.label || toolName;
|
|
1962
|
-
console.log(`[createSkedyulServer] Getting input schema for ${toolKey}`);
|
|
1963
2069
|
const inputZodSchema = getZodSchema(tool.inputSchema);
|
|
1964
|
-
console.log(`[createSkedyulServer] Getting output schema for ${toolKey}`);
|
|
1965
2070
|
const outputZodSchema = getZodSchema(tool.outputSchema);
|
|
1966
|
-
console.log(`[createSkedyulServer] Creating wrapped schema for ${toolKey}`);
|
|
1967
2071
|
const wrappedInputSchema = z3.object({
|
|
1968
2072
|
inputs: inputZodSchema ?? z3.record(z3.string(), z3.unknown()).optional(),
|
|
1969
2073
|
context: z3.record(z3.string(), z3.unknown()).optional(),
|
|
@@ -1971,7 +2075,6 @@ function createSkedyulServer(config) {
|
|
|
1971
2075
|
invocation: z3.record(z3.string(), z3.unknown()).optional(),
|
|
1972
2076
|
estimate: z3.boolean().optional()
|
|
1973
2077
|
}).passthrough();
|
|
1974
|
-
console.log(`[createSkedyulServer] Calling mcpServer.registerTool for ${toolKey}`);
|
|
1975
2078
|
mcpServer.registerTool(
|
|
1976
2079
|
toolName,
|
|
1977
2080
|
{
|
|
@@ -1983,20 +2086,11 @@ function createSkedyulServer(config) {
|
|
|
1983
2086
|
// outputSchema: outputZodSchema,
|
|
1984
2087
|
},
|
|
1985
2088
|
async (args) => {
|
|
1986
|
-
console.log(`[mcpServer.registerTool] Tool ${toolName} received raw args:`, JSON.stringify(args, null, 2));
|
|
1987
2089
|
const rawArgs = args;
|
|
1988
2090
|
const toolInputs = rawArgs.inputs ?? {};
|
|
1989
2091
|
const toolContext = rawArgs.context;
|
|
1990
2092
|
const toolEnv = rawArgs.env;
|
|
1991
2093
|
const toolInvocation = rawArgs.invocation;
|
|
1992
|
-
console.log(`[mcpServer.registerTool] Tool ${toolName} extracted:`, {
|
|
1993
|
-
hasInputs: !!rawArgs.inputs,
|
|
1994
|
-
hasContext: !!rawArgs.context,
|
|
1995
|
-
hasEnv: !!rawArgs.env,
|
|
1996
|
-
hasInvocation: !!rawArgs.invocation,
|
|
1997
|
-
envKeys: toolEnv ? Object.keys(toolEnv) : [],
|
|
1998
|
-
hasApiToken: toolEnv?.SKEDYUL_API_TOKEN ? `yes (${toolEnv.SKEDYUL_API_TOKEN.length} chars)` : "no"
|
|
1999
|
-
});
|
|
2000
2094
|
let validatedInputs = toolInputs;
|
|
2001
2095
|
if (inputZodSchema) {
|
|
2002
2096
|
try {
|
|
@@ -2030,10 +2124,10 @@ function createSkedyulServer(config) {
|
|
|
2030
2124
|
invocation: toolInvocation
|
|
2031
2125
|
});
|
|
2032
2126
|
const hasOutputSchema = Boolean(outputZodSchema);
|
|
2033
|
-
if (result
|
|
2034
|
-
const errorOutput =
|
|
2127
|
+
if (isToolCallFailure(result)) {
|
|
2128
|
+
const errorOutput = buildToolCallErrorOutput(result);
|
|
2035
2129
|
return {
|
|
2036
|
-
content: [{ type: "text", text:
|
|
2130
|
+
content: [{ type: "text", text: serializeMcpContentText(errorOutput) }],
|
|
2037
2131
|
// Don't provide structuredContent for error responses when tool has outputSchema
|
|
2038
2132
|
// because the error response won't match the success schema and MCP SDK validates it
|
|
2039
2133
|
structuredContent: hasOutputSchema ? void 0 : errorOutput,
|
|
@@ -2041,7 +2135,8 @@ function createSkedyulServer(config) {
|
|
|
2041
2135
|
billing: result.billing
|
|
2042
2136
|
};
|
|
2043
2137
|
}
|
|
2044
|
-
const
|
|
2138
|
+
const rawOutput = "output" in result ? result.output : null;
|
|
2139
|
+
const outputData = rawOutput ?? null;
|
|
2045
2140
|
const dataBlocks = result.dataBlocks;
|
|
2046
2141
|
let structuredContent;
|
|
2047
2142
|
if (outputData) {
|
|
@@ -2059,18 +2154,15 @@ function createSkedyulServer(config) {
|
|
|
2059
2154
|
structuredContent = {};
|
|
2060
2155
|
}
|
|
2061
2156
|
return {
|
|
2062
|
-
|
|
2157
|
+
// Ensure text is always a string - JSON.stringify(undefined) returns undefined, not a string
|
|
2158
|
+
content: [{ type: "text", text: serializeMcpContentText(outputData) }],
|
|
2063
2159
|
structuredContent,
|
|
2064
2160
|
billing: result.billing
|
|
2065
2161
|
};
|
|
2066
2162
|
}
|
|
2067
2163
|
);
|
|
2068
|
-
console.log(`[createSkedyulServer] Tool ${toolKey} registered successfully`);
|
|
2069
2164
|
}
|
|
2070
|
-
console.log("[createSkedyulServer] Step 7 done - all tools registered");
|
|
2071
|
-
console.log("[createSkedyulServer] Step 8: Creating server instance");
|
|
2072
2165
|
if (config.computeLayer === "dedicated") {
|
|
2073
|
-
console.log("[createSkedyulServer] Creating dedicated instance");
|
|
2074
2166
|
return createDedicatedServerInstance(
|
|
2075
2167
|
config,
|
|
2076
2168
|
tools,
|
|
@@ -2079,10 +2171,7 @@ function createSkedyulServer(config) {
|
|
|
2079
2171
|
mcpServer
|
|
2080
2172
|
);
|
|
2081
2173
|
}
|
|
2082
|
-
|
|
2083
|
-
const serverlessInstance = createServerlessInstance(config, tools, callTool, state, mcpServer);
|
|
2084
|
-
console.log("[createSkedyulServer] Serverless instance created successfully");
|
|
2085
|
-
return serverlessInstance;
|
|
2174
|
+
return createServerlessInstance(config, tools, callTool, state, mcpServer);
|
|
2086
2175
|
}
|
|
2087
2176
|
var server = {
|
|
2088
2177
|
create: createSkedyulServer
|