sonance-brand-mcp 1.3.75 → 1.3.76
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sonance-brand-mcp",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.76",
|
|
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",
|