MCP-Server für Buchhaltung und Organisation ergänzen
Fügt einen geschützten MCP-JSON-RPC-Endpunkt mit Buchhaltungs-Tools und Aufgaben-Tools hinzu. Berechtigungen werden rollenbasiert pro Mandant geprüft und die Auth-Logik berücksichtigt nun alle Rollen eines Nutzers.
This commit is contained in:
183
backend/src/mcp/tools/organisation.ts
Normal file
183
backend/src/mcp/tools/organisation.ts
Normal file
@@ -0,0 +1,183 @@
|
||||
import { and, desc, eq, ilike, or } from "drizzle-orm"
|
||||
import { tasks } from "../../../db/schema"
|
||||
import { McpTool } from "../types"
|
||||
|
||||
const limitFromArgs = (args: Record<string, unknown>, fallback = 25) => {
|
||||
const raw = Number(args.limit ?? fallback)
|
||||
if (!Number.isFinite(raw)) return fallback
|
||||
return Math.min(Math.max(Math.trunc(raw), 1), 100)
|
||||
}
|
||||
|
||||
const stringArg = (args: Record<string, unknown>, key: string) => {
|
||||
const value = args[key]
|
||||
return typeof value === "string" && value.trim() ? value.trim() : null
|
||||
}
|
||||
|
||||
const numberArg = (args: Record<string, unknown>, key: string) => {
|
||||
const value = Number(args[key])
|
||||
return Number.isFinite(value) ? value : null
|
||||
}
|
||||
|
||||
export const organisationTools: McpTool[] = [
|
||||
{
|
||||
name: "organisation.tasks.list",
|
||||
title: "Aufgaben auflisten",
|
||||
description: "Listet Aufgaben des aktiven Mandanten mit optionalen Filtern.",
|
||||
requiredPermissions: ["organisation.tasks.read"],
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
query: { type: "string", description: "Suchtext für Name, Beschreibung oder Kategorie." },
|
||||
project: { type: "number" },
|
||||
customer: { type: "number" },
|
||||
includeArchived: { type: "boolean", default: false },
|
||||
limit: { type: "number", minimum: 1, maximum: 100 },
|
||||
},
|
||||
},
|
||||
async handler(context, args) {
|
||||
const conditions = [eq(tasks.tenant, context.tenantId)]
|
||||
const query = stringArg(args, "query")
|
||||
const project = numberArg(args, "project")
|
||||
const customer = numberArg(args, "customer")
|
||||
|
||||
if (query) {
|
||||
conditions.push(or(
|
||||
ilike(tasks.name, `%${query}%`),
|
||||
ilike(tasks.description, `%${query}%`),
|
||||
ilike(tasks.categorie, `%${query}%`)
|
||||
))
|
||||
}
|
||||
if (project) conditions.push(eq(tasks.project, project))
|
||||
if (customer) conditions.push(eq(tasks.customer, customer))
|
||||
if (args.includeArchived !== true) conditions.push(eq(tasks.archived, false))
|
||||
|
||||
const rows = await context.server.db
|
||||
.select()
|
||||
.from(tasks)
|
||||
.where(and(...conditions))
|
||||
.orderBy(desc(tasks.createdAt))
|
||||
.limit(limitFromArgs(args))
|
||||
|
||||
return { rows }
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "organisation.tasks.get",
|
||||
title: "Aufgabe laden",
|
||||
description: "Lädt eine Aufgabe des aktiven Mandanten anhand ihrer ID.",
|
||||
requiredPermissions: ["organisation.tasks.read"],
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
required: ["id"],
|
||||
properties: {
|
||||
id: { type: "number" },
|
||||
},
|
||||
},
|
||||
async handler(context, args) {
|
||||
const id = numberArg(args, "id")
|
||||
if (!id) throw new Error("id ist erforderlich")
|
||||
|
||||
const rows = await context.server.db
|
||||
.select()
|
||||
.from(tasks)
|
||||
.where(and(eq(tasks.id, id), eq(tasks.tenant, context.tenantId)))
|
||||
.limit(1)
|
||||
|
||||
if (!rows[0]) throw new Error("Aufgabe nicht gefunden")
|
||||
return { task: rows[0] }
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "organisation.tasks.create",
|
||||
title: "Aufgabe erstellen",
|
||||
description: "Erstellt eine neue Aufgabe im aktiven Mandanten.",
|
||||
requiredPermissions: ["organisation.tasks.write"],
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
required: ["name"],
|
||||
properties: {
|
||||
name: { type: "string" },
|
||||
description: { type: "string" },
|
||||
categorie: { type: "string" },
|
||||
project: { type: "number" },
|
||||
plant: { type: "number" },
|
||||
customer: { type: "number" },
|
||||
userId: { type: "string" },
|
||||
profiles: { type: "array", items: { type: "string" } },
|
||||
},
|
||||
},
|
||||
async handler(context, args) {
|
||||
const name = stringArg(args, "name")
|
||||
if (!name) throw new Error("name ist erforderlich")
|
||||
|
||||
const [created] = await context.server.db
|
||||
.insert(tasks)
|
||||
.values({
|
||||
name,
|
||||
description: stringArg(args, "description"),
|
||||
categorie: stringArg(args, "categorie"),
|
||||
tenant: context.tenantId,
|
||||
userId: stringArg(args, "userId") || context.userId,
|
||||
project: numberArg(args, "project"),
|
||||
plant: numberArg(args, "plant"),
|
||||
customer: numberArg(args, "customer"),
|
||||
profiles: Array.isArray(args.profiles) ? args.profiles : [],
|
||||
updatedAt: new Date(),
|
||||
updatedBy: context.userId,
|
||||
})
|
||||
.returning()
|
||||
|
||||
return { task: created }
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "organisation.tasks.update",
|
||||
title: "Aufgabe aktualisieren",
|
||||
description: "Aktualisiert Felder einer Aufgabe im aktiven Mandanten.",
|
||||
requiredPermissions: ["organisation.tasks.write"],
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
required: ["id"],
|
||||
properties: {
|
||||
id: { type: "number" },
|
||||
name: { type: "string" },
|
||||
description: { type: "string" },
|
||||
categorie: { type: "string" },
|
||||
project: { type: "number" },
|
||||
plant: { type: "number" },
|
||||
customer: { type: "number" },
|
||||
userId: { type: "string" },
|
||||
profiles: { type: "array", items: { type: "string" } },
|
||||
archived: { type: "boolean" },
|
||||
},
|
||||
},
|
||||
async handler(context, args) {
|
||||
const id = numberArg(args, "id")
|
||||
if (!id) throw new Error("id ist erforderlich")
|
||||
|
||||
const update: Record<string, unknown> = {
|
||||
updatedAt: new Date(),
|
||||
updatedBy: context.userId,
|
||||
}
|
||||
|
||||
for (const key of ["name", "description", "categorie", "userId"] as const) {
|
||||
if (args[key] !== undefined) update[key] = stringArg(args, key)
|
||||
}
|
||||
for (const key of ["project", "plant", "customer"] as const) {
|
||||
if (args[key] !== undefined) update[key] = numberArg(args, key)
|
||||
}
|
||||
if (Array.isArray(args.profiles)) update.profiles = args.profiles
|
||||
if (typeof args.archived === "boolean") update.archived = args.archived
|
||||
|
||||
const [updated] = await context.server.db
|
||||
.update(tasks)
|
||||
.set(update)
|
||||
.where(and(eq(tasks.id, id), eq(tasks.tenant, context.tenantId)))
|
||||
.returning()
|
||||
|
||||
if (!updated) throw new Error("Aufgabe nicht gefunden")
|
||||
return { task: updated }
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user