feat: lazy load mcp server

This commit is contained in:
dswbx
2025-09-23 13:46:39 +02:00
parent 54eee8cd34
commit 49aee37199
13 changed files with 56 additions and 32 deletions

View File

@@ -16,6 +16,7 @@ describe("AppServer", () => {
mcp: {
enabled: false,
path: "/api/system/mcp",
logLevel: "warning",
},
});
}
@@ -38,6 +39,7 @@ describe("AppServer", () => {
mcp: {
enabled: false,
path: "/api/system/mcp",
logLevel: "warning",
},
});
}

View File

@@ -44,6 +44,7 @@ describe("mcp auth", async () => {
},
});
await app.build();
await app.getMcpClient().ping();
server = app.mcp!;
server.setLogLevel("error");
server.onNotification((message) => {

View File

@@ -34,6 +34,11 @@ describe("mcp", () => {
});
await app.build();
// expect mcp to not be loaded yet
expect(app.mcp).toBeNull();
// after first request, mcp should be loaded
await app.getMcpClient().listTools();
expect(app.mcp?.tools.length).toBeGreaterThan(0);
});
});

View File

@@ -50,6 +50,7 @@ describe("mcp data", async () => {
},
});
await app.build();
await app.getMcpClient().ping();
server = app.mcp!;
server.setLogLevel("error");
server.onNotification((message) => {

View File

@@ -39,6 +39,7 @@ describe("mcp media", async () => {
},
});
await app.build();
await app.getMcpClient().ping();
server = app.mcp!;
server.setLogLevel("error");
server.onNotification((message) => {

View File

@@ -24,6 +24,7 @@ describe("mcp system", async () => {
},
});
await app.build();
await app.getMcpClient().ping();
server = app.mcp!;
});

View File

@@ -23,6 +23,7 @@ describe("mcp system", async () => {
},
});
await app.build();
await app.getMcpClient().ping();
server = app.mcp!;
});

View File

@@ -65,7 +65,7 @@
"hono": "4.8.3",
"json-schema-library": "10.0.0-rc7",
"json-schema-to-ts": "^3.1.1",
"jsonv-ts": "0.8.2",
"jsonv-ts": "0.8.4",
"kysely": "0.27.6",
"lodash-es": "^4.17.21",
"oauth4webapi": "^2.11.1",

View File

@@ -302,13 +302,13 @@ export class App<
}
getMcpClient() {
if (!this.mcp) {
const config = this.modules.get("server").config.mcp;
if (!config.enabled) {
throw new Error("MCP is not enabled");
}
const mcpPath = this.modules.get("server").config.mcp.path;
return new McpClient({
url: "http://localhost" + mcpPath,
url: "http://localhost" + config.path,
fetch: this.server.request,
});
}

View File

@@ -6,7 +6,7 @@ import { getVersion } from "core/env";
export function getSystemMcp(app: App) {
const middlewareServer = getMcpServer(app.server);
const appConfig = app.modules.configs();
//const appConfig = app.modules.configs();
const { version, ...appSchema } = app.getSchema();
const schema = s.strictObject(appSchema);
const result = [...schema.walk({ maxDepth: 3 })];

View File

@@ -1,6 +1,6 @@
import { Exception } from "core/errors";
import { isDebug } from "core/env";
import { $console, s } from "bknd/utils";
import { $console, mcpLogLevels, s } from "bknd/utils";
import { $object } from "modules/mcp";
import { cors } from "hono/cors";
import { Module } from "modules/Module";
@@ -25,6 +25,10 @@ export const serverConfigSchema = $object(
mcp: s.strictObject({
enabled: s.boolean({ default: false }),
path: s.string({ default: "/api/system/mcp" }),
logLevel: s.string({
enum: mcpLogLevels,
default: "warning",
}),
}),
},
{

View File

@@ -70,33 +70,41 @@ export class SystemController extends Controller {
this.registerMcp();
this._mcpServer = getSystemMcp(app);
this._mcpServer.onNotification((message) => {
if (message.method === "notification/message") {
const consoleMap = {
emergency: "error",
alert: "error",
critical: "error",
error: "error",
warning: "warn",
notice: "log",
info: "info",
debug: "debug",
};
const level = consoleMap[message.params.level];
if (!level) return;
$console[level]("MCP notification", message.params.message ?? message.params);
}
});
app.server.use(
mcpMiddleware({
server: this._mcpServer,
setup: async () => {
if (!this._mcpServer) {
this._mcpServer = getSystemMcp(app);
this._mcpServer.onNotification((message) => {
if (message.method === "notification/message") {
const consoleMap = {
emergency: "error",
alert: "error",
critical: "error",
error: "error",
warning: "warn",
notice: "log",
info: "info",
debug: "debug",
};
const level = consoleMap[message.params.level];
if (!level) return;
$console[level](
"MCP notification",
message.params.message ?? message.params,
);
}
});
}
return {
server: this._mcpServer,
};
},
sessionsEnabled: true,
debug: {
logLevel: "debug",
logLevel: config.mcp.logLevel as any,
explainEndpoint: true,
},
endpoint: {