@rekog/mcp-nest 1.8.4 → 1.8.5-alpha.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 (58) hide show
  1. package/README.md +3 -0
  2. package/dist/authz/guards/jwt-auth.guard.d.ts +3 -1
  3. package/dist/authz/guards/jwt-auth.guard.d.ts.map +1 -1
  4. package/dist/authz/guards/jwt-auth.guard.js +26 -3
  5. package/dist/authz/guards/jwt-auth.guard.js.map +1 -1
  6. package/dist/mcp/decorators/index.d.ts +3 -0
  7. package/dist/mcp/decorators/index.d.ts.map +1 -1
  8. package/dist/mcp/decorators/index.js +3 -0
  9. package/dist/mcp/decorators/index.js.map +1 -1
  10. package/dist/mcp/decorators/public.decorator.d.ts +3 -0
  11. package/dist/mcp/decorators/public.decorator.d.ts.map +1 -0
  12. package/dist/mcp/decorators/public.decorator.js +8 -0
  13. package/dist/mcp/decorators/public.decorator.js.map +1 -0
  14. package/dist/mcp/decorators/require-roles.decorator.d.ts +3 -0
  15. package/dist/mcp/decorators/require-roles.decorator.d.ts.map +1 -0
  16. package/dist/mcp/decorators/require-roles.decorator.js +13 -0
  17. package/dist/mcp/decorators/require-roles.decorator.js.map +1 -0
  18. package/dist/mcp/decorators/require-scopes.decorator.d.ts +3 -0
  19. package/dist/mcp/decorators/require-scopes.decorator.d.ts.map +1 -0
  20. package/dist/mcp/decorators/require-scopes.decorator.js +13 -0
  21. package/dist/mcp/decorators/require-scopes.decorator.js.map +1 -0
  22. package/dist/mcp/decorators/tool.decorator.d.ts +10 -0
  23. package/dist/mcp/decorators/tool.decorator.d.ts.map +1 -1
  24. package/dist/mcp/decorators/tool.decorator.js.map +1 -1
  25. package/dist/mcp/interfaces/mcp-options.interface.d.ts +1 -0
  26. package/dist/mcp/interfaces/mcp-options.interface.d.ts.map +1 -1
  27. package/dist/mcp/interfaces/mcp-options.interface.js.map +1 -1
  28. package/dist/mcp/mcp.module.d.ts.map +1 -1
  29. package/dist/mcp/mcp.module.js +4 -1
  30. package/dist/mcp/mcp.module.js.map +1 -1
  31. package/dist/mcp/services/handlers/mcp-tools.handler.d.ts +6 -1
  32. package/dist/mcp/services/handlers/mcp-tools.handler.d.ts.map +1 -1
  33. package/dist/mcp/services/handlers/mcp-tools.handler.js +29 -4
  34. package/dist/mcp/services/handlers/mcp-tools.handler.js.map +1 -1
  35. package/dist/mcp/services/mcp-executor.service.d.ts +3 -1
  36. package/dist/mcp/services/mcp-executor.service.d.ts.map +1 -1
  37. package/dist/mcp/services/mcp-executor.service.js +5 -3
  38. package/dist/mcp/services/mcp-executor.service.js.map +1 -1
  39. package/dist/mcp/services/mcp-registry.service.d.ts.map +1 -1
  40. package/dist/mcp/services/mcp-registry.service.js +25 -1
  41. package/dist/mcp/services/mcp-registry.service.js.map +1 -1
  42. package/dist/mcp/services/tool-authorization.service.d.ts +11 -0
  43. package/dist/mcp/services/tool-authorization.service.d.ts.map +1 -0
  44. package/dist/mcp/services/tool-authorization.service.js +109 -0
  45. package/dist/mcp/services/tool-authorization.service.js.map +1 -0
  46. package/package.json +2 -2
  47. package/src/authz/guards/jwt-auth.guard.ts +34 -1
  48. package/src/mcp/decorators/index.ts +3 -0
  49. package/src/mcp/decorators/public.decorator.ts +26 -0
  50. package/src/mcp/decorators/require-roles.decorator.ts +46 -0
  51. package/src/mcp/decorators/require-scopes.decorator.ts +51 -0
  52. package/src/mcp/decorators/tool.decorator.ts +12 -0
  53. package/src/mcp/interfaces/mcp-options.interface.ts +16 -0
  54. package/src/mcp/mcp.module.ts +4 -1
  55. package/src/mcp/services/handlers/mcp-tools.handler.ts +49 -2
  56. package/src/mcp/services/mcp-executor.service.ts +11 -1
  57. package/src/mcp/services/mcp-registry.service.ts +43 -5
  58. package/src/mcp/services/tool-authorization.service.ts +211 -0
package/README.md CHANGED
@@ -30,6 +30,9 @@ With `@rekog/mcp-nest` you define tools, resources, and prompts in a way that's
30
30
  - 🌐 **[External Authorization Server](docs/external-authorization-server/README.md)** — Securing your MCP server with an external authorization server (Keycloak, Auth0, etc).
31
31
  - 💉 **[Dependency Injection](docs/dependency-injection.md)**: Leverage NestJS DI system throughout MCP components
32
32
 
