@nestjs-mcp/server 0.1.0-alpha.4

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 (178) hide show
  1. package/.copilotignore +38 -0
  2. package/.devcontainer/Dockerfile.dev +28 -0
  3. package/.devcontainer/devcontainer.json +56 -0
  4. package/.devcontainer/docker-compose.yml +15 -0
  5. package/.dockerignore +37 -0
  6. package/.github/codeql-config.yml +4 -0
  7. package/.github/copilot-instructions.md +138 -0
  8. package/.github/prompts/memory.prompt.md +120 -0
  9. package/.github/workflows/auto-tag-release.yml +84 -0
  10. package/.github/workflows/codeql-analysis.yml +56 -0
  11. package/.github/workflows/npm-publish.yml +58 -0
  12. package/.github/workflows/pr-branch-validation.yml +78 -0
  13. package/.github/workflows/run-tests.yml +41 -0
  14. package/.github/workflows/sync-main-to-develop.yml +53 -0
  15. package/.handbook/GIT_GUIDELINES.md +250 -0
  16. package/.handbook/PACKAGE_VERSIONING.md +140 -0
  17. package/.handbook/STACK.md +75 -0
  18. package/.prettierrc +4 -0
  19. package/.vscode/extensions.json +44 -0
  20. package/.vscode/settings.json +40 -0
  21. package/CONTRIBUTING.md +261 -0
  22. package/LICENSE +21 -0
  23. package/README.md +490 -0
  24. package/dist/examples/async-import/app.module.d.ts +2 -0
  25. package/dist/examples/async-import/app.module.js +33 -0
  26. package/dist/examples/async-import/app.module.js.map +1 -0
  27. package/dist/examples/async-import/main.d.ts +1 -0
  28. package/dist/examples/async-import/main.js +17 -0
  29. package/dist/examples/async-import/main.js.map +1 -0
  30. package/dist/examples/guards/app.module.d.ts +6 -0
  31. package/dist/examples/guards/app.module.js +48 -0
  32. package/dist/examples/guards/app.module.js.map +1 -0
  33. package/dist/examples/guards/guards.resolver.d.ts +13 -0
  34. package/dist/examples/guards/guards.resolver.js +61 -0
  35. package/dist/examples/guards/guards.resolver.js.map +1 -0
  36. package/dist/examples/guards/main.d.ts +1 -0
  37. package/dist/examples/guards/main.js +11 -0
  38. package/dist/examples/guards/main.js.map +1 -0
  39. package/dist/examples/mixed/app.module.d.ts +2 -0
  40. package/dist/examples/mixed/app.module.js +31 -0
  41. package/dist/examples/mixed/app.module.js.map +1 -0
  42. package/dist/examples/mixed/main.d.ts +1 -0
  43. package/dist/examples/mixed/main.js +11 -0
  44. package/dist/examples/mixed/main.js.map +1 -0
  45. package/dist/examples/mixed/mixed.resolver.d.ts +6 -0
  46. package/dist/examples/mixed/mixed.resolver.js +78 -0
  47. package/dist/examples/mixed/mixed.resolver.js.map +1 -0
  48. package/dist/examples/prompts/app.module.d.ts +2 -0
  49. package/dist/examples/prompts/app.module.js +31 -0
  50. package/dist/examples/prompts/app.module.js.map +1 -0
  51. package/dist/examples/prompts/main.d.ts +1 -0
  52. package/dist/examples/prompts/main.js +11 -0
  53. package/dist/examples/prompts/main.js.map +1 -0
  54. package/dist/examples/prompts/prompts.resolver.d.ts +14 -0
  55. package/dist/examples/prompts/prompts.resolver.js +165 -0
  56. package/dist/examples/prompts/prompts.resolver.js.map +1 -0
  57. package/dist/examples/resources/app.module.d.ts +2 -0
  58. package/dist/examples/resources/app.module.js +31 -0
  59. package/dist/examples/resources/app.module.js.map +1 -0
  60. package/dist/examples/resources/main.d.ts +1 -0
  61. package/dist/examples/resources/main.js +11 -0
  62. package/dist/examples/resources/main.js.map +1 -0
  63. package/dist/examples/resources/resources.resolver.d.ts +12 -0
  64. package/dist/examples/resources/resources.resolver.js +114 -0
  65. package/dist/examples/resources/resources.resolver.js.map +1 -0
  66. package/dist/examples/tools/app.module.d.ts +2 -0
  67. package/dist/examples/tools/app.module.js +31 -0
  68. package/dist/examples/tools/app.module.js.map +1 -0
  69. package/dist/examples/tools/main.d.ts +1 -0
  70. package/dist/examples/tools/main.js +11 -0
  71. package/dist/examples/tools/main.js.map +1 -0
  72. package/dist/examples/tools/tools.resolver.d.ts +23 -0
  73. package/dist/examples/tools/tools.resolver.js +175 -0
  74. package/dist/examples/tools/tools.resolver.js.map +1 -0
  75. package/dist/src/controllers/sse/index.d.ts +2 -0
  76. package/dist/src/controllers/sse/index.js +19 -0
  77. package/dist/src/controllers/sse/index.js.map +1 -0
  78. package/dist/src/controllers/sse/sse.controller.d.ts +8 -0
  79. package/dist/src/controllers/sse/sse.controller.js +51 -0
  80. package/dist/src/controllers/sse/sse.controller.js.map +1 -0
  81. package/dist/src/controllers/sse/sse.service.d.ts +16 -0
  82. package/dist/src/controllers/sse/sse.service.js +78 -0
  83. package/dist/src/controllers/sse/sse.service.js.map +1 -0
  84. package/dist/src/controllers/streamable/index.d.ts +2 -0
  85. package/dist/src/controllers/streamable/index.js +19 -0
  86. package/dist/src/controllers/streamable/index.js.map +1 -0
  87. package/dist/src/controllers/streamable/streamable.controller.d.ts +9 -0
  88. package/dist/src/controllers/streamable/streamable.controller.js +62 -0
  89. package/dist/src/controllers/streamable/streamable.controller.js.map +1 -0
  90. package/dist/src/controllers/streamable/streamable.service.d.ts +24 -0
  91. package/dist/src/controllers/streamable/streamable.service.js +117 -0
  92. package/dist/src/controllers/streamable/streamable.service.js.map +1 -0
  93. package/dist/src/decorators/capabilities.constants.d.ts +4 -0
  94. package/dist/src/decorators/capabilities.constants.js +8 -0
  95. package/dist/src/decorators/capabilities.constants.js.map +1 -0
  96. package/dist/src/decorators/capabilities.decorators.d.ts +8 -0
  97. package/dist/src/decorators/capabilities.decorators.js +49 -0
  98. package/dist/src/decorators/capabilities.decorators.js.map +1 -0
  99. package/dist/src/decorators/index.d.ts +2 -0
  100. package/dist/src/decorators/index.js +19 -0
  101. package/dist/src/decorators/index.js.map +1 -0
  102. package/dist/src/index.d.ts +4 -0
  103. package/dist/src/index.js +21 -0
  104. package/dist/src/index.js.map +1 -0
  105. package/dist/src/interfaces/capabilities.interface.d.ts +52 -0
  106. package/dist/src/interfaces/capabilities.interface.js +3 -0
  107. package/dist/src/interfaces/capabilities.interface.js.map +1 -0
  108. package/dist/src/interfaces/guards.interface.d.ts +4 -0
  109. package/dist/src/interfaces/guards.interface.js +3 -0
  110. package/dist/src/interfaces/guards.interface.js.map +1 -0
  111. package/dist/src/interfaces/index.d.ts +2 -0
  112. package/dist/src/interfaces/index.js +19 -0
  113. package/dist/src/interfaces/index.js.map +1 -0
  114. package/dist/src/interfaces/mcp-server-options.interface.d.ts +42 -0
  115. package/dist/src/interfaces/mcp-server-options.interface.js +3 -0
  116. package/dist/src/interfaces/mcp-server-options.interface.js.map +1 -0
  117. package/dist/src/mcp.module.d.ts +13 -0
  118. package/dist/src/mcp.module.js +176 -0
  119. package/dist/src/mcp.module.js.map +1 -0
  120. package/dist/src/registry/discovery.service.d.ts +16 -0
  121. package/dist/src/registry/discovery.service.js +85 -0
  122. package/dist/src/registry/discovery.service.js.map +1 -0
  123. package/dist/src/registry/index.d.ts +2 -0
  124. package/dist/src/registry/index.js +19 -0
  125. package/dist/src/registry/index.js.map +1 -0
  126. package/dist/src/registry/logger.service.d.ts +16 -0
  127. package/dist/src/registry/logger.service.js +97 -0
  128. package/dist/src/registry/logger.service.js.map +1 -0
  129. package/dist/src/registry/registry.service.d.ts +14 -0
  130. package/dist/src/registry/registry.service.js +165 -0
  131. package/dist/src/registry/registry.service.js.map +1 -0
  132. package/dist/tsconfig.build.tsbuildinfo +1 -0
  133. package/eslint.config.mjs +40 -0
  134. package/examples/README.md +56 -0
  135. package/examples/async-import/app.module.ts +22 -0
  136. package/examples/async-import/main.ts +15 -0
  137. package/examples/guards/app.module.ts +44 -0
  138. package/examples/guards/guards.resolver.ts +52 -0
  139. package/examples/guards/main.ts +11 -0
  140. package/examples/mixed/app.module.ts +20 -0
  141. package/examples/mixed/main.ts +11 -0
  142. package/examples/mixed/mixed.resolver.ts +56 -0
  143. package/examples/prompts/app.module.ts +20 -0
  144. package/examples/prompts/main.ts +11 -0
  145. package/examples/prompts/prompts.resolver.ts +184 -0
  146. package/examples/resources/app.module.ts +19 -0
  147. package/examples/resources/main.ts +11 -0
  148. package/examples/resources/resources.resolver.ts +123 -0
  149. package/examples/tools/app.module.ts +20 -0
  150. package/examples/tools/main.ts +11 -0
  151. package/examples/tools/tools.resolver.ts +205 -0
  152. package/nest-cli.json +8 -0
  153. package/package.json +106 -0
  154. package/scripts/npm-publish.js +301 -0
  155. package/src/controllers/sse/index.ts +2 -0
  156. package/src/controllers/sse/sse.controller.ts +19 -0
  157. package/src/controllers/sse/sse.service.ts +90 -0
  158. package/src/controllers/streamable/index.ts +2 -0
  159. package/src/controllers/streamable/streamable.controller.ts +24 -0
  160. package/src/controllers/streamable/streamable.service.ts +168 -0
  161. package/src/decorators/capabilities.constants.ts +7 -0
  162. package/src/decorators/capabilities.decorators.ts +150 -0
  163. package/src/decorators/index.ts +2 -0
  164. package/src/index.ts +11 -0
  165. package/src/interfaces/capabilities.interface.ts +95 -0
  166. package/src/interfaces/guards.interface.ts +13 -0
  167. package/src/interfaces/index.ts +2 -0
  168. package/src/interfaces/mcp-server-options.interface.ts +105 -0
  169. package/src/mcp.module.ts +233 -0
  170. package/src/mcp.service.spec.ts +28 -0
  171. package/src/registry/discovery.service.ts +116 -0
  172. package/src/registry/index.ts +2 -0
  173. package/src/registry/logger.service.ts +143 -0
  174. package/src/registry/registry.service.ts +281 -0
  175. package/test/base.e2e-spec.ts +74 -0
  176. package/test/jest-e2e.json +9 -0
  177. package/tsconfig.build.json +4 -0
  178. package/tsconfig.json +23 -0
