@muzikanto/nestjs-mcp 1.6.0 → 1.7.0

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 (27) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +64 -42
  3. package/dist/mcp-client/mcp-client.service.d.ts +2 -2
  4. package/dist/mcp-server/controllers/mcp.controller.d.ts +4 -3
  5. package/dist/mcp-server/controllers/mcp.controller.js +6 -6
  6. package/dist/mcp-server/decorators/mcp-prompt.decorator.d.ts +7 -3
  7. package/dist/mcp-server/decorators/mcp-resource.decorator.d.ts +3 -1
  8. package/dist/mcp-server/decorators/mcp-tool.decorator.d.ts +14 -2
  9. package/dist/mcp-server/dto/McpPrompResult.dto.d.ts +5 -0
  10. package/dist/mcp-server/dto/McpPrompResult.dto.js +31 -0
  11. package/dist/mcp-server/dto/McpPromptMessage.dto.d.ts +5 -0
  12. package/dist/mcp-server/dto/McpPromptMessage.dto.js +40 -0
  13. package/dist/mcp-server/dto/McpTool.dto.d.ts +1 -0
  14. package/dist/mcp-server/dto/McpTool.dto.js +10 -0
  15. package/dist/mcp-server/dto/McpToolResult.dto.d.ts +6 -0
  16. package/dist/mcp-server/dto/McpToolResult.dto.js +32 -0
  17. package/dist/mcp-server/dto/McpToolResultMessage.dto.d.ts +4 -0
  18. package/dist/mcp-server/dto/McpToolResultMessage.dto.js +26 -0
  19. package/dist/mcp-server/exceptions/index.d.ts +3 -1
  20. package/dist/mcp-server/exceptions/index.js +3 -1
  21. package/dist/mcp-server/services/mcp-dynamic.service.d.ts +2 -2
  22. package/dist/mcp-server/services/mcp-dynamic.service.js +5 -0
  23. package/dist/mcp-server/services/mcp.service.d.ts +8 -6
  24. package/dist/mcp-server/services/mcp.service.js +5 -2
  25. package/dist/mcp-server/services/mcp.sse.service.d.ts +3 -0
  26. package/dist/mcp-server/services/mcp.sse.service.js +20 -6
  27. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @muzikanto/nestjs-mcp
2
2
 
3
+ ## 1.7.0
4
+
5
+ ### Minor Changes
6
+
7
+ - fix tool result format
8
+
3
9
  ## 1.6.0
4
10
 
5
11
  ### Minor Changes
package/README.md CHANGED
@@ -85,7 +85,7 @@ export class AppModule {}
85
85
  ```ts
86
86
  import { IMcpTool, McpTool } from "@muzikanto/nestjs-mcp";
87
87
  import { Telegraf } from "telegraf";
88
- import z from "zod";
88
+ import z from "zod/v3";
89
89
 
90
90
  const schema = {
91
91
  chatId: z.number().describe("Telegram chat id"), // строка с описанием
@@ -98,14 +98,16 @@ export class TelegramSendMessageTool implements IMcpTool<
98
98
  { success: boolean }
99
99
  > {
100
100
  name = "telegram.sendMessage";
101
-
102
101
  inputSchema = schema;
103
102
 
104
103
  constructor(private readonly bot: Telegraf) {}
105
104
 
106
105
  async execute(input: { chatId: string; text: string }) {
107
106
  await this.bot.telegram.sendMessage(input.chatId, input.text);
108
- return { success: true };
107
+ return {
108
+ structuredContent: { success: true },
109
+ messages: [{ type: "text", text: "Success sent to user" }],
110
+ };
109
111
  }
110
112
  }
111
113
  ```
@@ -128,9 +130,10 @@ POST /mcp/tools
128
130
 
129
131
  ```json
130
132
  {
131
- "data": {
133
+ "structuredContent": {
132
134
  "success": true
133
- }
135
+ },
136
+ "messages": [{ "type": "text", "text": "Success sent to user" }]"
134
137
  }
135
138
  ```
136
139
 
@@ -167,7 +170,7 @@ GET /mcp/tools
167
170
 
