@rekog/mcp-nest 1.7.0-alpha.0 → 1.7.0-alpha.2

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 (104) hide show
  1. package/dist/authz/guards/jwt-auth.guard.d.ts +13 -0
  2. package/dist/authz/guards/jwt-auth.guard.d.ts.map +1 -0
  3. package/dist/authz/guards/jwt-auth.guard.js +46 -0
  4. package/dist/authz/guards/jwt-auth.guard.js.map +1 -0
  5. package/dist/authz/index.d.ts +8 -1
  6. package/dist/authz/index.d.ts.map +1 -1
  7. package/dist/authz/index.js +22 -0
  8. package/dist/authz/index.js.map +1 -1
  9. package/dist/authz/interfaces/index.d.ts +3 -0
  10. package/dist/authz/interfaces/index.d.ts.map +1 -0
  11. package/dist/authz/interfaces/index.js +19 -0
  12. package/dist/authz/interfaces/index.js.map +1 -0
  13. package/dist/authz/mcp-oauth.controller.d.ts +53 -0
  14. package/dist/authz/mcp-oauth.controller.d.ts.map +1 -0
  15. package/dist/authz/mcp-oauth.controller.js +305 -0
  16. package/dist/authz/mcp-oauth.controller.js.map +1 -0
  17. package/dist/authz/mcp-oauth.module.d.ts +11 -0
  18. package/dist/authz/mcp-oauth.module.d.ts.map +1 -0
  19. package/dist/authz/mcp-oauth.module.js +173 -0
  20. package/dist/authz/mcp-oauth.module.js.map +1 -0
  21. package/dist/authz/providers/github.provider.d.ts +3 -0
  22. package/dist/authz/providers/github.provider.d.ts.map +1 -0
  23. package/dist/authz/providers/github.provider.js +23 -0
  24. package/dist/authz/providers/github.provider.js.map +1 -0
  25. package/dist/authz/providers/google.provider.d.ts +3 -0
  26. package/dist/authz/providers/google.provider.d.ts.map +1 -0
  27. package/dist/authz/providers/google.provider.js +24 -0
  28. package/dist/authz/providers/google.provider.js.map +1 -0
  29. package/dist/authz/providers/oauth-provider.interface.d.ts +75 -0
  30. package/dist/authz/providers/oauth-provider.interface.d.ts.map +1 -0
  31. package/dist/authz/providers/oauth-provider.interface.js +3 -0
  32. package/dist/authz/providers/oauth-provider.interface.js.map +1 -0
  33. package/dist/authz/services/client.service.d.ts +10 -0
  34. package/dist/authz/services/client.service.d.ts.map +1 -0
  35. package/dist/authz/services/client.service.js +84 -0
  36. package/dist/authz/services/client.service.js.map +1 -0
  37. package/dist/authz/services/jwt-token.service.d.ts +29 -0
  38. package/dist/authz/services/jwt-token.service.d.ts.map +1 -0
  39. package/dist/authz/services/jwt-token.service.js +103 -0
  40. package/dist/authz/services/jwt-token.service.js.map +1 -0
  41. package/dist/authz/services/oauth-strategy.service.d.ts +11 -0
  42. package/dist/authz/services/oauth-strategy.service.d.ts.map +1 -0
  43. package/dist/authz/services/oauth-strategy.service.js +64 -0
  44. package/dist/authz/services/oauth-strategy.service.js.map +1 -0
  45. package/dist/authz/stores/memory-store.service.d.ts +19 -0
  46. package/dist/authz/stores/memory-store.service.d.ts.map +1 -0
  47. package/dist/authz/stores/memory-store.service.js +84 -0
  48. package/dist/authz/stores/memory-store.service.js.map +1 -0
  49. package/dist/authz/stores/memory-store.service.spec.d.ts +2 -0
  50. package/dist/authz/stores/memory-store.service.spec.d.ts.map +1 -0
  51. package/dist/authz/stores/memory-store.service.spec.js +195 -0
  52. package/dist/authz/stores/memory-store.service.spec.js.map +1 -0
  53. package/dist/authz/stores/oauth-store.interface.d.ts +52 -0
  54. package/dist/authz/stores/oauth-store.interface.d.ts.map +1 -0
  55. package/dist/authz/stores/oauth-store.interface.js +3 -0
  56. package/dist/authz/stores/oauth-store.interface.js.map +1 -0
  57. package/dist/authz/stores/sqlite/sqlite-store.service.d.ts +18 -0
  58. package/dist/authz/stores/sqlite/sqlite-store.service.d.ts.map +1 -0
  59. package/dist/authz/stores/sqlite/sqlite-store.service.js +206 -0
  60. package/dist/authz/stores/sqlite/sqlite-store.service.js.map +1 -0
  61. package/dist/authz/stores/typeorm/entities/authorization-code.entity.d.ts +13 -0
  62. package/dist/authz/stores/typeorm/entities/authorization-code.entity.d.ts.map +1 -0
  63. package/dist/authz/stores/typeorm/entities/authorization-code.entity.js +60 -0
  64. package/dist/authz/stores/typeorm/entities/authorization-code.entity.js.map +1 -0
  65. package/dist/authz/stores/typeorm/entities/index.d.ts +4 -0
  66. package/dist/authz/stores/typeorm/entities/index.d.ts.map +1 -0
  67. package/dist/authz/stores/typeorm/entities/index.js +10 -0
  68. package/dist/authz/stores/typeorm/entities/index.js.map +1 -0
  69. package/dist/authz/stores/typeorm/entities/oauth-client.entity.d.ts +16 -0
  70. package/dist/authz/stores/typeorm/entities/oauth-client.entity.d.ts.map +1 -0
  71. package/dist/authz/stores/typeorm/entities/oauth-client.entity.js +72 -0
  72. package/dist/authz/stores/typeorm/entities/oauth-client.entity.js.map +1 -0
  73. package/dist/authz/stores/typeorm/entities/oauth-session.entity.d.ts +13 -0
  74. package/dist/authz/stores/typeorm/entities/oauth-session.entity.d.ts.map +1 -0
  75. package/dist/authz/stores/typeorm/entities/oauth-session.entity.js +60 -0
  76. package/dist/authz/stores/typeorm/entities/oauth-session.entity.js.map +1 -0
  77. package/dist/authz/stores/typeorm/typeorm-store.service.d.ts +21 -0
  78. package/dist/authz/stores/typeorm/typeorm-store.service.d.ts.map +1 -0
  79. package/dist/authz/stores/typeorm/typeorm-store.service.js +87 -0
  80. package/dist/authz/stores/typeorm/typeorm-store.service.js.map +1 -0
  81. package/dist/mcp/services/handlers/mcp-tools.handler.d.ts.map +1 -1
  82. package/dist/mcp/services/handlers/mcp-tools.handler.js.map +1 -1
  83. package/package.json +17 -2
  84. package/src/authz/guards/jwt-auth.guard.ts +45 -0
  85. package/src/authz/index.ts +8 -1
  86. package/src/authz/mcp-oauth.controller.ts +394 -0
  87. package/src/authz/mcp-oauth.module.ts +235 -0
  88. package/src/authz/providers/github.provider.ts +24 -0
  89. package/src/authz/providers/google.provider.ts +22 -0
  90. package/src/authz/providers/oauth-provider.interface.ts +102 -0
  91. package/src/authz/services/client.service.ts +110 -0
  92. package/src/authz/services/jwt-token.service.ts +127 -0
  93. package/src/authz/services/oauth-strategy.service.ts +55 -0
  94. package/src/authz/stores/memory-store.service.spec.ts +240 -0
  95. package/src/authz/stores/memory-store.service.ts +100 -0
  96. package/src/authz/stores/oauth-store.interface.ts +105 -0
  97. package/src/authz/stores/sqlite/sqlite-store.service.ts +279 -0
  98. package/src/authz/stores/typeorm/README.md +133 -0
  99. package/src/authz/stores/typeorm/entities/authorization-code.entity.ts +34 -0
  100. package/src/authz/stores/typeorm/entities/index.ts +3 -0
  101. package/src/authz/stores/typeorm/entities/oauth-client.entity.ts +49 -0
  102. package/src/authz/stores/typeorm/entities/oauth-session.entity.ts +34 -0
  103. package/src/authz/stores/typeorm/typeorm-store.service.ts +98 -0
  104. package/src/mcp/services/handlers/mcp-tools.handler.ts +5 -2
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.TypeOrmStore = void 0;
16
+ const common_1 = require("@nestjs/common");
17
+ const typeorm_1 = require("@nestjs/typeorm");
18
+ const typeorm_2 = require("typeorm");
19
+ const crypto_1 = require("crypto");
20
+ const entities_1 = require("./entities");
21
+ let TypeOrmStore = class TypeOrmStore {
22
+ constructor(clientRepository, authCodeRepository, sessionRepository) {
23
+ this.clientRepository = clientRepository;
24
+ this.authCodeRepository = authCodeRepository;
25
+ this.sessionRepository = sessionRepository;
26
+ }
27
+ async storeClient(client) {
28
+ const savedClient = await this.clientRepository.save(client);
29
+ return savedClient;
30
+ }
31
+ async getClient(client_id) {
32
+ return ((await this.clientRepository.findOne({ where: { client_id } })) ??
33
+ undefined);
34
+ }
35
+ async findClient(client_name) {
36
+ return ((await this.clientRepository.findOne({ where: { client_name } })) ??
37
+ undefined);
38
+ }
39
+ async storeAuthCode(code) {
40
+ await this.authCodeRepository.save(code);
41
+ }
42
+ async getAuthCode(code) {
43
+ const authCode = await this.authCodeRepository.findOne({ where: { code } });
44
+ if (authCode && authCode.expires_at < Date.now()) {
45
+ await this.authCodeRepository.delete({ code });
46
+ return undefined;
47
+ }
48
+ return authCode ?? undefined;
49
+ }
50
+ async removeAuthCode(code) {
51
+ await this.authCodeRepository.delete({ code });
52
+ }
53
+ async storeOAuthSession(sessionId, session) {
54
+ await this.sessionRepository.save({ ...session, sessionId });
55
+ }
56
+ async getOAuthSession(sessionId) {
57
+ const session = await this.sessionRepository.findOne({
58
+ where: { sessionId },
59
+ });
60
+ if (session && session.expiresAt < Date.now()) {
61
+ await this.sessionRepository.delete({ sessionId });
62
+ return undefined;
63
+ }
64
+ return session ?? undefined;
65
+ }
66
+ async removeOAuthSession(sessionId) {
67
+ await this.sessionRepository.delete({ sessionId });
68
+ }
69
+ generateClientId(client) {
70
+ const normalizedName = client.client_name
71
+ .toLowerCase()
72
+ .replace(/[^a-z0-9]/g, '');
73
+ const salt = (0, crypto_1.randomBytes)(4).toString('hex');
74
+ return `${normalizedName}_${salt}`;
75
+ }
76
+ };
77
+ exports.TypeOrmStore = TypeOrmStore;
78
+ exports.TypeOrmStore = TypeOrmStore = __decorate([
79
+ (0, common_1.Injectable)(),
80
+ __param(0, (0, typeorm_1.InjectRepository)(entities_1.OAuthClientEntity)),
81
+ __param(1, (0, typeorm_1.InjectRepository)(entities_1.AuthorizationCodeEntity)),
82
+ __param(2, (0, typeorm_1.InjectRepository)(entities_1.OAuthSessionEntity)),
83
+ __metadata("design:paramtypes", [typeorm_2.Repository,
84
+ typeorm_2.Repository,
85
+ typeorm_2.Repository])
86
+ ], TypeOrmStore);
87
+ //# sourceMappingURL=typeorm-store.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typeorm-store.service.js","sourceRoot":"","sources":["../../../../src/authz/stores/typeorm/typeorm-store.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Injectable } from '@nestjs/common';\nimport { InjectRepository } from '@nestjs/typeorm';\nimport { Repository } from 'typeorm';\nimport { randomBytes } from 'crypto';\nimport {\n OAuthClientEntity,\n AuthorizationCodeEntity,\n OAuthSessionEntity,\n} from './entities';\nimport { OAuthSession } from '../../providers/oauth-provider.interface';\nimport {\n AuthorizationCode,\n IOAuthStore,\n OAuthClient,\n} from '../oauth-store.interface';\n\n@Injectable()\nexport class TypeOrmStore implements IOAuthStore {\n constructor(\n @InjectRepository(OAuthClientEntity)\n private readonly clientRepository: Repository<OAuthClientEntity>,\n @InjectRepository(AuthorizationCodeEntity)\n private readonly authCodeRepository: Repository<AuthorizationCodeEntity>,\n @InjectRepository(OAuthSessionEntity)\n private readonly sessionRepository: Repository<OAuthSessionEntity>,\n ) {}\n\n // Client management\n async storeClient(client: OAuthClient): Promise<OAuthClient> {\n const savedClient = await this.clientRepository.save(client);\n return savedClient;\n }\n\n async getClient(client_id: string): Promise<OAuthClient | undefined> {\n return (\n (await this.clientRepository.findOne({ where: { client_id } })) ??\n undefined\n );\n }\n\n async findClient(client_name: string): Promise<OAuthClient | undefined> {\n return (\n (await this.clientRepository.findOne({ where: { client_name } })) ??\n undefined\n );\n }\n\n // Authorization code management\n async storeAuthCode(code: AuthorizationCode): Promise<void> {\n await this.authCodeRepository.save(code);\n }\n\n async getAuthCode(code: string): Promise<AuthorizationCode | undefined> {\n const authCode = await this.authCodeRepository.findOne({ where: { code } });\n // Check if expired\n if (authCode && authCode.expires_at < Date.now()) {\n await this.authCodeRepository.delete({ code });\n return undefined;\n }\n return authCode ?? undefined;\n }\n\n async removeAuthCode(code: string): Promise<void> {\n await this.authCodeRepository.delete({ code });\n }\n\n // OAuth session management\n async storeOAuthSession(\n sessionId: string,\n session: OAuthSession,\n ): Promise<void> {\n await this.sessionRepository.save({ ...session, sessionId });\n }\n\n async getOAuthSession(sessionId: string): Promise<OAuthSession | undefined> {\n const session = await this.sessionRepository.findOne({\n where: { sessionId },\n });\n if (session && session.expiresAt < Date.now()) {\n await this.sessionRepository.delete({ sessionId });\n return undefined;\n }\n return session ?? undefined;\n }\n\n async removeOAuthSession(sessionId: string): Promise<void> {\n await this.sessionRepository.delete({ sessionId });\n }\n\n generateClientId(client: OAuthClient): string {\n // Create deterministic client ID based on name + random salt\n const normalizedName = client.client_name\n .toLowerCase()\n .replace(/[^a-z0-9]/g, '');\n const salt = randomBytes(4).toString('hex');\n return `${normalizedName}_${salt}`;\n }\n}\n"]}
@@ -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;AAC3D,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAGpD,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,OAAO;CAoG5D"}
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;AAC3D,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAGpD,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,OAAO;CAuG5D"}
@@ -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 { Request } from 'express';\nimport { zodToJsonSchema } from 'zod-to-json-schema';\nimport { McpRegistryService } from '../mcp-registry.service';\nimport { McpHandlerBase } from './mcp-handler.base';\nimport { ZodTypeAny } from 'zod';\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: Request) {\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 };\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 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,\n );\n\n const transformedResult = this.formatToolResult(result, toolInfo.metadata.outputSchema);\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n this.logger.debug(transformedResult, 'CallToolRequestSchema result');\n\n return transformedResult;\n } catch (error) {\n this.logger.error(error);\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 { Request } from 'express';\nimport { zodToJsonSchema } from 'zod-to-json-schema';\nimport { McpRegistryService } from '../mcp-registry.service';\nimport { McpHandlerBase } from './mcp-handler.base';\nimport { ZodTypeAny } from 'zod';\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: Request) {\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 };\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 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,\n );\n\n const transformedResult = this.formatToolResult(\n result,\n toolInfo.metadata.outputSchema,\n );\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n this.logger.debug(transformedResult, 'CallToolRequestSchema result');\n\n return transformedResult;\n } catch (error) {\n this.logger.error(error);\n return {\n content: [{ type: 'text', text: error.message }],\n isError: true,\n };\n }\n },\n );\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rekog/mcp-nest",
3
- "version": "1.7.0-alpha.0",
3
+ "version": "1.7.0-alpha.2",
4
4
  "description": "NestJS module for creating Model Context Protocol (MCP) servers",
