wolfpack-mcp 1.0.42 → 1.0.44

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/dist/client.js +38 -35
  2. package/dist/index.js +383 -168
  3. package/package.json +1 -1
package/dist/client.js CHANGED
@@ -53,6 +53,14 @@ export class WolfpackClient {
53
53
  const response = await this.api.get('/teams');
54
54
  return response.teams;
55
55
  }
56
+ // Team Member methods
57
+ async listTeamMembers(teamSlug) {
58
+ const params = new URLSearchParams();
59
+ if (teamSlug)
60
+ params.append('teamSlug', teamSlug);
61
+ const query = params.toString();
62
+ return this.api.get(`/team-members${query ? `?${query}` : ''}`);
63
+ }
56
64
  // Helper to fetch all pages of a paginated endpoint
57
65
  async fetchAllPages(endpoint, baseParams) {
58
66
  // First request to get total count
@@ -175,42 +183,9 @@ export class WolfpackClient {
175
183
  throw error;
176
184
  }
177
185
  }
178
- async updateWorkItemStatus(workItemId, status, teamSlug) {
179
- try {
180
- return await this.api.put(this.withTeamSlug(`/work-items/${workItemId}/status`, teamSlug), { status });
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) {
186
+ async updateWorkItem(workItemId, fields, teamSlug) {
201
187
  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 });
188
+ return await this.api.patch(this.withTeamSlug(`/work-items/${workItemId}`, teamSlug), fields);
214
189
  }
