@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,233 @@
1
+ import { Implementation } from '@modelcontextprotocol/sdk/types';
2
+ import { DynamicModule, Module, Provider, Type } from '@nestjs/common';
3
+ import { DiscoveryModule } from '@nestjs/core';
4
+
5
+ import { SseController, SseService } from './controllers/sse';
6
+ import {
7
+ StreamableController,
8
+ StreamableService,
9
+ } from './controllers/streamable';
10
+ import {
11
+ McpFeatureOptions,
12
+ McpLoggingOptions,
13
+ McpModuleOptions,
14
+ McpModuleTransportOptions,
15
+ ServerOptions,
16
+ } from './interfaces/mcp-server-options.interface';
17
+ import { DiscoveryService } from './registry/discovery.service';
18
+ import { McpLoggerService } from './registry/logger.service';
19
+ import { RegistryService } from './registry/registry.service';
20
+
21
+ @Module({
22
+ imports: [DiscoveryModule],
23
+ providers: [RegistryService, DiscoveryService, McpLoggerService],
24
+ })
25
+ export class McpModule {
26
+ /**
27
+ * Helper: Get active transport controllers and providers
28
+ */
29
+ private static getActiveTransportControllersAndProviders(
30
+ transports?: McpModuleTransportOptions,
31
+ ) {
32
+ const controllers = new Set<Type<any>>();
33
+ const providers = new Set<Provider>();
34
+
35
+ // Transport configurations
36
+ const STREAMABLE_TRANSPORT = {
37
+ controller: StreamableController,
38
+ service: StreamableService,
39
+ };
40
+
41
+ const SSE_TRANSPORT = {
42
+ controller: SseController,
43
+ service: SseService,
44
+ };
45
+
46
+ // Default configuration
47
+ const defaultTransports: McpModuleTransportOptions = {
48
+ streamable: { enabled: true },
49
+ sse: { enabled: true },
50
+ };
51
+
52
+ // Merge default with provided transports
53
+ const config = {
54
+ streamable: {
55
+ ...defaultTransports.streamable,
56
+ ...(transports?.streamable ?? {}),
57
+ },
58
+ sse: {
59
+ ...defaultTransports.sse,
60
+ ...(transports?.sse ?? {}),
61
+ },
62
+ };
63
+
64
+ // Add controllers and providers based on enabled transports
65
+ if (config.streamable.enabled) {
66
+ controllers.add(STREAMABLE_TRANSPORT.controller);
67
+ providers.add(STREAMABLE_TRANSPORT.service);
68
+ }
69
+
70
+ if (config.sse.enabled) {
71
+ controllers.add(SSE_TRANSPORT.controller);
72
+ providers.add(SSE_TRANSPORT.service);
73
+ }
74
+
75
+ return {
76
+ controllers: Array.from(controllers),
77
+ providers: Array.from(providers),
78
+ };
79
+ }
80
+
81
+ /**
82
+ * Helper to build server info, options, and logging config
83
+ */
84
+ private static buildServerConfig(options: McpModuleOptions) {
85
+ const serverInfo: Implementation = {
86
+ name: options.name,
87
+ version: options.version,
88
+ };
89
+ const serverOptions: ServerOptions = {
90
+ instructions: options?.instructions,
91
+ capabilities: options?.capabilities,
92
+ ...(options?.protocolOptions || {}),
93
+ };
94
+ const loggingOptions: McpLoggingOptions = {
95
+ enabled: options.logging?.enabled !== false,
96
+ level: options.logging?.level || 'verbose',
97
+ };
98
+ return { serverInfo, serverOptions, loggingOptions };
99
+ }
100
+
101
+ /**
102
+ * Configures the MCP module with global options
103
+ *
104
+ * @param options Configuration options for the MCP server
105
+ * @returns Dynamic module configuration
106
+ */
107
+ static forRoot(options: McpModuleOptions): DynamicModule {
108
+ const imports = options.imports || [];
109
+ const { controllers, providers } =
110
+ this.getActiveTransportControllersAndProviders(options.transports);
111
+ const allProviders = [...(options.providers || []), ...providers];
112
+ const { serverInfo, serverOptions, loggingOptions } =
113
+ this.buildServerConfig(options);
114
+ return {
115
+ module: McpModule,
116
+ imports,
117
+ controllers,
118
+ providers: [
119
+ ...allProviders,
120
+ {
121
+ provide: 'MCP_SERVER_OPTIONS',
122
+ useValue: {
123
+ serverInfo,
124
+ options: serverOptions,
125
+ logging: loggingOptions,
126
+ },
127
+ },
128
+ {
129
+ provide: 'MCP_LOGGING_OPTIONS',
130
+ useValue: loggingOptions,
131
+ },
132
+ {
133
+ provide: 'MCP_TRANSPORT_OPTIONS',
134
+ useValue: options.transports,
135
+ },
136
+ ],
137
+ global: true,
138
+ };
139
+ }
140
+
141
+ /**
142
+ * Configures the MCP module with global options and ConfigModule support
143
+ * Allows using environment variables and centralized configurations
144
+ *
145
+ * @param options Configuration options for the MCP server
146
+ * @returns Dynamic module configuration
147
+ */
148
+ static forRootAsync(options: {
149
+ imports?: any[];
150
+ useFactory: (
151
+ ...args: unknown[]
152
+ ) => Promise<McpModuleOptions> | McpModuleOptions;
153
+ inject?: any[];
154
+ }): DynamicModule {
155
+ const { imports = [], useFactory, inject = [] } = options;
156
+ const safeInject = Array.isArray(inject) ? inject : [];
157
+ const safeImports = Array.isArray(imports) ? imports : [];
158
+ const providers = [
159
+ {
160
+ provide: 'MCP_SERVER_OPTIONS',
161
+ useFactory: async (...args: unknown[]) => {
162
+ const mcpOptions = await useFactory(...args);
163
+ const { serverInfo, serverOptions, loggingOptions } =
164
+ this.buildServerConfig(mcpOptions);
165
+ return {
166
+ serverInfo,
167
+ options: serverOptions,
168
+ logging: loggingOptions,
169
+ };
170
+ },
171
+ inject: safeInject,
172
+ },
173
+ {
174
+ provide: 'MCP_LOGGING_OPTIONS',
175
+ useFactory: async (...args: unknown[]) => {
176
+ const mcpOptions = await useFactory(...args);
177
+ return {
178
+ enabled: mcpOptions.logging?.enabled !== false,
179
+ level: mcpOptions.logging?.level || 'verbose',
180
+ };
181
+ },
182
+ inject: safeInject,
183
+ },
184
+ ];
185
+ const asyncControllersFactory = async (...args: unknown[]) => {
186
+ const mcpOptions = await useFactory(...args);
187
+ return this.getActiveTransportControllersAndProviders(
188
+ mcpOptions.transports,
189
+ ).controllers;
190
+ };
191
+ const asyncProvidersFactory = async (...args: unknown[]) => {
192
+ const mcpOptions = await useFactory(...args);
193
+ const { providers } = this.getActiveTransportControllersAndProviders(
194
+ mcpOptions.transports,
195
+ );
196
+ return [...(mcpOptions.providers || []), ...providers];
197
+ };
198
+ return {
199
+ module: McpModule,
200
+ imports: safeImports,
201
+ controllers: [], // Will be resolved at runtime by NestJS
202
+ providers: [
203
+ ...providers,
204
+ {
205
+ provide: '__MCP_ASYNC_CONTROLLERS__',
206
+ useFactory: asyncControllersFactory,
207
+ inject: safeInject,
208
+ },
209
+ {
210
+ provide: '__MCP_ASYNC_PROVIDERS__',
211
+ useFactory: asyncProvidersFactory,
212
+ inject: safeInject,
213
+ },
214
+ ],
215
+ global: true,
216
+ };
217
+ }
218
+
219
+ /**
220
+ * Registers feature-specific capabilities like tools, prompts, and resources
221
+ * through dedicated service providers
222
+ *
223
+ * @param options Configuration options for the feature module
224
+ * @returns A dynamic module configuration
225
+ */
226
+ // TODO: Implement specific Module options
227
+
228
+ static forFeature(_options?: McpFeatureOptions): DynamicModule {
229
+ return {
230
+ module: McpModule,
231
+ };
232
+ }
233
+ }
@@ -0,0 +1,28 @@
1
+ import { Test, TestingModule } from '@nestjs/testing';
2
+
3
+ import { SseService } from './controllers/sse';
4
+ import { StreamableService } from './controllers/streamable';
5
+ import { McpModule } from './mcp.module';
6
+ describe('NestjsMcpServerService', () => {
7
+ let streamableService: StreamableService;
8
+ let sseService: SseService;
9
+
10
+ beforeEach(async () => {
11
+ const module: TestingModule = await Test.createTestingModule({
12
+ imports: [
13
+ McpModule.forRoot({
14
+ name: 'test',
15
+ version: '1.0.0',
16
+ }),
17
+ ],
18
+ }).compile();
19
+
20
+ streamableService = module.get<StreamableService>(StreamableService);
21
+ sseService = module.get<SseService>(SseService);
22
+ });
23
+
24
+ it('should be defined', () => {
25
+ expect(streamableService).toBeDefined();
26
+ expect(sseService).toBeDefined();
27
+ });
28
+ });
@@ -0,0 +1,116 @@
1
+ import { Injectable } from '@nestjs/common';
2
+ import {
3
+ MetadataScanner,
4
+ DiscoveryService as NestDiscoveryService,
5
+ Reflector,
6
+ } from '@nestjs/core';
7
+ import { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper';
8
+
9
+ export interface MethodWithMetadata<T = unknown> {
10
+ method: string;
11
+ metadata: T;
12
+ handler: (...args: any[]) => any;
13
+ instance: object;
14
+ }
15
+
16
+ type ProviderInstance = Record<string, unknown>;
17
+
18
+ @Injectable()
19
+ export class DiscoveryService {
20
+ constructor(
21
+ private readonly discoveryService: NestDiscoveryService,
22
+ private readonly metadataScanner: MetadataScanner,
23
+ private readonly reflector: Reflector,
24
+ ) {}
25
+
26
+ /**
27
+ * Get all methods with specific metadata from all providers (now scans all @Injectable, not just @McpProvider)
28
+ */
29
+ public getAllMethodsWithMetadata<T = unknown>(
30
+ metadataKey: string,
31
+ ): MethodWithMetadata<T>[] {
32
+ const providers = this.discoveryService.getProviders();
33
+ const result: MethodWithMetadata<T>[] = [];
34
+
35
+ // Scan ALL providers, not just those with MCP_PROVIDER
36
+ for (const provider of providers) {
37
+ if (!provider.instance) {
38
+ continue;
39
+ }
40
+
41
+ const methods = this.getMethodsWithMetadataFromProvider<T>(
42
+ provider,
43
+ metadataKey,
44
+ );
45
+
46
+ if (methods.length > 0) {
47
+ result.push(...methods);
48
+ }
49
+ }
50
+
51
+ return result;
52
+ }
53
+
54
+ /**
55
+ * Get all methods with a specific metadata from a single provider.
56
+ */
57
+ public getMethodsWithMetadataFromProvider<T = unknown>(
58
+ provider: InstanceWrapper,
59
+ metadataKey: string,
60
+ ): MethodWithMetadata<T>[] {
61
+ if (!provider.instance) {
62
+ return [];
63
+ }
64
+
65
+ const instance = provider.instance as ProviderInstance;
66
+ const instancePrototype = Object.getPrototypeOf(instance) as Record<
67
+ string,
68
+ unknown
69
+ >;
70
+
71
+ if (!instancePrototype) {
72
+ return [];
73
+ }
74
+
75
+ const methodNames =
76
+ this.metadataScanner.getAllMethodNames(instancePrototype);
77
+ const result: MethodWithMetadata<T>[] = [];
78
+
79
+ for (const methodName of methodNames) {
80
+ // Removed hasOwnProperty check to allow inherited methods
81
+ const methodFunction = instancePrototype[methodName];
82
+ if (typeof methodFunction !== 'function') {
83
+ continue;
84
+ }
85
+
86
+ const metadata = this.reflector.get<T>(metadataKey, methodFunction);
87
+
88
+ if (metadata) {
89
+ const handlerProperty = instance[methodName];
90
+
91
+ if (typeof handlerProperty !== 'function') {
92
+ continue;
93
+ }
94
+
95
+ const handler = ((...args: unknown[]): unknown => {
96
+ return handlerProperty.apply(instance, args);
97
+ }) as (...args: any[]) => any;
98
+
99
+ // Preserve the original method name
100
+ Object.defineProperty(handler, 'name', {
101
+ value: methodName,
102
+ writable: false,
103
+ });
104
+
105
+ result.push({
106
+ method: methodName,
107
+ metadata: metadata as T,
108
+ handler,
109
+ instance,
110
+ });
111
+ }
112
+ }
113
+
114
+ return result;
115
+ }
116
+ }
@@ -0,0 +1,2 @@
1
+ export * from './discovery.service';
2
+ export * from './registry.service';
@@ -0,0 +1,143 @@
1
+ import {
2
+ Inject,
3
+ Injectable,
4
+ Logger,
5
+ LoggerService,
6
+ Optional,
7
+ } from '@nestjs/common';
8
+
9
+ import { McpLoggingOptions } from '../interfaces/mcp-server-options.interface';
10
+
11
+ /**
12
+ * Servicio especializado de logging para el servidor MCP
13
+ */
14
+ @Injectable()
15
+ export class McpLoggerService implements LoggerService {
16
+ private readonly logger: Logger;
17
+ private readonly options: Required<McpLoggingOptions>;
18
+
19
+ constructor(
20
+ @Optional() @Inject('MCP_LOGGING_OPTIONS') options?: McpLoggingOptions,
21
+ ) {
22
+ this.options = {
23
+ enabled: options?.enabled !== false, // Habilitado por defecto
24
+ level: options?.level || 'verbose',
25
+ };
26
+
27
+ this.logger = new Logger('MCP');
28
+ }
29
+
30
+ /**
31
+ * Registra un mensaje de nivel debug
32
+ */
33
+ debug(message: string, context?: string): void {
34
+ if (
35
+ !this.options.enabled ||
36
+ this.getLevelValue(this.options.level) > this.getLevelValue('debug')
37
+ ) {
38
+ return;
39
+ }
40
+
41
+ const formattedContext = this.formatContext(context);
42
+ this.logger.debug(message, formattedContext);
43
+ }
44
+
45
+ /**
46
+ * Registra un mensaje de nivel verbose (detallado)
47
+ */
48
+ verbose(message: string, context?: string): void {
49
+ if (
50
+ !this.options.enabled ||
51
+ this.getLevelValue(this.options.level) > this.getLevelValue('verbose')
52
+ ) {
53
+ return;
54
+ }
55
+
56
+ const formattedContext = this.formatContext(context);
57
+ this.logger.verbose(message, formattedContext);
58
+ }
59
+
60
+ /**
61
+ * Registra un mensaje de nivel log (información)
62
+ */
63
+ log(message: string, context?: string): void {
64
+ if (
65
+ !this.options.enabled ||
66
+ this.getLevelValue(this.options.level) > this.getLevelValue('log')
67
+ ) {
68
+ return;
69
+ }
70
+
71
+ const formattedContext = this.formatContext(context);
72
+ this.logger.log(message, formattedContext);
73
+ }
74
+
75
+ /**
76
+ * Registra un mensaje de nivel warn (advertencia)
77
+ */
78
+ warn(message: string, context?: string): void {
79
+ if (
80
+ !this.options.enabled ||
81
+ this.getLevelValue(this.options.level) > this.getLevelValue('warn')
82
+ ) {
83
+ return;
84
+ }
85
+
86
+ const formattedContext = this.formatContext(context);
87
+ this.logger.warn(message, formattedContext);
88
+ }
89
+
90
+ /**
91
+ * Registra un mensaje de nivel error
92
+ */
93
+ error(message: string, trace?: string, context?: string): void {
94
+ if (
95
+ !this.options.enabled ||
96
+ this.getLevelValue(this.options.level) > this.getLevelValue('error')
97
+ ) {
98
+ return;
99
+ }
100
+
101
+ const formattedContext = this.formatContext(context);
102
+ this.logger.error(message, trace, formattedContext);
103
+ }
104
+
105
+ /**
106
+ * Método para determinar si el logging está habilitado
107
+ */
108
+ isEnabled(): boolean {
109
+ return this.options.enabled;
110
+ }
111
+
112
+ /**
113
+ * Método para obtener el nivel actual de logging
114
+ */
115
+ getLevel(): string {
116
+ return this.options.level;
117
+ }
118
+
119
+ /**
120
+ * Formatea el contexto para añadirle el prefijo '@mcp'
121
+ */
122
+ private formatContext(context?: string): string {
123
+ if (!context) {
124
+ return '@mcp';
125
+ }
126
+ return `@mcp:${context}`;
127
+ }
128
+
129
+ /**
130
+ * Convierte un nivel de log a un valor numérico para comparaciones
131
+ */
132
+ private getLevelValue(level: string): number {
133
+ const levels: Record<string, number> = {
134
+ debug: 0,
135
+ verbose: 1,
136
+ log: 2,
137
+ warn: 3,
138
+ error: 4,
139
+ };
140
+
141
+ return levels[level] ?? 2; // Por defecto, nivel 'log'
142
+ }
143
+ }