skedyul 0.1.0 → 0.1.1
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 +114 -77
- package/dist/types.d.ts +10 -1
- package/package.json +3 -2
package/dist/server.js
CHANGED
|
@@ -6,8 +6,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.server = void 0;
|
|
7
7
|
exports.createSkedyulServer = createSkedyulServer;
|
|
8
8
|
const http_1 = __importDefault(require("http"));
|
|
9
|
-
const
|
|
10
|
-
const
|
|
9
|
+
const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
10
|
+
const streamableHttp_js_1 = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
|
|
11
11
|
const service_1 = require("./core/service");
|
|
12
12
|
function normalizeBilling(billing) {
|
|
13
13
|
if (!billing || typeof billing.credits !== 'number') {
|
|
@@ -181,9 +181,9 @@ async function handleCoreMethod(method, params) {
|
|
|
181
181
|
};
|
|
182
182
|
}
|
|
183
183
|
function buildToolMetadata(registry) {
|
|
184
|
-
return Object.
|
|
185
|
-
name,
|
|
186
|
-
description:
|
|
184
|
+
return Object.values(registry).map((tool) => ({
|
|
185
|
+
name: tool.name,
|
|
186
|
+
description: tool.description,
|
|
187
187
|
inputSchema: {
|
|
188
188
|
type: 'object',
|
|
189
189
|
properties: {
|
|
@@ -224,13 +224,14 @@ function createRequestState(maxRequests, ttlExtendSeconds, runtimeLabel, toolNam
|
|
|
224
224
|
function createCallToolHandler(registry, state, onMaxRequests) {
|
|
225
225
|
return async function callTool(nameRaw, argsRaw) {
|
|
226
226
|
const toolName = String(nameRaw);
|
|
227
|
-
const
|
|
228
|
-
if (!
|
|
227
|
+
const tool = registry[toolName];
|
|
228
|
+
if (!tool) {
|
|
229
229
|
throw new Error(`Tool "${toolName}" not found in registry`);
|
|
230
230
|
}
|
|
231
|
-
if (typeof
|
|
232
|
-
throw new Error(`
|
|
231
|
+
if (!tool.handler || typeof tool.handler !== 'function') {
|
|
232
|
+
throw new Error(`Tool "${toolName}" handler is not a function`);
|
|
233
233
|
}
|
|
234
|
+
const fn = tool.handler;
|
|
234
235
|
const args = (argsRaw ?? {});
|
|
235
236
|
const estimateMode = args.estimate === true;
|
|
236
237
|
if (!estimateMode) {
|
|
@@ -333,7 +334,7 @@ function createSkedyulServer(config, registry) {
|
|
|
333
334
|
}
|
|
334
335
|
}
|
|
335
336
|
const tools = buildToolMetadata(registry);
|
|
336
|
-
const toolNames =
|
|
337
|
+
const toolNames = Object.values(registry).map((tool) => tool.name);
|
|
337
338
|
const runtimeLabel = config.computeLayer;
|
|
338
339
|
const maxRequests = config.maxRequests ??
|
|
339
340
|
parseNumberEnv(process.env.MCP_MAX_REQUESTS) ??
|
|
@@ -342,13 +343,9 @@ function createSkedyulServer(config, registry) {
|
|
|
342
343
|
parseNumberEnv(process.env.MCP_TTL_EXTEND) ??
|
|
343
344
|
3600;
|
|
344
345
|
const state = createRequestState(maxRequests, ttlExtendSeconds, runtimeLabel, toolNames);
|
|
345
|
-
const
|
|
346
|
+
const mcpServer = new mcp_js_1.McpServer({
|
|
346
347
|
name: config.metadata.name,
|
|
347
348
|
version: config.metadata.version,
|
|
348
|
-
}, {
|
|
349
|
-
capabilities: {
|
|
350
|
-
tools: {},
|
|
351
|
-
},
|
|
352
349
|
});
|
|
353
350
|
const dedicatedShutdown = () => {
|
|
354
351
|
// eslint-disable-next-line no-console
|
|
@@ -356,16 +353,34 @@ function createSkedyulServer(config, registry) {
|
|
|
356
353
|
setTimeout(() => process.exit(0), 1000);
|
|
357
354
|
};
|
|
358
355
|
const callTool = createCallToolHandler(registry, state, config.computeLayer === 'dedicated' ? dedicatedShutdown : undefined);
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
356
|
+
// Register all tools from the registry
|
|
357
|
+
for (const [toolKey, tool] of Object.entries(registry)) {
|
|
358
|
+
// Use the tool's name or fall back to the registry key
|
|
359
|
+
const toolName = tool.name || toolKey;
|
|
360
|
+
mcpServer.registerTool(toolName, {
|
|
361
|
+
title: toolName,
|
|
362
|
+
description: tool.description,
|
|
363
|
+
inputSchema: tool.inputs,
|
|
364
|
+
outputSchema: tool.outputSchema,
|
|
365
|
+
}, async (args) => {
|
|
366
|
+
// Args will be the parsed Zod schema values directly
|
|
367
|
+
const result = await callTool(toolKey, {
|
|
368
|
+
inputs: args,
|
|
369
|
+
});
|
|
370
|
+
return {
|
|
371
|
+
content: result.content,
|
|
372
|
+
structuredContent: result.isError
|
|
373
|
+
? undefined
|
|
374
|
+
: JSON.parse(result.content[0]?.text ?? '{}'),
|
|
375
|
+
};
|
|
376
|
+
});
|
|
377
|
+
}
|
|
363
378
|
if (config.computeLayer === 'dedicated') {
|
|
364
|
-
return createDedicatedServerInstance(config, tools, callTool, state);
|
|
379
|
+
return createDedicatedServerInstance(config, tools, callTool, state, mcpServer);
|
|
365
380
|
}
|
|
366
|
-
return createServerlessInstance(config, tools, callTool, state);
|
|
381
|
+
return createServerlessInstance(config, tools, callTool, state, mcpServer, registry);
|
|
367
382
|
}
|
|
368
|
-
function createDedicatedServerInstance(config, tools, callTool, state) {
|
|
383
|
+
function createDedicatedServerInstance(config, tools, callTool, state, mcpServer) {
|
|
369
384
|
const port = getListeningPort(config);
|
|
370
385
|
const httpServer = http_1.default.createServer(async (req, res) => {
|
|
371
386
|
function sendCoreResult(result) {
|
|
@@ -465,62 +480,22 @@ function createDedicatedServerInstance(config, tools, callTool, state) {
|
|
|
465
480
|
return;
|
|
466
481
|
}
|
|
467
482
|
if (pathname === '/mcp' && req.method === 'POST') {
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
id: null,
|
|
476
|
-
error: {
|
|
477
|
-
code: -32700,
|
|
478
|
-
message: 'Parse error',
|
|
479
|
-
},
|
|
480
|
-
});
|
|
481
|
-
return;
|
|
482
|
-
}
|
|
483
|
+
const transport = new streamableHttp_js_1.StreamableHTTPServerTransport({
|
|
484
|
+
sessionIdGenerator: undefined,
|
|
485
|
+
enableJsonResponse: true,
|
|
486
|
+
});
|
|
487
|
+
res.on('close', () => {
|
|
488
|
+
transport.close();
|
|
489
|
+
});
|
|
483
490
|
try {
|
|
484
|
-
const
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
jsonrpc: '2.0',
|
|
488
|
-
id,
|
|
489
|
-
error: {
|
|
490
|
-
code: -32600,
|
|
491
|
-
message: 'Invalid Request',
|
|
492
|
-
},
|
|
493
|
-
});
|
|
494
|
-
return;
|
|
495
|
-
}
|
|
496
|
-
let result;
|
|
497
|
-
if (method === 'tools/list') {
|
|
498
|
-
result = { tools };
|
|
499
|
-
}
|
|
500
|
-
else if (method === 'tools/call') {
|
|
501
|
-
result = await callTool(params?.name, params?.arguments);
|
|
502
|
-
}
|
|
503
|
-
else {
|
|
504
|
-
sendJSON(res, 200, {
|
|
505
|
-
jsonrpc: '2.0',
|
|
506
|
-
id,
|
|
507
|
-
error: {
|
|
508
|
-
code: -32601,
|
|
509
|
-
message: `Method not found: ${method}`,
|
|
510
|
-
},
|
|
511
|
-
});
|
|
512
|
-
return;
|
|
513
|
-
}
|
|
514
|
-
sendJSON(res, 200, {
|
|
515
|
-
jsonrpc: '2.0',
|
|
516
|
-
id,
|
|
517
|
-
result,
|
|
518
|
-
});
|
|
491
|
+
const body = await parseJSONBody(req);
|
|
492
|
+
await mcpServer.connect(transport);
|
|
493
|
+
await transport.handleRequest(req, res, body);
|
|
519
494
|
}
|
|
520
495
|
catch (err) {
|
|
521
496
|
sendJSON(res, 500, {
|
|
522
497
|
jsonrpc: '2.0',
|
|
523
|
-
id:
|
|
498
|
+
id: null,
|
|
524
499
|
error: {
|
|
525
500
|
code: -32603,
|
|
526
501
|
message: err instanceof Error ? err.message : String(err ?? ''),
|
|
@@ -568,7 +543,7 @@ function createDedicatedServerInstance(config, tools, callTool, state) {
|
|
|
568
543
|
getHealthStatus: () => state.getHealthStatus(),
|
|
569
544
|
};
|
|
570
545
|
}
|
|
571
|
-
function createServerlessInstance(config, tools, callTool, state) {
|
|
546
|
+
function createServerlessInstance(config, tools, callTool, state, mcpServer, registry) {
|
|
572
547
|
const headers = getDefaultHeaders(config.cors);
|
|
573
548
|
return {
|
|
574
549
|
async handler(event) {
|
|
@@ -634,8 +609,30 @@ function createServerlessInstance(config, tools, callTool, state) {
|
|
|
634
609
|
}, headers);
|
|
635
610
|
}
|
|
636
611
|
try {
|
|
637
|
-
const
|
|
638
|
-
|
|
612
|
+
const toolName = estimateBody.name;
|
|
613
|
+
const toolArgs = estimateBody.inputs ?? {};
|
|
614
|
+
// Find tool by name
|
|
615
|
+
let toolKey = null;
|
|
616
|
+
let tool = null;
|
|
617
|
+
for (const [key, t] of Object.entries(registry)) {
|
|
618
|
+
if (t.name === toolName || key === toolName) {
|
|
619
|
+
toolKey = key;
|
|
620
|
+
tool = t;
|
|
621
|
+
break;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
if (!tool || !toolKey) {
|
|
625
|
+
return createResponse(400, {
|
|
626
|
+
error: {
|
|
627
|
+
code: -32602,
|
|
628
|
+
message: `Tool "${toolName}" not found`,
|
|
629
|
+
},
|
|
630
|
+
}, headers);
|
|
631
|
+
}
|
|
632
|
+
// Validate arguments against Zod schema
|
|
633
|
+
const validatedArgs = tool.inputs.parse(toolArgs);
|
|
634
|
+
const estimateResponse = await callTool(toolKey, {
|
|
635
|
+
inputs: validatedArgs,
|
|
639
636
|
estimate: true,
|
|
640
637
|
});
|
|
641
638
|
return createResponse(200, {
|
|
@@ -686,7 +683,47 @@ function createServerlessInstance(config, tools, callTool, state) {
|
|
|
686
683
|
result = { tools };
|
|
687
684
|
}
|
|
688
685
|
else if (rpcMethod === 'tools/call') {
|
|
689
|
-
|
|
686
|
+
const toolName = params?.name;
|
|
687
|
+
const toolArgs = params?.arguments ?? {};
|
|
688
|
+
// Find tool by name (check both registry key and tool.name)
|
|
689
|
+
let toolKey = null;
|
|
690
|
+
let tool = null;
|
|
691
|
+
for (const [key, t] of Object.entries(registry)) {
|
|
692
|
+
if (t.name === toolName || key === toolName) {
|
|
693
|
+
toolKey = key;
|
|
694
|
+
tool = t;
|
|
695
|
+
break;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
if (!tool || !toolKey) {
|
|
699
|
+
return createResponse(200, {
|
|
700
|
+
jsonrpc: '2.0',
|
|
701
|
+
id,
|
|
702
|
+
error: {
|
|
703
|
+
code: -32602,
|
|
704
|
+
message: `Tool "${toolName}" not found`,
|
|
705
|
+
},
|
|
706
|
+
}, headers);
|
|
707
|
+
}
|
|
708
|
+
// Validate arguments against Zod schema
|
|
709
|
+
try {
|
|
710
|
+
const validatedArgs = tool.inputs.parse(toolArgs);
|
|
711
|
+
result = await callTool(toolKey, {
|
|
712
|
+
inputs: validatedArgs,
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
catch (validationError) {
|
|
716
|
+
return createResponse(200, {
|
|
717
|
+
jsonrpc: '2.0',
|
|
718
|
+
id,
|
|
719
|
+
error: {
|
|
720
|
+
code: -32602,
|
|
721
|
+
message: validationError instanceof Error
|
|
722
|
+
? validationError.message
|
|
723
|
+
: 'Invalid arguments',
|
|
724
|
+
},
|
|
725
|
+
}, headers);
|
|
726
|
+
}
|
|
690
727
|
}
|
|
691
728
|
else {
|
|
692
729
|
return createResponse(200, {
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CoreApiConfig } from './core/types';
|
|
2
|
+
import type { z } from 'zod';
|
|
2
3
|
export interface ToolContext {
|
|
3
4
|
env: Record<string, string | undefined>;
|
|
4
5
|
mode?: 'execute' | 'estimate';
|
|
@@ -15,7 +16,15 @@ export interface ToolExecutionResult<Output = unknown> {
|
|
|
15
16
|
billing: BillingInfo;
|
|
16
17
|
}
|
|
17
18
|
export type ToolHandler<Input, Output> = (params: ToolParams<Input, Output>) => Promise<ToolExecutionResult<Output>> | ToolExecutionResult<Output>;
|
|
18
|
-
export
|
|
19
|
+
export interface ToolDefinition<Input = unknown, Output = unknown, InputSchema extends z.ZodType<Input> = z.ZodType<Input>, OutputSchema extends z.ZodType<Output> = z.ZodType<Output>> {
|
|
20
|
+
name: string;
|
|
21
|
+
description: string;
|
|
22
|
+
inputs: InputSchema;
|
|
23
|
+
handler: ToolHandler<Input, Output>;
|
|
24
|
+
outputSchema?: OutputSchema;
|
|
25
|
+
[key: string]: unknown;
|
|
26
|
+
}
|
|
27
|
+
export type ToolRegistry = Record<string, ToolDefinition<unknown, unknown>>;
|
|
19
28
|
export type ToolName<T extends ToolRegistry> = Extract<keyof T, string>;
|
|
20
29
|
export interface ToolMetadata {
|
|
21
30
|
name: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skedyul",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "The Skedyul SDK for Node.js",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
"author": "Skedyul <support@skedyul.com>",
|
|
24
24
|
"license": "MIT",
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
26
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
27
|
+
"zod": "^4.0.0"
|
|
27
28
|
},
|
|
28
29
|
"devDependencies": {
|
|
29
30
|
"@types/node": "^24.10.1",
|