UNPKG

@aicrustech/mcp-figma-flow

Version:

MCP server otimizado para integração inteligente com Figma. 4 ferramentas essenciais: dados estruturados, layout skeleton, assets otimizados e validação visual pixel-perfect.

34 lines (25 loc) 40.8 kB
import{StdioServerTransport as gt}from"@modelcontextprotocol/sdk/server/stdio.js";import{config as ht}from"dotenv";import{resolve as yt}from"path";import{config as Ie}from"dotenv";import Re from"yargs";import{hideBin as Ae}from"yargs/helpers";import{resolve as X}from"path";function J(e){return!e||e.length<=4?"****":`****${e.slice(-4)}`}function Y(e){let t=Re(Ae(process.argv)).options({"figma-api-key":{type:"string",description:"Figma API key (Personal Access Token)"},"figma-oauth-token":{type:"string",description:"Figma OAuth Bearer token"},env:{type:"string",description:"Path to custom .env file to load environment variables from"},port:{type:"number",description:"Port to run the server on"},json:{type:"boolean",description:"Output data from tools in JSON format instead of YAML",default:!1}}).help().version("3.0.4").parseSync(),o,s;t.env?(o=X(t.env),s="cli"):(o=X(process.cwd(),".env"),s="default"),Ie({path:o,override:!0});let i={figmaApiKey:"",figmaOAuthToken:"",useOAuth:!1},n={port:3333,outputFormat:"yaml",configSources:{figmaApiKey:"env",figmaOAuthToken:"none",port:"default",outputFormat:"default",envFile:s}};return t["figma-api-key"]?(i.figmaApiKey=t["figma-api-key"],n.configSources.figmaApiKey="cli"):process.env.FIGMA_API_KEY&&(i.figmaApiKey=process.env.FIGMA_API_KEY,n.configSources.figmaApiKey="env"),t["figma-oauth-token"]?(i.figmaOAuthToken=t["figma-oauth-token"],n.configSources.figmaOAuthToken="cli",i.useOAuth=!0):process.env.FIGMA_OAUTH_TOKEN&&(i.figmaOAuthToken=process.env.FIGMA_OAUTH_TOKEN,n.configSources.figmaOAuthToken="env",i.useOAuth=!0),t.port?(n.port=t.port,n.configSources.port="cli"):process.env.PORT&&(n.port=parseInt(process.env.PORT,10),n.configSources.port="env"),t.json?(n.outputFormat="json",n.configSources.outputFormat="cli"):process.env.OUTPUT_FORMAT&&(n.outputFormat=process.env.OUTPUT_FORMAT,n.configSources.outputFormat="env"),!i.figmaApiKey&&!i.figmaOAuthToken&&(console.error("Either FIGMA_API_KEY or FIGMA_OAUTH_TOKEN is required (via CLI argument or .env file)"),process.exit(1)),e||(console.log(` Configuration:`),console.log(`- ENV_FILE: ${o} (source: ${n.configSources.envFile})`),i.useOAuth?(console.log(`- FIGMA_OAUTH_TOKEN: ${J(i.figmaOAuthToken)} (source: ${n.configSources.figmaOAuthToken})`),console.log("- Authentication Method: OAuth Bearer Token")):(console.log(`- FIGMA_API_KEY: ${J(i.figmaApiKey)} (source: ${n.configSources.figmaApiKey})`),console.log("- Authentication Method: Personal Access Token (X-Figma-Token)")),console.log(`- PORT: ${n.port} (source: ${n.configSources.port})`),console.log(`- OUTPUT_FORMAT: ${n.outputFormat} (source: ${n.configSources.outputFormat})`),console.log()),{...n,auth:i}}import{randomUUID as $e}from"node:crypto";import Q from"express";import{SSEServerTransport as Ce}from"@modelcontextprotocol/sdk/server/sse.js";import{StreamableHTTPServerTransport as Be}from"@modelcontextprotocol/sdk/server/streamableHttp.js";import{isInitializeRequest as Fe}from"@modelcontextprotocol/sdk/types.js";import"@modelcontextprotocol/sdk/server/mcp.js";var u={isHTTP:!1,log:(...e)=>{u.isHTTP?console.log("[INFO]",...e):console.error("[INFO]",...e)},error:(...e)=>{console.error("[ERROR]",...e)}};var Ne=null,R={streamable:{},sse:{}};async function ee(e,t){let o=Q();o.use("/mcp",Q.json()),o.post("/mcp",async(i,n)=>{u.log("Received StreamableHTTP request");let r=i.headers["mcp-session-id"],a;if(r&&R.streamable[r])u.log("Reusing existing StreamableHTTP transport for sessionId",r),a=R.streamable[r];else if(!r&&Fe(i.body))u.log("New initialization request for StreamableHTTP sessionId",r),a=new Be({sessionIdGenerator:()=>$e(),onsessioninitialized:m=>{R.streamable[m]=a}}),a.onclose=()=>{a.sessionId&&delete R.streamable[a.sessionId]},await t.connect(a);else{u.log("Invalid request:",i.body),n.status(400).json({jsonrpc:"2.0",error:{code:-32e3,message:"Bad Request: No valid session ID provided"},id:null});return}let l=null,d=i.body.params?._meta?.progressToken,c=0;d&&(u.log(`Setting up progress notifications for token ${d} on session ${r}`),l=setInterval(async()=>{u.log("Sending progress notification",c),await t.server.notification({method:"notifications/progress",params:{progress:c,progressToken:d}}),c++},1e3)),u.log("Handling StreamableHTTP request"),await a.handleRequest(i,n,i.body),l&&clearInterval(l),u.log("StreamableHTTP request handled")});let s=async(i,n)=>{let r=i.headers["mcp-session-id"];if(!r||!R.streamable[r]){n.status(400).send("Invalid or missing session ID");return}console.log(`Received session termination request for session ${r}`);try{await R.streamable[r].handleRequest(i,n)}catch(a){console.error("Error handling session termination:",a),n.headersSent||n.status(500).send("Error processing session termination")}};o.get("/mcp",s),o.delete("/mcp",s),o.get("/sse",async(i,n)=>{u.log("Establishing new SSE connection");let r=new Ce("/messages",n);u.log(`New SSE connection established for sessionId ${r.sessionId}`),u.log("/sse request headers:",i.headers),u.log("/sse request body:",i.body),R.sse[r.sessionId]=r,n.on("close",()=>{delete R.sse[r.sessionId]}),await t.connect(r)}),o.post("/messages",async(i,n)=>{let r=i.query.sessionId,a=R.sse[r];if(a)u.log(`Received SSE message for sessionId ${r}`),u.log("/messages request headers:",i.headers),u.log("/messages request body:",i.body),await a.handlePostMessage(i,n);else{n.status(400).send(`No transport found for sessionId ${r}`);return}}),Ne=o.listen(e,()=>{u.log(`HTTP server listening on port ${e}`),u.log(`SSE endpoint available at http://localhost:${e}/sse`),u.log(`Message endpoint available at http://localhost:${e}/messages`),u.log(`StreamableHTTP endpoint available at http://localhost:${e}/mcp`)}),process.on("SIGINT",async()=>{u.log("Shutting down server..."),await Z(R.sse),await Z(R.streamable),u.log("Server shutdown complete"),process.exit(0)})}async function Z(e){for(let t in e)try{await e[t]?.close(),delete e[t]}catch(o){console.error(`Error closing transport for session ${t}:`,o)}}import{McpServer as ft}from"@modelcontextprotocol/sdk/server/mcp.js";import{z as y}from"zod";import P from"fs";import{isTruthy as te}from"remeda";function b(e,t,o){if(!(typeof t=="object"&&t!==null)||!(e in t))return!1;let i=t[e];return o?o(i):i!==void 0}function k(e){return typeof e=="object"&&!!e&&"clipsContent"in e&&typeof e.clipsContent=="boolean"}function U(e){return typeof e=="object"&&!!e&&"absoluteBoundingBox"in e&&typeof e.absoluteBoundingBox=="object"&&!!e.absoluteBoundingBox&&"x"in e.absoluteBoundingBox&&"y"in e.absoluteBoundingBox&&"width"in e.absoluteBoundingBox&&"height"in e.absoluteBoundingBox}function oe(e,t){let o=["HORIZONTAL","VERTICAL"];return k(t)&&o.includes(t.layoutMode??"NONE")&&U(e)&&e.layoutPositioning!=="ABSOLUTE"}function ie(e){return typeof e=="object"&&e!==null&&"top"in e&&"right"in e&&"bottom"in e&&"left"in e}function ne(e,t){let o=t;return typeof t=="object"&&!!t&&e in o&&typeof o[e]=="object"&&!!o[e]&&"x"in o[e]&&"y"in o[e]&&"width"in o[e]&&"height"in o[e]}function re(e){return Array.isArray(e)&&e.length===4&&e.every(t=>typeof t=="number")}import C from"fs";import Pe from"path";async function B(e,t,o){try{C.existsSync(t)||C.mkdirSync(t,{recursive:!0});let s=Pe.join(t,e),i=await fetch(o,{method:"GET"});if(!i.ok)throw new Error(`Failed to download image: ${i.statusText}`);let n=C.createWriteStream(s),r=i.body?.getReader();if(!r)throw new Error("Failed to get response body");return new Promise((a,l)=>{let d=async()=>{try{for(;;){let{done:c,value:m}=await r.read();if(c){n.end();break}n.write(m)}}catch(c){n.end(),C.unlink(s,()=>{}),l(c)}};n.on("finish",()=>{a(s)}),n.on("error",c=>{r.cancel(),C.unlink(s,()=>{}),l(new Error(`Failed to write image: ${c.message}`))}),d()})}catch(s){let i=s instanceof Error?s.message:String(s);throw new Error(`Error downloading image: ${i}`)}}function O(e){if(typeof e!="object"||e===null)return e;if(Array.isArray(e))return e.map(o=>O(o));let t={};for(let o in e)if(Object.prototype.hasOwnProperty.call(e,o)){let s=e[o],i=O(s);i!==void 0&&!(Array.isArray(i)&&i.length===0)&&!(typeof i=="object"&&i!==null&&Object.keys(i).length===0)&&(t[o]=i)}return t}function se(e,t=1){let o=Math.round(e.r*255),s=Math.round(e.g*255),i=Math.round(e.b*255),n=Math.round(t*e.a*100)/100;return{hex:"#"+((1<<24)+(o<<16)+(s<<8)+i).toString(16).slice(1).toUpperCase(),opacity:n}}function D(e,t=1){let o=Math.round(e.r*255),s=Math.round(e.g*255),i=Math.round(e.b*255),n=Math.round(t*e.a*100)/100;return`rgba(${o}, ${s}, ${i}, ${n})`}function ae(e="var"){let t="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",o="";for(let s=0;s<6;s++){let i=Math.floor(Math.random()*t.length);o+=t[i]}return`${e}_${o}`}function L(e,{ignoreZero:t=!0,suffix:o="px"}={}){let{top:s,right:i,bottom:n,left:r}=e;if(!(t&&s===0&&i===0&&n===0&&r===0))return s===i&&i===n&&n===r?`${s}${o}`:i===r?s===n?`${s}${o} ${i}${o}`:`${s}${o} ${i}${o} ${n}${o}`:`${s}${o} ${i}${o} ${n}${o} ${r}${o}`}function G(e){if(e.type==="IMAGE")return{type:"IMAGE",imageRef:e.imageRef,scaleMode:e.scaleMode};if(e.type==="SOLID"){let{hex:t,opacity:o}=se(e.color,e.opacity);return o===1?t:D(e.color,o)}else{if(["GRADIENT_LINEAR","GRADIENT_RADIAL","GRADIENT_ANGULAR","GRADIENT_DIAMOND"].includes(e.type))return{type:e.type,gradientHandlePositions:e.gradientHandlePositions,gradientStops:e.gradientStops.map(({position:t,color:o})=>({position:t,color:se(o)}))};throw new Error(`Unknown paint type: ${e.type}`)}}function F(e){return e.visible??!0}function $(e){if(isNaN(e))throw new TypeError("Input must be a valid number");return Number(Number(e).toFixed(2))}function ue(e,t){let o=De(e),s=Le(e,t,o.mode)||{},i={...o,...s};return t&&"absoluteBoundingBox"in e&&e.absoluteBoundingBox&&"absoluteBoundingBox"in t&&t.absoluteBoundingBox&&(i.locationRelativeToParent={x:$(e.absoluteBoundingBox.x-t.absoluteBoundingBox.x),y:$(e.absoluteBoundingBox.y-t.absoluteBoundingBox.y)}),"absoluteBoundingBox"in e&&e.absoluteBoundingBox&&(i.dimensions={width:$(e.absoluteBoundingBox.width),height:$(e.absoluteBoundingBox.height)}),"constraints"in e&&e.constraints&&(i.constraints={horizontal:e.constraints.horizontal,vertical:e.constraints.vertical}),"opacity"in e&&typeof e.opacity=="number"&&e.opacity!==1&&(i.opacity=e.opacity),"rotation"in e&&typeof e.rotation=="number"&&e.rotation!==0&&(i.rotation=e.rotation),"cornerRadius"in e&&typeof e.cornerRadius=="number"&&(i.borderRadius=`${e.cornerRadius}px`),i}function le(e,t){if(t&&t.mode!=="none"){let{children:o,mode:s,axis:i}=t,n=Oe(i,s);if(o.length>0&&o.reduce((a,l)=>a?"layoutPositioning"in l&&l.layoutPositioning==="ABSOLUTE"?!0:n==="horizontal"?"layoutSizingHorizontal"in l&&l.layoutSizingHorizontal==="FILL":n==="vertical"?"layoutSizingVertical"in l&&l.layoutSizingVertical==="FILL":!1:!1,!0))return"stretch"}switch(e){case"MIN":return;case"MAX":return"flex-end";case"CENTER":return"center";case"SPACE_BETWEEN":return"space-between";case"BASELINE":return"baseline";default:return}}function ke(e){switch(e){case"MIN":return;case"MAX":return"flex-end";case"CENTER":return"center";case"STRETCH":return"stretch";default:return}}function ce(e){if(e==="FIXED")return"fixed";if(e==="FILL")return"fill";if(e==="HUG")return"hug"}function Oe(e,t){switch(e){case"primary":switch(t){case"row":return"horizontal";case"column":return"vertical"}case"counter":switch(t){case"row":return"horizontal";case"column":return"vertical"}}}function De(e){if(!k(e))return{mode:"none"};let t={mode:!e.layoutMode||e.layoutMode==="NONE"?"none":e.layoutMode==="HORIZONTAL"?"row":"column"},o=[];return e.overflowDirection?.includes("HORIZONTAL")&&o.push("x"),e.overflowDirection?.includes("VERTICAL")&&o.push("y"),o.length>0&&(t.overflowScroll=o),t.mode==="none"||(t.justifyContent=le(e.primaryAxisAlignItems??"MIN",{children:e.children,axis:"primary",mode:t.mode}),t.alignItems=le(e.counterAxisAlignItems??"MIN",{children:e.children,axis:"counter",mode:t.mode}),t.alignSelf=ke(e.layoutAlign),t.wrap=e.layoutWrap==="WRAP"?!0:void 0,t.gap=e.itemSpacing?`${e.itemSpacing??0}px`:void 0,(e.paddingTop||e.paddingBottom||e.paddingLeft||e.paddingRight)&&(t.padding=L({top:e.paddingTop??0,right:e.paddingRight??0,bottom:e.paddingBottom??0,left:e.paddingLeft??0}))),t}function Le(e,t,o){if(!U(e))return;let s={mode:o};if(s.sizing={horizontal:ce(e.layoutSizingHorizontal),vertical:ce(e.layoutSizingVertical)},k(t)&&!oe(e,t)&&(e.layoutPositioning==="ABSOLUTE"&&(s.position="absolute"),e.absoluteBoundingBox&&t.absoluteBoundingBox&&(s.locationRelativeToParent={x:$(e.absoluteBoundingBox.x-t.absoluteBoundingBox.x),y:$(e.absoluteBoundingBox.y-t.absoluteBoundingBox.y)})),ne("absoluteBoundingBox",e)){let i={};o==="row"?(!e.layoutGrow&&e.layoutSizingHorizontal=="FIXED"&&(i.width=e.absoluteBoundingBox.width),e.layoutAlign!=="STRETCH"&&e.layoutSizingVertical=="FIXED"&&(i.height=e.absoluteBoundingBox.height)):o==="column"?(e.layoutAlign!=="STRETCH"&&e.layoutSizingHorizontal=="FIXED"&&(i.width=e.absoluteBoundingBox.width),!e.layoutGrow&&e.layoutSizingVertical=="FIXED"&&(i.height=e.absoluteBoundingBox.height),e.preserveRatio&&(i.aspectRatio=e.absoluteBoundingBox?.width/e.absoluteBoundingBox?.height)):((!e.layoutSizingHorizontal||e.layoutSizingHorizontal==="FIXED")&&(i.width=e.absoluteBoundingBox.width),(!e.layoutSizingVertical||e.layoutSizingVertical==="FIXED")&&(i.height=e.absoluteBoundingBox.height)),Object.keys(i).length>0&&(i.width&&(i.width=$(i.width)),i.height&&(i.height=$(i.height)),s.dimensions=i)}return s}function de(e){return Object.fromEntries(Object.entries(e).map(([t,o])=>[t,{id:t,key:o.key,name:o.name,componentSetId:o.componentSetId}]))}function fe(e){return Object.fromEntries(Object.entries(e).map(([t,o])=>[t,{id:t,key:o.key,name:o.name,description:o.description}]))}function me(e){let t={colors:[]};return b("strokes",e)&&Array.isArray(e.strokes)&&e.strokes.length&&(t.colors=e.strokes.filter(F).map(G)),b("strokeWeight",e)&&typeof e.strokeWeight=="number"&&e.strokeWeight>0&&(t.strokeWeight=`${e.strokeWeight}px`),b("strokeDashes",e)&&Array.isArray(e.strokeDashes)&&e.strokeDashes.length&&(t.strokeDashes=e.strokeDashes),b("individualStrokeWeights",e,ie)&&(t.strokeWeight=L(e.individualStrokeWeights)),t}function ge(e){if(!b("effects",e))return{};let t=e.effects.filter(l=>l.visible),o=t.filter(l=>l.type==="DROP_SHADOW").map(Ge),s=t.filter(l=>l.type==="INNER_SHADOW").map(He),i=[...o,...s].join(", "),n=t.filter(l=>l.type==="LAYER_BLUR").map(pe).join(" "),r=t.filter(l=>l.type==="BACKGROUND_BLUR").map(pe).join(" "),a={};return i&&(e.type==="TEXT"?a.textShadow=i:a.boxShadow=i),n&&(a.filter=n),r&&(a.backdropFilter=r),a}function Ge(e){return`${e.offset.x}px ${e.offset.y}px ${e.radius}px ${e.spread??0}px ${D(e.color)}`}function He(e){return`inset ${e.offset.x}px ${e.offset.y}px ${e.radius}px ${e.spread??0}px ${D(e.color)}`}function pe(e){return`blur(${e.radius}px)`}function _(e){let t={},o={},s;"nodes"in e?s=Object.values(e.nodes).map(g=>g.document):(Object.assign(t,e.components),Object.assign(o,e.componentSets),s=e.document.children);let i=de(t),n=fe(o),{name:r,lastModified:a,thumbnailUrl:l}=e,d={styles:{}},c=s.filter(F).map(p=>he(d,p)).filter(p=>p!=null);return O({name:r,lastModified:a,thumbnailUrl:l||"",nodes:c,components:i,componentSets:n,globalVars:d})}function N(e,t,o){let[s]=Object.entries(e.styles).find(([n,r])=>JSON.stringify(r)===JSON.stringify(t))??[];if(s)return s;let i=ae(o);return e.styles[i]=t,i}function he(e,t,o){let{id:s,name:i,type:n}=t,r={id:s,name:i,type:n};if(b("absoluteBoundingBox",t)&&t.absoluteBoundingBox&&(r.absoluteBoundingBox={x:t.absoluteBoundingBox.x,y:t.absoluteBoundingBox.y,width:t.absoluteBoundingBox.width,height:t.absoluteBoundingBox.height},o&&b("absoluteBoundingBox",o)&&o.absoluteBoundingBox?r.boundingBox={x:t.absoluteBoundingBox.x-o.absoluteBoundingBox.x,y:t.absoluteBoundingBox.y-o.absoluteBoundingBox.y,width:t.absoluteBoundingBox.width,height:t.absoluteBoundingBox.height}:r.boundingBox=r.absoluteBoundingBox),o&&(r.parentId=o.id),b("layoutPositioning",t)&&(r.layoutPositioning=t.layoutPositioning),b("constraints",t)&&typeof t.constraints=="object"&&t.constraints){let c=t.constraints;r.constraints={horizontal:c.horizontal,vertical:c.vertical}}if(n==="INSTANCE"&&(b("componentId",t)&&(r.componentId=t.componentId),b("componentProperties",t)&&(r.componentProperties=Object.entries(t.componentProperties??{}).map(([c,{value:m,type:p}])=>({name:c,value:m.toString(),type:p}))),b("variantProperties",t)&&typeof t.variantProperties=="object"&&(r.variantProperties=t.variantProperties)),b("style",t)&&Object.keys(t.style).length){let c=t.style,m={fontFamily:c.fontFamily,fontWeight:c.fontWeight,fontSize:c.fontSize,lineHeight:c.lineHeightPx&&c.fontSize?`${c.lineHeightPx/c.fontSize}em`:void 0,letterSpacing:c.letterSpacing&&c.letterSpacing!==0&&c.fontSize?`${c.letterSpacing/c.fontSize*100}%`:void 0,textCase:c.textCase,textAlignHorizontal:c.textAlignHorizontal,textAlignVertical:c.textAlignVertical};r.textStyle=N(e,m,"style")}if(b("fills",t)&&Array.isArray(t.fills)&&t.fills.length){let c=t.fills.map(G);r.fills=N(e,c,"fill")}let a=me(t);a.colors.length&&(r.strokes=N(e,a,"stroke"));let l=ge(t);Object.keys(l).length&&(r.effects=N(e,l,"effect"));let d=ue(t,o);if(Object.keys(d).length>1&&(r.layout=N(e,d,"layout")),b("characters",t,te)&&(r.text=t.characters),b("opacity",t)&&typeof t.opacity=="number"&&t.opacity!==1&&(r.opacity=t.opacity),b("cornerRadius",t)&&typeof t.cornerRadius=="number"&&(r.borderRadius=`${t.cornerRadius}px`),b("rectangleCornerRadii",t,re)&&(r.borderRadius=`${t.rectangleCornerRadii[0]}px ${t.rectangleCornerRadii[1]}px ${t.rectangleCornerRadii[2]}px ${t.rectangleCornerRadii[3]}px`),b("children",t)&&t.children.length>0){let c=t.children.filter(F).map(m=>he(e,m,t)).filter(m=>m!=null);c.length&&(r.children=c)}return n==="VECTOR"&&(r.type="IMAGE-SVG"),r}import{exec as Me}from"child_process";import{promisify as Ve}from"util";var je=Ve(Me);async function ye(e,t={}){try{let o=await fetch(e,t);if(!o.ok)throw new Error(`Fetch failed with status ${o.status}: ${o.statusText}`);return await o.json()}catch(o){u.log(`[fetchWithRetry] Initial fetch failed for ${e}: ${o.message}. Likely a corporate proxy or SSL issue. Attempting curl fallback.`);let i=`curl -s -L ${ze(t.headers).join(" ")} "${e}"`;try{u.log(`[fetchWithRetry] Executing curl command: ${i}`);let{stdout:n,stderr:r}=await je(i);if(r){if(!n||r.toLowerCase().includes("error")||r.toLowerCase().includes("fail"))throw new Error(`Curl command failed with stderr: ${r}`);u.log(`[fetchWithRetry] Curl command for ${e} produced stderr (but might be informational): ${r}`)}if(!n)throw new Error("Curl command returned empty stdout.");return JSON.parse(n)}catch(n){throw u.error(`[fetchWithRetry] Curl fallback also failed for ${e}: ${n.message}`),o}}}function ze(e){if(!e)return[];let t=[];return e instanceof Headers?e.forEach((o,s)=>{t.push(`-H "${s}: ${o}"`)}):Array.isArray(e)?e.forEach(([o,s])=>{t.push(`-H "${o}: ${s}"`)}):Object.entries(e).forEach(([o,s])=>{t.push(`-H "${o}: ${s}"`)}),t}import Ue from"js-yaml";var M=class{apiKey;oauthToken;useOAuth;baseUrl="https://api.figma.com/v1";constructor({figmaApiKey:t,figmaOAuthToken:o,useOAuth:s}){this.apiKey=t||"",this.oauthToken=o||"",this.useOAuth=!!s&&!!this.oauthToken}async request(t){try{u.log(`Calling ${this.baseUrl}${t}`);let o={};return this.useOAuth?(u.log("Using OAuth Bearer token for authentication"),o.Authorization=`Bearer ${this.oauthToken}`):(u.log("Using Personal Access Token for authentication"),o["X-Figma-Token"]=this.apiKey),await ye(`${this.baseUrl}${t}`,{headers:o})}catch(o){throw o instanceof Error?new Error(`Failed to make request to Figma API: ${o.message}`):new Error(`Failed to make request to Figma API: ${o}`)}}async getImageFills(t,o,s){if(o.length===0)return[];let i=[],n=`/files/${t}/images`,r=await this.request(n),{images:a={}}=r.meta;return i=o.map(async({imageRef:l,fileName:d})=>{let c=a[l];return c?B(d,s,c):""}),Promise.all(i)}async getImages(t,o,s,i,n){let r=o.filter(({fileType:g})=>g==="png").map(({nodeId:g})=>g),a=r.length>0?this.request(`/images/${t}?ids=${r.join(",")}&format=png&scale=${i}`).then(({images:g={}})=>g):{},l=o.filter(({fileType:g})=>g==="svg").map(({nodeId:g})=>g),d=[`ids=${l.join(",")}`,"format=svg",`svg_outline_text=${n.outlineText}`,`svg_include_id=${n.includeId}`,`svg_simplify_stroke=${n.simplifyStroke}`].join("&"),c=l.length>0?this.request(`/images/${t}?${d}`).then(({images:g={}})=>g):{},m=await Promise.all([a,c]).then(([g,T])=>({...g,...T})),p=o.map(({nodeId:g,fileName:T})=>{let x=m[g];return x?B(T,s,x):!1}).filter(g=>!!g);return Promise.all(p)}async getFile(t,o){try{let s=`/files/${t}${o?`?depth=${o}`:""}`;u.log(`Retrieving Figma file: ${t} (depth: ${o??"default"})`);let i=await this.request(s);u.log("Got response");let n=_(i);return H("figma-raw.yml",i),H("figma-simplified.yml",n),n}catch(s){throw console.error("Failed to get file:",s),s}}async getNode(t,o,s){let i=`/files/${t}/nodes?ids=${o}${s?`&depth=${s}`:""}`,n=await this.request(i);u.log("Got response from getNode, now parsing."),H("figma-raw.yml",n);let r=_(n);return H("figma-simplified.yml",r),r}async getScreenshot(t,o,s){let{nodeId:i,fileName:n,scale:r=1}=o;u.log(`\u{1F3AF} Tentando capturar screenshot do node ${i} com escala ${r}x`);try{u.log(`\u{1F50D} Verificando se o node ${i} existe no arquivo ${t}`);let a=await this.getNode(t,i);if(!a.nodes||a.nodes.length===0){u.log(`\u26A0\uFE0F Node ${i} n\xE3o encontrado ou n\xE3o \xE9 um frame v\xE1lido`),u.log("\u{1F504} Tentando capturar como imagem normal...");let m=`/images/${t}?ids=${i}&format=png&scale=${r}`,g=(await this.request(m)).images?.[i];if(!g)throw new Error(`Node ${i} n\xE3o pode ser capturado nem como screenshot nem como imagem. Verifique se o nodeId est\xE1 correto e \xE9 um frame v\xE1lido.`);return u.log(`\u2705 Capturando como imagem normal: ${g}`),B(n,s,g)}u.log(`\u2705 Node ${i} encontrado, capturando screenshot...`);let l=`/images/${t}?ids=${i}&format=png&scale=${r}`,c=(await this.request(l)).images?.[i];if(!c)throw new Error(`API do Figma retornou resposta vazia para o node ${i}. Isso pode indicar que o node n\xE3o \xE9 um frame v\xE1lido ou h\xE1 problemas de permiss\xE3o.`);return u.log(`\u{1F4F8} Screenshot URL obtida: ${c}`),B(n,s,c)}catch(a){throw u.error(`\u274C Erro ao capturar screenshot do node ${i}:`,a),a instanceof Error?a.message.includes("404")?new Error(`Node ${i} n\xE3o encontrado no arquivo ${t}. Verifique se o nodeId est\xE1 correto.`):a.message.includes("403")?new Error(`Sem permiss\xE3o para acessar o node ${i}. Verifique se o token do Figma tem as permiss\xF5es corretas.`):new Error(`Falha ao capturar screenshot do node ${i}: ${a.message}`):new Error(`Erro desconhecido ao capturar screenshot do node ${i}`)}}};function H(e,t){try{if(process.env.NODE_ENV!=="development")return;let o="logs";try{P.accessSync(process.cwd(),P.constants.W_OK)}catch(s){u.log("Failed to write logs:",s);return}P.existsSync(o)||P.mkdirSync(o),P.writeFileSync(`${o}/${e}`,Ue.dump(t))}catch(o){console.debug("Failed to write logs:",o)}}function Se(e){let{name:t,lastModified:o,thumbnailUrl:s,nodes:i,globalVars:n}=e,r=_e(i,n.styles),a=xe(r),l=et(r,n.styles),d=tt(r),c=ot(r),m=it(r),p=nt(r);return{metadata:{name:t,lastModified:o,thumbnailUrl:s},designSystem:l,components:a,layouts:d,interactiveElements:c,images:m,svgIcons:p}}function _e(e,t){return e.map(o=>be(o,t))}function be(e,t){let o={...e};return e.fills&&typeof e.fills=="string"&&(o.fills=t[e.fills]),e.textStyle&&typeof e.textStyle=="string"&&(o.textStyle=t[e.textStyle]),e.layout&&typeof e.layout=="string"&&(o.layout=t[e.layout]),e.strokes&&typeof e.strokes=="string"&&(o.strokes=t[e.strokes]),e.effects&&typeof e.effects=="string"&&(o.effects=t[e.effects]),e.children&&(o.children=e.children.map(s=>be(s,t))),o}function xe(e){let t=[];for(let o of e){let s=We(o);s&&t.push(s),o.children&&t.push(...xe(o.children))}return t}function We(e){return qe(e)?Je(e):Ke(e)?Ye(e):Xe(e)?Qe(e):null}function qe(e){let t=e.name.toLowerCase();return t.includes("product")&&t.includes("card")||t.includes("favourite")&&t.includes("card")||!!(e.children&&e.children.some(o=>o.name.toLowerCase().includes("title"))&&e.children.some(o=>o.name.toLowerCase().includes("price"))&&e.children.some(o=>o.name.toLowerCase().includes("star")))}function Ke(e){let t=e.name.toLowerCase();return t.includes("navigation")||t.includes("sidebar")||t.includes("top bar")||!!(e.children&&e.children.length>3&&e.children.every(o=>o.name.toLowerCase().includes("item")))}function Xe(e){let t=e.name.toLowerCase();return t.includes("btn")||t.includes("button")||e.type==="TEXT"&&!!(e.text?.includes("Edit")||e.text?.includes("Add")||e.text?.includes("Cart"))}function Je(e){let t=[],o={};if(e.layout&&typeof e.layout=="object"){let s=e.layout;o.width=s.dimensions?.width,o.height=s.dimensions?.height}if(e.fills&&Array.isArray(e.fills)&&(o.backgroundColor=we(e.fills)),e.borderRadius&&(o.borderRadius=e.borderRadius),e.effects&&typeof e.effects=="object"&&(o.shadow=ct(e.effects)),e.children)for(let s of e.children){let i=Ze(s);i&&t.push(i)}return{id:e.id,name:e.name,type:"card",description:"Card de produto com imagem, t\xEDtulo, pre\xE7o, avalia\xE7\xE3o e bot\xE3o de a\xE7\xE3o",properties:o,elements:t}}function Ye(e){return{id:e.id,name:e.name,type:"navigation",description:"Componente de navega\xE7\xE3o com itens de menu",properties:{},elements:[]}}function Qe(e){return{id:e.id,name:e.name,type:"button",description:"Bot\xE3o interativo",properties:{},elements:[]}}function Ze(e){if(!e.layout||typeof e.layout!="object")return null;let t=e.layout,o={x:t.locationRelativeToParent?.x||0,y:t.locationRelativeToParent?.y||0,width:t.dimensions?.width||0,height:t.dimensions?.height||0},s="container";e.type==="TEXT"?s="text":e.type==="IMAGE-SVG"||e.name.toLowerCase().includes("image")?s="image":(e.name.toLowerCase().includes("btn")||e.name.toLowerCase().includes("button"))&&(s="button");let i={};return e.fills&&Array.isArray(e.fills)&&(i.backgroundColor=we(e.fills)),e.borderRadius&&(i.borderRadius=e.borderRadius),{name:e.name,type:s,content:e.text,style:i,position:o}}function et(e,t){let o={primary:[],secondary:[],accent:[],background:[],text:[]},s=[],i=[],n=[];return Object.values(t).forEach(r=>{Array.isArray(r)&&r.forEach(a=>{let l=W(a);l&&ut(l,o)})}),Object.entries(t).forEach(([r,a])=>{if(dt(a)){let l=a;s.push({name:`Text Style ${r}`,fontFamily:l.fontFamily||"Unknown",fontWeight:l.fontWeight||400,fontSize:l.fontSize||16,lineHeight:l.lineHeight,color:"#000000",usage:[]})}}),{colors:o,typography:s,borderRadius:[...new Set(i)],shadows:[...new Set(n)]}}function tt(e){return[]}function ot(e){return[]}function it(e){let t=[];function o(s){for(let i of s){if(i.fills&&Array.isArray(i.fills))for(let n of i.fills)n&&typeof n=="object"&&n.type==="IMAGE"&&n.imageRef&&t.push({id:i.id,name:i.name||"Image",type:rt(i.name||""),format:"png",dimensions:{width:i.layout?.dimensions?.width||0,height:i.layout?.dimensions?.height||0},downloadable:!0,imageRef:n.imageRef});i.children&&o(i.children)}}return o(e),t}function nt(e){let t=new Map;function o(i,n){for(let r of i){if(r.type==="IMAGE-SVG"||r.type==="BOOLEAN_OPERATION"){let a=n||r.name||"unnamed",l=at(a);t.has(l)||t.set(l,[]),t.get(l).push(r)}if(r.children&&st(r)){let a=r.name||"icon";o(r.children,a)}else r.children&&o(r.children,n)}}o(e);let s=[];for(let[i,n]of t)if(n.length>0){let r=lt(i,n);r&&s.push(r)}return s}function rt(e){let t=e.toLowerCase();return t.includes("logo")||t.includes("brand")?"logo":t.includes("icon")||t.includes("chevron")||t.includes("arrow")?"icon":t.includes("illustration")||t.includes("graphic")?"illustration":"photo"}function st(e){if(!e.children)return!1;let t=e.name?.toLowerCase()||"",o=t.includes("icon")||t.includes("chevron")||t.includes("arrow")||t.includes("star")||t.includes("heart")||t.includes("love"),s=e.children.some(i=>i.type==="IMAGE-SVG"||i.type==="BOOLEAN_OPERATION");return o&&s}function at(e){return e.replace(/\s+(copy|Copy|\d+)$/g,"").replace(/\s*-\s*\d+$/g,"").replace(/\s+/g,"-").toLowerCase()}function lt(e,t){if(t.length===0)return null;let o=1/0,s=1/0,i=-1/0,n=-1/0,r=[],a=[],l=[];for(let d of t){if(d.layout&&typeof d.layout=="object"){let m=d.layout;if(m.locationRelativeToParent){let p=m.locationRelativeToParent.x||0,g=m.locationRelativeToParent.y||0,T=m.dimensions?.width||0,x=m.dimensions?.height||0;o=Math.min(o,p),s=Math.min(s,g),i=Math.max(i,p+T),n=Math.max(n,g+x)}}if(d.fills&&Array.isArray(d.fills)){for(let m of d.fills)if(m&&typeof m=="object"){let p=W(m);p&&!a.includes(p)&&a.push(p)}}if(d.strokes&&Array.isArray(d.strokes)&&d.strokes.length>0){let m=d.strokes[0];if(m&&typeof m=="object"&&m.colors)for(let p of m.colors)p&&!l.includes(p)&&l.push(p)}let c=d.layout;r.push({d:`M0,0 L${c?.dimensions?.width||0},${c?.dimensions?.height||0}`,fill:a[0],stroke:l[0]})}return{id:t[0].id,name:e,type:e.includes("icon")?"icon":"illustration",paths:r,viewBox:{width:i!==-1/0&&o!==1/0?i-o:24,height:n!==-1/0&&s!==1/0?n-s:24},fills:a,strokes:l}}function we(e){if(!e||e.length===0)return"";let t=e[0];return W(t)}function W(e){return typeof e=="string"?e:e.hex?e.hex:e.rgba?e.rgba:""}function ct(e){return!e||!e.boxShadow?"":e.boxShadow}function ut(e,t){let o=e.toLowerCase();o.includes("4880ff")||o.includes("blue")?t.primary.push(e):o.includes("ff9500")||o.includes("orange")?t.secondary.push(e):o.includes("f93c65")||o.includes("pink")?t.accent.push(e):o.includes("f5f6fa")||o.includes("gray")||o.includes("grey")?t.background.push(e):(o.includes("202224")||o.includes("black"))&&t.text.push(e)}function dt(e){return e&&typeof e=="object"&&"fontFamily"in e}import ve from"js-yaml";var mt={name:"Figma MCP Server",version:"3.0.4"};function Ee(e,{isHTTP:t=!1,outputFormat:o="yaml",enhancedOutput:s=!1}={}){let i=new ft(mt),n=new M(e);return pt(i,n,o,s),u.isHTTP=t,i}function pt(e,t,o,s=!0){e.tool("figma_dados","\u{1F4CA} QUANDO USAR: Para extrair TODOS os dados do design - cores, textos, estilos, componentes e propriedades. Use diretamente com o nodeId da URL para obter dados espec\xEDficos de componentes/frames. \xC9 a ferramenta principal para implementa\xE7\xE3o pixel-perfect.",{fileKey:y.string().describe("The key of the Figma file to fetch, often found in a provided URL like figma.com/(file|design)/<fileKey>/..."),nodeId:y.string().optional().describe("The ID of the node to fetch, often found as URL parameter node-id=<nodeId>, always use if provided"),depth:y.number().optional().describe("OPTIONAL. Do NOT use unless explicitly requested by the user. Controls how many levels deep to traverse the node tree,")},async({fileKey:i,nodeId:n,depth:r})=>{try{u.log(`Fetching ${r?`${r} layers deep`:"all layers"} of ${n?`node ${n} from file`:"full file"} ${i}`);let a;n?a=await t.getNode(i,n,r):a=await t.getFile(i,r),u.log(`Successfully fetched file: ${a.name}`);let l;s?(u.log("Enhancing design data with intelligent parsing..."),l=Se(a)):(u.log("Generating hierarchical structure with detailed globalVars..."),l={metadata:{name:a.name,lastModified:a.lastModified,thumbnailUrl:a.thumbnailUrl,components:a.components,componentSets:a.componentSets},nodes:a.nodes,globalVars:a.globalVars}),u.log(`Generating ${o.toUpperCase()} result from file`);let d=o==="json"?JSON.stringify(l,null,2):ve.dump(l);return u.log("Sending result to client"),{content:[{type:"text",text:d}]}}catch(a){let l=a instanceof Error?a.message:JSON.stringify(a);return u.error(`Error fetching file ${i}:`,l),{isError:!0,content:[{type:"text",text:`Error fetching file: ${l}`}]}}}),e.tool("figma_imagens","\u{1F5BC}\uFE0F QUANDO USAR: Para baixar imagens espec\xEDficas - \xEDcones, fotos, logos. Use quando j\xE1 souber exatamente quais assets precisar para implementa\xE7\xE3o (bot\xF5es, imagens de fundo, \xEDcones).",{fileKey:y.string().describe("The key of the Figma file containing the node"),nodes:y.object({nodeId:y.string().describe("The ID of the Figma image node to fetch, formatted as 1234:5678"),imageRef:y.string().optional().describe("If a node has an imageRef fill, you must include this variable. Leave blank when downloading Vector SVG images."),fileName:y.string().describe("The local name for saving the fetched file")}).array().describe("The nodes to fetch as images"),pngScale:y.number().positive().optional().default(2).describe("Export scale for PNG images. Optional, defaults to 2 if not specified. Affects PNG images only."),localPath:y.string().describe("The absolute path to the directory where images are stored in the project. If the directory does not exist, it will be created. The format of this path should respect the directory format of the operating system you are running on. Don't use any special character escaping in the path name either."),svgOptions:y.object({outlineText:y.boolean().optional().default(!0).describe("Whether to outline text in SVG exports. Default is true."),includeId:y.boolean().optional().default(!1).describe("Whether to include IDs in SVG exports. Default is false."),simplifyStroke:y.boolean().optional().default(!0).describe("Whether to simplify strokes in SVG exports. Default is true.")}).optional().default({}).describe("Options for SVG export")},async({fileKey:i,nodes:n,localPath:r,svgOptions:a,pngScale:l})=>{try{let d=n.filter(({imageRef:x})=>!!x),c=t.getImageFills(i,d,r),m=n.filter(({imageRef:x})=>!x).map(({nodeId:x,fileName:A})=>({nodeId:x,fileName:A,fileType:A.endsWith(".svg")?"svg":"png"})),p=t.getImages(i,m,r,l,a),T=(await Promise.all([c,p]).then(([x,A])=>[...x,...A])).filter(x=>x.length>0);return u.log(`Downloaded ${T.length} assets to ${r}`),{content:[{type:"text",text:`Successfully downloaded ${T.length} assets to ${r}: ${T.map(x=>`- ${x}`).join(` `)}`}]}}catch(d){let c=d instanceof Error?d.message:JSON.stringify(d);return u.error("Error downloading assets:",c),{isError:!0,content:[{type:"text",text:`Error downloading assets: ${c}`}]}}}),e.tool("figma_layout","\u{1F3D7}\uFE0F QUANDO USAR: Para entender ONDE colocar cada coisa na tela - como se fosse o 'esqueleto' da p\xE1gina. Mostra posi\xE7\xF5es, tamanhos e hierarquia SEM cores/estilos. Ideal para criar v\xE1rios componentes de uma tela e organiz\xE1-los corretamente.",{fileKey:y.string().describe("The key of the Figma file to analyze layout from"),nodeId:y.string().optional().describe("The ID of the specific node to analyze layout from. If not provided, analyzes the entire file"),depth:y.number().int().min(1).max(5).optional().default(3).describe("How deep to traverse the layout tree. Optional, defaults to 3."),includeText:y.boolean().optional().default(!1).describe("Whether to include text content in the layout structure. Default is false for cleaner skeleton view.")},async({fileKey:i,nodeId:n,depth:r,includeText:a})=>{try{let V=function(w){return w.reduce((h,S)=>h+1+(S.children?V(S.children):0),0)},j=function(w){return w.reduce((h,S)=>{let f=S.children?j(S.children):0;return Math.max(h,1+f)},0)},q=function(w){let h={layoutPatterns:[],responsiveHints:[],structureAnalysis:{}},S=z(w),f=S.filter(v=>v.layoutProperties?.flexDirection);f.length>0&&h.layoutPatterns.push({type:"flexbox",count:f.length,examples:f.slice(0,3).map(v=>({name:v.name,direction:v.layoutProperties.flexDirection}))});let I=S.filter(v=>v.role==="navigation"&&v.name.toLowerCase().includes("sidebar"));I.length>0&&h.layoutPatterns.push({type:"sidebar-layout",description:"Layout com sidebar detectado",elements:I.map(v=>v.name)});let E=S.reduce((v,K)=>(v[K.role]=(v[K.role]||0)+1,v),{});return h.structureAnalysis={elementsByRole:E,hasNavigation:E.navigation>0,hasHeader:E.header>0,hasMainContent:E["main-content"]>0},h},z=function(w){let h=[];return w.forEach(S=>{h.push(S),S.children&&h.push(...z(S.children))}),h};var l=V,d=j,c=q,m=z;u.log(`\u{1F3D7}\uFE0F Extraindo estrutura de layout do arquivo ${i} ${n?`(node: ${n})`:"(arquivo completo)"}`);let p;n?p=await t.getNode(i,n,r):p=await t.getFile(i,r);let g=(w,h=0,S)=>!w||w.length===0?[]:w.map(f=>{let I={id:f.id,name:f.name,type:f.type,level:h};if(f.absoluteBoundingBox&&(I.dimensions={x:Math.round(f.absoluteBoundingBox.x),y:Math.round(f.absoluteBoundingBox.y),width:Math.round(f.absoluteBoundingBox.width),height:Math.round(f.absoluteBoundingBox.height)},S&&(I.relativePosition={x:Math.round(f.absoluteBoundingBox.x-S.x),y:Math.round(f.absoluteBoundingBox.y-S.y)})),f.layout){let E={};f.layout.mode&&f.layout.mode!=="none"&&(E.flexDirection=f.layout.mode),f.layout.justifyContent&&(E.justifyContent=f.layout.justifyContent),f.layout.alignItems&&(E.alignItems=f.layout.alignItems),f.layout.gap&&(E.gap=f.layout.gap),f.layout.padding&&(E.padding=f.layout.padding),Object.keys(E).length>0&&(I.layoutProperties=E)}return f.constraints&&(I.constraints={horizontal:f.constraints.horizontal,vertical:f.constraints.vertical}),a&&f.type==="TEXT"&&f.text&&(I.textContent=f.text),I.role=T(f),f.children&&f.children.length>0&&h<(r||3)&&(I.children=g(f.children,h+1,f.absoluteBoundingBox)),I}),T=w=>{let h=w.name?.toLowerCase()||"",S=w.type;return h.includes("sidebar")||h.includes("nav")||h.includes("menu")?"navigation":h.includes("header")||h.includes("top bar")||h.includes("navbar")?"header":h.includes("footer")?"footer":h.includes("main")||h.includes("content")||h.includes("body")?"main-content":S==="FRAME"?h.includes("container")||h.includes("wrapper")?"container":"section":S==="INSTANCE"||h.includes("button")||h.includes("btn")?"interactive":S==="TEXT"?"text":S==="GROUP"?"group":"element"},x=g(p.nodes||[]),A={metadata:{fileName:p.name,extractedAt:new Date().toISOString(),totalElements:V(x),maxDepth:j(x)},layoutSkeleton:x,layoutInsights:q(x)},Te=o==="json"?JSON.stringify(A,null,2):ve.dump(A);return u.log("\u2705 Estrutura de layout extra\xEDda com sucesso"),{content:[{type:"text",text:`\u{1F3D7}\uFE0F **Estrutura de Layout Extra\xEDda** \u{1F4CA} **Resumo**: ${A.metadata.totalElements} elementos em ${A.metadata.maxDepth} n\xEDveis \u{1F3AF} **Padr\xF5es Detectados**: ${A.layoutInsights.layoutPatterns.length} padr\xF5es de layout \u{1F4CB} **Estrutura Hier\xE1rquica**: \`\`\`${o} ${Te} \`\`\` \u{1F4A1} **Como usar**: - Use esta estrutura para entender o layout antes da implementa\xE7\xE3o - As dimens\xF5es e posi\xE7\xF5es ajudam no posicionamento CSS - Os 'roles' identificam a fun\xE7\xE3o de cada elemento - layoutProperties mostram configura\xE7\xF5es de flexbox quando dispon\xEDveis - Use com outros tools do MCP para download de assets espec\xEDficos`}]}}catch(p){let g=p instanceof Error?p.message:JSON.stringify(p);return u.error("Error extracting layout:",g),{isError:!0,content:[{type:"text",text:`Error extracting layout: ${g}`}]}}}),e.tool("figma_captura","\u{1F4F8} QUANDO USAR: Para tirar uma 'foto' da tela/frame completa. Use quando quiser comparar visualmente se sua implementa\xE7\xE3o est\xE1 igual ao design original - valida\xE7\xE3o pixel-perfect.",{fileKey:y.string().describe("The key of the Figma file containing the frame"),nodeId:y.string().describe("The ID of the frame/component to capture as screenshot, formatted as 1234:5678"),fileName:y.string().describe("The local name for saving the screenshot (e.g., 'dashboard-desktop.png', 'mobile-screen.png')"),localPath:y.string().describe("The absolute path to the directory where screenshot will be stored. If the directory does not exist, it will be created."),scale:y.number().int().min(1).max(4).optional().default(2).describe("Scale for the screenshot (1 = 1x, 2 = 2x retina). Default is 2 for high quality.")},async({fileKey:i,nodeId:n,fileName:r,localPath:a,scale:l})=>{try{u.log(`Capturing screenshot of node ${n} from file ${i} at ${l}x scale`);let d=await t.getScreenshot(i,{nodeId:n,fileName:r,scale:l},a);return u.log(`Screenshot successfully saved to: ${d}`),{content:[{type:"text",text:`\u2705 Screenshot capturado com sucesso! \u{1F4C1} Arquivo: ${d} \u{1F50D} Escala: ${l}x \u{1F4CB} Frame ID: ${n} \u{1F4A1} Agora voc\xEA pode usar este screenshot para: - Comparar sua implementa\xE7\xE3o com o design original - Validar fidelidade pixel-perfect - Identificar elementos esquecidos - Verificar posicionamento e espa\xE7amentos \u{1F3AF} Pr\xF3ximo passo: Pe\xE7a para a LLM analisar a imagem e comparar com sua implementa\xE7\xE3o!`}]}}catch(d){let c=d instanceof Error?d.message:JSON.stringify(d);return u.error("Error capturing screenshot:",c),{isError:!0,content:[{type:"text",text:`Error capturing screenshot: ${c}`}]}}})}ht({path:yt(process.cwd(),".env")});async function St(){let e=process.env.NODE_ENV==="cli"||process.argv.includes("--stdio"),t=Y(e),o=Ee(t.auth,{isHTTP:!e,outputFormat:t.outputFormat});if(e){let s=new gt;await o.connect(s)}else console.log(`Initializing Figma MCP Server in HTTP mode on port ${t.port}...`),await ee(t.port,o)}process.argv[1]&&St().catch(e=>{console.error("Failed to start server:",e),process.exit(1)});export{Y as a,Ee as b,St as c};