Skip to main content
Glama
timeentry-create.ts5.45 kB
/** * timeentry_create Tool * * Create a new time entry in FreshBooks. */ import { z } from 'zod'; import { TimeEntryCreateInputSchema, TimeEntrySingleOutputSchema } from './schemas.js'; import { FreshBooksClientWrapper } from '../../client/index.js'; import { ErrorHandler } from '../../errors/error-handler.js'; import { ToolContext } from '../../errors/types.js'; import { logger } from '../../utils/logger.js'; /** * Tool definition for timeentry_create */ export const timeentryCreateTool = { name: 'timeentry_create', description: `Create a new time entry in FreshBooks. WHEN TO USE: - User says "log my time", "track time", "record hours", "add time entry" - User mentions working on a project with duration - User wants to record billable or non-billable time - User asks "log 2 hours on project X", "track 45 minutes of work" REQUIRED: - accountId: FreshBooks account ID (get from auth_status if not specified) - duration: Time in seconds (or ask user for hours/minutes and convert) * 1 hour = 3600 seconds * 1 minute = 60 seconds * Example: 2 hours 30 minutes = 9000 seconds OPTIONAL BUT RECOMMENDED: - note: Description of work performed (improves billing accuracy) - projectId: Associate with a project - clientId: Associate with a client - serviceId: Service type for billing categorization - taskId: Associate with a task - billable: Whether time is billable (default: true if isLogged=true) - startedAt: When work began (ISO 8601 format, defaults to now) - isLogged: Whether time is logged (default: true) * Set to false for active timers (with duration=0 and active=true) SPECIAL CASES: - To start a timer: Set duration=0, active=true, isLogged=false - For completed work: Set duration to actual seconds, isLogged=true RETURNS: Created time entry with: - id: New time entry ID (save this for updates/deletes) - duration: Confirmed duration in seconds - All other time entry fields EXAMPLES: User says: "Log 2 hours on Project Alpha for code review" → duration: 7200 (2 * 3600) → note: "code review" → projectId: (look up Project Alpha) User says: "Track 45 minutes of meeting time" → duration: 2700 (45 * 60) → note: "meeting" User says: "Record 3 hours of development work for client ABC" → duration: 10800 (3 * 3600) → note: "development work" → clientId: (look up client ABC)`, inputSchema: TimeEntryCreateInputSchema, outputSchema: TimeEntrySingleOutputSchema, /** * Execute the tool */ async execute( input: z.infer<typeof TimeEntryCreateInputSchema>, client: FreshBooksClientWrapper ): Promise<z.infer<typeof TimeEntrySingleOutputSchema>> { const handler = ErrorHandler.wrapHandler( 'timeentry_create', async ( input: z.infer<typeof TimeEntryCreateInputSchema>, _context: ToolContext ) => { logger.debug('Creating time entry', { accountId: input.accountId, duration: input.duration, projectId: input.projectId, isLogged: input.isLogged, }); const result = await client.executeWithRetry( 'timeentry_create', async (fbClient) => { // Build time entry data object const timeEntryData: any = { duration: input.duration, isLogged: input.isLogged, }; // Add startedAt (default to now if not specified) if (input.startedAt) { timeEntryData.startedAt = new Date(input.startedAt); } else { timeEntryData.startedAt = new Date(); } // Add optional fields if provided if (input.note !== undefined) { timeEntryData.note = input.note; } if (input.projectId !== undefined) { timeEntryData.projectId = input.projectId; } if (input.clientId !== undefined) { timeEntryData.clientId = input.clientId; } if (input.serviceId !== undefined) { timeEntryData.serviceId = input.serviceId; } if (input.taskId !== undefined) { timeEntryData.taskId = input.taskId; } if (input.billable !== undefined) { timeEntryData.billable = input.billable; } if (input.active !== undefined) { timeEntryData.active = input.active; } if (input.internal !== undefined) { timeEntryData.internal = input.internal; } if (input.retainerId !== undefined) { timeEntryData.retainerId = input.retainerId; } const response = await fbClient.timeEntries.create( timeEntryData, parseInt(input.accountId) ); if (!response.ok) { throw response.error; } if (!response.data) { throw ErrorHandler.createValidationError( 'Failed to create time entry: No data returned from API' ); } return response.data; } ); logger.info('Time entry created successfully', { timeEntryId: result.id, duration: result.duration, isLogged: result.isLogged, }); return result as any; } ); return handler(input, { accountId: input.accountId }) as any; }, };

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/Good-Samaritan-Software-LLC/freshbooks-mcp'

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