super-feedback-mcp 0.2.5 → 0.2.6

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/index.js +77 -21
  2. package/package.json +1 -1
  3. package/src/index.ts +89 -21
package/dist/index.js CHANGED
@@ -39,7 +39,7 @@ async function callConvexAPI(endpoint, options) {
39
39
  // Create MCP server
40
40
  const server = new McpServer({
41
41
  name: "super-feedback",
42
- version: "0.2.5",
42
+ version: "0.2.6",
43
43
  });
44
44
  // Tool 1: Get feedback summary (lightweight overview)
45
45
  server.registerTool("get_feedback_summary", {
@@ -137,8 +137,10 @@ Returns everything needed to work on the feedback:
137
137
  - All replies/thread history
138
138
  - Design suggestions (if any)
139
139
  - Cross-element references (if any)
140
+ - Image attachment count (if any)
140
141
 
141
- Use this after get_feedback_summary to dive into a specific comment.`,
142
+ Use this after get_feedback_summary to dive into a specific comment.
143
+ If the comment has attached images, use get_comment_images to fetch and view them.`,
142
144
  inputSchema: {
143
145
  commentId: z.string().describe("The comment ID to get full details for"),
144
146
  },
@@ -207,46 +209,100 @@ Use this after get_feedback_summary to dive into a specific comment.`,
207
209
  text += `\n ${change.property}: ${change.oldValue} → ${change.newValue}`;
208
210
  });
209
211
  }
210
- // Note if there are images attached
212
+ // Note if there are images attached (use get_comment_images to fetch them)
211
213
  if (c.imageUrls && c.imageUrls.length > 0) {
212
- text += `\n\n🖼️ Attached Images: ${c.imageUrls.length} image(s)`;
214
+ text += `\n\n🖼️ Attached Images: ${c.imageUrls.length} image(s) - use get_comment_images("${c.id}") to view`;
215
+ }
216
+ return {
217
+ content: [{ type: "text", text }],
218
+ structuredContent: { found: true, comment: c },
219
+ };
220
+ }
221
+ catch (error) {
222
+ const message = error instanceof Error ? error.message : "Unknown error";
223
+ return {
224
+ content: [{ type: "text", text: `Error fetching comment details: ${message}` }],
225
+ structuredContent: { found: false, comment: null },
226
+ isError: true,
227
+ };
228
+ }
229
+ });
230
+ // Tool 3: Get images attached to a comment
231
+ server.registerTool("get_comment_images", {
232
+ title: "Get Comment Images",
233
+ description: `Fetches and returns the images attached to a feedback comment.
234
+
235
+ Use this when:
236
+ - get_comment_details indicates a comment has attached images
237
+ - You need to see visual context for the feedback (screenshots, mockups, etc.)
238
+ - The user's feedback references something visual
239
+
240
+ Returns the images as viewable content. Call this after get_comment_details
241
+ if you need to see the attached images.`,
242
+ inputSchema: {
243
+ commentId: z.string().describe("The comment ID to fetch images for"),
244
+ },
245
+ outputSchema: {
246
+ found: z.boolean(),
247
+ imageCount: z.number(),
248
+ },
249
+ }, async ({ commentId }) => {
250
+ try {
251
+ const params = getAuthParams();
252
+ params.set("commentId", commentId);
253
+ const response = await callConvexAPI(`/api/feedback/ai/comment?${params.toString()}`);
254
+ if (!response.found || !response.comment) {
255
+ return {
256
+ content: [{ type: "text", text: `Comment ${commentId} not found` }],
257
+ structuredContent: { found: false, imageCount: 0 },
258
+ };
259
+ }
260
+ const c = response.comment;
261
+ if (!c.imageUrls || c.imageUrls.length === 0) {
262
+ return {
263
+ content: [{ type: "text", text: `No images attached to comment ${commentId}` }],
264
+ structuredContent: { found: true, imageCount: 0 },
265
+ };
213
266
  }
214
267
  // Fetch images and convert to base64 for MCP ImageContent
215
268
  const imageContents = [];
216
- if (c.imageUrls && c.imageUrls.length > 0) {
217
- for (const url of c.imageUrls) {
218
- try {
219
- const imgResponse = await fetch(url);
220
- if (imgResponse.ok) {
221
- const buffer = await imgResponse.arrayBuffer();
222
- const base64 = Buffer.from(buffer).toString("base64");
223
- const mimeType = imgResponse.headers.get("content-type") || "image/png";
224
- imageContents.push({ type: "image", data: base64, mimeType });
225
- }
269
+ const failedUrls = [];
270
+ for (const url of c.imageUrls) {
271
+ try {
272
+ const imgResponse = await fetch(url);
273
+ if (imgResponse.ok) {
274
+ const buffer = await imgResponse.arrayBuffer();
275
+ const base64 = Buffer.from(buffer).toString("base64");
276
+ const mimeType = imgResponse.headers.get("content-type") || "image/png";
277
+ imageContents.push({ type: "image", data: base64, mimeType });
226
278
  }
227
- catch {
228
- // Skip images that fail to fetch
279
+ else {
280
+ failedUrls.push(url);
229
281
  }
230
282
  }
283
+ catch {
284
+ failedUrls.push(url);
285
+ }
231
286
  }
287
+ const summary = `🖼️ Images for comment ${commentId}: ${imageContents.length} of ${c.imageUrls.length} loaded successfully${failedUrls.length > 0 ? ` (${failedUrls.length} failed)` : ""}`;
232
288
  return {
233
289
  content: [
234
- { type: "text", text },
290
+ { type: "text", text: summary },
235
291
  ...imageContents,
236
292
  ],
237
- structuredContent: { found: true, comment: c },
293
+ structuredContent: { found: true, imageCount: imageContents.length },
238
294
  };
239
295
  }
240
296
  catch (error) {
241
297
  const message = error instanceof Error ? error.message : "Unknown error";
242
298
  return {
243
- content: [{ type: "text", text: `Error fetching comment details: ${message}` }],
244
- structuredContent: { found: false, comment: null },
299
+ content: [{ type: "text", text: `Error fetching images: ${message}` }],
300
+ structuredContent: { found: false, imageCount: 0 },
245
301
  isError: true,
246
302
  };
247
303
  }
248
304
  });
249
- // Tool 3: Mark feedback as resolved
305
+ // Tool 4: Mark feedback as resolved
250
306
  server.registerTool("mark_feedback_resolved", {
251
307
  title: "Mark Feedback Resolved",
252
308
  description: `Marks a feedback comment as resolved after you've fixed the issue.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "super-feedback-mcp",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "description": "MCP server for SuperFeedback - enables AI agents to query and resolve client feedback",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/src/index.ts CHANGED
@@ -116,7 +116,7 @@ async function callConvexAPI<T>(endpoint: string, options?: RequestInit): Promis
116
116
  // Create MCP server
117
117
  const server = new McpServer({
118
118
  name: "super-feedback",
119
- version: "0.2.5",
119
+ version: "0.2.6",
120
120
  });
121
121
 
122
122
  // Tool 1: Get feedback summary (lightweight overview)
@@ -226,8 +226,10 @@ Returns everything needed to work on the feedback:
226
226
  - All replies/thread history
227
227
  - Design suggestions (if any)
228
228
  - Cross-element references (if any)
229
+ - Image attachment count (if any)
229
230
 
230
- Use this after get_feedback_summary to dive into a specific comment.`,
231
+ Use this after get_feedback_summary to dive into a specific comment.
232
+ If the comment has attached images, use get_comment_images to fetch and view them.`,
231
233
  inputSchema: {
232
234
  commentId: z.string().describe("The comment ID to get full details for"),
233
235
  },
@@ -310,48 +312,114 @@ Use this after get_feedback_summary to dive into a specific comment.`,
310
312
  });
311
313
  }
312
314
 
313
- // Note if there are images attached
315
+ // Note if there are images attached (use get_comment_images to fetch them)
314
316
  if (c.imageUrls && c.imageUrls.length > 0) {
315
- text += `\n\n🖼️ Attached Images: ${c.imageUrls.length} image(s)`;
317
+ text += `\n\n🖼️ Attached Images: ${c.imageUrls.length} image(s) - use get_comment_images("${c.id}") to view`;
318
+ }
319
+
320
+ return {
321
+ content: [{ type: "text", text }],
322
+ structuredContent: { found: true, comment: c },
323
+ };
324
+ } catch (error) {
325
+ const message = error instanceof Error ? error.message : "Unknown error";
326
+ return {
327
+ content: [{ type: "text", text: `Error fetching comment details: ${message}` }],
328
+ structuredContent: { found: false, comment: null },
329
+ isError: true,
330
+ };
331
+ }
332
+ }
333
+ );
334
+
335
+ // Tool 3: Get images attached to a comment
336
+ server.registerTool(
337
+ "get_comment_images",
338
+ {
339
+ title: "Get Comment Images",
340
+ description: `Fetches and returns the images attached to a feedback comment.
341
+
342
+ Use this when:
343
+ - get_comment_details indicates a comment has attached images
344
+ - You need to see visual context for the feedback (screenshots, mockups, etc.)
345
+ - The user's feedback references something visual
346
+
347
+ Returns the images as viewable content. Call this after get_comment_details
348
+ if you need to see the attached images.`,
349
+ inputSchema: {
350
+ commentId: z.string().describe("The comment ID to fetch images for"),
351
+ },
352
+ outputSchema: {
353
+ found: z.boolean(),
354
+ imageCount: z.number(),
355
+ },
356
+ },
357
+ async ({ commentId }) => {
358
+ try {
359
+ const params = getAuthParams();
360
+ params.set("commentId", commentId);
361
+
362
+ const response = await callConvexAPI<{ found: boolean; comment: FullCommentDetails | null }>(
363
+ `/api/feedback/ai/comment?${params.toString()}`
364
+ );
365
+
366
+ if (!response.found || !response.comment) {
367
+ return {
368
+ content: [{ type: "text", text: `Comment ${commentId} not found` }],
369
+ structuredContent: { found: false, imageCount: 0 },
370
+ };
371
+ }
372
+
373
+ const c = response.comment;
374
+
375
+ if (!c.imageUrls || c.imageUrls.length === 0) {
376
+ return {
377
+ content: [{ type: "text", text: `No images attached to comment ${commentId}` }],
378
+ structuredContent: { found: true, imageCount: 0 },
379
+ };
316
380
  }
317
381
 
318
382
  // Fetch images and convert to base64 for MCP ImageContent
319
383
  const imageContents: Array<{ type: "image"; data: string; mimeType: string }> = [];
320
- if (c.imageUrls && c.imageUrls.length > 0) {
321
- for (const url of c.imageUrls) {
322
- try {
323
- const imgResponse = await fetch(url);
324
- if (imgResponse.ok) {
325
- const buffer = await imgResponse.arrayBuffer();
326
- const base64 = Buffer.from(buffer).toString("base64");
327
- const mimeType = imgResponse.headers.get("content-type") || "image/png";
328
- imageContents.push({ type: "image", data: base64, mimeType });
329
- }
330
- } catch {
331
- // Skip images that fail to fetch
384
+ const failedUrls: string[] = [];
385
+
386
+ for (const url of c.imageUrls) {
387
+ try {
388
+ const imgResponse = await fetch(url);
389
+ if (imgResponse.ok) {
390
+ const buffer = await imgResponse.arrayBuffer();
391
+ const base64 = Buffer.from(buffer).toString("base64");
392
+ const mimeType = imgResponse.headers.get("content-type") || "image/png";
393
+ imageContents.push({ type: "image", data: base64, mimeType });
394
+ } else {
395
+ failedUrls.push(url);
332
396
  }
397
+ } catch {
398
+ failedUrls.push(url);
333
399
  }
334
400
  }
335
401
 
402
+ const summary = `🖼️ Images for comment ${commentId}: ${imageContents.length} of ${c.imageUrls.length} loaded successfully${failedUrls.length > 0 ? ` (${failedUrls.length} failed)` : ""}`;
403
+
336
404
  return {
337
405
  content: [
338
- { type: "text", text },
406
+ { type: "text", text: summary },
339
407
  ...imageContents,
340
408
  ],
341
- structuredContent: { found: true, comment: c },
409
+ structuredContent: { found: true, imageCount: imageContents.length },
342
410
  };
343
411
  } catch (error) {
344
412
  const message = error instanceof Error ? error.message : "Unknown error";
345
413
  return {
346
- content: [{ type: "text", text: `Error fetching comment details: ${message}` }],
347
- structuredContent: { found: false, comment: null },
414
+ content: [{ type: "text", text: `Error fetching images: ${message}` }],
415
+ structuredContent: { found: false, imageCount: 0 },
348
416
  isError: true,
349
417
  };
350
418
  }
351
419
  }
352
420
  );
353
421
 
354
- // Tool 3: Mark feedback as resolved
422
+ // Tool 4: Mark feedback as resolved
355
423
  server.registerTool(
356
424
  "mark_feedback_resolved",
357
425
  {