5
5
  "main": "dist/index.js",
6
6
  "license": "MIT",
@@ -31,8 +31,12 @@
31
31
  "@modelcontextprotocol/sdk": ">=1.10.0",
32
32
  "@nestjs/common": ">=9.0.0",
33
33
  "@nestjs/core": ">=9.0.0",
34
+ "@nestjs/jwt": "^11.0.0",
35
+ "@nestjs/passport": "^11.0.5",
36
+ "@nestjs/typeorm": ">=9.0.0",
34
37
  "express": ">=4.0.0",
35
38
  "reflect-metadata": ">=0.1.14",
39
+ "typeorm": ">=0.3.25",
36
40
  "zod": "^3.0.0",
37
41
  "zod-to-json-schema": ">=3.23.0"
38
42
  },
@@ -44,9 +48,12 @@
44
48
  "@nestjs/core": "^11.1.1",
45
49
  "@nestjs/platform-express": "^11.1.1",
46
50
  "@nestjs/testing": "^11.1.1",
51
+ "@types/better-sqlite3": "^7.6.13",
52
+ "@types/cookie-parser": "^1.4.9",
47
53
  "@types/express": "^5.0.2",
48
54
  "@types/jest": "^29.5.14",
49
55
  "@types/node": "^22.10.10",
56
+ "@types/passport-google-oauth20": "^2.0.16",
50
57
  "@types/supertest": "^6.0.3",
