super-feedback-mcp 0.2.2 ā 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/README.md +5 -5
- package/dist/index.js +85 -4
- package/package.json +2 -2
- package/src/index.ts +99 -4
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
#
|
|
1
|
+
# SuperFeedback MCP Server
|
|
2
2
|
|
|
3
|
-
An MCP (Model Context Protocol) server that enables AI agents in Cursor to query and resolve client feedback from
|
|
3
|
+
An MCP (Model Context Protocol) server that enables AI agents in Cursor to query and resolve client feedback from SuperFeedback projects.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -57,7 +57,7 @@ Or if published to npm:
|
|
|
57
57
|
*At least one of `ACCESS_CODE` or `ADMIN_CODE` is required. For full functionality (read + resolve), provide both.
|
|
58
58
|
|
|
59
59
|
**Where to find these codes:**
|
|
60
|
-
- Go to your
|
|
60
|
+
- Go to your SuperFeedback dashboard
|
|
61
61
|
- Select your project
|
|
62
62
|
- Click "Settings" or the gear icon
|
|
63
63
|
- Copy the "Access Code" and "Admin Code"
|
|
@@ -121,7 +121,7 @@ The agent will:
|
|
|
121
121
|
|
|
122
122
|
## How It Works
|
|
123
123
|
|
|
124
|
-
The MCP server connects to your
|
|
124
|
+
The MCP server connects to your SuperFeedback project via the Convex API using your access code. It transforms the raw feedback data into AI-friendly format with:
|
|
125
125
|
|
|
126
126
|
- **Element identification**: Multiple selector strategies (ID, test ID, CSS path, XPath)
|
|
127
127
|
- **Route mapping**: Extracts page path and suggests likely source files
|
|
@@ -150,7 +150,7 @@ Make sure you've set the `SUPER_FEEDBACK_ACCESS_CODE` in your MCP config's `env`
|
|
|
150
150
|
|
|
151
151
|
### "API error: 401"
|
|
152
152
|
|
|
153
|
-
Your access code is invalid or expired. Check your project settings in
|
|
153
|
+
Your access code is invalid or expired. Check your project settings in SuperFeedback.
|
|
154
154
|
|
|
155
155
|
### "API error: 404"
|
|
156
156
|
|
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,6 +209,10 @@ 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
|
}
|
|
212
|
+
// Note if there are images attached (use get_comment_images to fetch them)
|
|
213
|
+
if (c.imageUrls && c.imageUrls.length > 0) {
|
|
214
|
+
text += `\n\nš¼ļø Attached Images: ${c.imageUrls.length} image(s) - use get_comment_images("${c.id}") to view`;
|
|
215
|
+
}
|
|
210
216
|
return {
|
|
211
217
|
content: [{ type: "text", text }],
|
|
212
218
|
structuredContent: { found: true, comment: c },
|
|
@@ -221,7 +227,82 @@ Use this after get_feedback_summary to dive into a specific comment.`,
|
|
|
221
227
|
};
|
|
222
228
|
}
|
|
223
229
|
});
|
|
224
|
-
// Tool 3:
|
|
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
|
+
};
|
|
266
|
+
}
|
|
267
|
+
// Fetch images and convert to base64 for MCP ImageContent
|
|
268
|
+
const imageContents = [];
|
|
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 });
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
failedUrls.push(url);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
catch {
|
|
284
|
+
failedUrls.push(url);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
const summary = `š¼ļø Images for comment ${commentId}: ${imageContents.length} of ${c.imageUrls.length} loaded successfully${failedUrls.length > 0 ? ` (${failedUrls.length} failed)` : ""}`;
|
|
288
|
+
return {
|
|
289
|
+
content: [
|
|
290
|
+
{ type: "text", text: summary },
|
|
291
|
+
...imageContents,
|
|
292
|
+
],
|
|
293
|
+
structuredContent: { found: true, imageCount: imageContents.length },
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
catch (error) {
|
|
297
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
298
|
+
return {
|
|
299
|
+
content: [{ type: "text", text: `Error fetching images: ${message}` }],
|
|
300
|
+
structuredContent: { found: false, imageCount: 0 },
|
|
301
|
+
isError: true,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
// Tool 4: Mark feedback as resolved
|
|
225
306
|
server.registerTool("mark_feedback_resolved", {
|
|
226
307
|
title: "Mark Feedback Resolved",
|
|
227
308
|
description: `Marks a feedback comment as resolved after you've fixed the issue.
|
|
@@ -277,7 +358,7 @@ async function main() {
|
|
|
277
358
|
const transport = new StdioServerTransport();
|
|
278
359
|
await server.connect(transport);
|
|
279
360
|
// Log to stderr so it doesn't interfere with MCP protocol on stdout
|
|
280
|
-
console.error("
|
|
361
|
+
console.error("SuperFeedback MCP server running...");
|
|
281
362
|
}
|
|
282
363
|
main().catch((error) => {
|
|
283
364
|
console.error("Fatal error:", error);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "super-feedback-mcp",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "MCP server for
|
|
3
|
+
"version": "0.2.6",
|
|
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",
|
|
7
7
|
"bin": {
|
package/src/index.ts
CHANGED
|
@@ -72,6 +72,7 @@ interface FullCommentDetails {
|
|
|
72
72
|
elementText: string;
|
|
73
73
|
label?: string;
|
|
74
74
|
}>;
|
|
75
|
+
imageUrls?: string[];
|
|
75
76
|
}
|
|
76
77
|
|
|
77
78
|
// Configuration from environment
|
|
@@ -115,7 +116,7 @@ async function callConvexAPI<T>(endpoint: string, options?: RequestInit): Promis
|
|
|
115
116
|
// Create MCP server
|
|
116
117
|
const server = new McpServer({
|
|
117
118
|
name: "super-feedback",
|
|
118
|
-
version: "0.2.
|
|
119
|
+
version: "0.2.6",
|
|
119
120
|
});
|
|
120
121
|
|
|
121
122
|
// Tool 1: Get feedback summary (lightweight overview)
|
|
@@ -225,8 +226,10 @@ Returns everything needed to work on the feedback:
|
|
|
225
226
|
- All replies/thread history
|
|
226
227
|
- Design suggestions (if any)
|
|
227
228
|
- Cross-element references (if any)
|
|
229
|
+
- Image attachment count (if any)
|
|
228
230
|
|
|
229
|
-
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.`,
|
|
230
233
|
inputSchema: {
|
|
231
234
|
commentId: z.string().describe("The comment ID to get full details for"),
|
|
232
235
|
},
|
|
@@ -309,6 +312,11 @@ Use this after get_feedback_summary to dive into a specific comment.`,
|
|
|
309
312
|
});
|
|
310
313
|
}
|
|
311
314
|
|
|
315
|
+
// Note if there are images attached (use get_comment_images to fetch them)
|
|
316
|
+
if (c.imageUrls && c.imageUrls.length > 0) {
|
|
317
|
+
text += `\n\nš¼ļø Attached Images: ${c.imageUrls.length} image(s) - use get_comment_images("${c.id}") to view`;
|
|
318
|
+
}
|
|
319
|
+
|
|
312
320
|
return {
|
|
313
321
|
content: [{ type: "text", text }],
|
|
314
322
|
structuredContent: { found: true, comment: c },
|
|
@@ -324,7 +332,94 @@ Use this after get_feedback_summary to dive into a specific comment.`,
|
|
|
324
332
|
}
|
|
325
333
|
);
|
|
326
334
|
|
|
327
|
-
// Tool 3:
|
|
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
|
+
};
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Fetch images and convert to base64 for MCP ImageContent
|
|
383
|
+
const imageContents: Array<{ type: "image"; data: string; mimeType: string }> = [];
|
|
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);
|
|
396
|
+
}
|
|
397
|
+
} catch {
|
|
398
|
+
failedUrls.push(url);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const summary = `š¼ļø Images for comment ${commentId}: ${imageContents.length} of ${c.imageUrls.length} loaded successfully${failedUrls.length > 0 ? ` (${failedUrls.length} failed)` : ""}`;
|
|
403
|
+
|
|
404
|
+
return {
|
|
405
|
+
content: [
|
|
406
|
+
{ type: "text", text: summary },
|
|
407
|
+
...imageContents,
|
|
408
|
+
],
|
|
409
|
+
structuredContent: { found: true, imageCount: imageContents.length },
|
|
410
|
+
};
|
|
411
|
+
} catch (error) {
|
|
412
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
413
|
+
return {
|
|
414
|
+
content: [{ type: "text", text: `Error fetching images: ${message}` }],
|
|
415
|
+
structuredContent: { found: false, imageCount: 0 },
|
|
416
|
+
isError: true,
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
);
|
|
421
|
+
|
|
422
|
+
// Tool 4: Mark feedback as resolved
|
|
328
423
|
server.registerTool(
|
|
329
424
|
"mark_feedback_resolved",
|
|
330
425
|
{
|
|
@@ -388,7 +483,7 @@ async function main() {
|
|
|
388
483
|
await server.connect(transport);
|
|
389
484
|
|
|
390
485
|
// Log to stderr so it doesn't interfere with MCP protocol on stdout
|
|
391
|
-
console.error("
|
|
486
|
+
console.error("SuperFeedback MCP server running...");
|
|
392
487
|
}
|
|
393
488
|
|
|
394
489
|
main().catch((error) => {
|