cocos-mcp-v3-ui
MCP server (UI-design tools) for Cocos Creator 3.8.x, built as an editor package. It lets an MCP client (Claude Code) drive the editor to build UI — ideal for translating a Figma design into a Cocos scene: read the design with the Figma MCP, then create nodes/components/sprites/prefabs here.
Ported (not mechanically) from a 2.x cocos-mcp-v2-ui. 3.x differs fundamentally: the scene bridge is the Promise-based Editor.Message.request('scene','execute-scene-script'), node size/anchor/opacity/color live on components (cc.UITransform / cc.UIOpacity / the render component — not on the node as in 2.x), and the package is plain CommonJS.
Architecture
Claude Code ──stdio(MCP)──▶ bridge/ ──HTTP /rpc──▶ editor package (main proc)
(system Node, core/server + registry + tools
@modelcontextprotocol) │
▼ Editor.Message.request
('scene','execute-scene-script')
scene.js (scene proc, cc.* live)
- Editor package (
main.js,core/,tools/,scene.js,scene/) runs inside
Cocos Creator. main.js starts an HTTP server (core/server.js) exposing GET /health, POST /rpc {tool,args}, GET /tools, and a stateless POST /mcp. The server is loopback-only and rejects browser-originated requests (no Origin header, loopback Host only) so a web page cannot drive the editor (see isLocalRequest).
- Bridge (
bridge/) is a separate Node process Claude spawns. It speaks the MCP
protocol over stdio (via the official SDK) and proxies tools/list+tools/call to the editor's HTTP /tools+/rpc. Keeping MCP-spec compliance out of the editor process.
- Single-responsibility / DRY: tool modules are thin (schema + delegate). Everything
crossing into the scene goes through one core/scene-bridge.js#callScene; one core/response.js shape; scene-side has one resolveNode, one serialize, one applyTransform. Each mutating tool records an editor snapshot (undo + dirty flag).
Install
- Symlink (or copy) this folder into the project's
extensions/(orpackages/):
ln -s /Users/nguyennt/Documents/MCP/cocos-mcp-v3-ui \
<your-cocos-3.x-project>/extensions/cocos-mcp-v3-ui
- Install the bridge deps once:
cd bridge && npm install. - Open the project in Cocos Creator 3.8.x. The package auto-starts the server on
port 8585 (autoStart: true). Panel: menu Extension → Cocos MCP v3 UI → Open Panel.
Connect to Claude Code
claude mcp add cocos-ui --transport stdio -- \
node /Users/nguyennt/Documents/MCP/cocos-mcp-v3-ui/bridge/bin/cocos-ui-bridge.js --port 8585
Verify: /mcp in Claude lists cocos-ui connected and the 36 tools. The editor must be running with a scene open for scene tools to work (otherwise they return No active scene).
Tools (36)
| Category | Tools | |---|---| | ping (1) | ping | | scene (5) | get_current_scene, get_scene_hierarchy, open_scene, save_scene, capture_node | | node (10) | create_node, get_node_info, find_nodes, set_node_transform, set_node_property, set_node_properties_batch, create_node_tree, set_sibling_index, delete_node, duplicate_node | | component (13) | add_component, set_component_property, get_components, remove_component, set_component_reference, add_click_event, add_components_batch, set_component_references_batch, add_click_events_batch, set_label_style, apply_widget, draw_rounded_rect, apply_gradient | | asset (2) | import_asset, query_asset_uuid | | prefab (2) | create_prefab, instantiate_prefab | | referenceImage (3) | add_reference_image, set_reference_image_data, remove_reference_image |
MCP tool name = <category>_<method> (e.g. node_create_node, referenceImage_add_reference_image). A category must not contain an underscore — dispatch splits on the FIRST underscore.
Highlights (added per build-experience feedback)
coloris forgiving:set_component_property/set_node_property/tree specs accept{r,g,b,a},
[r,g,b,a], or a hex string ("#161718").
create_node_tree: build an entire node + component + property subtree in ONE call (returns uuids).draw_rounded_rect: native rounded backgrounds/borders viacc.Graphics— no texture hacks.apply_gradient: bakes a rounded gradient PNG and binds it as a spriteFrame — real gradient
buttons/badges (cc.Graphics fill is solid-only).
set_component_reference+add_click_event: wireScrollView.content,Widget.target,
button handlers, etc.
- Enum names: enum props accept names (
type:"VERTICAL",sizeMode:"CUSTOM") as well as numbers. import_assetauto-creates the destination folder, auto-rasterizes SVG-content files, and fails
loudly instead of silently importing nothing. spriteFrame assignment preserves node size.
Figma → Cocos workflow
- Read the design with the Figma MCP (positions, sizes, colors, text).
scene_get_scene_hierarchy→ find theCanvasuuid.- (optional) export a screenshot from Figma →
asset_import_asset→
referenceImage_add_reference_image { spriteFrameUuid } to trace it.
node_create_nodeper element →component_add_component(cc.Sprite/cc.Label/
cc.Button/cc.Widget/cc.Layout) → component_set_component_property (string, fontSize, color, spriteFrame) → node_set_node_transform to place/size.
- For images:
asset_import_assetreturnsspriteFrameUuid— pass that to
set_component_property property:"spriteFrame" (it persists; the texture uuid does not).
- Reusable bits:
prefab_create_prefab→prefab_instantiate_prefab. scene_save_scene.
Notes & limitations (3.x specifics)
capture_node(edit-mode) — 3.x has no immediate-modecamera.render(node), so we
render through a temporary off-screen cc.Camera + cc.RenderTexture framed on the node's world bounds, force a frame, then readPixels() (falls back to borrowing the scene camera). The scene side returns base64 RGBA (bottom-up); the main side flips + PNG-encodes to disk and returns the path. Works in edit mode at the node's own aspect; maxSize caps the longest side (default 720). You can also verify by DATA with get_scene_hierarchy / get_components.
- One render component per node (3.8.x) —
cc.Sprite,cc.Graphics,cc.Label,
cc.RichText all derive from cc.UIRenderer and remain mutually exclusive on a node (verified: adding cc.Sprite next to cc.Graphics throws a conflict error). So a node can't have both draw_rounded_rect (Graphics) and apply_gradient/spriteFrame (Sprite) — apply_gradient auto-routes to a child gradient_bg node when the target already has a render component, and add_component surfaces the engine's conflict message.
- Reference image is simulated — Cocos has a native reference-image gizmo, but to keep the
workflow we add a low-opacity Sprite node __ReferenceImage__ behind the UI. Remove it before shipping (remove_reference_image); it is a real scene node.
- Editor Hierarchy/Inspector refresh on the next reselect/reload after raw-runtime
mutations. Each mutating tool records a snapshot (so undo works and the scene is marked dirty); the live scene/canvas updates immediately.
- No main-process hot-reload: editing
main.js/core//tools//scene/during
development requires a full editor restart (Developer → Reload Extensions/Editor). At runtime this is irrelevant — the server starts on project open.
- spriteFrame: importing a PNG yields a texture and a
sprite-framesub-asset. Bind the
sub-asset uuid (returned as spriteFrameUuid). In 3.x these sub-assets have stable uuid suffixes (<uuid>@f9941 spriteFrame, <uuid>@6c48a texture); assetdb.importFiles verifies them via asset-db:query-url before returning.
- Verified editor messages (3.8.x): scene
execute-scene-script/save-scene/open-scene/
snapshot; asset-db:query-uuid / query-url / import-asset / create-asset / refresh-asset. Prefab create uses the scene create-prefab message where available, with a manual drag-to-Assets fallback.
Security
The HTTP server binds 127.0.0.1 and only serves loopback requests that carry no Origin header (i.e. the Node bridge, not a browser). This prevents a malicious web page from POSTing to http://127.0.0.1:8585/rpc to drive the editor or write files, and the loopback Host check blocks DNS-rebinding. Do not expose the port beyond localhost.
Tests
- Unit (pure logic — colors, enums, PNG encoder, gradient, SVG sniff; no editor needed):
node test/unit.test.js
- Integration (error/edge paths for the scene & component tools; needs the editor running with a
scene open — creates a temp container node and deletes it afterwards): `` node test/integration.test.js --port 8585 ``
- See
test/manual-checklist.mdfor the per-tool manual verification steps.






