@leanmcp/core 0.3.1 → 0.3.2

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
@@ -434,9 +434,18 @@ declare class MCPServer {
434
434
  method: string;
435
435
  params?: {
436
436
  [x: string]: unknown;
437
+ task?: {
438
+ [x: string]: unknown;
439
+ ttl?: number | null | undefined;
440
+ pollInterval?: number | undefined;
441
+ } | undefined;
437
442
  _meta?: {
438
443
  [x: string]: unknown;
439
444
  progressToken?: string | number | undefined;
445
+ "io.modelcontextprotocol/related-task"?: {
446
+ [x: string]: unknown;
447
+ taskId: string;
448
+ } | undefined;
440
449
  } | undefined;
441
450
  } | undefined;
442
451
  }, {
@@ -445,12 +454,20 @@ declare class MCPServer {
445
454
  [x: string]: unknown;
446
455
  _meta?: {
447
456
  [x: string]: unknown;
457
+ "io.modelcontextprotocol/related-task"?: {
458
+ [x: string]: unknown;
459
+ taskId: string;
460
+ } | undefined;
448
461
  } | undefined;
449
462
  } | undefined;
450
463
  }, {
451
464
  [x: string]: unknown;
452
465
  _meta?: {
453
466
  [x: string]: unknown;
467
+ "io.modelcontextprotocol/related-task"?: {
468
+ [x: string]: unknown;
469
+ taskId: string;
470
+ } | undefined;
454
471
  } | undefined;
455
472
  }>;
456
473
  }
@@ -469,9 +486,18 @@ declare class MCPServerRuntime {
469
486
  method: string;
470
487
  params?: {
471
488
  [x: string]: unknown;
489
+ task?: {
490
+ [x: string]: unknown;
491
+ ttl?: number | null | undefined;
492
+ pollInterval?: number | undefined;
493
+ } | undefined;
472
494
  _meta?: {
473
495
  [x: string]: unknown;
474
496
  progressToken?: string | number | undefined;
497
+ "io.modelcontextprotocol/related-task"?: {
498
+ [x: string]: unknown;
499
+ taskId: string;
500
+ } | undefined;
475
501
  } | undefined;
476
502
  } | undefined;
477
503
  }, {
@@ -480,12 +506,20 @@ declare class MCPServerRuntime {
480
506
  [x: string]: unknown;
481
507
  _meta?: {
482
508
  [x: string]: unknown;
509
+ "io.modelcontextprotocol/related-task"?: {
510
+ [x: string]: unknown;
511
+ taskId: string;
512
+ } | undefined;
483
513
  } | undefined;
484
514
  } | undefined;
485
515
  }, {
486
516
  [x: string]: unknown;
487
517
  _meta?: {
488
518
  [x: string]: unknown;
519
+ "io.modelcontextprotocol/related-task"?: {
520
+ [x: string]: unknown;
521
+ taskId: string;
522
+ } | undefined;
489
523
  } | undefined;
490
524
  }>;
491
525
  getTools(): RegisteredTool[];
package/dist/index.d.ts CHANGED
@@ -434,9 +434,18 @@ declare class MCPServer {
434
434
  method: string;
435
435
  params?: {
436
436
  [x: string]: unknown;
437
+ task?: {
438
+ [x: string]: unknown;
439
+ ttl?: number | null | undefined;
440
+ pollInterval?: number | undefined;
441
+ } | undefined;
437
442
  _meta?: {
438
443
  [x: string]: unknown;
439
444
  progressToken?: string | number | undefined;
445
+ "io.modelcontextprotocol/related-task"?: {
446
+ [x: string]: unknown;
447
+ taskId: string;
448
+ } | undefined;
440
449
  } | undefined;
441
450
  } | undefined;
442
451
  }, {
@@ -445,12 +454,20 @@ declare class MCPServer {
445
454
  [x: string]: unknown;
446
455
  _meta?: {
447
456
  [x: string]: unknown;
457
+ "io.modelcontextprotocol/related-task"?: {
458
+ [x: string]: unknown;
459
+ taskId: string;
460
+ } | undefined;
448
461
  } | undefined;
449
462
  } | undefined;
450
463
  }, {
451
464
  [x: string]: unknown;
452
465
  _meta?: {
453
466
  [x: string]: unknown;
467
+ "io.modelcontextprotocol/related-task"?: {
468
+ [x: string]: unknown;
469
+ taskId: string;
470
+ } | undefined;
454
471
  } | undefined;
455
472
  }>;
