@verygoodplugins/mcp-freescout 1.1.0 → 1.1.2

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.
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "permissions": {
3
3
  "allow": [
4
- "Bash(npx @verygoodplugins/mcp-freescout@1.0.1:*)"
4
+ "Bash(npx @verygoodplugins/mcp-freescout@1.0.1:*)",
5
+ "Bash(node:*)"
5
6
  ],
6
7
  "deny": []
7
8
  }
package/README.md CHANGED
@@ -143,7 +143,9 @@ Fetch a FreeScout ticket with all its details and conversation threads.
143
143
  }
144
144
  ```
145
145
 
146
- <img width="575" height="751" alt="image" src="https://github.com/user-attachments/assets/0144056d-f6d6-4275-9f55-dade0be3ba8c" />
146
+ **Example: Fetching a FreeScout ticket with conversation threads**
147
+
148
+ ![FreeScout ticket details and conversation threads displayed in Cursor chat interface](https://github.com/user-attachments/assets/0144056d-f6d6-4275-9f55-dade0be3ba8c)
147
149
 
148
150
 
149
151
  #### `freescout_analyze_ticket`
@@ -160,6 +162,10 @@ Analyze a ticket to determine issue type, root cause, and suggested solutions.
160
162
  - Root cause analysis
161
163
  - Bug vs feature request vs third-party issue determination
162
164
 
165
+ **Example: Intelligent ticket analysis with issue classification**
166
+
167
+ ![Ticket analysis showing issue type, root cause, and implementation recommendations](https://github.com/user-attachments/assets/19080021-1f29-45a4-8601-556b55d379c3)
168
+
163
169
  #### `freescout_add_note`
164
170
  Add an internal note to a ticket for team communication.
165
171
 
@@ -168,8 +174,6 @@ Add an internal note to a ticket for team communication.
168
174
  - `note` (required): The note content
169
175
  - `userId` (optional): User ID for the note (defaults to env setting)
170
176
 
171
- <img width="570" height="691" alt="image" src="https://github.com/user-attachments/assets/19080021-1f29-45a4-8601-556b55d379c3" />
172
-
173
177
  #### `freescout_update_ticket`
174
178
  Update ticket status and/or assignment.
175
179
 
@@ -178,15 +182,44 @@ Update ticket status and/or assignment.
178
182
  - `status` (optional): New status ('active', 'pending', 'closed', 'spam')
179
183
  - `assignTo` (optional): User ID to assign the ticket to
180
184
 
181
- #### `freescout_draft_reply`
182
- Generate a customer reply based on ticket analysis and fix description.
185
+ #### `freescout_create_draft_reply`
186
+ Create a draft reply in FreeScout that can be edited before sending. This tool lets the LLM generate the reply content and saves it directly to FreeScout as a draft. **Automatically converts Markdown formatting to HTML** for proper display in FreeScout.
187
+
188
+ **Parameters:**
189
+ - `ticket` (required): Ticket ID, number, or FreeScout URL
190
+ - `replyText` (required): The draft reply content (generated by the LLM, supports Markdown formatting)
191
+ - `userId` (optional): User ID creating the draft (defaults to env setting)
192
+
193
+ **Markdown Support:**
194
+ - **Bold text**: `**text**` or `__text__` → **text**
195
+ - *Italic text*: `*text*` or `_text_` → *text*
196
+ - `Code`: `` `code` `` → `code`
197
+ - Numbered lists: `1. item` → proper ordered lists
198
+ - Bullet lists: `- item` or `* item` → proper unordered lists
199
+ - Line breaks: Double newlines create paragraphs, single newlines create line breaks
200
+
201
+ **Workflow:**
202
+ 1. Use `freescout_get_ticket_context` to get customer info and ticket details
203
+ 2. Let the LLM craft a personalized reply using Markdown formatting
204
+ 3. Use `freescout_create_draft_reply` to save the draft in FreeScout (Markdown automatically converted to HTML)
205
+ 4. Review and edit the draft in FreeScout before sending
206
+
207
+ **Example: Draft reply workflow with personalized customer response**
208
+
209
+ ![Draft reply generation showing personalized customer message with ticket context](https://github.com/user-attachments/assets/a4f9eb6c-3204-4744-8aed-8d16d7c7641c)
210
+
211
+ #### `freescout_get_ticket_context`
212
+ Get ticket context and customer information to help craft personalized replies.
183
213
 
184
214
  **Parameters:**
185
215
  - `ticket` (required): Ticket ID, number, or FreeScout URL
186
- - `fixDescription` (optional): Description of the implemented fix
187
- - `isExplanatory` (optional): Whether this is an explanatory reply (no code changes)
188
216
 
189
- <img width="570" height="204" alt="image" src="https://github.com/user-attachments/assets/781d4a09-5605-4271-ac14-e133dd9f383d" />
217
+ **Returns:**
218
+ - Customer name and email
219
+ - Ticket subject and status
220
+ - Issue description and analysis
221
+ - Recent customer and team messages
222
+ - Analysis results (bug vs feature vs third-party issue)
190
223
 
191
224
  #### `freescout_search_tickets`
192
225
  Search for tickets across your FreeScout instance.
@@ -280,19 +313,33 @@ Fixes the validation error reported in the checkout process.
280
313
  ticketId: '12345' // Automatically adds FreeScout link to PR
281
314
  });
282
315
 
283
- // 3. Draft a customer reply
284
- const reply = await mcp.callTool('freescout_draft_reply', {
285
- ticket: '12345',
286
- fixDescription: 'Fixed the validation error in the checkout process'
316
+ // 3. Get ticket context to craft a personalized reply
317
+ const context = await mcp.callTool('freescout_get_ticket_context', {
318
+ ticket: '12345'
287
319
  });
288
320
 
289
- // 4. Add the draft as an internal note for review
290
- await mcp.callTool('freescout_add_note', {
321
+ // 4. Create a draft reply directly in FreeScout (LLM generates the content with Markdown)
322
+ await mcp.callTool('freescout_create_draft_reply', {
291
323
  ticket: '12345',
292
- note: `Draft reply for customer:\n\n${reply}`
324
+ replyText: `Hi ${context.customer.name},
325
+
326
+ Thank you for working through that validation issue with us! Your detailed report was really helpful.
327
+
328
+ I've just implemented a fix that addresses the checkout validation error you experienced. The fix includes:
329
+
330
+ 1. **Improved validation logic** in the checkout process
331
+ 2. **Better error handling** for edge cases
332
+ 3. **Additional safeguards** to prevent similar issues
333
+
334
+ The fix has been submitted for review and will be included in the next plugin update. You'll receive the update through WordPress's automatic update system.
335
+
336
+ Thanks again for your patience and for helping us improve the plugin!
337
+
338
+ Best regards,
339
+ [Your name]`
293
340
  });
294
341
 
295
- // 5. Update ticket status and assignment
342
+ // 5. Update ticket status and assignment
296
343
  await mcp.callTool('freescout_update_ticket', {
297
344
  ticket: '12345',
298
345
  status: 'active',
@@ -305,6 +352,38 @@ await mcp.callTool('git_remove_worktree', {
305
352
  });
306
353
  ```
