@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/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
- // src/index.ts
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({ method: "tools/list" }, async () => ({
162
- tools: tools.map((tool) => ({
163
- name: tool.name,
164
- description: tool.description,
165
- inputSchema: tool.inputSchema ?? { type: "object", properties: {} }
166
- }))
167
- }));
168
- this.server.setRequestHandler({ method: "tools/call" }, async (request) => {
169
- const toolName = request.params.name;
170
- const tool = tools.find((t) => t.name === toolName);
171
- if (!tool) {
172
- throw new Error(`Unknown tool: ${toolName}`);
173
- }
174
- const method = this.instance[tool.propertyKey];
175
- if (typeof method !== "function") {
176
- throw new Error(`Method ${String(tool.propertyKey)} not found`);
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
- const args = this.resolveToolArgs(tool.propertyKey, request.params.arguments ?? {});
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({ method: "resources/list" }, async () => ({
194
- resources: resources.map((resource) => ({
195
- uri: resource.uri,
196
- name: resource.name,
197
- description: resource.description,
198
- mimeType: resource.mimeType
199
- }))
200
- }));
201
- this.server.setRequestHandler({ method: "resources/read" }, async (request) => {
202
- const uri = request.params.uri;
203
- for (const resource of resources) {
204
- const params = this.extractUriParams(resource.uri, uri);
205
- if (params) {
206
- const method = this.instance[resource.propertyKey];
207
- if (typeof method !== "function") {
208
- throw new Error(`Method ${String(resource.propertyKey)} not found`);
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
- throw new Error(`Resource not found: ${uri}`);
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({ method: "prompts/list" }, async () => ({
221
- prompts: prompts.map((prompt) => ({
222
- name: prompt.name,
223
- description: prompt.description,
224
- arguments: prompt.arguments ?? []
225
- }))
226
- }));
227
- this.server.setRequestHandler({ method: "prompts/get" }, async (request) => {
228
- const promptName = request.params.name;
229
- const prompt = prompts.find((p) => p.name === promptName);
230
- if (!prompt) {
231
- throw new Error(`Unknown prompt: ${promptName}`);
232
- }
233
- const method = this.instance[prompt.propertyKey];
234
- if (typeof method !== "function") {
235
- throw new Error(`Method ${String(prompt.propertyKey)} not found`);
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
- const args = this.resolvePromptArgs(prompt.propertyKey, request.params.arguments ?? {});
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
- await server.start();
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