innov-mcp-tasks 1.0.2 → 1.2.0

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.
Files changed (3) hide show
  1. package/README.md +19 -0
  2. package/index.mjs +381 -0
  3. package/package.json +4 -2
package/README.md CHANGED
@@ -50,11 +50,30 @@ Na raiz do repo existe [`.cursor/mcp.json`](../.cursor/mcp.json) com **dois** se
50
50
 
51
51
  ## Ferramentas
52
52
 
53
+ ### Tarefas e projetos
54
+
53
55
  - `tasks_list` — opcional `project_id`
54
56
  - `my_tasks` — tarefas do utilizador do token (endpoint “minhas” da API)
57
+ - `projects_list` — lista projetos visíveis (`GET /api/v1/projects`)
55
58
  - `project_create` — cria projeto (`POST /api/v1/projects`)
56
59
  - `task_get`, `task_create`, `task_update_status`, `task_assign`, `task_assign_to_me`
57
60
 
61
+ ### Anotações / documentação
62
+
63
+ - `notes_list` — lista com filtros opcionais (`project_id`, `note_type`, `notebook_id`)
64
+ - `notes_personal` — anotações pessoais
65
+ - `notes_by_project` — anotações de um projeto (inclui criador em `user`)
66
+ - `note_get`, `note_create`, `note_update`
67
+ - `note_delete` — soft delete (lixeira)
68
+ - `notes_trashed`, `note_restore`
69
+ - `annotations_search` — busca em cadernos, notas e fontes (`q` ≥ 2 caracteres)
70
+
71
+ ### Cadernos (notebooks)
72
+
73
+ - `notebooks_list` — lista com filtro opcional `project_id`
74
+ - `notebook_get`, `notebook_create`, `notebook_update`, `notebook_delete`
75
+ - `notebook_documentation` — notas + fontes do caderno
76
+
58
77
  ## Publicar no npm (mantenedor)
59
78
 
60
79
  1. Define o **nome** em `package.json` (`innov-mcp-tasks` ou `@scope/innov-mcp-tasks` se o nome simples estiver tomado).
package/index.mjs CHANGED
@@ -105,6 +105,23 @@ server.registerTool(
105
105
  },
106
106
  );
107
107
 