456
473
  }
@@ -469,9 +486,18 @@ declare class MCPServerRuntime {
469
486
  method: string;
470
487
  params?: {
471
488
  [x: string]: unknown;
489
+ task?: {
490
+ [x: string]: unknown;
491
+ ttl?: number | null | undefined;
492
+ pollInterval?: number | undefined;
493
+ } | undefined;
472
494
  _meta?: {
473
495
  [x: string]: unknown;
474
496
  progressToken?: string | number | undefined;
497
+ "io.modelcontextprotocol/related-task"?: {
498
+ [x: string]: unknown;
499
+ taskId: string;
500
+ } | undefined;
475
501
  } | undefined;
476
502
  } | undefined;
477
503
  }, {
@@ -480,12 +506,20 @@ declare class MCPServerRuntime {
480
506
  [x: string]: unknown;
481
507
  _meta?: {
482
508
  [x: string]: unknown;
509
+ "io.modelcontextprotocol/related-task"?: {
510
+ [x: string]: unknown;
511
+ taskId: string;
512
+ } | undefined;
483
513
  } | undefined;
484
514
  } | undefined;
485
515
  }, {
486
516
  [x: string]: unknown;
487
517
  _meta?: {
488
518
  [x: string]: unknown;
519
+ "io.modelcontextprotocol/related-task"?: {
520
+ [x: string]: unknown;
521
+ taskId: string;
522
+ } | undefined;
489
523
  } | undefined;
490
524
  }>;
491
525
  getTools(): RegisteredTool[];
package/dist/index.js CHANGED
@@ -464,14 +464,55 @@ async function createHTTPServer(serverInput, options) {
464
464
  httpOptions.cors ? import("cors").catch(() => null) : Promise.resolve(null)
465
465
  ]);
466
466
  const app = express.default();
467
- const port = httpOptions.port || 3001;
468
- validatePort(port);
467
+ const basePort = httpOptions.port || 3001;
468
+ validatePort(basePort);
469
469
  const transports = {};
470
470
  let mcpServer = null;
471
471
  const logger = httpOptions.logger || new Logger({
472
472
  level: httpOptions.logging ? LogLevel.INFO : LogLevel.NONE,
473
473
  prefix: "HTTP"
474
474
  });
