Skip to main content
Glama
time-tracking-schemas.ts7.48 kB
import { z } from 'zod'; // ======================================== // TIME TRACKING VALIDATION SCHEMAS // ======================================== // Team ID validation export const TeamIdSchema = z.string().min(1, 'Team ID is required'); // Timer ID validation export const TimerIdSchema = z.string().min(1, 'Timer ID is required'); // Task ID validation (optional for time entries) export const TaskIdSchema = z.string().min(1, 'Task ID is required'); // User ID validation export const UserIdSchema = z.number().positive('User ID must be positive'); // ======================================== // TIME ENTRY SCHEMAS // ======================================== // Time entry tag schema export const TimeEntryTagSchema = z.object({ name: z.string().min(1, 'Tag name is required'), tag_fg: z.string().optional(), tag_bg: z.string().optional(), creator: z.number().optional() }); // Create time entry schema export const CreateTimeEntrySchema = z.object({ team_id: TeamIdSchema, description: z.string().min(1, 'Description is required'), start: z.number().positive('Start time must be a positive Unix timestamp'), billable: z.boolean().default(false), end: z.number().positive().optional(), task_id: z.string().optional(), assignee: UserIdSchema.optional(), tags: z.array(TimeEntryTagSchema).optional() }); // Update time entry schema export const UpdateTimeEntrySchema = z.object({ team_id: TeamIdSchema, timer_id: TimerIdSchema, description: z.string().min(1).optional(), start: z.number().positive().optional(), end: z.number().positive().optional(), billable: z.boolean().optional(), task_id: z.string().optional(), tags: z.array(TimeEntryTagSchema).optional() }); // Delete time entry schema export const DeleteTimeEntrySchema = z.object({ team_id: TeamIdSchema, timer_id: TimerIdSchema }); // Get time entries schema export const GetTimeEntriesSchema = z.object({ team_id: TeamIdSchema, start_date: z.number().positive().optional(), end_date: z.number().positive().optional(), assignee: UserIdSchema.optional(), include_task_tags: z.boolean().optional().default(false), include_location_names: z.boolean().optional().default(false), space_id: z.string().optional(), folder_id: z.string().optional(), list_id: z.string().optional(), task_id: z.string().optional() }); // ======================================== // TIMER OPERATION SCHEMAS // ======================================== // Start timer schema export const StartTimerSchema = z.object({ team_id: TeamIdSchema, timer_id: TimerIdSchema, start: z.number().positive().optional() }); // Stop timer schema export const StopTimerSchema = z.object({ team_id: TeamIdSchema, timer_id: TimerIdSchema, end: z.number().positive().optional() }); // Get running timers schema export const GetRunningTimersSchema = z.object({ team_id: TeamIdSchema, assignee: UserIdSchema.optional() }); // ======================================== // TIME SUMMARY SCHEMAS // ======================================== // Get time summary schema export const GetTimeSummarySchema = z.object({ team_id: TeamIdSchema, start_date: z.number().positive().optional(), end_date: z.number().positive().optional(), assignee: UserIdSchema.optional(), task_id: z.string().optional(), list_id: z.string().optional(), folder_id: z.string().optional(), space_id: z.string().optional(), billable_only: z.boolean().optional().default(false) }); // ======================================== // HELPER SCHEMAS // ======================================== // Duration format schema export const DurationFormatSchema = z.enum(['milliseconds', 'seconds', 'minutes', 'hours']); // Time format validation export const TimeFormatSchema = z.object({ format: DurationFormatSchema.optional().default('milliseconds'), include_seconds: z.boolean().optional().default(true) }); // Billable time filter schema export const BillableFilterSchema = z.enum(['all', 'billable', 'non_billable']); // ======================================== // RESPONSE TYPE SCHEMAS // ======================================== // Time entry response schema export const TimeEntryResponseSchema = z.object({ id: z.string(), task: z.object({ id: z.string(), name: z.string(), status: z.object({ status: z.string(), color: z.string(), type: z.string(), orderindex: z.number() }), custom_type: z.string().nullable() }).nullable(), wid: z.string(), user: z.object({ id: z.number(), username: z.string(), email: z.string(), color: z.string(), initials: z.string(), profilePicture: z.string() }), billable: z.boolean(), start: z.string(), end: z.string().nullable(), duration: z.string(), description: z.string(), tags: z.array(TimeEntryTagSchema), source: z.string(), at: z.string() }); // Running timer response schema export const RunningTimerResponseSchema = z.object({ id: z.string(), task: z.object({ id: z.string(), name: z.string() }).nullable(), user: z.object({ id: z.number(), username: z.string(), email: z.string() }), start: z.string(), description: z.string(), billable: z.boolean(), tags: z.array(TimeEntryTagSchema) }); // ======================================== // UTILITY FUNCTIONS // ======================================== /** * Convert duration from milliseconds to specified format */ export function convertDuration(milliseconds: number, format: 'milliseconds' | 'seconds' | 'minutes' | 'hours'): number { switch (format) { case 'milliseconds': return milliseconds; case 'seconds': return Math.floor(milliseconds / 1000); case 'minutes': return Math.floor(milliseconds / (1000 * 60)); case 'hours': return Math.floor(milliseconds / (1000 * 60 * 60)); default: return milliseconds; } } /** * Format duration for human-readable display */ export function formatDuration(milliseconds: number, includeSeconds: boolean = true): string { const hours = Math.floor(milliseconds / (1000 * 60 * 60)); const minutes = Math.floor((milliseconds % (1000 * 60 * 60)) / (1000 * 60)); const seconds = Math.floor((milliseconds % (1000 * 60)) / 1000); if (hours > 0) { return includeSeconds ? `${hours}h ${minutes}m ${seconds}s` : `${hours}h ${minutes}m`; } else if (minutes > 0) { return includeSeconds ? `${minutes}m ${seconds}s` : `${minutes}m`; } return includeSeconds ? `${seconds}s` : '0m'; } /** * Validate time range (start must be before end) */ export function validateTimeRange(start: number, end?: number): boolean { if (end === undefined) return true; return start < end; } /** * Get current Unix timestamp in milliseconds */ export function getCurrentTimestamp(): number { return Date.now(); } // ======================================== // COMBINED TOOL SCHEMAS // ======================================== export const TimeTrackingToolSchemas = { // Time entry operations createTimeEntry: CreateTimeEntrySchema, updateTimeEntry: UpdateTimeEntrySchema, deleteTimeEntry: DeleteTimeEntrySchema, getTimeEntries: GetTimeEntriesSchema, // Timer operations startTimer: StartTimerSchema, stopTimer: StopTimerSchema, getRunningTimers: GetRunningTimersSchema, // Time analytics getTimeSummary: GetTimeSummarySchema, // Utility schemas timeFormat: TimeFormatSchema, billableFilter: BillableFilterSchema };

Latest Blog Posts

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/Chykalophia/ClickUp-MCP-Server---Enhanced'

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