51
58
  "eslint": "^9.18.0",
52
59
  "eslint-config-prettier": "^10.0.1",
@@ -60,6 +67,14 @@
60
67
  "typescript-eslint": "^8.20.0"
61
68
  },
62
69
  "dependencies": {
63
- "path-to-regexp": "^8.2.0"
70
+ "@nestjs/config": "^4.0.2",
71
+ "better-sqlite3": "11.10.0",
72
+ "cookie-parser": "^1.4.7",
73
+ "passport": "^0.7.0",
74
+ "passport-github": "^1.1.0",
75
+ "passport-google-oauth20": "^2.0.0",
76
+ "path-to-regexp": "^8.2.0",
77
+ "rxjs": "^7.8.2",
78
+ "sqlite3": "^5.1.7"
64
79
  }
65
80
  }
@@ -0,0 +1,45 @@
1
+ import {
2
+ Injectable,
3
+ CanActivate,
4
+ ExecutionContext,
5
+ UnauthorizedException,
6
+ } from '@nestjs/common';
7
+ import { Request } from 'express';
8
+ import { JwtPayload, JwtTokenService } from '../services/jwt-token.service';
9
+
10
+ export interface AuthenticatedRequest extends Request {
11
+ user: JwtPayload;
12
+ }
13
+
14
+ @Injectable()
15
+ export class JwtAuthGuard implements CanActivate {
16
+ constructor(private readonly jwtTokenService: JwtTokenService) {}
17
+
18
+ canActivate(context: ExecutionContext): boolean {
19
+ const request = context.switchToHttp().getRequest<AuthenticatedRequest>();
20
+ const token = this.extractTokenFromHeader(request);
21
+
22
+ if (!token) {
23
+ throw new UnauthorizedException('Access token required');
24
+ }
25
+
26
+ const payload = this.jwtTokenService.validateToken(token);
27
+
28
+ if (!payload || payload.type !== 'access') {
29
+ throw new UnauthorizedException('Invalid or expired access token');
30
+ }
31
+
32
+ request.user = payload;
33
+ return true;
34
+ }
35
+
36
+ private extractTokenFromHeader(request: Request): string | undefined {
37
+ const authHeader = request.headers.authorization;
38
+ if (!authHeader) {
39
+ return undefined;
40
+ }
41
+
42
+ const [type, token] = authHeader.split(' ');
43
+ return type === 'Bearer' ? token : undefined;
44
+ }
45
+ }
@@ -1 +1,8 @@
1
-
1
+ export * from './mcp-oauth.controller';
2
+ export * from './mcp-oauth.module';
3
+ export * from './providers/oauth-provider.interface';
4
+ export * from './providers/google.provider';
5
+ export * from './providers/github.provider';
6
+ export * from './stores/oauth-store.interface';
7
+ export * from './stores/memory-store.service';
8
+ export * from './stores/typeorm/typeorm-store.service';
@@ -0,0 +1,394 @@
1
+ import {
2
+ BadRequestException,
3
+ Body,
4
+ Controller,
5
+ Get,
6
+ Inject,
7
+ Next,
8
+ Post,
9
+ Query,
10
+ Req,
11
+ Res,
12
+ UseGuards,
13
+ } from '@nestjs/common';
14
+ import { createHash, randomBytes } from 'crypto';
15
+ import { Request as ExpressRequest, NextFunction, Response } from 'express';
16
+ import passport from 'passport';
17
+ import { AuthenticatedRequest, JwtAuthGuard } from './guards/jwt-auth.guard';
18
+ import {
19
+ OAuthModuleOptions,
20
+ OAuthSession,
21
+ OAuthUserProfile,
22
+ } from './providers/oauth-provider.interface';
23
+ import { ClientService } from './services/client.service';
24
+ import { JwtTokenService, TokenPair } from './services/jwt-token.service';
25
+ import { STRATEGY_NAME } from './services/oauth-strategy.service';
26
+ import {
27
+ ClientRegistrationDto,
28
+ IOAuthStore,
29
+ } from './stores/oauth-store.interface';
30
+
31
+ interface OAuthCallbackRequest extends ExpressRequest {
32
+ user?: {
33
+ profile: OAuthUserProfile;
34
+ accessToken: string;
35
+ provider: string;
36
+ };
37
+ }
38
+
39
+ @Controller()
40
+ export class McpOAuthController {
41
+ private readonly serverUrl: string;
42
+ private readonly isProduction: boolean;
43
+
44
+ constructor(
45
+ @Inject('OAUTH_MODULE_OPTIONS') private options: OAuthModuleOptions,
46
+ @Inject('IOAuthStore') private readonly store: IOAuthStore,
47
+ private readonly jwtTokenService: JwtTokenService,
48
+ private readonly clientService: ClientService,
49
+ ) {
50
+ this.serverUrl = this.options.serverUrl;
51
+ this.isProduction = this.options.cookieSecure;
52
+ }
53
+
54
+ // OAuth endpoints
55
+ @Get('/.well-known/oauth-authorization-server')
56
+ getAuthorizationServerMetadata() {
57
+ return {
58
+ issuer: this.serverUrl,
59
+ authorization_endpoint: `${this.serverUrl}/authorize`,
60
+ token_endpoint: `${this.serverUrl}/token`,
61
+ registration_endpoint: `${this.serverUrl}/register`,
62
+ response_types_supported: ['code'],
63
+ response_modes_supported: ['query'],
64
+ grant_types_supported: ['authorization_code', 'refresh_token'],
65
+ token_endpoint_auth_methods_supported: [
66
+ 'client_secret_basic',
67
+ 'client_secret_post',
68
+ 'none',
69
+ ],
70
+ revocation_endpoint: `${this.serverUrl}/revoke`,
71
+ code_challenge_methods_supported: ['plain', 'S256'],
72
+ };
73
+ }
74
+
75
+ @Post('/register')
76
+ async registerClient(@Body() registrationDto: ClientRegistrationDto) {
77
+ return await this.clientService.registerClient(registrationDto);
78
+ }
79
+
80
+ @Get('/authorize')
81
+ async authorize(@Query() query: any, @Res() res: Response) {
82
+ const {
83
+ response_type,
84
+ client_id,
85
+ redirect_uri,
86
+ code_challenge,
87
+ code_challenge_method,
88
+ state,
89
+ resource,
90
+ } = query;
91
+
92
+ // Validate parameters
93
+ if (response_type !== 'code') {
94
+ throw new BadRequestException('Only response_type=code is supported');
95
+ }
96
+
97
+ if (!client_id || !redirect_uri || !code_challenge) {
98
+ throw new BadRequestException('Missing required parameters');
99
+ }
100
+
101
+ // Validate client and redirect URI
102
+ const client = await this.clientService.getClient(client_id);
103
+ if (!client) {
104
+ throw new BadRequestException('Invalid client_id');
105
+ }
106
+
107
+ const validRedirect = await this.clientService.validateRedirectUri(
108
+ client_id,
109
+ redirect_uri,
110
+ );
111
+ if (!validRedirect) {
112
+ throw new BadRequestException('Invalid redirect_uri');
113
+ }
114
+
115
+ // Create OAuth session
116
+ const sessionId = randomBytes(32).toString('base64url');
117
+ const sessionState = randomBytes(32).toString('base64url');
118
+
119
+ const oauthSession: OAuthSession = {
120
+ sessionId,
121
+ state: sessionState,
122
+ clientId: client_id,
123
+ redirectUri: redirect_uri,
124
+ codeChallenge: code_challenge,
125
+ codeChallengeMethod: code_challenge_method || 'plain',
126
+ oauthState: state,
127
+ resource,
128
+ expiresAt: Date.now() + this.options.oauthSessionExpiresIn,
129
+ };
130
+
131
+ this.store.storeOAuthSession(sessionId, oauthSession);
132
+
133
+ // Set session cookie
134
+ res.cookie('oauth_session', sessionId, {
135
+ httpOnly: true,
136
+ secure: this.isProduction,
137
+ maxAge: this.options.oauthSessionExpiresIn,
138
+ });
139
+
140
+ // Store state for passport
141
+ res.cookie('oauth_state', sessionState, {
142
+ httpOnly: true,
143
+ secure: this.isProduction,
144
+ maxAge: this.options.oauthSessionExpiresIn,
145
+ });
146
+
147
+ // Redirect to the provider's auth endpoint
148
+ res.redirect(`/auth`);
149
+ }
150
+
151
+ @Get('/auth')
152
+ authenticate(
153
+ @Req() req: any,
154
+ @Res() res: Response,
155
+ @Next() next: NextFunction,
156
+ ) {
157
+ passport.authenticate(STRATEGY_NAME, {
158
+ state: req.cookies?.oauth_state,
159
+ })(req, res, next);
160
+ }
161
+
162
+ @Get('/auth/callback')
163
+ async handleProviderCallback(
164
+ @Req() req: OAuthCallbackRequest,
165
+ @Res() res: Response,
166
+ @Next() next: NextFunction,
167
+ ) {
168
+ // Use a custom callback to handle the authentication result
169
+ passport.authenticate(
170
+ STRATEGY_NAME,
171
+ { session: false },
172
+ async (err: any, user: any) => {
173
+ try {
174
+ if (err) {
175
+ console.error('OAuth callback error:', err);
176
+ throw new BadRequestException('Authentication failed');
177
+ }
178
+
179
+ if (!user) {
180
+ throw new BadRequestException('Authentication failed');
181
+ }
182
+
183
+ req.user = user;
184
+ await this.processAuthenticationSuccess(req, res);
185
+ } catch (error) {
186
+ next(error);
187
+ }
188
+ },
189
+ )(req, res, next);
190
+ }
191
+
192
+ private async processAuthenticationSuccess(
193
+ req: OAuthCallbackRequest,
194
+ res: Response,
195
+ ) {
196
+ const user = req.user;
197
+ if (!user) {
198
+ throw new BadRequestException('Authentication failed');
199
+ }
200
+
201
+ const sessionId = req.cookies?.oauth_session;
202
+ if (!sessionId) {
203
+ throw new BadRequestException('Missing OAuth session');
204
+ }
205
+
206
+ const session = await this.store.getOAuthSession(sessionId);
207
+ if (!session || session.expiresAt < Date.now()) {
208
+ throw new BadRequestException('Invalid or expired OAuth session');
209
+ }
210
+
211
+ // Verify state
212
+ const stateFromCookie = req.cookies?.oauth_state;
213
+ if (session.state !== stateFromCookie) {
214
+ throw new BadRequestException('Invalid state parameter');
215
+ }
216
+
217
+ // Generate JWT for UI access
218
+ const jwt = this.jwtTokenService.generateUserToken(
219
+ user.profile.username,
220
+ user.profile,
221
+ );
222
+
223
+ // Set JWT token as cookie for UI endpoints
224
+ res.cookie('auth_token', jwt, {
225
+ httpOnly: true,
226
+ secure: this.isProduction,
227
+ maxAge: this.options.cookieMaxAge,
228
+ });
229
+
230
+ // Clear temporary cookies
231
+ res.clearCookie('oauth_session');
232
+ res.clearCookie('oauth_state');
233
+
234
+ // Generate authorization code
235
+ const authCode = randomBytes(32).toString('base64url');
236
+
237
+ // Store the auth code
238
+ this.store.storeAuthCode({
239
+ code: authCode,
240
+ user_id: user.profile.username,
241
+ client_id: session.clientId!,
242
+ redirect_uri: session.redirectUri!,
243
+ code_challenge: session.codeChallenge!,
244
+ code_challenge_method: session.codeChallengeMethod!,
245
+ expires_at: Date.now() + this.options.authCodeExpiresIn,
246
+ github_access_token: '', // No longer provider-specific
247
+ });
248
+
249
+ // Build redirect URL with authorization code
250
+ const redirectUrl = new URL(session.redirectUri!);
251
+ redirectUrl.searchParams.set('code', authCode);
252
+ if (session.oauthState) {
253
+ redirectUrl.searchParams.set('state', session.oauthState);
254
+ }
255
+
256
+ // Clean up session
257
+ this.store.removeOAuthSession(sessionId);
258
+
259
+ res.redirect(redirectUrl.toString());
260
+ }
261
+
262
+ // Token endpoints (remain the same)
263
+ @Post('/token')
264
+ async exchangeToken(@Body() body: any): Promise<TokenPair> {
265
+ const {
266
+ grant_type,
267
+ code,
268
+ code_verifier,
269
+ redirect_uri,
270
+ client_id,
271
+ refresh_token,
272
+ } = body;
273
+
274
+ if (grant_type === 'authorization_code') {
275
+ return this.handleAuthorizationCodeGrant(
276
+ code,
277
+ code_verifier,
278
+ redirect_uri,
279
+ client_id,
280
+ );
281
+ } else if (grant_type === 'refresh_token') {
282
+ return this.handleRefreshTokenGrant(refresh_token);
283
+ } else {
284
+ throw new BadRequestException('Unsupported grant_type');
285
+ }
286
+ }
287
+
288
+ private async handleAuthorizationCodeGrant(
289
+ code: string,
290
+ code_verifier: string,
291
+ redirect_uri: string,
292
+ client_id: string,
293
+ ): Promise<TokenPair> {
294
+ // Validate the authorization code
295
+ const authCode = await this.store.getAuthCode(code);
296
+ if (!authCode) {
297
+ throw new BadRequestException('Invalid authorization code');
298
+ }
299
+
300
+ // Check if code has expired
301
+ if (authCode.expires_at < Date.now()) {
302
+ await this.store.removeAuthCode(code);
303
+ throw new BadRequestException('Authorization code has expired');
304
+ }
305
+
306
+ // Validate client_id matches
307
+ if (authCode.client_id !== client_id) {
308
+ throw new BadRequestException('Client ID mismatch');
309
+ }
310
+
311
+ // Validate PKCE if required
312
+ if (authCode.code_challenge) {
313
+ const isValid = this.validatePKCE(
314
+ code_verifier,
315
+ authCode.code_challenge,
316
+ authCode.code_challenge_method,
317
+ );
318
+ if (!isValid) {
319
+ throw new BadRequestException('Invalid PKCE verification');
320
+ }
321
+ }
322
+
323
+ // Generate tokens
324
+ const tokens = this.jwtTokenService.generateTokenPair(
325
+ authCode.user_id,
326
+ client_id,
327
+ 'mcp:access',
328
+ );
329
+
330
+ // Remove the used authorization code
331
+ this.store.removeAuthCode(code);
332
+
333
+ return tokens;
334
+ }
335
+
336
+ private async handleRefreshTokenGrant(
337
+ refresh_token: string,
338
+ ): Promise<TokenPair> {
339
+ const newTokens = this.jwtTokenService.refreshAccessToken(refresh_token);
340
+ if (!newTokens) {
341
+ throw new BadRequestException('Failed to refresh token');
342
+ }
343
+
344
+ return newTokens;
345
+ }
346
+
347
+ @Get('/validate')
348
+ @UseGuards(JwtAuthGuard)
349
+ validateToken(@Req() req: AuthenticatedRequest) {
350
+ return {
351
+ valid: true,
352
+ user_id: req.user.sub,
353
+ client_id: req.user.client_id,
354
+ scope: req.user.scope,
355
+ expires_at: req.user.exp! * 1000,
356
+ };
357
+ }
358
+
359
+ private parseExpiresInToMs(expiresIn: string): number {
360
+ // Handle formats like "60s", "30d", "24h", "1440m"
361
+ const match = expiresIn.match(/^(\d+)([smhd]?)$/);
362
+ if (!match) {
363
+ throw new Error(`Invalid expiresIn format: ${expiresIn}`);
364
+ }
365
+
366
+ const value = parseInt(match[1], 10);
367
+ const unit = match[2] || 's'; // default to seconds
368
+
369
+ const multipliers = {
370
+ s: 1000,
371
+ m: 60 * 1000,
372
+ h: 60 * 60 * 1000,
373
+ d: 24 * 60 * 60 * 1000,
374
+ };
375
+
376
+ return value * multipliers[unit as keyof typeof multipliers];
377
+ }
378
+
379
+ private validatePKCE(
380
+ code_verifier: string,
381
+ code_challenge: string,
382
+ method: string,
383
+ ): boolean {
384
+ if (method === 'plain') {
385
+ return code_verifier === code_challenge;
386
+ } else if (method === 'S256') {
387
+ const hash = createHash('sha256')
388
+ .update(code_verifier)
389
+ .digest('base64url');
390
+ return hash === code_challenge;
391
+ }
392
+ return false;
393
+ }
394
+ }