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
|
@@ -110,10 +110,24 @@ function parseNumberEnv(value) {
|
|
|
110
110
|
const parsed = Number.parseInt(value, 10);
|
|
111
111
|
return Number.isNaN(parsed) ? null : parsed;
|
|
112
112
|
}
|
|
113
|
+
function getBakedExecutableEnv() {
|
|
114
|
+
return {
|
|
115
|
+
...parseJsonRecord(process.env.MCP_ENV_JSON),
|
|
116
|
+
...parseJsonRecord(process.env.MCP_ENV)
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function buildToolExecutionEnv(requestEnv = {}) {
|
|
120
|
+
const bakedEnv = getBakedExecutableEnv();
|
|
121
|
+
const merged = { ...bakedEnv };
|
|
122
|
+
for (const [key, value] of Object.entries(requestEnv)) {
|
|
123
|
+
if (value !== void 0) {
|
|
124
|
+
merged[key] = value;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return merged;
|
|
128
|
+
}
|
|
113
129
|
function mergeRuntimeEnv() {
|
|
114
|
-
const
|
|
115
|
-
const runtimeEnv = parseJsonRecord(process.env.MCP_ENV);
|
|
116
|
-
const merged = { ...bakedEnv, ...runtimeEnv };
|
|
130
|
+
const merged = getBakedExecutableEnv();
|
|
117
131
|
Object.assign(process.env, merged);
|
|
118
132
|
}
|
|
119
133
|
|
|
@@ -158,6 +172,48 @@ function getListeningPort(config) {
|
|
|
158
172
|
return config.defaultPort ?? 3e3;
|
|
159
173
|
}
|
|
160
174
|
|
|
175
|
+
// src/server/utils/mcp-response.ts
|
|
176
|
+
function serializeMcpContentText(value) {
|
|
177
|
+
return JSON.stringify(value ?? null);
|
|
178
|
+
}
|
|
179
|
+
function isToolCallFailure(result) {
|
|
180
|
+
const isNewShapeFailure = "success" in result && result.success === false;
|
|
181
|
+
const isLegacyErrorFailure = "error" in result && result.error != null;
|
|
182
|
+
const isLegacyMetaFailure = "meta" in result && result.meta != null && typeof result.meta === "object" && "success" in result.meta && result.meta.success === false;
|
|
183
|
+
return isNewShapeFailure || isLegacyErrorFailure || isLegacyMetaFailure;
|
|
184
|
+
}
|
|
185
|
+
function buildToolCallErrorOutput(result) {
|
|
186
|
+
const isNewShapeFailure = "success" in result && result.success === false;
|
|
187
|
+
const isLegacyErrorFailure = "error" in result && result.error != null;
|
|
188
|
+
const isLegacyMetaFailure = "meta" in result && result.meta != null && typeof result.meta === "object" && "success" in result.meta && result.meta.success === false;
|
|
189
|
+
if (isNewShapeFailure && "error" in result) {
|
|
190
|
+
return {
|
|
191
|
+
error: result.error,
|
|
192
|
+
retry: "retry" in result ? result.retry : void 0
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
if (isLegacyErrorFailure && "error" in result) {
|
|
196
|
+
return { error: result.error };
|
|
197
|
+
}
|
|
198
|
+
if (isLegacyMetaFailure && "meta" in result && result.meta) {
|
|
199
|
+
const meta = result.meta;
|
|
200
|
+
return {
|
|
201
|
+
error: {
|
|
202
|
+
code: "TOOL_FAILED",
|
|
203
|
+
message: meta.message ?? "Tool execution failed",
|
|
204
|
+
category: "internal"
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
return {
|
|
209
|
+
error: {
|
|
210
|
+
code: "TOOL_FAILED",
|
|
211
|
+
message: "Tool execution failed",
|
|
212
|
+
category: "internal"
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
161
217
|
// src/core/client.ts
|
|
162
218
|
import { AsyncLocalStorage } from "async_hooks";
|
|
163
219
|
import { z as z2 } from "zod/v4";
|
|
@@ -281,14 +337,19 @@ function createContextLogger() {
|
|
|
281
337
|
function buildToolMetadata(registry) {
|
|
282
338
|
return Object.values(registry).map((tool) => {
|
|
283
339
|
const toolConfig = tool.config ?? {};
|
|
284
|
-
const
|
|
285
|
-
const
|
|
340
|
+
const rawTimeout = tool.timeout ?? toolConfig.timeout;
|
|
341
|
+
const rawRetries = tool.retries ?? toolConfig.retries;
|
|
342
|
+
const timeout = typeof rawTimeout === "number" && rawTimeout > 0 ? rawTimeout : 1e4;
|
|
343
|
+
const retries = typeof rawRetries === "number" && rawRetries >= 1 ? rawRetries : 1;
|
|
286
344
|
return {
|
|
287
345
|
name: tool.name,
|
|
288
346
|
displayName: tool.label || tool.name,
|
|
289
347
|
description: tool.description,
|
|
290
348
|
inputSchema: getJsonSchemaFromToolSchema(tool.inputSchema),
|
|
291
349
|
outputSchema: getJsonSchemaFromToolSchema(tool.outputSchema),
|
|
350
|
+
// Include timeout/retries at top-level for tools/list response (used by syncExecutableTools)
|
|
351
|
+
timeout,
|
|
352
|
+
retries,
|
|
292
353
|
config: {
|
|
293
354
|
timeout,
|
|
294
355
|
retries,
|
|
@@ -342,8 +403,9 @@ function createCallToolHandler(registry, state, onMaxRequests) {
|
|
|
342
403
|
}
|
|
343
404
|
}
|
|
344
405
|
const requestEnv = args.env ?? {};
|
|
406
|
+
const toolEnv = buildToolExecutionEnv(requestEnv);
|
|
345
407
|
const originalEnv = { ...process.env };
|
|
346
|
-
Object.assign(process.env,
|
|
408
|
+
Object.assign(process.env, toolEnv);
|
|
347
409
|
const invocation = args.invocation;
|
|
348
410
|
try {
|
|
349
411
|
const inputs = args.inputs ?? {};
|
|
@@ -356,7 +418,16 @@ function createCallToolHandler(registry, state, onMaxRequests) {
|
|
|
356
418
|
executionContext = {
|
|
357
419
|
trigger: "provision",
|
|
358
420
|
app,
|
|
359
|
-
env:
|
|
421
|
+
env: toolEnv,
|
|
422
|
+
mode: estimateMode ? "estimate" : "execute",
|
|
423
|
+
invocation,
|
|
424
|
+
log
|
|
425
|
+
};
|
|
426
|
+
} else if (trigger === "developer_page_action" || trigger === "developer_form_submit") {
|
|
427
|
+
executionContext = {
|
|
428
|
+
trigger,
|
|
429
|
+
app,
|
|
430
|
+
env: toolEnv,
|
|
360
431
|
mode: estimateMode ? "estimate" : "execute",
|
|
361
432
|
invocation,
|
|
362
433
|
log
|
|
@@ -365,7 +436,7 @@ function createCallToolHandler(registry, state, onMaxRequests) {
|
|
|
365
436
|
const workplace = rawContext.workplace;
|
|
366
437
|
const request = rawContext.request;
|
|
367
438
|
const appInstallationId = rawContext.appInstallationId;
|
|
368
|
-
const envVars =
|
|
439
|
+
const envVars = toolEnv;
|
|
369
440
|
const modeValue = estimateMode ? "estimate" : "execute";
|
|
370
441
|
if (trigger === "field_change") {
|
|
371
442
|
const field = rawContext.field;
|
|
@@ -385,8 +456,8 @@ function createCallToolHandler(registry, state, onMaxRequests) {
|
|
|
385
456
|
}
|
|
386
457
|
}
|
|
387
458
|
const requestConfig = {
|
|
388
|
-
baseUrl:
|
|
389
|
-
apiToken:
|
|
459
|
+
baseUrl: toolEnv.SKEDYUL_API_URL ?? "",
|
|
460
|
+
apiToken: toolEnv.SKEDYUL_API_TOKEN ?? ""
|
|
390
461
|
};
|
|
391
462
|
const functionResult = await runWithConfig(requestConfig, async () => {
|
|
392
463
|
return await runWithLogContext({ invocation }, async () => {
|
|
@@ -394,7 +465,17 @@ function createCallToolHandler(registry, state, onMaxRequests) {
|
|
|
394
465
|
});
|
|
395
466
|
});
|
|
396
467
|
const billing = normalizeBilling(functionResult.billing);
|
|
468
|
+
if ("success" in functionResult && functionResult.success === false) {
|
|
469
|
+
return {
|
|
470
|
+
success: false,
|
|
471
|
+
output: null,
|
|
472
|
+
billing,
|
|
473
|
+
error: "error" in functionResult ? functionResult.error : void 0,
|
|
474
|
+
effect: "effect" in functionResult ? functionResult.effect : void 0
|
|
475
|
+
};
|
|
476
|
+
}
|
|
397
477
|
return {
|
|
478
|
+
success: true,
|
|
398
479
|
output: functionResult.output,
|
|
399
480
|
billing,
|
|
400
481
|
meta: functionResult.meta ?? {
|
|
@@ -402,7 +483,9 @@ function createCallToolHandler(registry, state, onMaxRequests) {
|
|
|
402
483
|
message: "OK",
|
|
403
484
|
toolName
|
|
404
485
|
},
|
|
405
|
-
effect: functionResult.effect
|
|
486
|
+
effect: functionResult.effect,
|
|
487
|
+
dataBlocks: functionResult.dataBlocks,
|
|
488
|
+
cursor: functionResult.cursor
|
|
406
489
|
};
|
|
407
490
|
} catch (error) {
|
|
408
491
|
if (error instanceof AppAuthInvalidError) {
|
|
@@ -765,8 +848,9 @@ function serializeConfig(config) {
|
|
|
765
848
|
tools: registry ? Object.entries(registry).map(([key, tool]) => ({
|
|
766
849
|
name: tool.name || key,
|
|
767
850
|
description: tool.description,
|
|
768
|
-
timeout
|
|
769
|
-
|
|
851
|
+
// Read timeout/retries from top-level first, then fallback to config
|
|
852
|
+
timeout: tool.timeout ?? tool.config?.timeout,
|
|
853
|
+
retries: tool.retries ?? tool.config?.retries
|
|
770
854
|
})) : [],
|
|
771
855
|
webhooks: webhookRegistry ? Object.values(webhookRegistry).map((w) => ({
|
|
772
856
|
name: w.name,
|
|
@@ -1185,6 +1269,34 @@ function isMethodAllowed(webhookRegistry, handle, method) {
|
|
|
1185
1269
|
function getConfigFilePath() {
|
|
1186
1270
|
return process.env.LAMBDA_TASK_ROOT ? path.join(process.env.LAMBDA_TASK_ROOT, ".skedyul", "config.json") : ".skedyul/config.json";
|
|
1187
1271
|
}
|
|
1272
|
+
function findToolInRegistry(registry, toolName) {
|
|
1273
|
+
for (const [key, t] of Object.entries(registry)) {
|
|
1274
|
+
if (t.name === toolName || key === toolName) {
|
|
1275
|
+
return { toolKey: key, tool: t };
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
const inputParts = toolName.split(":");
|
|
1279
|
+
const inputShortName = inputParts[inputParts.length - 1];
|
|
1280
|
+
if (inputParts.length > 1) {
|
|
1281
|
+
for (const [key, t] of Object.entries(registry)) {
|
|
1282
|
+
if (t.name === inputShortName || key === inputShortName) {
|
|
1283
|
+
return { toolKey: key, tool: t };
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
const matches = [];
|
|
1288
|
+
for (const [key, t] of Object.entries(registry)) {
|
|
1289
|
+
const registryParts = t.name.split(":");
|
|
1290
|
+
const registryShortName = registryParts[registryParts.length - 1];
|
|
1291
|
+
if (registryShortName === toolName) {
|
|
1292
|
+
matches.push({ toolKey: key, tool: t });
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
if (matches.length === 1) {
|
|
1296
|
+
return matches[0];
|
|
1297
|
+
}
|
|
1298
|
+
return null;
|
|
1299
|
+
}
|
|
1188
1300
|
function handleHealthRoute(ctx) {
|
|
1189
1301
|
return {
|
|
1190
1302
|
status: 200,
|
|
@@ -1194,34 +1306,14 @@ function handleHealthRoute(ctx) {
|
|
|
1194
1306
|
function handleConfigRoute(ctx) {
|
|
1195
1307
|
const configFilePath = getConfigFilePath();
|
|
1196
1308
|
try {
|
|
1197
|
-
console.log(`[/config] Checking for config file at: ${configFilePath}`);
|
|
1198
1309
|
if (fs.existsSync(configFilePath)) {
|
|
1199
1310
|
const fileConfig = JSON.parse(fs.readFileSync(configFilePath, "utf-8"));
|
|
1200
|
-
console.log(
|
|
1201
|
-
`[/config] Loaded config from file: tools=${fileConfig.tools?.length ?? 0}, webhooks=${fileConfig.webhooks?.length ?? 0}`
|
|
1202
|
-
);
|
|
1203
|
-
console.log(
|
|
1204
|
-
`[/config] SENDING config with keys: ${Object.keys(fileConfig).join(", ")}`
|
|
1205
|
-
);
|
|
1206
|
-
console.log(
|
|
1207
|
-
`[/config] SENDING full config: ${JSON.stringify(fileConfig).substring(0, 2e3)}...`
|
|
1208
|
-
);
|
|
1209
1311
|
return { status: 200, body: fileConfig };
|
|
1210
1312
|
}
|
|
1211
|
-
console.log("[/config] Config file not found, falling back to runtime serialization");
|
|
1212
1313
|
} catch (err) {
|
|
1213
1314
|
console.warn("[/config] Failed to read config file, falling back to runtime serialization:", err);
|
|
1214
1315
|
}
|
|
1215
1316
|
const serialized = serializeConfig(ctx.config);
|
|
1216
|
-
console.log(
|
|
1217
|
-
`[/config] Runtime serialization: tools=${serialized.tools?.length ?? 0}, webhooks=${serialized.webhooks?.length ?? 0}`
|
|
1218
|
-
);
|
|
1219
|
-
console.log(
|
|
1220
|
-
`[/config] SENDING serialized config with keys: ${Object.keys(serialized).join(", ")}`
|
|
1221
|
-
);
|
|
1222
|
-
console.log(
|
|
1223
|
-
`[/config] SENDING full serialized config: ${JSON.stringify(serialized).substring(0, 2e3)}...`
|
|
1224
|
-
);
|
|
1225
1317
|
return { status: 200, body: serialized };
|
|
1226
1318
|
}
|
|
1227
1319
|
async function handleCoreRoute(req, ctx) {
|
|
@@ -1276,16 +1368,8 @@ async function handleEstimateRoute(req, ctx) {
|
|
|
1276
1368
|
try {
|
|
1277
1369
|
const toolName = estimateBody.name;
|
|
1278
1370
|
const toolArgs = estimateBody.inputs ?? {};
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
for (const [key, t] of Object.entries(ctx.registry)) {
|
|
1282
|
-
if (t.name === toolName || key === toolName) {
|
|
1283
|
-
toolKey = key;
|
|
1284
|
-
tool = t;
|
|
1285
|
-
break;
|
|
1286
|
-
}
|
|
1287
|
-
}
|
|
1288
|
-
if (!tool || !toolKey) {
|
|
1371
|
+
const found = findToolInRegistry(ctx.registry, toolName);
|
|
1372
|
+
if (!found) {
|
|
1289
1373
|
return {
|
|
1290
1374
|
status: 400,
|
|
1291
1375
|
body: {
|
|
@@ -1296,6 +1380,7 @@ async function handleEstimateRoute(req, ctx) {
|
|
|
1296
1380
|
}
|
|
1297
1381
|
};
|
|
1298
1382
|
}
|
|
1383
|
+
const { toolKey, tool } = found;
|
|
1299
1384
|
const inputSchema = getZodSchema(tool.inputSchema);
|
|
1300
1385
|
const validatedArgs = inputSchema ? inputSchema.parse(toolArgs) : toolArgs;
|
|
1301
1386
|
const estimateResponse = await ctx.callTool(toolKey, {
|
|
@@ -1458,35 +1543,13 @@ async function handleMcpRoute(req, ctx) {
|
|
|
1458
1543
|
async function handleMcpToolsCall(params, id, ctx) {
|
|
1459
1544
|
const toolName = params?.name;
|
|
1460
1545
|
const rawArgs = params?.arguments ?? {};
|
|
1461
|
-
console.log("[route-handlers /mcp] Received tools/call request:", JSON.stringify({
|
|
1462
|
-
toolName,
|
|
1463
|
-
hasArguments: !!params?.arguments,
|
|
1464
|
-
argumentKeys: rawArgs ? Object.keys(rawArgs) : [],
|
|
1465
|
-
hasEnv: "env" in rawArgs,
|
|
1466
|
-
envKeys: rawArgs.env ? Object.keys(rawArgs.env) : [],
|
|
1467
|
-
hasApiToken: !!rawArgs.env?.SKEDYUL_API_TOKEN
|
|
1468
|
-
}, null, 2));
|
|
1469
1546
|
const hasSkedyulFormat = "inputs" in rawArgs || "env" in rawArgs || "context" in rawArgs || "invocation" in rawArgs;
|
|
1470
1547
|
const toolInputs = hasSkedyulFormat ? rawArgs.inputs ?? {} : rawArgs;
|
|
1471
1548
|
const toolContext = hasSkedyulFormat ? rawArgs.context : void 0;
|
|
1472
1549
|
const toolEnv = hasSkedyulFormat ? rawArgs.env : void 0;
|
|
1473
1550
|
const toolInvocation = hasSkedyulFormat ? rawArgs.invocation : void 0;
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
hasToolEnv: !!toolEnv,
|
|
1477
|
-
toolEnvKeys: toolEnv ? Object.keys(toolEnv) : [],
|
|
1478
|
-
hasApiToken: toolEnv?.SKEDYUL_API_TOKEN ? `yes (${toolEnv.SKEDYUL_API_TOKEN.length} chars)` : "no"
|
|
1479
|
-
}, null, 2));
|
|
1480
|
-
let toolKey = null;
|
|
1481
|
-
let tool = null;
|
|
1482
|
-
for (const [key, t] of Object.entries(ctx.registry)) {
|
|
1483
|
-
if (t.name === toolName || key === toolName) {
|
|
1484
|
-
toolKey = key;
|
|
1485
|
-
tool = t;
|
|
1486
|
-
break;
|
|
1487
|
-
}
|
|
1488
|
-
}
|
|
1489
|
-
if (!tool || !toolKey) {
|
|
1551
|
+
const found = findToolInRegistry(ctx.registry, toolName);
|
|
1552
|
+
if (!found) {
|
|
1490
1553
|
return {
|
|
1491
1554
|
status: 200,
|
|
1492
1555
|
body: {
|
|
@@ -1499,6 +1562,7 @@ async function handleMcpToolsCall(params, id, ctx) {
|
|
|
1499
1562
|
}
|
|
1500
1563
|
};
|
|
1501
1564
|
}
|
|
1565
|
+
const { toolKey, tool } = found;
|
|
1502
1566
|
try {
|
|
1503
1567
|
const inputSchema = getZodSchema(tool.inputSchema);
|
|
1504
1568
|
const outputSchema = getZodSchema(tool.outputSchema);
|
|
@@ -1511,68 +1575,45 @@ async function handleMcpToolsCall(params, id, ctx) {
|
|
|
1511
1575
|
invocation: toolInvocation
|
|
1512
1576
|
});
|
|
1513
1577
|
let result;
|
|
1514
|
-
const
|
|
1515
|
-
const isLegacyErrorFailure = "error" in toolResult && toolResult.error != null;
|
|
1516
|
-
const isLegacyMetaFailure = "meta" in toolResult && toolResult.meta != null && typeof toolResult.meta === "object" && "success" in toolResult.meta && toolResult.meta.success === false;
|
|
1517
|
-
const isFailure = isNewShapeFailure || isLegacyErrorFailure || isLegacyMetaFailure;
|
|
1578
|
+
const isFailure = isToolCallFailure(toolResult);
|
|
1518
1579
|
if (isFailure) {
|
|
1519
|
-
|
|
1520
|
-
if (isNewShapeFailure && "error" in toolResult) {
|
|
1521
|
-
errorOutput = {
|
|
1522
|
-
error: toolResult.error,
|
|
1523
|
-
retry: "retry" in toolResult ? toolResult.retry : void 0
|
|
1524
|
-
};
|
|
1525
|
-
} else if (isLegacyErrorFailure && "error" in toolResult) {
|
|
1526
|
-
errorOutput = { error: toolResult.error };
|
|
1527
|
-
} else if (isLegacyMetaFailure && "meta" in toolResult && toolResult.meta) {
|
|
1528
|
-
const meta = toolResult.meta;
|
|
1529
|
-
errorOutput = {
|
|
1530
|
-
error: {
|
|
1531
|
-
code: "TOOL_FAILED",
|
|
1532
|
-
message: meta.message ?? "Tool execution failed",
|
|
1533
|
-
category: "internal"
|
|
1534
|
-
}
|
|
1535
|
-
};
|
|
1536
|
-
} else {
|
|
1537
|
-
errorOutput = {
|
|
1538
|
-
error: {
|
|
1539
|
-
code: "TOOL_FAILED",
|
|
1540
|
-
message: "Tool execution failed",
|
|
1541
|
-
category: "internal"
|
|
1542
|
-
}
|
|
1543
|
-
};
|
|
1544
|
-
}
|
|
1580
|
+
const errorOutput = buildToolCallErrorOutput(toolResult);
|
|
1545
1581
|
result = {
|
|
1546
|
-
content: [{ type: "text", text:
|
|
1582
|
+
content: [{ type: "text", text: serializeMcpContentText(errorOutput) }],
|
|
1547
1583
|
structuredContent: hasOutputSchema ? void 0 : errorOutput,
|
|
1548
1584
|
isError: true,
|
|
1549
1585
|
billing: "billing" in toolResult ? toolResult.billing : void 0
|
|
1550
1586
|
};
|
|
1551
1587
|
} else {
|
|
1552
|
-
const
|
|
1588
|
+
const rawOutput = "output" in toolResult ? toolResult.output : null;
|
|
1589
|
+
const outputData = rawOutput ?? null;
|
|
1553
1590
|
const effect = "effect" in toolResult ? toolResult.effect : void 0;
|
|
1554
1591
|
const warnings = "warnings" in toolResult ? toolResult.warnings : void 0;
|
|
1555
1592
|
const pagination = "pagination" in toolResult ? toolResult.pagination : void 0;
|
|
1593
|
+
const cursor = "cursor" in toolResult ? toolResult.cursor : void 0;
|
|
1556
1594
|
let structuredContent;
|
|
1557
1595
|
if (outputData) {
|
|
1558
1596
|
structuredContent = {
|
|
1559
1597
|
...outputData,
|
|
1560
1598
|
__effect: effect,
|
|
1561
1599
|
__warnings: warnings,
|
|
1562
|
-
__pagination: pagination
|
|
1600
|
+
__pagination: pagination,
|
|
1601
|
+
__cursor: cursor
|
|
1563
1602
|
};
|
|
1564
|
-
} else if (effect || warnings || pagination) {
|
|
1603
|
+
} else if (effect || warnings || pagination || cursor) {
|
|
1565
1604
|
structuredContent = {
|
|
1566
1605
|
__effect: effect,
|
|
1567
1606
|
__warnings: warnings,
|
|
1568
|
-
__pagination: pagination
|
|
1607
|
+
__pagination: pagination,
|
|
1608
|
+
__cursor: cursor
|
|
1569
1609
|
};
|
|
1570
1610
|
} else if (hasOutputSchema) {
|
|
1571
1611
|
structuredContent = {};
|
|
1572
1612
|
}
|
|
1573
1613
|
result = {
|
|
1574
|
-
content: [{ type: "text", text:
|
|
1614
|
+
content: [{ type: "text", text: serializeMcpContentText(outputData) }],
|
|
1575
1615
|
structuredContent,
|
|
1616
|
+
cursor,
|
|
1576
1617
|
billing: "billing" in toolResult ? toolResult.billing : void 0
|
|
1577
1618
|
};
|
|
1578
1619
|
}
|
|
@@ -1598,6 +1639,94 @@ async function handleMcpToolsCall(params, id, ctx) {
|
|
|
1598
1639
|
};
|
|
1599
1640
|
}
|
|
1600
1641
|
}
|
|
1642
|
+
async function handleMcpBatchRoute(req, ctx) {
|
|
1643
|
+
const parseResult = parseJsonBody(req);
|
|
1644
|
+
if (!parseResult.success) {
|
|
1645
|
+
return parseResult.error;
|
|
1646
|
+
}
|
|
1647
|
+
const { calls, deadline, env: batchEnv, context: batchContext } = parseResult.data;
|
|
1648
|
+
if (!calls || !Array.isArray(calls) || calls.length === 0) {
|
|
1649
|
+
return {
|
|
1650
|
+
status: 400,
|
|
1651
|
+
body: {
|
|
1652
|
+
error: {
|
|
1653
|
+
code: -32602,
|
|
1654
|
+
message: "Missing or invalid calls array"
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
};
|
|
1658
|
+
}
|
|
1659
|
+
const perCallTimeoutMs = deadline ? Math.min(deadline, 25e3) : 25e3;
|
|
1660
|
+
const executeWithTimeout = async (call, timeoutMs) => {
|
|
1661
|
+
return new Promise(async (resolve) => {
|
|
1662
|
+
const timeoutHandle = setTimeout(() => {
|
|
1663
|
+
resolve({
|
|
1664
|
+
id: call.id,
|
|
1665
|
+
success: false,
|
|
1666
|
+
error: "Timeout",
|
|
1667
|
+
timedOut: true
|
|
1668
|
+
});
|
|
1669
|
+
}, timeoutMs);
|
|
1670
|
+
try {
|
|
1671
|
+
const toolName = call.name;
|
|
1672
|
+
const toolArgs = call.arguments ?? {};
|
|
1673
|
+
const found = findToolInRegistry(ctx.registry, toolName);
|
|
1674
|
+
if (!found) {
|
|
1675
|
+
clearTimeout(timeoutHandle);
|
|
1676
|
+
resolve({
|
|
1677
|
+
id: call.id,
|
|
1678
|
+
success: false,
|
|
1679
|
+
error: `Tool "${toolName}" not found`
|
|
1680
|
+
});
|
|
1681
|
+
return;
|
|
1682
|
+
}
|
|
1683
|
+
const { toolKey, tool } = found;
|
|
1684
|
+
const inputSchema = getZodSchema(tool.inputSchema);
|
|
1685
|
+
const validatedInputs = inputSchema ? inputSchema.parse(toolArgs) : toolArgs;
|
|
1686
|
+
const toolResult = await ctx.callTool(toolKey, {
|
|
1687
|
+
inputs: validatedInputs,
|
|
1688
|
+
context: batchContext,
|
|
1689
|
+
env: batchEnv
|
|
1690
|
+
});
|
|
1691
|
+
clearTimeout(timeoutHandle);
|
|
1692
|
+
const isFailure = "success" in toolResult && toolResult.success === false || "error" in toolResult && toolResult.error != null;
|
|
1693
|
+
if (isFailure) {
|
|
1694
|
+
resolve({
|
|
1695
|
+
id: call.id,
|
|
1696
|
+
success: false,
|
|
1697
|
+
error: "error" in toolResult && toolResult.error ? typeof toolResult.error === "string" ? toolResult.error : JSON.stringify(toolResult.error) : "Tool execution failed"
|
|
1698
|
+
});
|
|
1699
|
+
} else {
|
|
1700
|
+
resolve({
|
|
1701
|
+
id: call.id,
|
|
1702
|
+
success: true,
|
|
1703
|
+
result: "output" in toolResult ? toolResult.output : toolResult
|
|
1704
|
+
});
|
|
1705
|
+
}
|
|
1706
|
+
} catch (err) {
|
|
1707
|
+
clearTimeout(timeoutHandle);
|
|
1708
|
+
resolve({
|
|
1709
|
+
id: call.id,
|
|
1710
|
+
success: false,
|
|
1711
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1712
|
+
});
|
|
1713
|
+
}
|
|
1714
|
+
});
|
|
1715
|
+
};
|
|
1716
|
+
const results = await Promise.all(
|
|
1717
|
+
calls.map((call) => executeWithTimeout(call, perCallTimeoutMs))
|
|
1718
|
+
);
|
|
1719
|
+
return {
|
|
1720
|
+
status: 200,
|
|
1721
|
+
body: {
|
|
1722
|
+
results,
|
|
1723
|
+
totalCalls: calls.length,
|
|
1724
|
+
successCount: results.filter((r) => r.success).length,
|
|
1725
|
+
failedCount: results.filter((r) => !r.success).length,
|
|
1726
|
+
timedOutCount: results.filter((r) => r.timedOut).length
|
|
1727
|
+
}
|
|
1728
|
+
};
|
|
1729
|
+
}
|
|
1601
1730
|
function createNotFoundResponse() {
|
|
1602
1731
|
return {
|
|
1603
1732
|
status: 404,
|
|
@@ -1671,6 +1800,9 @@ async function routeRequest(req, ctx) {
|
|
|
1671
1800
|
if (req.path === "/mcp" && req.method === "POST") {
|
|
1672
1801
|
return handleMcpRoute(req, ctx);
|
|
1673
1802
|
}
|
|
1803
|
+
if (req.path === "/mcp/batch" && req.method === "POST") {
|
|
1804
|
+
return handleMcpBatchRoute(req, ctx);
|
|
1805
|
+
}
|
|
1674
1806
|
return createNotFoundResponse();
|
|
1675
1807
|
} catch (err) {
|
|
1676
1808
|
return createErrorResponse(err);
|
|
@@ -1767,30 +1899,22 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
|
|
|
1767
1899
|
httpServer.once("error", reject);
|
|
1768
1900
|
});
|
|
1769
1901
|
},
|
|
1902
|
+
async close() {
|
|
1903
|
+
return new Promise((resolve, reject) => {
|
|
1904
|
+
const closable = httpServer;
|
|
1905
|
+
closable.closeAllConnections?.();
|
|
1906
|
+
httpServer.close((err) => {
|
|
1907
|
+
if (err) reject(err);
|
|
1908
|
+
else resolve();
|
|
1909
|
+
});
|
|
1910
|
+
});
|
|
1911
|
+
},
|
|
1770
1912
|
getHealthStatus: () => state.getHealthStatus()
|
|
1771
1913
|
};
|
|
1772
1914
|
}
|
|
1773
1915
|
async function handleMcpWithSdkTransport(req, res, url, ctx, mcpServer) {
|
|
1774
1916
|
try {
|
|
1775
1917
|
const body = await parseJSONBody(req);
|
|
1776
|
-
if (body?.method === "tools/call") {
|
|
1777
|
-
console.log(
|
|
1778
|
-
"[dedicated.ts /mcp] Received tools/call request:",
|
|
1779
|
-
JSON.stringify(
|
|
1780
|
-
{
|
|
1781
|
-
method: body.method,
|
|
1782
|
-
toolName: body.params?.name,
|
|
1783
|
-
hasArguments: !!body.params?.arguments,
|
|
1784
|
-
argumentKeys: body.params?.arguments ? Object.keys(body.params.arguments) : [],
|
|
1785
|
-
hasEnv: !!body.params?.arguments?.env,
|
|
1786
|
-
envKeys: body.params?.arguments?.env ? Object.keys(body.params.arguments.env) : [],
|
|
1787
|
-
hasApiToken: !!body.params?.arguments?.env?.SKEDYUL_API_TOKEN
|
|
1788
|
-
},
|
|
1789
|
-
null,
|
|
1790
|
-
2
|
|
1791
|
-
)
|
|
1792
|
-
);
|
|
1793
|
-
}
|
|
1794
1918
|
if (body?.method === "tools/list") {
|
|
1795
1919
|
sendJSON(res, 200, {
|
|
1796
1920
|
jsonrpc: "2.0",
|
|
@@ -1865,65 +1989,46 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
|
|
|
1865
1989
|
}
|
|
1866
1990
|
|
|
1867
1991
|
// src/server/index.ts
|
|
1868
|
-
console.log("[skedyul-node/server] Module loading - imports done");
|
|
1869
|
-
console.log("[skedyul-node/server] All imports complete");
|
|
1870
|
-
console.log("[skedyul-node/server] Installing context logger...");
|
|
1871
1992
|
installContextLogger();
|
|
1872
|
-
console.log("[skedyul-node/server] Context logger installed");
|
|
1873
1993
|
function createSkedyulServer(config) {
|
|
1874
|
-
console.log("[createSkedyulServer] Step 1: mergeRuntimeEnv()");
|
|
1875
1994
|
mergeRuntimeEnv();
|
|
1876
1995
|
const registry = config.tools;
|
|
1877
1996
|
const webhookRegistry = config.webhooks;
|
|
1878
|
-
console.log("[createSkedyulServer] Step 2: coreApi setup");
|
|
1879
1997
|
if (config.coreApi?.service) {
|
|
1880
1998
|
coreApiService.register(config.coreApi.service);
|
|
1881
1999
|
if (config.coreApi.webhookHandler) {
|
|
1882
2000
|
coreApiService.setWebhookHandler(config.coreApi.webhookHandler);
|
|
1883
2001
|
}
|
|
1884
2002
|
}
|
|
1885
|
-
console.log("[createSkedyulServer] Step 3: buildToolMetadata()");
|
|
1886
2003
|
const tools = buildToolMetadata(registry);
|
|
1887
|
-
console.log("[createSkedyulServer] Step 3 done, tools:", tools.length);
|
|
1888
2004
|
const toolNames = Object.values(registry).map((tool) => tool.name);
|
|
1889
2005
|
const runtimeLabel = config.computeLayer ?? "serverless";
|
|
1890
2006
|
const maxRequests = config.maxRequests ?? parseNumberEnv(process.env.MCP_MAX_REQUESTS) ?? null;
|
|
1891
2007
|
const ttlExtendSeconds = config.ttlExtendSeconds ?? parseNumberEnv(process.env.MCP_TTL_EXTEND) ?? 3600;
|
|
1892
|
-
console.log("[createSkedyulServer] Step 4: createRequestState()");
|
|
1893
2008
|
const state = createRequestState(
|
|
1894
2009
|
maxRequests,
|
|
1895
2010
|
ttlExtendSeconds,
|
|
1896
2011
|
runtimeLabel,
|
|
1897
2012
|
toolNames
|
|
1898
2013
|
);
|
|
1899
|
-
console.log("[createSkedyulServer] Step 4 done");
|
|
1900
|
-
console.log("[createSkedyulServer] Step 5: new McpServer()");
|
|
1901
2014
|
const mcpServer = new McpServer({
|
|
1902
2015
|
name: config.name,
|
|
1903
2016
|
version: config.version ?? "0.0.0"
|
|
1904
2017
|
});
|
|
1905
|
-
console.log("[createSkedyulServer] Step 5 done");
|
|
1906
2018
|
const dedicatedShutdown = () => {
|
|
1907
2019
|
console.log("Max requests reached, shutting down...");
|
|
1908
2020
|
setTimeout(() => process.exit(0), 1e3);
|
|
1909
2021
|
};
|
|
1910
|
-
console.log("[createSkedyulServer] Step 6: createCallToolHandler()");
|
|
1911
2022
|
const callTool = createCallToolHandler(
|
|
1912
2023
|
registry,
|
|
1913
2024
|
state,
|
|
1914
2025
|
config.computeLayer === "dedicated" ? dedicatedShutdown : void 0
|
|
1915
2026
|
);
|
|
1916
|
-
console.log("[createSkedyulServer] Step 6 done");
|
|
1917
|
-
console.log("[createSkedyulServer] Step 7: Registering tools...");
|
|
1918
2027
|
for (const [toolKey, tool] of Object.entries(registry)) {
|
|
1919
|
-
console.log(`[createSkedyulServer] Registering tool: ${toolKey}`);
|
|
1920
2028
|
const toolName = tool.name || toolKey;
|
|
1921
2029
|
const toolDisplayName = tool.label || toolName;
|
|
1922
|
-
console.log(`[createSkedyulServer] Getting input schema for ${toolKey}`);
|
|
1923
2030
|
const inputZodSchema = getZodSchema(tool.inputSchema);
|
|
1924
|
-
console.log(`[createSkedyulServer] Getting output schema for ${toolKey}`);
|
|
1925
2031
|
const outputZodSchema = getZodSchema(tool.outputSchema);
|
|
1926
|
-
console.log(`[createSkedyulServer] Creating wrapped schema for ${toolKey}`);
|
|
1927
2032
|
const wrappedInputSchema = z3.object({
|
|
1928
2033
|
inputs: inputZodSchema ?? z3.record(z3.string(), z3.unknown()).optional(),
|
|
1929
2034
|
context: z3.record(z3.string(), z3.unknown()).optional(),
|
|
@@ -1931,7 +2036,6 @@ function createSkedyulServer(config) {
|
|
|
1931
2036
|
invocation: z3.record(z3.string(), z3.unknown()).optional(),
|
|
1932
2037
|
estimate: z3.boolean().optional()
|
|
1933
2038
|
}).passthrough();
|
|
1934
|
-
console.log(`[createSkedyulServer] Calling mcpServer.registerTool for ${toolKey}`);
|
|
1935
2039
|
mcpServer.registerTool(
|
|
1936
2040
|
toolName,
|
|
1937
2041
|
{
|
|
@@ -1943,20 +2047,11 @@ function createSkedyulServer(config) {
|
|
|
1943
2047
|
// outputSchema: outputZodSchema,
|
|
1944
2048
|
},
|
|
1945
2049
|
async (args) => {
|
|
1946
|
-
console.log(`[mcpServer.registerTool] Tool ${toolName} received raw args:`, JSON.stringify(args, null, 2));
|
|
1947
2050
|
const rawArgs = args;
|
|
1948
2051
|
const toolInputs = rawArgs.inputs ?? {};
|
|
1949
2052
|
const toolContext = rawArgs.context;
|
|
1950
2053
|
const toolEnv = rawArgs.env;
|
|
1951
2054
|
const toolInvocation = rawArgs.invocation;
|
|
1952
|
-
console.log(`[mcpServer.registerTool] Tool ${toolName} extracted:`, {
|
|
1953
|
-
hasInputs: !!rawArgs.inputs,
|
|
1954
|
-
hasContext: !!rawArgs.context,
|
|
1955
|
-
hasEnv: !!rawArgs.env,
|
|
1956
|
-
hasInvocation: !!rawArgs.invocation,
|
|
1957
|
-
envKeys: toolEnv ? Object.keys(toolEnv) : [],
|
|
1958
|
-
hasApiToken: toolEnv?.SKEDYUL_API_TOKEN ? `yes (${toolEnv.SKEDYUL_API_TOKEN.length} chars)` : "no"
|
|
1959
|
-
});
|
|
1960
2055
|
let validatedInputs = toolInputs;
|
|
1961
2056
|
if (inputZodSchema) {
|
|
1962
2057
|
try {
|
|
@@ -1990,10 +2085,10 @@ function createSkedyulServer(config) {
|
|
|
1990
2085
|
invocation: toolInvocation
|
|
1991
2086
|
});
|
|
1992
2087
|
const hasOutputSchema = Boolean(outputZodSchema);
|
|
1993
|
-
if (result
|
|
1994
|
-
const errorOutput =
|
|
2088
|
+
if (isToolCallFailure(result)) {
|
|
2089
|
+
const errorOutput = buildToolCallErrorOutput(result);
|
|
1995
2090
|
return {
|
|
1996
|
-
content: [{ type: "text", text:
|
|
2091
|
+
content: [{ type: "text", text: serializeMcpContentText(errorOutput) }],
|
|
1997
2092
|
// Don't provide structuredContent for error responses when tool has outputSchema
|
|
1998
2093
|
// because the error response won't match the success schema and MCP SDK validates it
|
|
1999
2094
|
structuredContent: hasOutputSchema ? void 0 : errorOutput,
|
|
@@ -2001,28 +2096,34 @@ function createSkedyulServer(config) {
|
|
|
2001
2096
|
billing: result.billing
|
|
2002
2097
|
};
|
|
2003
2098
|
}
|
|
2004
|
-
const
|
|
2099
|
+
const rawOutput = "output" in result ? result.output : null;
|
|
2100
|
+
const outputData = rawOutput ?? null;
|
|
2101
|
+
const dataBlocks = result.dataBlocks;
|
|
2005
2102
|
let structuredContent;
|
|
2006
2103
|
if (outputData) {
|
|
2007
|
-
structuredContent = {
|
|
2008
|
-
|
|
2009
|
-
|
|
2104
|
+
structuredContent = {
|
|
2105
|
+
...outputData,
|
|
2106
|
+
__effect: result.effect,
|
|
2107
|
+
__dataBlocks: dataBlocks
|
|
2108
|
+
};
|
|
2109
|
+
} else if (result.effect || dataBlocks) {
|
|
2110
|
+
structuredContent = {
|
|
2111
|
+
__effect: result.effect,
|
|
2112
|
+
__dataBlocks: dataBlocks
|
|
2113
|
+
};
|
|
2010
2114
|
} else if (hasOutputSchema) {
|
|
2011
2115
|
structuredContent = {};
|
|
2012
2116
|
}
|
|
2013
2117
|
return {
|
|
2014
|
-
|
|
2118
|
+
// Ensure text is always a string - JSON.stringify(undefined) returns undefined, not a string
|
|
2119
|
+
content: [{ type: "text", text: serializeMcpContentText(outputData) }],
|
|
2015
2120
|
structuredContent,
|
|
2016
2121
|
billing: result.billing
|
|
2017
2122
|
};
|
|
2018
2123
|
}
|
|
2019
2124
|
);
|
|
2020
|
-
console.log(`[createSkedyulServer] Tool ${toolKey} registered successfully`);
|
|
2021
2125
|
}
|
|
2022
|
-
console.log("[createSkedyulServer] Step 7 done - all tools registered");
|
|
2023
|
-
console.log("[createSkedyulServer] Step 8: Creating server instance");
|
|
2024
2126
|
if (config.computeLayer === "dedicated") {
|
|
2025
|
-
console.log("[createSkedyulServer] Creating dedicated instance");
|
|
2026
2127
|
return createDedicatedServerInstance(
|
|
2027
2128
|
config,
|
|
2028
2129
|
tools,
|
|
@@ -2031,10 +2132,7 @@ function createSkedyulServer(config) {
|
|
|
2031
2132
|
mcpServer
|
|
2032
2133
|
);
|
|
2033
2134
|
}
|
|
2034
|
-
|
|
2035
|
-
const serverlessInstance = createServerlessInstance(config, tools, callTool, state, mcpServer);
|
|
2036
|
-
console.log("[createSkedyulServer] Serverless instance created successfully");
|
|
2037
|
-
return serverlessInstance;
|
|
2135
|
+
return createServerlessInstance(config, tools, callTool, state, mcpServer);
|
|
2038
2136
|
}
|
|
2039
2137
|
var server = {
|
|
2040
2138
|
create: createSkedyulServer
|