@nestjs-mcp/server 0.1.0-alpha.10
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/.copilotignore +38 -0
- package/.devcontainer/Dockerfile.dev +28 -0
- package/.devcontainer/devcontainer.json +56 -0
- package/.devcontainer/docker-compose.yml +15 -0
- package/.dockerignore +37 -0
- package/.prettierrc +4 -0
- package/LICENSE +21 -0
- package/README.md +540 -0
- package/dist/controllers/sse/index.d.ts +2 -0
- package/dist/controllers/sse/index.js +19 -0
- package/dist/controllers/sse/index.js.map +1 -0
- package/dist/controllers/sse/sse.controller.d.ts +10 -0
- package/dist/controllers/sse/sse.controller.js +57 -0
- package/dist/controllers/sse/sse.controller.js.map +1 -0
- package/dist/controllers/sse/sse.service.d.ts +16 -0
- package/dist/controllers/sse/sse.service.js +78 -0
- package/dist/controllers/sse/sse.service.js.map +1 -0
- package/dist/controllers/streamable/index.d.ts +2 -0
- package/dist/controllers/streamable/index.js +19 -0
- package/dist/controllers/streamable/index.js.map +1 -0
- package/dist/controllers/streamable/streamable.controller.d.ts +9 -0
- package/dist/controllers/streamable/streamable.controller.js +62 -0
- package/dist/controllers/streamable/streamable.controller.js.map +1 -0
- package/dist/controllers/streamable/streamable.service.d.ts +24 -0
- package/dist/controllers/streamable/streamable.service.js +118 -0
- package/dist/controllers/streamable/streamable.service.js.map +1 -0
- package/dist/decorators/capabilities.constants.d.ts +4 -0
- package/dist/decorators/capabilities.constants.js +8 -0
- package/dist/decorators/capabilities.constants.js.map +1 -0
- package/dist/decorators/capabilities.decorators.d.ts +8 -0
- package/dist/decorators/capabilities.decorators.js +49 -0
- package/dist/decorators/capabilities.decorators.js.map +1 -0
- package/dist/decorators/index.d.ts +2 -0
- package/dist/decorators/index.js +19 -0
- package/dist/decorators/index.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/interceptors/message.interceptor.d.ts +10 -0
- package/dist/interceptors/message.interceptor.js +61 -0
- package/dist/interceptors/message.interceptor.js.map +1 -0
- package/dist/interfaces/capabilities.interface.d.ts +52 -0
- package/dist/interfaces/capabilities.interface.js +3 -0
- package/dist/interfaces/capabilities.interface.js.map +1 -0
- package/dist/interfaces/context.interface.d.ts +6 -0
- package/dist/interfaces/context.interface.js +3 -0
- package/dist/interfaces/context.interface.js.map +1 -0
- package/dist/interfaces/index.d.ts +2 -0
- package/dist/interfaces/index.js +19 -0
- package/dist/interfaces/index.js.map +1 -0
- package/dist/interfaces/mcp-server-options.interface.d.ts +42 -0
- package/dist/interfaces/mcp-server-options.interface.js +3 -0
- package/dist/interfaces/mcp-server-options.interface.js.map +1 -0
- package/dist/interfaces/message.types.d.ts +8 -0
- package/dist/interfaces/message.types.js +3 -0
- package/dist/interfaces/message.types.js.map +1 -0
- package/dist/mcp.module.d.ts +13 -0
- package/dist/mcp.module.js +193 -0
- package/dist/mcp.module.js.map +1 -0
- package/dist/registry/discovery.service.d.ts +16 -0
- package/dist/registry/discovery.service.js +85 -0
- package/dist/registry/discovery.service.js.map +1 -0
- package/dist/registry/index.d.ts +2 -0
- package/dist/registry/index.js +19 -0
- package/dist/registry/index.js.map +1 -0
- package/dist/registry/logger.service.d.ts +16 -0
- package/dist/registry/logger.service.js +97 -0
- package/dist/registry/logger.service.js.map +1 -0
- package/dist/registry/registry.service.d.ts +16 -0
- package/dist/registry/registry.service.js +170 -0
- package/dist/registry/registry.service.js.map +1 -0
- package/dist/services/message.service.d.ts +7 -0
- package/dist/services/message.service.js +25 -0
- package/dist/services/message.service.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/eslint.config.mjs +40 -0
- package/package.json +109 -0
- package/src/controllers/sse/index.ts +2 -0
- package/src/controllers/sse/sse.controller.ts +25 -0
- package/src/controllers/sse/sse.service.ts +90 -0
- package/src/controllers/streamable/index.ts +2 -0
- package/src/controllers/streamable/streamable.controller.ts +24 -0
- package/src/controllers/streamable/streamable.service.ts +169 -0
- package/src/decorators/capabilities.constants.ts +7 -0
- package/src/decorators/capabilities.decorators.ts +150 -0
- package/src/decorators/index.ts +2 -0
- package/src/index.ts +11 -0
- package/src/interceptors/message.interceptor.ts +70 -0
- package/src/interfaces/capabilities.interface.ts +95 -0
- package/src/interfaces/context.interface.ts +18 -0
- package/src/interfaces/index.ts +2 -0
- package/src/interfaces/mcp-server-options.interface.ts +105 -0
- package/src/interfaces/message.types.ts +13 -0
- package/src/mcp.module.ts +250 -0
- package/src/mcp.service.spec.ts +28 -0
- package/src/registry/discovery.service.ts +116 -0
- package/src/registry/index.ts +2 -0
- package/src/registry/logger.service.ts +143 -0
- package/src/registry/registry.service.ts +282 -0
- package/src/services/message.service.ts +18 -0
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,282 @@
|
|
|
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
|
+
|
|
8
|
+
import { MCP_RESOLVER } from '../decorators';
|
|
9
|
+
import {
|
|
10
|
+
MCP_PROMPT,
|
|
11
|
+
MCP_RESOURCE,
|
|
12
|
+
MCP_TOOL,
|
|
13
|
+
} from '../decorators/capabilities.constants';
|
|
14
|
+
import { MCP_GUARDS } from '../decorators/capabilities.decorators';
|
|
15
|
+
import {
|
|
16
|
+
PromptOptions,
|
|
17
|
+
ResourceOptions,
|
|
18
|
+
ToolOptions,
|
|
19
|
+
} from '../interfaces/capabilities.interface';
|
|
20
|
+
import { McpExecutionContext } from '../interfaces/context.interface';
|
|
21
|
+
import { MessageService } from '../services/message.service';
|
|
22
|
+
import { DiscoveryService } from './discovery.service';
|
|
23
|
+
import { McpLoggerService } from './logger.service';
|
|
24
|
+
@Injectable()
|
|
25
|
+
export class RegistryService {
|
|
26
|
+
constructor(
|
|
27
|
+
private readonly discoveryService: DiscoveryService,
|
|
28
|
+
private readonly logger: McpLoggerService,
|
|
29
|
+
private readonly messageService: MessageService,
|
|
30
|
+
) {}
|
|
31
|
+
|
|
32
|
+
async registerAll(server: McpServer): Promise<void> {
|
|
33
|
+
this.logger.log(
|
|
34
|
+
'Starting registration of all MCP capabilities...',
|
|
35
|
+
'registry',
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
await Promise.all([
|
|
39
|
+
this.registerResources(server),
|
|
40
|
+
this.registerPrompts(server),
|
|
41
|
+
this.registerTools(server),
|
|
42
|
+
]);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Executes all guards attached to the resolver class and method.
|
|
47
|
+
* Throws an error if any guard denies access.
|
|
48
|
+
*
|
|
49
|
+
* @param instance The resolver instance
|
|
50
|
+
* @param methodName The method name being invoked
|
|
51
|
+
* @param args The arguments passed to the method
|
|
52
|
+
* @throws Error if any guard denies access
|
|
53
|
+
*/
|
|
54
|
+
private runGuards(
|
|
55
|
+
instance: object,
|
|
56
|
+
methodName: string,
|
|
57
|
+
args: unknown[],
|
|
58
|
+
): Promise<void> {
|
|
59
|
+
// Retrieve class-level guards
|
|
60
|
+
const classConstructor = instance.constructor;
|
|
61
|
+
const classGuards: (CanActivate | { new (): CanActivate })[] =
|
|
62
|
+
(Reflect.getMetadata(MCP_GUARDS, classConstructor) as (
|
|
63
|
+
| CanActivate
|
|
64
|
+
| { new (): CanActivate }
|
|
65
|
+
)[]) || [];
|
|
66
|
+
|
|
67
|
+
// Retrieve method-level guards
|
|
68
|
+
const prototype = Object.getPrototypeOf(instance) as Record<
|
|
69
|
+
string,
|
|
70
|
+
unknown
|
|
71
|
+
>;
|
|
72
|
+
const methodKey = prototype[methodName] as object | undefined;
|
|
73
|
+
|
|
74
|
+
const methodGuards: (CanActivate | { new (): CanActivate })[] =
|
|
75
|
+
(methodKey &&
|
|
76
|
+
(Reflect.getMetadata(MCP_GUARDS, methodKey) as (
|
|
77
|
+
| CanActivate
|
|
78
|
+
| { new (): CanActivate }
|
|
79
|
+
)[])) ||
|
|
80
|
+
[];
|
|
81
|
+
|
|
82
|
+
// Combine guards: class-level first, then method-level
|
|
83
|
+
const allGuards = [...classGuards, ...methodGuards];
|
|
84
|
+
|
|
85
|
+
if (!allGuards.length) return Promise.resolve();
|
|
86
|
+
// Build a minimal context (customize as needed)
|
|
87
|
+
const context: McpExecutionContext = {
|
|
88
|
+
args,
|
|
89
|
+
message: this.messageService.get(),
|
|
90
|
+
|
|
91
|
+
// @ts-expect-error: Default types are 'http' | 'ws' | 'rpc' but in our case
|
|
92
|
+
// we are using 'mcp'
|
|
93
|
+
getType: () => 'mcp',
|
|
94
|
+
getClass: () => instance.constructor as Type<any>,
|
|
95
|
+
getArgs: <T extends Array<any>>() => args as T,
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
return (async () => {
|
|
99
|
+
for (const Guard of allGuards) {
|
|
100
|
+
const guardInstance: CanActivate =
|
|
101
|
+
typeof Guard === 'function' ? new Guard() : Guard;
|
|
102
|
+
const allowed = await guardInstance.canActivate(context);
|
|
103
|
+
|
|
104
|
+
if (!allowed)
|
|
105
|
+
throw new Error(`Access denied by guard on ${methodName}`);
|
|
106
|
+
}
|
|
107
|
+
})();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
private async wrappedHandler<TArgs extends unknown[], TResult>(
|
|
111
|
+
instance: object,
|
|
112
|
+
handler: (...args: TArgs) => TResult,
|
|
113
|
+
args: unknown[],
|
|
114
|
+
) {
|
|
115
|
+
const isResolver = Reflect.hasMetadata(MCP_RESOLVER, instance.constructor);
|
|
116
|
+
|
|
117
|
+
if (!isResolver) {
|
|
118
|
+
throw new Error(
|
|
119
|
+
`Class "${instance.constructor.name}" must be decorated with @Resolver to use @Prompt, @Tool, or @Resource.`,
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const methodName = handler.name;
|
|
124
|
+
|
|
125
|
+
await this.runGuards(instance, methodName, args);
|
|
126
|
+
|
|
127
|
+
return handler(...(args as TArgs));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private registerResources(server: McpServer) {
|
|
131
|
+
const resourceMethods =
|
|
132
|
+
this.discoveryService.getAllMethodsWithMetadata<ResourceOptions>(
|
|
133
|
+
MCP_RESOURCE,
|
|
134
|
+
);
|
|
135
|
+
for (const method of resourceMethods) {
|
|
136
|
+
const { metadata, handler, instance } = method;
|
|
137
|
+
|
|
138
|
+
this.logger.log(
|
|
139
|
+
`Resource "${metadata?.name || 'unnamed'}" found.`,
|
|
140
|
+
'resources',
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
const wrappedHandler = (...args: unknown[]) =>
|
|
144
|
+
this.wrappedHandler(instance, handler, args);
|
|
145
|
+
|
|
146
|
+
try {
|
|
147
|
+
if ('template' in metadata) {
|
|
148
|
+
if ('metadata' in metadata) {
|
|
149
|
+
server.resource(
|
|
150
|
+
metadata.name,
|
|
151
|
+
new ResourceTemplate(metadata.template, { list: undefined }),
|
|
152
|
+
metadata.metadata,
|
|
153
|
+
wrappedHandler,
|
|
154
|
+
);
|
|
155
|
+
} else {
|
|
156
|
+
server.resource(
|
|
157
|
+
metadata.name,
|
|
158
|
+
new ResourceTemplate(metadata.template, { list: undefined }),
|
|
159
|
+
wrappedHandler,
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
} else if ('uri' in metadata) {
|
|
163
|
+
if ('metadata' in metadata) {
|
|
164
|
+
server.resource(
|
|
165
|
+
metadata.name,
|
|
166
|
+
metadata.uri,
|
|
167
|
+
metadata.metadata,
|
|
168
|
+
wrappedHandler,
|
|
169
|
+
);
|
|
170
|
+
} else {
|
|
171
|
+
server.resource(metadata.name, metadata.uri, wrappedHandler);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
} catch (error) {
|
|
175
|
+
this.logger.error(
|
|
176
|
+
`Error registering resource ${metadata.name}: ${error}`,
|
|
177
|
+
undefined,
|
|
178
|
+
'resources',
|
|
179
|
+
);
|
|
180
|
+
if (error && typeof error === 'object' && 'stack' in error) {
|
|
181
|
+
this.logger.error(
|
|
182
|
+
`Error stack: ${(error as Error).stack}`,
|
|
183
|
+
undefined,
|
|
184
|
+
'resources',
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
private registerPrompts(server: McpServer) {
|
|
192
|
+
const promptMethods =
|
|
193
|
+
this.discoveryService.getAllMethodsWithMetadata<PromptOptions>(
|
|
194
|
+
MCP_PROMPT,
|
|
195
|
+
);
|
|
196
|
+
for (const method of promptMethods) {
|
|
197
|
+
const { metadata, handler, instance } = method;
|
|
198
|
+
|
|
199
|
+
this.logger.log(
|
|
200
|
+
`Prompt "${metadata?.name || 'unnamed'}" found.`,
|
|
201
|
+
'prompts',
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
const wrappedHandler = (...args: unknown[]) =>
|
|
205
|
+
this.wrappedHandler(instance, handler, args);
|
|
206
|
+
|
|
207
|
+
try {
|
|
208
|
+
if ('description' in metadata && 'argsSchema' in metadata) {
|
|
209
|
+
server.prompt(
|
|
210
|
+
metadata.name,
|
|
211
|
+
metadata.description,
|
|
212
|
+
metadata.argsSchema,
|
|
213
|
+
wrappedHandler,
|
|
214
|
+
);
|
|
215
|
+
} else if ('argsSchema' in metadata) {
|
|
216
|
+
server.prompt(metadata.name, metadata.argsSchema, wrappedHandler);
|
|
217
|
+
} else if ('description' in metadata) {
|
|
218
|
+
server.prompt(metadata.name, metadata.description, wrappedHandler);
|
|
219
|
+
} else {
|
|
220
|
+
server.prompt(metadata.name, wrappedHandler);
|
|
221
|
+
}
|
|
222
|
+
} catch (error) {
|
|
223
|
+
this.logger.error(
|
|
224
|
+
`Error registering prompt ${metadata.name}: ${error}`,
|
|
225
|
+
undefined,
|
|
226
|
+
'prompts',
|
|
227
|
+
);
|
|
228
|
+
if (error && typeof error === 'object' && 'stack' in error) {
|
|
229
|
+
this.logger.error(
|
|
230
|
+
`Error stack: ${(error as Error).stack}`,
|
|
231
|
+
undefined,
|
|
232
|
+
'prompts',
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
private registerTools(server: McpServer) {
|
|
240
|
+
const toolMethods =
|
|
241
|
+
this.discoveryService.getAllMethodsWithMetadata<ToolOptions>(MCP_TOOL);
|
|
242
|
+
|
|
243
|
+
for (const method of toolMethods) {
|
|
244
|
+
const { metadata, handler, instance } = method;
|
|
245
|
+
|
|
246
|
+
this.logger.log(`Tool "${metadata?.name || 'unnamed'}" found.`, 'tools');
|
|
247
|
+
|
|
248
|
+
const wrappedHandler = (...args: unknown[]) =>
|
|
249
|
+
this.wrappedHandler(instance, handler, args);
|
|
250
|
+
|
|
251
|
+
try {
|
|
252
|
+
if ('description' in metadata && 'paramSchema' in metadata) {
|
|
253
|
+
server.tool(
|
|
254
|
+
metadata.name,
|
|
255
|
+
metadata.description,
|
|
256
|
+
metadata.paramSchema,
|
|
257
|
+
wrappedHandler,
|
|
258
|
+
);
|
|
259
|
+
} else if ('paramSchema' in metadata) {
|
|
260
|
+
server.tool(metadata.name, metadata.paramSchema, wrappedHandler);
|
|
261
|
+
} else if ('description' in metadata) {
|
|
262
|
+
server.tool(metadata.name, metadata.description, wrappedHandler);
|
|
263
|
+
} else {
|
|
264
|
+
server.tool(metadata.name, wrappedHandler);
|
|
265
|
+
}
|
|
266
|
+
} catch (error) {
|
|
267
|
+
this.logger.error(
|
|
268
|
+
`Error registering tool ${metadata.name}: ${error}`,
|
|
269
|
+
undefined,
|
|
270
|
+
'tools',
|
|
271
|
+
);
|
|
272
|
+
if (error && typeof error === 'object' && 'stack' in error) {
|
|
273
|
+
this.logger.error(
|
|
274
|
+
`Stack trace: ${(error as Error).stack}`,
|
|
275
|
+
undefined,
|
|
276
|
+
'tools',
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Injectable } from '@nestjs/common';
|
|
2
|
+
|
|
3
|
+
import { AsyncLocalStorage } from 'async_hooks';
|
|
4
|
+
|
|
5
|
+
import { McpMessage } from '../interfaces/message.types';
|
|
6
|
+
|
|
7
|
+
export const requestContext = new AsyncLocalStorage<McpMessage>();
|
|
8
|
+
|
|
9
|
+
@Injectable()
|
|
10
|
+
export class MessageService {
|
|
11
|
+
public set(context: McpMessage): void {
|
|
12
|
+
requestContext.enterWith(context);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
public get(): McpMessage | undefined {
|
|
16
|
+
return requestContext.getStore();
|
|
17
|
+
}
|
|
18
|
+
}
|