5. The MCP server
ud mcp runs a Model Context Protocol server (protocol 2025-06-18) over stdio, named ui-designer (CMD-7).
5.1 Registration#
ud mcp runs a Model Context Protocol server (protocol 2025-06-18) over stdio, named ui-designer (CMD-7).
ud mcp install adds it to the workspace’s .mcp.json, creating the file if needed and keeping other
servers:
{
"mcpServers": {
"ui-designer": {
"command": "ud",
"args": ["mcp"]
}
}
}When ud is not on the PATH outside VS Code terminals, install writes the absolute path of the stable
ud launcher that the extension keeps up to date (CLI-001) instead of "ud". Options:
| Option | Meaning |
|---|---|
ud mcp --file <path> |
default fileKey for calls that leave it out |
ud mcp --read-only |
lists only read tools; mutating calls are refused (for review-only agents) |
ud mcp --proposal <id> |
route every write to a proposal of the live editor (§6.5); used by the AI panel. Under --proposal the server refuses every registry command flagged writesFiles or network (CMD-17) and every URL input (upload_assets, generate_figma_design), whatever --allow says; create_new_file is refused too. Refused calls return unsupported with the hint “not allowed on a proposal”. |
ud mcp --allow <ids> |
allowlist: the tools and registry command ids the server will run in this process (comma-separated, read for every read tool, globs such as node.*). Everything not listed is refused with unsupported. The panel passes it on every run (§6.3); without it the server runs whatever the plan allows. |
ud mcp --as <name> |
signing actor (else UD_ACTOR, Claude inside Claude Code, git user; CMD-29) |
ud mcp install [--user] |
write the entry to .mcp.json (or to the user’s Claude Code settings) |
ud mcp install --proposal auto |
opt in to reviewable writes for external agents: the entry gets --proposal auto, and each MCP session’s writes go to one proposal that the editor opens on the first write and the user accepts or discards in the AI panel (§6.5). Headless (no editor), auto falls back to live writes under the file lock and says so in whoami. |
Claude Code shows the tools as mcp__ui-designer__<tool>. Other clients (Claude Desktop, Cursor, VS Code’s
own MCP support) use the same command.
5.2 Tools#
tools/list returns the supported Figma tools with their input schemas, property for property (CMD-1, CMD-8),
followed by the native tools. Two deliberate relaxations of Figma’s schemas: fileKey has no
^[0-9a-zA-Z]{22,128}$ pattern and is optional (CMD-2), and nodeId accepts the forms of CMD-3. Extra
optional parameters are allowed by CMD-8 and are marked "x-ud": true in the schema.
| Tool | Mutates | Plan | M | Notes |
|---|---|---|---|---|
whoami |
no | any | 0.1 | actor, plan (ud::local), seat, proposal mode |
get_metadata |
no | any | 0.1 | sparse XML; pages when no nodeId; prepends the live selection like Figma |
use_figma |
yes | paid | 0.1 | Plugin API script, atomic (CMD-10 – CMD-12); code ≤ 50 000 chars |
create_new_file |
yes (new file; writesFiles) |
paid | 0.1 | fileName, editorType: "design"; returns the path as fileKey; refused under --proposal |
get_screenshot |
no | any | 0.2 | maxDimension, contentsOnly, enableBase64Response; result path + loopback url or url: null (§5.9) |
upload_assets |
yes (network with URLs) |
paid | 0.2 | paths (local files) or URLs → <name>.design.assets/; URLs refused under --proposal; headless one-shot needs paths (§5.9) |
download_assets |
no (writesFiles) |
any | 0.2 | writes files, returns path and loopback url or url: null (§5.9); refused under --proposal |
get_variable_defs |
no | any | 0.4 | name → value for the current mode |
search_design_system |
no | any | 0.4 | queries: [{entity, query}], includeLibraryKeys |
get_libraries |
no | any | 0.4 | libraries_added_to_file (subscribed), libraries_available_to_add (available) |
get_motion_context |
no | any | 0.5 | keyframe tracks, CSS and motion.dev snippets |
get_design_context |
no | any | 0.6 | code with the code map applied, screenshot, assets; a read, so free like every read tool (LIC-5) |
get_code_connect_map, get_code_connect_suggestions, get_context_for_code_connect, list_file_components_for_code_connect |
no | any | 0.6 | the code map (DEV-4) |
add_code_connect_map, send_code_connect_mappings |
yes | paid | 0.6 | write code-map entries (events in the file) |
generate_figma_design |
yes (network) |
paid | 0.8 | capture a running page into frames (EXP-6) with headless Chromium; refused under --proposal (the panel attaches a screenshot instead, §9.1) |
ud_run |
per command | per command | 0.1 | {command, params, fileKey?, dryRun?, ifVersion?}; any registry command (CMD-19), including tree, inspect, system.digest, selection.set, diff, comment.add. Commands flagged writesFiles (exports, codegen, token export, render --out) or network are refused under --proposal, and --allow applies per command id |
ud_apply |
yes | paid | 0.1 | {actions: [{command, params, ref?}], …}; one transaction (CMD-28) |
ud_help |
no | any | 0.1 | {topic}: a command id, a tool name, commands, plugin-api, udx, selectors |
ud_render |
no | any | 0.2 | ud render with all its options (mode, variant, region, format, scale, prototype state) |
ud_lint |
no | any | 0.6 | {nodeId?, rules?}; findings as JSON (CMD-36) |
A tool that the installed milestone does not have yet is not listed. A Figma tool that UI Designer cannot
serve is never listed (CMD-6). Under --proposal, tools that are always refused (create_new_file,
download_assets, generate_figma_design) are not listed either, and the schema of upload_assets drops
its URL form; under --allow, only the listed tools appear.
5.3 Resources#
| URI | Content | MIME |
|---|---|---|
ud://<path>.design?resource=system |
the design-system digest of that file (ud system, AI-3) |
text/plain |
ud://<path>.design?resource=pages |
pages and top-level frames | application/json |
ud://<path>.design?node-id=<id> |
ud inspect of a node |
application/yaml |
ud://plugin-api/coverage |
ud plugin-api table (CMD-14) |
text/markdown |
ud://plugin-api/ud-plugin-api.d.ts |
typings | application/typescript |
skill://ui-designer/<skill>/SKILL.md, skill://ui-designer/<skill>/references/<file>, …/scripts/<file> |
the skills of §3 | text/markdown, text/javascript |
skill://ui-designer/index.json |
list of skills with name, description, version | application/json |
resources/list lists one ?resource=system entry per .design file in the workspace (up to 50). Resource
subscriptions are supported for ?resource=system: a change to the design system sends
notifications/resources/updated.
Clients that cannot read resources reach the same content through tools: ud_run system.digest,
ud_help plugin-api, and ud_help skill:ui-designer.
5.4 Prompts#
| Prompt | Arguments | Produces |
|---|---|---|
create_design_system_rules |
clientFrameworks, clientLanguages (as Figma’s) |
instructions to write a rules file (CLAUDE.md section or .cursor/rules/…) describing the design system and how code should use its tokens and components, built from ud system and the code map |
ud_design_screen |
description, fileKey?, page? |
the design loop of §4 as a prompt, for clients without skills |
ud_review |
nodeId, fileKey? |
the review procedure of §3.7 as a prompt |
5.5 Finding the live editor#
ud mcp is a long-lived process; it does not hold the document. For every call it resolves the file and then
the bridge, the same way every ud command does (CMD-27):
- Resolve
fileKey(path,doc-…id searched among the workspace’s.designfiles,ud://link, or the--filedefault, or the only open design). - Look in the bridge folder —
$XDG_RUNTIME_DIR/ui-designer, orui-designer-<uid>in the temporary folder;UD_BRIDGE_DIRoverrides — readable only by the user. Each running VS Code window with the extension leaves a file there with the address of its local socket (a Unix socket or a named pipe), a random token, and the paths of the designs it has open. Nothing listens beyond the loopback interface: the only TCP listener is the asset and screenshot server of §5.9, bound to 127.0.0.1 (NFR-4). - If a window has the file open, send the command over that socket with the token. The editor queues it with the user’s own edits, applies it, flashes the changed nodes (UI-7) and answers. Reads see unsaved state.
- Otherwise run headless: load the file with the same document core, take the file lock, apply, write, and release the lock.
The bridge is re-resolved per call, so opening or closing the editor during an agent session just switches
mode. ud_run where reports which mode the last call used. ARC-001 specifies the bridge protocol.
5.6 Authentication and licence#
- No login for the MCP server. It is a local process of the user, reached over stdio by the user’s client. The bridge token protects the editor’s socket from other local users.
- Licence. The plan is the one of the extension on this machine (LIC-2).
ud mcpis gated exactly likeud, command for command: every read tool —get_metadata,get_screenshot,get_design_context,get_variable_defs,search_design_system,get_libraries,get_motion_context, theget_*/list_*code-map tools,ud_render,ud_lint,ud_help,download_assets,whoamiand every read command throughud_run— is free (LIC-5). Mutating calls need the trial or a paid plan; on the Free plan they returnisErrorwith codelicence(exit 4 fromud) and a hint (“UI Designer Pro unlocks editing; run ‘UI Designer: Sign In’”).whoamireportsseat: "view"on Free, so a skill can tell before it starts. Neitherud mcpnor the language model tools of §10 are “AI features” in the sense of LIC-2: that term means the AI panel and the quick actions only, and those need the paid plan or the trial as a whole (§6.7). Agents the user runs throughud mcpor VS Code chat pay nothing beyond whatuditself charges for the same mutation. - Live editor decides. When the change goes through the bridge, the editor checks the licence; headless,
udchecks the same cached licence signature (offline grace, LIC-4).
5.7 Limits#
| Limit | Default | Configurable |
|---|---|---|
use_figma time / memory / nodes created |
30 s / 256 MB / 100 000 (CMD-11) | timeout via ud use_figma --timeout; not over MCP |
use_figma code length |
50 000 characters (as Figma) | no |
| Text in one tool result | 100 000 characters; longer get_metadata is cut at a node boundary with a hint to pass a nodeId or depth |
UD_MCP_MAX_TEXT |
| Screenshot size | maxDimension default 1024, clamped to 8192 (the clamp is reported in the result) |
no |
| Concurrency | reads in parallel; mutating calls on one document are queued and run one at a time | — |
| Queue depth | 32 pending mutating calls per document; more returns busy |
— |
| Rate | none beyond the queue | — |
| Speed | CMD-31: reads < 150 ms via the bridge, mutations < 250 ms, use_figma start-up < 50 ms |
— |
5.8 Errors#
Errors are tool results with isError: true (CMD-30). The text starts with a code, then a message and a
hint; structuredContent carries the same as JSON:
{
"code": "command",
"exit": 1,
"message": "layoutSizingHorizontal: FILL can only be set on children of auto-layout frames",
"hint": "Append the node to an auto-layout frame first, then set FILL.",
"script": { "line": 14, "column": 3, "name": "Error" },
"safeToRetryWithoutCanvasRead": true,
"documentVersion": 4182
}| Code | Exit | When | Agent’s next step |
|---|---|---|---|
usage |
2 | bad parameters, schema violation | fix the call |
command |
1 | the command or script failed (incl. UnsupportedApiError) |
fix and retry; nothing was applied |
conflict |
3 | ifVersion older than the document; a proposal was discarded under the agent |
re-read, then retry |
licence |
4 | mutation without a paid plan or trial | stop and tell the user |
not_found |
5 | file, node, command or tool not found | check ids with get_metadata |
unsupported |
6 | a Figma tool or editor type UI Designer does not serve; a tool, command or input form outside --allow or refused under --proposal (§5.1) |
use the alternative named in the message; on a proposal, leave the file-writing step to the user |
busy |
1 | mutating queue full | wait for earlier calls |
stopped |
1 | the user pressed Stop on the agent status item or ran “UI Designer: Stop Agent” (UI-7) | stop; do not retry |
5.9 Asset and screenshot results#
get_screenshot, ud_render, download_assets and upload_assets deal in files. Their results name the
file and, when something can serve it, a URL; there is no file:// URL anywhere in the interface.
| Situation | get_screenshot / ud_render / download_assets result |
upload_assets |
|---|---|---|
| The call went through the bridge (editor open) | { path, url }: path is the PNG on disk; url is a single-use loopback URL served by the extension host (http://127.0.0.1:<port>/…) |
returns loopback POST URLs, one per asset, served by the extension host; paths are accepted too |
ud mcp headless (no editor), server still running |
{ path, url }: the URL is served by the ud mcp process for as long as it runs |
as above, served by ud mcp |
One-shot headless ud get_screenshot / ud render / ud download_assets |
{ path, url: null }: the process exits before anyone could fetch, so nothing is served |
ud upload_assets needs --paths; a URL-only call exits 2 (usage) with the hint to pass paths |
Rules that hold for every URL: loopback only (127.0.0.1, never 0.0.0.0), a 128-bit single-use token in the
path, a 10-minute lifetime, a Host header that must match, requests with an Origin header rejected,
10 MB per asset. The server is specified in ARC-001 §9.6 and the hardening in SRS NFR-4. Figma’s skills,
which curl the screenshot URL or POST to the upload URL, therefore work unchanged while a server runs; our
skills tell the agent to prefer path (or the inline image) and to treat url: null as “read the file”.
In the AI panel enableBase64Response is forced on, so the agent never needs the URL at all.