168
171
  ```ts
169
172
  import { IMcpPrompt, McpPrompt } from "@muzikanto/nestjs-mcp";
170
- import z from "zod";
173
+ import z from "zod/v3";
171
174
 
172
175
  const schema = {
173
176
  chatId: z.number().describe("Telegram chat id"), // строка с описанием
@@ -185,26 +188,28 @@ export class TelegramAutoReplyPrompt implements IMcpPrompt<{
185
188
  schema = schema;
186
189
 
187
190
  async execute({ text, chatId }: { text: string; chatId: number }) {
188
- return [
189
- {
190
- role: "system",
191
- content: `You are a friendly Telegram bot. Reply briefly and to the point.`,
192
- },
193
- {
194
- role: "user",
195
- content: text,
196
- },
197
- {
198
- role: "assistant",
199
- tool_call: {
200
- name: "telegram.sendMessage",
201
- arguments: {
202
- chatId,
203
- text: "{{model_output}}",
191
+ return {
192
+ messages: [
193
+ {
194
+ role: "system",
195
+ content: `You are a friendly Telegram bot. Reply briefly and to the point.`,
196
+ },
197
+ {
198
+ role: "user",
199
+ content: text,
200
+ },
201
+ {
202
+ role: "assistant",
203
+ tool_call: {
204
+ name: "telegram.sendMessage",
205
+ arguments: {
206
+ chatId,
207
+ text: "{{model_output}}",
208
+ },
204
209
  },
205
210
  },
206
- },
207
- ];
211
+ ],
212
+ };
208
213
  }
209
214
  }
210
215
  ```
@@ -349,7 +354,11 @@ export class McpDynamic {
349
354
  this.mcpDynamicService.registerTool({
350
355
  name: "dynamic_tool",
351
356
  title: "Dynamic tool",
352
- execute: () => Promise.resolve("test"),
357
+ execute: () =>
358
+ Promise.resolve({
359
+ structuredContent: { text: "test" },
360
+ messages: [{ type: "text" as const, text: "test" }],
361
+ }),
353
362
  guards: [ExampleGuard],
354
363
  interceptors: [ExampleInterceptor],
355
364
  });
@@ -357,7 +366,8 @@ export class McpDynamic {
357
366
  this.mcpDynamicService.registerPrompt({
358
367
  name: "dynamic_prompt",
359
368
  title: "Dynamic prompt",
360
- execute: () => Promise.resolve([{ role: "assistant", content: "test" }]),
369
+ execute: () =>
370
+ Promise.resolve({ messages: [{ role: "assistant", content: "test" }] }),
361
371
  guards: [ExampleGuard],
362
372
  interceptors: [ExampleInterceptor],
363
373
  });
@@ -370,6 +380,11 @@ export class McpDynamic {
370
380
  Promise.resolve([{ uri: uri.href, text: `ID: ${input.testId}` }]),
371
381
  guards: [ExampleGuard],
372
382
  interceptors: [ExampleInterceptor],
383
+ list: async () => {
384
+ return [
385
+ { uri: "dynamic://test/1", name: "dynamice_1", title: "Dynamic 1" },
386
+ ];
387
+ },
373
388
  });
374
389
  }
375
390
  }
@@ -405,7 +420,10 @@ export class TelegramSendMessageTool implements IMcpTool<
405
420
 
406
421
  async execute() {
407
422
  // await this.bot.telegram.sendMessage(input.chatId, input.text);
408
- return { success: true };
423
+ return {
424
+ structuredContent: { success: true },
425
+ messages: { type: "text", text: "Success sent" },
426
+ };
409
427
  }
410
428
  }
411
429
  ```
@@ -473,7 +491,10 @@ export class TelegramSendMessageTool implements IMcpTool<
473
491
 
474
492
  async execute() {
475
493
  // await this.bot.telegram.sendMessage(input.chatId, input.text);
476
- return { success: true };
494
+ return {
495
+ success: true,
496
+ messagse: [{ type: "text", text: "Success sent" }],
497
+ };
477
498
  }
478
499
  }
479
500
  ```
@@ -554,11 +575,12 @@ export class TelegramSendMessageTool implements IMcpTool<
554
575
  ### Integration with OpenAI Function Calls
555
576
 