307
354
 
355
+ ### Draft Reply Workflow
356
+ ```javascript
357
+ // 1. Get ticket context for personalized reply
358
+ const context = await mcp.callTool('freescout_get_ticket_context', {
359
+ ticket: '34811'
360
+ });
361
+
362
+ // 2. Create draft reply in FreeScout (LLM crafts the content)
363
+ await mcp.callTool('freescout_create_draft_reply', {
364
+ ticket: '34811',
365
+ replyText: `Hi ${context.customer.name},
366
+
367
+ Thank you for reporting the HighLevel OAuth authorization issue! Your experience with the EngageBay LiveChat plugin conflict has been really valuable.
368
+
369
+ Based on what we learned from your case, I've added a new plugin conflict detection system to WP Fusion. In the next update (v3.46.7), users will see:
370
+
371
+ 🔍 **Plugin Conflict Detection**
372
+ - Automatic detection of known conflicting plugins
373
+ - Warning messages before HighLevel authorization
374
+ - Clear guidance when conflicts are detected
375
+
376
+ This should prevent the confusion you experienced and help other users avoid similar issues.
377
+
378
+ The update should be available within the next few weeks. Thanks for your patience and for helping us improve the plugin!
379
+
380
+ Best regards,
381
+ Jack`
382
+ });
383
+
384
+ // The draft is now saved in FreeScout and can be reviewed/edited before sending
385
+ ```
386
+
308
387
  ### Handling Non-Bug Issues
