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