@leanmcp/core 0.3.8 → 0.3.10

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/index.d.mts CHANGED
@@ -411,6 +411,7 @@ declare class MCPServer {
411
411
  private options;
412
412
  private initPromise;
413
413
  private autoDiscovered;
414
+ private manifestWatcher;
414
415
  constructor(options: MCPServerConstructorOptions);
415
416
  /**
416
417
  * Internal initialization - runs automatically in constructor
@@ -462,6 +463,14 @@ declare class MCPServer {
462
463
  * Register a service instance with decorated methods
463
464
  */
464
465
  registerService(instance: any): void;
466
+ /**
467
+ * Watch UI manifest for changes and reload resources dynamically
468
+ */
469
+ private watchUIManifest;
470
+ /**
471
+ * Reload UI manifest and update resource registrations
472
+ */
473
+ private reloadUIManifest;
465
474
  /**
466
475
  * Load UI manifest and auto-register resources for pre-built @UIApp components.
467
476
  * The manifest is generated by `leanmcp dev` or `leanmcp start` commands.
@@ -511,6 +520,10 @@ declare class MCPServer {
511
520
  } | undefined;
512
521
  } | undefined;
513
522
  }>;
523
+ /**
524
+ * Cleanup resources (call on server shutdown)
525
+ */
526
+ cleanup(): Promise<void>;
514
527
  }
