Skip to main content
Glama
common.js6.84 kB
import { z } from 'zod'; import sanitizeHtml from 'sanitize-html'; /** * Common Zod schemas for validation across all Ghost MCP resources. * These validators provide consistent validation and security controls. */ /** * HTML sanitization configuration * Prevents XSS attacks by allowing only safe HTML tags and attributes */ const htmlSanitizeConfig = { allowedTags: [ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'p', 'a', 'ul', 'ol', 'nl', 'li', 'b', 'i', 'strong', 'em', 'strike', 'code', 'hr', 'br', 'div', 'span', 'img', 'pre', 'figure', 'figcaption', ], allowedAttributes: { a: ['href', 'name', 'target', 'rel', 'title'], img: ['src', 'alt', 'title', 'width', 'height'], '*': ['class', 'id'], }, allowedSchemes: ['http', 'https', 'mailto'], allowedSchemesAppliedToAttributes: ['href', 'src'], }; // ----- Basic Type Validators ----- /** * Email validation schema * Validates proper email format */ export const emailSchema = z.string().email('Invalid email format'); /** * URL validation schema * Validates proper URL format (http/https) */ export const urlSchema = z.string().url('Invalid URL format'); /** * ISO 8601 datetime validation schema * Validates ISO datetime strings */ export const isoDateSchema = z.string().datetime('Invalid ISO 8601 datetime format'); /** * Slug validation schema * Validates URL-friendly slugs (lowercase alphanumeric with hyphens) */ export const slugSchema = z .string() .regex(/^[a-z0-9-]+$/, 'Slug must contain only lowercase letters, numbers, and hyphens'); /** * Ghost ID validation schema * Validates 24-character hexadecimal Ghost object IDs */ export const ghostIdSchema = z .string() .regex(/^[a-f0-9]{24}$/, 'Invalid Ghost ID format (must be 24 hex characters)'); // ----- Security Validators ----- /** * NQL (Ghost Query Language) filter validation schema * Prevents injection attacks by restricting allowed characters * Allows: alphanumeric, underscores, hyphens, colons, dots, quotes, spaces, commas, brackets, comparison operators */ export const nqlFilterSchema = z .string() .regex(/^[a-zA-Z0-9_\-:.'"\s,[\]<>=!+]+$/, 'Invalid NQL filter: contains disallowed characters') .optional(); // ----- Pagination Validators ----- /** * Limit validation schema for pagination * Restricts result count between 1 and 100 */ export const limitSchema = z .number() .int('Limit must be an integer') .min(1, 'Limit must be at least 1') .max(100, 'Limit cannot exceed 100') .default(15); /** * Page number validation schema for pagination * Must be a positive integer starting from 1 */ export const pageSchema = z .number() .int('Page must be an integer') .min(1, 'Page must be at least 1') .default(1); /** * Complete pagination options schema */ export const paginationSchema = z.object({ limit: limitSchema, page: pageSchema, }); // ----- Status Enum Validators ----- /** * Post/Page status validation schema * Valid values: draft, published, scheduled */ export const postStatusSchema = z.enum(['draft', 'published', 'scheduled'], { errorMap: () => ({ message: 'Status must be draft, published, or scheduled' }), }); /** * Visibility validation schema * Controls content visibility (public, members, paid, tiers) */ export const visibilitySchema = z.enum(['public', 'members', 'paid', 'tiers'], { errorMap: () => ({ message: 'Visibility must be public, members, paid, or tiers' }), }); // ----- Common Field Validators ----- /** * HTML content validation schema * Validates that content is a non-empty string and sanitizes HTML to prevent XSS * Uses transform to sanitize HTML at schema level (defense-in-depth) */ export const htmlContentSchema = z .string() .min(1, 'HTML content cannot be empty') .transform((html) => sanitizeHtml(html, htmlSanitizeConfig)); /** * Title validation schema * Validates post/page titles (1-255 characters) */ export const titleSchema = z .string() .min(1, 'Title cannot be empty') .max(255, 'Title cannot exceed 255 characters'); /** * Excerpt/description validation schema * Optional text snippet (max 500 characters) */ export const excerptSchema = z.string().max(500, 'Excerpt cannot exceed 500 characters').optional(); /** * Meta title validation schema (SEO) * Optional SEO title (max 300 characters) */ export const metaTitleSchema = z .string() .max(300, 'Meta title cannot exceed 300 characters') .optional(); /** * Meta description validation schema (SEO) * Optional SEO description (max 500 characters) */ export const metaDescriptionSchema = z .string() .max(500, 'Meta description cannot exceed 500 characters') .optional(); /** * Featured flag validation schema * Boolean indicating if content is featured */ export const featuredSchema = z.boolean().default(false); /** * Feature image URL validation schema * Optional URL for featured image */ export const featureImageSchema = z.string().url('Invalid feature image URL').optional(); /** * Feature image alt text validation schema * Optional alt text for accessibility */ export const featureImageAltSchema = z .string() .max(125, 'Feature image alt text cannot exceed 125 characters') .optional(); /** * Tag name validation schema * Tag names (1-191 characters) */ export const tagNameSchema = z .string() .min(1, 'Tag name cannot be empty') .max(191, 'Tag name cannot exceed 191 characters'); /** * Authors array validation schema * Array of Ghost author IDs or email addresses */ export const authorsSchema = z.array(z.string()).optional(); /** * Tags array validation schema * Array of tag names or Ghost tag IDs */ export const tagsSchema = z.array(z.string()).optional(); // ----- Utility Validators ----- /** * Canonical URL validation schema * Optional canonical URL for SEO */ export const canonicalUrlSchema = z.string().url('Invalid canonical URL').optional(); /** * Code injection validation schema * Optional HTML/JS code injection (use with caution) */ export const codeInjectionSchema = z .object({ head: z.string().optional(), foot: z.string().optional(), }) .optional(); /** * OG (Open Graph) image validation schema * Optional social media image URL */ export const ogImageSchema = z.string().url('Invalid OG image URL').optional(); /** * Twitter image validation schema * Optional Twitter card image URL */ export const twitterImageSchema = z.string().url('Invalid Twitter image URL').optional(); /** * Custom excerpt validation schema for social media * Optional custom excerpt for social sharing */ export const customExcerptSchema = z .string() .max(300, 'Custom excerpt cannot exceed 300 characters') .optional();

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/jgardner04/Ghost-MCP-Server'

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