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
package/README.md ADDED
@@ -0,0 +1,87 @@
1
+ <a href="https://sourcefuse.github.io/arc-docs/arc-api-docs" target="_blank"><img src="https://github.com/sourcefuse/loopback4-microservice-catalog/blob/master/docs/assets/logo-dark-bg.png?raw=true" alt="ARC By SourceFuse logo" title="ARC By SourceFuse" align="right" width="150" /></a>
2
+ # [loopback4-mcp](https://github.com/sourcefuse/loopback4-mcp)
3
+ <p align="left">
4
+ </a>
5
+ <a href="https://loopback.io/" target="_blank">
6
+ <img alt="Powered By LoopBack 4" src="https://img.shields.io/badge/Powered%20by-LoopBack 4-brightgreen" />
7
+ </a>
8
+ </p>
9
+
10
+ ## Overview
11
+
12
+ This extension provides a plug-and-play integration between LoopBack4 applications and the Model Context Protocol (MCP) specification.
13
+
14
+ Its purpose is to enable LoopBack APIs, services, and business logic to be exposed as MCP Tools, allowing external MCP clients (such as LLMs, agents, or MCP-compatible apps) to discover and execute server-defined operations.
15
+ ### Key Features
16
+ - Automatic MCP Tool Discovery :-
17
+ The extension scans your application at boot time and automatically registers all methods decorated with the custom @mcpTool() decorator.
18
+
19
+ This allows you to define MCP tools anywhere in your LoopBack project without manually wiring metadata.
20
+
21
+ - Lifecycle-managed Tool Registry :-
22
+ A dedicated `McpToolRegistry` service maintains all discovered tool metadata,their handlers and execution context.
23
+
24
+ A `McpToolRegistryBootObserver` ensures that registration happens only after the application has fully booted.
25
+ ## Installation
26
+ ```sh
27
+ npm install loopback4-mcp
28
+ ```
29
+ Then register the component inside your `application.ts`.
30
+ ```ts
31
+ this.component(McpComponent);
32
+ ```
33
+ ## Usage
34
+ Add the `@mcpTool()` decorator to any controller in your application.
35
+ ```ts
36
+ @mcpTool({
37
+ name: 'create-user',
38
+ description: 'Creates a new user in the system',
39
+ schema?: {
40
+ email: z.string().email(),
41
+ name: z.string(),
42
+ },
43
+ })
44
+ async createUser(args: {email: string; name: string}) {
45
+ return {message: `User ${args.name} created`};
46
+ }
47
+ ```
48
+
49
+ This decorator accepts a total of five fields, out of which `name` and `description` are mandatory and `schema`,`preHook` and `postHook` are optional enhancements.
50
+
51
+ The schema field allows defining a Zod-based validation schema for tool input parameters, while preHook and postHook enable execution of custom logic before and after the tool handler runs.
52
+
53
+ ### Mcp Hook Usage Details
54
+ To use hooks with MCP tools, follow the provider-based approach:
55
+
56
+ Step 1: Create a hook provider:
57
+ ```ts
58
+ // src/providers/my-hook.provider.ts
59
+ export class MyHookProvider implements Provider<McpHookFunction> {
60
+ constructor(@inject(LOGGER.LOGGER_INJECT) private logger: ILogger) {}
61
+ value(): McpHookFunction {
62
+ return async (context: McpHookContext) => {
63
+ this.logger.info(`Hook executed for tool: ${context.toolName}`);
64
+ };
65
+ }
66
+ }
67
+ ```
68
+ Step 2: Add binding key to McpHookBindings:
69
+ ```ts
70
+ // src/keys.ts
71
+ export namespace McpHookBindings {
72
+ export const MY_HOOK = BindingKey.create<McpHookFunction>('hooks.mcp.myHook');
73
+ }
74
+ ```
75
+ Step 3: Bind provider in `application.ts`:
76
+ ```typescript
77
+ this.bind(McpHookBindings.MY_HOOK).toProvider(MyHookProvider);
78
+ ```
79
+ Step 4: Use in decorator:
80
+ ```ts
81
+ @mcpTool({
82
+ name: 'my-tool',
83
+ description: 'my-description'
84
+ preHookBinding: McpHookBindings.MY_HOOK,
85
+ postHookBinding: 'hooks.mcp.myOtherHook' // or string binding key
86
+ })
87
+ ```
@@ -0,0 +1,11 @@
1
+ import { Application, Component } from '@loopback/core';
2
+ import { McpSchemaGeneratorService, McpServerFactory, McpToolRegistry } from './services';
3
+ import { McpController } from './controllers';
4
+ import { McpToolRegistryBootObserver } from './observers';
5
+ export declare class McpComponent implements Component {
6
+ private application;
7
+ services: (typeof McpSchemaGeneratorService | typeof McpToolRegistry | typeof McpServerFactory)[];
8
+ controllers: (typeof McpController)[];
9
+ lifeCycleObservers: (typeof McpToolRegistryBootObserver)[];
10
+ constructor(application: Application);
11
+ }
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.McpComponent = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const core_1 = require("@loopback/core");
6
+ const services_1 = require("./services");
7
+ const controllers_1 = require("./controllers");
8
+ const observers_1 = require("./observers");
9
+ let McpComponent = class McpComponent {
10
+ constructor(application) {
11
+ this.application = application;
12
+ this.services = [services_1.McpSchemaGeneratorService, services_1.McpServerFactory, services_1.McpToolRegistry];
13
+ this.controllers = [controllers_1.McpController];
14
+ this.lifeCycleObservers = [observers_1.McpToolRegistryBootObserver];
15
+ }
16
+ };
17
+ exports.McpComponent = McpComponent;
18
+ exports.McpComponent = McpComponent = tslib_1.__decorate([
19
+ tslib_1.__param(0, (0, core_1.inject)(core_1.CoreBindings.APPLICATION_INSTANCE)),
20
+ tslib_1.__metadata("design:paramtypes", [core_1.Application])
21
+ ], McpComponent);
22
+ //# sourceMappingURL=component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"component.js","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":";;;;AAAA,yCAA4E;AAC5E,yCAIoB;AACpB,+CAA4C;AAC5C,2CAAwD;AAExD,IAAa,YAAY,GAAzB,MAAa,YAAY;IAIvB,YAEE,WAAgC;QAAxB,gBAAW,GAAX,WAAW,CAAa;QALlC,aAAQ,GAAG,CAAC,oCAAyB,EAAE,2BAAgB,EAAE,0BAAe,CAAC,CAAC;QAC1E,gBAAW,GAAG,CAAC,2BAAa,CAAC,CAAC;QAC9B,uBAAkB,GAAG,CAAC,uCAA2B,CAAC,CAAC;IAIhD,CAAC;CACL,CAAA;AARY,oCAAY;uBAAZ,YAAY;IAKpB,mBAAA,IAAA,aAAM,EAAC,mBAAY,CAAC,oBAAoB,CAAC,CAAA;6CACrB,kBAAW;GANvB,YAAY,CAQxB"}
@@ -0,0 +1 @@
1
+ export * from './mcp-tool.constant';
@@ -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-tool.constant"), exports);
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/constants/index.ts"],"names":[],"mappings":";;;AAAA,8DAAoC"}
@@ -0,0 +1 @@
1
+ export declare const MCP_TOOL_METADATA_KEY = "mcp:tools";
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MCP_TOOL_METADATA_KEY = void 0;
4
+ // Metadata key for MCP tools
5
+ exports.MCP_TOOL_METADATA_KEY = 'mcp:tools';
6
+ //# sourceMappingURL=mcp-tool.constant.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-tool.constant.js","sourceRoot":"","sources":["../../src/constants/mcp-tool.constant.ts"],"names":[],"mappings":";;;AAAA,6BAA6B;AAChB,QAAA,qBAAqB,GAAG,WAAW,CAAC"}
@@ -0,0 +1 @@
1
+ export * from './mcp.controller';
@@ -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.controller"), exports);
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/controllers/index.ts"],"names":[],"mappings":";;;AAAA,2DAAiC"}
@@ -0,0 +1,10 @@
1
+ /// <reference types="express" />
2
+ import { Request, Response } from '@loopback/rest';
3
+ import { ILogger } from '@sourceloop/core';
4
+ import { McpServerFactory } from '../services';
5
+ export declare class McpController {
6
+ private readonly logger;
7
+ private readonly serverFactory;
8
+ constructor(logger: ILogger, serverFactory: McpServerFactory);
9
+ handleMCPRequest(req: Request, res: Response): Promise<void>;
10
+ }
@@ -0,0 +1,121 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.McpController = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const core_1 = require("@loopback/core");
6
+ const rest_1 = require("@loopback/rest");
7
+ const streamableHttp_js_1 = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
8
+ const core_2 = require("@sourceloop/core");
9
+ const loopback4_authentication_1 = require("loopback4-authentication");
10
+ const loopback4_authorization_1 = require("loopback4-authorization");
11
+ const services_1 = require("../services");
12
+ let McpController = class McpController {
13
+ constructor(logger, serverFactory) {
14
+ this.logger = logger;
15
+ this.serverFactory = serverFactory;
16
+ }
17
+ async handleMCPRequest(req, res) {
18
+ try {
19
+ // Server creation using factory service
20
+ const server = this.serverFactory.createServer();
21
+ const transport = new streamableHttp_js_1.StreamableHTTPServerTransport({
22
+ sessionIdGenerator: undefined,
23
+ });
24
+ // Set up cleanup handlers
25
+ /* eslint-disable @typescript-eslint/no-misused-promises */
26
+ res.on('close', async () => {
27
+ await transport.close();
28
+ await server.close();
29
+ this.logger.info('Session closed.');
30
+ });
31
+ res.on('error', async () => {
32
+ await transport.close();
33
+ await server.close();
34
+ this.logger.info('Closing Session as it errorred out.');
35
+ });
36
+ await server.connect(transport);
37
+ await transport.handleRequest(req, res, req.body);
38
+ }
39
+ catch (err) {
40
+ this.logger.error('Failed to establish MCP connection:', err);
41
+ if (!res.headersSent) {
42
+ res.status(500 /* STATUS_CODE.INTERNAL_SERVER_ERROR */).json({
43
+ jsonrpc: '2.0',
44
+ error: {
45
+ code: -32603,
46
+ message: 'Internal server error',
47
+ },
48
+ id: null,
49
+ });
50
+ }
51
+ }
52
+ }
53
+ };
54
+ exports.McpController = McpController;
55
+ tslib_1.__decorate([
56
+ (0, loopback4_authentication_1.authenticate)("bearer" /* STRATEGY.BEARER */, {
57
+ passReqToCallback: true,
58
+ }),
59
+ (0, loopback4_authorization_1.authorize)({
60
+ permissions: ['*'],
61
+ }),
62
+ (0, rest_1.post)('/mcp', {
63
+ summary: 'MCP HTTP Message',
64
+ description: 'Handle MCP message via StreamableHTTP transport',
65
+ requestBody: {
66
+ content: {
67
+ [core_2.CONTENT_TYPE.JSON]: {
68
+ schema: {
69
+ type: 'object',
70
+ description: 'MCP message payload',
71
+ },
72
+ },
73
+ },
74
+ },
75
+ responses: {
76
+ [200 /* STATUS_CODE.OK */]: {
77
+ description: 'MCP message processed successfully',
78
+ content: {
79
+ [core_2.CONTENT_TYPE.JSON]: {
80
+ schema: {
81
+ type: 'object',
82
+ description: 'MCP response message',
83
+ },
84
+ },
85
+ },
86
+ },
87
+ [500 /* STATUS_CODE.INTERNAL_SERVER_ERROR */]: {
88
+ description: 'Internal server error',
89
+ content: {
90
+ [core_2.CONTENT_TYPE.JSON]: {
91
+ schema: {
92
+ type: 'object',
93
+ properties: {
94
+ jsonrpc: { type: 'string' },
95
+ error: {
96
+ type: 'object',
97
+ properties: {
98
+ code: { type: 'number' },
99
+ message: { type: 'string' },
100
+ },
101
+ },
102
+ id: { type: 'null' },
103
+ },
104
+ },
105
+ },
106
+ },
107
+ },
108
+ },
109
+ }),
110
+ tslib_1.__param(0, (0, core_1.inject)(rest_1.RestBindings.Http.REQUEST)),
111
+ tslib_1.__param(1, (0, core_1.inject)(rest_1.RestBindings.Http.RESPONSE)),
112
+ tslib_1.__metadata("design:type", Function),
113
+ tslib_1.__metadata("design:paramtypes", [Object, Object]),
114
+ tslib_1.__metadata("design:returntype", Promise)
115
+ ], McpController.prototype, "handleMCPRequest", null);
116
+ exports.McpController = McpController = tslib_1.__decorate([
117
+ tslib_1.__param(0, (0, core_1.inject)(core_2.LOGGER.LOGGER_INJECT)),
118
+ tslib_1.__param(1, (0, core_1.service)(services_1.McpServerFactory)),
119
+ tslib_1.__metadata("design:paramtypes", [Object, services_1.McpServerFactory])
120
+ ], McpController);
121
+ //# sourceMappingURL=mcp.controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.controller.js","sourceRoot":"","sources":["../../src/controllers/mcp.controller.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export * from './mcp-tool.decorator';
@@ -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-tool.decorator"), exports);
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/decorators/index.ts"],"names":[],"mappings":";;;AAAA,+DAAqC"}
@@ -0,0 +1,2 @@
1
+ import { McpToolDecoratorOptions } from '../types';
2
+ export declare function mcpTool(options: McpToolDecoratorOptions): (target: Object, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mcpTool = void 0;
4
+ const core_1 = require("@loopback/core");
5
+ const constants_1 = require("../constants");
6
+ /**
7
+ * Process hook config to normalize binding keys
8
+ */
9
+ function processHookConfig(hookConfig) {
10
+ if (!hookConfig)
11
+ return undefined;
12
+ const binding = typeof hookConfig.binding === 'string'
13
+ ? core_1.BindingKey.create(hookConfig.binding)
14
+ : hookConfig.binding;
15
+ return {
16
+ binding,
17
+ config: hookConfig.config,
18
+ };
19
+ }
20
+ function mcpTool(options) {
21
+ return function (target, propertyKey, descriptor) {
22
+ var _a, _b;
23
+ // Create basic schema - parameter extraction happens in the service
24
+ const basicSchema = (_a = options.schema) !== null && _a !== void 0 ? _a : {};
25
+ const metadata = {
26
+ name: options.name,
27
+ description: options.description,
28
+ schema: basicSchema,
29
+ controllerFunction: descriptor.value,
30
+ preHook: processHookConfig(options.preHook),
31
+ postHook: processHookConfig(options.postHook),
32
+ parameterNames: [], // Will be populated by service
33
+ };
34
+ // Store metadata using LoopBack's metadata system
35
+ // Get existing metadata for the class
36
+ const existingMethodsMetadata = (_b = core_1.MetadataInspector.getAllMethodMetadata(constants_1.MCP_TOOL_METADATA_KEY, target)) !== null && _b !== void 0 ? _b : {};
37
+ // Add this method's metadata
38
+ existingMethodsMetadata[propertyKey] = metadata;
39
+ // Store the complete metadata map back
40
+ core_1.MetadataInspector.defineMetadata(constants_1.MCP_TOOL_METADATA_KEY, existingMethodsMetadata, target);
41
+ return descriptor;
42
+ };
43
+ }
44
+ exports.mcpTool = mcpTool;
45
+ //# sourceMappingURL=mcp-tool.decorator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-tool.decorator.js","sourceRoot":"","sources":["../../src/decorators/mcp-tool.decorator.ts"],"names":[],"mappings":";;;AAAA,yCAA6D;AAC7D,4CAAmD;AAQnD;;GAEG;AACH,SAAS,iBAAiB,CACxB,UAA0B;IAE1B,IAAI,CAAC,UAAU;QAAE,OAAO,SAAS,CAAC;IAElC,MAAM,OAAO,GACX,OAAO,UAAU,CAAC,OAAO,KAAK,QAAQ;QACpC,CAAC,CAAC,iBAAU,CAAC,MAAM,CAAkB,UAAU,CAAC,OAAO,CAAC;QACxD,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC;IAEzB,OAAO;QACL,OAAO;QACP,MAAM,EAAE,UAAU,CAAC,MAAM;KAC1B,CAAC;AACJ,CAAC;AAED,SAAgB,OAAO,CAAC,OAAgC;IACtD,OAAO,UACL,MAAc,EACd,WAAmB,EACnB,UAA8B;;QAE9B,oEAAoE;QACpE,MAAM,WAAW,GAAG,MAAA,OAAO,CAAC,MAAM,mCAAI,EAAE,CAAC;QAEzC,MAAM,QAAQ,GAAoB;YAChC,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,MAAM,EAAE,WAAW;YACnB,kBAAkB,EAAE,UAAU,CAAC,KAAK;YACpC,OAAO,EAAE,iBAAiB,CAAC,OAAO,CAAC,OAAO,CAAC;YAC3C,QAAQ,EAAE,iBAAiB,CAAC,OAAO,CAAC,QAAQ,CAAC;YAC7C,cAAc,EAAE,EAAE,EAAE,+BAA+B;SACpD,CAAC;QAEF,kDAAkD;QAClD,sCAAsC;QACtC,MAAM,uBAAuB,GAC3B,MAAA,wBAAiB,CAAC,oBAAoB,CACpC,iCAAqB,EACrB,MAAM,CACP,mCAAI,EAAE,CAAC;QAEV,6BAA6B;QAC7B,uBAAuB,CAAC,WAAW,CAAC,GAAG,QAAQ,CAAC;QAEhD,uCAAuC;QACvC,wBAAiB,CAAC,cAAc,CAC9B,iCAAqB,EACrB,uBAAuB,EACvB,MAAM,CACP,CAAC;QAEF,OAAO,UAAU,CAAC;IACpB,CAAC,CAAC;AACJ,CAAC;AAvCD,0BAuCC"}
@@ -0,0 +1,5 @@
1
+ export * from './component';
2
+ export * from './types';
3
+ export * from './decorators';
4
+ export * from './observers';
5
+ export * from './keys';
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./component"), exports);
5
+ tslib_1.__exportStar(require("./types"), exports);
6
+ tslib_1.__exportStar(require("./decorators"), exports);
7
+ tslib_1.__exportStar(require("./observers"), exports);
8
+ tslib_1.__exportStar(require("./keys"), exports);
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,sDAA4B;AAC5B,kDAAwB;AACxB,uDAA6B;AAC7B,sDAA4B;AAC5B,iDAAuB"}
@@ -0,0 +1 @@
1
+ export * from './mcp-hook-provider.interface';
@@ -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-hook-provider.interface"), exports);
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/interfaces/index.ts"],"names":[],"mappings":";;;AAAA,wEAA8C"}
@@ -0,0 +1,12 @@
1
+ export interface McpHookContext {
2
+ toolName: string;
3
+ args: {
4
+ [key: string]: unknown;
5
+ };
6
+ result?: unknown;
7
+ error?: Error;
8
+ metadata?: {
9
+ [key: string]: unknown;
10
+ };
11
+ }
12
+ export type McpHookFunction = (context: McpHookContext) => Promise<McpHookContext | void> | McpHookContext | void;
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=mcp-hook-provider.interface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-hook-provider.interface.js","sourceRoot":"","sources":["../../src/interfaces/mcp-hook-provider.interface.ts"],"names":[],"mappings":""}
package/dist/keys.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare namespace McpBindings { }
package/dist/keys.js ADDED
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=keys.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keys.js","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":""}
@@ -0,0 +1 @@
1
+ export * from './mcp-tool-registry-boot.observer';
@@ -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-tool-registry-boot.observer"), exports);
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/observers/index.ts"],"names":[],"mappings":";;;AAAA,4EAAkD"}
@@ -0,0 +1,19 @@
1
+ import { LifeCycleObserver } from '@loopback/core';
2
+ import { ILogger } from '@sourceloop/core';
3
+ import { McpToolRegistry } from '../services';
4
+ /**
5
+ * Lifecycle observer to initialize MCP tool registry after application boot
6
+ */
7
+ export declare class McpToolRegistryBootObserver implements LifeCycleObserver {
8
+ private readonly mcpToolRegistry;
9
+ private readonly logger;
10
+ constructor(mcpToolRegistry: McpToolRegistry, logger: ILogger);
11
+ /**
12
+ * Initialize MCP tool registry after application starts
13
+ */
14
+ start(): Promise<void>;
15
+ /**
16
+ * Called when application stops
17
+ */
18
+ stop(): Promise<void>;
19
+ }
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.McpToolRegistryBootObserver = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const core_1 = require("@loopback/core");
6
+ const core_2 = require("@sourceloop/core");
7
+ const services_1 = require("../services");
8
+ /**
9
+ * Lifecycle observer to initialize MCP tool registry after application boot
10
+ */
11
+ let McpToolRegistryBootObserver = class McpToolRegistryBootObserver {
12
+ constructor(mcpToolRegistry, logger) {
13
+ this.mcpToolRegistry = mcpToolRegistry;
14
+ this.logger = logger;
15
+ }
16
+ /**
17
+ * Initialize MCP tool registry after application starts
18
+ */
19
+ async start() {
20
+ try {
21
+ await this.mcpToolRegistry.initialize();
22
+ }
23
+ catch (error) {
24
+ this.logger.error('Failed to initialize MCP tool registry:', error);
25
+ throw error;
26
+ }
27
+ }
28
+ /**
29
+ * Called when application stops
30
+ */
31
+ async stop() {
32
+ this.logger.info('MCP tool registry stopping...');
33
+ }
34
+ };
35
+ exports.McpToolRegistryBootObserver = McpToolRegistryBootObserver;
36
+ exports.McpToolRegistryBootObserver = McpToolRegistryBootObserver = tslib_1.__decorate([
37
+ (0, core_1.lifeCycleObserver)('mcpToolRegistryInit'),
38
+ tslib_1.__param(0, (0, core_1.service)(services_1.McpToolRegistry)),
39
+ tslib_1.__param(1, (0, core_1.inject)(core_2.LOGGER.LOGGER_INJECT)),
40
+ tslib_1.__metadata("design:paramtypes", [services_1.McpToolRegistry, Object])
41
+ ], McpToolRegistryBootObserver);
42
+ //# sourceMappingURL=mcp-tool-registry-boot.observer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-tool-registry-boot.observer.js","sourceRoot":"","sources":["../../src/observers/mcp-tool-registry-boot.observer.ts"],"names":[],"mappings":";;;;AAAA,yCAKwB;AACxB,2CAAiD;AACjD,0CAA4C;AAE5C;;GAEG;AAEI,IAAM,2BAA2B,GAAjC,MAAM,2BAA2B;IACtC,YAEmB,eAAgC,EAEhC,MAAe;QAFf,oBAAe,GAAf,eAAe,CAAiB;QAEhC,WAAM,GAAN,MAAM,CAAS;IAC/B,CAAC;IAEJ;;OAEG;IACH,KAAK,CAAC,KAAK;QACT,IAAI;YACF,MAAM,IAAI,CAAC,eAAe,CAAC,UAAU,EAAE,CAAC;SACzC;QAAC,OAAO,KAAK,EAAE;YACd,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,yCAAyC,EAAE,KAAK,CAAC,CAAC;YACpE,MAAM,KAAK,CAAC;SACb;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,IAAI;QACR,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;IACpD,CAAC;CACF,CAAA;AA1BY,kEAA2B;sCAA3B,2BAA2B;IADvC,IAAA,wBAAiB,EAAC,qBAAqB,CAAC;IAGpC,mBAAA,IAAA,cAAO,EAAC,0BAAe,CAAC,CAAA;IAExB,mBAAA,IAAA,aAAM,EAAC,aAAM,CAAC,aAAa,CAAC,CAAA;6CADK,0BAAe;GAHxC,2BAA2B,CA0BvC"}
@@ -0,0 +1,3 @@
1
+ export * from './mcp-server-factory.service';
2
+ export * from './mcp-tool-registry.service';
3
+ export * from './mcp-schema-generator-service.service';
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./mcp-server-factory.service"), exports);
5
+ tslib_1.__exportStar(require("./mcp-tool-registry.service"), exports);
6
+ tslib_1.__exportStar(require("./mcp-schema-generator-service.service"), exports);
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/services/index.ts"],"names":[],"mappings":";;;AAAA,uEAA6C;AAC7C,sEAA4C;AAC5C,iFAAuD"}
@@ -0,0 +1,20 @@
1
+ import { ILogger } from '@sourceloop/core';
2
+ import { z, ZodRawShape } from 'zod';
3
+ export declare class McpSchemaGeneratorService {
4
+ private readonly logger;
5
+ constructor(logger: ILogger);
6
+ /**
7
+ * Create Zod schema for a single parameter using LoopBack type string
8
+ */
9
+ createZodSchemaForParameterType(paramType: string): z.ZodString | z.ZodNumber | z.ZodBoolean | z.ZodObject<{}, z.core.$loose> | z.ZodAny | z.ZodArray<z.ZodAny>;
10
+ /**
11
+ * Generate tool schema using LoopBack metadata
12
+ */
13
+ generateToolSchemaFromLoopBack(options: {
14
+ name: string;
15
+ description: string;
16
+ schema?: ZodRawShape;
17
+ }, target: Object, methodName: string): Readonly<{
18
+ [k: string]: z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
19
+ }>;
20
+ }