cocos-web-inspector-mcp
v2.1.0
Published
MCP server to inspect and debug Cocos Creator 3.x web builds in local Chromium over CDP
Readme
cocos-web-inspector-mcp
Read-only MCP server for inspecting Cocos Creator 3.x games running in a local Chromium browser.
Scope
The current MVP supports:
- Chromium connected through a loopback Chrome DevTools Protocol (CDP) endpoint
- Cocos Creator 3.x web builds served from loopback
- stdio MCP transport
- Scene, node, component, and public-property inspection
- Temporary DOM-only node highlighting
By default it does not edit Cocos game state, launch browsers, expose browser console/network/storage data, or support Firefox/WebKit. Runtime mutations require the explicit --allow-runtime-mutation startup flag and affect the attached web build only. Console, network, and storage tools require the separate --allow-browser-data flag.
Requirements
- Node.js 22+
- A Chromium browser started with remote debugging bound to loopback
- A Cocos Creator 3.x web build served from loopback
The quickest start launches a locally installed Chrome with a disposable profile, loopback-only remote debugging, and the game URL, then prints the matching claude mcp add command:
npx --yes cocos-web-inspector-mcp launch http://localhost:7456/
npx --yes cocos-web-inspector-mcp launch http://localhost:7456/ --port 9223 --device iphone-14 --landscape --cpu-slowdown 4 --network fast-3glaunch options: --port (default 9222), --profile <dir> (default a per-port directory under the system temp folder), --chrome-path (or CHROME_PATH), and the cocos_emulate_device settings --device, --landscape, --cpu-slowdown, and --network. Chrome drops emulation when its controlling CDP session closes, so with emulation flags the command stays attached; press Ctrl+C to close Chrome. The MCP server itself never launches a browser.
Check a setup step by step, with a fix for each failure:
npx --yes cocos-web-inspector-mcp doctor --cdp-endpoint http://127.0.0.1:9222doctor checks the endpoint policy, what answers on the port, eligible localhost pages, Cocos 3.x detection, and the active scene. It exits with status 1 when a check fails.
Or start Chrome manually on Windows with a disposable profile:
chrome.exe --remote-debugging-address=127.0.0.1 --remote-debugging-port=9222 --user-data-dir="$env:TEMP\cocos-mcp-profile"Keep this browser profile separate from normal browsing. CDP provides code-execution-level access to attached pages.
Install and run
Run the published npm package with the default CDP endpoint, http://127.0.0.1:9222:
npx --yes cocos-web-inspector-mcpRun with an explicit endpoint:
npx --yes cocos-web-inspector-mcp --cdp-endpoint http://127.0.0.1:9222Enable the bounded runtime debugger explicitly:
npx --yes cocos-web-inspector-mcp --allow-runtime-mutationThis flag registers runtime mutation tools for that server process only. Changes affect the current web build, may trigger Cocos callbacks, and disappear after reload; they do not edit Cocos project files.
Endpoint precedence is:
--cdp-endpointCOCOS_CDP_ENDPOINThttp://127.0.0.1:9222
--cdp-endpoint and --allow-runtime-mutation are the only server options; launch and doctor are separate commands. --cdp-endpoint identifies the browser CDP endpoint, not a game page URL.
The server uses stdio for MCP. Standard output is reserved for protocol traffic; startup diagnostics are written to standard error.
MCP configuration
Use the published npm package directly:
{
"mcpServers": {
"cocos-web-inspector": {
"command": "npx",
"args": [
"--yes",
"cocos-web-inspector-mcp",
"--cdp-endpoint",
"http://127.0.0.1:9222"
]
}
}
}The endpoint may instead be provided through the MCP process environment:
{
"mcpServers": {
"cocos-web-inspector": {
"command": "npx",
"args": ["--yes", "cocos-web-inspector-mcp"],
"env": {
"COCOS_CDP_ENDPOINT": "http://127.0.0.1:9222"
}
}
}
}Add --allow-runtime-mutation for debugger tools, --allow-browser-data for console, network, and storage tools, and --allow-method-call for cocos_call_method; each flag registers only its own tools.
Tools
All tool input objects are strict. Unknown fields are rejected. Inspection tools are annotated as read-only, idempotent, non-destructive, and closed-world. The highlight tool is annotated as a non-destructive, non-idempotent, closed-world temporary mutation because repeated calls reset its removal timer. Runtime debugger tools are non-read-only and closed-world; cocos_click_node, cocos_drag_node, and cocos_type_text are additionally destructive and open-world because game input handlers can reach real servers or make irreversible changes, and they, cocos_step_frame, and cocos_analyze_batches are non-idempotent.
| Tool | Purpose | Inputs |
| --- | --- | --- |
| cocos_list_pages | List bounded summaries of eligible localhost pages, including sanitized URL, title, Cocos detection, version, and scene name. | None |
| cocos_runtime_info | Return bounded engine, scene, canvas, view, director, and node-count information. | pageUrl? |
| cocos_runtime_diagnostics | Return passive bounded hierarchy counts, depth, duplicate names, and render metrics (FPS, frame time, draw calls, triangles, instances). | pageUrl? |
| cocos_set_node_active | Set one node's active state. Registered only with --allow-runtime-mutation; returns before/after state. | pageUrl?; uuid; active boolean |
| cocos_set_transform | Update supplied position, rotation, and/or scale fields for one node. | pageUrl?; uuid; position?, rotation?, scale? finite vectors |
| cocos_set_property | Update one bounded public component data property. | pageUrl?; node uuid; componentUuid; key; primitive/vector/size/color value matching current shape |
| cocos_click_node | Dispatch a real click at the visible center of one UI node so Button/touch handlers run (a tap under mobile emulation). Registered only with --allow-runtime-mutation. | pageUrl?; uuid |
| cocos_drag_node | Drag from the visible center of one UI node with real pointer input (touch under mobile emulation), to scroll a ScrollView, flip a PageView, or move a Slider. | pageUrl?; uuid; dx, dy CSS pixels -4000..4000, not both zero; steps? 1..60, default 10; durationMs? 0..5000, default 300 |
| cocos_type_text | Tap one EditBox and type with real keyboard input, replacing its content, so text-changed and editing events fire. Returns the resulting text, or redacted for password boxes. | pageUrl?; uuid; text up to 2,000 chars; submit? presses Enter |
| cocos_analyze_batches | Capture the 2D draw batches of the next rendered frame: the node that starts each batch and why the previous one broke. | pageUrl?; limit? 1..500, default 100; tintMs? 100..30000 overlays each batch in its own color |
| cocos_pause | Pause the Cocos director when its public API supports it. | pageUrl? |
| cocos_resume | Resume the Cocos director when its public API supports it. | pageUrl? |
| cocos_step_frame | Advance a paused game by 1–60 fixed-delta frames through cc.game.step, then stay paused. Fails unless paused first. | pageUrl?, frames? |
| cocos_set_time_scale | Speed up or slow down the whole game by scaling each frame's delta time; 1 restores it, no scale reads it. Registered only with --allow-runtime-mutation. | pageUrl?; scale? (0, 100] |
| cocos_show_stats | Show or hide the engine's FPS, draw-call, and triangle overlay through the public profiler API. | pageUrl?; visible boolean |
| cocos_emulate_device | Emulate a mobile device like the Chrome device toolbar: viewport, DPR, touch (mouse input arrives as touch), user agent and navigator.platform, and orientation; optionally slow the CPU or network. Settings merge across calls. | pageUrl?; preset? (iphone-se, iphone-14, iphone-14-pro-max, pixel-7, galaxy-s20, ipad-mini) or width+height 200..4000 with deviceScaleFactor? 1..4 and mobile?; orientation?; cpuSlowdown? 1..20; network? (online, offline, slow-3g, fast-3g, fast-4g); reload?; or reset: true |
| cocos_scene_tree | Return a bounded scene tree with node and component summaries. | pageUrl?; maxDepth? integer 0..20, default 6; maxNodes? integer 1..5000, default 500 |
| cocos_find_node | Find nodes with exact or combined bounded filters. | pageUrl?; at least one of uuid, name, path, nameContains, componentType, active, pathPrefix; limit? integer 1..100, default 20 |
| cocos_get_components | Return bounded component summaries for a node. | pageUrl?; uuid |
| cocos_get_node | Return one node's path, parent, bounded direct children, and components. | pageUrl?; uuid |
| cocos_snapshot_subtree | Return a bounded stateless hierarchy snapshot; clients compare snapshots. | pageUrl?; uuid; maxDepth?; maxNodes? |
| cocos_get_node_bounds | Return bounded canvas/viewport bounds, anchor, world position, and visibility for one UI node. | pageUrl?; uuid |
| cocos_capture_node | Return an in-memory viewport-clipped image for one visible UI node: PNG, falling back to JPEG when PNG exceeds the response limit; no file is written. | pageUrl?; uuid |
| cocos_get_properties | Serialize public properties for a node or one component selected by type or UUID. | pageUrl?; uuid; componentType? or componentUuid?; maxDepth? integer 0..6, default 3; 0 returns top-level primitives |
| cocos_wait_for_property | Poll one top-level property until it strictly equals a primitive value or the timeout passes. | pageUrl?; uuid; componentType? or componentUuid?; key; equals; timeoutMs? 100..30000, default 5000; intervalMs? 50..5000, default 200 |
| cocos_explain_click | Explain whether a tap reaches a node, or what a tap at a viewport point would hit, by replaying the engine's touch dispatch order without dispatching anything: the claiming node, the hit stack, and reasons. | pageUrl?; uuid or path, and/or x+y viewport CSS pixels |
| cocos_listener_report | Report timers, update callbacks, tweens, and director/game/view listeners that outlive their owner: on destroyed or detached nodes and components, and unowned callbacks (arrow functions, bind) grouped by event and name. | pageUrl?; limit? 1..500, default 100 |
| cocos_dynamic_atlas | Report the dynamic atlas: config, each page with its packed textures (position, size, a node that draws each), fill ratio and GPU bytes, and why visible sprites stayed out. | pageUrl?; limit? 1..500, default 100 |
| cocos_asset_report | List assets in the asset cache with type, refCount, bundle, texture GPU bytes, and a status: used (a live renderer references it), dependency (reached from one, the scene, or a persist-root node), builtin, or unused. Unused assets sort first. | pageUrl?; type? asset class such as Texture2D; unusedOnly?; limit? 1..500, default 100 |
| cocos_call_method | Call one public method of a node or component and return its bounded result or thrown error. Runs game code. Registered only with --allow-method-call. | pageUrl?; node uuid; componentUuid? (omit to call on the node); method identifier; args? up to 20 JSON values, where {"$node": uuid}, {"$component": uuid}, and {"$asset": uuid} pass live objects; awaitMs? 0..30000 waits for a returned promise; maxDepth? 0..6, default 2 |
| cocos_console_messages | Recent console messages and uncaught page errors since the server attached, newest last, with secrets masked. Registered only with --allow-browser-data. | pageUrl?; types? (log, debug, info, error, warning, assert, trace, pageerror); textContains?; limit? 1..200, default 50 |
| cocos_network_requests | Recent requests since the server attached: id, method, masked URL, resource type, status, failure, duration. | pageUrl?; urlContains?; resourceType?; failedOnly? (network failures and HTTP 4xx/5xx); limit? 1..200, default 50 |
| cocos_network_request | One request by id: masked headers, status, timing, and with includeBody the request and text response bodies, masked by key, up to 20 KB. | pageUrl?; id; includeBody? |
| cocos_storage | localStorage or sessionStorage entries with secret-like keys and JSON fields masked, or cookie names and attributes without values. | pageUrl?; area (local, session, cookies); keyContains?; limit? 1..500, default 100 |
| cocos_get_selection | Return the node the user last Alt+clicked on the game canvas: node summary and path, viewport box, and the hit stack under the point (topmost first). The first call installs the picker. | pageUrl?; disable? removes the picker, overlay, and selection |
| cocos_highlight_node | Draw a temporary pointer-transparent overlay around a UI node. | pageUrl?; uuid; durationMs? integer 100..10000, default 2000 |
cocos_runtime_diagnostics does not enable profiler/statistics systems. Render metrics are read from the values Root and the GFX device already update every frame (the same sources as root.fps and device.numDrawCalls), whether or not the profiler is shown; draw calls include the profiler overlay when it is visible. Generic invalid-reference checks still return UNSUPPORTED_PUBLIC_API. cocos_get_selection lets the user point at a node instead of describing it: it installs a capture-phase listener that consumes only Alt+clicks on the game canvas, so the game never sees them, and draws a labelled pointer-transparent overlay. It picks the topmost active node whose box contains the point and that draws or takes input (a 2D renderer such as Sprite or Label, or a Button, Toggle, EditBox, or Slider). Inactive nodes, layout-only containers, and nodes at zero opacity (including through a parent) are skipped, so invisible full-screen blockers do not swallow picks. The selection is reported stale once that node leaves the scene, and the picker disappears on reload. cocos_highlight_node temporarily mutates the page DOM only. It does not mutate the Cocos node/component graph or game state. cocos_capture_node clips only to the visible browser viewport, never falls back to full-page capture, bounds captures by the visible viewport and encoded response size rather than a fixed pixel cap, returns PNG (or JPEG fallback) base64 in-memory, downscales down to 0.25× (reported as scale) when JPEG quality steps are not enough, and rejects responses still oversized after that. Mutation results provide before values for manual inverse calls, but restoration cannot undo lifecycle callbacks or other runtime side effects. cocos_step_frame uses the public cc.game.step (fixed game.frameTime delta); because director.tick skips logic while the director is paused, it resumes the director only for the synchronous step call and pauses it again. cocos_show_stats is an explicit configuration change; while the overlay is visible, draw-call metrics include it. cocos_emulate_device holds a CDP session per page: emulation ends on reset, when the server disconnects, or when the server process exits, and reset also clears viewport overrides set by other CDP clients on that page. Cocos reads the user agent and touch support at startup, so pass reload: true for the game to see a new device class. The tool returns after the canvas has resized to the new viewport, so a following click or capture sees the new layout. Network throttling only delays traffic; it never reads requests or responses.
cocos_type_text exists because EditBox.string is an accessor that cocos_set_property refuses, and assigning it would skip the text-changed and editing-did-ended events that login forms listen for. It taps the EditBox, waits for the DOM input the engine opens, selects its content, and inserts the text through the browser keyboard. submit presses Enter: single-line boxes fire editing-return and close; multi-line boxes (the default InputMode.ANY) insert a newline instead. Password boxes (InputFlag.PASSWORD) report redacted, and cocos_get_properties omits their text. cocos_drag_node and cocos_click_node send mouse events normally and CDP touch events under mobile emulation, where Chrome would otherwise convert and never acknowledge the mouse input; Cocos only listens for touch once it detected a touch device at startup, so emulate with reload: true first.
cocos_analyze_batches explains draw calls. The 2D batcher merges consecutive renderers that share texture, material, stencil state, and layer into one draw call; anything else in between splits them. Batches are rebuilt every frame and keep no node reference, so the tool wraps the batcher's commitComp, commitModel, commitMiddleware, and commitIA for one rendered frame (from EVENT_BEFORE_DRAW to EVENT_AFTER_DRAW), then restores them. Each batch reports the node and component that started it and a reason: TEXTURE, MATERIAL, STENCIL, LAYER, MASK (a Mask enters a stencil level), MODEL (Graphics and UIMeshRenderer draw on their own), MIDDLEWARE (Spine or DragonBones that cannot merge), CUSTOM_IA, BUFFER (the vertex buffer filled up or the render data changed), FIRST, STATE_RESET, or AFTER_MODEL. With tintMs, a pointer-transparent DOM overlay outlines every node of each batch in that batch's color, labels the first node with its index and reason, and removes itself after tintMs; each batch reports its color and the result reports how many nodes were tinted (up to 1,000; nodes without a UI transform or off screen are skipped). The game must be running; a paused director renders no frames. Reasons are read from private Batcher2D fields verified on Creator 3.7.4 through 3.8.8, and drawCalls from the GFX device also includes non-2D passes and the stats overlay.
Node targets
Every tool that acts on one node takes either uuid or path, never both. A path is the absolute scene path that cocos_find_node and cocos_scene_tree report, such as /LoginScene/Canvas/panLogin/btnLogin. Paths survive scene reloads, while node UUIDs created at runtime change on each load, so agents can act in one call instead of looking the UUID up first. Sibling nodes may share a name, so a path that matches more than one node fails with AMBIGUOUS_NODE and lists the candidate UUIDs; no tool acts on an ambiguous path. cocos_call_method arguments accept {"$path": path} the same way.
Page selection
Eligible game pages must use HTTP or HTTPS on a loopback host.
- With one eligible page,
pageUrlmay be omitted. - With multiple eligible pages,
pageUrlis required. pageUrlmust exactly match the full URL reported by the browser, including path, query, and fragment.- Tabs with identical URLs cannot be told apart; close duplicates before inspecting.
Every MCP client pointed at the same CDP endpoint sees every loopback tab in that browser. When several projects run at once, give each project its own Chromium, port, and profile, then configure the server at project scope:
chrome --remote-debugging-address=127.0.0.1 --remote-debugging-port=9223 --user-data-dir="$HOME/.cocos-mcp/project-a"
claude mcp add cocos-web-inspector npx -- -y cocos-web-inspector-mcp@latest --cdp-endpoint http://127.0.0.1:9223Tool validation, connection, selection, and inspection failures are returned as MCP tool errors. Operational errors include JSON structuredContent and text content with stable code and message fields. Current codes include CDP_UNAVAILABLE, NO_LOCAL_PAGE, MULTIPLE_PAGES, PAGE_NOT_FOUND, COCOS_NOT_FOUND, SCENE_NOT_READY, NODE_NOT_FOUND, and COMPONENT_NOT_FOUND.
Output bounds
Responses are intentionally bounded:
- Total encoded JSON response: 200,000 UTF-8 bytes
- Scene traversal: caller-selected depth and node limits within the schema ranges above
- Property serialization: maximum depth
6, maximum1,000properties - Serialized strings: maximum
2,000characters - Node names: maximum
500characters - Page discovery: maximum
50eligible pages - Runtime node count: maximum
5,000traversed nodes
Truncation is reported instead of silently returning an unbounded object.
Security and threat model
CDP endpoints may use HTTP, HTTPS, WS, or WSS only on these loopback hosts:
localhost- Any
*.localhosthostname - IPv4
127.0.0.0/8 - IPv6
::1
CDP endpoint credentials, query strings, and fragments are rejected. Page targets must use HTTP or HTTPS on loopback.
The server never exposes arbitrary JavaScript evaluation or cookie values. cocos_call_method (only with --allow-method-call) runs existing public methods of game objects; it cannot run new code, refuses _-prefixed, secret-like, constructor, and destroy members, and returns results through the property serializer, so getters are not invoked and secret-like keys are dropped. A called method still runs with the game's full privileges.
Console messages, network requests, and storage are available only with --allow-browser-data, as cocos_console_messages, cocos_network_requests, cocos_network_request, and cocos_storage. Without the flag those tools are not registered. With it, redaction is best effort and runs before data leaves the server:
- Authorization, cookie, set-cookie, API-key, and CSRF headers are replaced with
[redacted]. - URL credentials are dropped and secret-like query parameters masked.
- JSON and form bodies, and JSON storage values, are masked by key name with the same secret-like key list as property serialization.
- Free text (console messages, plain-text bodies) is masked for JWTs,
Bearer/Basictokens, andkey=valueor"key": valuepairs with secret-like keys. - Cookie values are never returned; names and attributes are.
- Bodies are text only and capped at 20 KB; binary responses report their content type.
Secrets in free-form text that match none of these patterns can still be returned. Use the flag only against a disposable profile and test accounts, and treat everything these tools return as untrusted page output.
Inspection still executes fixed bridge code inside the attached page. Treat all page content as untrusted data, including text that resembles instructions or prompt injection.
Property serialization skips accessors, private-prefixed keys, functions, symbols, cycles, and secret-like key names such as tokens, cookies, passwords, credentials, authorization data, and storage.
An allowlist of display fields is read directly from their backing data fields, still without invoking getters: Label.string, RichText.string, Button.interactable, Toggle.isChecked, and Sprite.spriteFrame (name and UUID only).
These controls reduce accidental disclosure; they do not make CDP a complete security boundary. Always:
- Use a disposable browser profile.
- Bind remote debugging to loopback only.
- Never expose the debugging port to a LAN or the Internet.
- Avoid sensitive accounts and data in the debugging profile.
- Use an OS sandbox or VM when stronger isolation is required.
Development
npm run install:chromium
npm run checknpm run check type-checks, builds, runs the self-contained Node.js tests, exercises vendored Cocos Creator 3.7.4, 3.8.3, and 3.8.8 web fixtures through live Chromium and CDP, packs the npm tarball, installs it into a temporary project, and smoke-tests the installed binary. Use it before pushing. npm test remains available for build plus self-tests only; npm run test:integration runs the live browser test separately.
The test suite covers URL policy, CDP connection reuse and recovery, scene traversal, property redaction and cycle handling, output bounds, Cocos version rejection, strict tool schemas, exact tool annotations, real page selection, inspector and debugger tools, visual bounds/capture, snapshots, diagnostics, and browser reconnection. CI installs the matching Playwright Chromium revision, runs the full check on Ubuntu and Windows with Node.js 20, 22, and 24, and rejects high-severity production dependency advisories.
Tap and callback debugging
cocos_explain_click answers "why does tapping this do nothing". It rebuilds the order the engine's pointer dispatcher uses (higher camera priority first, then nodes drawn later before what they cover), runs the engine's own UITransform.hitTest (which also applies Masks) on every node listening for touch, and reports the first that would claim the tap. Reasons: INACTIVE, ZERO_SIZE, OUTSIDE_VIEWPORT, BUTTON_NOT_INTERACTABLE, BUTTON_DISABLED, NO_TOUCH_LISTENER, BLOCKED_BY_BLOCK_INPUT_EVENTS, COVERED_BY_OTHER_NODE, and MASKED_OR_OUTSIDE_HIT_AREA. A tap claimed by a descendant still counts, because touch events bubble up to the target. Nothing is dispatched.
cocos_listener_report complements cocos_asset_report. The engine purges callbacks bound to a destroyed Cocos object on its own (listeners on the next emit, tweens on the next frame, component timers on destroy), so those rarely leak. What it can never purge is a callback whose target is not a Cocos object: an arrow function or bind(this) registered on director, game, view, or systemEvent each time a popup opens keeps the popup's closure alive and runs again. The report groups such unowned callbacks by kind, event, and function name with a count; compare two calls around opening and closing a popup, and a growing count is the leak. Callbacks on destroyed objects are listed as destroyed, and those on nodes outside the scene as detached, which pooled nodes legitimately are.
cocos_set_time_scale multiplies the delta time director.tick passes to components, the scheduler, tweens, animation, physics, and rendering, so the whole game runs faster or slower; frame stepping with cocos_step_frame still uses the fixed frame time. Reload or scale: 1 restores normal speed.
Dynamic atlas and asset leaks
cocos_dynamic_atlas answers why sprites do or do not batch through the dynamic atlas. Each page lists its packed textures with their position, size, and one node that draws each, its fill (packed texture area over page area) and shelfFill (the height the shelf packer has used), and its GPU bytes. Runtime textures, such as Label text rendered in bitmap cache mode, have no asset uuid and report runtime: true. For every visible sprite outside the atlas it gives the reason, checked in the engine's own order: DISABLED, NOT_TEXTURE2D, COMPRESSED, TOO_LARGE (over maxFrameSize), NOT_PACKABLE, FILTER (not linear, or mipmapped), ATLAS_FULL, or NOT_YET_RENDERED. Pages are never compacted: removed textures leave holes until the atlas resets on scene change, so a high shelfFill with a low fill means wasted space.
cocos_asset_report is for leak hunting. One call is a snapshot, so compare two: take one, open and close the popup or scene under test, take another, and look for assets whose status is unused or whose refCount grew. An unused asset is still cached although no active renderer, scene dependency, or persist-root node reaches it; it usually means a missing decRef or releaseAsset. Asset names come from _name and are empty in release builds, so identify assets by uuid, type, and bundle. gpuMemory comes from the GFX device and covers every texture and buffer, including atlases and render targets the asset cache does not list. Both tools are read-only: they read backing fields such as _ref, _atlases, and Cache._map, call no getters except the side-effect-free isCompressed, and verified on Creator 3.7.4 through 3.8.8.
Troubleshooting
CDP_UNAVAILABLE: start Chromium with loopback remote debugging; rerunnpm run install:chromiumfor development tests. A404usually means Chrome's built-in remote debugging toggle (chrome://inspect/#remote-debugging) holds the port; turn it off or use another port, and check listeners withlsof -nP -iTCP:9222 -sTCP:LISTEN. When127.0.0.1returns 404 or refuses the connection, the server retries[::1]on the same port.MULTIPLE_PAGES: callcocos_list_pages, then pass the exact reportedpageUrl. If tabs share one URL, close duplicates or use a separate Chromium per project (see Page selection).COCOS_NOT_FOUNDorSCENE_NOT_READY: wait for the web build to finish loading; usecocos_runtime_infoafter the active scene exists.- Runtime mutation tools missing: restart the server with
--allow-runtime-mutation; a tool call cannot enable this mode. - Bounds/capture unavailable: select a visible UI node with
UITransform.INACTIVEmeans the node or an ancestor is inactive. Capture is viewport-only, bounded, and never falls back to a full-page screenshot;RESPONSE_LIMITmeans even a 0.25× JPEG exceeded the response budget, so capture a smaller node. cocos_snapshot_subtreereturnsRESPONSE_LIMITwith a partial tree: snapshot a deeper node, or lowermaxDepth.cocos_step_framereturnsINVALID_MUTATION: callcocos_pausefirst.cocos_explain_clicksaysNO_TOUCH_LISTENERfor a node that reacts in the game: the game may listen on an ancestor or through a globalinput.on; check the ancestors inhitStackor explain the tap byx/y.cocos_call_methodreturnspending: true: the method returned a promise; passawaitMsto wait for it.cocos_console_messagesorcocos_network_requestsmisses startup output: they list only what happened after the server attached; reload the page and call again.cocos_network_requestreturnsREQUEST_NOT_FOUND: Playwright drops old requests and bodies to bound memory; list requests again and inspect recent ones promptly.cocos_asset_reportlists an asset asunusedright after a scene change: the previous scene's assets stay cached untilautoReleaseAssetsor the game's own release runs; take the comparison snapshot a moment later.cocos_dynamic_atlasreportsNOT_YET_RENDERED: the sprite packs on its first render; check again after a frame.cocos_analyze_batchesreturnsINVALID_MUTATIONwith "no frame rendered": callcocos_resume; batches are captured from a live frame.cocos_type_textreturnsNOT_FOCUSED: the EditBox did not open its input, usually because another node covers it or it is disabled.cocos_drag_nodemoves nothing under mobile emulation: callcocos_emulate_devicewithreload: trueso Cocos starts listening for touch.doctorreports HTTP 404 on the port: Chrome's built-in remote debugging holds it; seeCDP_UNAVAILABLEabove.- Game still behaves like desktop after
cocos_emulate_device: call it again withreload: true; Cocos detects mobile and touch at startup. - Render metrics
fpsreads 0: the engine publishes FPS once per elapsed second, so read again after the scene has run for a second.
Release and compatibility
Architectural references
This is a clean implementation, not a fork. No source module was copied from these projects:
- Chrome DevTools MCP, Apache-2.0, inspected at
b2f522c8ba0fd2e00a679159b4aa5243de5f1b78. - Playwright MCP, Apache-2.0, inspected at
f183dad4a52965583e3cc1d59b88cdc279e2e57d. - CC Inspector, MIT, inspected at
ef5ef0ae033ee6a6ae496cf566fe5d91cf05aafd.
They informed MCP tool design, CDP connection constraints, Cocos runtime discovery, bounded responses, and highlight behavior. Their license files remain in separate read-only reference clones and are not redistributed here because this repository contains independently written code.
