imean-service-engine 1.3.0 → 1.4.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/dist/mod.cjs +146 -26
- package/dist/mod.d.cts +20 -12
- package/dist/mod.d.ts +20 -12
- package/dist/mod.js +145 -27
- package/package.json +87 -85
package/dist/mod.cjs
CHANGED
@@ -13,6 +13,10 @@ var prettier = require('prettier');
|
|
13
13
|
var crypto2 = require('crypto');
|
14
14
|
var zlib = require('zlib');
|
15
15
|
var nodeWs = require('@hono/node-ws');
|
16
|
+
var mcp_js = require('@modelcontextprotocol/sdk/server/mcp.js');
|
17
|
+
var types_js = require('@modelcontextprotocol/sdk/types.js');
|
18
|
+
var streaming = require('hono/streaming');
|
19
|
+
var ulid = require('ulid');
|
16
20
|
var dayjs = require('dayjs');
|
17
21
|
|
18
22
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
@@ -85,7 +89,8 @@ function Action(options) {
|
|
85
89
|
printError: options.printError ?? false,
|
86
90
|
cache: options.cache,
|
87
91
|
ttl: options.ttl,
|
88
|
-
stream: options.stream
|
92
|
+
stream: options.stream,
|
93
|
+
mcp: options.mcp
|
89
94
|
};
|
90
95
|
prototype[ACTION_METADATA] = existingMetadata;
|
91
96
|
});
|
@@ -121,6 +126,8 @@ var ScheduleMode = /* @__PURE__ */ ((ScheduleMode2) => {
|
|
121
126
|
ScheduleMode2["FIXED_DELAY"] = "FIXED_DELAY";
|
122
127
|
return ScheduleMode2;
|
123
128
|
})(ScheduleMode || {});
|
129
|
+
var Plugin = class {
|
130
|
+
};
|
124
131
|
var logger = winston__default.default.createLogger({
|
125
132
|
level: "info",
|
126
133
|
transports: [
|
@@ -520,6 +527,7 @@ var ActionHandler = class {
|
|
520
527
|
const now = Date.now();
|
521
528
|
if (cached !== null && (!this.metadata.ttl || cached?.expireAt > now)) {
|
522
529
|
span?.setAttribute("cacheHit", true);
|
530
|
+
span?.setAttribute("cacheKey", cacheKey);
|
523
531
|
return cached.data;
|
524
532
|
}
|
525
533
|
}
|
@@ -783,7 +791,16 @@ var Microservice = class {
|
|
783
791
|
this.options = {
|
784
792
|
prefix: "/api",
|
785
793
|
name: "microservice",
|
794
|
+
version: "0.0.1",
|
795
|
+
env: "default",
|
796
|
+
printError: false,
|
797
|
+
disableCache: false,
|
798
|
+
generateClient: false,
|
799
|
+
etcd: false,
|
800
|
+
events: {},
|
801
|
+
websocket: { enabled: false },
|
786
802
|
cacheAdapter: new MemoryCacheAdapter(),
|
803
|
+
plugins: [],
|
787
804
|
...options
|
788
805
|
};
|
789
806
|
this.cache = this.options.cacheAdapter;
|
@@ -802,6 +819,7 @@ var Microservice = class {
|
|
802
819
|
this.initStatsEventManager();
|
803
820
|
}
|
804
821
|
await this.registerService(true);
|
822
|
+
await this.initPlugins();
|
805
823
|
}
|
806
824
|
async initModules() {
|
807
825
|
for (const ModuleClass of this.options.modules) {
|
@@ -826,7 +844,7 @@ var Microservice = class {
|
|
826
844
|
);
|
827
845
|
this.actionHandlers.set(`${moduleName}.${actionName}`, handler);
|
828
846
|
logger_default.info(
|
829
|
-
`[ \u6CE8\u518C\u52A8\u4F5C ] ${moduleName}.${actionName} ${actionMetadata.description}`
|
847
|
+
`[ \u6CE8\u518C\u52A8\u4F5C ] ${moduleName}.${actionName} ${actionMetadata.description} ${actionMetadata.mcp ? "MCP:" + actionMetadata.mcp?.type : ""}`
|
830
848
|
);
|
831
849
|
}
|
832
850
|
const schedules = getScheduleMetadata(ModuleClass.prototype);
|
@@ -1189,32 +1207,16 @@ Received SIGINT signal`);
|
|
1189
1207
|
return ctx.json({ success: false, error: error.message });
|
1190
1208
|
}
|
1191
1209
|
};
|
1192
|
-
async
|
1193
|
-
|
1194
|
-
|
1195
|
-
|
1196
|
-
|
1197
|
-
|
1198
|
-
async getServices() {
|
1199
|
-
if (!this.etcdClient) {
|
1200
|
-
throw new Error("etcd is not configured");
|
1210
|
+
async initPlugins() {
|
1211
|
+
for (const plugin of this.options.plugins) {
|
1212
|
+
logger_default.info(`[ \u521D\u59CB\u5316\u63D2\u4EF6 ] ${plugin.constructor.name}`);
|
1213
|
+
await plugin.initialize(this).catch((e) => {
|
1214
|
+
logger_default.error(`[ \u521D\u59CB\u5316\u63D2\u4EF6\u5931\u8D25 ] ${plugin.constructor.name}: ${e}`);
|
1215
|
+
});
|
1201
1216
|
}
|
1202
|
-
const namespace = this.options.etcd?.namespace ? `${this.options.etcd.namespace}/` : "";
|
1203
|
-
const servicesKey = `${namespace}services`;
|
1204
|
-
const services = await this.etcdClient.getAll().prefix(servicesKey);
|
1205
|
-
return Object.values(services).map((value) => JSON.parse(value));
|
1206
1217
|
}
|
1207
|
-
|
1208
|
-
|
1209
|
-
*/
|
1210
|
-
async getServiceInstances(serviceName) {
|
1211
|
-
if (!this.etcdClient) {
|
1212
|
-
throw new Error("etcd is not configured");
|
1213
|
-
}
|
1214
|
-
const namespace = this.options.etcd?.namespace ? `${this.options.etcd.namespace}/` : "";
|
1215
|
-
const serviceKey = `${namespace}services/${serviceName}`;
|
1216
|
-
const instances = await this.etcdClient.getAll().prefix(serviceKey);
|
1217
|
-
return Object.values(instances).map((value) => JSON.parse(value));
|
1218
|
+
async init() {
|
1219
|
+
await this.waitingInitialization;
|
1218
1220
|
}
|
1219
1221
|
};
|
1220
1222
|
|
@@ -1239,6 +1241,122 @@ async function startCheck(checkers, pass) {
|
|
1239
1241
|
logger_default.info("[ \u9884\u68C0\u5B8C\u6210 ]");
|
1240
1242
|
if (pass) await pass();
|
1241
1243
|
}
|
1244
|
+
var HonoTransport = class {
|
1245
|
+
constructor(url, stream, closeStream) {
|
1246
|
+
this.url = url;
|
1247
|
+
this.stream = stream;
|
1248
|
+
this.closeStream = closeStream;
|
1249
|
+
this.sessionId = ulid.ulid();
|
1250
|
+
}
|
1251
|
+
sessionId;
|
1252
|
+
onclose;
|
1253
|
+
onerror;
|
1254
|
+
onmessage;
|
1255
|
+
async start() {
|
1256
|
+
await this.stream.writeSSE({
|
1257
|
+
event: "endpoint",
|
1258
|
+
data: `${encodeURI(this.url)}?sessionId=${this.sessionId}`
|
1259
|
+
});
|
1260
|
+
this.stream.onAbort(() => {
|
1261
|
+
this.close();
|
1262
|
+
});
|
1263
|
+
}
|
1264
|
+
async handleMessage(message) {
|
1265
|
+
let parsedMessage;
|
1266
|
+
try {
|
1267
|
+
parsedMessage = types_js.JSONRPCMessageSchema.parse(message);
|
1268
|
+
} catch (error) {
|
1269
|
+
this.onerror?.(error);
|
1270
|
+
throw error;
|
1271
|
+
}
|
1272
|
+
this.onmessage?.(parsedMessage);
|
1273
|
+
}
|
1274
|
+
async send(message) {
|
1275
|
+
await this.stream.writeln(
|
1276
|
+
`event: message
|
1277
|
+
data: ${JSON.stringify(message)}
|
1278
|
+
|
1279
|
+
`
|
1280
|
+
);
|
1281
|
+
}
|
1282
|
+
async close() {
|
1283
|
+
this.onclose?.();
|
1284
|
+
this.closeStream();
|
1285
|
+
}
|
1286
|
+
};
|
1287
|
+
var ModelContextProtocolPlugin = class extends Plugin {
|
1288
|
+
mcpServer;
|
1289
|
+
transports = {};
|
1290
|
+
registerMcpTools(engine) {
|
1291
|
+
const modules = engine.getModules(true);
|
1292
|
+
for (const module of Object.values(modules)) {
|
1293
|
+
for (const action of Object.values(module.actions)) {
|
1294
|
+
if (action.mcp?.type !== "tool") {
|
1295
|
+
continue;
|
1296
|
+
}
|
1297
|
+
const args = {};
|
1298
|
+
const argsIndex = {};
|
1299
|
+
for (const [index, param] of (action.params ?? []).entries()) {
|
1300
|
+
args[param.description] = param;
|
1301
|
+
argsIndex[param.description] = index;
|
1302
|
+
}
|
1303
|
+
this.mcpServer.tool(
|
1304
|
+
`${module.name}.${action.name}`,
|
1305
|
+
action.description ?? "",
|
1306
|
+
args,
|
1307
|
+
async (params) => {
|
1308
|
+
const args2 = Object.values(params);
|
1309
|
+
const argsList = [];
|
1310
|
+
for (const [key, value] of Object.entries(args2)) {
|
1311
|
+
argsList[argsIndex[key]] = value;
|
1312
|
+
}
|
1313
|
+
const result = await engine.getActionHandler(module.name, action.name).handle(argsList);
|
1314
|
+
return {
|
1315
|
+
content: [{ type: "text", text: JSON.stringify(result) }]
|
1316
|
+
};
|
1317
|
+
}
|
1318
|
+
);
|
1319
|
+
}
|
1320
|
+
}
|
1321
|
+
}
|
1322
|
+
initialize = async (engine) => {
|
1323
|
+
const app = engine.getApp();
|
1324
|
+
this.mcpServer = new mcp_js.McpServer({
|
1325
|
+
name: engine.options.name,
|
1326
|
+
version: engine.options.version
|
1327
|
+
});
|
1328
|
+
this.registerMcpTools(engine);
|
1329
|
+
app.get(`${engine.options.prefix}/mcp_sse`, async (ctx) => {
|
1330
|
+
return streaming.streamSSE(ctx, async (stream) => {
|
1331
|
+
return new Promise(async (resolve) => {
|
1332
|
+
const transport = new HonoTransport(
|
1333
|
+
`${engine.options.prefix}/mcp_messages`,
|
1334
|
+
stream,
|
1335
|
+
() => {
|
1336
|
+
delete this.transports[transport.sessionId];
|
1337
|
+
resolve();
|
1338
|
+
}
|
1339
|
+
);
|
1340
|
+
this.transports[transport.sessionId] = transport;
|
1341
|
+
await this.mcpServer.connect(transport);
|
1342
|
+
});
|
1343
|
+
});
|
1344
|
+
});
|
1345
|
+
app.post(`${engine.options.prefix}/mcp_messages`, async (ctx) => {
|
1346
|
+
const sessionId = ctx.req.query("sessionId");
|
1347
|
+
if (!sessionId) {
|
1348
|
+
return ctx.text("No transport found for sessionId", 400);
|
1349
|
+
}
|
1350
|
+
const transport = this.transports[sessionId];
|
1351
|
+
const message = await ctx.req.json();
|
1352
|
+
await transport.handleMessage(message);
|
1353
|
+
return ctx.text("Accepted", 202);
|
1354
|
+
});
|
1355
|
+
logger_default.info(
|
1356
|
+
`ModelContextProtocolPlugin endpoint: ${engine.options.prefix}/mcp_sse`
|
1357
|
+
);
|
1358
|
+
};
|
1359
|
+
};
|
1242
1360
|
|
1243
1361
|
Object.defineProperty(exports, "dayjs", {
|
1244
1362
|
enumerable: true,
|
@@ -1248,7 +1366,9 @@ exports.Action = Action;
|
|
1248
1366
|
exports.CacheAdapter = CacheAdapter;
|
1249
1367
|
exports.MemoryCacheAdapter = MemoryCacheAdapter;
|
1250
1368
|
exports.Microservice = Microservice;
|
1369
|
+
exports.ModelContextProtocolPlugin = ModelContextProtocolPlugin;
|
1251
1370
|
exports.Module = Module;
|
1371
|
+
exports.Plugin = Plugin;
|
1252
1372
|
exports.RedisCacheAdapter = RedisCacheAdapter;
|
1253
1373
|
exports.Schedule = Schedule;
|
1254
1374
|
exports.ScheduleMode = ScheduleMode;
|
package/dist/mod.d.cts
CHANGED
@@ -25,6 +25,9 @@ declare class MemoryCacheAdapter extends CacheAdapter {
|
|
25
25
|
}
|
26
26
|
|
27
27
|
type CacheFn = (...args: any[]) => any;
|
28
|
+
type McpOptions = {
|
29
|
+
type: "tool";
|
30
|
+
};
|
28
31
|
interface ActionOptions {
|
29
32
|
params?: z.ZodType<any>[];
|
30
33
|
returns?: z.ZodType<any>;
|
@@ -34,6 +37,7 @@ interface ActionOptions {
|
|
34
37
|
cache?: boolean | CacheFn;
|
35
38
|
ttl?: number;
|
36
39
|
stream?: boolean;
|
40
|
+
mcp?: McpOptions;
|
37
41
|
}
|
38
42
|
interface ActionMetadata extends ActionOptions {
|
39
43
|
name: string;
|
@@ -139,7 +143,7 @@ interface MicroserviceOptions {
|
|
139
143
|
disableCache?: boolean;
|
140
144
|
modules: (new () => any)[];
|
141
145
|
generateClient?: URL | boolean;
|
142
|
-
etcd?: EtcdConfig;
|
146
|
+
etcd?: EtcdConfig | false;
|
143
147
|
events?: StatisticsEventOptions & {
|
144
148
|
onStats?: (event: StatisticsEvent) => void | Promise<void>;
|
145
149
|
onRegister?: (serviceInfo: ServiceInfo) => void | Promise<void>;
|
@@ -150,6 +154,7 @@ interface MicroserviceOptions {
|
|
150
154
|
timeout?: number;
|
151
155
|
};
|
152
156
|
cacheAdapter?: CacheAdapter;
|
157
|
+
plugins?: Plugin[];
|
153
158
|
}
|
154
159
|
interface ModuleInfo extends ModuleOptions {
|
155
160
|
actions: Record<string, ActionMetadata>;
|
@@ -160,6 +165,9 @@ interface StreamResponse<T = any> {
|
|
160
165
|
done: boolean;
|
161
166
|
error?: string;
|
162
167
|
}
|
168
|
+
declare abstract class Plugin {
|
169
|
+
abstract initialize(engine: Microservice): Promise<void>;
|
170
|
+
}
|
163
171
|
|
164
172
|
declare class ActionHandler {
|
165
173
|
private moduleInstance;
|
@@ -195,7 +203,7 @@ declare class Microservice {
|
|
195
203
|
private actionHandlers;
|
196
204
|
modules: Map<string, ModuleInfo>;
|
197
205
|
readonly fetch: typeof fetch;
|
198
|
-
options: MicroserviceOptions
|
206
|
+
options: Required<MicroserviceOptions>;
|
199
207
|
cache: CacheAdapter;
|
200
208
|
serviceId: string;
|
201
209
|
constructor(options: MicroserviceOptions);
|
@@ -224,7 +232,7 @@ declare class Microservice {
|
|
224
232
|
/**
|
225
233
|
* 获取所有模块的元数据
|
226
234
|
*/
|
227
|
-
|
235
|
+
getModules(withTypes?: boolean): Record<string, ModuleInfo>;
|
228
236
|
/**
|
229
237
|
* 停止服务
|
230
238
|
*/
|
@@ -241,15 +249,8 @@ declare class Microservice {
|
|
241
249
|
private updateStats;
|
242
250
|
getActionHandler(moduleName: string, actionName: string): ActionHandler;
|
243
251
|
private handleRequest;
|
252
|
+
private initPlugins;
|
244
253
|
init(): Promise<void>;
|
245
|
-
/**
|
246
|
-
* 获取所有注册的服务
|
247
|
-
*/
|
248
|
-
getServices(): Promise<ServiceInfo[]>;
|
249
|
-
/**
|
250
|
-
* 获取指定服务的实例
|
251
|
-
*/
|
252
|
-
getServiceInstances(serviceName: string): Promise<ServiceInfo[]>;
|
253
254
|
}
|
254
255
|
|
255
256
|
interface PreStartChecker {
|
@@ -259,6 +260,13 @@ interface PreStartChecker {
|
|
259
260
|
}
|
260
261
|
declare function startCheck(checkers: PreStartChecker[], pass?: () => void | Promise<void>): Promise<void>;
|
261
262
|
|
263
|
+
declare class ModelContextProtocolPlugin extends Plugin {
|
264
|
+
private mcpServer;
|
265
|
+
private transports;
|
266
|
+
private registerMcpTools;
|
267
|
+
initialize: (engine: Microservice) => Promise<void>;
|
268
|
+
}
|
269
|
+
|
262
270
|
/**
|
263
271
|
* 用于给微服务模块方法添加的注解
|
264
272
|
*/
|
@@ -278,4 +286,4 @@ declare function Schedule(options: ScheduleOptions): Function;
|
|
278
286
|
|
279
287
|
declare const logger: winston.Logger;
|
280
288
|
|
281
|
-
export { Action, type ActionErrorEvent, type ActionMetadata, type ActionOptions, CacheAdapter, type CacheFn, type EtcdConfig, type EventServiceInfo, MemoryCacheAdapter, Microservice, type MicroserviceOptions, Module, type ModuleInfo, type ModuleMetadata, type ModuleOptions, type PreStartChecker, RedisCacheAdapter, Schedule, type ScheduleMetadata, ScheduleMode, type ScheduleOptions, ServiceContext, type ServiceInfo, type ServiceStats, type StatisticsEvent, type StreamResponse, logger, startCheck };
|
289
|
+
export { Action, type ActionErrorEvent, type ActionMetadata, type ActionOptions, CacheAdapter, type CacheFn, type EtcdConfig, type EventServiceInfo, type McpOptions, MemoryCacheAdapter, Microservice, type MicroserviceOptions, ModelContextProtocolPlugin, Module, type ModuleInfo, type ModuleMetadata, type ModuleOptions, Plugin, type PreStartChecker, RedisCacheAdapter, Schedule, type ScheduleMetadata, ScheduleMode, type ScheduleOptions, ServiceContext, type ServiceInfo, type ServiceStats, type StatisticsEvent, type StreamResponse, logger, startCheck };
|
package/dist/mod.d.ts
CHANGED
@@ -25,6 +25,9 @@ declare class MemoryCacheAdapter extends CacheAdapter {
|
|
25
25
|
}
|
26
26
|
|
27
27
|
type CacheFn = (...args: any[]) => any;
|
28
|
+
type McpOptions = {
|
29
|
+
type: "tool";
|
30
|
+
};
|
28
31
|
interface ActionOptions {
|
29
32
|
params?: z.ZodType<any>[];
|
30
33
|
returns?: z.ZodType<any>;
|
@@ -34,6 +37,7 @@ interface ActionOptions {
|
|
34
37
|
cache?: boolean | CacheFn;
|
35
38
|
ttl?: number;
|
36
39
|
stream?: boolean;
|
40
|
+
mcp?: McpOptions;
|
37
41
|
}
|
38
42
|
interface ActionMetadata extends ActionOptions {
|
39
43
|
name: string;
|
@@ -139,7 +143,7 @@ interface MicroserviceOptions {
|
|
139
143
|
disableCache?: boolean;
|
140
144
|
modules: (new () => any)[];
|
141
145
|
generateClient?: URL | boolean;
|
142
|
-
etcd?: EtcdConfig;
|
146
|
+
etcd?: EtcdConfig | false;
|
143
147
|
events?: StatisticsEventOptions & {
|
144
148
|
onStats?: (event: StatisticsEvent) => void | Promise<void>;
|
145
149
|
onRegister?: (serviceInfo: ServiceInfo) => void | Promise<void>;
|
@@ -150,6 +154,7 @@ interface MicroserviceOptions {
|
|
150
154
|
timeout?: number;
|
151
155
|
};
|
152
156
|
cacheAdapter?: CacheAdapter;
|
157
|
+
plugins?: Plugin[];
|
153
158
|
}
|
154
159
|
interface ModuleInfo extends ModuleOptions {
|
155
160
|
actions: Record<string, ActionMetadata>;
|
@@ -160,6 +165,9 @@ interface StreamResponse<T = any> {
|
|
160
165
|
done: boolean;
|
161
166
|
error?: string;
|
162
167
|
}
|
168
|
+
declare abstract class Plugin {
|
169
|
+
abstract initialize(engine: Microservice): Promise<void>;
|
170
|
+
}
|
163
171
|
|
164
172
|
declare class ActionHandler {
|
165
173
|
private moduleInstance;
|
@@ -195,7 +203,7 @@ declare class Microservice {
|
|
195
203
|
private actionHandlers;
|
196
204
|
modules: Map<string, ModuleInfo>;
|
197
205
|
readonly fetch: typeof fetch;
|
198
|
-
options: MicroserviceOptions
|
206
|
+
options: Required<MicroserviceOptions>;
|
199
207
|
cache: CacheAdapter;
|
200
208
|
serviceId: string;
|
201
209
|
constructor(options: MicroserviceOptions);
|
@@ -224,7 +232,7 @@ declare class Microservice {
|
|
224
232
|
/**
|
225
233
|
* 获取所有模块的元数据
|
226
234
|
*/
|
227
|
-
|
235
|
+
getModules(withTypes?: boolean): Record<string, ModuleInfo>;
|
228
236
|
/**
|
229
237
|
* 停止服务
|
230
238
|
*/
|
@@ -241,15 +249,8 @@ declare class Microservice {
|
|
241
249
|
private updateStats;
|
242
250
|
getActionHandler(moduleName: string, actionName: string): ActionHandler;
|
243
251
|
private handleRequest;
|
252
|
+
private initPlugins;
|
244
253
|
init(): Promise<void>;
|
245
|
-
/**
|
246
|
-
* 获取所有注册的服务
|
247
|
-
*/
|
248
|
-
getServices(): Promise<ServiceInfo[]>;
|
249
|
-
/**
|
250
|
-
* 获取指定服务的实例
|
251
|
-
*/
|
252
|
-
getServiceInstances(serviceName: string): Promise<ServiceInfo[]>;
|
253
254
|
}
|
254
255
|
|
255
256
|
interface PreStartChecker {
|
@@ -259,6 +260,13 @@ interface PreStartChecker {
|
|
259
260
|
}
|
260
261
|
declare function startCheck(checkers: PreStartChecker[], pass?: () => void | Promise<void>): Promise<void>;
|
261
262
|
|
263
|
+
declare class ModelContextProtocolPlugin extends Plugin {
|
264
|
+
private mcpServer;
|
265
|
+
private transports;
|
266
|
+
private registerMcpTools;
|
267
|
+
initialize: (engine: Microservice) => Promise<void>;
|
268
|
+
}
|
269
|
+
|
262
270
|
/**
|
263
271
|
* 用于给微服务模块方法添加的注解
|
264
272
|
*/
|
@@ -278,4 +286,4 @@ declare function Schedule(options: ScheduleOptions): Function;
|
|
278
286
|
|
279
287
|
declare const logger: winston.Logger;
|
280
288
|
|
281
|
-
export { Action, type ActionErrorEvent, type ActionMetadata, type ActionOptions, CacheAdapter, type CacheFn, type EtcdConfig, type EventServiceInfo, MemoryCacheAdapter, Microservice, type MicroserviceOptions, Module, type ModuleInfo, type ModuleMetadata, type ModuleOptions, type PreStartChecker, RedisCacheAdapter, Schedule, type ScheduleMetadata, ScheduleMode, type ScheduleOptions, ServiceContext, type ServiceInfo, type ServiceStats, type StatisticsEvent, type StreamResponse, logger, startCheck };
|
289
|
+
export { Action, type ActionErrorEvent, type ActionMetadata, type ActionOptions, CacheAdapter, type CacheFn, type EtcdConfig, type EventServiceInfo, type McpOptions, MemoryCacheAdapter, Microservice, type MicroserviceOptions, ModelContextProtocolPlugin, Module, type ModuleInfo, type ModuleMetadata, type ModuleOptions, Plugin, type PreStartChecker, RedisCacheAdapter, Schedule, type ScheduleMetadata, ScheduleMode, type ScheduleOptions, ServiceContext, type ServiceInfo, type ServiceStats, type StatisticsEvent, type StreamResponse, logger, startCheck };
|
package/dist/mod.js
CHANGED
@@ -13,6 +13,10 @@ import crypto2 from 'node:crypto';
|
|
13
13
|
import { brotliDecompress } from 'node:zlib';
|
14
14
|
import { brotliCompress, constants } from 'zlib';
|
15
15
|
import { createNodeWebSocket } from '@hono/node-ws';
|
16
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
17
|
+
import { JSONRPCMessageSchema } from '@modelcontextprotocol/sdk/types.js';
|
18
|
+
import { streamSSE } from 'hono/streaming';
|
19
|
+
import { ulid } from 'ulid';
|
16
20
|
export { default as dayjs } from 'dayjs';
|
17
21
|
|
18
22
|
// mod.ts
|
@@ -76,7 +80,8 @@ function Action(options) {
|
|
76
80
|
printError: options.printError ?? false,
|
77
81
|
cache: options.cache,
|
78
82
|
ttl: options.ttl,
|
79
|
-
stream: options.stream
|
83
|
+
stream: options.stream,
|
84
|
+
mcp: options.mcp
|
80
85
|
};
|
81
86
|
prototype[ACTION_METADATA] = existingMetadata;
|
82
87
|
});
|
@@ -112,6 +117,8 @@ var ScheduleMode = /* @__PURE__ */ ((ScheduleMode2) => {
|
|
112
117
|
ScheduleMode2["FIXED_DELAY"] = "FIXED_DELAY";
|
113
118
|
return ScheduleMode2;
|
114
119
|
})(ScheduleMode || {});
|
120
|
+
var Plugin = class {
|
121
|
+
};
|
115
122
|
var logger = winston.createLogger({
|
116
123
|
level: "info",
|
117
124
|
transports: [
|
@@ -511,6 +518,7 @@ var ActionHandler = class {
|
|
511
518
|
const now = Date.now();
|
512
519
|
if (cached !== null && (!this.metadata.ttl || cached?.expireAt > now)) {
|
513
520
|
span?.setAttribute("cacheHit", true);
|
521
|
+
span?.setAttribute("cacheKey", cacheKey);
|
514
522
|
return cached.data;
|
515
523
|
}
|
516
524
|
}
|
@@ -774,7 +782,16 @@ var Microservice = class {
|
|
774
782
|
this.options = {
|
775
783
|
prefix: "/api",
|
776
784
|
name: "microservice",
|
785
|
+
version: "0.0.1",
|
786
|
+
env: "default",
|
787
|
+
printError: false,
|
788
|
+
disableCache: false,
|
789
|
+
generateClient: false,
|
790
|
+
etcd: false,
|
791
|
+
events: {},
|
792
|
+
websocket: { enabled: false },
|
777
793
|
cacheAdapter: new MemoryCacheAdapter(),
|
794
|
+
plugins: [],
|
778
795
|
...options
|
779
796
|
};
|
780
797
|
this.cache = this.options.cacheAdapter;
|
@@ -793,6 +810,7 @@ var Microservice = class {
|
|
793
810
|
this.initStatsEventManager();
|
794
811
|
}
|
795
812
|
await this.registerService(true);
|
813
|
+
await this.initPlugins();
|
796
814
|
}
|
797
815
|
async initModules() {
|
798
816
|
for (const ModuleClass of this.options.modules) {
|
@@ -817,7 +835,7 @@ var Microservice = class {
|
|
817
835
|
);
|
818
836
|
this.actionHandlers.set(`${moduleName}.${actionName}`, handler);
|
819
837
|
logger_default.info(
|
820
|
-
`[ \u6CE8\u518C\u52A8\u4F5C ] ${moduleName}.${actionName} ${actionMetadata.description}`
|
838
|
+
`[ \u6CE8\u518C\u52A8\u4F5C ] ${moduleName}.${actionName} ${actionMetadata.description} ${actionMetadata.mcp ? "MCP:" + actionMetadata.mcp?.type : ""}`
|
821
839
|
);
|
822
840
|
}
|
823
841
|
const schedules = getScheduleMetadata(ModuleClass.prototype);
|
@@ -1180,32 +1198,16 @@ Received SIGINT signal`);
|
|
1180
1198
|
return ctx.json({ success: false, error: error.message });
|
1181
1199
|
}
|
1182
1200
|
};
|
1183
|
-
async
|
1184
|
-
|
1185
|
-
|
1186
|
-
|
1187
|
-
|
1188
|
-
|
1189
|
-
async getServices() {
|
1190
|
-
if (!this.etcdClient) {
|
1191
|
-
throw new Error("etcd is not configured");
|
1201
|
+
async initPlugins() {
|
1202
|
+
for (const plugin of this.options.plugins) {
|
1203
|
+
logger_default.info(`[ \u521D\u59CB\u5316\u63D2\u4EF6 ] ${plugin.constructor.name}`);
|
1204
|
+
await plugin.initialize(this).catch((e) => {
|
1205
|
+
logger_default.error(`[ \u521D\u59CB\u5316\u63D2\u4EF6\u5931\u8D25 ] ${plugin.constructor.name}: ${e}`);
|
1206
|
+
});
|
1192
1207
|
}
|
1193
|
-
const namespace = this.options.etcd?.namespace ? `${this.options.etcd.namespace}/` : "";
|
1194
|
-
const servicesKey = `${namespace}services`;
|
1195
|
-
const services = await this.etcdClient.getAll().prefix(servicesKey);
|
1196
|
-
return Object.values(services).map((value) => JSON.parse(value));
|
1197
1208
|
}
|
1198
|
-
|
1199
|
-
|
1200
|
-
*/
|
1201
|
-
async getServiceInstances(serviceName) {
|
1202
|
-
if (!this.etcdClient) {
|
1203
|
-
throw new Error("etcd is not configured");
|
1204
|
-
}
|
1205
|
-
const namespace = this.options.etcd?.namespace ? `${this.options.etcd.namespace}/` : "";
|
1206
|
-
const serviceKey = `${namespace}services/${serviceName}`;
|
1207
|
-
const instances = await this.etcdClient.getAll().prefix(serviceKey);
|
1208
|
-
return Object.values(instances).map((value) => JSON.parse(value));
|
1209
|
+
async init() {
|
1210
|
+
await this.waitingInitialization;
|
1209
1211
|
}
|
1210
1212
|
};
|
1211
1213
|
|
@@ -1230,5 +1232,121 @@ async function startCheck(checkers, pass) {
|
|
1230
1232
|
logger_default.info("[ \u9884\u68C0\u5B8C\u6210 ]");
|
1231
1233
|
if (pass) await pass();
|
1232
1234
|
}
|
1235
|
+
var HonoTransport = class {
|
1236
|
+
constructor(url, stream, closeStream) {
|
1237
|
+
this.url = url;
|
1238
|
+
this.stream = stream;
|
1239
|
+
this.closeStream = closeStream;
|
1240
|
+
this.sessionId = ulid();
|
1241
|
+
}
|
1242
|
+
sessionId;
|
1243
|
+
onclose;
|
1244
|
+
onerror;
|
1245
|
+
onmessage;
|
1246
|
+
async start() {
|
1247
|
+
await this.stream.writeSSE({
|
1248
|
+
event: "endpoint",
|
1249
|
+
data: `${encodeURI(this.url)}?sessionId=${this.sessionId}`
|
1250
|
+
});
|
1251
|
+
this.stream.onAbort(() => {
|
1252
|
+
this.close();
|
1253
|
+
});
|
1254
|
+
}
|
1255
|
+
async handleMessage(message) {
|
1256
|
+
let parsedMessage;
|
1257
|
+
try {
|
1258
|
+
parsedMessage = JSONRPCMessageSchema.parse(message);
|
1259
|
+
} catch (error) {
|
1260
|
+
this.onerror?.(error);
|
1261
|
+
throw error;
|
1262
|
+
}
|
1263
|
+
this.onmessage?.(parsedMessage);
|
1264
|
+
}
|
1265
|
+
async send(message) {
|
1266
|
+
await this.stream.writeln(
|
1267
|
+
`event: message
|
1268
|
+
data: ${JSON.stringify(message)}
|
1269
|
+
|
1270
|
+
`
|
1271
|
+
);
|
1272
|
+
}
|
1273
|
+
async close() {
|
1274
|
+
this.onclose?.();
|
1275
|
+
this.closeStream();
|
1276
|
+
}
|
1277
|
+
};
|
1278
|
+
var ModelContextProtocolPlugin = class extends Plugin {
|
1279
|
+
mcpServer;
|
1280
|
+
transports = {};
|
1281
|
+
registerMcpTools(engine) {
|
1282
|
+
const modules = engine.getModules(true);
|
1283
|
+
for (const module of Object.values(modules)) {
|
1284
|
+
for (const action of Object.values(module.actions)) {
|
1285
|
+
if (action.mcp?.type !== "tool") {
|
1286
|
+
continue;
|
1287
|
+
}
|
1288
|
+
const args = {};
|
1289
|
+
const argsIndex = {};
|
1290
|
+
for (const [index, param] of (action.params ?? []).entries()) {
|
1291
|
+
args[param.description] = param;
|
1292
|
+
argsIndex[param.description] = index;
|
1293
|
+
}
|
1294
|
+
this.mcpServer.tool(
|
1295
|
+
`${module.name}.${action.name}`,
|
1296
|
+
action.description ?? "",
|
1297
|
+
args,
|
1298
|
+
async (params) => {
|
1299
|
+
const args2 = Object.values(params);
|
1300
|
+
const argsList = [];
|
1301
|
+
for (const [key, value] of Object.entries(args2)) {
|
1302
|
+
argsList[argsIndex[key]] = value;
|
1303
|
+
}
|
1304
|
+
const result = await engine.getActionHandler(module.name, action.name).handle(argsList);
|
1305
|
+
return {
|
1306
|
+
content: [{ type: "text", text: JSON.stringify(result) }]
|
1307
|
+
};
|
1308
|
+
}
|
1309
|
+
);
|
1310
|
+
}
|
1311
|
+
}
|
1312
|
+
}
|
1313
|
+
initialize = async (engine) => {
|
1314
|
+
const app = engine.getApp();
|
1315
|
+
this.mcpServer = new McpServer({
|
1316
|
+
name: engine.options.name,
|
1317
|
+
version: engine.options.version
|
1318
|
+
});
|
1319
|
+
this.registerMcpTools(engine);
|
1320
|
+
app.get(`${engine.options.prefix}/mcp_sse`, async (ctx) => {
|
1321
|
+
return streamSSE(ctx, async (stream) => {
|
1322
|
+
return new Promise(async (resolve) => {
|
1323
|
+
const transport = new HonoTransport(
|
1324
|
+
`${engine.options.prefix}/mcp_messages`,
|
1325
|
+
stream,
|
1326
|
+
() => {
|
1327
|
+
delete this.transports[transport.sessionId];
|
1328
|
+
resolve();
|
1329
|
+
}
|
1330
|
+
);
|
1331
|
+
this.transports[transport.sessionId] = transport;
|
1332
|
+
await this.mcpServer.connect(transport);
|
1333
|
+
});
|
1334
|
+
});
|
1335
|
+
});
|
1336
|
+
app.post(`${engine.options.prefix}/mcp_messages`, async (ctx) => {
|
1337
|
+
const sessionId = ctx.req.query("sessionId");
|
1338
|
+
if (!sessionId) {
|
1339
|
+
return ctx.text("No transport found for sessionId", 400);
|
1340
|
+
}
|
1341
|
+
const transport = this.transports[sessionId];
|
1342
|
+
const message = await ctx.req.json();
|
1343
|
+
await transport.handleMessage(message);
|
1344
|
+
return ctx.text("Accepted", 202);
|
1345
|
+
});
|
1346
|
+
logger_default.info(
|
1347
|
+
`ModelContextProtocolPlugin endpoint: ${engine.options.prefix}/mcp_sse`
|
1348
|
+
);
|
1349
|
+
};
|
1350
|
+
};
|
1233
1351
|
|
1234
|
-
export { Action, CacheAdapter, MemoryCacheAdapter, Microservice, Module, RedisCacheAdapter, Schedule, ScheduleMode, ServiceContext, logger_default as logger, startCheck };
|
1352
|
+
export { Action, CacheAdapter, MemoryCacheAdapter, Microservice, ModelContextProtocolPlugin, Module, Plugin, RedisCacheAdapter, Schedule, ScheduleMode, ServiceContext, logger_default as logger, startCheck };
|
package/package.json
CHANGED
@@ -1,85 +1,87 @@
|
|
1
|
-
{
|
2
|
-
"name": "imean-service-engine",
|
3
|
-
"version": "1.
|
4
|
-
"description": "microservice engine",
|
5
|
-
"keywords": [
|
6
|
-
"microservice",
|
7
|
-
"websocket",
|
8
|
-
"http",
|
9
|
-
"node"
|
10
|
-
],
|
11
|
-
"author": "imean",
|
12
|
-
"type": "module",
|
13
|
-
"license": "MIT",
|
14
|
-
"repository": {
|
15
|
-
"type": "git",
|
16
|
-
"url": "git+https://git.imean.tech/imean/imean-microservice-framework.git"
|
17
|
-
},
|
18
|
-
"main": "dist/mod.js",
|
19
|
-
"module": "dist/mod.js",
|
20
|
-
"types": "dist/mod.d.ts",
|
21
|
-
"exports": {
|
22
|
-
".": {
|
23
|
-
"types": "./dist/mod.d.ts",
|
24
|
-
"import": "./dist/mod.js",
|
25
|
-
"require": "./dist/mod.cjs"
|
26
|
-
}
|
27
|
-
},
|
28
|
-
"files": [
|
29
|
-
"dist",
|
30
|
-
"README.md",
|
31
|
-
"LICENSE"
|
32
|
-
],
|
33
|
-
"scripts": {
|
34
|
-
"dev": "tsx watch dev/index.ts",
|
35
|
-
"build": "tsup",
|
36
|
-
"test": "vitest run",
|
37
|
-
"prepublishOnly": "npm run build && npm run test"
|
38
|
-
},
|
39
|
-
"dependencies": {
|
40
|
-
"@hono/node-server": "^1.13.7",
|
41
|
-
"@hono/node-ws": "^1.0.6",
|
42
|
-
"
|
43
|
-
"
|
44
|
-
"
|
45
|
-
"
|
46
|
-
"
|
47
|
-
"
|
48
|
-
"
|
49
|
-
"
|
50
|
-
"
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
"@opentelemetry/
|
60
|
-
"@opentelemetry/exporter-
|
61
|
-
"@opentelemetry/
|
62
|
-
"@opentelemetry/
|
63
|
-
"@opentelemetry/
|
64
|
-
"@opentelemetry/sdk-
|
65
|
-
"@opentelemetry/sdk-
|
66
|
-
"@opentelemetry/
|
67
|
-
"@
|
68
|
-
"@
|
69
|
-
"@types/
|
70
|
-
"@types/
|
71
|
-
"@
|
72
|
-
"
|
73
|
-
"
|
74
|
-
"
|
75
|
-
"
|
76
|
-
"
|
77
|
-
"
|
78
|
-
"
|
79
|
-
"
|
80
|
-
"
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
1
|
+
{
|
2
|
+
"name": "imean-service-engine",
|
3
|
+
"version": "1.4.1",
|
4
|
+
"description": "microservice engine",
|
5
|
+
"keywords": [
|
6
|
+
"microservice",
|
7
|
+
"websocket",
|
8
|
+
"http",
|
9
|
+
"node"
|
10
|
+
],
|
11
|
+
"author": "imean",
|
12
|
+
"type": "module",
|
13
|
+
"license": "MIT",
|
14
|
+
"repository": {
|
15
|
+
"type": "git",
|
16
|
+
"url": "git+https://git.imean.tech/imean/imean-microservice-framework.git"
|
17
|
+
},
|
18
|
+
"main": "dist/mod.js",
|
19
|
+
"module": "dist/mod.js",
|
20
|
+
"types": "dist/mod.d.ts",
|
21
|
+
"exports": {
|
22
|
+
".": {
|
23
|
+
"types": "./dist/mod.d.ts",
|
24
|
+
"import": "./dist/mod.js",
|
25
|
+
"require": "./dist/mod.cjs"
|
26
|
+
}
|
27
|
+
},
|
28
|
+
"files": [
|
29
|
+
"dist",
|
30
|
+
"README.md",
|
31
|
+
"LICENSE"
|
32
|
+
],
|
33
|
+
"scripts": {
|
34
|
+
"dev": "tsx watch dev/index.ts",
|
35
|
+
"build": "tsup",
|
36
|
+
"test": "vitest run",
|
37
|
+
"prepublishOnly": "npm run build && npm run test"
|
38
|
+
},
|
39
|
+
"dependencies": {
|
40
|
+
"@hono/node-server": "^1.13.7",
|
41
|
+
"@hono/node-ws": "^1.0.6",
|
42
|
+
"@modelcontextprotocol/sdk": "^1.8.0",
|
43
|
+
"dayjs": "^1.11.13",
|
44
|
+
"ejson": "^2.2.3",
|
45
|
+
"etcd3": "^1.1.2",
|
46
|
+
"fs-extra": "^11.3.0",
|
47
|
+
"hono": "^4.6.17",
|
48
|
+
"lru-cache": "^11.0.2",
|
49
|
+
"prettier": "^3.4.2",
|
50
|
+
"ulid": "^3.0.0",
|
51
|
+
"winston": "^3.17.0",
|
52
|
+
"zod": "^3.24.1"
|
53
|
+
},
|
54
|
+
"peerDependencies": {
|
55
|
+
"@opentelemetry/api": "^1.x",
|
56
|
+
"ioredis": "^5.6.0"
|
57
|
+
},
|
58
|
+
"devDependencies": {
|
59
|
+
"@opentelemetry/auto-instrumentations-node": "^0.55.3",
|
60
|
+
"@opentelemetry/exporter-logs-otlp-proto": "^0.57.1",
|
61
|
+
"@opentelemetry/exporter-metrics-otlp-proto": "^0.57.1",
|
62
|
+
"@opentelemetry/exporter-trace-otlp-proto": "^0.57.1",
|
63
|
+
"@opentelemetry/instrumentation-winston": "^0.44.0",
|
64
|
+
"@opentelemetry/sdk-logs": "^0.57.1",
|
65
|
+
"@opentelemetry/sdk-metrics": "^1.30.1",
|
66
|
+
"@opentelemetry/sdk-node": "^0.57.1",
|
67
|
+
"@opentelemetry/sdk-trace-node": "^1.30.1",
|
68
|
+
"@opentelemetry/winston-transport": "^0.10.0",
|
69
|
+
"@types/ejson": "^2.2.2",
|
70
|
+
"@types/fs-extra": "^11.0.4",
|
71
|
+
"@types/ioredis-mock": "^8.2.5",
|
72
|
+
"@types/node": "^20.0.0",
|
73
|
+
"@vitest/coverage-v8": "^3.0.4",
|
74
|
+
"imean-service-client": "^1.5.0",
|
75
|
+
"ioredis-mock": "^8.9.0",
|
76
|
+
"opentelemetry-instrumentation-fetch-node": "^1.2.3",
|
77
|
+
"tslib": "^2.8.1",
|
78
|
+
"tsup": "^8.0.1",
|
79
|
+
"tsx": "^4.19.2",
|
80
|
+
"typescript": "^5.3.3",
|
81
|
+
"vite-tsconfig-paths": "^5.1.4",
|
82
|
+
"vitest": "^3.0.3"
|
83
|
+
},
|
84
|
+
"engines": {
|
85
|
+
"node": ">=20"
|
86
|
+
}
|
87
|
+
}
|