mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-16 04:27:21 +00:00
feat: lazy load mcp server
This commit is contained in:
@@ -16,6 +16,7 @@ describe("AppServer", () => {
|
|||||||
mcp: {
|
mcp: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
path: "/api/system/mcp",
|
path: "/api/system/mcp",
|
||||||
|
logLevel: "warning",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -38,6 +39,7 @@ describe("AppServer", () => {
|
|||||||
mcp: {
|
mcp: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
path: "/api/system/mcp",
|
path: "/api/system/mcp",
|
||||||
|
logLevel: "warning",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ describe("mcp auth", async () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
await app.build();
|
await app.build();
|
||||||
|
await app.getMcpClient().ping();
|
||||||
server = app.mcp!;
|
server = app.mcp!;
|
||||||
server.setLogLevel("error");
|
server.setLogLevel("error");
|
||||||
server.onNotification((message) => {
|
server.onNotification((message) => {
|
||||||
|
|||||||
@@ -34,6 +34,11 @@ describe("mcp", () => {
|
|||||||
});
|
});
|
||||||
await app.build();
|
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);
|
expect(app.mcp?.tools.length).toBeGreaterThan(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ describe("mcp data", async () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
await app.build();
|
await app.build();
|
||||||
|
await app.getMcpClient().ping();
|
||||||
server = app.mcp!;
|
server = app.mcp!;
|
||||||
server.setLogLevel("error");
|
server.setLogLevel("error");
|
||||||
server.onNotification((message) => {
|
server.onNotification((message) => {
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ describe("mcp media", async () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
await app.build();
|
await app.build();
|
||||||
|
await app.getMcpClient().ping();
|
||||||
server = app.mcp!;
|
server = app.mcp!;
|
||||||
server.setLogLevel("error");
|
server.setLogLevel("error");
|
||||||
server.onNotification((message) => {
|
server.onNotification((message) => {
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ describe("mcp system", async () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
await app.build();
|
await app.build();
|
||||||
|
await app.getMcpClient().ping();
|
||||||
server = app.mcp!;
|
server = app.mcp!;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ describe("mcp system", async () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
await app.build();
|
await app.build();
|
||||||
|
await app.getMcpClient().ping();
|
||||||
server = app.mcp!;
|
server = app.mcp!;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -65,7 +65,7 @@
|
|||||||
"hono": "4.8.3",
|
"hono": "4.8.3",
|
||||||
"json-schema-library": "10.0.0-rc7",
|
"json-schema-library": "10.0.0-rc7",
|
||||||
"json-schema-to-ts": "^3.1.1",
|
"json-schema-to-ts": "^3.1.1",
|
||||||
"jsonv-ts": "0.8.2",
|
"jsonv-ts": "0.8.4",
|
||||||
"kysely": "0.27.6",
|
"kysely": "0.27.6",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"oauth4webapi": "^2.11.1",
|
"oauth4webapi": "^2.11.1",
|
||||||
|
|||||||
@@ -302,13 +302,13 @@ export class App<
|
|||||||
}
|
}
|
||||||
|
|
||||||
getMcpClient() {
|
getMcpClient() {
|
||||||
if (!this.mcp) {
|
const config = this.modules.get("server").config.mcp;
|
||||||
|
if (!config.enabled) {
|
||||||
throw new Error("MCP is not enabled");
|
throw new Error("MCP is not enabled");
|
||||||
}
|
}
|
||||||
const mcpPath = this.modules.get("server").config.mcp.path;
|
|
||||||
|
|
||||||
return new McpClient({
|
return new McpClient({
|
||||||
url: "http://localhost" + mcpPath,
|
url: "http://localhost" + config.path,
|
||||||
fetch: this.server.request,
|
fetch: this.server.request,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { getVersion } from "core/env";
|
|||||||
export function getSystemMcp(app: App) {
|
export function getSystemMcp(app: App) {
|
||||||
const middlewareServer = getMcpServer(app.server);
|
const middlewareServer = getMcpServer(app.server);
|
||||||
|
|
||||||
const appConfig = app.modules.configs();
|
//const appConfig = app.modules.configs();
|
||||||
const { version, ...appSchema } = app.getSchema();
|
const { version, ...appSchema } = app.getSchema();
|
||||||
const schema = s.strictObject(appSchema);
|
const schema = s.strictObject(appSchema);
|
||||||
const result = [...schema.walk({ maxDepth: 3 })];
|
const result = [...schema.walk({ maxDepth: 3 })];
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Exception } from "core/errors";
|
import { Exception } from "core/errors";
|
||||||
import { isDebug } from "core/env";
|
import { isDebug } from "core/env";
|
||||||
import { $console, s } from "bknd/utils";
|
import { $console, mcpLogLevels, s } from "bknd/utils";
|
||||||
import { $object } from "modules/mcp";
|
import { $object } from "modules/mcp";
|
||||||
import { cors } from "hono/cors";
|
import { cors } from "hono/cors";
|
||||||
import { Module } from "modules/Module";
|
import { Module } from "modules/Module";
|
||||||
@@ -25,6 +25,10 @@ export const serverConfigSchema = $object(
|
|||||||
mcp: s.strictObject({
|
mcp: s.strictObject({
|
||||||
enabled: s.boolean({ default: false }),
|
enabled: s.boolean({ default: false }),
|
||||||
path: s.string({ default: "/api/system/mcp" }),
|
path: s.string({ default: "/api/system/mcp" }),
|
||||||
|
logLevel: s.string({
|
||||||
|
enum: mcpLogLevels,
|
||||||
|
default: "warning",
|
||||||
|
}),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -70,33 +70,41 @@ export class SystemController extends Controller {
|
|||||||
|
|
||||||
this.registerMcp();
|
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(
|
app.server.use(
|
||||||
mcpMiddleware({
|
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,
|
sessionsEnabled: true,
|
||||||
debug: {
|
debug: {
|
||||||
logLevel: "debug",
|
logLevel: config.mcp.logLevel as any,
|
||||||
explainEndpoint: true,
|
explainEndpoint: true,
|
||||||
},
|
},
|
||||||
endpoint: {
|
endpoint: {
|
||||||
|
|||||||
6
bun.lock
6
bun.lock
@@ -15,7 +15,7 @@
|
|||||||
},
|
},
|
||||||
"app": {
|
"app": {
|
||||||
"name": "bknd",
|
"name": "bknd",
|
||||||
"version": "0.18.0-rc.4",
|
"version": "0.18.0-rc.6",
|
||||||
"bin": "./dist/cli/index.js",
|
"bin": "./dist/cli/index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cfworker/json-schema": "^4.1.1",
|
"@cfworker/json-schema": "^4.1.1",
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
"hono": "4.8.3",
|
"hono": "4.8.3",
|
||||||
"json-schema-library": "10.0.0-rc7",
|
"json-schema-library": "10.0.0-rc7",
|
||||||
"json-schema-to-ts": "^3.1.1",
|
"json-schema-to-ts": "^3.1.1",
|
||||||
"jsonv-ts": "0.8.2",
|
"jsonv-ts": "0.8.4",
|
||||||
"kysely": "0.27.6",
|
"kysely": "0.27.6",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"oauth4webapi": "^2.11.1",
|
"oauth4webapi": "^2.11.1",
|
||||||
@@ -2529,7 +2529,7 @@
|
|||||||
|
|
||||||
"jsonpointer": ["jsonpointer@5.0.1", "", {}, "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ=="],
|
"jsonpointer": ["jsonpointer@5.0.1", "", {}, "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ=="],
|
||||||
|
|
||||||
"jsonv-ts": ["jsonv-ts@0.8.2", "", { "optionalDependencies": { "hono": "*" }, "peerDependencies": { "typescript": "^5.0.0" } }, "sha512-1Z7+maCfoGGqBPu5vN8rU9gIsW7OatYmn+STBTPkybbtNqeMzAoJDDrXHjsZ89x5dPH9W+OgMpNLtN0ouwiMYg=="],
|
"jsonv-ts": ["jsonv-ts@0.8.4", "", { "optionalDependencies": { "hono": "*" }, "peerDependencies": { "typescript": "^5.0.0" } }, "sha512-TZOyAVGBZxHuzk09NgJCx2dbeh0XqVWVKHU1PtIuvjT9XO7zhvAD02RcVisJoUdt2rJNt3zlyeNQ2b8MMPc+ug=="],
|
||||||
|
|
||||||
"jsonwebtoken": ["jsonwebtoken@9.0.2", "", { "dependencies": { "jws": "^3.2.2", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", "lodash.isinteger": "^4.0.4", "lodash.isnumber": "^3.0.3", "lodash.isplainobject": "^4.0.6", "lodash.isstring": "^4.0.1", "lodash.once": "^4.0.0", "ms": "^2.1.1", "semver": "^7.5.4" } }, "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ=="],
|
"jsonwebtoken": ["jsonwebtoken@9.0.2", "", { "dependencies": { "jws": "^3.2.2", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", "lodash.isinteger": "^4.0.4", "lodash.isnumber": "^3.0.3", "lodash.isplainobject": "^4.0.6", "lodash.isstring": "^4.0.1", "lodash.once": "^4.0.0", "ms": "^2.1.1", "semver": "^7.5.4" } }, "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ=="],
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user