@nestjs-mcp/server 0.1.1 → 0.2.0-alpha.1

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 (106) hide show
  1. package/README.md +66 -0
  2. package/dist/controllers/sse/sse.controller.d.ts +3 -1
  3. package/dist/controllers/sse/sse.controller.js +8 -2
  4. package/dist/controllers/sse/sse.controller.js.map +1 -1
  5. package/dist/controllers/sse/sse.service.d.ts +3 -2
  6. package/dist/controllers/sse/sse.service.js +21 -6
  7. package/dist/controllers/sse/sse.service.js.map +1 -1
  8. package/dist/controllers/streamable/streamable.service.d.ts +6 -11
  9. package/dist/controllers/streamable/streamable.service.js +36 -11
  10. package/dist/controllers/streamable/streamable.service.js.map +1 -1
  11. package/dist/index.d.ts +1 -0
  12. package/dist/index.js +1 -0
  13. package/dist/index.js.map +1 -1
  14. package/dist/interfaces/capabilities.interface.d.ts +32 -2
  15. package/dist/interfaces/capabilities.interface.js +51 -0
  16. package/dist/interfaces/capabilities.interface.js.map +1 -1
  17. package/dist/interfaces/context.interface.d.ts +6 -0
  18. package/dist/interfaces/context.interface.js +3 -0
  19. package/dist/interfaces/context.interface.js.map +1 -0
  20. package/dist/interfaces/index.d.ts +2 -1
  21. package/dist/interfaces/index.js +2 -1
  22. package/dist/interfaces/index.js.map +1 -1
  23. package/dist/interfaces/mcp-server-options.interface.d.ts +2 -2
  24. package/dist/interfaces/message.types.d.ts +8 -0
  25. package/dist/interfaces/{guards.interface.js → message.types.js} +1 -1
  26. package/dist/interfaces/message.types.js.map +1 -0
  27. package/dist/mcp.module.js +13 -1
  28. package/dist/mcp.module.js.map +1 -1
  29. package/dist/registry/registry.service.d.ts +7 -1
  30. package/dist/registry/registry.service.js +72 -6
  31. package/dist/registry/registry.service.js.map +1 -1
  32. package/dist/services/index.d.ts +1 -0
  33. package/dist/services/index.js +18 -0
  34. package/dist/services/index.js.map +1 -0
  35. package/dist/services/session.manager.d.ts +14 -0
  36. package/dist/services/session.manager.js +27 -0
  37. package/dist/services/session.manager.js.map +1 -0
  38. package/dist/tsconfig.build.tsbuildinfo +1 -1
  39. package/package.json +23 -16
  40. package/.copilotignore +0 -38
  41. package/.devcontainer/Dockerfile.dev +0 -28
  42. package/.devcontainer/devcontainer.json +0 -56
  43. package/.devcontainer/docker-compose.yml +0 -15
  44. package/.dockerignore +0 -37
  45. package/.prettierrc +0 -4
  46. package/coverage/clover.xml +0 -507
  47. package/coverage/coverage-final.json +0 -19
  48. package/coverage/lcov-report/base.css +0 -224
  49. package/coverage/lcov-report/block-navigation.js +0 -87
  50. package/coverage/lcov-report/favicon.png +0 -0
  51. package/coverage/lcov-report/index.html +0 -206
  52. package/coverage/lcov-report/prettify.css +0 -1
  53. package/coverage/lcov-report/prettify.js +0 -2
  54. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  55. package/coverage/lcov-report/sorter.js +0 -196
  56. package/coverage/lcov-report/src/controllers/sse/index.html +0 -146
  57. package/coverage/lcov-report/src/controllers/sse/index.ts.html +0 -91
  58. package/coverage/lcov-report/src/controllers/sse/sse.controller.ts.html +0 -160
  59. package/coverage/lcov-report/src/controllers/sse/sse.service.ts.html +0 -403
  60. package/coverage/lcov-report/src/controllers/streamable/index.html +0 -146
  61. package/coverage/lcov-report/src/controllers/streamable/index.ts.html +0 -91
  62. package/coverage/lcov-report/src/controllers/streamable/streamable.controller.ts.html +0 -157
  63. package/coverage/lcov-report/src/controllers/streamable/streamable.service.ts.html +0 -655
  64. package/coverage/lcov-report/src/decorators/capabilities.constants.ts.html +0 -106
  65. package/coverage/lcov-report/src/decorators/capabilities.decorators.ts.html +0 -535
  66. package/coverage/lcov-report/src/decorators/index.html +0 -146
  67. package/coverage/lcov-report/src/decorators/index.ts.html +0 -91
  68. package/coverage/lcov-report/src/index.html +0 -131
  69. package/coverage/lcov-report/src/index.ts.html +0 -118
  70. package/coverage/lcov-report/src/interfaces/capabilities.interface.ts.html +0 -703
  71. package/coverage/lcov-report/src/interfaces/index.html +0 -131
  72. package/coverage/lcov-report/src/interfaces/index.ts.html +0 -91
  73. package/coverage/lcov-report/src/mcp.module.ts.html +0 -817
  74. package/coverage/lcov-report/src/registry/discovery.service.ts.html +0 -433
  75. package/coverage/lcov-report/src/registry/index.html +0 -161
  76. package/coverage/lcov-report/src/registry/index.ts.html +0 -91
  77. package/coverage/lcov-report/src/registry/logger.service.ts.html +0 -514
  78. package/coverage/lcov-report/src/registry/registry.service.ts.html +0 -1183
  79. package/coverage/lcov-report/src/services/index.html +0 -116
  80. package/coverage/lcov-report/src/services/session.manager.ts.html +0 -163
  81. package/coverage/lcov.info +0 -912
  82. package/dist/interfaces/guards.interface.d.ts +0 -4
  83. package/dist/interfaces/guards.interface.js.map +0 -1
  84. package/eslint.config.mjs +0 -40
  85. package/src/controllers/sse/index.ts +0 -2
  86. package/src/controllers/sse/sse.controller.ts +0 -25
  87. package/src/controllers/sse/sse.service.ts +0 -106
  88. package/src/controllers/streamable/index.ts +0 -2
  89. package/src/controllers/streamable/streamable.controller.ts +0 -24
  90. package/src/controllers/streamable/streamable.service.ts +0 -190
  91. package/src/decorators/capabilities.constants.ts +0 -7
  92. package/src/decorators/capabilities.decorators.ts +0 -150
  93. package/src/decorators/index.ts +0 -2
  94. package/src/index.ts +0 -11
  95. package/src/interfaces/capabilities.interface.ts +0 -206
  96. package/src/interfaces/context.interface.ts +0 -33
  97. package/src/interfaces/index.ts +0 -2
  98. package/src/interfaces/mcp-server-options.interface.ts +0 -105
  99. package/src/interfaces/message.types.ts +0 -13
  100. package/src/mcp.module.ts +0 -244
  101. package/src/mcp.service.spec.ts +0 -28
  102. package/src/registry/discovery.service.ts +0 -116
  103. package/src/registry/index.ts +0 -2
  104. package/src/registry/logger.service.ts +0 -143
  105. package/src/registry/registry.service.ts +0 -366
  106. package/src/services/session.manager.ts +0 -26
