@nestjs-mcp/server 0.1.0-alpha.8 → 0.1.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 (74) hide show
  1. package/README.md +450 -134
  2. package/coverage/clover.xml +507 -0
  3. package/coverage/coverage-final.json +19 -0
  4. package/coverage/lcov-report/base.css +224 -0
  5. package/coverage/lcov-report/block-navigation.js +87 -0
  6. package/coverage/lcov-report/favicon.png +0 -0
  7. package/coverage/lcov-report/index.html +206 -0
  8. package/coverage/lcov-report/prettify.css +1 -0
  9. package/coverage/lcov-report/prettify.js +2 -0
  10. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  11. package/coverage/lcov-report/sorter.js +196 -0
  12. package/coverage/lcov-report/src/controllers/sse/index.html +146 -0
  13. package/coverage/lcov-report/src/controllers/sse/index.ts.html +91 -0
  14. package/coverage/lcov-report/src/controllers/sse/sse.controller.ts.html +160 -0
  15. package/coverage/lcov-report/src/controllers/sse/sse.service.ts.html +403 -0
  16. package/coverage/lcov-report/src/controllers/streamable/index.html +146 -0
  17. package/coverage/lcov-report/src/controllers/streamable/index.ts.html +91 -0
  18. package/coverage/lcov-report/src/controllers/streamable/streamable.controller.ts.html +157 -0
  19. package/coverage/lcov-report/src/controllers/streamable/streamable.service.ts.html +655 -0
  20. package/coverage/lcov-report/src/decorators/capabilities.constants.ts.html +106 -0
  21. package/coverage/lcov-report/src/decorators/capabilities.decorators.ts.html +535 -0
  22. package/coverage/lcov-report/src/decorators/index.html +146 -0
  23. package/coverage/lcov-report/src/decorators/index.ts.html +91 -0
  24. package/coverage/lcov-report/src/index.html +131 -0
  25. package/coverage/lcov-report/src/index.ts.html +118 -0
  26. package/coverage/lcov-report/src/interfaces/capabilities.interface.ts.html +703 -0
  27. package/coverage/lcov-report/src/interfaces/index.html +131 -0
  28. package/coverage/lcov-report/src/interfaces/index.ts.html +91 -0
  29. package/coverage/lcov-report/src/mcp.module.ts.html +817 -0
  30. package/coverage/lcov-report/src/registry/discovery.service.ts.html +433 -0
  31. package/coverage/lcov-report/src/registry/index.html +161 -0
  32. package/coverage/lcov-report/src/registry/index.ts.html +91 -0
  33. package/coverage/lcov-report/src/registry/logger.service.ts.html +514 -0
  34. package/coverage/lcov-report/src/registry/registry.service.ts.html +1183 -0
  35. package/coverage/lcov-report/src/services/index.html +116 -0
  36. package/coverage/lcov-report/src/services/session.manager.ts.html +163 -0
  37. package/coverage/lcov.info +912 -0
  38. package/dist/controllers/sse/sse.controller.d.ts +1 -3
  39. package/dist/controllers/sse/sse.controller.js +2 -8
  40. package/dist/controllers/sse/sse.controller.js.map +1 -1
  41. package/dist/interfaces/capabilities.interface.d.ts +1 -1
  42. package/dist/interfaces/{context.interface.d.ts → guards.interface.d.ts} +0 -2
  43. package/dist/interfaces/{message.types.js → guards.interface.js} +1 -1
  44. package/dist/interfaces/guards.interface.js.map +1 -0
  45. package/dist/interfaces/index.d.ts +1 -1
  46. package/dist/interfaces/index.js +1 -1
  47. package/dist/interfaces/index.js.map +1 -1
  48. package/dist/interfaces/mcp-server-options.interface.d.ts +2 -2
  49. package/dist/mcp.module.js +1 -17
  50. package/dist/mcp.module.js.map +1 -1
  51. package/dist/registry/registry.service.d.ts +1 -3
  52. package/dist/registry/registry.service.js +3 -8
  53. package/dist/registry/registry.service.js.map +1 -1
  54. package/dist/tsconfig.build.tsbuildinfo +1 -1
  55. package/package.json +16 -17
  56. package/src/controllers/sse/sse.service.ts +21 -5
  57. package/src/controllers/streamable/streamable.service.ts +45 -23
  58. package/src/interfaces/capabilities.interface.ts +112 -1
  59. package/src/interfaces/context.interface.ts +21 -6
  60. package/src/mcp.module.ts +4 -9
  61. package/src/registry/registry.service.ts +94 -10
  62. package/src/services/session.manager.ts +26 -0
  63. package/dist/interceptors/message.interceptor.d.ts +0 -10
  64. package/dist/interceptors/message.interceptor.js +0 -61
  65. package/dist/interceptors/message.interceptor.js.map +0 -1
  66. package/dist/interfaces/context.interface.js +0 -3
  67. package/dist/interfaces/context.interface.js.map +0 -1
  68. package/dist/interfaces/message.types.d.ts +0 -8
  69. package/dist/interfaces/message.types.js.map +0 -1
  70. package/dist/services/message.service.d.ts +0 -7
  71. package/dist/services/message.service.js +0 -25
  72. package/dist/services/message.service.js.map +0 -1
  73. package/src/interceptors/message.interceptor.ts +0 -70
  74. package/src/services/message.service.ts +0 -18
package/README.md CHANGED
@@ -1,7 +1,13 @@
1
1
  # MCP Server NestJS Module Library <!-- omit in toc -->
2
2
 
