@rekog/mcp-nest 1.9.10 → 2.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -29
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/decorators/constants.d.ts +1 -0
- package/dist/mcp/decorators/constants.d.ts.map +1 -1
- package/dist/mcp/decorators/constants.js +2 -1
- package/dist/mcp/decorators/constants.js.map +1 -1
- package/dist/mcp/decorators/index.d.ts +3 -1
- package/dist/mcp/decorators/index.d.ts.map +1 -1
- package/dist/mcp/decorators/index.js +3 -1
- package/dist/mcp/decorators/index.js.map +1 -1
- package/dist/mcp/decorators/mcp-controller.decorator.d.ts +6 -0
- package/dist/mcp/decorators/mcp-controller.decorator.d.ts.map +1 -0
- package/dist/mcp/decorators/mcp-controller.decorator.js +43 -0
- package/dist/mcp/decorators/mcp-controller.decorator.js.map +1 -0
- package/dist/mcp/decorators/mcp-message-pattern.d.ts +3 -0
- package/dist/mcp/decorators/mcp-message-pattern.d.ts.map +1 -0
- package/dist/mcp/decorators/mcp-message-pattern.js +13 -0
- package/dist/mcp/decorators/mcp-message-pattern.js.map +1 -0
- package/dist/mcp/decorators/prompt.decorator.d.ts +1 -1
- package/dist/mcp/decorators/prompt.decorator.d.ts.map +1 -1
- package/dist/mcp/decorators/prompt.decorator.js +2 -1
- package/dist/mcp/decorators/prompt.decorator.js.map +1 -1
- package/dist/mcp/decorators/public.decorator.js.map +1 -1
- package/dist/mcp/decorators/raw-request.decorator.d.ts +2 -0
- package/dist/mcp/decorators/raw-request.decorator.d.ts.map +1 -0
- package/dist/mcp/decorators/raw-request.decorator.js +6 -0
- package/dist/mcp/decorators/raw-request.decorator.js.map +1 -0
- package/dist/mcp/decorators/resource-template.decorator.d.ts +1 -1
- package/dist/mcp/decorators/resource-template.decorator.d.ts.map +1 -1
- package/dist/mcp/decorators/resource-template.decorator.js +2 -1
- package/dist/mcp/decorators/resource-template.decorator.js.map +1 -1
- package/dist/mcp/decorators/resource.decorator.d.ts +1 -1
- package/dist/mcp/decorators/resource.decorator.d.ts.map +1 -1
- package/dist/mcp/decorators/resource.decorator.js +2 -1
- package/dist/mcp/decorators/resource.decorator.js.map +1 -1
- package/dist/mcp/decorators/tool.decorator.d.ts +1 -3
- package/dist/mcp/decorators/tool.decorator.d.ts.map +1 -1
- package/dist/mcp/decorators/tool.decorator.js +2 -1
- package/dist/mcp/decorators/tool.decorator.js.map +1 -1
- package/dist/mcp/filters/mcp-exception.filter.d.ts +6 -0
- package/dist/mcp/filters/mcp-exception.filter.d.ts.map +1 -0
- package/dist/mcp/filters/mcp-exception.filter.js +26 -0
- package/dist/mcp/filters/mcp-exception.filter.js.map +1 -0
- package/dist/mcp/index.d.ts +11 -7
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +11 -7
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/interfaces/authenticated-user.interface.d.ts +9 -0
- package/dist/mcp/interfaces/authenticated-user.interface.d.ts.map +1 -0
- package/dist/{authz/interfaces/request-with-user.js → mcp/interfaces/authenticated-user.interface.js} +1 -1
- package/dist/mcp/interfaces/authenticated-user.interface.js.map +1 -0
- package/dist/mcp/interfaces/dynamic-prompt.interface.d.ts +1 -1
- package/dist/mcp/interfaces/dynamic-prompt.interface.d.ts.map +1 -1
- package/dist/mcp/interfaces/dynamic-prompt.interface.js.map +1 -1
- package/dist/mcp/interfaces/dynamic-resource.interface.d.ts +1 -1
- package/dist/mcp/interfaces/dynamic-resource.interface.d.ts.map +1 -1
- package/dist/mcp/interfaces/dynamic-resource.interface.js.map +1 -1
- package/dist/mcp/interfaces/dynamic-tool.interface.d.ts +1 -1
- package/dist/mcp/interfaces/dynamic-tool.interface.d.ts.map +1 -1
- package/dist/mcp/interfaces/dynamic-tool.interface.js.map +1 -1
- package/dist/mcp/interfaces/index.d.ts +2 -2
- package/dist/mcp/interfaces/index.d.ts.map +1 -1
- package/dist/mcp/interfaces/index.js +0 -3
- package/dist/mcp/interfaces/index.js.map +1 -1
- package/dist/mcp/services/tool-authorization.service.d.ts +7 -5
- package/dist/mcp/services/tool-authorization.service.d.ts.map +1 -1
- package/dist/mcp/services/tool-authorization.service.js +9 -6
- package/dist/mcp/services/tool-authorization.service.js.map +1 -1
- package/dist/mcp/transport/mcp-context.d.ts +34 -0
- package/dist/mcp/transport/mcp-context.d.ts.map +1 -0
- package/dist/mcp/transport/mcp-context.js +74 -0
- package/dist/mcp/transport/mcp-context.js.map +1 -0
- package/dist/mcp/transport/mcp-http-handler.d.ts +7 -0
- package/dist/mcp/transport/mcp-http-handler.d.ts.map +1 -0
- package/dist/mcp/transport/mcp-http-handler.js +5 -0
- package/dist/mcp/transport/mcp-http-handler.js.map +1 -0
- package/dist/mcp/transport/mcp-server-options.interface.d.ts +23 -0
- package/dist/mcp/transport/mcp-server-options.interface.d.ts.map +1 -0
- package/dist/{authz/providers/oauth-provider.interface.js → mcp/transport/mcp-server-options.interface.js} +1 -1
- package/dist/mcp/transport/mcp-server-options.interface.js.map +1 -0
- package/dist/mcp/transport/mcp-transport.constants.d.ts +19 -0
- package/dist/mcp/transport/mcp-transport.constants.d.ts.map +1 -0
- package/dist/mcp/transport/mcp-transport.constants.js +21 -0
- package/dist/mcp/transport/mcp-transport.constants.js.map +1 -0
- package/dist/mcp/transport/mcp-transport.interface.d.ts +17 -0
- package/dist/mcp/transport/mcp-transport.interface.d.ts.map +1 -0
- package/dist/{authz/interfaces/oauth-common.interface.js → mcp/transport/mcp-transport.interface.js} +1 -1
- package/dist/mcp/transport/mcp-transport.interface.js.map +1 -0
- package/dist/mcp/transport/mcp.strategy.d.ts +56 -0
- package/dist/mcp/transport/mcp.strategy.d.ts.map +1 -0
- package/dist/mcp/transport/mcp.strategy.js +446 -0
- package/dist/mcp/transport/mcp.strategy.js.map +1 -0
- package/dist/mcp/transport/resource-matcher.d.ts +13 -0
- package/dist/mcp/transport/resource-matcher.d.ts.map +1 -0
- package/dist/mcp/transport/resource-matcher.js +83 -0
- package/dist/mcp/transport/resource-matcher.js.map +1 -0
- package/dist/mcp/transport/streamable-http.controller.d.ts +16 -0
- package/dist/mcp/transport/streamable-http.controller.d.ts.map +1 -0
- package/dist/mcp/transport/streamable-http.controller.js +98 -0
- package/dist/mcp/transport/streamable-http.controller.js.map +1 -0
- package/dist/mcp/transport/transports/index.d.ts +3 -0
- package/dist/mcp/transport/transports/index.d.ts.map +1 -0
- package/dist/{authz → mcp/transport/transports}/index.js +2 -10
- package/dist/mcp/transport/transports/index.js.map +1 -0
- package/dist/mcp/transport/transports/read-body.d.ts +3 -0
- package/dist/mcp/transport/transports/read-body.d.ts.map +1 -0
- package/dist/mcp/transport/transports/read-body.js +32 -0
- package/dist/mcp/transport/transports/read-body.js.map +1 -0
- package/dist/mcp/transport/transports/stdio.transport.d.ts +9 -0
- package/dist/mcp/transport/transports/stdio.transport.d.ts.map +1 -0
- package/dist/mcp/transport/transports/stdio.transport.js +27 -0
- package/dist/mcp/transport/transports/stdio.transport.js.map +1 -0
- package/dist/mcp/transport/transports/streamable-http.transport.d.ts +33 -0
- package/dist/mcp/transport/transports/streamable-http.transport.d.ts.map +1 -0
- package/dist/mcp/transport/transports/streamable-http.transport.js +216 -0
- package/dist/mcp/transport/transports/streamable-http.transport.js.map +1 -0
- package/dist/mcp/utils/mcp-logger.factory.d.ts +6 -2
- package/dist/mcp/utils/mcp-logger.factory.d.ts.map +1 -1
- package/dist/mcp/utils/mcp-logger.factory.js.map +1 -1
- package/package.json +3 -66
- package/src/index.ts +0 -1
- package/src/mcp/decorators/constants.ts +1 -0
- package/src/mcp/decorators/index.ts +3 -1
- package/src/mcp/decorators/mcp-controller.decorator.ts +126 -0
- package/src/mcp/decorators/mcp-message-pattern.ts +38 -0
- package/src/mcp/decorators/prompt.decorator.ts +6 -2
- package/src/mcp/decorators/public.decorator.ts +3 -3
- package/src/mcp/decorators/raw-request.decorator.ts +32 -0
- package/src/mcp/decorators/resource-template.decorator.ts +6 -2
- package/src/mcp/decorators/resource.decorator.ts +6 -2
- package/src/mcp/decorators/tool.decorator.ts +6 -3
- package/src/mcp/filters/mcp-exception.filter.ts +47 -0
- package/src/mcp/index.ts +13 -7
- package/src/mcp/interfaces/authenticated-user.interface.ts +22 -0
- package/src/mcp/interfaces/dynamic-prompt.interface.ts +1 -1
- package/src/mcp/interfaces/dynamic-resource.interface.ts +1 -1
- package/src/mcp/interfaces/dynamic-tool.interface.ts +2 -2
- package/src/mcp/interfaces/index.ts +3 -7
- package/src/mcp/services/tool-authorization.service.ts +52 -72
- package/src/mcp/transport/mcp-context.ts +138 -0
- package/src/mcp/transport/mcp-http-handler.ts +32 -0
- package/src/mcp/transport/mcp-server-options.interface.ts +75 -0
- package/src/mcp/transport/mcp-transport.constants.ts +99 -0
- package/src/mcp/transport/mcp-transport.interface.ts +50 -0
- package/src/mcp/transport/mcp.strategy.ts +752 -0
- package/src/mcp/transport/resource-matcher.ts +107 -0
- package/src/mcp/transport/streamable-http.controller.ts +114 -0
- package/src/mcp/transport/transports/index.ts +2 -0
- package/src/mcp/transport/transports/read-body.ts +39 -0
- package/src/mcp/transport/transports/stdio.transport.ts +35 -0
- package/src/mcp/transport/transports/streamable-http.transport.ts +384 -0
- package/src/mcp/utils/mcp-logger.factory.spec.ts +8 -22
- package/src/mcp/utils/mcp-logger.factory.ts +13 -2
- package/dist/authz/guards/jwt-auth.guard.d.ts +0 -19
- package/dist/authz/guards/jwt-auth.guard.d.ts.map +0 -1
- package/dist/authz/guards/jwt-auth.guard.js +0 -108
- package/dist/authz/guards/jwt-auth.guard.js.map +0 -1
- package/dist/authz/index.d.ts +0 -11
- package/dist/authz/index.d.ts.map +0 -1
- package/dist/authz/index.js.map +0 -1
- package/dist/authz/interfaces/oauth-common.interface.d.ts +0 -21
- package/dist/authz/interfaces/oauth-common.interface.d.ts.map +0 -1
- package/dist/authz/interfaces/oauth-common.interface.js.map +0 -1
- package/dist/authz/interfaces/request-with-user.d.ts +0 -13
- package/dist/authz/interfaces/request-with-user.d.ts.map +0 -1
- package/dist/authz/interfaces/request-with-user.js.map +0 -1
- package/dist/authz/mcp-oauth.controller.d.ts +0 -81
- package/dist/authz/mcp-oauth.controller.d.ts.map +0 -1
- package/dist/authz/mcp-oauth.controller.js +0 -540
- package/dist/authz/mcp-oauth.controller.js.map +0 -1
- package/dist/authz/mcp-oauth.module.d.ts +0 -12
- package/dist/authz/mcp-oauth.module.d.ts.map +0 -1
- package/dist/authz/mcp-oauth.module.js +0 -271
- package/dist/authz/mcp-oauth.module.js.map +0 -1
- package/dist/authz/providers/azure-ad.provider.d.ts +0 -3
- package/dist/authz/providers/azure-ad.provider.d.ts.map +0 -1
- package/dist/authz/providers/azure-ad.provider.js +0 -37
- package/dist/authz/providers/azure-ad.provider.js.map +0 -1
- package/dist/authz/providers/github.provider.d.ts +0 -3
- package/dist/authz/providers/github.provider.d.ts.map +0 -1
- package/dist/authz/providers/github.provider.js +0 -24
- package/dist/authz/providers/github.provider.js.map +0 -1
- package/dist/authz/providers/google.provider.d.ts +0 -3
- package/dist/authz/providers/google.provider.d.ts.map +0 -1
- package/dist/authz/providers/google.provider.js +0 -25
- package/dist/authz/providers/google.provider.js.map +0 -1
- package/dist/authz/providers/oauth-provider.interface.d.ts +0 -107
- package/dist/authz/providers/oauth-provider.interface.d.ts.map +0 -1
- package/dist/authz/providers/oauth-provider.interface.js.map +0 -1
- package/dist/authz/services/client.service.d.ts +0 -12
- package/dist/authz/services/client.service.d.ts.map +0 -1
- package/dist/authz/services/client.service.js +0 -81
- package/dist/authz/services/client.service.js.map +0 -1
- package/dist/authz/services/jwt-token.service.d.ts +0 -37
- package/dist/authz/services/jwt-token.service.d.ts.map +0 -1
- package/dist/authz/services/jwt-token.service.js +0 -182
- package/dist/authz/services/jwt-token.service.js.map +0 -1
- package/dist/authz/services/oauth-strategy.service.d.ts +0 -13
- package/dist/authz/services/oauth-strategy.service.d.ts.map +0 -1
- package/dist/authz/services/oauth-strategy.service.js +0 -71
- package/dist/authz/services/oauth-strategy.service.js.map +0 -1
- package/dist/authz/stores/memory-store.service.d.ts +0 -27
- package/dist/authz/stores/memory-store.service.d.ts.map +0 -1
- package/dist/authz/stores/memory-store.service.js +0 -107
- package/dist/authz/stores/memory-store.service.js.map +0 -1
- package/dist/authz/stores/oauth-store.interface.d.ts +0 -61
- package/dist/authz/stores/oauth-store.interface.d.ts.map +0 -1
- package/dist/authz/stores/oauth-store.interface.js +0 -5
- package/dist/authz/stores/oauth-store.interface.js.map +0 -1
- package/dist/authz/stores/typeorm/constants.d.ts +0 -3
- package/dist/authz/stores/typeorm/constants.d.ts.map +0 -1
- package/dist/authz/stores/typeorm/constants.js +0 -6
- package/dist/authz/stores/typeorm/constants.js.map +0 -1
- package/dist/authz/stores/typeorm/entities/authorization-code.entity.d.ts +0 -15
- package/dist/authz/stores/typeorm/entities/authorization-code.entity.d.ts.map +0 -1
- package/dist/authz/stores/typeorm/entities/authorization-code.entity.js +0 -69
- package/dist/authz/stores/typeorm/entities/authorization-code.entity.js.map +0 -1
- package/dist/authz/stores/typeorm/entities/index.d.ts +0 -5
- package/dist/authz/stores/typeorm/entities/index.d.ts.map +0 -1
- package/dist/authz/stores/typeorm/entities/index.js +0 -12
- package/dist/authz/stores/typeorm/entities/index.js.map +0 -1
- package/dist/authz/stores/typeorm/entities/oauth-client.entity.d.ts +0 -17
- package/dist/authz/stores/typeorm/entities/oauth-client.entity.d.ts.map +0 -1
- package/dist/authz/stores/typeorm/entities/oauth-client.entity.js +0 -77
- package/dist/authz/stores/typeorm/entities/oauth-client.entity.js.map +0 -1
- package/dist/authz/stores/typeorm/entities/oauth-session.entity.d.ts +0 -14
- package/dist/authz/stores/typeorm/entities/oauth-session.entity.d.ts.map +0 -1
- package/dist/authz/stores/typeorm/entities/oauth-session.entity.js +0 -65
- package/dist/authz/stores/typeorm/entities/oauth-session.entity.js.map +0 -1
- package/dist/authz/stores/typeorm/entities/user-profile.entity.d.ts +0 -13
- package/dist/authz/stores/typeorm/entities/user-profile.entity.d.ts.map +0 -1
- package/dist/authz/stores/typeorm/entities/user-profile.entity.js +0 -62
- package/dist/authz/stores/typeorm/entities/user-profile.entity.js.map +0 -1
- package/dist/authz/stores/typeorm/typeorm-store.service.d.ts +0 -28
- package/dist/authz/stores/typeorm/typeorm-store.service.d.ts.map +0 -1
- package/dist/authz/stores/typeorm/typeorm-store.service.js +0 -138
- package/dist/authz/stores/typeorm/typeorm-store.service.js.map +0 -1
- package/dist/mcp/constants/feature-registration.constants.d.ts +0 -7
- package/dist/mcp/constants/feature-registration.constants.d.ts.map +0 -1
- package/dist/mcp/constants/feature-registration.constants.js +0 -5
- package/dist/mcp/constants/feature-registration.constants.js.map +0 -1
- package/dist/mcp/decorators/tool-guards.decorator.d.ts +0 -12
- package/dist/mcp/decorators/tool-guards.decorator.d.ts.map +0 -1
- package/dist/mcp/decorators/tool-guards.decorator.js +0 -13
- package/dist/mcp/decorators/tool-guards.decorator.js.map +0 -1
- package/dist/mcp/interfaces/mcp-options.interface.d.ts +0 -53
- package/dist/mcp/interfaces/mcp-options.interface.d.ts.map +0 -1
- package/dist/mcp/interfaces/mcp-options.interface.js +0 -10
- package/dist/mcp/interfaces/mcp-options.interface.js.map +0 -1
- package/dist/mcp/mcp.module.d.ts +0 -15
- package/dist/mcp/mcp.module.d.ts.map +0 -1
- package/dist/mcp/mcp.module.js +0 -248
- package/dist/mcp/mcp.module.js.map +0 -1
- package/dist/mcp/services/handlers/mcp-handler.base.d.ts +0 -117
- package/dist/mcp/services/handlers/mcp-handler.base.d.ts.map +0 -1
- package/dist/mcp/services/handlers/mcp-handler.base.js +0 -125
- package/dist/mcp/services/handlers/mcp-handler.base.js.map +0 -1
- package/dist/mcp/services/handlers/mcp-prompts.handler.d.ts +0 -12
- package/dist/mcp/services/handlers/mcp-prompts.handler.d.ts.map +0 -1
- package/dist/mcp/services/handlers/mcp-prompts.handler.js +0 -98
- package/dist/mcp/services/handlers/mcp-prompts.handler.js.map +0 -1
- package/dist/mcp/services/handlers/mcp-resources.handler.d.ts +0 -13
- package/dist/mcp/services/handlers/mcp-resources.handler.d.ts.map +0 -1
- package/dist/mcp/services/handlers/mcp-resources.handler.js +0 -117
- package/dist/mcp/services/handlers/mcp-resources.handler.js.map +0 -1
- package/dist/mcp/services/handlers/mcp-tools.handler.d.ts +0 -22
- package/dist/mcp/services/handlers/mcp-tools.handler.d.ts.map +0 -1
- package/dist/mcp/services/handlers/mcp-tools.handler.js +0 -258
- package/dist/mcp/services/handlers/mcp-tools.handler.js.map +0 -1
- package/dist/mcp/services/mcp-dynamic-registry.service.d.ts +0 -26
- package/dist/mcp/services/mcp-dynamic-registry.service.d.ts.map +0 -1
- package/dist/mcp/services/mcp-dynamic-registry.service.js +0 -133
- package/dist/mcp/services/mcp-dynamic-registry.service.js.map +0 -1
- package/dist/mcp/services/mcp-executor.service.d.ts +0 -15
- package/dist/mcp/services/mcp-executor.service.d.ts.map +0 -1
- package/dist/mcp/services/mcp-executor.service.js +0 -47
- package/dist/mcp/services/mcp-executor.service.js.map +0 -1
- package/dist/mcp/services/mcp-registry-discovery.service.d.ts +0 -63
- package/dist/mcp/services/mcp-registry-discovery.service.d.ts.map +0 -1
- package/dist/mcp/services/mcp-registry-discovery.service.js +0 -397
- package/dist/mcp/services/mcp-registry-discovery.service.js.map +0 -1
- package/dist/mcp/services/mcp-sse.service.d.ts +0 -20
- package/dist/mcp/services/mcp-sse.service.d.ts.map +0 -1
- package/dist/mcp/services/mcp-sse.service.js +0 -91
- package/dist/mcp/services/mcp-sse.service.js.map +0 -1
- package/dist/mcp/services/mcp-streamable-http.service.d.ts +0 -32
- package/dist/mcp/services/mcp-streamable-http.service.d.ts.map +0 -1
- package/dist/mcp/services/mcp-streamable-http.service.js +0 -327
- package/dist/mcp/services/mcp-streamable-http.service.js.map +0 -1
- package/dist/mcp/services/sse-ping.service.d.ts +0 -23
- package/dist/mcp/services/sse-ping.service.d.ts.map +0 -1
- package/dist/mcp/services/sse-ping.service.js +0 -99
- package/dist/mcp/services/sse-ping.service.js.map +0 -1
- package/dist/mcp/transport/sse.controller.factory.d.ts +0 -14
- package/dist/mcp/transport/sse.controller.factory.d.ts.map +0 -1
- package/dist/mcp/transport/sse.controller.factory.js +0 -67
- package/dist/mcp/transport/sse.controller.factory.js.map +0 -1
- package/dist/mcp/transport/stdio.service.d.ts +0 -14
- package/dist/mcp/transport/stdio.service.d.ts.map +0 -1
- package/dist/mcp/transport/stdio.service.js +0 -66
- package/dist/mcp/transport/stdio.service.js.map +0 -1
- package/dist/mcp/transport/streamable-http.controller.factory.d.ts +0 -14
- package/dist/mcp/transport/streamable-http.controller.factory.d.ts.map +0 -1
- package/dist/mcp/transport/streamable-http.controller.factory.js +0 -74
- package/dist/mcp/transport/streamable-http.controller.factory.js.map +0 -1
- package/dist/mcp/utils/capabilities-builder.d.ts +0 -5
- package/dist/mcp/utils/capabilities-builder.d.ts.map +0 -1
- package/dist/mcp/utils/capabilities-builder.js +0 -25
- package/dist/mcp/utils/capabilities-builder.js.map +0 -1
- package/dist/mcp/utils/mcp-server.factory.d.ts +0 -6
- package/dist/mcp/utils/mcp-server.factory.d.ts.map +0 -1
- package/dist/mcp/utils/mcp-server.factory.js +0 -22
- package/dist/mcp/utils/mcp-server.factory.js.map +0 -1
- package/src/authz/guards/jwt-auth.guard.ts +0 -127
- package/src/authz/index.ts +0 -10
- package/src/authz/interfaces/oauth-common.interface.ts +0 -21
- package/src/authz/interfaces/request-with-user.ts +0 -16
- package/src/authz/mcp-oauth.controller.ts +0 -860
- package/src/authz/mcp-oauth.module.ts +0 -384
- package/src/authz/providers/azure-ad.provider.ts +0 -40
- package/src/authz/providers/github.provider.ts +0 -22
- package/src/authz/providers/google.provider.ts +0 -23
- package/src/authz/providers/oauth-provider.interface.ts +0 -147
- package/src/authz/services/client.service.ts +0 -125
- package/src/authz/services/jwt-token.service.spec.ts +0 -67
- package/src/authz/services/jwt-token.service.ts +0 -188
- package/src/authz/services/oauth-strategy.service.ts +0 -64
- package/src/authz/stores/memory-store.service.spec.ts +0 -475
- package/src/authz/stores/memory-store.service.ts +0 -141
- package/src/authz/stores/oauth-store.interface.ts +0 -131
- package/src/authz/stores/typeorm/README.md +0 -140
- package/src/authz/stores/typeorm/constants.ts +0 -6
- package/src/authz/stores/typeorm/entities/authorization-code.entity.ts +0 -41
- package/src/authz/stores/typeorm/entities/index.ts +0 -4
- package/src/authz/stores/typeorm/entities/oauth-client.entity.ts +0 -53
- package/src/authz/stores/typeorm/entities/oauth-session.entity.ts +0 -38
- package/src/authz/stores/typeorm/entities/user-profile.entity.ts +0 -46
- package/src/authz/stores/typeorm/typeorm-store.service.spec.ts +0 -494
- package/src/authz/stores/typeorm/typeorm-store.service.ts +0 -165
- package/src/mcp/constants/feature-registration.constants.ts +0 -24
- package/src/mcp/decorators/tool-guards.decorator.ts +0 -82
- package/src/mcp/interfaces/mcp-options.interface.ts +0 -95
- package/src/mcp/mcp.module.ts +0 -349
- package/src/mcp/services/handlers/mcp-handler.base.ts +0 -203
- package/src/mcp/services/handlers/mcp-prompts.handler.ts +0 -143
- package/src/mcp/services/handlers/mcp-resources.handler.ts +0 -183
- package/src/mcp/services/handlers/mcp-tools.handler.spec.ts +0 -41
- package/src/mcp/services/handlers/mcp-tools.handler.ts +0 -436
- package/src/mcp/services/mcp-dynamic-registry.service.ts +0 -236
- package/src/mcp/services/mcp-executor.service.ts +0 -64
- package/src/mcp/services/mcp-registry-discovery.service.spec.ts +0 -306
- package/src/mcp/services/mcp-registry-discovery.service.ts +0 -849
- package/src/mcp/services/mcp-sse.service.ts +0 -124
- package/src/mcp/services/mcp-streamable-http.service.ts +0 -472
- package/src/mcp/services/sse-ping.service.ts +0 -144
- package/src/mcp/transport/custom-decorator.spec.ts +0 -75
- package/src/mcp/transport/sse.controller.factory.ts +0 -84
- package/src/mcp/transport/stdio.service.ts +0 -75
- package/src/mcp/transport/streamable-http.controller.factory.ts +0 -80
- package/src/mcp/utils/capabilities-builder.ts +0 -43
- package/src/mcp/utils/mcp-server.factory.ts +0 -33
|
@@ -0,0 +1,752 @@
|
|
|
1
|
+
import { HttpServer, Logger } from '@nestjs/common';
|
|
2
|
+
import {
|
|
3
|
+
BaseRpcContext,
|
|
4
|
+
CustomTransportStrategy,
|
|
5
|
+
MessageHandler,
|
|
6
|
+
Server,
|
|
7
|
+
} from '@nestjs/microservices';
|
|
8
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
9
|
+
import {
|
|
10
|
+
CallToolRequestSchema,
|
|
11
|
+
ErrorCode,
|
|
12
|
+
GetPromptRequestSchema,
|
|
13
|
+
GetPromptResult,
|
|
14
|
+
ListPromptsRequestSchema,
|
|
15
|
+
ListResourcesRequestSchema,
|
|
16
|
+
ListResourceTemplatesRequestSchema,
|
|
17
|
+
ListToolsRequestSchema,
|
|
18
|
+
McpError,
|
|
19
|
+
PromptArgument,
|
|
20
|
+
ReadResourceRequestSchema,
|
|
21
|
+
ReadResourceResult,
|
|
22
|
+
ServerCapabilities,
|
|
23
|
+
} from '@modelcontextprotocol/sdk/types.js';
|
|
24
|
+
import { toJsonSchemaCompat } from '@modelcontextprotocol/sdk/server/zod-json-schema-compat.js';
|
|
25
|
+
import { normalizeObjectSchema } from '@modelcontextprotocol/sdk/server/zod-compat.js';
|
|
26
|
+
import { firstValueFrom } from 'rxjs';
|
|
27
|
+
import { z, ZodType } from 'zod';
|
|
28
|
+
|
|
29
|
+
import {
|
|
30
|
+
MCP_PROMPT_METADATA_KEY,
|
|
31
|
+
MCP_PUBLIC_METADATA_KEY,
|
|
32
|
+
MCP_RESOURCE_METADATA_KEY,
|
|
33
|
+
MCP_RESOURCE_TEMPLATE_METADATA_KEY,
|
|
34
|
+
MCP_ROLES_METADATA_KEY,
|
|
35
|
+
MCP_SCOPES_METADATA_KEY,
|
|
36
|
+
MCP_TOOL_METADATA_KEY,
|
|
37
|
+
PromptMetadata,
|
|
38
|
+
ResourceMetadata,
|
|
39
|
+
ResourceTemplateMetadata,
|
|
40
|
+
ToolMetadata,
|
|
41
|
+
} from '../decorators';
|
|
42
|
+
import { ToolAuthorizationService } from '../services/tool-authorization.service';
|
|
43
|
+
import { createMcpLogger } from '../utils/mcp-logger.factory';
|
|
44
|
+
import type { McpRequest } from '../interfaces/mcp-tool.interface';
|
|
45
|
+
import type {
|
|
46
|
+
DynamicPromptDefinition,
|
|
47
|
+
DynamicPromptHandler,
|
|
48
|
+
DynamicResourceDefinition,
|
|
49
|
+
DynamicResourceHandler,
|
|
50
|
+
DynamicToolDefinition,
|
|
51
|
+
DynamicToolHandler,
|
|
52
|
+
} from '../interfaces';
|
|
53
|
+
import {
|
|
54
|
+
mcpTransportFor,
|
|
55
|
+
McpHandlerExtras,
|
|
56
|
+
McpMethodRef,
|
|
57
|
+
} from './mcp-transport.constants';
|
|
58
|
+
import { McpContext, McpSessionInfo } from './mcp-context';
|
|
59
|
+
import { McpServerOptions } from './mcp-server-options.interface';
|
|
60
|
+
import { McpTransportContext } from './mcp-transport.interface';
|
|
61
|
+
import {
|
|
62
|
+
matchResourceByUri,
|
|
63
|
+
matchResourceTemplateByUri,
|
|
64
|
+
} from './resource-matcher';
|
|
65
|
+
|
|
66
|
+
/** DI token a user can wire (`{ provide: MCP_STRATEGY, useValue: strategy }`) to inject the strategy. */
|
|
67
|
+
export const MCP_STRATEGY = 'MCP_STRATEGY';
|
|
68
|
+
|
|
69
|
+
type Invoke = (payload: unknown, ctx: McpContext) => Promise<unknown>;
|
|
70
|
+
|
|
71
|
+
interface ToolCapability {
|
|
72
|
+
metadata: ToolMetadata;
|
|
73
|
+
invoke: Invoke;
|
|
74
|
+
}
|
|
75
|
+
interface ResourceCapability {
|
|
76
|
+
metadata: ResourceMetadata;
|
|
77
|
+
invoke: Invoke;
|
|
78
|
+
}
|
|
79
|
+
interface TemplateCapability {
|
|
80
|
+
metadata: ResourceTemplateMetadata;
|
|
81
|
+
invoke: Invoke;
|
|
82
|
+
}
|
|
83
|
+
interface PromptCapability {
|
|
84
|
+
metadata: PromptMetadata;
|
|
85
|
+
invoke: Invoke;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
let strategyIdCounter = 0;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* NestJS microservice transport strategy for the Model Context Protocol.
|
|
92
|
+
*
|
|
93
|
+
* Construct one, declare your `@McpController` classes in a module's
|
|
94
|
+
* `controllers`, and connect it: `app.connectMicroservice({ strategy })`. NestJS
|
|
95
|
+
* binds every `@Tool`/`@Resource`/`@Prompt` handler whose transport id matches
|
|
96
|
+
* this strategy's into its `messageHandlers`. On `listen()` the strategy reads each
|
|
97
|
+
* handler's MCP metadata directly off the decorated method, builds the SDK request
|
|
98
|
+
* handlers, and routes invocations through the standard NestJS RPC pipeline — so
|
|
99
|
+
* MCP tools get guards, pipes, interceptors, and exception filters for free.
|
|
100
|
+
*
|
|
101
|
+
* No `McpModule` or DI discovery service is required.
|
|
102
|
+
*/
|
|
103
|
+
export class McpStrategy extends Server implements CustomTransportStrategy {
|
|
104
|
+
public override transportId: symbol;
|
|
105
|
+
|
|
106
|
+
public readonly moduleId: string;
|
|
107
|
+
protected override readonly logger: Logger;
|
|
108
|
+
|
|
109
|
+
private readonly authService = new ToolAuthorizationService();
|
|
110
|
+
private httpAdapter?: HttpServer;
|
|
111
|
+
private built = false;
|
|
112
|
+
|
|
113
|
+
private readonly tools: ToolCapability[] = [];
|
|
114
|
+
private readonly resources: ResourceCapability[] = [];
|
|
115
|
+
private readonly templates: TemplateCapability[] = [];
|
|
116
|
+
private readonly prompts: PromptCapability[] = [];
|
|
117
|
+
|
|
118
|
+
private readonly dynamicTools = new Map<string, ToolCapability>();
|
|
119
|
+
private readonly dynamicResources = new Map<string, ResourceCapability>();
|
|
120
|
+
private readonly dynamicPrompts = new Map<string, PromptCapability>();
|
|
121
|
+
|
|
122
|
+
private readonly eventListeners = new Map<
|
|
123
|
+
string,
|
|
124
|
+
Array<(...a: any[]) => void>
|
|
125
|
+
>();
|
|
126
|
+
|
|
127
|
+
constructor(public readonly options: McpServerOptions) {
|
|
128
|
+
super();
|
|
129
|
+
// A named server gets its own transport id so NestJS routes only the
|
|
130
|
+
// matching `@McpController({ server })` handlers to this strategy; an
|
|
131
|
+
// unnamed server uses the default shared MCP_TRANSPORT id.
|
|
132
|
+
this.transportId = mcpTransportFor(options.server);
|
|
133
|
+
this.moduleId = `mcp-strategy-${strategyIdCounter++}`;
|
|
134
|
+
this.httpAdapter = options.httpAdapter;
|
|
135
|
+
this.logger = createMcpLogger(McpStrategy.name, this.options);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** Provide the Nest HTTP adapter for HTTP transports (call after NestFactory.create). */
|
|
139
|
+
setHttpAdapter(adapter: HttpServer): void {
|
|
140
|
+
this.httpAdapter = adapter;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ---------------------------------------------------------------------------
|
|
144
|
+
// CustomTransportStrategy lifecycle
|
|
145
|
+
// ---------------------------------------------------------------------------
|
|
146
|
+
|
|
147
|
+
async listen(callback: (err?: unknown, ...args: unknown[]) => void) {
|
|
148
|
+
try {
|
|
149
|
+
this.buildCapabilities();
|
|
150
|
+
this.warnIfNamedServerHasNoDecoratorCapabilities();
|
|
151
|
+
const ctx = this.createTransportContext();
|
|
152
|
+
for (const transport of this.options.transports) {
|
|
153
|
+
await transport.start(ctx);
|
|
154
|
+
}
|
|
155
|
+
callback();
|
|
156
|
+
} catch (err) {
|
|
157
|
+
this.logger.error('Failed to start MCP strategy', err as Error);
|
|
158
|
+
callback(err);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async close() {
|
|
163
|
+
for (const transport of this.options.transports) {
|
|
164
|
+
try {
|
|
165
|
+
await transport.close();
|
|
166
|
+
} catch (err) {
|
|
167
|
+
this.logger.error('Error closing MCP transport', err as Error);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
this.eventListeners.clear();
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
on<
|
|
174
|
+
EventKey extends string = string,
|
|
175
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
176
|
+
EventCallback extends Function = Function,
|
|
177
|
+
>(event: EventKey, callback: EventCallback): void {
|
|
178
|
+
const list = this.eventListeners.get(event) ?? [];
|
|
179
|
+
list.push(callback as unknown as (...args: any[]) => void);
|
|
180
|
+
this.eventListeners.set(event, list);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
unwrap<T>(): T {
|
|
184
|
+
return {
|
|
185
|
+
transports: this.options.transports,
|
|
186
|
+
httpAdapter: this.httpAdapter,
|
|
187
|
+
} as T;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* A named server (`McpStrategy({ server })`) that bound zero decorator
|
|
192
|
+
* capabilities almost certainly has a name mismatch: no
|
|
193
|
+
* `@McpController({ server: <name> })` targets it, so it would serve an empty
|
|
194
|
+
* endpoint silently. Warn so the misconfiguration is visible at startup.
|
|
195
|
+
*
|
|
196
|
+
* Scoped to named servers on purpose: an unnamed (default) server is commonly
|
|
197
|
+
* populated only at runtime via `registerTool()` (see the dynamic-capabilities
|
|
198
|
+
* multi-server example), and we don't want to warn on that supported pattern.
|
|
199
|
+
* For the same reason the message notes dynamic registration as the escape hatch.
|
|
200
|
+
*/
|
|
201
|
+
private warnIfNamedServerHasNoDecoratorCapabilities(): void {
|
|
202
|
+
if (!this.options.server) return;
|
|
203
|
+
const decoratorCount =
|
|
204
|
+
this.tools.length +
|
|
205
|
+
this.resources.length +
|
|
206
|
+
this.templates.length +
|
|
207
|
+
this.prompts.length;
|
|
208
|
+
if (decoratorCount > 0) return;
|
|
209
|
+
this.logger.warn(
|
|
210
|
+
`MCP server '${this.options.server}' started with no @McpController ` +
|
|
211
|
+
`capabilities bound to it. Check that a @McpController({ server: ` +
|
|
212
|
+
`'${this.options.server}' }) exists and is listed in a module's ` +
|
|
213
|
+
`controllers. (Safe to ignore if this server is populated at runtime ` +
|
|
214
|
+
`via strategy.registerTool()/registerResource()/registerPrompt().)`,
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// ---------------------------------------------------------------------------
|
|
219
|
+
// Capability discovery — straight from the bound handlers' metadata
|
|
220
|
+
// ---------------------------------------------------------------------------
|
|
221
|
+
|
|
222
|
+
private buildCapabilities(): void {
|
|
223
|
+
if (this.built) return;
|
|
224
|
+
this.built = true;
|
|
225
|
+
|
|
226
|
+
for (const [route, handler] of this.messageHandlers) {
|
|
227
|
+
const extras = (handler as MessageHandler & { extras?: McpHandlerExtras })
|
|
228
|
+
.extras;
|
|
229
|
+
if (!extras?.mcpType) {
|
|
230
|
+
// Prune stray non-MCP @MessagePattern handlers bound via the
|
|
231
|
+
// undefined-transport clause, so the MCP server only sees MCP handlers.
|
|
232
|
+
this.messageHandlers.delete(route);
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const { mcpType, mcpMethodKey, mcpMethodRef } = extras;
|
|
237
|
+
const invoke: Invoke = (payload, ctx) =>
|
|
238
|
+
this.invokeViaPipeline(handler, payload, ctx);
|
|
239
|
+
|
|
240
|
+
switch (mcpType) {
|
|
241
|
+
case 'tool':
|
|
242
|
+
this.tools.push({
|
|
243
|
+
metadata: this.readToolMetadata(mcpMethodRef, mcpMethodKey),
|
|
244
|
+
invoke,
|
|
245
|
+
});
|
|
246
|
+
break;
|
|
247
|
+
case 'resource':
|
|
248
|
+
this.resources.push({
|
|
249
|
+
metadata: this.readMetadata<ResourceMetadata>(
|
|
250
|
+
MCP_RESOURCE_METADATA_KEY,
|
|
251
|
+
mcpMethodRef,
|
|
252
|
+
mcpMethodKey,
|
|
253
|
+
),
|
|
254
|
+
invoke,
|
|
255
|
+
});
|
|
256
|
+
break;
|
|
257
|
+
case 'resource-template':
|
|
258
|
+
this.templates.push({
|
|
259
|
+
metadata: this.readMetadata<ResourceTemplateMetadata>(
|
|
260
|
+
MCP_RESOURCE_TEMPLATE_METADATA_KEY,
|
|
261
|
+
mcpMethodRef,
|
|
262
|
+
mcpMethodKey,
|
|
263
|
+
),
|
|
264
|
+
invoke,
|
|
265
|
+
});
|
|
266
|
+
break;
|
|
267
|
+
case 'prompt':
|
|
268
|
+
this.prompts.push({
|
|
269
|
+
metadata: this.readMetadata<PromptMetadata>(
|
|
270
|
+
MCP_PROMPT_METADATA_KEY,
|
|
271
|
+
mcpMethodRef,
|
|
272
|
+
mcpMethodKey,
|
|
273
|
+
),
|
|
274
|
+
invoke,
|
|
275
|
+
});
|
|
276
|
+
break;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
private readToolMetadata(
|
|
282
|
+
methodRef: McpMethodRef,
|
|
283
|
+
methodKey: string,
|
|
284
|
+
): ToolMetadata {
|
|
285
|
+
const base: ToolMetadata = {
|
|
286
|
+
...(Reflect.getMetadata(
|
|
287
|
+
MCP_TOOL_METADATA_KEY,
|
|
288
|
+
methodRef,
|
|
289
|
+
) as ToolMetadata),
|
|
290
|
+
};
|
|
291
|
+
if (!base.name) base.name = methodKey;
|
|
292
|
+
if (!base.parameters) base.parameters = z.object({});
|
|
293
|
+
|
|
294
|
+
const isPublic = Reflect.getMetadata(MCP_PUBLIC_METADATA_KEY, methodRef);
|
|
295
|
+
const requiredScopes = Reflect.getMetadata(
|
|
296
|
+
MCP_SCOPES_METADATA_KEY,
|
|
297
|
+
methodRef,
|
|
298
|
+
);
|
|
299
|
+
const requiredRoles = Reflect.getMetadata(
|
|
300
|
+
MCP_ROLES_METADATA_KEY,
|
|
301
|
+
methodRef,
|
|
302
|
+
);
|
|
303
|
+
if (isPublic !== undefined) base.isPublic = isPublic;
|
|
304
|
+
if (requiredScopes) base.requiredScopes = requiredScopes;
|
|
305
|
+
if (requiredRoles) base.requiredRoles = requiredRoles;
|
|
306
|
+
return base;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
private readMetadata<T extends { name?: string }>(
|
|
310
|
+
key: string,
|
|
311
|
+
methodRef: McpMethodRef,
|
|
312
|
+
methodKey: string,
|
|
313
|
+
): T {
|
|
314
|
+
const metadata = { ...(Reflect.getMetadata(key, methodRef) as T) };
|
|
315
|
+
if (!metadata.name) metadata.name = methodKey;
|
|
316
|
+
return metadata;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
private getTools(): ToolCapability[] {
|
|
320
|
+
return [...this.tools, ...this.dynamicTools.values()];
|
|
321
|
+
}
|
|
322
|
+
private getResources(): ResourceCapability[] {
|
|
323
|
+
return [...this.resources, ...this.dynamicResources.values()];
|
|
324
|
+
}
|
|
325
|
+
private getTemplates(): TemplateCapability[] {
|
|
326
|
+
return [...this.templates];
|
|
327
|
+
}
|
|
328
|
+
private getPrompts(): PromptCapability[] {
|
|
329
|
+
return [...this.prompts, ...this.dynamicPrompts.values()];
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// ---------------------------------------------------------------------------
|
|
333
|
+
// SDK server + request handlers
|
|
334
|
+
// ---------------------------------------------------------------------------
|
|
335
|
+
|
|
336
|
+
private createTransportContext(): McpTransportContext {
|
|
337
|
+
return {
|
|
338
|
+
createServer: () => this.createServer(),
|
|
339
|
+
bindRequestHandlers: (server, session, rawRequest) =>
|
|
340
|
+
this.bindRequestHandlers(server, session, rawRequest),
|
|
341
|
+
httpAdapter: this.httpAdapter,
|
|
342
|
+
options: this.options,
|
|
343
|
+
logger: this.logger,
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
private createServer(): McpServer {
|
|
348
|
+
const capabilities: ServerCapabilities = {
|
|
349
|
+
...(this.options.capabilities ?? {}),
|
|
350
|
+
};
|
|
351
|
+
if (this.getTools().length > 0) {
|
|
352
|
+
capabilities.tools = capabilities.tools ?? { listChanged: true };
|
|
353
|
+
}
|
|
354
|
+
if (this.getResources().length + this.getTemplates().length > 0) {
|
|
355
|
+
capabilities.resources = capabilities.resources ?? { listChanged: true };
|
|
356
|
+
}
|
|
357
|
+
if (this.getPrompts().length > 0) {
|
|
358
|
+
capabilities.prompts = capabilities.prompts ?? { listChanged: true };
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const server = new McpServer(
|
|
362
|
+
{
|
|
363
|
+
name: this.options.name,
|
|
364
|
+
version: this.options.version,
|
|
365
|
+
...(this.options.title && { title: this.options.title }),
|
|
366
|
+
...(this.options.description && {
|
|
367
|
+
description: this.options.description,
|
|
368
|
+
}),
|
|
369
|
+
...(this.options.websiteUrl && { websiteUrl: this.options.websiteUrl }),
|
|
370
|
+
...(this.options.icons && { icons: this.options.icons }),
|
|
371
|
+
},
|
|
372
|
+
{ capabilities, instructions: this.options.instructions ?? '' },
|
|
373
|
+
);
|
|
374
|
+
|
|
375
|
+
return this.options.serverMutator
|
|
376
|
+
? this.options.serverMutator(server)
|
|
377
|
+
: server;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
bindRequestHandlers(
|
|
381
|
+
server: McpServer,
|
|
382
|
+
session: Pick<McpSessionInfo, 'transport' | 'stateless' | 'sessionId'>,
|
|
383
|
+
rawRequest?: unknown,
|
|
384
|
+
): void {
|
|
385
|
+
this.bindToolHandlers(server, session, rawRequest);
|
|
386
|
+
this.bindResourceHandlers(server, session, rawRequest);
|
|
387
|
+
this.bindPromptHandlers(server, session, rawRequest);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
private buildContext(
|
|
391
|
+
server: McpServer,
|
|
392
|
+
request: McpRequest,
|
|
393
|
+
session: Pick<McpSessionInfo, 'transport' | 'stateless' | 'sessionId'>,
|
|
394
|
+
rawRequest?: unknown,
|
|
395
|
+
): McpContext {
|
|
396
|
+
const sessionId =
|
|
397
|
+
session.sessionId ??
|
|
398
|
+
(server.server.transport as { sessionId?: string } | undefined)
|
|
399
|
+
?.sessionId;
|
|
400
|
+
return new McpContext(
|
|
401
|
+
[server, request, { ...session, sessionId }, rawRequest],
|
|
402
|
+
this.logger,
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
private getUser(rawRequest?: unknown): any {
|
|
407
|
+
return rawRequest ? (rawRequest as { user?: unknown }).user : undefined;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
private bindToolHandlers(
|
|
411
|
+
server: McpServer,
|
|
412
|
+
session: Pick<McpSessionInfo, 'transport' | 'stateless' | 'sessionId'>,
|
|
413
|
+
rawRequest?: unknown,
|
|
414
|
+
): void {
|
|
415
|
+
if (this.getTools().length === 0) return;
|
|
416
|
+
|
|
417
|
+
const allowUnauthenticatedAccess =
|
|
418
|
+
this.options.allowUnauthenticatedAccess ?? false;
|
|
419
|
+
|
|
420
|
+
server.server.setRequestHandler(ListToolsRequestSchema, () => {
|
|
421
|
+
const user = this.getUser(rawRequest);
|
|
422
|
+
const tools = this.getTools()
|
|
423
|
+
.filter((tool) =>
|
|
424
|
+
this.authService.canAccessTool(
|
|
425
|
+
user,
|
|
426
|
+
tool,
|
|
427
|
+
allowUnauthenticatedAccess,
|
|
428
|
+
),
|
|
429
|
+
)
|
|
430
|
+
.map((tool) => {
|
|
431
|
+
const schema: Record<string, unknown> = {
|
|
432
|
+
name: tool.metadata.name,
|
|
433
|
+
description: tool.metadata.description,
|
|
434
|
+
annotations: tool.metadata.annotations,
|
|
435
|
+
_meta: tool.metadata._meta,
|
|
436
|
+
};
|
|
437
|
+
const securitySchemes = this.authService.generateSecuritySchemes(
|
|
438
|
+
tool,
|
|
439
|
+
allowUnauthenticatedAccess,
|
|
440
|
+
);
|
|
441
|
+
if (securitySchemes.length > 0) {
|
|
442
|
+
schema._meta = { ...(schema._meta as object), securitySchemes };
|
|
443
|
+
}
|
|
444
|
+
const input = normalizeObjectSchema(tool.metadata.parameters);
|
|
445
|
+
if (input) schema.inputSchema = toJsonSchemaCompat(input);
|
|
446
|
+
const output = normalizeObjectSchema(tool.metadata.outputSchema);
|
|
447
|
+
if (output) {
|
|
448
|
+
schema.outputSchema = {
|
|
449
|
+
...toJsonSchemaCompat(output),
|
|
450
|
+
type: 'object',
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
return schema;
|
|
454
|
+
});
|
|
455
|
+
return { tools };
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
server.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
459
|
+
const tool = this.getTools().find(
|
|
460
|
+
(t) => t.metadata.name === request.params.name,
|
|
461
|
+
);
|
|
462
|
+
if (!tool) {
|
|
463
|
+
throw new McpError(
|
|
464
|
+
ErrorCode.MethodNotFound,
|
|
465
|
+
`Unknown tool: ${request.params.name}`,
|
|
466
|
+
);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
this.authService.validateToolAccess(
|
|
470
|
+
this.getUser(rawRequest),
|
|
471
|
+
tool,
|
|
472
|
+
allowUnauthenticatedAccess,
|
|
473
|
+
);
|
|
474
|
+
|
|
475
|
+
if (tool.metadata.parameters) {
|
|
476
|
+
const validation = tool.metadata.parameters.safeParse(
|
|
477
|
+
request.params.arguments ?? {},
|
|
478
|
+
);
|
|
479
|
+
if (!validation.success) {
|
|
480
|
+
const issues = validation.error.issues
|
|
481
|
+
.map((issue) => {
|
|
482
|
+
const path = issue.path.length > 0 ? issue.path.join('.') : '';
|
|
483
|
+
return `${path ? `[${path}]: ` : ''}${issue.message}`;
|
|
484
|
+
})
|
|
485
|
+
.join('; ');
|
|
486
|
+
return {
|
|
487
|
+
content: [{ type: 'text', text: `Invalid parameters: ${issues}` }],
|
|
488
|
+
isError: true,
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
request.params.arguments = validation.data as Record<string, unknown>;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
const ctx = this.buildContext(server, request, session, rawRequest);
|
|
495
|
+
try {
|
|
496
|
+
const result = await tool.invoke(request.params.arguments ?? {}, ctx);
|
|
497
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
498
|
+
return this.formatToolResult(result, tool.metadata.outputSchema);
|
|
499
|
+
} catch (error) {
|
|
500
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
501
|
+
return this.toErrorResult(error);
|
|
502
|
+
}
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
private bindResourceHandlers(
|
|
507
|
+
server: McpServer,
|
|
508
|
+
session: Pick<McpSessionInfo, 'transport' | 'stateless' | 'sessionId'>,
|
|
509
|
+
rawRequest?: unknown,
|
|
510
|
+
): void {
|
|
511
|
+
if (this.getResources().length + this.getTemplates().length === 0) return;
|
|
512
|
+
|
|
513
|
+
server.server.setRequestHandler(ListResourcesRequestSchema, () => ({
|
|
514
|
+
resources: this.getResources().map((r) => r.metadata),
|
|
515
|
+
}));
|
|
516
|
+
|
|
517
|
+
server.server.setRequestHandler(ListResourceTemplatesRequestSchema, () => ({
|
|
518
|
+
resourceTemplates: this.getTemplates().map((r) => r.metadata),
|
|
519
|
+
}));
|
|
520
|
+
|
|
521
|
+
server.server.setRequestHandler(
|
|
522
|
+
ReadResourceRequestSchema,
|
|
523
|
+
async (request) => {
|
|
524
|
+
const uri = request.params.uri;
|
|
525
|
+
const templateMatch = matchResourceTemplateByUri(
|
|
526
|
+
this.getTemplates().map((cap) => ({
|
|
527
|
+
uriTemplate: cap.metadata.uriTemplate,
|
|
528
|
+
cap,
|
|
529
|
+
})),
|
|
530
|
+
uri,
|
|
531
|
+
);
|
|
532
|
+
const resourceMatch = matchResourceByUri(
|
|
533
|
+
this.getResources().map((cap) => ({ uri: cap.metadata.uri, cap })),
|
|
534
|
+
uri,
|
|
535
|
+
);
|
|
536
|
+
|
|
537
|
+
let invoke: Invoke;
|
|
538
|
+
let params: Record<string, unknown>;
|
|
539
|
+
if (templateMatch) {
|
|
540
|
+
invoke = templateMatch.template.cap.invoke;
|
|
541
|
+
params = { ...templateMatch.params, ...request.params };
|
|
542
|
+
} else if (resourceMatch) {
|
|
543
|
+
invoke = resourceMatch.resource.cap.invoke;
|
|
544
|
+
params = { ...resourceMatch.params, ...request.params };
|
|
545
|
+
} else {
|
|
546
|
+
throw new McpError(
|
|
547
|
+
ErrorCode.MethodNotFound,
|
|
548
|
+
`Unknown resource: ${uri}`,
|
|
549
|
+
);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
const ctx = this.buildContext(server, request, session, rawRequest);
|
|
553
|
+
return (await invoke(params, ctx)) as ReadResourceResult;
|
|
554
|
+
},
|
|
555
|
+
);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
private bindPromptHandlers(
|
|
559
|
+
server: McpServer,
|
|
560
|
+
session: Pick<McpSessionInfo, 'transport' | 'stateless' | 'sessionId'>,
|
|
561
|
+
rawRequest?: unknown,
|
|
562
|
+
): void {
|
|
563
|
+
if (this.getPrompts().length === 0) return;
|
|
564
|
+
|
|
565
|
+
server.server.setRequestHandler(ListPromptsRequestSchema, () => ({
|
|
566
|
+
prompts: this.getPrompts().map((prompt) => ({
|
|
567
|
+
name: prompt.metadata.name,
|
|
568
|
+
description: prompt.metadata.description,
|
|
569
|
+
arguments: prompt.metadata.parameters
|
|
570
|
+
? Object.entries(prompt.metadata.parameters.shape).map(
|
|
571
|
+
([name, field]): PromptArgument => ({
|
|
572
|
+
name,
|
|
573
|
+
description: field.description,
|
|
574
|
+
required: !field.isOptional(),
|
|
575
|
+
}),
|
|
576
|
+
)
|
|
577
|
+
: [],
|
|
578
|
+
})),
|
|
579
|
+
}));
|
|
580
|
+
|
|
581
|
+
server.server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
582
|
+
const prompt = this.getPrompts().find(
|
|
583
|
+
(p) => p.metadata.name === request.params.name,
|
|
584
|
+
);
|
|
585
|
+
if (!prompt) {
|
|
586
|
+
throw new McpError(
|
|
587
|
+
ErrorCode.MethodNotFound,
|
|
588
|
+
`Unknown prompt: ${request.params.name}`,
|
|
589
|
+
);
|
|
590
|
+
}
|
|
591
|
+
const ctx = this.buildContext(server, request, session, rawRequest);
|
|
592
|
+
return (await prompt.invoke(
|
|
593
|
+
request.params.arguments,
|
|
594
|
+
ctx,
|
|
595
|
+
)) as GetPromptResult;
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// ---------------------------------------------------------------------------
|
|
600
|
+
// RPC pipeline invocation
|
|
601
|
+
// ---------------------------------------------------------------------------
|
|
602
|
+
|
|
603
|
+
private async invokeViaPipeline(
|
|
604
|
+
handler: MessageHandler,
|
|
605
|
+
payload: unknown,
|
|
606
|
+
ctx: BaseRpcContext,
|
|
607
|
+
): Promise<unknown> {
|
|
608
|
+
let resultPromise!: Promise<unknown>;
|
|
609
|
+
const done = () => {
|
|
610
|
+
resultPromise = (async () => {
|
|
611
|
+
const resultOrStream: unknown = await handler(payload, ctx);
|
|
612
|
+
return firstValueFrom(this.transformToObservable(resultOrStream));
|
|
613
|
+
})();
|
|
614
|
+
return resultPromise;
|
|
615
|
+
};
|
|
616
|
+
// onProcessingStartHook may be async (request-scoped lifecycle); the base
|
|
617
|
+
// type declares it `void`, so wrap to await defensively without a lint error.
|
|
618
|
+
await Promise.resolve(
|
|
619
|
+
this.onProcessingStartHook(this.transportId, ctx, done),
|
|
620
|
+
);
|
|
621
|
+
try {
|
|
622
|
+
return await resultPromise;
|
|
623
|
+
} finally {
|
|
624
|
+
this.onProcessingEndHook?.(this.transportId, ctx);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
private formatToolResult(result: any, outputSchema?: ZodType): any {
|
|
629
|
+
if (result && typeof result === 'object' && Array.isArray(result.content)) {
|
|
630
|
+
return result;
|
|
631
|
+
}
|
|
632
|
+
const defaultContent = [{ type: 'text', text: JSON.stringify(result) }];
|
|
633
|
+
if (outputSchema) {
|
|
634
|
+
const validation = outputSchema.safeParse(result);
|
|
635
|
+
if (!validation.success) {
|
|
636
|
+
throw new McpError(
|
|
637
|
+
ErrorCode.InternalError,
|
|
638
|
+
`Tool result does not match outputSchema: ${validation.error.message}`,
|
|
639
|
+
);
|
|
640
|
+
}
|
|
641
|
+
return { structuredContent: validation.data, content: defaultContent };
|
|
642
|
+
}
|
|
643
|
+
return { content: defaultContent };
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* Turn an error thrown while invoking a tool into a result the calling agent
|
|
648
|
+
* can act on.
|
|
649
|
+
*
|
|
650
|
+
* We deliberately surface the most specific message available so the agent can
|
|
651
|
+
* tell *why* the call failed and whether retrying makes sense:
|
|
652
|
+
*
|
|
653
|
+
* - `McpError` is re-thrown so the SDK emits a real JSON-RPC error with its
|
|
654
|
+
* code (e.g. invalid params, output-schema mismatch).
|
|
655
|
+
* - Errors raised intentionally for the client — `throw new RpcException('…')`
|
|
656
|
+
* or anything surfaced by a `@UseFilters` exception filter — carry an
|
|
657
|
+
* explicit message/payload, which we return verbatim as an `isError` result.
|
|
658
|
+
* - Unexpected errors (`throw new Error('boom')` in a handler) are masked to a
|
|
659
|
+
* generic "Internal server error" by NestJS's default RPC exception handler
|
|
660
|
+
* *before* they reach here. That is intentional (don't leak internals); to
|
|
661
|
+
* surface a custom message, throw `RpcException` or register the exported
|
|
662
|
+
* `McpExceptionFilter`. Either way the agent gets `isError: true` and knows
|
|
663
|
+
* the failure is server-side, not a bad-input problem it can fix by retrying.
|
|
664
|
+
*/
|
|
665
|
+
private toErrorResult(error: unknown): any {
|
|
666
|
+
if (error instanceof McpError) {
|
|
667
|
+
throw error;
|
|
668
|
+
}
|
|
669
|
+
let message = 'Internal server error';
|
|
670
|
+
if (error instanceof Error) {
|
|
671
|
+
message = error.message;
|
|
672
|
+
} else if (typeof error === 'string') {
|
|
673
|
+
message = error;
|
|
674
|
+
} else if (
|
|
675
|
+
error &&
|
|
676
|
+
typeof error === 'object' &&
|
|
677
|
+
typeof (error as { message?: unknown }).message === 'string'
|
|
678
|
+
) {
|
|
679
|
+
// RpcException payloads arrive as `{ status: 'error', message: '…' }`.
|
|
680
|
+
message = (error as { message: string }).message;
|
|
681
|
+
}
|
|
682
|
+
this.logger.error(message);
|
|
683
|
+
return { content: [{ type: 'text', text: message }], isError: true };
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
// ---------------------------------------------------------------------------
|
|
687
|
+
// Dynamic capability registration
|
|
688
|
+
// ---------------------------------------------------------------------------
|
|
689
|
+
|
|
690
|
+
registerTool(definition: DynamicToolDefinition): void {
|
|
691
|
+
const handler: DynamicToolHandler = definition.handler;
|
|
692
|
+
this.dynamicTools.set(definition.name, {
|
|
693
|
+
metadata: {
|
|
694
|
+
name: definition.name,
|
|
695
|
+
description: definition.description,
|
|
696
|
+
parameters: definition.parameters ?? z.object({}),
|
|
697
|
+
outputSchema: definition.outputSchema,
|
|
698
|
+
annotations: definition.annotations,
|
|
699
|
+
_meta: definition._meta,
|
|
700
|
+
isPublic: definition.isPublic,
|
|
701
|
+
requiredScopes: definition.requiredScopes,
|
|
702
|
+
requiredRoles: definition.requiredRoles,
|
|
703
|
+
},
|
|
704
|
+
invoke: (payload, ctx) =>
|
|
705
|
+
Promise.resolve(
|
|
706
|
+
handler(payload as any, ctx, ctx.getRawRequest()) as unknown,
|
|
707
|
+
),
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
registerResource(definition: DynamicResourceDefinition): void {
|
|
712
|
+
const handler: DynamicResourceHandler = definition.handler;
|
|
713
|
+
this.dynamicResources.set(definition.uri, {
|
|
714
|
+
metadata: {
|
|
715
|
+
uri: definition.uri,
|
|
716
|
+
name: definition.name ?? definition.uri,
|
|
717
|
+
description: definition.description,
|
|
718
|
+
mimeType: definition.mimeType,
|
|
719
|
+
_meta: definition._meta,
|
|
720
|
+
},
|
|
721
|
+
invoke: (payload, ctx) =>
|
|
722
|
+
Promise.resolve(
|
|
723
|
+
handler(payload as any, ctx, ctx.getRawRequest()) as unknown,
|
|
724
|
+
),
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
registerPrompt(definition: DynamicPromptDefinition): void {
|
|
729
|
+
const handler: DynamicPromptHandler = definition.handler;
|
|
730
|
+
this.dynamicPrompts.set(definition.name, {
|
|
731
|
+
metadata: {
|
|
732
|
+
name: definition.name,
|
|
733
|
+
description: definition.description,
|
|
734
|
+
parameters: definition.parameters,
|
|
735
|
+
},
|
|
736
|
+
invoke: (payload, ctx) =>
|
|
737
|
+
Promise.resolve(
|
|
738
|
+
handler(payload as any, ctx, ctx.getRawRequest()) as unknown,
|
|
739
|
+
),
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
removeTool(name: string): void {
|
|
744
|
+
this.dynamicTools.delete(name);
|
|
745
|
+
}
|
|
746
|
+
removeResource(uri: string): void {
|
|
747
|
+
this.dynamicResources.delete(uri);
|
|
748
|
+
}
|
|
749
|
+
removePrompt(name: string): void {
|
|
750
|
+
this.dynamicPrompts.delete(name);
|
|
751
|
+
}
|
|
752
|
+
}
|