unity-explorer-mcp

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Unity Explorer MCP Scene Iteration

Unity Explorer MCP 场景迭代

Drive a running Decentraland Explorer build through its MCP automation server to build and test SDK7 scenes autonomously: edit the scene, watch it hot-reload, move the camera and player, take screenshots, and verify against what the code should produce.
The connected
mcp__explorer__*
tools are self-describing — each carries its name, arguments, and output shape. Treat that as the authoritative tool catalog; the names used below (
get_scene_state
,
get_scene_logs
,
screenshot
,
teleport
,
move_to
,
walk
,
look_at
,
set_camera_pose
,
set_camera_mode
,
list_scene_entities
,
get_entity_details
,
get_player_state
,
click_entity
,
send_chat
,
trigger_emote
,
reload_scene
,
get_scene_content_stats
,
get_scene_content_breakdown
,
get_performance_stats
) are the common ones.
Deeper reference, loaded only when the task reaches it:
  • reference/camera-and-movement.md
    — before framing screenshots, free-camera sweeps, or navigating precise lines
  • reference/assets.md
    — before placing, downloading, converting, or exporting any 3D model
  • reference/visuals.md
    — before tuning emissives/bloom, UI overlays, skybox time, or judging thin geometry
  • reference/performance-debugging.md
    — before answering whether a scene is within its content budget, why it runs slow, or what to optimize (
    get_scene_content_stats
    ,
    get_scene_content_breakdown
    ,
    get_performance_stats
    )
通过其 MCP 自动化服务器驱动正在运行的 Decentraland Explorer 构建,以自主构建和测试 SDK7 场景:编辑场景、观察热重载、移动摄像机和玩家、截取屏幕,并对照代码应产生的结果进行验证。
已连接的
mcp__explorer__*
工具具备自描述能力——每个工具都携带自身的名称、参数和输出结构。将其视为权威工具目录;下文使用的名称(
get_scene_state
、
get_scene_logs
、
screenshot
、
teleport
、
move_to
、
walk
、
look_at
、
set_camera_pose
、
set_camera_mode
、
list_scene_entities
、
get_entity_details
、
get_player_state
、
click_entity
、
send_chat
、
trigger_emote
、
reload_scene
、
get_scene_content_stats
、
get_scene_content_breakdown
、
get_performance_stats
)是常见工具。
仅在任务进入相应环节时加载更深入的参考资料:
  • reference/camera-and-movement.md
    ——在为截图取景、执行自由摄像机扫掠或沿精确路线导航之前
  • reference/assets.md
    ——在放置、下载、转换或导出任何 3D 模型之前
  • reference/visuals.md
    ——在调整自发光/bloom、UI 叠加层、天空盒时间或判断细薄几何体之前
  • reference/performance-debugging.md
    ——在回答场景是否处于内容预算内、运行缓慢的原因或应优化什么之前(
    get_scene_content_stats
    、
    get_scene_content_breakdown
    、
    get_performance_stats
    )

Gates

门控

Certain points in this skill are gates: you ask, call no tool after asking, and let the user answer. A gate opens only on their reply — never on your own judgment, never on their silence, never because a workaround is available to you. Running as a subagent, you cannot open a gate at all — there is no user to ask: stop and report the pending decision to your caller with your recommendation, rather than passing the gate on your own authority. Doing so violates the gate even when the workaround happens to work. The gates, in order: the skills-install gate and restart gate (below), the intent gate (pre-flight), the launch/kill gate (Setup step 1), and the bind gate (Setup step 2).
本 skill 中的某些时点是门控:提出问题后不得调用任何工具,并等待用户回答。只有用户回复才能开启门控——绝不能凭自己的判断、用户的沉默,或因为存在变通方案而开启。作为 subagent 运行时,你根本无法开启门控——因为没有用户可问:应停止操作,并将待定决策及建议报告给调用方,而不是擅自越过门控。即使变通方案可行,这样做仍违反门控规则。门控顺序如下:**skills-install gate(Skill 安装门控)**和 restart gate(重启门控)(见下文)、intent gate(意图门控)(预检)、launch/kill gate(启动/终止门控)(Setup 第 1 步)和 bind gate(绑定门控)(Setup 第 2 步)。

Load the SDK skills (before anything, either way)

加载 SDK skills(无论是否使用,均须先执行)

This skill only covers driving the Explorer; the SDK7 API knowledge (composite-first rule, component reference) lives in the other topic skills of the same
decentraland/sdk-skills
package this skill ships from (entry point
sdk-scenes
, plus
create-scene
,
add-3d-models
, etc.), and parts of the API (e.g. native
TriggerArea
) are newer than training data. You need them whether or not the Explorer ends up in play, so do this before the pre-flight below.
Load them: session skills first, then the filesystem — scene-local (
.claude/skills/
in the scene folder) and global (
~/.claude/skills/
). This is done when you can name the topic skills available to you — not when you've noticed they exist. If they cannot be loaded — e.g. only
unity-explorer-mcp
itself was installed, not the whole package — skills-install gate: pull in the rest of the package's topic skills from that same source? Recommend it. On yes, ask at which level — scene-local or global — and run the matching command:
bash
npx skills add decentraland/sdk-skills --all       # scene-local (run inside the scene folder)
npx skills add decentraland/sdk-skills --all -g    # global (user-level, ~/.claude/skills)
A fresh install lands on disk but does not bind — skills load at session start, and only the user can restart. Restart gate: restart now to pick the new skills up, or continue this session without them? Recommend restarting — until it happens the install buys nothing.
Declining either gate is fine — the scene can still be implemented, just less efficiently. Until the skills actually load, the stale memory rule under the iteration loop governs every SDK7 API you write.
本 skill 仅涵盖 Explorer 的驱动;SDK7 API 知识(composite-first 规则、组件参考)位于本 skill 所属
decentraland/sdk-skills
包中的其他主题 skill(入口为
sdk-scenes
,以及
create-scene
、
add-3d-models
等),且部分 API(如原生
TriggerArea
)比训练数据更新。无论最终是否使用 Explorer,你都需要这些 skill,因此必须在下面的预检之前加载它们。
加载方式:先加载 session skills,再从文件系统加载——场景本地(场景文件夹中的
.claude/skills/
)和全局(
~/.claude/skills/
)。只有当你能说出可用的主题 skill 名称时,才算完成加载——不能仅因为发现它们存在。如果无法加载,例如只安装了
unity-explorer-mcp
本身,而没有安装整个包,则进入 skills-install gate:是否从同一来源拉取该包的其他主题 skill?建议这样做。得到肯定答复后,询问安装级别——场景本地或全局——并运行对应命令:
bash
npx skills add decentraland/sdk-skills --all       # scene-local (run inside the scene folder)
npx skills add decentraland/sdk-skills --all -g    # global (user-level, ~/.claude/skills)
新安装的内容会落盘,但不会绑定——skill 在 session 启动时加载,且只有用户可以重启。restart gate:现在重启以加载新 skill,还是不使用它们继续当前 session?建议重启——在重启发生前,这次安装不会带来任何作用。
拒绝任一门控都没有问题——仍可实现场景,只是效率较低。在 skill 实际加载之前,下面迭代循环中的 stale memory(陈旧记忆) 规则适用于你编写的每一个 SDK7 API。

