Install
$ agentstack add skill-atezer-fmcp-fmcp-screen-recipes ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
FMCP Screen Recipes — Fast Path Cookbook
Ne Zaman Kullanılır
Fast Path DEVREYE GİRER (hepsi TRUE olmalı):
- ✅ Tek ekran üretimi
- ✅ Standart ekran tipi: 9 recipe'ten biri match ediyor
- ✅ DS tanımlı:
active-ds.mdStatus: ✅ Aktif - ✅ Platform belli
- ✅ Custom animation / prototype YOK
Devreye GİRMEZ: Multi-screen flow, custom layout, animation, explicit generate-figma-screen talebi, DS GATE geçilmemişse.
5 Mega-Adımlı Akış
Max 15 op/execute. Her mega-adım sonrası tek satır Türkçe micro-report.
M1: Pre-Flight Discovery + Token + Text Style (1 execute, ~15 op)
M2: Frame + Structure + Modes (1 execute, ~12-14 op)
M3: Component Placement (toplu, 3-4/execute) (2-3 execute, ~12-15 op each)
↳ Her M3 execute sonrası: figma_scan_ds_compliance(threshold=85) inline gate
M4: Dark Variant (1 execute, ~4 op)
M5: Validate + Final Report (ZORUNLU: figma_scan_ds_compliance(detailed) + figma_validate_screen)
Toplam execute: ~6-8 + 2-3 scan call. Hedef süre: ~10 dk. v1.9.4: Scan call'ları hızlıdır (~150ms), execute bütçesini yemezler.
3 MUTLAK KURAL
KURAL 1 — Fill Bind Zorunlu (frame + text dahil):
const paint = { type: 'SOLID', color: { r: 1, g: 1, b: 1 } };
const bound = figma.variables.setBoundVariableForPaint(paint, 'color', dsColorVar);
node.fills = [bound];
Fill panel'de variable icon 🎨 GÖRÜNMELI. Hardcoded hex YASAK. Frame fill + text fill + primitive dahil TÜM node'lar.
KURAL 2 — Variant Seçim: DEFAULT Koru:
setPropertiesile SADECE recipe'de explicit belirtilen property'leri set et- Diğer TÜM property'leri DEFAULT bırak
Product→ main (default), Boolean kontroller → recipe'de explicit yoksa DEFAULT
KURAL 3 — Token Bind, Alias Resolve ETME: setBoundVariable(property, importedVariable) ile bind et. Figma runtime alias chain'i otomatik çözer. valuesByMode okuma, alias traversal YASAK (timeout riski).
Mega-Adım Mapping
Aşağıdaki eski adımlar REFERANS amaçlıdır. AYRI AYRI execute ETME — mega-adım tablosunu takip et:
- M1: Adım 1 (validation) + 1.5 (discovery) + 1.6 (text style) → TEK execute
- M2: Adım 2 (frame) + 4b (mode) + 5.5 (content body) → TEK execute
- M3: Adım 6 (discovery) + 7 (placement) → 2-3 execute
- M4: Adım 8 (dark) → TEK execute
- M5: Adım 9 (validate) → 1-2 validate call
Adım 1 — Pre-Flight Check
Hiçbir figmaexecute çağırma. Doğrula: active-ds.md ✅, screentype geçerli, platform + device_preset geçerli, variants ≥1.
Micro-report: ✅ Pre-flight: screen_type=, platform=, device=, variants=
Adım 1.5 — Unified Pre-Flight Discovery
Cache-First (v3.0+): Önce .claude/design-systems/sui/tokens.md oku. Cache varsa ve v.name.endsWith("/" + suffix) || v.name === suffix)
#### Execute 1 — Collection & Mode Discovery (7 op)
```js
const colls = await figma.teamLibrary.getAvailableLibraryVariableCollectionsAsync();
function findColl(keywords) {
return colls.find(c => {
const n = c.name.toLowerCase().trim();
return keywords.some(kw => n.includes(kw));
});
}
const sizeColl = findColl(["semantic size", "semantic sizes", "size"]);
const colorsColl = findColl(["semantic color", "s theme"]);
const result = { availableColls: colls.map(c => ({name: c.name, key: c.key})), spacingTokenKeys: {}, collectionInfo: { colors: null, size: null }, surfaceKey: null };
if (sizeColl) {
const sizeVars = await figma.teamLibrary.getVariablesInLibraryCollectionAsync(sizeColl.key);
const suffixes = ["spacing-none","spacing-050","spacing-075","spacing-100","spacing-125","spacing-150","spacing-200"];
for (const s of suffixes) {
const f = sizeVars.find(v => v.name.endsWith("/"+s) || v.name === s);
if (f) result.spacingTokenKeys[s] = f.key;
}
if (sizeVars.length > 0) {
const first = await figma.variables.importVariableByKeyAsync(sizeVars[0].key);
const coll = await figma.variables.getVariableCollectionByIdAsync(first.variableCollectionId);
result.collectionInfo.size = { collId: coll.id, modes: coll.modes.map(m => ({name: m.name, modeId: m.modeId})) };
}
}
if (colorsColl) {
const colorsVars = await figma.teamLibrary.getVariablesInLibraryCollectionAsync(colorsColl.key);
const bgVar = colorsVars.find(v => v.name.toLowerCase().includes("background") && v.name.toLowerCase().includes("level-0"));
if (bgVar) result.surfaceKey = bgVar.key;
if (colorsVars.length > 0) {
const first = await figma.variables.importVariableByKeyAsync(colorsVars[0].key);
const coll = await figma.variables.getVariableCollectionByIdAsync(first.variableCollectionId);
result.collectionInfo.colors = { collId: coll.id, modes: coll.modes.map(m => ({name: m.name, modeId: m.modeId})) };
}
}
return result;
Execute 2 — Critical Token Import (7 op)
const tokenKeyMap = {};
const spacingKeys = { /* Execute 1'den gelen spacingTokenKeys */ };
for (const [suffix, key] of Object.entries(spacingKeys)) {
try {
const imported = await figma.variables.importVariableByKeyAsync(key);
tokenKeyMap[suffix] = imported.id;
} catch(e) {}
}
return { tokenKeyMap };
Micro-report: ✅ Pre-Flight Discovery: collection, token imported, surface=
Adım 1.6 — Text Style Resolution
Dosyadaki mevcut text style'ları tara, role mapping üret. importStyleByKeyAsync ÇAĞIRMA — direkt setTextStyleIdAsync(roleMap[role].id) kullan.
const allTexts = figma.currentPage.findAll(n => n.type === "TEXT");
const uniqueStyleIds = new Set();
for (const t of allTexts) { if (t.textStyleId && typeof t.textStyleId === 'string') uniqueStyleIds.add(t.textStyleId); }
const styleMap = {};
for (const id of uniqueStyleIds) {
try { const style = await figma.getStyleByIdAsync(id); if (style) styleMap[style.id] = { id: style.id, name: style.name, fontSize: style.fontSize || null }; } catch(e) {}
}
const roleKeywords = {
display: ["display","hero","amount","title-xl"], title: ["section-title","title","heading"],
subtitle: ["subtitle","body-semibold","body-bold"], body: ["body-medium","body-regular","body"],
caption: ["small","caption","footnote"], button: ["button"]
};
function findStyle(kws) { for (const kw of kws) { const m = Object.values(styleMap).find(s => (s.name||"").toLowerCase().includes(kw.toLowerCase())); if (m) return m; } return null; }
const roleMap = {};
for (const [role, kws] of Object.entries(roleKeywords)) { const m = findStyle(kws); if (m) roleMap[role] = { id: m.id, name: m.name, fontSize: m.fontSize }; }
if (!roleMap.display) {
const sorted = Object.values(styleMap).filter(s => s.fontSize).sort((a,b) => b.fontSize - a.fontSize);
if (sorted.length > 0) roleMap.display = { id: sorted[0].id, name: sorted[0].name, fontSize: sorted[0].fontSize };
}
return { totalStyles: Object.keys(styleMap).length, styleMap, roleMap };
Micro-report: ✅ Text Style: style, role eşleşti
Adım 2 — Wrapper Frame + Background (Edge-to-Edge)
Ana frame: device preset boyutu, auto-layout VERTICAL, padding=0, gap=0, background DS variable'a bağlı.
Edge-to-Edge yapı:
Ana Frame (padding:0, gap:0, VERTICAL)
├── NavigationTopBar (FILL — edge-to-edge)
├── Content Body (FILL both, padding:spacing-100, gap:spacing-075)
│ └── Recipe component'leri
└── BottomNavBar (FILL — edge-to-edge, varsa)
const frame = figma.createFrame();
frame.name = " — ";
frame.resize(, );
frame.layoutMode = "VERTICAL";
frame.primaryAxisSizingMode = "FIXED";
frame.counterAxisSizingMode = "FIXED";
frame.paddingTop = 0; frame.paddingBottom = 0; frame.paddingLeft = 0; frame.paddingRight = 0;
frame.itemSpacing = 0;
if (surfaceKey) {
const bgVar = await figma.variables.importVariableByKeyAsync(surfaceKey);
const paint = { type: 'SOLID', color: { r: 1, g: 1, b: 1 } };
frame.fills = [figma.variables.setBoundVariableForPaint(paint, 'color', bgVar)];
}
return { frameId: frame.id };
Micro-report: ✅ Frame: (×), edge-to-edge, background bound
Adım 3 — ⏭️ DEVRE DIŞI
Breakpoint bind frame boyutunu bozuyor (375 vs 402). Device preset boyutu korunur.
Adım 4 — Theme + Size Mode Setup
Collection/mode bilgisi Adım 1.5'ten gelir (ayrı execute yok).
Adım 4b — Mode apply:
const frame = await figma.getNodeByIdAsync(frameId);
if (collectionInfo.colors) {
const coll = await figma.variables.getVariableCollectionByIdAsync(collectionInfo.colors.collId);
const lightMode = collectionInfo.colors.modes.find(m => m.name.toLowerCase().includes("light"));
if (lightMode) frame.setExplicitVariableModeForCollection(coll, lightMode.modeId);
}
if (collectionInfo.size) {
const coll = await figma.variables.getVariableCollectionByIdAsync(collectionInfo.size.collId);
const kws = { mobile:["mobil","mobile"], tablet:["tablet"], desktop:["web","desktop"], web:["web","desktop"] };
const sizeMode = collectionInfo.size.modes.find(m => (kws[platform]||["mobil"]).some(k => m.name.toLowerCase().includes(k)));
if (sizeMode) frame.setExplicitVariableModeForCollection(coll, sizeMode.modeId);
}
return { modesApplied: true };
Micro-report: ✅ Theme: Light, Size:
Adım 5.5 — Content Body Wrapper
const parentFrame = await figma.getNodeByIdAsync(frameId);
const contentBody = figma.createFrame();
contentBody.name = "Content Body";
contentBody.layoutMode = "VERTICAL";
parentFrame.appendChild(contentBody); // ÖNCE (Rule 11)
contentBody.layoutSizingHorizontal = "FILL"; // SONRA
contentBody.layoutSizingVertical = "FILL";
const paddingVar = await figma.variables.importVariableByKeyAsync(spacing100Key);
const gapVar = await figma.variables.importVariableByKeyAsync(spacing075Key);
contentBody.setBoundVariable("paddingLeft", paddingVar);
contentBody.setBoundVariable("paddingRight", paddingVar);
contentBody.setBoundVariable("paddingTop", paddingVar);
contentBody.setBoundVariable("paddingBottom", paddingVar);
contentBody.setBoundVariable("itemSpacing", gapVar);
contentBody.fills = [];
return { contentBodyId: contentBody.id };
Micro-report: ✅ Content Body: FILL both, padding=spacing-100, gap=spacing-075
Adım 6 — Component Discovery
Cache-First (v3.0+): Önce .claude/design-systems/sui/components.md oku. Cache varsa → figma_search_assets ATLA, direkt importComponentByKeyAsync kullan. Yoksa: figma_search_assets(query="") + Rule 24 fallback.
Micro-report: ✅ Component keşfi: bulundu, eksik
Adım 7 — Recipe Component Placement
3-4 component TEK execute'ta. Parent routing: edge-to-edge (NavigationTopBar, BottomNavBar) → Ana Frame, diğer her şey → Content Body.
const edgeNames = /^(navigation|nav|top|bottom|status|tabbar|tab_bar)/i;
const parentId = edgeNames.test(spec.name) ? frameId : contentBodyId;
Text Style Binding: await textNode.setTextStyleIdAsync(roleMap[role].id) — DİREKT bağla, importStyleByKeyAsync ÇAĞIRMA, fontSize set ETME.
Text rolü mapping: display→Amount/Hero, title→Section Header, subtitle→Card Title, body→Body text, caption→Small/Info.
Adım 8 — Dark Variant
const lightFrame = await figma.getNodeByIdAsync(lightFrameId);
const darkFrame = lightFrame.clone();
figma.currentPage.appendChild(darkFrame);
darkFrame.x = lightFrame.x + lightFrame.width + 80;
darkFrame.name = lightFrame.name + " — Dark";
const coll = await figma.variables.getVariableCollectionByIdAsync(semColorsCollId);
darkFrame.setExplicitVariableModeForCollection(coll, darkModeId);
return { darkFrameId: darkFrame.id };
Adım 9 — Validation + Final Report (v1.9.4 Güçlendirildi)
ZORUNLU gate — atlama YASAK. Her ekran için iki aşamalı doğrulama:
9a. Inline scan (M3/M4 sonrası, isteğe bağlı retry):
figma_scan_ds_compliance(nodeId=frameId, threshold=85)
passed: falseveyacoverage.paddings.pct 0veclipsContent: true→ içerik kesiliyor, kullanıcıya "scroll mu, body genişletme mi?" sor.
9b. Final validate (her frame için, ZORUNLU):
figma_validate_screen(frameId, minScore=80)
- Skor <80 → 3 retry; 3× fail → kullanıcıya
generate-figma-screenskill'ine fallback öner.
Son Rapor (ZORUNLU alanlar):
- Screen type, DS, device, variants
- Frame ID'leri (light/dark)
- Score: validate + scan skorları birlikte (örn. "validate: 87, scan: 89")
- Coverage yüzdeleri: fills/paddings/radius/itemSpacing/textStyle/textColor (scan response'tan)
- Kullanılan library instance listesi + primitive fallback listesi (varsa)
- Token binding sayıları
- Overflow durumu (body kesiliyor mu?)
- Toplam execute sayısı, süre
- Hardcoded hex/fontSize sample'ları (varsa — kullanıcıya gösterilir, "bilinçli skip" değil "acil düzeltilecek")
Device Presets Lookup Table
Mobile
| Device | W | H | Keywords | |---|---|---|---| | iPhone 17 | 402 | 874 | mobile, iphone, ios | | iPhone 16 & 17 Pro | 402 | 874 | iphone pro | | iPhone 16 | 393 | 852 | iphone 16 | | iPhone 16 Pro Max | 440 | 956 | pro max | | iPhone 16 Plus | 430 | 932 | iphone plus | | iPhone Air | 420 | 912 | iphone air | | iPhone 13 & 14 | 390 | 844 | iphone 13, iphone 14 | | Android Compact | 412 | 917 | android | | Android Medium | 700 | 840 | android tablet |
Default: mobile → iPhone 17, android → Android Compact.
Tablet
| Device | W | H | |---|---|---| | iPad Pro 11" | 834 | 1194 | | iPad Pro 12.9" | 1024 | 1366 |
Default: tablet → iPad Pro 11".
Desktop / Web
| Device | W | H | |---|---|---| | Desktop | 1440 | 900 | | Desktop HD | 1920 | 1080 | | MacBook Pro 14" | 1512 | 982 | | MacBook Pro 16" | 1728 | 1117 |
Default: desktop/web → Desktop 1440×900.
9 Screen Type Recipes
Her recipe = component listesi + yerleşim sırası + search keyword'leri + setProperties.
Recipe 1: Login
Trigger: login, giriş, oturum aç, sign in
- AppBar —
["navigation top", "appbar"]
setProperties: { "Subtitle": false, "Right Controls": false }
- Logo —
["logo", "brand"] - Welcome Text (H1) —
["heading", "display"] - Subtitle Text —
["body medium", "text body"] - Email Input —
["text field", "input", "email"] - Password Input —
["password", "text field password"] - Primary Button ("Giriş Yap") —
["button primary"]
setProperties: { "Value": "Giriş Yap" }
- Forgot Password Link —
["link", "text button"] - Divider —
["divider"] - Register Link —
["text button", "link"]
Recipe 2: Payment
Trigger: ödeme, payment, checkout, satın al
- NavigationTopBar —
["navigation top", "appbar"]
setProperties: { "Title Text": "Ödeme", "Right Controls": false, "Subtitle": false, "Product": "main" }
- Amount Display —
["display large", "hero text"] - Currency Label —
["body small", "caption"] - Section Header —
["section header", "subtitle"] - Payment Method Cards (×3) —
["card payment", "list item"] - Add New Method Button —
["button secondary"] - Divider —
["divider"] - CTA Button —
["button primary large"]
setProperties: { "Value": "Ödemeyi Tamamla" }
- Security Info —
["text small", "caption"]
Recipe 3: Profile
Trigger: profil, profile, hesap, account
- AppBar —
["navigation top"]
setProperties: { "Title Text": "Profilim" }
- Avatar —
["avatar large", "profile picture"] - User Name Text —
["heading", "display medium"] - User Email Text —
["body", "text secondary"] - Divider —
["divider"] - Menu List Items (×4) —
["list item", "menu row"] - Destructive Button ("Çıkış Yap") —
["button destructive"]
setProperties: { "Value": "Çıkış Yap" }
Recipe 4: List
Trigger: liste, list, arama, search, katalog
- AppBar —
["navigation top"]
setProperties: { "Title Text": "Arama" }
- Search Bar —
["search", "search bar"] - Filter Chips Row —
["chip", "filter chip"] - Card List Items (×N) —
["card", "list card"] - Pagination — `
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: atezer
- Source: atezer/FMCP
- License: MIT
- Homepage: https://www.npmjs.com/package/@atezer/figma-mcp-bridge
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.