wolfpack-mcp 1.0.42 → 1.0.43
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/dist/client.js +9 -35
- package/dist/index.js +107 -168
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -175,42 +175,9 @@ export class WolfpackClient {
|
|
|
175
175
|
throw error;
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
|
-
async
|
|
178
|
+
async updateWorkItem(workItemId, fields, teamSlug) {
|
|
179
179
|
try {
|
|
180
|
-
return await this.api.
|
|
181
|
-
}
|
|
182
|
-
catch (error) {
|
|
183
|
-
if (error && typeof error === 'object' && 'status' in error && error.status === 404) {
|
|
184
|
-
return null;
|
|
185
|
-
}
|
|
186
|
-
throw error;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
async updateWorkItemAssignee(workItemId, data, teamSlug) {
|
|
190
|
-
try {
|
|
191
|
-
return await this.api.put(this.withTeamSlug(`/work-items/${workItemId}/assignee`, teamSlug), data);
|
|
192
|
-
}
|
|
193
|
-
catch (error) {
|
|
194
|
-
if (error && typeof error === 'object' && 'status' in error && error.status === 404) {
|
|
195
|
-
return null;
|
|
196
|
-
}
|
|
197
|
-
throw error;
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
async updateWorkItemTitle(workItemId, title, teamSlug) {
|
|
201
|
-
try {
|
|
202
|
-
return await this.api.put(this.withTeamSlug(`/work-items/${workItemId}/title`, teamSlug), { title });
|
|
203
|
-
}
|
|
204
|
-
catch (error) {
|
|
205
|
-
if (error && typeof error === 'object' && 'status' in error && error.status === 404) {
|
|
206
|
-
return null;
|
|
207
|
-
}
|
|
208
|
-
throw error;
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
async updateWorkItemInitiative(workItemId, radarItemId, teamSlug) {
|
|
212
|
-
try {
|
|
213
|
-
return await this.api.put(this.withTeamSlug(`/work-items/${workItemId}/initiative`, teamSlug), { radarItemId });
|
|
180
|
+
return await this.api.patch(this.withTeamSlug(`/work-items/${workItemId}`, teamSlug), fields);
|
|
214
181
|
}
|
|
215
182
|
catch (error) {
|
|
216
183
|
if (error && typeof error === 'object' && 'status' in error && error.status === 404) {
|
|
@@ -480,6 +447,13 @@ export class WolfpackClient {
|
|
|
480
447
|
const { buffer, contentType } = await this.api.getBuffer(`/images/${team}/${filename}`);
|
|
481
448
|
return { base64: buffer.toString('base64'), mimeType: contentType };
|
|
482
449
|
}
|
|
450
|
+
/**
|
|
451
|
+
* Download an issue attachment image and return raw base64 + mimeType.
|
|
452
|
+
*/
|
|
453
|
+
async downloadIssueAttachment(team, refId, attachmentId) {
|
|
454
|
+
const { buffer, contentType } = await this.api.getBuffer(`/issues/${team}/${refId}/attachments/${attachmentId}`);
|
|
455
|
+
return { base64: buffer.toString('base64'), mimeType: contentType };
|
|
456
|
+
}
|
|
483
457
|
// Skill methods (progressive disclosure)
|
|
484
458
|
async listSkills() {
|
|
485
459
|
return this.api.get('/skills');
|
package/dist/index.js
CHANGED
|
@@ -112,39 +112,31 @@ const VALID_STATUSES = [
|
|
|
112
112
|
'closed',
|
|
113
113
|
'archived',
|
|
114
114
|
];
|
|
115
|
-
const
|
|
116
|
-
work_item_id: z.string().describe('The ID of the work item'),
|
|
117
|
-
status: z.enum(VALID_STATUSES).describe('New status'),
|
|
118
|
-
project_slug: z
|
|
119
|
-
.string()
|
|
120
|
-
.optional()
|
|
121
|
-
.describe('Project slug (required for multi-project users, use list_projects to get slugs)'),
|
|
122
|
-
});
|
|
123
|
-
const UpdateWorkItemAssigneeSchema = z.object({
|
|
115
|
+
const UpdateWorkItemSchema = z.object({
|
|
124
116
|
work_item_id: z.string().describe('The ID of the work item'),
|
|
117
|
+
title: z.string().optional().describe('New title for the work item'),
|
|
118
|
+
status: z.enum(VALID_STATUSES).optional().describe('New status'),
|
|
125
119
|
leading_user_id: z
|
|
126
120
|
.string()
|
|
127
121
|
.nullable()
|
|
122
|
+
.optional()
|
|
128
123
|
.describe('User ID to assign as leading user, or null to unassign'),
|
|
129
|
-
|
|
124
|
+
radar_item_id: z
|
|
130
125
|
.string()
|
|
126
|
+
.nullable()
|
|
131
127
|
.optional()
|
|
132
|
-
.describe('
|
|
133
|
-
|
|
134
|
-
const UpdateWorkItemTitleSchema = z.object({
|
|
135
|
-
work_item_id: z.string().describe('The ID of the work item'),
|
|
136
|
-
title: z.string().describe('New title for the work item'),
|
|
137
|
-
project_slug: z
|
|
128
|
+
.describe('Radar/initiative item ID to link to, or null to unlink'),
|
|
129
|
+
category_id: z
|
|
138
130
|
.string()
|
|
131
|
+
.nullable()
|
|
139
132
|
.optional()
|
|
140
|
-
.describe('
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
radar_item_id: z
|
|
145
|
-
.string()
|
|
133
|
+
.describe('Category ID to set, or null to remove the category'),
|
|
134
|
+
priority: z.coerce.number().optional().describe('Priority level (0-4, higher is more important)'),
|
|
135
|
+
size: z
|
|
136
|
+
.enum(['S', 'M', 'L'])
|
|
146
137
|
.nullable()
|
|
147
|
-
.
|
|
138
|
+
.optional()
|
|
139
|
+
.describe('Size estimate: "S" (small), "M" (medium), "L" (large), or null to remove'),
|
|
148
140
|
project_slug: z
|
|
149
141
|
.string()
|
|
150
142
|
.optional()
|
|
@@ -612,16 +604,13 @@ class WolfpackMCPServer {
|
|
|
612
604
|
},
|
|
613
605
|
},
|
|
614
606
|
{
|
|
615
|
-
name: '
|
|
616
|
-
description: '
|
|
617
|
-
'
|
|
618
|
-
'
|
|
619
|
-
'
|
|
620
|
-
'
|
|
621
|
-
'
|
|
622
|
-
'5) If work cannot be done: move to "blocked" and add a detailed comment explaining the blocker, what was attempted, and what is needed to unblock. ' +
|
|
623
|
-
'Statuses: "pending" (backlog), "new" (to do), "doing" (in progress), "review" (work done, pending review), ' +
|
|
624
|
-
'"ready" (reviewed, awaiting deployment), "blocked", "completed" (deployed).',
|
|
607
|
+
name: 'update_work_item',
|
|
608
|
+
description: 'Update one or more fields on a work item. Only provide the fields you want to change. ' +
|
|
609
|
+
'Supports: title, status, leading_user_id (assignee), radar_item_id (initiative), ' +
|
|
610
|
+
'category_id, priority (0-4), and size (S/M/L). ' +
|
|
611
|
+
'STATUS WORKFLOW: "pending" (backlog) → "new" (to do) → "doing" (in progress) → "review" (work done) → "ready" (awaiting deployment) → "completed" (deployed). ' +
|
|
612
|
+
'Use "blocked" when work cannot proceed. For "pending" items use pull_work_item first. ' +
|
|
613
|
+
'When moving to "review", add a completion comment. When moving to "blocked", add a comment explaining the blocker.',
|
|
625
614
|
inputSchema: {
|
|
626
615
|
type: 'object',
|
|
627
616
|
properties: {
|
|
@@ -629,6 +618,10 @@ class WolfpackMCPServer {
|
|
|
629
618
|
type: 'string',
|
|
630
619
|
description: 'The ID of the work item',
|
|
631
620
|
},
|
|
621
|
+
title: {
|
|
622
|
+
type: 'string',
|
|
623
|
+
description: 'New title for the work item',
|
|
624
|
+
},
|
|
632
625
|
status: {
|
|
633
626
|
type: 'string',
|
|
634
627
|
enum: [
|
|
@@ -642,84 +635,35 @@ class WolfpackMCPServer {
|
|
|
642
635
|
'closed',
|
|
643
636
|
'archived',
|
|
644
637
|
],
|
|
645
|
-
description: 'New status
|
|
646
|
-
},
|
|
647
|
-
project_slug: {
|
|
648
|
-
type: 'string',
|
|
649
|
-
description: 'Project slug (required for multi-project users, use list_projects to get slugs)',
|
|
650
|
-
},
|
|
651
|
-
},
|
|
652
|
-
required: ['work_item_id', 'status'],
|
|
653
|
-
},
|
|
654
|
-
},
|
|
655
|
-
{
|
|
656
|
-
name: 'update_work_item_assignee',
|
|
657
|
-
description: 'Change the assignee (leading user) on a work item. ' +
|
|
658
|
-
'Use this to assign work to yourself or another project member, or to unassign by passing null. ' +
|
|
659
|
-
'Not applicable for personal projects (single user).',
|
|
660
|
-
inputSchema: {
|
|
661
|
-
type: 'object',
|
|
662
|
-
properties: {
|
|
663
|
-
work_item_id: {
|
|
664
|
-
type: 'string',
|
|
665
|
-
description: 'The ID of the work item',
|
|
638
|
+
description: 'New status',
|
|
666
639
|
},
|
|
667
640
|
leading_user_id: {
|
|
668
641
|
type: ['string', 'null'],
|
|
669
642
|
description: 'User ID to assign as leading user, or null to unassign',
|
|
670
643
|
},
|
|
671
|
-
|
|
672
|
-
type: 'string',
|
|
673
|
-
description: '
|
|
674
|
-
},
|
|
675
|
-
},
|
|
676
|
-
required: ['work_item_id', 'leading_user_id'],
|
|
677
|
-
},
|
|
678
|
-
},
|
|
679
|
-
{
|
|
680
|
-
name: 'update_work_item_title',
|
|
681
|
-
description: 'Change the title of a work item. ' +
|
|
682
|
-
'Use this to rename or update the title of an existing work item.',
|
|
683
|
-
inputSchema: {
|
|
684
|
-
type: 'object',
|
|
685
|
-
properties: {
|
|
686
|
-
work_item_id: {
|
|
687
|
-
type: 'string',
|
|
688
|
-
description: 'The ID of the work item',
|
|
689
|
-
},
|
|
690
|
-
title: {
|
|
691
|
-
type: 'string',
|
|
692
|
-
description: 'New title for the work item',
|
|
644
|
+
radar_item_id: {
|
|
645
|
+
type: ['string', 'null'],
|
|
646
|
+
description: 'Radar/initiative item ID to link to, or null to unlink',
|
|
693
647
|
},
|
|
694
|
-
|
|
695
|
-
type: 'string',
|
|
696
|
-
description: '
|
|
648
|
+
category_id: {
|
|
649
|
+
type: ['string', 'null'],
|
|
650
|
+
description: 'Category ID to set, or null to remove the category',
|
|
697
651
|
},
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
},
|
|
702
|
-
{
|
|
703
|
-
name: 'update_work_item_initiative',
|
|
704
|
-
description: 'Link or unlink a work item to/from a radar item (initiative). ' +
|
|
705
|
-
'Pass a radar_item_id to link, or null to remove the initiative link.',
|
|
706
|
-
inputSchema: {
|
|
707
|
-
type: 'object',
|
|
708
|
-
properties: {
|
|
709
|
-
work_item_id: {
|
|
710
|
-
type: 'string',
|
|
711
|
-
description: 'The ID of the work item',
|
|
652
|
+
priority: {
|
|
653
|
+
type: 'number',
|
|
654
|
+
description: 'Priority level: 0 (None), 1 (Low), 2 (Medium), 3 (High), 4 (Urgent)',
|
|
712
655
|
},
|
|
713
|
-
|
|
656
|
+
size: {
|
|
714
657
|
type: ['string', 'null'],
|
|
715
|
-
|
|
658
|
+
enum: ['S', 'M', 'L', null],
|
|
659
|
+
description: 'Size estimate: "S" (small), "M" (medium), "L" (large), or null to remove',
|
|
716
660
|
},
|
|
717
661
|
project_slug: {
|
|
718
662
|
type: 'string',
|
|
719
663
|
description: 'Project slug (required for multi-project users, use list_projects to get slugs)',
|
|
720
664
|
},
|
|
721
665
|
},
|
|
722
|
-
required: ['work_item_id'
|
|
666
|
+
required: ['work_item_id'],
|
|
723
667
|
},
|
|
724
668
|
},
|
|
725
669
|
{
|
|
@@ -1302,6 +1246,7 @@ class WolfpackMCPServer {
|
|
|
1302
1246
|
name: 'download_image',
|
|
1303
1247
|
description: 'Download and view an image referenced in content fields. ' +
|
|
1304
1248
|
'Content from work items, issues, wiki pages, journal entries, and comments may contain image references like ``. ' +
|
|
1249
|
+
'Issues may also have attachment images with URLs like `/api/teams/{team}/issues/{refId}/attachments/{id}`. ' +
|
|
1305
1250
|
'Use this tool with that URL path to download and view the image. ' +
|
|
1306
1251
|
'Requires mcp:images:read permission.',
|
|
1307
1252
|
inputSchema: {
|
|
@@ -1309,7 +1254,7 @@ class WolfpackMCPServer {
|
|
|
1309
1254
|
properties: {
|
|
1310
1255
|
image_url: {
|
|
1311
1256
|
type: 'string',
|
|
1312
|
-
description: 'The image URL path found in content fields (e.g. "/api/files/images/{team}/{filename}").',
|
|
1257
|
+
description: 'The image URL path found in content fields (e.g. "/api/files/images/{team}/{filename}" or "/api/teams/{team}/issues/{refId}/attachments/{id}").',
|
|
1313
1258
|
},
|
|
1314
1259
|
},
|
|
1315
1260
|
required: ['image_url'],
|
|
@@ -1450,78 +1395,61 @@ class WolfpackMCPServer {
|
|
|
1450
1395
|
content: [{ type: 'text', text: 'Work item not found or not assigned to you' }],
|
|
1451
1396
|
};
|
|
1452
1397
|
}
|
|
1453
|
-
case '
|
|
1454
|
-
const parsed =
|
|
1455
|
-
const teamSlug = parsed.project_slug || this.client.getProjectSlug() || undefined;
|
|
1456
|
-
const workItem = await this.client.updateWorkItemStatus(parsed.work_item_id, parsed.status, teamSlug);
|
|
1457
|
-
if (workItem) {
|
|
1458
|
-
return {
|
|
1459
|
-
content: [
|
|
1460
|
-
{
|
|
1461
|
-
type: 'text',
|
|
1462
|
-
text: `Updated status of "${workItem.title}" to ${parsed.status}\n\n${JSON.stringify(stripUuids(workItem), null, 2)}`,
|
|
1463
|
-
},
|
|
1464
|
-
],
|
|
1465
|
-
};
|
|
1466
|
-
}
|
|
1467
|
-
return {
|
|
1468
|
-
content: [{ type: 'text', text: 'Work item not found or not assigned to you' }],
|
|
1469
|
-
};
|
|
1470
|
-
}
|
|
1471
|
-
case 'update_work_item_assignee': {
|
|
1472
|
-
const parsed = UpdateWorkItemAssigneeSchema.parse(args);
|
|
1398
|
+
case 'update_work_item': {
|
|
1399
|
+
const parsed = UpdateWorkItemSchema.parse(args);
|
|
1473
1400
|
const teamSlug = parsed.project_slug || this.client.getProjectSlug() || undefined;
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1401
|
+
// Build the fields object with only provided values
|
|
1402
|
+
const fields = {};
|
|
1403
|
+
if (parsed.title !== undefined)
|
|
1404
|
+
fields.title = parsed.title;
|
|
1405
|
+
if (parsed.status !== undefined)
|
|
1406
|
+
fields.status = parsed.status;
|
|
1407
|
+
if (parsed.leading_user_id !== undefined)
|
|
1408
|
+
fields.leadingUserId = parsed.leading_user_id;
|
|
1409
|
+
if (parsed.radar_item_id !== undefined)
|
|
1410
|
+
fields.radarItemId = parsed.radar_item_id;
|
|
1411
|
+
if (parsed.category_id !== undefined)
|
|
1412
|
+
fields.categoryId = parsed.category_id;
|
|
1413
|
+
if (parsed.priority !== undefined)
|
|
1414
|
+
fields.priority = parsed.priority;
|
|
1415
|
+
if (parsed.size !== undefined)
|
|
1416
|
+
fields.size = parsed.size;
|
|
1417
|
+
const workItem = await this.client.updateWorkItem(parsed.work_item_id, fields, teamSlug);
|
|
1477
1418
|
if (workItem) {
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
}
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
}
|
|
1508
|
-
return {
|
|
1509
|
-
content: [{ type: 'text', text: 'Work item not found' }],
|
|
1510
|
-
};
|
|
1511
|
-
}
|
|
1512
|
-
case 'update_work_item_initiative': {
|
|
1513
|
-
const parsed = UpdateWorkItemInitiativeSchema.parse(args);
|
|
1514
|
-
const teamSlug = parsed.project_slug || this.client.getProjectSlug() || undefined;
|
|
1515
|
-
const workItem = await this.client.updateWorkItemInitiative(parsed.work_item_id, parsed.radar_item_id, teamSlug);
|
|
1516
|
-
if (workItem) {
|
|
1517
|
-
const initiativeText = parsed.radar_item_id
|
|
1518
|
-
? `linked to initiative ${parsed.radar_item_id}`
|
|
1519
|
-
: 'unlinked from initiative';
|
|
1419
|
+
// Build a summary of what changed
|
|
1420
|
+
const changes = [];
|
|
1421
|
+
if (parsed.status !== undefined)
|
|
1422
|
+
changes.push(`status → ${parsed.status}`);
|
|
1423
|
+
if (parsed.title !== undefined)
|
|
1424
|
+
changes.push(`title → "${parsed.title}"`);
|
|
1425
|
+
if (parsed.leading_user_id !== undefined)
|
|
1426
|
+
changes.push(parsed.leading_user_id
|
|
1427
|
+
? `assigned to ${workItem.leadingUser?.name || parsed.leading_user_id}`
|
|
1428
|
+
: 'unassigned');
|
|
1429
|
+
if (parsed.radar_item_id !== undefined)
|
|
1430
|
+
changes.push(parsed.radar_item_id ? `linked to initiative` : 'unlinked from initiative');
|
|
1431
|
+
if (parsed.category_id !== undefined)
|
|
1432
|
+
changes.push(parsed.category_id
|
|
1433
|
+
? `category → ${workItem.category?.name || parsed.category_id}`
|
|
1434
|
+
: 'category removed');
|
|
1435
|
+
if (parsed.priority !== undefined) {
|
|
1436
|
+
const labels = {
|
|
1437
|
+
0: 'None',
|
|
1438
|
+
1: 'Low',
|
|
1439
|
+
2: 'Medium',
|
|
1440
|
+
3: 'High',
|
|
1441
|
+
4: 'Urgent',
|
|
1442
|
+
};
|
|
1443
|
+
changes.push(`priority → ${labels[parsed.priority] || parsed.priority}`);
|
|
1444
|
+
}
|
|
1445
|
+
if (parsed.size !== undefined)
|
|
1446
|
+
changes.push(parsed.size ? `size → ${parsed.size}` : 'size removed');
|
|
1447
|
+
const summary = changes.length > 0 ? changes.join(', ') : 'no changes';
|
|
1520
1448
|
return {
|
|
1521
1449
|
content: [
|
|
1522
1450
|
{
|
|
1523
1451
|
type: 'text',
|
|
1524
|
-
text: `Updated "${workItem.title}"
|
|
1452
|
+
text: `Updated "${workItem.title}" (${summary})\n\n${JSON.stringify(stripUuids(workItem), null, 2)}`,
|
|
1525
1453
|
},
|
|
1526
1454
|
],
|
|
1527
1455
|
};
|
|
@@ -1918,12 +1846,23 @@ class WolfpackMCPServer {
|
|
|
1918
1846
|
}
|
|
1919
1847
|
case 'download_image': {
|
|
1920
1848
|
const parsed = DownloadImageSchema.parse(args);
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1849
|
+
// Try S3-stored image pattern: /api/files/images/{team}/{filename}
|
|
1850
|
+
const s3Match = parsed.image_url.match(/\/api\/files\/images\/([^/]+)\/(.+)$/);
|
|
1851
|
+
// Try issue attachment pattern: /api/teams/{team}/issues/{refId}/attachments/{id}
|
|
1852
|
+
const attachmentMatch = parsed.image_url.match(/\/api\/teams\/([^/]+)\/issues\/(\d+)\/attachments\/([^/]+)$/);
|
|
1853
|
+
let base64;
|
|
1854
|
+
let mimeType;
|
|
1855
|
+
if (s3Match) {
|
|
1856
|
+
const [, team, filename] = s3Match;
|
|
1857
|
+
({ base64, mimeType } = await this.client.downloadImage(team, filename));
|
|
1858
|
+
}
|
|
1859
|
+
else if (attachmentMatch) {
|
|
1860
|
+
const [, team, refId, attachmentId] = attachmentMatch;
|
|
1861
|
+
({ base64, mimeType } = await this.client.downloadIssueAttachment(team, refId, attachmentId));
|
|
1862
|
+
}
|
|
1863
|
+
else {
|
|
1864
|
+
throw new Error('Invalid image URL. Expected format: /api/files/images/{team}/{filename} or /api/teams/{team}/issues/{refId}/attachments/{id}');
|
|
1924
1865
|
}
|
|
1925
|
-
const [, team, filename] = match;
|
|
1926
|
-
const { base64, mimeType } = await this.client.downloadImage(team, filename);
|
|
1927
1866
|
return {
|
|
1928
1867
|
content: [
|
|
1929
1868
|
{
|