309
388
  ```javascript
310
389
  // For third-party issues or feature requests
@@ -3,6 +3,10 @@ export declare class FreeScoutAPI {
3
3
  private baseUrl;
4
4
  private apiKey;
5
5
  constructor(baseUrl: string, apiKey: string);
6
+ /**
7
+ * Convert Markdown formatting to HTML for FreeScout
8
+ */
9
+ private markdownToHtml;
6
10
  private request;
7
11
  getConversation(ticketId: string, includeThreads?: boolean): Promise<FreeScoutConversation>;
8
12
  addThread(ticketId: string, type: 'note' | 'message' | 'customer', text: string, userId?: number, state?: 'draft' | 'published'): Promise<FreeScoutThread>;
@@ -1 +1 @@
1
- {"version":3,"file":"freescout-api.d.ts","sourceRoot":"","sources":["../src/freescout-api.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,qBAAqB,EACrB,oBAAoB,EACpB,eAAe,EAChB,MAAM,YAAY,CAAC;AAEpB,qBAAa,YAAY;IACvB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAS;gBAEX,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;YAK7B,OAAO;IA6Bf,eAAe,CACnB,QAAQ,EAAE,MAAM,EAChB,cAAc,GAAE,OAAc,GAC7B,OAAO,CAAC,qBAAqB,CAAC;IAO3B,SAAS,CACb,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,UAAU,EACrC,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,OAAO,GAAG,WAAW,GAC5B,OAAO,CAAC,eAAe,CAAC;IAqBrB,gBAAgB,CACpB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,eAAe,CAAC;IAIrB,kBAAkB,CACtB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE;QACP,MAAM,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAC;QAClD,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,GACA,OAAO,CAAC,qBAAqB,CAAC;IAQ3B,mBAAmB,CACvB,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,oBAAoB,CAAC,qBAAqB,CAAC,CAAC;IAUvD,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAQlD,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;CAoBxC"}
1
+ {"version":3,"file":"freescout-api.d.ts","sourceRoot":"","sources":["../src/freescout-api.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,qBAAqB,EACrB,oBAAoB,EACpB,eAAe,EAChB,MAAM,YAAY,CAAC;AAEpB,qBAAa,YAAY;IACvB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAS;gBAEX,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAK3C;;OAEG;IACH,OAAO,CAAC,cAAc;YAgIR,OAAO;IA6Bf,eAAe,CACnB,QAAQ,EAAE,MAAM,EAChB,cAAc,GAAE,OAAc,GAC7B,OAAO,CAAC,qBAAqB,CAAC;IAO3B,SAAS,CACb,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,UAAU,EACrC,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,OAAO,GAAG,WAAW,GAC5B,OAAO,CAAC,eAAe,CAAC;IAqBrB,gBAAgB,CACpB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,eAAe,CAAC;IAMrB,kBAAkB,CACtB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE;QACP,MAAM,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAC;QAClD,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,GACA,OAAO,CAAC,qBAAqB,CAAC;IAQ3B,mBAAmB,CACvB,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,oBAAoB,CAAC,qBAAqB,CAAC,CAAC;IAUvD,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAQlD,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;CAoBxC"}
@@ -6,6 +6,122 @@ export class FreeScoutAPI {
6
6
  this.baseUrl = baseUrl.replace(/\/$/, ''); // Remove trailing slash
7
7
  this.apiKey = apiKey;
8
8
  }
9
+ /**
10
+ * Convert Markdown formatting to HTML for FreeScout
11
+ */
12
+ markdownToHtml(text) {
13
+ // Convert bold text: **text** or __text__ -> <strong>text</strong>
14
+ let html = text
15
+ .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
16
+ .replace(/__(.*?)__/g, '<strong>$1</strong>');
17
+ // Convert italic text: *text* or _text_ -> <em>text</em> (avoid conflicts with bold)
18
+ html = html.replace(/(?<!\*)\*([^*]+)\*(?!\*)/g, '<em>$1</em>');
19
+ html = html.replace(/(?<!_)_([^_]+)_(?!_)/g, '<em>$1</em>');
20
+ // Convert code: `text` -> <code>text</code>
21
+ html = html.replace(/`(.*?)`/g, '<code>$1</code>');
22
+ // Process the entire text to handle lists that span paragraph breaks
23
+ const lines = html.split('\n');
24
+ const processedLines = [];
25
+ let inOrderedList = false;
26
+ let inUnorderedList = false;
27
+ let currentParagraph = [];
28
+ for (let i = 0; i < lines.length; i++) {
29
+ const line = lines[i];
30
+ const trimmedLine = line.trim();
31
+ // Check for empty lines (paragraph breaks)
32
+ if (!trimmedLine) {
33
+ // Check if the next non-empty line is also a list item
34
+ const nextListItemIndex = lines.slice(i + 1).findIndex(nextLine => {
35
+ const nextTrimmed = nextLine.trim();
36
+ return nextTrimmed && (/^\d+\.\s+/.test(nextTrimmed) || /^[-*]\s+/.test(nextTrimmed));
37
+ });
38
+ // If we're in a list and the next item is also a list item, continue the list
39
+ if ((inOrderedList || inUnorderedList) && nextListItemIndex !== -1) {
40
+ continue; // Skip this empty line but keep the list open
41
+ }
42
+ // Close any current lists before starting new paragraph
43
+ if (inOrderedList) {
44
+ processedLines.push('</ol>');
45
+ inOrderedList = false;
46
+ }
47
+ if (inUnorderedList) {
48
+ processedLines.push('</ul>');
49
+ inUnorderedList = false;
50
+ }
51
+ // Process accumulated paragraph
52
+ if (currentParagraph.length > 0) {
53
+ const paragraphContent = currentParagraph.join('<br>');
54
+ processedLines.push(`<p>${paragraphContent}</p>`);
55
+ currentParagraph = [];
56
+ }
57
+ // Skip extra empty lines
58
+ continue;
59
+ }
60
+ // Check for numbered list items
61
+ if (/^\d+\.\s+/.test(trimmedLine)) {
62
+ // Finish any current paragraph
63
+ if (currentParagraph.length > 0) {
64
+ const paragraphContent = currentParagraph.join('<br>');
65
+ processedLines.push(`<p>${paragraphContent}</p>`);
66
+ currentParagraph = [];
67
+ }
68
+ if (!inOrderedList) {
69
+ if (inUnorderedList) {
70
+ processedLines.push('</ul>');
71
+ inUnorderedList = false;
72
+ }
73
+ processedLines.push('<ol>');
74
+ inOrderedList = true;
75
+ }
76
+ const content = trimmedLine.replace(/^\d+\.\s+/, '');
77
+ processedLines.push(`<li>${content}</li>`);
78
+ }
79
+ // Check for bullet list items
80
+ else if (/^[-*]\s+/.test(trimmedLine)) {
81
+ // Finish any current paragraph
82
+ if (currentParagraph.length > 0) {
83
+ const paragraphContent = currentParagraph.join('<br>');
84
+ processedLines.push(`<p>${paragraphContent}</p>`);
85
+ currentParagraph = [];
86
+ }
87
+ if (!inUnorderedList) {
88
+ if (inOrderedList) {
89
+ processedLines.push('</ol>');
90
+ inOrderedList = false;
91
+ }
92
+ processedLines.push('<ul>');
93
+ inUnorderedList = true;
94
+ }
95
+ const content = trimmedLine.replace(/^[-*]\s+/, '');
96
+ processedLines.push(`<li>${content}</li>`);
97
+ }
98
+ // Regular line
99
+ else {
100
+ // Close any lists
101
+ if (inOrderedList) {
102
+ processedLines.push('</ol>');
103
+ inOrderedList = false;
104
+ }
105
+ if (inUnorderedList) {
106
+ processedLines.push('</ul>');
107
+ inUnorderedList = false;
108
+ }
109
+ // Add to current paragraph
110
+ currentParagraph.push(trimmedLine);
111
+ }
112
+ }
113
+ // Close any remaining lists
114
+ if (inOrderedList)
115
+ processedLines.push('</ol>');
116
+ if (inUnorderedList)
117
+ processedLines.push('</ul>');
118
+ // Process any remaining paragraph
119
+ if (currentParagraph.length > 0) {
120
+ const paragraphContent = currentParagraph.join('<br>');
121
+ processedLines.push(`<p>${paragraphContent}</p>`);
122
+ }
123
+ return processedLines.join('\n\n');
124
+ }
9
125
  async request(path, method = 'GET', body) {
10
126
  const url = `${this.baseUrl}/api${path}`;
11
127
  const headers = {
@@ -43,7 +159,9 @@ export class FreeScoutAPI {
43
159
  return this.request(`/conversations/${ticketId}/threads`, 'POST', body);
44
160
  }
45
161
  async createDraftReply(ticketId, text, userId) {
46
- return this.addThread(ticketId, 'message', text, userId, 'draft');
162
+ // Convert Markdown formatting to HTML for proper display in FreeScout
163
+ const htmlText = this.markdownToHtml(text);
164
+ return this.addThread(ticketId, 'message', htmlText, userId, 'draft');
47
165
  }
48
166
  async updateConversation(ticketId, updates) {
49
167
  return this.request(`/conversations/${ticketId}`, 'PUT', updates);
@@ -1 +1 @@
1
- {"version":3,"file":"freescout-api.js","sourceRoot":"","sources":["../src/freescout-api.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"freescout-api.js","sourceRoot":"","sources":["../src/freescout-api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,YAAY,CAAC;AAO/B,MAAM,OAAO,YAAY;IACf,OAAO,CAAS;IAChB,MAAM,CAAS;IAEvB,YAAY,OAAe,EAAE,MAAc;QACzC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,wBAAwB;QACnE,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED;;OAEG;IACK,cAAc,CAAC,IAAY;QACjC,mEAAmE;QACnE,IAAI,IAAI,GAAG,IAAI;aACZ,OAAO,CAAC,gBAAgB,EAAE,qBAAqB,CAAC;aAChD,OAAO,CAAC,YAAY,EAAE,qBAAqB,CAAC,CAAC;QAEhD,qFAAqF;QACrF,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,2BAA2B,EAAE,aAAa,CAAC,CAAC;QAChE,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,uBAAuB,EAAE,aAAa,CAAC,CAAC;QAE5D,4CAA4C;QAC5C,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,iBAAiB,CAAC,CAAC;QAEnD,qEAAqE;QACrE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC/B,MAAM,cAAc,GAAG,EAAE,CAAC;QAC1B,IAAI,aAAa,GAAG,KAAK,CAAC;QAC1B,IAAI,eAAe,GAAG,KAAK,CAAC;QAC5B,IAAI,gBAAgB,GAAG,EAAE,CAAC;QAE1B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACtB,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;YAEhC,2CAA2C;YAC3C,IAAI,CAAC,WAAW,EAAE,CAAC;gBACjB,uDAAuD;gBACvD,MAAM,iBAAiB,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE;oBAChE,MAAM,WAAW,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC;oBACpC,OAAO,WAAW,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC;gBACxF,CAAC,CAAC,CAAC;gBAEH,8EAA8E;gBAC9E,IAAI,CAAC,aAAa,IAAI,eAAe,CAAC,IAAI,iBAAiB,KAAK,CAAC,CAAC,EAAE,CAAC;oBACnE,SAAS,CAAC,8CAA8C;gBAC1D,CAAC;gBAED,wDAAwD;gBACxD,IAAI,aAAa,EAAE,CAAC;oBAClB,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;oBAC7B,aAAa,GAAG,KAAK,CAAC;gBACxB,CAAC;gBACD,IAAI,eAAe,EAAE,CAAC;oBACpB,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;oBAC7B,eAAe,GAAG,KAAK,CAAC;gBAC1B,CAAC;gBAED,gCAAgC;gBAChC,IAAI,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBAChC,MAAM,gBAAgB,GAAG,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;oBACvD,cAAc,CAAC,IAAI,CAAC,MAAM,gBAAgB,MAAM,CAAC,CAAC;oBAClD,gBAAgB,GAAG,EAAE,CAAC;gBACxB,CAAC;gBAED,yBAAyB;gBACzB,SAAS;YACX,CAAC;YAED,gCAAgC;YAChC,IAAI,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;gBAClC,+BAA+B;gBAC/B,IAAI,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBAChC,MAAM,gBAAgB,GAAG,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;oBACvD,cAAc,CAAC,IAAI,CAAC,MAAM,gBAAgB,MAAM,CAAC,CAAC;oBAClD,gBAAgB,GAAG,EAAE,CAAC;gBACxB,CAAC;gBAED,IAAI,CAAC,aAAa,EAAE,CAAC;oBACnB,IAAI,eAAe,EAAE,CAAC;wBACpB,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;wBAC7B,eAAe,GAAG,KAAK,CAAC;oBAC1B,CAAC;oBACD,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;oBAC5B,aAAa,GAAG,IAAI,CAAC;gBACvB,CAAC;gBACD,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;gBACrD,cAAc,CAAC,IAAI,CAAC,OAAO,OAAO,OAAO,CAAC,CAAC;YAC7C,CAAC;YACD,8BAA8B;iBACzB,IAAI,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;gBACtC,+BAA+B;gBAC/B,IAAI,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBAChC,MAAM,gBAAgB,GAAG,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;oBACvD,cAAc,CAAC,IAAI,CAAC,MAAM,gBAAgB,MAAM,CAAC,CAAC;oBAClD,gBAAgB,GAAG,EAAE,CAAC;gBACxB,CAAC;gBAED,IAAI,CAAC,eAAe,EAAE,CAAC;oBACrB,IAAI,aAAa,EAAE,CAAC;wBAClB,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;wBAC7B,aAAa,GAAG,KAAK,CAAC;oBACxB,CAAC;oBACD,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;oBAC5B,eAAe,GAAG,IAAI,CAAC;gBACzB,CAAC;gBACD,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;gBACpD,cAAc,CAAC,IAAI,CAAC,OAAO,OAAO,OAAO,CAAC,CAAC;YAC7C,CAAC;YACD,eAAe;iBACV,CAAC;gBACJ,kBAAkB;gBAClB,IAAI,aAAa,EAAE,CAAC;oBAClB,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;oBAC7B,aAAa,GAAG,KAAK,CAAC;gBACxB,CAAC;gBACD,IAAI,eAAe,EAAE,CAAC;oBACpB,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;oBAC7B,eAAe,GAAG,KAAK,CAAC;gBAC1B,CAAC;gBAED,2BAA2B;gBAC3B,gBAAgB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YACrC,CAAC;QACH,CAAC;QAED,4BAA4B;QAC5B,IAAI,aAAa;YAAE,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAChD,IAAI,eAAe;YAAE,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAElD,kCAAkC;QAClC,IAAI,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAChC,MAAM,gBAAgB,GAAG,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACvD,cAAc,CAAC,IAAI,CAAC,MAAM,gBAAgB,MAAM,CAAC,CAAC;QACpD,CAAC;QAED,OAAO,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACrC,CAAC;IAEO,KAAK,CAAC,OAAO,CACnB,IAAY,EACZ,SAAiB,KAAK,EACtB,IAAU;QAEV,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,OAAO,OAAO,IAAI,EAAE,CAAC;QAEzC,MAAM,OAAO,GAA2B;YACtC,qBAAqB,EAAE,IAAI,CAAC,MAAM;SACnC,CAAC;QAEF,IAAI,IAAI,EAAE,CAAC;YACT,OAAO,CAAC,cAAc,CAAC,GAAG,kBAAkB,CAAC;QAC/C,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAChC,MAAM;YACN,OAAO;YACP,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;SAC9C,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YACxC,MAAM,IAAI,KAAK,CAAC,wBAAwB,QAAQ,CAAC,MAAM,MAAM,SAAS,EAAE,CAAC,CAAC;QAC5E,CAAC;QAED,OAAO,QAAQ,CAAC,IAAI,EAAgB,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,eAAe,CACnB,QAAgB,EAChB,iBAA0B,IAAI;QAE9B,MAAM,KAAK,GAAG,cAAc,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC;QACrD,OAAO,IAAI,CAAC,OAAO,CACjB,kBAAkB,QAAQ,GAAG,KAAK,EAAE,CACrC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,SAAS,CACb,QAAgB,EAChB,IAAqC,EACrC,IAAY,EACZ,MAAe,EACf,KAA6B;QAE7B,MAAM,IAAI,GAAQ;YAChB,IAAI;YACJ,IAAI;SACL,CAAC;QAEF,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC;QACrB,CAAC;QAED,IAAI,KAAK,EAAE,CAAC;YACV,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACrB,CAAC;QAED,OAAO,IAAI,CAAC,OAAO,CACjB,kBAAkB,QAAQ,UAAU,EACpC,MAAM,EACN,IAAI,CACL,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,gBAAgB,CACpB,QAAgB,EAChB,IAAY,EACZ,MAAc;QAEd,sEAAsE;QACtE,MAAM,QAAQ,GAAG,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;QAC3C,OAAO,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IACxE,CAAC;IAED,KAAK,CAAC,kBAAkB,CACtB,QAAgB,EAChB,OAIC;QAED,OAAO,IAAI,CAAC,OAAO,CACjB,kBAAkB,QAAQ,EAAE,EAC5B,KAAK,EACL,OAAO,CACR,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,mBAAmB,CACvB,KAAa,EACb,MAAe;QAEf,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;QACrC,IAAI,KAAK;YAAE,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACzC,IAAI,MAAM;YAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAE5C,OAAO,IAAI,CAAC,OAAO,CACjB,kBAAkB,MAAM,CAAC,QAAQ,EAAE,EAAE,CACtC,CAAC;IACJ,CAAC;IAED,sBAAsB,CAAC,GAAW;QAChC,uBAAuB;QACvB,wCAAwC;QACxC,yCAAyC;QACzC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC;QACjD,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACjC,CAAC;IAED,gBAAgB,CAAC,KAAa;QAC5B,0BAA0B;QAC1B,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3B,MAAM,QAAQ,GAAG,IAAI,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC;YACpD,IAAI,QAAQ;gBAAE,OAAO,QAAQ,CAAC;QAChC,CAAC;QAED,wCAAwC;QACxC,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;YAC/B,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC;QACtB,CAAC;QAED,0DAA0D;QAC1D,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QACrC,IAAI,KAAK,EAAE,CAAC;YACV,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,MAAM,IAAI,KAAK,CAAC,2CAA2C,KAAK,EAAE,CAAC,CAAC;IACtE,CAAC;CACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@verygoodplugins/mcp-freescout",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "MCP server for FreeScout ticket management and workflow automation",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -0,0 +1,150 @@
1
+ // Quick test of the markdown converter
2
+ const testInput = `Hi Victor,
3
+
4
+ Thanks for helping us identify this issue! Based on your experience with the EngageBay LiveChat plugin conflict, we've implemented a solution to prevent this confusion for future users.
5
+
6
+ **What we've added:**
7
+
8
+ 1. **Proactive Warning System** - When users have conflicting plugins installed (like EngageBay LiveChat, LeadConnector, or GHL Connect), WP Fusion will now display a warning message before they attempt OAuth authorization, letting them know these plugins may interfere with the connection process.
9
+
10
+ 2. **Enhanced Error Messages** - If the \`invalid_grant\` error does occur, users will now receive specific guidance about potential plugin conflicts rather than the cryptic error message you experienced.
11
+
12
+ This improvement will be included in the next WP Fusion update and should help other users avoid the frustration you encountered. The system will specifically detect the EngageBay LiveChat plugin (and others we discover) and provide clear guidance on temporarily disabling them during the OAuth setup process.
13
+
14
+ Thanks again for your patience in working through this issue and helping us identify the root cause. Your feedback directly led to this improvement that will benefit all WP Fusion users integrating with HighLevel!
15
+
16
+ Best regards,
17
+ Jack`;
18
+
19
+ function markdownToHtml(text) {
20
+ // Convert bold text: **text** or __text__ -> <strong>text</strong>
21
+ let html = text
22
+ .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
23
+ .replace(/__(.*?)__/g, '<strong>$1</strong>');
24
+
25
+ // Convert italic text: *text* or _text_ -> <em>text</em> (avoid conflicts with bold)
26
+ html = html.replace(/(?<!\*)\*([^*]+)\*(?!\*)/g, '<em>$1</em>');
27
+ html = html.replace(/(?<!_)_([^_]+)_(?!_)/g, '<em>$1</em>');
28
+
29
+ // Convert code: `text` -> <code>text</code>
30
+ html = html.replace(/`(.*?)`/g, '<code>$1</code>');
31
+
32
+ // Process the entire text to handle lists that span paragraph breaks
33
+ const lines = html.split('\n');
34
+ const processedLines = [];
35
+ let inOrderedList = false;
36
+ let inUnorderedList = false;
37
+ let currentParagraph = [];
38
+
39
+ for (let i = 0; i < lines.length; i++) {
40
+ const line = lines[i];
41
+ const trimmedLine = line.trim();
42
+
43
+ // Check for empty lines (paragraph breaks)
44
+ if (!trimmedLine) {
45
+ // Check if the next non-empty line is also a list item
46
+ const nextListItemIndex = lines.slice(i + 1).findIndex(nextLine => {
47
+ const nextTrimmed = nextLine.trim();
48
+ return nextTrimmed && (/^\d+\.\s+/.test(nextTrimmed) || /^[-*]\s+/.test(nextTrimmed));
49
+ });
50
+
51
+ // If we're in a list and the next item is also a list item, continue the list
52
+ if ((inOrderedList || inUnorderedList) && nextListItemIndex !== -1) {
53
+ continue; // Skip this empty line but keep the list open
54
+ }
55
+
56
+ // Close any current lists before starting new paragraph
57
+ if (inOrderedList) {
58
+ processedLines.push('</ol>');
59
+ inOrderedList = false;
60
+ }
61
+ if (inUnorderedList) {
62
+ processedLines.push('</ul>');
63
+ inUnorderedList = false;
64
+ }
65
+
66
+ // Process accumulated paragraph
67
+ if (currentParagraph.length > 0) {
68
+ const paragraphContent = currentParagraph.join('<br>');
69
+ processedLines.push(`<p>${paragraphContent}</p>`);
70
+ currentParagraph = [];
71
+ }
72
+
73
+ // Skip extra empty lines
74
+ continue;
75
+ }
76
+
77
+ // Check for numbered list items
78
+ if (/^\d+\.\s+/.test(trimmedLine)) {
79
+ // Finish any current paragraph
80
+ if (currentParagraph.length > 0) {
81
+ const paragraphContent = currentParagraph.join('<br>');
82
+ processedLines.push(`<p>${paragraphContent}</p>`);
83
+ currentParagraph = [];
84
+ }
85
+
86
+ if (!inOrderedList) {
87
+ if (inUnorderedList) {
88
+ processedLines.push('</ul>');
89
+ inUnorderedList = false;
90
+ }
91
+ processedLines.push('<ol>');
92
+ inOrderedList = true;
93
+ }
94
+ const content = trimmedLine.replace(/^\d+\.\s+/, '');
95
+ processedLines.push(`<li>${content}</li>`);
96
+ }
97
+ // Check for bullet list items
98
+ else if (/^[-*]\s+/.test(trimmedLine)) {
99
+ // Finish any current paragraph
100
+ if (currentParagraph.length > 0) {
101
+ const paragraphContent = currentParagraph.join('<br>');
102
+ processedLines.push(`<p>${paragraphContent}</p>`);
103
+ currentParagraph = [];
104
+ }
105
+
106
+ if (!inUnorderedList) {
107
+ if (inOrderedList) {
108
+ processedLines.push('</ol>');
109
+ inOrderedList = false;
110
+ }
111
+ processedLines.push('<ul>');
112
+ inUnorderedList = true;
113
+ }
114
+ const content = trimmedLine.replace(/^[-*]\s+/, '');
115
+ processedLines.push(`<li>${content}</li>`);
116
+ }
117
+ // Regular line
118
+ else {
119
+ // Close any lists
120
+ if (inOrderedList) {
121
+ processedLines.push('</ol>');
122
+ inOrderedList = false;
123
+ }
124
+ if (inUnorderedList) {
125
+ processedLines.push('</ul>');
126
+ inUnorderedList = false;
127
+ }
128
+
129
+ // Add to current paragraph
130
+ currentParagraph.push(trimmedLine);
131
+ }
132
+ }
133
+
134
+ // Close any remaining lists
135
+ if (inOrderedList) processedLines.push('</ol>');
136
+ if (inUnorderedList) processedLines.push('</ul>');
137
+
138
+ // Process any remaining paragraph
139
+ if (currentParagraph.length > 0) {
140
+ const paragraphContent = currentParagraph.join('<br>');
141
+ processedLines.push(`<p>${paragraphContent}</p>`);
142
+ }
143
+
144
+ return processedLines.join('\n\n');
145
+ }
146
+
147
+ console.log('=== INPUT ===');
148
+ console.log(testInput);
149
+ console.log('\n=== OUTPUT ===');
150
+ console.log(markdownToHtml(testInput));