108
+ server.registerTool(
109
+ 'projects_list',
110
+ {
111
+ description:
112
+ 'Lista projetos visíveis ao utilizador do token (GET /projects). Inclui creator, tasks, team e allowedUsers.',
113
+ inputSchema: {},
114
+ },
115
+ async () => {
116
+ try {
117
+ const data = await apiFetch('/api/v1/projects');
118
+ return jsonText(data);
119
+ } catch (e) {
120
+ return jsonError(e instanceof Error ? e.message : String(e));
121
+ }
122
+ },
123
+ );
124
+
108
125
  server.registerTool(
109
126
  'project_create',
110
127
  {
@@ -248,5 +265,369 @@ server.registerTool(
248
265
  },
249
266
  );
250
267
 
268
+ function notesQueryString(filters) {
269
+ const params = new URLSearchParams();
270
+ if (filters.project_id != null) {
271
+ params.set('project_id', String(filters.project_id));
272
+ }
273
+ if (filters.note_type != null) {
274
+ params.set('note_type', filters.note_type);
275
+ }
276
+ if (filters.notebook_id != null) {
277
+ params.set('notebook_id', String(filters.notebook_id));
278
+ }
279
+ const qs = params.toString();
280
+ return qs ? `?${qs}` : '';
281
+ }
282
+
283
+ server.registerTool(
284
+ 'notes_list',
285
+ {
286
+ description:
287
+ 'Lista anotações do utilizador do token (GET /notes). Filtros opcionais: project_id, note_type (personal|project), notebook_id.',
288
+ inputSchema: {
289
+ project_id: z.number().int().positive().optional(),
290
+ note_type: z.enum(['personal', 'project']).optional(),
291
+ notebook_id: z.number().int().positive().optional(),
292
+ },
293
+ },
294
+ async (args) => {
295
+ try {
296
+ const data = await apiFetch(`/api/v1/notes${notesQueryString(args)}`);
297
+ return jsonText(data);
298
+ } catch (e) {
299
+ return jsonError(e instanceof Error ? e.message : String(e));
300
+ }
301
+ },
302
+ );
303
+
304
+ server.registerTool(
305
+ 'notes_personal',
306
+ {
307
+ description: 'Anotações pessoais do utilizador do token (GET /notes/personal).',
308
+ inputSchema: {},
309
+ },
310
+ async () => {
311
+ try {
312
+ const data = await apiFetch('/api/v1/notes/personal');
313
+ return jsonText(data);
314
+ } catch (e) {
315
+ return jsonError(e instanceof Error ? e.message : String(e));
316
+ }
317
+ },
318
+ );
319
+
320
+ server.registerTool(
321
+ 'notes_by_project',
322
+ {
323
+ description:
324
+ 'Anotações de um projeto visível (GET /notes/project/{project_id}). Inclui user (criador).',
325
+ inputSchema: { project_id: z.number().int().positive() },
326
+ },
327
+ async (args) => {
328
+ try {
329
+ const data = await apiFetch(
330
+ `/api/v1/notes/project/${args.project_id}`,
331
+ );
332
+ return jsonText(data);
333
+ } catch (e) {
334
+ return jsonError(e instanceof Error ? e.message : String(e));
335
+ }
336
+ },
337
+ );
338
+
339
+ server.registerTool(
340
+ 'note_get',
341
+ {
342
+ description: 'Obtém uma anotação por id (GET /notes/{id}). Só o autor.',
343
+ inputSchema: { note_id: z.number().int().positive() },
344
+ },
345
+ async (args) => {
346
+ try {
347
+ const data = await apiFetch(`/api/v1/notes/${args.note_id}`);
348
+ return jsonText(data);
349
+ } catch (e) {
350
+ return jsonError(e instanceof Error ? e.message : String(e));
351
+ }
352
+ },
353
+ );
354
+
355
+ server.registerTool(
356
+ 'note_create',
357
+ {
358
+ description:
359
+ 'Cria anotação (POST /notes). Pessoal sem project_id; de projeto com note_type project + project_id; opcional notebook_id.',
360
+ inputSchema: {
361
+ content: z.string().min(1),
362
+ title: z.string().max(255).optional(),
363
+ note_type: z.enum(['personal', 'project']).optional(),
364
+ project_id: z.number().int().positive().optional(),
365
+ notebook_id: z.number().int().positive().optional(),
366
+ },
367
+ },
368
+ async (args) => {
369
+ try {
370
+ const body = {
371
+ content: args.content,
372
+ title: args.title ?? null,
373
+ note_type: args.note_type ?? null,
374
+ project_id: args.project_id ?? null,
375
+ notebook_id: args.notebook_id ?? null,
376
+ };
377
+ const data = await apiFetch('/api/v1/notes', {
378
+ method: 'POST',
379
+ body: JSON.stringify(body),
380
+ });
381
+ return jsonText(data);
382
+ } catch (e) {
383
+ return jsonError(e instanceof Error ? e.message : String(e));
384
+ }
385
+ },
386
+ );
387
+
388
+ server.registerTool(
389
+ 'note_update',
390
+ {
391
+ description: 'Atualiza anotação (PUT /notes/{id}). Só o autor.',
392
+ inputSchema: {
393
+ note_id: z.number().int().positive(),
394
+ title: z.string().max(255).optional(),
395
+ content: z.string().optional(),
396
+ note_type: z.enum(['personal', 'project']).optional(),
397
+ project_id: z.number().int().positive().optional(),
398
+ notebook_id: z.number().int().positive().optional(),
399
+ },
400
+ },
401
+ async (args) => {
402
+ try {
403
+ const { note_id, ...fields } = args;
404
+ const body = Object.fromEntries(
405
+ Object.entries(fields).filter(([, v]) => v !== undefined),
406
+ );
407
+ const data = await apiFetch(`/api/v1/notes/${note_id}`, {
408
+ method: 'PUT',
409
+ body: JSON.stringify(body),
410
+ });
411
+ return jsonText(data);
412
+ } catch (e) {
413
+ return jsonError(e instanceof Error ? e.message : String(e));
414
+ }
415
+ },
416
+ );
417
+
418
+ server.registerTool(
419
+ 'note_delete',
420
+ {
421
+ description:
422
+ 'Remove anotação para a lixeira — soft delete (DELETE /notes/{id}). Só o autor.',
423
+ inputSchema: { note_id: z.number().int().positive() },
424
+ },
425
+ async (args) => {
426
+ try {
427
+ const data = await apiFetch(`/api/v1/notes/${args.note_id}`, {
428
+ method: 'DELETE',
429
+ });
430
+ return jsonText(data);
431
+ } catch (e) {
432
+ return jsonError(e instanceof Error ? e.message : String(e));
433
+ }
434
+ },
435
+ );
436
+
437
+ server.registerTool(
438
+ 'notes_trashed',
439
+ {
440
+ description:
441
+ 'Lista anotações na lixeira do utilizador (GET /notes/trashed). Filtros opcionais como notes_list.',
442
+ inputSchema: {
443
+ project_id: z.number().int().positive().optional(),
444
+ note_type: z.enum(['personal', 'project']).optional(),
445
+ notebook_id: z.number().int().positive().optional(),
446
+ },
447
+ },
448
+ async (args) => {
449
+ try {
450
+ const data = await apiFetch(
451
+ `/api/v1/notes/trashed${notesQueryString(args)}`,
452
+ );
453
+ return jsonText(data);
454
+ } catch (e) {
455
+ return jsonError(e instanceof Error ? e.message : String(e));
456
+ }
457
+ },
458
+ );
459
+
460
+ server.registerTool(
461
+ 'note_restore',
462
+ {
463
+ description:
464
+ 'Restaura anotação da lixeira (POST /notes/{id}/restore). Só o autor.',
465
+ inputSchema: { note_id: z.number().int().positive() },
466
+ },
467
+ async (args) => {
468
+ try {
469
+ const data = await apiFetch(
470
+ `/api/v1/notes/${args.note_id}/restore`,
471
+ { method: 'POST' },
472
+ );
473
+ return jsonText(data);
474
+ } catch (e) {
475
+ return jsonError(e instanceof Error ? e.message : String(e));
476
+ }
477
+ },
478
+ );
479
+
480
+ server.registerTool(
481
+ 'annotations_search',
482
+ {
483
+ description:
484
+ 'Busca em cadernos, anotações e fontes (GET /annotations/search?q=). Mínimo 2 caracteres.',
485
+ inputSchema: { q: z.string().min(2) },
486
+ },
487
+ async (args) => {
488
+ try {
489
+ const data = await apiFetch(
490
+ `/api/v1/annotations/search?q=${encodeURIComponent(args.q)}`,
491
+ );
492
+ return jsonText(data);
493
+ } catch (e) {
494
+ return jsonError(e instanceof Error ? e.message : String(e));
495
+ }
496
+ },
497
+ );
498
+
499
+ server.registerTool(
500
+ 'notebooks_list',
501
+ {
502
+ description:
503
+ 'Lista cadernos visíveis ao utilizador do token (GET /notebooks). Filtro opcional project_id.',
504
+ inputSchema: {
505
+ project_id: z.number().int().positive().optional(),
506
+ },
507
+ },
508
+ async (args) => {
509
+ try {
510
+ const qs =
511
+ args.project_id != null
512
+ ? `?project_id=${encodeURIComponent(String(args.project_id))}`
513
+ : '';
514
+ const data = await apiFetch(`/api/v1/notebooks${qs}`);
515
+ return jsonText(data);
516
+ } catch (e) {
517
+ return jsonError(e instanceof Error ? e.message : String(e));
518
+ }
519
+ },
520
+ );
521
+
522
+ server.registerTool(
523
+ 'notebook_get',
524
+ {
525
+ description: 'Obtém um caderno por id (GET /notebooks/{id}).',
526
+ inputSchema: { notebook_id: z.number().int().positive() },
527
+ },
528
+ async (args) => {
529
+ try {
530
+ const data = await apiFetch(`/api/v1/notebooks/${args.notebook_id}`);
531
+ return jsonText(data);
532
+ } catch (e) {
533
+ return jsonError(e instanceof Error ? e.message : String(e));
534
+ }
535
+ },
536
+ );
537
+
538
+ server.registerTool(
539
+ 'notebook_create',
540
+ {
541
+ description:
542
+ 'Cria caderno (POST /notebooks). Pessoal sem project_id; de projeto com project_id. name obrigatório.',
543
+ inputSchema: {
544
+ name: z.string().min(1).max(255),
545
+ description: z.string().optional(),
546
+ project_id: z.number().int().positive().optional(),
547
+ },
548
+ },
549
+ async (args) => {
550
+ try {
551
+ const body = {
552
+ name: args.name,
553
+ description: args.description ?? null,
554
+ project_id: args.project_id ?? null,
555
+ };
556
+ const data = await apiFetch('/api/v1/notebooks', {
557
+ method: 'POST',
558
+ body: JSON.stringify(body),
559
+ });
560
+ return jsonText(data);
561
+ } catch (e) {
562
+ return jsonError(e instanceof Error ? e.message : String(e));
563
+ }
564
+ },
565
+ );
566
+
567
+ server.registerTool(
568
+ 'notebook_update',
569
+ {
570
+ description: 'Atualiza caderno (PATCH /notebooks/{id}). Só cadernos do utilizador.',
571
+ inputSchema: {
572
+ notebook_id: z.number().int().positive(),
573
+ name: z.string().min(1).max(255).optional(),
574
+ description: z.string().optional(),
575
+ project_id: z.number().int().positive().optional(),
576
+ },
577
+ },
578
+ async (args) => {
579
+ try {
580
+ const { notebook_id, ...fields } = args;
581
+ const body = Object.fromEntries(
582
+ Object.entries(fields).filter(([, v]) => v !== undefined),
583
+ );
584
+ const data = await apiFetch(`/api/v1/notebooks/${notebook_id}`, {
585
+ method: 'PATCH',
586
+ body: JSON.stringify(body),
587
+ });
588
+ return jsonText(data);
589
+ } catch (e) {
590
+ return jsonError(e instanceof Error ? e.message : String(e));
591
+ }
592
+ },
593
+ );
594
+
595
+ server.registerTool(
596
+ 'notebook_delete',
597
+ {
598
+ description: 'Apaga caderno (DELETE /notebooks/{id}). Só cadernos do utilizador.',
599
+ inputSchema: { notebook_id: z.number().int().positive() },
600
+ },
601
+ async (args) => {
602
+ try {
603
+ const data = await apiFetch(`/api/v1/notebooks/${args.notebook_id}`, {
604
+ method: 'DELETE',
605
+ });
606
+ return jsonText(data);
607
+ } catch (e) {
608
+ return jsonError(e instanceof Error ? e.message : String(e));
609
+ }
610
+ },
611
+ );
612
+
613
+ server.registerTool(
614
+ 'notebook_documentation',
615
+ {
616
+ description:
617
+ 'Documentação agregada do caderno: notas do utilizador + fontes (GET /notebooks/{id}/documentation).',
618
+ inputSchema: { notebook_id: z.number().int().positive() },
619
+ },
620
+ async (args) => {
621
+ try {
622
+ const data = await apiFetch(
623
+ `/api/v1/notebooks/${args.notebook_id}/documentation`,
624
+ );
625
+ return jsonText(data);
626
+ } catch (e) {
627
+ return jsonError(e instanceof Error ? e.message : String(e));
628
+ }
629
+ },
630
+ );
631
+
251
632
  const transport = new StdioServerTransport();
252
633
  await server.connect(transport);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "innov-mcp-tasks",
3
- "version": "1.0.2",
4
- "description": "MCP stdio — ferramentas de tarefas Innov (API a configurar via INNOV_API_BASE_URL + token Sanctum)",
3
+ "version": "1.2.0",
4
+ "description": "MCP stdio — tarefas e anotações Innov (INNOV_API_BASE_URL + token Sanctum)",
5
5
  "type": "module",
6
6
  "main": "index.mjs",
7
7
  "bin": {
@@ -26,6 +26,8 @@
26
26
  "mcp",
27
27
  "modelcontextprotocol",
28
28
  "tasks",
29
+ "notes",
30
+ "annotations",
29
31
  "innov"
30
32
  ],
31
33
  "author": "",