@@ -0,0 +1,56 @@
1
+ # Examples Directory
2
+
3
+ This directory contains ready-to-use example MCP servers demonstrating how to use the NestJS MCP Server module and expose different MCP capabilities. Each subfolder is a self-contained NestJS application showing a specific integration pattern.
4
+
5
+ ## Directory Overview
6
+
7
+ - **mixed/**
8
+ Demonstrates how to implement and expose multiple MCP capabilities (resources, tools, and prompts) in a single server. Useful for learning how to combine different features in one service.
9
+
10
+ - **resources/**
11
+ Shows how to define and register MCP resources using the `@Resource` decorator. Resources are used to expose structured data endpoints to LLMs.
12
+
13
+ - **tools/**
14
+ Provides examples of tools implemented with the `@Tool` decorator. Tools allow LLMs to invoke server-side actions or computations, including parameter validation.
15
+
16
+ - **prompts/**
17
+ Contains examples of conversational prompts using the `@Prompt` decorator. Prompts enable dynamic, parameterized conversational flows for LLMs.
18
+
19
+ - **guards/**
20
+ Shows how to implement and use guards in MCP servers to control access to capabilities and resources. Demonstrates different guard patterns and how to apply them to protect specific MCP features.
21
+
22
+ - **async-import/**
23
+ Example of asynchronous module configuration of the MCP module using `forRootAsync` and environment variables.
24
+
25
+ ## How to Run Examples
26
+
27
+ To run any example, set the EXAMPLE environment variable to the folder name and use:
28
+
29
+ ```sh
30
+ EXAMPLE=<example-folder> pnpm start:example
31
+ ```
32
+
33
+ - Example for the `mixed` example: [mixed/](./mixed/)
34
+ ```sh
35
+ EXAMPLE=mixed pnpm start:example
36
+ ```
37
+
38
+ > **Windows users:** Use [cross-env](https://www.npmjs.com/package/cross-env):
39
+ >
40
+ > ```sh
41
+ > npx cross-env EXAMPLE=mixed pnpm start:example
42
+ > ```
43
+
44
+ The server will be available at [http://localhost:3000](http://localhost:3000).
45
+
46
+ ## Inspector Playground
47
+
48
+ You can interactively test and debug these examples using the MCP Inspector Playground:
49
+
50
+ ```sh
51
+ pnpm start:inspector
52
+ ```
53
+
54
+ ---
55
+
56
+ For more details, see the code and comments in each example folder.
@@ -0,0 +1,22 @@
1
+ import { Module } from '@nestjs/common';
2
+
3
+ import { ConfigModule, ConfigService } from '@nestjs/config';
4
+
5
+ import { McpModule } from '../../src/mcp.module';
6
+
7
+ @Module({
8
+ imports: [
9
+ McpModule.forRootAsync({
10
+ imports: [ConfigModule.forRoot()],
11
+ useFactory: (configService: ConfigService) => ({
12
+ name: 'tools',
13
+ version: '1.0.0',
14
+ logging: {
15
+ enabled: configService.get('MCP_LOGGING_ENABLED'),
16
+ level: configService.get('MCP_LOGGING_LEVEL'),
17
+ },
18
+ }),
19
+ }),
20
+ ],
21
+ })
22
+ export class AppModule {}
@@ -0,0 +1,15 @@
1
+ import { NestFactory } from '@nestjs/core';
2
+ import { AppModule } from './app.module';
3
+
4
+ async function bootstrap() {
5
+ try {
6
+ const app = await NestFactory.create(AppModule);
7
+ await app.listen(3000);
8
+ console.log('Async-import example server running on http://localhost:3000');
9
+ } catch (error) {
10
+ console.error('Failed to start server:', error);
11
+ process.exit(1);
12
+ }
13
+ }
14
+
15
+ void bootstrap();
@@ -0,0 +1,44 @@
1
+ import {
2
+ CanActivate,
3
+ ExecutionContext,
4
+ Injectable,
5
+ Module,
6
+ } from '@nestjs/common';
7
+
8
+ import { McpModule } from '../../src/mcp.module';
9
+
10
+ import { APP_GUARD } from '@nestjs/core';
11
+ import { GuardsResolver } from './guards.resolver';
12
+
13
+ // Global guard (to be registered in app.module.ts)
14
+ @Injectable()
15
+ export class GlobalLogGuard implements CanActivate {
16
+ canActivate(_context: ExecutionContext): boolean {
17
+ // This will log for every request (NestJS global guard)
18
+ // You can add more context-specific logic if needed
19
+
20
+ console.log('[GlobalLogGuard] Global guard executed');
21
+ return true;
22
+ }
23
+ }
24
+
25
+ @Module({
26
+ imports: [
27
+ McpModule.forRoot({
28
+ name: 'tools',
29
+ version: '1.0.0',
30
+ logging: {
31
+ enabled: true,
32
+ level: 'verbose',
33
+ },
34
+ }),
35
+ ],
36
+ providers: [
37
+ {
38
+ provide: APP_GUARD,
39
+ useClass: GlobalLogGuard,
40
+ },
41
+ GuardsResolver,
42
+ ],
43
+ })
44
+ export class AppModule {}
@@ -0,0 +1,52 @@
1
+ import { CanActivate } from '@nestjs/common';
2
+
3
+ import {
4
+ McpExecutionContext,
5
+ Prompt,
6
+ Resolver,
7
+ Tool,
8
+ UseGuards,
9
+ } from '../../src';
10
+
11
+ import {
12
+ CallToolResult,
13
+ GetPromptResult,
14
+ } from '@modelcontextprotocol/sdk/types';
15
+
16
+ // Resolver-level guard
17
+ export class ResolverLogGuard implements CanActivate {
18
+ canActivate(_context: McpExecutionContext): boolean {
19
+ console.log('[ResolverLogGuard] Resolver-level guard executed');
20
+ return true;
21
+ }
22
+ }
23
+
24
+ // Method-level guard
25
+ export class MethodLogGuard implements CanActivate {
26
+ canActivate(_context: McpExecutionContext): boolean {
27
+ console.log('[MethodLogGuard] Method-level guard executed');
28
+ return true;
29
+ }
30
+ }
31
+
32
+ @UseGuards(ResolverLogGuard)
33
+ @Resolver('guards')
34
+ export class GuardsResolver {
35
+ @Prompt({ name: 'logPrompt' })
36
+ logPrompt(): GetPromptResult {
37
+ return {
38
+ messages: [
39
+ {
40
+ role: 'assistant',
41
+ content: { type: 'text', text: 'Prompt executed' },
42
+ },
43
+ ],
44
+ };
45
+ }
46
+
47
+ @UseGuards(MethodLogGuard)
48
+ @Tool({ name: 'logTool' })
49
+ logTool(): CallToolResult {
50
+ return { content: [{ type: 'text', text: 'Tool executed' }] };
51
+ }
52
+ }
@@ -0,0 +1,11 @@
1
+ import { NestFactory } from '@nestjs/core';
2
+
3
+ import { AppModule } from './app.module';
4
+
5
+ async function bootstrap() {
6
+ const app = await NestFactory.create(AppModule);
7
+ await app.listen(3000);
8
+ console.log('Resources example server running on http://localhost:3000');
9
+ }
10
+
11
+ void bootstrap();
@@ -0,0 +1,20 @@
1
+ import { Module } from '@nestjs/common';
2
+
3
+ import { McpModule } from '../../src/mcp.module';
4
+
5
+ import { MixedResolver } from './mixed.resolver';
6
+
7
+ @Module({
8
+ imports: [
9
+ McpModule.forRoot({
10
+ name: 'tools',
11
+ version: '1.0.0',
12
+ logging: {
13
+ enabled: true,
14
+ level: 'verbose',
15
+ },
16
+ }),
17
+ ],
18
+ providers: [MixedResolver],
19
+ })
20
+ export class AppModule {}
@@ -0,0 +1,11 @@
1
+ import { NestFactory } from '@nestjs/core';
2
+
3
+ import { AppModule } from './app.module';
4
+
5
+ async function bootstrap() {
6
+ const app = await NestFactory.create(AppModule);
7
+ await app.listen(3000);
8
+ console.log('Resources example server running on http://localhost:3000');
9
+ }
10
+
11
+ void bootstrap();
@@ -0,0 +1,56 @@
1
+ import {
2
+ CallToolResult,
3
+ GetPromptResult,
4
+ ReadResourceResult,
5
+ } from '@modelcontextprotocol/sdk/types';
6
+
7
+ import { Prompt, Resolver, Resource, Tool } from '../../src';
8
+
9
+ @Resolver('mixed')
10
+ export class MixedResolver {
11
+ @Resource({
12
+ name: 'country_list',
13
+ uri: 'contry://list',
14
+ })
15
+ getCountryList(uri: URL): ReadResourceResult {
16
+ return {
17
+ contents: [
18
+ {
19
+ uri: uri.href,
20
+ text: 'Mexico, USA, Canada',
21
+ },
22
+ ],
23
+ };
24
+ }
25
+
26
+ @Tool({
27
+ name: 'get_country_info',
28
+ })
29
+ getCountryInfo(): CallToolResult {
30
+ return {
31
+ content: [
32
+ {
33
+ type: 'text',
34
+ text: 'The capital of France is Paris.',
35
+ },
36
+ ],
37
+ };
38
+ }
39
+
40
+ @Prompt({
41
+ name: 'get_country_info',
42
+ })
43
+ getCountryInfoPrompt(): GetPromptResult {
44
+ return {
45
+ messages: [
46
+ {
47
+ role: 'assistant',
48
+ content: {
49
+ type: 'text',
50
+ text: 'What is the capital of France?',
51
+ },
52
+ },
53
+ ],
54
+ };
55
+ }
56
+ }
@@ -0,0 +1,20 @@
1
+ import { Module } from '@nestjs/common';
2
+
3
+ import { McpModule } from '../../src/mcp.module';
4
+
5
+ import { PromptsResolver } from './prompts.resolver';
6
+
7
+ @Module({
8
+ imports: [
9
+ McpModule.forRoot({
10
+ name: 'tools',
11
+ version: '1.0.0',
12
+ logging: {
13
+ enabled: true,
14
+ level: 'verbose',
15
+ },
16
+ }),
17
+ ],
18
+ providers: [PromptsResolver],
19
+ })
20
+ export class AppModule {}
@@ -0,0 +1,11 @@
1
+ import { NestFactory } from '@nestjs/core';
2
+
3
+ import { AppModule } from './app.module';
4
+
5
+ async function bootstrap() {
6
+ const app = await NestFactory.create(AppModule);
7
+ await app.listen(3000);
8
+ console.log('Resources example server running on http://localhost:3000');
9
+ }
10
+
11
+ void bootstrap();
@@ -0,0 +1,184 @@
1
+ import { GetPromptResult } from '@modelcontextprotocol/sdk/types';
2
+ import { z } from 'zod';
3
+
4
+ import { Prompt, Resolver } from '../../src';
5
+
6
+ @Resolver('prompts')
7
+ export class PromptsResolver {
8
+ /**
9
+ * Simple prompt with only a name
10
+ * Use case: Basic functionality that requires no parameters
11
+ */
12
+ @Prompt({
13
+ name: 'greeting',
14
+ })
15
+ basicGreeting(): GetPromptResult {
16
+ return {
17
+ messages: [
18
+ {
19
+ role: 'assistant',
20
+ content: {
21
+ type: 'text',
22
+ text: 'Hello! How can I assist you today?',
23
+ },
24
+ },
25
+ ],
26
+ };
27
+ }
28
+
29
+ /**
30
+ * Prompt with name and argsSchema
31
+ * Use case: When you need validated input parameters
32
+ */
33
+ @Prompt({
34
+ name: 'personalized_greeting',
35
+ argsSchema: {
36
+ name: z.string().min(1),
37
+ timeOfDay: z.enum(['morning', 'afternoon', 'evening', 'night']),
38
+ },
39
+ })
40
+ personalizedGreeting({
41
+ name,
42
+ timeOfDay,
43
+ }: {
44
+ name: string;
45
+ timeOfDay: 'morning' | 'afternoon' | 'evening' | 'night';
46
+ }): GetPromptResult {
47
+ const greetings = {
48
+ morning: 'Good morning',
49
+ afternoon: 'Good afternoon',
50
+ evening: 'Good evening',
51
+ night: 'Good night',
52
+ };
53
+
54
+ return {
55
+ messages: [
56
+ {
57
+ role: 'assistant',
58
+ content: {
59
+ type: 'text',
60
+ text: `${greetings[timeOfDay]}, ${name}! How can I assist you today?`,
61
+ },
62
+ },
63
+ ],
64
+ };
65
+ }
66
+
67
+ /**
68
+ * Prompt with name and description
69
+ * Use case: When additional context helps the user understand the prompt's purpose
70
+ */
71
+ @Prompt({
72
+ name: 'help_guide',
73
+ description:
74
+ 'Provides a general help guide about available system features',
75
+ })
76
+ helpGuide(): GetPromptResult {
77
+ return {
78
+ messages: [
79
+ {
80
+ role: 'assistant',
81
+ content: {
82
+ type: 'text',
83
+ text: `
84
+ # System Help Guide
85
+
86
+ Here are the main features available:
87
+ - **Account Management**: Update your profile, change settings
88
+ - **Content Creation**: Create and edit documents, media, and more
89
+ - **Collaboration**: Share and work with others
90
+ - **Analytics**: View performance metrics and reports
91
+
92
+ For more detailed help, please specify which feature you need assistance with.
93
+ `,
94
+ },
95
+ },
96
+ ],
97
+ };
98
+ }
99
+
100
+ /**
101
+ * Comprehensive prompt with all options
102
+ * Use case: Complex functionality requiring validated input and clear explanation
103
+ */
104
+ @Prompt({
105
+ name: 'product_recommendation',
106
+ description:
107
+ 'Recommends products based on user preferences and requirements',
108
+ argsSchema: {
109
+ category: z.string(),
110
+ budget: z.string(),
111
+ preferences: z.string().optional(),
112
+ },
113
+ })
114
+ productRecommendation({
115
+ category,
116
+ budget,
117
+ preferences,
118
+ }: {
119
+ category: string;
120
+ budget: string;
121
+ preferences?: string;
122
+ }): GetPromptResult {
123
+ // Parse inputs
124
+ const parsedCategory = category as
125
+ | 'electronics'
126
+ | 'clothing'
127
+ | 'home'
128
+ | 'beauty';
129
+ const parsedBudget = parseFloat(budget);
130
+ const parsedPreferences = preferences
131
+ ? preferences.split(',').map((p) => p.trim())
132
+ : undefined;
133
+
134
+ // Mock product recommendations based on input parameters
135
+ const recommendations = {
136
+ electronics: [
137
+ { name: 'Wireless Earbuds', price: 79.99 },
138
+ { name: 'Smart Watch', price: 149.99 },
139
+ { name: 'Bluetooth Speaker', price: 59.99 },
140
+ ],
141
+ clothing: [
142
+ { name: 'Casual T-Shirt', price: 24.99 },
143
+ { name: 'Jeans', price: 49.99 },
144
+ { name: 'Sneakers', price: 89.99 },
145
+ ],
146
+ home: [
147
+ { name: 'Coffee Maker', price: 69.99 },
148
+ { name: 'Throw Blanket', price: 34.99 },
149
+ { name: 'Table Lamp', price: 42.99 },
150
+ ],
151
+ beauty: [
152
+ { name: 'Face Serum', price: 29.99 },
153
+ { name: 'Makeup Palette', price: 54.99 },
154
+ { name: 'Hair Dryer', price: 99.99 },
155
+ ],
156
+ };
157
+
158
+ // Filter by budget
159
+ const affordableProducts = recommendations[parsedCategory].filter(
160
+ (product) => product.price <= parsedBudget,
161
+ );
162
+
163
+ // Format recommendations
164
+ const productList = affordableProducts
165
+ .map((product) => `- ${product.name}: $${product.price.toFixed(2)}`)
166
+ .join('\n');
167
+
168
+ return {
169
+ messages: [
170
+ {
171
+ role: 'assistant',
172
+ content: {
173
+ type: 'text',
174
+ text: `Based on your ${parsedCategory} category and budget of $${parsedBudget.toFixed(2)}${
175
+ parsedPreferences
176
+ ? ` with preferences for ${parsedPreferences.join(', ')}`
177
+ : ''
178
+ }, here are my recommendations:\n\n${productList}`,
179
+ },
180
+ },
181
+ ],
182
+ };
183
+ }
184
+ }
@@ -0,0 +1,19 @@
1
+ import { Module } from '@nestjs/common';
2
+
3
+ import { McpModule } from '../../src/mcp.module';
4
+ import { ResourcesResolver } from './resources.resolver';
5
+
6
+ @Module({
7
+ imports: [
8
+ McpModule.forRoot({
9
+ name: 'tools',
10
+ version: '1.0.0',
11
+ logging: {
12
+ enabled: true,
13
+ level: 'verbose',
14
+ },
15
+ }),
16
+ ],
17
+ providers: [ResourcesResolver],
18
+ })
19
+ export class AppModule {}
@@ -0,0 +1,11 @@
1
+ import { NestFactory } from '@nestjs/core';
2
+
3
+ import { AppModule } from './app.module';
4
+
5
+ async function bootstrap() {
6
+ const app = await NestFactory.create(AppModule);
7
+ await app.listen(3000);
8
+ console.log('Resources example server running on http://localhost:3000');
9
+ }
10
+
11
+ void bootstrap();
@@ -0,0 +1,123 @@
1
+ import { RequestHandlerExtra } from '@modelcontextprotocol/sdk/shared/protocol';
2
+ import {
3
+ ReadResourceResult,
4
+ ServerNotification,
5
+ ServerRequest,
6
+ } from '@modelcontextprotocol/sdk/types';
7
+
8
+ import { Resolver, Resource } from '../../src';
9
+
10
+ const USER_LIST = [
11
+ {
12
+ id: '1',
13
+ name: 'John Doe',
14
+ email: 'john.doe@example.com',
15
+ },
16
+ {
17
+ id: '2',
18
+ name: 'Jane Smith',
19
+ email: 'jane.smith@example.com',
20
+ },
21
+ ];
22
+
23
+ @Resolver('resources')
24
+ export class ResourcesResolver {
25
+ /**
26
+ * Simple resource with only a name
27
+ * Use case: Basic resource without metadata or template
28
+ */
29
+ @Resource({
30
+ name: 'get_users',
31
+ uri: 'users://list',
32
+ })
33
+ getSimpleDocument(
34
+ uri: URL,
35
+ _extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
36
+ ): ReadResourceResult {
37
+ return {
38
+ contents: [
39
+ {
40
+ uri: uri.href,
41
+ text: JSON.stringify(USER_LIST),
42
+ },
43
+ ],
44
+ };
45
+ }
46
+
47
+ /**
48
+ * Resource with URI and metadata
49
+ * Use case: When you need to add contextual information about the resource
50
+ */
51
+ @Resource({
52
+ name: 'get_users_with_filters',
53
+ uri: 'users://list/metadata',
54
+ metadata: {},
55
+ })
56
+ getUserProfile(uri: URL): ReadResourceResult {
57
+ const users = USER_LIST.map((user) => ({ id: user.id, name: user.name }));
58
+
59
+ return {
60
+ contents: [
61
+ {
62
+ uri: uri.href,
63
+ text: JSON.stringify(users),
64
+ },
65
+ ],
66
+ };
67
+ }
68
+
69
+ /**
70
+ * Resource with template
71
+ * Use case: When you need dynamic parameters in the resource URI
72
+ */
73
+ @Resource({
74
+ name: 'greet',
75
+ template: 'geet://{name}',
76
+ })
77
+ getUserById(
78
+ uri: URL,
79
+ variables: {
80
+ name: string;
81
+ },
82
+ _extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
83
+ ): ReadResourceResult {
84
+ return {
85
+ contents: [
86
+ {
87
+ uri: uri.href,
88
+ text: `Hello, ${variables.name}!`,
89
+ },
90
+ ],
91
+ };
92
+ }
93
+
94
+ /**
95
+ * Resource with template
96
+ * Use case: When you need dynamic parameters in the resource URI
97
+ */
98
+ @Resource({
99
+ name: 'get_user_by_id',
100
+ template: 'users://{userId}',
101
+ metadata: {
102
+ version: 1,
103
+ },
104
+ })
105
+ getUserByIdWithMetada(
106
+ uri: URL,
107
+ variables: {
108
+ userId: string;
109
+ },
110
+ _extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
111
+ ): ReadResourceResult {
112
+ const user = USER_LIST.find((user) => user.id === variables.userId);
113
+
114
+ return {
115
+ contents: [
116
+ {
117
+ uri: uri.href,
118
+ text: user ? JSON.stringify(user) : 'User not found',
119
+ },
120
+ ],
121
+ };
122
+ }
123
+ }
@@ -0,0 +1,20 @@
1
+ import { Module } from '@nestjs/common';
2
+
3
+ import { McpModule } from '../../src/mcp.module';
4
+
5
+ import { ToolsResolver } from './tools.resolver';
6
+
7
+ @Module({
8
+ imports: [
9
+ McpModule.forRoot({
10
+ name: 'tools',
11
+ version: '1.0.0',
12
+ logging: {
13
+ enabled: true,
14
+ level: 'verbose',
15
+ },
16
+ }),
17
+ ],
18
+ providers: [ToolsResolver],
19
+ })
20
+ export class AppModule {}