add_time_tracking
Add a time tracking entry to a task by providing start time and either end time or duration in minutes.
Instructions
Add a time tracking entry to a task. Supports started_at/finished_at or duration_minutes.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| dart_id | Yes | Task dart_id (also accepts "id" or "task_id") | |
| started_at | Yes | Start time in ISO8601 format (e.g., 2026-01-25T10:00:00Z) | |
| finished_at | No | End time in ISO8601 format (optional if duration_minutes provided) | |
| duration_minutes | No | Duration in minutes (optional if finished_at provided) | |
| note | No | Optional note about the time entry |
Implementation Reference
- src/tools/add_time_tracking.ts:1-95 (handler)Main handler function handleAddTimeTracking that validates input (ISO8601 dates, duration_minutes), resolves dart_id aliases, and calls DartClient.addTimeTracking to POST time entry data to the API.
/** * add_time_tracking Tool Handler * * Adds a time tracking entry to a task. * Supports started_at/finished_at timestamps or duration_minutes. */ import { DartClient } from '../api/dartClient.js'; import { AddTimeTrackingInput, AddTimeTrackingOutput, DartAPIError, ValidationError, resolveDartId, } from '../types/index.js'; // ISO8601 date pattern const ISO8601_PATTERN = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})?)?$/; /** * Handle add_time_tracking tool calls * * @param input - AddTimeTrackingInput with task_id and time entry details * @returns AddTimeTrackingOutput with created entry */ export async function handleAddTimeTracking(input: AddTimeTrackingInput): Promise<AddTimeTrackingOutput> { const DART_TOKEN = process.env.DART_TOKEN; if (!DART_TOKEN) { throw new DartAPIError( 'DART_TOKEN environment variable is required. Get your token from: https://app.dartai.com/?settings=account', 401 ); } // Validate input if (!input || typeof input !== 'object') { throw new ValidationError('Input must be an object', 'input'); } // Accept id, task_id, or taskId as aliases for dart_id input.dart_id = resolveDartId(input as unknown as Record<string, unknown>); if (!input.started_at || typeof input.started_at !== 'string') { throw new ValidationError('started_at is required and must be an ISO8601 string', 'started_at'); } if (!ISO8601_PATTERN.test(input.started_at)) { throw new ValidationError( 'started_at must be a valid ISO8601 date (e.g., 2026-01-25T10:00:00Z)', 'started_at' ); } if (input.finished_at !== undefined) { if (typeof input.finished_at !== 'string' || !ISO8601_PATTERN.test(input.finished_at)) { throw new ValidationError( 'finished_at must be a valid ISO8601 date if provided', 'finished_at' ); } } if (input.duration_minutes !== undefined) { if (typeof input.duration_minutes !== 'number' || input.duration_minutes < 0) { throw new ValidationError( 'duration_minutes must be a non-negative number if provided', 'duration_minutes' ); } } const client = new DartClient({ token: DART_TOKEN }); const result = await client.addTimeTracking({ dart_id: input.dart_id.trim(), started_at: input.started_at, finished_at: input.finished_at, duration_minutes: input.duration_minutes, note: input.note, }); return { entry: { entry_id: result.entry_id, dart_id: result.dart_id, started_at: result.started_at, finished_at: result.finished_at, duration_minutes: result.duration_minutes, note: result.note, }, task_id: input.dart_id.trim(), url: `https://app.dartai.com/task/${input.dart_id.trim()}`, }; } - src/index.ts:877-906 (registration)Tool registration with name 'add_time_tracking', description, and inputSchema defining dart_id, started_at, finished_at, duration_minutes, and note properties.
{ name: 'add_time_tracking', description: 'Add a time tracking entry to a task. Supports started_at/finished_at or duration_minutes.', inputSchema: { type: 'object', properties: { dart_id: { type: 'string', description: 'Task dart_id (also accepts "id" or "task_id")', }, started_at: { type: 'string', description: 'Start time in ISO8601 format (e.g., 2026-01-25T10:00:00Z)', }, finished_at: { type: 'string', description: 'End time in ISO8601 format (optional if duration_minutes provided)', }, duration_minutes: { type: 'integer', description: 'Duration in minutes (optional if finished_at provided)', }, note: { type: 'string', description: 'Optional note about the time entry', }, }, required: ['dart_id', 'started_at'], }, }, - src/index.ts:1220-1230 (registration)Case handler in the main tool dispatcher that calls handleAddTimeTracking and returns the result as JSON text content.
case 'add_time_tracking': { const result = await handleAddTimeTracking((args || {}) as any); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } - src/types/index.ts:843-864 (schema)TypeScript interfaces: AddTimeTrackingInput (dart_id, started_at, finished_at?, duration_minutes?, note?), TimeTrackingEntry, and AddTimeTrackingOutput (entry, task_id, url).
export interface AddTimeTrackingInput { dart_id: string; started_at: string; finished_at?: string; duration_minutes?: number; note?: string; } export interface TimeTrackingEntry { entry_id: string; dart_id: string; started_at: string; finished_at?: string; duration_minutes: number; note?: string; } export interface AddTimeTrackingOutput { entry: TimeTrackingEntry; task_id: string; url: string; } - src/api/dartClient.ts:627-690 (helper)API client method addTimeTracking that computes finishedAt from duration if needed, resolves current user via config, and POSTs to /tasks/{dart_id}/time-tracking endpoint.
/** * Add time tracking entry to a task */ async addTimeTracking(input: { dart_id: string; started_at: string; finished_at?: string; duration_minutes?: number; note?: string; }): Promise<{ entry_id: string; dart_id: string; started_at: string; finished_at?: string; duration_minutes: number; note?: string; }> { if (!input.dart_id || typeof input.dart_id !== 'string' || input.dart_id.trim() === '') { throw new DartAPIError('dart_id is required and must be a non-empty string', 400); } if (!input.started_at || typeof input.started_at !== 'string') { throw new DartAPIError('started_at is required and must be an ISO8601 string', 400); } // Compute finishedAt from duration if not provided let finishedAt = input.finished_at; if (!finishedAt && input.duration_minutes) { const start = new Date(input.started_at); finishedAt = new Date(start.getTime() + input.duration_minutes * 60000).toISOString(); } if (!finishedAt) { throw new DartAPIError('Either finished_at or duration_minutes is required', 400); } // Get current user from config for the required user field const config = await this.getConfig(); const currentUser = config.user || config.assignees?.find(a => a.email); const userId = currentUser?.dart_id || currentUser?.email || currentUser?.name; if (!userId) { throw new DartAPIError('Cannot determine current user for time tracking. Check workspace config.', 400); } const apiInput: Record<string, unknown> = { user: userId, startedAt: input.started_at, finishedAt: finishedAt, }; if (input.note !== undefined) apiInput.note = input.note; const response = await this.request<{ item: any }>( 'POST', `/tasks/${encodeURIComponent(input.dart_id.trim())}/time-tracking`, apiInput ); return { entry_id: response.item?.id || '', dart_id: input.dart_id, started_at: response.item?.startedAt || input.started_at, finished_at: response.item?.finishedAt, duration_minutes: response.item?.durationMinutes || input.duration_minutes || 0, note: response.item?.note, }; }