Studio
Studio for AI Agents
Efecto Studio is a real-time web design tool that AI agents can control programmatically. Create artboards, add elements, style with Tailwind CSS, and see changes instantly in the browser — no API keys required.
Using Claude Code?
npx @efectoapp/mcp-studio installHow it works
Your agent creates a session via REST. The user opens the session URL in their browser. The agent pushes tool calls over HTTP, which execute in the browser in real time. The user watches the design being built live.
Agent ──POST /execute──▶ Server ──SSE──▶ Browser (live preview) Agent ◀──poll/response── Server ◀──POST── Browser (tool results)
Quick Start
Step 1: Create a session
curl -X POST https://efecto.app/api/v1/studio/sessions \
-H "Content-Type: application/json" \
-d '{"label": "My design session"}'Response:
{
"sessionId": "abc123",
"studioUrl": "https://efecto.app/studio?session=abc123",
"expiresAt": "2026-02-26T12:30:00.000Z"
}Step 2: Open the URL in a browser
Open the studioUrl from the response. The browser connects to your session via SSE and waits for tool calls.
Step 3: Push tool calls
# Create an artboard
curl -X POST https://efecto.app/api/v1/studio/sessions/abc123/execute \
-H "Content-Type: application/json" \
-d '{
"tool": "studio_create_artboard",
"input": {
"name": "Homepage",
"width": 1440,
"height": 900,
"className": "flex flex-col"
}
}'The browser executes the tool and the artboard appears instantly. The response includes a callId and the tool result:
{
"callId": "call_xyz",
"status": "completed",
"result": {
"success": true,
"message": "Created artboard \"Homepage\" (1440x900)",
"data": { "artboardId": "art_001" }
}
}No API keys needed
Session API
Six endpoints manage the lifecycle of a Studio session.
POST /api/v1/studio/sessions
Create a new session. Returns a session ID and a URL for the browser.
curl -X POST https://efecto.app/api/v1/studio/sessions \
-H "Content-Type: application/json" \
-d '{"label": "Claude Code session"}'| Field | Type | Description |
|---|---|---|
label | string? | Optional human-readable label |
Response: { sessionId, studioUrl, expiresAt }
GET /api/v1/studio/sessions/:sessionId
Get session info, including whether a browser is connected.
curl https://efecto.app/api/v1/studio/sessions/abc123Response: { session, pendingCalls, completedCalls }
DELETE /api/v1/studio/sessions/:sessionId
Close a session and disconnect the browser.
curl -X DELETE https://efecto.app/api/v1/studio/sessions/abc123POST /api/v1/studio/sessions/:sessionId/execute
Push a tool call for the browser to execute. If the browser is connected, the tool executes immediately and the result is returned. If the browser is not connected, the call is queued (up to 100 pending calls).
curl -X POST https://efecto.app/api/v1/studio/sessions/abc123/execute \
-H "Content-Type: application/json" \
-d '{
"tool": "studio_add_section",
"input": {
"parentId": "art_001",
"jsx": "<nav className=\"flex items-center justify-between px-8 py-4 w-full\"><h1 className=\"text-xl font-bold\">Acme</h1></nav>"
}
}'| Field | Type | Description |
|---|---|---|
tool | string | Tool name (see Tool Reference below) |
input | object? | Tool input parameters |
Response: { callId, status, result? }. Status is completed when executed immediately, or pending if queued.
GET /api/v1/studio/sessions/:sessionId/stream
Server-Sent Events stream for the browser. The browser connects to this endpoint to receive tool calls in real time. You do not need to call this directly — opening the studioUrl handles it automatically.
| Event | Description |
|---|---|
connected | Confirms SSE connection is established |
tool_call | New tool call to execute (includes callId, tool, input) |
heartbeat | Keep-alive ping (every 15s) |
session_closed | Session has been closed |
POST /api/v1/studio/sessions/:sessionId/result
Browser submits the result of a tool call back to the server. This is handled automatically by the Studio client — agents do not call this directly.
Tool Reference
33 tools for reading, creating, and modifying Studio documents. All tools are invoked via the POST /execute endpoint.
State & Query
| Tool | Description | Required Params |
|---|---|---|
studio_get_state | Returns the full document as JSX-like markup with data-id attributes | (none) |
studio_list_artboards | Lists all artboards with IDs, names, dimensions, positions | (none) |
studio_find_nodes | Search nodes by name, text content, type, or className | query? type? classNameContains? |
Artboards
| Tool | Description | Required Params |
|---|---|---|
studio_create_artboard | Create a new artboard. Use className "flex flex-col" for vertical layout. | name width height |
studio_update_artboard | Update artboard name, size, background, className | artboardId |
studio_delete_artboard | Delete an artboard and all its contents | artboardId |
studio_duplicate_artboard | Deep-clone an artboard with fresh IDs | artboardId |
Nodes
| Tool | Description | Required Params |
|---|---|---|
studio_add_node | Add a single node (frame, text, image, button, link, icon, input, video) | parentId type |
studio_add_section | Add complex layouts from JSX-like markup (most efficient for multi-element structures) | parentId jsx |
studio_update_node | Update any node property (className, textContent, tag, src, etc.) | nodeId |
studio_update_className | Update only the Tailwind classes of a node (shortcut) | nodeId className |
studio_delete_node | Delete one or more nodes and their children | nodeIds |
studio_move_node | Reparent or reorder a node within its parent | nodeId newParentId |
studio_duplicate_node | Deep-clone a node (inserted after the original) | nodeId |
studio_group_nodes | Wrap nodes in a new frame container | nodeIds |
studio_ungroup_node | Unwrap a group, moving children up to the parent | groupId |
studio_reorder_node | Bring to front or send to back within parent | nodeId position |
studio_batch_update | Update multiple nodes in one call (bulk styling) | updates |
studio_replace_section | Replace a node and its children with new JSX in-place | nodeId jsx |
UI
| Tool | Description | Required Params |
|---|---|---|
studio_select_nodes | Highlight nodes on the canvas for the user | nodeIds |
studio_set_visibility | Show or hide a node (hidden nodes remain in the tree) | nodeId visible |
Alignment & Distribution
| Tool | Description | Required Params |
|---|---|---|
studio_align_nodes | Align multiple nodes (left, center, right, top, middle, bottom) | nodeIds alignment |
studio_distribute_nodes | Distribute nodes evenly (horizontal or vertical) | nodeIds direction |
History
| Tool | Description | Required Params |
|---|---|---|
studio_undo | Undo the last action (equivalent to Cmd+Z) | (none) |
studio_redo | Redo the last undone action (equivalent to Cmd+Shift+Z) | (none) |
Viewport & Document
| Tool | Description | Required Params |
|---|---|---|
studio_zoom_to_artboard | Zoom the viewport to show a specific artboard | artboardId |
studio_zoom_to_fit | Zoom to fit all artboards in the viewport | (none) |
studio_set_viewport | Set viewport zoom level and pan position | zoom? panX? panY? |
studio_rename_document | Rename the Studio document | name |
studio_new_document | Create a new blank document (replaces current) | name? |
Canvas & Reading
| Tool | Description | Required Params |
|---|---|---|
studio_move_artboard | Reposition an artboard on the canvas for multi-screen flows | artboardId x y |
studio_deselect_all | Clear the current node selection | (none) |
studio_get_node_tree | Get JSX for a specific node or artboard subtree (faster than full document) | nodeId |
Data Model
Studio documents are trees of HTML nodes styled with Tailwind CSS. Every node maps to a real DOM element.
StudioDocument
└── Pages[]
└── Artboards[] # Root containers (like frames in Figma)
├── width, height # Fixed dimensions in CSS pixels
├── backgroundColor # Hex color (inline style)
├── className # Tailwind classes ("flex flex-col")
└── children[] # StudioNode tree
├── type # frame | text | image | button | link | icon | input | video
├── tag # HTML tag (div, section, h1, p, img, button, a, ...)
├── className # ALL styling lives here (Tailwind CSS)
├── style # Inline CSS escape hatch (use sparingly)
└── children[] # Nested nodesclassName is the source of truth
className as Tailwind CSS classes. There is no node.gap, node.padding, or node.flexDirection. Use Tailwind classes like flex flex-col gap-4 p-6.Node Types
| Type | HTML Tags | Content Properties |
|---|---|---|
| frame | div, section, article, aside, main, nav, header, footer, ul, ol, li, figure | (none — pure container) |
| text | h1-h6, p, span, blockquote, label | textContent, richText? |
| image | img | src, alt, objectFit? |
| button | button | textContent, variant?, disabled? |
| link | a | href, textContent, target? |
| icon | svg | iconName, iconWeight?, iconSize? |
| input | input, textarea, select | inputType, placeholder, label? |
| video | video | src, poster?, autoPlay?, loop? |
Common Artboard Sizes
| Device | Width | Height |
|---|---|---|
| Desktop | 1440 | 900 |
| Tablet | 768 | 1024 |
| Mobile | 375 | 812 |
Example: Build a Landing Page Hero
A complete sequence of tool calls that builds a hero section from scratch. Each call uses the POST /execute endpoint.
1. Create the artboard
{
"tool": "studio_create_artboard",
"input": {
"name": "Landing Page",
"width": 1440,
"height": 900,
"backgroundColor": "#ffffff",
"className": "flex flex-col"
}
}Returns: { artboardId: "art_001" }
2. Add the navigation bar
{
"tool": "studio_add_section",
"input": {
"parentId": "art_001",
"jsx": "<nav className=\"flex items-center justify-between px-12 py-4 w-full\"><h1 className=\"text-xl font-bold text-gray-900\">Acme Inc</h1><div className=\"flex items-center gap-6\"><a className=\"text-sm text-gray-600\">Features</a><a className=\"text-sm text-gray-600\">Pricing</a><a className=\"text-sm text-gray-600\">About</a><button className=\"px-4 py-2 bg-gray-900 text-white text-sm rounded-lg\">Get Started</button></div></nav>"
}
}3. Add the hero section
{
"tool": "studio_add_section",
"input": {
"parentId": "art_001",
"jsx": "<section className=\"flex flex-col items-center justify-center grow px-12 py-24 w-full\"><h1 className=\"text-6xl font-bold text-gray-900 text-center\">Build faster with Acme</h1><p className=\"text-xl text-gray-500 text-center mt-6 max-w-2xl\">The modern platform for building beautiful, responsive web applications. Ship in days, not months.</p><div className=\"flex items-center gap-4 mt-10\"><button className=\"px-8 py-3 bg-gray-900 text-white rounded-lg text-lg font-medium\">Start Free Trial</button><button className=\"px-8 py-3 border border-gray-300 text-gray-700 rounded-lg text-lg font-medium\">Watch Demo</button></div></section>"
}
}4. Read back the state
{
"tool": "studio_get_state",
"input": {}
}Returns the full document as JSX-like markup with data-id attributes you can use to reference nodes for updates.
5. Update styling
{
"tool": "studio_batch_update",
"input": {
"updates": [
{
"nodeId": "<nav-id-from-get_state>",
"className": "flex items-center justify-between px-12 py-4 w-full border-b border-gray-100"
},
{
"nodeId": "<h1-id-from-get_state>",
"textContent": "Build 10x faster with Acme"
}
]
}
}6. Select nodes for the user
{
"tool": "studio_select_nodes",
"input": {
"nodeIds": ["<hero-section-id>"]
}
}Tailwind CSS Tips
Studio renders nodes as real HTML with Tailwind classes. Follow these guidelines for best results.
- Use named colors:
bg-white,bg-gray-900,text-blue-500. Arbitrary hex values likebg-[#f9f9f9]are auto-converted to inline styles but named colors are preferred. - Always set display on containers:
flex,flex flex-col,grid. Without it, children stack at (0,0). - Top-level children need w-full: Direct children of a flex artboard should include
w-fullto span the artboard width (auto-added by validation). - Use grow instead of flex-1: The
flex-1class is not inspector-safe. Usegrowinstead. - Use semantic HTML tags:
header,nav,main,section,footerfor better structure. - Text goes in text nodes: Use h1-h6, p, span for text content, not inside frame containers directly.
Claude Code Integration
The recommended way to use Studio programmatically is via the Efecto Studio MCP Server, which exposes all 33 Studio design tools as first-class MCP tools to Claude Code.
Install the MCP Server
npx @efectoapp/mcp-studio installRestart Claude Code after installing. All 36 Studio tools (3 session + 33 design) are immediately available as first-class MCP tools — no proxy wrappers needed.
Example prompts
"Open Studio and design a SaaS landing page"
"Create a mobile app login screen in Studio"
"Design a pricing table with 3 tiers in Studio"
"Build a dashboard layout with sidebar navigation"MCP tools
@efectoapp/mcp-studio server exposes every Studio tool as a first-class MCP tool. Claude Code creates a session, opens the browser URL, and pushes design tools directly — you just describe what you want.Direct API Usage
You can also use the Session API directly from any HTTP client, script, or CI pipeline. The full flow is:
- Create a session (
POST /sessions) - Open the
studioUrlin a browser - Push tool calls (
POST /sessions/:id/execute) - Read results from each response
- Close the session when done (
DELETE /sessions/:id)
Limits
| Limit | Value |
|---|---|
| Session TTL | 15 minutes of inactivity |
| Max sessions per IP | 10 |
| Max pending tool calls | 100 |
| Tool call timeout | 30 seconds |