init mcp tools test

This commit is contained in:
dswbx
2025-08-12 08:51:32 +02:00
parent 822e6fd644
commit 2e145bbf95
13 changed files with 233 additions and 51 deletions

View File

@@ -1,42 +0,0 @@
import { describe, it, expect } from "bun:test";
import { makeAppFromEnv } from "cli/commands/run";
import { createApp } from "core/test/utils";
import { ObjectToolSchema } from "modules/mcp";
import { s } from "bknd/utils";
describe("mcp", () => {
it("...", async () => {
const app = createApp({
initialConfig: {
auth: {
enabled: true,
},
},
});
await app.build();
const appConfig = app.modules.configs();
const { version, ...appSchema } = app.getSchema();
const schema = s.strictObject(appSchema);
const nodes = [...schema.walk({ data: appConfig })]
.map((n) => {
const path = n.instancePath.join(".");
if (path.startsWith("auth")) {
console.log("schema", n.instancePath, n.schema.constructor.name);
if (path === "auth.jwt") {
//console.log("jwt", n.schema.IS_MCP);
}
}
return n;
})
.filter((n) => n.schema instanceof ObjectToolSchema) as s.Node<ObjectToolSchema>[];
const tools = nodes.flatMap((n) => n.schema.getTools(n));
console.log(
"tools",
tools.map((t) => t.name),
);
});
});

View File

@@ -0,0 +1,41 @@
import { describe, it, expect, beforeAll } from "bun:test";
import { type App, createApp } from "core/test/utils";
import { getSystemMcp } from "modules/mcp/system-mcp";
/**
* - [ ] auth_me
* - [ ] auth_strategies
* - [ ] auth_user_create
* - [ ] auth_user_token
* - [ ] auth_user_password_change
* - [ ] auth_user_password_test
* - [ ] config_auth_update
* - [ ] config_auth_strategies_get
* - [ ] config_auth_strategies_add
* - [ ] config_auth_strategies_update
* - [ ] config_auth_strategies_remove
* - [ ] config_auth_roles_get
* - [ ] config_auth_roles_add
* - [ ] config_auth_roles_update
* - [ ] config_auth_roles_remove
*/
describe("mcp auth", async () => {
let app: App;
let server: ReturnType<typeof getSystemMcp>;
beforeAll(async () => {
app = createApp({
initialConfig: {
auth: {
enabled: true,
},
server: {
mcp: {
enabled: true,
},
},
},
});
await app.build();
server = getSystemMcp(app);
});
});

View File

@@ -0,0 +1,32 @@
import { describe, it, expect } from "bun:test";
import { createApp } from "core/test/utils";
import { getSystemMcp } from "modules/mcp/system-mcp";
import { registries } from "index";
import { StorageLocalAdapter } from "adapter/node/storage/StorageLocalAdapter";
describe("mcp", () => {
it("should have tools", async () => {
registries.media.register("local", StorageLocalAdapter);
const app = createApp({
initialConfig: {
auth: {
enabled: true,
},
media: {
enabled: true,
adapter: {
type: "local",
config: {
path: "./",
},
},
},
},
});
await app.build();
const server = getSystemMcp(app);
expect(server.tools.length).toBeGreaterThan(0);
});
});

View File

@@ -0,0 +1,48 @@
import { describe, it, expect, beforeAll } from "bun:test";
import { type App, createApp } from "core/test/utils";
import { getSystemMcp } from "modules/mcp/system-mcp";
/**
* - [ ] data_sync
* - [ ] data_entity_fn_count
* - [ ] data_entity_fn_exists
* - [ ] data_entity_read_one
* - [ ] data_entity_read_many
* - [ ] data_entity_insert
* - [ ] data_entity_update_many
* - [ ] data_entity_update_one
* - [ ] data_entity_delete_one
* - [ ] data_entity_delete_many
* - [ ] data_entity_info
* - [ ] config_data_get
* - [ ] config_data_update
* - [ ] config_data_entities_get
* - [ ] config_data_entities_add
* - [ ] config_data_entities_update
* - [ ] config_data_entities_remove
* - [ ] config_data_relations_get
* - [ ] config_data_relations_add
* - [ ] config_data_relations_update
* - [ ] config_data_relations_remove
* - [ ] config_data_indices_get
* - [ ] config_data_indices_add
* - [ ] config_data_indices_update
* - [ ] config_data_indices_remove
*/
describe("mcp data", async () => {
let app: App;
let server: ReturnType<typeof getSystemMcp>;
beforeAll(async () => {
app = createApp({
initialConfig: {
server: {
mcp: {
enabled: true,
},
},
},
});
await app.build();
server = getSystemMcp(app);
});
});

