loopback4-mcp 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +87 -0
  2. package/dist/component.d.ts +11 -0
  3. package/dist/component.js +22 -0
  4. package/dist/component.js.map +1 -0
  5. package/dist/constants/index.d.ts +1 -0
  6. package/dist/constants/index.js +5 -0
  7. package/dist/constants/index.js.map +1 -0
  8. package/dist/constants/mcp-tool.constant.d.ts +1 -0
  9. package/dist/constants/mcp-tool.constant.js +6 -0
  10. package/dist/constants/mcp-tool.constant.js.map +1 -0
  11. package/dist/controllers/index.d.ts +1 -0
  12. package/dist/controllers/index.js +5 -0
  13. package/dist/controllers/index.js.map +1 -0
  14. package/dist/controllers/mcp.controller.d.ts +10 -0
  15. package/dist/controllers/mcp.controller.js +121 -0
  16. package/dist/controllers/mcp.controller.js.map +1 -0
  17. package/dist/decorators/index.d.ts +1 -0
  18. package/dist/decorators/index.js +5 -0
  19. package/dist/decorators/index.js.map +1 -0
  20. package/dist/decorators/mcp-tool.decorator.d.ts +2 -0
  21. package/dist/decorators/mcp-tool.decorator.js +45 -0
  22. package/dist/decorators/mcp-tool.decorator.js.map +1 -0
  23. package/dist/index.d.ts +5 -0
  24. package/dist/index.js +9 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/interfaces/index.d.ts +1 -0
  27. package/dist/interfaces/index.js +5 -0
  28. package/dist/interfaces/index.js.map +1 -0
  29. package/dist/interfaces/mcp-hook-provider.interface.d.ts +12 -0
  30. package/dist/interfaces/mcp-hook-provider.interface.js +3 -0
  31. package/dist/interfaces/mcp-hook-provider.interface.js.map +1 -0
  32. package/dist/keys.d.ts +1 -0
  33. package/dist/keys.js +3 -0
  34. package/dist/keys.js.map +1 -0
  35. package/dist/observers/index.d.ts +1 -0
  36. package/dist/observers/index.js +5 -0
  37. package/dist/observers/index.js.map +1 -0
  38. package/dist/observers/mcp-tool-registry-boot.observer.d.ts +19 -0
  39. package/dist/observers/mcp-tool-registry-boot.observer.js +42 -0
  40. package/dist/observers/mcp-tool-registry-boot.observer.js.map +1 -0
  41. package/dist/services/index.d.ts +3 -0
  42. package/dist/services/index.js +7 -0
  43. package/dist/services/index.js.map +1 -0
  44. package/dist/services/mcp-schema-generator-service.service.d.ts +20 -0
  45. package/dist/services/mcp-schema-generator-service.service.js +73 -0
  46. package/dist/services/mcp-schema-generator-service.service.js.map +1 -0
  47. package/dist/services/mcp-server-factory.service.d.ts +13 -0
  48. package/dist/services/mcp-server-factory.service.js +51 -0
  49. package/dist/services/mcp-server-factory.service.js.map +1 -0
  50. package/dist/services/mcp-tool-registry.service.d.ts +49 -0
  51. package/dist/services/mcp-tool-registry.service.js +245 -0
  52. package/dist/services/mcp-tool-registry.service.js.map +1 -0
  53. package/dist/types.d.ts +46 -0
  54. package/dist/types.js +17 -0
  55. package/dist/types.js.map +1 -0
  56. package/dist/utils/index.d.ts +1 -0
  57. package/dist/utils/index.js +5 -0
  58. package/dist/utils/index.js.map +1 -0
  59. package/dist/utils/mcp-parameter-extractor.d.ts +9 -0
  60. package/dist/utils/mcp-parameter-extractor.js +51 -0
  61. package/dist/utils/mcp-parameter-extractor.js.map +1 -0
  62. package/package.json +139 -0
  63. package/src/component.ts +18 -0
  64. package/src/constants/index.ts +1 -0
  65. package/src/constants/mcp-tool.constant.ts +2 -0
  66. package/src/controllers/README.md +6 -0
  67. package/src/controllers/index.ts +1 -0
  68. package/src/controllers/mcp.controller.ts +112 -0
  69. package/src/decorators/README.md +34 -0
  70. package/src/decorators/index.ts +1 -0
  71. package/src/decorators/mcp-tool.decorator.ts +68 -0
  72. package/src/index.ts +5 -0
  73. package/src/interfaces/index.ts +1 -0
  74. package/src/interfaces/mcp-hook-provider.interface.ts +11 -0
  75. package/src/keys.ts +1 -0
  76. package/src/mixins/README.md +216 -0
  77. package/src/observers/index.ts +1 -0
  78. package/src/observers/mcp-tool-registry-boot.observer.ts +40 -0
  79. package/src/providers/README.md +129 -0
  80. package/src/repositories/README.md +5 -0
  81. package/src/services/index.ts +3 -0
  82. package/src/services/mcp-schema-generator-service.service.ts +80 -0
  83. package/src/services/mcp-server-factory.service.ts +71 -0
  84. package/src/services/mcp-tool-registry.service.ts +368 -0
  85. package/src/types.ts +59 -0
  86. package/src/utils/index.ts +1 -0
  87. package/src/utils/mcp-parameter-extractor.ts +67 -0
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.McpSchemaGeneratorService = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const core_1 = require("@loopback/core");
6
+ const core_2 = require("@sourceloop/core");
7
+ const zod_1 = require("zod");
8
+ const utils_1 = require("../utils");
9
+ let McpSchemaGeneratorService = class McpSchemaGeneratorService {
10
+ constructor(logger) {
11
+ this.logger = logger;
12
+ }
13
+ /**
14
+ * Create Zod schema for a single parameter using LoopBack type string
15
+ */
16
+ createZodSchemaForParameterType(paramType) {
17
+ switch (paramType) {
18
+ case 'string':
19
+ return zod_1.z.string();
20
+ case 'number':
21
+ return zod_1.z.number();
22
+ case 'boolean':
23
+ return zod_1.z.boolean();
24
+ case 'object':
25
+ return zod_1.z.object({}).passthrough();
26
+ case 'array':
27
+ return zod_1.z.array(zod_1.z.any());
28
+ default:
29
+ return zod_1.z.any();
30
+ }
31
+ }
32
+ /**
33
+ * Generate tool schema using LoopBack metadata
34
+ */
35
+ generateToolSchemaFromLoopBack(options, target, methodName) {
36
+ let schema = options.schema;
37
+ if (!schema || Object.keys(schema).length === 0) {
38
+ try {
39
+ const { parameterNames, parameterOptional, parameterTypes } = (0, utils_1.extractParameterInfo)(target, methodName);
40
+ if (parameterNames.length > 0) {
41
+ let combinedSchema = zod_1.z.object({});
42
+ parameterTypes.forEach((paramType, index) => {
43
+ const zodSchema = this.createZodSchemaForParameterType(paramType);
44
+ const paramName = parameterNames[index];
45
+ const isOptional = parameterOptional[index];
46
+ combinedSchema = combinedSchema.merge(zod_1.z.object({
47
+ [paramName]: isOptional ? zodSchema.optional() : zodSchema,
48
+ }));
49
+ });
50
+ schema = combinedSchema.shape;
51
+ }
52
+ else {
53
+ schema = {};
54
+ }
55
+ }
56
+ catch (error) {
57
+ this.logger.warn(`Failed to generate schema for tool ${options.name} from LoopBack metadata:`, error);
58
+ schema = {};
59
+ }
60
+ }
61
+ else {
62
+ schema = schema !== null && schema !== void 0 ? schema : {};
63
+ }
64
+ return schema;
65
+ }
66
+ };
67
+ exports.McpSchemaGeneratorService = McpSchemaGeneratorService;
68
+ exports.McpSchemaGeneratorService = McpSchemaGeneratorService = tslib_1.__decorate([
69
+ (0, core_1.injectable)({ scope: core_1.BindingScope.SINGLETON }),
70
+ tslib_1.__param(0, (0, core_1.inject)(core_2.LOGGER.LOGGER_INJECT)),
71
+ tslib_1.__metadata("design:paramtypes", [Object])
72
+ ], McpSchemaGeneratorService);
73
+ //# sourceMappingURL=mcp-schema-generator-service.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-schema-generator-service.service.js","sourceRoot":"","sources":["../../src/services/mcp-schema-generator-service.service.ts"],"names":[],"mappings":";;;;AAAA,yCAAgE;AAChE,2CAAiD;AACjD,6BAAmC;AACnC,oCAA8C;AAGvC,IAAM,yBAAyB,GAA/B,MAAM,yBAAyB;IACpC,YAEmB,MAAe;QAAf,WAAM,GAAN,MAAM,CAAS;IAC/B,CAAC;IAEJ;;OAEG;IACH,+BAA+B,CAAC,SAAiB;QAC/C,QAAQ,SAAS,EAAE;YACjB,KAAK,QAAQ;gBACX,OAAO,OAAC,CAAC,MAAM,EAAE,CAAC;YACpB,KAAK,QAAQ;gBACX,OAAO,OAAC,CAAC,MAAM,EAAE,CAAC;YACpB,KAAK,SAAS;gBACZ,OAAO,OAAC,CAAC,OAAO,EAAE,CAAC;YACrB,KAAK,QAAQ;gBACX,OAAO,OAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;YACpC,KAAK,OAAO;gBACV,OAAO,OAAC,CAAC,KAAK,CAAC,OAAC,CAAC,GAAG,EAAE,CAAC,CAAC;YAC1B;gBACE,OAAO,OAAC,CAAC,GAAG,EAAE,CAAC;SAClB;IACH,CAAC;IAED;;OAEG;IACH,8BAA8B,CAC5B,OAAkE,EAClE,MAAc,EACd,UAAkB;QAElB,IAAI,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAE5B,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE;YAC/C,IAAI;gBACF,MAAM,EAAC,cAAc,EAAE,iBAAiB,EAAE,cAAc,EAAC,GACvD,IAAA,4BAAoB,EAAC,MAAM,EAAE,UAAU,CAAC,CAAC;gBAE3C,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE;oBAC7B,IAAI,cAAc,GAAG,OAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;oBAElC,cAAc,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,KAAK,EAAE,EAAE;wBAC1C,MAAM,SAAS,GAAG,IAAI,CAAC,+BAA+B,CAAC,SAAS,CAAC,CAAC;wBAClE,MAAM,SAAS,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;wBACxC,MAAM,UAAU,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;wBAE5C,cAAc,GAAG,cAAc,CAAC,KAAK,CACnC,OAAC,CAAC,MAAM,CAAC;4BACP,CAAC,SAAS,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,SAAS;yBAC3D,CAAC,CACH,CAAC;oBACJ,CAAC,CAAC,CAAC;oBAEH,MAAM,GAAG,cAAc,CAAC,KAAK,CAAC;iBAC/B;qBAAM;oBACL,MAAM,GAAG,EAAE,CAAC;iBACb;aACF;YAAC,OAAO,KAAK,EAAE;gBACd,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,sCAAsC,OAAO,CAAC,IAAI,0BAA0B,EAC5E,KAAK,CACN,CAAC;gBACF,MAAM,GAAG,EAAE,CAAC;aACb;SACF;aAAM;YACL,MAAM,GAAG,MAAM,aAAN,MAAM,cAAN,MAAM,GAAI,EAAE,CAAC;SACvB;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;CACF,CAAA;AAzEY,8DAAyB;oCAAzB,yBAAyB;IADrC,IAAA,iBAAU,EAAC,EAAC,KAAK,EAAE,mBAAY,CAAC,SAAS,EAAC,CAAC;IAGvC,mBAAA,IAAA,aAAM,EAAC,aAAM,CAAC,aAAa,CAAC,CAAA;;GAFpB,yBAAyB,CAyErC"}
@@ -0,0 +1,13 @@
1
+ import { Context } from '@loopback/core';
2
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ import { McpToolRegistry } from './mcp-tool-registry.service';
4
+ export declare class McpServerFactory {
5
+ private readonly ctx;
6
+ private readonly toolRegistry;
7
+ constructor(ctx: Context, toolRegistry: McpToolRegistry);
8
+ /**
9
+ * Create a new MCP server instance with tool registration
10
+ * Uses singleton registry with pre-computed tools and controller instances
11
+ */
12
+ createServer(): McpServer;
13
+ }
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.McpServerFactory = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const core_1 = require("@loopback/core");
6
+ const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
7
+ const mcp_tool_registry_service_1 = require("./mcp-tool-registry.service");
8
+ let McpServerFactory = class McpServerFactory {
9
+ constructor(ctx, toolRegistry) {
10
+ this.ctx = ctx;
11
+ this.toolRegistry = toolRegistry;
12
+ }
13
+ /**
14
+ * Create a new MCP server instance with tool registration
15
+ * Uses singleton registry with pre-computed tools and controller instances
16
+ */
17
+ createServer() {
18
+ // Create fresh server instance
19
+ const server = new mcp_js_1.McpServer({
20
+ name: 'Project management MCP server',
21
+ version: '1.0.0',
22
+ }, {
23
+ capabilities: {
24
+ tools: {},
25
+ },
26
+ });
27
+ // Tool registration from singleton registry
28
+ const toolDefinitions = this.toolRegistry.getToolDefinitions();
29
+ for (const toolDef of toolDefinitions) {
30
+ // Adapt the registry handler to work with the new API signature
31
+ // The new API expects (parameters, extra) instead of (context, args, extras)
32
+ const adaptedHandler = async (parameters, extra) => toolDef.handler(this.ctx, parameters, extra);
33
+ // Use the new registerTool API with type assertion to avoid deep type recursion
34
+ const registerTool = server.registerTool;
35
+ registerTool.call(server, toolDef.name, {
36
+ description: toolDef.description,
37
+ inputSchema: toolDef.schema,
38
+ }, adaptedHandler);
39
+ }
40
+ return server;
41
+ }
42
+ };
43
+ exports.McpServerFactory = McpServerFactory;
44
+ exports.McpServerFactory = McpServerFactory = tslib_1.__decorate([
45
+ (0, core_1.bind)({ scope: core_1.BindingScope.REQUEST }),
46
+ tslib_1.__param(0, core_1.inject.context()),
47
+ tslib_1.__param(1, (0, core_1.service)(mcp_tool_registry_service_1.McpToolRegistry)),
48
+ tslib_1.__metadata("design:paramtypes", [core_1.Context,
49
+ mcp_tool_registry_service_1.McpToolRegistry])
50
+ ], McpServerFactory);
51
+ //# sourceMappingURL=mcp-server-factory.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-server-factory.service.js","sourceRoot":"","sources":["../../src/services/mcp-server-factory.service.ts"],"names":[],"mappings":";;;;AAAA,yCAA4E;AAC5E,oEAAkE;AAClE,2EAA4D;AAQrD,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;IAC3B,YAEmB,GAAY,EAEZ,YAA6B;QAF7B,QAAG,GAAH,GAAG,CAAS;QAEZ,iBAAY,GAAZ,YAAY,CAAiB;IAC7C,CAAC;IAEJ;;;OAGG;IACH,YAAY;QACV,+BAA+B;QAC/B,MAAM,MAAM,GAAG,IAAI,kBAAS,CAC1B;YACE,IAAI,EAAE,+BAA+B;YACrC,OAAO,EAAE,OAAO;SACjB,EACD;YACE,YAAY,EAAE;gBACZ,KAAK,EAAE,EAAE;aACV;SACF,CACF,CAAC;QAEF,4CAA4C;QAC5C,MAAM,eAAe,GAAG,IAAI,CAAC,YAAY,CAAC,kBAAkB,EAAE,CAAC;QAC/D,KAAK,MAAM,OAAO,IAAI,eAAe,EAAE;YACrC,gEAAgE;YAChE,6EAA6E;YAC7E,MAAM,cAAc,GAAG,KAAK,EAC1B,UAAmC,EACnC,KAA6D,EAC7D,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,CAAC,CAAC;YAElD,gFAAgF;YAChF,MAAM,YAAY,GAChB,MAOD,CAAC,YAAY,CAAC;YAEf,YAAY,CAAC,IAAI,CACf,MAAM,EACN,OAAO,CAAC,IAAI,EACZ;gBACE,WAAW,EAAE,OAAO,CAAC,WAAW;gBAChC,WAAW,EAAE,OAAO,CAAC,MAAM;aAC5B,EACD,cAAc,CACf,CAAC;SACH;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;CACF,CAAA;AA5DY,4CAAgB;2BAAhB,gBAAgB;IAD5B,IAAA,WAAI,EAAC,EAAC,KAAK,EAAE,mBAAY,CAAC,OAAO,EAAC,CAAC;IAG/B,mBAAA,aAAM,CAAC,OAAO,EAAE,CAAA;IAEhB,mBAAA,IAAA,cAAO,EAAC,2CAAe,CAAC,CAAA;6CADH,cAAO;QAEE,2CAAe;GALrC,gBAAgB,CA4D5B"}
@@ -0,0 +1,49 @@
1
+ import { Application } from '@loopback/core';
2
+ import { ILogger } from '@sourceloop/core';
3
+ import { McpSchemaGeneratorService } from './mcp-schema-generator-service.service';
4
+ import { McpTool } from '../types';
5
+ export declare class McpToolRegistry {
6
+ private readonly app;
7
+ private readonly logger;
8
+ private readonly schemaGenerator;
9
+ private toolDefinitions;
10
+ private isInitialized;
11
+ constructor(app: Application, logger: ILogger, schemaGenerator: McpSchemaGeneratorService);
12
+ /**
13
+ * Get MCP tools from a class using LoopBack MetadataInspector
14
+ */
15
+ private getMcpToolsFromClass;
16
+ /**
17
+ * Initialize tool registry at startup - stores metadata without instantiating controllers
18
+ */
19
+ initialize(): Promise<void>;
20
+ /**
21
+ * Get all precomputed tool definitions (ultra-fast)
22
+ */
23
+ getToolDefinitions(): McpTool[];
24
+ /**
25
+ * Resolve hook function from provider binding
26
+ */
27
+ private resolveHook;
28
+ /**
29
+ * Set up authorization context and perform authorization check
30
+ */
31
+ private setupAuthorizationContext;
32
+ /**
33
+ * Prepare method arguments based on parameter patterns
34
+ */
35
+ private prepareMethodArguments;
36
+ /**
37
+ * Execute pre-hook if configured
38
+ */
39
+ private executePreHook;
40
+ /**
41
+ * Execute post-hook if configured
42
+ */
43
+ private executePostHook;
44
+ /**
45
+ * Execute the tool method with proper result wrapping
46
+ */
47
+ private executeToolMethod;
48
+ private errorToCallToolResult;
49
+ }
@@ -0,0 +1,245 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.McpToolRegistry = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const core_1 = require("@loopback/core");
6
+ const rest_1 = require("@loopback/rest");
7
+ const core_2 = require("@sourceloop/core");
8
+ const loopback4_authentication_1 = require("loopback4-authentication");
9
+ const loopback4_authorization_1 = require("loopback4-authorization");
10
+ const constants_1 = require("../constants");
11
+ const utils_1 = require("../utils");
12
+ const mcp_schema_generator_service_service_1 = require("./mcp-schema-generator-service.service");
13
+ let McpToolRegistry = class McpToolRegistry {
14
+ constructor(app, logger, schemaGenerator) {
15
+ this.app = app;
16
+ this.logger = logger;
17
+ this.schemaGenerator = schemaGenerator;
18
+ this.toolDefinitions = [];
19
+ this.isInitialized = false;
20
+ }
21
+ /**
22
+ * Get MCP tools from a class using LoopBack MetadataInspector
23
+ */
24
+ getMcpToolsFromClass(targetClass) {
25
+ const processedTools = [];
26
+ const prototype = targetClass.prototype;
27
+ const allTools = core_1.MetadataInspector.getAllMethodMetadata(constants_1.MCP_TOOL_METADATA_KEY, prototype);
28
+ if (!allTools || Object.keys(allTools).length === 0) {
29
+ return [];
30
+ }
31
+ for (const [methodName, tool] of Object.entries(allTools)) {
32
+ try {
33
+ const { parameterNames } = (0, utils_1.extractParameterInfo)(prototype, methodName);
34
+ // Create a copy with updated parameter info
35
+ const processedTool = {
36
+ ...tool,
37
+ parameterNames,
38
+ };
39
+ // Generate schema if needed
40
+ if (!processedTool.schema ||
41
+ Object.keys(processedTool.schema).length === 0) {
42
+ const schema = this.schemaGenerator.generateToolSchemaFromLoopBack({
43
+ name: processedTool.name,
44
+ description: processedTool.description,
45
+ schema: processedTool.schema,
46
+ }, prototype, methodName);
47
+ processedTool.schema = schema;
48
+ }
49
+ processedTools.push(processedTool);
50
+ }
51
+ catch (error) {
52
+ // Tool doesn't have LoopBack parameter decorators
53
+ this.logger.warn(`Tool ${methodName} missing LoopBack parameter decorators:`, error);
54
+ }
55
+ }
56
+ return processedTools;
57
+ }
58
+ /**
59
+ * Initialize tool registry at startup - stores metadata without instantiating controllers
60
+ */
61
+ async initialize() {
62
+ if (this.isInitialized)
63
+ return;
64
+ const controllerBindings = this.app.findByTag('controller');
65
+ // Process all bindings and collect tool metadata without instantiating controllers
66
+ const toolArrays = controllerBindings.map(binding => {
67
+ const controllerClass = binding.valueConstructor;
68
+ if (!controllerClass) {
69
+ this.logger.info(`No constructor found for binding ${binding.key}`);
70
+ return [];
71
+ }
72
+ const tools = this.getMcpToolsFromClass(controllerClass);
73
+ if (tools.length === 0)
74
+ return [];
75
+ // Create pre-bound tool definitions
76
+ return tools.map(tool => {
77
+ return {
78
+ ...tool,
79
+ controllerBinding: core_1.BindingKey.create(binding.key),
80
+ handler: async (requestContext, args, extras) => {
81
+ var _a, _b;
82
+ const ctx = await this.setupAuthorizationContext(requestContext, tool, controllerClass);
83
+ const hookContext = {
84
+ toolName: tool.name,
85
+ args: args,
86
+ metadata: (_a = tool.postHook) === null || _a === void 0 ? void 0 : _a.config,
87
+ };
88
+ await this.executePreHook(ctx, tool, hookContext);
89
+ let result;
90
+ try {
91
+ result = await this.executeToolMethod(ctx, binding.key, tool, hookContext, extras);
92
+ hookContext.result = result;
93
+ await this.executePostHook(ctx, tool, hookContext);
94
+ }
95
+ catch (err) {
96
+ const error = err instanceof Error ? err : new Error(String(err));
97
+ hookContext.error = error;
98
+ this.logger.error(`MCP Tool '${tool.name}' execution failed:`, error.message);
99
+ return this.errorToCallToolResult(error);
100
+ }
101
+ finally {
102
+ ctx.close();
103
+ }
104
+ return (_b = hookContext.result) !== null && _b !== void 0 ? _b : result;
105
+ },
106
+ };
107
+ });
108
+ });
109
+ this.toolDefinitions = toolArrays.flat();
110
+ this.isInitialized = true;
111
+ }
112
+ /**
113
+ * Get all precomputed tool definitions (ultra-fast)
114
+ */
115
+ getToolDefinitions() {
116
+ return this.toolDefinitions;
117
+ }
118
+ /**
119
+ * Resolve hook function from provider binding
120
+ */
121
+ async resolveHook(ctx, hookBinding) {
122
+ if (!hookBinding) {
123
+ return undefined;
124
+ }
125
+ try {
126
+ const bindingKey = typeof hookBinding === 'string'
127
+ ? core_1.BindingKey.create(hookBinding)
128
+ : hookBinding;
129
+ return await ctx.get(bindingKey);
130
+ }
131
+ catch (error) {
132
+ // Hook binding not found - this is not an error, just log and continue
133
+ this.logger.warn(`Hook binding ${hookBinding.toString()} not found:`, error);
134
+ return undefined;
135
+ }
136
+ }
137
+ /**
138
+ * Set up authorization context and perform authorization check
139
+ */
140
+ async setupAuthorizationContext(requestContext, tool, controllerClass) {
141
+ // Get authorization metadata from the controller method
142
+ const authMetadata = core_1.MetadataInspector.getMethodMetadata(loopback4_authorization_1.AUTHORIZATION_METADATA_ACCESSOR, controllerClass.prototype, tool.controllerFunction.name);
143
+ if (!authMetadata) {
144
+ this.logger.warn(`No authorization metadata found for MCP tool '${tool.name}'`);
145
+ throw new rest_1.HttpErrors.Forbidden(`MCP tool '${tool.name}' is missing authorization configuration.`);
146
+ }
147
+ // Check authorization first - get authorize function from context
148
+ const ctx = new core_1.Context(requestContext);
149
+ // Bind the authorization metadata so the authorize action provider can access it
150
+ ctx.bind(loopback4_authorization_1.AuthorizationBindings.METADATA).to(authMetadata);
151
+ // Bind controller information for authorization
152
+ ctx.bind(core_1.CoreBindings.CONTROLLER_CLASS).to(controllerClass);
153
+ ctx
154
+ .bind(core_1.CoreBindings.CONTROLLER_METHOD_NAME)
155
+ .to(tool.controllerFunction.name);
156
+ const user = await ctx.get(loopback4_authentication_1.AuthenticationBindings.CURRENT_USER);
157
+ const authorizeAction = await ctx.get(loopback4_authorization_1.AuthorizationBindings.AUTHORIZE_ACTION);
158
+ const isAuthorized = await authorizeAction(user.permissions);
159
+ if (!isAuthorized) {
160
+ this.logger.warn(`User ${user.id} is not authorized to access MCP tool '${tool.name}'`);
161
+ throw new rest_1.HttpErrors.Forbidden(`Access denied for MCP tool '${tool.name}'.`);
162
+ }
163
+ return ctx;
164
+ }
165
+ /**
166
+ * Prepare method arguments based on parameter patterns
167
+ */
168
+ prepareMethodArguments(tool, args) {
169
+ var _a;
170
+ // Extract individual parameter values by name
171
+ if (!((_a = tool.parameterNames) === null || _a === void 0 ? void 0 : _a.length)) {
172
+ // Fallback - pass args object directly if no parameter names
173
+ return [args];
174
+ }
175
+ // Extract individual values by parameter names
176
+ return tool.parameterNames.map(paramName => args === null || args === void 0 ? void 0 : args[paramName]);
177
+ }
178
+ /**
179
+ * Execute pre-hook if configured
180
+ */
181
+ async executePreHook(ctx, tool, hookContext) {
182
+ var _a;
183
+ const preHook = await this.resolveHook(ctx, (_a = tool.preHook) === null || _a === void 0 ? void 0 : _a.binding);
184
+ if (preHook) {
185
+ const preHookResult = await preHook(hookContext);
186
+ if (preHookResult) {
187
+ hookContext.args = preHookResult.args;
188
+ }
189
+ }
190
+ }
191
+ /**
192
+ * Execute post-hook if configured
193
+ */
194
+ async executePostHook(ctx, tool, hookContext) {
195
+ var _a;
196
+ const postHook = await this.resolveHook(ctx, (_a = tool.postHook) === null || _a === void 0 ? void 0 : _a.binding);
197
+ if (postHook) {
198
+ const postHookResult = await postHook(hookContext);
199
+ if (postHookResult) {
200
+ hookContext.result = postHookResult.result;
201
+ hookContext.args = postHookResult.args;
202
+ hookContext.error = postHookResult.error;
203
+ }
204
+ }
205
+ }
206
+ /**
207
+ * Execute the tool method with proper result wrapping
208
+ */
209
+ async executeToolMethod(ctx, bindingKey, tool, hookContext, extras) {
210
+ const controllerInstance = await ctx.get(bindingKey);
211
+ const methodArgs = this.prepareMethodArguments(tool, hookContext.args);
212
+ let result = await tool.controllerFunction.call(controllerInstance, ...methodArgs, extras);
213
+ // Automatically wrap result in MCP response format if not already wrapped
214
+ if (result && typeof result === 'object' && !result.content) {
215
+ result = {
216
+ content: [
217
+ {
218
+ type: 'text',
219
+ text: JSON.stringify(result, null, 2),
220
+ },
221
+ ],
222
+ };
223
+ }
224
+ return result;
225
+ }
226
+ errorToCallToolResult(error) {
227
+ return {
228
+ content: [
229
+ {
230
+ type: 'text',
231
+ text: error.message || 'MCP tool execution failed',
232
+ },
233
+ ],
234
+ };
235
+ }
236
+ };
237
+ exports.McpToolRegistry = McpToolRegistry;
238
+ exports.McpToolRegistry = McpToolRegistry = tslib_1.__decorate([
239
+ (0, core_1.bind)({ scope: core_1.BindingScope.SINGLETON }),
240
+ tslib_1.__param(0, (0, core_1.inject)(core_1.CoreBindings.APPLICATION_INSTANCE)),
241
+ tslib_1.__param(1, (0, core_1.inject)(core_2.LOGGER.LOGGER_INJECT)),
242
+ tslib_1.__param(2, (0, core_1.service)(mcp_schema_generator_service_service_1.McpSchemaGeneratorService)),
243
+ tslib_1.__metadata("design:paramtypes", [core_1.Application, Object, mcp_schema_generator_service_service_1.McpSchemaGeneratorService])
244
+ ], McpToolRegistry);
245
+ //# sourceMappingURL=mcp-tool-registry.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-tool-registry.service.js","sourceRoot":"","sources":["../../src/services/mcp-tool-registry.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import { BindingKey, Context } from '@loopback/core';
2
+ import { RequestHandlerExtra } from '@modelcontextprotocol/sdk/shared/protocol';
3
+ import { CallToolResult, ServerNotification, ServerRequest } from '@modelcontextprotocol/sdk/types';
4
+ import * as z from 'zod';
5
+ import { McpHookFunction } from './interfaces';
6
+ declare const objectSchema: z.ZodObject<{
7
+ content: z.ZodArray<z.ZodObject<{
8
+ type: z.ZodLiteral<"text">;
9
+ text: z.ZodString;
10
+ }, z.core.$strip>>;
11
+ isError: z.ZodOptional<z.ZodBoolean>;
12
+ }, z.core.$strip>;
13
+ export type McpToolHandler = (ctx: Context, args: {
14
+ [key: string]: unknown;
15
+ }, extras: RequestHandlerExtra<ServerRequest, ServerNotification>) => CallToolResult | Promise<CallToolResult>;
16
+ export interface McpTool {
17
+ name: string;
18
+ description: string;
19
+ schema: z.ZodRawShape;
20
+ handler: McpToolHandler;
21
+ }
22
+ export interface McpHookConfig {
23
+ binding: BindingKey<McpHookFunction> | string;
24
+ config?: {
25
+ [key: string]: unknown;
26
+ };
27
+ }
28
+ export interface McpToolMetadata {
29
+ name: string;
30
+ description: string;
31
+ schema: z.ZodRawShape;
32
+ controllerFunction: Function;
33
+ preHook?: McpHookConfig;
34
+ postHook?: McpHookConfig;
35
+ parameterNames?: string[];
36
+ controllerBinding?: BindingKey<object>;
37
+ }
38
+ export interface McpToolDecoratorOptions {
39
+ name: string;
40
+ description: string;
41
+ schema?: z.ZodRawShape;
42
+ preHook?: McpHookConfig;
43
+ postHook?: McpHookConfig;
44
+ }
45
+ export declare function isTextMessage(message: unknown): message is z.infer<typeof objectSchema>;
46
+ export {};
package/dist/types.js ADDED
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isTextMessage = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const z = tslib_1.__importStar(require("zod"));
6
+ const objectSchema = z.object({
7
+ content: z.array(z.object({
8
+ type: z.literal('text'),
9
+ text: z.string(),
10
+ })),
11
+ isError: z.boolean().optional(),
12
+ });
13
+ function isTextMessage(message) {
14
+ return objectSchema.safeParse(message).success;
15
+ }
16
+ exports.isTextMessage = isTextMessage;
17
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";;;;AAOA,+CAAyB;AAEzB,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5B,OAAO,EAAE,CAAC,CAAC,KAAK,CACd,CAAC,CAAC,MAAM,CAAC;QACP,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC;QACvB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;KACjB,CAAC,CACH;IACD,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;CAChC,CAAC,CAAC;AAqCH,SAAgB,aAAa,CAC3B,OAAgB;IAEhB,OAAO,YAAY,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC;AACjD,CAAC;AAJD,sCAIC"}
@@ -0,0 +1 @@
1
+ export * from './mcp-parameter-extractor';
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./mcp-parameter-extractor"), exports);
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":";;;AAAA,oEAA0C"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Extract parameter information using LoopBack's parameter metadata system
3
+ * Developers creating MCP tools must use @param decorators
4
+ */
5
+ export declare function extractParameterInfo(target: Object, propertyKey: string): {
6
+ parameterNames: string[];
7
+ parameterOptional: boolean[];
8
+ parameterTypes: ("object" | "number" | "string" | "boolean" | "array" | "any")[];
9
+ };
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.extractParameterInfo = void 0;
4
+ const core_1 = require("@loopback/core");
5
+ const keys_1 = require("@loopback/openapi-v3/dist/keys");
6
+ /**
7
+ * Extract parameter information using LoopBack's parameter metadata system
8
+ * Developers creating MCP tools must use @param decorators
9
+ */
10
+ function extractParameterInfo(target, propertyKey) {
11
+ const paramMetadata = core_1.MetadataInspector.getMethodMetadata(keys_1.OAI3Keys.PARAMETERS_KEY, // LoopBack parameter metadata key
12
+ target, propertyKey);
13
+ if (!paramMetadata ||
14
+ !Array.isArray(paramMetadata) ||
15
+ paramMetadata.length === 0) {
16
+ throw new Error(`No LoopBack parameter metadata found for ${propertyKey}. ` +
17
+ `MCP tools must use @param decorators (e.g., @param.query.string('paramName'))`);
18
+ }
19
+ const parameterNames = paramMetadata.map((param, index) => {
20
+ if (!param.name) {
21
+ throw new Error(`Parameter ${index} in ${propertyKey} is missing name. Use @param.query.string('name')`);
22
+ }
23
+ return param.name;
24
+ });
25
+ const parameterOptional = paramMetadata.map((param) => param.required === false);
26
+ const parameterTypes = paramMetadata.map((param) => {
27
+ // Extract type information from LoopBack parameter schema
28
+ const schema = param.schema;
29
+ if (schema === null || schema === void 0 ? void 0 : schema.type) {
30
+ switch (schema.type) {
31
+ case 'string':
32
+ return 'string';
33
+ case 'number':
34
+ case 'integer':
35
+ return 'number';
36
+ case 'boolean':
37
+ return 'boolean';
38
+ case 'object':
39
+ return 'object';
40
+ case 'array':
41
+ return 'array';
42
+ default:
43
+ return 'any';
44
+ }
45
+ }
46
+ return 'any';
47
+ });
48
+ return { parameterNames, parameterOptional, parameterTypes };
49
+ }
50
+ exports.extractParameterInfo = extractParameterInfo;
51
+ //# sourceMappingURL=mcp-parameter-extractor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-parameter-extractor.js","sourceRoot":"","sources":["../../src/utils/mcp-parameter-extractor.ts"],"names":[],"mappings":";;;AAAA,yCAAiD;AAEjD,yDAAwD;AAGxD;;;GAGG;AACH,SAAgB,oBAAoB,CAAC,MAAc,EAAE,WAAmB;IACtE,MAAM,aAAa,GAAG,wBAAiB,CAAC,iBAAiB,CACvD,eAAQ,CAAC,cAAc,EAAE,kCAAkC;IAC3D,MAAM,EACN,WAAW,CACZ,CAAC;IAEF,IACE,CAAC,aAAa;QACd,CAAC,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC;QAC7B,aAAa,CAAC,MAAM,KAAK,CAAC,EAC1B;QACA,MAAM,IAAI,KAAK,CACb,4CAA4C,WAAW,IAAI;YACzD,+EAA+E,CAClF,CAAC;KACH;IAED,MAAM,cAAc,GAAG,aAAa,CAAC,GAAG,CACtC,CAAC,KAAsB,EAAE,KAAa,EAAE,EAAE;QACxC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE;YACf,MAAM,IAAI,KAAK,CACb,aAAa,KAAK,OAAO,WAAW,mDAAmD,CACxF,CAAC;SACH;QACD,OAAO,KAAK,CAAC,IAAI,CAAC;IACpB,CAAC,CACF,CAAC;IAEF,MAAM,iBAAiB,GAAG,aAAa,CAAC,GAAG,CACzC,CAAC,KAAsB,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,KAAK,KAAK,CACrD,CAAC;IAEF,MAAM,cAAc,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,KAAsB,EAAE,EAAE;QAClE,0DAA0D;QAC1D,MAAM,MAAM,GAAG,KAAK,CAAC,MAAsB,CAAC;QAC5C,IAAI,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,IAAI,EAAE;YAChB,QAAQ,MAAM,CAAC,IAAI,EAAE;gBACnB,KAAK,QAAQ;oBACX,OAAO,QAAQ,CAAC;gBAClB,KAAK,QAAQ,CAAC;gBACd,KAAK,SAAS;oBACZ,OAAO,QAAQ,CAAC;gBAClB,KAAK,SAAS;oBACZ,OAAO,SAAS,CAAC;gBACnB,KAAK,QAAQ;oBACX,OAAO,QAAQ,CAAC;gBAClB,KAAK,OAAO;oBACV,OAAO,OAAO,CAAC;gBACjB;oBACE,OAAO,KAAK,CAAC;aAChB;SACF;QACD,OAAO,KAAK,CAAC;IACf,CAAC,CAAC,CAAC;IAEH,OAAO,EAAC,cAAc,EAAE,iBAAiB,EAAE,cAAc,EAAC,CAAC;AAC7D,CAAC;AAzDD,oDAyDC"}