@zoobbe/cli 1.2.4 → 1.3.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/package.json +1 -1
- package/src/commands/card.js +3 -12
- package/src/mcp-server.js +41 -18
package/package.json
CHANGED
package/src/commands/card.js
CHANGED
|
@@ -23,30 +23,21 @@ card
|
|
|
23
23
|
let cards = [];
|
|
24
24
|
|
|
25
25
|
if (options.board) {
|
|
26
|
-
// Get lists with populated cards for this board
|
|
26
|
+
// Get lists with populated cards for this board (single API call)
|
|
27
27
|
const listsData = await withSpinner('Fetching cards...', () =>
|
|
28
28
|
client.get(`/boards/${options.board}/lists`)
|
|
29
29
|
);
|
|
30
30
|
const lists = listsData.lists || listsData.data || listsData;
|
|
31
31
|
const listArr = Array.isArray(lists) ? lists : Object.values(lists);
|
|
32
32
|
|
|
33
|
-
//
|
|
34
|
-
const cardRefs = [];
|
|
33
|
+
// Extract cards directly from list data (no extra API calls)
|
|
35
34
|
for (const list of listArr) {
|
|
36
35
|
if (list.cards && Array.isArray(list.cards)) {
|
|
37
36
|
for (const c of list.cards) {
|
|
38
|
-
|
|
37
|
+
cards.push({ ...c, _listName: list.title || '' });
|
|
39
38
|
}
|
|
40
39
|
}
|
|
41
40
|
}
|
|
42
|
-
|
|
43
|
-
// Fetch full card details in parallel
|
|
44
|
-
const results = await Promise.allSettled(
|
|
45
|
-
cardRefs.map(ref => client.get(`/cards/${ref.id}`).then(d => ({ ...d, _listName: ref.listName })))
|
|
46
|
-
);
|
|
47
|
-
for (const r of results) {
|
|
48
|
-
if (r.status === 'fulfilled') cards.push(r.value);
|
|
49
|
-
}
|
|
50
41
|
} else {
|
|
51
42
|
// Get cards for current user
|
|
52
43
|
const userId = config.get('userId');
|
package/src/mcp-server.js
CHANGED
|
@@ -19,6 +19,25 @@ const config = require('./lib/config');
|
|
|
19
19
|
// Mark all requests from MCP server as AI agent actions
|
|
20
20
|
client.setSource('mcp');
|
|
21
21
|
|
|
22
|
+
// ── Priority Normalization ──
|
|
23
|
+
// Backend expects: Low, Normal, High, Urgent
|
|
24
|
+
// MCP accepts both formats for convenience
|
|
25
|
+
const PRIORITY_MAP = {
|
|
26
|
+
low: 'Low',
|
|
27
|
+
normal: 'Normal',
|
|
28
|
+
medium: 'Normal',
|
|
29
|
+
high: 'High',
|
|
30
|
+
urgent: 'Urgent',
|
|
31
|
+
critical: 'Urgent',
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
function normalizePriority(value) {
|
|
35
|
+
if (!value) return null;
|
|
36
|
+
const mapped = PRIORITY_MAP[value.toLowerCase()];
|
|
37
|
+
if (!mapped) throw new Error(`Invalid priority "${value}". Valid: Low, Normal, High, Urgent (or: low, medium, high, critical)`);
|
|
38
|
+
return mapped;
|
|
39
|
+
}
|
|
40
|
+
|
|
22
41
|
// ── Tool Definitions ──
|
|
23
42
|
|
|
24
43
|
const TOOLS = [
|
|
@@ -201,7 +220,7 @@ const TOOLS = [
|
|
|
201
220
|
list_id: { type: 'string', description: 'List ID (uses first list if omitted)' },
|
|
202
221
|
description: { type: 'string', description: 'Card description' },
|
|
203
222
|
due_date: { type: 'string', description: 'Due date (YYYY-MM-DD)' },
|
|
204
|
-
priority: { type: 'string', description: 'Priority: low, medium, high, critical' },
|
|
223
|
+
priority: { type: 'string', description: 'Priority: Low, Normal, High, Urgent (also accepts: low, medium, high, critical)' },
|
|
205
224
|
},
|
|
206
225
|
required: ['title', 'board_id'],
|
|
207
226
|
},
|
|
@@ -297,7 +316,7 @@ const TOOLS = [
|
|
|
297
316
|
type: 'object',
|
|
298
317
|
properties: {
|
|
299
318
|
card_id: { type: 'string', description: 'Card short ID' },
|
|
300
|
-
priority: { type: 'string', description: 'Priority: low, medium, high, critical' },
|
|
319
|
+
priority: { type: 'string', description: 'Priority: Low, Normal, High, Urgent (also accepts: low, medium, high, critical)' },
|
|
301
320
|
},
|
|
302
321
|
required: ['card_id', 'priority'],
|
|
303
322
|
},
|
|
@@ -383,7 +402,7 @@ async function executeTool(name, args = {}) {
|
|
|
383
402
|
}
|
|
384
403
|
|
|
385
404
|
case 'list_lists': {
|
|
386
|
-
const data = await client.get(
|
|
405
|
+
const data = await client.get(`/boards/${args.board_id}/lists`);
|
|
387
406
|
let lists = data.lists || data.data || data;
|
|
388
407
|
lists = Array.isArray(lists) ? lists : Object.values(lists);
|
|
389
408
|
return lists.filter(l => !l.archived).map(l => ({
|
|
@@ -427,21 +446,16 @@ async function executeTool(name, args = {}) {
|
|
|
427
446
|
const lists = listsData.lists || listsData.data || listsData;
|
|
428
447
|
const listArr = Array.isArray(lists) ? lists : Object.values(lists);
|
|
429
448
|
|
|
430
|
-
|
|
449
|
+
// Extract cards directly from list data (single API call, no N+1)
|
|
450
|
+
let cards = [];
|
|
431
451
|
for (const list of listArr) {
|
|
432
452
|
if (list.cards && Array.isArray(list.cards)) {
|
|
433
453
|
for (const c of list.cards) {
|
|
434
|
-
|
|
454
|
+
cards.push({ ...c, _listName: list.title || '' });
|
|
435
455
|
}
|
|
436
456
|
}
|
|
437
457
|
}
|
|
438
458
|
|
|
439
|
-
const results = await Promise.allSettled(
|
|
440
|
-
cardRefs.map(ref => client.get(`/cards/${ref.id}`).then(d => ({ ...d, _listName: ref.listName })))
|
|
441
|
-
);
|
|
442
|
-
|
|
443
|
-
let cards = results.filter(r => r.status === 'fulfilled').map(r => r.value);
|
|
444
|
-
|
|
445
459
|
if (args.due) {
|
|
446
460
|
const now = new Date();
|
|
447
461
|
cards = cards.filter(c => {
|
|
@@ -457,6 +471,7 @@ async function executeTool(name, args = {}) {
|
|
|
457
471
|
return cards.map(c => ({
|
|
458
472
|
title: c.title || c.name,
|
|
459
473
|
id: c.shortId || c._id,
|
|
474
|
+
cardNumber: c.cardNumber || null,
|
|
460
475
|
list: c._listName || c.actionListTitle || '',
|
|
461
476
|
due: c.dueDate?.date || c.dueDate || null,
|
|
462
477
|
members: c.members?.length || 0,
|
|
@@ -471,6 +486,7 @@ async function executeTool(name, args = {}) {
|
|
|
471
486
|
return {
|
|
472
487
|
title: c.title || c.name,
|
|
473
488
|
id: c.shortId || c._id,
|
|
489
|
+
cardNumber: c.cardNumber || null,
|
|
474
490
|
board: c.boardTitle || c.board,
|
|
475
491
|
list: c.actionListTitle || c.actionList?.name,
|
|
476
492
|
description: c.description || '',
|
|
@@ -525,6 +541,7 @@ async function executeTool(name, args = {}) {
|
|
|
525
541
|
return (Array.isArray(cards) ? cards : []).map(c => ({
|
|
526
542
|
title: c.title || c.name,
|
|
527
543
|
id: c.shortId || c._id,
|
|
544
|
+
cardNumber: c.cardNumber || null,
|
|
528
545
|
board: c.boardTitle || '',
|
|
529
546
|
list: c.actionListTitle || '',
|
|
530
547
|
due: c.dueDate?.date || c.dueDate || null,
|
|
@@ -534,8 +551,13 @@ async function executeTool(name, args = {}) {
|
|
|
534
551
|
}
|
|
535
552
|
|
|
536
553
|
case 'search': {
|
|
537
|
-
const data = await client.
|
|
538
|
-
|
|
554
|
+
const data = await client.post(`/global/search?query=${encodeURIComponent(args.query)}`, {});
|
|
555
|
+
const results = data.data || data;
|
|
556
|
+
const out = [];
|
|
557
|
+
if (results.boards) out.push(...results.boards.map(b => ({ type: 'board', title: b.title || b.name, id: b.shortId || b._id })));
|
|
558
|
+
if (results.cards) out.push(...results.cards.map(c => ({ type: 'card', title: c.title, id: c.shortId || c._id, board: c.boardTitle || '' })));
|
|
559
|
+
if (results.pages) out.push(...results.pages.map(p => ({ type: 'page', title: p.title, id: p.shortId || p._id })));
|
|
560
|
+
return out.length ? out : results;
|
|
539
561
|
}
|
|
540
562
|
|
|
541
563
|
case 'list_notifications': {
|
|
@@ -580,10 +602,10 @@ async function executeTool(name, args = {}) {
|
|
|
580
602
|
await client.post(`/cards/${cardId}/duedate`, { dueDate: args.due_date });
|
|
581
603
|
}
|
|
582
604
|
if (args.priority) {
|
|
583
|
-
await client.post(`/cards/${cardId}/priority`, { priority: args.priority });
|
|
605
|
+
await client.post(`/cards/${cardId}/priority`, { priority: normalizePriority(args.priority) });
|
|
584
606
|
}
|
|
585
607
|
|
|
586
|
-
return { created: true, title: args.title, id: cardId, changed_by: 'AI Agent (via MCP)' };
|
|
608
|
+
return { created: true, title: args.title, id: cardId, cardNumber: c.cardNumber || null, changed_by: 'AI Agent (via MCP)' };
|
|
587
609
|
}
|
|
588
610
|
|
|
589
611
|
case 'update_card': {
|
|
@@ -656,8 +678,9 @@ async function executeTool(name, args = {}) {
|
|
|
656
678
|
}
|
|
657
679
|
|
|
658
680
|
case 'set_priority': {
|
|
659
|
-
|
|
660
|
-
|
|
681
|
+
const normalized = normalizePriority(args.priority);
|
|
682
|
+
await client.post(`/cards/${args.card_id}/priority`, { priority: normalized });
|
|
683
|
+
return { set: true, card_id: args.card_id, priority: normalized, changed_by: 'AI Agent (via MCP)' };
|
|
661
684
|
}
|
|
662
685
|
|
|
663
686
|
case 'archive_card': {
|
|
@@ -701,7 +724,7 @@ async function executeTool(name, args = {}) {
|
|
|
701
724
|
|
|
702
725
|
const SERVER_INFO = {
|
|
703
726
|
name: 'zoobbe',
|
|
704
|
-
version: '1.
|
|
727
|
+
version: '1.3.0',
|
|
705
728
|
};
|
|
706
729
|
|
|
707
730
|
function makeResponse(id, result) {
|