@leanmcp/core 0.3.8 → 0.3.9

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
@@ -990,6 +990,7 @@ var init_index = __esm({
990
990
  options;
991
991
  initPromise;
992
992
  autoDiscovered = false;
993
+ manifestWatcher = null;
993
994
  constructor(options) {
994
995
  this.options = options;
995
996
  this.logging = options.logging || false;
@@ -1023,6 +1024,7 @@ var init_index = __esm({
1023
1024
  await this.autoDiscoverServices(options.mcpDir, options.serviceFactories);
1024
1025
  }
1025
1026
  await this.loadUIManifest();
1027
+ this.watchUIManifest();
1026
1028
  }
1027
1029
  /**
1028
1030
  * Wait for initialization to complete
@@ -1420,6 +1422,113 @@ var init_index = __esm({
1420
1422
  }
1421
1423
  }
1422
1424
  /**
1425
+ * Watch UI manifest for changes and reload resources dynamically
1426
+ */
1427
+ watchUIManifest() {
1428
+ try {
1429
+ const manifestPath = import_path.default.join(process.cwd(), "dist", "ui-manifest.json");
1430
+ if (!import_fs.default.existsSync(manifestPath)) {
1431
+ return;
1432
+ }
1433
+ if (this.logging) {
1434
+ this.logger.debug(`Watching UI manifest: ${manifestPath}`);
1435
+ }
1436
+ import("chokidar").then(({ default: chokidar }) => {
1437
+ this.manifestWatcher = chokidar.watch(manifestPath, {
1438
+ ignoreInitial: true,
1439
+ persistent: true,
1440
+ awaitWriteFinish: {
1441
+ stabilityThreshold: 100,
1442
+ pollInterval: 50
1443
+ }
1444
+ });
1445
+ this.manifestWatcher.on("change", async () => {
1446
+ if (this.logging) {
1447
+ this.logger.debug("UI manifest changed, reloading resources");
1448
+ }
1449
+ await this.reloadUIManifest();
1450
+ });
1451
+ this.manifestWatcher.on("error", (error) => {
1452
+ if (this.logging) {
1453
+ this.logger.warn(`Manifest watcher error: ${error.message}`);
1454
+ }
1455
+ });
1456
+ }).catch((error) => {
1457
+ if (this.logging) {
1458
+ this.logger.warn(`Failed to initialize manifest watcher: ${error.message}`);
1459
+ }
1460
+ });
1461
+ } catch (error) {
1462
+ if (this.logging) {
1463
+ this.logger.warn(`Failed to setup manifest watcher: ${error.message}`);
1464
+ }
1465
+ }
1466
+ }
1467
+ /**
1468
+ * Reload UI manifest and update resource registrations
1469
+ */
1470
+ async reloadUIManifest() {
1471
+ try {
1472
+ const manifestPath = import_path.default.join(process.cwd(), "dist", "ui-manifest.json");
1473
+ if (!import_fs.default.existsSync(manifestPath)) {
1474
+ const uiResourceUris = Array.from(this.resources.keys()).filter((uri) => uri.startsWith("ui://"));
1475
+ for (const uri of uiResourceUris) {
1476
+ this.resources.delete(uri);
1477
+ if (this.logging) {
1478
+ this.logger.debug(`Removed UI resource: ${uri}`);
1479
+ }
1480
+ }
1481
+ return;
1482
+ }
1483
+ const manifest = JSON.parse(import_fs.default.readFileSync(manifestPath, "utf-8"));
1484
+ const currentUIUris = new Set(Object.keys(manifest));
1485
+ const registeredUIUris = Array.from(this.resources.keys()).filter((uri) => uri.startsWith("ui://"));
1486
+ for (const uri of registeredUIUris) {
1487
+ if (!currentUIUris.has(uri)) {
1488
+ this.resources.delete(uri);
1489
+ if (this.logging) {
1490
+ this.logger.debug(`Removed UI resource: ${uri}`);
1491
+ }
1492
+ }
1493
+ }
1494
+ for (const [uri, htmlPath] of Object.entries(manifest)) {
1495
+ if (!import_fs.default.existsSync(htmlPath)) {
1496
+ if (this.logging) {
1497
+ this.logger.warn(`UI HTML file not found: ${htmlPath}`);
1498
+ }
1499
+ continue;
1500
+ }
1501
+ const wasRegistered = this.resources.has(uri);
1502
+ this.resources.set(uri, {
1503
+ uri,
1504
+ name: uri.replace("ui://", "").replace(/\//g, "-"),
1505
+ description: `Auto-generated UI resource from pre-built HTML`,
1506
+ mimeType: "text/html;profile=mcp-app",
1507
+ inputSchema: void 0,
1508
+ method: /* @__PURE__ */ __name(async () => {
1509
+ if (import_fs.default.existsSync(htmlPath)) {
1510
+ const html = import_fs.default.readFileSync(htmlPath, "utf-8");
1511
+ return {
1512
+ text: html
1513
+ };
1514
+ }
1515
+ throw new Error(`UI HTML file not found: ${htmlPath}`);
1516
+ }, "method"),
1517
+ instance: null,
1518
+ propertyKey: "getUI"
1519
+ });
1520
+ if (this.logging) {
1521
+ const action = wasRegistered ? "Updated" : "Registered";
1522
+ this.logger.debug(`${action} UI resource: ${uri}`);
1523
+ }
1524
+ }
1525
+ } catch (error) {
1526
+ if (this.logging) {
1527
+ this.logger.warn(`Failed to reload UI manifest: ${error.message}`);
1528
+ }
1529
+ }
1530
+ }
1531
+ /**
1423
1532
  * Load UI manifest and auto-register resources for pre-built @UIApp components.
1424
1533
  * The manifest is generated by `leanmcp dev` or `leanmcp start` commands.
1425
1534
  */
@@ -1474,6 +1583,15 @@ var init_index = __esm({
1474
1583
  this.server.waitForInit = () => this.waitForInit();
1475
1584
  return this.server;
1476
1585
  }
1586
+ /**
1587
+ * Cleanup resources (call on server shutdown)
1588
+ */
1589
+ async cleanup() {
1590
+ if (this.manifestWatcher) {
1591
+ await this.manifestWatcher.close();
1592
+ this.manifestWatcher = null;
1593
+ }
1594
+ }
1477
1595
  };
1478
1596
  MCPServerRuntime = class {
1479
1597
  static {
package/dist/index.mjs CHANGED
@@ -887,6 +887,7 @@ var MCPServer = class {
887
887
  options;
888
888
  initPromise;
889
889
  autoDiscovered = false;
890
+ manifestWatcher = null;
890
891
  constructor(options) {
891
892
  this.options = options;
892
893
  this.logging = options.logging || false;
@@ -920,6 +921,7 @@ var MCPServer = class {
920
921
  await this.autoDiscoverServices(options.mcpDir, options.serviceFactories);
921
922
  }
922
923
  await this.loadUIManifest();
924
+ this.watchUIManifest();
923
925
  }
924
926
  /**
925
927
  * Wait for initialization to complete
@@ -1317,6 +1319,113 @@ var MCPServer = class {
1317
1319
  }
1318
1320
  }
1319
1321
  /**
1322
+ * Watch UI manifest for changes and reload resources dynamically
1323
+ */
1324
+ watchUIManifest() {
1325
+ try {
1326
+ const manifestPath = path.join(process.cwd(), "dist", "ui-manifest.json");
1327
+ if (!fs.existsSync(manifestPath)) {
1328
+ return;
1329
+ }
1330
+ if (this.logging) {
1331
+ this.logger.debug(`Watching UI manifest: ${manifestPath}`);
1332
+ }
1333
+ import("chokidar").then(({ default: chokidar }) => {
1334
+ this.manifestWatcher = chokidar.watch(manifestPath, {
1335
+ ignoreInitial: true,
1336
+ persistent: true,
1337
+ awaitWriteFinish: {
1338
+ stabilityThreshold: 100,
1339
+ pollInterval: 50
1340
+ }
1341
+ });
1342
+ this.manifestWatcher.on("change", async () => {
1343
+ if (this.logging) {
1344
+ this.logger.debug("UI manifest changed, reloading resources");
1345
+ }
1346
+ await this.reloadUIManifest();
1347
+ });
1348
+ this.manifestWatcher.on("error", (error) => {
1349
+ if (this.logging) {
1350
+ this.logger.warn(`Manifest watcher error: ${error.message}`);
1351
+ }
1352
+ });
1353
+ }).catch((error) => {
1354
+ if (this.logging) {
1355
+ this.logger.warn(`Failed to initialize manifest watcher: ${error.message}`);
1356
+ }
1357
+ });
1358
+ } catch (error) {
1359
+ if (this.logging) {
1360
+ this.logger.warn(`Failed to setup manifest watcher: ${error.message}`);
1361
+ }
1362
+ }
1363
+ }
1364
+ /**
1365
+ * Reload UI manifest and update resource registrations
1366
+ */
1367
+ async reloadUIManifest() {
1368
+ try {
1369
+ const manifestPath = path.join(process.cwd(), "dist", "ui-manifest.json");
1370
+ if (!fs.existsSync(manifestPath)) {
1371
+ const uiResourceUris = Array.from(this.resources.keys()).filter((uri) => uri.startsWith("ui://"));
1372
+ for (const uri of uiResourceUris) {
1373
+ this.resources.delete(uri);
1374
+ if (this.logging) {
1375
+ this.logger.debug(`Removed UI resource: ${uri}`);
1376
+ }
1377
+ }
1378
+ return;
1379
+ }
1380
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
1381
+ const currentUIUris = new Set(Object.keys(manifest));
1382
+ const registeredUIUris = Array.from(this.resources.keys()).filter((uri) => uri.startsWith("ui://"));
1383
+ for (const uri of registeredUIUris) {
1384
+ if (!currentUIUris.has(uri)) {
1385
+ this.resources.delete(uri);
1386
+ if (this.logging) {
1387
+ this.logger.debug(`Removed UI resource: ${uri}`);
1388
+ }
1389
+ }
1390
+ }
1391
+ for (const [uri, htmlPath] of Object.entries(manifest)) {
1392
+ if (!fs.existsSync(htmlPath)) {
1393
+ if (this.logging) {
1394
+ this.logger.warn(`UI HTML file not found: ${htmlPath}`);
1395
+ }
1396
+ continue;
1397
+ }
1398
+ const wasRegistered = this.resources.has(uri);
1399
+ this.resources.set(uri, {
1400
+ uri,
1401
+ name: uri.replace("ui://", "").replace(/\//g, "-"),
1402
+ description: `Auto-generated UI resource from pre-built HTML`,
1403
+ mimeType: "text/html;profile=mcp-app",
1404
+ inputSchema: void 0,
1405
+ method: /* @__PURE__ */ __name(async () => {
1406
+ if (fs.existsSync(htmlPath)) {
1407
+ const html = fs.readFileSync(htmlPath, "utf-8");
1408
+ return {
1409
+ text: html
1410
+ };
1411
+ }
1412
+ throw new Error(`UI HTML file not found: ${htmlPath}`);
1413
+ }, "method"),
1414
+ instance: null,
1415
+ propertyKey: "getUI"
1416
+ });
1417
+ if (this.logging) {
1418
+ const action = wasRegistered ? "Updated" : "Registered";
1419
+ this.logger.debug(`${action} UI resource: ${uri}`);
1420
+ }
1421
+ }
1422
+ } catch (error) {
1423
+ if (this.logging) {
1424
+ this.logger.warn(`Failed to reload UI manifest: ${error.message}`);
1425
+ }
1426
+ }
1427
+ }
1428
+ /**
1320
1429
  * Load UI manifest and auto-register resources for pre-built @UIApp components.
1321
1430
  * The manifest is generated by `leanmcp dev` or `leanmcp start` commands.
1322
1431
  */
@@ -1371,6 +1480,15 @@ var MCPServer = class {
1371
1480
  this.server.waitForInit = () => this.waitForInit();
1372
1481
  return this.server;
1373
1482
  }
1483
+ /**
1484
+ * Cleanup resources (call on server shutdown)
1485
+ */
1486
+ async cleanup() {
1487
+ if (this.manifestWatcher) {
1488
+ await this.manifestWatcher.close();
1489
+ this.manifestWatcher = null;
1490
+ }
1491
+ }
1374
1492
  };
1375
1493
  var MCPServerRuntime = class {
1376
1494
  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.9",
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
  },