@createlex/figma-swiftui-mcp 1.2.0 → 1.2.4
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.
- package/companion/bridge-server.cjs +17 -1
- package/companion/mcp-server.mjs +42 -3
- package/package.json +2 -3
|
@@ -276,7 +276,23 @@ function startBridgeServer(options = {}) {
|
|
|
276
276
|
return res.status(502).json({ ok: false, error: response.error || 'Plugin returned an error' });
|
|
277
277
|
}
|
|
278
278
|
|
|
279
|
-
|
|
279
|
+
const data = response.data || {};
|
|
280
|
+
|
|
281
|
+
// Attach explicit AI workflow instructions so any agent that fetches this URL
|
|
282
|
+
// knows exactly how to generate and write correct SwiftUI code with real images.
|
|
283
|
+
const assetCount = data.assetExportPlan?.candidates?.length ?? 0;
|
|
284
|
+
const aiWorkflow = {
|
|
285
|
+
step1: 'You have the Figma design context. Do NOT generate SwiftUI code from scratch — that produces grey placeholder rectangles instead of real images.',
|
|
286
|
+
step2: 'Call the MCP tool `write_selection_to_xcode`. It generates SwiftUI with correct Image("name") asset references AND writes the PNG files to Assets.xcassets automatically.',
|
|
287
|
+
step3: 'After write_selection_to_xcode returns, ONLY refine the code it produced: add GeometryReader for responsiveness, wire interactions. DO NOT rewrite Image("name") calls or replace them with Rectangle()/shapes.',
|
|
288
|
+
step4: 'If the caller has no MCP access, use the structName and assetExportPlan below to write SwiftUI that references Image("name") for every asset candidate listed.',
|
|
289
|
+
mcpTool: 'write_selection_to_xcode',
|
|
290
|
+
mcpServer: 'figma-swiftui',
|
|
291
|
+
assetCandidateCount: assetCount,
|
|
292
|
+
warning: 'Photos, 3D renders, illustrations and icons are pre-exported as PNG assets. Always use Image("name") — never reconstruct them with SwiftUI shapes or colors.',
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
res.json({ ok: true, ...data, _aiWorkflow: aiWorkflow });
|
|
280
296
|
});
|
|
281
297
|
|
|
282
298
|
function sendBridgeMessage(ws, payload) {
|
package/companion/mcp-server.mjs
CHANGED
|
@@ -706,7 +706,7 @@ server.registerTool('analyze_generation', {
|
|
|
706
706
|
});
|
|
707
707
|
|
|
708
708
|
server.registerTool('write_generated_swiftui_to_xcode', {
|
|
709
|
-
description: 'Write generated SwiftUI code
|
|
709
|
+
description: 'Write generated SwiftUI code into the configured Xcode project. Images are automatically exported from the current Figma selection and written to Assets.xcassets — you do not need to provide them manually. Supports multiple files: use additionalFiles for DesignTokens.swift (dir: "shared"), reusable components (dir: "components"), or extra screen files (dir: "screens"). AI tools generating code with their own model should call this after generating SwiftUI from get_design_context or the /design-context URL.',
|
|
710
710
|
inputSchema: {
|
|
711
711
|
code: z.string().describe('SwiftUI source code for the primary view'),
|
|
712
712
|
structName: z.string().optional().describe('Optional Swift struct name; if omitted it will be inferred from the code'),
|
|
@@ -725,11 +725,31 @@ server.registerTool('write_generated_swiftui_to_xcode', {
|
|
|
725
725
|
}, async ({ code, structName, projectPath, images, selectionNames, additionalFiles }) => {
|
|
726
726
|
const targetDir = resolveTargetProjectPath(projectPath);
|
|
727
727
|
const effectiveStructName = inferStructName({ structName, code, selectionNames });
|
|
728
|
+
|
|
729
|
+
// Auto-fetch images from the current Figma selection when none were provided.
|
|
730
|
+
// This ensures Assets.xcassets is always populated even when the AI supplied
|
|
731
|
+
// its own SwiftUI code (e.g. from get_design_context / the /design-context URL).
|
|
732
|
+
let effectiveImages = Array.isArray(images) && images.length > 0 ? images : [];
|
|
733
|
+
if (effectiveImages.length === 0) {
|
|
734
|
+
try {
|
|
735
|
+
const generated = await callBridge('generate_swiftui', {
|
|
736
|
+
includeImages: true,
|
|
737
|
+
generationMode: 'editable',
|
|
738
|
+
includeOverflow: false,
|
|
739
|
+
});
|
|
740
|
+
if (Array.isArray(generated.images) && generated.images.length > 0) {
|
|
741
|
+
effectiveImages = generated.images;
|
|
742
|
+
}
|
|
743
|
+
} catch {
|
|
744
|
+
// Non-fatal: bridge may not be connected or selection may have no images.
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
|
|
728
748
|
const result = writeSwiftUIScreen({
|
|
729
749
|
targetDir,
|
|
730
750
|
code,
|
|
731
751
|
structName: effectiveStructName,
|
|
732
|
-
images,
|
|
752
|
+
images: effectiveImages,
|
|
733
753
|
additionalFiles,
|
|
734
754
|
});
|
|
735
755
|
|
|
@@ -740,6 +760,7 @@ server.registerTool('write_generated_swiftui_to_xcode', {
|
|
|
740
760
|
return jsonResult({
|
|
741
761
|
...stripImageData(result),
|
|
742
762
|
structName: effectiveStructName,
|
|
763
|
+
imagesWritten: effectiveImages.length,
|
|
743
764
|
});
|
|
744
765
|
});
|
|
745
766
|
|
|
@@ -811,6 +832,8 @@ server.registerTool('write_selection_to_xcode', {
|
|
|
811
832
|
filePath: result.results?.swiftFile ?? null,
|
|
812
833
|
});
|
|
813
834
|
|
|
835
|
+
const imageNames = (generated.images ?? []).map((img) => img.name);
|
|
836
|
+
|
|
814
837
|
return jsonResult(buildCompactResponse({
|
|
815
838
|
result,
|
|
816
839
|
structName: effectiveStructName,
|
|
@@ -818,6 +841,14 @@ server.registerTool('write_selection_to_xcode', {
|
|
|
818
841
|
diagnostics: generated.diagnostics ?? [],
|
|
819
842
|
refinementInstructions,
|
|
820
843
|
analysisHints,
|
|
844
|
+
_critical: [
|
|
845
|
+
`✅ SwiftUI written to ${result.results?.swiftFile ?? 'Xcode'}. Images written to Assets.xcassets.`,
|
|
846
|
+
imageNames.length > 0
|
|
847
|
+
? `🖼 ${imageNames.length} PNG asset(s) written: ${imageNames.join(', ')}. The generated code already references them with Image("name") calls.`
|
|
848
|
+
: null,
|
|
849
|
+
'🚫 DO NOT rewrite or overwrite the generated Swift file. DO NOT replace Image("name") calls with Rectangle(), shapes, or colors.',
|
|
850
|
+
'✏️ You may ONLY: add GeometryReader for responsiveness, wire @State interactions, extract reusable sub-views. Nothing else.',
|
|
851
|
+
].filter(Boolean),
|
|
821
852
|
}));
|
|
822
853
|
});
|
|
823
854
|
|
|
@@ -940,8 +971,16 @@ function buildRefinementInstructions({ diagnostics, rasterNodes, analysisHints,
|
|
|
940
971
|
|
|
941
972
|
// Raster replacements
|
|
942
973
|
if (rasterNodes.length > 0) {
|
|
974
|
+
const rasterNames = rasterNodes
|
|
975
|
+
.map((d) => d.assetName || d.nodeName || d.name)
|
|
976
|
+
.filter(Boolean)
|
|
977
|
+
.slice(0, 8)
|
|
978
|
+
.join(', ');
|
|
943
979
|
instructions.push(
|
|
944
|
-
|
|
980
|
+
`IMPORTANT: ${rasterNodes.length} element(s) were rasterized and written to Assets.xcassets as PNG files${rasterNames ? ` (${rasterNames})` : ''}. ` +
|
|
981
|
+
`The generated code already references them with Image("name") calls — DO NOT remove or replace these Image() calls. ` +
|
|
982
|
+
`Only replace Image() calls if the asset name clearly identifies a native UI control (button, toggle, tab bar, text field). ` +
|
|
983
|
+
`Photos, illustrations, 3D renders, icons, and decorative graphics MUST stay as Image("name") — do not attempt to reconstruct them with SwiftUI shapes or colors.`
|
|
945
984
|
);
|
|
946
985
|
}
|
|
947
986
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@createlex/figma-swiftui-mcp",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.4",
|
|
4
4
|
"description": "CreateLex MCP runtime for Figma-to-SwiftUI generation and Xcode export",
|
|
5
5
|
"bin": {
|
|
6
6
|
"figma-swiftui-mcp": "bin/figma-swiftui-mcp.js"
|
|
@@ -41,8 +41,7 @@
|
|
|
41
41
|
"zod": "^4.3.6"
|
|
42
42
|
},
|
|
43
43
|
"optionalDependencies": {
|
|
44
|
-
"@anthropic-ai/sdk": "^0.52.0"
|
|
45
|
-
"openai": "^4.100.0"
|
|
44
|
+
"@anthropic-ai/sdk": "^0.52.0"
|
|
46
45
|
},
|
|
47
46
|
"devDependencies": {
|
|
48
47
|
"@figma/plugin-typings": "*",
|