@@ -1,116 +0,0 @@
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
- }
@@ -1,2 +0,0 @@
1
- export * from './discovery.service';
2
- export * from './registry.service';
@@ -1,143 +0,0 @@
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
- }
@@ -1,366 +0,0 @@
1
- import {
2
- McpServer,
3
- ResourceTemplate,
4
- } from '@modelcontextprotocol/sdk/server/mcp.js';
5
- import type { CanActivate, Type } from '@nestjs/common';
6
- import { Injectable } from '@nestjs/common';
7
- import { Reflector } from '@nestjs/core';
8
-
9
- import { MCP_RESOLVER } from '../decorators';
10
- import {
11
- MCP_PROMPT,
12
- MCP_RESOURCE,
13
- MCP_TOOL,
14
- } from '../decorators/capabilities.constants';
15
- import { MCP_GUARDS } from '../decorators/capabilities.decorators';
16
- import {
17
- PromptHandlerParams,
18
- PromptOptions,
19
- RequestHandlerExtra,
20
- ResourceOptions,
21
- ResourceTemplateHandlerParams,
22
- ResourceUriHandlerParams,
23
- ToolHandlerParams,
24
- ToolOptions,
25
- } from '../interfaces/capabilities.interface';
26
- import { McpExecutionContext } from '../interfaces/context.interface';
27
- import { SessionManager } from '../services/session.manager';
28
- import { DiscoveryService } from './discovery.service';
29
- import { McpLoggerService } from './logger.service';
30
-
31
- @Injectable()
32
- export class RegistryService {
33
- constructor(
34
- private readonly discoveryService: DiscoveryService,
35
- private readonly logger: McpLoggerService,
36
- private readonly reflector: Reflector,
37
- private readonly sessionManager: SessionManager,
38
- ) {}
39
-
40
- async registerAll(server: McpServer): Promise<void> {
41
- this.logger.log(
42
- 'Starting registration of all MCP capabilities...',
43
- 'registry',
44
- );
45
-
46
- await Promise.all([
47
- this.registerResources(server),
48
- this.registerPrompts(server),
49
- this.registerTools(server),
50
- ]);
51
- }
52
-
53
- private getDecoratorType(method: Type<any> | undefined): string | null {
54
- if (!method) return null;
55
-
56
- if (this.reflector.get(MCP_TOOL, method)) return 'TOOL';
57
- if (this.reflector.get(MCP_PROMPT, method)) return 'PROMPT';
58
- if (this.reflector.get(MCP_RESOURCE, method)) return 'RESOURCE';
59
-
60
- return null;
61
- }
62
-
63
- private getHandlerArgs(
64
- method: Type<any> | undefined,
65
- args: unknown[],
66
- ):
67
- | ResourceUriHandlerParams
68
- | ResourceTemplateHandlerParams
69
- | PromptHandlerParams
70
- | ToolHandlerParams {
71
- if (!method) throw new Error('Method not found');
72
-
73
- switch (this.getDecoratorType(method)) {
74
- case 'RESOURCE':
75
- return args[0] instanceof URL
76
- ? ResourceUriHandlerParams.from(
77
- args[0],
78
- args[1] as RequestHandlerExtra,
79
- )
80
- : ResourceTemplateHandlerParams.from(
81
- args[0] as any,
82
- args[2] as RequestHandlerExtra,
83
- args[1] as Record<string, string>,
84
- );
85
- case 'PROMPT':
86
- return args.length === 1
87
- ? PromptHandlerParams.from(args[0] as RequestHandlerExtra)
88
- : PromptHandlerParams.from(
89
- args[1] as RequestHandlerExtra,
90
- args[0] as any,
91
- );
92
- case 'TOOL':
93
- return args.length === 1
94
- ? ToolHandlerParams.from(args[0] as RequestHandlerExtra)
95
- : ToolHandlerParams.from(
96
- args[1] as RequestHandlerExtra,
97
- args[0] as any,
98
- );
99
- default:
100
- throw new Error(`Unknown decorator type for method ${method.name}`);
101
- }
102
- }
103
-
104
- /**
105
- * Executes all guards attached to the resolver class and method.
106
- * Throws an error if any guard denies access.
107
- *
108
- * @param instance The resolver instance
109
- * @param methodName The method name being invoked
110
- * @param args The arguments passed to the method
111
- * @throws Error if any guard denies access
112
- */
113
- private runGuards(
114
- instance: object,
115
- methodName: string,
116
- args: unknown[],
117
- ): Promise<void> {
118
- // Retrieve class-level guards
119
- const classConstructor = instance.constructor;
120
-
121
- const classGuards: (CanActivate | { new (): CanActivate })[] =
122
- (Reflect.getMetadata(MCP_GUARDS, classConstructor) as (
123
- | CanActivate
124
- | { new (): CanActivate }
125
- )[]) || [];
126
-
127
- // Retrieve method-level guards
128
- const prototype = Object.getPrototypeOf(instance) as Record<
129
- string,
130
- unknown
131
- >;
132
-
133
- const methodKey = prototype[methodName] as Type<any> | undefined;
134
-
135
- const methodGuards: (CanActivate | { new (): CanActivate })[] =
136
- (methodKey &&
137
- (Reflect.getMetadata(MCP_GUARDS, methodKey) as (
138
- | CanActivate
139
- | { new (): CanActivate }
140
- )[])) ||
141
- [];
142
-
143
- // Combine guards: class-level first, then method-level
144
- const allGuards = [...classGuards, ...methodGuards];
145
-
146
- if (!allGuards.length) return Promise.resolve();
147
-
148
- const handlerArgs = this.getHandlerArgs(methodKey, args);
149
-
150
- const { sessionId } = handlerArgs.extra;
151
-
152
- if (!sessionId) {
153
- throw new Error('Session not found');
154
- }
155
-
156
- const session = this.sessionManager.getSession(sessionId);
157
-
158
- if (!session) {
159
- throw new Error('Session not found');
160
- }
161
-
162
- const context: McpExecutionContext = {
163
- args: handlerArgs,
164
- // @ts-expect-error: Default types are 'http' | 'ws' | 'rpc' but in our case, we are using 'mcp'
165
- getType: () => 'mcp',
166
- getClass: () => instance.constructor as Type<any>,
167
- getArgs: <T = any>() => args as T,
168
- getArgByIndex: <T = any>(index: number) => args[index] as T,
169
- getSessionId: () => sessionId,
170
- getHandler: () => methodKey as unknown as Type<any>,
171
- switchToHttp: () => ({
172
- getRequest: <R = Request>() => session.request as R,
173
- getResponse: () => {
174
- throw new Error('Response not available in MCP context');
175
- },
176
- getNext: () => {
177
- throw new Error('Next not available in MCP context');
178
- },
179
- }),
180
- };
181
-
182
- return (async () => {
183
- for (const Guard of allGuards) {
184
- const guardInstance: CanActivate =
185
- typeof Guard === 'function' ? new Guard() : Guard;
186
- const allowed = await guardInstance.canActivate(context);
187
-
188
- if (!allowed)
189
- throw new Error(`Access denied by guard on ${methodName}`);
190
- }
191
- })();
192
- }
193
-
194
- private async wrappedHandler<TArgs extends unknown[], TResult>(
195
- instance: object,
196
- handler: (...args: TArgs) => TResult,
197
- args: unknown[],
198
- ) {
199
- const isResolver = Reflect.hasMetadata(MCP_RESOLVER, instance.constructor);
200
-
201
- if (!isResolver) {
202
- throw new Error(
203
- `Class "${instance.constructor.name}" must be decorated with @Resolver to use @Prompt, @Tool, or @Resource.`,
204
- );
205
- }
206
-
207
- const methodName = handler.name;
208
-
209
- await this.runGuards(instance, methodName, args);
210
-
211
- return handler(...(args as TArgs));
212
- }
213
-
214
- private registerResources(server: McpServer) {
215
- const resourceMethods =
216
- this.discoveryService.getAllMethodsWithMetadata<ResourceOptions>(
217
- MCP_RESOURCE,
218
- );
219
- for (const method of resourceMethods) {
220
- const { metadata, handler, instance } = method;
221
-
222
- this.logger.log(
223
- `Resource "${metadata?.name || 'unnamed'}" found.`,
224
- 'resources',
225
- );
226
-
227
- const wrappedHandler = (...args: unknown[]) =>
228
- this.wrappedHandler(instance, handler, args);
229
-
230
- try {
231
- if ('template' in metadata) {
232
- if ('metadata' in metadata) {
233
- server.resource(
234
- metadata.name,
235
- new ResourceTemplate(metadata.template, { list: undefined }),
236
- metadata.metadata,
237
- wrappedHandler,
238
- );
239
- } else {
240
- server.resource(
241
- metadata.name,
242
- new ResourceTemplate(metadata.template, { list: undefined }),
243
- wrappedHandler,
244
- );
245
- }
246
- } else if ('uri' in metadata) {
247
- if ('metadata' in metadata) {
248
- server.resource(
249
- metadata.name,
250
- metadata.uri,
251
- metadata.metadata,
252
- wrappedHandler,
253
- );
254
- } else {
255
- server.resource(metadata.name, metadata.uri, wrappedHandler);
256
- }
257
- }
258
- } catch (error) {
259
- this.logger.error(
260
- `Error registering resource ${metadata.name}: ${error}`,
261
- undefined,
262
- 'resources',
263
- );
264
- if (error && typeof error === 'object' && 'stack' in error) {
265
- this.logger.error(
266
- `Error stack: ${(error as Error).stack}`,
267
- undefined,
268
- 'resources',
269
- );
270
- }
271
- }
272
- }
273
- }
274
-
275
- private registerPrompts(server: McpServer) {
276
- const promptMethods =
277
- this.discoveryService.getAllMethodsWithMetadata<PromptOptions>(
278
- MCP_PROMPT,
279
- );
280
- for (const method of promptMethods) {
281
- const { metadata, handler, instance } = method;
282
-
283
- this.logger.log(
284
- `Prompt "${metadata?.name || 'unnamed'}" found.`,
285
- 'prompts',
286
- );
287
-
288
- const wrappedHandler = (...args: unknown[]) =>
289
- this.wrappedHandler(instance, handler, args);
290
-
291
- try {
292
- if ('description' in metadata && 'argsSchema' in metadata) {
293
- server.prompt(
294
- metadata.name,
295
- metadata.description,
296
- metadata.argsSchema,
297
- wrappedHandler,
298
- );
299
- } else if ('argsSchema' in metadata) {
300
- server.prompt(metadata.name, metadata.argsSchema, wrappedHandler);
301
- } else if ('description' in metadata) {
302
- server.prompt(metadata.name, metadata.description, wrappedHandler);
303
- } else {
304
- server.prompt(metadata.name, wrappedHandler);
305
- }
306
- } catch (error) {
307
- this.logger.error(
308
- `Error registering prompt ${metadata.name}: ${error}`,
309
- undefined,
310
- 'prompts',
311
- );
312
- if (error && typeof error === 'object' && 'stack' in error) {
313
- this.logger.error(
314
- `Error stack: ${(error as Error).stack}`,
315
- undefined,
316
- 'prompts',
317
- );
318
- }
319
- }
320
- }
321
- }
322
-
323
- private registerTools(server: McpServer) {
324
- const toolMethods =
325
- this.discoveryService.getAllMethodsWithMetadata<ToolOptions>(MCP_TOOL);
326
-
327
- for (const method of toolMethods) {
328
- const { metadata, handler, instance } = method;
329
-
330
- this.logger.log(`Tool "${metadata?.name || 'unnamed'}" found.`, 'tools');
331
-
332
- const wrappedHandler = (...args: unknown[]) =>
333
- this.wrappedHandler(instance, handler, args);
334
-
335
- try {
336
- if ('description' in metadata && 'paramSchema' in metadata) {
337
- server.tool(
338
- metadata.name,
339
- metadata.description,
340
- metadata.paramSchema,
341
- wrappedHandler,
342
- );
343
- } else if ('paramSchema' in metadata) {
344
- server.tool(metadata.name, metadata.paramSchema, wrappedHandler);
345
- } else if ('description' in metadata) {
346
- server.tool(metadata.name, metadata.description, wrappedHandler);
347
- } else {
348
- server.tool(metadata.name, wrappedHandler);
349
- }
350
- } catch (error) {
351
- this.logger.error(
352
- `Error registering tool ${metadata.name}: ${error}`,
353
- undefined,
354
- 'tools',
355
- );
356
- if (error && typeof error === 'object' && 'stack' in error) {
357
- this.logger.error(
358
- `Stack trace: ${(error as Error).stack}`,
359
- undefined,
360
- 'tools',
361
- );
362
- }
363
- }
364
- }
365
- }
366
- }
@@ -1,26 +0,0 @@
1
- import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse';
2
- import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
3
- import { Injectable } from '@nestjs/common';
4
- import { Request } from 'express';
5
-
6
- type Session = {
7
- transport: StreamableHTTPServerTransport | SSEServerTransport;
8
- request: Request;
9
- };
10
-
11
- @Injectable()
12
- export class SessionManager {
13
- private sessions = new Map<string, Session>();
14
-
15
- public getSession(sessionId: string): Session | undefined {
16
- return this.sessions.get(sessionId);
17
- }
18
-
19
- public setSession(sessionId: string, session: Session): void {
20
- this.sessions.set(sessionId, session);
21
- }
22
-
23
- public deleteSession(sessionId: string): void {
24
- this.sessions.delete(sessionId);
25
- }
26
- }