create_time_entry
Log work hours in Harvest by specifying project, task, date, and either duration or start/end times to track billable time.
Instructions
Create a new time entry. Requires project_id, task_id, and spent_date. Must provide either hours OR both started_time and ended_time.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| project_id | Yes | The project ID to log time against | |
| task_id | Yes | The task ID to log time against | |
| spent_date | Yes | The date the time was spent (YYYY-MM-DD) | |
| started_time | No | Start time in HH:MM format (24-hour) | |
| ended_time | No | End time in HH:MM format (24-hour) | |
| hours | No | Decimal hours (e.g., 0.5 = 30min, 1.25 = 1h15m) | |
| notes | No | Notes for the time entry | |
| external_reference | No |
Implementation Reference
- src/tools/time-entries.ts:61-77 (handler)Handler class that executes the create_time_entry tool logic.
class CreateTimeEntryHandler implements ToolHandler { constructor(private readonly config: BaseToolConfig) {} async execute(args: Record<string, any>): Promise<CallToolResult> { try { const validatedArgs = validateInput(CreateTimeEntrySchema, args, 'create time entry'); logger.info('Creating time entry via Harvest API'); const timeEntry = await this.config.harvestClient.createTimeEntry(validatedArgs); return { content: [{ type: 'text', text: JSON.stringify(timeEntry, null, 2) }], }; } catch (error) { return handleMCPToolError(error, 'create_time_entry'); } } } - src/schemas/time-entry.ts:97-120 (schema)Zod schema definition for validating inputs to the create_time_entry tool.
export const CreateTimeEntrySchema = z.object({ project_id: z.number().int().positive(), task_id: z.number().int().positive(), spent_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, 'Date must be in YYYY-MM-DD format'), started_time: z.string().regex(/^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$/).optional(), // HH:MM ended_time: z.string().regex(/^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$/).optional(), // HH:MM hours: z.number().min(0).max(24).optional(), // Either hours or start/end time notes: z.string().max(2000).optional(), // Harvest has a notes limit external_reference: z.object({ id: z.string().optional(), group_id: z.string().optional(), account_id: z.string().optional(), permalink: z.string().url().optional(), }).optional(), }).refine((data) => { // Must have either hours or both start/end times const hasHours = data.hours !== undefined && data.hours > 0; const hasTimes = data.started_time && data.ended_time; return hasHours || hasTimes; }, { message: "Must provide either 'hours' or both 'started_time' and 'ended_time'", }); // For updates, we need to work around the refined schema - src/tools/time-entries.ts:213-242 (registration)Registration of the create_time_entry tool.
tool: { name: 'create_time_entry', description: 'Create a new time entry. Requires project_id, task_id, and spent_date. Must provide either hours OR both started_time and ended_time.', inputSchema: { type: 'object', properties: { project_id: { type: 'number', description: 'The project ID to log time against' }, task_id: { type: 'number', description: 'The task ID to log time against' }, spent_date: { type: 'string', format: 'date', description: 'The date the time was spent (YYYY-MM-DD)' }, started_time: { type: 'string', pattern: '^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$', description: 'Start time in HH:MM format (24-hour)' }, ended_time: { type: 'string', pattern: '^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$', description: 'End time in HH:MM format (24-hour)' }, hours: { type: 'number', minimum: 0, maximum: 24, description: 'Decimal hours (e.g., 0.5 = 30min, 1.25 = 1h15m)' }, notes: { type: 'string', maxLength: 2000, description: 'Notes for the time entry' }, external_reference: { type: 'object', properties: { id: { type: 'string', description: 'External reference ID' }, group_id: { type: 'string', description: 'External group ID' }, account_id: { type: 'string', description: 'External account ID' }, permalink: { type: 'string', format: 'uri', description: 'External permalink URL' }, }, additionalProperties: false, }, }, required: ['project_id', 'task_id', 'spent_date'], additionalProperties: false, }, }, handler: new CreateTimeEntryHandler(config), },