imean-service-engine 1.4.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 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 }; }
@@ -523,6 +527,7 @@ var ActionHandler = class {
523
527
  const now = Date.now();
524
528
  if (cached !== null && (!this.metadata.ttl || cached?.expireAt > now)) {
525
529
  span?.setAttribute("cacheHit", true);
530
+ span?.setAttribute("cacheKey", cacheKey);
526
531
  return cached.data;
527
532
  }
528
533
  }
@@ -1236,6 +1241,122 @@ async function startCheck(checkers, pass) {
1236
1241
  logger_default.info("[ \u9884\u68C0\u5B8C\u6210 ]");
1237
1242
  if (pass) await pass();
1238
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
+ };
1239
1360
 
1240
1361
  Object.defineProperty(exports, "dayjs", {
1241
1362
  enumerable: true,
@@ -1245,6 +1366,7 @@ exports.Action = Action;
1245
1366
  exports.CacheAdapter = CacheAdapter;
1246
1367
  exports.MemoryCacheAdapter = MemoryCacheAdapter;
1247
1368
  exports.Microservice = Microservice;
1369
+ exports.ModelContextProtocolPlugin = ModelContextProtocolPlugin;
1248
1370
  exports.Module = Module;
1249
1371
  exports.Plugin = Plugin;
1250
1372
  exports.RedisCacheAdapter = RedisCacheAdapter;
package/dist/mod.d.cts CHANGED
@@ -260,6 +260,13 @@ interface PreStartChecker {
260
260
  }
261
261
  declare function startCheck(checkers: PreStartChecker[], pass?: () => void | Promise<void>): Promise<void>;
262
262
 
263
+ declare class ModelContextProtocolPlugin extends Plugin {
264
+ private mcpServer;
265
+ private transports;
266
+ private registerMcpTools;
267
+ initialize: (engine: Microservice) => Promise<void>;
268
+ }
269
+
263
270
  /**
264
271
  * 用于给微服务模块方法添加的注解
265
272
  */
@@ -279,4 +286,4 @@ declare function Schedule(options: ScheduleOptions): Function;
279
286
 
280
287
  declare const logger: winston.Logger;
281
288
 
282
- export { Action, type ActionErrorEvent, type ActionMetadata, type ActionOptions, CacheAdapter, type CacheFn, type EtcdConfig, type EventServiceInfo, type McpOptions, MemoryCacheAdapter, Microservice, type MicroserviceOptions, 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 };
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
@@ -260,6 +260,13 @@ interface PreStartChecker {
260
260
  }
261
261
  declare function startCheck(checkers: PreStartChecker[], pass?: () => void | Promise<void>): Promise<void>;
262
262
 
263
+ declare class ModelContextProtocolPlugin extends Plugin {
264
+ private mcpServer;
265
+ private transports;
266
+ private registerMcpTools;
267
+ initialize: (engine: Microservice) => Promise<void>;
268
+ }
269
+
263
270
  /**
264
271
  * 用于给微服务模块方法添加的注解
265
272
  */
@@ -279,4 +286,4 @@ declare function Schedule(options: ScheduleOptions): Function;
279
286
 
280
287
  declare const logger: winston.Logger;
281
288
 
282
- export { Action, type ActionErrorEvent, type ActionMetadata, type ActionOptions, CacheAdapter, type CacheFn, type EtcdConfig, type EventServiceInfo, type McpOptions, MemoryCacheAdapter, Microservice, type MicroserviceOptions, 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 };
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
@@ -514,6 +518,7 @@ var ActionHandler = class {
514
518
  const now = Date.now();
515
519
  if (cached !== null && (!this.metadata.ttl || cached?.expireAt > now)) {
516
520
  span?.setAttribute("cacheHit", true);
521
+ span?.setAttribute("cacheKey", cacheKey);
517
522
  return cached.data;
518
523
  }
519
524
  }
@@ -1227,5 +1232,121 @@ async function startCheck(checkers, pass) {
1227
1232
  logger_default.info("[ \u9884\u68C0\u5B8C\u6210 ]");
1228
1233
  if (pass) await pass();
1229
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
+ };
1230
1351
 
1231
- export { Action, CacheAdapter, MemoryCacheAdapter, Microservice, Module, Plugin, 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,87 +1,87 @@
1
- {
2
- "name": "imean-service-engine",
3
- "version": "1.4.0",
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
- }
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
+ }