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