@leanmcp/core 0.3.5 → 0.3.7
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/LICENSE +21 -21
- package/README.md +555 -555
- package/dist/index.d.mts +35 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.js +105 -13
- package/dist/index.mjs +105 -13
- package/package.json +71 -71
package/dist/index.d.mts
CHANGED
|
@@ -252,6 +252,7 @@ interface HTTPServerOptions {
|
|
|
252
252
|
logger?: Logger;
|
|
253
253
|
sessionTimeout?: number;
|
|
254
254
|
stateless?: boolean;
|
|
255
|
+
dashboard?: boolean;
|
|
255
256
|
}
|
|
256
257
|
interface MCPServerFactory {
|
|
257
258
|
(): Server | Promise<Server>;
|
|
@@ -474,9 +475,18 @@ declare class MCPServer {
|
|
|
474
475
|
method: string;
|
|
475
476
|
params?: {
|
|
476
477
|
[x: string]: unknown;
|
|
478
|
+
task?: {
|
|
479
|
+
[x: string]: unknown;
|
|
480
|
+
ttl?: number | null | undefined;
|
|
481
|
+
pollInterval?: number | undefined;
|
|
482
|
+
} | undefined;
|
|
477
483
|
_meta?: {
|
|
478
484
|
[x: string]: unknown;
|
|
479
485
|
progressToken?: string | number | undefined;
|
|
486
|
+
"io.modelcontextprotocol/related-task"?: {
|
|
487
|
+
[x: string]: unknown;
|
|
488
|
+
taskId: string;
|
|
489
|
+
} | undefined;
|
|
480
490
|
} | undefined;
|
|
481
491
|
} | undefined;
|
|
482
492
|
}, {
|
|
@@ -485,12 +495,20 @@ declare class MCPServer {
|
|
|
485
495
|
[x: string]: unknown;
|
|
486
496
|
_meta?: {
|
|
487
497
|
[x: string]: unknown;
|
|
498
|
+
"io.modelcontextprotocol/related-task"?: {
|
|
499
|
+
[x: string]: unknown;
|
|
500
|
+
taskId: string;
|
|
501
|
+
} | undefined;
|
|
488
502
|
} | undefined;
|
|
489
503
|
} | undefined;
|
|
490
504
|
}, {
|
|
491
505
|
[x: string]: unknown;
|
|
492
506
|
_meta?: {
|
|
493
507
|
[x: string]: unknown;
|
|
508
|
+
"io.modelcontextprotocol/related-task"?: {
|
|
509
|
+
[x: string]: unknown;
|
|
510
|
+
taskId: string;
|
|
511
|
+
} | undefined;
|
|
494
512
|
} | undefined;
|
|
495
513
|
}>;
|
|
496
514
|
}
|
|
@@ -509,9 +527,18 @@ declare class MCPServerRuntime {
|
|
|
509
527
|
method: string;
|
|
510
528
|
params?: {
|
|
511
529
|
[x: string]: unknown;
|
|
530
|
+
task?: {
|
|
531
|
+
[x: string]: unknown;
|
|
532
|
+
ttl?: number | null | undefined;
|
|
533
|
+
pollInterval?: number | undefined;
|
|
534
|
+
} | undefined;
|
|
512
535
|
_meta?: {
|
|
513
536
|
[x: string]: unknown;
|
|
514
537
|
progressToken?: string | number | undefined;
|
|
538
|
+
"io.modelcontextprotocol/related-task"?: {
|
|
539
|
+
[x: string]: unknown;
|
|
540
|
+
taskId: string;
|
|
541
|
+
} | undefined;
|
|
515
542
|
} | undefined;
|
|
516
543
|
} | undefined;
|
|
517
544
|
}, {
|
|
@@ -520,12 +547,20 @@ declare class MCPServerRuntime {
|
|
|
520
547
|
[x: string]: unknown;
|
|
521
548
|
_meta?: {
|
|
522
549
|
[x: string]: unknown;
|
|
550
|
+
"io.modelcontextprotocol/related-task"?: {
|
|
551
|
+
[x: string]: unknown;
|
|
552
|
+
taskId: string;
|
|
553
|
+
} | undefined;
|
|
523
554
|
} | undefined;
|
|
524
555
|
} | undefined;
|
|
525
556
|
}, {
|
|
526
557
|
[x: string]: unknown;
|
|
527
558
|
_meta?: {
|
|
528
559
|
[x: string]: unknown;
|
|
560
|
+
"io.modelcontextprotocol/related-task"?: {
|
|
561
|
+
[x: string]: unknown;
|
|
562
|
+
taskId: string;
|
|
563
|
+
} | undefined;
|
|
529
564
|
} | undefined;
|
|
530
565
|
}>;
|
|
531
566
|
getTools(): RegisteredTool[];
|
package/dist/index.d.ts
CHANGED
|
@@ -252,6 +252,7 @@ interface HTTPServerOptions {
|
|
|
252
252
|
logger?: Logger;
|
|
253
253
|
sessionTimeout?: number;
|
|
254
254
|
stateless?: boolean;
|
|
255
|
+
dashboard?: boolean;
|
|
255
256
|
}
|
|
256
257
|
interface MCPServerFactory {
|
|
257
258
|
(): Server | Promise<Server>;
|
|
@@ -474,9 +475,18 @@ declare class MCPServer {
|
|
|
474
475
|
method: string;
|
|
475
476
|
params?: {
|
|
476
477
|
[x: string]: unknown;
|
|
478
|
+
task?: {
|
|
479
|
+
[x: string]: unknown;
|
|
480
|
+
ttl?: number | null | undefined;
|
|
481
|
+
pollInterval?: number | undefined;
|
|
482
|
+
} | undefined;
|
|
477
483
|
_meta?: {
|
|
478
484
|
[x: string]: unknown;
|
|
479
485
|
progressToken?: string | number | undefined;
|
|
486
|
+
"io.modelcontextprotocol/related-task"?: {
|
|
487
|
+
[x: string]: unknown;
|
|
488
|
+
taskId: string;
|
|
489
|
+
} | undefined;
|
|
480
490
|
} | undefined;
|
|
481
491
|
} | undefined;
|
|
482
492
|
}, {
|
|
@@ -485,12 +495,20 @@ declare class MCPServer {
|
|
|
485
495
|
[x: string]: unknown;
|
|
486
496
|
_meta?: {
|
|
487
497
|
[x: string]: unknown;
|
|
498
|
+
"io.modelcontextprotocol/related-task"?: {
|
|
499
|
+
[x: string]: unknown;
|
|
500
|
+
taskId: string;
|
|
501
|
+
} | undefined;
|
|
488
502
|
} | undefined;
|
|
489
503
|
} | undefined;
|
|
490
504
|
}, {
|
|
491
505
|
[x: string]: unknown;
|
|
492
506
|
_meta?: {
|
|
493
507
|
[x: string]: unknown;
|
|
508
|
+
"io.modelcontextprotocol/related-task"?: {
|
|
509
|
+
[x: string]: unknown;
|
|
510
|
+
taskId: string;
|
|
511
|
+
} | undefined;
|
|
494
512
|
} | undefined;
|
|
495
513
|
}>;
|
|
496
514
|
}
|
|
@@ -509,9 +527,18 @@ declare class MCPServerRuntime {
|
|
|
509
527
|
method: string;
|
|
510
528
|
params?: {
|
|
511
529
|
[x: string]: unknown;
|
|
530
|
+
task?: {
|
|
531
|
+
[x: string]: unknown;
|
|
532
|
+
ttl?: number | null | undefined;
|
|
533
|
+
pollInterval?: number | undefined;
|
|
534
|
+
} | undefined;
|
|
512
535
|
_meta?: {
|
|
513
536
|
[x: string]: unknown;
|
|
514
537
|
progressToken?: string | number | undefined;
|
|
538
|
+
"io.modelcontextprotocol/related-task"?: {
|
|
539
|
+
[x: string]: unknown;
|
|
540
|
+
taskId: string;
|
|
541
|
+
} | undefined;
|
|
515
542
|
} | undefined;
|
|
516
543
|
} | undefined;
|
|
517
544
|
}, {
|
|
@@ -520,12 +547,20 @@ declare class MCPServerRuntime {
|
|
|
520
547
|
[x: string]: unknown;
|
|
521
548
|
_meta?: {
|
|
522
549
|
[x: string]: unknown;
|
|
550
|
+
"io.modelcontextprotocol/related-task"?: {
|
|
551
|
+
[x: string]: unknown;
|
|
552
|
+
taskId: string;
|
|
553
|
+
} | undefined;
|
|
523
554
|
} | undefined;
|
|
524
555
|
} | undefined;
|
|
525
556
|
}, {
|
|
526
557
|
[x: string]: unknown;
|
|
527
558
|
_meta?: {
|
|
528
559
|
[x: string]: unknown;
|
|
560
|
+
"io.modelcontextprotocol/related-task"?: {
|
|
561
|
+
[x: string]: unknown;
|
|
562
|
+
taskId: string;
|
|
563
|
+
} | undefined;
|
|
529
564
|
} | undefined;
|
|
530
565
|
}>;
|
|
531
566
|
getTools(): RegisteredTool[];
|
package/dist/index.js
CHANGED
|
@@ -483,17 +483,66 @@ var init_validation = __esm({
|
|
|
483
483
|
function isInitializeRequest(body) {
|
|
484
484
|
return body && body.method === "initialize";
|
|
485
485
|
}
|
|
486
|
+
function getCallerFile() {
|
|
487
|
+
const originalPrepareStackTrace = Error.prepareStackTrace;
|
|
488
|
+
try {
|
|
489
|
+
const err = new Error();
|
|
490
|
+
Error.prepareStackTrace = (_, stack2) => stack2;
|
|
491
|
+
const stack = err.stack;
|
|
492
|
+
for (let i = 0; i < stack.length; i++) {
|
|
493
|
+
let fileName = stack[i].getFileName();
|
|
494
|
+
if (!fileName) continue;
|
|
495
|
+
if (fileName.startsWith("file://")) {
|
|
496
|
+
try {
|
|
497
|
+
const url = new URL(fileName);
|
|
498
|
+
fileName = decodeURIComponent(url.pathname);
|
|
499
|
+
if (process.platform === "win32" && fileName.startsWith("/")) {
|
|
500
|
+
fileName = fileName.substring(1);
|
|
501
|
+
}
|
|
502
|
+
} catch (e) {
|
|
503
|
+
fileName = fileName.replace("file://", "");
|
|
504
|
+
if (process.platform === "win32" && fileName.startsWith("/")) {
|
|
505
|
+
fileName = fileName.substring(1);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
const normalizedPath = fileName.replace(/\\/g, "/");
|
|
510
|
+
const isLeanMCPCore = normalizedPath.includes("@leanmcp/core") || normalizedPath.includes("leanmcp-sdk/packages/core");
|
|
511
|
+
const isValidExtension = fileName.endsWith(".ts") || fileName.endsWith(".js") || fileName.endsWith(".mjs");
|
|
512
|
+
if (!isLeanMCPCore && isValidExtension) {
|
|
513
|
+
return fileName;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
return null;
|
|
517
|
+
} finally {
|
|
518
|
+
Error.prepareStackTrace = originalPrepareStackTrace;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
486
521
|
async function createHTTPServer(serverInput, options) {
|
|
487
522
|
let serverFactory;
|
|
488
523
|
let httpOptions;
|
|
524
|
+
let resolvedMcpDir;
|
|
489
525
|
if (typeof serverInput === "function") {
|
|
490
526
|
serverFactory = serverInput;
|
|
491
527
|
httpOptions = options || {};
|
|
492
528
|
} else {
|
|
493
529
|
const serverOptions = serverInput;
|
|
494
530
|
const { MCPServer: MCPServer2 } = await Promise.resolve().then(() => (init_index(), index_exports));
|
|
531
|
+
if (!serverOptions.mcpDir) {
|
|
532
|
+
const callerFile = getCallerFile();
|
|
533
|
+
if (callerFile) {
|
|
534
|
+
const path2 = await import("path");
|
|
535
|
+
const callerDir = path2.dirname(callerFile);
|
|
536
|
+
resolvedMcpDir = path2.join(callerDir, "mcp");
|
|
537
|
+
}
|
|
538
|
+
} else {
|
|
539
|
+
resolvedMcpDir = serverOptions.mcpDir;
|
|
540
|
+
}
|
|
495
541
|
serverFactory = /* @__PURE__ */ __name(async () => {
|
|
496
|
-
const mcpServer2 = new MCPServer2(
|
|
542
|
+
const mcpServer2 = new MCPServer2({
|
|
543
|
+
...serverOptions,
|
|
544
|
+
mcpDir: resolvedMcpDir || serverOptions.mcpDir
|
|
545
|
+
});
|
|
497
546
|
return mcpServer2.getServer();
|
|
498
547
|
}, "serverFactory");
|
|
499
548
|
httpOptions = {
|
|
@@ -501,7 +550,8 @@ async function createHTTPServer(serverInput, options) {
|
|
|
501
550
|
cors: serverOptions.cors,
|
|
502
551
|
logging: serverOptions.logging,
|
|
503
552
|
sessionTimeout: serverOptions.sessionTimeout,
|
|
504
|
-
stateless: serverOptions.stateless
|
|
553
|
+
stateless: serverOptions.stateless,
|
|
554
|
+
dashboard: serverOptions.dashboard
|
|
505
555
|
};
|
|
506
556
|
}
|
|
507
557
|
const [express, { StreamableHTTPServerTransport }, cors] = await Promise.all([
|
|
@@ -521,6 +571,7 @@ async function createHTTPServer(serverInput, options) {
|
|
|
521
571
|
validatePort(basePort);
|
|
522
572
|
const transports = {};
|
|
523
573
|
let mcpServer = null;
|
|
574
|
+
let statelessServerFactory = null;
|
|
524
575
|
const logger = httpOptions.logger || new Logger({
|
|
525
576
|
level: httpOptions.logging ? LogLevel.INFO : LogLevel.NONE,
|
|
526
577
|
prefix: "HTTP"
|
|
@@ -618,6 +669,42 @@ async function createHTTPServer(serverInput, options) {
|
|
|
618
669
|
app.use(express.json());
|
|
619
670
|
const isStateless = httpOptions.stateless !== false;
|
|
620
671
|
console.log(`Starting LeanMCP HTTP Server (${isStateless ? "STATELESS" : "STATEFUL"})...`);
|
|
672
|
+
const DASHBOARD_URL = process.env.DASHBOARD_URL || "https://s3-dashboard-build.s3.us-west-2.amazonaws.com/out/index.html";
|
|
673
|
+
let cachedDashboard = null;
|
|
674
|
+
let cacheTimestamp = 0;
|
|
675
|
+
const CACHE_DURATION = 5 * 60 * 1e3;
|
|
676
|
+
async function fetchDashboard() {
|
|
677
|
+
const now = Date.now();
|
|
678
|
+
if (cachedDashboard && now - cacheTimestamp < CACHE_DURATION) {
|
|
679
|
+
return cachedDashboard;
|
|
680
|
+
}
|
|
681
|
+
try {
|
|
682
|
+
const response = await fetch(DASHBOARD_URL);
|
|
683
|
+
if (!response.ok) {
|
|
684
|
+
throw new Error(`Failed to fetch dashboard: ${response.status}`);
|
|
685
|
+
}
|
|
686
|
+
const html = await response.text();
|
|
687
|
+
cachedDashboard = html;
|
|
688
|
+
cacheTimestamp = now;
|
|
689
|
+
return html;
|
|
690
|
+
} catch (error) {
|
|
691
|
+
logger.error("Error fetching dashboard from S3:", error);
|
|
692
|
+
throw error;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
__name(fetchDashboard, "fetchDashboard");
|
|
696
|
+
const isDashboardEnabled = httpOptions.dashboard !== false;
|
|
697
|
+
if (isDashboardEnabled) {
|
|
698
|
+
app.get("/", async (req, res) => {
|
|
699
|
+
try {
|
|
700
|
+
const html = await fetchDashboard();
|
|
701
|
+
res.setHeader("Content-Type", "text/html");
|
|
702
|
+
res.send(html);
|
|
703
|
+
} catch (error) {
|
|
704
|
+
res.status(500).send("<h1>Dashboard temporarily unavailable</h1><p>Please try again later.</p>");
|
|
705
|
+
}
|
|
706
|
+
});
|
|
707
|
+
}
|
|
621
708
|
app.get("/health", (req, res) => {
|
|
622
709
|
res.json({
|
|
623
710
|
status: "ok",
|
|
@@ -698,7 +785,7 @@ async function createHTTPServer(serverInput, options) {
|
|
|
698
785
|
else if (params?.uri) logMessage += ` [${params.uri}]`;
|
|
699
786
|
logger.info(logMessage);
|
|
700
787
|
try {
|
|
701
|
-
const freshServer = await
|
|
788
|
+
const freshServer = await statelessServerFactory();
|
|
702
789
|
if (freshServer && typeof freshServer.waitForInit === "function") {
|
|
703
790
|
await freshServer.waitForInit();
|
|
704
791
|
}
|
|
@@ -725,18 +812,19 @@ async function createHTTPServer(serverInput, options) {
|
|
|
725
812
|
}
|
|
726
813
|
}
|
|
727
814
|
}, "handleMCPRequestStateless");
|
|
815
|
+
if (isDashboardEnabled) {
|
|
816
|
+
app.get("/mcp", async (req, res) => {
|
|
817
|
+
try {
|
|
818
|
+
const html = await fetchDashboard();
|
|
819
|
+
res.setHeader("Content-Type", "text/html");
|
|
820
|
+
res.send(html);
|
|
821
|
+
} catch (error) {
|
|
822
|
+
res.status(500).send("<h1>Dashboard temporarily unavailable</h1><p>Please try again later.</p>");
|
|
823
|
+
}
|
|
824
|
+
});
|
|
825
|
+
}
|
|
728
826
|
if (isStateless) {
|
|
729
827
|
app.post("/mcp", handleMCPRequestStateless);
|
|
730
|
-
app.get("/mcp", (_req, res) => {
|
|
731
|
-
res.status(405).json({
|
|
732
|
-
jsonrpc: "2.0",
|
|
733
|
-
error: {
|
|
734
|
-
code: -32e3,
|
|
735
|
-
message: "Method not allowed (stateless mode)"
|
|
736
|
-
},
|
|
737
|
-
id: null
|
|
738
|
-
});
|
|
739
|
-
});
|
|
740
828
|
app.delete("/mcp", (_req, res) => {
|
|
741
829
|
res.status(405).json({
|
|
742
830
|
jsonrpc: "2.0",
|
|
@@ -758,6 +846,9 @@ async function createHTTPServer(serverInput, options) {
|
|
|
758
846
|
if (mcpServer && typeof mcpServer.waitForInit === "function") {
|
|
759
847
|
await mcpServer.waitForInit();
|
|
760
848
|
}
|
|
849
|
+
if (isStateless) {
|
|
850
|
+
statelessServerFactory = serverFactory;
|
|
851
|
+
}
|
|
761
852
|
const { listener, port } = await startServerWithPortRetry();
|
|
762
853
|
activeListener = listener;
|
|
763
854
|
process.env.PORT = String(port);
|
|
@@ -800,6 +891,7 @@ var init_http_server = __esm({
|
|
|
800
891
|
init_logger();
|
|
801
892
|
init_validation();
|
|
802
893
|
__name(isInitializeRequest, "isInitializeRequest");
|
|
894
|
+
__name(getCallerFile, "getCallerFile");
|
|
803
895
|
__name(createHTTPServer, "createHTTPServer");
|
|
804
896
|
}
|
|
805
897
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -444,17 +444,67 @@ function isInitializeRequest(body) {
|
|
|
444
444
|
return body && body.method === "initialize";
|
|
445
445
|
}
|
|
446
446
|
__name(isInitializeRequest, "isInitializeRequest");
|
|
447
|
+
function getCallerFile() {
|
|
448
|
+
const originalPrepareStackTrace = Error.prepareStackTrace;
|
|
449
|
+
try {
|
|
450
|
+
const err = new Error();
|
|
451
|
+
Error.prepareStackTrace = (_, stack2) => stack2;
|
|
452
|
+
const stack = err.stack;
|
|
453
|
+
for (let i = 0; i < stack.length; i++) {
|
|
454
|
+
let fileName = stack[i].getFileName();
|
|
455
|
+
if (!fileName) continue;
|
|
456
|
+
if (fileName.startsWith("file://")) {
|
|
457
|
+
try {
|
|
458
|
+
const url = new URL(fileName);
|
|
459
|
+
fileName = decodeURIComponent(url.pathname);
|
|
460
|
+
if (process.platform === "win32" && fileName.startsWith("/")) {
|
|
461
|
+
fileName = fileName.substring(1);
|
|
462
|
+
}
|
|
463
|
+
} catch (e) {
|
|
464
|
+
fileName = fileName.replace("file://", "");
|
|
465
|
+
if (process.platform === "win32" && fileName.startsWith("/")) {
|
|
466
|
+
fileName = fileName.substring(1);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
const normalizedPath = fileName.replace(/\\/g, "/");
|
|
471
|
+
const isLeanMCPCore = normalizedPath.includes("@leanmcp/core") || normalizedPath.includes("leanmcp-sdk/packages/core");
|
|
472
|
+
const isValidExtension = fileName.endsWith(".ts") || fileName.endsWith(".js") || fileName.endsWith(".mjs");
|
|
473
|
+
if (!isLeanMCPCore && isValidExtension) {
|
|
474
|
+
return fileName;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
return null;
|
|
478
|
+
} finally {
|
|
479
|
+
Error.prepareStackTrace = originalPrepareStackTrace;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
__name(getCallerFile, "getCallerFile");
|
|
447
483
|
async function createHTTPServer(serverInput, options) {
|
|
448
484
|
let serverFactory;
|
|
449
485
|
let httpOptions;
|
|
486
|
+
let resolvedMcpDir;
|
|
450
487
|
if (typeof serverInput === "function") {
|
|
451
488
|
serverFactory = serverInput;
|
|
452
489
|
httpOptions = options || {};
|
|
453
490
|
} else {
|
|
454
491
|
const serverOptions = serverInput;
|
|
455
492
|
const { MCPServer: MCPServer2 } = await import("./index.mjs");
|
|
493
|
+
if (!serverOptions.mcpDir) {
|
|
494
|
+
const callerFile = getCallerFile();
|
|
495
|
+
if (callerFile) {
|
|
496
|
+
const path2 = await import("path");
|
|
497
|
+
const callerDir = path2.dirname(callerFile);
|
|
498
|
+
resolvedMcpDir = path2.join(callerDir, "mcp");
|
|
499
|
+
}
|
|
500
|
+
} else {
|
|
501
|
+
resolvedMcpDir = serverOptions.mcpDir;
|
|
502
|
+
}
|
|
456
503
|
serverFactory = /* @__PURE__ */ __name(async () => {
|
|
457
|
-
const mcpServer2 = new MCPServer2(
|
|
504
|
+
const mcpServer2 = new MCPServer2({
|
|
505
|
+
...serverOptions,
|
|
506
|
+
mcpDir: resolvedMcpDir || serverOptions.mcpDir
|
|
507
|
+
});
|
|
458
508
|
return mcpServer2.getServer();
|
|
459
509
|
}, "serverFactory");
|
|
460
510
|
httpOptions = {
|
|
@@ -462,7 +512,8 @@ async function createHTTPServer(serverInput, options) {
|
|
|
462
512
|
cors: serverOptions.cors,
|
|
463
513
|
logging: serverOptions.logging,
|
|
464
514
|
sessionTimeout: serverOptions.sessionTimeout,
|
|
465
|
-
stateless: serverOptions.stateless
|
|
515
|
+
stateless: serverOptions.stateless,
|
|
516
|
+
dashboard: serverOptions.dashboard
|
|
466
517
|
};
|
|
467
518
|
}
|
|
468
519
|
const [express, { StreamableHTTPServerTransport }, cors] = await Promise.all([
|
|
@@ -482,6 +533,7 @@ async function createHTTPServer(serverInput, options) {
|
|
|
482
533
|
validatePort(basePort);
|
|
483
534
|
const transports = {};
|
|
484
535
|
let mcpServer = null;
|
|
536
|
+
let statelessServerFactory = null;
|
|
485
537
|
const logger = httpOptions.logger || new Logger({
|
|
486
538
|
level: httpOptions.logging ? LogLevel.INFO : LogLevel.NONE,
|
|
487
539
|
prefix: "HTTP"
|
|
@@ -579,6 +631,42 @@ async function createHTTPServer(serverInput, options) {
|
|
|
579
631
|
app.use(express.json());
|
|
580
632
|
const isStateless = httpOptions.stateless !== false;
|
|
581
633
|
console.log(`Starting LeanMCP HTTP Server (${isStateless ? "STATELESS" : "STATEFUL"})...`);
|
|
634
|
+
const DASHBOARD_URL = process.env.DASHBOARD_URL || "https://s3-dashboard-build.s3.us-west-2.amazonaws.com/out/index.html";
|
|
635
|
+
let cachedDashboard = null;
|
|
636
|
+
let cacheTimestamp = 0;
|
|
637
|
+
const CACHE_DURATION = 5 * 60 * 1e3;
|
|
638
|
+
async function fetchDashboard() {
|
|
639
|
+
const now = Date.now();
|
|
640
|
+
if (cachedDashboard && now - cacheTimestamp < CACHE_DURATION) {
|
|
641
|
+
return cachedDashboard;
|
|
642
|
+
}
|
|
643
|
+
try {
|
|
644
|
+
const response = await fetch(DASHBOARD_URL);
|
|
645
|
+
if (!response.ok) {
|
|
646
|
+
throw new Error(`Failed to fetch dashboard: ${response.status}`);
|
|
647
|
+
}
|
|
648
|
+
const html = await response.text();
|
|
649
|
+
cachedDashboard = html;
|
|
650
|
+
cacheTimestamp = now;
|
|
651
|
+
return html;
|
|
652
|
+
} catch (error) {
|
|
653
|
+
logger.error("Error fetching dashboard from S3:", error);
|
|
654
|
+
throw error;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
__name(fetchDashboard, "fetchDashboard");
|
|
658
|
+
const isDashboardEnabled = httpOptions.dashboard !== false;
|
|
659
|
+
if (isDashboardEnabled) {
|
|
660
|
+
app.get("/", async (req, res) => {
|
|
661
|
+
try {
|
|
662
|
+
const html = await fetchDashboard();
|
|
663
|
+
res.setHeader("Content-Type", "text/html");
|
|
664
|
+
res.send(html);
|
|
665
|
+
} catch (error) {
|
|
666
|
+
res.status(500).send("<h1>Dashboard temporarily unavailable</h1><p>Please try again later.</p>");
|
|
667
|
+
}
|
|
668
|
+
});
|
|
669
|
+
}
|
|
582
670
|
app.get("/health", (req, res) => {
|
|
583
671
|
res.json({
|
|
584
672
|
status: "ok",
|
|
@@ -659,7 +747,7 @@ async function createHTTPServer(serverInput, options) {
|
|
|
659
747
|
else if (params?.uri) logMessage += ` [${params.uri}]`;
|
|
660
748
|
logger.info(logMessage);
|
|
661
749
|
try {
|
|
662
|
-
const freshServer = await
|
|
750
|
+
const freshServer = await statelessServerFactory();
|
|
663
751
|
if (freshServer && typeof freshServer.waitForInit === "function") {
|
|
664
752
|
await freshServer.waitForInit();
|
|
665
753
|
}
|
|
@@ -686,18 +774,19 @@ async function createHTTPServer(serverInput, options) {
|
|
|
686
774
|
}
|
|
687
775
|
}
|
|
688
776
|
}, "handleMCPRequestStateless");
|
|
777
|
+
if (isDashboardEnabled) {
|
|
778
|
+
app.get("/mcp", async (req, res) => {
|
|
779
|
+
try {
|
|
780
|
+
const html = await fetchDashboard();
|
|
781
|
+
res.setHeader("Content-Type", "text/html");
|
|
782
|
+
res.send(html);
|
|
783
|
+
} catch (error) {
|
|
784
|
+
res.status(500).send("<h1>Dashboard temporarily unavailable</h1><p>Please try again later.</p>");
|
|
785
|
+
}
|
|
786
|
+
});
|
|
787
|
+
}
|
|
689
788
|
if (isStateless) {
|
|
690
789
|
app.post("/mcp", handleMCPRequestStateless);
|
|
691
|
-
app.get("/mcp", (_req, res) => {
|
|
692
|
-
res.status(405).json({
|
|
693
|
-
jsonrpc: "2.0",
|
|
694
|
-
error: {
|
|
695
|
-
code: -32e3,
|
|
696
|
-
message: "Method not allowed (stateless mode)"
|
|
697
|
-
},
|
|
698
|
-
id: null
|
|
699
|
-
});
|
|
700
|
-
});
|
|
701
790
|
app.delete("/mcp", (_req, res) => {
|
|
702
791
|
res.status(405).json({
|
|
703
792
|
jsonrpc: "2.0",
|
|
@@ -719,6 +808,9 @@ async function createHTTPServer(serverInput, options) {
|
|
|
719
808
|
if (mcpServer && typeof mcpServer.waitForInit === "function") {
|
|
720
809
|
await mcpServer.waitForInit();
|
|
721
810
|
}
|
|
811
|
+
if (isStateless) {
|
|
812
|
+
statelessServerFactory = serverFactory;
|
|
813
|
+
}
|
|
722
814
|
const { listener, port } = await startServerWithPortRetry();
|
|
723
815
|
activeListener = listener;
|
|
724
816
|
process.env.PORT = String(port);
|
package/package.json
CHANGED
|
@@ -1,71 +1,71 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@leanmcp/core",
|
|
3
|
-
"version": "0.3.
|
|
4
|
-
"description": "Core library implementing decorators, reflection, and MCP runtime server",
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"module": "dist/index.mjs",
|
|
7
|
-
"types": "dist/index.d.ts",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": {
|
|
10
|
-
"types": "./dist/index.d.ts",
|
|
11
|
-
"require": "./dist/index.js",
|
|
12
|
-
"import": "./dist/index.mjs"
|
|
13
|
-
}
|
|
14
|
-
},
|
|
15
|
-
"files": [
|
|
16
|
-
"dist",
|
|
17
|
-
"README.md",
|
|
18
|
-
"LICENSE"
|
|
19
|
-
],
|
|
20
|
-
"scripts": {
|
|
21
|
-
"build": "tsup src/index.ts --format esm,cjs --dts",
|
|
22
|
-
"dev": "tsup src/index.ts --format esm,cjs --dts --watch",
|
|
23
|
-
"test": "jest --passWithNoTests",
|
|
24
|
-
"test:watch": "jest --watch"
|
|
25
|
-
},
|
|
26
|
-
"dependencies": {
|
|
27
|
-
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
28
|
-
"ajv": "^8.12.0",
|
|
29
|
-
"dotenv": "^16.3.1",
|
|
30
|
-
"reflect-metadata": "^0.2.1"
|
|
31
|
-
},
|
|
32
|
-
"peerDependencies": {
|
|
33
|
-
"cors": "^2.8.5",
|
|
34
|
-
"express": "^5.0.0"
|
|
35
|
-
},
|
|
36
|
-
"peerDependenciesMeta": {
|
|
37
|
-
"express": {
|
|
38
|
-
"optional": true
|
|
39
|
-
},
|
|
40
|
-
"cors": {
|
|
41
|
-
"optional": true
|
|
42
|
-
}
|
|
43
|
-
},
|
|
44
|
-
"devDependencies": {
|
|
45
|
-
"@types/cors": "^2.8.0",
|
|
46
|
-
"@types/express": "^5.0.0",
|
|
47
|
-
"@types/node": "^20.0.0"
|
|
48
|
-
},
|
|
49
|
-
"repository": {
|
|
50
|
-
"type": "git",
|
|
51
|
-
"url": "git+https://github.com/LeanMCP/leanmcp-sdk.git",
|
|
52
|
-
"directory": "packages/core"
|
|
53
|
-
},
|
|
54
|
-
"homepage": "https://github.com/LeanMCP/leanmcp-sdk#readme",
|
|
55
|
-
"bugs": {
|
|
56
|
-
"url": "https://github.com/LeanMCP/leanmcp-sdk/issues"
|
|
57
|
-
},
|
|
58
|
-
"keywords": [
|
|
59
|
-
"mcp",
|
|
60
|
-
"model-context-protocol",
|
|
61
|
-
"typescript",
|
|
62
|
-
"decorators",
|
|
63
|
-
"server",
|
|
64
|
-
"runtime"
|
|
65
|
-
],
|
|
66
|
-
"author": "LeanMCP <admin@leanmcp.com>",
|
|
67
|
-
"license": "MIT",
|
|
68
|
-
"publishConfig": {
|
|
69
|
-
"access": "public"
|
|
70
|
-
}
|
|
71
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@leanmcp/core",
|
|
3
|
+
"version": "0.3.7",
|
|
4
|
+
"description": "Core library implementing decorators, reflection, and MCP runtime server",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"require": "./dist/index.js",
|
|
12
|
+
"import": "./dist/index.mjs"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md",
|
|
18
|
+
"LICENSE"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsup src/index.ts --format esm,cjs --dts",
|
|
22
|
+
"dev": "tsup src/index.ts --format esm,cjs --dts --watch",
|
|
23
|
+
"test": "jest --passWithNoTests",
|
|
24
|
+
"test:watch": "jest --watch"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
28
|
+
"ajv": "^8.12.0",
|
|
29
|
+
"dotenv": "^16.3.1",
|
|
30
|
+
"reflect-metadata": "^0.2.1"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"cors": "^2.8.5",
|
|
34
|
+
"express": "^5.0.0"
|
|
35
|
+
},
|
|
36
|
+
"peerDependenciesMeta": {
|
|
37
|
+
"express": {
|
|
38
|
+
"optional": true
|
|
39
|
+
},
|
|
40
|
+
"cors": {
|
|
41
|
+
"optional": true
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/cors": "^2.8.0",
|
|
46
|
+
"@types/express": "^5.0.0",
|
|
47
|
+
"@types/node": "^20.0.0"
|
|
48
|
+
},
|
|
49
|
+
"repository": {
|
|
50
|
+
"type": "git",
|
|
51
|
+
"url": "git+https://github.com/LeanMCP/leanmcp-sdk.git",
|
|
52
|
+
"directory": "packages/core"
|
|
53
|
+
},
|
|
54
|
+
"homepage": "https://github.com/LeanMCP/leanmcp-sdk#readme",
|
|
55
|
+
"bugs": {
|
|
56
|
+
"url": "https://github.com/LeanMCP/leanmcp-sdk/issues"
|
|
57
|
+
},
|
|
58
|
+
"keywords": [
|
|
59
|
+
"mcp",
|
|
60
|
+
"model-context-protocol",
|
|
61
|
+
"typescript",
|
|
62
|
+
"decorators",
|
|
63
|
+
"server",
|
|
64
|
+
"runtime"
|
|
65
|
+
],
|
|
66
|
+
"author": "LeanMCP <admin@leanmcp.com>",
|
|
67
|
+
"license": "MIT",
|
|
68
|
+
"publishConfig": {
|
|
69
|
+
"access": "public"
|
|
70
|
+
}
|
|
71
|
+
}
|