sonance-brand-mcp 1.3.75 → 1.3.77

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.
@@ -91,6 +91,39 @@ function debugLog(message: string, data?: unknown) {
91
91
  }
92
92
  }
93
93
 
94
+ /**
95
+ * Extract JSON from LLM response that may contain preamble text
96
+ * Handles: pure JSON, markdown code fences, and text with embedded JSON
97
+ */
98
+ function extractJsonFromResponse(text: string): string {
99
+ // Try direct parse first - if it starts with {, it's likely pure JSON
100
+ const trimmed = text.trim();
101
+ if (trimmed.startsWith('{')) return trimmed;
102
+
103
+ // Extract from markdown code fence (```json ... ``` or ``` ... ```)
104
+ const fenceMatch = text.match(/```(?:json)?\s*([\s\S]*?)```/);
105
+ if (fenceMatch) {
106
+ const extracted = fenceMatch[1].trim();
107
+ debugLog("Extracted JSON from markdown fence", { previewLength: extracted.length });
108
+ return extracted;
109
+ }
110
+
111
+ // Find the JSON object in the text (for responses with preamble)
112
+ const jsonStart = text.indexOf('{');
113
+ const jsonEnd = text.lastIndexOf('}');
114
+ if (jsonStart !== -1 && jsonEnd > jsonStart) {
115
+ const extracted = text.slice(jsonStart, jsonEnd + 1);
116
+ debugLog("Extracted JSON from preamble text", {
117
+ preambleLength: jsonStart,
118
+ jsonLength: extracted.length
119
+ });
120
+ return extracted;
121
+ }
122
+
123
+ // Return as-is if no JSON structure found
124
+ return text;
125
+ }
126
+
94
127
  /**
95
128
  * AST-based syntax validation using Babel parser
96
129
  * This catches actual syntax errors, not just tag counting
@@ -748,7 +781,10 @@ function searchFilesSmart(
748
781
 
749
782
  const VISION_SYSTEM_PROMPT = `You are an expert frontend developer. Edit the code to fulfill the user's request.
750
783
 
751
- Output ONLY this JSON (no other text):
784
+ CRITICAL: Return ONLY the JSON object. No explanations, no preamble, no markdown code fences.
785
+ Start your response with { and end with }
786
+
787
+ Output format:
752
788
  {"modifications":[{"filePath":"path","patches":[{"search":"exact original code","replace":"modified code"}]}]}
753
789
 
754
790
  The "search" field must match the file EXACTLY (copy-paste from the code provided).`;
@@ -1295,62 +1331,8 @@ This is better than generating patches with made-up code.`,
1295
1331
  };
1296
1332
 
1297
1333
  try {
1298
- let jsonText = textResponse.text.trim();
1299
-
1300
- // Try to extract JSON from markdown code blocks
1301
- const jsonMatch = jsonText.match(/```json\n([\s\S]*?)\n```/) ||
1302
- jsonText.match(/```\n([\s\S]*?)\n```/);
1303
-
1304
- if (jsonMatch) {
1305
- jsonText = jsonMatch[1];
1306
- } else if (jsonText.includes("```json")) {
1307
- const start = jsonText.indexOf("```json") + 7;
1308
- const end = jsonText.lastIndexOf("```");
1309
- if (end > start) {
1310
- jsonText = jsonText.substring(start, end);
1311
- }
1312
- }
1313
-
1314
- jsonText = jsonText.trim();
1315
-
1316
- // Robust JSON extraction: look for {"modifications" pattern specifically
1317
- // This handles cases where the LLM includes preamble text with code blocks
1318
- const jsonStartPatterns = ['{"modifications"', '{ "modifications"', '{\n "modifications"'];
1319
- let jsonStart = -1;
1320
-
1321
- for (const pattern of jsonStartPatterns) {
1322
- const idx = jsonText.indexOf(pattern);
1323
- if (idx !== -1 && (jsonStart === -1 || idx < jsonStart)) {
1324
- jsonStart = idx;
1325
- }
1326
- }
1327
-
1328
- if (jsonStart !== -1) {
1329
- // Find the matching closing brace by counting braces
1330
- let braceCount = 0;
1331
- let jsonEnd = -1;
1332
- for (let i = jsonStart; i < jsonText.length; i++) {
1333
- if (jsonText[i] === '{') braceCount++;
1334
- if (jsonText[i] === '}') {
1335
- braceCount--;
1336
- if (braceCount === 0) {
1337
- jsonEnd = i;
1338
- break;
1339
- }
1340
- }
1341
- }
1342
- if (jsonEnd !== -1) {
1343
- jsonText = jsonText.substring(jsonStart, jsonEnd + 1);
1344
- }
1345
- } else {
1346
- // Fallback: try first { to last }
1347
- const firstBrace = jsonText.indexOf('{');
1348
- const lastBrace = jsonText.lastIndexOf('}');
1349
- if (firstBrace !== -1 && lastBrace > firstBrace) {
1350
- jsonText = jsonText.substring(firstBrace, lastBrace + 1);
1351
- }
1352
- }
1353
-
1334
+ // Use robust JSON extraction that handles preamble text, code fences, etc.
1335
+ const jsonText = extractJsonFromResponse(textResponse.text);
1354
1336
  aiResponse = JSON.parse(jsonText);
1355
1337
  } catch {
1356
1338
  console.error("Failed to parse AI response:", textResponse.text);
@@ -87,6 +87,39 @@ function debugLog(message: string, data?: unknown) {
87
87
  }
88
88
  }
89
89
 
90
+ /**
91
+ * Extract JSON from LLM response that may contain preamble text
92
+ * Handles: pure JSON, markdown code fences, and text with embedded JSON
93
+ */
94
+ function extractJsonFromResponse(text: string): string {
95
+ // Try direct parse first - if it starts with {, it's likely pure JSON
96
+ const trimmed = text.trim();
97
+ if (trimmed.startsWith('{')) return trimmed;
98
+
99
+ // Extract from markdown code fence (```json ... ``` or ``` ... ```)
100
+ const fenceMatch = text.match(/```(?:json)?\s*([\s\S]*?)```/);
101
+ if (fenceMatch) {
102
+ const extracted = fenceMatch[1].trim();
103
+ debugLog("Extracted JSON from markdown fence", { previewLength: extracted.length });
104
+ return extracted;
105
+ }
106
+
107
+ // Find the JSON object in the text (for responses with preamble)
108
+ const jsonStart = text.indexOf('{');
109
+ const jsonEnd = text.lastIndexOf('}');
110
+ if (jsonStart !== -1 && jsonEnd > jsonStart) {
111
+ const extracted = text.slice(jsonStart, jsonEnd + 1);
112
+ debugLog("Extracted JSON from preamble text", {
113
+ preambleLength: jsonStart,
114
+ jsonLength: extracted.length
115
+ });
116
+ return extracted;
117
+ }
118
+
119
+ // Return as-is if no JSON structure found
120
+ return text;
121
+ }
122
+
90
123
  /**
91
124
  * AST-based syntax validation using Babel parser
92
125
  * This catches actual syntax errors, not just tag counting
@@ -744,7 +777,10 @@ function searchFilesSmart(
744
777
 
745
778
  const VISION_SYSTEM_PROMPT = `You are an expert frontend developer. Edit the code to fulfill the user's request.
746
779
 
747
- Output ONLY this JSON (no other text):
780
+ CRITICAL: Return ONLY the JSON object. No explanations, no preamble, no markdown code fences.
781
+ Start your response with { and end with }
782
+
783
+ Output format:
748
784
  {"modifications":[{"filePath":"path","patches":[{"search":"exact original code","replace":"modified code"}]}]}
749
785
 
750
786
  The "search" field must match the file EXACTLY (copy-paste from the code provided).`;
@@ -1260,64 +1296,8 @@ This is better than generating patches with made-up code.`,
1260
1296
  };
1261
1297
 
1262
1298
  try {
1263
- let jsonText = textResponse.text.trim();
1264
-
1265
- // Try to extract JSON from markdown code blocks
1266
- const jsonMatch = jsonText.match(/```json\n([\s\S]*?)\n```/) ||
1267
- jsonText.match(/```\n([\s\S]*?)\n```/);
1268
-
1269
- if (jsonMatch) {
1270
- jsonText = jsonMatch[1];
1271
- } else if (jsonText.includes("```json")) {
1272
- // Fallback for cases where regex might miss due to newlines
1273
- const start = jsonText.indexOf("```json") + 7;
1274
- const end = jsonText.lastIndexOf("```");
1275
- if (end > start) {
1276
- jsonText = jsonText.substring(start, end);
1277
- }
1278
- }
1279
-
1280
- // Clean up any remaining whitespace
1281
- jsonText = jsonText.trim();
1282
-
1283
- // Robust JSON extraction: look for {"modifications" pattern specifically
1284
- // This handles cases where the LLM includes preamble text with code blocks
1285
- const jsonStartPatterns = ['{"modifications"', '{ "modifications"', '{\n "modifications"'];
1286
- let jsonStart = -1;
1287
-
1288
- for (const pattern of jsonStartPatterns) {
1289
- const idx = jsonText.indexOf(pattern);
1290
- if (idx !== -1 && (jsonStart === -1 || idx < jsonStart)) {
1291
- jsonStart = idx;
1292
- }
1293
- }
1294
-
1295
- if (jsonStart !== -1) {
1296
- // Find the matching closing brace by counting braces
1297
- let braceCount = 0;
1298
- let jsonEnd = -1;
1299
- for (let i = jsonStart; i < jsonText.length; i++) {
1300
- if (jsonText[i] === '{') braceCount++;
1301
- if (jsonText[i] === '}') {
1302
- braceCount--;
1303
- if (braceCount === 0) {
1304
- jsonEnd = i;
1305
- break;
1306
- }
1307
- }
1308
- }
1309
- if (jsonEnd !== -1) {
1310
- jsonText = jsonText.substring(jsonStart, jsonEnd + 1);
1311
- }
1312
- } else {
1313
- // Fallback: try first { to last }
1314
- const firstBrace = jsonText.indexOf('{');
1315
- const lastBrace = jsonText.lastIndexOf('}');
1316
- if (firstBrace !== -1 && lastBrace > firstBrace) {
1317
- jsonText = jsonText.substring(firstBrace, lastBrace + 1);
1318
- }
1319
- }
1320
-
1299
+ // Use robust JSON extraction that handles preamble text, code fences, etc.
1300
+ const jsonText = extractJsonFromResponse(textResponse.text);
1321
1301
  aiResponse = JSON.parse(jsonText);
1322
1302
  } catch {
1323
1303
  console.error("Failed to parse AI response:", textResponse.text);
@@ -2384,7 +2384,7 @@ export function SonanceDevTools() {
2384
2384
  <button
2385
2385
  onClick={() => setIsOpen(true)}
2386
2386
  className={cn(
2387
- "fixed bottom-6 right-6 z-[9998]",
2387
+ "fixed bottom-6 right-6 z-[2147483646]",
2388
2388
  "flex h-14 w-14 items-center justify-center",
2389
2389
  "rounded-full bg-[#333F48] text-white shadow-lg",
2390
2390
  "hover:scale-105 hover:shadow-xl",
@@ -2403,7 +2403,7 @@ export function SonanceDevTools() {
2403
2403
  ref={panelRef}
2404
2404
  data-sonance-devtools="true"
2405
2405
  className={cn(
2406
- "fixed bottom-6 right-6 z-[9999]",
2406
+ "fixed bottom-6 right-6 z-[2147483647]",
2407
2407
  "w-[360px] max-h-[80vh]",
2408
2408
  "bg-white rounded-lg shadow-2xl border border-gray-200",
2409
2409
  "flex flex-col overflow-hidden",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sonance-brand-mcp",
3
- "version": "1.3.75",
3
+ "version": "1.3.77",
4
4
  "description": "MCP Server for Sonance Brand Guidelines and Component Library - gives Claude instant access to brand colors, typography, and UI components.",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",