import { z } from 'zod';
/**
* Validation schemas for MCP tool parameters
*/
export const ToolParameterSchemas = {
// Common pagination parameters
pagination: z.object({
page_size: z.number().int().min(1).max(100).optional(),
start_cursor: z.string().optional(),
}),
// Database query parameters
databaseQuery: z.object({
database_id: z.string().min(1, 'Database ID is required'),
filter: z.object({}).optional(), // Complex filter object - will be validated by Notion API
sorts: z.array(z.object({})).optional(), // Array of sort objects
start_cursor: z.string().optional(),
page_size: z.number().int().min(1).max(100).optional(),
}),
// Page creation parameters
createPage: z.object({
parent: z.object({
database_id: z.string().min(1, 'Database ID is required').optional(),
page_id: z.string().min(1, 'Page ID is required').optional(),
}).refine((parent) => parent.database_id || parent.page_id, {
message: 'Either database_id or page_id must be provided in parent',
}),
properties: z.record(z.string(), z.any()).refine(
(obj) => Object.keys(obj).length > 0,
{ message: 'At least one property is required' }
),
children: z.array(z.object({})).optional(),
icon: z.object({
type: z.enum(['emoji', 'file', 'external']),
emoji: z.string().optional(),
file: z.object({}).optional(),
external: z.object({}).optional(),
}).optional(),
cover: z.object({
type: z.enum(['external', 'file']),
external: z.object({}).optional(),
file: z.object({}).optional(),
}).optional(),
}),
// Page update parameters
updatePage: z.object({
page_id: z.string().min(1, 'Page ID is required'),
properties: z.record(z.any()).optional(),
archived: z.boolean().optional(),
icon: z.object({
type: z.enum(['emoji', 'file', 'external']),
emoji: z.string().optional(),
file: z.object({}).optional(),
external: z.object({}).optional(),
}).optional(),
cover: z.object({
type: z.enum(['external', 'file']),
external: z.object({}).optional(),
file: z.object({}).optional(),
}).optional(),
}),
// Block operations
blockChildren: z.object({
block_id: z.string().min(1, 'Block ID is required'),
start_cursor: z.string().optional(),
page_size: z.number().int().min(1).max(100).optional(),
}),
appendBlockChildren: z.object({
block_id: z.string().min(1, 'Block ID is required'),
children: z.array(z.object({
object: z.literal('block'),
type: z.string(),
// Allow any additional properties
...Object.fromEntries(
['string', 'number', 'boolean', 'object', 'array'].map(type => [type, z.any()])
)
})).min(1, 'At least one block child is required'),
}),
// Search parameters
search: z.object({
query: z.string().min(1, 'Search query is required'),
filter: z.object({
property: z.string(),
value: z.object({}),
}).optional(),
sort: z.object({
direction: z.enum(['ascending', 'descending']),
timestamp: z.enum(['created_time', 'last_edited_time']),
}).optional(),
start_cursor: z.string().optional(),
page_size: z.number().int().min(1).max(100).optional(),
}),
} as const;
/**
* Generic parameter validation function
*/
export function validateToolParameters<T>(
schema: z.ZodSchema<T>,
params: unknown
): { success: true; data: T } | { success: false; error: string } {
try {
const data = schema.parse(params);
return { success: true, data };
} catch (error) {
if (error instanceof z.ZodError) {
const errorMessages = error.errors.map(
(e) => `${e.path.join('.')}: ${e.message}`
);
return {
success: false,
error: `Invalid parameters: ${errorMessages.join(', ')}`
};
}
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown validation error'
};
}
}