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.
- package/dist/index.js +77 -21
- package/package.json +1 -1
- 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.
|
|
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
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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
|
-
|
|
228
|
-
|
|
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,
|
|
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
|
|
244
|
-
structuredContent: { found: false,
|
|
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
|
|
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
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.
|
|
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
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
|
|
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,
|
|
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
|
|
347
|
-
structuredContent: { found: false,
|
|
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
|
|
422
|
+
// Tool 4: Mark feedback as resolved
|
|
355
423
|
server.registerTool(
|
|
356
424
|
"mark_feedback_resolved",
|
|
357
425
|
{
|