MCP server for interacting with Harvest time tracking API
The server provides 5 time-tracking tools with clear verb-prefixed names (get_, list_, create_, update_) and reasonable descriptions. However, there are significant gaps: most parameter descriptions are minimal or missing, output schemas are undocumented, and error handling provides no recovery guidance. The tool set is well-focused on a single domain (Harvest time tracking), but parameter validation rules, enum constraints, and response field documentation are largely absent. Parameter handling is inconsistent, some parameters lack descriptions or type clarity, and there's no guidance on idempotency or non-destructible operation patterns for write operations.
Create a time entry via duration. Creates a new time entry object. Returns a time entry object and a 201 Created response code if the call succeeded. You should only use this method to create time entries when your account is configured to track time via duration. You can verify this by visiting the Settings page in your Harvest account or by checking if wants_timestamp_timers is false in the Company API.
Get information about the current authenticated user. Returns detailed information about the authenticated Harvest user, including name, email, role, and other account details.
Retrieve a time entry by ID. Retrieves the time entry with the given ID. Returns a time entry object and a 200 OK response code if a valid identifier was provided.
List time entries from Harvest. Args: from_date: Start date in YYYY-MM-DD format (optional) to_date: End date in YYYY-MM-DD format (optional) Returns: A dictionary containing time entries Raises: HarvestAPIError: If the API returns an error response
Update a time entry. Updates the specific time entry by setting the values of the parameters passed. Any parameters not provided will be left unchanged. Returns a time entry object and a 200 OK response code if the call succeeded.
Output schemas are not documented. The tools return JSON from the Harvest API, but LLMs have no visibility into the response structure (fields, types, required vs optional). This forces LLMs to guess at field names when composing downstream calls.
Minimal parameter descriptions. E.g., list_time_entries has 'from_date' and 'to_date' with generic descriptions ('Start date in YYYY-MM-DD format (optional)'). No guidance on edge cases, dependencies, or when to use each. update_time_entry has 11 parameters but most descriptions are single-phrase placeholders (e.g., 'The ID of the project to associate with the time entry'). LLMs cannot infer constraints or mutual exclusivity.
Inferred effective spec: <=2025-11-25.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-21 | D | 55 | <=2025-11-25 | v2 |
| 2026-03-09 | F | 16 | - | v1 |
No enum constraints for categorical parameters. create_time_entry and update_time_entry accept flexible objects (e.g., 'external_reference' as a free-form Dict). No guidance on valid object shapes, required/optional fields within those objects, or examples of valid structures. LLMs will hallucinate invalid payloads.
No pagination or result-limiting guidance. list_time_entries does not document max_results, limit, offset, or page_cursor parameters. If the Harvest API returns hundreds of time entries, the response could consume the entire context window. The tool description says it returns 'A dictionary containing time entries' with no indication of scaling or caps.
Error handling provides no recovery guidance. The HarvestAPIError is raised but does not tell the LLM what to do next. E.g., a 404 'not found' error does not suggest calling list_time_entries() to discover valid IDs. A 401 'unauthorized' error does not explain how to check credentials. Raw HTTP status codes and response text offer no actionable next steps.
No idempotency or confirmation guidance for write operations. create_time_entry and update_time_entry are destructive (they modify state). The docstrings do not declare whether repeated calls with identical parameters are safe to retry or will cause duplicate entries/charges. No dry-run or confirmation pattern exists.
Parameter 'time_entry_id' is a string, but no guidance on format. Is it a numeric ID cast to string, a UUID, or an opaque token? The LLM must infer this from context or trial. A format description ('e.g., numeric ID') would clarify.
Secrets are handled well (stored as env vars, not exposed as params), but responses are not filtered. If the Harvest API returns a token or session ID in the response, it enters the LLM context. No evidence of response filtering or masking in the code paths that return user data.