UNPKG

pocketsmith-mcp

Version:

MCP server for managing budgets via PocketSmith API

81 lines (80 loc) 3.38 kB
/** * @fileoverview Reusable schema helpers for nullable PocketSmith API fields */ import { z } from "zod"; /** * Creates a nullable field schema that accepts both the type and null */ export const createNullableField = (baseType, description) => { return z.union([baseType, z.null()]).describe(description); }; /** * Creates an optional nullable field schema */ export const createOptionalNullableField = (baseType, description) => { return createNullableField(baseType, description).optional(); }; /** * Common nullable field definitions used across PocketSmith tools */ export const NULLABLE_FIELDS = { // Category-related fields parent_id: createOptionalNullableField(z.number().int(), "Parent category ID"), category_id: createOptionalNullableField(z.number().int(), "Category ID"), colour: createOptionalNullableField(z.string(), "Color (hex format)"), // Transaction-related fields note: createOptionalNullableField(z.string(), "Notes"), memo: createOptionalNullableField(z.string(), "Memo"), // Account-related fields safe_balance: createOptionalNullableField(z.number(), "Safe balance"), current_balance_exchange_rate: createOptionalNullableField(z.number(), "Exchange rate"), safe_balance_in_base_currency: createOptionalNullableField(z.number(), "Safe balance in base currency"), // General metadata fields updated_at: createOptionalNullableField(z.string(), "Last update timestamp"), created_at: createOptionalNullableField(z.string(), "Creation timestamp"), symbol: createOptionalNullableField(z.string(), "Symbol"), description: createOptionalNullableField(z.string(), "Description"), // Account number (often null for some account types) number: createOptionalNullableField(z.string(), "Account number"), }; /** * Required nullable fields (not optional, but can be null) */ export const REQUIRED_NULLABLE_FIELDS = { // Category-related fields that are always present but can be null parent_id: createNullableField(z.number().int(), "Parent category ID"), colour: createNullableField(z.string(), "Color (hex format)"), // Transaction-related fields that are always present but can be null note: createNullableField(z.string(), "Notes"), memo: createNullableField(z.string(), "Memo"), // Account-related fields safe_balance: createNullableField(z.number(), "Safe balance"), current_balance_exchange_rate: createNullableField(z.number(), "Exchange rate"), safe_balance_in_base_currency: createNullableField(z.number(), "Safe balance in base currency"), number: createNullableField(z.string(), "Account number"), }; /** * Helper function to clean up data to match nullable schemas */ export function cleanNullableFields(data, fieldNames) { const cleaned = { ...data }; fieldNames.forEach(field => { // Convert undefined to null for explicit nullable fields if (cleaned[field] === undefined) { cleaned[field] = null; } }); return cleaned; } /** * Helper to validate that nullable fields are properly handled */ export function validateNullableField(value, field, allowNull = true) { if (value === null && !allowNull) { throw new Error(`Field ${field} cannot be null`); } if (value === undefined && allowNull) { return null; } return value; }