Intent gate (pre-flight, do this first)

intent gate(预检,必须首先执行)

This skill fires on its own — the mere presence of an
mcp__explorer__*
tool triggers it — so it is often loaded when the user never asked for it. Before probing for a server, launching the Explorer, or editing the scene, confirm they want to drive the scene through the Unity Explorer MCP server: "Do you want to build/test this scene against a running Decentraland Explorer via the Unity Explorer MCP server? This will launch/connect to the Explorer and iterate in-world."
  • YES — continue to Setup below.
  • NO — run no setup, launch, or MCP step from this skill. Work on the scene without the Explorer (edit code, lean on the topic skills you just loaded), and let the user re-invoke this skill later if they change their mind.
本 skill 会自行触发——仅存在某个
mcp__explorer__*
工具就会触发,因此即使用户从未要求使用它,也常会被加载。在探测服务器、启动 Explorer 或编辑场景之前,确认用户是否希望通过 Unity Explorer MCP 服务器驱动场景:“Do you want to build/test this scene against a running Decentraland Explorer via the Unity Explorer MCP server? This will launch/connect to the Explorer and iterate in-world.”
  • YES——继续执行下方的 Setup。
  • NO——不得执行本 skill 中的任何 setup、launch 或 MCP 步骤。在不使用 Explorer 的情况下处理场景(编辑代码、依赖刚加载的主题 skills),并告知用户改变主意后可以再次调用本 skill。

Setup (once per session)

