cocos-mcp
Allows AI assistants to drive the Cocos Creator editor: query the live scene graph, read and write assets, start builds, monitor build status and logs, capture editor screenshots, and fetch preview server information.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@cocos-mcpList all nodes in the active scene with their positions."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
cocos-mcp
A Streamable HTTP MCP server that runs inside the Cocos Creator editor process, so an AI assistant can drive the editor the same way you do: query the live scene graph, read and write assets, kick off builds, read the log, and look at a screenshot of what it just did.
Eleven tools. Zero dependencies. No build step. Tested against Cocos Creator 3.8.8.
If you landed here because the editor silently did nothing, go straight to Gotchas — that section is the real content.
Why eleven tools and not a hundred
The editor API is huge — scene alone declares 200+ messages. Wrapping each one as its own MCP tool means
a giant tool list permanently occupying the model's context, and a maintenance burden every time Creator
ships a release. Other Cocos MCP servers expose 100+ tools and then have to add "tool profiles" to hide
most of them again.
Instead this exposes a few general tools plus one that reads the editor's own TypeScript definitions, so
the model looks up a signature and then calls it. Everything the big servers do with create_label,
set_node_transform, duplicate_prefab and so on is one editor_request call here.
Related MCP server: cocos-mcp-server
Install
Copy this folder to
<your-project>/extensions/cocos-mcp.In Creator: Extension → Extension Manager → Project, enable
cocos-mcp. The console should print[cocos-mcp] http://127.0.0.1:1314/mcp.Point your MCP client at it. For Claude Code:
{ "mcpServers": { "cocos-creator": { "type": "http", "url": "http://127.0.0.1:1314/mcp" } } }
Ports
One editor instance per port. If you keep two projects open in Creator at once, the second one loses the
race for 1314 and silently has no server, so give each project its own port — either via COCOS_MCP_PORT,
or by dropping a .port file (gitignored) next to main.js:
echo 1315 > extensions/cocos-mcp/.portThen point each project's MCP config at its own port. Keeping the server name the same across projects
means the tool names (mcp__cocos-creator__*) stay stable wherever you are:
{ "projects": {
"/path/to/project-a": { "mcpServers": { "cocos-creator": { "type": "http", "url": "http://127.0.0.1:1314/mcp" } } },
"/path/to/project-b": { "mcpServers": { "cocos-creator": { "type": "http", "url": "http://127.0.0.1:1315/mcp" } } }
} }Transport
Streamable HTTP (MCP spec 2025-03-26 and later), which replaced the two-endpoint HTTP+SSE transport from
2024-11-05. One endpoint; POST returns application/json — the spec makes the SSE stream optional, and a
single JSON object is the other allowed answer. GET returns 405, which is what the spec prescribes for a
server that offers no stream.
Request | Response | Spec |
|
| server MUST return either this or |
|
| required |
|
| allowed when the server offers no SSE stream |
unsupported |
| required |
no | served, assumed | required |
any request carrying |
| Origin validation is required — see Security |
SSE would only earn its place if the server needed to push without being asked. Build progress is polled
through build_status instead, which is simpler and enough. The one case that would benefit is
build with wait: true, which currently blocks for the whole build.
Tools
Tool | What it does |
|
|
| Async JS in the editor main process; |
| Async JS in the scene process; |
| Greps the editor's |
| One asset: size, sub-assets, resolved referencers, dependencies |
| Starts a build, reusing the last task's options; |
| Task state, progress, and the messages carrying build errors |
| Tails the project / builder / asset-db log with a regex filter |
| Captures an editor window as a PNG image block |
| Preview server URL, platform, and connection count |
| Reloads this extension so code edits take effect |
Resources
cocos://project, cocos://scene/active, cocos://build/latest, cocos://log/recent.
Gotchas
Every bug found while building this had the same shape. The editor rarely throws — it returns something that looks like success, and you find out later. Half of these are Cocos's behaviour, half are mistakes this extension made itself while working around the first half.
What you see | What actually happened |
| the scene script exported its functions at the top level instead of under |
| a build is already running; yours is waiting behind it |
| the platform was invalid — nothing says which option, and |
| a build is actively processing |
| the directory inode, not the 62 KB inside it (our bug) |
a blank line from a log | the file ended with a newline (our bug) |
a green test run |
|
| any web page could still POST here and run code (our bug) |
| the notice never said anything was cut (our bug) |
a missing | the test suite deleted it in a |
| it was only searching a quarter of the API (our bug) |
The practical consequence: read every "it worked" twice, and when adding a tool here, ask what its failure looks like before asking what its success returns. The details behind each row follow.
A scene script must export methods. Exporting the functions at the top level makes
execute-scene-script return undefined — silently, with no error, even though the extension is enabled
and contributions.scene.script is correct.
exports.methods = { run(code) { /* ... */ } }; // right
module.exports = { run(code) { /* ... */ } }; // silently never calledA reference index can name assets that are not in this checkout. query-asset-users returns uuids, and
some of them resolve to nothing — other branches that share the same asset folder, or deleted files. On a
multi-game repo where every branch checks out only its own game code but shares assets/common/, this is
routine: one shared image reported 61 referencers on one branch and 94 on another, plus 15 unresolvable
either way. asset_info counts those separately as ghostUsers instead of silently inflating the total.
A zero reference count is not permission to delete. It only means nothing in this checkout references it. Five images that looked dead on one branch turned out to be used by another game on a sibling branch.
scene_eval returns must be JSON-serializable, and undefined used to be ambiguous. The value crosses
a process boundary, so returning a live Node/Component/Scene fails with a circular-structure error —
return node.name or node.children.map(n => n.name) instead. And undefined means either "your code has
no return" or "the scene script was never registered", the second being completely silent; scene_eval now
pings the scene script before accepting an undefined and tells you to reload if it is dead.
@cocos/creator-types only covers a fraction of the API. The bundled types declare ~51 scene
messages; the running editor has 200+. add-task, query-preview-url and many others are declared only
in each installed package's own @types inside CocosCreator.app. editor_api scans both.
add-task returns two different enums. With shouldWait: false it returns TaskAddResult
(1 = SUCCESS); with true it returns BuildExitCode (36 = BUILD_SUCCESS). Same integer space, different
meanings. build decodes whichever applies.
Build warnings are not in build_status. Its detailMessage only carries the last hook name. Real
warnings and errors go to temp/logs/project.log — use editor_log.
A second build queues silently, and free does not tell you. add-task returns SUCCESS even when a
build is already running — the new task just sits there with Wait a moment, build task is busy in its
message. query-tasks-info().free stays true throughout, so it cannot be used to detect this; check task
state for processing/waiting instead. build now reports queuedBehind with those ids.
Cancel with editor_request builder break-task [id].
Bad build options queue successfully, then fail silently. add-task accepts anything — an invalid
platform gets a cheerful SUCCESS (queued), and the task only fails later with 构建参数校验失败 and an
empty detailMessage, so nothing tells you which option was wrong. build now runs options through the
builder's own check-and-complete-options first, which rejects them up front and lists the valid platforms.
That call also completes every required field and follows taskName to outputName, so it beats merging a
previous task's options by hand.
Build detail lives in the builder's own log. temp/logs/project.log gets the warnings, but
editor_log with source: "builder" reaches temp/builder/log/, which has per-stage timings and memory
tracking. Those files run to megabytes, so only the tail is read.
Reloading needs a deferred disable/enable. The reload tool handles it: it replies first, then tears
the server down 300 ms later — disabling the package kills the HTTP server, so doing it inline loses the
response. Keep-alive sockets would otherwise hold the port across a reload, so unload() destroys them
explicitly. Scene scripts are require-cached, so editing scene-script.js does nothing until you reload.
Large results are truncated at 20 000 chars. Do not retry a truncated call as-is — query-assets on a
folder blows the cap with a dozen assets. Loop inside editor_eval and return only the fields you need;
the round trips happen inside the editor and cost nothing.
Driving the running game is a browser automation job — preview just hands over the URL. Two things
that bite when clicking the canvas from Puppeteer/Playwright:
The engine registers
mousedown/mouseupon the canvas (pal/input/web/mouse-input.ts). SyntheticPointerEvents are ignored.cc.UITransformisundefinedon the preview page'sccglobal; usenode.worldPosition, orgetComponent('cc.UITransform')by string. And re-measuregetBoundingClientRect()every time — the preview's device toolbar collapses and shifts the canvas.
const cam = cc.director.getScene().getComponentInChildren('cc.Canvas').cameraComponent;
const s = cam.worldToScreen(cc.find('Canvas/path/to/node').worldPosition);
const r = document.querySelector('canvas').getBoundingClientRect();
const base = { bubbles: true, cancelable: true, view: window, button: 0,
clientX: r.x + s.x / devicePixelRatio,
clientY: r.y + (r.height - s.y / devicePixelRatio) }; // Cocos origin is bottom-left
canvas.dispatchEvent(new MouseEvent('mousedown', { ...base, buttons: 1 }));
canvas.dispatchEvent(new MouseEvent('mouseup', { ...base, buttons: 0 }));Tests
Runs offline against a stubbed Editor — no editor needed:
node test.jsIt binds port 19314 and aborts if that is taken, so it can never accidentally run its assertions
against a live editor. Override with COCOS_MCP_PORT.
Security
editor_eval and scene_eval execute arbitrary code with full editor privileges, so the server has to
assume any request could be hostile.
The spec is explicit about which of these is which: validating Origin is a MUST, binding to
localhost is only a SHOULD. This server originally did the SHOULD and skipped the MUST — and said so
in this very section, as if the SHOULD were the guarantee.
Binding to 127.0.0.1 is not enough, and believing otherwise was a real hole here. A page the
developer visits while Creator is open can POST to a loopback port: text/plain makes it a CORS simple
request, so there is no preflight to block, and although the attacker cannot read the reply, the code
has already run. Confirmed against an earlier build of this server — a cross-origin request executed
require('os').hostname() inside the editor.
Two things guard it now: any request carrying an Origin header is refused with 403 (browsers always
send one, MCP clients never do), and bodies over 4 MB are rejected before being buffered. Keep both.
Anything that reaches this port can run code as the editor.
License
MIT
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Control Unreal Engine to browse assets, import content, and manage levels and sequences. Automate…
A comprehensive Model Context Protocol (MCP) server that enables AI assistants to control Unreal E…
Drive a live Cinevva game session: edit game files, import CC0 assets, preview changes.
Remote MCP server for AI.TV creators — delegate account operations to your AI agent over MCP.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables AI assistants to control Cocos Creator game development directly within the engine, providing tools for node manipulation, asset management, scene operations, and AI-powered image generation.34ISC
- AlicenseNot gradedqualityFmaintenanceEnables AI assistants to directly control the Cocos Creator 3.8.x editor via MCP protocol, providing over 130 tools for scene, node, component, asset, and project operations.3039MIT
- -licenseNot gradedqualityNot gradedmaintenanceEnables AI assistants to interact with the Cocos Creator 3.8+ editor through standardized protocols for scene, node, component, prefab, asset, project, debugging, and server operations.
- AlicenseNot gradedqualityCmaintenanceEnables AI assistants to directly control the Cocos Creator game editor via MCP protocol, supporting scene management, node manipulation, component attachment, and asset management.302MIT
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/az198071123/cocos-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server