@vibesharingapp/mcp-server 0.5.0 → 0.6.0

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 (2) hide show
  1. package/dist/index.js +271 -3
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -78,6 +78,7 @@ class VibesharingClient {
78
78
  status: updates.status,
79
79
  priority: updates.priority,
80
80
  assignedTo: updates.assigned_to,
81
+ resolutionNote: updates.resolution_note,
81
82
  }),
82
83
  });
83
84
  }
@@ -88,6 +89,7 @@ class VibesharingClient {
88
89
  status: updates.status,
89
90
  priority: updates.priority,
90
91
  assignedTo: updates.assigned_to,
92
+ resolutionNote: updates.resolution_note,
91
93
  }),
92
94
  });
93
95
  }
@@ -162,6 +164,31 @@ class VibesharingClient {
162
164
  method: "DELETE",
163
165
  });
164
166
  }
167
+ async generateFeedbackTopics(projectId, topics) {
168
+ return this.request("/api/feedback-topics", {
169
+ method: "POST",
170
+ body: JSON.stringify({
171
+ projectId,
172
+ topics,
173
+ source: "auto",
174
+ }),
175
+ });
176
+ }
177
+ async notifyFeedbackResolved(projectId, feedbackIds, deployUrl) {
178
+ return this.request("/api/feedback/notify-resolved", {
179
+ method: "POST",
180
+ body: JSON.stringify({ projectId, feedbackIds, deployUrl }),
181
+ });
182
+ }
183
+ async updateFeedbackBrief(projectId, brief, focus) {
184
+ const updates = { feedback_brief: brief };
185
+ if (focus)
186
+ updates.feedback_focus = focus;
187
+ return this.request(`/api/prototypes/${projectId}`, {
188
+ method: "PATCH",
189
+ body: JSON.stringify(updates),
190
+ });
191
+ }
165
192
  }
