/**
* @copyright 2025 Chris Bunting <cbuntingde@gmail.com>
* @license MIT
*
* Validation utilities for the Memory MCP Server
*/
import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
export class ValidationUtils {
/**
* Validates that a value is a non-empty string
*/
static validateNonEmptyString(value: any, fieldName: string): void {
if (typeof value !== 'string' || value.trim() === '') {
throw new McpError(
ErrorCode.InvalidParams,
`${fieldName} must be a non-empty string`
);
}
}
/**
* Validates that a value is a valid user ID
*/
static validateUserId(userId: any): void {
this.validateNonEmptyString(userId, 'userId');
// Additional validation for user ID format if needed
if (userId.length > 100) {
throw new McpError(
ErrorCode.InvalidParams,
'userId must be 100 characters or less'
);
}
}
/**
* Validates that a value is a valid session ID
*/
static validateSessionId(sessionId: any): void {
this.validateNonEmptyString(sessionId, 'sessionId');
// Additional validation for session ID format if needed
if (sessionId.length > 100) {
throw new McpError(
ErrorCode.InvalidParams,
'sessionId must be 100 characters or less'
);
}
}
/**
* Validates that a value is a valid memory key
*/
static validateMemoryKey(key: any): void {
this.validateNonEmptyString(key, 'key');
// Additional validation for key format if needed
if (key.length > 50) {
throw new McpError(
ErrorCode.InvalidParams,
'key must be 50 characters or less'
);
}
}
/**
* Validates that a value is a valid TTL in minutes
*/
static validateTTLMinutes(ttlMinutes: any): void {
if (ttlMinutes !== undefined) {
if (typeof ttlMinutes !== 'number' || ttlMinutes <= 0) {
throw new McpError(
ErrorCode.InvalidParams,
'ttlMinutes must be a positive number'
);
}
if (ttlMinutes > 1440) { // 24 hours
throw new McpError(
ErrorCode.InvalidParams,
'ttlMinutes must be 1440 minutes (24 hours) or less'
);
}
}
}
/**
* Validates that a value is a valid limit
*/
static validateLimit(limit: any): void {
if (limit !== undefined) {
if (typeof limit !== 'number' || limit <= 0) {
throw new McpError(
ErrorCode.InvalidParams,
'limit must be a positive number'
);
}
if (limit > 1000) {
throw new McpError(
ErrorCode.InvalidParams,
'limit must be 1000 or less'
);
}
}
}
/**
* Validates that a value is a valid search query
*/
static validateSearchQuery(query: any): void {
this.validateNonEmptyString(query, 'query');
if (query.length > 200) {
throw new McpError(
ErrorCode.InvalidParams,
'query must be 200 characters or less'
);
}
}
/**
* Validates that a value is a valid sentiment
*/
static validateSentiment(sentiment: any): void {
if (sentiment !== undefined) {
if (!['positive', 'negative', 'neutral'].includes(sentiment)) {
throw new McpError(
ErrorCode.InvalidParams,
'sentiment must be one of: positive, negative, neutral'
);
}
}
}
/**
* Validates that a value is a valid array of tags
*/
static validateTags(tags: any): void {
if (tags !== undefined) {
if (!Array.isArray(tags)) {
throw new McpError(
ErrorCode.InvalidParams,
'tags must be an array'
);
}
if (tags.length > 20) {
throw new McpError(
ErrorCode.InvalidParams,
'tags must contain 20 items or less'
);
}
for (const tag of tags) {
if (typeof tag !== 'string' || tag.trim() === '') {
throw new McpError(
ErrorCode.InvalidParams,
'all tags must be non-empty strings'
);
}
if (tag.length > 30) {
throw new McpError(
ErrorCode.InvalidParams,
'each tag must be 30 characters or less'
);
}
}
}
}
/**
* Validates that a value is a valid episodic memory event
*/
static validateEpisodicEvent(event: any): void {
this.validateNonEmptyString(event, 'event');
if (event.length > 500) {
throw new McpError(
ErrorCode.InvalidParams,
'event must be 500 characters or less'
);
}
}
/**
* Validates that a value is a valid episodic memory context
*/
static validateEpisodicContext(context: any): void {
this.validateNonEmptyString(context, 'context');
if (context.length > 1000) {
throw new McpError(
ErrorCode.InvalidParams,
'context must be 1000 characters or less'
);
}
}
/**
* Validates that a value is a valid episodic memory outcome
*/
static validateEpisodicOutcome(outcome: any): void {
if (outcome !== undefined) {
if (typeof outcome !== 'string') {
throw new McpError(
ErrorCode.InvalidParams,
'outcome must be a string'
);
}
if (outcome.length > 500) {
throw new McpError(
ErrorCode.InvalidParams,
'outcome must be 500 characters or less'
);
}
}
}
}