@mcp-weave/nestjs 0.1.0 → 0.2.0
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/README.md +287 -0
- package/dist/index.d.mts +44 -5
- package/dist/index.d.ts +44 -5
- package/dist/index.js +473 -70
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +473 -70
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -3,8 +3,15 @@ import { METADATA_KEYS, extractMetadata } from '@mcp-weave/core';
|
|
|
3
3
|
export { METADATA_KEYS, extractMetadata } from '@mcp-weave/core';
|
|
4
4
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
5
5
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
6
|
+
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
|
|
7
|
+
import { createServer } from 'http';
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
10
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
11
|
+
}) : x)(function(x) {
|
|
12
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
13
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
14
|
+
});
|
|
8
15
|
function McpServer(options) {
|
|
9
16
|
return (target) => {
|
|
10
17
|
const metadata = {
|
|
@@ -157,85 +164,118 @@ var McpRuntimeServer = class {
|
|
|
157
164
|
setupToolHandlers() {
|
|
158
165
|
const tools = this.metadata.tools;
|
|
159
166
|
if (tools.length === 0) return;
|
|
160
|
-
this.server.setRequestHandler(
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
throw new Error(
|
|
167
|
+
this.server.setRequestHandler(
|
|
168
|
+
{ method: "tools/list" },
|
|
169
|
+
async () => ({
|
|
170
|
+
tools: tools.map((tool) => ({
|
|
171
|
+
name: tool.name,
|
|
172
|
+
description: tool.description,
|
|
173
|
+
inputSchema: tool.inputSchema ?? { type: "object", properties: {} }
|
|
174
|
+
}))
|
|
175
|
+
})
|
|
176
|
+
);
|
|
177
|
+
this.server.setRequestHandler(
|
|
178
|
+
{ method: "tools/call" },
|
|
179
|
+
async (request) => {
|
|
180
|
+
if (!request || typeof request !== "object") throw new Error("Invalid request");
|
|
181
|
+
const params = request.params;
|
|
182
|
+
if (!params || typeof params !== "object") throw new Error("Invalid params");
|
|
183
|
+
const toolName = params.name;
|
|
184
|
+
const tool = tools.find((t) => t.name === toolName);
|
|
185
|
+
if (!tool) {
|
|
186
|
+
throw new Error(`Unknown tool: ${toolName}`);
|
|
187
|
+
}
|
|
188
|
+
const method = Reflect.get(this.instance, tool.propertyKey);
|
|
189
|
+
if (typeof method !== "function") {
|
|
190
|
+
throw new Error(`Method ${String(tool.propertyKey)} not found`);
|
|
191
|
+
}
|
|
192
|
+
const args = this.resolveToolArgs(
|
|
193
|
+
tool.propertyKey,
|
|
194
|
+
params.arguments ?? {}
|
|
195
|
+
);
|
|
196
|
+
const result = await method.apply(this.instance, args);
|
|
197
|
+
return {
|
|
198
|
+
content: [
|
|
199
|
+
{
|
|
200
|
+
type: "text",
|
|
201
|
+
text: typeof result === "string" ? result : JSON.stringify(result)
|
|
202
|
+
}
|
|
203
|
+
]
|
|
204
|
+
};
|
|
176
205
|
}
|
|
177
|
-
|
|
178
|
-
const result = await method.apply(this.instance, args);
|
|
179
|
-
return {
|
|
180
|
-
content: [
|
|
181
|
-
{
|
|
182
|
-
type: "text",
|
|
183
|
-
text: typeof result === "string" ? result : JSON.stringify(result)
|
|
184
|
-
}
|
|
185
|
-
]
|
|
186
|
-
};
|
|
187
|
-
});
|
|
206
|
+
);
|
|
188
207
|
}
|
|
189
208
|
setupResourceHandlers() {
|
|
190
209
|
const resources = this.metadata.resources;
|
|
191
210
|
if (resources.length === 0) return;
|
|
192
|
-
this.server.setRequestHandler(
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
211
|
+
this.server.setRequestHandler(
|
|
212
|
+
{ method: "resources/list" },
|
|
213
|
+
async () => ({
|
|
214
|
+
resources: resources.map((resource) => ({
|
|
215
|
+
uri: resource.uri,
|
|
216
|
+
name: resource.name,
|
|
217
|
+
description: resource.description,
|
|
218
|
+
mimeType: resource.mimeType
|
|
219
|
+
}))
|
|
220
|
+
})
|
|
221
|
+
);
|
|
222
|
+
this.server.setRequestHandler(
|
|
223
|
+
{ method: "resources/read" },
|
|
224
|
+
async (request) => {
|
|
225
|
+
if (!request || typeof request !== "object") throw new Error("Invalid request");
|
|
226
|
+
const params = request.params;
|
|
227
|
+
if (!params || typeof params !== "object") throw new Error("Invalid params");
|
|
228
|
+
const uri = params.uri;
|
|
229
|
+
for (const resource of resources) {
|
|
230
|
+
const uriParams = this.extractUriParams(resource.uri, uri);
|
|
231
|
+
if (uriParams) {
|
|
232
|
+
const method = Reflect.get(this.instance, resource.propertyKey);
|
|
233
|
+
if (typeof method !== "function") {
|
|
234
|
+
throw new Error(`Method ${String(resource.propertyKey)} not found`);
|
|
235
|
+
}
|
|
236
|
+
const args = this.resolveResourceArgs(resource.propertyKey, uriParams);
|
|
237
|
+
return await method.apply(this.instance, args);
|
|
208
238
|
}
|
|
209
|
-
const args = this.resolveResourceArgs(resource.propertyKey, params);
|
|
210
|
-
return await method.apply(this.instance, args);
|
|
211
239
|
}
|
|
240
|
+
throw new Error(`Resource not found: ${uri}`);
|
|
212
241
|
}
|
|
213
|
-
|
|
214
|
-
});
|
|
242
|
+
);
|
|
215
243
|
}
|
|
216
244
|
setupPromptHandlers() {
|
|
217
245
|
const prompts = this.metadata.prompts;
|
|
218
246
|
if (prompts.length === 0) return;
|
|
219
|
-
this.server.setRequestHandler(
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
throw new Error(
|
|
247
|
+
this.server.setRequestHandler(
|
|
248
|
+
{ method: "prompts/list" },
|
|
249
|
+
async () => ({
|
|
250
|
+
prompts: prompts.map((prompt) => ({
|
|
251
|
+
name: prompt.name,
|
|
252
|
+
description: prompt.description,
|
|
253
|
+
arguments: prompt.arguments ?? []
|
|
254
|
+
}))
|
|
255
|
+
})
|
|
256
|
+
);
|
|
257
|
+
this.server.setRequestHandler(
|
|
258
|
+
{ method: "prompts/get" },
|
|
259
|
+
async (request) => {
|
|
260
|
+
if (!request || typeof request !== "object") throw new Error("Invalid request");
|
|
261
|
+
const params = request.params;
|
|
262
|
+
if (!params || typeof params !== "object") throw new Error("Invalid params");
|
|
263
|
+
const promptName = params.name;
|
|
264
|
+
const prompt = prompts.find((p) => p.name === promptName);
|
|
265
|
+
if (!prompt) {
|
|
266
|
+
throw new Error(`Unknown prompt: ${promptName}`);
|
|
267
|
+
}
|
|
268
|
+
const method = Reflect.get(this.instance, prompt.propertyKey);
|
|
269
|
+
if (typeof method !== "function") {
|
|
270
|
+
throw new Error(`Method ${String(prompt.propertyKey)} not found`);
|
|
271
|
+
}
|
|
272
|
+
const args = this.resolvePromptArgs(
|
|
273
|
+
prompt.propertyKey,
|
|
274
|
+
params.arguments ?? {}
|
|
275
|
+
);
|
|
276
|
+
return await method.apply(this.instance, args);
|
|
235
277
|
}
|
|
236
|
-
|
|
237
|
-
return await method.apply(this.instance, args);
|
|
238
|
-
});
|
|
278
|
+
);
|
|
239
279
|
}
|
|
240
280
|
resolveToolArgs(propertyKey, input) {
|
|
241
281
|
const params = this.metadata.params.filter(
|
|
@@ -290,23 +330,386 @@ var McpRuntimeServer = class {
|
|
|
290
330
|
return params;
|
|
291
331
|
}
|
|
292
332
|
/**
|
|
293
|
-
* Start the MCP server
|
|
333
|
+
* Start the MCP server with stdio transport
|
|
294
334
|
*/
|
|
295
335
|
async start() {
|
|
296
336
|
const transport = new StdioServerTransport();
|
|
297
337
|
await this.server.connect(transport);
|
|
298
338
|
console.error(`MCP server '${this.metadata.server?.name}' started on stdio`);
|
|
299
339
|
}
|
|
340
|
+
/**
|
|
341
|
+
* Start the MCP server with SSE transport
|
|
342
|
+
*/
|
|
343
|
+
async startSSE(options = {}) {
|
|
344
|
+
const port = options.port ?? 3e3;
|
|
345
|
+
const endpoint = options.endpoint ?? "/sse";
|
|
346
|
+
const httpServer = createServer(async (req, res) => {
|
|
347
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
348
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
349
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
350
|
+
if (req.method === "OPTIONS") {
|
|
351
|
+
res.writeHead(200);
|
|
352
|
+
res.end();
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
const url = new URL(req.url ?? "/", `http://localhost:${port}`);
|
|
356
|
+
if (url.pathname === endpoint && req.method === "GET") {
|
|
357
|
+
const transport = new SSEServerTransport(endpoint, res);
|
|
358
|
+
await this.server.connect(transport);
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
if (url.pathname === `${endpoint}/message` && req.method === "POST") {
|
|
362
|
+
let body = "";
|
|
363
|
+
req.on("data", (chunk) => {
|
|
364
|
+
body += chunk.toString();
|
|
365
|
+
});
|
|
366
|
+
req.on("end", () => {
|
|
367
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
368
|
+
res.end(JSON.stringify({ received: true }));
|
|
369
|
+
});
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
if (url.pathname === "/health") {
|
|
373
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
374
|
+
res.end(
|
|
375
|
+
JSON.stringify({
|
|
376
|
+
status: "ok",
|
|
377
|
+
server: this.metadata.server?.name,
|
|
378
|
+
version: this.metadata.server?.version
|
|
379
|
+
})
|
|
380
|
+
);
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
384
|
+
res.end(JSON.stringify({ error: "Not found" }));
|
|
385
|
+
});
|
|
386
|
+
return new Promise((resolve, reject) => {
|
|
387
|
+
httpServer.on("error", reject);
|
|
388
|
+
httpServer.listen(port, () => {
|
|
389
|
+
console.error(
|
|
390
|
+
`MCP server '${this.metadata.server?.name}' started on http://localhost:${port}${endpoint}`
|
|
391
|
+
);
|
|
392
|
+
resolve(httpServer);
|
|
393
|
+
});
|
|
394
|
+
});
|
|
395
|
+
}
|
|
300
396
|
/**
|
|
301
397
|
* Get the underlying MCP server instance
|
|
302
398
|
*/
|
|
303
399
|
getServer() {
|
|
304
400
|
return this.server;
|
|
305
401
|
}
|
|
402
|
+
/**
|
|
403
|
+
* Start the MCP server with WebSocket transport
|
|
404
|
+
*/
|
|
405
|
+
async startWebSocket(options = {}) {
|
|
406
|
+
const port = options.port ?? 8080;
|
|
407
|
+
const endpoint = options.endpoint ?? "/ws";
|
|
408
|
+
const httpServer = createServer((req, res) => {
|
|
409
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
410
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
411
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Upgrade, Connection");
|
|
412
|
+
if (req.method === "OPTIONS") {
|
|
413
|
+
res.writeHead(200);
|
|
414
|
+
res.end();
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
if (req.url === "/health") {
|
|
418
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
419
|
+
res.end(
|
|
420
|
+
JSON.stringify({
|
|
421
|
+
status: "ok",
|
|
422
|
+
server: this.metadata.server?.name,
|
|
423
|
+
version: this.metadata.server?.version,
|
|
424
|
+
transport: "websocket"
|
|
425
|
+
})
|
|
426
|
+
);
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
if (req.url === "/") {
|
|
430
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
431
|
+
res.end(
|
|
432
|
+
JSON.stringify({
|
|
433
|
+
name: this.metadata.server?.name,
|
|
434
|
+
version: this.metadata.server?.version,
|
|
435
|
+
websocket: `ws://localhost:${port}${endpoint}`,
|
|
436
|
+
tools: this.metadata.tools.length,
|
|
437
|
+
resources: this.metadata.resources.length,
|
|
438
|
+
prompts: this.metadata.prompts.length
|
|
439
|
+
})
|
|
440
|
+
);
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
res.writeHead(426, { "Content-Type": "application/json" });
|
|
444
|
+
res.end(JSON.stringify({ error: "Upgrade Required", message: `Connect via WebSocket at ${endpoint}` }));
|
|
445
|
+
});
|
|
446
|
+
httpServer.on("upgrade", (req, socket, _head) => {
|
|
447
|
+
const url = new URL(req.url ?? "/", `http://localhost:${port}`);
|
|
448
|
+
if (url.pathname !== endpoint) {
|
|
449
|
+
socket.write("HTTP/1.1 404 Not Found\r\n\r\n");
|
|
450
|
+
socket.destroy();
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
const key = req.headers["sec-websocket-key"];
|
|
454
|
+
if (!key) {
|
|
455
|
+
socket.write("HTTP/1.1 400 Bad Request\r\n\r\n");
|
|
456
|
+
socket.destroy();
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
const crypto = __require("crypto");
|
|
460
|
+
const acceptKey = crypto.createHash("sha1").update(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").digest("base64");
|
|
461
|
+
socket.write(
|
|
462
|
+
`HTTP/1.1 101 Switching Protocols\r
|
|
463
|
+
Upgrade: websocket\r
|
|
464
|
+
Connection: Upgrade\r
|
|
465
|
+
Sec-WebSocket-Accept: ${acceptKey}\r
|
|
466
|
+
\r
|
|
467
|
+
`
|
|
468
|
+
);
|
|
469
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
470
|
+
const sessionId = crypto.randomUUID();
|
|
471
|
+
const encodeFrame = (data) => {
|
|
472
|
+
const payload = Buffer.from(data, "utf8");
|
|
473
|
+
const length = payload.length;
|
|
474
|
+
let frame;
|
|
475
|
+
if (length < 126) {
|
|
476
|
+
frame = Buffer.alloc(2 + length);
|
|
477
|
+
frame[0] = 129;
|
|
478
|
+
frame[1] = length;
|
|
479
|
+
payload.copy(frame, 2);
|
|
480
|
+
} else if (length < 65536) {
|
|
481
|
+
frame = Buffer.alloc(4 + length);
|
|
482
|
+
frame[0] = 129;
|
|
483
|
+
frame[1] = 126;
|
|
484
|
+
frame.writeUInt16BE(length, 2);
|
|
485
|
+
payload.copy(frame, 4);
|
|
486
|
+
} else {
|
|
487
|
+
frame = Buffer.alloc(10 + length);
|
|
488
|
+
frame[0] = 129;
|
|
489
|
+
frame[1] = 127;
|
|
490
|
+
frame.writeBigUInt64BE(BigInt(length), 2);
|
|
491
|
+
payload.copy(frame, 10);
|
|
492
|
+
}
|
|
493
|
+
return frame;
|
|
494
|
+
};
|
|
495
|
+
const decodeFrame = (buffer) => {
|
|
496
|
+
if (buffer.length < 2) return null;
|
|
497
|
+
const byte0 = buffer[0];
|
|
498
|
+
const byte1 = buffer[1];
|
|
499
|
+
const opcode = byte0 & 15;
|
|
500
|
+
const masked = (byte1 & 128) !== 0;
|
|
501
|
+
let payloadLength = byte1 & 127;
|
|
502
|
+
let offset = 2;
|
|
503
|
+
if (payloadLength === 126) {
|
|
504
|
+
if (buffer.length < 4) return null;
|
|
505
|
+
payloadLength = buffer.readUInt16BE(2);
|
|
506
|
+
offset = 4;
|
|
507
|
+
} else if (payloadLength === 127) {
|
|
508
|
+
if (buffer.length < 10) return null;
|
|
509
|
+
payloadLength = Number(buffer.readBigUInt64BE(2));
|
|
510
|
+
offset = 10;
|
|
511
|
+
}
|
|
512
|
+
if (masked) {
|
|
513
|
+
if (buffer.length < offset + 4 + payloadLength) return null;
|
|
514
|
+
const mask = buffer.slice(offset, offset + 4);
|
|
515
|
+
offset += 4;
|
|
516
|
+
const payload = buffer.slice(offset, offset + payloadLength);
|
|
517
|
+
for (let i = 0; i < payload.length; i++) {
|
|
518
|
+
const maskByte = mask[i % 4] ?? 0;
|
|
519
|
+
payload[i] = (payload[i] ?? 0) ^ maskByte;
|
|
520
|
+
}
|
|
521
|
+
return { opcode, payload: payload.toString("utf8") };
|
|
522
|
+
} else {
|
|
523
|
+
if (buffer.length < offset + payloadLength) return null;
|
|
524
|
+
return { opcode, payload: buffer.slice(offset, offset + payloadLength).toString("utf8") };
|
|
525
|
+
}
|
|
526
|
+
};
|
|
527
|
+
const send = (data) => {
|
|
528
|
+
try {
|
|
529
|
+
socket.write(encodeFrame(data));
|
|
530
|
+
} catch {
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
sessions.set(sessionId, { send });
|
|
534
|
+
send(JSON.stringify({
|
|
535
|
+
jsonrpc: "2.0",
|
|
536
|
+
method: "connection/established",
|
|
537
|
+
params: { sessionId, server: this.metadata.server?.name }
|
|
538
|
+
}));
|
|
539
|
+
let messageBuffer = Buffer.alloc(0);
|
|
540
|
+
socket.on("data", async (chunk) => {
|
|
541
|
+
messageBuffer = Buffer.concat([messageBuffer, chunk]);
|
|
542
|
+
while (messageBuffer.length > 0) {
|
|
543
|
+
const frame = decodeFrame(messageBuffer);
|
|
544
|
+
if (!frame) break;
|
|
545
|
+
let frameSize = 2;
|
|
546
|
+
const byte1 = messageBuffer[1] ?? 0;
|
|
547
|
+
const payloadLength = byte1 & 127;
|
|
548
|
+
if (payloadLength === 126) frameSize = 4;
|
|
549
|
+
else if (payloadLength === 127) frameSize = 10;
|
|
550
|
+
if ((byte1 & 128) !== 0) frameSize += 4;
|
|
551
|
+
frameSize += frame.payload.length;
|
|
552
|
+
messageBuffer = messageBuffer.slice(frameSize);
|
|
553
|
+
if (frame.opcode === 8) {
|
|
554
|
+
socket.end();
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
if (frame.opcode === 9) {
|
|
558
|
+
socket.write(Buffer.from([138, 0]));
|
|
559
|
+
continue;
|
|
560
|
+
}
|
|
561
|
+
if (frame.opcode === 1) {
|
|
562
|
+
try {
|
|
563
|
+
const message = JSON.parse(frame.payload);
|
|
564
|
+
const response = await this.handleWebSocketMessage(message);
|
|
565
|
+
if (response) {
|
|
566
|
+
send(JSON.stringify(response));
|
|
567
|
+
}
|
|
568
|
+
} catch (error) {
|
|
569
|
+
send(JSON.stringify({
|
|
570
|
+
jsonrpc: "2.0",
|
|
571
|
+
error: { code: -32700, message: "Parse error" },
|
|
572
|
+
id: null
|
|
573
|
+
}));
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
});
|
|
578
|
+
socket.on("close", () => {
|
|
579
|
+
sessions.delete(sessionId);
|
|
580
|
+
});
|
|
581
|
+
socket.on("error", () => {
|
|
582
|
+
sessions.delete(sessionId);
|
|
583
|
+
});
|
|
584
|
+
});
|
|
585
|
+
return new Promise((resolve, reject) => {
|
|
586
|
+
httpServer.on("error", reject);
|
|
587
|
+
httpServer.listen(port, () => {
|
|
588
|
+
console.error(
|
|
589
|
+
`MCP server '${this.metadata.server?.name}' started on ws://localhost:${port}${endpoint}`
|
|
590
|
+
);
|
|
591
|
+
resolve(httpServer);
|
|
592
|
+
});
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
596
|
+
* Handle WebSocket JSON-RPC messages
|
|
597
|
+
*/
|
|
598
|
+
async handleWebSocketMessage(message) {
|
|
599
|
+
if (!message || typeof message !== "object") {
|
|
600
|
+
return { jsonrpc: "2.0", error: { code: -32600, message: "Invalid Request" }, id: null };
|
|
601
|
+
}
|
|
602
|
+
const req = message;
|
|
603
|
+
if (req.jsonrpc !== "2.0" || !req.method) {
|
|
604
|
+
return { jsonrpc: "2.0", error: { code: -32600, message: "Invalid Request" }, id: req.id ?? null };
|
|
605
|
+
}
|
|
606
|
+
try {
|
|
607
|
+
let result;
|
|
608
|
+
switch (req.method) {
|
|
609
|
+
case "initialize":
|
|
610
|
+
result = {
|
|
611
|
+
protocolVersion: "2024-11-05",
|
|
612
|
+
serverInfo: {
|
|
613
|
+
name: this.metadata.server?.name,
|
|
614
|
+
version: this.metadata.server?.version ?? "1.0.0"
|
|
615
|
+
},
|
|
616
|
+
capabilities: {
|
|
617
|
+
tools: this.metadata.tools.length > 0 ? {} : void 0,
|
|
618
|
+
resources: this.metadata.resources.length > 0 ? {} : void 0,
|
|
619
|
+
prompts: this.metadata.prompts.length > 0 ? {} : void 0
|
|
620
|
+
}
|
|
621
|
+
};
|
|
622
|
+
break;
|
|
623
|
+
case "tools/list":
|
|
624
|
+
result = {
|
|
625
|
+
tools: this.metadata.tools.map((t) => ({
|
|
626
|
+
name: t.name,
|
|
627
|
+
description: t.description,
|
|
628
|
+
inputSchema: t.inputSchema ?? { type: "object", properties: {} }
|
|
629
|
+
}))
|
|
630
|
+
};
|
|
631
|
+
break;
|
|
632
|
+
case "tools/call":
|
|
633
|
+
const toolParams = req.params;
|
|
634
|
+
const tool = this.metadata.tools.find((t) => t.name === toolParams.name);
|
|
635
|
+
if (!tool) {
|
|
636
|
+
return { jsonrpc: "2.0", error: { code: -32602, message: `Unknown tool: ${toolParams.name}` }, id: req.id };
|
|
637
|
+
}
|
|
638
|
+
const method = Reflect.get(this.instance, tool.propertyKey);
|
|
639
|
+
const toolResult = await method.apply(this.instance, [toolParams.arguments ?? {}]);
|
|
640
|
+
result = {
|
|
641
|
+
content: [{
|
|
642
|
+
type: "text",
|
|
643
|
+
text: typeof toolResult === "string" ? toolResult : JSON.stringify(toolResult)
|
|
644
|
+
}]
|
|
645
|
+
};
|
|
646
|
+
break;
|
|
647
|
+
case "resources/list":
|
|
648
|
+
result = {
|
|
649
|
+
resources: this.metadata.resources.map((r) => ({
|
|
650
|
+
uri: r.uri,
|
|
651
|
+
name: r.name,
|
|
652
|
+
description: r.description,
|
|
653
|
+
mimeType: r.mimeType
|
|
654
|
+
}))
|
|
655
|
+
};
|
|
656
|
+
break;
|
|
657
|
+
case "resources/read":
|
|
658
|
+
const resParams = req.params;
|
|
659
|
+
for (const resource of this.metadata.resources) {
|
|
660
|
+
const uriParams = this.extractUriParams(resource.uri, resParams.uri);
|
|
661
|
+
if (uriParams) {
|
|
662
|
+
const resMethod = Reflect.get(this.instance, resource.propertyKey);
|
|
663
|
+
const args = this.resolveResourceArgs(resource.propertyKey, uriParams);
|
|
664
|
+
result = await resMethod.apply(this.instance, args);
|
|
665
|
+
break;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
if (!result) {
|
|
669
|
+
return { jsonrpc: "2.0", error: { code: -32602, message: `Resource not found: ${resParams.uri}` }, id: req.id };
|
|
670
|
+
}
|
|
671
|
+
break;
|
|
672
|
+
case "prompts/list":
|
|
673
|
+
result = {
|
|
674
|
+
prompts: this.metadata.prompts.map((p) => ({
|
|
675
|
+
name: p.name,
|
|
676
|
+
description: p.description,
|
|
677
|
+
arguments: p.arguments ?? []
|
|
678
|
+
}))
|
|
679
|
+
};
|
|
680
|
+
break;
|
|
681
|
+
case "prompts/get":
|
|
682
|
+
const promptParams = req.params;
|
|
683
|
+
const prompt = this.metadata.prompts.find((p) => p.name === promptParams.name);
|
|
684
|
+
if (!prompt) {
|
|
685
|
+
return { jsonrpc: "2.0", error: { code: -32602, message: `Unknown prompt: ${promptParams.name}` }, id: req.id };
|
|
686
|
+
}
|
|
687
|
+
const promptMethod = Reflect.get(this.instance, prompt.propertyKey);
|
|
688
|
+
const promptArgs = this.resolvePromptArgs(prompt.propertyKey, promptParams.arguments ?? {});
|
|
689
|
+
result = await promptMethod.apply(this.instance, promptArgs);
|
|
690
|
+
break;
|
|
691
|
+
default:
|
|
692
|
+
return { jsonrpc: "2.0", error: { code: -32601, message: `Method not found: ${req.method}` }, id: req.id };
|
|
693
|
+
}
|
|
694
|
+
return { jsonrpc: "2.0", result, id: req.id };
|
|
695
|
+
} catch (error) {
|
|
696
|
+
return {
|
|
697
|
+
jsonrpc: "2.0",
|
|
698
|
+
error: { code: -32e3, message: error instanceof Error ? error.message : "Internal error" },
|
|
699
|
+
id: req.id
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
}
|
|
306
703
|
};
|
|
307
|
-
async function createMcpServer(target, options) {
|
|
704
|
+
async function createMcpServer(target, options = {}) {
|
|
308
705
|
const server = new McpRuntimeServer(target, options);
|
|
309
|
-
|
|
706
|
+
if (options.transport === "sse") {
|
|
707
|
+
await server.startSSE({ port: options.port, endpoint: options.endpoint });
|
|
708
|
+
} else if (options.transport === "websocket") {
|
|
709
|
+
await server.startWebSocket({ port: options.port, endpoint: options.endpoint });
|
|
710
|
+
} else {
|
|
711
|
+
await server.start();
|
|
712
|
+
}
|
|
310
713
|
return server;
|
|
311
714
|
}
|
|
312
715
|
|