@nestjs-mcp/server 0.1.0-alpha.9 → 0.1.1

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