@nestjs-mcp/server 0.1.0-alpha.12 → 0.1.0-alpha.14

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