@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 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 };
@@ -0,0 +1,2 @@
1
+ export * from "./mcp.module";
2
+ export * from "./decorators/mcp-tool.decorator";
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export * from "./mcp.module";
2
+ export * from "./decorators/mcp-tool.decorator";
@@ -0,0 +1,4 @@
1
+ export interface McpModuleOptions {
2
+ name?: string;
3
+ version?: string;
4
+ }
@@ -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,4 @@
1
+ import { DynamicModule } from "@nestjs/common";
2
+ export declare class McpModule {
3
+ static forRoot(): DynamicModule;
4
+ }
@@ -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
+ }