Skip to main content
Glama

Stampchain MCP Server

Official
validators.ts5.35 kB
/** * Validation utility functions * These utilities provide additional validation beyond Zod schemas */ import { z } from 'zod'; import { BitcoinAddressSchema, TransactionHashSchema, CPIDSchema } from '../schemas/common.js'; import type { BitcoinAddress, TransactionHash, CPID } from '../schemas/common.js'; /** * Validate and parse a stamp ID * Accepts both number and string formats */ export function parseStampId(input: unknown): number { if (typeof input === 'number') { if (!Number.isInteger(input) || input < 0) { throw new Error('Stamp ID must be a non-negative integer'); } return input; } if (typeof input === 'string') { const parsed = parseInt(input, 10); if (isNaN(parsed) || parsed < 0) { throw new Error('Invalid stamp ID format'); } return parsed; } throw new Error('Stamp ID must be a number or string'); } /** * Validate pagination parameters */ export function validatePagination( page?: number, pageSize?: number ): { page: number; pageSize: number } { const validPage = page && page > 0 ? page : 1; const validPageSize = pageSize && pageSize > 0 && pageSize <= 100 ? pageSize : 20; return { page: validPage, pageSize: validPageSize }; } /** * Validate and normalize a Bitcoin address */ export function validateBitcoinAddress(address: string): BitcoinAddress { const trimmed = address.trim(); return BitcoinAddressSchema.parse(trimmed); } /** * Validate and normalize a transaction hash */ export function validateTransactionHash(hash: string): TransactionHash { const normalized = hash.toLowerCase().trim(); return TransactionHashSchema.parse(normalized); } /** * Validate and normalize a CPID */ export function validateCPID(cpid: string): CPID { const trimmed = cpid.trim(); return CPIDSchema.parse(trimmed); } /** * Check if a string is a valid stamp identifier (either numeric ID or CPID) */ export function isStampIdentifier(value: string): { type: 'id' | 'cpid'; value: number | string } { // Check if it's a numeric ID const numericId = parseInt(value, 10); if (!isNaN(numericId) && numericId >= 0) { return { type: 'id', value: numericId }; } // Check if it's a valid CPID try { const cpid = validateCPID(value); return { type: 'cpid', value: cpid }; } catch { throw new Error('Invalid stamp identifier. Must be a numeric ID or valid CPID'); } } /** * Validate date range parameters */ export function validateDateRange( startDate?: string, endDate?: string ): { startDate?: Date; endDate?: Date } { const result: { startDate?: Date; endDate?: Date } = {}; if (startDate) { const start = new Date(startDate); if (isNaN(start.getTime())) { throw new Error('Invalid start date format'); } result.startDate = start; } if (endDate) { const end = new Date(endDate); if (isNaN(end.getTime())) { throw new Error('Invalid end date format'); } result.endDate = end; } if (result.startDate && result.endDate && result.startDate > result.endDate) { throw new Error('Start date must be before end date'); } return result; } /** * Validate and parse a boolean parameter that might come as a string */ export function parseBoolean(value: unknown): boolean | undefined { if (value === undefined || value === null) { return undefined; } if (typeof value === 'boolean') { return value; } if (typeof value === 'string') { const lower = value.toLowerCase(); if (lower === 'true' || lower === '1' || lower === 'yes') { return true; } if (lower === 'false' || lower === '0' || lower === 'no') { return false; } } throw new Error('Invalid boolean value'); } /** * Validate supply amount based on divisibility */ export function validateSupply(supply: number, divisible: boolean): void { if (supply < 0) { throw new Error('Supply cannot be negative'); } if (!divisible && !Number.isInteger(supply)) { throw new Error('Non-divisible assets must have integer supply'); } const MAX_SUPPLY = 1000000000000000; // 1 quadrillion if (supply > MAX_SUPPLY) { throw new Error('Supply exceeds maximum allowed value'); } } /** * Create a Zod schema that coerces string numbers to actual numbers */ export function coerceNumber() { return z.union([ z.number(), z .string() .regex(/^\d+$/) .transform((val) => parseInt(val, 10)), ]); } /** * Create a Zod schema that coerces string booleans to actual booleans */ export function coerceBoolean() { return z.union([ z.boolean(), z.literal('true').transform(() => true), z.literal('false').transform(() => false), z.literal('1').transform(() => true), z.literal('0').transform(() => false), ]); } /** * Sanitize user input to prevent injection attacks */ export function sanitizeInput(input: string): string { // Remove any control characters // eslint-disable-next-line no-control-regex const sanitized = input.replace(/[\x00-\x1F\x7F]/g, ''); // Trim whitespace return sanitized.trim(); } /** * Validate array limits */ export function validateArrayLimit<T>(array: T[], maxLength: number, itemName: string): void { if (array.length > maxLength) { throw new Error(`Too many ${itemName}. Maximum allowed: ${maxLength}`); } }

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/stampchain-io/stampchain-mcp'

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