{"openapi":"3.1.0","info":{"title":"Efecto Design API","version":"1.0.0","description":"The Efecto Design API gives AI agents and developer tools programmatic control over the Efecto design studio. Create sessions, execute design tools (artboards, nodes, styling, layout, theming, export), manage sharing/permissions, upload media, and publish sites — all via REST.\n\n## How it works\n\n1. **Create a session** → receive a `sessionId` and `designUrl`\n2. **User opens `designUrl`** in their browser (connects via SSE)\n3. **Agent executes tools** via `POST /sessions/{id}/execute` — browser runs them client-side\n4. **Agent polls or long-polls** for results\n5. **Close session** when done\n\n## Authentication\n\nSession endpoints require no auth — sessions are ephemeral and rate-limited per IP.\nSharing and publishing endpoints require authentication (session cookie).","contact":{"name":"Efecto","url":"https://efecto.app"},"license":{"name":"MIT"}},"servers":[{"url":"https://efecto.app/api/v1","description":"Production"},{"url":"http://localhost:3000/api/v1","description":"Local development"}],"tags":[{"name":"Sessions","description":"Create, inspect, and close design sessions. A session bridges an external agent (CLI, MCP server) with a user's browser running Efecto."},{"name":"Tool Execution","description":"Execute design tools within a session. Tools modify the document client-side in the browser. Use `?wait=true` for synchronous (long-poll) or poll for async results."},{"name":"Tool Discovery","description":"List available design tools and their JSON Schema parameter definitions. Use this for dynamic tool registration in MCP servers and SDK code generators."},{"name":"Media","description":"Upload images, proxy external images (SSRF-protected), and fetch icon SVGs."},{"name":"Sharing","description":"Manage document shares (invite by email, link sharing) and check permissions."},{"name":"Publishing","description":"Publish designs to Vercel as static sites."},{"name":"AI Chat","description":"Streaming AI chat with tool use for interactive design assistance."},{"name":"Configuration","description":"Realtime connection config and theme management."}],"paths":{"/design/sessions":{"post":{"tags":["Sessions"],"summary":"Create a design session","operationId":"createSession","description":"Creates a new ephemeral design session. Returns a `sessionId` and a `designUrl` that the user must open in their browser before tools can execute.\n\nSessions expire after 15 minutes of inactivity. Max 10 sessions per IP.","requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSessionRequest"}}}},"responses":{"201":{"description":"Session created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSessionResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/design/sessions/{sessionId}":{"parameters":[{"$ref":"#/components/parameters/SessionId"}],"get":{"tags":["Sessions"],"summary":"Get session status","operationId":"getSession","description":"Returns session metadata, connection state, and pending/completed call counts.","responses":{"200":{"description":"Session info","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSessionResponse"}}}},"404":{"$ref":"#/components/responses/NotFound"}}},"delete":{"tags":["Sessions"],"summary":"Close a session","operationId":"closeSession","description":"Closes and deletes the session. Disconnects the browser SSE stream.","responses":{"200":{"description":"Session closed","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","const":true}},"required":["success"]}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/design/sessions/{sessionId}/execute":{"parameters":[{"$ref":"#/components/parameters/SessionId"}],"post":{"tags":["Tool Execution"],"summary":"Execute a design tool","operationId":"executeTool","description":"Pushes a tool call into the session queue. The browser picks it up via SSE, executes it in the browser's design engine, and posts the result back.\n\n**Async (default)**: Returns 202 with a `callId`. Poll via `/poll/{callId}`.\n\n**Sync (long-poll)**: Add `?wait=true` to block up to 30 seconds for the result. Returns 200 with the result inline.","parameters":[{"name":"wait","in":"query","description":"If `true`, long-poll up to 30 seconds for the tool result instead of returning immediately.","schema":{"type":"boolean","default":false}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecuteToolRequest"}}}},"responses":{"200":{"description":"Tool completed (when `wait=true` and result available within timeout)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecuteToolResponse"}}}},"202":{"description":"Tool call queued (async mode)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecuteToolResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/design/sessions/{sessionId}/poll/{callId}":{"parameters":[{"$ref":"#/components/parameters/SessionId"},{"$ref":"#/components/parameters/CallId"}],"get":{"tags":["Tool Execution"],"summary":"Poll for tool result","operationId":"pollToolResult","description":"Polls for the result of a specific tool call. Returns the current status and result if completed.","responses":{"200":{"description":"Current status of the tool call","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PollResultResponse"}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/design/sessions/{sessionId}/result":{"parameters":[{"$ref":"#/components/parameters/SessionId"}],"post":{"tags":["Tool Execution"],"summary":"Submit tool result (browser → server)","operationId":"submitToolResult","description":"Called by the browser after executing a tool. Posts the result back to the server so the polling agent receives it. Not typically called by external agents.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitResultRequest"}}}},"responses":{"200":{"description":"Result accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitResultResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/design/sessions/{sessionId}/stream":{"parameters":[{"$ref":"#/components/parameters/SessionId"}],"get":{"tags":["Sessions"],"summary":"SSE event stream (browser)","operationId":"sessionStream","description":"Server-Sent Events stream for the browser. Delivers tool calls from the agent, heartbeats (every 15s), and session lifecycle events.\n\n**Event types**: `connected`, `tool_call`, `heartbeat`, `session_closed`","responses":{"200":{"description":"SSE event stream","content":{"text/event-stream":{"schema":{"type":"string","description":"Server-Sent Events stream. Each event is a JSON object with a `type` field:\n- `connected`: `{ type: \"connected\", sessionId: string }`\n- `tool_call`: `{ type: \"tool_call\", callId: string, tool: string, input: object }`\n- `heartbeat`: `{ type: \"heartbeat\", timestamp: number }`\n- `session_closed`: `{ type: \"session_closed\", reason: string }`"}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/design/tools":{"get":{"tags":["Tool Discovery"],"summary":"List all design tools","operationId":"listTools","description":"Returns all available design tools with their names, descriptions, and JSON Schema input definitions. Use this for MCP server registration, SDK code generation, or dynamic tool discovery.","responses":{"200":{"description":"Tool list","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToolListResponse"}}}}}},"post":{"tags":["Tool Discovery"],"summary":"Get schema for a specific tool","operationId":"getToolSchema","description":"Returns the full JSON Schema definition and execution guidance for a single tool. Accepts `studio_*` names for backward compatibility (auto-mapped to `efecto_*`).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string","description":"Tool name (e.g., \"efecto_add_node\" or legacy \"studio_add_node\")"},"input":{"type":"object","additionalProperties":true,"description":"Optional sample input to include in execution guidance"}},"required":["tool"]}}}},"responses":{"200":{"description":"Tool schema and execution guidance","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToolSchemaResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"description":"Unknown tool","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"available_tools":{"type":"array","items":{"type":"string"}}}}}}}}}},"/design/upload-image":{"post":{"tags":["Media"],"summary":"Upload or fetch an image","operationId":"uploadImage","description":"Uploads an image file (multipart/form-data) or fetches one from a URL (JSON body). Returns a blob URL usable in image nodes.","requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary","description":"Image file to upload"}},"required":["file"]}},"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"URL of an image to fetch server-side"}},"required":["url"]}}}},"responses":{"200":{"description":"Image uploaded","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"Blob URL of the uploaded image"}},"required":["url"]}}}},"400":{"$ref":"#/components/responses/BadRequest"}}}},"/design/image-proxy":{"get":{"tags":["Media"],"summary":"Proxy an external image","operationId":"proxyImage","description":"Proxies an external image URL to avoid CORS issues. SSRF-protected — blocks private/internal IPs.","parameters":[{"name":"url","in":"query","required":true,"description":"External image URL to proxy","schema":{"type":"string","format":"uri"}}],"responses":{"200":{"description":"Image binary data","content":{"image/*":{"schema":{"type":"string","format":"binary"}}}},"400":{"$ref":"#/components/responses/BadRequest"}}}},"/design/icons/svg":{"get":{"tags":["Media"],"summary":"Get icon SVG","operationId":"getIconSvg","description":"Returns an SVG for a named icon. Supports 4 libraries: Phosphor (~7k icons), Lucide (~1.5k), Heroicons (~600), Radix (~300). Response is cached with 1-year immutable headers.","parameters":[{"name":"name","in":"query","required":true,"description":"Icon name in kebab-case (e.g., \"arrow-right\", \"google-logo\")","schema":{"type":"string"}},{"name":"library","in":"query","description":"Icon library","schema":{"type":"string","enum":["phosphor","lucide","heroicons","radix"],"default":"phosphor"}},{"name":"weight","in":"query","description":"Icon weight/variant (Phosphor: thin, light, regular, bold, fill, duotone; Heroicons: outline, solid, mini)","schema":{"type":"string","default":"regular"}}],"responses":{"200":{"description":"SVG content","headers":{"Cache-Control":{"schema":{"type":"string","example":"public, max-age=31536000, immutable"}}},"content":{"image/svg+xml":{"schema":{"type":"string"}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/design/icons/search":{"get":{"tags":["Media"],"summary":"Search icons","operationId":"searchIcons","description":"Search available icons across all libraries by name or keyword.","parameters":[{"name":"q","in":"query","required":true,"description":"Search query","schema":{"type":"string"}},{"name":"library","in":"query","description":"Filter by icon library","schema":{"type":"string","enum":["phosphor","lucide","heroicons","radix"]}},{"name":"limit","in":"query","description":"Max results to return","schema":{"type":"integer","default":50}}],"responses":{"200":{"description":"Search results","content":{"application/json":{"schema":{"type":"object","properties":{"total":{"type":"integer"},"icons":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"library":{"type":"string"},"weights":{"type":"array","items":{"type":"string"}}}}}}}}}}}}},"/design/permissions":{"get":{"tags":["Sharing"],"summary":"Check document permissions","operationId":"checkPermissions","description":"Returns the caller's role for a given document. Supports both authenticated (cookie) and unauthenticated (share token via query param) access.","parameters":[{"name":"documentId","in":"query","required":true,"description":"Document ID to check","schema":{"type":"string"}},{"name":"token","in":"query","description":"Share token (for link-based access)","schema":{"type":"string"}}],"responses":{"200":{"description":"Permission check result","content":{"application/json":{"schema":{"type":"object","properties":{"role":{"type":["string","null"],"enum":["owner","editor","viewer",null],"description":"User's role, or null if no access"}},"required":["role"]}}}}}}},"/design/shares":{"get":{"tags":["Sharing"],"summary":"List shares for a document","operationId":"listShares","security":[{"cookieAuth":[]}],"description":"Lists all shares (invites and links) for a document. Owner only.","parameters":[{"name":"documentId","in":"query","required":true,"description":"Document ID","schema":{"type":"string"}}],"responses":{"200":{"description":"Share list","content":{"application/json":{"schema":{"type":"object","properties":{"shares":{"type":"array","items":{"$ref":"#/components/schemas/Share"}}},"required":["shares"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}},"post":{"tags":["Sharing"],"summary":"Create a share","operationId":"createShare","security":[{"cookieAuth":[]}],"description":"Invites a user by email or creates a shareable link. Owner only.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"documentId":{"type":"string"},"email":{"type":"string","format":"email","description":"Invite by email (omit for link share)"},"role":{"type":"string","enum":["editor","viewer"]},"type":{"type":"string","enum":["invite","link"],"default":"invite"}},"required":["documentId","role"]}}}},"responses":{"201":{"description":"Share created","content":{"application/json":{"schema":{"type":"object","properties":{"share":{"$ref":"#/components/schemas/Share"}},"required":["share"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"description":"User already has access","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}},"patch":{"tags":["Sharing"],"summary":"Update a share","operationId":"updateShare","security":[{"cookieAuth":[]}],"description":"Changes the role on an existing share.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"shareId":{"type":"string"},"role":{"type":"string","enum":["editor","viewer"]}},"required":["shareId","role"]}}}},"responses":{"200":{"description":"Share updated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","const":true}},"required":["success"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}},"delete":{"tags":["Sharing"],"summary":"Remove a share","operationId":"deleteShare","security":[{"cookieAuth":[]}],"description":"Revokes a share (invite or link).","parameters":[{"name":"shareId","in":"query","required":true,"description":"Share ID to remove","schema":{"type":"string"}}],"responses":{"200":{"description":"Share removed","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","const":true}},"required":["success"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}}},"/design/publish":{"post":{"tags":["Publishing"],"summary":"Publish design to Vercel","operationId":"publishDesign","security":[{"cookieAuth":[]}],"description":"Deploys the design as a static site on Vercel. Requires auth.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"files":{"type":"array","items":{"type":"object","properties":{"file":{"type":"string","description":"File path (e.g., \"index.html\")"},"data":{"type":"string","description":"File content"}},"required":["file","data"]},"description":"Files to deploy"},"siteName":{"type":"string","description":"Site subdomain name"},"projectId":{"type":"string","description":"Existing Vercel project ID (for redeployment)"},"connectionId":{"type":"string","description":"Vercel integration connection ID"}},"required":["files","siteName"]}}}},"responses":{"200":{"description":"Deployment initiated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublishResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}},"get":{"tags":["Publishing"],"summary":"Check deployment status","operationId":"getDeploymentStatus","description":"Returns the status of the latest deployment for a project.","parameters":[{"name":"projectId","in":"query","required":true,"description":"Vercel project ID","schema":{"type":"string"}}],"responses":{"200":{"description":"Deployment status","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"status":{"type":"string"},"createdAt":{"type":"string","format":"date-time"}}}}}}}}},"/design/ai/chat":{"post":{"tags":["AI Chat"],"summary":"AI chat with tool use (SSE)","operationId":"aiChat","security":[{"cookieAuth":[]}],"description":"Sends a message to the AI design assistant (Jules). Returns an SSE stream with text deltas and tool calls. The client executes tools and sends results back for up to 15 agentic iterations.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"messages":{"type":"array","items":{"$ref":"#/components/schemas/ChatMessage"},"description":"Conversation history"},"documentContext":{"type":"string","description":"Current document state as JSX (from get_document)"},"pendingToolCalls":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"input":{"type":"object","additionalProperties":true}}},"description":"Tool calls from previous assistant response (for agentic loop)"},"pendingAssistantText":{"type":"string","description":"Text from previous assistant response before tool calls"},"pendingToolResults":{"type":"array","items":{"type":"object","properties":{"tool_use_id":{"type":"string"},"result":{"type":"string"},"is_error":{"type":"boolean"}}},"description":"Results of executed tool calls"}},"required":["messages"]}}}},"responses":{"200":{"description":"SSE stream with chat events","content":{"text/event-stream":{"schema":{"type":"string","description":"SSE events:\n- `text_delta`: `{ type: \"text_delta\", text: string }`\n- `tool_start`: `{ type: \"tool_start\", id: string, name: string }`\n- `tool_call`: `{ type: \"tool_call\", id: string, name: string, input: object }`\n- `tool_calls_pending`: `{ type: \"tool_calls_pending\" }`\n- `done`: `{ type: \"done\" }`\n- `error`: `{ type: \"error\", message: string }`"}}}}}}},"/design/ai/usage":{"get":{"tags":["AI Chat"],"summary":"Get AI usage stats","operationId":"getAiUsage","security":[{"cookieAuth":[]}],"description":"Returns the authenticated user's AI credit usage, limits, and remaining balance.","responses":{"200":{"description":"Usage stats","content":{"application/json":{"schema":{"type":"object","properties":{"used":{"type":"integer","description":"Credits used this period"},"limit":{"type":"integer","description":"Credit limit for this period"},"remaining":{"type":"integer","description":"Credits remaining"},"bonusCredits":{"type":"integer","description":"Bonus credits available"},"totalRemaining":{"type":"integer","description":"Total remaining including bonus"},"unlimited":{"type":"boolean","description":"Whether the user has unlimited credits"}},"required":["used","limit","remaining","totalRemaining","unlimited"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/design/realtime-config":{"get":{"tags":["Configuration"],"summary":"Get realtime connection config","operationId":"getRealtimeConfig","description":"Returns the Realtime WebSocket configuration for MCP auto-discovery. Used by the MCP server to connect via WebSocket instead of HTTP/SSE.","responses":{"200":{"description":"Realtime configuration","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"Realtime service URL"},"anonKey":{"type":"string","description":"Public access token for the realtime service"}},"required":["url","anonKey"]}}}},"503":{"description":"Realtime not configured","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}}},"components":{"securitySchemes":{"cookieAuth":{"type":"apiKey","in":"cookie","name":"sb-auth-token","description":"Supabase session cookie (set automatically by the Supabase auth SDK). Required for sharing, publishing, and AI chat endpoints. Authenticate via the Supabase client SDK — the cookie is managed by the browser."}},"parameters":{"SessionId":{"name":"sessionId","in":"path","required":true,"description":"Design session ID","schema":{"type":"string"}},"CallId":{"name":"callId","in":"path","required":true,"description":"Tool call correlation ID","schema":{"type":"string"}}},"responses":{"BadRequest":{"description":"Invalid request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string"}},"required":["error"]}}}},"NotFound":{"description":"Resource not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"RateLimited":{"description":"Too many requests","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"Unauthorized":{"description":"Authentication required","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"Forbidden":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"InternalError":{"description":"Server error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string"}},"required":["error"]}}}}},"schemas":{"CreateSessionRequest":{"type":"object","properties":{"label":{"type":"string","description":"Human-readable label (e.g., \"Claude Code session\")"}}},"CreateSessionResponse":{"type":"object","properties":{"sessionId":{"type":"string","description":"Unique session identifier"},"designUrl":{"type":"string","format":"uri","description":"URL the user must open in their browser (e.g., https://efecto.app/design?session=abc123)"},"expiresAt":{"type":"string","format":"date-time","description":"When the session expires (ISO 8601)"}},"required":["sessionId","designUrl","expiresAt"]},"GetSessionResponse":{"type":"object","properties":{"session":{"$ref":"#/components/schemas/StudioSession"},"pendingCalls":{"type":"integer","description":"Number of tool calls waiting for execution"},"completedCalls":{"type":"integer","description":"Number of completed tool calls"}},"required":["session","pendingCalls","completedCalls"]},"StudioSession":{"type":"object","properties":{"id":{"type":"string"},"createdAt":{"type":"integer","description":"Unix timestamp (ms)"},"lastActivity":{"type":"integer","description":"Unix timestamp (ms)"},"expiresAt":{"type":"integer","description":"Unix timestamp (ms)"},"browserConnected":{"type":"boolean","description":"Whether a browser is connected via SSE"},"label":{"type":"string"}},"required":["id","createdAt","lastActivity","expiresAt","browserConnected"]},"ExecuteToolRequest":{"type":"object","properties":{"tool":{"type":"string","description":"Tool name. Accepts MCP names (e.g. \"add_node\"), internal names (e.g. \"efecto_add_node\"), or legacy \"studio_*\" names. See ToolSchemas for all available tool names."},"input":{"type":"object","additionalProperties":true,"description":"Tool input parameters (see /design/tools for schemas)"}},"required":["tool"]},"ExecuteToolResponse":{"type":"object","properties":{"callId":{"type":"string","description":"Unique correlation ID for this tool call"},"status":{"$ref":"#/components/schemas/ToolCallStatus"},"result":{"$ref":"#/components/schemas/StudioAIToolResult"}},"required":["callId","status"]},"SubmitResultRequest":{"type":"object","properties":{"callId":{"type":"string","description":"Correlation ID from the tool_call event"},"result":{"$ref":"#/components/schemas/StudioAIToolResult"}},"required":["callId","result"]},"SubmitResultResponse":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]},"PollResultResponse":{"$ref":"#/components/schemas/ExecuteToolResponse"},"ToolCallStatus":{"type":"string","enum":["pending","executing","completed","error","timeout"],"description":"Current state of a tool call in the execution pipeline"},"StudioAIToolResult":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string","description":"Human-readable result description"},"data":{"type":["object","array","string","null"],"description":"Tool-specific result data (shape varies by tool)"}},"required":["success","message"]},"ToolListResponse":{"type":"object","properties":{"total":{"type":"integer","description":"Number of available tools"},"tools":{"type":"array","items":{"$ref":"#/components/schemas/ToolDefinition"}},"usage":{"type":"object","description":"Execution guidance and examples"}},"required":["total","tools"]},"ToolDefinition":{"type":"object","properties":{"name":{"type":"string","description":"Tool name (e.g., \"efecto_add_node\")"},"description":{"type":"string","description":"What the tool does"},"input_schema":{"type":"object","description":"JSON Schema for the tool's input parameters"}},"required":["name","description","input_schema"]},"ToolSchemaResponse":{"type":"object","properties":{"tool":{"type":"string"},"description":{"type":"string"},"input_schema":{"type":"object"},"provided_input":{"type":["object","null"],"additionalProperties":true,"description":"The sample input provided in the request, echoed back"},"execution":{"type":"object","properties":{"note":{"type":"string"},"example":{"type":"string"}}}},"required":["tool","description","input_schema"]},"Share":{"type":"object","properties":{"id":{"type":"string"},"documentId":{"type":"string"},"type":{"type":"string","enum":["invite","link"]},"role":{"type":"string","enum":["editor","viewer"]},"email":{"type":"string","format":"email","description":"Present for invite shares"},"token":{"type":"string","description":"Present for link shares"},"createdAt":{"type":"string","format":"date-time"}},"required":["id","documentId","type","role","createdAt"]},"PublishResponse":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"Public site URL"},"vercelUrl":{"type":"string","format":"uri","description":"Vercel deployment URL"},"projectId":{"type":"string"},"projectName":{"type":"string"},"deploymentId":{"type":"string"},"status":{"type":"string"},"connectionId":{"type":"string"}},"required":["url","projectId","deploymentId","status"]},"ChatMessage":{"type":"object","properties":{"role":{"type":"string","enum":["user","assistant"]},"content":{"type":"string"}},"required":["role","content"]},"ToolCategories":{"type":"object","description":"Design tools organized by category (59 browser-executed tools + 4 MCP session tools). Tool names match the keys in ToolSchemas. See GET /design/tools for runtime schema discovery.","properties":{"session":{"type":"array","items":{"type":"string"},"description":"Session management (MCP-layer only, not available via /execute)","default":["create_session","wait_for_connection","session_status","close_session"]},"reading":{"type":"array","items":{"type":"string"},"description":"reading","default":["get_document","get_selection","list_artboards","find_nodes","get_node_tree"]},"creating":{"type":"array","items":{"type":"string"},"description":"creating","default":["create_artboard","add_node","add_section","replace_section"]},"modifying":{"type":"array","items":{"type":"string"},"description":"modifying","default":["update_artboard","delete_artboard","update_node","update_class","delete_nodes","batch_update","set_fill"]},"organizing":{"type":"array","items":{"type":"string"},"description":"organizing","default":["move_node","duplicate_node","group_nodes","reorder_node","duplicate_artboard","ungroup_node","align_nodes","distribute_nodes"]},"display":{"type":"array","items":{"type":"string"},"description":"display","default":["select_nodes","set_visibility","deselect_all"]},"history":{"type":"array","items":{"type":"string"},"description":"history","default":["undo","redo"]},"viewport":{"type":"array","items":{"type":"string"},"description":"viewport","default":["zoom_to_artboard","move_artboard","zoom_to_fit","set_viewport"]},"document":{"type":"array","items":{"type":"string"},"description":"document","default":["rename_document","new_document"]},"advanced":{"type":"array","items":{"type":"string"},"description":"advanced","default":["export_image","audit_design","repair_design"]},"theme":{"type":"array","items":{"type":"string"},"description":"theme","default":["get_theme","set_theme","set_theme_mode","reset_theme","get_brand_kit","set_brand_kit","list_brand_kits","detach_brand_kit","create_brand_kit","update_brand_ai_guidelines","duplicate_brand_kit","update_brand_identity","update_brand_colors","export_brand_system","save_brand_component","list_brand_components","insert_brand_component","delete_brand_component","save_brand_template","get_brand_images","audit_brand_compliance"]}}},"ToolSchemas":{"type":"object","description":"Complete input schemas for all 63 design tools. Keys use MCP-friendly names (e.g. \"add_node\"). The execute endpoint accepts these names, as well as \"efecto_*\" internal names. GET /design/tools returns the same tools using \"efecto_*\" names.","properties":{"create_session":{"type":"object","properties":{"description":{"type":"string","const":"Create a new Efecto design session. Returns a URL for the user to open in their browser. Only one session is active at a time; creating a new session replaces the previous one. After creating a session, share the URL with the user and call wait_for_connection — it blocks until the browser is ready, so you never have to guess or poll."},"inputSchema":{"type":"object","properties":{"label":{"type":"string","description":"Human-readable label for the session (e.g. \"Landing page design\")"}},"required":[]}}},"wait_for_connection":{"type":"object","properties":{"description":{"type":"string","const":"Blocks until the browser opens the design URL and pairs with this session. Call this right after create_session — it waits so you don't have to poll or guess. Returns immediately if already connected. Default timeout is 120 seconds."},"inputSchema":{"type":"object","properties":{"timeout":{"type":"number","description":"Max seconds to wait (default: 120, min: 5, max: 300)"}},"required":[]}}},"session_status":{"type":"object","properties":{"description":{"type":"string","const":"Check the status of the active session — whether the browser is connected, pending calls, etc. If no session is active, returns an error."},"inputSchema":{"type":"object","properties":{},"required":[]}}},"close_session":{"type":"object","properties":{"description":{"type":"string","const":"Close the active session. Disconnects the browser and cleans up resources."},"inputSchema":{"type":"object","properties":{},"required":[]}}},"get_document":{"type":"object","properties":{"description":{"type":"string","const":"Returns the current document serialized as JSX-like markup. Each element has a data-id attribute you can reference in other tools. Always call this before modifying an existing design."},"inputSchema":{"type":"object","properties":{},"required":[]}}},"get_selection":{"type":"object","properties":{"description":{"type":"string","const":"Returns the user’s current selection: which node or artboard IDs are selected, plus the serialized JSX subtree of each selected element. Use this to understand what the user is looking at before making contextual edits."},"inputSchema":{"type":"object","properties":{},"required":[]}}},"create_artboard":{"type":"object","properties":{"description":{"type":"string","const":"Creates a new artboard (frame/screen) in the document. IMPORTANT: Set className to \"flex flex-col\" so children layout vertically (most common). Common sizes: Desktop (1440x900), Tablet (768x1024), Mobile (375x812). New artboards are automatically placed to the right of existing ones — do NOT pass x/y unless you need a specific position. Use backgroundColor for solid fills. Use className for Tailwind-based fills (gradients, background images, opacity) and layout (flex, padding, gap)."},"inputSchema":{"type":"object","properties":{"name":{"type":"string","description":"Name of the artboard (e.g. \"Homepage\", \"Mobile Login\")"},"width":{"type":"number","description":"Width in CSS pixels (default: 1440)"},"height":{"type":"number","description":"Height in CSS pixels (default: 900)"},"x":{"type":"number","description":"Horizontal position on the canvas. Auto-positions to the right of existing artboards if omitted."},"y":{"type":"number","description":"Vertical position on the canvas (default: 0)"},"backgroundColor":{"type":"string","description":"Solid background color as hex string (default: \"#ffffff\"). This is rendered as an inline style behind any gradient or image set via className."},"className":{"type":"string","description":"Tailwind CSS classes for the artboard container. Use \"flex flex-col\" for vertical flex layout (recommended). Supports layout (flex, items-center, gap-4, p-6), gradients (bg-gradient-to-r from-blue-500 to-purple-500), background images (bg-cover bg-center bg-no-repeat with arbitrary url syntax), and opacity (opacity-80). The solid backgroundColor is rendered behind these."}},"required":["name","width","height"]}}},"update_artboard":{"type":"object","properties":{"description":{"type":"string","const":"Updates properties of an existing artboard (resize, rename, change background, set layout). Only the specified properties are changed; others remain untouched. To make children layout with flexbox, set className to include \"flex flex-col\"."},"inputSchema":{"type":"object","properties":{"artboardId":{"type":"string","description":"ID of the artboard to update"},"name":{"type":"string","description":"New name for the artboard"},"width":{"type":"number","description":"New width in CSS pixels"},"height":{"type":"number","description":"New height in CSS pixels"},"backgroundColor":{"type":"string","description":"New background color as hex string"},"className":{"type":"string","description":"Tailwind classes for the artboard (e.g. \"flex flex-col\" for vertical flex layout)"}},"required":["artboardId"]}}},"delete_artboard":{"type":"object","properties":{"description":{"type":"string","const":"Deletes an artboard and all its contents. This cannot be undone from the AI. Only use when explicitly asked to remove an artboard."},"inputSchema":{"type":"object","properties":{"artboardId":{"type":"string","description":"ID of the artboard to delete"}},"required":["artboardId"]}}},"add_node":{"type":"object","properties":{"description":{"type":"string","const":"Adds a single node (element) to the document tree. The parentId can be an artboard ID, any container node ID, or \"__floating__\" to place it on the canvas outside any artboard. Alternatively, set floating: true to place the node on the canvas at the given x/y coordinates. One of parentId or floating is required. Use className for Tailwind CSS styling. Every node maps to an HTML element."},"inputSchema":{"type":"object","properties":{"parentId":{"type":"string","description":"ID of the parent artboard or node to add this node into. Use \"__floating__\" to place on the canvas outside any artboard."},"floating":{"type":"boolean","description":"If true, place the node on the canvas as a floating element (overrides parentId). Requires x, y, width, height."},"x":{"type":"number","description":"Canvas X position (only when using \"__floating__\")."},"y":{"type":"number","description":"Canvas Y position (only when using \"__floating__\")."},"width":{"type":"number","description":"Width in pixels (only when using \"__floating__\")."},"height":{"type":"number","description":"Height in pixels (only when using \"__floating__\")."},"type":{"type":"string","enum":["frame","text","image","button","link","icon","input","video","component","shader"]},"tag":{"type":"string","description":"HTML tag to render (e.g. \"div\", \"section\", \"nav\", \"h1\", \"p\", \"button\", \"img\"). Defaults to the natural tag for the type."},"className":{"type":"string","description":"Tailwind CSS classes for styling. Supports: layout (flex, items-center, gap-4, p-6), solid fills (bg-white, bg-blue-500, bg-neutral-900 — use named colors, NOT arbitrary hex like bg-[#hex]), gradients (bg-gradient-to-r from-blue-500 to-purple-500), opacity (opacity-80), borders (border border-gray-200 rounded-lg), and all standard Tailwind utilities."},"textContent":{"type":"string","description":"Text content for text, button, and link nodes"},"src":{"type":"string"},"alt":{"type":"string"},"href":{"type":"string"},"placeholder":{"type":"string"},"variant":{"type":"string","enum":["primary","secondary","outline","ghost","destructive"]},"disabled":{"type":"boolean"},"name":{"type":"string"},"index":{"type":"number","description":"Insert position in parent. Omit to append at end (bottom of visual flow in flex-col containers)."},"style":{"type":"object"},"link":{"type":"object","description":"Link configuration. Makes any node clickable. Set to null to remove. Types: url (external/path), page (artboard ID), section (element ID anchor), email, phone.","properties":{"type":{"type":"string","enum":["url","page","section","email","phone"],"description":"Link type. Required."},"url":{"type":"string","description":"URL or path (when type is \"url\")"},"pageId":{"type":"string","description":"Artboard ID to link to (when type is \"page\")"},"sectionId":{"type":"string","description":"Element ID to scroll to (when type is \"section\")"},"email":{"type":"string","description":"Email address (when type is \"email\")"},"phone":{"type":"string","description":"Phone number (when type is \"phone\")"},"target":{"type":"string","enum":["_blank"],"description":"Set to \"_blank\" to open in new tab."}},"required":["type"]},"elementId":{"type":"string"},"iconName":{"type":"string","description":"Icon name for type \"icon\" (e.g. \"arrow-right\", \"heart\"). Phosphor PascalCase names like \"ArrowRight\" are also accepted."},"iconLibrary":{"type":"string","enum":["phosphor","lucide","heroicons","radix"],"description":"Icon library (default: \"phosphor\"). Available: phosphor (~7k icons, 6 weights), lucide (~1.5k), heroicons (~600, outline/solid), radix (~300)."},"iconWeight":{"type":"string","description":"Icon weight/variant. Phosphor: thin|light|regular|bold|fill|duotone. Heroicons: outline|solid|mini. Others: regular."},"iconSize":{"type":"number","description":"Icon size in pixels (default: 24)."},"shaderType":{"type":"string","enum":["meshGradient","dotGrid","voronoi","liquidMetal","chrome","pulsar","blackHole","glass","spiral","particles","fireworks"],"description":"Shader type for shader nodes (required when type is \"shader\")."},"shaderSettings":{"type":"object","description":"Full shader settings object (colors, backgroundColor, and per-shader params like meshGradient, dotGrid, etc.). If omitted, defaults are used for the chosen shaderType."},"inputType":{"type":"string","enum":["shader","image","video","fill"],"description":"Input source type for shader nodes. Default \"shader\" for generative shaders."},"mediaInput":{"type":"object","description":"Media input settings when inputType is \"image\" or \"video\". Properties: mediaUrl, mediaType, objectFit, brightness, contrast, saturation."},"fillColor":{"type":"string","description":"Fill color hex when inputType is \"fill\" (e.g. \"#ff0000\")."},"effectId":{"type":"string","description":"Effect to apply. Examples: \"ascii-standard\", \"dither-floyd-steinberg\", \"halftone-mono\", \"glitch-vhs\", \"art-kuwahara\", \"none\"."},"effectEnabled":{"type":"boolean","description":"Whether the effect is active (default false)."},"effectSettings":{"type":"object","description":"Full effect settings object with per-effect params (ascii, dither, glitch, monoHalftone, cmykHalftone, kuwahara, etc.)."},"postProcesses":{"type":"array","description":"Stackable post-process effects. Each: { id, type, enabled, settings }. Types: scanlines, vignette, bloom, grain, pixelate, wave, etc."}},"required":["type"]}}},"add_section":{"type":"object","properties":{"description":{"type":"string","const":"Adds a complete section from JSX-like markup. Most efficient for complex layouts. Write standard JSX with HTML tags and Tailwind className attributes. ORDERING: Sections append to the BOTTOM of the parent. In flex-col artboards, the first section added is at the TOP of the page. Build pages top-to-bottom: add nav/header first, then hero, then content sections, then footer last. Do NOT build bottom-up. IMPORTANT styling rules: (1) ALL visual styling goes in className as Tailwind classes — text sizes (text-xl, text-4xl), colors (text-gray-900, bg-blue-600), spacing (p-8, gap-6), borders, shadows, etc. (2) Headings MUST have explicit text sizes (h1: text-5xl+, h2: text-4xl, h3: text-2xl). (3) Buttons/CTAs need bg + padding + text color (e.g. bg-gray-900 text-white px-6 py-3 rounded-lg). (4) Use named Tailwind colors only — NEVER arbitrary hex like bg-[#f9f9f9] (silently fails). (5) Images: use <img src=\"...\" /> with a real URL or placeholder. Empty <div className=\"rounded-2xl\" /> is invisible. (6) DO NOT use inline style for things Tailwind handles (colors, sizes, spacing). Reserve style for rare overrides. (7) ICONS: Use <svg icon=\"icon-name\" className=\"w-5 h-5 text-gray-600\" /> with Phosphor icon names (kebab-case). Common icons: arrow-right, heart, check, x, plus, star, envelope, lock, user, house, magnifying-glass, gear. Other formats also work: <HeartIcon className=\"w-5 h-5\" />, <i className=\"w-5 h-5\" icon=\"star\" />, <LucideCheck className=\"w-5 h-5\" />. Size icons via className (w-5 h-5), color via text-* classes. Available libraries: phosphor (default, ~7k), lucide, heroicons, radix."},"inputSchema":{"type":"object","properties":{"parentId":{"type":"string","description":"ID of the parent artboard or node. Use \"__floating__\" to place on the canvas outside any artboard."},"jsx":{"type":"string","description":"JSX markup. Use className for ALL styling with Tailwind classes. Include text sizes, colors, backgrounds, and spacing on every element."},"index":{"type":"number","description":"Insert position in parent. Omit to append at end (bottom of visual flow)."},"x":{"type":"number","description":"Canvas X position (only when parentId is \"__floating__\")."},"y":{"type":"number","description":"Canvas Y position (only when parentId is \"__floating__\")."},"width":{"type":"number","description":"Width in pixels (only when parentId is \"__floating__\")."},"height":{"type":"number","description":"Height in pixels (only when parentId is \"__floating__\")."}},"required":["parentId","jsx"]}}},"update_node":{"type":"object","properties":{"description":{"type":"string","const":"Updates properties of an existing node. Use data-id from the document to reference the node. Only the specified properties are changed; others remain untouched."},"inputSchema":{"type":"object","properties":{"nodeId":{"type":"string","description":"ID of the node to update (from data-id attribute)"},"className":{"type":"string","description":"New Tailwind CSS classes (replaces existing className entirely)"},"textContent":{"type":"string","description":"New text content (for text, button, link nodes)"},"tag":{"type":"string","description":"Change the HTML tag (e.g. change a \"div\" to \"section\")"},"style":{"type":"object","description":"Inline CSS overrides (merged with existing style)"},"name":{"type":"string","description":"New display name in the layers panel"},"src":{"type":"string","description":"New source URL (for image, video nodes)"},"alt":{"type":"string","description":"New alt text (for image nodes)"},"href":{"type":"string","description":"New link destination (for link nodes)"},"placeholder":{"type":"string","description":"New placeholder (for input nodes)"},"variant":{"type":"string","enum":["primary","secondary","outline","ghost","destructive"],"description":"Button variant (only for button nodes)"},"disabled":{"type":"boolean","description":"Disabled state (for button/input nodes)"},"link":{"type":"object","description":"Link configuration. Makes any node clickable. Set to null to remove. Types: url (external/path), page (artboard ID), section (element ID anchor), email, phone.","properties":{"type":{"type":"string","enum":["url","page","section","email","phone"],"description":"Link type. Required."},"url":{"type":"string","description":"URL or path (when type is \"url\")"},"pageId":{"type":"string","description":"Artboard ID to link to (when type is \"page\")"},"sectionId":{"type":"string","description":"Element ID to scroll to (when type is \"section\")"},"email":{"type":"string","description":"Email address (when type is \"email\")"},"phone":{"type":"string","description":"Phone number (when type is \"phone\")"},"target":{"type":"string","enum":["_blank"],"description":"Set to \"_blank\" to open in new tab."}},"required":["type"]},"elementId":{"type":"string","description":"Element ID for anchor linking (e.g. \"pricing\" → linkable via #pricing). Set to empty string to remove."},"iconName":{"type":"string","description":"Icon name (for icon nodes)"},"iconLibrary":{"type":"string","enum":["phosphor","lucide","heroicons","radix"],"description":"Icon library (for icon nodes)"},"iconWeight":{"type":"string","description":"Icon weight/variant (for icon nodes)"},"iconSize":{"type":"number","description":"Icon size in pixels (for icon nodes)"},"shaderType":{"type":"string","enum":["meshGradient","dotGrid","voronoi","liquidMetal","chrome","pulsar","blackHole","glass","spiral","particles","fireworks"],"description":"Shader type (for shader nodes)"},"shaderSettings":{"type":"object","description":"Full shader settings object (for shader nodes)"},"inputType":{"type":"string","enum":["shader","image","video","fill"],"description":"Input source type for shader nodes."},"mediaInput":{"type":"object","description":"Media input settings: { mediaUrl, mediaType, objectFit, brightness, contrast, saturation }."},"fillColor":{"type":"string","description":"Fill color hex for shader nodes with inputType \"fill\"."},"effectId":{"type":"string","description":"Effect to apply (e.g. \"ascii-standard\", \"halftone-mono\", \"glitch-vhs\", \"none\")."},"effectEnabled":{"type":"boolean","description":"Whether the effect is active."},"effectSettings":{"type":"object","description":"Full effect settings object."},"postProcesses":{"type":"array","description":"Stackable post-process effects array."}},"required":["nodeId"]}}},"update_class":{"type":"object","properties":{"description":{"type":"string","const":"Updates only the className (Tailwind classes) of a node. This is a shortcut for the most common update operation. The new className replaces the existing one entirely."},"inputSchema":{"type":"object","properties":{"nodeId":{"type":"string","description":"ID of the node to update"},"className":{"type":"string","description":"New Tailwind CSS classes"}},"required":["nodeId","className"]}}},"delete_nodes":{"type":"object","properties":{"description":{"type":"string","const":"Deletes one or more nodes from the document. Deleting a node also removes all its children."},"inputSchema":{"type":"object","properties":{"nodeIds":{"type":"array","items":{"type":"string"},"description":"Array of node IDs to delete"}},"required":["nodeIds"]}}},"move_node":{"type":"object","properties":{"description":{"type":"string","const":"Moves a node to a new parent or reorders it within its current parent. The node and all its children are moved together. Use \"__floating__\" as newParentId to detach a node from its artboard and make it float on the canvas."},"inputSchema":{"type":"object","properties":{"nodeId":{"type":"string","description":"ID of the node to move"},"newParentId":{"type":"string","description":"ID of the new parent (artboard, container node, or \"__floating__\" for canvas)"},"index":{"type":"number","description":"Position within the new parent (0 = first). Appends to end if omitted."},"x":{"type":"number","description":"Canvas X position (only when moving to \"__floating__\"). Provide explicit coordinates for flow-positioned nodes — defaults fall back to the artboard origin."},"y":{"type":"number","description":"Canvas Y position (only when moving to \"__floating__\"). Provide explicit coordinates for flow-positioned nodes — defaults fall back to the artboard origin."},"width":{"type":"number","description":"Width in pixels (only when moving to \"__floating__\")."},"height":{"type":"number","description":"Height in pixels (only when moving to \"__floating__\")."}},"required":["nodeId","newParentId"]}}},"duplicate_node":{"type":"object","properties":{"description":{"type":"string","const":"Duplicates a node (and all its children). The duplicate is inserted right after the original in the same parent."},"inputSchema":{"type":"object","properties":{"nodeId":{"type":"string","description":"ID of the node to duplicate"}},"required":["nodeId"]}}},"group_nodes":{"type":"object","properties":{"description":{"type":"string","const":"Groups nodes into a new flex frame container. Nodes can be from different parents. The group is inserted at the position of the first selected node."},"inputSchema":{"type":"object","properties":{"nodeIds":{"type":"array","items":{"type":"string"},"description":"Array of node IDs to group (must share the same parent)"}},"required":["nodeIds"]}}},"reorder_node":{"type":"object","properties":{"description":{"type":"string","const":"Changes the z-order of a node within its parent. \"front\" brings it to the top (rendered last / visually on top), \"back\" sends it to the bottom (rendered first / visually behind)."},"inputSchema":{"type":"object","properties":{"nodeId":{"type":"string","description":"ID of the node to reorder"},"position":{"type":"string","enum":["front","back"],"description":"\"front\" = bring to front, \"back\" = send to back"}},"required":["nodeId","position"]}}},"select_nodes":{"type":"object","properties":{"description":{"type":"string","const":"Selects one or more nodes on the canvas, highlighting them for the user. Use this after creating or editing elements so the user can see what was changed. Pass an empty array to deselect all."},"inputSchema":{"type":"object","properties":{"nodeIds":{"type":"array","items":{"type":"string"},"description":"Array of node IDs to select. Empty array deselects all."}},"required":["nodeIds"]}}},"set_visibility":{"type":"object","properties":{"description":{"type":"string","const":"Shows or hides a node. Hidden nodes are not rendered but remain in the document tree."},"inputSchema":{"type":"object","properties":{"nodeId":{"type":"string","description":"ID of the node"},"visible":{"type":"boolean","description":"true to show, false to hide"}},"required":["nodeId","visible"]}}},"list_artboards":{"type":"object","properties":{"description":{"type":"string","const":"Lists all artboards in the document with their IDs, names, dimensions, and positions. Use this to find artboard IDs for adding content."},"inputSchema":{"type":"object","properties":{},"required":[]}}},"find_nodes":{"type":"object","properties":{"description":{"type":"string","const":"Searches for nodes by name, text, type, tag, className, style, elementId, or link presence. Returns rich results with artboard context, ancestor breadcrumb trail, child count, depth, elementId, link info, and a text snippet — enough to disambiguate without a full document read. Use artboardId to scope the search to one artboard. Results capped at limit (default 50)."},"inputSchema":{"type":"object","properties":{"query":{"type":"string","description":"Search term to match against node names and text content (case-insensitive)"},"type":{"type":"string","enum":["frame","text","image","button","link","icon","input","video","component","shader"],"description":"Filter by node type"},"tag":{"type":"string","description":"Filter by HTML tag (e.g. \"nav\", \"section\", \"h1\", \"footer\")"},"classNameContains":{"type":"string","description":"Filter nodes whose className contains this substring (case-insensitive)"},"hasClass":{"type":"string","description":"Filter nodes that have this exact Tailwind class (e.g. \"flex\", \"bg-white\", \"text-lg\")"},"styleContains":{"type":"string","description":"Filter nodes whose inline style contains this substring (e.g. \"background\", \"linear-gradient\")"},"artboardId":{"type":"string","description":"Scope search to a single artboard by ID"},"hasLink":{"type":"boolean","description":"If true, only return nodes that have a link attached"},"hasElementId":{"type":"boolean","description":"If true, only return nodes that have an elementId (anchor target)"},"limit":{"type":"number","description":"Max results to return (default 50). Use a smaller number for faster, more targeted queries."}},"required":[]}}},"duplicate_artboard":{"type":"object","properties":{"description":{"type":"string","const":"Duplicates an entire artboard with all its content. Creates a deep copy with fresh IDs. Useful for creating design variations or starting a new screen from an existing design. The duplicate is positioned to the right of the original with a 100px gap by default."},"inputSchema":{"type":"object","properties":{"artboardId":{"type":"string","description":"ID of the artboard to duplicate"},"newName":{"type":"string","description":"Name for the duplicated artboard (defaults to \"Original Name (Copy)\")"},"offsetX":{"type":"number","description":"Horizontal offset from the original (default: original width + 100)"},"width":{"type":"number","description":"Override width of the duplicated artboard (CSS pixels)"},"height":{"type":"number","description":"Override height of the duplicated artboard (CSS pixels)"}},"required":["artboardId"]}}},"batch_update":{"type":"object","properties":{"description":{"type":"string","const":"Updates multiple nodes in a single call. Efficient for bulk styling changes like changing a color palette, updating font sizes, or applying consistent spacing."},"inputSchema":{"type":"object","properties":{"updates":{"type":"array","description":"Array of updates, each with a nodeId and properties to change","items":{"type":"object","properties":{"nodeId":{"type":"string","description":"ID of the node to update"},"className":{"type":"string","description":"New Tailwind classes (replaces existing)"},"textContent":{"type":"string","description":"New text content"},"style":{"type":"object","description":"Inline CSS overrides"},"link":{"type":"object","description":"Link configuration. Makes any node clickable. Set to null to remove. Types: url (external/path), page (artboard ID), section (element ID anchor), email, phone.","properties":{"type":{"type":"string","enum":["url","page","section","email","phone"],"description":"Link type. Required."},"url":{"type":"string","description":"URL or path (when type is \"url\")"},"pageId":{"type":"string","description":"Artboard ID to link to (when type is \"page\")"},"sectionId":{"type":"string","description":"Element ID to scroll to (when type is \"section\")"},"email":{"type":"string","description":"Email address (when type is \"email\")"},"phone":{"type":"string","description":"Phone number (when type is \"phone\")"},"target":{"type":"string","enum":["_blank"],"description":"Set to \"_blank\" to open in new tab."}},"required":["type"]},"elementId":{"type":"string"}},"required":["nodeId"]}}},"required":["updates"]}}},"replace_section":{"type":"object","properties":{"description":{"type":"string","const":"Replaces an existing node (and all its children) with new JSX content in-place. Preserves position in parent. Useful for redesigning a section. Same JSX rules as add_section — use Tailwind className for ALL styling (text sizes, colors, spacing, backgrounds)."},"inputSchema":{"type":"object","properties":{"nodeId":{"type":"string","description":"ID of the node to replace"},"jsx":{"type":"string","description":"JSX markup with full Tailwind styling in className"}},"required":["nodeId","jsx"]}}},"undo":{"type":"object","properties":{"description":{"type":"string","const":"Undoes the last action. Equivalent to Cmd+Z."},"inputSchema":{"type":"object","properties":{},"required":[]}}},"redo":{"type":"object","properties":{"description":{"type":"string","const":"Redoes the last undone action. Equivalent to Cmd+Shift+Z."},"inputSchema":{"type":"object","properties":{},"required":[]}}},"ungroup_node":{"type":"object","properties":{"description":{"type":"string","const":"Ungroups a frame container, moving its children up to the parent. The group node is removed and its children take its place."},"inputSchema":{"type":"object","properties":{"groupId":{"type":"string","description":"ID of the group/frame node to ungroup"}},"required":["groupId"]}}},"align_nodes":{"type":"object","properties":{"description":{"type":"string","const":"Aligns multiple nodes along a shared axis. Nodes must be absolutely positioned or this adjusts their position within the parent."},"inputSchema":{"type":"object","properties":{"nodeIds":{"type":"array","items":{"type":"string"},"description":"Array of node IDs to align (minimum 2)"},"alignment":{"type":"string","enum":["left","center","right","top","middle","bottom"],"description":"Alignment direction"}},"required":["nodeIds","alignment"]}}},"distribute_nodes":{"type":"object","properties":{"description":{"type":"string","const":"Distributes nodes evenly with equal spacing between them."},"inputSchema":{"type":"object","properties":{"nodeIds":{"type":"array","items":{"type":"string"},"description":"Array of node IDs to distribute (minimum 3)"},"direction":{"type":"string","enum":["horizontal","vertical"],"description":"Distribution direction"}},"required":["nodeIds","direction"]}}},"zoom_to_artboard":{"type":"object","properties":{"description":{"type":"string","const":"Zooms the viewport to fit a specific artboard. Useful after creating or modifying an artboard to ensure the user can see it."},"inputSchema":{"type":"object","properties":{"artboardId":{"type":"string","description":"ID of the artboard to zoom to"}},"required":["artboardId"]}}},"rename_document":{"type":"object","properties":{"description":{"type":"string","const":"Renames the Studio document."},"inputSchema":{"type":"object","properties":{"name":{"type":"string","description":"New name for the document"}},"required":["name"]}}},"move_artboard":{"type":"object","properties":{"description":{"type":"string","const":"Repositions an artboard on the infinite canvas. Use this to lay out multi-screen flows (e.g. place Desktop at 0,0, Tablet at 1540,0, Mobile at 2408,0)."},"inputSchema":{"type":"object","properties":{"artboardId":{"type":"string","description":"ID of the artboard to move"},"x":{"type":"number","description":"New X position on the canvas"},"y":{"type":"number","description":"New Y position on the canvas"}},"required":["artboardId","x","y"]}}},"zoom_to_fit":{"type":"object","properties":{"description":{"type":"string","const":"Zooms the viewport to fit all artboards. Useful after creating multiple artboards to show the full flow to the user."},"inputSchema":{"type":"object","properties":{},"required":[]}}},"deselect_all":{"type":"object","properties":{"description":{"type":"string","const":"Deselects all currently selected nodes. Useful to clear the selection between operations."},"inputSchema":{"type":"object","properties":{},"required":[]}}},"set_viewport":{"type":"object","properties":{"description":{"type":"string","const":"Sets the viewport zoom level and pan position. Zoom 1.0 = 100%. Use to control exactly what the user sees."},"inputSchema":{"type":"object","properties":{"zoom":{"type":"number","description":"Zoom level (0.1 to 5.0, where 1.0 = 100%)"},"panX":{"type":"number","description":"Horizontal pan offset"},"panY":{"type":"number","description":"Vertical pan offset"}},"required":[]}}},"new_document":{"type":"object","properties":{"description":{"type":"string","const":"Creates a new blank document, replacing the current one. Use to start fresh. WARNING: This discards all unsaved changes."},"inputSchema":{"type":"object","properties":{"name":{"type":"string","description":"Name for the new document (optional)"}},"required":[]}}},"get_node_tree":{"type":"object","properties":{"description":{"type":"string","const":"Returns the JSX-like serialization of a specific node or artboard subtree, instead of the full document. Faster than get_document when you only need one section."},"inputSchema":{"type":"object","properties":{"nodeId":{"type":"string","description":"ID of the node or artboard to serialize"}},"required":["nodeId"]}}},"set_fill":{"type":"object","properties":{"description":{"type":"string","const":"Sets the fill (background) of a node or artboard. Supports solid colors, gradients, and images. Handles the dual-write pattern automatically: writes Tailwind className for AI readability AND inline styles for rendering. Prefer this over manually setting background properties."},"inputSchema":{"type":"object","properties":{"targetId":{"type":"string","description":"ID of the node or artboard to apply the fill to"},"fill":{"type":"object","description":"The fill to apply. Set to null or omit to clear the fill.","properties":{"type":{"type":"string","enum":["solid","gradient","image"],"description":"Fill type"},"color":{"type":"string","description":"For solid fills: Tailwind color name (blue-500) or hex (#3b82f6)"},"opacity":{"type":"number","description":"For solid fills: opacity 0-1 (default 1). Only works with hex colors."},"gradient":{"type":"object","description":"For gradient fills: gradient configuration","properties":{"type":{"type":"string","enum":["linear","radial"]},"angle":{"type":"number","description":"For linear gradients: angle in degrees (0-360)"},"stops":{"type":"array","description":"Color stops array","items":{"type":"object","properties":{"color":{"type":"string","description":"Hex color"},"position":{"type":"number","description":"0-1 position along gradient"},"opacity":{"type":"number","description":"0-1 opacity (default 1)"}},"required":["color","position"]}},"center":{"type":"object","description":"For radial gradients: center point","properties":{"x":{"type":"number"},"y":{"type":"number"}}},"radius":{"type":"number","description":"For radial gradients: radius (0-2, default 0.85)"}},"required":["type","stops"]},"url":{"type":"string","description":"For image fills: image URL"},"size":{"type":"string","enum":["cover","contain","fill","auto"],"description":"For image fills: background-size (default cover)"},"position":{"type":"string","description":"For image fills: background-position (default center)"},"repeat":{"type":"string","enum":["no-repeat","repeat","repeat-x","repeat-y"],"description":"For image fills: background-repeat (default no-repeat)"}},"required":["type"]}},"required":["targetId"]}}},"export_image":{"type":"object","properties":{"description":{"type":"string","const":"Exports an artboard or node as an image. Returns a base64 data URL. Only use when the USER explicitly asks to export or download an image. Do NOT use this to preview or self-review your own work — the user can already see the canvas live. Use thumbnail: true for a small preview (~200px wide) suitable for AI agents."},"inputSchema":{"type":"object","properties":{"artboardId":{"type":"string","description":"ID of the artboard to export"},"nodeId":{"type":"string","description":"ID of a specific node to export (optional, exports artboard if omitted)"},"format":{"type":"string","enum":["png","jpeg","webp","svg"],"description":"Image format (default: png)"},"scale":{"type":"number","description":"Scale multiplier (default: 1). Use 0.25 or 0.5 for smaller exports, 1-4 for retina. Ignored when thumbnail is true."},"thumbnail":{"type":"boolean","description":"If true, returns a small preview (~200px wide, scale 0.25). Useful for AI agents to quickly preview designs without large base64 payloads."},"quality":{"type":"number","description":"Quality 0-1 for JPEG/WebP (default: 0.92)"}},"required":["artboardId"]}}},"get_theme":{"type":"object","properties":{"description":{"type":"string","const":"Returns the current document theme including all modes and their tokens. When a brand system is active, the theme IS the brand — they are the same thing. Use get_brand_kit instead if you need full brand identity (fonts, icons, guidelines). Returns id, name, modes (light/dark/custom), activeMode, and the resolved active tokens."},"inputSchema":{"type":"object","properties":{},"required":[]}}},"set_theme":{"type":"object","properties":{"description":{"type":"string","const":"Sets the document theme. Three ways to use:\n(1) presetId — apply a built-in preset (default, zinc, slate, rose, blue, green, violet, orange).\n(2) css — import raw CSS from ANY source (your user's repo globals.css, a shadcn theme, Radix colors, Vercel Geist, or hand-crafted tokens) with :root {} and .dark {} blocks. Recognizes standard tokens (--background, --primary, etc.) AND arbitrary custom CSS variables (--ds-gray-100, --geist-foreground, etc.) — all variables are preserved and applied to artboards.\n(3) modes — provide token objects directly per mode (e.g. { light: { background: \"#fff\", ... }, dark: { ... } }).\nAll presets include both light and dark variants. The css option is the most flexible — if the user has a design system or brand colors, read their CSS file and pass it here.\nOptional: pass name to give the theme a custom display name (e.g. \"Vercel Geist\", \"Brand Dark\")."},"inputSchema":{"type":"object","properties":{"presetId":{"type":"string","enum":["default","zinc","slate","rose","blue","green","violet","orange"],"description":"Built-in theme preset ID. Applying a preset clears any existing custom variables."},"css":{"type":"string","description":"Raw CSS with :root {} and/or .dark {} selectors containing CSS custom properties. Standard tokens (--background, --primary, etc.) are mapped to the theme system. All other CSS variables are preserved as custom vars and applied to artboards."},"modes":{"type":"object","description":"Token objects keyed by mode name. Each value is a partial ThemeTokens object with keys like background, foreground, primary, etc. Missing keys inherit defaults.","additionalProperties":{"type":"object","properties":{"background":{"type":"string"},"foreground":{"type":"string"},"card":{"type":"string"},"cardForeground":{"type":"string"},"popover":{"type":"string"},"popoverForeground":{"type":"string"},"primary":{"type":"string"},"primaryForeground":{"type":"string"},"secondary":{"type":"string"},"secondaryForeground":{"type":"string"},"muted":{"type":"string"},"mutedForeground":{"type":"string"},"accent":{"type":"string"},"accentForeground":{"type":"string"},"destructive":{"type":"string"},"destructiveForeground":{"type":"string"},"border":{"type":"string"},"input":{"type":"string"},"ring":{"type":"string"},"radius":{"type":"string"}}}},"name":{"type":"string","description":"Custom display name for the theme (e.g. \"Vercel Geist\", \"Brand Dark\")"}},"required":[]}}},"set_theme_mode":{"type":"object","properties":{"description":{"type":"string","const":"Switches the active theme mode. All artboards update to show the new mode's colors. Standard modes are \"light\" and \"dark\" but custom modes are supported too."},"inputSchema":{"type":"object","properties":{"mode":{"type":"string","description":"The mode to activate (e.g. \"light\", \"dark\", or a custom mode name)"}},"required":["mode"]}}},"reset_theme":{"type":"object","properties":{"description":{"type":"string","const":"Removes the document theme entirely, returning to raw Tailwind colors with no semantic tokens. If a brand system is active, use detach_brand_kit instead to preserve colors while removing the brand reference. This is different from set_theme with presetId \"default\" — that applies the default theme (semantic tokens still active), while reset_theme removes the theme object so bg-primary, text-foreground, etc. are no longer bound to any tokens."},"inputSchema":{"type":"object","properties":{},"required":[]}}},"get_brand_kit":{"type":"object","properties":{"description":{"type":"string","const":"Returns the active brand system for the current document, including brand identity (company name, tagline, values, voice), color palette (semantic tokens for light/dark modes + extended brand colors), typography (heading/body/mono fonts + type scale), icon preferences, and AI directives. Returns null if no brand system is active. A brand system IS the theme — when active, it controls all semantic tokens (bg-primary, text-foreground, etc.). Always call this at the start of any design task to check if a brand system is in use — if it is, all design decisions should follow it."},"inputSchema":{"type":"object","properties":{},"required":[]}}},"set_brand_kit":{"type":"object","properties":{"description":{"type":"string","const":"Apply a brand system to the current document by ID. This sets the document theme to the brand system's colors (brand = theme), stores the brand reference, and ensures all future design decisions follow the brand. Use list_brand_kits first to see available brand systems."},"inputSchema":{"type":"object","properties":{"brandKitId":{"type":"string","description":"The ID of the brand system to apply."}},"required":["brandKitId"]}}},"list_brand_kits":{"type":"object","properties":{"description":{"type":"string","const":"Lists all brand systems available to the user. Returns an array of brand system summaries with id, name, primary color, and company name. Use to discover which brand systems exist before applying one."},"inputSchema":{"type":"object","properties":{},"required":[]}}},"detach_brand_kit":{"type":"object","properties":{"description":{"type":"string","const":"Detaches the active brand system from the document. The current theme colors are preserved as a standalone theme, but the brand system reference is removed. Future design decisions will no longer follow the brand system automatically."},"inputSchema":{"type":"object","properties":{},"required":[]}}},"create_brand_kit":{"type":"object","properties":{"description":{"type":"string","const":"Create a new brand system from scratch and apply it to the current document as the active theme. Provide a company name and optionally any identity fields. The brand system is saved to the user's library. After creation, use update_brand_colors to set specific colors, and update_brand_identity for more details."},"inputSchema":{"type":"object","properties":{"companyName":{"type":"string","description":"Company/brand name. Required."},"tagline":{"type":"string","description":"Short tagline"},"industry":{"type":"string","description":"Industry"},"voiceTone":{"type":"string","description":"Voice & tone"},"primary":{"type":"string","description":"Primary color hex (e.g. \"#3b82f6\")"},"headingFontName":{"type":"string","description":"Heading font name (e.g. \"Inter\")"},"bodyFontName":{"type":"string","description":"Body font name (e.g. \"DM Sans\")"}},"required":["companyName"]}}},"update_brand_ai_guidelines":{"type":"object","properties":{"description":{"type":"string","const":"Update the AI guidelines markdown document for the active brand system. This is the \"brand bible\" — freeform markdown that tells AI how to design for this brand. Include writing guidelines, visual rules, dos & don'ts. ALL components MUST use semantic tokens (bg-primary, text-foreground). Never hardcode Tailwind colors."},"inputSchema":{"type":"object","properties":{"aiContextDocument":{"type":"string","description":"Full markdown content for the AI guidelines document."}},"required":["aiContextDocument"]}}},"duplicate_brand_kit":{"type":"object","properties":{"description":{"type":"string","const":"Duplicate an existing brand system. Creates a copy with \" (Copy)\" appended to the name. The copy is saved to the library but NOT applied to the current document."},"inputSchema":{"type":"object","properties":{"brandKitId":{"type":"string","description":"ID of the brand system to duplicate."}},"required":["brandKitId"]}}},"update_brand_identity":{"type":"object","properties":{"description":{"type":"string","const":"Update the active brand system's identity fields: company name, tagline, description, industry, target audience, brand values, voice & tone, AI directives. Only updates the fields you provide — others are preserved. Saves to library and re-applies to the document theme."},"inputSchema":{"type":"object","properties":{"companyName":{"type":"string","description":"Company/brand name"},"tagline":{"type":"string","description":"Short tagline or slogan"},"description":{"type":"string","description":"Brand description"},"industry":{"type":"string","description":"Industry or domain"},"targetAudience":{"type":"string","description":"Target audience"},"brandValues":{"type":"array","items":{"type":"string"},"description":"Core brand values"},"voiceTone":{"type":"string","description":"Voice & tone description"},"aiDirectives":{"type":"string","description":"Special instructions for AI when designing"}},"required":[]}}},"update_brand_colors":{"type":"object","properties":{"description":{"type":"string","const":"Update colors in the active brand system (which IS the document theme). Provide a mode (\"light\" or \"dark\") and the token values to change. Only updates the tokens you provide — others are preserved. Changes are immediately reflected in all artboards. Example: set primary to blue in light mode."},"inputSchema":{"type":"object","properties":{"mode":{"type":"string","enum":["light","dark"],"description":"Which mode to update. Default: light."},"primary":{"type":"string","description":"Primary color (hex)"},"primaryForeground":{"type":"string","description":"Primary foreground color (hex)"},"secondary":{"type":"string","description":"Secondary color (hex)"},"secondaryForeground":{"type":"string","description":"Secondary foreground color (hex)"},"accent":{"type":"string","description":"Accent color (hex)"},"accentForeground":{"type":"string","description":"Accent foreground color (hex)"},"background":{"type":"string","description":"Background color (hex)"},"foreground":{"type":"string","description":"Foreground color (hex)"},"muted":{"type":"string","description":"Muted color (hex)"},"mutedForeground":{"type":"string","description":"Muted foreground color (hex)"},"destructive":{"type":"string","description":"Destructive/error color (hex)"},"border":{"type":"string","description":"Border color (hex)"}},"required":[]}}},"export_brand_system":{"type":"object","properties":{"description":{"type":"string","const":"Export the active brand system (which IS the document theme) in a given format. Returns the file content as text. Formats: \"css\" (CSS custom properties), \"tailwind\" (Tailwind v4 @theme), \"style-dictionary\" (W3C Design Tokens JSON), \"figma\" (Figma Variables JSON), \"package\" (complete Efecto brand system JSON)."},"inputSchema":{"type":"object","properties":{"format":{"type":"string","enum":["css","tailwind","style-dictionary","figma","package"],"description":"Export format."}},"required":["format"]}}},"save_brand_component":{"type":"object","properties":{"description":{"type":"string","const":"Save selected node(s) as a reusable brand component in the active brand system. Components should use semantic tokens (bg-primary, text-foreground) so they re-theme automatically. The component can be inserted later via insert_brand_component."},"inputSchema":{"type":"object","properties":{"name":{"type":"string","description":"Display name for the component (e.g. \"Primary Button\", \"Hero Card\")."},"category":{"type":"string","enum":["button","card","input","badge","nav","footer","custom"],"description":"Component category."},"nodeIds":{"type":"array","items":{"type":"string"},"description":"Array of node IDs to capture as the component."}},"required":["name","category","nodeIds"]}}},"list_brand_components":{"type":"object","properties":{"description":{"type":"string","const":"List all saved brand components and templates in the active brand system. Returns component/template IDs, names, and categories for use with insert_brand_component."},"inputSchema":{"type":"object","properties":{}}}},"insert_brand_component":{"type":"object","properties":{"description":{"type":"string","const":"Insert a saved brand component or template into the design. Creates a fresh copy with new IDs. Use list_brand_components to find available items."},"inputSchema":{"type":"object","properties":{"componentId":{"type":"string","description":"ID of the brand component or template to insert."},"parentId":{"type":"string","description":"Parent node or artboard ID to insert into."},"index":{"type":"number","description":"Optional insertion index among siblings."}},"required":["componentId","parentId"]}}},"delete_brand_component":{"type":"object","properties":{"description":{"type":"string","const":"Delete a saved brand component or template from the active brand system."},"inputSchema":{"type":"object","properties":{"componentId":{"type":"string","description":"ID of the component or template to delete."}},"required":["componentId"]}}},"save_brand_template":{"type":"object","properties":{"description":{"type":"string","const":"Save selected node(s) as a reusable brand template (section/page layout) in the active brand system. Templates should use semantic tokens so they re-theme automatically when the brand changes."},"inputSchema":{"type":"object","properties":{"name":{"type":"string","description":"Display name for the template (e.g. \"Hero Section\", \"Pricing Page\")."},"category":{"type":"string","enum":["page","section","hero","features","testimonials","pricing","footer","custom"],"description":"Template category."},"nodeIds":{"type":"array","items":{"type":"string"},"description":"Array of node IDs to capture as the template."}},"required":["name","category","nodeIds"]}}},"get_brand_images":{"type":"object","properties":{"description":{"type":"string","const":"Get curated brand images and Lummi seed IDs from the active brand system. Returns image URLs that define the brand's visual style, plus Lummi image IDs that can be used with the similar images API to find FRESH on-brand imagery. Don't reuse the same curated images everywhere — use them as seeds to discover new ones."},"inputSchema":{"type":"object","properties":{"limit":{"type":"number","description":"Max number of images to return (default: all)."}}}}},"audit_brand_compliance":{"type":"object","properties":{"description":{"type":"string","const":"Audit the design for brand system compliance violations. Checks: hardcoded colors that won't theme, wrong fonts (not matching brand heading/body fonts), wrong icon library/weight, named Tailwind colors instead of semantic tokens (ALL components MUST use semantic tokens). Returns violations with auto-fix suggestions. Set autoFix=true to automatically fix what can be fixed."},"inputSchema":{"type":"object","properties":{"artboardId":{"type":"string","description":"Optional artboard ID to audit. Omit for full document."},"autoFix":{"type":"boolean","description":"If true, automatically fix violations that have auto-fix available (font replacements)."}}}}},"audit_design":{"type":"object","properties":{"description":{"type":"string","const":"Audits the design against professional quality rules: typography (scale, weight contrast, sizing), color (neutral consistency, pure black, known low-contrast combos), spacing (4pt grid, touch targets on buttons/links), and AI slop detection (monotonous centered layouts). Returns issues with severity and actionable suggestions. Use after creating or editing a design to catch quality issues."},"inputSchema":{"type":"object","properties":{"artboardId":{"type":"string","description":"Audit a specific artboard (audits all if omitted)"},"checks":{"type":"object","description":"Which checks to run (all enabled by default)","properties":{"typography":{"type":"boolean"},"color":{"type":"boolean"},"spacing":{"type":"boolean"},"slop":{"type":"boolean"}}}},"required":[]}}},"repair_design":{"type":"object","properties":{"description":{"type":"string","const":"Repairs the design by applying safe, deterministic fixes across the document (or one artboard). Fixes: adds missing w-full on top-level sections, adds missing flex display, converts arbitrary Tailwind values (hex colors, sizing, typography) to inline styles, replaces pure black with zinc-950, bumps small touch targets to 44px, adds placeholder images to empty image nodes, fills empty text content. Use after generation or editing to clean up in one pass. Pass dryRun: true to preview fixes without applying them."},"inputSchema":{"type":"object","properties":{"artboardId":{"type":"string","description":"Repair a specific artboard (repairs all if omitted)"},"dryRun":{"type":"boolean","description":"If true, report fixes without applying them (default: false)"}},"required":[]}}}}}}}}