sonance-brand-mcp 1.3.41 → 1.3.42
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.
|
@@ -589,6 +589,27 @@ export async function POST(request: Request) {
|
|
|
589
589
|
});
|
|
590
590
|
}
|
|
591
591
|
|
|
592
|
+
// ========== SMART CONTEXT BUDGETING ==========
|
|
593
|
+
// Total budget: 100k chars (~25k tokens, safe for Claude)
|
|
594
|
+
// Priority: Recommended file (full) > Page file (limited) > Other components (dynamic)
|
|
595
|
+
const TOTAL_CONTEXT_BUDGET = 100000;
|
|
596
|
+
const MAX_RECOMMENDED_FILE = 50000; // 50k chars max for recommended file
|
|
597
|
+
const MAX_PAGE_FILE = 2000; // Page file is just a wrapper
|
|
598
|
+
const MAX_GLOBALS_CSS = 1500;
|
|
599
|
+
const MAX_FILES = 25;
|
|
600
|
+
|
|
601
|
+
let usedContext = 0;
|
|
602
|
+
|
|
603
|
+
// Extract recommended file from component sources (to show first, avoid duplication)
|
|
604
|
+
let recommendedFileContent: { path: string; content: string } | null = null;
|
|
605
|
+
if (recommendedFile) {
|
|
606
|
+
const idx = pageContext.componentSources.findIndex(c => c.path === recommendedFile.path);
|
|
607
|
+
if (idx !== -1) {
|
|
608
|
+
recommendedFileContent = pageContext.componentSources[idx];
|
|
609
|
+
pageContext.componentSources.splice(idx, 1); // Remove to avoid duplication
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
592
613
|
// Build text content
|
|
593
614
|
let textContent = `VISION MODE EDIT REQUEST
|
|
594
615
|
|
|
@@ -604,41 +625,63 @@ ${focusedElements.map((el) => `- ${el.name} (${el.type}) at (${el.coordinates.x}
|
|
|
604
625
|
`;
|
|
605
626
|
}
|
|
606
627
|
|
|
607
|
-
|
|
628
|
+
// ========== TARGET COMPONENT (RECOMMENDED FILE) - SHOWN FIRST ==========
|
|
629
|
+
if (recommendedFileContent) {
|
|
630
|
+
const content = recommendedFileContent.content.length > MAX_RECOMMENDED_FILE
|
|
631
|
+
? recommendedFileContent.content.substring(0, MAX_RECOMMENDED_FILE) + "\n// ... (file truncated at 50k chars, showing component definition and main logic)"
|
|
632
|
+
: recommendedFileContent.content;
|
|
633
|
+
|
|
634
|
+
textContent += `═══════════════════════════════════════════════════════════════════════════════
|
|
635
|
+
⚡ TARGET COMPONENT - YOU MUST EDIT THIS FILE
|
|
636
|
+
═══════════════════════════════════════════════════════════════════════════════
|
|
637
|
+
|
|
638
|
+
This is the component that renders the UI you see in the screenshot.
|
|
639
|
+
File: ${recommendedFileContent.path}
|
|
640
|
+
|
|
641
|
+
\`\`\`tsx
|
|
642
|
+
${content}
|
|
643
|
+
\`\`\`
|
|
644
|
+
|
|
645
|
+
`;
|
|
646
|
+
usedContext += content.length;
|
|
647
|
+
debugLog("Added TARGET COMPONENT to context", {
|
|
648
|
+
path: recommendedFileContent.path,
|
|
649
|
+
originalSize: recommendedFileContent.content.length,
|
|
650
|
+
includedSize: content.length
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// ========== PAGE CONTEXT (wrapper - de-emphasized) ==========
|
|
655
|
+
const pageContentTruncated = pageContext.pageContent.substring(0, MAX_PAGE_FILE);
|
|
656
|
+
const pageWasTruncated = pageContext.pageContent.length > MAX_PAGE_FILE;
|
|
657
|
+
|
|
658
|
+
textContent += `PAGE CONTEXT (wrapper only${recommendedFileContent ? " - DO NOT edit this, edit the TARGET COMPONENT above" : ""}):
|
|
608
659
|
|
|
609
660
|
Page File: ${pageContext.pageFile || "Not found"}
|
|
610
|
-
${pageContext.pageContent ? `\`\`\`tsx\n${
|
|
661
|
+
${pageContext.pageContent ? `\`\`\`tsx\n${pageContentTruncated}${pageWasTruncated ? "\n// ... (wrapper truncated)" : ""}\n\`\`\`` : ""}
|
|
611
662
|
|
|
612
663
|
`;
|
|
664
|
+
usedContext += pageContentTruncated.length;
|
|
613
665
|
|
|
666
|
+
// ========== SUPPORTING COMPONENTS (dynamic budget) ==========
|
|
614
667
|
if (pageContext.componentSources.length > 0) {
|
|
615
|
-
|
|
616
|
-
const
|
|
617
|
-
const
|
|
618
|
-
const MAX_PER_FILE_SECONDARY = 1500; // Remaining files get less
|
|
619
|
-
const MAX_FILES = 30; // Limit total number of files
|
|
668
|
+
const remainingBudget = TOTAL_CONTEXT_BUDGET - usedContext - MAX_GLOBALS_CSS - 5000; // Reserve 5k for instructions
|
|
669
|
+
const filesToInclude = pageContext.componentSources.slice(0, MAX_FILES);
|
|
670
|
+
const perFileLimit = Math.max(1000, Math.floor(remainingBudget / Math.max(filesToInclude.length, 1)));
|
|
620
671
|
|
|
621
|
-
|
|
622
|
-
const truncatedComponents = pageContext.componentSources.slice(0, MAX_FILES);
|
|
672
|
+
textContent += `SUPPORTING COMPONENTS (${filesToInclude.length} files, ~${Math.round(perFileLimit/1000)}k chars each):\n`;
|
|
623
673
|
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
const comp = truncatedComponents[i];
|
|
628
|
-
const isPriority = i < 10; // First 10 files are priority (direct imports)
|
|
629
|
-
const maxSize = isPriority ? MAX_PER_FILE_PRIORITY : MAX_PER_FILE_SECONDARY;
|
|
630
|
-
|
|
631
|
-
// Stop if we've used too much context
|
|
632
|
-
if (usedContext > MAX_TOTAL_CONTEXT) {
|
|
633
|
-
textContent += `\n// ... (${truncatedComponents.length - i} more files omitted to stay within context limits)\n`;
|
|
674
|
+
for (const comp of filesToInclude) {
|
|
675
|
+
if (usedContext > TOTAL_CONTEXT_BUDGET - 10000) {
|
|
676
|
+
textContent += `\n// ... (remaining files omitted to stay within context limits)\n`;
|
|
634
677
|
break;
|
|
635
678
|
}
|
|
636
679
|
|
|
637
|
-
const truncatedContent = comp.content.substring(0,
|
|
638
|
-
const wasTruncated = comp.content.length >
|
|
680
|
+
const truncatedContent = comp.content.substring(0, perFileLimit);
|
|
681
|
+
const wasTruncated = comp.content.length > perFileLimit;
|
|
639
682
|
|
|
640
683
|
textContent += `
|
|
641
|
-
File: ${comp.path}
|
|
684
|
+
File: ${comp.path}
|
|
642
685
|
\`\`\`tsx
|
|
643
686
|
${truncatedContent}${wasTruncated ? "\n// ... (truncated)" : ""}
|
|
644
687
|
\`\`\`
|
|
@@ -647,42 +690,28 @@ ${truncatedContent}${wasTruncated ? "\n// ... (truncated)" : ""}
|
|
|
647
690
|
}
|
|
648
691
|
}
|
|
649
692
|
|
|
693
|
+
// ========== GLOBALS CSS ==========
|
|
694
|
+
const globalsTruncated = pageContext.globalsCSS.substring(0, MAX_GLOBALS_CSS);
|
|
650
695
|
textContent += `
|
|
651
|
-
GLOBALS.CSS (
|
|
696
|
+
GLOBALS.CSS (theme variables):
|
|
652
697
|
\`\`\`css
|
|
653
|
-
${
|
|
698
|
+
${globalsTruncated}${pageContext.globalsCSS.length > MAX_GLOBALS_CSS ? "\n/* ... (truncated) */" : ""}
|
|
654
699
|
\`\`\`
|
|
655
700
|
|
|
656
701
|
`;
|
|
657
702
|
|
|
658
|
-
//
|
|
659
|
-
if (recommendedFile) {
|
|
660
|
-
textContent += `═══════════════════════════════════════════════════════════════════════════════
|
|
661
|
-
⚡ RECOMMENDED FILE TO EDIT
|
|
662
|
-
═══════════════════════════════════════════════════════════════════════════════
|
|
663
|
-
|
|
664
|
-
Based on the screenshot analysis, the component you should edit is:
|
|
665
|
-
**${recommendedFile.path}**
|
|
666
|
-
Reason: ${recommendedFile.reason}
|
|
667
|
-
|
|
668
|
-
The page file (${pageContext.pageFile}) is just a wrapper - the actual UI elements are in the component above.
|
|
669
|
-
YOU MUST edit the recommended file unless you have a specific reason not to.
|
|
670
|
-
|
|
671
|
-
`;
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
// Build VALID FILES list with recommended file first and marked
|
|
703
|
+
// ========== VALID FILES LIST ==========
|
|
675
704
|
const validFilesList: string[] = [];
|
|
676
705
|
const recommendedPath = recommendedFile?.path;
|
|
677
706
|
|
|
678
707
|
// Add recommended file first with marker
|
|
679
708
|
if (recommendedPath) {
|
|
680
|
-
validFilesList.push(`- ${recommendedPath} (***
|
|
709
|
+
validFilesList.push(`- ${recommendedPath} (*** TARGET - EDIT THIS FILE ***)`);
|
|
681
710
|
}
|
|
682
711
|
|
|
683
712
|
// Add page file (if not the recommended file)
|
|
684
713
|
if (pageContext.pageFile && pageContext.pageFile !== recommendedPath) {
|
|
685
|
-
validFilesList.push(`- ${pageContext.pageFile}`);
|
|
714
|
+
validFilesList.push(`- ${pageContext.pageFile} (wrapper only)`);
|
|
686
715
|
}
|
|
687
716
|
|
|
688
717
|
// Add other component sources (excluding recommended file)
|
|
@@ -697,14 +726,14 @@ ${validFilesList.join("\n")}
|
|
|
697
726
|
|
|
698
727
|
INSTRUCTIONS:
|
|
699
728
|
1. Look at the screenshot and identify elements mentioned in the user's request
|
|
700
|
-
2.
|
|
729
|
+
2. The TARGET COMPONENT shown first contains the UI - EDIT THAT FILE
|
|
701
730
|
3. Make SURGICAL EDITS - change only the specific lines needed
|
|
702
731
|
4. PRESERVE all existing logic, hooks, API calls, and error handling
|
|
703
|
-
5. Return the
|
|
732
|
+
5. Return patches in the specified format (search/replace)
|
|
704
733
|
6. Only use file paths from the VALID FILES list above
|
|
705
|
-
7.
|
|
734
|
+
7. DO NOT edit the page wrapper unless the TARGET COMPONENT is unavailable
|
|
706
735
|
|
|
707
|
-
CRITICAL:
|
|
736
|
+
CRITICAL: Edit the TARGET COMPONENT (marked with ***), not the page wrapper.`;
|
|
708
737
|
|
|
709
738
|
messageContent.push({
|
|
710
739
|
type: "text",
|
|
@@ -599,6 +599,27 @@ export async function POST(request: Request) {
|
|
|
599
599
|
});
|
|
600
600
|
}
|
|
601
601
|
|
|
602
|
+
// ========== SMART CONTEXT BUDGETING ==========
|
|
603
|
+
// Total budget: 100k chars (~25k tokens, safe for Claude)
|
|
604
|
+
// Priority: Recommended file (full) > Page file (limited) > Other components (dynamic)
|
|
605
|
+
const TOTAL_CONTEXT_BUDGET = 100000;
|
|
606
|
+
const MAX_RECOMMENDED_FILE = 50000; // 50k chars max for recommended file
|
|
607
|
+
const MAX_PAGE_FILE = 2000; // Page file is just a wrapper
|
|
608
|
+
const MAX_GLOBALS_CSS = 1500;
|
|
609
|
+
const MAX_FILES = 25;
|
|
610
|
+
|
|
611
|
+
let usedContext = 0;
|
|
612
|
+
|
|
613
|
+
// Extract recommended file from component sources (to show first, avoid duplication)
|
|
614
|
+
let recommendedFileContent: { path: string; content: string } | null = null;
|
|
615
|
+
if (recommendedFile) {
|
|
616
|
+
const idx = pageContext.componentSources.findIndex(c => c.path === recommendedFile.path);
|
|
617
|
+
if (idx !== -1) {
|
|
618
|
+
recommendedFileContent = pageContext.componentSources[idx];
|
|
619
|
+
pageContext.componentSources.splice(idx, 1); // Remove to avoid duplication
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
|
|
602
623
|
// Build text content
|
|
603
624
|
let textContent = `VISION MODE EDIT REQUEST
|
|
604
625
|
|
|
@@ -614,41 +635,63 @@ ${focusedElements.map((el) => `- ${el.name} (${el.type}) at (${el.coordinates.x}
|
|
|
614
635
|
`;
|
|
615
636
|
}
|
|
616
637
|
|
|
617
|
-
|
|
638
|
+
// ========== TARGET COMPONENT (RECOMMENDED FILE) - SHOWN FIRST ==========
|
|
639
|
+
if (recommendedFileContent) {
|
|
640
|
+
const content = recommendedFileContent.content.length > MAX_RECOMMENDED_FILE
|
|
641
|
+
? recommendedFileContent.content.substring(0, MAX_RECOMMENDED_FILE) + "\n// ... (file truncated at 50k chars, showing component definition and main logic)"
|
|
642
|
+
: recommendedFileContent.content;
|
|
643
|
+
|
|
644
|
+
textContent += `═══════════════════════════════════════════════════════════════════════════════
|
|
645
|
+
⚡ TARGET COMPONENT - YOU MUST EDIT THIS FILE
|
|
646
|
+
═══════════════════════════════════════════════════════════════════════════════
|
|
647
|
+
|
|
648
|
+
This is the component that renders the UI you see in the screenshot.
|
|
649
|
+
File: ${recommendedFileContent.path}
|
|
650
|
+
|
|
651
|
+
\`\`\`tsx
|
|
652
|
+
${content}
|
|
653
|
+
\`\`\`
|
|
654
|
+
|
|
655
|
+
`;
|
|
656
|
+
usedContext += content.length;
|
|
657
|
+
debugLog("Added TARGET COMPONENT to context", {
|
|
658
|
+
path: recommendedFileContent.path,
|
|
659
|
+
originalSize: recommendedFileContent.content.length,
|
|
660
|
+
includedSize: content.length
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// ========== PAGE CONTEXT (wrapper - de-emphasized) ==========
|
|
665
|
+
const pageContentTruncated = pageContext.pageContent.substring(0, MAX_PAGE_FILE);
|
|
666
|
+
const pageWasTruncated = pageContext.pageContent.length > MAX_PAGE_FILE;
|
|
667
|
+
|
|
668
|
+
textContent += `PAGE CONTEXT (wrapper only${recommendedFileContent ? " - DO NOT edit this, edit the TARGET COMPONENT above" : ""}):
|
|
618
669
|
|
|
619
670
|
Page File: ${pageContext.pageFile || "Not found"}
|
|
620
|
-
${pageContext.pageContent ? `\`\`\`tsx\n${
|
|
671
|
+
${pageContext.pageContent ? `\`\`\`tsx\n${pageContentTruncated}${pageWasTruncated ? "\n// ... (wrapper truncated)" : ""}\n\`\`\`` : ""}
|
|
621
672
|
|
|
622
673
|
`;
|
|
674
|
+
usedContext += pageContentTruncated.length;
|
|
623
675
|
|
|
676
|
+
// ========== SUPPORTING COMPONENTS (dynamic budget) ==========
|
|
624
677
|
if (pageContext.componentSources.length > 0) {
|
|
625
|
-
|
|
626
|
-
const
|
|
627
|
-
const
|
|
628
|
-
const MAX_PER_FILE_SECONDARY = 1500; // Remaining files get less
|
|
629
|
-
const MAX_FILES = 30; // Limit total number of files
|
|
678
|
+
const remainingBudget = TOTAL_CONTEXT_BUDGET - usedContext - MAX_GLOBALS_CSS - 5000; // Reserve 5k for instructions
|
|
679
|
+
const filesToInclude = pageContext.componentSources.slice(0, MAX_FILES);
|
|
680
|
+
const perFileLimit = Math.max(1000, Math.floor(remainingBudget / Math.max(filesToInclude.length, 1)));
|
|
630
681
|
|
|
631
|
-
|
|
632
|
-
const truncatedComponents = pageContext.componentSources.slice(0, MAX_FILES);
|
|
682
|
+
textContent += `SUPPORTING COMPONENTS (${filesToInclude.length} files, ~${Math.round(perFileLimit/1000)}k chars each):\n`;
|
|
633
683
|
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
const comp = truncatedComponents[i];
|
|
638
|
-
const isPriority = i < 10; // First 10 files are priority (direct imports)
|
|
639
|
-
const maxSize = isPriority ? MAX_PER_FILE_PRIORITY : MAX_PER_FILE_SECONDARY;
|
|
640
|
-
|
|
641
|
-
// Stop if we've used too much context
|
|
642
|
-
if (usedContext > MAX_TOTAL_CONTEXT) {
|
|
643
|
-
textContent += `\n// ... (${truncatedComponents.length - i} more files omitted to stay within context limits)\n`;
|
|
684
|
+
for (const comp of filesToInclude) {
|
|
685
|
+
if (usedContext > TOTAL_CONTEXT_BUDGET - 10000) {
|
|
686
|
+
textContent += `\n// ... (remaining files omitted to stay within context limits)\n`;
|
|
644
687
|
break;
|
|
645
688
|
}
|
|
646
689
|
|
|
647
|
-
const truncatedContent = comp.content.substring(0,
|
|
648
|
-
const wasTruncated = comp.content.length >
|
|
690
|
+
const truncatedContent = comp.content.substring(0, perFileLimit);
|
|
691
|
+
const wasTruncated = comp.content.length > perFileLimit;
|
|
649
692
|
|
|
650
693
|
textContent += `
|
|
651
|
-
File: ${comp.path}
|
|
694
|
+
File: ${comp.path}
|
|
652
695
|
\`\`\`tsx
|
|
653
696
|
${truncatedContent}${wasTruncated ? "\n// ... (truncated)" : ""}
|
|
654
697
|
\`\`\`
|
|
@@ -657,42 +700,28 @@ ${truncatedContent}${wasTruncated ? "\n// ... (truncated)" : ""}
|
|
|
657
700
|
}
|
|
658
701
|
}
|
|
659
702
|
|
|
703
|
+
// ========== GLOBALS CSS ==========
|
|
704
|
+
const globalsTruncated = pageContext.globalsCSS.substring(0, MAX_GLOBALS_CSS);
|
|
660
705
|
textContent += `
|
|
661
|
-
GLOBALS.CSS (
|
|
706
|
+
GLOBALS.CSS (theme variables):
|
|
662
707
|
\`\`\`css
|
|
663
|
-
${
|
|
708
|
+
${globalsTruncated}${pageContext.globalsCSS.length > MAX_GLOBALS_CSS ? "\n/* ... (truncated) */" : ""}
|
|
664
709
|
\`\`\`
|
|
665
710
|
|
|
666
711
|
`;
|
|
667
712
|
|
|
668
|
-
//
|
|
669
|
-
if (recommendedFile) {
|
|
670
|
-
textContent += `═══════════════════════════════════════════════════════════════════════════════
|
|
671
|
-
⚡ RECOMMENDED FILE TO EDIT
|
|
672
|
-
═══════════════════════════════════════════════════════════════════════════════
|
|
673
|
-
|
|
674
|
-
Based on the screenshot analysis, the component you should edit is:
|
|
675
|
-
**${recommendedFile.path}**
|
|
676
|
-
Reason: ${recommendedFile.reason}
|
|
677
|
-
|
|
678
|
-
The page file (${pageContext.pageFile}) is just a wrapper - the actual UI elements are in the component above.
|
|
679
|
-
YOU MUST edit the recommended file unless you have a specific reason not to.
|
|
680
|
-
|
|
681
|
-
`;
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
// Build VALID FILES list with recommended file first and marked
|
|
713
|
+
// ========== VALID FILES LIST ==========
|
|
685
714
|
const validFilesList: string[] = [];
|
|
686
715
|
const recommendedPath = recommendedFile?.path;
|
|
687
716
|
|
|
688
717
|
// Add recommended file first with marker
|
|
689
718
|
if (recommendedPath) {
|
|
690
|
-
validFilesList.push(`- ${recommendedPath} (***
|
|
719
|
+
validFilesList.push(`- ${recommendedPath} (*** TARGET - EDIT THIS FILE ***)`);
|
|
691
720
|
}
|
|
692
721
|
|
|
693
722
|
// Add page file (if not the recommended file)
|
|
694
723
|
if (pageContext.pageFile && pageContext.pageFile !== recommendedPath) {
|
|
695
|
-
validFilesList.push(`- ${pageContext.pageFile}`);
|
|
724
|
+
validFilesList.push(`- ${pageContext.pageFile} (wrapper only)`);
|
|
696
725
|
}
|
|
697
726
|
|
|
698
727
|
// Add other component sources (excluding recommended file)
|
|
@@ -707,14 +736,14 @@ ${validFilesList.join("\n")}
|
|
|
707
736
|
|
|
708
737
|
INSTRUCTIONS:
|
|
709
738
|
1. Look at the screenshot and identify elements mentioned in the user's request
|
|
710
|
-
2.
|
|
739
|
+
2. The TARGET COMPONENT shown first contains the UI - EDIT THAT FILE
|
|
711
740
|
3. Choose which of the VALID FILES above need modifications
|
|
712
741
|
4. Generate complete modified code for each file
|
|
713
742
|
5. Provide previewCSS for immediate visual feedback
|
|
714
743
|
6. Return as JSON in the specified format
|
|
715
|
-
7.
|
|
744
|
+
7. DO NOT edit the page wrapper unless the TARGET COMPONENT is unavailable
|
|
716
745
|
|
|
717
|
-
CRITICAL:
|
|
746
|
+
CRITICAL: Edit the TARGET COMPONENT (marked with ***), not the page wrapper.`;
|
|
718
747
|
|
|
719
748
|
messageContent.push({
|
|
720
749
|
type: "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.42",
|
|
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",
|