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
|
@@ -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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"}
|
package/dist/types.d.ts
ADDED
|
@@ -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 @@
|
|
|
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"}
|