475
+ const logPrimary = /* @__PURE__ */ __name((message) => {
476
+ console.log(message);
477
+ logger.info?.(message);
478
+ }, "logPrimary");
479
+ const warnPrimary = /* @__PURE__ */ __name((message) => {
480
+ console.warn(message);
481
+ logger.warn?.(message);
482
+ }, "warnPrimary");
483
+ const startServerWithPortRetry = /* @__PURE__ */ __name(async () => {
484
+ const maxAttempts = 20;
485
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
486
+ const portToTry = basePort + attempt;
487
+ const listener = await new Promise((resolve, reject) => {
488
+ const server = app.listen(portToTry);
489
+ const onListening = /* @__PURE__ */ __name(() => {
490
+ server.off("error", onError);
491
+ resolve(server);
492
+ }, "onListening");
493
+ const onError = /* @__PURE__ */ __name((error) => {
494
+ server.off("listening", onListening);
495
+ server.close();
496
+ reject(error);
497
+ }, "onError");
498
+ server.once("listening", onListening);
499
+ server.once("error", onError);
500
+ }).catch((error) => {
501
+ if (error?.code === "EADDRINUSE" && attempt < maxAttempts - 1) {
502
+ warnPrimary(`Port ${portToTry} in use, trying ${portToTry + 1}...`);
503
+ return null;
504
+ }
505
+ throw error;
506
+ });
507
+ if (listener) {
508
+ return {
509
+ listener,
510
+ port: portToTry
511
+ };
512
+ }
513
+ }
514
+ throw new Error(`No available port found in range ${basePort}-${basePort + maxAttempts - 1}`);
515
+ }, "startServerWithPortRetry");
475
516
  if (cors && httpOptions.cors) {
476
517
  const corsOptions = typeof httpOptions.cors === "object" ? {
477
518
  origin: httpOptions.cors.origin || false,
@@ -498,7 +539,7 @@ async function createHTTPServer(serverInput, options) {
498
539
  }
499
540
  }
500
541
  app.use(express.json());
501
- logger.info("Starting LeanMCP HTTP Server...");
542
+ logPrimary("Starting LeanMCP HTTP Server...");
502
543
  app.get("/health", (req, res) => {
503
544
  res.json({
504
545
  status: "ok",
@@ -573,16 +614,22 @@ async function createHTTPServer(serverInput, options) {
573
614
  app.post("/mcp", handleMCPRequest);
574
615
  app.delete("/mcp", handleMCPRequest);
575
616
  return new Promise(async (resolve, reject) => {
617
+ let activeListener;
576
618
  try {
577
619
  mcpServer = await serverFactory();
578
620
  if (mcpServer && typeof mcpServer.waitForInit === "function") {
579
621
  await mcpServer.waitForInit();
580
622
  }
581
- const listener = app.listen(port, () => {
582
- logger.info(`Server running on http://localhost:${port}`);
583
- logger.info(`MCP endpoint: http://localhost:${port}/mcp`);
584
- logger.info(`Health check: http://localhost:${port}/health`);
585
- resolve(listener);
623
+ const { listener, port } = await startServerWithPortRetry();
624
+ activeListener = listener;
625
+ process.env.PORT = String(port);
626
+ listener.port = port;
627
+ logPrimary(`Server running on http://localhost:${port}`);
628
+ logPrimary(`MCP endpoint: http://localhost:${port}/mcp`);
629
+ logPrimary(`Health check: http://localhost:${port}/health`);
630
+ resolve({
631
+ listener,
632
+ port
586
633
  });
587
634
  listener.on("error", (error) => {
588
635
  logger.error(`Server error: ${error.message}`);
@@ -591,7 +638,7 @@ async function createHTTPServer(serverInput, options) {
591
638
  const cleanup = /* @__PURE__ */ __name(() => {
592
639
  logger.info("\nShutting down server...");
593
640
  Object.values(transports).forEach((t) => t.close?.());
594
- listener.close(() => {
641
+ activeListener?.close(() => {
595
642
  logger.info("Server closed");
596
643
  process.exit(0);
597
644
  });
@@ -769,24 +816,29 @@ var init_index = __esm({
769
816
  const stack = err.stack;
770
817
  for (let i = 0; i < stack.length; i++) {
771
818
  let fileName = stack[i].getFileName();
772
- if (fileName && !fileName.includes("@leanmcp") && !fileName.includes("leanmcp-sdk\\packages\\core") && !fileName.includes("leanmcp-sdk/packages/core") && (fileName.endsWith(".ts") || fileName.endsWith(".js") || fileName.endsWith(".mjs"))) {
773
- if (fileName.startsWith("file://")) {
774
- try {
775
- const url = new URL(fileName);
776
- fileName = decodeURIComponent(url.pathname);
777
- if (process.platform === "win32" && fileName.startsWith("/")) {
778
- fileName = fileName.substring(1);
779
- }
780
- } catch (e) {
781
- fileName = fileName.replace("file://", "");
782
- if (process.platform === "win32" && fileName.startsWith("/")) {
783
- fileName = fileName.substring(1);
784
- }
819
+ if (!fileName) continue;
820
+ if (fileName.startsWith("file://")) {
821
+ try {
822
+ const url = new URL(fileName);
823
+ fileName = decodeURIComponent(url.pathname);
824
+ if (process.platform === "win32" && fileName.startsWith("/")) {
825
+ fileName = fileName.substring(1);
826
+ }
827
+ } catch (e) {
828
+ fileName = fileName.replace("file://", "");
829
+ if (process.platform === "win32" && fileName.startsWith("/")) {
830
+ fileName = fileName.substring(1);
785
831
  }
786
832
  }
833
+ }
834
+ const normalizedPath = fileName.replace(/\\/g, "/");
835
+ const isLeanMCPCore = normalizedPath.includes("@leanmcp/core") || normalizedPath.includes("leanmcp-sdk/packages/core");
836
+ const isValidExtension = fileName.endsWith(".ts") || fileName.endsWith(".js") || fileName.endsWith(".mjs");
837
+ if (!isLeanMCPCore && isValidExtension) {
787
838
  return fileName;
788
839
  }
789
840
  }
841
+ this.logger.debug("No suitable caller file found in stack trace");
790
842
  return null;
791
843
  } finally {
792
844
  Error.prepareStackTrace = originalPrepareStackTrace;
package/dist/index.mjs CHANGED
@@ -425,14 +425,55 @@ async function createHTTPServer(serverInput, options) {
425
425
  httpOptions.cors ? import("cors").catch(() => null) : Promise.resolve(null)
426
426
  ]);
427
427
  const app = express.default();
428
- const port = httpOptions.port || 3001;
429
- validatePort(port);
428
+ const basePort = httpOptions.port || 3001;
429
+ validatePort(basePort);
430
430
  const transports = {};
431
431
  let mcpServer = null;
432
432
  const logger = httpOptions.logger || new Logger({
433
433
  level: httpOptions.logging ? LogLevel.INFO : LogLevel.NONE,
434
434
  prefix: "HTTP"
435
435
  });
436
+ const logPrimary = /* @__PURE__ */ __name((message) => {
437
+ console.log(message);
438
+ logger.info?.(message);
439
+ }, "logPrimary");
440
+ const warnPrimary = /* @__PURE__ */ __name((message) => {
441
+ console.warn(message);
442
+ logger.warn?.(message);
443
+ }, "warnPrimary");
444
+ const startServerWithPortRetry = /* @__PURE__ */ __name(async () => {
445
+ const maxAttempts = 20;
446
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
447
+ const portToTry = basePort + attempt;
448
+ const listener = await new Promise((resolve, reject) => {
449
+ const server = app.listen(portToTry);
450
+ const onListening = /* @__PURE__ */ __name(() => {
451
+ server.off("error", onError);
452
+ resolve(server);
453
+ }, "onListening");
454
+ const onError = /* @__PURE__ */ __name((error) => {
455
+ server.off("listening", onListening);
456
+ server.close();
457
+ reject(error);
458
+ }, "onError");
459
+ server.once("listening", onListening);
460
+ server.once("error", onError);
461
+ }).catch((error) => {
462
+ if (error?.code === "EADDRINUSE" && attempt < maxAttempts - 1) {
463
+ warnPrimary(`Port ${portToTry} in use, trying ${portToTry + 1}...`);
464
+ return null;
465
+ }
466
+ throw error;
467
+ });
468
+ if (listener) {
469
+ return {
470
+ listener,
471
+ port: portToTry
472
+ };
473
+ }
474
+ }
475
+ throw new Error(`No available port found in range ${basePort}-${basePort + maxAttempts - 1}`);
476
+ }, "startServerWithPortRetry");
436
477
  if (cors && httpOptions.cors) {
437
478
  const corsOptions = typeof httpOptions.cors === "object" ? {
438
479
  origin: httpOptions.cors.origin || false,
@@ -459,7 +500,7 @@ async function createHTTPServer(serverInput, options) {
459
500
  }
460
501
  }
461
502
  app.use(express.json());
462
- logger.info("Starting LeanMCP HTTP Server...");
503
+ logPrimary("Starting LeanMCP HTTP Server...");
463
504
  app.get("/health", (req, res) => {
464
505
  res.json({
465
506
  status: "ok",
@@ -534,16 +575,22 @@ async function createHTTPServer(serverInput, options) {
534
575
  app.post("/mcp", handleMCPRequest);
535
576
  app.delete("/mcp", handleMCPRequest);
536
577
  return new Promise(async (resolve, reject) => {
578
+ let activeListener;
537
579
  try {
538
580
  mcpServer = await serverFactory();
539
581
  if (mcpServer && typeof mcpServer.waitForInit === "function") {
540
582
  await mcpServer.waitForInit();
541
583
  }
542
- const listener = app.listen(port, () => {
543
- logger.info(`Server running on http://localhost:${port}`);
544
- logger.info(`MCP endpoint: http://localhost:${port}/mcp`);
545
- logger.info(`Health check: http://localhost:${port}/health`);
546
- resolve(listener);
584
+ const { listener, port } = await startServerWithPortRetry();
585
+ activeListener = listener;
586
+ process.env.PORT = String(port);
587
+ listener.port = port;
588
+ logPrimary(`Server running on http://localhost:${port}`);
589
+ logPrimary(`MCP endpoint: http://localhost:${port}/mcp`);
590
+ logPrimary(`Health check: http://localhost:${port}/health`);
591
+ resolve({
592
+ listener,
593
+ port
547
594
  });
548
595
  listener.on("error", (error) => {
549
596
  logger.error(`Server error: ${error.message}`);
@@ -552,7 +599,7 @@ async function createHTTPServer(serverInput, options) {
552
599
  const cleanup = /* @__PURE__ */ __name(() => {
553
600
  logger.info("\nShutting down server...");
554
601
  Object.values(transports).forEach((t) => t.close?.());
555
- listener.close(() => {
602
+ activeListener?.close(() => {
556
603
  logger.info("Server closed");
557
604
  process.exit(0);
558
605
  });
@@ -666,24 +713,29 @@ var MCPServer = class {
666
713
  const stack = err.stack;
667
714
  for (let i = 0; i < stack.length; i++) {
668
715
  let fileName = stack[i].getFileName();
669
- if (fileName && !fileName.includes("@leanmcp") && !fileName.includes("leanmcp-sdk\\packages\\core") && !fileName.includes("leanmcp-sdk/packages/core") && (fileName.endsWith(".ts") || fileName.endsWith(".js") || fileName.endsWith(".mjs"))) {
670
- if (fileName.startsWith("file://")) {
671
- try {
672
- const url = new URL(fileName);
673
- fileName = decodeURIComponent(url.pathname);
674
- if (process.platform === "win32" && fileName.startsWith("/")) {
675
- fileName = fileName.substring(1);
676
- }
677
- } catch (e) {
678
- fileName = fileName.replace("file://", "");
679
- if (process.platform === "win32" && fileName.startsWith("/")) {
680
- fileName = fileName.substring(1);
681
- }
716
+ if (!fileName) continue;
717
+ if (fileName.startsWith("file://")) {
718
+ try {
719
+ const url = new URL(fileName);
720
+ fileName = decodeURIComponent(url.pathname);
721
+ if (process.platform === "win32" && fileName.startsWith("/")) {
722
+ fileName = fileName.substring(1);
723
+ }
724
+ } catch (e) {
725
+ fileName = fileName.replace("file://", "");
726
+ if (process.platform === "win32" && fileName.startsWith("/")) {
727
+ fileName = fileName.substring(1);
682
728
  }
683
729
  }
730
+ }
731
+ const normalizedPath = fileName.replace(/\\/g, "/");
732
+ const isLeanMCPCore = normalizedPath.includes("@leanmcp/core") || normalizedPath.includes("leanmcp-sdk/packages/core");
733
+ const isValidExtension = fileName.endsWith(".ts") || fileName.endsWith(".js") || fileName.endsWith(".mjs");
734
+ if (!isLeanMCPCore && isValidExtension) {
684
735
  return fileName;
685
736
  }
686
737
  }
738
+ this.logger.debug("No suitable caller file found in stack trace");
687
739
  return null;
688
740
  } finally {
689
741
  Error.prepareStackTrace = originalPrepareStackTrace;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leanmcp/core",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "Core library implementing decorators, reflection, and MCP runtime server",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",