Skip to main content
Glama
validation.ts•4.91 kB
import { z } from 'zod'; const MAX_CONTENT_SIZE = 1048576; // 1MB const MAX_TAGS = 20; const MAX_TAG_LENGTH = 50; const MAX_USER_CONTEXT_LENGTH = 100; // Sanitization helpers const sanitizeString = (str: string): string => { // Remove null bytes and control characters except newlines and tabs // biome-ignore lint/suspicious/noControlCharactersInRegex: These control characters are intentionally removed for security return str.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, '').trim(); }; const sanitizeTag = (tag: string): string => { // Allow only alphanumeric, spaces, hyphens, and underscores in tags return tag .replace(/[^a-zA-Z0-9\s\-_]/g, '') .trim() .substring(0, MAX_TAG_LENGTH); }; // Base schemas for reusability export const MemoryTypeSchema = z.enum([ 'fact', 'conversation', 'decision', 'insight', 'error', 'context', 'preference', 'task', 'merged', 'summary', ]); export const RelationTypeSchema = z.enum(['references', 'contradicts', 'supports', 'extends']); // Input validation schemas export const StoreMemorySchema = z.object({ content: z.any().refine((data) => JSON.stringify(data).length <= MAX_CONTENT_SIZE, { message: `Content exceeds ${MAX_CONTENT_SIZE} bytes`, }), type: MemoryTypeSchema, tags: z .array(z.string().transform(sanitizeTag).pipe(z.string().min(1).max(MAX_TAG_LENGTH))) .max(MAX_TAGS) .optional() .default([]), source: z.string().min(1).max(200).transform(sanitizeString), confidence: z.number().min(0).max(1), parent_id: z.string().uuid().optional(), relation_type: RelationTypeSchema.optional(), importance_score: z.number().min(0).max(1).default(0.5), user_context: z.string().max(MAX_USER_CONTEXT_LENGTH).transform(sanitizeString).optional(), relate_to: z .array( z.object({ memory_id: z.string().uuid(), relation_type: RelationTypeSchema, strength: z.number().min(0).max(1).default(0.5), }) ) .optional(), }); export const SearchMemorySchema = z.object({ query: z.string().min(1).max(1000).transform(sanitizeString), type: MemoryTypeSchema.optional(), tags: z.array(z.string().transform(sanitizeTag)).optional(), limit: z.number().int().min(1).max(100).default(10), threshold: z.number().min(0).max(1).default(0.35), user_context: z.string().max(MAX_USER_CONTEXT_LENGTH).transform(sanitizeString).optional(), include_relations: z.boolean().default(false), }); export const UpdateMemorySchema = z.object({ id: z.string().uuid(), updates: z.object({ tags: z .array(z.string().transform(sanitizeTag).pipe(z.string().min(1).max(MAX_TAG_LENGTH))) .max(MAX_TAGS) .optional(), confidence: z.number().min(0).max(1).optional(), importance_score: z.number().min(0).max(1).optional(), type: MemoryTypeSchema.optional(), source: z.string().min(1).max(200).transform(sanitizeString).optional(), }), preserve_timestamps: z.boolean().default(false), }); export const DeleteMemorySchema = z .object({ id: z.string().uuid().optional(), content_hash: z.string().optional(), }) .refine((data) => data.id || data.content_hash, { message: 'Either id or content_hash must be provided', }); export const ListMemorySchema = z.object({ type: MemoryTypeSchema.optional(), tags: z.array(z.string().transform(sanitizeTag)).optional(), limit: z.number().int().min(1).max(100).default(10), offset: z.number().int().min(0).default(0), user_context: z.string().max(MAX_USER_CONTEXT_LENGTH).transform(sanitizeString).optional(), }); export const BatchMemorySchema = z.object({ memories: z.array(StoreMemorySchema).min(1).max(100), user_context: z.string().max(MAX_USER_CONTEXT_LENGTH).transform(sanitizeString).optional(), }); export const GraphSearchSchema = SearchMemorySchema.extend({ depth: z.number().int().min(1).max(3).default(1), }); export const ConsolidateMemorySchema = z.object({ threshold: z.number().min(0.5).max(0.95).default(0.8), min_cluster_size: z.number().int().min(2).default(3), user_context: z.string().max(MAX_USER_CONTEXT_LENGTH).transform(sanitizeString).optional(), }); export const StatsSchema = z.object({ user_context: z.string().max(MAX_USER_CONTEXT_LENGTH).transform(sanitizeString).optional(), }); // Type inference export type StoreMemoryInput = z.infer<typeof StoreMemorySchema>; export type SearchMemoryInput = z.infer<typeof SearchMemorySchema>; export type UpdateMemoryInput = z.infer<typeof UpdateMemorySchema>; export type DeleteMemoryInput = z.infer<typeof DeleteMemorySchema>; export type ListMemoryInput = z.infer<typeof ListMemorySchema>; export type BatchMemoryInput = z.infer<typeof BatchMemorySchema>; export type GraphSearchInput = z.infer<typeof GraphSearchSchema>; export type ConsolidateMemoryInput = z.infer<typeof ConsolidateMemorySchema>; export type StatsInput = z.infer<typeof StatsSchema>;

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/scanadi/mcp-ai-memory'

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