wolfpack-mcp 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -103,14 +103,15 @@ Work items are tasks or tickets assigned to team members.
103
103
 
104
104
  #### `list_work_items`
105
105
 
106
- Lists work items assigned to you.
106
+ Lists work items assigned to you. By default, excludes completed and closed items to show active work.
107
107
 
108
108
  - **Parameters:**
109
109
  - `team_id` (number, optional): Team ID (use `list_teams` to get IDs)
110
- - `status` (string, optional): Filter by status (new, doing, review, completed, blocked)
110
+ - `status` (string, optional): Filter by status (new, doing, review, completed, blocked). Use `completed` or `closed` to see those items.
111
111
  - `limit` (number, optional): Maximum number of items to return
112
112
  - `offset` (number, optional): Number of items to skip for pagination
113
113
  - **Returns:** Array of work item objects
114
+ - **Note:** Results are automatically paginated. If there are more than 200 items, you'll receive a truncation warning with the first 50 items.
114
115
 
115
116
  #### `get_work_item`
116
117
 
@@ -157,17 +158,18 @@ Issues track bugs, feature requests, and other items in the issue tracker.
157
158
 
158
159
  #### `list_issues`
159
160
 
160
- Lists issues from the team issue tracker.
161
+ Lists issues from the team issue tracker. By default, excludes closed issues.
161
162
 
162
163
  - **Parameters:**
163
164
  - `team_id` (number, optional): Team ID to filter issues
164
- - `status` (string, optional): Filter by status (open, in-progress, resolved, closed)
165
+ - `status` (string, optional): Filter by status (open, in-progress, resolved, closed). Use `closed` to see closed items.
165
166
  - `severity` (string, optional): Filter by severity (low, medium, high, critical)
166
167
  - `type` (string, optional): Filter by type (bug, feature-request, task, improvement, question)
167
168
  - `assigned_to_id` (string, optional): Filter by assignee ('unassigned' or 'me' for special cases)
168
169
  - `limit` (number, optional): Maximum number of items
169
170
  - `offset` (number, optional): Items to skip
170
171
  - **Returns:** Array of issue objects
172
+ - **Note:** Results are automatically paginated. If there are more than 200 items, you'll receive a truncation warning.
171
173
 
172
174
  #### `get_issue`
173
175
 
@@ -319,14 +321,15 @@ Radar items are strategic initiatives or roadmap items that group related work.
319
321
 
320
322
  #### `list_radar_items`
321
323
 
322
- Lists radar items from the team.
324
+ Lists radar items from the team. By default, excludes completed items.
323
325
 
324
326
  - **Parameters:**
325
327
  - `team_id` (number, optional): Team ID to filter
326
- - `stage` (string, optional): Filter by stage (pending, now, next, later, completed)
328
+ - `stage` (string, optional): Filter by stage (pending, now, next, later, completed). Use `completed` to see completed items.
327
329
  - `limit` (number, optional): Maximum items to return
328
330
  - `offset` (number, optional): Items to skip
329
331
  - **Returns:** Array of radar item objects
332
+ - **Note:** Results are automatically paginated. If there are more than 200 items, you'll receive a truncation warning.
330
333
 
331
334
  #### `get_radar_item`
332
335
 
@@ -337,6 +340,14 @@ Gets a specific radar item by ID or reference number.
337
340
  - `team_id` (number, optional): Team ID (required when using refId)
338
341
  - **Returns:** Radar item object with work items and participants
339
342
 
343
+ ## Automatic Updates
344
+
345
+ The MCP server checks for updates on startup. If a newer version is available, you'll see a notification in the server logs with instructions to update:
346
+
347
+ ```
348
+ npm install -g wolfpack-mcp@latest
349
+ ```
350
+
340
351
  ## Security
341
352
 
342
353
  - API keys authenticate with your Wolfpack account
package/dist/client.js CHANGED
@@ -1,4 +1,8 @@
1
1
  import { ApiClient } from './apiClient.js';