3
- [![NPM Version](https://img.shields.io/npm/v/@your-org/nestjs-mcp-server)](https://www.npmjs.com/package/@nestjs-mcp/server)
3
+ [![NPM Version](https://img.shields.io/npm/v/@nestjs-mcp/server)](https://www.npmjs.com/package/@nestjs-mcp/server)
4
+ [![Semantic Release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)
5
+ [![Downloads](https://img.shields.io/npm/dm/@nestjs-mcp/server)](https://www.npmjs.com/package/@nestjs-mcp/server)
6
+ [![CI Pipeline](https://github.com/adrian-d-hidalgo/nestjs-mcp-server/actions/workflows/run-tests.yml/badge.svg)](https://github.com/adrian-d-hidalgo/nestjs-mcp-server/actions/workflows/run-tests.yml)
7
+ [![codecov](https://codecov.io/gh/adrian-d-hidalgo/nestjs-mcp-server/graph/badge.svg?token=5E228VKY5K)](https://codecov.io/gh/adrian-d-hidalgo/nestjs-mcp-server)
8
+ [![Known Vulnerabilities](https://snyk.io/test/github/adrian-d-hidalgo/nestjs-mcp-server/badge.svg)](https://snyk.io/test/github/adrian-d-hidalgo/nestjs-mcp-server)
4
9
  [![MIT License](https://img.shields.io/badge/license-MIT-green.svg)](./LICENSE)
10
+ [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](./CONTRIBUTING.md)
5
11
 
6
12
  ---
7
13
 
@@ -24,18 +30,24 @@
24
30
  - [Prompt](#prompt)
25
31
  - [Module API](#module-api)
26
32
  - [forRoot](#mcpmoduleforroot)
33
+ - [forRootAsync](#mcpmoduleforrootasync)
27
34
  - [forFeature](#mcpmoduleforfeature)
28
- - [Resolver](#resolver)
35
+ - [Module Usage](#module-usage)
36
+ - [1. Global Registration with `McpModule.forRoot`](#1-global-registration-with-mcpmoduleforroot)
37
+ - [2. Feature Module Registration with `McpModule.forFeature`](#2-feature-module-registration-with-mcpmoduleforfeature)
29
38
  - [Capabilities](#capabilities)
30
- - [@Resolver](#resolver-decorator)
31
- - [@Prompt](#prompt-decorator)
32
- - [@Resource](#resource-decorator)
33
- - [@Tool](#tool-decorator)
39
+ - [Resolver Decorator](#resolver-decorator)
40
+ - [Prompt Decorator](#prompt-decorator)
41
+ - [Resource Decorator](#resource-decorator)
42
+ - [Tool Decorator](#tool-decorator)
34
43
  - [Guards](#guards)
35
- - [Global-level guards:](#global-guard-guards)
36
- - [Resolver-level guards:](#resolver-level-guards)
37
- - [Method-level guards:](#method-level-guards)
44
+ - [Global-level guards](#global-level-guards)
45
+ - [Resolver-level guards](#resolver-level-guards)
46
+ - [Method-level guards](#method-level-guards)
38
47
  - [Guard Example](#guard-example)
48
+ - [MCP Execution Context](#mcp-execution-context)
49
+ - [Session Management](#session-management)
50
+ - [Transport Options](#transport-options)
39
51
  - [Inspector Playground](#inspector-playground)
40
52
  - [Examples](#examples)
41
53
  - [Changelog](#changelog)
@@ -60,14 +72,13 @@ Register the MCP module in your NestJS app and expose a simple tool:
60
72
 
61
73
  ```ts
62
74
  import { Module } from '@nestjs/common';
63
- import { Injectable } from '@nestjs/common';
64
75
 
65
76
  import { CallToolResult } from '@modelcontextprotocol/sdk/types';
66
77
 
67
- import { Tool } from '@nestjs-mcp/server';
78
+ import { Resolver, Tool, McpModule } from '@nestjs-mcp/server';
68
79
 
69
- @Injectable()
70
- export class AppService {
80
+ @Resolver()
81
+ export class HealthResolver {
71
82
  /**
72
83
  * Simple health check tool
73
84
  */
@@ -91,7 +102,7 @@ export class AppService {
91
102
  version: '1.0.0',
92
103
  }),
93
104
  ],
94
- providers: [AppService],
105
+ providers: [HealthResolver],
95
106
  })
96
107
  export class AppModule {}
97
108
  ```
@@ -138,19 +149,31 @@ A Prompt defines a conversational flow, template, or interaction pattern for LLM
138
149
 
139
150
  ### `McpModule.forRoot`
140
151
 
141
- Registers the MCP Server globally in your NestJS application. Accepts an options object compatible with the MCP Server specification from `@modelcontextprotocol/sdk`.
152
+ Registers the MCP Server globally in your NestJS application.
142
153
 
143
154
  **Parameters:**
144
155
 
145
- - `options: McpServerOptions` — Main server configuration (name, version, description, etc.)
156
+ - `options: McpModuleOptions` — Main server configuration object:
157
+ - `name: string`: The name of your MCP server.
158
+ - `version: string`: The version of your MCP server.
159
+ - `instructions?: string`: Optional description of the MCP server for the client.
160
+ - `capabilities?: Record<string, unknown>`: Optional additional capabilities metadata.
161
+ - `providers?: Provider[]`: Optional array of NestJS providers to include in the module.
162
+ - `imports?: any[]`: Optional array of NestJS modules to import.
163
+ - `logging?: McpLoggingOptions`: Optional logging configuration:
164
+ - `enabled?: boolean` (default: `true`): Enable/disable logging.
165
+ - `level?: 'error' | 'warn' | 'log' | 'debug' | 'verbose'` (default: `'verbose'`): Set the logging level.
166
+ - `transports?: McpModuleTransportOptions`: Optional transport configuration (see [Transport Options](#transport-options)).
167
+ - `protocolOptions?: Record<string, unknown>`: Optional parameters passed directly to the underlying `@modelcontextprotocol/sdk` server instance.
146
168
 
147
169
  **Returns:**
148
170
 
149
- - A dynamic NestJS module with all MCP providers registered
171
+ - A dynamic NestJS module with all MCP providers registered.
150
172
 
151
173
  **Example:**
152
174
 
153
175
  ```ts
176
+ import { Module } from '@nestjs/common';
154
177
  import { McpModule } from '@nestjs-mcp/server';
155
178
 
156
179
  @Module({
@@ -158,6 +181,9 @@ import { McpModule } from '@nestjs-mcp/server';
158
181
  McpModule.forRoot({
159
182
  name: 'My Server',
160
183
  version: '1.0.0',
184
+ instructions: 'A server providing utility tools and data.',
185
+ logging: { level: 'log' },
186
+ transports: { sse: { enabled: false } }, // Disable SSE transport
161
187
  // ...other MCP options
162
188
  }),
163
189
  ],
@@ -165,28 +191,84 @@ import { McpModule } from '@nestjs-mcp/server';
165
191
  export class AppModule {}
166
192
  ```
167
193
 
194
+ ### `McpModule.forRootAsync`
195
+
196
+ Registers the MCP Server globally using asynchronous options, useful for integrating with configuration modules like `@nestjs/config`.
197
+
198
+ **Parameters:**
199
+
200
+ - `options: McpModuleAsyncOptions` — Asynchronous configuration object:
201
+ - `imports?: any[]`: Optional modules to import before the factory runs.
202
+ - `useFactory: (...args: any[]) => Promise<McpModuleOptions> | McpModuleOptions`: A factory function that returns the `McpModuleOptions`.
203
+ - `inject?: any[]`: Optional providers to inject into the `useFactory`.
204
+
205
+ **Returns:**
206
+
207
+ - A dynamic NestJS module.
208
+
209
+ **Example (with ConfigModule):**
210
+
211
+ ```ts
212
+ import { Module } from '@nestjs/common';
213
+ import { ConfigModule, ConfigService } from '@nestjs/config';
214
+ import { McpModule } from '@nestjs-mcp/server';
215
+
216
+ @Module({
217
+ imports: [
218
+ ConfigModule.forRoot(), // Make sure ConfigModule is imported
219
+ McpModule.forRootAsync({
220
+ imports: [ConfigModule], // Import ConfigModule here too
221
+ useFactory: (configService: ConfigService) => ({
222
+ name: configService.get<string>('MCP_SERVER_NAME', 'Default Server'),
223
+ version: configService.get<string>('MCP_SERVER_VERSION', '1.0.0'),
224
+ instructions: configService.get<string>('MCP_SERVER_DESC'),
225
+ logging: {
226
+ level: configService.get('MCP_LOG_LEVEL', 'verbose'),
227
+ },
228
+ // ... other options from configService
229
+ }),
230
+ inject: [ConfigService], // Inject ConfigService into the factory
231
+ }),
232
+ ],
233
+ })
234
+ export class AppModule {}
235
+ ```
236
+
168
237
  ### `McpModule.forFeature`
169
238
 
170
- Registers additional MCP resources, tools, or prompts in a feature module. Use this to organize large servers into multiple modules.
239
+ Registers additional MCP resources, tools, or prompts within a feature module. Use this to organize large servers into multiple modules. Resolvers containing MCP capabilities must be included in the `providers` array of the feature module.
171
240
 
172
241
  **Parameters:**
173
242
 
174
- - `providers: Provider[]` Array of NestJS providers (resources, tools, prompts)
243
+ - `options?: McpFeatureOptions` (Currently unused, reserved for future enhancements).
175
244
 
176
245
  **Returns:**
177
246
 
178
- - A dynamic module with the specified providers
247
+ - A dynamic module.
179
248
 
180
249
  **Example:**
181
250
 
182
251
  ```ts
252
+ // src/status/status.resolver.ts
253
+ import { Resolver, Tool } from '@nestjs-mcp/server';
254
+ import { CallToolResult } from '@modelcontextprotocol/sdk/types';
255
+
256
+ @Resolver('status')
257
+ export class StatusResolver {
258
+ @Tool({ name: 'health_check' })
259
+ healthCheck(): CallToolResult {
260
+ return { content: [{ type: 'text', text: 'OK' }] };
261
+ }
262
+ }
263
+
264
+ // src/status/status.module.ts
265
+ import { Module } from '@nestjs/common';
183
266
  import { McpModule } from '@nestjs-mcp/server';
267
+ import { StatusResolver } from './status.resolver';
184
268
 
185
269
  @Module({
186
- imports: [McpModule.forFeature()],
187
- providers: [
188
- /*Your Providers with Mcp Capabilities*/
189
- ],
270
+ imports: [McpModule.forFeature()], // Import forFeature here
271
+ providers: [StatusResolver], // Register your resolver
190
272
  })
191
273
  export class StatusModule {}
192
274
  ```
@@ -236,9 +318,9 @@ import { ToolsResolver } from './tools.resolver';
236
318
  export class ToolsModule {}
237
319
  ```
238
320
 
239
- - Use `forRoot` only once in your root module.
240
- - Use `forFeature` as many times as needed in feature modules.
241
- - All resolvers, tools, and resources must be registered as providers.
321
+ - Use `forRoot` or `forRootAsync` **only once** in your root module (`AppModule`).
322
+ - Use `forFeature` in any feature module where you define MCP capabilities (`@Resolver` classes).
323
+ - Ensure all Resolvers are listed in the `providers` array of their respective modules.
242
324
 
243
325
  ---
244
326
 
@@ -248,27 +330,38 @@ This library provides a set of decorators to define MCP capabilities and apply c
248
330
 
249
331
  ### Resolver Decorator
250
332
 
251
- A Resolver is a class that groups related MCP capabilities (such as prompts, resources, and tools) and provides a workspace context for them. Use the `@Resolver` decorator to mark a class as a resolver. Dependency injection is supported, and you can apply guards or other cross-cutting concerns at the class level.
333
+ A Resolver is a class that groups related MCP capabilities. **All** MCP capability methods (`@Prompt`, `@Resource`, `@Tool`) **must** belong to a class decorated with `@Resolver`.
334
+
335
+ - **No `@Injectable()` Needed:** Resolver classes are automatically treated as providers by the MCP module and **do not** require the `@Injectable()` decorator.
336
+ - **Dependency Injection:** Standard NestJS dependency injection works within Resolver constructors.
337
+ - **Namespacing:** You can optionally provide a string argument to `@Resolver('my_namespace')` to namespace the capabilities within that resolver.
338
+ - **Guards:** Guards can be applied at the class level using `@UseGuards()`.
252
339
 
253
340
  **Example:**
254
341
 
255
342
  ```ts
256
343
  import { Resolver, Prompt, Resource, Tool } from '@nestjs-mcp/server';
344
+ // Import any services you need to inject
345
+ import { SomeService } from '../some.service';
257
346
 
258
- @Resolver('workspace')
347
+ @Resolver('workspace') // No @Injectable()
259
348
  export class MyResolver {
260
- @Prompt({ name: 'greet' })
261
- greetPrompt() {
349
+ // Inject dependencies as usual
350
+ constructor(private readonly someService: SomeService) {}
351
+
352
+ @Prompt({ name: 'greet_user' }) // Capabilities must be inside a Resolver
353
+ greetPrompt(/*...args...*/) {
354
+ const greeting = this.someService.getGreeting();
262
355
  /* ... */
263
356
  }
264
357
 
265
- @Resource({ name: 'user', uri: 'user://{id}' })
266
- getUserResource() {
358
+ @Resource({ name: 'user_profile', uri: 'user://{id}' })
359
+ getUserResource(/*...args...*/) {
267
360
  /* ... */
268
361
  }
269
362
 
270
- @Tool({ name: 'sum' })
271
- sumTool() {
363
+ @Tool({ name: 'calculate_sum' })
364
+ sumTool(/*...args...*/) {
272
365
  /* ... */
273
366
  }
274
367
  }
@@ -280,57 +373,123 @@ You can also apply guards at the resolver level:
280
373
  import { UseGuards, Resolver } from '@nestjs-mcp/server';
281
374
  import { MyGuard } from './guards/my.guard';
282
375
 
283
- @UseGuards(MyGuard)
284
- @Resolver('secure')
376
+ @UseGuards(MyGuard) // Applied to all capabilities in this Resolver
377
+ @Resolver('secure') // No @Injectable()
285
378
  export class SecureResolver {
286
- // ...
379
+ // All capabilities in this resolver will use MyGuard
287
380
  }
288
381
  ```
289
382
 
290
383
  ### Prompt Decorator
291
384
 
292
- Decorate methods within a Resolver to expose them as MCP Prompts.
385
+ Decorate methods within a Resolver class to expose them as MCP Prompts. Accepts options compatible with `server.prompt()` from `@modelcontextprotocol/sdk`. **The `name` should use `snake_case`.**
293
386
 
294
387
  ```ts
295
- import { Prompt } from '@nestjs-mcp/server';
296
-
297
- @Resolver('workspace')
298
- export class MyResolver {
299
- @Prompt({ name: 'greet' })
300
- greetPrompt() {
301
- /* ... */
388
+ import { Prompt, Resolver } from '@nestjs-mcp/server';
389
+ import { RequestHandlerExtra } from '@nestjs-mcp/server'; // Import type for extra info
390
+ import { z } from 'zod'; // Example if using Zod schema
391
+
392
+ // Optional: Define schema if needed
393
+ // const SummaryArgs = z.object({ topic: z.string() });
394
+
395
+ @Resolver('prompts') // Must be in a Resolver class
396
+ export class MyPrompts {
397
+ @Prompt({
398
+ name: 'generate_summary',
399
+ description: 'Generates a summary for the given text.',
400
+ // argsSchema: SummaryArgs
401
+ })
402
+ generateSummaryPrompt(
403
+ // params: z.infer<typeof SummaryArgs>, // Arguments based on argsSchema (if defined)
404
+ extra: RequestHandlerExtra, // Contains sessionId and other metadata
405
+ ) {
406
+ console.log(`Generating summary for session: ${extra.sessionId}`);
407
+ /* ... return CallPromptResult ... */
408
+ return { content: [{ type: 'text', text: 'Summary generated.' }] };
302
409
  }
303
410
  }
304
411
  ```
305
412
 
306
413
  ### Resource Decorator
307
414
 
308
- Decorate methods within a Resolver to expose them as MCP Resources.
415
+ Decorate methods within a Resolver class to expose them as MCP Resources. Accepts options compatible with `server.resource()` from `@modelcontextprotocol/sdk`. **The `name` should use `snake_case`.**
309
416
 
310
417
  ```ts
311
- import { Resource } from '@nestjs-mcp/server';
418
+ import { Resource, Resolver } from '@nestjs-mcp/server';
419
+ import { RequestHandlerExtra } from '@nestjs-mcp/server'; // Import type for extra info
420
+ import { URL } from 'url'; // Type for URI resource
421
+ import { z } from 'zod'; // Example if using Zod template
422
+
423
+ // Optional: Define template schema if needed
424
+ // const DocQueryTemplate = z.object({ query: z.string() });
425
+
426
+ @Resolver('data') // Must be in a Resolver class
427
+ export class MyResources {
428
+ @Resource({
429
+ name: 'user_profile',
430
+ uri: 'user://profiles/{userId}',
431
+ // metadata: { description: '...' } // Optional
432
+ })
433
+ getUserProfile(
434
+ uri: URL, // First argument is the parsed URI
435
+ // metadata: Record<string, any> // Second argument if is defined
436
+ extra: RequestHandlerExtra, // Contains sessionId and other metadata
437
+ ) {
438
+ const userId = uri.pathname.split('/').pop(); // Example: Extract ID from URI
439
+ console.log(`Fetching profile for ${userId}, session: ${extra.sessionId}`);
440
+ /* ... return CallResourceResult ... */
441
+ return { content: [{ type: 'text', text: `Profile data for ${userId}` }] };
442
+ }
312
443
 
313
- @Resolver('workspace')
314
- export class MyResolver {
315
- @Resource({ name: 'user', uri: 'user://{id}' })
316
- getUserResource() {
317
- /* ... */
444
+ @Resource({
445
+ name: 'document_list',
446
+ template: { type: 'string', description: 'Document content query' }, // Simple template example
447
+ // metadata: { list: true } // Optional
448
+ })
449
+ findDocuments(
450
+ uri: URL, // First arg based on simple template type
451
+ variables: Record<string, string>, // Second arg is path params (if any)
452
+ extra: RequestHandlerExtra, // Contains sessionId and other metadata
453
+ ) {
454
+ console.log(
455
+ `Finding documents matching '${query}', session: ${extra.sessionId}`,
456
+ );
457
+ /* ... return CallResourceResult ... */
458
+ return { content: [{ type: 'text', text: 'List of documents.' }] };
318
459
  }
319
460
  }
320
461
  ```
321
462
 
322
463
  ### Tool Decorator
323
464
 
324
- Decorate methods within a Resolver to expose them as MCP Tools.
465
+ Decorate methods within a Resolver class to expose them as MCP Tools. Accepts options compatible with `server.tool()` from `@modelcontextprotocol/sdk`. **The `name` should use `snake_case`.**
325
466
 
326
467
  ```ts
327
- import { Tool } from '@nestjs-mcp/server';
328
-
329
- @Resolver('workspace')
330
- export class MyResolver {
331
- @Tool({ name: 'sum' })
332
- sumTool() {
333
- /* ... */
468
+ import { Tool, Resolver } from '@nestjs-mcp/server';
469
+ import { RequestHandlerExtra } from '@nestjs-mcp/server'; // Import type for extra info
470
+ import { z } from 'zod'; // Example using Zod for schema
471
+
472
+ // Example Zod schema for parameters
473
+ const SumParams = z.object({
474
+ num1: z.number(),
475
+ num2: z.number(),
476
+ });
477
+
478
+ @Resolver('utils') // Must be in a Resolver class
479
+ export class MyTools {
480
+ @Tool({
481
+ name: 'calculate_sum',
482
+ description: 'Calculates the sum of two numbers.',
483
+ paramSchema: SumParams, // Use the Zod schema
484
+ })
485
+ sumTool(
486
+ params: z.infer<typeof SumParams>, // First arg is typed parameters from schema
487
+ extra: RequestHandlerExtra, // Contains sessionId and other metadata
488
+ ) {
489
+ console.log(`Calculating sum for session: ${extra.sessionId}`);
490
+ const result = params.num1 + params.num2;
491
+ /* ... return CallToolResult ... */
492
+ return { content: [{ type: 'text', text: `Result: ${result}` }] };
334
493
  }
335
494
  }
336
495
  ```
@@ -341,20 +500,22 @@ export class MyResolver {
341
500
 
342
501
  Apply one or more guards to a Resolver, to individual methods, or globally. Guards must implement the NestJS `CanActivate` interface.
343
502
 
344
- ### Global-level guards:
503
+ ### Global-level guards
345
504
 
346
- This approach uses the standard NestJS global guard system. A global guard will protect all entry points of your MCP server by running before any connection is handled. Use this for authentication, API key checks, or any logic that should apply to every connection.
505
+ This approach uses the standard NestJS global guard system (`APP_GUARD`). A global guard will protect **all** NestJS routes, including the MCP transport endpoints (like `/mcp` or `/sse`). Use this for broad authentication or checks that apply before any MCP-specific logic runs.
347
506
 
348
507
  ```ts
349
508
  // src/guards/global-auth.guard.ts
350
509
  import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
510
+ import { Request } from 'express';
351
511
 
352
512
  @Injectable()
353
513
  export class GlobalAuthGuard implements CanActivate {
354
514
  canActivate(context: ExecutionContext): boolean {
355
- // Example: Allow all requests (replace with real logic)
356
- // You can access request info via context.switchToHttp().getRequest() if needed
357
- return true;
515
+ const request = context.switchToHttp().getRequest<Request>();
516
+ const apiKey = request.headers['x-api-key'];
517
+ // Example: Check for a valid API key
518
+ return !!apiKey && apiKey === 'EXPECTED_KEY';
358
519
  }
359
520
  }
360
521
  ```
@@ -362,21 +523,15 @@ export class GlobalAuthGuard implements CanActivate {
362
523
  Register the guard globally in your main module:
363
524
 
364
525
  ```ts
526
+ // src/app.module.ts
365
527
  import { Module } from '@nestjs/common';
366
528
  import { APP_GUARD } from '@nestjs/core';
367
529
  import { McpModule } from '@nestjs-mcp/server';
368
530
  import { GlobalAuthGuard } from './guards/global-auth.guard';
369
- import { PromptsResolver } from './prompts.resolver';
370
531
 
371
532
  @Module({
372
- imports: [
373
- McpModule.forRoot({
374
- name: 'My MCP Server',
375
- version: '1.0.0',
376
- }),
377
- ],
533
+ imports: [McpModule.forRoot(/*...*/)],
378
534
  providers: [
379
- PromptsResolver,
380
535
  {
381
536
  provide: APP_GUARD,
382
537
  useClass: GlobalAuthGuard,
@@ -386,116 +541,275 @@ import { PromptsResolver } from './prompts.resolver';
386
541
  export class AppModule {}
387
542
  ```
388
543
 
389
- **Key points:**
390
-
391
- - The guard will run for every request handled by your NestJS application, including all MCP endpoints.
392
- - You can implement any logic in canActivate, such as checking headers, tokens, or user roles.
393
- - This approach is fully compatible with NestJS and your MCP Server module.
544
+ ### Resolver-level guards
394
545
 
395
- ### Resolver-level guards:
396
-
397
- This is a custom feature of this library. Resolver-level guards are applied using the `@UseGuards` decorator on a Resolver class. All MCP methods (`@Prompt`, `@Resource`, `@Tool`) in the resolver will be protected by these guards. Use this to enforce logic (e.g., role checks) for a specific group of capabilities.
546
+ This is a custom feature of this library. Resolver-level guards are applied using the `@UseGuards()` decorator (exported from `@nestjs-mcp/server`) on a Resolver class. All MCP methods (`@Prompt`, `@Resource`, `@Tool`) **within that specific resolver** will be protected by these guards. Use this to enforce logic (e.g., role checks) for a group of related capabilities.
398
547
 
399
548
  ```ts
400
549
  import { UseGuards, Resolver, Prompt } from '@nestjs-mcp/server';
401
- import { MyGuard } from './guards/my.guard';
550
+ import { RoleGuard } from './guards/role.guard';
402
551
 
403
- @UseGuards(MyGuard)
404
- @Resolver('secure')
405
- export class SecureResolver {
406
- @Prompt({ name: 'securePrompt' })
407
- securePrompt() {
552
+ @UseGuards(RoleGuard)
553
+ @Resolver('admin')
554
+ export class AdminResolver {
555
+ @Prompt({ name: 'admin_action' })
556
+ adminAction(/*...*/) {
408
557
  /* ... */
409
558
  }
559
+ // ... other admin capabilities
410
560
  }
411
561
  ```
412
562
 
413
- ### Method-level guards:
563
+ ### Method-level guards
414
564
 
415
- This is a custom feature of this library. Method-level guards are applied using the `@UseGuards` decorator directly on a method. Only the decorated MCP method will be protected by these guards. Use this for fine-grained access control on specific capabilities.
565
+ This is a custom feature of this library. Method-level guards are applied using the `@UseGuards()` decorator directly on an MCP capability method (`@Prompt`, `@Resource`, `@Tool`). Only the decorated method will be protected by these guards. Use this for fine-grained access control on specific capabilities.
416
566
 
417
567
  ```ts
418
- import { UseGuards, Resolver, Prompt } from '@nestjs-mcp/server';
419
- import { MyGuard } from './guards/my.guard';
568
+ import { UseGuards, Resolver, Prompt, Tool } from '@nestjs-mcp/server';
569
+ import { SpecificCheckGuard } from './guards/specific-check.guard';
420
570
 
421
571
  @Resolver('mixed')
422
572
  export class MixedResolver {
423
- @Prompt({ name: 'publicPrompt' })
573
+ @Prompt({ name: 'public_prompt' })
424
574
  publicPrompt() {
425
- /* ... */
575
+ /* Publicly accessible */
426
576
  }
427
577
 
428
- @UseGuards(MyGuard)
429
- @Prompt({ name: 'protectedPrompt' })
430
- protectedPrompt() {
431
- /* ... */
578
+ @UseGuards(SpecificCheckGuard)
579
+ @Tool({ name: 'protected_tool' })
580
+ protectedTool(/*...*/) {
581
+ /* Requires SpecificCheckGuard to pass */
432
582
  }
433
583
  }
434
584
  ```
435
585
 
586
+ **Important:** Resolver and Method-level guards **only run for MCP capability invocations**, not for the initial connection establishment handled by global guards. They use the custom `McpExecutionContext`.
587
+
436
588
  ### Guard Example
437
589
 
590
+ A guard for Resolver or Method-level protection:
591
+
438
592
  ```ts
439
- import { CanActivate } from '@nestjs/common';
440
- import { McpExecutionContext } from '@nestjs-mcp/server';
593
+ // src/guards/my-mcp.guard.ts
594
+ import { CanActivate, Injectable } from '@nestjs/common';
595
+ import { McpExecutionContext, SessionManager } from '@nestjs-mcp/server';
596
+
597
+ @Injectable()
598
+ export class MyMcpGuard implements CanActivate {
599
+ constructor(private readonly sessionManager: SessionManager) {}
441
600
 
442
- export class MyGuard implements CanActivate {
443
601
  canActivate(context: McpExecutionContext): boolean {
444
- // Custom logic: allow or deny
602
+ const sessionId = context.getSessionId();
603
+ if (!sessionId) return false;
604
+
605
+ const handlerArgs = context.getArgs();
606
+
607
+ const session = this.sessionManager.getSession(sessionId);
608
+ const request = session?.request;
609
+ const userAgent = request?.headers['user-agent'];
610
+
611
+ console.log(`Guard activated for session ${sessionId} from ${userAgent}`);
612
+ console.log('Handler args:', handlerArgs);
613
+
445
614
  return true;
446
615
  }
447
616
  }
448
617
  ```
449
618
 
450
- ### Using McpContext in Guards
619
+ ### MCP Execution Context
451
620
 
452
- When implementing guards for MCP resolvers and methods, you can use the `McpExecutionContext` interface to access MCP-specific context information. This interface extends the standard NestJS `ExecutionContext` and provides additional properties specific to MCP operations.
621
+ When implementing **Resolver-level** or **Method-level** guards using `@UseGuards()` from this library, your `canActivate` method receives an `McpExecutionContext` instance. This context provides access to MCP-specific information:
453
622
 
454
- ```ts
455
- import { CanActivate } from '@nestjs/common';
456
- import { McpExecutionContext } from '@nestjs-mcp/server';
457
- import { Request, Response } from 'express';
623
+ ```typescript
624
+ import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
625
+ import { McpExecutionContext, SessionManager } from '@nestjs-mcp/server';
626
+ import { Request } from 'express';
458
627
 
459
628
  @Injectable()
460
629
  export class McpAuthGuard implements CanActivate {
630
+ constructor(private readonly sessionManager: SessionManager) {}
631
+
461
632
  canActivate(context: McpExecutionContext): boolean {
462
- // Access MCP-specific context
463
- const { args, message } = context;
633
+ const sessionId = context.getSessionId();
634
+ if (!sessionId) {
635
+ console.error('Guard Error: MCP Session ID not found in context.');
636
+ return false;
637
+ }
638
+
639
+ const handlerArgs = context.getArgs<any>();
640
+ console.log('MCP Handler Arguments:', handlerArgs);
641
+
642
+ const session = this.sessionManager.getSession(sessionId);
643
+ if (!session) {
644
+ console.error(`Guard Error: Session not found for ID: ${sessionId}`);
645
+ return false;
646
+ }
647
+ const request = session.request as Request;
648
+
649
+ const authHeader = request.headers.authorization;
650
+ if (!authHeader || !authHeader.startsWith('Bearer ')) {
651
+ console.log('Guard Denied: Missing or invalid Bearer token.');
652
+ return false;
653
+ }
654
+ const token = authHeader.split(' ')[1];
655
+ const isValidToken = token === 'VALID_TOKEN';
656
+
657
+ if (isValidToken) {
658
+ console.log(`Guard Passed for session ${sessionId} with token.`);
659
+ return true;
660
+ } else {
661
+ console.log(`Guard Denied: Invalid token for session ${sessionId}.`);
662
+ return false;
663
+ }
664
+ }
665
+ }
666
+ ```
667
+
668
+ **Key points for `McpExecutionContext`:**
669
+
670
+ - `getSessionId()`: Retrieves the unique ID for the current MCP session. **Crucial** for relating the guard check to the session state stored by `SessionManager`.
671
+ - Arguments (`handlerArgs`): Provides the arguments passed specifically to the MCP handler method (`@Tool`, `@Prompt`, `@Resource`) being invoked. The structure of these arguments depends on the capability type and its definition (e.g., `params` for tools, `query`/`params` for resources). You access these via `context.getArgs()`, but be mindful of the actual structure based on the capability.
672
+ - Request Data: Use the `SessionManager` injected into your guard to fetch the session details (including the original `Request`) based on the `sessionId` obtained from the context.
673
+ - `switchToHttp().getResponse()` / `switchToHttp().getNext()`: These will throw errors as the Response object is not directly available or relevant in this context.
674
+
675
+ Use `SessionManager` injected into your guard to fetch the session details (including the original `Request`) based on the `sessionId` obtained from the context.
464
676
 
465
- // Access the current message from the request
466
- if (message) {
467
- const { req, res } = message;
677
+ ---
678
+
679
+ ## Session Management
680
+
681
+ This library includes a `SessionManager` service responsible for tracking active MCP sessions. Each incoming MCP connection establishes a session, identified by a unique `sessionId`. The `SessionManager` typically stores the associated initial `Request` object for each session.
682
+
683
+ **Why is it important?**
684
+
685
+ - **Accessing Request Data:** Since MCP operations (tool calls, prompt executions) might happen independently of the initial HTTP connection (especially with streaming transports like SSE), the `SessionManager` provides a way to retrieve the original `Request` context associated with a specific `sessionId`. This is essential for guards or capability methods (within Resolvers) that need access to request headers, parameters, or other connection-specific details from the original request.
686
+ - **State Management:** While currently focused on storing the request, the `SessionManager` could be extended to store additional session-specific state if needed by your application.
468
687
 
469
- // Access Express request and response objects
470
- const request = req as Request;
471
- const response = res as Response;
688
+ **Usage Example (in a Resolver):**
472
689
 
473
- // Example: Check authorization header
474
- const authHeader = request.headers.authorization;
690
+ Resolvers might need access to the original request, for example, to get user information or API keys passed in headers during the initial connection.
475
691
 
476
- // Implement your authentication logic here
477
- // For example, check if the authorization header is valid
692
+ ```typescript
693
+ import { Tool, Resolver, SessionManager } from '@nestjs-mcp/server';
694
+ import { RequestHandlerExtra } from '@nestjs-mcp/server'; // Provides sessionId
695
+ import { Request } from 'express';
696
+ import { CallToolResult } from '@modelcontextprotocol/sdk/types';
697
+ import { z } from 'zod';
698
+
699
+ const UserToolParams = z.object({
700
+ user_id: z.string().optional(),
701
+ });
702
+
703
+ @Resolver('user_tools') // No @Injectable() needed
704
+ export class UserToolsResolver {
705
+ // Inject SessionManager
706
+ constructor(private readonly sessionManager: SessionManager) {}
707
+
708
+ @Tool({
709
+ name: 'get_user_agent',
710
+ description:
711
+ 'Gets the user agent from the original request for the session.',
712
+ paramSchema: UserToolParams,
713
+ })
714
+ getUserAgent(
715
+ params: z.infer<typeof UserToolParams>,
716
+ extra: RequestHandlerExtra, // Get extra info, including sessionId
717
+ ): CallToolResult {
718
+ const sessionId = extra.sessionId;
719
+ if (!sessionId) {
720
+ return {
721
+ content: [{ type: 'text', text: 'Error: Session ID missing.' }],
722
+ };
723
+ }
724
+
725
+ // Use sessionId to get the session from the manager
726
+ const session = this.sessionManager.getSession(sessionId);
727
+ if (!session) {
728
+ return {
729
+ content: [
730
+ {
731
+ type: 'text',
732
+ text: `Error: Session not found for ID: ${sessionId}`,
733
+ },
734
+ ],
735
+ };
478
736
  }
479
737
 
480
- // Access the arguments passed to the MCP method
481
- const methodArgs = args;
738
+ // Access the original request stored in the session
739
+ const request = session.request as Request;
740
+ const userAgent = request.headers['user-agent'] || 'Unknown';
482
741
 
483
- return true; // or false to deny access
742
+ return {
743
+ content: [
744
+ { type: 'text', text: `Session ${sessionId} User Agent: ${userAgent}` },
745
+ ],
746
+ };
484
747
  }
485
748
  }
486
749
  ```
487
750
 
488
- **Key properties of McpExecutionContext:**
751
+ In this example:
752
+
753
+ 1. The `@Tool` method receives `extra: RequestHandlerExtra`, which contains the `sessionId`.
754
+ 2. The `SessionManager` is injected into the `UserToolsResolver`.
755
+ 3. The `sessionId` is used with `sessionManager.getSession()` to retrieve the session data.
756
+ 4. The original `request` object is accessed from the retrieved session data.
757
+
758
+ The `SessionManager` is automatically registered as a provider when you use `McpModule.forRoot` or `McpModule.forRootAsync` and can be injected like any other NestJS provider.
759
+
760
+ ---
761
+
762
+ ## Transport Options
763
+
764
+ The MCP server can communicate over different transport mechanisms. This library includes built-in support for:
765
+
766
+ 1. **Streamable (`/mcp` endpoint):** A common transport using standard HTTP POST requests and responses. Suitable for most request/response interactions. Enabled by default.
767
+ 2. **SSE (Server-Sent Events) (`/sse` endpoint):** A transport mechanism allowing the server to push updates to the client over a single HTTP connection. Useful for streaming responses or long-running operations. **Note:** This is considered a legacy transport but remains supported for compatibility. Enabled by default.
768
+
769
+ You can configure which transports are enabled globally using the `transports` option in `McpModule.forRoot` or `McpModule.forRootAsync`.
770
+
771
+ **Configuration:**
772
+
773
+ ```typescript
774
+ import { Module } from '@nestjs/common';
775
+ import { McpModule } from '@nestjs-mcp/server';
776
+
777
+ @Module({
778
+ imports: [
779
+ McpModule.forRoot({
780
+ name: 'My Server',
781
+ version: '1.0.0',
782
+ transports: {
783
+ streamable: { enabled: true }, // Keep streamable enabled (default)
784
+ sse: { enabled: false }, // Disable legacy SSE transport
785
+ },
786
+ }),
787
+ ],
788
+ })
789
+ export class AppModule {}
790
+ ```
791
+
792
+ **Default Configuration:**
489
793
 
490
- - `args`: The arguments passed to the MCP method being guarded
491
- - `message`: The current message from the request, containing:
492
- - `req`: The Express Request object
493
- - `res`: The Express Response object
494
- - `getType()`: Returns the type of execution context (always 'mcp' for MCP operations)
495
- - `getClass()`: Returns the class of the resolver
496
- - `getArgs()`: Returns the arguments passed to the method
794
+ If the `transports` option is omitted, both `streamable` (`/mcp`) and `sse` (`/sse`) are enabled by default.
497
795
 
498
- This context allows you to implement guards that are aware of the MCP protocol and can make decisions based on MCP-specific information, such as checking request headers, query parameters, or other request data.
796
+ ```typescript
797
+ import { Module } from '@nestjs/common';
798
+ import { McpModule } from '@nestjs-mcp/server';
799
+
800
+ @Module({
801
+ imports: [
802
+ McpModule.forRoot({
803
+ name: 'My Server',
804
+ version: '1.0.0',
805
+ // Both streamable and sse will be enabled
806
+ }),
807
+ ],
808
+ })
809
+ export class AppModule {}
810
+ ```
811
+
812
+ Disabling unused transports can slightly reduce the application's surface area and resource usage.
499
813
 
500
814
  ---
501
815
 
@@ -507,17 +821,19 @@ Use the Inspector Playground to interactively test and debug your MCP server end
507
821
  - Invoke endpoints and view responses in real time
508
822
  - Validate your server implementation against the MCP specification
509
823
 
510
- To launch the Inspector Playground:
824
+ To launch the Inspector Playground (make sure your NestJS MCP server is running):
511
825
 
512
826
  ```sh
513
827
  npx @modelcontextprotocol/inspector
514
828
  ```
515
829
 
830
+ It will typically connect to `http://localhost:3000` by default, or you can specify a different target URL.
831
+
516
832
  ---
517
833
 
518
834
  ## Examples
519
835
 
520
- The [`examples/`](./examples/) directory contains ready-to-use scenarios demonstrating how to register and expose MCP capabilities
836
+ The [`examples/`](./examples/) directory contains ready-to-use scenarios demonstrating how to register and expose MCP capabilities.
521
837
 
522
838
  Each example is self-contained and follows best practices. For advanced usage, see the code and documentation in each example.
523
839