wolfpack-mcp 1.0.22 → 1.0.24

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/apiClient.js CHANGED
@@ -96,34 +96,6 @@ export class ApiClient {
96
96
  throw new Error(`Failed to PATCH ${path}: ${error}`);
97
97
  }
98
98
  }
99
- async postMultipart(path, buffer, filename, mimeType) {
100
- const url = `${config.apiUrl}${path}`;
101
- const boundary = `----FormBoundary${Date.now()}`;
102
- const header = `--${boundary}\r\nContent-Disposition: form-data; name="file"; filename="${filename}"\r\nContent-Type: ${mimeType}\r\n\r\n`;
103
- const footer = `\r\n--${boundary}--\r\n`;
104
- const body = Buffer.concat([Buffer.from(header), buffer, Buffer.from(footer)]);
105
- try {
106
- const response = await fetch(url, {
107
- method: 'POST',
108
- headers: {
109
- Authorization: `Bearer ${config.apiKey}`,
110
- 'Content-Type': `multipart/form-data; boundary=${boundary}`,
111
- },
112
- body,
113
- });
114
- if (!response.ok) {
115
- const errorText = await response.text();
116
- throw new ApiError(response.status, `API error: ${response.statusText} - ${errorText}`);
117
- }
118
- return (await response.json());
119
- }
120
- catch (error) {
121
- if (error instanceof ApiError) {
122
- throw error;
123
- }
124
- throw new Error(`Failed to POST multipart ${path}: ${error}`);
125
- }
126
- }
127
99
  async delete(path) {
128
100
  const url = `${config.apiUrl}${path}`;
129
101
  try {
package/dist/client.js CHANGED
@@ -6,7 +6,6 @@ const PAGE_SIZE = 50;
6
6
  export class WolfpackClient {
7
7
  api;
8
8
  teamId = null;
9
- teamSlug = null;
10
9
  constructor() {
11
10
  this.api = new ApiClient();
12
11
  }
@@ -18,7 +17,6 @@ export class WolfpackClient {
18
17
  try {
19
18
  const team = await this.api.get(`/team/${teamSlug}`);
20
19
  this.teamId = team.id;
21
- this.teamSlug = team.slug;
22
20
  return team.id;
23
21
  }
24
22
  catch (error) {
@@ -28,46 +26,15 @@ export class WolfpackClient {
28
26
  getTeamId() {
29
27
  return this.teamId;
30
28
  }
31
- getTeamSlug() {
32
- return this.teamSlug;
33
- }
34
29
  setTeamId(teamId) {
35
30
  this.teamId = teamId;
36
31
  }
37
- setTeamSlug(slug) {
38
- this.teamSlug = slug;
39
- }
40
- /**
41
- * Resolve a team slug from a team ID, the cached slug, or by fetching teams.
42
- */
43
- async resolveSlug(teamId) {
44
- // If a specific team ID is provided, fetch teams to find its slug
45
- if (teamId) {
46
- const teams = await this.listTeams();
47
- const team = teams.find((t) => t.id === teamId);
48
- if (!team)
49
- throw new Error(`Team with ID ${teamId} not found`);
50
- return team.slug;
51
- }
52
- // Fall back to cached slug
53
- if (this.teamSlug)
54
- return this.teamSlug;
55
- // Try to resolve from cached team ID
56
- if (this.teamId) {
57
- const teams = await this.listTeams();
58
- const team = teams.find((t) => t.id === this.teamId);
59
- if (!team)
60
- throw new Error(`Team with ID ${this.teamId} not found`);
61
- this.teamSlug = team.slug;
62
- return team.slug;
63
- }
64
- throw new Error('No team selected. Use list_teams first.');
65
- }
66
32
  /**
67
33
  * List all teams the authenticated user is a member of.
68
34
  */
69
35
  async listTeams() {
70
- return this.api.get('/teams');
36
+ const response = await this.api.get('/teams');
37
+ return response.teams;
71
38
  }
72
39
  // Helper to fetch all pages of a paginated endpoint
73
40
  async fetchAllPages(endpoint, baseParams) {
@@ -412,10 +379,6 @@ export class WolfpackClient {
412
379
  /**
413
380
  * Upload an image and get back a URL that can be used in markdown content.
414
381
  */
415
- async uploadImage(teamSlug, base64Data, filename, mimeType) {
416
- const buffer = Buffer.from(base64Data, 'base64');
417
- return this.api.postMultipart(`/teams/${teamSlug}/upload-image`, buffer, filename, mimeType);
418
- }
419
382
  close() {
420
383
  // No cleanup needed for API client
421
384
  }
package/dist/index.js CHANGED
@@ -4,8 +4,6 @@ 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
6
  import { createRequire } from 'module';
7
- import { readFile, stat } from 'fs/promises';
8
- import { resolve, basename, extname } from 'path';
9
7
  import { WolfpackClient } from './client.js';
10
8
  import { validateConfig, config } from './config.js';
11
9
  // Get current package version
@@ -242,17 +240,6 @@ const CreateIssueCommentSchema = z.object({
242
240
  .string()
243
241
  .describe('Comment content (markdown). Supports base64-encoded images which will be auto-uploaded.'),
244
242
  });
245
- // Image upload schema
246
- const UploadImageSchema = z.object({
247
- file_path: z
248
- .string()
249
- .describe('Absolute path to an image file on disk (e.g., "/tmp/screenshot.png"). ' +
250
- 'Supported formats: JPEG, PNG, GIF, WebP. Max 5MB.'),
251
- team_id: z
252
- .number()
253
- .optional()
254
- .describe('Team ID (required for multi-team users, use list_teams to get IDs)'),
255
- });
256
243
  // Helper to detect if a work item description contains a plan
257
244
  function hasPlan(description) {
258
245
  if (!description)
@@ -796,29 +783,6 @@ class WolfpackMCPServer {
796
783
  required: ['issue_id', 'content'],
797
784
  },
798
785
  },
799
- // Image tools
800
- {
801
- name: 'upload_image',
802
- description: 'Upload an image file from disk and get back a permanent URL. Much faster than embedding base64 in content. ' +
803
- 'Pass the absolute file path - the file is read directly from disk without going through the LLM. ' +
804
- 'Returns a markdown image URL you can include in any content field. ' +
805
- 'Requires mcp:images:upload permission.',
806
- inputSchema: {
807
- type: 'object',
808
- properties: {
809
- file_path: {
810
- type: 'string',
811
- description: 'Absolute path to an image file on disk (e.g., "/tmp/screenshot.png"). ' +
812
- 'Supported formats: JPEG, PNG, GIF, WebP. Max 5MB.',
813
- },
814
- team_id: {
815
- type: 'number',
816
- description: 'Team ID (required for multi-team users, use list_teams to get IDs)',
817
- },
818
- },
819
- required: ['file_path'],
820
- },
821
- },
822
786
  ],
823
787
  }));
824
788
  this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
@@ -1234,45 +1198,6 @@ class WolfpackMCPServer {
1234
1198
  ],
1235
1199
  };
1236
1200
  }
1237
- case 'upload_image': {
1238
- const parsed = UploadImageSchema.parse(args);
1239
- const teamSlug = await this.client.resolveSlug(parsed.team_id || this.client.getTeamId() || undefined);
1240
- const filePath = resolve(parsed.file_path);
1241
- // Validate file extension
1242
- const ext = extname(filePath).toLowerCase();
1243
- const allowedExtensions = {
1244
- '.png': 'image/png',
1245
- '.jpg': 'image/jpeg',
1246
- '.jpeg': 'image/jpeg',
1247
- '.gif': 'image/gif',
1248
- '.webp': 'image/webp',
1249
- };
1250
- const mimeType = allowedExtensions[ext];
1251
- if (!mimeType) {
1252
- throw new Error(`Unsupported image format "${ext}". Supported: ${Object.keys(allowedExtensions).join(', ')}`);
1253
- }
1254
- // Check file exists and size
1255
- const fileStat = await stat(filePath);
1256
- const maxSize = 5 * 1024 * 1024; // 5MB
1257
- if (fileStat.size > maxSize) {
1258
- throw new Error(`File too large (${fileStat.size} bytes). Maximum size is 5MB.`);
1259
- }
1260
- // Validate magic bytes to confirm it's actually an image
1261
- const buffer = await readFile(filePath);
1262
- if (!this.isImageBuffer(buffer)) {
1263
- throw new Error('File does not appear to be a valid image.');
1264
- }
1265
- const filename = basename(filePath);
1266
- const result = await this.client.uploadImage(teamSlug, buffer.toString('base64'), filename, mimeType);
1267
- return {
1268
- content: [
1269
- {
1270
- type: 'text',
1271
- text: `Image uploaded successfully.\n\nURL: ${result.url}\n\nUse this URL in markdown: ![${filename}](${result.url})`,
1272
- },
1273
- ],
1274
- };
1275
- }
1276
1201
  default:
1277
1202
  throw new Error(`Unknown tool: ${name}`);
1278
1203
  }
@@ -1286,31 +1211,6 @@ class WolfpackMCPServer {
1286
1211
  }
1287
1212
  });
