Skip to main content
Glama
validation.ts6.13 kB
import { z } from "zod"; // Validation schemas for MCP tools export const PaginationSchema = z .object({ page: z.number().optional(), count: z.number().optional(), sort: z.string().optional(), }) .optional(); export const TagSchema = z.object({ name: z.string(), value: z.string(), order: z.number().optional(), }); export const CreateBookSchema = z.object({ name: z.string().min(1).max(255), description: z.string().optional(), description_html: z.string().optional(), tags: z.array(TagSchema).optional(), default_template_id: z.number().optional(), }); export const UpdateBookSchema = CreateBookSchema.partial(); export const CreateChapterSchema = z.object({ book_id: z.number(), name: z.string().min(1).max(255), description: z.string().optional(), description_html: z.string().optional(), tags: z.array(TagSchema).optional(), priority: z.number().optional(), default_template_id: z.number().optional(), }); export const UpdateChapterSchema = CreateChapterSchema.partial().omit({ book_id: true, }); export const CreatePageSchema = z .object({ book_id: z.number().optional(), chapter_id: z.number().optional(), name: z.string().min(1).max(255), html: z.string().optional(), markdown: z.string().optional(), tags: z.array(TagSchema).optional(), priority: z.number().optional(), }) .refine((data) => data.book_id || data.chapter_id, { message: "Either book_id or chapter_id must be provided", }) .refine((data) => data.html || data.markdown, { message: "Either html or markdown content must be provided", }); export const UpdatePageSchema = z.object({ book_id: z.number().optional(), chapter_id: z.number().optional(), name: z.string().min(1).max(255).optional(), html: z.string().optional(), markdown: z.string().optional(), tags: z.array(TagSchema).optional(), priority: z.number().optional(), }); export const CreateShelfSchema = z.object({ name: z.string().min(1).max(255), description: z.string().optional(), description_html: z.string().optional(), books: z.array(z.number()).optional(), tags: z.array(TagSchema).optional(), }); export const UpdateShelfSchema = CreateShelfSchema.partial(); export const CreateUserSchema = z.object({ name: z.string().min(1).max(100), email: z.string().email(), external_auth_id: z.string().optional(), language: z.string().max(15).optional(), password: z.string().min(8).optional(), roles: z.array(z.number()).optional(), send_invite: z.boolean().optional(), }); export const UpdateUserSchema = CreateUserSchema.partial(); export const CreateRoleSchema = z.object({ display_name: z.string().min(3).max(180), description: z.string().max(180).optional(), mfa_enforced: z.boolean().optional(), external_auth_id: z.string().max(180).optional(), permissions: z.array(z.string()).optional(), }); export const UpdateRoleSchema = CreateRoleSchema.partial(); export const CreateAttachmentSchema = z.object({ name: z.string().min(1).max(255), uploaded_to: z.number(), link: z.string().optional(), }); export const UpdateAttachmentSchema = CreateAttachmentSchema.partial().omit({ uploaded_to: true, }); export const CreateImageSchema = z.object({ type: z.enum(["gallery", "drawio"]), uploaded_to: z.number(), name: z.string().max(180).optional(), }); export const UpdateImageSchema = z.object({ name: z.string().max(180).optional(), }); export const SearchSchema = z.object({ query: z.string().min(1), page: z.number().optional(), count: z.number().optional(), }); export const ContentPermissionsSchema = z.object({ owner_id: z.number().optional(), role_permissions: z .array( z.object({ role_id: z.number(), view: z.boolean(), create: z.boolean(), update: z.boolean(), delete: z.boolean(), }) ) .optional(), fallback_permissions: z .object({ inheriting: z.boolean(), view: z.boolean().optional(), create: z.boolean().optional(), update: z.boolean().optional(), delete: z.boolean().optional(), }) .optional(), }); // Helper functions for common operations export function formatApiResponse<T>(data: T, total?: number): string { const response = total !== undefined ? { data, total } : data; return JSON.stringify(response, null, 2); } export function parseInteger(value: unknown): number { if (typeof value === "number") return value; if (typeof value === "string") { const parsed = parseInt(value, 10); if (isNaN(parsed)) throw new Error(`Invalid integer: ${value}`); return parsed; } throw new Error(`Expected integer, got ${typeof value}`); } export function parseTags( tags: unknown ): Array<{ name: string; value: string; order?: number }> { if (!tags) return []; if (typeof tags === "string") { try { tags = JSON.parse(tags); } catch { throw new Error("Invalid JSON for tags"); } } return z.array(TagSchema).parse(tags); } export function parseBoolean(value: unknown): boolean { if (typeof value === "boolean") return value; if (typeof value === "string") { const lower = value.toLowerCase(); if (lower === "true" || lower === "1") return true; if (lower === "false" || lower === "0") return false; } throw new Error(`Invalid boolean: ${value}`); } export function handleApiError(error: any): string { if (error.response) { // API returned an error response const status = error.response.status; const message = error.response.data?.message || error.response.statusText; return `API Error ${status}: ${message}`; } else if (error.request) { // Network error return `Network Error: Unable to reach BookStack API`; } else { // Other error return `Error: ${error.message}`; } } export function validateContentType(contentType: string): string { const validTypes = ["page", "book", "chapter", "bookshelf"]; if (!validTypes.includes(contentType)) { throw new Error( `Invalid content type. Must be one of: ${validTypes.join(", ")}` ); } return contentType; }

Implementation Reference

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/lautarobarba/bookstack_mcp_server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server