View File

@@ -0,0 +1,39 @@
import { describe, it, expect, beforeAll } from "bun:test";
import { type App, createApp } from "core/test/utils";
import { getSystemMcp } from "modules/mcp/system-mcp";
import { registries } from "index";
import { StorageLocalAdapter } from "adapter/node/storage/StorageLocalAdapter";
/**
* - [ ] config_media_get
* - [ ] config_media_update
* - [ ] config_media_adapter_get
* - [ ] config_media_adapter_update
*/
describe("mcp media", async () => {
let app: App;
let server: ReturnType<typeof getSystemMcp>;
beforeAll(async () => {
registries.media.register("local", StorageLocalAdapter);
app = createApp({
initialConfig: {
media: {
enabled: true,
adapter: {
type: "local",
config: {
path: "./",
},
},
},
server: {
mcp: {
enabled: true,
},
},
},
});
await app.build();
server = getSystemMcp(app);
});
});

View File

@@ -0,0 +1,38 @@
import { describe, test, expect, beforeAll } from "bun:test";
import { type App, createApp, createMcpToolCaller } from "core/test/utils";
import { getSystemMcp } from "modules/mcp/system-mcp";
import { inspect } from "node:util";
inspect.defaultOptions.depth = 10;
/**
* - [ ] system_config
* - [ ] system_build
* - [ ] system_ping
* - [ ] system_info
* - [ ] config_server_get
* - [ ] config_server_update
*/
describe("mcp system", async () => {
let app: App;
let server: ReturnType<typeof getSystemMcp>;
beforeAll(async () => {
app = createApp({
initialConfig: {
server: {
mcp: {
enabled: true,
},
},
},
});
await app.build();
server = getSystemMcp(app);
});
const tool = createMcpToolCaller();
test("system_ping", async () => {
const result = await tool(server, "system_ping", {});
expect(result).toEqual({ pong: true });
});
});

View File

@@ -2,4 +2,5 @@
#registry = "http://localhost:4873"
[test]
coverageSkipTestFiles = true
coverageSkipTestFiles = true
console.depth = 10

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.7.1",
"jsonv-ts": "^0.7.2",
"kysely": "0.27.6",
"lodash-es": "^4.17.21",
"oauth4webapi": "^2.11.1",

View File

@@ -1,6 +1,6 @@
import type { CliCommand } from "cli/types";
import { makeAppFromEnv } from "../run";
import { getSystemMcp } from "modules/server/system-mcp";
import { getSystemMcp } from "modules/mcp/system-mcp";
import { $console } from "bknd/utils";
import { stdioTransport } from "jsonv-ts/mcp";

View File

@@ -1,6 +1,7 @@
import { createApp as createAppInternal, type CreateAppConfig } from "App";
import { bunSqlite } from "adapter/bun/connection/BunSqliteConnection";
import { Connection } from "data/connection/Connection";
import type { getSystemMcp } from "modules/mcp/system-mcp";
export { App } from "App";
@@ -10,3 +11,17 @@ export function createApp({ connection, ...config }: CreateAppConfig = {}) {
connection: Connection.isConnection(connection) ? connection : bunSqlite(connection as any),
});
}
export function createMcpToolCaller() {
return async (server: ReturnType<typeof getSystemMcp>, name: string, args: any) => {
const res = await server.handle({
jsonrpc: "2.0",
method: "tools/call",
params: {
name,
arguments: args,
},
});
return JSON.parse((res.result as any)?.content?.[0]?.text ?? "null");
};
}

View File

@@ -15,6 +15,7 @@ import {
openAPISpecs,
mcpTool,
mcp as mcpMiddleware,
isNode,
} from "bknd/utils";
import type { Context, Hono } from "hono";
import { Controller } from "modules/Controller";
@@ -28,7 +29,7 @@ import {
import * as SystemPermissions from "modules/permissions";
import { getVersion } from "core/env";
import type { Module } from "modules/Module";
import { getSystemMcp } from "./system-mcp";
import { getSystemMcp } from "modules/mcp/system-mcp";
export type ConfigUpdate<Key extends ModuleKey = ModuleKey> = {
success: true;
@@ -94,6 +95,8 @@ export class SystemController extends Controller {
},
endpoint: {
path: "/mcp",
// @ts-ignore
_init: isNode() ? { duplex: "half" } : {},
},
}),
);
@@ -365,7 +368,10 @@ export class SystemController extends Controller {
}),
(c) =>
c.json({
version: c.get("app")?.version(),
version: {
config: c.get("app")?.version(),
bknd: getVersion(),
},
runtime: getRuntimeKey(),
connection: {
name: this.app.em.connection.name,