@leanmcp/core 0.3.9 → 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.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
  }
@@ -1491,7 +1507,11 @@ var init_index = __esm({
1491
1507
  }
1492
1508
  }
1493
1509
  }
1494
- for (const [uri, htmlPath] of Object.entries(manifest)) {
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;
1495
1515
  if (!import_fs.default.existsSync(htmlPath)) {
1496
1516
  if (this.logging) {
1497
1517
  this.logger.warn(`UI HTML file not found: ${htmlPath}`);
@@ -1499,17 +1519,25 @@ var init_index = __esm({
1499
1519
  continue;
1500
1520
  }
1501
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
+ }
1502
1529
  this.resources.set(uri, {
1503
1530
  uri,
1504
1531
  name: uri.replace("ui://", "").replace(/\//g, "-"),
1505
1532
  description: `Auto-generated UI resource from pre-built HTML`,
1506
- mimeType: "text/html;profile=mcp-app",
1533
+ mimeType,
1507
1534
  inputSchema: void 0,
1508
1535
  method: /* @__PURE__ */ __name(async () => {
1509
1536
  if (import_fs.default.existsSync(htmlPath)) {
1510
1537
  const html = import_fs.default.readFileSync(htmlPath, "utf-8");
1511
1538
  return {
1512
- text: html
1539
+ text: html,
1540
+ _meta: Object.keys(_meta).length > 0 ? _meta : void 0
1513
1541
  };
1514
1542
  }
1515
1543
  throw new Error(`UI HTML file not found: ${htmlPath}`);
@@ -1539,7 +1567,11 @@ var init_index = __esm({
1539
1567
  return;
1540
1568
  }
1541
1569
  const manifest = JSON.parse(import_fs.default.readFileSync(manifestPath, "utf-8"));
1542
- 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;
1543
1575
  if (this.resources.has(uri)) {
1544
1576
  if (this.logging) {
1545
1577
  this.logger.debug(`Skipping UI resource ${uri} - already registered`);
@@ -1553,14 +1585,22 @@ var init_index = __esm({
1553
1585
  continue;
1554
1586
  }
1555
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
+ }
1556
1595
  this.resources.set(uri, {
1557
1596
  uri,
1558
1597
  name: uri.replace("ui://", "").replace(/\//g, "-"),
1559
1598
  description: `Auto-generated UI resource from pre-built HTML`,
1560
- mimeType: "text/html;profile=mcp-app",
1599
+ mimeType,
1561
1600
  inputSchema: void 0,
1562
1601
  method: /* @__PURE__ */ __name(async () => ({
1563
- text: html
1602
+ text: html,
1603
+ _meta: Object.keys(_meta).length > 0 ? _meta : void 0
1564
1604
  }), "method"),
1565
1605
  instance: null,
1566
1606
  propertyKey: "getUI"
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
  }
@@ -1388,7 +1404,11 @@ var MCPServer = class {
1388
1404
  }
1389
1405
  }
1390
1406
  }
1391
- for (const [uri, htmlPath] of Object.entries(manifest)) {
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;
1392
1412
  if (!fs.existsSync(htmlPath)) {
1393
1413
  if (this.logging) {
1394
1414
  this.logger.warn(`UI HTML file not found: ${htmlPath}`);
@@ -1396,17 +1416,25 @@ var MCPServer = class {
1396
1416
  continue;
1397
1417
  }
1398
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
+ }
1399
1426
  this.resources.set(uri, {
1400
1427
  uri,
1401
1428
  name: uri.replace("ui://", "").replace(/\//g, "-"),
1402
1429
  description: `Auto-generated UI resource from pre-built HTML`,
1403
- mimeType: "text/html;profile=mcp-app",
1430
+ mimeType,
1404
1431
  inputSchema: void 0,
1405
1432
  method: /* @__PURE__ */ __name(async () => {
1406
1433
  if (fs.existsSync(htmlPath)) {
1407
1434
  const html = fs.readFileSync(htmlPath, "utf-8");
1408
1435
  return {
1409
- text: html
1436
+ text: html,
1437
+ _meta: Object.keys(_meta).length > 0 ? _meta : void 0
1410
1438
  };
1411
1439
  }
1412
1440
  throw new Error(`UI HTML file not found: ${htmlPath}`);
@@ -1436,7 +1464,11 @@ var MCPServer = class {
1436
1464
  return;
1437
1465
  }
1438
1466
  const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
1439
- 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;
1440
1472
  if (this.resources.has(uri)) {
1441
1473
  if (this.logging) {
1442
1474
  this.logger.debug(`Skipping UI resource ${uri} - already registered`);
@@ -1450,14 +1482,22 @@ var MCPServer = class {
1450
1482
  continue;
1451
1483
  }
1452
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
+ }
1453
1492
  this.resources.set(uri, {
1454
1493
  uri,
1455
1494
  name: uri.replace("ui://", "").replace(/\//g, "-"),
1456
1495
  description: `Auto-generated UI resource from pre-built HTML`,
1457
- mimeType: "text/html;profile=mcp-app",
1496
+ mimeType,
1458
1497
  inputSchema: void 0,
1459
1498
  method: /* @__PURE__ */ __name(async () => ({
1460
- text: html
1499
+ text: html,
1500
+ _meta: Object.keys(_meta).length > 0 ? _meta : void 0
1461
1501
  }), "method"),
1462
1502
  instance: null,
1463
1503
  propertyKey: "getUI"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leanmcp/core",
3
- "version": "0.3.9",
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",