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.
- package/README.md +87 -0
- package/dist/component.d.ts +11 -0
- package/dist/component.js +22 -0
- package/dist/component.js.map +1 -0
- package/dist/constants/index.d.ts +1 -0
- package/dist/constants/index.js +5 -0
- package/dist/constants/index.js.map +1 -0
- package/dist/constants/mcp-tool.constant.d.ts +1 -0
- package/dist/constants/mcp-tool.constant.js +6 -0
- package/dist/constants/mcp-tool.constant.js.map +1 -0
- package/dist/controllers/index.d.ts +1 -0
- package/dist/controllers/index.js +5 -0
- package/dist/controllers/index.js.map +1 -0
- package/dist/controllers/mcp.controller.d.ts +10 -0
- package/dist/controllers/mcp.controller.js +121 -0
- package/dist/controllers/mcp.controller.js.map +1 -0
- package/dist/decorators/index.d.ts +1 -0
- package/dist/decorators/index.js +5 -0
- package/dist/decorators/index.js.map +1 -0
- package/dist/decorators/mcp-tool.decorator.d.ts +2 -0
- package/dist/decorators/mcp-tool.decorator.js +45 -0
- package/dist/decorators/mcp-tool.decorator.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/interfaces/index.d.ts +1 -0
- package/dist/interfaces/index.js +5 -0
- package/dist/interfaces/index.js.map +1 -0
- package/dist/interfaces/mcp-hook-provider.interface.d.ts +12 -0
- package/dist/interfaces/mcp-hook-provider.interface.js +3 -0
- package/dist/interfaces/mcp-hook-provider.interface.js.map +1 -0
- package/dist/keys.d.ts +1 -0
- package/dist/keys.js +3 -0
- package/dist/keys.js.map +1 -0
- package/dist/observers/index.d.ts +1 -0
- package/dist/observers/index.js +5 -0
- package/dist/observers/index.js.map +1 -0
- package/dist/observers/mcp-tool-registry-boot.observer.d.ts +19 -0
- package/dist/observers/mcp-tool-registry-boot.observer.js +42 -0
- package/dist/observers/mcp-tool-registry-boot.observer.js.map +1 -0
- package/dist/services/index.d.ts +3 -0
- package/dist/services/index.js +7 -0
- package/dist/services/index.js.map +1 -0
- package/dist/services/mcp-schema-generator-service.service.d.ts +20 -0
- package/dist/services/mcp-schema-generator-service.service.js +73 -0
- package/dist/services/mcp-schema-generator-service.service.js.map +1 -0
- package/dist/services/mcp-server-factory.service.d.ts +13 -0
- package/dist/services/mcp-server-factory.service.js +51 -0
- package/dist/services/mcp-server-factory.service.js.map +1 -0
- package/dist/services/mcp-tool-registry.service.d.ts +49 -0
- package/dist/services/mcp-tool-registry.service.js +245 -0
- package/dist/services/mcp-tool-registry.service.js.map +1 -0
- package/dist/types.d.ts +46 -0
- package/dist/types.js +17 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +5 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/mcp-parameter-extractor.d.ts +9 -0
- package/dist/utils/mcp-parameter-extractor.js +51 -0
- package/dist/utils/mcp-parameter-extractor.js.map +1 -0
- package/package.json +139 -0
- package/src/component.ts +18 -0
- package/src/constants/index.ts +1 -0
- package/src/constants/mcp-tool.constant.ts +2 -0
- package/src/controllers/README.md +6 -0
- package/src/controllers/index.ts +1 -0
- package/src/controllers/mcp.controller.ts +112 -0
- package/src/decorators/README.md +34 -0
- package/src/decorators/index.ts +1 -0
- package/src/decorators/mcp-tool.decorator.ts +68 -0
- package/src/index.ts +5 -0
- package/src/interfaces/index.ts +1 -0
- package/src/interfaces/mcp-hook-provider.interface.ts +11 -0
- package/src/keys.ts +1 -0
- package/src/mixins/README.md +216 -0
- package/src/observers/index.ts +1 -0
- package/src/observers/mcp-tool-registry-boot.observer.ts +40 -0
- package/src/providers/README.md +129 -0
- package/src/repositories/README.md +5 -0
- package/src/services/index.ts +3 -0
- package/src/services/mcp-schema-generator-service.service.ts +80 -0
- package/src/services/mcp-server-factory.service.ts +71 -0
- package/src/services/mcp-tool-registry.service.ts +368 -0
- package/src/types.ts +59 -0
- package/src/utils/index.ts +1 -0
- 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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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":";;;;AAAA,yCAA+C;AAC/C,yCAAqE;AACrE,0FAAiG;AACjG,2CAA4E;AAC5E,uEAAgE;AAChE,qEAAkD;AAClD,0CAA6C;AAE7C,IAAa,aAAa,GAA1B,MAAa,aAAa;IACxB,YAEmB,MAAe,EAEf,aAA+B;QAF/B,WAAM,GAAN,MAAM,CAAS;QAEf,kBAAa,GAAb,aAAa,CAAkB;IAC/C,CAAC;IAwDE,AAAN,KAAK,CAAC,gBAAgB,CACe,GAAY,EACX,GAAa;QAEjD,IAAI;YACF,wCAAwC;YACxC,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,YAAY,EAAE,CAAC;YACjD,MAAM,SAAS,GAAG,IAAI,iDAA6B,CAAC;gBAClD,kBAAkB,EAAE,SAAS;aAC9B,CAAC,CAAC;YAEH,0BAA0B;YAC1B,2DAA2D;YAC3D,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE;gBACzB,MAAM,SAAS,CAAC,KAAK,EAAE,CAAC;gBACxB,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;gBACrB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;YACtC,CAAC,CAAC,CAAC;YAEH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE;gBACzB,MAAM,SAAS,CAAC,KAAK,EAAE,CAAC;gBACxB,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;gBACrB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAC;YAC1D,CAAC,CAAC,CAAC;YAEH,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YAChC,MAAM,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;SACnD;QAAC,OAAO,GAAG,EAAE;YACZ,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,qCAAqC,EAAE,GAAG,CAAC,CAAC;YAC9D,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE;gBACpB,GAAG,CAAC,MAAM,6CAAmC,CAAC,IAAI,CAAC;oBACjD,OAAO,EAAE,KAAK;oBACd,KAAK,EAAE;wBACL,IAAI,EAAE,CAAC,KAAK;wBACZ,OAAO,EAAE,uBAAuB;qBACjC;oBACD,EAAE,EAAE,IAAI;iBACT,CAAC,CAAC;aACJ;SACF;IACH,CAAC;CACF,CAAA;AAvGY,sCAAa;AA8DlB;IAtDL,IAAA,uCAAY,kCAAkB;QAC7B,iBAAiB,EAAE,IAAI;KACxB,CAAC;IACD,IAAA,mCAAS,EAAC;QACT,WAAW,EAAE,CAAC,GAAG,CAAC;KACnB,CAAC;IACD,IAAA,WAAI,EAAC,MAAM,EAAE;QACZ,OAAO,EAAE,kBAAkB;QAC3B,WAAW,EAAE,iDAAiD;QAC9D,WAAW,EAAE;YACX,OAAO,EAAE;gBACP,CAAC,mBAAY,CAAC,IAAI,CAAC,EAAE;oBACnB,MAAM,EAAE;wBACN,IAAI,EAAE,QAAQ;wBACd,WAAW,EAAE,qBAAqB;qBACnC;iBACF;aACF;SACF;QACD,SAAS,EAAE;YACT,0BAAgB,EAAE;gBAChB,WAAW,EAAE,oCAAoC;gBACjD,OAAO,EAAE;oBACP,CAAC,mBAAY,CAAC,IAAI,CAAC,EAAE;wBACnB,MAAM,EAAE;4BACN,IAAI,EAAE,QAAQ;4BACd,WAAW,EAAE,sBAAsB;yBACpC;qBACF;iBACF;aACF;YACD,6CAAmC,EAAE;gBACnC,WAAW,EAAE,uBAAuB;gBACpC,OAAO,EAAE;oBACP,CAAC,mBAAY,CAAC,IAAI,CAAC,EAAE;wBACnB,MAAM,EAAE;4BACN,IAAI,EAAE,QAAQ;4BACd,UAAU,EAAE;gCACV,OAAO,EAAE,EAAC,IAAI,EAAE,QAAQ,EAAC;gCACzB,KAAK,EAAE;oCACL,IAAI,EAAE,QAAQ;oCACd,UAAU,EAAE;wCACV,IAAI,EAAE,EAAC,IAAI,EAAE,QAAQ,EAAC;wCACtB,OAAO,EAAE,EAAC,IAAI,EAAE,QAAQ,EAAC;qCAC1B;iCACF;gCACD,EAAE,EAAE,EAAC,IAAI,EAAE,MAAM,EAAC;6BACnB;yBACF;qBACF;iBACF;aACF;SACF;KACF,CAAC;IAEC,mBAAA,IAAA,aAAM,EAAC,mBAAY,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IACjC,mBAAA,IAAA,aAAM,EAAC,mBAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;;;;qDAsCpC;wBAtGU,aAAa;IAErB,mBAAA,IAAA,aAAM,EAAC,aAAM,CAAC,aAAa,CAAC,CAAA;IAE5B,mBAAA,IAAA,cAAO,EAAC,2BAAgB,CAAC,CAAA;qDACM,2BAAgB;GALvC,aAAa,CAuGzB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './mcp-tool.decorator';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/decorators/index.ts"],"names":[],"mappings":";;;AAAA,+DAAqC"}
|
|
@@ -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"}
|
package/dist/index.d.ts
ADDED
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 @@
|
|
|
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 @@
|
|
|
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
package/dist/keys.js.map
ADDED
|
@@ -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 @@
|
|
|
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,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
|
+
}
|