Skip to content

Commit 970dbf2

Browse files
committed
Add harvest_update_time_entry tool
Inherited surface from southleft had create + stop_timer + delete but no update path — meaning edits required delete + recreate (losing the entry ID, breaking any external_reference links). Tool wraps HarvestClient.updateTimeEntry which was already implemented in src/harvest/client.ts. Schema mirrors UpdateTimeEntryParams: id required, every other field optional (PATCH semantics — only provided fields change). Tool count: 21 -> 22. README + landing-page anchor updated.
1 parent 71d16ea commit 970dbf2

3 files changed

Lines changed: 50 additions & 3 deletions

File tree

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,15 @@ Works the same in claude.ai web, Claude Desktop, and Claude Code — the Connect
5454
5555
---
5656

57-
## Available tools (21)
57+
## Available tools (22)
5858

5959
### Time tracking
6060
| Tool | Description |
6161
|------|-------------|
6262
| `harvest_list_time_entries` | List and filter time entries by user, client, project, date range |
6363
| `harvest_get_time_entry` | Get a specific time entry by ID |
6464
| `harvest_create_time_entry` | Create new time entries with optional timer |
65+
| `harvest_update_time_entry` | Patch fields on an existing time entry |
6566
| `harvest_stop_timer` | Stop a running timer |
6667
| `harvest_delete_time_entry` | Delete a time entry |
6768

src/tools/index.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,52 @@ export function registerTools(
358358
}
359359
);
360360

361+
server.tool(
362+
'harvest_update_time_entry',
363+
'Update an existing time entry. Only the provided fields are changed; others are left intact.',
364+
{
365+
id: z.number().describe('Time entry ID to update'),
366+
project_id: z.number().optional().describe('New project ID'),
367+
task_id: z.number().optional().describe('New task ID'),
368+
spent_date: z.string().optional().describe('New date (YYYY-MM-DD)'),
369+
hours: z.number().optional().describe('New hours'),
370+
notes: z.string().optional().describe('New notes/description'),
371+
started_time: z.string().optional().describe('New start time (HH:MM, 12h format like "9:00am")'),
372+
ended_time: z.string().optional().describe('New end time (HH:MM, 12h format like "5:00pm")'),
373+
},
374+
async ({ id, ...patch }) => {
375+
const client = await getHarvestClient(session, sessionStore, config);
376+
377+
if (!client) {
378+
const authUrl = getAuthUrl(session, config);
379+
return {
380+
content: [
381+
{
382+
type: 'text',
383+
text: `Authentication required. Please authorize the application:\n\n${authUrl}`,
384+
},
385+
],
386+
isError: true,
387+
};
388+
}
389+
390+
try {
391+
const timeEntry = await client.updateTimeEntry(id, patch);
392+
return {
393+
content: [{ type: 'text', text: JSON.stringify(timeEntry, null, 2) }],
394+
};
395+
} catch (error) {
396+
if (error instanceof HarvestApiError) {
397+
return {
398+
content: [{ type: 'text', text: `Harvest API error: ${error.message}` }],
399+
isError: true,
400+
};
401+
}
402+
throw error;
403+
}
404+
}
405+
);
406+
361407
server.tool(
362408
'harvest_stop_timer',
363409
'Stop a running timer on a time entry',

src/workers/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -490,9 +490,9 @@ app.get('/', (c) => {
490490
<div class="logo-text">Harvest <span>MCP</span></div>
491491
</a>
492492
<div class="header-links">
493-
<a href="https://github.com/TheBeyondGroup/harvest-mcp#available-tools-21" target="_blank">
493+
<a href="https://github.com/TheBeyondGroup/harvest-mcp#available-tools-22" target="_blank">
494494
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/></svg>
495-
21 Tools
495+
22 Tools
496496
</a>
497497
<a href="https://github.com/TheBeyondGroup/harvest-mcp" target="_blank">
498498
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>

0 commit comments

Comments
 (0)