@rekog/mcp-nest 1.9.4 → 1.9.6
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.
- package/README.md +5 -1
- package/dist/mcp/decorators/prompt.decorator.d.ts +1 -2
- package/dist/mcp/decorators/prompt.decorator.d.ts.map +1 -1
- package/dist/mcp/decorators/prompt.decorator.js.map +1 -1
- package/dist/mcp/index.d.ts +2 -2
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +2 -2
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/interfaces/dynamic-prompt.interface.d.ts +11 -0
- package/dist/mcp/interfaces/dynamic-prompt.interface.d.ts.map +1 -0
- package/dist/mcp/interfaces/dynamic-prompt.interface.js +3 -0
- package/dist/mcp/interfaces/dynamic-prompt.interface.js.map +1 -0
- package/dist/mcp/interfaces/dynamic-resource.interface.d.ts +11 -0
- package/dist/mcp/interfaces/dynamic-resource.interface.d.ts.map +1 -0
- package/dist/mcp/interfaces/dynamic-resource.interface.js +3 -0
- package/dist/mcp/interfaces/dynamic-resource.interface.js.map +1 -0
- package/dist/mcp/interfaces/dynamic-tool.interface.js.map +1 -1
- package/dist/mcp/interfaces/index.d.ts +2 -0
- package/dist/mcp/interfaces/index.d.ts.map +1 -1
- package/dist/mcp/interfaces/index.js.map +1 -1
- package/dist/mcp/mcp.module.d.ts.map +1 -1
- package/dist/mcp/mcp.module.js +15 -11
- package/dist/mcp/mcp.module.js.map +1 -1
- package/dist/mcp/services/handlers/mcp-handler.base.d.ts +4 -4
- package/dist/mcp/services/handlers/mcp-handler.base.d.ts.map +1 -1
- package/dist/mcp/services/handlers/mcp-handler.base.js.map +1 -1
- package/dist/mcp/services/handlers/mcp-prompts.handler.d.ts +2 -2
- package/dist/mcp/services/handlers/mcp-prompts.handler.d.ts.map +1 -1
- package/dist/mcp/services/handlers/mcp-prompts.handler.js +13 -3
- package/dist/mcp/services/handlers/mcp-prompts.handler.js.map +1 -1
- package/dist/mcp/services/handlers/mcp-resources.handler.d.ts +2 -2
- package/dist/mcp/services/handlers/mcp-resources.handler.d.ts.map +1 -1
- package/dist/mcp/services/handlers/mcp-resources.handler.js +12 -2
- package/dist/mcp/services/handlers/mcp-resources.handler.js.map +1 -1
- package/dist/mcp/services/handlers/mcp-tools.handler.d.ts +2 -2
- package/dist/mcp/services/handlers/mcp-tools.handler.d.ts.map +1 -1
- package/dist/mcp/services/handlers/mcp-tools.handler.js +5 -5
- package/dist/mcp/services/handlers/mcp-tools.handler.js.map +1 -1
- package/dist/mcp/services/mcp-dynamic-registry.service.d.ts +26 -0
- package/dist/mcp/services/mcp-dynamic-registry.service.d.ts.map +1 -0
- package/dist/mcp/services/mcp-dynamic-registry.service.js +133 -0
- package/dist/mcp/services/mcp-dynamic-registry.service.js.map +1 -0
- package/dist/mcp/services/mcp-executor.service.d.ts +2 -2
- package/dist/mcp/services/mcp-executor.service.d.ts.map +1 -1
- package/dist/mcp/services/mcp-executor.service.js +2 -2
- package/dist/mcp/services/mcp-executor.service.js.map +1 -1
- package/dist/mcp/services/{mcp-registry.service.d.ts → mcp-registry-discovery.service.d.ts} +16 -15
- package/dist/mcp/services/mcp-registry-discovery.service.d.ts.map +1 -0
- package/dist/mcp/services/{mcp-registry.service.js → mcp-registry-discovery.service.js} +41 -25
- package/dist/mcp/services/mcp-registry-discovery.service.js.map +1 -0
- package/dist/mcp/services/mcp-sse.service.d.ts +2 -2
- package/dist/mcp/services/mcp-sse.service.d.ts.map +1 -1
- package/dist/mcp/services/mcp-sse.service.js +2 -2
- package/dist/mcp/services/mcp-sse.service.js.map +1 -1
- package/dist/mcp/services/mcp-streamable-http.service.d.ts +2 -2
- package/dist/mcp/services/mcp-streamable-http.service.d.ts.map +1 -1
- package/dist/mcp/services/mcp-streamable-http.service.js +2 -2
- package/dist/mcp/services/mcp-streamable-http.service.js.map +1 -1
- package/dist/mcp/services/tool-authorization.service.d.ts +4 -4
- package/dist/mcp/services/tool-authorization.service.d.ts.map +1 -1
- package/dist/mcp/services/tool-authorization.service.js.map +1 -1
- package/dist/mcp/transport/stdio.service.d.ts +2 -2
- package/dist/mcp/transport/stdio.service.d.ts.map +1 -1
- package/dist/mcp/transport/stdio.service.js +7 -7
- package/dist/mcp/transport/stdio.service.js.map +1 -1
- package/dist/mcp/utils/capabilities-builder.d.ts +2 -2
- package/dist/mcp/utils/capabilities-builder.d.ts.map +1 -1
- package/dist/mcp/utils/capabilities-builder.js.map +1 -1
- package/dist/mcp/utils/mcp-server.factory.d.ts +2 -2
- package/dist/mcp/utils/mcp-server.factory.d.ts.map +1 -1
- package/dist/mcp/utils/mcp-server.factory.js.map +1 -1
- package/package.json +9 -9
- package/src/mcp/decorators/prompt.decorator.ts +1 -1
- package/src/mcp/index.ts +2 -2
- package/src/mcp/interfaces/dynamic-prompt.interface.ts +20 -0
- package/src/mcp/interfaces/dynamic-resource.interface.ts +22 -0
- package/src/mcp/interfaces/dynamic-tool.interface.ts +2 -2
- package/src/mcp/interfaces/index.ts +10 -0
- package/src/mcp/mcp.module.ts +15 -11
- package/src/mcp/services/handlers/mcp-handler.base.ts +6 -3
- package/src/mcp/services/handlers/mcp-prompts.handler.ts +36 -4
- package/src/mcp/services/handlers/mcp-resources.handler.ts +24 -2
- package/src/mcp/services/handlers/mcp-tools.handler.ts +11 -8
- package/src/mcp/services/mcp-dynamic-registry.service.ts +236 -0
- package/src/mcp/services/mcp-executor.service.ts +2 -2
- package/src/mcp/services/{mcp-registry.service.spec.ts → mcp-registry-discovery.service.spec.ts} +18 -12
- package/src/mcp/services/{mcp-registry.service.ts → mcp-registry-discovery.service.ts} +77 -35
- package/src/mcp/services/mcp-sse.service.ts +2 -2
- package/src/mcp/services/mcp-streamable-http.service.ts +2 -2
- package/src/mcp/services/tool-authorization.service.ts +4 -4
- package/src/mcp/transport/stdio.service.ts +8 -8
- package/src/mcp/utils/capabilities-builder.ts +2 -2
- package/src/mcp/utils/mcp-server.factory.ts +2 -2
- package/dist/authz/services/jwt-token.service.spec.d.ts +0 -2
- package/dist/authz/services/jwt-token.service.spec.d.ts.map +0 -1
- package/dist/authz/services/jwt-token.service.spec.js +0 -86
- package/dist/authz/services/jwt-token.service.spec.js.map +0 -1
- package/dist/authz/stores/memory-store.service.spec.d.ts +0 -2
- package/dist/authz/stores/memory-store.service.spec.d.ts.map +0 -1
- package/dist/authz/stores/memory-store.service.spec.js +0 -382
- package/dist/authz/stores/memory-store.service.spec.js.map +0 -1
- package/dist/authz/stores/typeorm/typeorm-store.service.spec.d.ts +0 -2
- package/dist/authz/stores/typeorm/typeorm-store.service.spec.d.ts.map +0 -1
- package/dist/authz/stores/typeorm/typeorm-store.service.spec.js +0 -340
- package/dist/authz/stores/typeorm/typeorm-store.service.spec.js.map +0 -1
- package/dist/mcp/services/mcp-registry.service.d.ts.map +0 -1
- package/dist/mcp/services/mcp-registry.service.js.map +0 -1
- package/dist/mcp/services/mcp-registry.service.spec.d.ts +0 -2
- package/dist/mcp/services/mcp-registry.service.spec.d.ts.map +0 -1
- package/dist/mcp/services/mcp-registry.service.spec.js +0 -245
- package/dist/mcp/services/mcp-registry.service.spec.js.map +0 -1
- package/dist/mcp/services/mcp-tool-builder.service.d.ts +0 -15
- package/dist/mcp/services/mcp-tool-builder.service.d.ts.map +0 -1
- package/dist/mcp/services/mcp-tool-builder.service.js +0 -70
- package/dist/mcp/services/mcp-tool-builder.service.js.map +0 -1
- package/dist/mcp/transport/custom-decorator.spec.d.ts +0 -2
- package/dist/mcp/transport/custom-decorator.spec.d.ts.map +0 -1
- package/dist/mcp/transport/custom-decorator.spec.js +0 -34
- package/dist/mcp/transport/custom-decorator.spec.js.map +0 -1
- package/dist/mcp/utils/mcp-logger.factory.spec.d.ts +0 -2
- package/dist/mcp/utils/mcp-logger.factory.spec.d.ts.map +0 -1
- package/dist/mcp/utils/mcp-logger.factory.spec.js +0 -150
- package/dist/mcp/utils/mcp-logger.factory.spec.js.map +0 -1
- package/dist/mcp/utils/normalize-endpoint.spec.d.ts +0 -2
- package/dist/mcp/utils/normalize-endpoint.spec.d.ts.map +0 -1
- package/dist/mcp/utils/normalize-endpoint.spec.js +0 -40
- package/dist/mcp/utils/normalize-endpoint.spec.js.map +0 -1
- package/src/mcp/services/mcp-tool-builder.service.ts +0 -151
|
@@ -11,23 +11,23 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
11
11
|
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
12
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
13
|
};
|
|
14
|
-
var
|
|
14
|
+
var McpRegistryDiscoveryService_1;
|
|
15
15
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
-
exports.
|
|
16
|
+
exports.McpRegistryDiscoveryService = void 0;
|
|
17
17
|
const common_1 = require("@nestjs/common");
|
|
18
18
|
const core_1 = require("@nestjs/core");
|
|
19
19
|
const decorators_1 = require("../decorators");
|
|
20
20
|
const path_to_regexp_1 = require("path-to-regexp");
|
|
21
21
|
const mcp_logger_factory_1 = require("../utils/mcp-logger.factory");
|
|
22
22
|
const feature_registration_constants_1 = require("../constants/feature-registration.constants");
|
|
23
|
-
let
|
|
23
|
+
let McpRegistryDiscoveryService = McpRegistryDiscoveryService_1 = class McpRegistryDiscoveryService {
|
|
24
24
|
constructor(discovery, metadataScanner, modulesContainer, options) {
|
|
25
25
|
this.discovery = discovery;
|
|
26
26
|
this.metadataScanner = metadataScanner;
|
|
27
27
|
this.modulesContainer = modulesContainer;
|
|
28
28
|
this.options = options;
|
|
29
|
-
this.
|
|
30
|
-
this.logger = (0, mcp_logger_factory_1.createMcpLogger)(
|
|
29
|
+
this.discoveredCapabilitiesByMcpModuleId = new Map();
|
|
30
|
+
this.logger = (0, mcp_logger_factory_1.createMcpLogger)(McpRegistryDiscoveryService_1.name, this.options);
|
|
31
31
|
}
|
|
32
32
|
onApplicationBootstrap() {
|
|
33
33
|
this.discoverTools();
|
|
@@ -175,10 +175,10 @@ let McpRegistryService = McpRegistryService_1 = class McpRegistryService {
|
|
|
175
175
|
if (!metadata['name']) {
|
|
176
176
|
metadata['name'] = methodName;
|
|
177
177
|
}
|
|
178
|
-
if (!this.
|
|
179
|
-
this.
|
|
178
|
+
if (!this.discoveredCapabilitiesByMcpModuleId.has(mcpModuleId)) {
|
|
179
|
+
this.discoveredCapabilitiesByMcpModuleId.set(mcpModuleId, []);
|
|
180
180
|
}
|
|
181
|
-
this.
|
|
181
|
+
this.discoveredCapabilitiesByMcpModuleId.get(mcpModuleId)?.push({
|
|
182
182
|
type,
|
|
183
183
|
metadata,
|
|
184
184
|
providerClass: token,
|
|
@@ -211,10 +211,10 @@ let McpRegistryService = McpRegistryService_1 = class McpRegistryService {
|
|
|
211
211
|
if (guards) {
|
|
212
212
|
baseMetadata.guards = guards;
|
|
213
213
|
}
|
|
214
|
-
if (!this.
|
|
215
|
-
this.
|
|
214
|
+
if (!this.discoveredCapabilitiesByMcpModuleId.has(mcpModuleId)) {
|
|
215
|
+
this.discoveredCapabilitiesByMcpModuleId.set(mcpModuleId, []);
|
|
216
216
|
}
|
|
217
|
-
this.
|
|
217
|
+
this.discoveredCapabilitiesByMcpModuleId.get(mcpModuleId)?.push({
|
|
218
218
|
type: 'tool',
|
|
219
219
|
metadata: baseMetadata,
|
|
220
220
|
providerClass: token,
|
|
@@ -230,10 +230,10 @@ let McpRegistryService = McpRegistryService_1 = class McpRegistryService {
|
|
|
230
230
|
this.addDiscovery('resource-template', decorators_1.MCP_RESOURCE_TEMPLATE_METADATA_KEY, mcpModuleId, methodRef, token, methodName);
|
|
231
231
|
}
|
|
232
232
|
getMcpModuleIds() {
|
|
233
|
-
return Array.from(this.
|
|
233
|
+
return Array.from(this.discoveredCapabilitiesByMcpModuleId.keys());
|
|
234
234
|
}
|
|
235
235
|
getTools(mcpModuleId) {
|
|
236
|
-
return (this.
|
|
236
|
+
return (this.discoveredCapabilitiesByMcpModuleId
|
|
237
237
|
.get(mcpModuleId)
|
|
238
238
|
?.filter((tool) => tool.type === 'tool') ?? []);
|
|
239
239
|
}
|
|
@@ -241,7 +241,7 @@ let McpRegistryService = McpRegistryService_1 = class McpRegistryService {
|
|
|
241
241
|
return this.getTools(mcpModuleId).find((tool) => tool.metadata.name === name);
|
|
242
242
|
}
|
|
243
243
|
getResources(mcpModuleId) {
|
|
244
|
-
return (this.
|
|
244
|
+
return (this.discoveredCapabilitiesByMcpModuleId
|
|
245
245
|
.get(mcpModuleId)
|
|
246
246
|
?.filter((tool) => tool.type === 'resource') ?? []);
|
|
247
247
|
}
|
|
@@ -249,7 +249,7 @@ let McpRegistryService = McpRegistryService_1 = class McpRegistryService {
|
|
|
249
249
|
return this.getResources(mcpModuleId).find((tool) => tool.metadata.name === name);
|
|
250
250
|
}
|
|
251
251
|
getResourceTemplates(mcpModuleId) {
|
|
252
|
-
return (this.
|
|
252
|
+
return (this.discoveredCapabilitiesByMcpModuleId
|
|
253
253
|
.get(mcpModuleId)
|
|
254
254
|
?.filter((tool) => tool.type === 'resource-template') ?? []);
|
|
255
255
|
}
|
|
@@ -257,7 +257,7 @@ let McpRegistryService = McpRegistryService_1 = class McpRegistryService {
|
|
|
257
257
|
return this.getResourceTemplates(mcpModuleId).find((tool) => tool.metadata.name === name);
|
|
258
258
|
}
|
|
259
259
|
getPrompts(mcpModuleId) {
|
|
260
|
-
return (this.
|
|
260
|
+
return (this.discoveredCapabilitiesByMcpModuleId
|
|
261
261
|
.get(mcpModuleId)
|
|
262
262
|
?.filter((tool) => tool.type === 'prompt') ?? []);
|
|
263
263
|
}
|
|
@@ -361,21 +361,37 @@ let McpRegistryService = McpRegistryService_1 = class McpRegistryService {
|
|
|
361
361
|
}
|
|
362
362
|
return undefined;
|
|
363
363
|
}
|
|
364
|
-
|
|
365
|
-
if (!this.
|
|
366
|
-
this.
|
|
364
|
+
registerDynamicCapability(mcpModuleId, capability) {
|
|
365
|
+
if (!this.discoveredCapabilitiesByMcpModuleId.has(mcpModuleId)) {
|
|
366
|
+
this.discoveredCapabilitiesByMcpModuleId.set(mcpModuleId, []);
|
|
367
367
|
}
|
|
368
|
-
|
|
369
|
-
|
|
368
|
+
const capabilities = this.discoveredCapabilitiesByMcpModuleId.get(mcpModuleId);
|
|
369
|
+
const existingIndex = capabilities.findIndex((c) => c.type === capability.type && c.methodName === capability.methodName);
|
|
370
|
+
if (existingIndex !== -1) {
|
|
371
|
+
this.logger.warn(`Dynamic ${capability.type} '${capability.methodName}' already registered in module: ${mcpModuleId}. Overwriting.`);
|
|
372
|
+
capabilities[existingIndex] = capability;
|
|
373
|
+
}
|
|
374
|
+
else {
|
|
375
|
+
this.logger.debug(`Dynamic ${capability.type} registered: ${capability.metadata['name']} in module: ${mcpModuleId}`);
|
|
376
|
+
capabilities.push(capability);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
removeDynamicCapability(mcpModuleId, type, identifier) {
|
|
380
|
+
const capabilities = this.discoveredCapabilitiesByMcpModuleId.get(mcpModuleId);
|
|
381
|
+
if (!capabilities) {
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
this.logger.debug(`Dynamic ${type} removed: ${identifier} in module: ${mcpModuleId}`);
|
|
385
|
+
this.discoveredCapabilitiesByMcpModuleId.set(mcpModuleId, capabilities.filter((c) => !(c.type === type && c.methodName === identifier)));
|
|
370
386
|
}
|
|
371
387
|
};
|
|
372
|
-
exports.
|
|
373
|
-
exports.
|
|
388
|
+
exports.McpRegistryDiscoveryService = McpRegistryDiscoveryService;
|
|
389
|
+
exports.McpRegistryDiscoveryService = McpRegistryDiscoveryService = McpRegistryDiscoveryService_1 = __decorate([
|
|
374
390
|
(0, common_1.Injectable)(),
|
|
375
391
|
__param(3, (0, common_1.Optional)()),
|
|
376
392
|
__param(3, (0, common_1.Inject)('MCP_OPTIONS')),
|
|
377
393
|
__metadata("design:paramtypes", [core_1.DiscoveryService,
|
|
378
394
|
core_1.MetadataScanner,
|
|
379
395
|
core_1.ModulesContainer, Object])
|
|
380
|
-
],
|
|
381
|
-
//# sourceMappingURL=mcp-registry.service.js.map
|
|
396
|
+
], McpRegistryDiscoveryService);
|
|
397
|
+
//# sourceMappingURL=mcp-registry-discovery.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-registry-discovery.service.js","sourceRoot":"","sources":["../../../src/mcp/services/mcp-registry-discovery.service.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Injectable,\n InjectionToken,\n Logger,\n OnApplicationBootstrap,\n Inject,\n Optional,\n} from '@nestjs/common';\nimport {\n DiscoveryService,\n MetadataScanner,\n ModulesContainer,\n} from '@nestjs/core';\nimport {\n MCP_PROMPT_METADATA_KEY,\n MCP_RESOURCE_METADATA_KEY,\n MCP_RESOURCE_TEMPLATE_METADATA_KEY,\n MCP_TOOL_METADATA_KEY,\n MCP_PUBLIC_METADATA_KEY,\n MCP_SCOPES_METADATA_KEY,\n MCP_ROLES_METADATA_KEY,\n MCP_GUARDS_METADATA_KEY,\n ToolMetadata,\n} from '../decorators';\nimport { ResourceMetadata } from '../decorators/resource.decorator';\nimport { match } from 'path-to-regexp';\nimport { PromptMetadata } from '../decorators/prompt.decorator';\nimport { Module } from '@nestjs/core/injector/module';\nimport { ResourceTemplateMetadata } from '../decorators/resource-template.decorator';\nimport type { McpOptions } from '../interfaces';\nimport { createMcpLogger } from '../utils/mcp-logger.factory';\nimport {\n MCP_FEATURE_REGISTRATION,\n McpFeatureRegistration,\n} from '../constants/feature-registration.constants';\n\n/**\n * Interface representing a discovered capability\n */\nexport type DiscoveredCapability<T extends object> = {\n type: 'tool' | 'resource' | 'resource-template' | 'prompt';\n metadata: T;\n providerClass: InjectionToken;\n methodName: string;\n};\n\nexport type InjectionTokenWithName = InjectionToken & { name: string };\n\n/**\n * Singleton service that discovers and registers tools during application bootstrap\n */\n@Injectable()\nexport class McpRegistryDiscoveryService implements OnApplicationBootstrap {\n private readonly logger: Logger;\n private discoveredCapabilitiesByMcpModuleId: Map<\n string,\n DiscoveredCapability<any>[]\n > = new Map();\n\n constructor(\n private readonly discovery: DiscoveryService,\n private readonly metadataScanner: MetadataScanner,\n private readonly modulesContainer: ModulesContainer,\n @Optional() @Inject('MCP_OPTIONS') private readonly options?: McpOptions,\n ) {\n this.logger = createMcpLogger(\n McpRegistryDiscoveryService.name,\n this.options,\n );\n }\n\n onApplicationBootstrap() {\n this.discoverTools();\n }\n\n /**\n * Finds all modules that import the McpModule and scans only the root module providers and controllers.\n * This prevents unintentionally exposing tools from imported dependencies.\n */\n private discoverTools() {\n // First, build a map of server names to module IDs\n const serverNameToModuleId = this.buildServerNameToModuleIdMap();\n\n // Then, collect feature registrations\n const featureRegistrations = this.collectFeatureRegistrations();\n\n const getImportedMcpModules = (module: Module) =>\n Array.from(module.imports).filter(\n (m) =>\n (m.instance as any).__isMcpModule &&\n !(m.instance as any).__isMcpFeatureModule,\n );\n\n const pairs = Array.from(this.modulesContainer.values())\n .map((module): [Module, Module[]] => [\n module,\n getImportedMcpModules(module),\n ])\n .filter(([, importedMcpModules]) => importedMcpModules.length > 0);\n\n for (const [rootModule, mcpModules] of pairs) {\n this.logger.debug(\n `Discovering tools, resources, resource templates, and prompts for module: ${rootModule.name}`,\n );\n\n for (const mcpModule of mcpModules) {\n const mcpModuleId =\n mcpModule.getProviderByKey<string>('MCP_MODULE_ID')?.instance;\n\n this.discoverToolsForModuleSubtree(mcpModuleId, [rootModule]);\n }\n }\n\n // Process feature registrations\n this.processFeatureRegistrations(\n featureRegistrations,\n serverNameToModuleId,\n );\n }\n\n /**\n * Builds a map from server names to their module IDs.\n */\n private buildServerNameToModuleIdMap(): Map<string, string> {\n const map = new Map<string, string>();\n\n for (const module of this.modulesContainer.values()) {\n if ((module.instance as any)?.__isMcpModule) {\n const moduleId =\n module.getProviderByKey<string>('MCP_MODULE_ID')?.instance;\n const options =\n module.getProviderByKey<McpOptions>('MCP_OPTIONS')?.instance;\n\n if (moduleId && options?.name) {\n map.set(options.name, moduleId);\n }\n }\n }\n\n return map;\n }\n\n /**\n * Collects all feature registrations from modules that import McpModule.forFeature().\n */\n private collectFeatureRegistrations(): Array<{\n registration: McpFeatureRegistration;\n sourceModule: Module;\n }> {\n const registrations: Array<{\n registration: McpFeatureRegistration;\n sourceModule: Module;\n }> = [];\n\n for (const module of this.modulesContainer.values()) {\n // Check for feature registration providers (tokens start with MCP_FEATURE_REGISTRATION)\n for (const [key, provider] of module.providers) {\n if (\n typeof key === 'string' &&\n key.startsWith(MCP_FEATURE_REGISTRATION) &&\n provider?.instance\n ) {\n registrations.push({\n registration: provider.instance as McpFeatureRegistration,\n sourceModule: module,\n });\n }\n }\n }\n\n return registrations;\n }\n\n /**\n * Processes feature registrations and discovers tools from their providers.\n */\n private processFeatureRegistrations(\n registrations: Array<{\n registration: McpFeatureRegistration;\n sourceModule: Module;\n }>,\n serverNameToModuleId: Map<string, string>,\n ) {\n for (const { registration, sourceModule } of registrations) {\n const mcpModuleId = serverNameToModuleId.get(registration.serverName);\n\n if (!mcpModuleId) {\n this.logger.warn(\n `McpModule.forFeature: No MCP server found with name '${registration.serverName}'. ` +\n `Make sure McpModule.forRoot({ name: '${registration.serverName}', ... }) is imported.`,\n );\n continue;\n }\n\n this.logger.debug(\n `Processing forFeature registration for server '${registration.serverName}' ` +\n `with ${registration.providerTokens.length} provider(s)`,\n );\n\n // Find the module that actually provides these providers\n // The sourceModule imports the forFeature module, so we look at its parent\n const parentModule = this.findModuleWithProviders(\n registration.providerTokens,\n sourceModule,\n );\n\n if (parentModule) {\n this.discoverToolsFromProviders(\n mcpModuleId,\n registration.providerTokens,\n parentModule,\n );\n }\n }\n }\n\n /**\n * Finds a module that contains the specified providers.\n * Searches the source module and its parent modules.\n */\n private findModuleWithProviders(\n providerTokens: InjectionToken[],\n sourceModule: Module,\n ): Module | undefined {\n // First check if the source module's parent has the providers\n for (const module of this.modulesContainer.values()) {\n if (module.imports.has(sourceModule)) {\n // This module imports our source module, check if it has the providers\n const hasAllProviders = providerTokens.every((token) =>\n module.getProviderByKey(token),\n );\n if (hasAllProviders) {\n return module;\n }\n }\n }\n\n // Fallback: search all modules\n for (const module of this.modulesContainer.values()) {\n const hasAllProviders = providerTokens.every((token) =>\n module.getProviderByKey(token),\n );\n if (hasAllProviders) {\n return module;\n }\n }\n\n return undefined;\n }\n\n /**\n * Discovers tools from specific providers within a module.\n */\n private discoverToolsFromProviders(\n mcpModuleId: string,\n providerTokens: InjectionToken[],\n module: Module,\n ) {\n for (const token of providerTokens) {\n const provider = module.getProviderByKey(token);\n if (!provider?.instance || typeof provider.instance !== 'object') {\n this.logger.warn(\n `McpModule.forFeature: Provider '${String(token)}' not found or not instantiated`,\n );\n continue;\n }\n\n const instance = provider.instance as object;\n this.metadataScanner.getAllMethodNames(instance).forEach((methodName) => {\n const methodRef = instance[methodName] as object;\n const methodMetaKeys = Reflect.getOwnMetadataKeys(methodRef);\n\n if (methodMetaKeys.includes(MCP_TOOL_METADATA_KEY)) {\n this.addDiscoveryTool(\n mcpModuleId,\n methodRef,\n token as InjectionTokenWithName,\n methodName,\n );\n }\n\n if (methodMetaKeys.includes(MCP_RESOURCE_METADATA_KEY)) {\n this.addDiscoveryResource(\n mcpModuleId,\n methodRef,\n token as InjectionTokenWithName,\n methodName,\n );\n }\n\n if (methodMetaKeys.includes(MCP_RESOURCE_TEMPLATE_METADATA_KEY)) {\n this.addDiscoveryResourceTemplate(\n mcpModuleId,\n methodRef,\n token as InjectionTokenWithName,\n methodName,\n );\n }\n\n if (methodMetaKeys.includes(MCP_PROMPT_METADATA_KEY)) {\n this.addDiscoveryPrompt(\n mcpModuleId,\n methodRef,\n token as InjectionTokenWithName,\n methodName,\n );\n }\n });\n }\n }\n\n /**\n * Scans all providers and controllers for @Tool decorators\n */\n private discoverToolsForModuleSubtree(\n mcpModuleId: string,\n modules: Module[],\n ) {\n const providers = this.discovery.getProviders(undefined, modules);\n const controllers = this.discovery.getControllers(undefined, modules);\n const allInstances = [...providers, ...controllers]\n .filter(\n (wrapper) =>\n wrapper.instance &&\n typeof wrapper.instance === 'object' &&\n wrapper.instance !== null,\n )\n .map((wrapper) => ({\n instance: wrapper.instance as object,\n token: wrapper.token,\n }));\n\n allInstances.forEach(({ instance, token }) => {\n this.metadataScanner.getAllMethodNames(instance).forEach((methodName) => {\n const methodRef = instance[methodName] as object;\n const methodMetaKeys = Reflect.getOwnMetadataKeys(methodRef);\n\n if (methodMetaKeys.includes(MCP_TOOL_METADATA_KEY)) {\n this.addDiscoveryTool(\n mcpModuleId,\n methodRef,\n token as InjectionTokenWithName,\n methodName,\n );\n }\n\n if (methodMetaKeys.includes(MCP_RESOURCE_METADATA_KEY)) {\n this.addDiscoveryResource(\n mcpModuleId,\n methodRef,\n token as InjectionTokenWithName,\n methodName,\n );\n }\n\n if (methodMetaKeys.includes(MCP_RESOURCE_TEMPLATE_METADATA_KEY)) {\n this.addDiscoveryResourceTemplate(\n mcpModuleId,\n methodRef,\n token as InjectionTokenWithName,\n methodName,\n );\n }\n\n if (methodMetaKeys.includes(MCP_PROMPT_METADATA_KEY)) {\n this.addDiscoveryPrompt(\n mcpModuleId,\n methodRef,\n token as InjectionTokenWithName,\n methodName,\n );\n }\n });\n });\n }\n\n /**\n * Adds a discovered tool to the registry\n */\n private addDiscovery<T>(\n type: 'tool' | 'resource' | 'resource-template' | 'prompt',\n metadataKey: string,\n mcpModuleId: string,\n methodRef: object,\n token: InjectionTokenWithName,\n methodName: string,\n ) {\n const metadata: T = Reflect.getMetadata(metadataKey, methodRef);\n\n if (!metadata['name']) {\n metadata['name'] = methodName;\n }\n\n if (!this.discoveredCapabilitiesByMcpModuleId.has(mcpModuleId)) {\n this.discoveredCapabilitiesByMcpModuleId.set(mcpModuleId, []);\n }\n\n this.discoveredCapabilitiesByMcpModuleId.get(mcpModuleId)?.push({\n type,\n metadata,\n providerClass: token,\n methodName,\n });\n }\n\n private addDiscoveryPrompt(\n mcpModuleId: string,\n methodRef: object,\n token: InjectionTokenWithName,\n methodName: string,\n ) {\n this.logger.debug(\n `Prompt discovered: ${token.name}.${methodName} in module: ${mcpModuleId}`,\n );\n this.addDiscovery<PromptMetadata>(\n 'prompt',\n MCP_PROMPT_METADATA_KEY,\n mcpModuleId,\n methodRef,\n token,\n methodName,\n );\n }\n\n private addDiscoveryTool(\n mcpModuleId: string,\n methodRef: object,\n token: InjectionTokenWithName,\n methodName: string,\n ) {\n this.logger.debug(\n `Tool discovered: ${token.name}.${methodName} in module: ${mcpModuleId}`,\n );\n\n // Collect security metadata from decorators\n const isPublic = Reflect.getMetadata(MCP_PUBLIC_METADATA_KEY, methodRef);\n const requiredScopes = Reflect.getMetadata(\n MCP_SCOPES_METADATA_KEY,\n methodRef,\n );\n const requiredRoles = Reflect.getMetadata(\n MCP_ROLES_METADATA_KEY,\n methodRef,\n );\n const guards = Reflect.getMetadata(MCP_GUARDS_METADATA_KEY, methodRef);\n\n // Add tool with security metadata\n const baseMetadata: ToolMetadata = Reflect.getMetadata(\n MCP_TOOL_METADATA_KEY,\n methodRef,\n );\n\n if (!baseMetadata.name) {\n baseMetadata.name = methodName;\n }\n\n // Enrich with security metadata\n if (isPublic !== undefined) {\n baseMetadata.isPublic = isPublic;\n }\n if (requiredScopes) {\n baseMetadata.requiredScopes = requiredScopes;\n }\n if (requiredRoles) {\n baseMetadata.requiredRoles = requiredRoles;\n }\n if (guards) {\n baseMetadata.guards = guards;\n }\n\n if (!this.discoveredCapabilitiesByMcpModuleId.has(mcpModuleId)) {\n this.discoveredCapabilitiesByMcpModuleId.set(mcpModuleId, []);\n }\n\n this.discoveredCapabilitiesByMcpModuleId.get(mcpModuleId)?.push({\n type: 'tool',\n metadata: baseMetadata,\n providerClass: token,\n methodName,\n });\n }\n\n private addDiscoveryResource(\n mcpModuleId: string,\n methodRef: object,\n token: InjectionTokenWithName,\n methodName: string,\n ) {\n this.logger.debug(\n `Resource discovered: ${token.name}.${methodName} in module: ${mcpModuleId}`,\n );\n this.addDiscovery<ResourceMetadata>(\n 'resource',\n MCP_RESOURCE_METADATA_KEY,\n mcpModuleId,\n methodRef,\n token,\n methodName,\n );\n }\n\n private addDiscoveryResourceTemplate(\n mcpModuleId: string,\n methodRef: object,\n token: InjectionTokenWithName,\n methodName: string,\n ) {\n this.logger.debug(\n `Resource Template discovered: ${token.name}.${methodName} in module: ${mcpModuleId}`,\n );\n this.addDiscovery<ResourceTemplateMetadata>(\n 'resource-template',\n MCP_RESOURCE_TEMPLATE_METADATA_KEY,\n mcpModuleId,\n methodRef,\n token,\n methodName,\n );\n }\n\n /**\n * Return all discovered MCP module IDs\n */\n getMcpModuleIds(): string[] {\n return Array.from(this.discoveredCapabilitiesByMcpModuleId.keys());\n }\n\n /**\n * Get all discovered tools\n */\n getTools(mcpModuleId: string): DiscoveredCapability<ToolMetadata>[] {\n return (\n this.discoveredCapabilitiesByMcpModuleId\n .get(mcpModuleId)\n ?.filter((tool) => tool.type === 'tool') ?? []\n );\n }\n\n /**\n * Find a tool by name\n */\n findTool(\n mcpModuleId: string,\n name: string,\n ): DiscoveredCapability<ToolMetadata> | undefined {\n return this.getTools(mcpModuleId).find(\n (tool) => tool.metadata.name === name,\n );\n }\n\n /**\n * Get all discovered resources\n */\n getResources(mcpModuleId: string): DiscoveredCapability<ResourceMetadata>[] {\n return (\n this.discoveredCapabilitiesByMcpModuleId\n .get(mcpModuleId)\n ?.filter((tool) => tool.type === 'resource') ?? []\n );\n }\n\n /**\n * Find a resource by name\n */\n findResource(\n mcpModuleId: string,\n name: string,\n ): DiscoveredCapability<ResourceMetadata> | undefined {\n return this.getResources(mcpModuleId).find(\n (tool) => tool.metadata.name === name,\n );\n }\n\n /**\n * Get all discovered resource templates\n */\n getResourceTemplates(\n mcpModuleId: string,\n ): DiscoveredCapability<ResourceTemplateMetadata>[] {\n return (\n this.discoveredCapabilitiesByMcpModuleId\n .get(mcpModuleId)\n ?.filter((tool) => tool.type === 'resource-template') ?? []\n );\n }\n\n /**\n * Find a resource by name\n */\n findResourceTemplate(\n mcpModuleId: string,\n name: string,\n ): DiscoveredCapability<ResourceTemplateMetadata> | undefined {\n return this.getResourceTemplates(mcpModuleId).find(\n (tool) => tool.metadata.name === name,\n );\n }\n\n /**\n * Get all discovered prompts\n */\n getPrompts(mcpModuleId: string): DiscoveredCapability<PromptMetadata>[] {\n return (\n this.discoveredCapabilitiesByMcpModuleId\n .get(mcpModuleId)\n ?.filter((tool) => tool.type === 'prompt') ?? []\n );\n }\n\n /**\n * Find a prompt by name\n */\n findPrompt(\n mcpModuleId: string,\n name: string,\n ): DiscoveredCapability<PromptMetadata> | undefined {\n return this.getPrompts(mcpModuleId).find(\n (tool) => tool.metadata.name === name,\n );\n }\n\n /**\n * Convert RFC 6570 URI template to path-to-regexp compatible format.\n * Handles both path parameters {param} and query parameters {?param} or {?param1,param2}\n */\n private convertTemplate(template: string): string {\n if (!template) return template;\n\n // Remove RFC 6570 query parameter syntax {?...} from the template\n // These will be handled separately via URL query string parsing\n const withoutQueryParams = template.replace(/\\{\\?[^}]+\\}/g, '');\n\n // Convert path parameters {param} to path-to-regexp format :param\n return withoutQueryParams.replace(/{(\\w+)}/g, ':$1');\n }\n\n /**\n * Extract query parameter names from an RFC 6570 URI template.\n * E.g., 'mcp://example{?foo,bar}' returns ['foo', 'bar']\n */\n private extractTemplateQueryParams(template: string): string[] {\n const queryParamMatch = template.match(/\\{\\?([^}]+)\\}/);\n if (!queryParamMatch) return [];\n return queryParamMatch[1].split(',').map((p) => p.trim());\n }\n\n /**\n * Parse query string from a URI and return as key-value pairs.\n */\n private parseQueryString(uri: string): Record<string, string> {\n const queryIndex = uri.indexOf('?');\n if (queryIndex === -1) return {};\n\n const queryString = uri.substring(queryIndex + 1);\n const params: Record<string, string> = {};\n\n for (const pair of queryString.split('&')) {\n const [key, value] = pair.split('=');\n if (key) {\n params[decodeURIComponent(key)] = value\n ? decodeURIComponent(value)\n : '';\n }\n }\n\n return params;\n }\n\n /**\n * Strip query string from a URI, returning only the path portion.\n */\n private stripQueryString(uri: string): string {\n const queryIndex = uri.indexOf('?');\n return queryIndex === -1 ? uri : uri.substring(0, queryIndex);\n }\n\n private convertUri(uri: string): string {\n if (uri.includes('://')) {\n return uri.split('://')[1];\n }\n\n return uri;\n }\n\n /**\n * Find a resource by uri\n * @returns An object containing the found resource and extracted parameters, or undefined if no resource is found\n */\n findResourceByUri(\n mcpModuleId: string,\n uri: string,\n ):\n | {\n resource: DiscoveredCapability<ResourceMetadata>;\n params: Record<string, string>;\n }\n | undefined {\n const resources = this.getResources(mcpModuleId).map((tool) => ({\n name: tool.metadata.name,\n uri: tool.metadata.uri,\n }));\n\n const strippedInputUri = this.convertUri(uri);\n\n for (const t of resources) {\n if (!t.uri) continue;\n\n const rawTemplate = t.uri;\n const templatePath = this.convertTemplate(this.convertUri(rawTemplate));\n const matcher = match(templatePath, { decode: decodeURIComponent });\n const result = matcher(strippedInputUri);\n\n if (result) {\n const foundResource = this.findResource(mcpModuleId, t.name);\n if (!foundResource) continue;\n\n return {\n resource: foundResource,\n params: result.params as Record<string, string>,\n };\n }\n }\n\n return undefined;\n }\n\n /**\n * Find a resource template by uri\n * @returns An object containing the found resource template and extracted parameters, or undefined if no resource template is found\n */\n findResourceTemplateByUri(\n mcpModuleId: string,\n uri: string,\n ):\n | {\n resourceTemplate: DiscoveredCapability<ResourceTemplateMetadata>;\n params: Record<string, string>;\n }\n | undefined {\n const resourceTemplates = this.getResourceTemplates(mcpModuleId).map(\n (tool) => ({\n name: tool.metadata.name,\n uriTemplate: tool.metadata.uriTemplate,\n }),\n );\n\n // Strip query string from input URI for path matching\n const strippedInputUri = this.stripQueryString(this.convertUri(uri));\n // Parse query parameters from input URI\n const inputQueryParams = this.parseQueryString(uri);\n\n for (const t of resourceTemplates) {\n if (!t.uriTemplate) continue;\n\n const rawTemplate = t.uriTemplate;\n // Convert template (removes {?...} query params and converts {param} to :param)\n const templatePath = this.convertTemplate(this.convertUri(rawTemplate));\n const matcher = match(templatePath, { decode: decodeURIComponent });\n const result = matcher(strippedInputUri);\n\n if (result) {\n const foundResourceTemplate = this.findResourceTemplate(\n mcpModuleId,\n t.name,\n );\n if (!foundResourceTemplate) continue;\n\n // Get path params from matching\n const pathParams = result.params as Record<string, string>;\n\n // Get expected query params from template and filter input query params\n const expectedQueryParams =\n this.extractTemplateQueryParams(rawTemplate);\n const queryParams: Record<string, string> = {};\n for (const paramName of expectedQueryParams) {\n if (inputQueryParams[paramName] !== undefined) {\n queryParams[paramName] = inputQueryParams[paramName];\n }\n }\n\n return {\n resourceTemplate: foundResourceTemplate,\n params: { ...pathParams, ...queryParams },\n };\n }\n }\n\n return undefined;\n }\n\n /**\n * Register a tool programmatically (for dynamic tools).\n * Use McpRegistryService.registerTool() instead of calling this directly.\n *\n * @param mcpModuleId - The module ID to register the tool with\n * @param capability - The discovered capability object to register\n */\n registerDynamicCapability<T extends object>(\n mcpModuleId: string,\n capability: DiscoveredCapability<T>,\n ): void {\n if (!this.discoveredCapabilitiesByMcpModuleId.has(mcpModuleId)) {\n this.discoveredCapabilitiesByMcpModuleId.set(mcpModuleId, []);\n }\n\n const capabilities =\n this.discoveredCapabilitiesByMcpModuleId.get(mcpModuleId)!;\n\n const existingIndex = capabilities.findIndex(\n (c) =>\n c.type === capability.type && c.methodName === capability.methodName,\n );\n\n if (existingIndex !== -1) {\n this.logger.warn(\n `Dynamic ${capability.type} '${capability.methodName}' already registered in module: ${mcpModuleId}. Overwriting.`,\n );\n capabilities[existingIndex] = capability;\n } else {\n this.logger.debug(\n `Dynamic ${capability.type} registered: ${capability.metadata['name']} in module: ${mcpModuleId}`,\n );\n capabilities.push(capability);\n }\n }\n\n removeDynamicCapability(\n mcpModuleId: string,\n type: DiscoveredCapability<object>['type'],\n identifier: string,\n ): void {\n const capabilities =\n this.discoveredCapabilitiesByMcpModuleId.get(mcpModuleId);\n if (!capabilities) {\n return;\n }\n\n this.logger.debug(\n `Dynamic ${type} removed: ${identifier} in module: ${mcpModuleId}`,\n );\n\n this.discoveredCapabilitiesByMcpModuleId.set(\n mcpModuleId,\n capabilities.filter(\n (c) => !(c.type === type && c.methodName === identifier),\n ),\n );\n }\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ApplicationConfig, ModuleRef } from '@nestjs/core';
|
|
2
2
|
import type { McpOptions } from '../interfaces';
|
|
3
|
-
import {
|
|
3
|
+
import { McpRegistryDiscoveryService } from './mcp-registry-discovery.service';
|
|
4
4
|
import { SsePingService } from './sse-ping.service';
|
|
5
5
|
export declare class McpSseService {
|
|
6
6
|
private readonly options;
|
|
@@ -12,7 +12,7 @@ export declare class McpSseService {
|
|
|
12
12
|
private readonly logger;
|
|
13
13
|
private readonly transports;
|
|
14
14
|
private readonly mcpServers;
|
|
15
|
-
constructor(options: McpOptions, mcpModuleId: string, applicationConfig: ApplicationConfig, moduleRef: ModuleRef, toolRegistry:
|
|
15
|
+
constructor(options: McpOptions, mcpModuleId: string, applicationConfig: ApplicationConfig, moduleRef: ModuleRef, toolRegistry: McpRegistryDiscoveryService, pingService: SsePingService);
|
|
16
16
|
initialize(): void;
|
|
17
17
|
createSseConnection(rawReq: any, rawRes: any, messagesEndpoint: string, apiPrefix: string): Promise<void>;
|
|
18
18
|
handleMessage(rawReq: any, rawRes: any, body: unknown): Promise<any>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-sse.service.d.ts","sourceRoot":"","sources":["../../../src/mcp/services/mcp-sse.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAG9E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"mcp-sse.service.d.ts","sourceRoot":"","sources":["../../../src/mcp/services/mcp-sse.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAG9E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,OAAO,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAC;AAC/E,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAMpD,qBACa,aAAa;IAWC,OAAO,CAAC,QAAQ,CAAC,OAAO;IACtB,OAAO,CAAC,QAAQ,CAAC,WAAW;IACrD,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,YAAY;IACL,OAAO,CAAC,QAAQ,CAAC,WAAW;IAftD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAKhC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAyC;IAEpE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAgC;gBAGjB,OAAO,EAAE,UAAU,EACjB,WAAW,EAAE,MAAM,EAC5C,iBAAiB,EAAE,iBAAiB,EACpC,SAAS,EAAE,SAAS,EACpB,YAAY,EAAE,2BAA2B,EACjB,WAAW,EAAE,cAAc;IAQtE,UAAU;IAWJ,mBAAmB,CACvB,MAAM,EAAE,GAAG,EACX,MAAM,EAAE,GAAG,EACX,gBAAgB,EAAE,MAAM,EACxB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC;IAwCV,aAAa,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC;CA6B3E"}
|
|
@@ -18,7 +18,7 @@ const common_1 = require("@nestjs/common");
|
|
|
18
18
|
const core_1 = require("@nestjs/core");
|
|
19
19
|
const sse_js_1 = require("@modelcontextprotocol/sdk/server/sse.js");
|
|
20
20
|
const mcp_executor_service_1 = require("./mcp-executor.service");
|
|
21
|
-
const
|
|
21
|
+
const mcp_registry_discovery_service_1 = require("./mcp-registry-discovery.service");
|
|
22
22
|
const sse_ping_service_1 = require("./sse-ping.service");
|
|
23
23
|
const normalize_endpoint_1 = require("../utils/normalize-endpoint");
|
|
24
24
|
const adapters_1 = require("../adapters");
|
|
@@ -85,7 +85,7 @@ exports.McpSseService = McpSseService = McpSseService_1 = __decorate([
|
|
|
85
85
|
__param(5, (0, common_1.Inject)(sse_ping_service_1.SsePingService)),
|
|
86
86
|
__metadata("design:paramtypes", [Object, String, core_1.ApplicationConfig,
|
|
87
87
|
core_1.ModuleRef,
|
|
88
|
-
|
|
88
|
+
mcp_registry_discovery_service_1.McpRegistryDiscoveryService,
|
|
89
89
|
sse_ping_service_1.SsePingService])
|
|
90
90
|
], McpSseService);
|
|
91
91
|
//# sourceMappingURL=mcp-sse.service.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-sse.service.js","sourceRoot":"","sources":["../../../src/mcp/services/mcp-sse.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAA4D;AAC5D,uCAA8E;AAE9E,oEAA6E;AAE7E,iEAA4D;AAC5D,
|
|
1
|
+
{"version":3,"file":"mcp-sse.service.js","sourceRoot":"","sources":["../../../src/mcp/services/mcp-sse.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Inject, Injectable, Logger } from '@nestjs/common';\nimport { ApplicationConfig, ContextIdFactory, ModuleRef } from '@nestjs/core';\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';\nimport type { McpOptions } from '../interfaces';\nimport { McpExecutorService } from './mcp-executor.service';\nimport { McpRegistryDiscoveryService } from './mcp-registry-discovery.service';\nimport { SsePingService } from './sse-ping.service';\nimport { normalizeEndpoint } from '../utils/normalize-endpoint';\nimport { HttpAdapterFactory } from '../adapters';\nimport { createMcpLogger } from '../utils/mcp-logger.factory';\nimport { createMcpServer } from '../utils/mcp-server.factory';\n\n@Injectable()\nexport class McpSseService {\n private readonly logger: Logger;\n\n // Note: Currently, storing transports and servers makes it a requirement to have sticky sessions.\n\n // Map to store active transports by session ID\n private readonly transports = new Map<string, SSEServerTransport>();\n // Map to store MCP server instances by session ID\n private readonly mcpServers = new Map<string, McpServer>();\n\n constructor(\n @Inject('MCP_OPTIONS') private readonly options: McpOptions,\n @Inject('MCP_MODULE_ID') private readonly mcpModuleId: string,\n private readonly applicationConfig: ApplicationConfig,\n private readonly moduleRef: ModuleRef,\n private readonly toolRegistry: McpRegistryDiscoveryService,\n @Inject(SsePingService) private readonly pingService: SsePingService,\n ) {\n this.logger = createMcpLogger(McpSseService.name, this.options);\n }\n\n /**\n * Initialize the SSE service and configure ping service\n */\n initialize() {\n // Configure ping service with options\n this.pingService.configure({\n pingEnabled: this.options.sse?.pingEnabled, // Enable by default\n pingIntervalMs: this.options.sse?.pingIntervalMs,\n });\n }\n\n /**\n * Create and manage SSE connection\n */\n async createSseConnection(\n rawReq: any,\n rawRes: any,\n messagesEndpoint: string,\n apiPrefix: string,\n ): Promise<void> {\n const adapter = HttpAdapterFactory.getAdapter(rawReq, rawRes);\n const res = adapter.adaptResponse(rawRes);\n\n // Create a new SSE transport instance\n const transport = new SSEServerTransport(\n normalizeEndpoint(\n `${apiPrefix}/${this.applicationConfig.getGlobalPrefix()}/${messagesEndpoint}`,\n ),\n res.raw,\n );\n const sessionId = transport.sessionId;\n\n const mcpServer = createMcpServer(\n this.mcpModuleId,\n this.toolRegistry,\n this.options,\n this.logger,\n );\n\n // Store the transport and server for this session\n this.transports.set(sessionId, transport);\n this.mcpServers.set(sessionId, mcpServer);\n\n // Register the connection with the ping service\n this.pingService.registerConnection(sessionId, transport, res);\n\n transport.onclose = () => {\n // Clean up when the connection closes\n this.transports.delete(sessionId);\n this.mcpServers.delete(sessionId);\n this.pingService.removeConnection(sessionId);\n };\n\n await mcpServer.connect(transport);\n }\n\n /**\n * Handle message processing for SSE\n */\n async handleMessage(rawReq: any, rawRes: any, body: unknown): Promise<any> {\n const adapter = HttpAdapterFactory.getAdapter(rawReq, rawRes);\n const req = adapter.adaptRequest(rawReq);\n const res = adapter.adaptResponse(rawRes);\n const sessionId = req.query.sessionId as string;\n const transport = this.transports.get(sessionId);\n\n if (!transport) {\n return res.status(404).send('Session not found');\n }\n\n const mcpServer = this.mcpServers.get(sessionId);\n if (!mcpServer) {\n return res.status(404).send('MCP server not found for session');\n }\n\n // Resolve the request-scoped tool executor service\n const contextId = ContextIdFactory.getByRequest(req);\n const executor = await this.moduleRef.resolve(\n McpExecutorService,\n contextId,\n );\n\n // Register request handlers with the user context from this specific request\n executor.registerRequestHandlers(mcpServer, req);\n\n // Process the message\n await transport.handlePostMessage(req.raw, res.raw, body);\n }\n}\n"]}
|
|
@@ -4,7 +4,7 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
|
4
4
|
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
5
5
|
import { HttpRequest, HttpResponse } from '../interfaces/http-adapter.interface';
|
|
6
6
|
import type { McpOptions } from '../interfaces';
|
|
7
|
-
import {
|
|
7
|
+
import { McpRegistryDiscoveryService } from './mcp-registry-discovery.service';
|
|
8
8
|
export declare class McpStreamableHttpService implements OnModuleDestroy {
|
|
9
9
|
private readonly options;
|
|
10
10
|
private readonly mcpModuleId;
|
|
@@ -15,7 +15,7 @@ export declare class McpStreamableHttpService implements OnModuleDestroy {
|
|
|
15
15
|
private readonly mcpServers;
|
|
16
16
|
private readonly executors;
|
|
17
17
|
private readonly isStatelessMode;
|
|
18
|
-
constructor(options: McpOptions, mcpModuleId: string, moduleRef: ModuleRef, toolRegistry:
|
|
18
|
+
constructor(options: McpOptions, mcpModuleId: string, moduleRef: ModuleRef, toolRegistry: McpRegistryDiscoveryService);
|
|
19
19
|
createStatelessServer(rawReq: any): Promise<{
|
|
20
20
|
server: McpServer;
|
|
21
21
|
transport: StreamableHTTPServerTransport;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-streamable-http.service.d.ts","sourceRoot":"","sources":["../../../src/mcp/services/mcp-streamable-http.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC7E,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AAEnG,OAAO,EACL,WAAW,EACX,YAAY,EACb,MAAM,sCAAsC,CAAC;AAC9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"mcp-streamable-http.service.d.ts","sourceRoot":"","sources":["../../../src/mcp/services/mcp-streamable-http.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC7E,OAAO,EAAoB,SAAS,EAAE,MAAM,cAAc,CAAC;AAE3D,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AAEnG,OAAO,EACL,WAAW,EACX,YAAY,EACb,MAAM,sCAAsC,CAAC;AAC9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,OAAO,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAC;AAI/E,qBACa,wBAAyB,YAAW,eAAe;IAUrC,OAAO,CAAC,QAAQ,CAAC,OAAO;IACtB,OAAO,CAAC,QAAQ,CAAC,WAAW;IACrD,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAZ/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAEpB;IACP,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA0C;IACrE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAmD;IAC7E,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAU;gBAGA,OAAO,EAAE,UAAU,EACjB,WAAW,EAAE,MAAM,EAC5C,SAAS,EAAE,SAAS,EACpB,YAAY,EAAE,2BAA2B;IAUtD,qBAAqB,CAAC,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC;QAChD,MAAM,EAAE,SAAS,CAAC;QAClB,SAAS,EAAE,6BAA6B,CAAC;KAC1C,CAAC;IAsCI,iBAAiB,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAuCnE,sBAAsB,CAC1B,GAAG,EAAE,GAAG,EACR,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,OAAO,GACZ,OAAO,CAAC,IAAI,CAAC;IA6CV,qBAAqB,CACzB,GAAG,EAAE,WAAW,EAChB,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,OAAO,GACZ,OAAO,CAAC,IAAI,CAAC;IA4HV,gBAAgB,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAsDnD,mBAAmB,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAyD5D,OAAO,CAAC,mBAAmB;YAqBb,cAAc;IAgCtB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;CAUvC"}
|
|
@@ -20,7 +20,7 @@ const crypto_1 = require("crypto");
|
|
|
20
20
|
const streamableHttp_js_1 = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
|
|
21
21
|
const http_adapter_factory_1 = require("../adapters/http-adapter.factory");
|
|
22
22
|
const mcp_executor_service_1 = require("./mcp-executor.service");
|
|
23
|
-
const
|
|
23
|
+
const mcp_registry_discovery_service_1 = require("./mcp-registry-discovery.service");
|
|
24
24
|
const mcp_logger_factory_1 = require("../utils/mcp-logger.factory");
|
|
25
25
|
const mcp_server_factory_1 = require("../utils/mcp-server.factory");
|
|
26
26
|
let McpStreamableHttpService = McpStreamableHttpService_1 = class McpStreamableHttpService {
|
|
@@ -322,6 +322,6 @@ exports.McpStreamableHttpService = McpStreamableHttpService = McpStreamableHttpS
|
|
|
322
322
|
__param(0, (0, common_1.Inject)('MCP_OPTIONS')),
|
|
323
323
|
__param(1, (0, common_1.Inject)('MCP_MODULE_ID')),
|
|
324
324
|
__metadata("design:paramtypes", [Object, String, core_1.ModuleRef,
|
|
325
|
-
|
|
325
|
+
mcp_registry_discovery_service_1.McpRegistryDiscoveryService])
|
|
326
326
|
], McpStreamableHttpService);
|
|
327
327
|
//# sourceMappingURL=mcp-streamable-http.service.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-streamable-http.service.js","sourceRoot":"","sources":["../../../src/mcp/services/mcp-streamable-http.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Inject, Injectable, Logger, OnModuleDestroy } from '@nestjs/common';\nimport { ContextIdFactory, ModuleRef } from '@nestjs/core';\nimport { randomUUID } from 'crypto';\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';\nimport { HttpAdapterFactory } from '../adapters/http-adapter.factory';\nimport {\n HttpRequest,\n HttpResponse,\n} from '../interfaces/http-adapter.interface';\nimport type { McpOptions } from '../interfaces';\nimport { McpExecutorService } from './mcp-executor.service';\nimport { McpRegistryService } from './mcp-registry.service';\nimport { createMcpLogger } from '../utils/mcp-logger.factory';\nimport { createMcpServer } from '../utils/mcp-server.factory';\n\n@Injectable()\nexport class McpStreamableHttpService implements OnModuleDestroy {\n private readonly logger: Logger;\n private readonly transports: {\n [sessionId: string]: StreamableHTTPServerTransport;\n } = {};\n private readonly mcpServers: { [sessionId: string]: McpServer } = {};\n private readonly executors: { [sessionId: string]: McpExecutorService } = {};\n private readonly isStatelessMode: boolean;\n\n constructor(\n @Inject('MCP_OPTIONS') private readonly options: McpOptions,\n @Inject('MCP_MODULE_ID') private readonly mcpModuleId: string,\n private readonly moduleRef: ModuleRef,\n private readonly toolRegistry: McpRegistryService,\n ) {\n // Determine if we're in stateless mode\n this.isStatelessMode = !!options.streamableHttp?.statelessMode;\n this.logger = createMcpLogger(McpStreamableHttpService.name, this.options);\n }\n\n /**\n * Create a new MCP server instance for stateless requests\n */\n async createStatelessServer(rawReq: any): Promise<{\n server: McpServer;\n transport: StreamableHTTPServerTransport;\n }> {\n // Create a new transport for this request (stateless = no session management)\n const transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n enableJsonResponse:\n this.options.streamableHttp?.enableJsonResponse || false,\n });\n\n const server = createMcpServer(\n this.mcpModuleId,\n this.toolRegistry,\n this.options,\n this.logger,\n );\n\n // Connect the transport to the MCP server first\n await server.connect(transport);\n\n // Now resolve the request-scoped tool executor service\n const contextId = ContextIdFactory.getByRequest(rawReq);\n const executor = await this.moduleRef.resolve(\n McpExecutorService,\n contextId,\n { strict: true },\n );\n\n // Register request handlers after connection\n this.logger.debug(\n '[Stateless] Registering request handlers for stateless MCP server',\n );\n executor.registerRequestHandlers(server, rawReq);\n\n return { server, transport };\n }\n\n /**\n * Handle POST requests\n */\n async handlePostRequest(req: any, res: any, body: unknown): Promise<void> {\n // Get the appropriate HTTP adapter for the request/response\n const adapter = HttpAdapterFactory.getAdapter(req, res);\n const adaptedReq = adapter.adaptRequest(req);\n const adaptedRes = adapter.adaptResponse(res);\n const sessionId = adaptedReq.headers['mcp-session-id'] as\n | string\n | undefined;\n\n this.logger.debug(\n `[${sessionId || 'No-Session'}] Received MCP request: ${JSON.stringify(body)}`,\n );\n\n try {\n if (this.isStatelessMode) {\n return this.handleStatelessRequest(adaptedReq, adaptedRes, body);\n } else {\n return this.handleStatefulRequest(adaptedReq, adaptedRes, body);\n }\n } catch (error) {\n this.logger.error(\n `[${sessionId || 'No-Session'}] Error handling MCP request: ${error}`,\n );\n if (!adaptedRes.headersSent) {\n adaptedRes.status(500).json({\n jsonrpc: '2.0',\n error: {\n code: -32603,\n message: 'Internal server error',\n },\n id: null,\n });\n }\n }\n }\n\n /**\n * Handle requests in stateless mode\n */\n async handleStatelessRequest(\n req: any,\n res: HttpResponse,\n body: unknown,\n ): Promise<void> {\n this.logger.debug(\n `[Stateless] Handling stateless MCP request at ${req.url}`,\n );\n\n let server: McpServer | null = null;\n let transport: StreamableHTTPServerTransport | null = null;\n\n try {\n // Create a new server and transport for each request\n const stateless = await this.createStatelessServer(req);\n server = stateless.server;\n transport = stateless.transport;\n\n // Handle the request\n await transport.handleRequest(req.raw, res.raw, body);\n\n // Clean up after response is sent\n res.raw.on('finish', async () => {\n this.logger.debug('[Stateless] Response sent, cleaning up');\n try {\n if (transport) await transport.close();\n if (server) await server.close();\n } catch (error) {\n this.logger.error('[Stateless] Error cleaning up:', error);\n }\n });\n } catch (error) {\n this.logger.error(\n `[Stateless] Error in stateless request handling: ${error}`,\n );\n // Clean up on error\n try {\n if (transport) await transport.close();\n if (server) await server.close();\n } catch (error) {\n this.logger.error('[Stateless] Error cleaning up on error:', error);\n }\n throw error;\n }\n }\n\n /**\n * Handle requests in stateful mode\n */\n async handleStatefulRequest(\n req: HttpRequest,\n res: HttpResponse,\n body: unknown,\n ): Promise<void> {\n const sessionId = req.headers['mcp-session-id'] as string | undefined;\n\n this.logger.debug(`[${sessionId || 'New'}] Handling stateful MCP request`);\n\n // Case 1: New initialization request\n if (!sessionId && this.isInitializeRequest(body)) {\n // Validate it's not a batch with multiple requests\n if (Array.isArray(body) && body.length > 1) {\n res.status(400).json({\n jsonrpc: '2.0',\n error: {\n code: -32600,\n message:\n 'Invalid Request: Only one initialization request is allowed',\n },\n id: null,\n });\n return;\n }\n\n const mcpServer = createMcpServer(\n this.mcpModuleId,\n this.toolRegistry,\n this.options,\n this.logger,\n );\n\n // Create transport with session management\n const transport = new StreamableHTTPServerTransport({\n sessionIdGenerator:\n this.options.streamableHttp?.sessionIdGenerator ||\n (() => randomUUID()),\n enableJsonResponse:\n this.options.streamableHttp?.enableJsonResponse || false,\n onsessioninitialized: async (sid: string) => {\n this.logger.debug(`[${sid}] Session initialized, storing references`);\n // Store all session data\n this.transports[sid] = transport;\n this.mcpServers[sid] = mcpServer;\n\n // Resolve and store the executor for this session\n const contextId = ContextIdFactory.getByRequest(req);\n const executor = await this.moduleRef.resolve(\n McpExecutorService,\n contextId,\n { strict: true },\n );\n this.executors[sid] = executor;\n\n // Register request handlers ONCE during initialization\n executor.registerRequestHandlers(mcpServer, req);\n },\n onsessionclosed: async (sid: string) => {\n this.logger.debug(`[${sid}] Session closed via DELETE`);\n await this.cleanupSession(sid);\n },\n });\n\n // Connect transport to server\n await mcpServer.connect(transport);\n\n // Handle the initialization request\n await transport.handleRequest(req.raw, res.raw, body);\n\n this.logger.log(`[${transport.sessionId}] New session initialized`);\n return;\n }\n\n // Case 2: Request with session ID\n if (sessionId) {\n // Check if session exists\n if (!this.transports[sessionId]) {\n this.logger.debug(`[${sessionId}] Session not found`);\n res.status(404).json({\n jsonrpc: '2.0',\n error: {\n code: -32001,\n message: 'Session not found',\n },\n id: null,\n });\n return;\n }\n\n // Reject re-initialization attempts\n if (this.isInitializeRequest(body)) {\n res.status(400).json({\n jsonrpc: '2.0',\n error: {\n code: -32600,\n message: 'Invalid Request: Server already initialized',\n },\n id: null,\n });\n return;\n }\n\n // Use existing transport\n const transport = this.transports[sessionId];\n\n this.logger.debug(\n `[${sessionId}] Handling request with existing session`,\n );\n\n // Handle the request with existing transport and handlers\n await transport.handleRequest(req.raw, res.raw, body);\n return;\n }\n\n // Case 3: No session ID and not initialization\n res.status(400).json({\n jsonrpc: '2.0',\n error: {\n code: -32000,\n message: 'Bad Request: Mcp-Session-Id header is required',\n },\n id: null,\n });\n }\n\n /**\n * Handle GET requests for SSE streams\n */\n async handleGetRequest(req: any, res: any): Promise<void> {\n const adapter = HttpAdapterFactory.getAdapter(req, res);\n const adaptedReq = adapter.adaptRequest(req);\n const adaptedRes = adapter.adaptResponse(res);\n\n if (this.isStatelessMode) {\n adaptedRes.status(405).json({\n jsonrpc: '2.0',\n error: {\n code: -32000,\n message: 'Method not allowed in stateless mode',\n },\n id: null,\n });\n return;\n }\n\n const sessionId = adaptedReq.headers['mcp-session-id'] as\n | string\n | undefined;\n\n if (!sessionId) {\n adaptedRes.status(400).json({\n jsonrpc: '2.0',\n error: {\n code: -32000,\n message: 'Bad Request: Mcp-Session-Id header is required',\n },\n id: null,\n });\n return;\n }\n\n if (!this.transports[sessionId]) {\n this.logger.debug(`[${sessionId}] GET request - session not found`);\n adaptedRes.status(404).json({\n jsonrpc: '2.0',\n error: {\n code: -32001,\n message: 'Session not found',\n },\n id: null,\n });\n return;\n }\n\n this.logger.debug(`[${sessionId}] Establishing SSE stream`);\n const transport = this.transports[sessionId];\n await transport.handleRequest(adaptedReq.raw, adaptedRes.raw);\n }\n\n /**\n * Handle DELETE requests for terminating sessions\n */\n async handleDeleteRequest(req: any, res: any): Promise<void> {\n const adapter = HttpAdapterFactory.getAdapter(req, res);\n const adaptedReq = adapter.adaptRequest(req);\n const adaptedRes = adapter.adaptResponse(res);\n\n if (this.isStatelessMode) {\n adaptedRes.status(405).json({\n jsonrpc: '2.0',\n error: {\n code: -32000,\n message: 'Method not allowed in stateless mode',\n },\n id: null,\n });\n return;\n }\n\n const sessionId = adaptedReq.headers['mcp-session-id'] as\n | string\n | undefined;\n\n if (!sessionId) {\n adaptedRes.status(400).json({\n jsonrpc: '2.0',\n error: {\n code: -32000,\n message: 'Bad Request: Mcp-Session-Id header is required',\n },\n id: null,\n });\n return;\n }\n\n if (!this.transports[sessionId]) {\n this.logger.debug(`[${sessionId}] DELETE request - session not found`);\n adaptedRes.status(404).json({\n jsonrpc: '2.0',\n error: {\n code: -32001,\n message: 'Session not found',\n },\n id: null,\n });\n return;\n }\n\n this.logger.debug(`[${sessionId}] Processing DELETE request`);\n const transport = this.transports[sessionId];\n\n // Let transport handle the DELETE request\n // The onsessionclosed callback will handle cleanup\n await transport.handleRequest(adaptedReq.raw, adaptedRes.raw);\n }\n\n /**\n * Helper function to detect initialize requests\n */\n private isInitializeRequest(body: unknown): boolean {\n if (Array.isArray(body)) {\n return body.some(\n (msg) =>\n typeof msg === 'object' &&\n msg !== null &&\n 'method' in msg &&\n msg.method === 'initialize',\n );\n }\n return (\n typeof body === 'object' &&\n body !== null &&\n 'method' in body &&\n (body as any).method === 'initialize'\n );\n }\n\n /**\n * Clean up session resources\n */\n private async cleanupSession(sessionId: string): Promise<void> {\n if (!sessionId || !this.transports[sessionId]) {\n return;\n }\n\n this.logger.debug(`[${sessionId}] Cleaning up session`);\n\n try {\n // Close transport if still open\n const transport = this.transports[sessionId];\n if (transport) {\n await transport.close();\n }\n\n // Close MCP server\n const server = this.mcpServers[sessionId];\n if (server) {\n await server.close();\n }\n\n // Clean up all references\n delete this.transports[sessionId];\n delete this.mcpServers[sessionId];\n delete this.executors[sessionId];\n } catch (error) {\n this.logger.error(`[${sessionId}] Error during cleanup:`, error);\n }\n }\n\n /**\n * Clean up all sessions on module destroy\n */\n async onModuleDestroy(): Promise<void> {\n this.logger.log('Cleaning up all MCP sessions...');\n const sessionIds = Object.keys(this.transports);\n\n await Promise.all(\n sessionIds.map((sessionId) => this.cleanupSession(sessionId)),\n );\n\n this.logger.log(`Cleaned up ${sessionIds.length} MCP sessions`);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"mcp-streamable-http.service.js","sourceRoot":"","sources":["../../../src/mcp/services/mcp-streamable-http.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Inject, Injectable, Logger, OnModuleDestroy } from '@nestjs/common';\nimport { ContextIdFactory, ModuleRef } from '@nestjs/core';\nimport { randomUUID } from 'crypto';\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';\nimport { HttpAdapterFactory } from '../adapters/http-adapter.factory';\nimport {\n HttpRequest,\n HttpResponse,\n} from '../interfaces/http-adapter.interface';\nimport type { McpOptions } from '../interfaces';\nimport { McpExecutorService } from './mcp-executor.service';\nimport { McpRegistryDiscoveryService } from './mcp-registry-discovery.service';\nimport { createMcpLogger } from '../utils/mcp-logger.factory';\nimport { createMcpServer } from '../utils/mcp-server.factory';\n\n@Injectable()\nexport class McpStreamableHttpService implements OnModuleDestroy {\n private readonly logger: Logger;\n private readonly transports: {\n [sessionId: string]: StreamableHTTPServerTransport;\n } = {};\n private readonly mcpServers: { [sessionId: string]: McpServer } = {};\n private readonly executors: { [sessionId: string]: McpExecutorService } = {};\n private readonly isStatelessMode: boolean;\n\n constructor(\n @Inject('MCP_OPTIONS') private readonly options: McpOptions,\n @Inject('MCP_MODULE_ID') private readonly mcpModuleId: string,\n private readonly moduleRef: ModuleRef,\n private readonly toolRegistry: McpRegistryDiscoveryService,\n ) {\n // Determine if we're in stateless mode\n this.isStatelessMode = !!options.streamableHttp?.statelessMode;\n this.logger = createMcpLogger(McpStreamableHttpService.name, this.options);\n }\n\n /**\n * Create a new MCP server instance for stateless requests\n */\n async createStatelessServer(rawReq: any): Promise<{\n server: McpServer;\n transport: StreamableHTTPServerTransport;\n }> {\n // Create a new transport for this request (stateless = no session management)\n const transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n enableJsonResponse:\n this.options.streamableHttp?.enableJsonResponse || false,\n });\n\n const server = createMcpServer(\n this.mcpModuleId,\n this.toolRegistry,\n this.options,\n this.logger,\n );\n\n // Connect the transport to the MCP server first\n await server.connect(transport);\n\n // Now resolve the request-scoped tool executor service\n const contextId = ContextIdFactory.getByRequest(rawReq);\n const executor = await this.moduleRef.resolve(\n McpExecutorService,\n contextId,\n { strict: true },\n );\n\n // Register request handlers after connection\n this.logger.debug(\n '[Stateless] Registering request handlers for stateless MCP server',\n );\n executor.registerRequestHandlers(server, rawReq);\n\n return { server, transport };\n }\n\n /**\n * Handle POST requests\n */\n async handlePostRequest(req: any, res: any, body: unknown): Promise<void> {\n // Get the appropriate HTTP adapter for the request/response\n const adapter = HttpAdapterFactory.getAdapter(req, res);\n const adaptedReq = adapter.adaptRequest(req);\n const adaptedRes = adapter.adaptResponse(res);\n const sessionId = adaptedReq.headers['mcp-session-id'] as\n | string\n | undefined;\n\n this.logger.debug(\n `[${sessionId || 'No-Session'}] Received MCP request: ${JSON.stringify(body)}`,\n );\n\n try {\n if (this.isStatelessMode) {\n return this.handleStatelessRequest(adaptedReq, adaptedRes, body);\n } else {\n return this.handleStatefulRequest(adaptedReq, adaptedRes, body);\n }\n } catch (error) {\n this.logger.error(\n `[${sessionId || 'No-Session'}] Error handling MCP request: ${error}`,\n );\n if (!adaptedRes.headersSent) {\n adaptedRes.status(500).json({\n jsonrpc: '2.0',\n error: {\n code: -32603,\n message: 'Internal server error',\n },\n id: null,\n });\n }\n }\n }\n\n /**\n * Handle requests in stateless mode\n */\n async handleStatelessRequest(\n req: any,\n res: HttpResponse,\n body: unknown,\n ): Promise<void> {\n this.logger.debug(\n `[Stateless] Handling stateless MCP request at ${req.url}`,\n );\n\n let server: McpServer | null = null;\n let transport: StreamableHTTPServerTransport | null = null;\n\n try {\n // Create a new server and transport for each request\n const stateless = await this.createStatelessServer(req);\n server = stateless.server;\n transport = stateless.transport;\n\n // Handle the request\n await transport.handleRequest(req.raw, res.raw, body);\n\n // Clean up after response is sent\n res.raw.on('finish', async () => {\n this.logger.debug('[Stateless] Response sent, cleaning up');\n try {\n if (transport) await transport.close();\n if (server) await server.close();\n } catch (error) {\n this.logger.error('[Stateless] Error cleaning up:', error);\n }\n });\n } catch (error) {\n this.logger.error(\n `[Stateless] Error in stateless request handling: ${error}`,\n );\n // Clean up on error\n try {\n if (transport) await transport.close();\n if (server) await server.close();\n } catch (error) {\n this.logger.error('[Stateless] Error cleaning up on error:', error);\n }\n throw error;\n }\n }\n\n /**\n * Handle requests in stateful mode\n */\n async handleStatefulRequest(\n req: HttpRequest,\n res: HttpResponse,\n body: unknown,\n ): Promise<void> {\n const sessionId = req.headers['mcp-session-id'] as string | undefined;\n\n this.logger.debug(`[${sessionId || 'New'}] Handling stateful MCP request`);\n\n // Case 1: New initialization request\n if (!sessionId && this.isInitializeRequest(body)) {\n // Validate it's not a batch with multiple requests\n if (Array.isArray(body) && body.length > 1) {\n res.status(400).json({\n jsonrpc: '2.0',\n error: {\n code: -32600,\n message:\n 'Invalid Request: Only one initialization request is allowed',\n },\n id: null,\n });\n return;\n }\n\n const mcpServer = createMcpServer(\n this.mcpModuleId,\n this.toolRegistry,\n this.options,\n this.logger,\n );\n\n // Create transport with session management\n const transport = new StreamableHTTPServerTransport({\n sessionIdGenerator:\n this.options.streamableHttp?.sessionIdGenerator ||\n (() => randomUUID()),\n enableJsonResponse:\n this.options.streamableHttp?.enableJsonResponse || false,\n onsessioninitialized: async (sid: string) => {\n this.logger.debug(`[${sid}] Session initialized, storing references`);\n // Store all session data\n this.transports[sid] = transport;\n this.mcpServers[sid] = mcpServer;\n\n // Resolve and store the executor for this session\n const contextId = ContextIdFactory.getByRequest(req);\n const executor = await this.moduleRef.resolve(\n McpExecutorService,\n contextId,\n { strict: true },\n );\n this.executors[sid] = executor;\n\n // Register request handlers ONCE during initialization\n executor.registerRequestHandlers(mcpServer, req);\n },\n onsessionclosed: async (sid: string) => {\n this.logger.debug(`[${sid}] Session closed via DELETE`);\n await this.cleanupSession(sid);\n },\n });\n\n // Connect transport to server\n await mcpServer.connect(transport);\n\n // Handle the initialization request\n await transport.handleRequest(req.raw, res.raw, body);\n\n this.logger.log(`[${transport.sessionId}] New session initialized`);\n return;\n }\n\n // Case 2: Request with session ID\n if (sessionId) {\n // Check if session exists\n if (!this.transports[sessionId]) {\n this.logger.debug(`[${sessionId}] Session not found`);\n res.status(404).json({\n jsonrpc: '2.0',\n error: {\n code: -32001,\n message: 'Session not found',\n },\n id: null,\n });\n return;\n }\n\n // Reject re-initialization attempts\n if (this.isInitializeRequest(body)) {\n res.status(400).json({\n jsonrpc: '2.0',\n error: {\n code: -32600,\n message: 'Invalid Request: Server already initialized',\n },\n id: null,\n });\n return;\n }\n\n // Use existing transport\n const transport = this.transports[sessionId];\n\n this.logger.debug(\n `[${sessionId}] Handling request with existing session`,\n );\n\n // Handle the request with existing transport and handlers\n await transport.handleRequest(req.raw, res.raw, body);\n return;\n }\n\n // Case 3: No session ID and not initialization\n res.status(400).json({\n jsonrpc: '2.0',\n error: {\n code: -32000,\n message: 'Bad Request: Mcp-Session-Id header is required',\n },\n id: null,\n });\n }\n\n /**\n * Handle GET requests for SSE streams\n */\n async handleGetRequest(req: any, res: any): Promise<void> {\n const adapter = HttpAdapterFactory.getAdapter(req, res);\n const adaptedReq = adapter.adaptRequest(req);\n const adaptedRes = adapter.adaptResponse(res);\n\n if (this.isStatelessMode) {\n adaptedRes.status(405).json({\n jsonrpc: '2.0',\n error: {\n code: -32000,\n message: 'Method not allowed in stateless mode',\n },\n id: null,\n });\n return;\n }\n\n const sessionId = adaptedReq.headers['mcp-session-id'] as\n | string\n | undefined;\n\n if (!sessionId) {\n adaptedRes.status(400).json({\n jsonrpc: '2.0',\n error: {\n code: -32000,\n message: 'Bad Request: Mcp-Session-Id header is required',\n },\n id: null,\n });\n return;\n }\n\n if (!this.transports[sessionId]) {\n this.logger.debug(`[${sessionId}] GET request - session not found`);\n adaptedRes.status(404).json({\n jsonrpc: '2.0',\n error: {\n code: -32001,\n message: 'Session not found',\n },\n id: null,\n });\n return;\n }\n\n this.logger.debug(`[${sessionId}] Establishing SSE stream`);\n const transport = this.transports[sessionId];\n await transport.handleRequest(adaptedReq.raw, adaptedRes.raw);\n }\n\n /**\n * Handle DELETE requests for terminating sessions\n */\n async handleDeleteRequest(req: any, res: any): Promise<void> {\n const adapter = HttpAdapterFactory.getAdapter(req, res);\n const adaptedReq = adapter.adaptRequest(req);\n const adaptedRes = adapter.adaptResponse(res);\n\n if (this.isStatelessMode) {\n adaptedRes.status(405).json({\n jsonrpc: '2.0',\n error: {\n code: -32000,\n message: 'Method not allowed in stateless mode',\n },\n id: null,\n });\n return;\n }\n\n const sessionId = adaptedReq.headers['mcp-session-id'] as\n | string\n | undefined;\n\n if (!sessionId) {\n adaptedRes.status(400).json({\n jsonrpc: '2.0',\n error: {\n code: -32000,\n message: 'Bad Request: Mcp-Session-Id header is required',\n },\n id: null,\n });\n return;\n }\n\n if (!this.transports[sessionId]) {\n this.logger.debug(`[${sessionId}] DELETE request - session not found`);\n adaptedRes.status(404).json({\n jsonrpc: '2.0',\n error: {\n code: -32001,\n message: 'Session not found',\n },\n id: null,\n });\n return;\n }\n\n this.logger.debug(`[${sessionId}] Processing DELETE request`);\n const transport = this.transports[sessionId];\n\n // Let transport handle the DELETE request\n // The onsessionclosed callback will handle cleanup\n await transport.handleRequest(adaptedReq.raw, adaptedRes.raw);\n }\n\n /**\n * Helper function to detect initialize requests\n */\n private isInitializeRequest(body: unknown): boolean {\n if (Array.isArray(body)) {\n return body.some(\n (msg) =>\n typeof msg === 'object' &&\n msg !== null &&\n 'method' in msg &&\n msg.method === 'initialize',\n );\n }\n return (\n typeof body === 'object' &&\n body !== null &&\n 'method' in body &&\n (body as any).method === 'initialize'\n );\n }\n\n /**\n * Clean up session resources\n */\n private async cleanupSession(sessionId: string): Promise<void> {\n if (!sessionId || !this.transports[sessionId]) {\n return;\n }\n\n this.logger.debug(`[${sessionId}] Cleaning up session`);\n\n try {\n // Close transport if still open\n const transport = this.transports[sessionId];\n if (transport) {\n await transport.close();\n }\n\n // Close MCP server\n const server = this.mcpServers[sessionId];\n if (server) {\n await server.close();\n }\n\n // Clean up all references\n delete this.transports[sessionId];\n delete this.mcpServers[sessionId];\n delete this.executors[sessionId];\n } catch (error) {\n this.logger.error(`[${sessionId}] Error during cleanup:`, error);\n }\n }\n\n /**\n * Clean up all sessions on module destroy\n */\n async onModuleDestroy(): Promise<void> {\n this.logger.log('Cleaning up all MCP sessions...');\n const sessionIds = Object.keys(this.transports);\n\n await Promise.all(\n sessionIds.map((sessionId) => this.cleanupSession(sessionId)),\n );\n\n this.logger.log(`Cleaned up ${sessionIds.length} MCP sessions`);\n }\n}\n"]}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DiscoveredCapability } from './mcp-registry-discovery.service';
|
|
2
2
|
import { ToolMetadata, SecurityScheme } from '../decorators/tool.decorator';
|
|
3
3
|
import { JwtPayload } from '../../authz/services/jwt-token.service';
|
|
4
4
|
export declare class ToolAuthorizationService {
|
|
5
|
-
generateSecuritySchemes(tool:
|
|
6
|
-
canAccessTool(user: JwtPayload | undefined, tool:
|
|
7
|
-
validateToolAccess(user: JwtPayload | undefined, tool:
|
|
5
|
+
generateSecuritySchemes(tool: DiscoveredCapability<ToolMetadata>, moduleHasGuards: boolean): SecurityScheme[];
|
|
6
|
+
canAccessTool(user: JwtPayload | undefined, tool: DiscoveredCapability<ToolMetadata>, moduleHasGuards: boolean, allowUnauthenticatedAccess?: boolean): boolean;
|
|
7
|
+
validateToolAccess(user: JwtPayload | undefined, tool: DiscoveredCapability<ToolMetadata>, moduleHasGuards: boolean, allowUnauthenticatedAccess?: boolean): void;
|
|
8
8
|
private hasRequiredScopes;
|
|
9
9
|
private hasRequiredRoles;
|
|
10
10
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-authorization.service.d.ts","sourceRoot":"","sources":["../../../src/mcp/services/tool-authorization.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"tool-authorization.service.d.ts","sourceRoot":"","sources":["../../../src/mcp/services/tool-authorization.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC5E,OAAO,EAAE,UAAU,EAAE,MAAM,wCAAwC,CAAC;AAKpE,qBACa,wBAAwB;IAQnC,uBAAuB,CACrB,IAAI,EAAE,oBAAoB,CAAC,YAAY,CAAC,EACxC,eAAe,EAAE,OAAO,GACvB,cAAc,EAAE;IAmCnB,aAAa,CACX,IAAI,EAAE,UAAU,GAAG,SAAS,EAC5B,IAAI,EAAE,oBAAoB,CAAC,YAAY,CAAC,EACxC,eAAe,EAAE,OAAO,EACxB,0BAA0B,GAAE,OAAe,GAC1C,OAAO;IAuEV,kBAAkB,CAChB,IAAI,EAAE,UAAU,GAAG,SAAS,EAC5B,IAAI,EAAE,oBAAoB,CAAC,YAAY,CAAC,EACxC,eAAe,EAAE,OAAO,EACxB,0BAA0B,GAAE,OAAe,GAC1C,IAAI;IAiFP,OAAO,CAAC,iBAAiB;IA8BzB,OAAO,CAAC,gBAAgB;CAwBzB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-authorization.service.js","sourceRoot":"","sources":["../../../src/mcp/services/tool-authorization.service.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAA4C;AAC5C,iEAAyE;AASlE,IAAM,wBAAwB,GAA9B,MAAM,wBAAwB;IAQnC,uBAAuB,CACrB,IAAkC,EAClC,eAAwB;QAExB,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC/B,MAAM,OAAO,GAAqB,EAAE,CAAC;QAGrC,IAAI,QAAQ,CAAC,QAAQ,EAAE,CAAC;YACtB,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;QACnC,CAAC;QAGD,IAAI,QAAQ,CAAC,cAAc,IAAI,QAAQ,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAClE,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,cAAc,EAAE,CAAC,CAAC;QACpE,CAAC;aAEI,IAAI,eAAe,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC;YAC/C,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;QACnC,CAAC;QAGD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;QACnC,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAWD,aAAa,CACX,IAA4B,EAC5B,IAAkC,EAClC,eAAwB,EACxB,6BAAsC,KAAK;QAE3C,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;QAG/B,IAAI,QAAQ,CAAC,QAAQ,EAAE,CAAC;YACtB,OAAO,IAAI,CAAC;QACd,CAAC;QAGD,MAAM,uBAAuB,GAC3B,CAAC,QAAQ,CAAC,cAAc,IAAI,QAAQ,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,CAAC;YAC/D,CAAC,QAAQ,CAAC,aAAa,IAAI,QAAQ,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAEhE,IAAI,uBAAuB,IAAI,CAAC,IAAI,EAAE,CAAC;YACrC,OAAO,KAAK,CAAC;QACf,CAAC;QAGD,IAAI,QAAQ,CAAC,cAAc,IAAI,QAAQ,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAClE,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,IAAI,EAAE,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;gBAC3D,OAAO,KAAK,CAAC;YACf,CAAC;QACH,CAAC;QAGD,IAAI,QAAQ,CAAC,aAAa,IAAI,QAAQ,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAChE,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,IAAI,EAAE,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;gBACzD,OAAO,KAAK,CAAC;YACf,CAAC;QACH,CAAC;QAoBD,IAAI,0BAA0B,IAAI,eAAe,IAAI,CAAC,IAAI,EAAE,CAAC;YAG3D,OAAO,KAAK,CAAC;QACf,CAAC;QAMD,OAAO,IAAI,CAAC;IACd,CAAC;IAWD,kBAAkB,CAChB,IAA4B,EAC5B,IAAkC,EAClC,eAAwB,EACxB,6BAAsC,KAAK;QAE3C,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC/B,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC;QAG/B,IAAI,QAAQ,CAAC,QAAQ,EAAE,CAAC;YACtB,OAAO;QACT,CAAC;QAGD,MAAM,uBAAuB,GAC3B,CAAC,QAAQ,CAAC,cAAc,IAAI,QAAQ,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,CAAC;YAC/D,CAAC,QAAQ,CAAC,aAAa,IAAI,QAAQ,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAEhE,IAAI,uBAAuB,IAAI,CAAC,IAAI,EAAE,CAAC;YACrC,MAAM,IAAI,mBAAQ,CAChB,oBAAS,CAAC,cAAc,EACxB,SAAS,QAAQ,2BAA2B,CAC7C,CAAC;QACJ,CAAC;QAGD,IAAI,QAAQ,CAAC,cAAc,IAAI,QAAQ,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAClE,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,IAAI,EAAE,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;gBAC3D,MAAM,IAAI,mBAAQ,CAChB,oBAAS,CAAC,cAAc,EACxB,SAAS,QAAQ,sBAAsB,QAAQ,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC5E,CAAC;YACJ,CAAC;QACH,CAAC;QAGD,IAAI,QAAQ,CAAC,aAAa,IAAI,QAAQ,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAChE,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,IAAI,EAAE,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;gBACzD,MAAM,IAAI,mBAAQ,CAChB,oBAAS,CAAC,cAAc,EACxB,SAAS,QAAQ,qBAAqB,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC1E,CAAC;YACJ,CAAC;QACH,CAAC;QAoBD,IAAI,0BAA0B,IAAI,eAAe,IAAI,CAAC,IAAI,EAAE,CAAC;YAG3D,MAAM,IAAI,mBAAQ,CAChB,oBAAS,CAAC,cAAc,EACxB,SAAS,QAAQ,2BAA2B,CAC7C,CAAC;QACJ,CAAC;IAMH,CAAC;IASO,iBAAiB,CACvB,IAA4B,EAC5B,cAAwB;QAExB,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO,KAAK,CAAC;QACf,CAAC;QAGD,IAAI,UAAU,GAAa,EAAE,CAAC;QAE9B,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAEf,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACjE,CAAC;aAAM,IAAK,IAAY,CAAC,MAAM,IAAI,KAAK,CAAC,OAAO,CAAE,IAAY,CAAC,MAAM,CAAC,EAAE,CAAC;YAEvE,UAAU,GAAI,IAAY,CAAC,MAAM,CAAC;QACpC,CAAC;QAGD,OAAO,cAAc,CAAC,KAAK,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC3E,CAAC;IASO,gBAAgB,CACtB,IAA4B,EAC5B,aAAuB;QAEvB,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO,KAAK,CAAC;QACf,CAAC;QAGD,IAAI,SAAS,GAAa,EAAE,CAAC;QAE7B,IAAK,IAAY,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,CAAE,IAAY,CAAC,KAAK,CAAC,EAAE,CAAC;YAC9D,SAAS,GAAI,IAAY,CAAC,KAAK,CAAC;QAClC,CAAC;aAAM,IACL,IAAI,CAAC,SAAS;YACd,IAAI,CAAC,SAAS,CAAC,KAAK;YACpB,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EACnC,CAAC;YACD,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;QACnC,CAAC;QAGD,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;IACzE,CAAC;CACF,CAAA;AAtQY,4DAAwB;mCAAxB,wBAAwB;IADpC,IAAA,mBAAU,GAAE;GACA,wBAAwB,CAsQpC","sourcesContent":["import { Injectable } from '@nestjs/common';\nimport { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';\nimport { DiscoveredTool } from './mcp-registry.service';\nimport { ToolMetadata, SecurityScheme } from '../decorators/tool.decorator';\nimport { JwtPayload } from '../../authz/services/jwt-token.service';\n\n/**\n * Service responsible for tool-level authorization logic\n */\n@Injectable()\nexport class ToolAuthorizationService {\n /**\n * Generate security schemes for a tool based on its metadata and module configuration\n *\n * @param tool - The discovered tool\n * @param moduleHasGuards - Whether the module has guards configured\n * @returns Array of security schemes for the tool\n */\n generateSecuritySchemes(\n tool: DiscoveredTool<ToolMetadata>,\n moduleHasGuards: boolean,\n ): SecurityScheme[] {\n const metadata = tool.metadata;\n const schemes: SecurityScheme[] = [];\n\n // If tool is marked as @PublicTool(), it can be accessed without auth\n if (metadata.isPublic) {\n schemes.push({ type: 'noauth' });\n }\n\n // If tool has required scopes, add oauth2 scheme with those scopes\n if (metadata.requiredScopes && metadata.requiredScopes.length > 0) {\n schemes.push({ type: 'oauth2', scopes: metadata.requiredScopes });\n }\n // Else if module has guards and tool is not public, require oauth2\n else if (moduleHasGuards && !metadata.isPublic) {\n schemes.push({ type: 'oauth2' });\n }\n\n // If no schemes were added, tool is accessible without authentication\n if (schemes.length === 0) {\n schemes.push({ type: 'noauth' });\n }\n\n return schemes;\n }\n\n /**\n * Check if a user can access a tool based on security requirements\n *\n * @param user - The authenticated user (may be undefined)\n * @param tool - The discovered tool\n * @param moduleHasGuards - Whether the module has guards configured\n * @param allowUnauthenticatedAccess - Whether unauthenticated access is allowed (freemium mode)\n * @returns true if user can access the tool, false otherwise\n */\n canAccessTool(\n user: JwtPayload | undefined,\n tool: DiscoveredTool<ToolMetadata>,\n moduleHasGuards: boolean,\n allowUnauthenticatedAccess: boolean = false,\n ): boolean {\n const metadata = tool.metadata;\n\n // If tool is public, always allow access\n if (metadata.isPublic) {\n return true;\n }\n\n // If tool has specific scope/role requirements, user MUST be authenticated\n const hasSpecificRequirements =\n (metadata.requiredScopes && metadata.requiredScopes.length > 0) ||\n (metadata.requiredRoles && metadata.requiredRoles.length > 0);\n\n if (hasSpecificRequirements && !user) {\n return false;\n }\n\n // If tool has specific scopes, validate them\n if (metadata.requiredScopes && metadata.requiredScopes.length > 0) {\n if (!this.hasRequiredScopes(user, metadata.requiredScopes)) {\n return false;\n }\n }\n\n // If tool has specific roles, validate them\n if (metadata.requiredRoles && metadata.requiredRoles.length > 0) {\n if (!this.hasRequiredRoles(user, metadata.requiredRoles)) {\n return false;\n }\n }\n\n // At this point:\n // - Tool is not public\n // - Tool has no specific scope/role requirements (or they passed)\n //\n // Decision logic based on allowUnauthenticatedAccess:\n //\n // allowUnauthenticatedAccess = false (default, standard auth mode):\n // - Guards are expected to fully authorize requests (via JWT, API keys, etc)\n // - If guard let request through AND there's no user object:\n // * Guard used non-JWT auth mechanism (API key, IP whitelist, etc)\n // * Trust the guard's decision to authorize\n // - If no guards configured, allow access (no auth required)\n //\n // allowUnauthenticatedAccess = true (freemium mode):\n // - Guards may let unauthenticated requests through for per-tool auth\n // - Only @PublicTool or tools with specific scopes/roles accessible\n // - Tools without decorators require authentication (user must be present)\n //\n if (allowUnauthenticatedAccess && moduleHasGuards && !user) {\n // Freemium mode: unauthenticated access only for @PublicTool or specific scopes\n // This tool has no decorators, so it requires authentication\n return false;\n }\n\n // Standard mode: If we're here, either:\n // - No guards (open access)\n // - Guards authorized it (trust guard decision, even without user object)\n // - User is present and passed all checks\n return true;\n }\n\n /**\n * Validate that a user can access a tool, throwing an error if not authorized\n *\n * @param user - The authenticated user (may be undefined)\n * @param tool - The discovered tool\n * @param moduleHasGuards - Whether the module has guards configured\n * @param allowUnauthenticatedAccess - Whether unauthenticated access is allowed (freemium mode)\n * @throws McpError if user is not authorized to access the tool\n */\n validateToolAccess(\n user: JwtPayload | undefined,\n tool: DiscoveredTool<ToolMetadata>,\n moduleHasGuards: boolean,\n allowUnauthenticatedAccess: boolean = false,\n ): void {\n const metadata = tool.metadata;\n const toolName = metadata.name;\n\n // If tool is public, allow access\n if (metadata.isPublic) {\n return;\n }\n\n // Check if tool has specific scope/role requirements\n const hasSpecificRequirements =\n (metadata.requiredScopes && metadata.requiredScopes.length > 0) ||\n (metadata.requiredRoles && metadata.requiredRoles.length > 0);\n\n if (hasSpecificRequirements && !user) {\n throw new McpError(\n ErrorCode.InvalidRequest,\n `Tool '${toolName}' requires authentication`,\n );\n }\n\n // Validate scopes if required\n if (metadata.requiredScopes && metadata.requiredScopes.length > 0) {\n if (!this.hasRequiredScopes(user, metadata.requiredScopes)) {\n throw new McpError(\n ErrorCode.InvalidRequest,\n `Tool '${toolName}' requires scopes: ${metadata.requiredScopes.join(', ')}`,\n );\n }\n }\n\n // Validate roles if required\n if (metadata.requiredRoles && metadata.requiredRoles.length > 0) {\n if (!this.hasRequiredRoles(user, metadata.requiredRoles)) {\n throw new McpError(\n ErrorCode.InvalidRequest,\n `Tool '${toolName}' requires roles: ${metadata.requiredRoles.join(', ')}`,\n );\n }\n }\n\n // At this point:\n // - Tool is not public\n // - Tool has no specific scope/role requirements (or they passed)\n //\n // Decision logic based on allowUnauthenticatedAccess:\n //\n // allowUnauthenticatedAccess = false (default, standard auth mode):\n // - Guards are expected to fully authorize requests (via JWT, API keys, etc)\n // - If guard let request through AND there's no user object:\n // * Guard used non-JWT auth mechanism (API key, IP whitelist, etc)\n // * Trust the guard's decision to authorize\n // - If no guards configured, allow access (no auth required)\n //\n // allowUnauthenticatedAccess = true (freemium mode):\n // - Guards may let unauthenticated requests through for per-tool auth\n // - Only @PublicTool or tools with specific scopes/roles accessible\n // - Tools without decorators require authentication (user must be present)\n //\n if (allowUnauthenticatedAccess && moduleHasGuards && !user) {\n // Freemium mode: unauthenticated access only for @PublicTool or specific scopes\n // This tool has no decorators, so it requires authentication\n throw new McpError(\n ErrorCode.InvalidRequest,\n `Tool '${toolName}' requires authentication`,\n );\n }\n\n // Standard mode: If we're here, either:\n // - No guards (open access)\n // - Guards authorized it (trust guard decision, even without user object)\n // - User is present and passed all checks\n }\n\n /**\n * Check if user has all required scopes\n *\n * @param user - The authenticated user (may be undefined)\n * @param requiredScopes - Array of required scope strings\n * @returns true if user has all required scopes\n */\n private hasRequiredScopes(\n user: JwtPayload | undefined,\n requiredScopes: string[],\n ): boolean {\n if (!user) {\n return false;\n }\n\n // Get user scopes - could be in 'scope' (space-delimited string) or 'scopes' (array)\n let userScopes: string[] = [];\n\n if (user.scope) {\n // OAuth 2.0 standard: space-delimited string\n userScopes = user.scope.split(' ').filter((s) => s.length > 0);\n } else if ((user as any).scopes && Array.isArray((user as any).scopes)) {\n // Alternative: array of scopes\n userScopes = (user as any).scopes;\n }\n\n // Check if user has ALL required scopes\n return requiredScopes.every((required) => userScopes.includes(required));\n }\n\n /**\n * Check if user has all required roles\n *\n * @param user - The authenticated user (may be undefined)\n * @param requiredRoles - Array of required role strings\n * @returns true if user has all required roles\n */\n private hasRequiredRoles(\n user: JwtPayload | undefined,\n requiredRoles: string[],\n ): boolean {\n if (!user) {\n return false;\n }\n\n // Get user roles from user data\n let userRoles: string[] = [];\n\n if ((user as any).roles && Array.isArray((user as any).roles)) {\n userRoles = (user as any).roles;\n } else if (\n user.user_data &&\n user.user_data.roles &&\n Array.isArray(user.user_data.roles)\n ) {\n userRoles = user.user_data.roles;\n }\n\n // Check if user has ALL required roles\n return requiredRoles.every((required) => userRoles.includes(required));\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"tool-authorization.service.js","sourceRoot":"","sources":["../../../src/mcp/services/tool-authorization.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Injectable } from '@nestjs/common';\nimport { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';\nimport { DiscoveredCapability } from './mcp-registry-discovery.service';\nimport { ToolMetadata, SecurityScheme } from '../decorators/tool.decorator';\nimport { JwtPayload } from '../../authz/services/jwt-token.service';\n\n/**\n * Service responsible for tool-level authorization logic\n */\n@Injectable()\nexport class ToolAuthorizationService {\n /**\n * Generate security schemes for a tool based on its metadata and module configuration\n *\n * @param tool - The discovered tool\n * @param moduleHasGuards - Whether the module has guards configured\n * @returns Array of security schemes for the tool\n */\n generateSecuritySchemes(\n tool: DiscoveredCapability<ToolMetadata>,\n moduleHasGuards: boolean,\n ): SecurityScheme[] {\n const metadata = tool.metadata;\n const schemes: SecurityScheme[] = [];\n\n // If tool is marked as @PublicTool(), it can be accessed without auth\n if (metadata.isPublic) {\n schemes.push({ type: 'noauth' });\n }\n\n // If tool has required scopes, add oauth2 scheme with those scopes\n if (metadata.requiredScopes && metadata.requiredScopes.length > 0) {\n schemes.push({ type: 'oauth2', scopes: metadata.requiredScopes });\n }\n // Else if module has guards and tool is not public, require oauth2\n else if (moduleHasGuards && !metadata.isPublic) {\n schemes.push({ type: 'oauth2' });\n }\n\n // If no schemes were added, tool is accessible without authentication\n if (schemes.length === 0) {\n schemes.push({ type: 'noauth' });\n }\n\n return schemes;\n }\n\n /**\n * Check if a user can access a tool based on security requirements\n *\n * @param user - The authenticated user (may be undefined)\n * @param tool - The discovered tool\n * @param moduleHasGuards - Whether the module has guards configured\n * @param allowUnauthenticatedAccess - Whether unauthenticated access is allowed (freemium mode)\n * @returns true if user can access the tool, false otherwise\n */\n canAccessTool(\n user: JwtPayload | undefined,\n tool: DiscoveredCapability<ToolMetadata>,\n moduleHasGuards: boolean,\n allowUnauthenticatedAccess: boolean = false,\n ): boolean {\n const metadata = tool.metadata;\n\n // If tool is public, always allow access\n if (metadata.isPublic) {\n return true;\n }\n\n // If tool has specific scope/role requirements, user MUST be authenticated\n const hasSpecificRequirements =\n (metadata.requiredScopes && metadata.requiredScopes.length > 0) ||\n (metadata.requiredRoles && metadata.requiredRoles.length > 0);\n\n if (hasSpecificRequirements && !user) {\n return false;\n }\n\n // If tool has specific scopes, validate them\n if (metadata.requiredScopes && metadata.requiredScopes.length > 0) {\n if (!this.hasRequiredScopes(user, metadata.requiredScopes)) {\n return false;\n }\n }\n\n // If tool has specific roles, validate them\n if (metadata.requiredRoles && metadata.requiredRoles.length > 0) {\n if (!this.hasRequiredRoles(user, metadata.requiredRoles)) {\n return false;\n }\n }\n\n // At this point:\n // - Tool is not public\n // - Tool has no specific scope/role requirements (or they passed)\n //\n // Decision logic based on allowUnauthenticatedAccess:\n //\n // allowUnauthenticatedAccess = false (default, standard auth mode):\n // - Guards are expected to fully authorize requests (via JWT, API keys, etc)\n // - If guard let request through AND there's no user object:\n // * Guard used non-JWT auth mechanism (API key, IP whitelist, etc)\n // * Trust the guard's decision to authorize\n // - If no guards configured, allow access (no auth required)\n //\n // allowUnauthenticatedAccess = true (freemium mode):\n // - Guards may let unauthenticated requests through for per-tool auth\n // - Only @PublicTool or tools with specific scopes/roles accessible\n // - Tools without decorators require authentication (user must be present)\n //\n if (allowUnauthenticatedAccess && moduleHasGuards && !user) {\n // Freemium mode: unauthenticated access only for @PublicTool or specific scopes\n // This tool has no decorators, so it requires authentication\n return false;\n }\n\n // Standard mode: If we're here, either:\n // - No guards (open access)\n // - Guards authorized it (trust guard decision, even without user object)\n // - User is present and passed all checks\n return true;\n }\n\n /**\n * Validate that a user can access a tool, throwing an error if not authorized\n *\n * @param user - The authenticated user (may be undefined)\n * @param tool - The discovered tool\n * @param moduleHasGuards - Whether the module has guards configured\n * @param allowUnauthenticatedAccess - Whether unauthenticated access is allowed (freemium mode)\n * @throws McpError if user is not authorized to access the tool\n */\n validateToolAccess(\n user: JwtPayload | undefined,\n tool: DiscoveredCapability<ToolMetadata>,\n moduleHasGuards: boolean,\n allowUnauthenticatedAccess: boolean = false,\n ): void {\n const metadata = tool.metadata;\n const toolName = metadata.name;\n\n // If tool is public, allow access\n if (metadata.isPublic) {\n return;\n }\n\n // Check if tool has specific scope/role requirements\n const hasSpecificRequirements =\n (metadata.requiredScopes && metadata.requiredScopes.length > 0) ||\n (metadata.requiredRoles && metadata.requiredRoles.length > 0);\n\n if (hasSpecificRequirements && !user) {\n throw new McpError(\n ErrorCode.InvalidRequest,\n `Tool '${toolName}' requires authentication`,\n );\n }\n\n // Validate scopes if required\n if (metadata.requiredScopes && metadata.requiredScopes.length > 0) {\n if (!this.hasRequiredScopes(user, metadata.requiredScopes)) {\n throw new McpError(\n ErrorCode.InvalidRequest,\n `Tool '${toolName}' requires scopes: ${metadata.requiredScopes.join(', ')}`,\n );\n }\n }\n\n // Validate roles if required\n if (metadata.requiredRoles && metadata.requiredRoles.length > 0) {\n if (!this.hasRequiredRoles(user, metadata.requiredRoles)) {\n throw new McpError(\n ErrorCode.InvalidRequest,\n `Tool '${toolName}' requires roles: ${metadata.requiredRoles.join(', ')}`,\n );\n }\n }\n\n // At this point:\n // - Tool is not public\n // - Tool has no specific scope/role requirements (or they passed)\n //\n // Decision logic based on allowUnauthenticatedAccess:\n //\n // allowUnauthenticatedAccess = false (default, standard auth mode):\n // - Guards are expected to fully authorize requests (via JWT, API keys, etc)\n // - If guard let request through AND there's no user object:\n // * Guard used non-JWT auth mechanism (API key, IP whitelist, etc)\n // * Trust the guard's decision to authorize\n // - If no guards configured, allow access (no auth required)\n //\n // allowUnauthenticatedAccess = true (freemium mode):\n // - Guards may let unauthenticated requests through for per-tool auth\n // - Only @PublicTool or tools with specific scopes/roles accessible\n // - Tools without decorators require authentication (user must be present)\n //\n if (allowUnauthenticatedAccess && moduleHasGuards && !user) {\n // Freemium mode: unauthenticated access only for @PublicTool or specific scopes\n // This tool has no decorators, so it requires authentication\n throw new McpError(\n ErrorCode.InvalidRequest,\n `Tool '${toolName}' requires authentication`,\n );\n }\n\n // Standard mode: If we're here, either:\n // - No guards (open access)\n // - Guards authorized it (trust guard decision, even without user object)\n // - User is present and passed all checks\n }\n\n /**\n * Check if user has all required scopes\n *\n * @param user - The authenticated user (may be undefined)\n * @param requiredScopes - Array of required scope strings\n * @returns true if user has all required scopes\n */\n private hasRequiredScopes(\n user: JwtPayload | undefined,\n requiredScopes: string[],\n ): boolean {\n if (!user) {\n return false;\n }\n\n // Get user scopes - could be in 'scope' (space-delimited string) or 'scopes' (array)\n let userScopes: string[] = [];\n\n if (user.scope) {\n // OAuth 2.0 standard: space-delimited string\n userScopes = user.scope.split(' ').filter((s) => s.length > 0);\n } else if ((user as any).scopes && Array.isArray((user as any).scopes)) {\n // Alternative: array of scopes\n userScopes = (user as any).scopes;\n }\n\n // Check if user has ALL required scopes\n return requiredScopes.every((required) => userScopes.includes(required));\n }\n\n /**\n * Check if user has all required roles\n *\n * @param user - The authenticated user (may be undefined)\n * @param requiredRoles - Array of required role strings\n * @returns true if user has all required roles\n */\n private hasRequiredRoles(\n user: JwtPayload | undefined,\n requiredRoles: string[],\n ): boolean {\n if (!user) {\n return false;\n }\n\n // Get user roles from user data\n let userRoles: string[] = [];\n\n if ((user as any).roles && Array.isArray((user as any).roles)) {\n userRoles = (user as any).roles;\n } else if (\n user.user_data &&\n user.user_data.roles &&\n Array.isArray(user.user_data.roles)\n ) {\n userRoles = user.user_data.roles;\n }\n\n // Check if user has ALL required roles\n return requiredRoles.every((required) => userRoles.includes(required));\n }\n}\n"]}
|