556
577
  ```ts
557
- import axios from 'axios';
558
- import OpenAI from 'openai';
578
+ import axios from "axios";
579
+ import OpenAI from "openai";
559
580
 
560
- const MCP_TOOLS_URL = 'http://localhost:3000/mcp/tools';
561
- const MCP_TELEGRAM_PROMPT_URL = 'http://localhost:3000/mcp/prompts/telegram_auto_reply';
581
+ const MCP_TOOLS_URL = "http://localhost:3000/mcp/tools";
582
+ const MCP_TELEGRAM_PROMPT_URL =
583
+ "http://localhost:3000/mcp/prompts/telegram_auto_reply";
562
584
 
563
585
  // Create OpenAI client
564
586
  const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
@@ -568,7 +590,7 @@ const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
568
590
  */
569
591
  async function getMcpTools() {
570
592
  const response = await axios.get(MCP_TOOLS_URL);
571
- return response.data.tools.map(el => ({
593
+ return response.data.tools.map((el) => ({
572
594
  name: el.name,
573
595
  description: el.description,
574
596
  parameters: el.inputSchema,
@@ -579,17 +601,17 @@ async function getMcpTools() {
579
601
  * Get all prompts
580
602
  */
581
603
  async function getMcpPrompt() {
582
- const response = await axios.post(MCP_TELEGRAM_PROMPT_URL, {/* prompt generation arguments */});
604
+ const response = await axios.post(MCP_TELEGRAM_PROMPT_URL, {
605
+ /* prompt generation arguments */
606
+ });
583
607
  return response.data;
608
+ }
584
609
 
585
610
  /**
586
611
  * Request mcp tool
587
612
  */
588
613
  async function callMcpTool(toolName: string, payload: Record<string, any>) {
589
- const response = await axios.post(
590
- MCP_TOOLS_URL,
591
- { type: toolName, payload },
592
- );
614
+ const response = await axios.post(MCP_TOOLS_URL, { type: toolName, payload });
593
615
  return response.data;
594
616
  }
595
617
 
@@ -601,10 +623,10 @@ async function callMcpTool(toolName: string, payload: Record<string, any>) {
601
623
  const promptResponse = await getMcpPrompt();
602
624
 
603
625
  const completion = await client.chat.completions.create({
604
- model: 'gpt-4.1-mini',
626
+ model: "gpt-4.1-mini",
605
627
  messages: promptResponse.messages,
606
628
  functions: toolsResponse.tools,
607
- function_call: 'auto',
629
+ function_call: "auto",
608
630
  });
609
631
 
610
632
  const message = completion.choices[0].message;
@@ -613,8 +635,8 @@ async function callMcpTool(toolName: string, payload: Record<string, any>) {
613
635
  const { name, arguments: argsJson } = message.function_call;
614
636
  const args = JSON.parse(argsJson);
615
637
 
616
- const result = await callMcpTool(name, args);
617
- console.log('Result from MCP tool:', result);
638
+ const { messages, structuredContent } = await callMcpTool(name, args);
639
+ console.log("Result from MCP tool:", messages, structuredContent);
618
640
  }
619
641
  })();
620
642
  ```
@@ -1,12 +1,12 @@
1
1
  import { HttpService } from "@nestjs/axios";
2
2
  import { McpToolsDto } from "../mcp-server/dto/McpTools.dto";
3
3
  import { McpPromptsDto } from "../mcp-server/dto/McpPrompts.dto";
4
- import { McpPromptMessagesDto } from "../mcp-server/dto/McpPromptMessages.dto";
4
+ import { McpPromptResultDto } from "../mcp-server/dto/McpPrompResult.dto";
5
5
  export declare class McpClientService {
6
6
  private readonly httpService;
7
7
  constructor(httpService: HttpService);
8
8
  getAllTools(): Promise<McpToolsDto>;
9
9
  getAllPrompts(): Promise<McpPromptsDto>;
10
- getPromptByName<Payload = any>(promptName: string, params: Payload): Promise<McpPromptMessagesDto>;
10
+ getPromptByName<Payload = any>(promptName: string, params: Payload): Promise<McpPromptResultDto>;
11
11
  callMcpTool<Payload = any, Result = any>(toolName: string, payload: Payload): Promise<Result>;
12
12
  }
@@ -3,20 +3,21 @@ import { McpService } from "../services/mcp.service";
3
3
  import { McpMessageDto } from "../dto/McpMessage.dto";
4
4
  import { McpToolsDto } from "../dto/McpTools.dto";
5
5
  import { McpPromptsDto } from "../dto/McpPrompts.dto";
6
- import { McpPromptMessagesDto } from "../dto/McpPromptMessages.dto";
6
+ import { McpPromptResultDto } from "../dto/McpPrompResult.dto";
7
7
  import { IMcpConfig } from "../config";
8
8
  import { McpResourcesDto } from "../dto/McpResources.dto";
9
9
  import { McpResourceItemsDto } from "../dto/McpResourceItems.dto";
10
10
  import { McpResourceRequestDto } from "../dto/McpResourceRequest.dto";
