@muzikanto/nestjs-mcp 1.5.0 → 1.5.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.
- package/CHANGELOG.md +6 -0
- package/README.md +5 -5
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/mcp-server/exceptions/index.d.ts +31 -0
- package/dist/mcp-server/exceptions/index.js +40 -0
- package/dist/mcp-server/mcp.service.js +57 -39
- package/dist/mcp-server/utils/run-guards.js +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -456,23 +456,23 @@ import { APP_INTERCEPTOR } from "@nestjs/core";
|
|
|
456
456
|
export class TestModule {}
|
|
457
457
|
```
|
|
458
458
|
|
|
459
|
-
###
|
|
459
|
+
### Filters
|
|
460
460
|
|
|
461
461
|
```ts
|
|
462
|
-
import { IMcpTool, McpTool } from "@muzikanto/nestjs-mcp";
|
|
462
|
+
import { IMcpTool, McpTool, McpUnauthorizedException } from "@muzikanto/nestjs-mcp";
|
|
463
463
|
import {
|
|
464
464
|
ExceptionFilter,
|
|
465
465
|
Catch,
|
|
466
466
|
ArgumentsHost,
|
|
467
|
-
NotImplementedException,
|
|
468
467
|
UseFilters,
|
|
469
468
|
} from "@nestjs/common";
|
|
470
469
|
|
|
471
|
-
@Catch(
|
|
470
|
+
@Catch(McpUnauthorizedException)
|
|
472
471
|
class ExampleFilter implements ExceptionFilter {
|
|
473
472
|
catch(exception: unknown, host: ArgumentsHost) {
|
|
474
473
|
return {
|
|
475
|
-
|
|
474
|
+
isError: true,
|
|
475
|
+
text: (exception as Error).message,
|
|
476
476
|
};
|
|
477
477
|
}
|
|
478
478
|
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -21,3 +21,4 @@ __exportStar(require("./mcp-server/decorators/mcp-resource.decorator"), exports)
|
|
|
21
21
|
__exportStar(require("./mcp-client/mcp-client.module"), exports);
|
|
22
22
|
__exportStar(require("./mcp-client/mcp-client.service"), exports);
|
|
23
23
|
__exportStar(require("./mcp-server/mcp-dynamic.service"), exports);
|
|
24
|
+
__exportStar(require("./mcp-server/exceptions"), exports);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { HttpException, HttpStatus } from "@nestjs/common";
|
|
2
|
+
export declare class McpException extends HttpException {
|
|
3
|
+
constructor(message: string, status?: HttpStatus, options?: {
|
|
4
|
+
cause?: unknown;
|
|
5
|
+
});
|
|
6
|
+
}
|
|
7
|
+
export declare class McpBadRequestException extends McpException {
|
|
8
|
+
constructor(message: string, options?: {
|
|
9
|
+
cause?: unknown;
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
export declare class McpNotFoundException extends McpException {
|
|
13
|
+
constructor(message: string, options?: {
|
|
14
|
+
cause?: unknown;
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
export declare class McpInternalServerErrorException extends McpException {
|
|
18
|
+
constructor(message: string, options?: {
|
|
19
|
+
cause?: unknown;
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
export declare class McpUnauthorizedException extends McpException {
|
|
23
|
+
constructor(message: string, options?: {
|
|
24
|
+
cause?: unknown;
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
export declare class McpNotImplementedException extends McpException {
|
|
28
|
+
constructor(message: string, options?: {
|
|
29
|
+
cause?: unknown;
|
|
30
|
+
});
|
|
31
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.McpNotImplementedException = exports.McpUnauthorizedException = exports.McpInternalServerErrorException = exports.McpNotFoundException = exports.McpBadRequestException = exports.McpException = void 0;
|
|
4
|
+
const common_1 = require("@nestjs/common");
|
|
5
|
+
class McpException extends common_1.HttpException {
|
|
6
|
+
constructor(message, status = common_1.HttpStatus.INTERNAL_SERVER_ERROR, options) {
|
|
7
|
+
super({ message, ...(options?.cause ? { cause: options.cause } : {}) }, status);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
exports.McpException = McpException;
|
|
11
|
+
class McpBadRequestException extends McpException {
|
|
12
|
+
constructor(message, options) {
|
|
13
|
+
super(message, common_1.HttpStatus.BAD_REQUEST, options);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.McpBadRequestException = McpBadRequestException;
|
|
17
|
+
class McpNotFoundException extends McpException {
|
|
18
|
+
constructor(message, options) {
|
|
19
|
+
super(message, common_1.HttpStatus.NOT_FOUND, options);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.McpNotFoundException = McpNotFoundException;
|
|
23
|
+
class McpInternalServerErrorException extends McpException {
|
|
24
|
+
constructor(message, options) {
|
|
25
|
+
super(message, common_1.HttpStatus.INTERNAL_SERVER_ERROR, options);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
exports.McpInternalServerErrorException = McpInternalServerErrorException;
|
|
29
|
+
class McpUnauthorizedException extends McpException {
|
|
30
|
+
constructor(message, options) {
|
|
31
|
+
super(message, common_1.HttpStatus.UNAUTHORIZED, options);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.McpUnauthorizedException = McpUnauthorizedException;
|
|
35
|
+
class McpNotImplementedException extends McpException {
|
|
36
|
+
constructor(message, options) {
|
|
37
|
+
super(message, common_1.HttpStatus.NOT_IMPLEMENTED, options);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
exports.McpNotImplementedException = McpNotImplementedException;
|
|
@@ -20,6 +20,7 @@ const core_1 = require("@nestjs/core");
|
|
|
20
20
|
const run_interceptors_1 = require("./utils/run-interceptors");
|
|
21
21
|
const rxjs_1 = require("rxjs");
|
|
22
22
|
const run_fillters_1 = require("./utils/run-fillters");
|
|
23
|
+
const exceptions_1 = require("./exceptions");
|
|
23
24
|
let McpService = class McpService {
|
|
24
25
|
moduleRef;
|
|
25
26
|
tools = new Map();
|
|
@@ -88,23 +89,20 @@ let McpService = class McpService {
|
|
|
88
89
|
}
|
|
89
90
|
async executePrompt(name, payload, context) {
|
|
90
91
|
if (!this.prompts.has(name)) {
|
|
91
|
-
throw new
|
|
92
|
-
}
|
|
93
|
-
const { instance: prompt, metatype } = this.prompts.get(name) || {};
|
|
94
|
-
if (!prompt || !metatype) {
|
|
95
|
-
throw new common_1.NotFoundException(`Unknown prompt: "${name}"`);
|
|
96
|
-
}
|
|
97
|
-
await (0, run_guards_1.runGuards)(this.moduleRef, metatype, context);
|
|
98
|
-
// Валидация через AJV, если есть inputSchema
|
|
99
|
-
if (prompt.inputSchema) {
|
|
100
|
-
const zodSchema = (0, zod_1.zodToJsonSchema)(prompt.inputSchema);
|
|
101
|
-
const validate = this.ajv.compile(zodSchema);
|
|
102
|
-
const valid = validate(payload);
|
|
103
|
-
if (!valid) {
|
|
104
|
-
throw new common_1.NotFoundException("Invalid prompt arguments");
|
|
105
|
-
}
|
|
92
|
+
throw new exceptions_1.McpNotFoundException("Not found prompt");
|
|
106
93
|
}
|
|
94
|
+
const { instance: prompt, metatype } = this.prompts.get(name);
|
|
107
95
|
try {
|
|
96
|
+
await (0, run_guards_1.runGuards)(this.moduleRef, metatype, context);
|
|
97
|
+
// Валидация через AJV, если есть inputSchema
|
|
98
|
+
if (prompt.inputSchema) {
|
|
99
|
+
const zodSchema = (0, zod_1.zodToJsonSchema)(prompt.inputSchema);
|
|
100
|
+
const validate = this.ajv.compile(zodSchema);
|
|
101
|
+
const valid = validate(payload);
|
|
102
|
+
if (!valid) {
|
|
103
|
+
throw new exceptions_1.McpBadRequestException("Invalid prompt arguments");
|
|
104
|
+
}
|
|
105
|
+
}
|
|
108
106
|
// const result = await prompt.execute(payload);
|
|
109
107
|
const stream = await (0, run_interceptors_1.runInterceptors)(this.moduleRef, metatype, context, () => {
|
|
110
108
|
return (0, rxjs_1.from)(prompt.execute(payload));
|
|
@@ -112,28 +110,33 @@ let McpService = class McpService {
|
|
|
112
110
|
return stream.pipe((0, rxjs_1.catchError)((err) => (0, rxjs_1.from)((0, run_fillters_1.runFilters)(this.moduleRef, metatype, err, context))));
|
|
113
111
|
}
|
|
114
112
|
catch (err) {
|
|
115
|
-
|
|
113
|
+
const result = await (0, run_fillters_1.runFilters)(this.moduleRef, metatype, err, context);
|
|
114
|
+
if (result) {
|
|
115
|
+
return (0, rxjs_1.of)(result);
|
|
116
|
+
}
|
|
117
|
+
// Пробрасываем дальше, чтобы Observable корректно завершился
|
|
118
|
+
return (0, rxjs_1.throwError)(() => err);
|
|
116
119
|
}
|
|
117
120
|
}
|
|
118
121
|
/**
|
|
119
122
|
* Отправить сообщение в MCP "сервер"
|
|
120
123
|
*/
|
|
121
124
|
async executeTool(msg, context) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
throw new common_1.NotFoundException(`Unknown tool: "${msg.type}"`);
|
|
125
|
-
}
|
|
126
|
-
await (0, run_guards_1.runGuards)(this.moduleRef, metatype, context);
|
|
127
|
-
// Валидация через AJV, если есть inputSchema
|
|
128
|
-
if (tool.inputSchema) {
|
|
129
|
-
const zodSchema = (0, zod_1.zodToJsonSchema)(tool.inputSchema);
|
|
130
|
-
const validate = this.ajv.compile(zodSchema);
|
|
131
|
-
const valid = validate(msg.payload);
|
|
132
|
-
if (!valid) {
|
|
133
|
-
throw new common_1.BadRequestException(this.ajv.errorsText(validate.errors));
|
|
134
|
-
}
|
|
125
|
+
if (!this.tools.has(msg.type)) {
|
|
126
|
+
throw new exceptions_1.McpNotFoundException("Not found tool");
|
|
135
127
|
}
|
|
128
|
+
const { instance: tool, metatype } = this.tools.get(msg.type);
|
|
136
129
|
try {
|
|
130
|
+
await (0, run_guards_1.runGuards)(this.moduleRef, metatype, context);
|
|
131
|
+
// Валидация через AJV, если есть inputSchema
|
|
132
|
+
if (tool.inputSchema) {
|
|
133
|
+
const zodSchema = (0, zod_1.zodToJsonSchema)(tool.inputSchema);
|
|
134
|
+
const validate = this.ajv.compile(zodSchema);
|
|
135
|
+
const valid = validate(msg.payload);
|
|
136
|
+
if (!valid) {
|
|
137
|
+
throw new exceptions_1.McpBadRequestException(this.ajv.errorsText(validate.errors));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
137
140
|
// const result = await tool.execute(msg.payload);
|
|
138
141
|
const stream = await (0, run_interceptors_1.runInterceptors)(this.moduleRef, metatype, context, () => {
|
|
139
142
|
return (0, rxjs_1.from)(tool.execute(msg.payload));
|
|
@@ -141,16 +144,21 @@ let McpService = class McpService {
|
|
|
141
144
|
return stream.pipe((0, rxjs_1.catchError)((err) => (0, rxjs_1.from)((0, run_fillters_1.runFilters)(this.moduleRef, metatype, err, context))));
|
|
142
145
|
}
|
|
143
146
|
catch (err) {
|
|
144
|
-
|
|
147
|
+
const result = await (0, run_fillters_1.runFilters)(this.moduleRef, metatype, err, context);
|
|
148
|
+
if (result) {
|
|
149
|
+
return (0, rxjs_1.of)(result);
|
|
150
|
+
}
|
|
151
|
+
// Пробрасываем дальше, чтобы Observable корректно завершился
|
|
152
|
+
return (0, rxjs_1.throwError)(() => err);
|
|
145
153
|
}
|
|
146
154
|
}
|
|
147
155
|
async executeResource(name, uri, vars, context) {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
throw new common_1.NotFoundException(`Unknown resource: "${name}"`);
|
|
156
|
+
if (!this.resources.has(name)) {
|
|
157
|
+
throw new exceptions_1.McpNotFoundException("Not found prompt");
|
|
151
158
|
}
|
|
152
|
-
|
|
159
|
+
const { instance: resource, metatype } = this.resources.get(name);
|
|
153
160
|
try {
|
|
161
|
+
await (0, run_guards_1.runGuards)(this.moduleRef, metatype, context);
|
|
154
162
|
// const result = await resource.execute(uri, vars);
|
|
155
163
|
const stream = await (0, run_interceptors_1.runInterceptors)(this.moduleRef, metatype, context, () => {
|
|
156
164
|
return (0, rxjs_1.from)(resource.execute(uri, vars));
|
|
@@ -158,7 +166,12 @@ let McpService = class McpService {
|
|
|
158
166
|
return stream.pipe((0, rxjs_1.catchError)((err) => (0, rxjs_1.from)((0, run_fillters_1.runFilters)(this.moduleRef, metatype, err, context))));
|
|
159
167
|
}
|
|
160
168
|
catch (err) {
|
|
161
|
-
|
|
169
|
+
const result = await (0, run_fillters_1.runFilters)(this.moduleRef, metatype, err, context);
|
|
170
|
+
if (result) {
|
|
171
|
+
return (0, rxjs_1.of)(result);
|
|
172
|
+
}
|
|
173
|
+
// Пробрасываем дальше, чтобы Observable корректно завершился
|
|
174
|
+
return (0, rxjs_1.throwError)(() => err);
|
|
162
175
|
}
|
|
163
176
|
}
|
|
164
177
|
createServer(context) {
|
|
@@ -182,8 +195,9 @@ let McpService = class McpService {
|
|
|
182
195
|
};
|
|
183
196
|
}
|
|
184
197
|
catch (e) {
|
|
185
|
-
|
|
186
|
-
|
|
198
|
+
throw new exceptions_1.McpInternalServerErrorException(`Faild to execute tool ${tool.name}`, {
|
|
199
|
+
cause: e,
|
|
200
|
+
});
|
|
187
201
|
}
|
|
188
202
|
});
|
|
189
203
|
}
|
|
@@ -209,7 +223,9 @@ let McpService = class McpService {
|
|
|
209
223
|
return { messages: messages };
|
|
210
224
|
}
|
|
211
225
|
catch (e) {
|
|
212
|
-
throw new
|
|
226
|
+
throw new exceptions_1.McpInternalServerErrorException(`Faild to execute tool ${prompt.name}`, {
|
|
227
|
+
cause: e,
|
|
228
|
+
});
|
|
213
229
|
}
|
|
214
230
|
});
|
|
215
231
|
}
|
|
@@ -234,7 +250,9 @@ let McpService = class McpService {
|
|
|
234
250
|
return { contents: result };
|
|
235
251
|
}
|
|
236
252
|
catch (e) {
|
|
237
|
-
throw new
|
|
253
|
+
throw new exceptions_1.McpInternalServerErrorException(`Faild to execute tool ${prompt.name}`, {
|
|
254
|
+
cause: e,
|
|
255
|
+
});
|
|
238
256
|
}
|
|
239
257
|
});
|
|
240
258
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.runGuards = runGuards;
|
|
4
|
-
const common_1 = require("@nestjs/common");
|
|
5
4
|
const constants_1 = require("@nestjs/common/constants");
|
|
5
|
+
const exceptions_1 = require("../exceptions");
|
|
6
6
|
/**
|
|
7
7
|
* Универсальный helper для проверки массивов NestJS Guards
|
|
8
8
|
*/
|
|
@@ -28,7 +28,7 @@ async function runGuards(moduleRef, metatype, context) {
|
|
|
28
28
|
const guardInstance = await resolveGuard(Guard);
|
|
29
29
|
const can = await guardInstance.canActivate(context);
|
|
30
30
|
if (!can) {
|
|
31
|
-
throw new
|
|
31
|
+
throw new exceptions_1.McpUnauthorizedException("Guard blocked execution");
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@muzikanto/nestjs-mcp",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.1",
|
|
4
4
|
"description": "NestJS MCP (Model Context Protocol) module for creating tools for LLM or HTTP with validation, decorators, and OpenAI Function Calls integration.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"nestjs",
|