1288
1213
  }
1289
- isImageBuffer(buffer) {
1290
- if (buffer.length < 4)
1291
- return false;
1292
- // PNG: 89 50 4E 47
1293
- if (buffer[0] === 0x89 && buffer[1] === 0x50 && buffer[2] === 0x4e && buffer[3] === 0x47)
1294
- return true;
1295
- // JPEG: FF D8 FF
1296
- if (buffer[0] === 0xff && buffer[1] === 0xd8 && buffer[2] === 0xff)
1297
- return true;
1298
- // GIF: 47 49 46 38
1299
- if (buffer[0] === 0x47 && buffer[1] === 0x49 && buffer[2] === 0x46 && buffer[3] === 0x38)
1300
- return true;
1301
- // WebP: 52 49 46 46 ... 57 45 42 50
1302
- if (buffer.length >= 12 &&
1303
- buffer[0] === 0x52 &&
1304
- buffer[1] === 0x49 &&
1305
- buffer[2] === 0x46 &&
1306
- buffer[3] === 0x46 &&
1307
- buffer[8] === 0x57 &&
1308
- buffer[9] === 0x45 &&
1309
- buffer[10] === 0x42 &&
1310
- buffer[11] === 0x50)
1311
- return true;
1312
- return false;
1313
- }
1314
1214
  async start() {
1315
1215
  // Start version check early (non-blocking)
1316
1216
  const updateCheckPromise = checkForUpdates();
@@ -1333,7 +1233,6 @@ class WolfpackMCPServer {
1333
1233
  if (teams.length === 1) {
1334
1234
  // Auto-select the only team
1335
1235
  this.client.setTeamId(teams[0].id);
1336
- this.client.setTeamSlug(teams[0].slug);
1337
1236
  console.error(`Auto-selected team: ${teams[0].name} (${teams[0].slug})`);
1338
1237
  }
1339
1238
  else if (teams.length === 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wolfpack-mcp",
3
- "version": "1.0.22",
3
+ "version": "1.0.24",
4
4
  "description": "MCP server for Wolfpack AI-enhanced software delivery tools",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",