cocos-web-inspector-mcp 0.1.7 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -5
- package/dist/src/bridge.d.ts +3 -0
- package/dist/src/bridge.js +39 -1
- package/dist/src/server.js +8 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -98,19 +98,20 @@ The endpoint may instead be provided through the MCP process environment:
|
|
|
98
98
|
|
|
99
99
|
## Tools
|
|
100
100
|
|
|
101
|
-
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.
|
|
101
|
+
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` is additionally destructive and open-world because game click handlers can reach real servers or make irreversible changes, and it and `cocos_step_frame` are non-idempotent.
|
|
102
102
|
|
|
103
103
|
| Tool | Purpose | Inputs |
|
|
104
104
|
| --- | --- | --- |
|
|
105
105
|
| `cocos_list_pages` | List bounded summaries of eligible localhost pages, including sanitized URL, title, Cocos detection, version, and scene name. | None |
|
|
106
106
|
| `cocos_runtime_info` | Return bounded engine, scene, canvas, view, director, and node-count information. | `pageUrl?` |
|
|
107
|
-
| `cocos_runtime_diagnostics` | Return passive bounded hierarchy counts, depth,
|
|
107
|
+
| `cocos_runtime_diagnostics` | Return passive bounded hierarchy counts, depth, duplicate names, and render metrics (FPS, frame time, draw calls, triangles, instances). | `pageUrl?` |
|
|
108
108
|
| `cocos_set_node_active` | Set one node's active state. Registered only with `--allow-runtime-mutation`; returns before/after state. | `pageUrl?`; `uuid`; `active` boolean |
|
|
109
109
|
| `cocos_set_transform` | Update supplied position, rotation, and/or scale fields for one node. | `pageUrl?`; `uuid`; `position?`, `rotation?`, `scale?` finite vectors |
|
|
110
110
|
| `cocos_set_property` | Update one bounded public component data property. | `pageUrl?`; node `uuid`; `componentUuid`; `key`; primitive/vector/size/color `value` matching current shape |
|
|
111
111
|
| `cocos_click_node` | Dispatch a real mouse click at the visible center of one UI node so Button/touch handlers run. Registered only with `--allow-runtime-mutation`. | `pageUrl?`; `uuid` |
|
|
112
112
|
| `cocos_pause` | Pause the Cocos director when its public API supports it. | `pageUrl?` |
|
|
113
113
|
| `cocos_resume` | Resume the Cocos director when its public API supports it. | `pageUrl?` |
|
|
114
|
+
| `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?` |
|
|
114
115
|
| `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` |
|
|
115
116
|
| `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` |
|
|
116
117
|
| `cocos_get_components` | Return bounded component summaries for a node. | `pageUrl?`; `uuid` |
|
|
@@ -122,7 +123,7 @@ All tool input objects are strict. Unknown fields are rejected. Inspection tools
|
|
|
122
123
|
| `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` |
|
|
123
124
|
| `cocos_highlight_node` | Draw a temporary pointer-transparent overlay around a UI node. | `pageUrl?`; `uuid`; `durationMs?` integer `100..10000`, default `2000` |
|
|
124
125
|
|
|
125
|
-
`cocos_runtime_diagnostics` does not enable profiler/statistics systems.
|
|
126
|
+
`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_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.
|
|
126
127
|
|
|
127
128
|
### Page selection
|
|
128
129
|
|
|
@@ -198,7 +199,7 @@ npm run check
|
|
|
198
199
|
|
|
199
200
|
`npm run check` type-checks, builds, runs the self-contained Node.js tests, exercises a vendored Cocos Creator 3.8.8 web fixture 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.
|
|
200
201
|
|
|
201
|
-
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 and
|
|
202
|
+
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.
|
|
202
203
|
|
|
203
204
|
## Troubleshooting
|
|
204
205
|
|
|
@@ -206,7 +207,10 @@ The test suite covers URL policy, CDP connection reuse and recovery, scene trave
|
|
|
206
207
|
- `MULTIPLE_PAGES`: call `cocos_list_pages`, then pass the exact reported `pageUrl`. If tabs share one URL, close duplicates or use a separate Chromium per project (see Page selection).
|
|
207
208
|
- `COCOS_NOT_FOUND` or `SCENE_NOT_READY`: wait for the web build to finish loading; use `cocos_runtime_info` after the active scene exists.
|
|
208
209
|
- Runtime mutation tools missing: restart the server with `--allow-runtime-mutation`; a tool call cannot enable this mode.
|
|
209
|
-
- Bounds/capture unavailable: select a visible UI node with `UITransform`. Capture is viewport-only, bounded, and never falls back to a full-page screenshot.
|
|
210
|
+
- Bounds/capture unavailable: select a visible UI node with `UITransform`. `INACTIVE` means the node or an ancestor is inactive. Capture is viewport-only, bounded, and never falls back to a full-page screenshot; `RESPONSE_LIMIT` means even a 0.25× JPEG exceeded the response budget, so capture a smaller node.
|
|
211
|
+
- `cocos_snapshot_subtree` returns `RESPONSE_LIMIT` with a partial tree: snapshot a deeper node, or lower `maxDepth`.
|
|
212
|
+
- `cocos_step_frame` returns `INVALID_MUTATION`: call `cocos_pause` first.
|
|
213
|
+
- Render metrics `fps` reads 0: the engine publishes FPS once per elapsed second, so read again after the scene has run for a second.
|
|
210
214
|
|
|
211
215
|
## Release and compatibility
|
|
212
216
|
|
package/dist/src/bridge.d.ts
CHANGED
package/dist/src/bridge.js
CHANGED
|
@@ -246,13 +246,28 @@ export function inspectCocos(request) {
|
|
|
246
246
|
}
|
|
247
247
|
});
|
|
248
248
|
const duplicateNames = [...names.entries()].filter(([, uuids]) => uuids.length > 1).slice(0, 100).map(([name, uuids]) => ({ name, uuids, count: uuids.length }));
|
|
249
|
+
// Root and the GFX device update these every frame whether or not the profiler is shown; read backing fields, never getters.
|
|
250
|
+
const renderRoot = cc.director && typeof cc.director === 'object' ? dataProperty(cc.director, '_root') : undefined;
|
|
251
|
+
const device = renderRoot && typeof renderRoot === 'object' ? dataProperty(renderRoot, '_device') : undefined;
|
|
252
|
+
const metric = (owner, key) => {
|
|
253
|
+
const value = owner && typeof owner === 'object' ? dataProperty(owner, key) : undefined;
|
|
254
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
|
|
255
|
+
};
|
|
256
|
+
const render = { fps: metric(renderRoot, '_fps'), frameTimeMs: metric(renderRoot, '_frameTime'), drawCalls: metric(device, '_numDrawCalls'), triangles: metric(device, '_numTris'), instances: metric(device, '_numInstances') };
|
|
257
|
+
if (render.frameTimeMs !== undefined)
|
|
258
|
+
render.frameTimeMs *= 1_000;
|
|
259
|
+
const unavailableMetrics = { invalidComponentReferences: 'UNSUPPORTED_PUBLIC_API' };
|
|
260
|
+
for (const [key, value] of Object.entries(render))
|
|
261
|
+
if (value === undefined)
|
|
262
|
+
unavailableMetrics[key] = 'UNSUPPORTED_PUBLIC_API';
|
|
249
263
|
return {
|
|
250
264
|
version,
|
|
251
265
|
nodeCount,
|
|
252
266
|
componentCount,
|
|
253
267
|
maxHierarchyDepth: maxDepth,
|
|
254
268
|
duplicateNames,
|
|
255
|
-
|
|
269
|
+
render: Object.fromEntries(Object.entries(render).filter(([, value]) => value !== undefined)),
|
|
270
|
+
unavailableMetrics,
|
|
256
271
|
truncated: traversal.truncated || duplicateNames.length >= 100,
|
|
257
272
|
truncationReasons: traversal.truncated || duplicateNames.length >= 100 ? ['NODE_LIMIT'] : [],
|
|
258
273
|
};
|
|
@@ -559,6 +574,29 @@ export function inspectCocos(request) {
|
|
|
559
574
|
const after = directorState();
|
|
560
575
|
return { changed: before.paused !== after.paused, target: {}, before, after, runtimeOnly: true };
|
|
561
576
|
}
|
|
577
|
+
if (request.action === 'stepFrame') {
|
|
578
|
+
const game = cc.game;
|
|
579
|
+
if (typeof game?.step !== 'function' || typeof game.isPaused !== 'function' || typeof cc.director?.getTotalFrames !== 'function')
|
|
580
|
+
invalidMutation('game.step is unavailable');
|
|
581
|
+
const directorPaused = directorState().paused;
|
|
582
|
+
if (!directorPaused && !game.isPaused())
|
|
583
|
+
invalidMutation('pause the game before stepping');
|
|
584
|
+
const before = cc.director.getTotalFrames();
|
|
585
|
+
// game.step ticks the director, which skips logic while director-paused; unpause only inside this synchronous call.
|
|
586
|
+
for (let frame = 0; frame < (request.frames ?? 1); frame++) {
|
|
587
|
+
if (directorPaused)
|
|
588
|
+
cc.director.resume();
|
|
589
|
+
try {
|
|
590
|
+
game.step();
|
|
591
|
+
}
|
|
592
|
+
finally {
|
|
593
|
+
if (directorPaused)
|
|
594
|
+
cc.director.pause();
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
const after = cc.director.getTotalFrames();
|
|
598
|
+
return { changed: after !== before, target: {}, before: { totalFrames: before }, after: { totalFrames: after }, runtimeOnly: true };
|
|
599
|
+
}
|
|
562
600
|
if (request.action === 'setNodeActive') {
|
|
563
601
|
const node = findByUuid(request.uuid);
|
|
564
602
|
const before = node.active !== false;
|
package/dist/src/server.js
CHANGED
|
@@ -171,9 +171,10 @@ export function createServer(browser, options = {}) {
|
|
|
171
171
|
annotations: runtimeMutation,
|
|
172
172
|
}, input => execute({ action: 'setProperty', uuid: input.uuid, componentUuid: input.componentUuid, key: input.key, value: input.value }, input.pageUrl));
|
|
173
173
|
server.registerTool('cocos_click_node', {
|
|
174
|
-
description: 'Dispatch a real mouse click at the visible center of one Cocos UI node, so Button and touch handlers run.',
|
|
174
|
+
description: 'Dispatch a real mouse click at the visible center of one Cocos UI node, so Button and touch handlers run. Handlers may call game servers or make irreversible changes.',
|
|
175
175
|
inputSchema: z.object({ pageUrl, uuid: z.string().min(1) }).strict(),
|
|
176
|
-
|
|
176
|
+
// Game click handlers run arbitrary code: a login button reaches real servers, a buy button spends currency.
|
|
177
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
|
|
177
178
|
}, async (input) => {
|
|
178
179
|
try {
|
|
179
180
|
return response(await clickNode(await browser.page(input.pageUrl), input.uuid));
|
|
@@ -189,6 +190,11 @@ export function createServer(browser, options = {}) {
|
|
|
189
190
|
annotations: runtimeMutation,
|
|
190
191
|
}, input => execute({ action }, input.pageUrl));
|
|
191
192
|
}
|
|
193
|
+
server.registerTool('cocos_step_frame', {
|
|
194
|
+
description: 'Advance a paused Cocos game by fixed-delta frames through cc.game.step; requires cocos_pause first.',
|
|
195
|
+
inputSchema: z.object({ pageUrl, frames: z.number().int().min(1).max(60).optional() }).strict(),
|
|
196
|
+
annotations: { ...runtimeMutation, idempotentHint: false },
|
|
197
|
+
}, input => execute({ action: 'stepFrame', frames: input.frames }, input.pageUrl));
|
|
192
198
|
}
|
|
193
199
|
server.registerTool('cocos_highlight_node', {
|
|
194
200
|
description: 'Temporarily draw a pointer-transparent DOM overlay around one Cocos UI node.',
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cocos-web-inspector-mcp",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.9",
|
|
4
|
+
"description": "MCP server to inspect and debug Cocos Creator 3.x web builds in local Chromium over CDP",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|