@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.
@@ -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
- res.json({ ok: true, ...(response.data || {}) });
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) {
@@ -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 and optional exported images directly into the configured Xcode project. Supports writing 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 get_swiftui_generation_prompt.',
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
- `${rasterNodes.length} element(s) were exported as raster images because Figma could not map them to native SwiftUI. Review these and replace obvious UI controls (buttons, text fields, toggles, tab bars) with native SwiftUI equivalents. Keep actual images/illustrations as Image assets.`
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.0",
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": "*",