Setup(每个 session 一次)

  1. Probe for an already-running MCP server, then start the scene. Check through the harness first: if
    mcp__explorer__*
    tools are available in the session, call
    get_scene_state
    — an answer means the server is up. Fall back to curl only if the tools are absent:
    bash
    curl -s -m 2 http://127.0.0.1:8123/unity-explorer-mcp -X POST \
      -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \
      -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"probe","version":"1"}}}'
    Two launch paths produce the same server, and you cannot tell them apart from the probe:
    npm run start -- --mcp
    from the CLI, and the Creator Hub's scene Preview button with the MCP checkbox ticked (it passes
    --mcp
    to the same preview process). Same endpoint, same tools, same behaviour — a Creator Hub launch is never the reason a connection fails, so recognise it as a normal setup rather than an anomaly to relaunch out of.
    Server found (tool answer or
    serverInfo
    result) — launch/kill gate: use the already-running Explorer, or start the scene from scratch with the MCP flag?
    • Use it: launch nothing. If port 8000 isn't serving the target scene folder (
      lsof -nP -i :8000 -sTCP:LISTEN
      , then check the PID's cwd), kill whatever holds it and run
      npm run start -- --no-client
      . Skip step 2 if the tools are already available.
    • From scratch: same gate, follow-up question, before touching anything — kill the previously-running scene server, or keep it and run a second stack alongside?
      • Kill it: kill the port-8000 dev server, have the user close the running client (never kill an Editor process yourself), then continue below.
      • Keep it: leave it and its Explorer untouched and start a second stack on its own ports — see "Running a second stack" in
        reference/setup.md
        .
    No server found — serve the scene and launch the Explorer in one command from the scene folder (keep it running in the background; if something else already holds port 8000, apply the same kill-or-keep question and port overrides as above):
    bash
    npm install && npm run start -- --mcp
    Alternatively the user can launch from the Creator Hub: enable the "Enable MCP Server" checkbox in the scene's preview settings (only shown when the scene's
    @dcl/sdk-commands
    version supports the
    --mcp
    flag), then hit Preview. Equivalent to the command above in every respect that matters here.
    This serves the scene at
    http://127.0.0.1:8000
    , auto-launches the installed Decentraland client connected to it with the MCP server enabled (port 8123;
    --mcp-port <port>
    picks another and implies
    --mcp
    — adjust the 8123 URLs in steps 1 and 2 to match), and hot-reloads the scene whenever a source file changes. Useful extra flags:
    --port <port>
    (dev-server port; the launched client follows it automatically),
    --position x,y
    (spawn parcel),
    --skip-auth-screen
    ,
    -n
    (force a new client instance),
    --multi-instance
    (allow concurrent Explorer instances),
    --no-client
    (serve only, launch nothing). Anything after a second standalone
    --
    is forwarded verbatim into the Explorer launch as extra parameters, e.g.
    npm run start -- --mcp -- --windowed-mode --resolution 1280x720
    (npm consumes the first
    --
    ). If the command rejects
    --mcp
    as an unknown option, the scene's
    @dcl/sdk-commands
    predates the flag — update
    @dcl/sdk
    , or launch a specific build by hand ("Launching a specific Explorer build manually" in
    reference/setup.md
    ). If the CLI prints "Please download & install the Decentraland Desktop Client" the dev server is fine but no client is installed — install one, or point the launch at a specific build the same way.
    A freshly launched Explorer needs the user to log in. The client opens on the auth screen unless a previous session's login is still cached (
    --skip-auth-screen
    only skips it when a valid identity exists — a missing or expired login shows it anyway, and extra
    --multi-instance
    instances always ask). Tell the user to log in, then wait — step 3's polling only starts succeeding once they're through, and only then can you continue working on the scene through the MCP server.
  2. Register the MCP server, then confirm its tools are actually bound (default port 8123). Registration and binding are two different things — you need both.
    bash
    claude mcp add --transport http --scope user explorer http://127.0.0.1:8123/unity-explorer-mcp
    Errors with "already exists in local config" if a previous session registered it — that's fine and expected; registration is persistent config, nothing to do.
    This command can hang with no output in non-terminal harnesses (observed in the Claude Code VS Code extension: >2 min, no result, no error). Run it with a short timeout —
    timeout 15 claude mcp add ...
    — and read a hang as probably already registered, not as a failure: registration persists across sessions, so a prior session's
    claude mcp add
    still counts. Don't retry it; confirm the endpoint with the step 1 probe instead and move on to the bind gate.
    MANDATORY — bind gate: if you launched the Explorer yourself in this session, STOP here as soon as the readiness probe answers, and do NOT start the iteration loop over curl. Claude Code opens its MCP connections once, at session startup, and this server lives inside the Explorer process — so a session that started before the Explorer was up has already failed its one connection attempt, and
    mcp__explorer__*
    tools will never appear on their own. Mid-session, only an explicit
    /mcp
    reconnect re-binds them; alternatively, starting a fresh Claude session while the Explorer keeps running binds them automatically at that session's startup. Both are user actions: there is nothing you can run instead, so ask rather than trying to engineer around it.
    Open with the situation — "the Explorer is up, but this Claude session started before it, so the native MCP tools aren't bound" — then offer three paths, best first, with the fallback's costs stated before they can land in it:
    • Reconnect the
      explorer
      server
      — fastest, and keeps this conversation. How, depends on the harness: the terminal CLI opens an interactive server menu on
      /mcp
      , with a reconnect entry to pick. The VS Code extension (and any embedded chat panel, e.g. the Creator Hub's) has no such menu — a bare
      /mcp
      there only prints a status line plus usage, so the command to give the user is
      /mcp reconnect explorer
      (or
      /mcp reconnect all
      ), typed into the chat.
    • Start a fresh session / new conversation tab, leaving the Explorer running — the tools bind automatically at the new session's startup. In the VS Code extension and embedded panels this is just a new conversation tab, not restarting the app or the Explorer — say so, because users assume the expensive reading and decline. Make it the recommended path there whenever
      /mcp reconnect
      answers "MCP controls aren't available right now — the terminal is still starting up or is showing another view." — a known failure mode in that harness with no in-chat fix, and the new tab is the path verified to work.
    • The curl fallback — one shell command per player action, permission prompts on each new call shape, more tokens, and no screenshot images you can inspect.
    Use
    AskUserQuestion
    if your client has it, one option each; label the second "Restart session / new tab (Explorer stays up)" so it can't be misread as closing the Explorer. Otherwise put the same three in plain text and close with "Which do you prefer?".
    You may only move past this step in one of these three states — no fourth reading exists, and a subagent can only ever be in the first:
    • Tools already present (the Explorer was running before this session started) — the gate doesn't apply; continue to step 3.
    • The user rebound the tools — via
      /mcp
      reconnect, or by opening a fresh session/tab with the Explorer left up. Verify by calling a native
      mcp__explorer__*
      tool, then continue to step 3. (A fresh session ends this conversation; the new one finds them bound and skips this gate.)
    • The user has explicitly chosen the curl fallback in writing, this session, after being told its costs — only then read
      reference/curl-fallback.md
      and drive the endpoint over HTTP. A working curl probe is not a decline, and neither is silence.
    The probe in step 1 is the only curl call you make before this gate. Getting an answer out of it proves the Explorer is up — which is the trigger to ask, not permission to continue.
    Mention the prevention once, not every session: starting the Explorer before Claude Code — or simply leaving it running between sessions — skips this gate entirely.
    Not running in Claude Code?
    claude mcp add
    and
    /mcp
    are Claude Code commands — for Cursor, Cline, or a custom SDK harness, the connection details and config shapes are in
    reference/setup.md
    . The Claude Code VS Code extension and the Creator Hub's embedded chat are Claude Code: they take the commands above, with the harness differences noted in the bind gate.
  3. Wait for the world to load: poll
    get_scene_state
    until
    loadingScreenOn
    is false and the scene reports
    isReady: true
    .
  1. 探测已经运行的 MCP 服务器,然后启动场景。 先通过 harness 检查:如果 session 中已有
    mcp__explorer__*
    工具,调用
    get_scene_state
    ——收到响应即表示服务器已启动。仅当工具不存在时,才回退到 curl:
    bash
    curl -s -m 2 http://127.0.0.1:8123/unity-explorer-mcp -X POST \
      -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \
      -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\":{\"protocolVersion\":\"2025-06-18\",\"capabilities\":{},\"clientInfo\":{\"name\":\"probe\",\"version\":\"1\"}}}'
    两种启动路径会生成相同的服务器,而且通过探测无法区分二者:从 CLI 运行
    npm run start -- --mcp
    ,以及从 Creator Hub 的场景 Preview 按钮启动并勾选 MCP(它会将
    --mcp
    传递给同一个 preview 进程)。二者使用相同 endpoint、相同工具和相同行为,因此 Creator Hub 启动并不是连接失败的原因;应将其视为正常 setup,而不是需要重新启动的异常。
    服务器已找到(工具响应或
    serverInfo
    结果)——进入 launch/kill gate:使用已经运行的 Explorer,还是带 MCP 标志从头启动场景?
    • 使用现有实例:不启动任何内容。如果端口 8000 未提供目标场景文件夹的服务(运行
      lsof -nP -i :8000 -sTCP:LISTEN
      ,然后检查 PID 的 cwd),则终止占用该端口的进程,并运行
      npm run start -- --no-client
      。如果工具已经可用,跳过第 2 步。
    • 从头启动:这是同一门控的后续问题——在触碰任何内容之前,是终止先前运行的场景服务器,还是保留它并并行运行第二套栈?
      • 终止它:终止端口 8000 上的 dev server,让用户关闭正在运行的 client(绝不要自行终止 Editor 进程),然后继续执行下方步骤。
      • 保留它:保持该服务及其 Explorer 不变,在独立端口上启动第二套栈——参见
        reference/setup.md
        中的“Running a second stack”。
    未找到服务器——在场景文件夹中用一条命令提供场景服务并启动 Explorer(让它在后台持续运行;如果端口 8000 已被其他进程占用,则应用上述终止或保留问题以及端口覆盖设置):
    bash
    npm install && npm run start -- --mcp
    或者,用户可以从 Creator Hub 启动:在场景的预览设置中启用 “Enable MCP Server” 复选框(仅当场景的
    @dcl/sdk-commands
    版本支持
    --mcp
    标志时才会显示),然后点击 Preview。在所有关键方面,它都等同于上面的命令。
    这会在
    http://127.0.0.1:8000
    提供场景服务,自动启动已安装且连接到该服务的 Decentraland client,并启用 MCP 服务器(端口 8123;
    --mcp-port <port>
    可选择其他端口,并隐含启用
    --mcp
    ——相应调整第 1 步和第 2 步中的 8123 URL),且每当源文件发生变化时都会热重载场景。额外可用标志包括:
    --port <port>
    (dev-server 端口;启动的 client 会自动跟随)、
    --position x,y
    (生成位置对应的 parcel)、
    --skip-auth-screen
    、
    -n
    (强制启动新的 client 实例)、
    --multi-instance
    (允许并发 Explorer 实例)和
    --no-client
    (仅提供服务,不启动任何内容)。第二个独立的
    --
    之后的所有内容都会逐字转发到 Explorer 启动命令中作为额外参数,例如
    npm run start -- --mcp -- --windowed-mode --resolution 1280x720
    (npm 会消耗第一个
    --
    )。如果命令提示
    --mcp
    是未知选项,说明场景的
    @dcl/sdk-commands
    版本早于该标志——请更新
    @dcl/sdk
    ,或手动启动特定构建(参见
    reference/setup.md
    中的“Launching a specific Explorer build manually”)。如果 CLI 提示 “Please download & install the Decentraland Desktop Client”,则说明 dev server 正常,只是尚未安装 client——请安装 client,或按照相同方式让启动命令指向特定构建。
    全新启动的 Explorer 需要用户登录。 除非仍缓存着上一 session 的登录状态,client 都会打开身份验证界面(
    --skip-auth-screen
    仅在存在有效身份时才会跳过;缺失或过期的登录仍会显示,而且额外的
    --multi-instance
    实例始终会要求登录)。告知用户登录,然后等待——只有用户通过验证后,第 3 步的轮询才会成功,此后才能继续通过 MCP 服务器处理场景。
  2. 注册 MCP 服务器,然后确认其工具实际已绑定(默认端口 8123)。注册与绑定是两件不同的事——两者缺一不可。
    bash
    claude mcp add --transport http --scope user explorer http://127.0.0.1:8123/unity-explorer-mcp
    如果报错提示 “already exists in local config”,说明上一 session 已经注册过——这是正常且符合预期的情况;注册是持久化配置,无需处理。
    此命令在非终端 harness 中可能无输出并一直挂起(已观察到 Claude Code VS Code 扩展中超过 2 分钟,无结果也无错误)。使用短超时运行——
    timeout 15 claude mcp add ...
    ——并将其挂起视为“可能已经注册”,而不是失败:注册会跨 session 持久化,因此上一 session 的
    claude mcp add
    仍然有效。不要重试;改用第 1 步的探测确认 endpoint,然后进入 bind gate。
    必须执行——bind gate:如果是你在本 session 中启动了 Explorer,就绪探测一有响应便立即在此停止,切勿通过 curl 启动迭代循环。 Claude Code 只在 session 启动时建立一次 MCP 连接,而此 server 位于 Explorer 进程内部——因此,在 Explorer 启动之前就已开始的 session 已经用掉了唯一一次连接尝试,
    mcp__explorer__*
    工具永远不会自行出现。在 session 中途,只有显式执行
    /mcp
    重新连接才能重新绑定;另一种方法是,在 Explorer 保持运行时启动一个全新的 Claude session,这会在新 session 启动时自动完成绑定。这两种都是用户操作:没有你可以代为执行的命令,所以应询问用户,而不是尝试绕过。
    先说明情况——“Explorer 已启动,但这个 Claude session 在它之前就开始了,因此原生 MCP 工具尚未绑定”——然后提供三条路径,优先给出最佳选项,并在用户选择回退方案前说明其代价:
    • 重新连接
      explorer
      server
      ——速度最快,并且可以保留当前对话。具体方式取决于 harness:终端 CLI 的
      /mcp
      会打开交互式服务器菜单,其中有可供选择的重新连接项。VS Code 扩展(以及任何嵌入式聊天面板,例如 Creator Hub)没有该菜单——在这些环境中,单独输入
      /mcp
      只会显示状态行和使用说明,因此应让用户在聊天中输入
      /mcp reconnect explorer
      (或
      /mcp reconnect all
      )。
    • 启动全新 session/新建对话标签页,让 Explorer 保持运行——工具会在新 session 启动时自动绑定。在 VS Code 扩展和嵌入式面板中,这只需新建一个对话标签页,无需重启应用或 Explorer——应明确说明这一点,因为用户通常会误以为需要执行耗时的读取和终止操作。当
      /mcp reconnect
      回复 “MCP controls aren't available right now — the terminal is still starting up or is showing another view.” 时,应在这些 harness 中优先推荐此路径;这是已知且无法在聊天内修复的失败模式,而新建标签页是已经验证可行的方式。
    • curl 回退方案——每个玩家动作需要一条 shell 命令,每次出现新的调用形式都会触发权限提示,消耗更多 token,而且无法检查返回的截图图像。
    如果客户端提供
    AskUserQuestion
    ,则每项提供一个选项;将第二项标记为 “Restart session / new tab (Explorer stays up)”,以免被误解为关闭 Explorer。否则,以纯文本给出相同的三项,并以 “Which do you prefer?” 收尾。
    只有在以下三种状态之一才能继续——不存在第四种解读,且 subagent 永远只能处于第一种:
    • 工具已经存在(Explorer 在本 session 启动前已经运行)——此门控不适用;继续执行第 3 步。
    • 用户已重新绑定工具——通过
      /mcp
      重新连接,或在 Explorer 保持运行时打开全新 session/标签页。调用原生
      mcp__explorer__*
      工具进行验证,然后继续执行第 3 步。(全新 session 会结束当前对话;新 session 会发现工具已经绑定,并跳过此门控。)
    • 用户在本 session 中明确选择了 curl 回退方案——只有在告知其成本之后,才能读取
      reference/curl-fallback.md
      并通过 HTTP 驱动 endpoint。能够成功执行 curl 探测并不代表用户拒绝原生工具,沉默也不代表拒绝。
    第 1 步中的探测是执行此门控前唯一一次 curl 调用。获得响应只能证明 Explorer 已经启动——这是询问用户的触发条件,而不是继续执行的许可。
    预防措施只需提及一次,不必每个 session 重复:在 Claude Code 之前启动 Explorer——或者干脆让 Explorer 在多个 session 之间保持运行——即可完全跳过此门控。
    不在 Claude Code 中运行?
    claude mcp add
    和
    /mcp
    是 Claude Code 命令;对于 Cursor、Cline 或自定义 SDK harness,连接详情和配置结构参见
    reference/setup.md
    。Claude Code VS Code 扩展和 Creator Hub 的嵌入式聊天属于 Claude Code:它们支持上述命令,只需遵循 bind gate 中列出的 harness 差异。
  3. 等待世界加载:轮询
    get_scene_state
    ,直到
    loadingScreenOn
    为 false,且场景报告
    isReady: true
    。

The iteration loop

迭代循环

Repeat until every requirement has proof: a screenshot or state read demonstrating it, captured from a retail camera mode (
first_person
/
third_person
, not the free camera), with
get_scene_state
healthy and no unexplained errors in the logs.
  1. Edit the scene TypeScript in
    src/
    — read the owning topic skill before writing an API you can't recall exactly (see below). The dev server hot-reloads the running Explorer within a few seconds. If you need a deterministic reset instead, call
    reload_scene
    .
  2. Confirm the scene is healthy:
    get_scene_state
    — a
    state
    of
    JavaScriptError
    or
    EcsError
    means your code crashed the scene runtime.
  3. Read the runtime output:
    get_scene_logs
    with
    sinceSeq
    set to the last sequence number you saw. Scene
    console.log
    output and exceptions land here.
  4. Look and verify: position the view (
    teleport
    ,
    move_to
    ,
    walk
    ,
    look_at
    — for precise framing or free-camera sweeps read
    reference/camera-and-movement.md
    ), then
    screenshot
    and inspect the image against what the scene code should produce.
  5. Exercise behavior:
    walk
    into trigger areas,
    click_entity
    on interactables,
    send_chat
    for commands,
    trigger_emote
    , and re-screenshot to verify reactions.
    list_scene_entities
    +
    get_entity_details
    show the scene's ECS state when visuals aren't enough.
  6. Measure budget & performance (when the question is limits, frame rate, or what to optimize):
    get_scene_content_stats
    for the whole-scene counts against their soft caps,
    get_scene_content_breakdown
    to rank the heaviest source models (position the camera first — the visible columns are per point of view), and
    get_performance_stats
    to sample the real frame rate at a viewpoint. Don't prescribe from counts alone — correlate content with measured FPS. Full method and the materials-vs-shaderVariants interpretation are in
    reference/performance-debugging.md
    .
Your SDK7 API memory is stale. The SDK ships components newer than training data — native
TriggerArea
+
triggerAreaEventsSystem
(
TriggerArea.setBox(entity, ColliderLayer.CL_MAIN_PLAYER)
) among them — so failing to recall an API is no evidence it's missing, and neither is a version number you remember. When you can't recall an API exactly, or catch yourself hand-rolling a workaround (polling the player
Transform
for a trigger volume, mutating
engine.PlayerEntity
to move the player), read the topic skill that owns it before writing the workaround:
add-interactivity
for triggers and pointer events,
player-physics
for forces on the player,
advanced-input
for held keys,
sdk-scenes
for the component reference and the index of the rest. Reach for the official docs only where no skill covers it, and say so when you do.
Cross-examine every conclusion: confirm each visual claim with a state read (ECS values via
get_entity_details
, logs,
get_player_state
position), and each state claim with pixels. One channel lies routinely — colliders exist that pixels don't show, entities render invisible while their state looks healthy, animations silently don't play. The reference files call out where cross-examination is mandatory.
MANDATORY — camera cleanup before finishing. NEVER leave the camera in
free
mode when you stop working (end of task, handing back to the user, or pausing for their input): always restore it with
set_camera_mode third_person
as your last camera action, and confirm via
get_player_state
→
camera.mode
if anything in between could have failed.
重复执行,直到每项需求都有证据:以面向用户的摄像机模式(
first_person
/
third_person
,而不是自由摄像机)采集一张截图或状态读取结果,同时
get_scene_state
保持健康,且日志中不存在无法解释的错误。
  1. 编辑
    src/
    中的场景 TypeScript
    ——编写无法准确回忆的 API 前,先阅读负责该 API 的主题 skill(见下文)。dev server 会在几秒内热重载正在运行的 Explorer。如果需要确定性的重置,则调用
    reload_scene
    。
  2. 确认场景健康:调用
    get_scene_state
    ——
    state
    为
    JavaScriptError
    或
    EcsError
    表示代码导致场景运行时崩溃。
  3. 读取运行时输出:调用
    get_scene_logs
    ,并将
    sinceSeq
    设置为上次看到的序列号。场景的
    console.log
    输出和异常会出现在这里。
  4. 观察并验证:调整视角(
    teleport
    、
    move_to
    、
    walk
    、
    look_at
    ——精确取景或自由摄像机扫掠请参阅
    reference/camera-and-movement.md
    ),然后调用
    screenshot
    ,根据场景代码应产生的结果检查图像。
  5. 测试行为:使用
    walk
    进入触发区域,对可交互对象调用
    click_entity
    ,使用
    send_chat
    发送命令,使用
    trigger_emote
    ,然后重新截图以验证响应。当视觉信息不足时,使用
    list_scene_entities
    和
    get_entity_details
    查看场景的 ECS 状态。
  6. 测量预算与性能(当问题涉及限制、帧率或应优化什么时):使用
    get_scene_content_stats
    获取整个场景的计数及其软限制,使用
    get_scene_content_breakdown
    对最重的源模型进行排序(先定位摄像机——可见列取决于视点),并使用
    get_performance_stats
    在指定视点采样真实帧率。不要仅根据计数给出建议——应将内容与实测 FPS 关联分析。完整方法以及 materials 与 shaderVariants 的解释参见
    reference/performance-debugging.md
    。
你的 SDK7 API 记忆是陈旧的。 SDK 随包提供的组件可能比训练数据更新——原生
TriggerArea
+
triggerAreaEventsSystem
(
TriggerArea.setBox(entity, ColliderLayer.CL_MAIN_PLAYER)
)就是其中之一——因此想不起某个 API 并不代表它不存在,你记得的版本号也不是证据。当你无法准确回想某个 API,或发现自己正在绕道手写替代方案(轮询玩家
Transform
来实现触发体、修改
engine.PlayerEntity
来移动玩家)时,先阅读负责该 API 的主题 skill,再编写替代方案:触发器和 pointer event 使用
add-interactivity
,玩家受力使用
player-physics
,长按按键使用
advanced-input
,组件参考及其他索引使用
sdk-scenes
。只有在没有 skill 覆盖某项内容时才查阅官方文档,并明确说明这一点。
交叉验证每个结论: 每个视觉声明都用状态读取来确认(通过
get_entity_details
检查 ECS 值、查看日志以及
get_player_state
中的位置),每个状态声明都用像素来确认。某一通道经常会说谎——碰撞体存在但像素中看不到,实体渲染为不可见但其状态看起来正常,动画悄无声息地没有播放。参考文件会指出哪些地方必须进行交叉验证。
必须执行——结束前清理摄像机。 停止工作时(任务结束、交还用户或等待用户输入),绝不要把摄像机留在
free
模式:始终将
set_camera_mode third_person
作为最后一个摄像机操作;如果中间有任何操作可能失败,则通过
get_player_state
→
camera.mode
确认。

Screenshot frequency & cost

截图频率与成本

Every screenshot returned by the MCP
screenshot
tool lands in your context as an image (~1.2k tokens at 1280×720, scaling with pixel count). Occasional captures through the tool are fine; frequent or burst captures must go through the bundled script instead, which saves frames to disk (zero context cost) and prints only the caption:
Call it by absolute path — your cwd is the scene folder, not this skill's.
<skill-dir>
below is the base directory reported when this skill loads (
.claude/skills/unity-explorer-mcp/
when installed scene-local,
~/.claude/skills/unity-explorer-mcp/
when global):
bash
<skill-dir>/scripts/screenshot.sh -o /tmp/shot.jpg        # single frame to a file
<skill-dir>/scripts/screenshot.sh -n 10 -i 0.5            # burst: 10 frames every 0.5s (time-based behavior: tweens, animations)
<skill-dir>/scripts/screenshot.sh -w 640                  # cheap sanity-check resolution (~4x fewer tokens when you Read it)
<skill-dir>/scripts/screenshot.sh --world-only --png      # UI-less lossless frame
Frames default to
$TMPDIR/mcp-shots
, deliberately outside the scene folder: anything left in the project gets uploaded on deploy and counts against the per-parcel MB limits. Keep
-d
/
-o
targets out of the scene too — or add the directory to
.dclignore
if the user wants the frames kept beside their scene.
Requires curl + python3; pass
-p <port>
when not on 8123.
Read
only the frames you actually need to inspect — capture many, look at few. For before/after comparisons, capture both to disk and read just those two. Use
maxWidth
640 for quick checks and 1280 only for final verification. Captures are serialized server-side (concurrent requests are rejected), so keep burst intervals ≥ 0.2s.
通过 MCP
screenshot
工具返回的每张截图都会以图像形式进入上下文(1280×720 时约 1.2k tokens,成本随像素数增加)。偶尔通过工具捕获没有问题;频繁或连续捕获必须改用随附脚本,它会将帧保存到磁盘(上下文成本为零),并且只打印 caption:
按绝对路径调用它——当前工作目录是场景文件夹,而不是本 skill 的目录。下文的
<skill-dir>
是本 skill 加载时报告的基目录(安装在场景本地时为
.claude/skills/unity-explorer-mcp/
,安装在全局时为
~/.claude/skills/unity-explorer-mcp/
):
bash
<skill-dir>/scripts/screenshot.sh -o /tmp/shot.jpg        # single frame to a file
<skill-dir>/scripts/screenshot.sh -n 10 -i 0.5            # burst: 10 frames every 0.5s (time-based behavior: tweens, animations)
<skill-dir>/scripts/screenshot.sh -w 640                  # cheap sanity-check resolution (~4x fewer tokens when you Read it)
<skill-dir>/scripts/screenshot.sh --world-only --png      # UI-less lossless frame
帧默认保存到
$TMPDIR/mcp-shots
,刻意放在场景文件夹之外:项目中遗留的任何内容都会在部署时上传,并计入每个 parcel 的 MB 限制。将
-d
/
-o
目标放在场景之外——或者,如果用户希望把帧保存在场景旁边,将该目录加入
.dclignore
。
此功能需要 curl + python3;端口不是 8123 时,传入
-p <port>
。只
Read
实际需要检查的帧——可以捕获许多帧,但只查看少数帧。对于前后对比,将两张图都捕获到磁盘,并且只读取这两张。快速检查使用
maxWidth
640,最终验证使用 1280。捕获操作在服务端串行执行(并发请求会被拒绝),因此连续捕获的间隔应 ≥ 0.2s。

Scene health & recovery

场景健康与恢复

  • Sequence-poll logs (
    sinceSeq
    ) instead of re-reading the whole buffer; errors survive in the buffer even if they scrolled by.
  • scene.json
    changes (parcels, spawn points) are not hot-reloaded — restart the
    npm run start
    process, then
    reload_scene
    .
  • After
    teleport
    or
    reload_scene
    , always re-check
    get_scene_state
    before interacting; readiness can lag a few seconds.
  • One parcel is 16×16 m; parcel
    (x, y)
    spans world positions
    (16x..16x+16, 16y..16y+16)
    .
    --position 0,0
    spawns at parcel 0,0.
  • If the connection drops, the client probably crashed or was closed — relaunch it the same way it was started (
    npm run start -- --mcp
    , or the manual launch line in
    reference/setup.md
    ); the MCP endpoint URL stays the same.
  • Missing tools:
    mcp__explorer__*
    tools absent in-session is the bind gate — go back to Setup step 2, ask the user to reconnect the server (
    /mcp
    menu in the terminal CLI,
    /mcp reconnect explorer
    in the VS Code extension) or open a fresh session/conversation tab with the Explorer left running, and end your turn there. The HTTP fallback is in
    reference/curl-fallback.md
    , to be opened only after they have been warned of its costs and explicitly chosen it.
  • After a hot reload the player can end up off-parcel (e.g. parcel
    0,-1
    );
    get_scene_state
    then reports a null scene and
    reload_scene
    fails with "no scene at the current parcel". Check
    get_player_state
    →
    parcel
    ,
    move_to
    back inside, and the scene loads again.
  • One file write per change. Each save triggers a rebuild, so a multi-part edit split across saves breaks the scene in between — mildly, when usage and import land in separate saves (a transient
    SceneError: X is not defined
    ), or terminally: two saves seconds apart can make the Explorer load a mid-write bundle →
    SyntaxError: Invalid or unexpected token
    at scene start → the scene drops out and
    get_scene_state
    reports
    scene: null
    while you're standing on the parcel. From that state nothing recovers in-session:
    reload_scene
    errors ("no scene at the current parcel"),
    /reload
    hangs, the minimap RELOAD SCENE button no-ops, and moving off-parcel and back does not bring it back — only exiting/re-entering play mode (editor) or relaunching the standalone build. So batch multi-part changes into ONE write, write new modules before wiring them in, and after any save landing seconds after a previous one verify
    get_scene_state
    still shows a scene before saving again.
  • The
    teleport
    tool silently no-ops in local-scene-development mode:
    /goto
    teleports are disallowed there (chat shows "Teleport is not allowed in local scene development mode") but the tool still answers "Arrived at (x,y)". Use
    move_to
    for repositioning in local-scene sessions.
  • The Explorer under test may be the Unity Editor in play mode, not a standalone Decentraland.app — check
    ps aux | grep -i unity
    before considering a relaunch. Never kill the editor process; recovery from a wedged client is then a user action (exit/re-enter play mode).
  • 按序列号轮询日志(
    sinceSeq
    ),不要重复读取整个缓冲区;即使错误已经滚出屏幕,它们仍会保留在缓冲区中。
  • scene.json
    的变化(parcel、生成点)不会被热重载——重启
    npm run start
    进程,然后调用
    reload_scene
    。
  • 调用
    teleport
    或
    reload_scene
    后,务必再次检查
    get_scene_state
    ,然后再进行交互;就绪状态可能延迟几秒。
  • 一个 parcel 为 16×16 m;parcel
    (x, y)
    覆盖世界坐标
    (16x..16x+16, 16y..16y+16)
    。
    --position 0,0
    会在 parcel 0,0 生成。
  • 如果连接中断,client 很可能已经崩溃或被关闭——按照原先的启动方式重新启动(
    npm run start -- --mcp
    ,或使用
    reference/setup.md
    中的手动启动命令);MCP endpoint URL 保持不变。
  • 缺少工具:session 中不存在
    mcp__explorer__*
    工具意味着遇到了 bind gate——返回 Setup 第 2 步,询问用户是否重新连接 server(终端 CLI 使用
    /mcp
    菜单,VS Code 扩展使用
    /mcp reconnect explorer
    ),或让 Explorer 保持运行并打开全新 session/对话标签页,然后结束当前轮次。HTTP 回退方案位于
    reference/curl-fallback.md
    中,只有在警告用户其成本且用户明确选择后才能打开。
  • 热重载后,玩家可能落到 parcel 之外(例如 parcel
    0,-1
    );此时
    get_scene_state
    会报告 null 场景,并且
    reload_scene
    失败并提示 “no scene at the current parcel”。检查
    get_player_state
    →
    parcel
    ,使用
    move_to
    移回 parcel 内,场景便会重新加载。
  • 每次变更只写入一个文件。 每次保存都会触发重建,因此将一次变更拆分成多次保存会在中间破坏场景——轻则
    usage
    和
    import
    分开保存时暂时出现
    SceneError: X is not defined
    ,重则相隔数秒的两次保存可能让 Explorer 加载到写入中途的 bundle → 场景启动时出现
    SyntaxError: Invalid or unexpected token
    → 场景掉线,而
    get_scene_state
    在玩家仍站在 parcel 上时会报告
    scene: null
    。从该状态出发,session 内无法恢复:
    reload_scene
    会报错(“no scene at the current parcel”),
    /reload
    会挂起,小地图上的 RELOAD SCENE 按钮不起作用,移动到 parcel 外再返回也无法使其恢复——只有退出并重新进入 play mode(editor)或重启 standalone build 才能恢复。因此,应将多部分更改合并为一次写入,先编写新 module,再将它们连接起来;任何一次保存若与前一次相隔数秒,都应先验证
    get_scene_state
    仍显示场景,然后再进行下一次保存。
  • teleport
    工具在本地场景开发模式下会静默失效:此时禁止
    /goto
    传送(聊天会显示 “Teleport is not allowed in local scene development mode”),但工具仍会回复 “Arrived at (x,y)”。本地场景 session 应使用
    move_to
    重新定位。
  • 被测试的 Explorer 可能是处于 play mode 的 Unity Editor,而不是独立的 Decentraland.app——考虑重新启动之前,先运行
    ps aux | grep -i unity
    。绝不要终止 Editor 进程;如果 client 已卡死,恢复操作必须由用户完成(退出并重新进入 play mode)。

Interaction testing

交互测试

  • click_entity
    presses a pointer button on a scene entity (get ids from
    list_scene_entities
    ). The target needs a
    PointerEvents
    component and a collider; the aim is validated by a real camera-origin raycast, so occluders return
    hit:false
    +
    blockedBy*
    (reposition and retry) and the entity's
    maxDistance
    (default 10 m) applies — get close first.
    upRayMissed: true
    means the target moved between press and release (e.g. a door starting to swing) and the release was delivered with the press-frame hit. For GLTF entities whose collider sits away from the pivot, pass an explicit
    x/y/z
    aim point. The player must be standing on the scene's parcel — off-parcel clicks fail with "no running current scene".
  • walk
    moves relative to the camera and requires an explicit direction: pass
    directionY: 1
    for forward (
    directionX
    strafes); omitting both errors with "directionX and directionY must not both be zero".
  • Collider checks beat pixels for physics (cross-examine):
    look_at
    straight at the target,
    walk
    forward, then compare
    get_player_state
    positions to prove passage or blockage.
  • Trigger areas fire
    onTriggerEnter
    immediately after
    reload_scene
    if the player is already standing inside one — reposition the player outside all triggers before testing enter/exit sequencing (and treat post-reload trigger logs as stale state, not gameplay).
  • click_entity
    会在场景实体上按下 pointer button(从
    list_scene_entities
    获取 id)。目标必须具有
    PointerEvents
    component 和 collider;瞄准会通过从摄像机原点发起的真实 raycast 进行验证,因此遮挡物会返回
    hit:false
    和
    blockedBy*
    (重新定位后重试),并且实体的
    maxDistance
    (默认 10 m)也会生效——应先靠近目标。
    upRayMissed: true
    表示目标在按下和释放之间发生了移动(例如一扇开始摆动的门),并且释放操作使用的是按下帧命中的目标。对于 collider 位于 pivot 之外的 GLTF 实体,请传入显式的
    x/y/z
    瞄准点。玩家必须站在场景的 parcel 上——在 parcel 外点击会失败并提示 “no running current scene”。
  • walk
    会相对于摄像机移动,并且要求提供明确方向:向前传递
    directionY: 1
    (
    directionX
    用于横向移动);如果两者都省略,则会报错 “directionX and directionY must not both be zero”。
  • 对 physics 而言,collider 检查比像素更可靠(交叉验证):使用
    look_at
    正对目标,使用
    walk
    向前移动,然后比较
    get_player_state
    中的位置,以证明能否通过或被阻挡。
  • 如果
    reload_scene
    后玩家已经站在触发区域内,
    onTriggerEnter
    会立即触发——测试进入/退出顺序前,将玩家重新定位到所有触发区域之外(并将重载后的触发日志视为陈旧状态,而不是游戏行为)。

When a capability is missing

缺少某项能力时

If the loop is blocked because no connected MCP tool can do what you need (e.g. pressing a specific key, reading a value no tool exposes), stop and hand it to the user: name the concrete action you're blocked on and why the existing tools can't cover it, and let them decide how to extend the setup. The MCP server and Explorer live outside this scene's repo — extending them is the user's call, not something to work around from here.
如果迭代循环因已连接的 MCP 工具无法完成所需操作而受阻(例如无法按下特定按键,或无法读取工具未暴露的值),应停止操作并将其交给用户:明确说明被阻断的具体操作,以及现有工具为何无法覆盖。不要绕过限制;MCP server 和 Explorer 位于此场景的 repo 之外,扩展它们由用户决定。