166
193
  /**
167
194
  * Simple fuzzy text matching. Scores based on:
@@ -207,8 +234,18 @@ function fuzzyMatch(query, items, getName, threshold = 0.3) {
207
234
  .sort((a, b) => b.score - a.score);
208
235
  }
209
236
  // ---- Version tracking & What's New ----
210
- const CURRENT_VERSION = "0.5.0";
237
+ const CURRENT_VERSION = "0.6.0";
211
238
  const WHATS_NEW = {
239
+ "0.6.0": [
240
+ "🆕 VibeSharing MCP v0.6.0 — What's New:",
241
+ "",
242
+ "• Auto-generated feedback questions — After deploying, use generate_feedback_topics",
243
+ " to create 3-5 targeted questions that guide stakeholders toward useful feedback.",
244
+ " Questions are categorized by theme: vision alignment, feasibility, design fidelity,",
245
+ " or interaction design. Previous auto-generated questions are replaced on each deploy.",
246
+ "• Feedback briefs — Include a brief parameter to set context stakeholders see in the",
247
+ " Context tab. Also auto-extracted from ## Feedback Brief in CLAUDE.md at deploy time.",
248
+ ].join("\n"),
212
249
  "0.5.0": [
213
250
  "🆕 VibeSharing MCP v0.5.0 — What's New:",
214
251
  "",
@@ -272,7 +309,7 @@ const client = new VibesharingClient(VIBESHARING_URL, VIBESHARING_TOKEN);
272
309
  // Create MCP server
273
310
  const server = new index_js_1.Server({
274
311
  name: "vibesharing",
275
- version: "0.5.0",
312
+ version: "0.6.0",
276
313
  }, {
277
314
  capabilities: {
278
315
  tools: {},
@@ -398,10 +435,55 @@ server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => {
398
435
  type: "string",
399
436
  description: "User ID to assign to. Use empty string to unassign.",
400
437
  },
438
+ resolution_note: {
439
+ type: "string",
440
+ description: "Brief explanation of how the feedback was addressed (shown to the stakeholder). Only meaningful when status is resolved/wont_fix/deferred.",
441
+ },
401
442
  },
402
443
  required: ["feedback_ids"],
403
444
  },
404
445
  },
446
+ {
447
+ name: "close_feedback_loop",
448
+ description: "CALL THIS AFTER DEPLOYING when there is open feedback. Matches what you just built to open feedback items, resolves them with explanations, and notifies the original stakeholders with a personalized digest. The stakeholder sees exactly what happened to their feedback.\n\nFlow:\n1. Pull open feedback via get_feedback\n2. Look at what you built and match changes to feedback items\n3. Call this tool with the resolutions\n4. Stakeholders get email: 'Your feedback was addressed' with per-item explanations",
449
+ inputSchema: {
450
+ type: "object",
451
+ properties: {
452
+ project_id: {
453
+ type: "string",
454
+ description: "The VibeSharing prototype ID",
455
+ },
456
+ resolutions: {
457
+ type: "array",
458
+ items: {
459
+ type: "object",
460
+ properties: {
461
+ feedback_id: {
462
+ type: "string",
463
+ description: "The feedback item ID being resolved",
464
+ },
465
+ status: {
466
+ type: "string",
467
+ enum: ["resolved", "wont_fix", "deferred", "in_progress"],
468
+ description: "What happened: resolved (fixed), wont_fix (intentional), deferred (later), in_progress (started but not done)",
469
+ },
470
+ note: {
471
+ type: "string",
472
+ description: "Brief explanation of what changed or why it was deferred. This is shown directly to the stakeholder. Be specific: 'Nav restructured to separate admin and user flows' not 'Fixed the navigation'.",
473
+ },
474
+ },
475
+ required: ["feedback_id", "status", "note"],
476
+ },
477
+ description: "Array of feedback items to resolve with explanations",
478
+ },
479
+ deploy_url: {
480
+ type: "string",
481
+ description: "Optional: URL of the new deploy (included in notification so stakeholders can see the update)",
482
+ },
483
+ },
484
+ required: ["project_id", "resolutions"],
485
+ },
486
+ },
405
487
  {
406
488
  name: "sync_context",
407
489
  description: "Sync your CLAUDE.md, AGENTS.md, or project context to VibeSharing. This helps maintain context across AI sessions and team members.",
@@ -654,6 +736,52 @@ server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => {
654
736
  required: ["link_id"],
655
737
  },
656
738
  },
739
+ {
740
+ name: "generate_feedback_topics",
741
+ description: "Auto-generate feedback questions for a prototype based on what was built. IMPORTANT: Before generating questions, ask the user: 'What type of feedback is most important for this deploy?' and present these options:\n\n 1. Awareness only — just sharing progress, no feedback needed\n 2. Design direction — brand, visual, layout feedback\n 3. Technical feasibility — is this buildable, are these features doable\n 4. Vision alignment — does this match where we're going\n 5. Interaction design — usability, flow, UX patterns\n 6. Full review — all feedback welcome (default)\n\nUse their answer as the 'focus' parameter. If 'awareness', skip topic generation and just set the brief. Otherwise generate 3-5 questions, weighting toward the chosen focus theme.",
742
+ inputSchema: {
743
+ type: "object",
744
+ properties: {
745
+ project_id: {
746
+ type: "string",
747
+ description: "The VibeSharing prototype ID",
748
+ },
749
+ focus: {
750
+ type: "string",
751
+ enum: ["awareness", "design", "feasibility", "vision", "interaction", "full"],
752
+ description: "The type of feedback the designer wants. 'awareness' = no questions, just FYI. Others emphasize that theme. 'full' = all themes equally. Default: 'full'.",
753
+ },
754
+ brief: {
755
+ type: "string",
756
+ description: "A short (2-4 sentence) feedback brief explaining what this prototype is and what's ready for review. Stored on the prototype and shown to stakeholders in the Context tab.",
757
+ },
758
+ topics: {
759
+ type: "array",
760
+ items: {
761
+ type: "object",
762
+ properties: {
763
+ title: {
764
+ type: "string",
765
+ description: "The feedback question (e.g., 'Does this navigation flow match how your team actually works?')",
766
+ },
767
+ description: {
768
+ type: "string",
769
+ description: "Optional: Additional context for the question",
770
+ },
771
+ theme: {
772
+ type: "string",
773
+ enum: ["vision", "feasibility", "design", "interaction"],
774
+ description: "Question theme: 'vision' (alignment with goals), 'feasibility' (technical possibility), 'design' (brand/visual fidelity), 'interaction' (usability/UX patterns)",
775
+ },
776
+ },
777
+ required: ["title"],
778
+ },
779
+ description: "Array of feedback questions to create. Generate 3-5 based on what you built. Weight toward the focus theme (e.g., if focus is 'feasibility', 2-3 questions should be feasibility-themed). Not required when focus is 'awareness'.",
780
+ },
781
+ },
782
+ required: ["project_id"],
783
+ },
784
+ },
657
785
  ],
658
786
  };
659
787
  });
@@ -828,7 +956,7 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
828
956
  };
829
957
  }
830
958
  case "triage_feedback": {
831
- const { feedback_ids, status: newStatus, priority: newPriority, assigned_to: newAssignee } = args;
959
+ const { feedback_ids, status: newStatus, priority: newPriority, assigned_to: newAssignee, resolution_note } = args;
832
960
  if (!feedback_ids || feedback_ids.length === 0) {
833
961
  return {
834
962
  content: [{ type: "text", text: "Error: feedback_ids array is required" }],
@@ -841,6 +969,8 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
841
969
  updates.priority = newPriority;
842
970
  if (newAssignee !== undefined)
843
971
  updates.assigned_to = newAssignee || null;
972
+ if (resolution_note)
973
+ updates.resolution_note = resolution_note;
844
974
  const result = await client.triageFeedback(feedback_ids, updates);
845
975
  const changes = [];
846
976
  if (newStatus)
@@ -849,6 +979,8 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
849
979
  changes.push(`priority → ${newPriority}`);
850
980
  if (newAssignee !== undefined)
851
981
  changes.push(newAssignee ? `assigned → ${newAssignee}` : "unassigned");
982
+ if (resolution_note)
983
+ changes.push(`note: "${resolution_note}"`);
852
984
  return {
853
985
  content: [
854
986
  {
@@ -858,6 +990,63 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
858
990
  ],
859
991
  };
860
992
  }
993
+ case "close_feedback_loop": {
994
+ const { project_id, resolutions, deploy_url } = args;
995
+ if (!resolutions || resolutions.length === 0) {
996
+ return {
997
+ content: [{ type: "text", text: "Error: At least one resolution is required." }],
998
+ };
999
+ }
1000
+ // Step 1: Resolve each feedback item with its note
1001
+ const resolvedIds = [];
1002
+ const errors = [];
1003
+ for (const res of resolutions) {
1004
+ try {
1005
+ await client.triageFeedback([res.feedback_id], {
1006
+ status: res.status,
1007
+ resolution_note: res.note,
1008
+ });
1009
+ resolvedIds.push(res.feedback_id);
1010
+ }
1011
+ catch (err) {
1012
+ errors.push(`Failed to update ${res.feedback_id}: ${err}`);
1013
+ }
1014
+ }
1015
+ // Step 2: Notify stakeholders
1016
+ let notifyResult = { notified: 0, emailed: 0, stakeholders: 0 };
1017
+ if (resolvedIds.length > 0) {
1018
+ try {
1019
+ notifyResult = await client.notifyFeedbackResolved(project_id, resolvedIds, deploy_url);
1020
+ }
1021
+ catch (err) {
1022
+ errors.push(`Notification failed: ${err}`);
1023
+ }
1024
+ }
1025
+ const statusLabels = {
1026
+ resolved: "Resolved",
1027
+ wont_fix: "Won't fix",
1028
+ deferred: "Deferred",
1029
+ in_progress: "In progress",
1030
+ };
1031
+ const lines = [
1032
+ `✅ Feedback loop closed — ${resolvedIds.length} item${resolvedIds.length === 1 ? "" : "s"} updated:`,
1033
+ "",
1034
+ ...resolutions.map((r, i) => {
1035
+ const label = statusLabels[r.status] || r.status;
1036
+ const success = resolvedIds.includes(r.feedback_id) ? "✓" : "✗";
1037
+ return ` ${success} [${label}] ${r.note}`;
1038
+ }),
1039
+ ];
1040
+ if (notifyResult.stakeholders > 0) {
1041
+ lines.push("", `📬 ${notifyResult.stakeholders} stakeholder${notifyResult.stakeholders === 1 ? "" : "s"} notified (${notifyResult.emailed} email${notifyResult.emailed === 1 ? "" : "s"} sent)`, "Each stakeholder received a personalized digest showing what happened to their specific feedback.");
1042
+ }
1043
+ if (errors.length > 0) {
1044
+ lines.push("", `⚠️ ${errors.length} error(s):`, ...errors.map(e => ` - ${e}`));
1045
+ }
1046
+ return {
1047
+ content: [{ type: "text", text: lines.join("\n") }],
1048
+ };
1049
+ }
861
1050
  case "sync_context": {
862
1051
  const { project_id, content } = args;
863
1052
  await client.syncContext(project_id, content);
@@ -1284,6 +1473,85 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
1284
1473
  ],
1285
1474
  };
1286
1475
  }
1476
+ case "generate_feedback_topics": {
1477
+ const { project_id, topics, brief, focus } = args;
1478
+ const feedbackFocus = focus || "full";
1479
+ const themeLabels = {
1480
+ vision: "Vision Alignment",
1481
+ feasibility: "Feasibility",
1482
+ design: "Design Fidelity",
1483
+ interaction: "Interaction Design",
1484
+ };
1485
+ const focusLabels = {
1486
+ awareness: "Awareness only",
1487
+ design: "Design direction",
1488
+ feasibility: "Technical feasibility",
1489
+ vision: "Vision alignment",
1490
+ interaction: "Interaction design",
1491
+ full: "Full review",
1492
+ };
1493
+ // Store brief and focus
1494
+ try {
1495
+ await client.updateFeedbackBrief(project_id, brief || "", feedbackFocus);
1496
+ }
1497
+ catch (err) {
1498
+ console.error("Failed to store feedback brief/focus:", err);
1499
+ }
1500
+ // Awareness mode: no questions, just the brief
1501
+ if (feedbackFocus === "awareness") {
1502
+ // Clear any existing auto-generated questions
1503
+ try {
1504
+ await client.generateFeedbackTopics(project_id, []);
1505
+ }
1506
+ catch {
1507
+ // OK if this fails — just means no old auto topics to clear
1508
+ }
1509
+ const lines = [
1510
+ `📢 Prototype set to awareness mode — no feedback questions will be shown.`,
1511
+ ];
1512
+ if (brief) {
1513
+ lines.push("", `📋 Brief saved: "${brief.substring(0, 100)}${brief.length > 100 ? "..." : ""}"`);
1514
+ }
1515
+ lines.push("", "Stakeholders will see this as an FYI with the Context tab available.", "They can still leave general comments if they choose to.");
1516
+ return {
1517
+ content: [{ type: "text", text: lines.join("\n") }],
1518
+ };
1519
+ }
1520
+ // All other modes: generate questions
1521
+ if (!topics || topics.length === 0) {
1522
+ return {
1523
+ content: [{ type: "text", text: "Error: At least one topic is required when focus is not 'awareness'." }],
1524
+ };
1525
+ }
1526
+ // Sort topics: focused theme first, then others
1527
+ const sortedTopics = feedbackFocus !== "full"
1528
+ ? [
1529
+ ...topics.filter(t => t.theme === feedbackFocus),
1530
+ ...topics.filter(t => t.theme !== feedbackFocus),
1531
+ ]
1532
+ : topics;
1533
+ const result = await client.generateFeedbackTopics(project_id, sortedTopics);
1534
+ const created = result.topics || [];
1535
+ const lines = [
1536
+ `✅ Created ${created.length} feedback question${created.length === 1 ? "" : "s"} (focus: ${focusLabels[feedbackFocus] || feedbackFocus}):`,
1537
+ "",
1538
+ ...created.map((t, i) => {
1539
+ const tag = t.theme ? ` [${themeLabels[t.theme] || t.theme}]` : "";
1540
+ const star = t.theme === feedbackFocus ? " ★" : "";
1541
+ return ` ${i + 1}. ${t.title}${tag}${star}`;
1542
+ }),
1543
+ ];
1544
+ if (brief) {
1545
+ lines.push("", `📋 Feedback brief saved — stakeholders can see it in the Context tab.`);
1546
+ }
1547
+ if (feedbackFocus !== "full") {
1548
+ lines.push("", `${themeLabels[feedbackFocus] || feedbackFocus} questions are pinned to the top for stakeholders.`);
1549
+ }
1550
+ lines.push("", "Previous auto-generated questions were replaced. Manual questions are untouched.");
1551
+ return {
1552
+ content: [{ type: "text", text: lines.join("\n") }],
1553
+ };
1554
+ }
1287
1555
  default:
1288
1556
  return {
1289
1557
  content: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibesharingapp/mcp-server",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "MCP server for VibeSharing - register prototypes and get feedback directly from Claude Code",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",