515
528
  declare class MCPServerRuntime {
516
529
  private server;
package/dist/index.d.ts CHANGED
@@ -411,6 +411,7 @@ declare class MCPServer {
411
411
  private options;
412
412
  private initPromise;
413
413
  private autoDiscovered;
414
+ private manifestWatcher;
414
415
  constructor(options: MCPServerConstructorOptions);
415
416
  /**
416
417
  * Internal initialization - runs automatically in constructor
@@ -462,6 +463,14 @@ declare class MCPServer {
462
463
  * Register a service instance with decorated methods
463
464
  */
464
465
  registerService(instance: any): void;
466
+ /**
467
+ * Watch UI manifest for changes and reload resources dynamically
468
+ */
469
+ private watchUIManifest;
470
+ /**
471
+ * Reload UI manifest and update resource registrations
472
+ */
473
+ private reloadUIManifest;
465
474
  /**
466
475
  * Load UI manifest and auto-register resources for pre-built @UIApp components.
467
476
  * The manifest is generated by `leanmcp dev` or `leanmcp start` commands.
@@ -511,6 +520,10 @@ declare class MCPServer {
511
520
  } | undefined;
512
521
  } | undefined;
513
522
  }>;
523
+ /**
524
+ * Cleanup resources (call on server shutdown)
525
+ */
526
+ cleanup(): Promise<void>;
514
527
  }
515
528
  declare class MCPServerRuntime {
516
529
  private server;
package/dist/index.js CHANGED
@@ -889,20 +889,36 @@ async function createHTTPServer(serverInput, options) {
889
889
  logger.error(`Server error: ${error.message}`);
890
890
  reject(error);
891
891
  });
892
- const cleanup = /* @__PURE__ */ __name(() => {
892
+ let isShuttingDown = false;
893
+ const cleanup = /* @__PURE__ */ __name(async () => {
894
+ if (isShuttingDown) return;
895
+ isShuttingDown = true;
893
896
  logger.info("\nShutting down server...");
894
- Object.values(transports).forEach((t) => t.close?.());
895
- activeListener?.close(() => {
896
- logger.info("Server closed");
897
- process.exit(0);
898
- });
899
- setTimeout(() => {
900
- logger.warn("Forcing shutdown...");
901
- process.exit(1);
902
- }, 5e3);
897
+ for (const transport of Object.values(transports)) {
898
+ try {
899
+ transport.close?.();
900
+ } catch (e) {
901
+ }
902
+ }
903
+ if (activeListener) {
904
+ await new Promise((resolveClose) => {
905
+ activeListener.close((err) => {
906
+ if (err) {
907
+ logger.warn(`Error closing server: ${err.message}`);
908
+ } else {
909
+ logger.info("Server closed");
910
+ }
911
+ resolveClose();
912
+ });
913
+ });
914
+ }
903
915
  }, "cleanup");
904
- process.on("SIGINT", cleanup);
905
- process.on("SIGTERM", cleanup);
916
+ const handleShutdown = /* @__PURE__ */ __name(() => {
917
+ cleanup().finally(() => {
918
+ });
919
+ }, "handleShutdown");
920
+ process.once("SIGINT", handleShutdown);
921
+ process.once("SIGTERM", handleShutdown);
906
922
  } catch (error) {
907
923
  reject(error);
908
924
  }
@@ -990,6 +1006,7 @@ var init_index = __esm({
990
1006
  options;
991
1007
  initPromise;
992
1008
  autoDiscovered = false;
1009
+ manifestWatcher = null;
993
1010
  constructor(options) {
994
1011
  this.options = options;
995
1012
  this.logging = options.logging || false;
@@ -1023,6 +1040,7 @@ var init_index = __esm({
1023
1040
  await this.autoDiscoverServices(options.mcpDir, options.serviceFactories);
1024
1041
  }
1025
1042
  await this.loadUIManifest();
1043
+ this.watchUIManifest();
1026
1044
  }
1027
1045
  /**
1028
1046
  * Wait for initialization to complete
@@ -1420,6 +1438,125 @@ var init_index = __esm({
1420
1438
  }
1421
1439
  }
1422
1440
  /**
1441
+ * Watch UI manifest for changes and reload resources dynamically
1442
+ */
1443
+ watchUIManifest() {
1444
+ try {
1445
+ const manifestPath = import_path.default.join(process.cwd(), "dist", "ui-manifest.json");
1446
+ if (!import_fs.default.existsSync(manifestPath)) {
1447
+ return;
1448
+ }
1449
+ if (this.logging) {
1450
+ this.logger.debug(`Watching UI manifest: ${manifestPath}`);
1451
+ }
1452
+ import("chokidar").then(({ default: chokidar }) => {
1453
+ this.manifestWatcher = chokidar.watch(manifestPath, {
1454
+ ignoreInitial: true,
1455
+ persistent: true,
1456
+ awaitWriteFinish: {
1457
+ stabilityThreshold: 100,
1458
+ pollInterval: 50
1459
+ }
1460
+ });
1461
+ this.manifestWatcher.on("change", async () => {
1462
+ if (this.logging) {
1463
+ this.logger.debug("UI manifest changed, reloading resources");
1464
+ }
1465
+ await this.reloadUIManifest();
1466
+ });
1467
+ this.manifestWatcher.on("error", (error) => {
1468
+ if (this.logging) {
1469
+ this.logger.warn(`Manifest watcher error: ${error.message}`);
1470
+ }
1471
+ });
1472
+ }).catch((error) => {
1473
+ if (this.logging) {
1474
+ this.logger.warn(`Failed to initialize manifest watcher: ${error.message}`);
1475
+ }
1476
+ });
1477
+ } catch (error) {
1478
+ if (this.logging) {
1479
+ this.logger.warn(`Failed to setup manifest watcher: ${error.message}`);
1480
+ }
1481
+ }
1482
+ }
1483
+ /**
1484
+ * Reload UI manifest and update resource registrations
1485
+ */
1486
+ async reloadUIManifest() {
1487
+ try {
1488
+ const manifestPath = import_path.default.join(process.cwd(), "dist", "ui-manifest.json");
1489
+ if (!import_fs.default.existsSync(manifestPath)) {
1490
+ const uiResourceUris = Array.from(this.resources.keys()).filter((uri) => uri.startsWith("ui://"));
1491
+ for (const uri of uiResourceUris) {
1492
+ this.resources.delete(uri);
1493
+ if (this.logging) {
1494
+ this.logger.debug(`Removed UI resource: ${uri}`);
1495
+ }
1496
+ }
1497
+ return;
1498
+ }
1499
+ const manifest = JSON.parse(import_fs.default.readFileSync(manifestPath, "utf-8"));
1500
+ const currentUIUris = new Set(Object.keys(manifest));
1501
+ const registeredUIUris = Array.from(this.resources.keys()).filter((uri) => uri.startsWith("ui://"));
1502
+ for (const uri of registeredUIUris) {
1503
+ if (!currentUIUris.has(uri)) {
1504
+ this.resources.delete(uri);
1505
+ if (this.logging) {
1506
+ this.logger.debug(`Removed UI resource: ${uri}`);
1507
+ }
1508
+ }
1509
+ }
1510
+ for (const [uri, entry] of Object.entries(manifest)) {
1511
+ const isString = typeof entry === "string";
1512
+ const htmlPath = isString ? entry : entry.htmlPath;
1513
+ const isGPTApp = !isString && entry.isGPTApp;
1514
+ const gptMeta = !isString ? entry.gptMeta : void 0;
1515
+ if (!import_fs.default.existsSync(htmlPath)) {
1516
+ if (this.logging) {
1517
+ this.logger.warn(`UI HTML file not found: ${htmlPath}`);
1518
+ }
1519
+ continue;
1520
+ }
1521
+ const wasRegistered = this.resources.has(uri);
1522
+ const mimeType = isGPTApp ? "text/html+skybridge" : "text/html;profile=mcp-app";
1523
+ const _meta = {};
1524
+ if (isGPTApp) {
1525
+ _meta["openai/outputTemplate"] = uri;
1526
+ if (gptMeta) Object.assign(_meta, gptMeta);
1527
+ if (_meta["openai/widgetPrefersBorder"] === void 0) _meta["openai/widgetPrefersBorder"] = true;
1528
+ }
1529
+ this.resources.set(uri, {
1530
+ uri,
1531
+ name: uri.replace("ui://", "").replace(/\//g, "-"),
1532
+ description: `Auto-generated UI resource from pre-built HTML`,
1533
+ mimeType,
1534
+ inputSchema: void 0,
1535
+ method: /* @__PURE__ */ __name(async () => {
1536
+ if (import_fs.default.existsSync(htmlPath)) {
1537
+ const html = import_fs.default.readFileSync(htmlPath, "utf-8");
1538
+ return {
1539
+ text: html,
1540
+ _meta: Object.keys(_meta).length > 0 ? _meta : void 0
1541
+ };
1542
+ }
1543
+ throw new Error(`UI HTML file not found: ${htmlPath}`);
1544
+ }, "method"),
1545
+ instance: null,
1546
+ propertyKey: "getUI"
1547
+ });
1548
+ if (this.logging) {
1549
+ const action = wasRegistered ? "Updated" : "Registered";
1550
+ this.logger.debug(`${action} UI resource: ${uri}`);
1551
+ }
1552
+ }
1553
+ } catch (error) {
1554
+ if (this.logging) {
1555
+ this.logger.warn(`Failed to reload UI manifest: ${error.message}`);
1556
+ }
1557
+ }
1558
+ }
1559
+ /**
1423
1560
  * Load UI manifest and auto-register resources for pre-built @UIApp components.
1424
1561
  * The manifest is generated by `leanmcp dev` or `leanmcp start` commands.
1425
1562
  */
@@ -1430,7 +1567,11 @@ var init_index = __esm({
1430
1567
  return;
1431
1568
  }
1432
1569
  const manifest = JSON.parse(import_fs.default.readFileSync(manifestPath, "utf-8"));
1433
- for (const [uri, htmlPath] of Object.entries(manifest)) {
1570
+ for (const [uri, entry] of Object.entries(manifest)) {
1571
+ const isString = typeof entry === "string";
1572
+ const htmlPath = isString ? entry : entry.htmlPath;
1573
+ const isGPTApp = !isString && entry.isGPTApp;
1574
+ const gptMeta = !isString ? entry.gptMeta : void 0;
1434
1575
  if (this.resources.has(uri)) {
1435
1576
  if (this.logging) {
1436
1577
  this.logger.debug(`Skipping UI resource ${uri} - already registered`);
@@ -1444,14 +1585,22 @@ var init_index = __esm({
1444
1585
  continue;
1445
1586
  }
1446
1587
  const html = import_fs.default.readFileSync(htmlPath, "utf-8");
1588
+ const mimeType = isGPTApp ? "text/html+skybridge" : "text/html;profile=mcp-app";
1589
+ const _meta = {};
1590
+ if (isGPTApp) {
1591
+ _meta["openai/outputTemplate"] = uri;
1592
+ if (gptMeta) Object.assign(_meta, gptMeta);
1593
+ if (_meta["openai/widgetPrefersBorder"] === void 0) _meta["openai/widgetPrefersBorder"] = true;
1594
+ }
1447
1595
  this.resources.set(uri, {
1448
1596
  uri,
1449
1597
  name: uri.replace("ui://", "").replace(/\//g, "-"),
1450
1598
  description: `Auto-generated UI resource from pre-built HTML`,
1451
- mimeType: "text/html;profile=mcp-app",
1599
+ mimeType,
1452
1600
  inputSchema: void 0,
1453
1601
  method: /* @__PURE__ */ __name(async () => ({
1454
- text: html
1602
+ text: html,
1603
+ _meta: Object.keys(_meta).length > 0 ? _meta : void 0
1455
1604
  }), "method"),
1456
1605
  instance: null,
1457
1606
  propertyKey: "getUI"
@@ -1474,6 +1623,15 @@ var init_index = __esm({
1474
1623
  this.server.waitForInit = () => this.waitForInit();
1475
1624
  return this.server;
1476
1625
  }
1626
+ /**
1627
+ * Cleanup resources (call on server shutdown)
1628
+ */
1629
+ async cleanup() {
1630
+ if (this.manifestWatcher) {
1631
+ await this.manifestWatcher.close();
1632
+ this.manifestWatcher = null;
1633
+ }
1634
+ }
1477
1635
  };
1478
1636
  MCPServerRuntime = class {
1479
1637
  static {
package/dist/index.mjs CHANGED
@@ -851,20 +851,36 @@ async function createHTTPServer(serverInput, options) {
851
851
  logger.error(`Server error: ${error.message}`);
852
852
  reject(error);
853
853
  });
854
- const cleanup = /* @__PURE__ */ __name(() => {
854
+ let isShuttingDown = false;
855
+ const cleanup = /* @__PURE__ */ __name(async () => {
856
+ if (isShuttingDown) return;
857
+ isShuttingDown = true;
855
858
  logger.info("\nShutting down server...");
856
- Object.values(transports).forEach((t) => t.close?.());
857
- activeListener?.close(() => {
858
- logger.info("Server closed");
859
- process.exit(0);
860
- });
861
- setTimeout(() => {
862
- logger.warn("Forcing shutdown...");
863
- process.exit(1);
864
- }, 5e3);
859
+ for (const transport of Object.values(transports)) {
860
+ try {
861
+ transport.close?.();
862
+ } catch (e) {
863
+ }
864
+ }
865
+ if (activeListener) {
866
+ await new Promise((resolveClose) => {
867
+ activeListener.close((err) => {
868
+ if (err) {
869
+ logger.warn(`Error closing server: ${err.message}`);
870
+ } else {
871
+ logger.info("Server closed");
872
+ }
873
+ resolveClose();
874
+ });
875
+ });
876
+ }
865
877
  }, "cleanup");
866
- process.on("SIGINT", cleanup);
867
- process.on("SIGTERM", cleanup);
878
+ const handleShutdown = /* @__PURE__ */ __name(() => {
879
+ cleanup().finally(() => {
880
+ });
881
+ }, "handleShutdown");
882
+ process.once("SIGINT", handleShutdown);
883
+ process.once("SIGTERM", handleShutdown);
868
884
  } catch (error) {
869
885
  reject(error);
870
886
  }
@@ -887,6 +903,7 @@ var MCPServer = class {
887
903
  options;
888
904
  initPromise;
889
905
  autoDiscovered = false;
906
+ manifestWatcher = null;
890
907
  constructor(options) {
891
908
  this.options = options;
892
909
  this.logging = options.logging || false;
@@ -920,6 +937,7 @@ var MCPServer = class {
920
937
  await this.autoDiscoverServices(options.mcpDir, options.serviceFactories);
921
938
  }
922
939
  await this.loadUIManifest();
940
+ this.watchUIManifest();
923
941
  }
924
942
  /**
925
943
  * Wait for initialization to complete
@@ -1317,6 +1335,125 @@ var MCPServer = class {
1317
1335
  }
1318
1336
  }
1319
1337
  /**
1338
+ * Watch UI manifest for changes and reload resources dynamically
1339
+ */
1340
+ watchUIManifest() {
1341
+ try {
1342
+ const manifestPath = path.join(process.cwd(), "dist", "ui-manifest.json");
1343
+ if (!fs.existsSync(manifestPath)) {
1344
+ return;
1345
+ }
1346
+ if (this.logging) {
1347
+ this.logger.debug(`Watching UI manifest: ${manifestPath}`);
1348
+ }
1349
+ import("chokidar").then(({ default: chokidar }) => {
1350
+ this.manifestWatcher = chokidar.watch(manifestPath, {
1351
+ ignoreInitial: true,
1352
+ persistent: true,
1353
+ awaitWriteFinish: {
1354
+ stabilityThreshold: 100,
1355
+ pollInterval: 50
1356
+ }
1357
+ });
1358
+ this.manifestWatcher.on("change", async () => {
1359
+ if (this.logging) {
1360
+ this.logger.debug("UI manifest changed, reloading resources");
1361
+ }
1362
+ await this.reloadUIManifest();
1363
+ });
1364
+ this.manifestWatcher.on("error", (error) => {
1365
+ if (this.logging) {
1366
+ this.logger.warn(`Manifest watcher error: ${error.message}`);
1367
+ }
1368
+ });
1369
+ }).catch((error) => {
1370
+ if (this.logging) {
1371
+ this.logger.warn(`Failed to initialize manifest watcher: ${error.message}`);
1372
+ }
1373
+ });
1374
+ } catch (error) {
1375
+ if (this.logging) {
1376
+ this.logger.warn(`Failed to setup manifest watcher: ${error.message}`);
1377
+ }
1378
+ }
1379
+ }
1380
+ /**
1381
+ * Reload UI manifest and update resource registrations
1382
+ */
1383
+ async reloadUIManifest() {
1384
+ try {
1385
+ const manifestPath = path.join(process.cwd(), "dist", "ui-manifest.json");
1386
+ if (!fs.existsSync(manifestPath)) {
1387
+ const uiResourceUris = Array.from(this.resources.keys()).filter((uri) => uri.startsWith("ui://"));
1388
+ for (const uri of uiResourceUris) {
1389
+ this.resources.delete(uri);
1390
+ if (this.logging) {
1391
+ this.logger.debug(`Removed UI resource: ${uri}`);
1392
+ }
1393
+ }
1394
+ return;
1395
+ }
1396
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
1397
+ const currentUIUris = new Set(Object.keys(manifest));
1398
+ const registeredUIUris = Array.from(this.resources.keys()).filter((uri) => uri.startsWith("ui://"));
1399
+ for (const uri of registeredUIUris) {
1400
+ if (!currentUIUris.has(uri)) {
1401
+ this.resources.delete(uri);
1402
+ if (this.logging) {
1403
+ this.logger.debug(`Removed UI resource: ${uri}`);
1404
+ }
1405
+ }
1406
+ }
1407
+ for (const [uri, entry] of Object.entries(manifest)) {
1408
+ const isString = typeof entry === "string";
1409
+ const htmlPath = isString ? entry : entry.htmlPath;
1410
+ const isGPTApp = !isString && entry.isGPTApp;
1411
+ const gptMeta = !isString ? entry.gptMeta : void 0;
1412
+ if (!fs.existsSync(htmlPath)) {
1413
+ if (this.logging) {
1414
+ this.logger.warn(`UI HTML file not found: ${htmlPath}`);
1415
+ }
1416
+ continue;
1417
+ }
1418
+ const wasRegistered = this.resources.has(uri);
1419
+ const mimeType = isGPTApp ? "text/html+skybridge" : "text/html;profile=mcp-app";
1420
+ const _meta = {};
1421
+ if (isGPTApp) {
1422
+ _meta["openai/outputTemplate"] = uri;
1423
+ if (gptMeta) Object.assign(_meta, gptMeta);
1424
+ if (_meta["openai/widgetPrefersBorder"] === void 0) _meta["openai/widgetPrefersBorder"] = true;
1425
+ }
1426
+ this.resources.set(uri, {
1427
+ uri,
1428
+ name: uri.replace("ui://", "").replace(/\//g, "-"),
1429
+ description: `Auto-generated UI resource from pre-built HTML`,
1430
+ mimeType,
1431
+ inputSchema: void 0,
1432
+ method: /* @__PURE__ */ __name(async () => {
1433
+ if (fs.existsSync(htmlPath)) {
1434
+ const html = fs.readFileSync(htmlPath, "utf-8");
1435
+ return {
1436
+ text: html,
1437
+ _meta: Object.keys(_meta).length > 0 ? _meta : void 0
1438
+ };
1439
+ }
1440
+ throw new Error(`UI HTML file not found: ${htmlPath}`);
1441
+ }, "method"),
1442
+ instance: null,
1443
+ propertyKey: "getUI"
1444
+ });
1445
+ if (this.logging) {
1446
+ const action = wasRegistered ? "Updated" : "Registered";
1447
+ this.logger.debug(`${action} UI resource: ${uri}`);
1448
+ }
1449
+ }
1450
+ } catch (error) {
1451
+ if (this.logging) {
1452
+ this.logger.warn(`Failed to reload UI manifest: ${error.message}`);
1453
+ }
1454
+ }
1455
+ }
1456
+ /**
1320
1457
  * Load UI manifest and auto-register resources for pre-built @UIApp components.
1321
1458
  * The manifest is generated by `leanmcp dev` or `leanmcp start` commands.
1322
1459
  */
@@ -1327,7 +1464,11 @@ var MCPServer = class {
1327
1464
  return;
1328
1465
  }
1329
1466
  const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
1330
- for (const [uri, htmlPath] of Object.entries(manifest)) {
1467
+ for (const [uri, entry] of Object.entries(manifest)) {
1468
+ const isString = typeof entry === "string";
1469
+ const htmlPath = isString ? entry : entry.htmlPath;
1470
+ const isGPTApp = !isString && entry.isGPTApp;
1471
+ const gptMeta = !isString ? entry.gptMeta : void 0;
1331
1472
  if (this.resources.has(uri)) {
1332
1473
  if (this.logging) {
1333
1474
  this.logger.debug(`Skipping UI resource ${uri} - already registered`);
@@ -1341,14 +1482,22 @@ var MCPServer = class {
1341
1482
  continue;
1342
1483
  }
1343
1484
  const html = fs.readFileSync(htmlPath, "utf-8");
1485
+ const mimeType = isGPTApp ? "text/html+skybridge" : "text/html;profile=mcp-app";
1486
+ const _meta = {};
1487
+ if (isGPTApp) {
1488
+ _meta["openai/outputTemplate"] = uri;
1489
+ if (gptMeta) Object.assign(_meta, gptMeta);
1490
+ if (_meta["openai/widgetPrefersBorder"] === void 0) _meta["openai/widgetPrefersBorder"] = true;
1491
+ }
1344
1492
  this.resources.set(uri, {
1345
1493
  uri,
1346
1494
  name: uri.replace("ui://", "").replace(/\//g, "-"),
1347
1495
  description: `Auto-generated UI resource from pre-built HTML`,
1348
- mimeType: "text/html;profile=mcp-app",
1496
+ mimeType,
1349
1497
  inputSchema: void 0,
1350
1498
  method: /* @__PURE__ */ __name(async () => ({
1351
- text: html
1499
+ text: html,
1500
+ _meta: Object.keys(_meta).length > 0 ? _meta : void 0
1352
1501
  }), "method"),
1353
1502
  instance: null,
1354
1503
  propertyKey: "getUI"
@@ -1371,6 +1520,15 @@ var MCPServer = class {
1371
1520
  this.server.waitForInit = () => this.waitForInit();
1372
1521
  return this.server;
1373
1522
  }
1523
+ /**
1524
+ * Cleanup resources (call on server shutdown)
1525
+ */
1526
+ async cleanup() {
1527
+ if (this.manifestWatcher) {
1528
+ await this.manifestWatcher.close();
1529
+ this.manifestWatcher = null;
1530
+ }
1531
+ }
1374
1532
  };
1375
1533
  var MCPServerRuntime = class {
1376
1534
  static {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leanmcp/core",
3
- "version": "0.3.8",
3
+ "version": "0.3.10",
4
4
  "description": "Core library implementing decorators, reflection, and MCP runtime server",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -26,6 +26,7 @@
26
26
  "dependencies": {
27
27
  "@modelcontextprotocol/sdk": "^1.0.0",
28
28
  "ajv": "^8.12.0",
29
+ "chokidar": "^4.0.0",
29
30
  "dotenv": "^16.3.1",
30
31
  "reflect-metadata": "^0.2.1"
31
32
  },