Skip to main content
Glama
ceciliomichael

Feedback Collector MCP

collect_feedback

Gather user feedback via an Electron app with customizable prompts, titles, and time formats, enabling efficient collection and processing of detailed responses.

Instructions

Collect feedback from the user through an Electron app

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
promptNoThe message to display to the user in the feedback windowPlease provide your feedback or describe your issue:
time_formatNoThe format for time informationfull
timezoneNoThe timezone to use (defaults to local)
titleNoThe title of the feedback windowAI Feedback Collection

Implementation Reference

  • mcp-server.js:148-247 (registration)
    Registration of the 'collect_feedback' tool with McpServer using server.tool(name, description, inputSchema, handlerFn). Includes the Zod input schema and the complete handler function.
    server.tool( "collect_feedback", "Collect feedback from the user through an Electron app", { prompt: z.string({ description: "The message to display to the user in the feedback window" }).default("Please provide your feedback or describe your issue:"), title: z.string({ description: "The title of the feedback window" }).default("AI Feedback Collection"), time_format: z.enum(["full", "iso", "date", "time", "unix"], { description: "The format for time information" }).default("full"), timezone: z.string({ description: "The timezone to use (defaults to local)" }).optional() }, async ({ prompt, title, time_format, timezone }) => { try { // Launch a new Electron app instance for each request const { sessionId, port } = await launchAppInstance(); // Send the request to the Electron app and wait for feedback try { const feedback = await sendRequestToApp({ prompt, title, time_format, timezone, port }); // Get time information const { formattedTime, additionalInfo } = getTimeInfo(time_format, timezone); // Format the time information as requested const timeInfo = Object.entries(additionalInfo) .map(([key, value]) => `${key}: ${value}`) .join('\n'); // Create the response content array with requested format let responseContent = []; // Add feedback text if (typeof feedback === 'string') { // Simple string feedback (old format) responseContent.push({ type: "text", text: feedback }); } else { // Object with text and possibly image (new format) responseContent.push({ type: "text", text: feedback.text }); // Add image if present if (feedback.hasImage && feedback.imagePath) { try { const imageBuffer = fs.readFileSync(feedback.imagePath); const base64Image = imageBuffer.toString('base64'); // Add the image responseContent.push({ type: "image", data: base64Image, mimeType: feedback.imageType || "image/png" }); } catch (error) { console.error("Error processing image:", error.message); responseContent.push({ type: "text", text: `Note: User attached an image, but it could not be processed. Error: ${error.message}` }); } } } // Add time information responseContent.push({ type: "text", text: timeInfo }); return { content: responseContent }; } finally { // Clean up the app instance when done const instance = appInstances.get(sessionId); if (instance && instance.process) { try { instance.process.kill(); } catch (error) { console.error(`Error killing app process (sessionId: ${sessionId}):`, error); } appInstances.delete(sessionId); } } } catch (error) { console.error("Error collecting feedback:", error); return { content: [{ type: "text", text: `Error collecting feedback: ${error.message}` }], isError: true }; } } );
  • The core handler logic for executing the 'collect_feedback' tool. Launches Electron app, sends HTTP request for feedback, handles text and image response, adds time info, cleans up process.
    async ({ prompt, title, time_format, timezone }) => { try { // Launch a new Electron app instance for each request const { sessionId, port } = await launchAppInstance(); // Send the request to the Electron app and wait for feedback try { const feedback = await sendRequestToApp({ prompt, title, time_format, timezone, port }); // Get time information const { formattedTime, additionalInfo } = getTimeInfo(time_format, timezone); // Format the time information as requested const timeInfo = Object.entries(additionalInfo) .map(([key, value]) => `${key}: ${value}`) .join('\n'); // Create the response content array with requested format let responseContent = []; // Add feedback text if (typeof feedback === 'string') { // Simple string feedback (old format) responseContent.push({ type: "text", text: feedback }); } else { // Object with text and possibly image (new format) responseContent.push({ type: "text", text: feedback.text }); // Add image if present if (feedback.hasImage && feedback.imagePath) { try { const imageBuffer = fs.readFileSync(feedback.imagePath); const base64Image = imageBuffer.toString('base64'); // Add the image responseContent.push({ type: "image", data: base64Image, mimeType: feedback.imageType || "image/png" }); } catch (error) { console.error("Error processing image:", error.message); responseContent.push({ type: "text", text: `Note: User attached an image, but it could not be processed. Error: ${error.message}` }); } } } // Add time information responseContent.push({ type: "text", text: timeInfo }); return { content: responseContent }; } finally { // Clean up the app instance when done const instance = appInstances.get(sessionId); if (instance && instance.process) { try { instance.process.kill(); } catch (error) { console.error(`Error killing app process (sessionId: ${sessionId}):`, error); } appInstances.delete(sessionId); } } } catch (error) { console.error("Error collecting feedback:", error); return { content: [{ type: "text", text: `Error collecting feedback: ${error.message}` }], isError: true }; } }
  • Zod schema for tool inputs: prompt, title, time_format, timezone with descriptions and defaults.
    prompt: z.string({ description: "The message to display to the user in the feedback window" }).default("Please provide your feedback or describe your issue:"), title: z.string({ description: "The title of the feedback window" }).default("AI Feedback Collection"), time_format: z.enum(["full", "iso", "date", "time", "unix"], { description: "The format for time information" }).default("full"), timezone: z.string({ description: "The timezone to use (defaults to local)" }).optional() },
  • Helper function to launch a new Electron app instance on a random port, track it, and return sessionId and port.
    async function launchAppInstance() { const sessionId = randomUUID(); const port = getAvailablePort(); console.error(`Launching new Electron app instance (sessionId: ${sessionId}, port: ${port})...`); // Pass parameters to the Electron app const env = { ...process.env, MCP_SERVER_PORT: port.toString() }; const appProcess = spawn(electronAppPath, [], { env, detached: false, windowsHide: false }); appProcess.on("error", (err) => { console.error(`Failed to start Electron app (sessionId: ${sessionId}):`, err); appInstances.delete(sessionId); }); appProcess.on("exit", (code) => { console.error(`Electron app exited with code ${code} (sessionId: ${sessionId})`); appInstances.delete(sessionId); }); // Store the instance information appInstances.set(sessionId, { process: appProcess, port: port }); // Wait a bit for the app to start await new Promise(resolve => setTimeout(resolve, 1000)); return { sessionId, port }; }
  • Helper to send HTTP POST request to the Electron app's /feedback endpoint and return the feedback response.
    async function sendRequestToApp({ prompt, title, time_format, timezone, port }) { return new Promise((resolve, reject) => { const requestData = JSON.stringify({ prompt, title, time_format, timezone }); const req = http.request({ hostname: "localhost", port: port, path: "/feedback", method: "POST", headers: { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(requestData) } }, (res) => { let responseData = ""; res.on("data", (chunk) => { responseData += chunk; }); res.on("end", () => { if (res.statusCode === 200) { try { const jsonResponse = JSON.parse(responseData); resolve(jsonResponse.feedback); } catch (e) { reject(new Error("Invalid response from Electron app")); } } else { reject(new Error(`HTTP error ${res.statusCode}: ${responseData}`)); } }); }); req.on("error", (error) => { reject(error); }); req.write(requestData); req.end(); }); }

Other Tools

Related Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ceciliomichael/feedbackjs-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server