@muzikanto/nestjs-mcp 1.0.0-canary
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/LICENSE +21 -0
- package/README.md +164 -0
- package/dist/decorators/mcp-tool.decorator.d.ts +12 -0
- package/dist/decorators/mcp-tool.decorator.js +12 -0
- package/dist/example/example.tool.d.ts +14 -0
- package/dist/example/example.tool.js +23 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/interfaces/mcp-options.interface.d.ts +4 -0
- package/dist/interfaces/mcp-options.interface.js +1 -0
- package/dist/mcp.controller.d.ts +21 -0
- package/dist/mcp.controller.js +46 -0
- package/dist/mcp.explorer.d.ts +9 -0
- package/dist/mcp.explorer.js +42 -0
- package/dist/mcp.module.d.ts +4 -0
- package/dist/mcp.module.js +27 -0
- package/dist/mcp.service.d.ts +40 -0
- package/dist/mcp.service.js +56 -0
- package/package.json +39 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 [Muzikanto]
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# @muzikanto/nestjs-mcp
|
|
2
|
+
|
|
3
|
+
NestJS MCP (Model Context Protocol) module — allows you to create “tools” (functions) for LLM or HTTP, with automatic detection via decorators, validation, and integration with OpenAI Function Calls.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- Register MCP tools using the `@McpTool()` decorator
|
|
10
|
+
- Automatic detection of all providers (tools) in the module
|
|
11
|
+
- Input data validation
|
|
12
|
+
- HTTP endpoint for calling tools (`POST /mcp`)
|
|
13
|
+
- Endpoint for a list of all tools (`GET /mcp/tools`)
|
|
14
|
+
- Easy integration with LLM (OpenAI Function Calls)
|
|
15
|
+
- Full TypeScript typing
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
yarn add @muzikanto/nestjs-mcp
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Peer dependencies: `@nestjs/common, @nestjs/core, reflect-metadata`
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
### Connecting the MCP module
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { Module } from '@nestjs/common';
|
|
33
|
+
import { McpModule } from '@muzikanto/nestjs-mcp';
|
|
34
|
+
import { PaymentTool } from './tools/payment.tool';
|
|
35
|
+
|
|
36
|
+
@Module({
|
|
37
|
+
imports: [
|
|
38
|
+
McpModule.forRoot(),
|
|
39
|
+
],
|
|
40
|
+
providers: [
|
|
41
|
+
PaymentTool,
|
|
42
|
+
],
|
|
43
|
+
})
|
|
44
|
+
export class AppModule {}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Create MCP tool
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import { IMcpTool, McpTool } from '../decorators/mcp-tool.decorator';
|
|
51
|
+
|
|
52
|
+
@McpTool()
|
|
53
|
+
export class GetCurrentDate implements IMcpTool<{ country: string; }, { date: string }> {
|
|
54
|
+
name = 'get-date';
|
|
55
|
+
|
|
56
|
+
inputSchema = {
|
|
57
|
+
"type": "object",
|
|
58
|
+
"properties": {
|
|
59
|
+
"country": { "type": "string", "description": "Country" }
|
|
60
|
+
},
|
|
61
|
+
"required": ["country"]
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
async execute(input: { country: string }) {
|
|
65
|
+
return { date: new Date().toLocaleString() };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Calling MCP tools via HTTP
|
|
71
|
+
|
|
72
|
+
POST /mcp
|
|
73
|
+
|
|
74
|
+
```json
|
|
75
|
+
{
|
|
76
|
+
"type": "get-date",
|
|
77
|
+
"payload": {
|
|
78
|
+
"country": "ru"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Ответ
|
|
84
|
+
```json
|
|
85
|
+
{
|
|
86
|
+
"data": {
|
|
87
|
+
"date": "21/02/2026, 16:17:00"
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Obtaining all tools
|
|
93
|
+
|
|
94
|
+
GET /mcp/tool
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
[
|
|
98
|
+
{
|
|
99
|
+
"name": "get-date",
|
|
100
|
+
"description": "",
|
|
101
|
+
"inputSchema": {
|
|
102
|
+
"country": { "type": "string", "description": "Страна" },
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
]
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Integration with OpenAI Function Calls
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
import axios from 'axios';
|
|
112
|
+
import OpenAI from 'openai';
|
|
113
|
+
import { z } from 'zod';
|
|
114
|
+
|
|
115
|
+
const MCP_URL = 'http://localhost:3000/mcp';
|
|
116
|
+
const MCP_TOOLS_URL = 'http://localhost:3000/mcp/tools';
|
|
117
|
+
|
|
118
|
+
// Create OpenAI client
|
|
119
|
+
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Get all tools
|
|
123
|
+
*/
|
|
124
|
+
async function listMcpTools() {
|
|
125
|
+
const response = await axios.get(MCP_TOOLS_URL);
|
|
126
|
+
return response.data;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Request mcp tool
|
|
131
|
+
*/
|
|
132
|
+
async function callMcpTool(toolName: string, payload: Record<string, any>) {
|
|
133
|
+
const response = await axios.post(
|
|
134
|
+
MCP_URL,
|
|
135
|
+
{ type: toolName, payload },
|
|
136
|
+
{ headers: { 'Content-Type': 'application/json' } }
|
|
137
|
+
);
|
|
138
|
+
return response.data;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Example
|
|
143
|
+
*/
|
|
144
|
+
(async () => {
|
|
145
|
+
const functions = await listMcpTools();
|
|
146
|
+
|
|
147
|
+
const completion = await client.chat.completions.create({
|
|
148
|
+
model: 'gpt-4.1-mini',
|
|
149
|
+
messages: [{ role: 'user', content: 'Confirm cart abc123' }],
|
|
150
|
+
functions,
|
|
151
|
+
function_call: 'auto',
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
const message = completion.choices[0].message;
|
|
155
|
+
|
|
156
|
+
if (message.function_call) {
|
|
157
|
+
const { name, arguments: argsJson } = message.function_call;
|
|
158
|
+
const args = JSON.parse(argsJson);
|
|
159
|
+
|
|
160
|
+
const result = await callMcpTool(name, args);
|
|
161
|
+
console.log('Result from MCP tool:', result);
|
|
162
|
+
}
|
|
163
|
+
})();
|
|
164
|
+
```
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import "reflect-metadata";
|
|
2
|
+
export interface IMcpTool<Payload = any, Result = any> {
|
|
3
|
+
name: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
inputSchema?: object | object[];
|
|
6
|
+
execute(input: Payload): Promise<Result>;
|
|
7
|
+
}
|
|
8
|
+
export declare const MCP_TOOL_METADATA = "mcp:tool-class";
|
|
9
|
+
/**
|
|
10
|
+
* Декоратор класса для MCP тулзы
|
|
11
|
+
*/
|
|
12
|
+
export declare const McpTool: () => (target: any) => void;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Injectable } from "@nestjs/common";
|
|
2
|
+
import "reflect-metadata";
|
|
3
|
+
export const MCP_TOOL_METADATA = "mcp:tool-class";
|
|
4
|
+
/**
|
|
5
|
+
* Декоратор класса для MCP тулзы
|
|
6
|
+
*/
|
|
7
|
+
export const McpTool = () => {
|
|
8
|
+
return (target) => {
|
|
9
|
+
Injectable()(target);
|
|
10
|
+
Reflect.defineMetadata(MCP_TOOL_METADATA, {}, target);
|
|
11
|
+
};
|
|
12
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { IMcpTool } from '../decorators/mcp-tool.decorator';
|
|
3
|
+
export declare class PaymentTool implements IMcpTool {
|
|
4
|
+
name: string;
|
|
5
|
+
static inputSchema: z.ZodObject<{
|
|
6
|
+
cartId: z.ZodString;
|
|
7
|
+
}, z.core.$strip>;
|
|
8
|
+
execute(input: {
|
|
9
|
+
cartId: string;
|
|
10
|
+
}): Promise<{
|
|
11
|
+
status: string;
|
|
12
|
+
cartId: string;
|
|
13
|
+
}>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { Injectable } from '@nestjs/common';
|
|
8
|
+
import { z } from 'zod';
|
|
9
|
+
import { McpTool } from '../decorators/mcp-tool.decorator';
|
|
10
|
+
let PaymentTool = class PaymentTool {
|
|
11
|
+
name = 'example';
|
|
12
|
+
static inputSchema = z.object({
|
|
13
|
+
cartId: z.string().describe('ID'),
|
|
14
|
+
});
|
|
15
|
+
async execute(input) {
|
|
16
|
+
return { status: 'confirmed', cartId: input.cartId };
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
PaymentTool = __decorate([
|
|
20
|
+
McpTool('example'),
|
|
21
|
+
Injectable()
|
|
22
|
+
], PaymentTool);
|
|
23
|
+
export { PaymentTool };
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { McpMessage, McpService } from "./mcp.service";
|
|
2
|
+
export declare class McpController {
|
|
3
|
+
private readonly service;
|
|
4
|
+
constructor(service: McpService);
|
|
5
|
+
handle(body: McpMessage): Promise<{
|
|
6
|
+
success: boolean;
|
|
7
|
+
data: any;
|
|
8
|
+
error?: undefined;
|
|
9
|
+
} | {
|
|
10
|
+
success: boolean;
|
|
11
|
+
error: any;
|
|
12
|
+
data?: undefined;
|
|
13
|
+
}>;
|
|
14
|
+
getTools(): Promise<{
|
|
15
|
+
tools: {
|
|
16
|
+
name: string;
|
|
17
|
+
description: string;
|
|
18
|
+
parameters: object;
|
|
19
|
+
}[];
|
|
20
|
+
}>;
|
|
21
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
11
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
|
+
};
|
|
13
|
+
import { Controller, Post, Body, Header, Get } from "@nestjs/common";
|
|
14
|
+
import { McpService } from "./mcp.service";
|
|
15
|
+
let McpController = class McpController {
|
|
16
|
+
service;
|
|
17
|
+
constructor(service) {
|
|
18
|
+
this.service = service;
|
|
19
|
+
}
|
|
20
|
+
async handle(body) {
|
|
21
|
+
return this.service.sendMessage(body);
|
|
22
|
+
}
|
|
23
|
+
async getTools() {
|
|
24
|
+
const tools = this.service.listTools();
|
|
25
|
+
return { tools };
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
__decorate([
|
|
29
|
+
Post(),
|
|
30
|
+
Header("Content-Type", "application/json"),
|
|
31
|
+
__param(0, Body()),
|
|
32
|
+
__metadata("design:type", Function),
|
|
33
|
+
__metadata("design:paramtypes", [Object]),
|
|
34
|
+
__metadata("design:returntype", Promise)
|
|
35
|
+
], McpController.prototype, "handle", null);
|
|
36
|
+
__decorate([
|
|
37
|
+
Get("tools"),
|
|
38
|
+
__metadata("design:type", Function),
|
|
39
|
+
__metadata("design:paramtypes", []),
|
|
40
|
+
__metadata("design:returntype", Promise)
|
|
41
|
+
], McpController.prototype, "getTools", null);
|
|
42
|
+
McpController = __decorate([
|
|
43
|
+
Controller("mcp"),
|
|
44
|
+
__metadata("design:paramtypes", [McpService])
|
|
45
|
+
], McpController);
|
|
46
|
+
export { McpController };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { DiscoveryService } from "@nestjs/core";
|
|
2
|
+
import { OnModuleInit } from "@nestjs/common";
|
|
3
|
+
import { McpService } from "./mcp.service";
|
|
4
|
+
export declare class McpExplorer implements OnModuleInit {
|
|
5
|
+
private readonly discovery;
|
|
6
|
+
private readonly mcpService;
|
|
7
|
+
constructor(discovery: DiscoveryService, mcpService: McpService);
|
|
8
|
+
onModuleInit(): void;
|
|
9
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
import { DiscoveryService } from "@nestjs/core";
|
|
11
|
+
import { Injectable } from "@nestjs/common";
|
|
12
|
+
import { McpService } from "./mcp.service";
|
|
13
|
+
import { MCP_TOOL_METADATA } from "./decorators/mcp-tool.decorator";
|
|
14
|
+
let McpExplorer = class McpExplorer {
|
|
15
|
+
discovery;
|
|
16
|
+
mcpService;
|
|
17
|
+
constructor(discovery, mcpService) {
|
|
18
|
+
this.discovery = discovery;
|
|
19
|
+
this.mcpService = mcpService;
|
|
20
|
+
}
|
|
21
|
+
onModuleInit() {
|
|
22
|
+
const providers = this.discovery.getProviders();
|
|
23
|
+
providers.forEach(({ instance, metatype }) => {
|
|
24
|
+
if (!instance || !metatype)
|
|
25
|
+
return;
|
|
26
|
+
const metadata = Reflect.getMetadata(MCP_TOOL_METADATA, metatype);
|
|
27
|
+
if (!metadata)
|
|
28
|
+
return;
|
|
29
|
+
// Проверка интерфейса
|
|
30
|
+
if (typeof instance.execute !== "function") {
|
|
31
|
+
throw new Error(`MCP Tool ${metadata.name} must implement IMcpTool with execute() method`);
|
|
32
|
+
}
|
|
33
|
+
this.mcpService.registerTool(metadata.name, instance);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
McpExplorer = __decorate([
|
|
38
|
+
Injectable(),
|
|
39
|
+
__metadata("design:paramtypes", [DiscoveryService,
|
|
40
|
+
McpService])
|
|
41
|
+
], McpExplorer);
|
|
42
|
+
export { McpExplorer };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var McpModule_1;
|
|
8
|
+
import { Module } from "@nestjs/common";
|
|
9
|
+
import { DiscoveryModule } from "@nestjs/core";
|
|
10
|
+
import { McpService } from "./mcp.service";
|
|
11
|
+
import { McpExplorer } from "./mcp.explorer";
|
|
12
|
+
import { McpController } from "./mcp.controller";
|
|
13
|
+
let McpModule = McpModule_1 = class McpModule {
|
|
14
|
+
static forRoot() {
|
|
15
|
+
return {
|
|
16
|
+
module: McpModule_1,
|
|
17
|
+
imports: [DiscoveryModule],
|
|
18
|
+
providers: [McpService, McpExplorer],
|
|
19
|
+
exports: [McpService],
|
|
20
|
+
controllers: [McpController],
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
McpModule = McpModule_1 = __decorate([
|
|
25
|
+
Module({})
|
|
26
|
+
], McpModule);
|
|
27
|
+
export { McpModule };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { OnModuleInit } from "@nestjs/common";
|
|
2
|
+
import { IMcpTool } from "./decorators/mcp-tool.decorator";
|
|
3
|
+
export interface McpMessage {
|
|
4
|
+
type: string;
|
|
5
|
+
payload: any;
|
|
6
|
+
}
|
|
7
|
+
export interface McpResponse {
|
|
8
|
+
success: boolean;
|
|
9
|
+
data?: any;
|
|
10
|
+
error?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare class McpService implements OnModuleInit {
|
|
13
|
+
private tools;
|
|
14
|
+
private ajv;
|
|
15
|
+
onModuleInit(): void;
|
|
16
|
+
/**
|
|
17
|
+
* Возвращает список зарегистрированных тулз
|
|
18
|
+
*/
|
|
19
|
+
listTools(): {
|
|
20
|
+
name: string;
|
|
21
|
+
description: string;
|
|
22
|
+
parameters: object;
|
|
23
|
+
}[];
|
|
24
|
+
registerTool(name: string, handler: IMcpTool): void;
|
|
25
|
+
/**
|
|
26
|
+
* Отправить сообщение в MCP "сервер"
|
|
27
|
+
*/
|
|
28
|
+
sendMessage(msg: {
|
|
29
|
+
type: string;
|
|
30
|
+
payload: any;
|
|
31
|
+
}): Promise<{
|
|
32
|
+
success: boolean;
|
|
33
|
+
data: any;
|
|
34
|
+
error?: undefined;
|
|
35
|
+
} | {
|
|
36
|
+
success: boolean;
|
|
37
|
+
error: any;
|
|
38
|
+
data?: undefined;
|
|
39
|
+
}>;
|
|
40
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { Injectable } from "@nestjs/common";
|
|
8
|
+
import Ajv from "ajv";
|
|
9
|
+
let McpService = class McpService {
|
|
10
|
+
tools = new Map();
|
|
11
|
+
ajv = new Ajv();
|
|
12
|
+
onModuleInit() {
|
|
13
|
+
// console.log('MCP Service initialized');
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Возвращает список зарегистрированных тулз
|
|
17
|
+
*/
|
|
18
|
+
listTools() {
|
|
19
|
+
return Array.from(this.tools.values()).map((t) => ({
|
|
20
|
+
name: t.name,
|
|
21
|
+
description: t.description || "",
|
|
22
|
+
parameters: t.inputSchema || {},
|
|
23
|
+
}));
|
|
24
|
+
}
|
|
25
|
+
registerTool(name, handler) {
|
|
26
|
+
this.tools.set(name, handler);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Отправить сообщение в MCP "сервер"
|
|
30
|
+
*/
|
|
31
|
+
async sendMessage(msg) {
|
|
32
|
+
const tool = this.tools.get(msg.type);
|
|
33
|
+
if (!tool) {
|
|
34
|
+
return { success: false, error: `Unknown tool: ${msg.type}` };
|
|
35
|
+
}
|
|
36
|
+
// Валидация через AJV, если есть inputSchema
|
|
37
|
+
if (tool.inputSchema) {
|
|
38
|
+
const validate = this.ajv.compile(tool.inputSchema);
|
|
39
|
+
const valid = validate(msg.payload);
|
|
40
|
+
if (!valid) {
|
|
41
|
+
return { success: false, error: this.ajv.errorsText(validate.errors) };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
const result = await tool.execute(msg.payload);
|
|
46
|
+
return { success: true, data: result };
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
return { success: false, error: err.message };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
McpService = __decorate([
|
|
54
|
+
Injectable()
|
|
55
|
+
], McpService);
|
|
56
|
+
export { McpService };
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@muzikanto/nestjs-mcp",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "1.0.0-canary",
|
|
5
|
+
"description": "Model Context Protocol integration for NestJS",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"nestjs",
|
|
8
|
+
"mcp",
|
|
9
|
+
"ai",
|
|
10
|
+
"model-context-protocol"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"main": "dist/index.js",
|
|
14
|
+
"types": "dist/index.d.ts",
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc",
|
|
20
|
+
"prepublishOnly": "npm run build"
|
|
21
|
+
},
|
|
22
|
+
"peerDependencies": {
|
|
23
|
+
"@nestjs/common": "^10 || ^11",
|
|
24
|
+
"@nestjs/core": "^10 || ^11",
|
|
25
|
+
"reflect-metadata": "^0.1.13"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
29
|
+
"ajv": "^8.18.0",
|
|
30
|
+
"@nestjs/common": "^10 || ^11",
|
|
31
|
+
"@nestjs/core": "^10 || ^11",
|
|
32
|
+
"reflect-metadata": "^0.1.13"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/node": "^20.0.0",
|
|
36
|
+
"prettier": "^3.8.1",
|
|
37
|
+
"typescript": "^5.0.0"
|
|
38
|
+
}
|
|
39
|
+
}
|