@nestjs-mcp/server 0.1.0-alpha.4

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.
Files changed (178) hide show
  1. package/.copilotignore +38 -0
  2. package/.devcontainer/Dockerfile.dev +28 -0
  3. package/.devcontainer/devcontainer.json +56 -0
  4. package/.devcontainer/docker-compose.yml +15 -0
  5. package/.dockerignore +37 -0
  6. package/.github/codeql-config.yml +4 -0
  7. package/.github/copilot-instructions.md +138 -0
  8. package/.github/prompts/memory.prompt.md +120 -0
  9. package/.github/workflows/auto-tag-release.yml +84 -0
  10. package/.github/workflows/codeql-analysis.yml +56 -0
  11. package/.github/workflows/npm-publish.yml +58 -0
  12. package/.github/workflows/pr-branch-validation.yml +78 -0
  13. package/.github/workflows/run-tests.yml +41 -0
  14. package/.github/workflows/sync-main-to-develop.yml +53 -0
  15. package/.handbook/GIT_GUIDELINES.md +250 -0
  16. package/.handbook/PACKAGE_VERSIONING.md +140 -0
  17. package/.handbook/STACK.md +75 -0
  18. package/.prettierrc +4 -0
  19. package/.vscode/extensions.json +44 -0
  20. package/.vscode/settings.json +40 -0
  21. package/CONTRIBUTING.md +261 -0
  22. package/LICENSE +21 -0
  23. package/README.md +490 -0
  24. package/dist/examples/async-import/app.module.d.ts +2 -0
  25. package/dist/examples/async-import/app.module.js +33 -0
  26. package/dist/examples/async-import/app.module.js.map +1 -0
  27. package/dist/examples/async-import/main.d.ts +1 -0
  28. package/dist/examples/async-import/main.js +17 -0
  29. package/dist/examples/async-import/main.js.map +1 -0
  30. package/dist/examples/guards/app.module.d.ts +6 -0
  31. package/dist/examples/guards/app.module.js +48 -0
  32. package/dist/examples/guards/app.module.js.map +1 -0
  33. package/dist/examples/guards/guards.resolver.d.ts +13 -0
  34. package/dist/examples/guards/guards.resolver.js +61 -0
  35. package/dist/examples/guards/guards.resolver.js.map +1 -0
  36. package/dist/examples/guards/main.d.ts +1 -0
  37. package/dist/examples/guards/main.js +11 -0
  38. package/dist/examples/guards/main.js.map +1 -0
  39. package/dist/examples/mixed/app.module.d.ts +2 -0
  40. package/dist/examples/mixed/app.module.js +31 -0
  41. package/dist/examples/mixed/app.module.js.map +1 -0
  42. package/dist/examples/mixed/main.d.ts +1 -0
  43. package/dist/examples/mixed/main.js +11 -0
  44. package/dist/examples/mixed/main.js.map +1 -0
  45. package/dist/examples/mixed/mixed.resolver.d.ts +6 -0
  46. package/dist/examples/mixed/mixed.resolver.js +78 -0
  47. package/dist/examples/mixed/mixed.resolver.js.map +1 -0
  48. package/dist/examples/prompts/app.module.d.ts +2 -0
  49. package/dist/examples/prompts/app.module.js +31 -0
  50. package/dist/examples/prompts/app.module.js.map +1 -0
  51. package/dist/examples/prompts/main.d.ts +1 -0
  52. package/dist/examples/prompts/main.js +11 -0
  53. package/dist/examples/prompts/main.js.map +1 -0
  54. package/dist/examples/prompts/prompts.resolver.d.ts +14 -0
  55. package/dist/examples/prompts/prompts.resolver.js +165 -0
  56. package/dist/examples/prompts/prompts.resolver.js.map +1 -0
  57. package/dist/examples/resources/app.module.d.ts +2 -0
  58. package/dist/examples/resources/app.module.js +31 -0
  59. package/dist/examples/resources/app.module.js.map +1 -0
  60. package/dist/examples/resources/main.d.ts +1 -0
  61. package/dist/examples/resources/main.js +11 -0
  62. package/dist/examples/resources/main.js.map +1 -0
  63. package/dist/examples/resources/resources.resolver.d.ts +12 -0
  64. package/dist/examples/resources/resources.resolver.js +114 -0
  65. package/dist/examples/resources/resources.resolver.js.map +1 -0
  66. package/dist/examples/tools/app.module.d.ts +2 -0
  67. package/dist/examples/tools/app.module.js +31 -0
  68. package/dist/examples/tools/app.module.js.map +1 -0
  69. package/dist/examples/tools/main.d.ts +1 -0
  70. package/dist/examples/tools/main.js +11 -0
  71. package/dist/examples/tools/main.js.map +1 -0
  72. package/dist/examples/tools/tools.resolver.d.ts +23 -0
  73. package/dist/examples/tools/tools.resolver.js +175 -0
  74. package/dist/examples/tools/tools.resolver.js.map +1 -0
  75. package/dist/src/controllers/sse/index.d.ts +2 -0
  76. package/dist/src/controllers/sse/index.js +19 -0
  77. package/dist/src/controllers/sse/index.js.map +1 -0
  78. package/dist/src/controllers/sse/sse.controller.d.ts +8 -0
  79. package/dist/src/controllers/sse/sse.controller.js +51 -0
  80. package/dist/src/controllers/sse/sse.controller.js.map +1 -0
  81. package/dist/src/controllers/sse/sse.service.d.ts +16 -0
  82. package/dist/src/controllers/sse/sse.service.js +78 -0
  83. package/dist/src/controllers/sse/sse.service.js.map +1 -0
  84. package/dist/src/controllers/streamable/index.d.ts +2 -0
  85. package/dist/src/controllers/streamable/index.js +19 -0
  86. package/dist/src/controllers/streamable/index.js.map +1 -0
  87. package/dist/src/controllers/streamable/streamable.controller.d.ts +9 -0
  88. package/dist/src/controllers/streamable/streamable.controller.js +62 -0
  89. package/dist/src/controllers/streamable/streamable.controller.js.map +1 -0
  90. package/dist/src/controllers/streamable/streamable.service.d.ts +24 -0
  91. package/dist/src/controllers/streamable/streamable.service.js +117 -0
  92. package/dist/src/controllers/streamable/streamable.service.js.map +1 -0
  93. package/dist/src/decorators/capabilities.constants.d.ts +4 -0
  94. package/dist/src/decorators/capabilities.constants.js +8 -0
  95. package/dist/src/decorators/capabilities.constants.js.map +1 -0
  96. package/dist/src/decorators/capabilities.decorators.d.ts +8 -0
  97. package/dist/src/decorators/capabilities.decorators.js +49 -0
  98. package/dist/src/decorators/capabilities.decorators.js.map +1 -0
  99. package/dist/src/decorators/index.d.ts +2 -0
  100. package/dist/src/decorators/index.js +19 -0
  101. package/dist/src/decorators/index.js.map +1 -0
  102. package/dist/src/index.d.ts +4 -0
  103. package/dist/src/index.js +21 -0
  104. package/dist/src/index.js.map +1 -0
  105. package/dist/src/interfaces/capabilities.interface.d.ts +52 -0
  106. package/dist/src/interfaces/capabilities.interface.js +3 -0
  107. package/dist/src/interfaces/capabilities.interface.js.map +1 -0
  108. package/dist/src/interfaces/guards.interface.d.ts +4 -0
  109. package/dist/src/interfaces/guards.interface.js +3 -0
  110. package/dist/src/interfaces/guards.interface.js.map +1 -0
  111. package/dist/src/interfaces/index.d.ts +2 -0
  112. package/dist/src/interfaces/index.js +19 -0
  113. package/dist/src/interfaces/index.js.map +1 -0
  114. package/dist/src/interfaces/mcp-server-options.interface.d.ts +42 -0
  115. package/dist/src/interfaces/mcp-server-options.interface.js +3 -0
  116. package/dist/src/interfaces/mcp-server-options.interface.js.map +1 -0
  117. package/dist/src/mcp.module.d.ts +13 -0
  118. package/dist/src/mcp.module.js +176 -0
  119. package/dist/src/mcp.module.js.map +1 -0
  120. package/dist/src/registry/discovery.service.d.ts +16 -0
  121. package/dist/src/registry/discovery.service.js +85 -0
  122. package/dist/src/registry/discovery.service.js.map +1 -0
  123. package/dist/src/registry/index.d.ts +2 -0
  124. package/dist/src/registry/index.js +19 -0
  125. package/dist/src/registry/index.js.map +1 -0
  126. package/dist/src/registry/logger.service.d.ts +16 -0
  127. package/dist/src/registry/logger.service.js +97 -0
  128. package/dist/src/registry/logger.service.js.map +1 -0
  129. package/dist/src/registry/registry.service.d.ts +14 -0
  130. package/dist/src/registry/registry.service.js +165 -0
  131. package/dist/src/registry/registry.service.js.map +1 -0
  132. package/dist/tsconfig.build.tsbuildinfo +1 -0
  133. package/eslint.config.mjs +40 -0
  134. package/examples/README.md +56 -0
  135. package/examples/async-import/app.module.ts +22 -0
  136. package/examples/async-import/main.ts +15 -0
  137. package/examples/guards/app.module.ts +44 -0
  138. package/examples/guards/guards.resolver.ts +52 -0
  139. package/examples/guards/main.ts +11 -0
  140. package/examples/mixed/app.module.ts +20 -0
  141. package/examples/mixed/main.ts +11 -0
  142. package/examples/mixed/mixed.resolver.ts +56 -0
  143. package/examples/prompts/app.module.ts +20 -0
  144. package/examples/prompts/main.ts +11 -0
  145. package/examples/prompts/prompts.resolver.ts +184 -0
  146. package/examples/resources/app.module.ts +19 -0
  147. package/examples/resources/main.ts +11 -0
  148. package/examples/resources/resources.resolver.ts +123 -0
  149. package/examples/tools/app.module.ts +20 -0
  150. package/examples/tools/main.ts +11 -0
  151. package/examples/tools/tools.resolver.ts +205 -0
  152. package/nest-cli.json +8 -0
  153. package/package.json +106 -0
  154. package/scripts/npm-publish.js +301 -0
  155. package/src/controllers/sse/index.ts +2 -0
  156. package/src/controllers/sse/sse.controller.ts +19 -0
  157. package/src/controllers/sse/sse.service.ts +90 -0
  158. package/src/controllers/streamable/index.ts +2 -0
  159. package/src/controllers/streamable/streamable.controller.ts +24 -0
  160. package/src/controllers/streamable/streamable.service.ts +168 -0
  161. package/src/decorators/capabilities.constants.ts +7 -0
  162. package/src/decorators/capabilities.decorators.ts +150 -0
  163. package/src/decorators/index.ts +2 -0
  164. package/src/index.ts +11 -0
  165. package/src/interfaces/capabilities.interface.ts +95 -0
  166. package/src/interfaces/guards.interface.ts +13 -0
  167. package/src/interfaces/index.ts +2 -0
  168. package/src/interfaces/mcp-server-options.interface.ts +105 -0
  169. package/src/mcp.module.ts +233 -0
  170. package/src/mcp.service.spec.ts +28 -0
  171. package/src/registry/discovery.service.ts +116 -0
  172. package/src/registry/index.ts +2 -0
  173. package/src/registry/logger.service.ts +143 -0
  174. package/src/registry/registry.service.ts +281 -0
  175. package/test/base.e2e-spec.ts +74 -0
  176. package/test/jest-e2e.json +9 -0
  177. package/tsconfig.build.json +4 -0
  178. package/tsconfig.json +23 -0