215
190
  catch (error) {
216
191
  if (error && typeof error === 'object' && 'status' in error && error.status === 404) {
@@ -285,6 +260,13 @@ export class WolfpackClient {
285
260
  throw error;
286
261
  }
287
262
  }
263
+ async createRadarItem(data) {
264
+ const { teamSlug, ...rest } = data;
265
+ return this.api.post('/radar-items', { ...rest, teamSlug });
266
+ }
267
+ async updateRadarItem(itemId, data, teamSlug) {
268
+ return this.api.patch(this.withTeamSlug(`/radar-items/${itemId}`, teamSlug), data);
269
+ }
288
270
  // Issue methods
289
271
  async listIssues(options) {
290
272
  const params = new URLSearchParams();
@@ -466,6 +448,20 @@ export class WolfpackClient {
466
448
  async createIssueComment(issueId, data, teamSlug) {
467
449
  return this.api.post(this.withTeamSlug(`/issues/${issueId}/comments`, teamSlug), data);
468
450
  }
451
+ async deleteWorkItemComment(workItemId, commentId, teamSlug) {
452
+ return this.api.delete(this.withTeamSlug(`/work-items/${workItemId}/comments/${commentId}`, teamSlug));
453
+ }
454
+ async deleteIssueComment(issueId, commentId, teamSlug) {
455
+ return this.api.delete(this.withTeamSlug(`/issues/${issueId}/comments/${commentId}`, teamSlug));
456
+ }
457
+ // Category methods
458
+ async listCategories(teamSlug) {
459
+ const params = new URLSearchParams();
460
+ if (teamSlug)
461
+ params.append('teamSlug', teamSlug);
462
+ const query = params.toString();
463
+ return this.api.get(`/categories${query ? `?${query}` : ''}`);
464
+ }
469
465
  /**
470
466
  * Upload an image and get back a URL that can be used in markdown content.
471
467
  */
@@ -480,6 +476,13 @@ export class WolfpackClient {
480
476
  const { buffer, contentType } = await this.api.getBuffer(`/images/${team}/${filename}`);
481
477
  return { base64: buffer.toString('base64'), mimeType: contentType };
482
478
  }
479
+ /**
480
+ * Download an issue attachment image and return raw base64 + mimeType.
481
+ */
482
+ async downloadIssueAttachment(team, refId, attachmentId) {
483
+ const { buffer, contentType } = await this.api.getBuffer(`/issues/${team}/${refId}/attachments/${attachmentId}`);
484
+ return { base64: buffer.toString('base64'), mimeType: contentType };
485
+ }
483
486
  // Skill methods (progressive disclosure)
484
487
  async listSkills() {
485
488
  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 UpdateWorkItemStatusSchema = z.object({
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
- project_slug: z
124
+ radar_item_id: z
130
125
  .string()
126
+ .nullable()
131
127
  .optional()
132
- .describe('Project slug (required for multi-project users, use list_projects to get slugs)'),
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('Project slug (required for multi-project users, use list_projects to get slugs)'),
141
- });
142
- const UpdateWorkItemInitiativeSchema = z.object({
143
- work_item_id: z.string().describe('The ID of the work item'),
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
- .describe('Radar/initiative item ID to link to, or null to unlink'),
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()
@@ -184,6 +176,31 @@ const GetRadarItemSchema = z.object({
184
176
  item_id: z.string().describe('The refId (number) of the radar item'),
185
177
  project_slug: z.string().optional().describe('Project slug (required when looking up by refId)'),
186
178
  });
179
+ const CreateRadarItemSchema = z.object({
180
+ project_slug: z
181
+ .string()
182
+ .optional()
183
+ .describe('Project slug (required for multi-project users, use list_projects to get slugs)'),
184
+ title: z.string().describe('Title of the radar/initiative item'),
185
+ description: z.string().optional().describe('Description of the initiative (markdown)'),
186
+ stage: z
187
+ .enum(['pending', 'now', 'next', 'later', 'completed'])
188
+ .describe('Stage: "pending" (not started), "now" (current sprint), "next" (upcoming), "later" (future), "completed"'),
189
+ });
190
+ const UpdateRadarItemSchema = z.object({
191
+ item_id: z.string().describe('The refId (number) of the radar item to update'),
192
+ title: z.string().optional().describe('Updated title'),
193
+ description: z.string().optional().describe('Updated description (markdown)'),
194
+ notes: z.string().optional().describe('Updated internal notes (markdown)'),
195
+ stage: z
196
+ .enum(['pending', 'now', 'next', 'later', 'completed'])
197
+ .optional()
198
+ .describe('Updated stage'),
199
+ project_slug: z
200
+ .string()
201
+ .optional()
202
+ .describe('Project slug (required for multi-project users, use list_projects to get slugs)'),
203
+ });
187
204
  // Issue schemas
188
205
  const ListIssuesSchema = z.object({
189
206
  project_slug: z.string().optional().describe('Project slug to filter issues'),
@@ -271,8 +288,18 @@ const UpdateIssueSchema = z.object({
271
288
  .optional()
272
289
  .describe('Updated status'),
273
290
  severity: z.enum(['low', 'medium', 'high', 'critical']).optional().describe('Updated severity'),
291
+ type: z
292
+ .enum(['bug', 'feature-request', 'task', 'improvement', 'question'])
293
+ .optional()
294
+ .describe('Updated type'),
274
295
  assigned_to_id: z.string().optional().describe('Updated assignee'),
275
296
  closing_note: z.string().optional().describe('Closing note (when closing the issue)'),
297
+ environment: z.string().optional().describe('Environment where the issue occurred'),
298
+ affected_version: z.string().optional().describe('Affected version'),
299
+ reproducible: z.boolean().optional().describe('Whether reproducible'),
300
+ steps_to_reproduce: z.string().optional().describe('Steps to reproduce'),
301
+ expected_behavior: z.string().optional().describe('Expected behavior'),
302
+ actual_behavior: z.string().optional().describe('Actual behavior'),
276
303
  project_slug: z
277
304
  .string()
278
305
  .optional()
@@ -388,6 +415,22 @@ const CreateIssueCommentSchema = z.object({
388
415
  .optional()
389
416
  .describe('Project slug (required for multi-project users, use list_projects to get slugs)'),
390
417
  });
418
+ const DeleteWorkItemCommentSchema = z.object({
419
+ work_item_id: z.string().describe('The work item refId (number)'),
420
+ comment_id: z.string().describe('The comment ID to delete'),
421
+ project_slug: z
422
+ .string()
423
+ .optional()
424
+ .describe('Project slug (required for multi-project users, use list_projects to get slugs)'),
425
+ });
426
+ const DeleteIssueCommentSchema = z.object({
427
+ issue_id: z.string().describe('The issue refId (number)'),
428
+ comment_id: z.string().describe('The comment ID to delete'),
429
+ project_slug: z
430
+ .string()
431
+ .optional()
432
+ .describe('Project slug (required for multi-project users, use list_projects to get slugs)'),
433
+ });
391
434
  // Image upload schema
392
435
  const UploadImageSchema = z.object({
393
436
  file_path: z
@@ -412,6 +455,20 @@ const GetSkillResourceSchema = z.object({
412
455
  skill_name: z.string().describe('The name of the skill'),
413
456
  resource_name: z.string().describe('The name of the resource file'),
414
457
  });
458
+ // Category schemas
459
+ const ListCategoriesSchema = z.object({
460
+ project_slug: z
461
+ .string()
462
+ .optional()
463
+ .describe('Project slug (required for multi-project users, use list_projects to get slugs)'),
464
+ });
465
+ // Team member schemas
466
+ const ListTeamMembersSchema = z.object({
467
+ project_slug: z
468
+ .string()
469
+ .optional()
470
+ .describe('Project slug (required for multi-project users, use list_projects to get slugs)'),
471
+ });
415
472
  // UUID v4 field names to remove from responses.
416
473
  // These are internal database IDs that agents should not use.
417
474
  // Agents should use refId (sequential int) or slug instead.
@@ -612,16 +669,13 @@ class WolfpackMCPServer {
612
669
  },
613
670
  },
614
671
  {
615
- name: 'update_work_item_status',
616
- description: 'Change work item status. ' +
617
- 'REQUIRED WORKFLOW when working on items: ' +
618
- '1) For "new" items: move to "doing" when starting work. ' +
619
- '2) For "pending" (backlog) items: use pull_work_item first (claims and sets to "new"), then move to "doing". ' +
620
- '3) For items in "blocked", "ready", "completed", or "closed": move to "new" first, then to "doing". ' +
621
- '4) When work is complete: move to "review" (NOT "completed" - that is only for deployed work) and add a completion comment summarizing what was done. ' +
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).',
672
+ name: 'update_work_item',
673
+ description: 'Update one or more fields on a work item. Only provide the fields you want to change. ' +
674
+ 'Supports: title, status, leading_user_id (assignee), radar_item_id (initiative), ' +
675
+ 'category_id, priority (0-4), and size (S/M/L). ' +
676
+ 'STATUS WORKFLOW: "pending" (backlog) "new" (to do) → "doing" (in progress) "review" (work done) "ready" (awaiting deployment) → "completed" (deployed). ' +
677
+ 'Use "blocked" when work cannot proceed. For "pending" items use pull_work_item first. ' +
678
+ 'When moving to "review", add a completion comment. When moving to "blocked", add a comment explaining the blocker.',
625
679
  inputSchema: {
626
680
  type: 'object',
627
681
  properties: {
@@ -629,6 +683,10 @@ class WolfpackMCPServer {
629
683
  type: 'string',
630
684
  description: 'The ID of the work item',
631
685
  },
686
+ title: {
687
+ type: 'string',
688
+ description: 'New title for the work item',
689
+ },
632
690
  status: {
633
691
  type: 'string',
634
692
  enum: [
@@ -642,84 +700,35 @@ class WolfpackMCPServer {
642
700
  'closed',
643
701
  'archived',
644
702
  ],
645
- description: 'New status: "pending" (backlog), "new" (to do), "doing" (in progress), "review" (work done), "ready" (awaiting deployment), "blocked", "completed" (deployed), "closed", "archived"',
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',
703
+ description: 'New status',
666
704
  },
667
705
  leading_user_id: {
668
706
  type: ['string', 'null'],
669
707
  description: 'User ID to assign as leading user, or null to unassign',
670
708
  },
671
- project_slug: {
672
- type: 'string',
673
- description: 'Project slug (required for multi-project users, use list_projects to get slugs)',
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',
709
+ radar_item_id: {
710
+ type: ['string', 'null'],
711
+ description: 'Radar/initiative item ID to link to, or null to unlink',
693
712
  },
694
- project_slug: {
695
- type: 'string',
696
- description: 'Project slug (required for multi-project users, use list_projects to get slugs)',
713
+ category_id: {
714
+ type: ['string', 'null'],
715
+ description: 'Category ID to set, or null to remove the category',
697
716
  },
698
- },
699
- required: ['work_item_id', 'title'],
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',
717
+ priority: {
718
+ type: 'number',
719
+ description: 'Priority level: 0 (None), 1 (Low), 2 (Medium), 3 (High), 4 (Urgent)',
712
720
  },
713
- radar_item_id: {
721
+ size: {
714
722
  type: ['string', 'null'],
715
- description: 'Radar/initiative item ID to link to, or null to unlink',
723
+ enum: ['S', 'M', 'L', null],
724
+ description: 'Size estimate: "S" (small), "M" (medium), "L" (large), or null to remove',
716
725
  },
717
726
  project_slug: {
718
727
  type: 'string',
719
728
  description: 'Project slug (required for multi-project users, use list_projects to get slugs)',
720
729
  },
721
730
  },
722
- required: ['work_item_id', 'radar_item_id'],
731
+ required: ['work_item_id'],
723
732
  },
724
733
  },
725
734
  {
@@ -817,6 +826,58 @@ class WolfpackMCPServer {
817
826
  required: ['item_id'],
818
827
  },
819
828
  },
829
+ {
830
+ name: 'create_radar_item',
831
+ description: 'Create a new radar/initiative item in your current project. Requires mcp:radar:create permission. ' +
832
+ CONTENT_LINKING_HELP,
833
+ inputSchema: {
834
+ type: 'object',
835
+ properties: {
836
+ project_slug: {
837
+ type: 'string',
838
+ description: 'Project slug (required for multi-project users, use list_projects to get slugs)',
839
+ },
840
+ title: { type: 'string', description: 'Title of the radar/initiative item' },
841
+ description: {
842
+ type: 'string',
843
+ description: 'Description of the initiative (markdown)',
844
+ },
845
+ stage: {
846
+ type: 'string',
847
+ enum: ['pending', 'now', 'next', 'later', 'completed'],
848
+ description: 'Stage: "pending" (not started), "now" (current sprint), "next" (upcoming), "later" (future), "completed"',
849
+ },
850
+ },
851
+ required: ['title', 'stage'],
852
+ },
853
+ },
854
+ {
855
+ name: 'update_radar_item',
856
+ description: 'Update an existing radar/initiative item. Requires mcp:radar:update permission. ' +
857
+ CONTENT_LINKING_HELP,
858
+ inputSchema: {
859
+ type: 'object',
860
+ properties: {
861
+ item_id: {
862
+ type: 'string',
863
+ description: 'The refId (number) of the radar item to update',
864
+ },
865
+ title: { type: 'string', description: 'Updated title' },
866
+ description: { type: 'string', description: 'Updated description (markdown)' },
867
+ notes: { type: 'string', description: 'Updated internal notes (markdown)' },
868
+ stage: {
869
+ type: 'string',
870
+ enum: ['pending', 'now', 'next', 'later', 'completed'],
871
+ description: 'Updated stage',
872
+ },
873
+ project_slug: {
874
+ type: 'string',
875
+ description: 'Project slug (required for multi-project users, use list_projects to get slugs)',
876
+ },
877
+ },
878
+ required: ['item_id'],
879
+ },
880
+ },
820
881
  // Issue tools
821
882
  {
822
883
  name: 'list_issues',
@@ -1009,11 +1070,22 @@ class WolfpackMCPServer {
1009
1070
  enum: ['low', 'medium', 'high', 'critical'],
1010
1071
  description: 'Updated severity',
1011
1072
  },
1073
+ type: {
1074
+ type: 'string',
1075
+ enum: ['bug', 'feature-request', 'task', 'improvement', 'question'],
1076
+ description: 'Updated type',
1077
+ },
1012
1078
  assigned_to_id: { type: 'string', description: 'Updated assignee' },
1013
1079
  closing_note: {
1014
1080
  type: 'string',
1015
1081
  description: 'Closing note (when closing the issue)',
1016
1082
  },
1083
+ environment: { type: 'string', description: 'Environment where the issue occurred' },
1084
+ affected_version: { type: 'string', description: 'Affected version' },
1085
+ reproducible: { type: 'boolean', description: 'Whether reproducible' },
1086
+ steps_to_reproduce: { type: 'string', description: 'Steps to reproduce' },
1087
+ expected_behavior: { type: 'string', description: 'Expected behavior' },
1088
+ actual_behavior: { type: 'string', description: 'Actual behavior' },
1017
1089
  project_slug: {
1018
1090
  type: 'string',
1019
1091
  description: 'Project slug (required for multi-project users, use list_projects to get slugs)',
@@ -1273,6 +1345,40 @@ class WolfpackMCPServer {
1273
1345
  required: ['issue_id', 'content'],
1274
1346
  },
1275
1347
  },
1348
+ {
1349
+ name: 'delete_work_item_comment',
1350
+ description: 'Delete a comment from a work item. Only the comment author can delete their own comments. ' +
1351
+ 'Requires mcp:comments:delete permission. Use list_work_item_comments to get comment IDs.',
1352
+ inputSchema: {
1353
+ type: 'object',
1354
+ properties: {
1355
+ work_item_id: { type: 'string', description: 'The work item refId (number)' },
1356
+ comment_id: { type: 'string', description: 'The comment ID to delete' },
1357
+ project_slug: {
1358
+ type: 'string',
1359
+ description: 'Project slug (required for multi-project users, use list_projects to get slugs)',
1360
+ },
1361
+ },
1362
+ required: ['work_item_id', 'comment_id'],
1363
+ },
1364
+ },
1365
+ {
1366
+ name: 'delete_issue_comment',
1367
+ description: 'Delete a comment from an issue. Only the comment author can delete their own comments. ' +
1368
+ 'Requires mcp:comments:delete permission. Use list_issue_comments to get comment IDs.',
1369
+ inputSchema: {
1370
+ type: 'object',
1371
+ properties: {
1372
+ issue_id: { type: 'string', description: 'The issue refId (number)' },
1373
+ comment_id: { type: 'string', description: 'The comment ID to delete' },
1374
+ project_slug: {
1375
+ type: 'string',
1376
+ description: 'Project slug (required for multi-project users, use list_projects to get slugs)',
1377
+ },
1378
+ },
1379
+ required: ['issue_id', 'comment_id'],
1380
+ },
1381
+ },
1276
1382
  // Image tools
1277
1383
  {
1278
1384
  name: 'upload_image',
@@ -1302,6 +1408,7 @@ class WolfpackMCPServer {
1302
1408
  name: 'download_image',
1303
1409
  description: 'Download and view an image referenced in content fields. ' +
1304
1410
  'Content from work items, issues, wiki pages, journal entries, and comments may contain image references like `![alt](/api/files/images/...)`. ' +
1411
+ 'Issues may also have attachment images with URLs like `/api/teams/{team}/issues/{refId}/attachments/{id}`. ' +
1305
1412
  'Use this tool with that URL path to download and view the image. ' +
1306
1413
  'Requires mcp:images:read permission.',
1307
1414
  inputSchema: {
@@ -1309,12 +1416,42 @@ class WolfpackMCPServer {
1309
1416
  properties: {
1310
1417
  image_url: {
1311
1418
  type: 'string',
1312
- description: 'The image URL path found in content fields (e.g. "/api/files/images/{team}/{filename}").',
1419
+ 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
1420
  },
1314
1421
  },
1315
1422
  required: ['image_url'],
1316
1423
  },
1317
1424
  },
1425
+ // Category tools
1426
+ {
1427
+ name: 'list_categories',
1428
+ description: 'List all categories available in a project. Returns category IDs, names, and descriptions. ' +
1429
+ 'Use this to look up category IDs by name when creating or updating work items.',
1430
+ inputSchema: {
1431
+ type: 'object',
1432
+ properties: {
1433
+ project_slug: {
1434
+ type: 'string',
1435
+ description: 'Project slug (required for multi-project users, use list_projects to get slugs)',
1436
+ },
1437
+ },
1438
+ },
1439
+ },
1440
+ // Team member tools
1441
+ {
1442
+ name: 'list_team_members',
1443
+ description: 'List all members in a project/team. Returns user IDs, names, usernames, roles, and whether the member is an agent. ' +
1444
+ 'Use this to look up user IDs when assigning work items or issues.',
1445
+ inputSchema: {
1446
+ type: 'object',
1447
+ properties: {
1448
+ project_slug: {
1449
+ type: 'string',
1450
+ description: 'Project slug (required for multi-project users, use list_projects to get slugs)',
1451
+ },
1452
+ },
1453
+ },
1454
+ },
1318
1455
  // Skill tools (progressive disclosure)
1319
1456
  {
1320
1457
  name: 'list_skills',
@@ -1450,78 +1587,61 @@ class WolfpackMCPServer {
1450
1587
  content: [{ type: 'text', text: 'Work item not found or not assigned to you' }],
1451
1588
  };
1452
1589
  }
1453
- case 'update_work_item_status': {
1454
- const parsed = UpdateWorkItemStatusSchema.parse(args);
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);
1473
- const teamSlug = parsed.project_slug || this.client.getProjectSlug() || undefined;
1474
- const workItem = await this.client.updateWorkItemAssignee(parsed.work_item_id, {
1475
- leadingUserId: parsed.leading_user_id,
1476
- }, teamSlug);
1477
- if (workItem) {
1478
- const assigneeText = parsed.leading_user_id
1479
- ? `assigned to ${parsed.leading_user_id}`
1480
- : 'unassigned';
1481
- return {
1482
- content: [
1483
- {
1484
- type: 'text',
1485
- text: `Updated "${workItem.title}" - now ${assigneeText}\n\n${JSON.stringify(stripUuids(workItem), null, 2)}`,
1486
- },
1487
- ],
1488
- };
1489
- }
1490
- return {
1491
- content: [{ type: 'text', text: 'Work item not found' }],
1492
- };
1493
- }
1494
- case 'update_work_item_title': {
1495
- const parsed = UpdateWorkItemTitleSchema.parse(args);
1496
- const teamSlug = parsed.project_slug || this.client.getProjectSlug() || undefined;
1497
- const workItem = await this.client.updateWorkItemTitle(parsed.work_item_id, parsed.title, teamSlug);
1498
- if (workItem) {
1499
- return {
1500
- content: [
1501
- {
1502
- type: 'text',
1503
- text: `Updated title to "${workItem.title}"\n\n${JSON.stringify(stripUuids(workItem), null, 2)}`,
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);
1590
+ case 'update_work_item': {
1591
+ const parsed = UpdateWorkItemSchema.parse(args);
1514
1592
  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);
1593
+ // Build the fields object with only provided values
1594
+ const fields = {};
1595
+ if (parsed.title !== undefined)
1596
+ fields.title = parsed.title;
1597
+ if (parsed.status !== undefined)
1598
+ fields.status = parsed.status;
1599
+ if (parsed.leading_user_id !== undefined)
1600
+ fields.leadingUserId = parsed.leading_user_id;
1601
+ if (parsed.radar_item_id !== undefined)
1602
+ fields.radarItemId = parsed.radar_item_id;
1603
+ if (parsed.category_id !== undefined)
1604
+ fields.categoryId = parsed.category_id;
1605
+ if (parsed.priority !== undefined)
1606
+ fields.priority = parsed.priority;
1607
+ if (parsed.size !== undefined)
1608
+ fields.size = parsed.size;
1609
+ const workItem = await this.client.updateWorkItem(parsed.work_item_id, fields, teamSlug);
1516
1610
  if (workItem) {
1517
- const initiativeText = parsed.radar_item_id
1518
- ? `linked to initiative ${parsed.radar_item_id}`
1519
- : 'unlinked from initiative';
1611
+ // Build a summary of what changed
1612
+ const changes = [];
1613
+ if (parsed.status !== undefined)
1614
+ changes.push(`status → ${parsed.status}`);
1615
+ if (parsed.title !== undefined)
1616
+ changes.push(`title → "${parsed.title}"`);
1617
+ if (parsed.leading_user_id !== undefined)
1618
+ changes.push(parsed.leading_user_id
1619
+ ? `assigned to ${workItem.leadingUser?.name || parsed.leading_user_id}`
1620
+ : 'unassigned');
1621
+ if (parsed.radar_item_id !== undefined)
1622
+ changes.push(parsed.radar_item_id ? `linked to initiative` : 'unlinked from initiative');
1623
+ if (parsed.category_id !== undefined)
1624
+ changes.push(parsed.category_id
1625
+ ? `category → ${workItem.category?.name || parsed.category_id}`
1626
+ : 'category removed');
1627
+ if (parsed.priority !== undefined) {
1628
+ const labels = {
1629
+ 0: 'None',
1630
+ 1: 'Low',
1631
+ 2: 'Medium',
1632
+ 3: 'High',
1633
+ 4: 'Urgent',
1634
+ };
1635
+ changes.push(`priority → ${labels[parsed.priority] || parsed.priority}`);
1636
+ }
1637
+ if (parsed.size !== undefined)
1638
+ changes.push(parsed.size ? `size → ${parsed.size}` : 'size removed');
1639
+ const summary = changes.length > 0 ? changes.join(', ') : 'no changes';
1520
1640
  return {
1521
1641
  content: [
1522
1642
  {
1523
1643
  type: 'text',
1524
- text: `Updated "${workItem.title}" - ${initiativeText}\n\n${JSON.stringify(stripUuids(workItem), null, 2)}`,
1644
+ text: `Updated "${workItem.title}" (${summary})\n\n${JSON.stringify(stripUuids(workItem), null, 2)}`,
1525
1645
  },
1526
1646
  ],
1527
1647
  };
@@ -1598,6 +1718,41 @@ class WolfpackMCPServer {
1598
1718
  content: [{ type: 'text', text: 'Radar item not found' }],
1599
1719
  };
1600
1720
  }
1721
+ case 'create_radar_item': {
1722
+ const parsed = CreateRadarItemSchema.parse(args);
1723
+ const radarItem = await this.client.createRadarItem({
1724
+ title: parsed.title,
1725
+ description: parsed.description,
1726
+ stage: parsed.stage,
1727
+ teamSlug: parsed.project_slug || this.client.getProjectSlug() || undefined,
1728
+ });
1729
+ return {
1730
+ content: [
1731
+ {
1732
+ type: 'text',
1733
+ text: `Created radar item #${radarItem.refId}: ${radarItem.title}\n\n${JSON.stringify(stripUuids(radarItem), null, 2)}`,
1734
+ },
1735
+ ],
1736
+ };
1737
+ }
1738
+ case 'update_radar_item': {
1739
+ const parsed = UpdateRadarItemSchema.parse(args);
1740
+ const teamSlug = parsed.project_slug || this.client.getProjectSlug() || undefined;
1741
+ const radarItem = await this.client.updateRadarItem(parsed.item_id, {
1742
+ title: parsed.title,
1743
+ description: parsed.description,
1744
+ notes: parsed.notes,
1745
+ stage: parsed.stage,
1746
+ }, teamSlug);
1747
+ return {
1748
+ content: [
1749
+ {
1750
+ type: 'text',
1751
+ text: `Updated radar item #${radarItem.refId}: ${radarItem.title}\n\n${JSON.stringify(stripUuids(radarItem), null, 2)}`,
1752
+ },
1753
+ ],
1754
+ };
1755
+ }
1601
1756
  // Issue handlers
1602
1757
  case 'list_issues': {
1603
1758
  const parsed = ListIssuesSchema.parse(args);
@@ -1691,8 +1846,15 @@ class WolfpackMCPServer {
1691
1846
  description: parsed.description,
1692
1847
  status: parsed.status,
1693
1848
  severity: parsed.severity,
1849
+ type: parsed.type,
1694
1850
  assignedToId: parsed.assigned_to_id,
1695
1851
  closingNote: parsed.closing_note,
1852
+ environment: parsed.environment,
1853
+ affectedVersion: parsed.affected_version,
1854
+ reproducible: parsed.reproducible,
1855
+ stepsToReproduce: parsed.steps_to_reproduce,
1856
+ expectedBehavior: parsed.expected_behavior,
1857
+ actualBehavior: parsed.actual_behavior,
1696
1858
  }, teamSlug);
1697
1859
  return {
1698
1860
  content: [
@@ -1877,6 +2039,32 @@ class WolfpackMCPServer {
1877
2039
  ],
1878
2040
  };
1879
2041
  }
2042
+ case 'delete_work_item_comment': {
2043
+ const parsed = DeleteWorkItemCommentSchema.parse(args);
2044
+ const teamSlug = parsed.project_slug || this.client.getProjectSlug() || undefined;
2045
+ await this.client.deleteWorkItemComment(parsed.work_item_id, parsed.comment_id, teamSlug);
2046
+ return {
2047
+ content: [
2048
+ {
2049
+ type: 'text',
2050
+ text: 'Comment deleted successfully',
2051
+ },
2052
+ ],
2053
+ };
2054
+ }
2055
+ case 'delete_issue_comment': {
2056
+ const parsed = DeleteIssueCommentSchema.parse(args);
2057
+ const teamSlug = parsed.project_slug || this.client.getProjectSlug() || undefined;
2058
+ await this.client.deleteIssueComment(parsed.issue_id, parsed.comment_id, teamSlug);
2059
+ return {
2060
+ content: [
2061
+ {
2062
+ type: 'text',
2063
+ text: 'Comment deleted successfully',
2064
+ },
2065
+ ],
2066
+ };
2067
+ }
1880
2068
  case 'upload_image': {
1881
2069
  const parsed = UploadImageSchema.parse(args);
1882
2070
  const teamSlug = await this.client.resolveSlug(parsed.project_slug || this.client.getProjectSlug() || undefined);
@@ -1918,12 +2106,23 @@ class WolfpackMCPServer {
1918
2106
  }
1919
2107
  case 'download_image': {
1920
2108
  const parsed = DownloadImageSchema.parse(args);
1921
- const match = parsed.image_url.match(/\/api\/files\/images\/([^/]+)\/(.+)$/);
1922
- if (!match) {
1923
- throw new Error('Invalid image URL. Expected format: /api/files/images/{team}/{filename}');
2109
+ // Try S3-stored image pattern: /api/files/images/{team}/{filename}
2110
+ const s3Match = parsed.image_url.match(/\/api\/files\/images\/([^/]+)\/(.+)$/);
2111
+ // Try issue attachment pattern: /api/teams/{team}/issues/{refId}/attachments/{id}
2112
+ const attachmentMatch = parsed.image_url.match(/\/api\/teams\/([^/]+)\/issues\/(\d+)\/attachments\/([^/]+)$/);
2113
+ let base64;
2114
+ let mimeType;
2115
+ if (s3Match) {
2116
+ const [, team, filename] = s3Match;
2117
+ ({ base64, mimeType } = await this.client.downloadImage(team, filename));
2118
+ }
2119
+ else if (attachmentMatch) {
2120
+ const [, team, refId, attachmentId] = attachmentMatch;
2121
+ ({ base64, mimeType } = await this.client.downloadIssueAttachment(team, refId, attachmentId));
2122
+ }
2123
+ else {
2124
+ throw new Error('Invalid image URL. Expected format: /api/files/images/{team}/{filename} or /api/teams/{team}/issues/{refId}/attachments/{id}');
1924
2125
  }
1925
- const [, team, filename] = match;
1926
- const { base64, mimeType } = await this.client.downloadImage(team, filename);
1927
2126
  return {
1928
2127
  content: [
1929
2128
  {
@@ -1934,6 +2133,22 @@ class WolfpackMCPServer {
1934
2133
  ],
1935
2134
  };
1936
2135
  }
2136
+ // Category handlers
2137
+ case 'list_categories': {
2138
+ const parsed = ListCategoriesSchema.parse(args);
2139
+ const categories = await this.client.listCategories(parsed.project_slug || this.client.getProjectSlug() || undefined);
2140
+ return {
2141
+ content: [{ type: 'text', text: JSON.stringify(categories, null, 2) }],
2142
+ };
2143
+ }
2144
+ // Team member handlers
2145
+ case 'list_team_members': {
2146
+ const parsed = ListTeamMembersSchema.parse(args);
2147
+ const members = await this.client.listTeamMembers(parsed.project_slug || this.client.getProjectSlug() || undefined);
2148
+ return {
2149
+ content: [{ type: 'text', text: JSON.stringify(members, null, 2) }],
2150
+ };
2151
+ }
1937
2152
  // Skill handlers
1938
2153
  case 'list_skills': {
1939
2154
  const skills = await this.client.listSkills();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wolfpack-mcp",
3
- "version": "1.0.42",
3
+ "version": "1.0.44",
4
4
  "description": "MCP server for Wolfpack AI-enhanced software delivery tools",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",