Skip to main content
Glama
timer-stop.ts4.14 kB
/** * Timer Stop Tool * * Stops a running timer by updating the TimeEntry with active=false. * FreshBooks automatically calculates the duration based on the elapsed time * between startedAt and now. */ import { z } from "zod"; import { TimerStopInputSchema, TimerStopOutputSchema } from "./schemas.js"; import { ErrorHandler } from "../../errors/error-handler.js"; import { ToolContext } from "../../errors/types.js"; import { FreshBooksClientWrapper } from "../../client/freshbooks-client.js"; /** * Tool definition for timer_stop */ export const timerStopTool = { name: "timer_stop", description: `Stop a running timer and log the time in FreshBooks. WHEN TO USE: - User says "stop timer", "stop tracking", "clock out" - User finishes work on a task/project and wants to log the time - User wants to pause work and save the time entry HOW IT WORKS: Updates the time entry with: - active=false (timer stops running) - isLogged=true (time is now logged) - duration: Auto-calculated by FreshBooks from startedAt to now AUTOMATIC DURATION CALCULATION: FreshBooks automatically calculates the duration based on: - startedAt: When timer was started - Now: Current timestamp when stopped - Result: Duration in seconds REQUIRED: - accountId: FreshBooks account ID - timeEntryId: ID of the running timer (from timer_start or timer_current) OPTIONAL: - note: Update the description before stopping (adds/modifies work notes) FINDING THE TIMER ID: If you don't know the timer ID: 1. Use timer_current to get all active timers 2. The response includes the timeEntryId for each active timer WHAT IF NO TIMER IS RUNNING: - If the timeEntryId doesn't exist: Returns "not found" error - If timer is already stopped: Updates it anyway (idempotent) EXAMPLE USAGE: - "Stop my timer" (requires getting current timer first) - "Stop timer 12345" - "Clock out and add note: completed bug fixes" RETURNS: Updated time entry with: - id: The time entry ID - active: false (timer has stopped) - duration: Auto-calculated time in seconds - startedAt: Original start time - note: Work description (updated if provided) - isLogged: true (time is now logged)`, inputSchema: TimerStopInputSchema, outputSchema: TimerStopOutputSchema, async execute( input: z.infer<typeof TimerStopInputSchema>, client: FreshBooksClientWrapper ): Promise<z.infer<typeof TimerStopOutputSchema>> { const handler = ErrorHandler.wrapHandler( 'timer_stop', async (input: z.infer<typeof TimerStopInputSchema>, _context: ToolContext) => { const { accountId, timeEntryId, note } = input; // Build the update data to stop the timer const updateData: Record<string, unknown> = { active: false, // Stop the timer isLogged: true, // Mark time as logged // Note: duration is auto-calculated by FreshBooks }; // Optionally update the note if (note !== undefined) { updateData.note = note; } // Use the FreshBooks client to update the time entry const result = await client.executeWithRetry( "timer_stop", async (fbClient) => { const response = await fbClient.timeEntries.update( updateData as any, parseInt(accountId), timeEntryId ); if (!response.ok) { throw response.error; } return response.data; } ); // Extract the time entry from the response // FreshBooks returns: { time_entry: { ... } } const timeEntry = (result as { time_entry?: unknown }).time_entry ?? result; return timeEntry as z.infer<typeof TimerStopOutputSchema>; } ); return handler(input, { accountId: input.accountId }); }, }; /** * Wrapped handler for backward compatibility with tests */ export async function timerStopHandler( input: z.infer<typeof TimerStopInputSchema>, context: ToolContext & { client: FreshBooksClientWrapper } ): Promise<z.infer<typeof TimerStopOutputSchema>> { return timerStopTool.execute(input, context.client); }

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