wolfpack-mcp 1.0.31 → 1.0.33

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 CHANGED
@@ -5,42 +5,38 @@ const MAX_ITEMS_THRESHOLD = 200;
5
5
  const PAGE_SIZE = 50;
6
6
  export class WolfpackClient {
7
7
  api;
8
- projectId = null;
8
+ projectSlug = null;
9
9
  constructor() {
10
10
  this.api = new ApiClient();
11
11
  }
12
12
  /**
13
- * Fetch and cache the project ID from a project slug.
14
- * Uses the MCP team endpoint to resolve slug to ID.
13
+ * Fetch and cache the project slug by verifying it exists.
14
+ * Uses the MCP team endpoint to validate the slug.
15
15
  */
16
16
  async resolveProjectSlug(projectSlug) {
17
17
  try {
18
- const project = await this.api.get(`/team/${projectSlug}`);
19
- this.projectId = project.id;
20
- return project.id;
18
+ await this.api.get(`/team/${projectSlug}`);
19
+ this.projectSlug = projectSlug;
20
+ return projectSlug;
21
21
  }
22
22
  catch (error) {
23
23
  throw new Error(`Failed to resolve project slug "${projectSlug}": ${error instanceof Error ? error.message : String(error)}`);
24
24
  }
25
25
  }
26
- getProjectId() {
27
- return this.projectId;
26
+ getProjectSlug() {
27
+ return this.projectSlug;
28
28
  }
29
- setProjectId(projectId) {
30
- this.projectId = projectId;
29
+ setProjectSlug(slug) {
30
+ this.projectSlug = slug;
31
31
  }
32
32
  /**
33
- * Resolve a project slug from a project ID or the cached project ID.
33
+ * Resolve a project slug from the provided slug or the cached project slug.
34
34
  */
35
- async resolveSlug(projectId) {
36
- const id = projectId || this.projectId;
37
- if (!id)
35
+ async resolveSlug(teamSlug) {
36
+ const slug = teamSlug || this.projectSlug;
37
+ if (!slug)
38
38
  throw new Error('No project selected. Use list_projects first.');
39
- const projects = await this.listProjects();
40
- const project = projects.find((t) => t.id === id);
41
- if (!project)
42
- throw new Error(`Project with ID ${id} not found`);
43
- return project.slug;
39
+ return slug;
44
40
  }
45
41
  /**
46
42
  * List all projects the authenticated user is a member of.
@@ -98,8 +94,8 @@ export class WolfpackClient {
98
94
  // Work Item methods
99
95
  async listWorkItems(options) {
100
96
  const params = new URLSearchParams();
101
- if (options?.teamId)
102
- params.append('teamId', options.teamId.toString());
97
+ if (options?.teamSlug)
98
+ params.append('teamSlug', options.teamSlug);
103
99
  if (options?.status)
104
100
  params.append('status', options.status);
105
101
  if (options?.assignedToId)
@@ -145,11 +141,11 @@ export class WolfpackClient {
145
141
  // Otherwise, fetch all pages automatically
146
142
  return this.fetchAllPages('/work-items', params);
147
143
  }
148
- async getWorkItem(workItemId, teamId) {
144
+ async getWorkItem(workItemId, teamSlug) {
149
145
  try {
150
146
  const params = new URLSearchParams();
151
- if (teamId)
152
- params.append('teamId', teamId.toString());
147
+ if (teamSlug)
148
+ params.append('teamSlug', teamSlug);
153
149
  const query = params.toString();
154
150
  return await this.api.get(`/work-items/${workItemId}${query ? `?${query}` : ''}`);
155
151
  }
@@ -209,8 +205,8 @@ export class WolfpackClient {
209
205
  // Radar Item (Initiative/Roadmap) methods
210
206
  async listRadarItems(options) {
211
207
  const params = new URLSearchParams();
212
- if (options?.teamId)
213
- params.append('teamId', options.teamId.toString());
208
+ if (options?.teamSlug)
209
+ params.append('teamSlug', options.teamSlug);
214
210
  if (options?.stage)
215
211
  params.append('stage', options.stage);
216
212
  if (options?.search)
@@ -235,11 +231,11 @@ export class WolfpackClient {
235
231
  }
236
232
  return this.fetchAllPages('/radar-items', params);
237
233
  }
238
- async getRadarItem(itemId, teamId) {
234
+ async getRadarItem(itemId, teamSlug) {
239
235
  try {
240
236
  const params = new URLSearchParams();
241
- if (teamId)
242
- params.append('teamId', teamId.toString());
237
+ if (teamSlug)
238
+ params.append('teamSlug', teamSlug);
243
239
  const query = params.toString();
244
240
  return await this.api.get(`/radar-items/${itemId}${query ? `?${query}` : ''}`);
245
241
  }
@@ -253,8 +249,8 @@ export class WolfpackClient {
253
249
  // Issue methods
254
250
  async listIssues(options) {
255
251
  const params = new URLSearchParams();
256
- if (options?.teamId)
257
- params.append('teamId', options.teamId.toString());
252
+ if (options?.teamSlug)
253
+ params.append('teamSlug', options.teamSlug);
258
254
  if (options?.status)
259
255
  params.append('status', options.status);
260
256
  if (options?.severity)
@@ -293,11 +289,11 @@ export class WolfpackClient {
293
289
  }
294
290
  return this.fetchAllPages('/issues', params);
295
291
  }
296
- async getIssue(issueId, teamId) {
292
+ async getIssue(issueId, teamSlug) {
297
293
  try {
298
294
  const params = new URLSearchParams();
299
- if (teamId)
300
- params.append('teamId', teamId.toString());
295
+ if (teamSlug)
296
+ params.append('teamSlug', teamSlug);
301
297
  const query = params.toString();
302
298
  return await this.api.get(`/issues/${issueId}${query ? `?${query}` : ''}`);
303
299
  }
@@ -310,10 +306,12 @@ export class WolfpackClient {
310
306
  }
311
307
  // Create methods
312
308
  async createWorkItem(data) {
313
- return this.api.post('/work-items', data);
309
+ const { teamSlug, ...rest } = data;
310
+ return this.api.post('/work-items', { ...rest, teamSlug });
314
311
  }
315
312
  async createIssue(data) {
316
- return this.api.post('/issues', data);
313
+ const { teamSlug, ...rest } = data;
314
+ return this.api.post('/issues', { ...rest, teamSlug });
317
315
  }
318
316
  async updateIssue(issueId, data) {
319
317
  return this.api.patch(`/issues/${issueId}`, data);
@@ -321,6 +319,8 @@ export class WolfpackClient {
321
319
  // Wiki Page methods
322
320
  async listWikiPages(options) {
323
321
  const params = new URLSearchParams();
322
+ if (options?.teamSlug)
323
+ params.append('teamSlug', options.teamSlug);
324
324
  if (options?.search)
325
325
  params.append('search', options.search);
326
326
  if (options?.dateFrom)
@@ -359,7 +359,8 @@ export class WolfpackClient {
359
359
  }
360
360
  }
361
361
  async createWikiPage(data) {
362
- return this.api.post('/wiki-pages', data);
362
+ const { teamSlug, ...rest } = data;
363
+ return this.api.post('/wiki-pages', { ...rest, teamSlug });
363
364
  }
364
365
  async updateWikiPage(pageId, data) {
365
366
  return this.api.patch(`/wiki-pages/${pageId}`, data);
@@ -367,6 +368,8 @@ export class WolfpackClient {
367
368
  // Journal Entry methods
368
369
  async listJournalEntries(options) {
369
370
  const params = new URLSearchParams();
371
+ if (options?.teamSlug)
372
+ params.append('teamSlug', options.teamSlug);
370
373
  if (options?.search)
371
374
  params.append('search', options.search);
372
375
  if (options?.dateFrom)
@@ -389,11 +392,11 @@ export class WolfpackClient {
389
392
  }
390
393
  return this.fetchAllPages('/journal-entries', params);
391
394
  }
392
- async getJournalEntry(entryId, teamId) {
395
+ async getJournalEntry(entryId, teamSlug) {
393
396
  try {
394
397
  const params = new URLSearchParams();
395
- if (teamId)
396
- params.append('teamId', teamId.toString());
398
+ if (teamSlug)
399
+ params.append('teamSlug', teamSlug);
397
400
  const query = params.toString();
398
401
  return await this.api.get(`/journal-entries/${entryId}${query ? `?${query}` : ''}`);
399
402
  }
@@ -405,7 +408,8 @@ export class WolfpackClient {
405
408
  }
406
409
  }
407
410
  async createJournalEntry(data) {
408
- return this.api.post('/journal-entries', data);
411
+ const { teamSlug, ...rest } = data;
412
+ return this.api.post('/journal-entries', { ...rest, teamSlug });
409
413
  }
410
414
  async updateJournalEntry(entryId, data) {
411
415
  return this.api.patch(`/journal-entries/${entryId}`, data);
package/dist/index.js CHANGED
@@ -46,7 +46,7 @@ async function checkForUpdates() {
46
46
  // Work Item schemas
47
47
  // Use z.coerce.string() to handle any type coercion issues from MCP args
48
48
  const ListWorkItemsSchema = z.object({
49
- project_id: z.number().optional().describe('Project ID to filter work items'),
49
+ project_slug: z.string().optional().describe('Project slug to filter work items'),
50
50
  status: z.coerce
51
51
  .string()
52
52
  .optional()
@@ -85,15 +85,24 @@ const ListWorkItemsSchema = z.object({
85
85
  });
86
86
  const GetWorkItemSchema = z.object({
87
87
  work_item_id: z.string().describe('The ID (UUID) or refId (number) of the work item'),
88
- project_id: z.number().optional().describe('Project ID (required when looking up by refId)'),
88
+ project_slug: z.string().optional().describe('Project slug (required when looking up by refId)'),
89
89
  });
90
90
  const UpdateWorkProgressSchema = z.object({
91
91
  work_item_id: z.string().describe('The ID of the work item'),
92
92
  description: z.string().describe('Updated description/notes for the work item'),
93
93
  });
94
+ const VALID_STATUSES = [
95
+ 'pending',
96
+ 'new',
97
+ 'doing',
98
+ 'blocked',
99
+ 'review',
100
+ 'ready',
101
+ 'completed',
102
+ ];
94
103
  const UpdateWorkItemStatusSchema = z.object({
95
104
  work_item_id: z.string().describe('The ID of the work item'),
96
- status: z.string().describe('New status'),
105
+ status: z.enum(VALID_STATUSES).describe('New status'),
97
106
  });
98
107
  const UpdateWorkItemAssigneeSchema = z.object({
99
108
  work_item_id: z.string().describe('The ID of the work item'),
@@ -111,7 +120,7 @@ const PullWorkItemSchema = z.object({
111
120
  });
112
121
  // Radar Item (Initiative/Roadmap) schemas
113
122
  const ListRadarItemsSchema = z.object({
114
- project_id: z.number().optional().describe('Project ID to filter radar items'),
123
+ project_slug: z.string().optional().describe('Project slug to filter radar items'),
115
124
  stage: z
116
125
  .enum(['pending', 'now', 'next', 'later', 'completed'])
117
126
  .optional()
@@ -122,11 +131,11 @@ const ListRadarItemsSchema = z.object({
122
131
  });
123
132
  const GetRadarItemSchema = z.object({
124
133
  item_id: z.string().describe('The ID (UUID) or refId (number) of the radar item'),
125
- project_id: z.number().optional().describe('Project ID (required when looking up by refId)'),
134
+ project_slug: z.string().optional().describe('Project slug (required when looking up by refId)'),
126
135
  });
127
136
  // Issue schemas
128
137
  const ListIssuesSchema = z.object({
129
- project_id: z.number().optional().describe('Project ID to filter issues'),
138
+ project_slug: z.string().optional().describe('Project slug to filter issues'),
130
139
  status: z
131
140
  .enum(['open', 'in-progress', 'resolved', 'closed'])
132
141
  .optional()
@@ -159,17 +168,18 @@ const ListIssuesSchema = z.object({
159
168
  });
160
169
  const GetIssueSchema = z.object({
161
170
  issue_id: z.string().describe('The ID (UUID) or refId (number) of the issue'),
162
- project_id: z.number().optional().describe('Project ID (required when looking up by refId)'),
171
+ project_slug: z.string().optional().describe('Project slug (required when looking up by refId)'),
163
172
  });
164
173
  // Create schemas
165
174
  const CreateWorkItemSchema = z.object({
166
- title: z.string().describe('Title of the work item'),
167
- description: z
175
+ project_slug: z
168
176
  .string()
169
177
  .optional()
170
- .describe('Description/notes for the work item (markdown). Supports base64-encoded images which will be auto-uploaded.'),
178
+ .describe('Project slug (required for multi-project users, use list_projects to get slugs)'),
179
+ title: z.string().describe('Title of the work item'),
180
+ description: z.string().optional().describe('Description/notes for the work item (markdown)'),
171
181
  status: z
172
- .string()
182
+ .enum(VALID_STATUSES)
173
183
  .optional()
174
184
  .describe('Initial status: "pending" (backlog), "new" (to do), "doing", "review", "ready", "blocked", "completed". Defaults to "new".'),
175
185
  priority: z.number().optional().describe('Priority level (0-4, higher is more important)'),
@@ -178,6 +188,10 @@ const CreateWorkItemSchema = z.object({
178
188
  radar_item_id: z.string().optional().describe('Radar/initiative item ID to link to'),
179
189
  });
180
190
  const CreateIssueSchema = z.object({
191
+ project_slug: z
192
+ .string()
193
+ .optional()
194
+ .describe('Project slug (required for multi-project users, use list_projects to get slugs)'),
181
195
  title: z.string().describe('Title of the issue'),
182
196
  description: z.string().optional().describe('Description of the issue'),
183
197
  severity: z.enum(['low', 'medium', 'high', 'critical']).optional().describe('Severity level'),
@@ -207,6 +221,7 @@ const UpdateIssueSchema = z.object({
207
221
  });
208
222
  // Wiki Page schemas
209
223
  const ListWikiPagesSchema = z.object({
224
+ project_slug: z.string().optional().describe('Project slug to filter wiki pages'),
210
225
  search: z.coerce.string().optional().describe('Text search in title and content'),
211
226
  date_from: z.coerce
212
227
  .string()
@@ -223,6 +238,10 @@ const GetWikiPageSchema = z.object({
223
238
  page_id: z.string().describe('The page UUID or slug'),
224
239
  });
225
240
  const CreateWikiPageSchema = z.object({
241
+ project_slug: z
242
+ .string()
243
+ .optional()
244
+ .describe('Project slug (required for multi-project users, use list_projects to get slugs)'),
226
245
  slug: z
227
246
  .string()
228
247
  .describe('URL slug for the page (without team prefix). The system automatically prepends the team and wiki path. For example, to create a page at "myteam/wiki/guides/onboarding", pass just "guides/onboarding". Do NOT include the team slug or "wiki/" prefix.')
@@ -232,20 +251,16 @@ const CreateWikiPageSchema = z.object({
232
251
  return slug.replace(wikiPrefix, '');
233
252
  }),
234
253
  title: z.string().describe('Page title'),
235
- content: z
236
- .string()
237
- .describe('Page content (markdown). Supports base64-encoded images (e.g., ![alt](data:image/png;base64,...)) which will be auto-uploaded.'),
254
+ content: z.string().describe('Page content (markdown)'),
238
255
  });
239
256
  const UpdateWikiPageSchema = z.object({
240
257
  page_id: z.string().describe('The page UUID'),
241
258
  title: z.string().optional().describe('Updated title'),
242
- content: z
243
- .string()
244
- .optional()
245
- .describe('Updated content (markdown). Supports base64-encoded images which will be auto-uploaded.'),
259
+ content: z.string().optional().describe('Updated content (markdown)'),
246
260
  });
247
261
  // Journal Entry schemas
248
262
  const ListJournalEntriesSchema = z.object({
263
+ project_slug: z.string().optional().describe('Project slug to filter journal entries'),
249
264
  search: z.coerce.string().optional().describe('Text search in title and content'),
250
265
  date_from: z.coerce
251
266
  .string()
@@ -260,22 +275,21 @@ const ListJournalEntriesSchema = z.object({
260
275
  });
261
276
  const GetJournalEntrySchema = z.object({
262
277
  entry_id: z.string().describe('The entry UUID or refId'),
263
- project_id: z.number().optional().describe('Project ID (required when looking up by refId)'),
278
+ project_slug: z.string().optional().describe('Project slug (required when looking up by refId)'),
264
279
  });
265
280
  const CreateJournalEntrySchema = z.object({
266
- title: z.string().describe('Entry title'),
267
- content: z
281
+ project_slug: z
268
282
  .string()
269
- .describe('Entry content (markdown). Supports base64-encoded images which will be auto-uploaded.'),
283
+ .optional()
284
+ .describe('Project slug (required for multi-project users, use list_projects to get slugs)'),
285
+ title: z.string().describe('Entry title'),
286
+ content: z.string().describe('Entry content (markdown)'),
270
287
  date: z.string().optional().describe('Entry date (ISO format, defaults to now)'),
271
288
  });
272
289
  const UpdateJournalEntrySchema = z.object({
273
290
  entry_id: z.string().describe('The entry UUID'),
274
291
  title: z.string().optional().describe('Updated title'),
275
- content: z
276
- .string()
277
- .optional()
278
- .describe('Updated content (markdown). Supports base64-encoded images which will be auto-uploaded.'),
292
+ content: z.string().optional().describe('Updated content (markdown)'),
279
293
  });
280
294
  // Comment schemas
281
295
  const ListWorkItemCommentsSchema = z.object({
@@ -286,15 +300,11 @@ const ListIssueCommentsSchema = z.object({
286
300
  });
287
301
  const CreateWorkItemCommentSchema = z.object({
288
302
  work_item_id: z.string().describe('The work item UUID'),
289
- content: z
290
- .string()
291
- .describe('Comment content (markdown). Supports base64-encoded images which will be auto-uploaded.'),
303
+ content: z.string().describe('Comment content (markdown)'),
292
304
  });
293
305
  const CreateIssueCommentSchema = z.object({
294
306
  issue_id: z.string().describe('The issue UUID'),
295
- content: z
296
- .string()
297
- .describe('Comment content (markdown). Supports base64-encoded images which will be auto-uploaded.'),
307
+ content: z.string().describe('Comment content (markdown)'),
298
308
  });
299
309
  // Image upload schema
300
310
  const UploadImageSchema = z.object({
@@ -302,10 +312,10 @@ const UploadImageSchema = z.object({
302
312
  .string()
303
313
  .describe('Absolute path to an image file on disk (e.g., "/tmp/screenshot.png"). ' +
304
314
  'Supported formats: JPEG, PNG, GIF, WebP. Max 5MB.'),
305
- project_id: z
306
- .number()
315
+ project_slug: z
316
+ .string()
307
317
  .optional()
308
- .describe('Project ID (required for multi-project users, use list_projects to get IDs)'),
318
+ .describe('Project slug (required for multi-project users, use list_projects to get slugs)'),
309
319
  });
310
320
  const DownloadImageSchema = z.object({
311
321
  image_url: z
@@ -353,7 +363,7 @@ class WolfpackMCPServer {
353
363
  '3) NEVER perform bulk actions across multiple projects - always work in one project at a time. ' +
354
364
  '4) If the user mentions a specific project, use that for all subsequent operations. ' +
355
365
  '5) If unclear which project to use, ASK the user before proceeding. ' +
356
- 'Returns project IDs, slugs, names, and types. Single-project users have their project auto-selected; multi-project users must specify project_id in other tool calls.',
366
+ 'Returns project slugs, names, and types. Single-project users have their project auto-selected; multi-project users must specify project_slug in other tool calls.',
357
367
  inputSchema: {
358
368
  type: 'object',
359
369
  properties: {},
@@ -374,9 +384,9 @@ class WolfpackMCPServer {
374
384
  inputSchema: {
375
385
  type: 'object',
376
386
  properties: {
377
- project_id: {
378
- type: 'number',
379
- description: 'Project ID (required for multi-project users, use list_projects to get IDs)',
387
+ project_slug: {
388
+ type: 'string',
389
+ description: 'Project slug (required for multi-project users, use list_projects to get slugs)',
380
390
  },
381
391
  status: {
382
392
  type: 'string',
@@ -440,9 +450,9 @@ class WolfpackMCPServer {
440
450
  type: 'string',
441
451
  description: 'The ID (UUID) or refId (number) of the work item',
442
452
  },
443
- project_id: {
444
- type: 'number',
445
- description: 'Project ID (required when looking up by refId)',
453
+ project_slug: {
454
+ type: 'string',
455
+ description: 'Project slug (required when looking up by refId)',
446
456
  },
447
457
  },
448
458
  required: ['work_item_id'],
@@ -485,6 +495,7 @@ class WolfpackMCPServer {
485
495
  },
486
496
  status: {
487
497
  type: 'string',
498
+ enum: ['pending', 'new', 'doing', 'blocked', 'review', 'ready', 'completed'],
488
499
  description: 'New status: "pending" (backlog), "new" (to do), "doing" (in progress), "review" (work done), "ready" (awaiting deployment), "blocked", "completed" (deployed)',
489
500
  },
490
501
  },
@@ -541,9 +552,9 @@ class WolfpackMCPServer {
541
552
  inputSchema: {
542
553
  type: 'object',
543
554
  properties: {
544
- project_id: {
545
- type: 'number',
546
- description: 'Project ID (use list_projects to get IDs)',
555
+ project_slug: {
556
+ type: 'string',
557
+ description: 'Project slug (use list_projects to get slugs)',
547
558
  },
548
559
  stage: {
549
560
  type: 'string',
@@ -570,9 +581,9 @@ class WolfpackMCPServer {
570
581
  type: 'string',
571
582
  description: 'The ID (UUID) or refId (number) of the radar item',
572
583
  },
573
- project_id: {
574
- type: 'number',
575
- description: 'Project ID (required when looking up by refId)',
584
+ project_slug: {
585
+ type: 'string',
586
+ description: 'Project slug (required when looking up by refId)',
576
587
  },
577
588
  },
578
589
  required: ['item_id'],
@@ -589,9 +600,9 @@ class WolfpackMCPServer {
589
600
  inputSchema: {
590
601
  type: 'object',
591
602
  properties: {
592
- project_id: {
593
- type: 'number',
594
- description: 'Project ID (required for multi-project users, use list_projects to get IDs)',
603
+ project_slug: {
604
+ type: 'string',
605
+ description: 'Project slug (required for multi-project users, use list_projects to get slugs)',
595
606
  },
596
607
  status: {
597
608
  type: 'string',
@@ -648,9 +659,9 @@ class WolfpackMCPServer {
648
659
  type: 'string',
649
660
  description: 'The ID (UUID) or refId (number) of the issue',
650
661
  },
651
- project_id: {
652
- type: 'number',
653
- description: 'Project ID (required when looking up by refId)',
662
+ project_slug: {
663
+ type: 'string',
664
+ description: 'Project slug (required when looking up by refId)',
654
665
  },
655
666
  },
656
667
  required: ['issue_id'],
@@ -663,13 +674,18 @@ class WolfpackMCPServer {
663
674
  inputSchema: {
664
675
  type: 'object',
665
676
  properties: {
677
+ project_slug: {
678
+ type: 'string',
679
+ description: 'Project slug (required for multi-project users, use list_projects to get slugs)',
680
+ },
666
681
  title: { type: 'string', description: 'Title of the work item' },
667
682
  description: {
668
683
  type: 'string',
669
- description: 'Description/notes for the work item (markdown). Supports base64-encoded images which will be auto-uploaded.',
684
+ description: 'Description/notes for the work item (markdown)',
670
685
  },
671
686
  status: {
672
687
  type: 'string',
688
+ enum: ['pending', 'new', 'doing', 'blocked', 'review', 'ready', 'completed'],
673
689
  description: 'Initial status: "pending" (backlog), "new" (to do), "doing", "review", "ready", "blocked", "completed". Defaults to "new".',
674
690
  },
675
691
  priority: {
@@ -698,6 +714,10 @@ class WolfpackMCPServer {
698
714
  inputSchema: {
699
715
  type: 'object',
700
716
  properties: {
717
+ project_slug: {
718
+ type: 'string',
719
+ description: 'Project slug (required for multi-project users, use list_projects to get slugs)',
720
+ },
701
721
  title: { type: 'string', description: 'Title of the issue' },
702
722
  description: { type: 'string', description: 'Description of the issue' },
703
723
  severity: {
@@ -756,6 +776,10 @@ class WolfpackMCPServer {
756
776
  inputSchema: {
757
777
  type: 'object',
758
778
  properties: {
779
+ project_slug: {
780
+ type: 'string',
781
+ description: 'Project slug (required for multi-project users, use list_projects to get slugs)',
782
+ },
759
783
  search: {
760
784
  type: 'string',
761
785
  description: 'Text search in title and content',
@@ -794,6 +818,10 @@ class WolfpackMCPServer {
794
818
  inputSchema: {
795
819
  type: 'object',
796
820
  properties: {
821
+ project_slug: {
822
+ type: 'string',
823
+ description: 'Project slug (required for multi-project users, use list_projects to get slugs)',
824
+ },
797
825
  slug: {
798
826
  type: 'string',
799
827
  description: 'URL slug for the page (without team prefix). The system automatically prepends the team and wiki path. For example, to create a page at "myteam/wiki/guides/onboarding", pass just "guides/onboarding". Do NOT include the team slug or "wiki/" prefix.',
@@ -801,7 +829,7 @@ class WolfpackMCPServer {
801
829
  title: { type: 'string', description: 'Page title' },
802
830
  content: {
803
831
  type: 'string',
804
- description: 'Page content (markdown). Supports base64-encoded images (e.g., ![alt](data:image/png;base64,...)) which will be auto-uploaded.',
832
+ description: 'Page content (markdown)',
805
833
  },
806
834
  },
807
835
  required: ['slug', 'title', 'content'],
@@ -817,7 +845,7 @@ class WolfpackMCPServer {
817
845
  title: { type: 'string', description: 'Updated title' },
818
846
  content: {
819
847
  type: 'string',
820
- description: 'Updated content (markdown). Supports base64-encoded images which will be auto-uploaded.',
848
+ description: 'Updated content (markdown)',
821
849
  },
822
850
  },
823
851
  required: ['page_id'],
@@ -830,6 +858,10 @@ class WolfpackMCPServer {
830
858
  inputSchema: {
831
859
  type: 'object',
832
860
  properties: {
861
+ project_slug: {
862
+ type: 'string',
863
+ description: 'Project slug (required for multi-project users, use list_projects to get slugs)',
864
+ },
833
865
  search: {
834
866
  type: 'string',
835
867
  description: 'Text search in title and content',
@@ -855,9 +887,9 @@ class WolfpackMCPServer {
855
887
  type: 'object',
856
888
  properties: {
857
889
  entry_id: { type: 'string', description: 'The entry UUID or refId' },
858
- project_id: {
859
- type: 'number',
860
- description: 'Project ID (required when looking up by refId)',
890
+ project_slug: {
891
+ type: 'string',
892
+ description: 'Project slug (required when looking up by refId)',
861
893
  },
862
894
  },
863
895
  required: ['entry_id'],
@@ -869,10 +901,14 @@ class WolfpackMCPServer {
869
901
  inputSchema: {
870
902
  type: 'object',
871
903
  properties: {
904
+ project_slug: {
905
+ type: 'string',
906
+ description: 'Project slug (required for multi-project users, use list_projects to get slugs)',
907
+ },
872
908
  title: { type: 'string', description: 'Entry title' },
873
909
  content: {
874
910
  type: 'string',
875
- description: 'Entry content (markdown). Supports base64-encoded images which will be auto-uploaded.',
911
+ description: 'Entry content (markdown)',
876
912
  },
877
913
  date: { type: 'string', description: 'Entry date (ISO format, defaults to now)' },
878
914
  },
@@ -889,7 +925,7 @@ class WolfpackMCPServer {
889
925
  title: { type: 'string', description: 'Updated title' },
890
926
  content: {
891
927
  type: 'string',
892
- description: 'Updated content (markdown). Supports base64-encoded images which will be auto-uploaded.',
928
+ description: 'Updated content (markdown)',
893
929
  },
894
930
  },
895
931
  required: ['entry_id'],
@@ -934,7 +970,7 @@ class WolfpackMCPServer {
934
970
  work_item_id: { type: 'string', description: 'The work item UUID' },
935
971
  content: {
936
972
  type: 'string',
937
- description: 'Comment content (markdown). Supports base64-encoded images which will be auto-uploaded.',
973
+ description: 'Comment content (markdown)',
938
974
  },
939
975
  },
940
976
  required: ['work_item_id', 'content'],
@@ -949,7 +985,7 @@ class WolfpackMCPServer {
949
985
  issue_id: { type: 'string', description: 'The issue UUID' },
950
986
  content: {
951
987
  type: 'string',
952
- description: 'Comment content (markdown). Supports base64-encoded images which will be auto-uploaded.',
988
+ description: 'Comment content (markdown)',
953
989
  },
954
990
  },
955
991
  required: ['issue_id', 'content'],
@@ -959,8 +995,10 @@ class WolfpackMCPServer {
959
995
  {
960
996
  name: 'upload_image',
961
997
  description: 'Upload an image file from disk and get back a permanent URL. ' +
962
- 'Reads the file directly from disk, avoiding slow base64 token generation. ' +
998
+ 'Reads the file directly from disk. ' +
963
999
  'Returns a markdown image URL you can include in any content field. ' +
1000
+ 'IMPORTANT: This tool requires direct filesystem access and may not work in sandboxed environments (e.g. Claude.ai, Claude Desktop). ' +
1001
+ 'If the MCP server cannot access the file path, inform the user that image uploads are not supported in their environment. ' +
964
1002
  'Requires mcp:images:upload permission.',
965
1003
  inputSchema: {
966
1004
  type: 'object',
@@ -970,9 +1008,9 @@ class WolfpackMCPServer {
970
1008
  description: 'Absolute path to an image file on disk (e.g., "/tmp/screenshot.png"). ' +
971
1009
  'Supported formats: JPEG, PNG, GIF, WebP. Max 5MB.',
972
1010
  },
973
- project_id: {
974
- type: 'number',
975
- description: 'Project ID (required for multi-project users, use list_projects to get IDs)',
1011
+ project_slug: {
1012
+ type: 'string',
1013
+ description: 'Project slug (required for multi-project users, use list_projects to get slugs)',
976
1014
  },
977
1015
  },
978
1016
  required: ['file_path'],
@@ -1004,17 +1042,17 @@ class WolfpackMCPServer {
1004
1042
  // Project handlers
1005
1043
  case 'list_projects': {
1006
1044
  const teams = await this.client.listProjects();
1007
- const currentTeamId = this.client.getProjectId();
1045
+ const currentProjectSlug = this.client.getProjectSlug();
1008
1046
  return {
1009
1047
  content: [
1010
1048
  {
1011
1049
  type: 'text',
1012
1050
  text: JSON.stringify({
1013
1051
  projects: teams,
1014
- currentProjectId: currentTeamId,
1052
+ currentProjectSlug,
1015
1053
  hint: teams.length === 1
1016
1054
  ? 'Only one project available - it will be used automatically.'
1017
- : 'Multiple projects available. Use project_id parameter in other tools to specify which project to use.',
1055
+ : 'Multiple projects available. Use project_slug parameter in other tools to specify which project to use.',
1018
1056
  }, null, 2),
1019
1057
  },
1020
1058
  ],
@@ -1024,7 +1062,7 @@ class WolfpackMCPServer {
1024
1062
  case 'list_work_items': {
1025
1063
  const parsed = ListWorkItemsSchema.parse(args);
1026
1064
  const result = await this.client.listWorkItems({
1027
- teamId: parsed.project_id || this.client.getProjectId() || undefined,
1065
+ teamSlug: parsed.project_slug || this.client.getProjectSlug() || undefined,
1028
1066
  status: parsed.status,
1029
1067
  assignedToId: parsed.assigned_to_id,
1030
1068
  search: parsed.search,
@@ -1050,7 +1088,7 @@ class WolfpackMCPServer {
1050
1088
  }
1051
1089
  case 'get_work_item': {
1052
1090
  const parsed = GetWorkItemSchema.parse(args);
1053
- const workItem = await this.client.getWorkItem(parsed.work_item_id, parsed.project_id || this.client.getProjectId() || undefined);
1091
+ const workItem = await this.client.getWorkItem(parsed.work_item_id, parsed.project_slug || this.client.getProjectSlug() || undefined);
1054
1092
  if (workItem) {
1055
1093
  let text = JSON.stringify(workItem, null, 2);
1056
1094
  // Add hint if no plan detected in description
@@ -1146,7 +1184,7 @@ class WolfpackMCPServer {
1146
1184
  case 'list_radar_items': {
1147
1185
  const parsed = ListRadarItemsSchema.parse(args);
1148
1186
  const result = await this.client.listRadarItems({
1149
- teamId: parsed.project_id,
1187
+ teamSlug: parsed.project_slug,
1150
1188
  stage: parsed.stage,
1151
1189
  search: parsed.search,
1152
1190
  limit: parsed.limit,
@@ -1162,7 +1200,7 @@ class WolfpackMCPServer {
1162
1200
  }
1163
1201
  case 'get_radar_item': {
1164
1202
  const parsed = GetRadarItemSchema.parse(args);
1165
- const radarItem = await this.client.getRadarItem(parsed.item_id, parsed.project_id);
1203
+ const radarItem = await this.client.getRadarItem(parsed.item_id, parsed.project_slug);
1166
1204
  if (radarItem) {
1167
1205
  return {
1168
1206
  content: [{ type: 'text', text: JSON.stringify(radarItem, null, 2) }],
@@ -1176,7 +1214,7 @@ class WolfpackMCPServer {
1176
1214
  case 'list_issues': {
1177
1215
  const parsed = ListIssuesSchema.parse(args);
1178
1216
  const result = await this.client.listIssues({
1179
- teamId: parsed.project_id,
1217
+ teamSlug: parsed.project_slug,
1180
1218
  status: parsed.status,
1181
1219
  severity: parsed.severity,
1182
1220
  type: parsed.type,
@@ -1199,7 +1237,7 @@ class WolfpackMCPServer {
1199
1237
  }
1200
1238
  case 'get_issue': {
1201
1239
  const parsed = GetIssueSchema.parse(args);
1202
- const issue = await this.client.getIssue(parsed.issue_id, parsed.project_id);
1240
+ const issue = await this.client.getIssue(parsed.issue_id, parsed.project_slug);
1203
1241
  if (issue) {
1204
1242
  return {
1205
1243
  content: [{ type: 'text', text: JSON.stringify(issue, null, 2) }],
@@ -1220,6 +1258,7 @@ class WolfpackMCPServer {
1220
1258
  leadingUserId: parsed.leading_user_id,
1221
1259
  categoryId: parsed.category_id,
1222
1260
  radarItemId: parsed.radar_item_id,
1261
+ teamSlug: parsed.project_slug || this.client.getProjectSlug() || undefined,
1223
1262
  });
1224
1263
  return {
1225
1264
  content: [
@@ -1244,6 +1283,7 @@ class WolfpackMCPServer {
1244
1283
  stepsToReproduce: parsed.steps_to_reproduce,
1245
1284
  expectedBehavior: parsed.expected_behavior,
1246
1285
  actualBehavior: parsed.actual_behavior,
1286
+ teamSlug: parsed.project_slug || this.client.getProjectSlug() || undefined,
1247
1287
  });
1248
1288
  return {
1249
1289
  content: [
@@ -1277,6 +1317,7 @@ class WolfpackMCPServer {
1277
1317
  case 'list_wiki_pages': {
1278
1318
  const parsed = ListWikiPagesSchema.parse(args);
1279
1319
  const result = await this.client.listWikiPages({
1320
+ teamSlug: parsed.project_slug || this.client.getProjectSlug() || undefined,
1280
1321
  search: parsed.search,
1281
1322
  dateFrom: parsed.date_from,
1282
1323
  dateTo: parsed.date_to,
@@ -1309,6 +1350,7 @@ class WolfpackMCPServer {
1309
1350
  slug: parsed.slug,
1310
1351
  title: parsed.title,
1311
1352
  content: parsed.content,
1353
+ teamSlug: parsed.project_slug || this.client.getProjectSlug() || undefined,
1312
1354
  });
1313
1355
  return {
1314
1356
  content: [
@@ -1338,6 +1380,7 @@ class WolfpackMCPServer {
1338
1380
  case 'list_journal_entries': {
1339
1381
  const parsed = ListJournalEntriesSchema.parse(args);
1340
1382
  const result = await this.client.listJournalEntries({
1383
+ teamSlug: parsed.project_slug || this.client.getProjectSlug() || undefined,
1341
1384
  search: parsed.search,
1342
1385
  dateFrom: parsed.date_from,
1343
1386
  dateTo: parsed.date_to,
@@ -1354,7 +1397,7 @@ class WolfpackMCPServer {
1354
1397
  }
1355
1398
  case 'get_journal_entry': {
1356
1399
  const parsed = GetJournalEntrySchema.parse(args);
1357
- const entry = await this.client.getJournalEntry(parsed.entry_id, parsed.project_id);
1400
+ const entry = await this.client.getJournalEntry(parsed.entry_id, parsed.project_slug);
1358
1401
  if (entry) {
1359
1402
  return {
1360
1403
  content: [{ type: 'text', text: JSON.stringify(entry, null, 2) }],
@@ -1370,6 +1413,7 @@ class WolfpackMCPServer {
1370
1413
  title: parsed.title,
1371
1414
  content: parsed.content,
1372
1415
  date: parsed.date,
1416
+ teamSlug: parsed.project_slug || this.client.getProjectSlug() || undefined,
1373
1417
  });
1374
1418
  return {
1375
1419
  content: [
@@ -1440,7 +1484,7 @@ class WolfpackMCPServer {
1440
1484
  }
1441
1485
  case 'upload_image': {
1442
1486
  const parsed = UploadImageSchema.parse(args);
1443
- const teamSlug = await this.client.resolveSlug(parsed.project_id || this.client.getProjectId() || undefined);
1487
+ const teamSlug = await this.client.resolveSlug(parsed.project_slug || this.client.getProjectSlug() || undefined);
1444
1488
  const filePath = resolve(parsed.file_path);
1445
1489
  // Validate file extension
1446
1490
  const ext = extname(filePath).toLowerCase();
@@ -1540,8 +1584,8 @@ class WolfpackMCPServer {
1540
1584
  if (config.projectSlug) {
1541
1585
  // Explicit project slug configured - use it
1542
1586
  try {
1543
- const teamId = await this.client.resolveProjectSlug(config.projectSlug);
1544
- console.error(`Resolved project "${config.projectSlug}" to ID: ${teamId}`);
1587
+ await this.client.resolveProjectSlug(config.projectSlug);
1588
+ console.error(`Resolved project: "${config.projectSlug}"`);
1545
1589
  }
1546
1590
  catch (error) {
1547
1591
  console.error(`Failed to resolve project slug: ${error instanceof Error ? error.message : String(error)}`);
@@ -1554,14 +1598,14 @@ class WolfpackMCPServer {
1554
1598
  const teams = await this.client.listProjects();
1555
1599
  if (teams.length === 1) {
1556
1600
  // Auto-select the only project
1557
- this.client.setProjectId(teams[0].id);
1601
+ this.client.setProjectSlug(teams[0].slug);
1558
1602
  console.error(`Auto-selected project: ${teams[0].name} (${teams[0].slug})`);
1559
1603
  }
1560
1604
  else if (teams.length === 0) {
1561
1605
  console.error('Warning: No projects found for this API key');
1562
1606
  }
1563
1607
  else {
1564
- console.error(`Multiple projects available (${teams.length}). Use list_projects tool to see them, then specify project_id in tool calls.`);
1608
+ console.error(`Multiple projects available (${teams.length}). Use list_projects tool to see them, then specify project_slug in tool calls.`);
1565
1609
  }
1566
1610
  }
1567
1611
  catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wolfpack-mcp",
3
- "version": "1.0.31",
3
+ "version": "1.0.33",
4
4
  "description": "MCP server for Wolfpack AI-enhanced software delivery tools",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",