innov-mcp-tasks 1.3.0 → 1.4.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.
- package/README.md +3 -2
- package/index.mjs +93 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -52,11 +52,12 @@ Na raiz do repo existe [`.cursor/mcp.json`](../.cursor/mcp.json) com **dois** se
|
|
|
52
52
|
|
|
53
53
|
### Tarefas e projetos
|
|
54
54
|
|
|
55
|
-
- `tasks_list` — opcional `project_id`
|
|
55
|
+
- `tasks_list` — opcional `project_id`, `milestones_only` (filtra marcos no cliente)
|
|
56
56
|
- `my_tasks` — tarefas do utilizador do token (endpoint “minhas” da API)
|
|
57
57
|
- `projects_list` — lista projetos visíveis (`GET /api/v1/projects`)
|
|
58
58
|
- `project_create` — cria projeto (`POST /api/v1/projects`)
|
|
59
|
-
- `task_get`, `task_create`, `task_update_status`, `task_assign`, `task_assign_to_me`
|
|
59
|
+
- `task_get`, `task_create`, `task_update`, `task_update_status`, `task_assign`, `task_assign_to_me`
|
|
60
|
+
- `task_create` / `task_update` — marcos: `is_milestone`, `start_date` (YYYY-MM-DD), `duration` (dias); marco exige `project_id` na criação
|
|
60
61
|
- `task_attachment_download` — baixa anexo por `attachment_id` (conteúdo em base64; ids em `task_get` → `attachments`)
|
|
61
62
|
|
|
62
63
|
### Anotações / documentação
|
package/index.mjs
CHANGED
|
@@ -87,13 +87,49 @@ async function apiFetchBinary(path) {
|
|
|
87
87
|
};
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
/** Campos de marco / cronograma (is_milestone exige project_id na API). */
|
|
91
|
+
const taskMilestoneInputSchema = {
|
|
92
|
+
is_milestone: z.boolean().optional(),
|
|
93
|
+
start_date: z
|
|
94
|
+
.string()
|
|
95
|
+
.optional()
|
|
96
|
+
.describe('Data de início (YYYY-MM-DD); usada em marcos e Gantt'),
|
|
97
|
+
duration: z
|
|
98
|
+
.number()
|
|
99
|
+
.int()
|
|
100
|
+
.min(1)
|
|
101
|
+
.optional()
|
|
102
|
+
.describe('Duração em dias (marco/cronograma)'),
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
function pickDefined(fields) {
|
|
106
|
+
return Object.fromEntries(
|
|
107
|
+
Object.entries(fields).filter(([, v]) => v !== undefined),
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function filterTasksMilestonesOnly(data) {
|
|
112
|
+
if (!Array.isArray(data)) {
|
|
113
|
+
return data;
|
|
114
|
+
}
|
|
115
|
+
return data.filter((t) => t && t.is_milestone);
|
|
116
|
+
}
|
|
117
|
+
|
|
90
118
|
const server = new McpServer({ name: 'innov-tasks', version: '1.0.0' });
|
|
91
119
|
|
|
92
120
|
server.registerTool(
|
|
93
121
|
'tasks_list',
|
|
94
122
|
{
|
|
95
|
-
description:
|
|
96
|
-
|
|
123
|
+
description:
|
|
124
|
+
'Lista tarefas. Com project_id usa GET /tasks?project_id=…; sem project_id usa GET /tasks/kanban. ' +
|
|
125
|
+
'Cada item inclui is_milestone, start_date e duration. Com milestones_only=true filtra só marcos (no cliente).',
|
|
126
|
+
inputSchema: {
|
|
127
|
+
project_id: z.number().int().positive().optional(),
|
|
128
|
+
milestones_only: z
|
|
129
|
+
.boolean()
|
|
130
|
+
.optional()
|
|
131
|
+
.describe('Se true, devolve apenas tarefas com is_milestone=true'),
|
|
132
|
+
},
|
|
97
133
|
},
|
|
98
134
|
async (args) => {
|
|
99
135
|
try {
|
|
@@ -101,7 +137,10 @@ server.registerTool(
|
|
|
101
137
|
args.project_id != null
|
|
102
138
|
? `/api/v1/tasks?project_id=${encodeURIComponent(String(args.project_id))}`
|
|
103
139
|
: '/api/v1/tasks/kanban';
|
|
104
|
-
|
|
140
|
+
let data = await apiFetch(path);
|
|
141
|
+
if (args.milestones_only) {
|
|
142
|
+
data = filterTasksMilestonesOnly(data);
|
|
143
|
+
}
|
|
105
144
|
return jsonText(data);
|
|
106
145
|
} catch (e) {
|
|
107
146
|
return jsonError(e instanceof Error ? e.message : String(e));
|
|
@@ -129,7 +168,8 @@ server.registerTool(
|
|
|
129
168
|
server.registerTool(
|
|
130
169
|
'task_get',
|
|
131
170
|
{
|
|
132
|
-
description:
|
|
171
|
+
description:
|
|
172
|
+
'Obtém uma tarefa por id (GET /tasks/{id}). Inclui is_milestone, start_date e duration.',
|
|
133
173
|
inputSchema: { task_id: z.number().int().positive() },
|
|
134
174
|
},
|
|
135
175
|
async (args) => {
|
|
@@ -233,7 +273,8 @@ server.registerTool(
|
|
|
233
273
|
server.registerTool(
|
|
234
274
|
'task_create',
|
|
235
275
|
{
|
|
236
|
-
description:
|
|
276
|
+
description:
|
|
277
|
+
'Cria tarefa (POST /tasks). Marcos: is_milestone=true exige project_id; opcional start_date e duration (dias).',
|
|
237
278
|
inputSchema: {
|
|
238
279
|
title: z.string().min(1).max(255),
|
|
239
280
|
project_id: z.number().int().positive().optional(),
|
|
@@ -241,18 +282,24 @@ server.registerTool(
|
|
|
241
282
|
description: z.string().optional(),
|
|
242
283
|
assigned_to: z.number().int().positive().optional(),
|
|
243
284
|
priority: z.enum(['low', 'medium', 'high', 'urgent']).optional(),
|
|
285
|
+
due_date: z.string().optional().describe('Prazo (YYYY-MM-DD)'),
|
|
286
|
+
...taskMilestoneInputSchema,
|
|
244
287
|
},
|
|
245
288
|
},
|
|
246
289
|
async (args) => {
|
|
247
290
|
try {
|
|
248
|
-
const body = {
|
|
291
|
+
const body = pickDefined({
|
|
249
292
|
title: args.title,
|
|
250
|
-
project_id: args.project_id
|
|
251
|
-
team_id: args.team_id
|
|
252
|
-
description: args.description
|
|
253
|
-
assigned_to: args.assigned_to
|
|
293
|
+
project_id: args.project_id,
|
|
294
|
+
team_id: args.team_id,
|
|
295
|
+
description: args.description,
|
|
296
|
+
assigned_to: args.assigned_to,
|
|
254
297
|
priority: args.priority ?? 'medium',
|
|
255
|
-
|
|
298
|
+
due_date: args.due_date,
|
|
299
|
+
is_milestone: args.is_milestone,
|
|
300
|
+
start_date: args.start_date,
|
|
301
|
+
duration: args.duration,
|
|
302
|
+
});
|
|
256
303
|
const data = await apiFetch('/api/v1/tasks', {
|
|
257
304
|
method: 'POST',
|
|
258
305
|
body: JSON.stringify(body),
|
|
@@ -264,6 +311,41 @@ server.registerTool(
|
|
|
264
311
|
},
|
|
265
312
|
);
|
|
266
313
|
|
|
314
|
+
server.registerTool(
|
|
315
|
+
'task_update',
|
|
316
|
+
{
|
|
317
|
+
description:
|
|
318
|
+
'Atualiza tarefa (PATCH /tasks/{id}). Campos opcionais; marcos: is_milestone, start_date, duration (dias). ' +
|
|
319
|
+
'Marco exige projeto e não pode ser subtarefa (regras da API).',
|
|
320
|
+
inputSchema: {
|
|
321
|
+
task_id: z.number().int().positive(),
|
|
322
|
+
title: z.string().min(1).max(255).optional(),
|
|
323
|
+
description: z.string().optional(),
|
|
324
|
+
status: z
|
|
325
|
+
.enum(['pending', 'in_progress', 'in_review', 'completed', 'cancelled'])
|
|
326
|
+
.optional(),
|
|
327
|
+
priority: z.enum(['low', 'medium', 'high', 'urgent']).optional(),
|
|
328
|
+
due_date: z.string().optional(),
|
|
329
|
+
assigned_to: z.number().int().positive().optional(),
|
|
330
|
+
progress: z.number().int().min(0).max(100).optional(),
|
|
331
|
+
...taskMilestoneInputSchema,
|
|
332
|
+
},
|
|
333
|
+
},
|
|
334
|
+
async (args) => {
|
|
335
|
+
try {
|
|
336
|
+
const { task_id, ...fields } = args;
|
|
337
|
+
const body = pickDefined(fields);
|
|
338
|
+
const data = await apiFetch(`/api/v1/tasks/${task_id}`, {
|
|
339
|
+
method: 'PATCH',
|
|
340
|
+
body: JSON.stringify(body),
|
|
341
|
+
});
|
|
342
|
+
return jsonText(data);
|
|
343
|
+
} catch (e) {
|
|
344
|
+
return jsonError(e instanceof Error ? e.message : String(e));
|
|
345
|
+
}
|
|
346
|
+
},
|
|
347
|
+
);
|
|
348
|
+
|
|
267
349
|
server.registerTool(
|
|
268
350
|
'task_update_status',
|
|
269
351
|
{
|