2
+ // Maximum number of items to fetch without confirmation
3
+ const MAX_ITEMS_THRESHOLD = 200;
4
+ // Page size for fetching all items
5
+ const PAGE_SIZE = 50;
2
6
  export class WolfpackClient {
3
7
  api;
4
8
  teamId = null;
@@ -31,6 +35,48 @@ export class WolfpackClient {
31
35
  async listTeams() {
32
36
  return this.api.get('/teams');
33
37
  }
38
+ // Helper to fetch all pages of a paginated endpoint
39
+ async fetchAllPages(endpoint, baseParams) {
40
+ // First request to get total count
41
+ const firstParams = new URLSearchParams(baseParams);
42
+ firstParams.set('limit', PAGE_SIZE.toString());
43
+ firstParams.set('offset', '0');
44
+ const firstQuery = firstParams.toString();
45
+ const firstPage = await this.api.get(`${endpoint}${firstQuery ? `?${firstQuery}` : ''}`);
46
+ // If total exceeds threshold, return first page with truncation warning
47
+ if (firstPage.total > MAX_ITEMS_THRESHOLD) {
48
+ return {
49
+ items: firstPage.items,
50
+ total: firstPage.total,
51
+ truncated: true,
52
+ };
53
+ }
54
+ // If all items fit in first page, return them
55
+ if (firstPage.items.length >= firstPage.total) {
56
+ return {
57
+ items: firstPage.items,
58
+ total: firstPage.total,
59
+ truncated: false,
60
+ };
61
+ }
62
+ // Fetch remaining pages
63
+ const allItems = [...firstPage.items];
64
+ let offset = PAGE_SIZE;
65
+ while (offset < firstPage.total) {
66
+ const params = new URLSearchParams(baseParams);
67
+ params.set('limit', PAGE_SIZE.toString());
68
+ params.set('offset', offset.toString());
69
+ const query = params.toString();
70
+ const page = await this.api.get(`${endpoint}${query ? `?${query}` : ''}`);
71
+ allItems.push(...page.items);
72
+ offset += PAGE_SIZE;
73
+ }
74
+ return {
75
+ items: allItems,
76
+ total: firstPage.total,
77
+ truncated: false,
78
+ };
79
+ }
34
80
  // Work Item methods
35
81
  async listWorkItems(options) {
36
82
  const params = new URLSearchParams();
@@ -38,12 +84,22 @@ export class WolfpackClient {
38
84
  params.append('teamId', options.teamId.toString());
39
85
  if (options?.status)
40
86
  params.append('status', options.status);
41
- if (options?.limit)
42
- params.append('limit', options.limit.toString());
43
- if (options?.offset)
44
- params.append('offset', options.offset.toString());
45
- const query = params.toString();
46
- return this.api.get(`/work-items${query ? `?${query}` : ''}`);
87
+ // If explicit limit/offset provided, use single request (for manual pagination)
88
+ if (options?.limit !== undefined || options?.offset !== undefined) {
89
+ if (options?.limit)
90
+ params.append('limit', options.limit.toString());
91
+ if (options?.offset)
92
+ params.append('offset', options.offset.toString());
93
+ const query = params.toString();
94
+ const response = await this.api.get(`/work-items${query ? `?${query}` : ''}`);
95
+ return {
96
+ items: response.items,
97
+ total: response.total,
98
+ truncated: response.total > response.items.length,
99
+ };
100
+ }
101
+ // Otherwise, fetch all pages automatically
102
+ return this.fetchAllPages('/work-items', params);
47
103
  }
48
104
  async getWorkItem(workItemId, teamId) {
49
105
  try {
@@ -91,12 +147,21 @@ export class WolfpackClient {
91
147
  params.append('teamId', options.teamId.toString());
92
148
  if (options?.stage)
93
149
  params.append('stage', options.stage);
94
- if (options?.limit)
95
- params.append('limit', options.limit.toString());
96
- if (options?.offset)
97
- params.append('offset', options.offset.toString());
98
- const query = params.toString();
99
- return this.api.get(`/radar-items${query ? `?${query}` : ''}`);
150
+ // If explicit limit/offset provided, use single request
151
+ if (options?.limit !== undefined || options?.offset !== undefined) {
152
+ if (options?.limit)
153
+ params.append('limit', options.limit.toString());
154
+ if (options?.offset)
155
+ params.append('offset', options.offset.toString());
156
+ const query = params.toString();
157
+ const response = await this.api.get(`/radar-items${query ? `?${query}` : ''}`);
158
+ return {
159
+ items: response.items,
160
+ total: response.total,
161
+ truncated: response.total > response.items.length,
162
+ };
163
+ }
164
+ return this.fetchAllPages('/radar-items', params);
100
165
  }
101
166
  async getRadarItem(itemId, teamId) {
102
167
  try {
@@ -126,12 +191,21 @@ export class WolfpackClient {
126
191
  params.append('type', options.type);
127
192
  if (options?.assignedToId)
128
193
  params.append('assignedToId', options.assignedToId);
129
- if (options?.limit)
130
- params.append('limit', options.limit.toString());
131
- if (options?.offset)
132
- params.append('offset', options.offset.toString());
133
- const query = params.toString();
134
- return this.api.get(`/issues${query ? `?${query}` : ''}`);
194
+ // If explicit limit/offset provided, use single request
195
+ if (options?.limit !== undefined || options?.offset !== undefined) {
196
+ if (options?.limit)
197
+ params.append('limit', options.limit.toString());
198
+ if (options?.offset)
199
+ params.append('offset', options.offset.toString());
200
+ const query = params.toString();
201
+ const response = await this.api.get(`/issues${query ? `?${query}` : ''}`);
202
+ return {
203
+ items: response.items,
204
+ total: response.total,
205
+ truncated: response.total > response.items.length,
206
+ };
207
+ }
208
+ return this.fetchAllPages('/issues', params);
135
209
  }
136
210
  async getIssue(issueId, teamId) {
137
211
  try {
@@ -161,12 +235,21 @@ export class WolfpackClient {
161
235
  // Wiki Page methods
162
236
  async listWikiPages(options) {
163
237
  const params = new URLSearchParams();
164
- if (options?.limit)
165
- params.append('limit', options.limit.toString());
166
- if (options?.offset)
167
- params.append('offset', options.offset.toString());
168
- const query = params.toString();
169
- return this.api.get(`/wiki-pages${query ? `?${query}` : ''}`);
238
+ // If explicit limit/offset provided, use single request
239
+ if (options?.limit !== undefined || options?.offset !== undefined) {
240
+ if (options?.limit)
241
+ params.append('limit', options.limit.toString());
242
+ if (options?.offset)
243
+ params.append('offset', options.offset.toString());
244
+ const query = params.toString();
245
+ const response = await this.api.get(`/wiki-pages${query ? `?${query}` : ''}`);
246
+ return {
247
+ items: response.items,
248
+ total: response.total,
249
+ truncated: response.total > response.items.length,
250
+ };
251
+ }
252
+ return this.fetchAllPages('/wiki-pages', params);
170
253
  }
171
254
  async getWikiPage(pageId) {
172
255
  try {
@@ -188,12 +271,21 @@ export class WolfpackClient {
188
271
  // Journal Entry methods
189
272
  async listJournalEntries(options) {
190
273
  const params = new URLSearchParams();
191
- if (options?.limit)
192
- params.append('limit', options.limit.toString());
193
- if (options?.offset)
194
- params.append('offset', options.offset.toString());
195
- const query = params.toString();
196
- return this.api.get(`/journal-entries${query ? `?${query}` : ''}`);
274
+ // If explicit limit/offset provided, use single request
275
+ if (options?.limit !== undefined || options?.offset !== undefined) {
276
+ if (options?.limit)
277
+ params.append('limit', options.limit.toString());
278
+ if (options?.offset)
279
+ params.append('offset', options.offset.toString());
280
+ const query = params.toString();
281
+ const response = await this.api.get(`/journal-entries${query ? `?${query}` : ''}`);
282
+ return {
283
+ items: response.items,
284
+ total: response.total,
285
+ truncated: response.total > response.items.length,
286
+ };
287
+ }
288
+ return this.fetchAllPages('/journal-entries', params);
197
289
  }
198
290
  async getJournalEntry(entryId, teamId) {
199
291
  try {
package/dist/index.js CHANGED
@@ -3,8 +3,51 @@ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
3
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
4
  import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
5
5
  import { z } from 'zod';
6
+ import { createRequire } from 'module';
6
7
  import { WolfpackClient } from './client.js';
7
8
  import { validateConfig, config } from './config.js';
9
+ // Get current package version
10
+ const require = createRequire(import.meta.url);
11
+ const packageJson = require('../package.json');
12
+ const CURRENT_VERSION = packageJson.version;
13
+ const PACKAGE_NAME = packageJson.name;
14
+ // Check for updates from npm registry
15
+ async function checkForUpdates() {
16
+ try {
17
+ const response = await fetch(`https://registry.npmjs.org/${PACKAGE_NAME}/latest`, {
18
+ headers: { Accept: 'application/json' },
19
+ signal: AbortSignal.timeout(5000), // 5 second timeout
20
+ });
21
+ if (!response.ok)
22
+ return;
23
+ const data = (await response.json());
24
+ const latestVersion = data.version;
25
+ if (latestVersion && latestVersion !== CURRENT_VERSION) {
26
+ const [latestMajor, latestMinor, latestPatch] = latestVersion.split('.').map(Number);
27
+ const [currentMajor, currentMinor, currentPatch] = CURRENT_VERSION.split('.').map(Number);
28
+ // Check if latest is actually newer
29
+ const isNewer = latestMajor > currentMajor ||
30
+ (latestMajor === currentMajor && latestMinor > currentMinor) ||
31
+ (latestMajor === currentMajor &&
32
+ latestMinor === currentMinor &&
33
+ latestPatch > currentPatch);
34
+ if (isNewer) {
35
+ console.error('');
36
+ console.error('╔════════════════════════════════════════════════════════════╗');
37
+ console.error('║ UPDATE AVAILABLE ║');
38
+ console.error(`║ ${PACKAGE_NAME} ${CURRENT_VERSION} → ${latestVersion}`.padEnd(61) + '║');
39
+ console.error('║ ║');
40
+ console.error('║ Run: npm install -g wolfpack-mcp@latest ║');
41
+ console.error('║ Or update your package.json and run: npm install ║');
42
+ console.error('╚════════════════════════════════════════════════════════════╝');
43
+ console.error('');
44
+ }
45
+ }
46
+ }
47
+ catch {
48
+ // Silently ignore update check failures - don't block startup
49
+ }
50
+ }
8
51
  // Work Item schemas
9
52
  const ListWorkItemsSchema = z.object({
10
53
  team_id: z.number().optional().describe('Team ID to filter work items'),
@@ -573,14 +616,18 @@ class WolfpackMCPServer {
573
616
  // Work Item handlers
574
617
  case 'list_work_items': {
575
618
  const parsed = ListWorkItemsSchema.parse(args);
576
- const workItems = await this.client.listWorkItems({
619
+ const result = await this.client.listWorkItems({
577
620
  teamId: parsed.team_id || this.client.getTeamId() || undefined,
578
621
  status: parsed.status,
579
622
  limit: parsed.limit,
580
623
  offset: parsed.offset,
581
624
  });
625
+ let text = JSON.stringify(result.items, null, 2);
626
+ if (result.truncated) {
627
+ text = `Note: Results truncated. Showing ${result.items.length} of ${result.total} items. Use status filter or limit/offset for pagination.\n\n${text}`;
628
+ }
582
629
  return {
583
- content: [{ type: 'text', text: JSON.stringify(workItems, null, 2) }],
630
+ content: [{ type: 'text', text }],
584
631
  };
585
632
  }
586
633
  case 'get_work_item': {
@@ -632,14 +679,18 @@ class WolfpackMCPServer {
632
679
  // Radar Item handlers
633
680
  case 'list_radar_items': {
634
681
  const parsed = ListRadarItemsSchema.parse(args);
635
- const radarItems = await this.client.listRadarItems({
682
+ const result = await this.client.listRadarItems({
636
683
  teamId: parsed.team_id,
637
684
  stage: parsed.stage,
638
685
  limit: parsed.limit,
639
686
  offset: parsed.offset,
640
687
  });
688
+ let text = JSON.stringify(result.items, null, 2);
689
+ if (result.truncated) {
690
+ text = `Note: Results truncated. Showing ${result.items.length} of ${result.total} items. Use stage filter or limit/offset for pagination.\n\n${text}`;
691
+ }
641
692
  return {
642
- content: [{ type: 'text', text: JSON.stringify(radarItems, null, 2) }],
693
+ content: [{ type: 'text', text }],
643
694
  };
644
695
  }
645
696
  case 'get_radar_item': {
@@ -657,7 +708,7 @@ class WolfpackMCPServer {
657
708
  // Issue handlers
658
709
  case 'list_issues': {
659
710
  const parsed = ListIssuesSchema.parse(args);
660
- const issues = await this.client.listIssues({
711
+ const result = await this.client.listIssues({
661
712
  teamId: parsed.team_id,
662
713
  status: parsed.status,
663
714
  severity: parsed.severity,
@@ -666,8 +717,12 @@ class WolfpackMCPServer {
666
717
  limit: parsed.limit,
667
718
  offset: parsed.offset,
668
719
  });
720
+ let text = JSON.stringify(result.items, null, 2);
721
+ if (result.truncated) {
722
+ text = `Note: Results truncated. Showing ${result.items.length} of ${result.total} items. Use filters or limit/offset for pagination.\n\n${text}`;
723
+ }
669
724
  return {
670
- content: [{ type: 'text', text: JSON.stringify(issues, null, 2) }],
725
+ content: [{ type: 'text', text }],
671
726
  };
672
727
  }
673
728
  case 'get_issue': {
@@ -747,12 +802,16 @@ class WolfpackMCPServer {
747
802
  // Wiki Page handlers
748
803
  case 'list_wiki_pages': {
749
804
  const parsed = ListWikiPagesSchema.parse(args);
750
- const pages = await this.client.listWikiPages({
805
+ const result = await this.client.listWikiPages({
751
806
  limit: parsed.limit,
752
807
  offset: parsed.offset,
753
808
  });
809
+ let text = JSON.stringify(result.items, null, 2);
810
+ if (result.truncated) {
811
+ text = `Note: Results truncated. Showing ${result.items.length} of ${result.total} pages. Use limit/offset for pagination.\n\n${text}`;
812
+ }
754
813
  return {
755
- content: [{ type: 'text', text: JSON.stringify(pages, null, 2) }],
814
+ content: [{ type: 'text', text }],
756
815
  };
757
816
  }
758
817
  case 'get_wiki_page': {
@@ -801,12 +860,16 @@ class WolfpackMCPServer {
801
860
  // Journal Entry handlers
802
861
  case 'list_journal_entries': {
803
862
  const parsed = ListJournalEntriesSchema.parse(args);
804
- const entries = await this.client.listJournalEntries({
863
+ const result = await this.client.listJournalEntries({
805
864
  limit: parsed.limit,
806
865
  offset: parsed.offset,
807
866
  });
867
+ let text = JSON.stringify(result.items, null, 2);
868
+ if (result.truncated) {
869
+ text = `Note: Results truncated. Showing ${result.items.length} of ${result.total} entries. Use limit/offset for pagination.\n\n${text}`;
870
+ }
808
871
  return {
809
- content: [{ type: 'text', text: JSON.stringify(entries, null, 2) }],
872
+ content: [{ type: 'text', text }],
810
873
  };
811
874
  }
812
875
  case 'get_journal_entry': {
@@ -895,6 +958,8 @@ class WolfpackMCPServer {
895
958
  });
896
959
  }
897
960
  async start() {
961
+ // Check for updates (non-blocking)
962
+ checkForUpdates();
898
963
  // Resolve team on startup
899
964
  if (config.teamSlug) {
900
965
  // Explicit team slug configured - use it
@@ -930,7 +995,7 @@ class WolfpackMCPServer {
930
995
  }
931
996
  const transport = new StdioServerTransport();
932
997
  await this.server.connect(transport);
933
- console.error('Wolfpack MCP Server v1.0.0 started');
998
+ console.error(`Wolfpack MCP Server v${CURRENT_VERSION} started`);
934
999
  }
935
1000
  }
936
1001
  // Validate configuration before starting
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wolfpack-mcp",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "MCP server for Wolfpack AI-enhanced software delivery tools",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",