Merge pull request #258 from bknd-io/fix/mcp-auth-and-insert

fix: handle numbered object conversion and update MCP tool URL
This commit is contained in:
dswbx
2025-09-14 17:09:55 +02:00
committed by GitHub
5 changed files with 30 additions and 7 deletions

View File

@@ -1,10 +1,7 @@
{ {
"mcpServers": { "mcpServers": {
"bknd": { "bknd": {
"url": "http://localhost:3000/mcp", "url": "http://localhost:28623/api/system/mcp"
"headers": {
"API_KEY": "value"
}
} }
} }
} }

View File

@@ -221,6 +221,7 @@ export class AuthController extends Controller {
return user; return user;
}; };
const roles = Object.keys(this.auth.config.roles ?? {});
mcp.tool( mcp.tool(
// @todo: needs permission // @todo: needs permission
"auth_user_create", "auth_user_create",
@@ -231,7 +232,7 @@ export class AuthController extends Controller {
password: s.string({ minLength: 8 }), password: s.string({ minLength: 8 }),
role: s role: s
.string({ .string({
enum: Object.keys(this.auth.config.roles ?? {}), enum: roles.length > 0 ? roles : undefined,
}) })
.optional(), .optional(),
}), }),

View File

@@ -473,3 +473,10 @@ export function deepFreeze<T extends object>(object: T): T {
return Object.freeze(object); return Object.freeze(object);
} }
export function convertNumberedObjectToArray(obj: object): any[] | object {
if (Object.keys(obj).every((key) => Number.isInteger(Number(key)))) {
return Object.values(obj);
}
return obj;
}

View File

@@ -1,6 +1,15 @@
import type { ModuleBuildContext } from "modules"; import type { ModuleBuildContext } from "modules";
import { Controller } from "modules/Controller"; import { Controller } from "modules/Controller";
import { jsc, s, describeRoute, schemaToSpec, omitKeys, pickKeys, mcpTool } from "bknd/utils"; import {
jsc,
s,
describeRoute,
schemaToSpec,
omitKeys,
pickKeys,
mcpTool,
convertNumberedObjectToArray,
} from "bknd/utils";
import * as SystemPermissions from "modules/permissions"; import * as SystemPermissions from "modules/permissions";
import type { AppDataConfig } from "../data-schema"; import type { AppDataConfig } from "../data-schema";
import type { EntityManager, EntityData } from "data/entities"; import type { EntityManager, EntityData } from "data/entities";
@@ -420,7 +429,13 @@ export class DataController extends Controller {
if (!this.entityExists(entity)) { if (!this.entityExists(entity)) {
return this.notFound(c); return this.notFound(c);
} }
const body = (await c.req.json()) as EntityData | EntityData[];
const _body = (await c.req.json()) as EntityData | EntityData[];
// @todo: check on jsonv-ts how to handle this better
// temporary fix for numbered object to array
// this happens when the MCP tool uses the allOf function
// to transform all validation targets into a single object
const body = convertNumberedObjectToArray(_body);
if (Array.isArray(body)) { if (Array.isArray(body)) {
const result = await this.em.mutator(entity).insertMany(body); const result = await this.em.mutator(entity).insertMany(body);

View File

@@ -9,6 +9,9 @@ import { nodeSqlite } from "./src/adapter/node/connection/NodeSqliteConnection";
import { libsql } from "./src/data/connection/sqlite/libsql/LibsqlConnection"; import { libsql } from "./src/data/connection/sqlite/libsql/LibsqlConnection";
import { $console } from "core/utils/console"; import { $console } from "core/utils/console";
import { createClient } from "@libsql/client"; import { createClient } from "@libsql/client";
import util from "node:util";
util.inspect.defaultOptions.depth = 5;
registries.media.register("local", StorageLocalAdapter); registries.media.register("local", StorageLocalAdapter);