clokio-mcp 0.28.0 → 0.28.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -54,12 +54,20 @@ export function entryClock(e, opts = {}) {
54
54
  * The zone is the entry's `timezone` (local times) or UTC (an older server).
55
55
  * `zone: false` drops a local entry's zone, for a page whose header names it once.
56
56
  */
57
+ /**
58
+ * What an entry is on, in words: its task, else its project (task #5775: an
59
+ * entry may be on a project with no task), else nothing. ONE copy for every
60
+ * line that names it (timeEntryLine, clokio_delete_time_entry), so they
61
+ * cannot drift (MCP QA 0.28.0).
62
+ */
63
+ export function entryTarget(e) {
64
+ if (e?.task)
65
+ return `task #${e.task.id} ${e.task.title}`;
66
+ return e?.project?.name ? `no task, project ${e.project.name}` : 'no task';
67
+ }
57
68
  export function timeEntryLine(e, opts = {}) {
58
69
  const id = e?.id === undefined || e?.id === null ? '(no id: server older than 2026-10-16)' : `#${e.id}`;
59
- // An entry may be on a project with no task (task #5775): name the project then.
60
- const task = e?.task
61
- ? `task #${e.task.id} ${e.task.title}`
62
- : e?.project?.name ? `no task, project ${e.project.name}` : 'no task';
70
+ const task = entryTarget(e);
63
71
  return `${id} ${e?.date ?? '-'} ${entryClock(e, opts)} ${e?.work_hour ?? 0}h | ${e?.employee_code ?? '-'} | ${task} | ${e?.description ?? ''}`;
64
72
  }
65
73
  const ZONE_NOTE = "the person's CURRENT location zone, which clokio_update_time_entry reads date/start/end in (after a location change it may differ from where the entry was logged)";
@@ -211,6 +219,21 @@ export function withShortEntryWarning(res) {
211
219
  return { ...res, warning };
212
220
  }
213
221
  /** #3: the line clokio_stop_timer adds when the stopped entry lasted under a minute, else null. */
222
+ /**
223
+ * clokio_start_timer's answer: the entry line, and when force: true stopped a
224
+ * running timer (the server's `stopped_entry`, backend 2026-10-21), a
225
+ * "Stopped first" line naming it and, for one under a minute, the same
226
+ * warning clokio_stop_timer gives (MCP QA 0.28.0). An older server sends no
227
+ * stopped_entry and the answer is as before.
228
+ */
229
+ export function withStoppedFirst(res) {
230
+ const out = withEntryLine(res);
231
+ const stopped = res?.stopped_entry;
232
+ if (!stopped || typeof stopped !== 'object' || out === null || typeof out !== 'object' || Array.isArray(out))
233
+ return out;
234
+ const warning = shortEntryWarning(stopped);
235
+ return { ...out, stopped_first: `Stopped first: ${timeEntryLine(stopped)}`, ...(warning ? { warning } : {}) };
236
+ }
214
237
  export function shortEntryWarning(entry) {
215
238
  const a = Date.parse(entry?.check_in ?? '');
216
239
  const b = Date.parse(entry?.check_out ?? '');
@@ -465,7 +488,8 @@ export function registerAttendanceTools(server, config) {
465
488
  name: 'clokio_start_timer',
466
489
  description: 'Start a running timer on a task, a project, or neither (a personal timer). ' + actingPerson + ' ' + targetRule + ' ' +
467
490
  'If a timer is already running the call is refused (409) and the running entry is returned; ' +
468
- 'pass force: true to stop that timer and start this one. The description defaults to the task title (empty without a task). ' +
491
+ 'pass force: true to stop that timer and start this one (the answer then names the stopped entry under stopped_first, ' +
492
+ 'with a warning when it lasted under a minute). The description defaults to the task title (empty without a task). ' +
469
493
  'clokio_stop_timer stops any timer; the web Timer page asks for a project and a description before its own Stop, ' +
470
494
  'so give a personal timer a description (and stop it here) if the person may stop it on the web. ' +
471
495
  ENTRY_LINE_NOTE,
@@ -479,7 +503,7 @@ export function registerAttendanceTools(server, config) {
479
503
  mutates: true,
480
504
  handler: async (args, cfg) => {
481
505
  try {
482
- return withEntryLine(await request(cfg, '/timer/start', {
506
+ return withStoppedFirst(await request(cfg, '/timer/start', {
483
507
  method: 'POST',
484
508
  body: { task_id: args.task_id, project_id: args.project_id, description: args.description, force: args.force },
485
509
  }));
@@ -558,8 +582,7 @@ export function registerAttendanceTools(server, config) {
558
582
  handler: async (args, cfg) => {
559
583
  const res = await request(cfg, `/time-entries/${seg(args.id)}`, { method: 'DELETE' });
560
584
  const e = res?.entry ?? {};
561
- const task = e.task ? `task #${e.task.id} ${e.task.title}` : 'no task';
562
- return `Deleted entry ${e.id ?? args.id}: ${e.date ?? '-'} ${entryClock(e)}, ${task}`;
585
+ return `Deleted entry ${e.id ?? args.id}: ${e.date ?? '-'} ${entryClock(e)}, ${entryTarget(e)}`;
563
586
  },
564
587
  });
565
588
  registerTool(server, config, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clokio-mcp",
3
- "version": "0.28.0",
3
+ "version": "0.28.1",
4
4
  "description": "Model Context Protocol (MCP) server for Clokio - manage tasks, projects, clients, employees, attendance and documents from Claude and any MCP client.",
5
5
  "type": "module",
6
6
  "bin": {