skedyul 0.1.26 → 0.1.27
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/server.js +147 -13
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -401,6 +401,110 @@ function getListeningPort(config) {
|
|
|
401
401
|
}
|
|
402
402
|
return config.defaultPort ?? 3000;
|
|
403
403
|
}
|
|
404
|
+
/**
|
|
405
|
+
* Prints a styled startup log showing server configuration
|
|
406
|
+
*/
|
|
407
|
+
function printStartupLog(config, tools, webhookRegistry, port) {
|
|
408
|
+
const webhookCount = webhookRegistry ? Object.keys(webhookRegistry).length : 0;
|
|
409
|
+
const webhookNames = webhookRegistry ? Object.keys(webhookRegistry) : [];
|
|
410
|
+
const maxRequests = config.maxRequests ??
|
|
411
|
+
parseNumberEnv(process.env.MCP_MAX_REQUESTS) ??
|
|
412
|
+
null;
|
|
413
|
+
const ttlExtendSeconds = config.ttlExtendSeconds ??
|
|
414
|
+
parseNumberEnv(process.env.MCP_TTL_EXTEND) ??
|
|
415
|
+
3600;
|
|
416
|
+
const executableId = process.env.SKEDYUL_EXECUTABLE_ID || 'local';
|
|
417
|
+
const divider = '═'.repeat(70);
|
|
418
|
+
const thinDivider = '─'.repeat(70);
|
|
419
|
+
// eslint-disable-next-line no-console
|
|
420
|
+
console.log('');
|
|
421
|
+
// eslint-disable-next-line no-console
|
|
422
|
+
console.log(`╔${divider}╗`);
|
|
423
|
+
// eslint-disable-next-line no-console
|
|
424
|
+
console.log(`║ 🚀 Skedyul MCP Server Starting ║`);
|
|
425
|
+
// eslint-disable-next-line no-console
|
|
426
|
+
console.log(`╠${divider}╣`);
|
|
427
|
+
// eslint-disable-next-line no-console
|
|
428
|
+
console.log(`║ ║`);
|
|
429
|
+
// eslint-disable-next-line no-console
|
|
430
|
+
console.log(`║ 📦 Server: ${padEnd(config.metadata.name, 49)}║`);
|
|
431
|
+
// eslint-disable-next-line no-console
|
|
432
|
+
console.log(`║ 🏷️ Version: ${padEnd(config.metadata.version, 49)}║`);
|
|
433
|
+
// eslint-disable-next-line no-console
|
|
434
|
+
console.log(`║ ⚡ Compute: ${padEnd(config.computeLayer, 49)}║`);
|
|
435
|
+
if (port) {
|
|
436
|
+
// eslint-disable-next-line no-console
|
|
437
|
+
console.log(`║ 🌐 Port: ${padEnd(String(port), 49)}║`);
|
|
438
|
+
}
|
|
439
|
+
// eslint-disable-next-line no-console
|
|
440
|
+
console.log(`║ 🔑 Executable: ${padEnd(executableId, 49)}║`);
|
|
441
|
+
// eslint-disable-next-line no-console
|
|
442
|
+
console.log(`║ ║`);
|
|
443
|
+
// eslint-disable-next-line no-console
|
|
444
|
+
console.log(`╟${thinDivider}╢`);
|
|
445
|
+
// eslint-disable-next-line no-console
|
|
446
|
+
console.log(`║ ║`);
|
|
447
|
+
// eslint-disable-next-line no-console
|
|
448
|
+
console.log(`║ 🔧 Tools (${tools.length}): ║`);
|
|
449
|
+
// List tools (max 10, then show "and X more...")
|
|
450
|
+
const maxToolsToShow = 10;
|
|
451
|
+
const toolsToShow = tools.slice(0, maxToolsToShow);
|
|
452
|
+
for (const tool of toolsToShow) {
|
|
453
|
+
// eslint-disable-next-line no-console
|
|
454
|
+
console.log(`║ • ${padEnd(tool.name, 61)}║`);
|
|
455
|
+
}
|
|
456
|
+
if (tools.length > maxToolsToShow) {
|
|
457
|
+
// eslint-disable-next-line no-console
|
|
458
|
+
console.log(`║ ... and ${tools.length - maxToolsToShow} more ║`);
|
|
459
|
+
}
|
|
460
|
+
if (webhookCount > 0) {
|
|
461
|
+
// eslint-disable-next-line no-console
|
|
462
|
+
console.log(`║ ║`);
|
|
463
|
+
// eslint-disable-next-line no-console
|
|
464
|
+
console.log(`║ 🪝 Webhooks (${webhookCount}): ║`);
|
|
465
|
+
const maxWebhooksToShow = 5;
|
|
466
|
+
const webhooksToShow = webhookNames.slice(0, maxWebhooksToShow);
|
|
467
|
+
for (const name of webhooksToShow) {
|
|
468
|
+
// eslint-disable-next-line no-console
|
|
469
|
+
console.log(`║ • /webhooks/${padEnd(name, 51)}║`);
|
|
470
|
+
}
|
|
471
|
+
if (webhookCount > maxWebhooksToShow) {
|
|
472
|
+
// eslint-disable-next-line no-console
|
|
473
|
+
console.log(`║ ... and ${webhookCount - maxWebhooksToShow} more ║`);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
// eslint-disable-next-line no-console
|
|
477
|
+
console.log(`║ ║`);
|
|
478
|
+
// eslint-disable-next-line no-console
|
|
479
|
+
console.log(`╟${thinDivider}╢`);
|
|
480
|
+
// eslint-disable-next-line no-console
|
|
481
|
+
console.log(`║ ║`);
|
|
482
|
+
// eslint-disable-next-line no-console
|
|
483
|
+
console.log(`║ ⚙️ Configuration: ║`);
|
|
484
|
+
// eslint-disable-next-line no-console
|
|
485
|
+
console.log(`║ Max Requests: ${padEnd(maxRequests !== null ? String(maxRequests) : 'unlimited', 46)}║`);
|
|
486
|
+
// eslint-disable-next-line no-console
|
|
487
|
+
console.log(`║ TTL Extend: ${padEnd(`${ttlExtendSeconds}s`, 46)}║`);
|
|
488
|
+
// eslint-disable-next-line no-console
|
|
489
|
+
console.log(`║ ║`);
|
|
490
|
+
// eslint-disable-next-line no-console
|
|
491
|
+
console.log(`╟${thinDivider}╢`);
|
|
492
|
+
// eslint-disable-next-line no-console
|
|
493
|
+
console.log(`║ ✅ Ready at ${padEnd(new Date().toISOString(), 55)}║`);
|
|
494
|
+
// eslint-disable-next-line no-console
|
|
495
|
+
console.log(`╚${divider}╝`);
|
|
496
|
+
// eslint-disable-next-line no-console
|
|
497
|
+
console.log('');
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Pad a string to the right with spaces
|
|
501
|
+
*/
|
|
502
|
+
function padEnd(str, length) {
|
|
503
|
+
if (str.length >= length) {
|
|
504
|
+
return str.slice(0, length);
|
|
505
|
+
}
|
|
506
|
+
return str + ' '.repeat(length - str.length);
|
|
507
|
+
}
|
|
404
508
|
function createSkedyulServer(config, registry, webhookRegistry) {
|
|
405
509
|
mergeRuntimeEnv();
|
|
406
510
|
if (config.coreApi?.service) {
|
|
@@ -678,15 +782,32 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
|
|
|
678
782
|
return;
|
|
679
783
|
}
|
|
680
784
|
if (pathname === '/mcp' && req.method === 'POST') {
|
|
681
|
-
const transport = new streamableHttp_js_1.StreamableHTTPServerTransport({
|
|
682
|
-
sessionIdGenerator: undefined,
|
|
683
|
-
enableJsonResponse: true,
|
|
684
|
-
});
|
|
685
|
-
res.on('close', () => {
|
|
686
|
-
transport.close();
|
|
687
|
-
});
|
|
688
785
|
try {
|
|
689
786
|
const body = await parseJSONBody(req);
|
|
787
|
+
// Handle webhooks/list before passing to MCP SDK transport
|
|
788
|
+
if (body?.method === 'webhooks/list') {
|
|
789
|
+
const webhooks = webhookRegistry
|
|
790
|
+
? Object.values(webhookRegistry).map((w) => ({
|
|
791
|
+
name: w.name,
|
|
792
|
+
description: w.description,
|
|
793
|
+
methods: w.methods ?? ['POST'],
|
|
794
|
+
}))
|
|
795
|
+
: [];
|
|
796
|
+
sendJSON(res, 200, {
|
|
797
|
+
jsonrpc: '2.0',
|
|
798
|
+
id: body.id ?? null,
|
|
799
|
+
result: { webhooks },
|
|
800
|
+
});
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
// Pass to MCP SDK transport for standard MCP methods
|
|
804
|
+
const transport = new streamableHttp_js_1.StreamableHTTPServerTransport({
|
|
805
|
+
sessionIdGenerator: undefined,
|
|
806
|
+
enableJsonResponse: true,
|
|
807
|
+
});
|
|
808
|
+
res.on('close', () => {
|
|
809
|
+
transport.close();
|
|
810
|
+
});
|
|
690
811
|
await mcpServer.connect(transport);
|
|
691
812
|
await transport.handleRequest(req, res, body);
|
|
692
813
|
}
|
|
@@ -727,12 +848,7 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
|
|
|
727
848
|
const finalPort = listenPort ?? port;
|
|
728
849
|
return new Promise((resolve, reject) => {
|
|
729
850
|
httpServer.listen(finalPort, () => {
|
|
730
|
-
|
|
731
|
-
console.log(`MCP Server running on port ${finalPort}`);
|
|
732
|
-
// eslint-disable-next-line no-console
|
|
733
|
-
console.log(`Registry loaded with ${tools.length} tools: ${tools
|
|
734
|
-
.map((tool) => tool.name)
|
|
735
|
-
.join(', ')}`);
|
|
851
|
+
printStartupLog(config, tools, webhookRegistry, finalPort);
|
|
736
852
|
resolve();
|
|
737
853
|
});
|
|
738
854
|
httpServer.once('error', reject);
|
|
@@ -743,8 +859,15 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
|
|
|
743
859
|
}
|
|
744
860
|
function createServerlessInstance(config, tools, callTool, state, mcpServer, registry, webhookRegistry) {
|
|
745
861
|
const headers = getDefaultHeaders(config.cors);
|
|
862
|
+
// Print startup log once on cold start
|
|
863
|
+
let hasLoggedStartup = false;
|
|
746
864
|
return {
|
|
747
865
|
async handler(event) {
|
|
866
|
+
// Log startup info on first invocation (cold start)
|
|
867
|
+
if (!hasLoggedStartup) {
|
|
868
|
+
printStartupLog(config, tools, webhookRegistry);
|
|
869
|
+
hasLoggedStartup = true;
|
|
870
|
+
}
|
|
748
871
|
try {
|
|
749
872
|
const path = event.path;
|
|
750
873
|
const method = event.httpMethod;
|
|
@@ -1039,6 +1162,17 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer, reg
|
|
|
1039
1162
|
}, headers);
|
|
1040
1163
|
}
|
|
1041
1164
|
}
|
|
1165
|
+
else if (rpcMethod === 'webhooks/list') {
|
|
1166
|
+
// Return registered webhooks with their metadata
|
|
1167
|
+
const webhooks = webhookRegistry
|
|
1168
|
+
? Object.values(webhookRegistry).map((w) => ({
|
|
1169
|
+
name: w.name,
|
|
1170
|
+
description: w.description,
|
|
1171
|
+
methods: w.methods ?? ['POST'],
|
|
1172
|
+
}))
|
|
1173
|
+
: [];
|
|
1174
|
+
result = { webhooks };
|
|
1175
|
+
}
|
|
1042
1176
|
else {
|
|
1043
1177
|
return createResponse(200, {
|
|
1044
1178
|
jsonrpc: '2.0',
|