33
+ **Are you interested to build ChatGPT widgets (with the OpenAI SDK)?**
34
+ Find out how to do that with `@rekog/MCP-Nest` in this repository [MCP-Nest-Samples/pizzaz-openai-apps-sdk](https://github.com/rinormaloku/MCP-Nest-Samples/tree/main/pizzaz-openai-apps-sdk)
35
+
33
36
  ## Installation
34
37
 
35
38
  ```bash
@@ -2,13 +2,15 @@ import { CanActivate, ExecutionContext } from '@nestjs/common';
2
2
  import { Request } from 'express';
3
3
  import { JwtPayload, JwtTokenService } from '../services/jwt-token.service';
4
4
  import { IOAuthStore } from '../stores/oauth-store.interface';
5
+ import { McpOptions } from '../../mcp';
5
6
  export interface AuthenticatedRequest extends Request {
6
7
  user: JwtPayload;
7
8
  }
8
9
  export declare class McpAuthJwtGuard implements CanActivate {
9
10
  private readonly jwtTokenService;
10
11
  private readonly store;
11
- constructor(jwtTokenService: JwtTokenService, store: IOAuthStore);
12
+ private readonly options?;
13
+ constructor(jwtTokenService: JwtTokenService, store: IOAuthStore, options?: McpOptions | undefined);
12
14
  canActivate(context: ExecutionContext): Promise<boolean>;
13
15
  private extractTokenFromHeader;
14
16
  }
@@ -1 +1 @@
1
- {"version":3,"file":"jwt-auth.guard.d.ts","sourceRoot":"","sources":["../../../src/authz/guards/jwt-auth.guard.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,WAAW,EACX,gBAAgB,EAGjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAC5E,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAE9D,MAAM,WAAW,oBAAqB,SAAQ,OAAO;IACnD,IAAI,EAAE,UAAU,CAAC;CAClB;AAED,qBACa,eAAgB,YAAW,WAAW;IAE/C,OAAO,CAAC,QAAQ,CAAC,eAAe;IACT,OAAO,CAAC,QAAQ,CAAC,KAAK;gBAD5B,eAAe,EAAE,eAAe,EACT,KAAK,EAAE,WAAW;IAGtD,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;IA8C9D,OAAO,CAAC,sBAAsB;CAS/B"}
1
+ {"version":3,"file":"jwt-auth.guard.d.ts","sourceRoot":"","sources":["../../../src/authz/guards/jwt-auth.guard.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,WAAW,EACX,gBAAgB,EAIjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAC5E,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC,MAAM,WAAW,oBAAqB,SAAQ,OAAO;IACnD,IAAI,EAAE,UAAU,CAAC;CAClB;AAED,qBACa,eAAgB,YAAW,WAAW;IAE/C,OAAO,CAAC,QAAQ,CAAC,eAAe;IACT,OAAO,CAAC,QAAQ,CAAC,KAAK;IAG7C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;gBAJR,eAAe,EAAE,eAAe,EACT,KAAK,EAAE,WAAW,EAGzC,OAAO,CAAC,EAAE,UAAU,YAAA;IAGjC,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;IA0E9D,OAAO,CAAC,sBAAsB;CAS/B"}
@@ -16,15 +16,22 @@ exports.McpAuthJwtGuard = void 0;
16
16
  const common_1 = require("@nestjs/common");
17
17
  const jwt_token_service_1 = require("../services/jwt-token.service");
18
18
  let McpAuthJwtGuard = class McpAuthJwtGuard {
19
- constructor(jwtTokenService, store) {
19
+ constructor(jwtTokenService, store, options) {
20
20
  this.jwtTokenService = jwtTokenService;
21
21
  this.store = store;
22
+ this.options = options;
22
23
  }
23
24
  async canActivate(context) {
24
25
  const request = context.switchToHttp().getRequest();
25
26
  const token = this.extractTokenFromHeader(request);
27
+ const allowUnauthenticated = this.options?.allowUnauthenticatedAccess ?? false;
26
28
  if (!token) {
27
- throw new common_1.UnauthorizedException('Access token required');
29
+ if (allowUnauthenticated) {
30
+ return true;
31
+ }
32
+ else {
33
+ throw new common_1.UnauthorizedException('Access token required');
34
+ }
28
35
  }
29
36
  const payload = this.jwtTokenService.validateToken(token);
30
37
  if (!payload) {
@@ -50,6 +57,20 @@ let McpAuthJwtGuard = class McpAuthJwtGuard {
50
57
  ud.username ||
51
58
  ud.email ||
52
59
  enriched.sub;
60
+ if (enriched.scope && typeof enriched.scope === 'string') {
61
+ enriched.scopes = enriched.scope
62
+ .split(' ')
63
+ .filter((s) => s.length > 0);
64
+ }
65
+ else if (!enriched.scopes) {
66
+ enriched.scopes = [];
67
+ }
68
+ if (!enriched.roles && ud.roles && Array.isArray(ud.roles)) {
69
+ enriched.roles = ud.roles;
70
+ }
71
+ else if (!enriched.roles) {
72
+ enriched.roles = [];
73
+ }
53
74
  }
54
75
  catch {
55
76
  }
@@ -69,6 +90,8 @@ exports.McpAuthJwtGuard = McpAuthJwtGuard;
69
90
  exports.McpAuthJwtGuard = McpAuthJwtGuard = __decorate([
70
91
  (0, common_1.Injectable)(),
71
92
  __param(1, (0, common_1.Inject)('IOAuthStore')),
72
- __metadata("design:paramtypes", [jwt_token_service_1.JwtTokenService, Object])
93
+ __param(2, (0, common_1.Optional)()),
94
+ __param(2, (0, common_1.Inject)('MCP_OPTIONS')),
95
+ __metadata("design:paramtypes", [jwt_token_service_1.JwtTokenService, Object, Object])
73
96
  ], McpAuthJwtGuard);
74
97
  //# sourceMappingURL=jwt-auth.guard.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"jwt-auth.guard.js","sourceRoot":"","sources":["../../../src/authz/guards/jwt-auth.guard.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAMwB;AAExB,qEAA4E;AAQrE,IAAM,eAAe,GAArB,MAAM,eAAe;IAC1B,YACmB,eAAgC,EACT,KAAkB;QADzC,oBAAe,GAAf,eAAe,CAAiB;QACT,UAAK,GAAL,KAAK,CAAa;IACzD,CAAC;IAEJ,KAAK,CAAC,WAAW,CAAC,OAAyB;QACzC,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAwB,CAAC;QAC1E,MAAM,KAAK,GAAG,IAAI,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC;QAEnD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,8BAAqB,CAAC,uBAAuB,CAAC,CAAC;QAC3D,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAE1D,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,8BAAqB,CAAC,iCAAiC,CAAC,CAAC;QACrE,CAAC;QAGD,MAAM,QAAQ,GAAQ,EAAE,GAAG,OAAO,EAAE,CAAC;QACrC,IAAI,CAAC;YACH,IAAI,CAAC,QAAQ,CAAC,SAAS,IAAI,QAAQ,CAAC,eAAe,EAAE,CAAC;gBACpD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,kBAAkB,CACjD,QAAQ,CAAC,eAAe,CACzB,CAAC;gBACF,IAAI,OAAO,EAAE,CAAC;oBACZ,QAAQ,CAAC,SAAS,GAAG,OAAO,CAAC;gBAC/B,CAAC;YACH,CAAC;YACD,MAAM,EAAE,GAAG,QAAQ,CAAC,SAAS,IAAI,EAAE,CAAC;YAEpC,QAAQ,CAAC,QAAQ;gBACf,QAAQ,CAAC,QAAQ,IAAI,EAAE,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,IAAI,QAAQ,CAAC,GAAG,CAAC;YAC5D,QAAQ,CAAC,KAAK,GAAG,QAAQ,CAAC,KAAK,IAAI,EAAE,CAAC,KAAK,CAAC;YAC5C,QAAQ,CAAC,WAAW,GAAG,QAAQ,CAAC,WAAW,IAAI,EAAE,CAAC,WAAW,CAAC;YAC9D,QAAQ,CAAC,SAAS,GAAG,QAAQ,CAAC,SAAS,IAAI,EAAE,CAAC,SAAS,CAAC;YACxD,QAAQ,CAAC,IAAI;gBACX,QAAQ,CAAC,IAAI;oBACb,EAAE,CAAC,WAAW;oBACd,EAAE,CAAC,QAAQ;oBACX,EAAE,CAAC,KAAK;oBACR,QAAQ,CAAC,GAAG,CAAC;QACjB,CAAC;QAAC,MAAM,CAAC;QAET,CAAC;QAED,OAAO,CAAC,IAAI,GAAG,QAAsB,CAAC;QACtC,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,sBAAsB,CAAC,OAAgB;QAC7C,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC;QACjD,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC5C,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IAC/C,CAAC;CACF,CAAA;AA7DY,0CAAe;0BAAf,eAAe;IAD3B,IAAA,mBAAU,GAAE;IAIR,WAAA,IAAA,eAAM,EAAC,aAAa,CAAC,CAAA;qCADY,mCAAe;GAFxC,eAAe,CA6D3B","sourcesContent":["import {\n Injectable,\n CanActivate,\n ExecutionContext,\n UnauthorizedException,\n Inject,\n} from '@nestjs/common';\nimport { Request } from 'express';\nimport { JwtPayload, JwtTokenService } from '../services/jwt-token.service';\nimport { IOAuthStore } from '../stores/oauth-store.interface';\n\nexport interface AuthenticatedRequest extends Request {\n user: JwtPayload;\n}\n\n@Injectable()\nexport class McpAuthJwtGuard implements CanActivate {\n constructor(\n private readonly jwtTokenService: JwtTokenService,\n @Inject('IOAuthStore') private readonly store: IOAuthStore,\n ) {}\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const request = context.switchToHttp().getRequest<AuthenticatedRequest>();\n const token = this.extractTokenFromHeader(request);\n\n if (!token) {\n throw new UnauthorizedException('Access token required');\n }\n\n const payload = this.jwtTokenService.validateToken(token);\n\n if (!payload) {\n throw new UnauthorizedException('Invalid or expired access token');\n }\n\n // Enrich request.user with friendly fields for tools\n const enriched: any = { ...payload };\n try {\n if (!enriched.user_data && enriched.user_profile_id) {\n const profile = await this.store.getUserProfileById(\n enriched.user_profile_id,\n );\n if (profile) {\n enriched.user_data = profile;\n }\n }\n const ud = enriched.user_data || {};\n // Provide convenient top-level fields commonly used by tools\n enriched.username =\n enriched.username || ud.username || ud.id || enriched.sub;\n enriched.email = enriched.email || ud.email;\n enriched.displayName = enriched.displayName || ud.displayName;\n enriched.avatarUrl = enriched.avatarUrl || ud.avatarUrl;\n enriched.name =\n enriched.name ||\n ud.displayName ||\n ud.username ||\n ud.email ||\n enriched.sub;\n } catch {\n // Non-fatal; proceed with raw payload\n }\n\n request.user = enriched as JwtPayload;\n return true;\n }\n\n private extractTokenFromHeader(request: Request): string | undefined {\n const authHeader = request.headers.authorization;\n if (!authHeader) {\n return undefined;\n }\n\n const [type, token] = authHeader.split(' ');\n return type === 'Bearer' ? token : undefined;\n }\n}\n"]}
1
+ {"version":3,"file":"jwt-auth.guard.js","sourceRoot":"","sources":["../../../src/authz/guards/jwt-auth.guard.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Injectable,\n CanActivate,\n ExecutionContext,\n UnauthorizedException,\n Inject,\n Optional,\n} from '@nestjs/common';\nimport { Request } from 'express';\nimport { JwtPayload, JwtTokenService } from '../services/jwt-token.service';\nimport { IOAuthStore } from '../stores/oauth-store.interface';\nimport { McpOptions } from '../../mcp';\n\nexport interface AuthenticatedRequest extends Request {\n user: JwtPayload;\n}\n\n@Injectable()\nexport class McpAuthJwtGuard implements CanActivate {\n constructor(\n private readonly jwtTokenService: JwtTokenService,\n @Inject('IOAuthStore') private readonly store: IOAuthStore,\n @Optional()\n @Inject('MCP_OPTIONS')\n private readonly options?: McpOptions,\n ) {}\n\n async canActivate(context: ExecutionContext): Promise<boolean> {\n const request = context.switchToHttp().getRequest<AuthenticatedRequest>();\n const token = this.extractTokenFromHeader(request);\n\n // Check if unauthenticated access is allowed\n const allowUnauthenticated =\n this.options?.allowUnauthenticatedAccess ?? false;\n\n if (!token) {\n if (allowUnauthenticated) {\n // Allow unauthenticated sessions\n // Per-tool authorization will decide what's accessible (@Public() tools only)\n return true;\n } else {\n // Standard OAuth flow: Reject and trigger authorization\n throw new UnauthorizedException('Access token required');\n }\n }\n\n // If a token is provided, it must be valid\n const payload = this.jwtTokenService.validateToken(token);\n\n if (!payload) {\n throw new UnauthorizedException('Invalid or expired access token');\n }\n\n // Enrich request.user with friendly fields for tools\n const enriched: any = { ...payload };\n try {\n if (!enriched.user_data && enriched.user_profile_id) {\n const profile = await this.store.getUserProfileById(\n enriched.user_profile_id,\n );\n if (profile) {\n enriched.user_data = profile;\n }\n }\n const ud = enriched.user_data || {};\n // Provide convenient top-level fields commonly used by tools\n enriched.username =\n enriched.username || ud.username || ud.id || enriched.sub;\n enriched.email = enriched.email || ud.email;\n enriched.displayName = enriched.displayName || ud.displayName;\n enriched.avatarUrl = enriched.avatarUrl || ud.avatarUrl;\n enriched.name =\n enriched.name ||\n ud.displayName ||\n ud.username ||\n ud.email ||\n enriched.sub;\n\n // Parse scopes: OAuth 2.0 standard is space-delimited string in 'scope' field\n if (enriched.scope && typeof enriched.scope === 'string') {\n enriched.scopes = enriched.scope\n .split(' ')\n .filter((s: string) => s.length > 0);\n } else if (!enriched.scopes) {\n enriched.scopes = [];\n }\n\n // Extract roles from user_data if present\n if (!enriched.roles && ud.roles && Array.isArray(ud.roles)) {\n enriched.roles = ud.roles;\n } else if (!enriched.roles) {\n enriched.roles = [];\n }\n } catch {\n // Non-fatal; proceed with raw payload\n }\n\n request.user = enriched as JwtPayload;\n return true;\n }\n\n private extractTokenFromHeader(request: Request): string | undefined {\n const authHeader = request.headers.authorization;\n if (!authHeader) {\n return undefined;\n }\n\n const [type, token] = authHeader.split(' ');\n return type === 'Bearer' ? token : undefined;\n }\n}\n"]}
@@ -3,4 +3,7 @@ export * from './constants';
3
3
  export * from './resource.decorator';
4
4
  export * from './resource-template.decorator';
5
5
  export * from './prompt.decorator';
6
+ export * from './public.decorator';
7
+ export * from './require-scopes.decorator';
8
+ export * from './require-roles.decorator';
6
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/mcp/decorators/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC;AAC5B,cAAc,sBAAsB,CAAC;AACrC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/mcp/decorators/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC;AAC5B,cAAc,sBAAsB,CAAC;AACrC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,2BAA2B,CAAC"}
@@ -19,4 +19,7 @@ __exportStar(require("./constants"), exports);
19
19
  __exportStar(require("./resource.decorator"), exports);
20
20
  __exportStar(require("./resource-template.decorator"), exports);
21
21
  __exportStar(require("./prompt.decorator"), exports);
22
+ __exportStar(require("./public.decorator"), exports);
23
+ __exportStar(require("./require-scopes.decorator"), exports);
24
+ __exportStar(require("./require-roles.decorator"), exports);
22
25
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/mcp/decorators/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,mDAAiC;AACjC,8CAA4B;AAC5B,uDAAqC;AACrC,gEAA8C;AAC9C,qDAAmC","sourcesContent":["export * from './tool.decorator';\nexport * from './constants';\nexport * from './resource.decorator';\nexport * from './resource-template.decorator';\nexport * from './prompt.decorator';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/mcp/decorators/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,mDAAiC;AACjC,8CAA4B;AAC5B,uDAAqC;AACrC,gEAA8C;AAC9C,qDAAmC;AACnC,qDAAmC;AACnC,6DAA2C;AAC3C,4DAA0C","sourcesContent":["export * from './tool.decorator';\nexport * from './constants';\nexport * from './resource.decorator';\nexport * from './resource-template.decorator';\nexport * from './prompt.decorator';\nexport * from './public.decorator';\nexport * from './require-scopes.decorator';\nexport * from './require-roles.decorator';\n"]}
@@ -0,0 +1,3 @@
1
+ export declare const MCP_PUBLIC_METADATA_KEY = "mcp:public";
2
+ export declare const Public: () => import("@nestjs/common").CustomDecorator<string>;
3
+ //# sourceMappingURL=public.decorator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"public.decorator.d.ts","sourceRoot":"","sources":["../../../src/mcp/decorators/public.decorator.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,uBAAuB,eAAe,CAAC;AAoBpD,eAAO,MAAM,MAAM,wDAAmD,CAAC"}
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Public = exports.MCP_PUBLIC_METADATA_KEY = void 0;
4
+ const common_1 = require("@nestjs/common");
5
+ exports.MCP_PUBLIC_METADATA_KEY = 'mcp:public';
6
+ const Public = () => (0, common_1.SetMetadata)(exports.MCP_PUBLIC_METADATA_KEY, true);
7
+ exports.Public = Public;
8
+ //# sourceMappingURL=public.decorator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"public.decorator.js","sourceRoot":"","sources":["../../../src/mcp/decorators/public.decorator.ts"],"names":[],"mappings":";;;AAAA,2CAA6C;AAKhC,QAAA,uBAAuB,GAAG,YAAY,CAAC;AAoB7C,MAAM,MAAM,GAAG,GAAG,EAAE,CAAC,IAAA,oBAAW,EAAC,+BAAuB,EAAE,IAAI,CAAC,CAAC;AAA1D,QAAA,MAAM,UAAoD","sourcesContent":["import { SetMetadata } from '@nestjs/common';\n\n/**\n * Metadata key for marking a tool as publicly accessible\n */\nexport const MCP_PUBLIC_METADATA_KEY = 'mcp:public';\n\n/**\n * Decorator to mark a tool as publicly accessible, bypassing authentication requirements.\n *\n * When applied to a tool method, it allows the tool to be called without authentication,\n * even if the module has guards configured.\n *\n * @example\n * ```typescript\n * @Injectable()\n * export class MyTools {\n * @Tool({ name: 'public-search', description: 'Search publicly' })\n * @Public()\n * async search() {\n * return { content: [{ type: 'text', text: 'Public results' }] };\n * }\n * }\n * ```\n */\nexport const Public = () => SetMetadata(MCP_PUBLIC_METADATA_KEY, true);\n"]}
@@ -0,0 +1,3 @@
1
+ export declare const MCP_ROLES_METADATA_KEY = "mcp:roles";
2
+ export declare const RequireRoles: (roles: string[]) => import("@nestjs/common").CustomDecorator<string>;
3
+ //# sourceMappingURL=require-roles.decorator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"require-roles.decorator.d.ts","sourceRoot":"","sources":["../../../src/mcp/decorators/require-roles.decorator.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,sBAAsB,cAAc,CAAC;AAiClD,eAAO,MAAM,YAAY,GAAI,OAAO,MAAM,EAAE,qDAO3C,CAAC"}
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RequireRoles = exports.MCP_ROLES_METADATA_KEY = void 0;
4
+ const common_1 = require("@nestjs/common");
5
+ exports.MCP_ROLES_METADATA_KEY = 'mcp:roles';
6
+ const RequireRoles = (roles) => {
7
+ if (!Array.isArray(roles) || roles.length === 0) {
8
+ throw new Error('@RequireRoles() requires a non-empty array of role strings');
9
+ }
10
+ return (0, common_1.SetMetadata)(exports.MCP_ROLES_METADATA_KEY, roles);
11
+ };
12
+ exports.RequireRoles = RequireRoles;
13
+ //# sourceMappingURL=require-roles.decorator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"require-roles.decorator.js","sourceRoot":"","sources":["../../../src/mcp/decorators/require-roles.decorator.ts"],"names":[],"mappings":";;;AAAA,2CAA6C;AAKhC,QAAA,sBAAsB,GAAG,WAAW,CAAC;AAiC3C,MAAM,YAAY,GAAG,CAAC,KAAe,EAAE,EAAE;IAC9C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CACb,4DAA4D,CAC7D,CAAC;IACJ,CAAC;IACD,OAAO,IAAA,oBAAW,EAAC,8BAAsB,EAAE,KAAK,CAAC,CAAC;AACpD,CAAC,CAAC;AAPW,QAAA,YAAY,gBAOvB","sourcesContent":["import { SetMetadata } from '@nestjs/common';\n\n/**\n * Metadata key for storing required roles\n */\nexport const MCP_ROLES_METADATA_KEY = 'mcp:roles';\n\n/**\n * Decorator to specify roles required to access a tool.\n *\n * When applied to a tool method, it requires the authenticated user to have\n * ALL specified roles in their JWT token or user profile.\n *\n * Can be combined with @RequireScopes() for fine-grained access control.\n *\n * @param roles - Array of required role strings\n *\n * @example\n * ```typescript\n * @Injectable()\n * export class MyTools {\n * // Requires 'admin' role\n * @Tool({ name: 'system-config', description: 'Configure system' })\n * @RequireRoles(['admin'])\n * async configureSystem(args, ctx, req: McpRequestWithUser) {\n * return { content: [{ type: 'text', text: 'System configured' }] };\n * }\n *\n * // Requires both role and scope\n * @Tool({ name: 'audit-log', description: 'View audit logs' })\n * @RequireRoles(['auditor'])\n * @RequireScopes(['logs.read'])\n * async viewAuditLog(args, ctx, req: McpRequestWithUser) {\n * return { content: [{ type: 'text', text: 'Audit log data...' }] };\n * }\n * }\n * ```\n */\nexport const RequireRoles = (roles: string[]) => {\n if (!Array.isArray(roles) || roles.length === 0) {\n throw new Error(\n '@RequireRoles() requires a non-empty array of role strings',\n );\n }\n return SetMetadata(MCP_ROLES_METADATA_KEY, roles);\n};\n"]}
@@ -0,0 +1,3 @@
1
+ export declare const MCP_SCOPES_METADATA_KEY = "mcp:scopes";
2
+ export declare const RequireScopes: (scopes: string[]) => import("@nestjs/common").CustomDecorator<string>;
3
+ //# sourceMappingURL=require-scopes.decorator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"require-scopes.decorator.d.ts","sourceRoot":"","sources":["../../../src/mcp/decorators/require-scopes.decorator.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,uBAAuB,eAAe,CAAC;AAsCpD,eAAO,MAAM,aAAa,GAAI,QAAQ,MAAM,EAAE,qDAO7C,CAAC"}
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RequireScopes = exports.MCP_SCOPES_METADATA_KEY = void 0;
4
+ const common_1 = require("@nestjs/common");
5
+ exports.MCP_SCOPES_METADATA_KEY = 'mcp:scopes';
6
+ const RequireScopes = (scopes) => {
7
+ if (!Array.isArray(scopes) || scopes.length === 0) {
8
+ throw new Error('@RequireScopes() requires a non-empty array of scope strings');
9
+ }
10
+ return (0, common_1.SetMetadata)(exports.MCP_SCOPES_METADATA_KEY, scopes);
11
+ };
12
+ exports.RequireScopes = RequireScopes;
13
+ //# sourceMappingURL=require-scopes.decorator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"require-scopes.decorator.js","sourceRoot":"","sources":["../../../src/mcp/decorators/require-scopes.decorator.ts"],"names":[],"mappings":";;;AAAA,2CAA6C;AAKhC,QAAA,uBAAuB,GAAG,YAAY,CAAC;AAsC7C,MAAM,aAAa,GAAG,CAAC,MAAgB,EAAE,EAAE;IAChD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClD,MAAM,IAAI,KAAK,CACb,8DAA8D,CAC/D,CAAC;IACJ,CAAC;IACD,OAAO,IAAA,oBAAW,EAAC,+BAAuB,EAAE,MAAM,CAAC,CAAC;AACtD,CAAC,CAAC;AAPW,QAAA,aAAa,iBAOxB","sourcesContent":["import { SetMetadata } from '@nestjs/common';\n\n/**\n * Metadata key for storing required OAuth scopes\n */\nexport const MCP_SCOPES_METADATA_KEY = 'mcp:scopes';\n\n/**\n * Decorator to specify OAuth scopes required to access a tool.\n *\n * When applied to a tool method, it requires the authenticated user to have\n * ALL specified scopes in their JWT token.\n *\n * Can be combined with @Public() to create tools that work better with authentication\n * but are also accessible anonymously.\n *\n * @param scopes - Array of required OAuth scope strings\n *\n * @example\n * ```typescript\n * @Injectable()\n * export class MyTools {\n * // Requires 'admin' and 'write' scopes\n * @Tool({ name: 'delete-user', description: 'Delete a user' })\n * @RequireScopes(['admin', 'write'])\n * async deleteUser(args, ctx, req: McpRequestWithUser) {\n * // Only users with both 'admin' AND 'write' scopes can call this\n * return { content: [{ type: 'text', text: 'User deleted' }] };\n * }\n *\n * // Optional auth - works better with 'premium' scope\n * @Tool({ name: 'search', description: 'Search content' })\n * @Public()\n * @RequireScopes(['premium'])\n * async search(args, ctx, req: McpRequestWithUser) {\n * if (req.user?.scopes?.includes('premium')) {\n * return { content: [{ type: 'text', text: 'AI-powered results' }] };\n * }\n * return { content: [{ type: 'text', text: 'Basic results' }] };\n * }\n * }\n * ```\n */\nexport const RequireScopes = (scopes: string[]) => {\n if (!Array.isArray(scopes) || scopes.length === 0) {\n throw new Error(\n '@RequireScopes() requires a non-empty array of scope strings',\n );\n }\n return SetMetadata(MCP_SCOPES_METADATA_KEY, scopes);\n};\n"]}
@@ -1,5 +1,11 @@
1
1
  import { z } from 'zod';
2
2
  import { ToolAnnotations as SdkToolAnnotations } from '@modelcontextprotocol/sdk/types.js';
3
+ export type SecurityScheme = {
4
+ type: 'noauth';
5
+ } | {
6
+ type: 'oauth2';
7
+ scopes?: string[];
8
+ };
3
9
  export interface ToolMetadata {
4
10
  name: string;
5
11
  description: string;
@@ -7,6 +13,10 @@ export interface ToolMetadata {
7
13
  outputSchema?: z.ZodTypeAny;
8
14
  annotations?: SdkToolAnnotations;
9
15
  _meta?: Record<string, any>;
16
+ securitySchemes?: SecurityScheme[];
17
+ isPublic?: boolean;
18
+ requiredScopes?: string[];
19
+ requiredRoles?: string[];
10
20
  }
11
21
  export interface ToolAnnotations extends SdkToolAnnotations {
12
22
  }
@@ -1 +1 @@
1
- {"version":3,"file":"tool.decorator.d.ts","sourceRoot":"","sources":["../../../src/mcp/decorators/tool.decorator.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,eAAe,IAAI,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAE3F,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,CAAC,CAAC,UAAU,CAAC;IAC1B,YAAY,CAAC,EAAE,CAAC,CAAC,UAAU,CAAC;IAC5B,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC7B;AAGD,MAAM,WAAW,eAAgB,SAAQ,kBAAkB;CAAG;AAE9D,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,CAAC,CAAC,UAAU,CAAC;IAC1B,YAAY,CAAC,EAAE,CAAC,CAAC,UAAU,CAAC;IAC5B,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC7B;AAWD,eAAO,MAAM,IAAI,GAAI,SAAS,WAAW,qDAMxC,CAAC"}
1
+ {"version":3,"file":"tool.decorator.d.ts","sourceRoot":"","sources":["../../../src/mcp/decorators/tool.decorator.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,eAAe,IAAI,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAK3F,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAE1C,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,CAAC,CAAC,UAAU,CAAC;IAC1B,YAAY,CAAC,EAAE,CAAC,CAAC,UAAU,CAAC;IAC5B,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAE5B,eAAe,CAAC,EAAE,cAAc,EAAE,CAAC;IACnC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AAGD,MAAM,WAAW,eAAgB,SAAQ,kBAAkB;CAAG;AAE9D,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,CAAC,CAAC,UAAU,CAAC;IAC1B,YAAY,CAAC,EAAE,CAAC,CAAC,UAAU,CAAC;IAC5B,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC7B;AAWD,eAAO,MAAM,IAAI,GAAI,SAAS,WAAW,qDAMxC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"tool.decorator.js","sourceRoot":"","sources":["../../../src/mcp/decorators/tool.decorator.ts"],"names":[],"mappings":";;;AAAA,2CAA6C;AAC7C,2CAAoD;AACpD,6BAAwB;AAiCjB,MAAM,IAAI,GAAG,CAAC,OAAoB,EAAE,EAAE;IAC3C,IAAI,OAAO,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;QACrC,OAAO,CAAC,UAAU,GAAG,OAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACpC,CAAC;IAED,OAAO,IAAA,oBAAW,EAAC,iCAAqB,EAAE,OAAO,CAAC,CAAC;AACrD,CAAC,CAAC;AANW,QAAA,IAAI,QAMf","sourcesContent":["import { SetMetadata } from '@nestjs/common';\nimport { MCP_TOOL_METADATA_KEY } from './constants';\nimport { z } from 'zod';\nimport { ToolAnnotations as SdkToolAnnotations } from '@modelcontextprotocol/sdk/types.js';\n\nexport interface ToolMetadata {\n name: string;\n description: string;\n parameters?: z.ZodTypeAny;\n outputSchema?: z.ZodTypeAny;\n annotations?: SdkToolAnnotations;\n _meta?: Record<string, any>;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface ToolAnnotations extends SdkToolAnnotations {}\n\nexport interface ToolOptions {\n name?: string;\n description?: string;\n parameters?: z.ZodTypeAny;\n outputSchema?: z.ZodTypeAny;\n annotations?: ToolAnnotations;\n _meta?: Record<string, any>;\n}\n\n/**\n * Decorator that marks a controller method as an MCP tool.\n * @param {Object} options - The options for the decorator\n * @param {string} options.name - The name of the tool\n * @param {string} options.description - The description of the tool\n * @param {z.ZodTypeAny} [options.parameters] - The parameters of the tool\n * @param {z.ZodTypeAny} [options.outputSchema] - The output schema of the tool\n * @returns {MethodDecorator} - The decorator\n */\nexport const Tool = (options: ToolOptions) => {\n if (options.parameters === undefined) {\n options.parameters = z.object({});\n }\n\n return SetMetadata(MCP_TOOL_METADATA_KEY, options);\n};\n"]}
1
+ {"version":3,"file":"tool.decorator.js","sourceRoot":"","sources":["../../../src/mcp/decorators/tool.decorator.ts"],"names":[],"mappings":";;;AAAA,2CAA6C;AAC7C,2CAAoD;AACpD,6BAAwB;AA6CjB,MAAM,IAAI,GAAG,CAAC,OAAoB,EAAE,EAAE;IAC3C,IAAI,OAAO,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;QACrC,OAAO,CAAC,UAAU,GAAG,OAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACpC,CAAC;IAED,OAAO,IAAA,oBAAW,EAAC,iCAAqB,EAAE,OAAO,CAAC,CAAC;AACrD,CAAC,CAAC;AANW,QAAA,IAAI,QAMf","sourcesContent":["import { SetMetadata } from '@nestjs/common';\nimport { MCP_TOOL_METADATA_KEY } from './constants';\nimport { z } from 'zod';\nimport { ToolAnnotations as SdkToolAnnotations } from '@modelcontextprotocol/sdk/types.js';\n\n/**\n * Security scheme type for MCP tools\n */\nexport type SecurityScheme =\n | { type: 'noauth' }\n | { type: 'oauth2'; scopes?: string[] };\n\nexport interface ToolMetadata {\n name: string;\n description: string;\n parameters?: z.ZodTypeAny;\n outputSchema?: z.ZodTypeAny;\n annotations?: SdkToolAnnotations;\n _meta?: Record<string, any>;\n // Security-related metadata\n securitySchemes?: SecurityScheme[];\n isPublic?: boolean;\n requiredScopes?: string[];\n requiredRoles?: string[];\n}\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface ToolAnnotations extends SdkToolAnnotations {}\n\nexport interface ToolOptions {\n name?: string;\n description?: string;\n parameters?: z.ZodTypeAny;\n outputSchema?: z.ZodTypeAny;\n annotations?: ToolAnnotations;\n _meta?: Record<string, any>;\n}\n\n/**\n * Decorator that marks a controller method as an MCP tool.\n * @param {Object} options - The options for the decorator\n * @param {string} options.name - The name of the tool\n * @param {string} options.description - The description of the tool\n * @param {z.ZodTypeAny} [options.parameters] - The parameters of the tool\n * @param {z.ZodTypeAny} [options.outputSchema] - The output schema of the tool\n * @returns {MethodDecorator} - The decorator\n */\nexport const Tool = (options: ToolOptions) => {\n if (options.parameters === undefined) {\n options.parameters = z.object({});\n }\n\n return SetMetadata(MCP_TOOL_METADATA_KEY, options);\n};\n"]}
@@ -17,6 +17,7 @@ export interface McpOptions {
17
17
  globalApiPrefix?: never;
18
18
  apiPrefix?: string;
19
19
  guards?: Type<CanActivate>[];
20
+ allowUnauthenticatedAccess?: boolean;
20
21
  decorators?: ClassDecorator[];
21
22
  sse?: {
22
23
  pingEnabled?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-options.interface.d.ts","sourceRoot":"","sources":["../../../src/mcp/interfaces/mcp-options.interface.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AACxE,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAEnE,oBAAY,gBAAgB;IAC1B,GAAG,QAAQ;IACX,eAAe,oBAAoB;IACnC,KAAK,UAAU;CAChB;AAED,MAAM,WAAW,UAAU;IAGzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,kBAAkB,CAAC;IAClC,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,SAAS,CAAC,EAAE,gBAAgB,GAAG,gBAAgB,EAAE,CAAC;IAClD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IAIrB,eAAe,CAAC,EAAE,KAAK,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;IAC7B,UAAU,CAAC,EAAE,cAAc,EAAE,CAAC;IAC9B,GAAG,CAAC,EAAE;QACJ,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB,CAAC;IACF,cAAc,CAAC,EAAE;QACf,kBAAkB,CAAC,EAAE,OAAO,CAAC;QAC7B,kBAAkB,CAAC,EAAE,MAAM,MAAM,CAAC;QAIlC,aAAa,CAAC,EAAE,OAAO,CAAC;KACzB,CAAC;CACH;AAGD,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;AAE5D,MAAM,WAAW,iBAAiB;IAChC,gBAAgB,IAAI,OAAO,CAAC,eAAe,CAAC,GAAG,eAAe,CAAC;CAChE;AAED,MAAM,WAAW,qBAAsB,SAAQ,IAAI,CAAC,cAAc,EAAE,SAAS,CAAC;IAC5E,WAAW,CAAC,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC;IACtC,QAAQ,CAAC,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC;IACnC,UAAU,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,eAAe,CAAC,GAAG,eAAe,CAAC;IAC5E,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC;IACf,cAAc,CAAC,EAAE,GAAG,EAAE,CAAC;CACxB"}
1
+ {"version":3,"file":"mcp-options.interface.d.ts","sourceRoot":"","sources":["../../../src/mcp/interfaces/mcp-options.interface.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AACxE,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAEnE,oBAAY,gBAAgB;IAC1B,GAAG,QAAQ;IACX,eAAe,oBAAoB;IACnC,KAAK,UAAU;CAChB;AAED,MAAM,WAAW,UAAU;IAGzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,kBAAkB,CAAC;IAClC,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,SAAS,CAAC,EAAE,gBAAgB,GAAG,gBAAgB,EAAE,CAAC;IAClD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IAIrB,eAAe,CAAC,EAAE,KAAK,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;IAgB7B,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,UAAU,CAAC,EAAE,cAAc,EAAE,CAAC;IAC9B,GAAG,CAAC,EAAE;QACJ,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB,CAAC;IACF,cAAc,CAAC,EAAE;QACf,kBAAkB,CAAC,EAAE,OAAO,CAAC;QAC7B,kBAAkB,CAAC,EAAE,MAAM,MAAM,CAAC;QAIlC,aAAa,CAAC,EAAE,OAAO,CAAC;KACzB,CAAC;CACH;AAGD,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;AAE5D,MAAM,WAAW,iBAAiB;IAChC,gBAAgB,IAAI,OAAO,CAAC,eAAe,CAAC,GAAG,eAAe,CAAC;CAChE;AAED,MAAM,WAAW,qBAAsB,SAAQ,IAAI,CAAC,cAAc,EAAE,SAAS,CAAC;IAC5E,WAAW,CAAC,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC;IACtC,QAAQ,CAAC,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC;IACnC,UAAU,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,eAAe,CAAC,GAAG,eAAe,CAAC;IAC5E,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC;IACf,cAAc,CAAC,EAAE,GAAG,EAAE,CAAC;CACxB"}
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-options.interface.js","sourceRoot":"","sources":["../../../src/mcp/interfaces/mcp-options.interface.ts"],"names":[],"mappings":";;;AAGA,IAAY,gBAIX;AAJD,WAAY,gBAAgB;IAC1B,+BAAW,CAAA;IACX,uDAAmC,CAAA;IACnC,mCAAe,CAAA;AACjB,CAAC,EAJW,gBAAgB,gCAAhB,gBAAgB,QAI3B","sourcesContent":["import { ServerCapabilities } from '@modelcontextprotocol/sdk/types.js';\nimport { CanActivate, ModuleMetadata, Type } from '@nestjs/common';\n\nexport enum McpTransportType {\n SSE = 'sse',\n STREAMABLE_HTTP = 'streamable-http',\n STDIO = 'stdio',\n}\n\nexport interface McpOptions {\n // When and if, additional properties are introduced in ServerOptions or ServerInfo,\n // consider deprecating these fields in favor of using ServerOptions and ServerInfo directly.\n name: string;\n version: string;\n capabilities?: ServerCapabilities;\n instructions?: string;\n\n transport?: McpTransportType | McpTransportType[];\n sseEndpoint?: string;\n messagesEndpoint?: string;\n mcpEndpoint?: string;\n /**\n * @deprecated Use `app.setGlobalPrefix()` for global api prefix. Use apiPrefix to attach a prefix to the handshake.\n */\n globalApiPrefix?: never;\n apiPrefix?: string;\n guards?: Type<CanActivate>[];\n decorators?: ClassDecorator[];\n sse?: {\n pingEnabled?: boolean;\n pingIntervalMs?: number;\n };\n streamableHttp?: {\n enableJsonResponse?: boolean;\n sessionIdGenerator?: () => string;\n /**\n * @experimental: The current implementation does not fully comply with the MCP Specification.\n */\n statelessMode?: boolean;\n };\n}\n\n// Async variant omits transport since controllers are not auto-registered in forRootAsync\nexport type McpAsyncOptions = Omit<McpOptions, 'transport'>;\n\nexport interface McpOptionsFactory {\n createMcpOptions(): Promise<McpAsyncOptions> | McpAsyncOptions;\n}\n\nexport interface McpModuleAsyncOptions extends Pick<ModuleMetadata, 'imports'> {\n useExisting?: Type<McpOptionsFactory>;\n useClass?: Type<McpOptionsFactory>;\n useFactory?: (...args: any[]) => Promise<McpAsyncOptions> | McpAsyncOptions;\n inject?: any[];\n extraProviders?: any[]; // allow user to provide additional providers in async mode\n}\n"]}
1
+ {"version":3,"file":"mcp-options.interface.js","sourceRoot":"","sources":["../../../src/mcp/interfaces/mcp-options.interface.ts"],"names":[],"mappings":";;;AAGA,IAAY,gBAIX;AAJD,WAAY,gBAAgB;IAC1B,+BAAW,CAAA;IACX,uDAAmC,CAAA;IACnC,mCAAe,CAAA;AACjB,CAAC,EAJW,gBAAgB,gCAAhB,gBAAgB,QAI3B","sourcesContent":["import { ServerCapabilities } from '@modelcontextprotocol/sdk/types.js';\nimport { CanActivate, ModuleMetadata, Type } from '@nestjs/common';\n\nexport enum McpTransportType {\n SSE = 'sse',\n STREAMABLE_HTTP = 'streamable-http',\n STDIO = 'stdio',\n}\n\nexport interface McpOptions {\n // When and if, additional properties are introduced in ServerOptions or ServerInfo,\n // consider deprecating these fields in favor of using ServerOptions and ServerInfo directly.\n name: string;\n version: string;\n capabilities?: ServerCapabilities;\n instructions?: string;\n\n transport?: McpTransportType | McpTransportType[];\n sseEndpoint?: string;\n messagesEndpoint?: string;\n mcpEndpoint?: string;\n /**\n * @deprecated Use `app.setGlobalPrefix()` for global api prefix. Use apiPrefix to attach a prefix to the handshake.\n */\n globalApiPrefix?: never;\n apiPrefix?: string;\n guards?: Type<CanActivate>[];\n /**\n * Allow unauthenticated sessions to connect and access @Public() tools.\n *\n * When true (freemium mode):\n * - Unauthenticated requests are allowed through guards\n * - Can access tools marked with @Public()\n * - Must authenticate to access protected tools (based on scopes/roles)\n *\n * When false or undefined (standard OAuth flow - default):\n * - Unauthenticated requests receive 401 response\n * - Triggers MCP OAuth authorization flow\n * - All tools require authentication\n *\n * @default false\n */\n allowUnauthenticatedAccess?: boolean;\n decorators?: ClassDecorator[];\n sse?: {\n pingEnabled?: boolean;\n pingIntervalMs?: number;\n };\n streamableHttp?: {\n enableJsonResponse?: boolean;\n sessionIdGenerator?: () => string;\n /**\n * @experimental: The current implementation does not fully comply with the MCP Specification.\n */\n statelessMode?: boolean;\n };\n}\n\n// Async variant omits transport since controllers are not auto-registered in forRootAsync\nexport type McpAsyncOptions = Omit<McpOptions, 'transport'>;\n\nexport interface McpOptionsFactory {\n createMcpOptions(): Promise<McpAsyncOptions> | McpAsyncOptions;\n}\n\nexport interface McpModuleAsyncOptions extends Pick<ModuleMetadata, 'imports'> {\n useExisting?: Type<McpOptionsFactory>;\n useClass?: Type<McpOptionsFactory>;\n useFactory?: (...args: any[]) => Promise<McpAsyncOptions> | McpAsyncOptions;\n inject?: any[];\n extraProviders?: any[]; // allow user to provide additional providers in async mode\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"mcp.module.d.ts","sourceRoot":"","sources":["../../src/mcp/mcp.module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAA0B,MAAM,gBAAgB,CAAC;AAGvE,OAAO,KAAK,EACV,UAAU,EACV,qBAAqB,EAGtB,MAAM,cAAc,CAAC;AAatB,qBAIa,SAAS;IAIpB,QAAQ,CAAC,aAAa,QAAQ;IAE9B,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,UAAU,GAAG,aAAa;IAoDlD,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,qBAAqB,GAAG,aAAa;IA8BlE,OAAO,CAAC,MAAM,CAAC,oBAAoB;IAgDnC,OAAO,CAAC,MAAM,CAAC,6BAA6B;IA2B5C,OAAO,CAAC,MAAM,CAAC,4BAA4B;IA0C3C,OAAO,CAAC,MAAM,CAAC,0BAA0B;CAuB1C"}
1
+ {"version":3,"file":"mcp.module.d.ts","sourceRoot":"","sources":["../../src/mcp/mcp.module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAA0B,MAAM,gBAAgB,CAAC;AAGvE,OAAO,KAAK,EACV,UAAU,EACV,qBAAqB,EAGtB,MAAM,cAAc,CAAC;AActB,qBAIa,SAAS;IAIpB,QAAQ,CAAC,aAAa,QAAQ;IAE9B,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,UAAU,GAAG,aAAa;IAoDlD,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,qBAAqB,GAAG,aAAa;IA+BlE,OAAO,CAAC,MAAM,CAAC,oBAAoB;IAgDnC,OAAO,CAAC,MAAM,CAAC,6BAA6B;IA2B5C,OAAO,CAAC,MAAM,CAAC,4BAA4B;IA0C3C,OAAO,CAAC,MAAM,CAAC,0BAA0B;CAwB1C"}
@@ -16,6 +16,7 @@ const mcp_registry_service_1 = require("./services/mcp-registry.service");
16
16
  const mcp_sse_service_1 = require("./services/mcp-sse.service");
17
17
  const mcp_streamable_http_service_1 = require("./services/mcp-streamable-http.service");
18
18
  const sse_ping_service_1 = require("./services/sse-ping.service");
19
+ const tool_authorization_service_1 = require("./services/tool-authorization.service");
19
20
  const sse_controller_factory_1 = require("./transport/sse.controller.factory");
20
21
  const stdio_service_1 = require("./transport/stdio.service");
21
22
  const streamable_http_controller_factory_1 = require("./transport/streamable-http.controller.factory");
@@ -71,6 +72,7 @@ let McpModule = McpModule_1 = class McpModule {
71
72
  },
72
73
  mcp_registry_service_1.McpRegistryService,
73
74
  mcp_executor_service_1.McpExecutorService,
75
+ tool_authorization_service_1.ToolAuthorizationService,
74
76
  sse_ping_service_1.SsePingService,
75
77
  mcp_sse_service_1.McpSseService,
76
78
  mcp_streamable_http_service_1.McpStreamableHttpService,
@@ -184,6 +186,7 @@ let McpModule = McpModule_1 = class McpModule {
184
186
  },
185
187
  mcp_registry_service_1.McpRegistryService,
186
188
  mcp_executor_service_1.McpExecutorService,
189
+ tool_authorization_service_1.ToolAuthorizationService,
187
190
  sse_ping_service_1.SsePingService,
188
191
  mcp_sse_service_1.McpSseService,
189
192
  mcp_streamable_http_service_1.McpStreamableHttpService,
@@ -196,7 +199,7 @@ exports.McpModule = McpModule;
196
199
  exports.McpModule = McpModule = McpModule_1 = __decorate([
197
200
  (0, common_1.Module)({
198
201
  imports: [core_1.DiscoveryModule],
199
- providers: [mcp_registry_service_1.McpRegistryService, mcp_executor_service_1.McpExecutorService],
202
+ providers: [mcp_registry_service_1.McpRegistryService, mcp_executor_service_1.McpExecutorService, tool_authorization_service_1.ToolAuthorizationService],
200
203
  })
201
204
  ], McpModule);
202
205
  //# sourceMappingURL=mcp.module.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"mcp.module.js","sourceRoot":"","sources":["../../src/mcp/mcp.module.ts"],"names":[],"mappings":"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","sourcesContent":["import { DynamicModule, Module, Provider, Type } from '@nestjs/common';\nimport { DiscoveryModule } from '@nestjs/core';\nimport { McpTransportType } from './interfaces';\nimport type {\n McpOptions,\n McpModuleAsyncOptions,\n McpOptionsFactory,\n McpAsyncOptions,\n} from './interfaces';\nimport { McpExecutorService } from './services/mcp-executor.service';\nimport { McpRegistryService } from './services/mcp-registry.service';\nimport { McpSseService } from './services/mcp-sse.service';\nimport { McpStreamableHttpService } from './services/mcp-streamable-http.service';\nimport { SsePingService } from './services/sse-ping.service';\nimport { createSseController } from './transport/sse.controller.factory';\nimport { StdioService } from './transport/stdio.service';\nimport { createStreamableHttpController } from './transport/streamable-http.controller.factory';\nimport { normalizeEndpoint } from './utils/normalize-endpoint';\n\nlet instanceIdCounter = 0;\n\n@Module({\n imports: [DiscoveryModule],\n providers: [McpRegistryService, McpExecutorService],\n})\nexport class McpModule {\n /**\n * To avoid import circular dependency issues, we use a marker property.\n */\n readonly __isMcpModule = true;\n\n static forRoot(options: McpOptions): DynamicModule {\n const defaultOptions: Partial<McpOptions> = {\n transport: [\n McpTransportType.SSE,\n McpTransportType.STREAMABLE_HTTP,\n McpTransportType.STDIO,\n ],\n sseEndpoint: 'sse',\n messagesEndpoint: 'messages',\n mcpEndpoint: 'mcp',\n guards: [],\n decorators: [],\n streamableHttp: {\n enableJsonResponse: true,\n sessionIdGenerator: undefined,\n statelessMode: true,\n },\n sse: {\n pingEnabled: true,\n pingIntervalMs: 30000,\n },\n };\n const mergedOptions = { ...defaultOptions, ...options } as McpOptions;\n mergedOptions.sseEndpoint = normalizeEndpoint(mergedOptions.sseEndpoint);\n mergedOptions.messagesEndpoint = normalizeEndpoint(\n mergedOptions.messagesEndpoint,\n );\n mergedOptions.mcpEndpoint = normalizeEndpoint(mergedOptions.mcpEndpoint);\n\n const moduleId = `mcp-module-${instanceIdCounter++}`;\n const providers = this.createProvidersFromOptions(mergedOptions, moduleId);\n const controllers = this.createControllersFromOptions(mergedOptions);\n return {\n module: McpModule,\n controllers,\n providers,\n exports: [McpRegistryService, McpSseService, McpStreamableHttpService],\n };\n }\n\n /**\n * Asynchronous variant of forRoot. Controllers are NOT auto-registered here because\n * they must be declared synchronously at module definition time. This keeps the\n * API explicit: when using forRootAsync, you are responsible for creating and\n * registering any transport controllers (e.g. via createSseController / createStreamableHttpController).\n *\n * The exposed async options intentionally omit the `transport` property. Transport\n * selection only influences automatic controller creation (which does not occur here)\n * and STDIO auto-start. If you need STDIO with forRootAsync, manually instantiate\n * and bootstrap it (e.g. by importing a module that injects StdioService) or add\n * an explicit provider that sets options.transport before use.\n */\n static forRootAsync(options: McpModuleAsyncOptions): DynamicModule {\n const moduleId = `mcp-module-${instanceIdCounter++}`;\n const asyncProviders = this.createAsyncProviders(options);\n const baseProviders: Provider[] = [\n {\n provide: 'MCP_MODULE_ID',\n useValue: moduleId,\n },\n McpRegistryService,\n McpExecutorService,\n SsePingService,\n McpSseService,\n McpStreamableHttpService,\n StdioService,\n ];\n\n return {\n module: McpModule,\n imports: options.imports ?? [],\n // No automatic controllers in async mode\n controllers: [],\n providers: [\n ...asyncProviders,\n ...baseProviders,\n ...(options.extraProviders ?? []),\n ],\n exports: [McpRegistryService, McpSseService, McpStreamableHttpService],\n };\n }\n\n private static createAsyncProviders(\n options: McpModuleAsyncOptions,\n ): Provider[] {\n if (options.useFactory) {\n return [\n {\n provide: 'MCP_OPTIONS',\n useFactory: async (...args: unknown[]) => {\n const resolved: McpAsyncOptions = await options.useFactory!(\n ...args,\n );\n return this.mergeAndNormalizeAsyncOptions(resolved);\n },\n inject: options.inject ?? [],\n },\n ];\n }\n\n // useClass / useExisting path\n const inject: any[] = [];\n let optionsFactoryProvider: Provider | undefined;\n\n if (options.useExisting || options.useClass) {\n const useExisting = options.useExisting || options.useClass!;\n inject.push(useExisting);\n if (options.useClass) {\n optionsFactoryProvider = {\n provide: options.useClass,\n useClass: options.useClass,\n } as Provider;\n }\n\n return [\n ...(optionsFactoryProvider ? [optionsFactoryProvider] : []),\n {\n provide: 'MCP_OPTIONS',\n useFactory: async (factory: McpOptionsFactory) => {\n const resolved = await factory.createMcpOptions();\n return this.mergeAndNormalizeAsyncOptions(resolved);\n },\n inject,\n },\n ];\n }\n\n throw new Error('Invalid McpModuleAsyncOptions configuration.');\n }\n\n private static mergeAndNormalizeAsyncOptions(\n resolved: McpAsyncOptions,\n ): McpOptions {\n const defaultOptions: Partial<McpOptions> = {\n sseEndpoint: 'sse',\n messagesEndpoint: 'messages',\n mcpEndpoint: 'mcp',\n guards: [],\n decorators: [],\n streamableHttp: {\n enableJsonResponse: true,\n sessionIdGenerator: undefined,\n statelessMode: true,\n },\n sse: {\n pingEnabled: true,\n pingIntervalMs: 30000,\n },\n };\n // Note: transport intentionally omitted\n const merged = { ...defaultOptions, ...resolved } as McpOptions;\n merged.sseEndpoint = normalizeEndpoint(merged.sseEndpoint);\n merged.messagesEndpoint = normalizeEndpoint(merged.messagesEndpoint);\n merged.mcpEndpoint = normalizeEndpoint(merged.mcpEndpoint);\n return merged;\n }\n\n private static createControllersFromOptions(\n options: McpOptions,\n ): Type<any>[] {\n const sseEndpoint = options.sseEndpoint ?? 'sse';\n const messagesEndpoint = options.messagesEndpoint ?? 'messages';\n const mcpEndpoint = options.mcpEndpoint ?? 'mcp';\n const guards = options.guards ?? [];\n const transports = Array.isArray(options.transport)\n ? options.transport\n : [options.transport ?? McpTransportType.SSE];\n const controllers: Type<any>[] = [];\n const decorators = options.decorators ?? [];\n const apiPrefix = options.apiPrefix ?? '';\n\n if (transports.includes(McpTransportType.SSE)) {\n const sseController = createSseController(\n sseEndpoint,\n messagesEndpoint,\n apiPrefix,\n guards,\n decorators,\n );\n controllers.push(sseController);\n }\n\n if (transports.includes(McpTransportType.STREAMABLE_HTTP)) {\n const streamableHttpController = createStreamableHttpController(\n mcpEndpoint,\n apiPrefix,\n guards,\n decorators,\n );\n controllers.push(streamableHttpController);\n }\n\n if (transports.includes(McpTransportType.STDIO)) {\n // STDIO transport is handled by injectable StdioService, no controller\n }\n\n return controllers;\n }\n\n private static createProvidersFromOptions(\n options: McpOptions,\n moduleId: string,\n ): Provider[] {\n const providers: Provider[] = [\n {\n provide: 'MCP_OPTIONS',\n useValue: options,\n },\n {\n provide: 'MCP_MODULE_ID',\n useValue: moduleId,\n },\n McpRegistryService,\n McpExecutorService,\n SsePingService,\n McpSseService,\n McpStreamableHttpService,\n StdioService,\n ];\n\n return providers;\n }\n}\n"]}
1
+ {"version":3,"file":"mcp.module.js","sourceRoot":"","sources":["../../src/mcp/mcp.module.ts"],"names":[],"mappings":"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","sourcesContent":["import { DynamicModule, Module, Provider, Type } from '@nestjs/common';\nimport { DiscoveryModule } from '@nestjs/core';\nimport { McpTransportType } from './interfaces';\nimport type {\n McpOptions,\n McpModuleAsyncOptions,\n McpOptionsFactory,\n McpAsyncOptions,\n} from './interfaces';\nimport { McpExecutorService } from './services/mcp-executor.service';\nimport { McpRegistryService } from './services/mcp-registry.service';\nimport { McpSseService } from './services/mcp-sse.service';\nimport { McpStreamableHttpService } from './services/mcp-streamable-http.service';\nimport { SsePingService } from './services/sse-ping.service';\nimport { ToolAuthorizationService } from './services/tool-authorization.service';\nimport { createSseController } from './transport/sse.controller.factory';\nimport { StdioService } from './transport/stdio.service';\nimport { createStreamableHttpController } from './transport/streamable-http.controller.factory';\nimport { normalizeEndpoint } from './utils/normalize-endpoint';\n\nlet instanceIdCounter = 0;\n\n@Module({\n imports: [DiscoveryModule],\n providers: [McpRegistryService, McpExecutorService, ToolAuthorizationService],\n})\nexport class McpModule {\n /**\n * To avoid import circular dependency issues, we use a marker property.\n */\n readonly __isMcpModule = true;\n\n static forRoot(options: McpOptions): DynamicModule {\n const defaultOptions: Partial<McpOptions> = {\n transport: [\n McpTransportType.SSE,\n McpTransportType.STREAMABLE_HTTP,\n McpTransportType.STDIO,\n ],\n sseEndpoint: 'sse',\n messagesEndpoint: 'messages',\n mcpEndpoint: 'mcp',\n guards: [],\n decorators: [],\n streamableHttp: {\n enableJsonResponse: true,\n sessionIdGenerator: undefined,\n statelessMode: true,\n },\n sse: {\n pingEnabled: true,\n pingIntervalMs: 30000,\n },\n };\n const mergedOptions = { ...defaultOptions, ...options } as McpOptions;\n mergedOptions.sseEndpoint = normalizeEndpoint(mergedOptions.sseEndpoint);\n mergedOptions.messagesEndpoint = normalizeEndpoint(\n mergedOptions.messagesEndpoint,\n );\n mergedOptions.mcpEndpoint = normalizeEndpoint(mergedOptions.mcpEndpoint);\n\n const moduleId = `mcp-module-${instanceIdCounter++}`;\n const providers = this.createProvidersFromOptions(mergedOptions, moduleId);\n const controllers = this.createControllersFromOptions(mergedOptions);\n return {\n module: McpModule,\n controllers,\n providers,\n exports: [McpRegistryService, McpSseService, McpStreamableHttpService],\n };\n }\n\n /**\n * Asynchronous variant of forRoot. Controllers are NOT auto-registered here because\n * they must be declared synchronously at module definition time. This keeps the\n * API explicit: when using forRootAsync, you are responsible for creating and\n * registering any transport controllers (e.g. via createSseController / createStreamableHttpController).\n *\n * The exposed async options intentionally omit the `transport` property. Transport\n * selection only influences automatic controller creation (which does not occur here)\n * and STDIO auto-start. If you need STDIO with forRootAsync, manually instantiate\n * and bootstrap it (e.g. by importing a module that injects StdioService) or add\n * an explicit provider that sets options.transport before use.\n */\n static forRootAsync(options: McpModuleAsyncOptions): DynamicModule {\n const moduleId = `mcp-module-${instanceIdCounter++}`;\n const asyncProviders = this.createAsyncProviders(options);\n const baseProviders: Provider[] = [\n {\n provide: 'MCP_MODULE_ID',\n useValue: moduleId,\n },\n McpRegistryService,\n McpExecutorService,\n ToolAuthorizationService,\n SsePingService,\n McpSseService,\n McpStreamableHttpService,\n StdioService,\n ];\n\n return {\n module: McpModule,\n imports: options.imports ?? [],\n // No automatic controllers in async mode\n controllers: [],\n providers: [\n ...asyncProviders,\n ...baseProviders,\n ...(options.extraProviders ?? []),\n ],\n exports: [McpRegistryService, McpSseService, McpStreamableHttpService],\n };\n }\n\n private static createAsyncProviders(\n options: McpModuleAsyncOptions,\n ): Provider[] {\n if (options.useFactory) {\n return [\n {\n provide: 'MCP_OPTIONS',\n useFactory: async (...args: unknown[]) => {\n const resolved: McpAsyncOptions = await options.useFactory!(\n ...args,\n );\n return this.mergeAndNormalizeAsyncOptions(resolved);\n },\n inject: options.inject ?? [],\n },\n ];\n }\n\n // useClass / useExisting path\n const inject: any[] = [];\n let optionsFactoryProvider: Provider | undefined;\n\n if (options.useExisting || options.useClass) {\n const useExisting = options.useExisting || options.useClass!;\n inject.push(useExisting);\n if (options.useClass) {\n optionsFactoryProvider = {\n provide: options.useClass,\n useClass: options.useClass,\n } as Provider;\n }\n\n return [\n ...(optionsFactoryProvider ? [optionsFactoryProvider] : []),\n {\n provide: 'MCP_OPTIONS',\n useFactory: async (factory: McpOptionsFactory) => {\n const resolved = await factory.createMcpOptions();\n return this.mergeAndNormalizeAsyncOptions(resolved);\n },\n inject,\n },\n ];\n }\n\n throw new Error('Invalid McpModuleAsyncOptions configuration.');\n }\n\n private static mergeAndNormalizeAsyncOptions(\n resolved: McpAsyncOptions,\n ): McpOptions {\n const defaultOptions: Partial<McpOptions> = {\n sseEndpoint: 'sse',\n messagesEndpoint: 'messages',\n mcpEndpoint: 'mcp',\n guards: [],\n decorators: [],\n streamableHttp: {\n enableJsonResponse: true,\n sessionIdGenerator: undefined,\n statelessMode: true,\n },\n sse: {\n pingEnabled: true,\n pingIntervalMs: 30000,\n },\n };\n // Note: transport intentionally omitted\n const merged = { ...defaultOptions, ...resolved } as McpOptions;\n merged.sseEndpoint = normalizeEndpoint(merged.sseEndpoint);\n merged.messagesEndpoint = normalizeEndpoint(merged.messagesEndpoint);\n merged.mcpEndpoint = normalizeEndpoint(merged.mcpEndpoint);\n return merged;\n }\n\n private static createControllersFromOptions(\n options: McpOptions,\n ): Type<any>[] {\n const sseEndpoint = options.sseEndpoint ?? 'sse';\n const messagesEndpoint = options.messagesEndpoint ?? 'messages';\n const mcpEndpoint = options.mcpEndpoint ?? 'mcp';\n const guards = options.guards ?? [];\n const transports = Array.isArray(options.transport)\n ? options.transport\n : [options.transport ?? McpTransportType.SSE];\n const controllers: Type<any>[] = [];\n const decorators = options.decorators ?? [];\n const apiPrefix = options.apiPrefix ?? '';\n\n if (transports.includes(McpTransportType.SSE)) {\n const sseController = createSseController(\n sseEndpoint,\n messagesEndpoint,\n apiPrefix,\n guards,\n decorators,\n );\n controllers.push(sseController);\n }\n\n if (transports.includes(McpTransportType.STREAMABLE_HTTP)) {\n const streamableHttpController = createStreamableHttpController(\n mcpEndpoint,\n apiPrefix,\n guards,\n decorators,\n );\n controllers.push(streamableHttpController);\n }\n\n if (transports.includes(McpTransportType.STDIO)) {\n // STDIO transport is handled by injectable StdioService, no controller\n }\n\n return controllers;\n }\n\n private static createProvidersFromOptions(\n options: McpOptions,\n moduleId: string,\n ): Provider[] {\n const providers: Provider[] = [\n {\n provide: 'MCP_OPTIONS',\n useValue: options,\n },\n {\n provide: 'MCP_MODULE_ID',\n useValue: moduleId,\n },\n McpRegistryService,\n McpExecutorService,\n ToolAuthorizationService,\n SsePingService,\n McpSseService,\n McpStreamableHttpService,\n StdioService,\n ];\n\n return providers;\n }\n}\n"]}
@@ -3,9 +3,14 @@ import { ModuleRef } from '@nestjs/core';
3
3
  import { McpRegistryService } from '../mcp-registry.service';
4
4
  import { McpHandlerBase } from './mcp-handler.base';
5
5
  import { HttpRequest } from '../../interfaces/http-adapter.interface';
6
+ import { ToolAuthorizationService } from '../tool-authorization.service';
7
+ import { McpOptions } from '../../interfaces/mcp-options.interface';
6
8
  export declare class McpToolsHandler extends McpHandlerBase {
7
9
  private readonly mcpModuleId;
8
- constructor(moduleRef: ModuleRef, registry: McpRegistryService, mcpModuleId: string);
10
+ private readonly options;
11
+ private readonly authService;
12
+ private readonly moduleHasGuards;
13
+ constructor(moduleRef: ModuleRef, registry: McpRegistryService, mcpModuleId: string, options: McpOptions, authService: ToolAuthorizationService);
9
14
  private buildDefaultContentBlock;
10
15
  private formatToolResult;
11
16
  registerHandlers(mcpServer: McpServer, httpRequest: HttpRequest): void;
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-tools.handler.d.ts","sourceRoot":"","sources":["../../../../src/mcp/services/handlers/mcp-tools.handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAQpE,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,EAAE,WAAW,EAAE,MAAM,yCAAyC,CAAC;AAGtE,qBACa,eAAgB,SAAQ,cAAc;IAItB,OAAO,CAAC,QAAQ,CAAC,WAAW;gBAFrD,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,kBAAkB,EACc,WAAW,EAAE,MAAM;IAK/D,OAAO,CAAC,wBAAwB;IAShC,OAAO,CAAC,gBAAgB;IAwBxB,gBAAgB,CAAC,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,WAAW;CA4HhE"}
1
+ {"version":3,"file":"mcp-tools.handler.d.ts","sourceRoot":"","sources":["../../../../src/mcp/services/handlers/mcp-tools.handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAQpE,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,EAAE,WAAW,EAAE,MAAM,yCAAyC,CAAC;AAEtE,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AACzE,OAAO,EAAE,UAAU,EAAE,MAAM,wCAAwC,CAAC;AAEpE,qBACa,eAAgB,SAAQ,cAAc;IAMtB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO;IAC/C,OAAO,CAAC,QAAQ,CAAC,WAAW;IAP9B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAU;gBAGxC,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,kBAAkB,EACc,WAAW,EAAE,MAAM,EACrB,OAAO,EAAE,UAAU,EAC1C,WAAW,EAAE,wBAAwB;IAOxD,OAAO,CAAC,wBAAwB;IAShC,OAAO,CAAC,gBAAgB;IAwBxB,gBAAgB,CAAC,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,WAAW;CAmKhE"}
@@ -20,16 +20,21 @@ const core_1 = require("@nestjs/core");
20
20
  const zod_to_json_schema_1 = require("zod-to-json-schema");
21
21
  const mcp_registry_service_1 = require("../mcp-registry.service");
22
22
  const mcp_handler_base_1 = require("./mcp-handler.base");
23
+ const tool_authorization_service_1 = require("../tool-authorization.service");
23
24
  let McpToolsHandler = McpToolsHandler_1 = class McpToolsHandler extends mcp_handler_base_1.McpHandlerBase {
24
- constructor(moduleRef, registry, mcpModuleId) {
25
+ constructor(moduleRef, registry, mcpModuleId, options, authService) {
25
26
  super(moduleRef, registry, McpToolsHandler_1.name);
26
27
  this.mcpModuleId = mcpModuleId;
28
+ this.options = options;
29
+ this.authService = authService;
30
+ this.moduleHasGuards =
31
+ this.options.guards !== undefined && this.options.guards.length > 0;
27
32
  }
28
33
  buildDefaultContentBlock(result) {
29
34
  return [
30
35
  {
31
36
  type: 'text',
32
- text: JSON.stringify(result, null, 2),
37
+ text: JSON.stringify(result),
33
38
  },
34
39
  ];
35
40
  }
@@ -57,13 +62,25 @@ let McpToolsHandler = McpToolsHandler_1 = class McpToolsHandler extends mcp_hand
57
62
  return;
58
63
  }
59
64
  mcpServer.server.setRequestHandler(types_js_1.ListToolsRequestSchema, () => {
60
- const tools = this.registry.getTools(this.mcpModuleId).map((tool) => {
65
+ const user = httpRequest.raw
66
+ ? httpRequest.raw.user
67
+ : undefined;
68
+ const allTools = this.registry.getTools(this.mcpModuleId);
69
+ const effectiveModuleHasGuards = httpRequest.raw
70
+ ? this.moduleHasGuards
71
+ : false;
72
+ const authorizedTools = allTools.filter((tool) => this.authService.canAccessTool(user, tool, effectiveModuleHasGuards));
73
+ const tools = authorizedTools.map((tool) => {
61
74
  const toolSchema = {
62
75
  name: tool.metadata.name,
63
76
  description: tool.metadata.description,
64
77
  annotations: tool.metadata.annotations,
65
78
  _meta: tool.metadata._meta,
66
79
  };
80
+ const securitySchemes = this.authService.generateSecuritySchemes(tool, effectiveModuleHasGuards);
81
+ if (securitySchemes.length > 0) {
82
+ toolSchema['securitySchemes'] = securitySchemes;
83
+ }
67
84
  if (tool.metadata.parameters) {
68
85
  toolSchema['inputSchema'] = (0, zod_to_json_schema_1.zodToJsonSchema)(tool.metadata.parameters);
69
86
  }
@@ -87,6 +104,13 @@ let McpToolsHandler = McpToolsHandler_1 = class McpToolsHandler extends mcp_hand
87
104
  if (!toolInfo) {
88
105
  throw new types_js_1.McpError(types_js_1.ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`);
89
106
  }
107
+ const user = httpRequest.raw
108
+ ? httpRequest.raw.user
109
+ : undefined;
110
+ const effectiveModuleHasGuards = httpRequest.raw
111
+ ? this.moduleHasGuards
112
+ : false;
113
+ this.authService.validateToolAccess(user, toolInfo, effectiveModuleHasGuards);
90
114
  try {
91
115
  if (toolInfo.metadata.parameters) {
92
116
  const validation = toolInfo.metadata.parameters.safeParse(request.params.arguments || {});
@@ -124,7 +148,8 @@ exports.McpToolsHandler = McpToolsHandler;
124
148
  exports.McpToolsHandler = McpToolsHandler = McpToolsHandler_1 = __decorate([
125
149
  (0, common_1.Injectable)({ scope: common_1.Scope.REQUEST }),
126
150
  __param(2, (0, common_1.Inject)('MCP_MODULE_ID')),
151
+ __param(3, (0, common_1.Inject)('MCP_OPTIONS')),
127
152
  __metadata("design:paramtypes", [core_1.ModuleRef,
128
- mcp_registry_service_1.McpRegistryService, String])
153
+ mcp_registry_service_1.McpRegistryService, String, Object, tool_authorization_service_1.ToolAuthorizationService])
129
154
  ], McpToolsHandler);
130
155
  //# sourceMappingURL=mcp-tools.handler.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-tools.handler.js","sourceRoot":"","sources":["../../../../src/mcp/services/handlers/mcp-tools.handler.ts"],"names":[],"mappings":"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","sourcesContent":["import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport {\n CallToolRequestSchema,\n ErrorCode,\n ListToolsRequestSchema,\n McpError,\n} from '@modelcontextprotocol/sdk/types.js';\nimport { Inject, Injectable, Scope } from '@nestjs/common';\nimport { ContextIdFactory, ModuleRef } from '@nestjs/core';\nimport { zodToJsonSchema } from 'zod-to-json-schema';\nimport { McpRegistryService } from '../mcp-registry.service';\nimport { McpHandlerBase } from './mcp-handler.base';\nimport { ZodTypeAny } from 'zod';\nimport { HttpRequest } from '../../interfaces/http-adapter.interface';\nimport { McpRequestWithUser } from 'src/authz';\n\n@Injectable({ scope: Scope.REQUEST })\nexport class McpToolsHandler extends McpHandlerBase {\n constructor(\n moduleRef: ModuleRef,\n registry: McpRegistryService,\n @Inject('MCP_MODULE_ID') private readonly mcpModuleId: string,\n ) {\n super(moduleRef, registry, McpToolsHandler.name);\n }\n\n private buildDefaultContentBlock(result: any) {\n return [\n {\n type: 'text',\n text: JSON.stringify(result, null, 2),\n },\n ];\n }\n\n private formatToolResult(result: any, outputSchema?: ZodTypeAny): any {\n if (result && typeof result === 'object' && Array.isArray(result.content)) {\n return result;\n }\n\n if (outputSchema) {\n const validation = outputSchema.safeParse(result);\n if (!validation.success) {\n throw new McpError(\n ErrorCode.InternalError,\n `Tool result does not match outputSchema: ${validation.error.message}`,\n );\n }\n return {\n structuredContent: result,\n content: this.buildDefaultContentBlock(result),\n };\n }\n\n return {\n content: this.buildDefaultContentBlock(result),\n };\n }\n\n registerHandlers(mcpServer: McpServer, httpRequest: HttpRequest) {\n if (this.registry.getTools(this.mcpModuleId).length === 0) {\n this.logger.debug('No tools registered, skipping tool handlers');\n return;\n }\n\n mcpServer.server.setRequestHandler(ListToolsRequestSchema, () => {\n const tools = this.registry.getTools(this.mcpModuleId).map((tool) => {\n // Create base schema\n const toolSchema = {\n name: tool.metadata.name,\n description: tool.metadata.description,\n annotations: tool.metadata.annotations,\n _meta: tool.metadata._meta,\n };\n\n // Add input schema if defined\n if (tool.metadata.parameters) {\n toolSchema['inputSchema'] = zodToJsonSchema(tool.metadata.parameters);\n }\n\n // Add output schema if defined, ensuring it has type: 'object'\n if (tool.metadata.outputSchema) {\n const outputSchema = zodToJsonSchema(tool.metadata.outputSchema);\n\n // Create a new object that explicitly includes type: 'object'\n const jsonSchema = {\n ...outputSchema,\n type: 'object',\n };\n\n toolSchema['outputSchema'] = jsonSchema;\n }\n\n return toolSchema;\n });\n\n return {\n tools,\n };\n });\n\n mcpServer.server.setRequestHandler(\n CallToolRequestSchema,\n async (request) => {\n this.logger.debug('CallToolRequestSchema is being called');\n\n const toolInfo = this.registry.findTool(\n this.mcpModuleId,\n request.params.name,\n );\n\n if (!toolInfo) {\n throw new McpError(\n ErrorCode.MethodNotFound,\n `Unknown tool: ${request.params.name}`,\n );\n }\n\n try {\n // Validate input parameters against the tool's schema\n if (toolInfo.metadata.parameters) {\n const validation = toolInfo.metadata.parameters.safeParse(\n request.params.arguments || {},\n );\n if (!validation.success) {\n throw new McpError(\n ErrorCode.InvalidParams,\n `Invalid parameters: ${validation.error.message}`,\n );\n }\n // Use validated arguments to ensure defaults and transformations are applied\n request.params.arguments = validation.data;\n }\n\n const contextId = ContextIdFactory.getByRequest(httpRequest);\n this.moduleRef.registerRequestByContextId(httpRequest, contextId);\n\n const toolInstance = await this.moduleRef.resolve(\n toolInfo.providerClass,\n contextId,\n { strict: false },\n );\n\n const context = this.createContext(mcpServer, request);\n\n if (!toolInstance) {\n throw new McpError(\n ErrorCode.MethodNotFound,\n `Unknown tool: ${request.params.name}`,\n );\n }\n\n const result = await toolInstance[toolInfo.methodName].call(\n toolInstance,\n request.params.arguments,\n context,\n httpRequest.raw as McpRequestWithUser,\n );\n\n const transformedResult = this.formatToolResult(\n result,\n toolInfo.metadata.outputSchema,\n );\n\n this.logger.debug(transformedResult, 'CallToolRequestSchema result');\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return transformedResult;\n } catch (error) {\n this.logger.error(error);\n // Re-throw McpErrors (like validation errors) so they are handled by the MCP protocol layer\n if (error instanceof McpError) {\n throw error;\n }\n // For other errors, return formatted error response\n return {\n content: [{ type: 'text', text: error.message }],\n isError: true,\n };\n }\n },\n );\n }\n}\n"]}
1
+ {"version":3,"file":"mcp-tools.handler.js","sourceRoot":"","sources":["../../../../src/mcp/services/handlers/mcp-tools.handler.ts"],"names":[],"mappings":"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","sourcesContent":["import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport {\n CallToolRequestSchema,\n ErrorCode,\n ListToolsRequestSchema,\n McpError,\n} from '@modelcontextprotocol/sdk/types.js';\nimport { Inject, Injectable, Scope } from '@nestjs/common';\nimport { ContextIdFactory, ModuleRef } from '@nestjs/core';\nimport { zodToJsonSchema } from 'zod-to-json-schema';\nimport { McpRegistryService } from '../mcp-registry.service';\nimport { McpHandlerBase } from './mcp-handler.base';\nimport { ZodTypeAny } from 'zod';\nimport { HttpRequest } from '../../interfaces/http-adapter.interface';\nimport { McpRequestWithUser } from 'src/authz';\nimport { ToolAuthorizationService } from '../tool-authorization.service';\nimport { McpOptions } from '../../interfaces/mcp-options.interface';\n\n@Injectable({ scope: Scope.REQUEST })\nexport class McpToolsHandler extends McpHandlerBase {\n private readonly moduleHasGuards: boolean;\n\n constructor(\n moduleRef: ModuleRef,\n registry: McpRegistryService,\n @Inject('MCP_MODULE_ID') private readonly mcpModuleId: string,\n @Inject('MCP_OPTIONS') private readonly options: McpOptions,\n private readonly authService: ToolAuthorizationService,\n ) {\n super(moduleRef, registry, McpToolsHandler.name);\n this.moduleHasGuards =\n this.options.guards !== undefined && this.options.guards.length > 0;\n }\n\n private buildDefaultContentBlock(result: any) {\n return [\n {\n type: 'text',\n text: JSON.stringify(result),\n },\n ];\n }\n\n private formatToolResult(result: any, outputSchema?: ZodTypeAny): any {\n if (result && typeof result === 'object' && Array.isArray(result.content)) {\n return result;\n }\n\n if (outputSchema) {\n const validation = outputSchema.safeParse(result);\n if (!validation.success) {\n throw new McpError(\n ErrorCode.InternalError,\n `Tool result does not match outputSchema: ${validation.error.message}`,\n );\n }\n return {\n structuredContent: result,\n content: this.buildDefaultContentBlock(result),\n };\n }\n\n return {\n content: this.buildDefaultContentBlock(result),\n };\n }\n\n registerHandlers(mcpServer: McpServer, httpRequest: HttpRequest) {\n if (this.registry.getTools(this.mcpModuleId).length === 0) {\n this.logger.debug('No tools registered, skipping tool handlers');\n return;\n }\n\n mcpServer.server.setRequestHandler(ListToolsRequestSchema, () => {\n // Extract user from request (may be undefined if not authenticated or STDIO)\n // For STDIO transport, httpRequest.raw is undefined, so bypass auth entirely\n const user = httpRequest.raw\n ? (httpRequest.raw as McpRequestWithUser).user\n : undefined;\n\n // Get all tools and filter based on user permissions\n // STDIO: If no httpRequest.raw, disable guards (local dev mode)\n const allTools = this.registry.getTools(this.mcpModuleId);\n const effectiveModuleHasGuards = httpRequest.raw\n ? this.moduleHasGuards\n : false;\n const authorizedTools = allTools.filter((tool) =>\n this.authService.canAccessTool(user, tool, effectiveModuleHasGuards),\n );\n\n const tools = authorizedTools.map((tool) => {\n // Create base schema\n const toolSchema = {\n name: tool.metadata.name,\n description: tool.metadata.description,\n annotations: tool.metadata.annotations,\n _meta: tool.metadata._meta,\n };\n\n // Add security schemes\n const securitySchemes = this.authService.generateSecuritySchemes(\n tool,\n effectiveModuleHasGuards,\n );\n if (securitySchemes.length > 0) {\n toolSchema['securitySchemes'] = securitySchemes;\n }\n\n // Add input schema if defined\n if (tool.metadata.parameters) {\n toolSchema['inputSchema'] = zodToJsonSchema(tool.metadata.parameters);\n }\n\n // Add output schema if defined, ensuring it has type: 'object'\n if (tool.metadata.outputSchema) {\n const outputSchema = zodToJsonSchema(tool.metadata.outputSchema);\n\n // Create a new object that explicitly includes type: 'object'\n const jsonSchema = {\n ...outputSchema,\n type: 'object',\n };\n\n toolSchema['outputSchema'] = jsonSchema;\n }\n\n return toolSchema;\n });\n\n return {\n tools,\n };\n });\n\n mcpServer.server.setRequestHandler(\n CallToolRequestSchema,\n async (request) => {\n this.logger.debug('CallToolRequestSchema is being called');\n\n const toolInfo = this.registry.findTool(\n this.mcpModuleId,\n request.params.name,\n );\n\n if (!toolInfo) {\n throw new McpError(\n ErrorCode.MethodNotFound,\n `Unknown tool: ${request.params.name}`,\n );\n }\n\n // Validate authorization before execution\n // For STDIO transport, bypass auth entirely (local dev mode)\n const user = httpRequest.raw\n ? (httpRequest.raw as McpRequestWithUser).user\n : undefined;\n const effectiveModuleHasGuards = httpRequest.raw\n ? this.moduleHasGuards\n : false;\n this.authService.validateToolAccess(\n user,\n toolInfo,\n effectiveModuleHasGuards,\n );\n\n try {\n // Validate input parameters against the tool's schema\n if (toolInfo.metadata.parameters) {\n const validation = toolInfo.metadata.parameters.safeParse(\n request.params.arguments || {},\n );\n if (!validation.success) {\n throw new McpError(\n ErrorCode.InvalidParams,\n `Invalid parameters: ${validation.error.message}`,\n );\n }\n // Use validated arguments to ensure defaults and transformations are applied\n request.params.arguments = validation.data;\n }\n\n const contextId = ContextIdFactory.getByRequest(httpRequest);\n this.moduleRef.registerRequestByContextId(httpRequest, contextId);\n\n const toolInstance = await this.moduleRef.resolve(\n toolInfo.providerClass,\n contextId,\n { strict: false },\n );\n\n const context = this.createContext(mcpServer, request);\n\n if (!toolInstance) {\n throw new McpError(\n ErrorCode.MethodNotFound,\n `Unknown tool: ${request.params.name}`,\n );\n }\n\n const result = await toolInstance[toolInfo.methodName].call(\n toolInstance,\n request.params.arguments,\n context,\n httpRequest.raw as McpRequestWithUser,\n );\n\n const transformedResult = this.formatToolResult(\n result,\n toolInfo.metadata.outputSchema,\n );\n\n this.logger.debug(transformedResult, 'CallToolRequestSchema result');\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return transformedResult;\n } catch (error) {\n this.logger.error(error);\n // Re-throw McpErrors (like validation errors) so they are handled by the MCP protocol layer\n if (error instanceof McpError) {\n throw error;\n }\n // For other errors, return formatted error response\n return {\n content: [{ type: 'text', text: error.message }],\n isError: true,\n };\n }\n },\n );\n }\n}\n"]}