@@ -0,0 +1,90 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
3
+ import { Inject, Injectable, OnModuleInit } from '@nestjs/common';
4
+ import { Request, Response } from 'express';
5
+
6
+ import { McpServerOptions } from '../../interfaces/mcp-server-options.interface';
7
+ import { McpLoggerService } from '../../registry/logger.service';
8
+ import { RegistryService } from '../../registry/registry.service';
9
+
10
+ @Injectable()
11
+ export class SseService implements OnModuleInit {
12
+ private server: McpServer;
13
+
14
+ private transports = {} as Record<string, SSEServerTransport>;
15
+
16
+ constructor(
17
+ @Inject('MCP_SERVER_OPTIONS')
18
+ private readonly options: McpServerOptions,
19
+ private readonly registry: RegistryService,
20
+ private readonly logger: McpLoggerService,
21
+ ) {
22
+ this.server = new McpServer(this.options.serverInfo, this.options.options);
23
+ }
24
+
25
+ async onModuleInit() {
26
+ this.logger.log('Starting MCP controller registration', 'MCP_SERVER');
27
+ await this.registry.registerAll(this.server);
28
+ this.logger.log('MCP initialization completed', 'MCP_SERVER');
29
+ }
30
+
31
+ /**
32
+ * Handle an SSE request for server-sent events
33
+ *
34
+ * This establishes a connection for server-to-client notifications
35
+ */
36
+ async handleSse(req: Request, res: Response) {
37
+ // Create SSE transport for legacy clients
38
+ const transport = new SSEServerTransport('/messages', res);
39
+ this.transports[transport.sessionId] = transport;
40
+
41
+ this.logger.debug(
42
+ `Starting SSE for sessionId: ${transport.sessionId}`,
43
+ 'api',
44
+ );
45
+
46
+ res.on('close', () => {
47
+ delete this.transports[transport.sessionId];
48
+ });
49
+
50
+ await this.server.connect(transport);
51
+ }
52
+
53
+ /**
54
+ * Handle SSE messages sent from client to server
55
+ */
56
+ async handleMessage(req: Request, res: Response) {
57
+ const sessionId = req.query.sessionId as string;
58
+ const transport = this.transports[sessionId];
59
+
60
+ this.logger.debug(
61
+ `Receiving SSE message for sessionId: ${sessionId}`,
62
+ 'api',
63
+ );
64
+
65
+ this.logger.debug(`SSE message: ${JSON.stringify(req.body)}`, 'MCP_SERVER');
66
+
67
+ try {
68
+ if (transport) {
69
+ await transport.handlePostMessage(req, res, req.body);
70
+ } else {
71
+ res.status(400).send('No transport found for sessionId');
72
+ }
73
+ } catch (error) {
74
+ const errorMessage =
75
+ error instanceof Error ? error.message : 'Unknown error';
76
+
77
+ this.logger.error(
78
+ 'Error al manejar mensaje SSE',
79
+ errorMessage,
80
+ 'MCP_SERVER',
81
+ );
82
+
83
+ res.status(500).send({
84
+ statusCode: 500,
85
+ error: 'Internal Server Error',
86
+ message: errorMessage,
87
+ });
88
+ }
89
+ }
90
+ }
@@ -0,0 +1,2 @@
1
+ export * from './streamable.controller';
2
+ export * from './streamable.service';
@@ -0,0 +1,24 @@
1
+ import { Controller, Delete, Get, Post, Req, Res } from '@nestjs/common';
2
+ import { Request, Response } from 'express';
3
+
4
+ import { StreamableService } from './streamable.service';
5
+
6
+ @Controller()
7
+ export class StreamableController {
8
+ constructor(private readonly service: StreamableService) {}
9
+
10
+ @Post('mcp')
11
+ async handleMcpPost(@Req() req: Request, @Res() res: Response) {
12
+ await this.service.handlePostRequest(req, res);
13
+ }
14
+
15
+ @Get('mcp')
16
+ async handleMcpGet(@Req() req: Request, @Res() res: Response) {
17
+ await this.service.handleGetRequest(req, res);
18
+ }
19
+
20
+ @Delete('mcp')
21
+ async handleMcpDelete(@Req() req: Request, @Res() res: Response) {
22
+ await this.service.handleDeleteRequest(req, res);
23
+ }
24
+ }
@@ -0,0 +1,168 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
3
+ import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
4
+ import { Inject, Injectable, OnModuleInit } from '@nestjs/common';
5
+ import { randomUUID } from 'crypto';
6
+ import { Request, Response } from 'express';
7
+
8
+ import {
9
+ McpModuleTransportOptions,
10
+ McpServerOptions,
11
+ } from '../../interfaces/mcp-server-options.interface';
12
+ import { McpLoggerService } from '../../registry/logger.service';
13
+ import { RegistryService } from '../../registry/registry.service';
14
+
15
+ // TODO: Stateless mode should be handled here or in another service
16
+
17
+ @Injectable()
18
+ export class StreamableService implements OnModuleInit {
19
+ private server: McpServer;
20
+
21
+ private transports = {} as Record<string, StreamableHTTPServerTransport>;
22
+
23
+ constructor(
24
+ @Inject('MCP_SERVER_OPTIONS')
25
+ private readonly options: McpServerOptions,
26
+ @Inject('MCP_TRANSPORT_OPTIONS')
27
+ private readonly transportOptions: McpModuleTransportOptions,
28
+ private readonly registry: RegistryService,
29
+ private readonly logger: McpLoggerService,
30
+ ) {
31
+ this.server = new McpServer(this.options.serverInfo, this.options.options);
32
+ }
33
+
34
+ async onModuleInit() {
35
+ await this.registry.registerAll(this.server);
36
+
37
+ this.logger.log('MCP STREAMEABLE initialization completed');
38
+ }
39
+
40
+ /**
41
+ * Handle a streamable HTTP POST request from a client
42
+ *
43
+ * - Uses sessionId from query or generates a new one if missing
44
+ * - Stores the transport by sessionId for later retrieval
45
+ * - Cleans up transport on connection close
46
+ *
47
+ * @param req Express Request object (expects sessionId in query)
48
+ * @param res Express Response object
49
+ */
50
+ async handlePostRequest(
51
+ req: Request<any, any, any, { sessionId?: string }>,
52
+ res: Response,
53
+ ) {
54
+ const sessionId = req.headers['mcp-session-id'] as string | undefined;
55
+ let transport: StreamableHTTPServerTransport;
56
+
57
+ const { options } = this.transportOptions?.streamable || {};
58
+
59
+ if (sessionId && this.transports[sessionId]) {
60
+ transport = this.transports[sessionId];
61
+ } else if (!sessionId && isInitializeRequest(req.body)) {
62
+ transport = new StreamableHTTPServerTransport({
63
+ sessionIdGenerator: () =>
64
+ options?.sessionIdGenerator?.() || randomUUID(),
65
+ onsessioninitialized: (sessionId) => {
66
+ this.transports[sessionId] = transport;
67
+ },
68
+ enableJsonResponse: options?.enableJsonResponse,
69
+ eventStore: options?.eventStore,
70
+ });
71
+
72
+ transport.onclose = () => {
73
+ if (transport.sessionId) {
74
+ delete this.transports[transport.sessionId];
75
+ }
76
+ };
77
+ } else {
78
+ // Invalid request
79
+ res.status(400).json({
80
+ jsonrpc: '2.0',
81
+ error: {
82
+ code: -32000,
83
+ message: 'Bad Request: No valid session ID provided',
84
+ },
85
+ id: null,
86
+ });
87
+
88
+ return;
89
+ }
90
+
91
+ await transport.handleRequest(req, res, req.body);
92
+ }
93
+
94
+ /**
95
+ * Handle a streamable HTTP GET request from a client
96
+ *
97
+ * This method retrieves the existing streamable transport for the session and delegates the request.
98
+ *
99
+ * @param req Express Request object (expects sessionId in query)
100
+ * @param res Express Response object
101
+ */
102
+ async handleGetRequest(
103
+ req: Request<any, any, any, { sessionId?: string }>,
104
+ res: Response,
105
+ ) {
106
+ const sessionId = req.headers['mcp-session-id'] as string | undefined;
107
+
108
+ if (!sessionId || !this.transports[sessionId]) {
109
+ res.status(400).send('Invalid or missing session ID');
110
+ return;
111
+ }
112
+
113
+ const transport = this.transports[sessionId];
114
+
115
+ await transport.handleRequest(req, res);
116
+ }
117
+
118
+ /**
119
+ * Handle a streamable HTTP DELETE request to clean up a session
120
+ *
121
+ * - Accepts sessionId from query or x-mcp-session-id header
122
+ * - Closes and removes the transport if found
123
+ * - Always sends a response
124
+ *
125
+ * @param req Express Request object
126
+ * @param res Express Response object
127
+ */
128
+ async handleDeleteRequest(
129
+ req: Request<any, any, any, { sessionId?: string }>,
130
+ res: Response,
131
+ ) {
132
+ const sessionId = req.headers['mcp-session-id'] as string | undefined;
133
+
134
+ if (!sessionId) {
135
+ res.status(400).json({ error: 'Missing sessionId' });
136
+ return;
137
+ }
138
+
139
+ const transport = this.transports[sessionId];
140
+
141
+ if (transport) {
142
+ this.logger.debug(
143
+ `Closing streamable transport for sessionId: ${sessionId}`,
144
+ 'STREAMABLE',
145
+ );
146
+
147
+ await transport.close();
148
+
149
+ const uuidV4Regex =
150
+ /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
151
+
152
+ if (!uuidV4Regex.test(sessionId)) {
153
+ res.status(400).json({ error: 'Invalid sessionId format' });
154
+ return;
155
+ }
156
+
157
+ delete this.transports[sessionId];
158
+
159
+ res.status(200).json({ success: true, sessionId });
160
+ } else {
161
+ this.logger.debug(
162
+ `No streamable transport found for sessionId: ${sessionId}`,
163
+ 'STREAMABLE',
164
+ );
165
+ res.status(404).json({ error: 'Transport not found', sessionId });
166
+ }
167
+ }
168
+ }
@@ -0,0 +1,7 @@
1
+ // Prefijo para todas las constantes de metadatos
2
+ export const PREFIX = 'MCP';
3
+
4
+ // Constantes de metadatos para decoradores de método
5
+ export const MCP_TOOL = `${PREFIX}:tool`;
6
+ export const MCP_PROMPT = `${PREFIX}:prompt`;
7
+ export const MCP_RESOURCE = `${PREFIX}:resource`;
@@ -0,0 +1,150 @@
1
+ import { Injectable, SetMetadata } from '@nestjs/common';
2
+
3
+ import {
4
+ PromptOptions,
5
+ ResourceOptions,
6
+ ToolOptions,
7
+ } from '../interfaces/capabilities.interface';
8
+ import { MCP_PROMPT, MCP_RESOURCE, MCP_TOOL } from './capabilities.constants';
9
+
10
+ /**
11
+ * Metadata key to mark a class as an MCP Resolver.
12
+ */
13
+ export const MCP_RESOLVER = '__mcp_resolver__';
14
+
15
+ /**
16
+ * Metadata key to attach guards to a Resolver class or method.
17
+ */
18
+ export const MCP_GUARDS = '__mcp_guards__';
19
+
20
+ /**
21
+ * Decorator for marking a class as an MCP Resolver.
22
+ * Enables dependency injection and workspace grouping for MCP capabilities.
23
+ *
24
+ * @param workspace Optional workspace/namespace for grouping capabilities
25
+ * @example
26
+ * @Resolver('my-workspace')
27
+ * export class MyResolver { ... }
28
+ */
29
+ export function Resolver(workspace?: string): ClassDecorator {
30
+ return function (target: any) {
31
+ Injectable()(target);
32
+ SetMetadata(MCP_RESOLVER, workspace || true)(target);
33
+ };
34
+ }
35
+
36
+ /**
37
+ * Decorator to attach one or more guards to a Resolver class or method.
38
+ * Accepts guard classes or instances implementing CanActivate.
39
+ *
40
+ * @param guards One or more guard classes or instances
41
+ * @example
42
+ * @UseGuards(MyGuard)
43
+ * @Resolver('workspace')
44
+ * export class MyResolver { ... }
45
+ */
46
+ export function UseGuards(...guards: any[]): ClassDecorator & MethodDecorator {
47
+ return SetMetadata(MCP_GUARDS, guards);
48
+ }
49
+
50
+ /**
51
+ * Decorator for marking a method as an MCP Tool.
52
+ * Use with @McpProvider.
53
+ *
54
+ * La herramienta debe devolver un objeto con el formato:
55
+ * {
56
+ * content: [
57
+ * {
58
+ * type: 'text', // Puede ser 'text', 'image', 'video', 'audio', etc.
59
+ * text: 'Texto de la respuesta',
60
+ * }
61
+ * ]
62
+ * }
63
+ *
64
+ * @param options Tool configuration
65
+ */
66
+ export function Tool(options: ToolOptions) {
67
+ return function (
68
+ target: object,
69
+ propertyKey: string,
70
+ descriptor: PropertyDescriptor,
71
+ ) {
72
+ SetMetadata(MCP_TOOL, {
73
+ ...options,
74
+ methodName: propertyKey,
75
+ })(target, propertyKey, descriptor);
76
+
77
+ return descriptor;
78
+ };
79
+ }
80
+
81
+ /**
82
+ * Decorator for marking a method as an MCP Prompt.
83
+ * Use with @McpProvider.
84
+ *
85
+ * El prompt debe devolver un objeto con el formato:
86
+ * {
87
+ * messages: [
88
+ * {
89
+ * role: 'assistant',
90
+ * content: {
91
+ * type: 'text',
92
+ * text: 'Texto del mensaje'
93
+ * }
94
+ * }
95
+ * ]
96
+ * }
97
+ *
98
+ * @param options Prompt configuration
99
+ */
100
+ export function Prompt(options: PromptOptions) {
101
+ return function (
102
+ target: object,
103
+ propertyKey: string,
104
+ descriptor: PropertyDescriptor,
105
+ ) {
106
+ SetMetadata(MCP_PROMPT, {
107
+ ...options,
108
+ methodName: propertyKey,
109
+ })(target, propertyKey, descriptor);
110
+
111
+ return descriptor;
112
+ };
113
+ }
114
+
115
+ /**
116
+ * Decorator for marking a method as an MCP Resource provider.
117
+ * Use with @McpProvider.
118
+ *
119
+ * Hay dos modos de uso para los recursos:
120
+ *
121
+ * 1. Recurso con URI fija:
122
+ * @Resource({
123
+ * name: 'nombreRecurso',
124
+ * uri: 'resource://midominio/recurso'
125
+ * })
126
+ *
127
+ * 2. Recurso con plantilla (para parámetros dinámicos):
128
+ * @Resource({
129
+ * name: 'nombreRecurso',
130
+ * template: 'resource://midominio/recurso/{parametro}'
131
+ * })
132
+ *
133
+ * También se puede proporcionar solo el nombre como string:
134
+ * @Resource('nombreRecurso')
135
+ *
136
+ * @param options Resource configuration or just the name as a string
137
+ */
138
+ export function Resource(options: ResourceOptions) {
139
+ return function (
140
+ target: object,
141
+ propertyKey: string,
142
+ descriptor: PropertyDescriptor,
143
+ ) {
144
+ SetMetadata(MCP_RESOURCE, {
145
+ ...options,
146
+ methodName: propertyKey,
147
+ })(target, propertyKey, descriptor);
148
+ return descriptor;
149
+ };
150
+ }
@@ -0,0 +1,2 @@
1
+ export * from './capabilities.constants';
2
+ export * from './capabilities.decorators';
package/src/index.ts ADDED
@@ -0,0 +1,11 @@
1
+ // Core components
2
+ export * from './mcp.module';
3
+
4
+ // Interfaces
5
+ export * from './interfaces';
6
+
7
+ // Registry components
8
+ export * from './registry';
9
+
10
+ // Decorators
11
+ export * from './decorators';
@@ -0,0 +1,95 @@
1
+ import {
2
+ CompleteResourceTemplateCallback,
3
+ ListResourcesCallback,
4
+ } from '@modelcontextprotocol/sdk/server/mcp';
5
+ import { ZodOptional, ZodRawShape, ZodType, ZodTypeDef } from 'zod';
6
+
7
+ export interface ResourceBaseOptions {
8
+ name: string;
9
+ }
10
+
11
+ export interface ResourceUriOptions extends ResourceBaseOptions {
12
+ uri: string;
13
+ }
14
+
15
+ export interface ResourceUriWithMetadataOptions extends ResourceUriOptions {
16
+ metadata: Record<string, any>;
17
+ }
18
+
19
+ export interface ResourceTemplateOptions extends ResourceBaseOptions {
20
+ template: string;
21
+ }
22
+
23
+ export interface ResourceTemplateWithMetadataOptions
24
+ extends ResourceTemplateOptions {
25
+ metadata: Record<string, any>;
26
+ }
27
+
28
+ export type ResourceOptions =
29
+ | ResourceUriOptions
30
+ | ResourceUriWithMetadataOptions
31
+ | ResourceTemplateOptions
32
+ | ResourceTemplateWithMetadataOptions;
33
+
34
+ export interface ToolNameOptions {
35
+ name: string;
36
+ }
37
+
38
+ export interface ToolWithDescriptionOptions extends ToolNameOptions {
39
+ description: string;
40
+ }
41
+
42
+ export interface ToolWithParamSchemaOptions extends ToolNameOptions {
43
+ paramSchema: ZodRawShape;
44
+ }
45
+
46
+ export interface ToolWithDescriptionAndParamSchemaOptions
47
+ extends ToolWithDescriptionOptions,
48
+ ToolWithParamSchemaOptions {}
49
+
50
+ export type ToolOptions =
51
+ | ToolNameOptions
52
+ | ToolWithDescriptionOptions
53
+ | ToolWithParamSchemaOptions
54
+ | ToolWithDescriptionAndParamSchemaOptions;
55
+
56
+ export interface PromptBaseOptions {
57
+ name: string;
58
+ }
59
+
60
+ export interface PromptWithDescriptionOptions extends PromptBaseOptions {
61
+ description: string;
62
+ }
63
+
64
+ type PromptArgsRawShape = {
65
+ [k: string]:
66
+ | ZodType<string, ZodTypeDef, string>
67
+ | ZodOptional<ZodType<string, ZodTypeDef, string>>;
68
+ };
69
+
70
+ export interface PromptWithArgsSchemaOptions extends PromptBaseOptions {
71
+ argsSchema: PromptArgsRawShape;
72
+ }
73
+
74
+ export interface PromptWithDescriptionAndArgsSchemaOptions
75
+ extends PromptWithDescriptionOptions,
76
+ PromptWithArgsSchemaOptions {}
77
+
78
+ export type PromptOptions =
79
+ | PromptBaseOptions
80
+ | PromptWithDescriptionOptions
81
+ | PromptWithArgsSchemaOptions
82
+ | PromptWithDescriptionAndArgsSchemaOptions;
83
+
84
+ export interface TemplateCallbacks {
85
+ /**
86
+ * A callback to list all resources matching this template. This is required to specified, even if `undefined`, to avoid accidentally forgetting resource listing.
87
+ */
88
+ list: ListResourcesCallback | undefined;
89
+ /**
90
+ * An optional callback to autocomplete variables within the URI template. Useful for clients and users to discover possible values.
91
+ */
92
+ complete?: {
93
+ [variable: string]: CompleteResourceTemplateCallback;
94
+ };
95
+ }
@@ -0,0 +1,13 @@
1
+ import { ExecutionContext } from '@nestjs/common';
2
+
3
+ /**
4
+ * Custom execution context for MCP guards.
5
+ * Extends NestJS ExecutionContext and adds args for MCP method arguments.
6
+ *
7
+ * @property args - The arguments passed to the MCP method
8
+ */
9
+ // TODO: Type Args correctly
10
+ export interface McpExecutionContext extends ExecutionContext {
11
+ /** The arguments passed to the MCP method */
12
+ args: unknown[];
13
+ }
@@ -0,0 +1,2 @@
1
+ export * from './guards.interface';
2
+ export * from './mcp-server-options.interface';
@@ -0,0 +1,105 @@
1
+ import { StreamableHTTPServerTransportOptions } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
2
+ import { ProtocolOptions } from '@modelcontextprotocol/sdk/shared/protocol';
3
+ import {
4
+ Implementation,
5
+ ServerCapabilities,
6
+ } from '@modelcontextprotocol/sdk/types';
7
+ import { Provider, Type } from '@nestjs/common';
8
+
9
+ export type ServerOptions = {
10
+ instructions?: string;
11
+ capabilities?: ServerCapabilities;
12
+ protocolOptions?: ProtocolOptions;
13
+ };
14
+
15
+ export type McpServerOptions = {
16
+ serverInfo: Implementation;
17
+ options?: ServerOptions;
18
+ logging?: McpLoggingOptions;
19
+ };
20
+
21
+ /**
22
+ * Opciones para configurar el logging del servidor MCP
23
+ */
24
+ export interface McpLoggingOptions {
25
+ /**
26
+ * Habilitar o deshabilitar el logging
27
+ * @default true
28
+ */
29
+ enabled?: boolean;
30
+
31
+ /**
32
+ * Nivel de detalle del logging
33
+ * @default 'verbose'
34
+ */
35
+ level?: 'debug' | 'verbose' | 'log' | 'warn' | 'error';
36
+ }
37
+
38
+ /**
39
+ * Options for configuring the global MCP server module
40
+ */
41
+ export interface McpModuleOptions {
42
+ /**
43
+ * Additional modules to import
44
+ */
45
+ imports?: Type<any>[];
46
+ /**
47
+ * Providers to register in the module
48
+ * These will be available globally
49
+ */
50
+ providers?: Provider[];
51
+ /**
52
+ * Name of the MCP server
53
+ */
54
+ name: string;
55
+ /**
56
+ * Version of the MCP server
57
+ */
58
+ version: string;
59
+ /**
60
+ * Description to give the AI about the server
61
+ */
62
+ instructions?: string;
63
+ /**
64
+ * Describes the server's purpose or behavior for the AI
65
+ */
66
+ capabilities?: ServerCapabilities;
67
+ /**
68
+ * Protocol-specific options
69
+ */
70
+ protocolOptions?: ProtocolOptions;
71
+ /**
72
+ * Options for configuring MCP server logging
73
+ */
74
+ logging?: McpLoggingOptions;
75
+ /**
76
+ * Options for configuring a feature module with MCP capabilities
77
+ */
78
+ transports?: McpModuleTransportOptions;
79
+ }
80
+
81
+ export type McpModuleTransportOptions = {
82
+ streamable?: {
83
+ enabled: boolean;
84
+ /**
85
+ * Streamable transport options. sessionIdGenerator is optional here, even if required in the SDK type.
86
+ */
87
+ options?: Omit<
88
+ StreamableHTTPServerTransportOptions,
89
+ 'onsessioninitialized' | 'sessionIdGenerator'
90
+ > & {
91
+ sessionIdGenerator?: () => string | undefined;
92
+ };
93
+ };
94
+ sse?: {
95
+ enabled: boolean;
96
+ };
97
+ };
98
+
99
+ /**
100
+ * Options for configuring a feature module with MCP capabilities
101
+ */
102
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type
103
+ export interface McpFeatureOptions {
104
+ // TODO: Maybe its needed to implement Guards for all capabilities in this module o a specific logger configuration
105
+ }