11
+ import { McpToolResultDto } from "../dto/McpToolResult.dto";
11
12
  export declare class McpController {
12
13
  private readonly service;
13
14
  private readonly guard;
14
15
  private readonly config;
15
16
  constructor(service: McpService, guard: CanActivate, config: IMcpConfig);
16
- handleTool(body: McpMessageDto, request: any, context: ExecutionContext): Promise<any>;
17
+ handleTool(body: McpMessageDto, request: any, context: ExecutionContext): Promise<McpToolResultDto>;
17
18
  getTools(context: ExecutionContext): Promise<McpToolsDto>;
18
19
  getPrompts(context: ExecutionContext): Promise<McpPromptsDto>;
19
- getPrompt(name: string, body: object, context: ExecutionContext): Promise<McpPromptMessagesDto>;
20
+ getPrompt(name: string, body: object, context: ExecutionContext): Promise<McpPromptResultDto>;
20
21
  getResources(context: ExecutionContext): Promise<McpResourcesDto>;
21
22
  executeResource(name: string, body: McpResourceRequestDto, context: ExecutionContext): Promise<McpResourceItemsDto>;
22
23
  private checkGuard;
@@ -19,7 +19,7 @@ const McpMessage_dto_1 = require("../dto/McpMessage.dto");
19
19
  const swagger_1 = require("@nestjs/swagger");
20
20
  const McpTools_dto_1 = require("../dto/McpTools.dto");
21
21
  const McpPrompts_dto_1 = require("../dto/McpPrompts.dto");
22
- const McpPromptMessages_dto_1 = require("../dto/McpPromptMessages.dto");
22
+ const McpPrompResult_dto_1 = require("../dto/McpPrompResult.dto");
23
23
  const context_decorator_1 = require("../utils/context.decorator");
24
24
  const inject_tokens_1 = require("../utils/inject-tokens");
25
25
  const config_1 = require("../config");
@@ -28,6 +28,7 @@ const McpResources_dto_1 = require("../dto/McpResources.dto");
28
28
  const McpResourceItems_dto_1 = require("../dto/McpResourceItems.dto");
29
29
  const McpResourceRequest_dto_1 = require("../dto/McpResourceRequest.dto");
30
30
  const uri_1 = require("../utils/uri");
31
+ const McpToolResult_dto_1 = require("../dto/McpToolResult.dto");
31
32
  let McpController = class McpController {
32
33
  service;
33
34
  guard;
@@ -56,10 +57,8 @@ let McpController = class McpController {
56
57
  async getPrompt(name, body, context) {
57
58
  await this.checkGuard(context);
58
59
  const observable = await this.service.executePrompt(name, body, context);
59
- const messages = await (0, rxjs_1.firstValueFrom)(observable);
60
- return {
61
- messages,
62
- };
60
+ const result = await (0, rxjs_1.firstValueFrom)(observable);
61
+ return result;
63
62
  }
64
63
  async getResources(context) {
65
64
  await this.checkGuard(context);
@@ -91,6 +90,7 @@ __decorate([
91
90
  summary: "Request tool",
92
91
  }),
93
92
  (0, swagger_1.ApiResponse)({
93
+ type: McpToolResult_dto_1.McpToolResultDto,
94
94
  status: 200,
95
95
  description: "Tool execution result",
96
96
  }),
@@ -155,7 +155,7 @@ __decorate([
155
155
  (0, swagger_1.ApiResponse)({
156
156
  status: 200,
157
157
  description: "Prompt messages",
158
- type: McpPromptMessages_dto_1.McpPromptMessagesDto,
158
+ type: McpPrompResult_dto_1.McpPromptResultDto,
159
159
  }),
160
160
  (0, swagger_1.ApiNotFoundResponse)({
161
161
  description: "Not found prompt",
@@ -2,7 +2,11 @@ import "reflect-metadata";
2
2
  import type * as z3 from "zod/v3";
3
3
  type AnySchema = z3.ZodTypeAny;
4
4
  type ZodRawShapeCompat = Record<string, AnySchema>;
5
- export type IMcpPromptMessage = {
5
+ export type IMcpPromptResult = {
6
+ messages: IMcpPromptResultMessage[];
7
+ description?: string;
8
+ };
9
+ export type IMcpPromptResultMessage = {
6
10
  role: "user" | "assistant" | "system";
7
11
  content?: string;
8
12
  tool_call?: {
@@ -10,12 +14,12 @@ export type IMcpPromptMessage = {
10
14
  arguments: object;
11
15
  };
12
16
  };
13
- export interface IMcpPrompt<Payload = any, Result extends IMcpPromptMessage[] = IMcpPromptMessage[]> {
17
+ export interface IMcpPrompt<Payload = any> {
14
18
  name: string;
15
19
  title?: string;
16
20
  description?: string;
17
21
  inputSchema?: ZodRawShapeCompat;
18
- execute(input: Payload): Promise<Result>;
22
+ execute(input: Payload): Promise<IMcpPromptResult>;
19
23
  }
20
24
  export declare const MCP_PROMPT_METADATA = "mcp:prompt-class";
21
25
  /**
@@ -1,5 +1,5 @@
1
1
  import { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol";
2
- import { ServerNotification, ServerRequest } from "@modelcontextprotocol/sdk/types";
2
+ import { Resource, ServerNotification, ServerRequest } from "@modelcontextprotocol/sdk/types";
3
3
  import "reflect-metadata";
4
4
  export type IMcpResourceResult = {
5
5
  uri: string;
@@ -16,6 +16,8 @@ export interface IMcpResource<Payload = any> {
16
16
  uri: string;
17
17
  title?: string;
18
18
  description?: string;
19
+ annotations?: Resource["annotations"];
20
+ _meta?: Resource["_meta"];
19
21
  execute(url: URL, input: Payload): Promise<IMcpResourceResult[]>;
20
22
  list?(extra: RequestHandlerExtra<ServerRequest, ServerNotification>): Promise<IMcpResourceListItem[]>;
21
23
  }
@@ -1,13 +1,25 @@
1
+ import { ToolAnnotations } from "@modelcontextprotocol/sdk/types";
1
2
  import "reflect-metadata";
2
3
  import type * as z3 from "zod/v3";
3
4
  type AnySchema = z3.ZodTypeAny;
4
5
  type ZodRawShapeCompat = Record<string, AnySchema>;
5
- export interface IMcpTool<Payload = any, Result = any> {
6
+ export type IMcpToolResult<Result extends Record<string, unknown>> = {
7
+ structuredContent?: Result;
8
+ messages: Array<{
9
+ type: "text";
10
+ text: string;
11
+ }>;
12
+ isError?: boolean;
13
+ };
14
+ export interface IMcpTool<Payload = any, Result extends Record<string, unknown> = Record<string, unknown>> {
6
15
  name: string;
7
16
  title?: string;
8
17
  description?: string;
9
18
  inputSchema?: ZodRawShapeCompat;
10
- execute(input: Payload): Promise<Result>;
19
+ outputSchema?: ZodRawShapeCompat;
20
+ annotations?: ToolAnnotations;
21
+ _meta?: Record<string, unknown>;
22
+ execute(input: Payload): Promise<IMcpToolResult<Result>>;
11
23
  }
12
24
  export declare const MCP_TOOL_METADATA = "mcp:tool-class";
13
25
  /**
@@ -0,0 +1,5 @@
1
+ import { McpPromptMessageDto } from "./McpPromptMessage.dto";
2
+ export declare class McpPromptResultDto {
3
+ messages: McpPromptMessageDto[];
4
+ description?: string;
5
+ }
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ 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;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.McpPromptResultDto = void 0;
13
+ const swagger_1 = require("@nestjs/swagger");
14
+ const McpPromptMessage_dto_1 = require("./McpPromptMessage.dto");
15
+ class McpPromptResultDto {
16
+ messages;
17
+ description;
18
+ }
19
+ exports.McpPromptResultDto = McpPromptResultDto;
20
+ __decorate([
21
+ (0, swagger_1.ApiProperty)({
22
+ type: McpPromptMessage_dto_1.McpPromptMessageDto,
23
+ isArray: true,
24
+ description: "Messages list",
25
+ }),
26
+ __metadata("design:type", Array)
27
+ ], McpPromptResultDto.prototype, "messages", void 0);
28
+ __decorate([
29
+ (0, swagger_1.ApiProperty)({ type: "string", nullable: true }),
30
+ __metadata("design:type", String)
31
+ ], McpPromptResultDto.prototype, "description", void 0);
@@ -0,0 +1,5 @@
1
+ export declare class McpPromptMessageDto {
2
+ role: string;
3
+ content?: string;
4
+ tool_call?: object;
5
+ }
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ 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;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.McpPromptMessageDto = void 0;
13
+ const swagger_1 = require("@nestjs/swagger");
14
+ class McpPromptMessageDto {
15
+ role;
16
+ content;
17
+ tool_call;
18
+ }
19
+ exports.McpPromptMessageDto = McpPromptMessageDto;
20
+ __decorate([
21
+ (0, swagger_1.ApiProperty)({ type: "string", description: "AI role" }),
22
+ __metadata("design:type", String)
23
+ ], McpPromptMessageDto.prototype, "role", void 0);
24
+ __decorate([
25
+ (0, swagger_1.ApiProperty)({
26
+ type: "string",
27
+ description: "Request ai message",
28
+ nullable: true,
29
+ }),
30
+ __metadata("design:type", String)
31
+ ], McpPromptMessageDto.prototype, "content", void 0);
32
+ __decorate([
33
+ (0, swagger_1.ApiProperty)({
34
+ type: "object",
35
+ description: "Tool call context",
36
+ nullable: true,
37
+ additionalProperties: false,
38
+ }),
39
+ __metadata("design:type", Object)
40
+ ], McpPromptMessageDto.prototype, "tool_call", void 0);
@@ -3,4 +3,5 @@ export declare class McpToolDto {
3
3
  title?: string;
4
4
  description?: string;
5
5
  inputSchema?: any;
6
+ outputSchema?: any;
6
7
  }
@@ -16,6 +16,7 @@ class McpToolDto {
16
16
  title;
17
17
  description;
18
18
  inputSchema;
19
+ outputSchema;
19
20
  }
20
21
  exports.McpToolDto = McpToolDto;
21
22
  __decorate([
@@ -47,3 +48,12 @@ __decorate([
47
48
  }),
48
49
  __metadata("design:type", Object)
49
50
  ], McpToolDto.prototype, "inputSchema", void 0);
51
+ __decorate([
52
+ (0, swagger_1.ApiProperty)({
53
+ type: "object",
54
+ description: "Tool result schema",
55
+ nullable: true,
56
+ additionalProperties: false,
57
+ }),
58
+ __metadata("design:type", Object)
59
+ ], McpToolDto.prototype, "outputSchema", void 0);
@@ -0,0 +1,6 @@
1
+ import { McpToolResultMessageDto } from "./McpToolResultMessage.dto";
2
+ export declare class McpToolResultDto {
3
+ structuredContent?: any;
4
+ messages: McpToolResultMessageDto[];
5
+ isError?: boolean;
6
+ }
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ 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;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.McpToolResultDto = void 0;
13
+ const swagger_1 = require("@nestjs/swagger");
14
+ const McpToolResultMessage_dto_1 = require("./McpToolResultMessage.dto");
15
+ class McpToolResultDto {
16
+ structuredContent;
17
+ messages;
18
+ isError;
19
+ }
20
+ exports.McpToolResultDto = McpToolResultDto;
21
+ __decorate([
22
+ (0, swagger_1.ApiProperty)({ description: "Tool result", nullable: true }),
23
+ __metadata("design:type", Object)
24
+ ], McpToolResultDto.prototype, "structuredContent", void 0);
25
+ __decorate([
26
+ (0, swagger_1.ApiProperty)({ type: McpToolResultMessage_dto_1.McpToolResultMessageDto, isArray: true }),
27
+ __metadata("design:type", Array)
28
+ ], McpToolResultDto.prototype, "messages", void 0);
29
+ __decorate([
30
+ (0, swagger_1.ApiProperty)({ type: "boolean", nullable: true }),
31
+ __metadata("design:type", Boolean)
32
+ ], McpToolResultDto.prototype, "isError", void 0);
@@ -0,0 +1,4 @@
1
+ export declare class McpToolResultMessageDto {
2
+ type: string;
3
+ text: string;
4
+ }
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ 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;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.McpToolResultMessageDto = void 0;
13
+ const swagger_1 = require("@nestjs/swagger");
14
+ class McpToolResultMessageDto {
15
+ type;
16
+ text;
17
+ }
18
+ exports.McpToolResultMessageDto = McpToolResultMessageDto;
19
+ __decorate([
20
+ (0, swagger_1.ApiProperty)({ type: "string", description: "Message type" }),
21
+ __metadata("design:type", String)
22
+ ], McpToolResultMessageDto.prototype, "type", void 0);
23
+ __decorate([
24
+ (0, swagger_1.ApiProperty)({ type: "string", description: "Message text" }),
25
+ __metadata("design:type", String)
26
+ ], McpToolResultMessageDto.prototype, "text", void 0);
@@ -1,11 +1,13 @@
1
1
  import { HttpException, HttpStatus } from "@nestjs/common";
2
+ import { ErrorObject } from "ajv";
2
3
  export declare class McpException extends HttpException {
3
4
  constructor(message: string, status?: HttpStatus, options?: {
4
5
  cause?: unknown;
5
6
  });
6
7
  }
7
8
  export declare class McpBadRequestException extends McpException {
8
- constructor(message: string, options?: {
9
+ errors?: ErrorObject[];
10
+ constructor(message: string, errors: ErrorObject[] | undefined, options?: {
9
11
  cause?: unknown;
10
12
  });
11
13
  }
@@ -9,8 +9,10 @@ class McpException extends common_1.HttpException {
9
9
  }
10
10
  exports.McpException = McpException;
11
11
  class McpBadRequestException extends McpException {
12
- constructor(message, options) {
12
+ errors;
13
+ constructor(message, errors, options) {
13
14
  super(message, common_1.HttpStatus.BAD_REQUEST, options);
15
+ this.errors = errors;
14
16
  }
15
17
  }
16
18
  exports.McpBadRequestException = McpBadRequestException;
@@ -4,7 +4,7 @@ import { McpService } from "../services/mcp.service";
4
4
  import { IMcpTool } from "../decorators/mcp-tool.decorator";
5
5
  import { IMcpResource } from "../decorators/mcp-resource.decorator";
6
6
  import { IMcpPrompt } from "../decorators/mcp-prompt.decorator";
7
- export type IMcpDynamicTool<Input, Result> = IMcpTool<Input, Result> & {
7
+ export type IMcpDynamicTool<Input, Result extends Record<string, unknown>> = IMcpTool<Input, Result> & {
8
8
  guards?: Type<CanActivate>[];
9
9
  interceptors?: Type<NestInterceptor>[];
10
10
  };
@@ -21,7 +21,7 @@ export declare class McpDynamicService {
21
21
  private readonly mcpService;
22
22
  constructor(moduleRef: ModuleRef, mcpService: McpService);
23
23
  /** Динамически регистрирует MCP tools */
24
- registerTool<Input, Result>(tool: IMcpDynamicTool<Input, Result>): Promise<void>;
24
+ registerTool<Input, Result extends Record<string, unknown>>(tool: IMcpDynamicTool<Input, Result>): Promise<void>;
25
25
  /** Динамически регистрирует MCP prompts */
26
26
  registerPrompt<Input>(prompt: IMcpDynamicPrompt<Input>): Promise<void>;
27
27
  /** Динамически регистрирует MCP resources */
@@ -29,6 +29,9 @@ let McpDynamicService = class McpDynamicService {
29
29
  description = tool.description;
30
30
  execute = tool.execute;
31
31
  inputSchema = tool.inputSchema;
32
+ outputSchema = tool.outputSchema;
33
+ annotations = tool.annotations;
34
+ _meta = tool._meta;
32
35
  };
33
36
  if (tool.guards) {
34
37
  this.applyGuards(DynamicToolClass, tool.guards);
@@ -72,6 +75,8 @@ let McpDynamicService = class McpDynamicService {
72
75
  description = resource.description;
73
76
  execute = resource.execute;
74
77
  list = resource.list;
78
+ annotations = resource.annotations;
79
+ _meta = resource._meta;
75
80
  };
76
81
  if (resource.guards) {
77
82
  this.applyGuards(DynamicResourceClass, resource.guards);
@@ -1,8 +1,9 @@
1
1
  import { ExecutionContext, OnModuleInit } from "@nestjs/common";
2
- import { IMcpTool } from "../decorators/mcp-tool.decorator";
3
- import { IMcpPrompt } from "../decorators/mcp-prompt.decorator";
4
- import { IMcpResource } from "../decorators/mcp-resource.decorator";
2
+ import { IMcpTool, IMcpToolResult } from "../decorators/mcp-tool.decorator";
3
+ import { IMcpPrompt, IMcpPromptResult } from "../decorators/mcp-prompt.decorator";
4
+ import { IMcpResource, IMcpResourceResult } from "../decorators/mcp-resource.decorator";
5
5
  import { ModuleRef } from "@nestjs/core";
6
+ import { Observable } from "rxjs";
6
7
  import { IMcpConfig } from "../config";
7
8
  export interface McpMessage {
8
9
  type: string;
@@ -39,6 +40,7 @@ export declare class McpService implements OnModuleInit {
39
40
  title: string | undefined;
40
41
  description: string | undefined;
41
42
  inputSchema: any;
43
+ outputSchema: any;
42
44
  }[];
43
45
  listPrompts(): {
44
46
  name: string;
@@ -64,13 +66,13 @@ export declare class McpService implements OnModuleInit {
64
66
  instance: IMcpResource;
65
67
  metatype: Function;
66
68
  }): void;
67
- executePrompt(name: string, payload: object, context: ExecutionContext): Promise<import("rxjs").Observable<any>>;
69
+ executePrompt(name: string, payload: object, context: ExecutionContext): Promise<Observable<IMcpPromptResult>>;
68
70
  /**
69
71
  * Отправить сообщение в MCP "сервер"
70
72
  */
71
73
  executeTool(msg: {
72
74
  type: string;
73
75
  payload: any;
74
- }, context: ExecutionContext): Promise<import("rxjs").Observable<any>>;
75
- executeResource(name: string, uri: URL, vars: Record<string, any>, context: ExecutionContext): Promise<import("rxjs").Observable<any>>;
76
+ }, context: ExecutionContext): Promise<Observable<IMcpToolResult<Record<string, unknown>>>>;
77
+ executeResource(name: string, uri: URL, vars: Record<string, any>, context: ExecutionContext): Promise<Observable<IMcpResourceResult[]>>;
76
78
  }
@@ -46,6 +46,9 @@ let McpService = class McpService {
46
46
  title: t.title,
47
47
  description: t.description,
48
48
  inputSchema: t.inputSchema ? (0, zod_1.zodToJsonSchema)(t.inputSchema) : undefined,
49
+ outputSchema: t.outputSchema
50
+ ? (0, zod_1.zodToJsonSchema)(t.outputSchema)
51
+ : undefined,
49
52
  }));
50
53
  }
51
54
  listPrompts() {
@@ -86,7 +89,7 @@ let McpService = class McpService {
86
89
  const validate = this.ajv.compile(zodSchema);
87
90
  const valid = validate(payload);
88
91
  if (!valid) {
89
- throw new exceptions_1.McpBadRequestException("Invalid prompt arguments");
92
+ throw new exceptions_1.McpBadRequestException("Invalid prompt arguments", validate.errors || []);
90
93
  }
91
94
  }
92
95
  // const result = await prompt.execute(payload);
@@ -120,7 +123,7 @@ let McpService = class McpService {
120
123
  const validate = this.ajv.compile(zodSchema);
121
124
  const valid = validate(msg.payload);
122
125
  if (!valid) {
123
- throw new exceptions_1.McpBadRequestException(this.ajv.errorsText(validate.errors));
126
+ throw new exceptions_1.McpBadRequestException("Invalid tool arguments", validate.errors || []);
124
127
  }
125
128
  }
126
129
  // const result = await tool.execute(msg.payload);
@@ -22,4 +22,7 @@ export declare class McpSseService implements OnModuleInit {
22
22
  handleSse(_: IRequest, res: IResponse, context: ExecutionContext): Promise<void>;
23
23
  handleSseMessage(req: IRequest, res: IResponse): Promise<void>;
24
24
  private createServer;
25
+ private registerTools;
26
+ private registerPrompts;
27
+ private registerResources;
25
28
  }
@@ -64,11 +64,20 @@ let McpSseService = class McpSseService {
64
64
  version: this.config.version || "1",
65
65
  name: this.config.name || "MCP server",
66
66
  }, {});
67
+ this.registerTools(server, context);
68
+ this.registerPrompts(server, context);
69
+ this.registerResources(server, context);
70
+ return server;
71
+ }
72
+ registerTools(server, context) {
67
73
  for (const [toolName, { instance: tool }] of this.service.tools.entries()) {
68
74
  server.registerTool(tool.name, {
69
75
  title: tool.title,
70
76
  description: tool.description,
71
77
  inputSchema: tool.inputSchema,
78
+ outputSchema: tool.outputSchema,
79
+ annotations: tool.annotations,
80
+ _meta: tool._meta,
72
81
  }, async (payload) => {
73
82
  try {
74
83
  // const result = await tool.execute(
@@ -77,9 +86,9 @@ let McpSseService = class McpSseService {
77
86
  const observable = await this.service.executeTool({ type: tool.name, payload }, context);
78
87
  const result = await (0, rxjs_1.firstValueFrom)(observable);
79
88
  return {
80
- content: [
81
- { text: JSON.stringify(result), type: "text" },
82
- ],
89
+ content: result.messages,
90
+ structuredContent: result.structuredContent,
91
+ isError: result.isError,
83
92
  };
84
93
  }
85
94
  catch (e) {
@@ -89,6 +98,8 @@ let McpSseService = class McpSseService {
89
98
  }
90
99
  });
91
100
  }
101
+ }
102
+ registerPrompts(server, context) {
92
103
  for (const [promptName, { instance: prompt },] of this.service.prompts.entries()) {
93
104
  server.registerPrompt(prompt.name, {
94
105
  title: prompt.title,
@@ -99,7 +110,7 @@ let McpSseService = class McpSseService {
99
110
  // const result = await prompt.execute({ ...params });
100
111
  const observable = await this.service.executePrompt(prompt.name, payload, context);
101
112
  const result = await (0, rxjs_1.firstValueFrom)(observable);
102
- const messages = result.map((el) => ({
113
+ const messages = result.messages.map((el) => ({
103
114
  role: (el.role === "system" ? "assistant" : el.role),
104
115
  content: el.tool_call
105
116
  ? {
@@ -108,7 +119,7 @@ let McpSseService = class McpSseService {
108
119
  }
109
120
  : { type: "text", text: el.content },
110
121
  }));
111
- return { messages: messages };
122
+ return { messages: messages, description: result.description };
112
123
  }
113
124
  catch (e) {
114
125
  throw new exceptions_1.McpInternalServerErrorException(`Faild to execute tool ${prompt.name}`, {
@@ -117,6 +128,8 @@ let McpSseService = class McpSseService {
117
128
  }
118
129
  });
119
130
  }
131
+ }
132
+ registerResources(server, context) {
120
133
  for (const [_, { instance: resource },] of this.service.resources.entries()) {
121
134
  const resourceList = resource.list;
122
135
  server.registerResource(resource.name, new mcp_js_1.ResourceTemplate(resource.uri, {
@@ -130,6 +143,8 @@ let McpSseService = class McpSseService {
130
143
  }), {
131
144
  title: resource.title,
132
145
  description: resource.description,
146
+ annotations: resource.annotations,
147
+ _meta: resource._meta,
133
148
  }, async (url, variables) => {
134
149
  try {
135
150
  // const resources = await resource.execute(url, variables);
@@ -144,7 +159,6 @@ let McpSseService = class McpSseService {
144
159
  }
145
160
  });
146
161
  }
147
- return server;
148
162
  }
149
163
  };
150
164
  exports.McpSseService = McpSseService;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@muzikanto/nestjs-mcp",
3
- "version": "1.6.0",
3
+ "version": "1.7.0",
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",