From a8b81c56a056ed1087b7093c9ca03c0b662d040a Mon Sep 17 00:00:00 2001 From: SHL0MS Date: Thu, 16 Jul 2026 17:14:14 -0400 Subject: [PATCH] feat(optional-skills): add unreal-mcp companion skill for the unreal-engine MCP catalog entry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Companion to optional-mcps/unreal-engine (Epic's official editor-embedded MCP server, UE 5.8 experimental). Mirrors the blender-mcp catalog-entry + companion-skill pattern, sized up for Unreal's discovery-based surface: - SKILL.md: tool-search discovery contract (list_toolsets/describe_toolset/ call_tool), serial game-thread call discipline (explicitly overrides the parallel-batching default), plain-English->scene translation workflow, save/verify hygiene, art-direction loop - references/tool-surface.md: architecture (Unreal MCP / Toolset Registry / AllToolsets), confirmed shipped toolsets, call_tool dispatch semantics, project Agent Skills (AgentSkillToolset), capture paths, custom Python/C++ toolset authoring, config/CVar/console reference, cooked-build notes - references/scene-craft.md: physically-based lighting values (lux/lumens/ Kelvin/EV100), mood recipes, Lumen Movable-mobility rule, scale tables, content-path conventions, CineCamera framing, editor Python entry points - references/recipes.md: four end-to-end builds in INTENT/DISCOVER/VALUES/ VERIFY grammar (exterior, night interior, golden-hour cinematic still, import+populate) that stay honest about the project-dependent surface - references/pitfalls.md: 25+ failure modes with fixes: start order, modal deadlocks, Hermes-timeout-vs-editor-completion, _C class suffix, PascalCase silent no-op writes, referenced-asset delete crash, async shader compiles, editor sprite icons in screenshots, PIE interference Grounded in Epic's UE 5.8 docs and Epic's agent-facing skill pack for this server; no fabricated tool names — live describe_toolset schemas are the contract throughout. --- optional-skills/creative/unreal-mcp/SKILL.md | 238 +++++++++++++++ .../unreal-mcp/references/pitfalls.md | 244 ++++++++++++++++ .../creative/unreal-mcp/references/recipes.md | 222 ++++++++++++++ .../unreal-mcp/references/scene-craft.md | 271 +++++++++++++++++ .../unreal-mcp/references/tool-surface.md | 274 ++++++++++++++++++ 5 files changed, 1249 insertions(+) create mode 100644 optional-skills/creative/unreal-mcp/SKILL.md create mode 100644 optional-skills/creative/unreal-mcp/references/pitfalls.md create mode 100644 optional-skills/creative/unreal-mcp/references/recipes.md create mode 100644 optional-skills/creative/unreal-mcp/references/scene-craft.md create mode 100644 optional-skills/creative/unreal-mcp/references/tool-surface.md diff --git a/optional-skills/creative/unreal-mcp/SKILL.md b/optional-skills/creative/unreal-mcp/SKILL.md new file mode 100644 index 0000000000000..2b1cb9de15fd6 --- /dev/null +++ b/optional-skills/creative/unreal-mcp/SKILL.md @@ -0,0 +1,238 @@ +--- +name: unreal-mcp +description: "Use when the user wants to do anything in Unreal Engine through Epic's official editor-embedded MCP server (catalog entry: unreal-engine) — build/light/populate scenes, place and transform actors, create material instances, frame cameras, take screenshots, render, import assets, run automation tests, or automate the editor end-to-end from plain-English prompts with no Unreal knowledge required. Covers the tool-search discovery walk (list_toolsets/describe_toolset/call_tool), serial game-thread call discipline, scene-craft numbers (physical light units, exposure, scale conventions), complete build recipes, save/undo hygiene, and extending the tool surface with custom Python toolsets." +version: 1.0.0 +requires: Unreal Editor 5.8+ with the Unreal MCP plugin enabled and its server running +author: Hermes Agent +license: MIT +tags: [unreal, unreal-engine, ue5, 3d, mcp, scenes, cinematics, lighting, gamedev] +platforms: [linux, macos, windows] +metadata: + hermes: + tags: [unreal, unreal-engine, ue5, 3d, mcp, scenes, cinematics, lighting, gamedev] + related_skills: [blender-mcp] +--- + +# Unreal Engine MCP Skill + +Companion skill for the `unreal-engine` entry in the Hermes MCP catalog. The +MCP server (Epic's official, experimental "Unreal MCP" plugin, internal id +`ModelContextProtocol`) runs INSIDE the Unreal Editor process and exposes +editor functionality as typed tools. This skill teaches how to drive it well: +discovering the live tool surface, sequencing calls safely, translating +plain-English asks into scenes that actually look good, and verifying work +visually. The user should never need to touch the editor beyond launching it. + +## When to Use + +Use when the user wants anything done in Unreal Engine: build or dress a +level, spawn/move/delete actors, set up lighting and atmosphere, create or +tune material instances, frame a camera shot, capture screenshots or renders, +import assets, inspect the scene or UI, run automation tests, or script the +editor. Works for single actions ("make the sun golden hour") and for +complete multi-step projects ("build me a moody forest clearing with a +campfire and render a shot of it"). + +Don't use for: DCC-style mesh modeling/sculpting (use `blender-mcp` and +import the result), or for editing Unreal C++ project source (that's normal +code work — use the terminal; this skill is about the live editor). + +## Prerequisites + +Two halves, in this order: the editor side must be up before Hermes connects. + +### One-time, editor side + +1. Unreal Editor **5.8+** with a project open. +2. **Edit > Plugins** — enable **Unreal MCP** (its Toolset Registry + dependency auto-enables). Restart the editor when prompted. +3. The typed toolsets ship separately from the server: also enable the + **AllToolsets** plugin in the same Plugins browser. Unreal MCP ships NO + tools itself — AllToolsets provides the shipped toolsets (SceneTools, + ActorTools, MaterialInstanceTools, ObjectTools, …); skip it and the + server connects but the agent has nothing to call. +4. **Edit > Editor Preferences > General > Model Context Protocol** — enable + **Auto Start Server**. Default bind is `http://127.0.0.1:8000/mcp` + (port/path configurable in the same panel; server name is `unreal-mcp`). + To start manually instead, run `ModelContextProtocol.StartServer` in the + editor console (backtick key). + +### One-time, Hermes side + + hermes mcp install unreal-engine + +This writes the `mcp_servers.unreal-engine` HTTP entry pointing at +`http://127.0.0.1:8000/mcp` and probes the live server for its tools. Run it +while the editor + server are up so the probe sees the real surface. If the +user changed port/path in Editor Preferences, edit the `url` in +`~/.hermes/config.yaml` under `mcp_servers.unreal-engine` to match. + +Do NOT use `ModelContextProtocol.GenerateClientConfig` for Hermes — that +writes `.mcp.json`-style files for Claude Code/Cursor/etc. Hermes connects +from `config.yaml` via the catalog entry. + +### Every session + +1. Launch Unreal Editor, wait for the project to finish loading; confirm the + server started (Output Log shows the bind address, or run + `ModelContextProtocol.StartServer` manually). +2. Start the Hermes session. Tools register as `mcp_unreal_engine_*`. If + they're missing: editor wasn't up first — start it, then open a new + Hermes session. +3. Sanity check: call `mcp_unreal_engine_list_toolsets` and confirm toolsets + come back. + +## The Tool Surface: Discovery, Not a Fixed List + +By default the plugin runs in **tool-search mode**: `tools/list` returns only +three meta-tools, and every real tool is reached through them. Through Hermes +they appear as: + +| Hermes tool | Purpose | +|---|---| +| `mcp_unreal_engine_list_toolsets` | Names + descriptions of every registered toolset | +| `mcp_unreal_engine_describe_toolset` | Full JSON schemas for one named toolset's tools | +| `mcp_unreal_engine_call_tool` | Invoke a named tool with arguments, get the result | + +The discovery walk, always in this order: + +1. `list_toolsets` → see what capability groups this project actually has + (the surface is project-dependent: enabled plugins, Game Feature Plugins, + and any custom toolsets all contribute). +2. `describe_toolset` on the group you need → read the real parameter + schemas. Never guess parameter names — schemas are the contract. +3. `call_tool` with exact toolset/tool name and arguments. + +Cache what you learn for the session; re-list only after the editor side +changes (new plugin enabled, toolset authored, `RefreshTools` run). + +The alternative eager mode (`Enable Tool Search` off in Editor Preferences) +advertises every tool as its own `mcp_unreal_engine_` entry. Discovery +then happens at `hermes mcp install`/`configure` time instead. Tool-search +mode is the default and what this skill assumes; it also keeps schema tokens +out of every API call, so prefer it. + +See `references/tool-surface.md` for the shipped toolset catalog, authoring +custom toolsets, and the full plugin configuration/console-command reference. + +## Operating Loop + +Every Unreal task follows the same loop: + +1. **Inspect first.** List toolsets, then query the scene/level state before + touching anything. Never assume an empty or default level. In an + unfamiliar project, also check for project-registered Agent Skills + (`call_tool` → `AgentSkillToolset.ListSkills`): a matching project skill's + instructions override this skill's generic defaults. +2. **Act in small, single-purpose calls.** One logical step per `call_tool`. + The server executes tools **serially on the game thread** — a big + monolithic operation freezes the editor UI until it finishes and risks + client timeouts. +3. **NEVER issue overlapping calls.** Do not batch multiple + `mcp_unreal_engine_*` calls in one turn — Hermes runs batched calls + concurrently, and parallel calls against the game thread deadlock or + fail. Strictly one call, await result, next call. This overrides the + general parallel-tool-calls guidance. +4. **Read every result.** Many tools (Blueprint compiles, material edits, + widget creation) report success/failure in the response body with no + protocol-level exception. Anything that isn't an explicit success is a + stop-and-diagnose, not a shrug. After property writes, read the value + back — several write paths silently no-op (see pitfalls). +5. **Verify visually and structurally.** After each milestone, confirm state + by querying the actors/properties you changed, and capture a viewport + screenshot when composition matters (see `references/tool-surface.md` for + the capture options; `vision_analyze` the image — you are the art + director, judge it). +6. **Save often.** Editor edits are in-memory until packages/levels are + saved; an editor crash loses everything since the last save, and MCP + edits are not reliably undoable. Save before AND after any bulk change, + and after every milestone. +7. **Report concretely.** Actor labels, asset paths (`/Game/...`), file + locations of captures/renders. + +Rules of the world while you work: + +- Units are **centimeters**; axes are **Z-up**, X-forward; rotations are + degrees (Rotator: Roll around X, Pitch around Y, Yaw around Z). Human eye + height ≈ 165 cm; a door ≈ 210×90 cm. Full tables in + `references/scene-craft.md`. +- Content paths use long package names: `/Game/Folder/Asset.Asset` for + project content, `/Engine/BasicShapes/Cube.Cube` for engine primitives. +- Actor **labels** (what you see in the Outliner, settable, non-unique) are + not actor **names** (internal, unique). Prefer resolving actors by + label/class queries, then hold on to whatever handle the tool returns. +- Prefer physically-plausible lighting values (lux/candela/Kelvin) over + arbitrary brightness numbers — `references/scene-craft.md` has the + numeric recipes (sun intensities, exposure, fog densities, moods). + +## From Plain English to a Scene + +The user gives intent, not specs. Translate before you build: + +1. **Extract the brief.** Subject, mood, time of day, interior/exterior, + style, deliverable (screenshot? render? playable level?). Ask at most one + round of clarifying questions, then commit — you are the technical + director; don't bounce Unreal jargon back at the user. +2. **Plan the build order.** The order that works: level/environment shell → + blocking (major geometry/meshes in place) → lighting + atmosphere → + materials → set dressing/detail → camera → capture/render. Post the plan + as a todo list for multi-step builds. +3. **Build with the loop above**, one milestone at a time, screenshot at + each milestone. +4. **Art-direct yourself.** Compare each screenshot against the brief: + readable silhouette? believable light direction/intensity? horizon not + dead-center? scale correct against a human-height reference? Fix before + moving on. +5. **Deliver.** Screenshots/renders as files (`MEDIA:` path), plus a short + summary of what exists in the level and where it was saved. + +`references/recipes.md` has complete worked builds (exterior daylight scene, +moody interior, golden-hour cinematic + render, asset import & placement) +with the exact call sequences and values. + +## Reference Files + +Load on demand; keep SKILL.md-level rules in mind throughout. + +| Reference | Contents | +|---|---| +| `references/tool-surface.md` | Shipped toolsets catalog, discovery protocol detail, plugin console commands/CVars/flags, screenshot & capture paths, MCP Inspector debugging, extending with custom Python/C++ toolsets | +| `references/scene-craft.md` | Numeric cheat sheet: physical light intensities, color temperatures, exposure/EV100, fog densities, mood recipes (noon/golden hour/overcast/night/interior), scale tables, content path conventions | +| `references/recipes.md` | End-to-end worked builds with exact call sequences | +| `references/pitfalls.md` | Setup, runtime, and workflow pitfalls with fixes — read before your first session and whenever something misbehaves | + +## Pitfalls (top of mind — full list in references/pitfalls.md) + +- **Start order matters.** Editor + server up first, then the Hermes + session. Missing `mcp_unreal_engine_*` tools = wrong order. +- **One call at a time.** Serial game thread; no batching, no overlap. +- **The editor UI freezes during each call.** That's by design (game-thread + execution). Warn the user during long operations; keep calls small. +- **Modal dialogs block everything.** A tool call that opens (or collides + with) a modal editor dialog stalls until a human dismisses it. If a call + hangs indefinitely, tell the user to check the editor for a dialog. +- **Timeouts on long operations.** Hermes' per-call default is 120 s; asset + imports, big level saves, and renders can exceed it. Raise + `mcp_servers.unreal-engine.timeout` in `~/.hermes/config.yaml` for + render/import-heavy sessions. +- **Stale tool schemas.** After authoring/hot-reloading toolsets or enabling + a plugin, run `ModelContextProtocol.RefreshTools` in the editor console + and re-`list_toolsets`. New C++ `UFUNCTION`s need a full editor restart — + Live Coding won't surface them. +- **Experimental plugin.** APIs and tool shapes can change between engine + versions; trust `describe_toolset` over memory, including this skill's + examples. When docs and the live schema disagree, the live schema wins. +- **Don't expose the server beyond localhost.** Loopback-only, no auth, by + design. Never suggest binding it wider. + +## Verification Checklist + +- [ ] `list_toolsets` returns toolsets at session start (connection healthy) +- [ ] Scene state queried before first edit (never assumed empty) +- [ ] After each milestone: changed actors/properties re-queried and a + screenshot reviewed against the brief +- [ ] Level/dirty packages saved after each milestone and at the end +- [ ] Deliverables exist on disk (screenshot/render paths confirmed) and are + reported to the user with absolute paths +- [ ] Editor left in a clean state: no pending modal, no unsaved surprise, + user told exactly what was created/changed and where diff --git a/optional-skills/creative/unreal-mcp/references/pitfalls.md b/optional-skills/creative/unreal-mcp/references/pitfalls.md new file mode 100644 index 0000000000000..449fe331147cb --- /dev/null +++ b/optional-skills/creative/unreal-mcp/references/pitfalls.md @@ -0,0 +1,244 @@ +# Unreal MCP — Pitfalls & Lessons + +Read before your first session; return whenever something misbehaves. Ordered +by when they bite: setup → calling discipline → editor state → content → +delivery. + +## Setup & Connection + +### 1. Start order: editor first, Hermes second + +Hermes probes MCP servers at session start. If the editor (and its server) +isn't up yet, no `mcp_unreal_engine_*` tools exist in the session. Fix: +launch the editor, confirm the server bound (Output Log shows +`LogModelContextProtocol` with the address), then open a NEW Hermes session. +Tools don't hot-appear mid-session. + +### 2. Server enabled but no tools advertised + +The Unreal MCP plugin ships the SERVER, not the tools. If `list_toolsets` +returns nothing/near-nothing, the toolset provider plugin (AllToolsets) or +Toolset Registry isn't enabled in this project. Fix in Edit > Plugins, +restart the editor, restart the Hermes session. + +### 3. Port 8000 conflicts + +Common collisions: local dev servers, Jupyter, other MCP hosts. Symptom: the +server fails to bind (Output Log) or Hermes' probe times out. Fix: change +Server Port Number in Editor Preferences > Model Context Protocol AND the +`url` in `~/.hermes/config.yaml` (`mcp_servers.unreal-engine`), then restart +both sides. Verify: `curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8000/mcp` +(non-000 means something is listening; whether it's Unreal is a different +question — check the Output Log). + +### 4. "Connection refused" mid-session + +The editor was closed, crashed, or the server was stopped +(`ModelContextProtocol.StopServer`). Don't retry the tool in a loop — tell +the user, have them relaunch/restart the server, then reconnect (new session +if tools were lost). + +### 5. GenerateClientConfig is not for Hermes + +`ModelContextProtocol.GenerateClientConfig` writes config files for Claude +Code/Cursor/VSCode/Gemini/Codex into the project root. Hermes' connection +lives in `~/.hermes/config.yaml` via `hermes mcp install unreal-engine`. +Running GenerateClientConfig neither helps nor harms Hermes — just don't +mistake it for the Hermes setup step. + +## Calling Discipline + +### 6. One call at a time — never batch MCP calls + +The server executes tool calls serially on the game thread and Epic +explicitly warns against overlapping calls. Hermes executes same-turn tool +calls concurrently — so batching two `mcp_unreal_engine_*` calls in one turn +IS issuing overlapping calls. Strictly sequential: call, await, then next. +This deliberately overrides the general "batch independent calls" guidance. + +### 7. The editor freezes during every call — keep calls small + +Game-thread execution means the editor UI hitches for the duration of each +tool call. A 30-second operation is 30 seconds of frozen editor. Split big +asks (e.g. "spawn 200 trees") into chunks so the user's editor stays +responsive and any failure loses only one chunk. + +### 8. Modal dialogs deadlock the loop + +Anything that pops a modal (some deletes, import options, save prompts, +experimental-plugin warnings) blocks the game thread — and your call — +until a human clicks. If a call hangs far beyond its normal duration, tell +the user to look at the editor for a dialog. Prefer tool paths/parameters +that avoid interactive prompts; save proactively so "unsaved changes" +prompts don't appear at bad times. + +### 9. Timeouts: Hermes gives up before Unreal does + +Hermes' default per-call timeout is 120 s. Asset imports, first-shader +compiles, big saves, and renders can exceed it — the call "fails" while the +editor happily finishes the work. Symptoms: timeout error, then the next +scene query shows the operation actually completed. Fixes: raise +`mcp_servers.unreal-engine.timeout` in config for heavy sessions; after any +timeout, RE-QUERY state before retrying, or you'll do the work twice +(duplicate actors are the classic case). + +### 10. Stale schemas after editor-side changes + +Toolsets are cached: after enabling a plugin, authoring a toolset, or Live +Coding, run `ModelContextProtocol.RefreshTools` in the editor console, then +re-run `list_toolsets`/`describe_toolset`. New C++ `UFUNCTION`s need a full +editor restart regardless. If a call fails with "unknown tool" that +`describe_toolset` just showed, refresh + reconnect. + +### 11. Experimental means drift + +Tool names, parameters, and result shapes may change across engine versions. +The live schema from `describe_toolset` is the only contract. If this +skill's examples and the live schema disagree, the schema wins — and patch +this skill afterward. + +## Editor & Scene State + +### 12. Never assume a fresh level + +Query the scene before the first edit. The user's level may have existing +actors, a non-default sun, post-process volumes with exposure overrides — +your lighting changes can look wrong because of a pre-existing volume, not +your values. + +### 13. In-memory edits are lost on crash — save per milestone + +Everything you do lives in unsaved packages until a save happens. The editor +is an application that can crash, especially mid-experimental-feature. Save +the level + dirty packages after every milestone. Also: some operations +(level streaming, some asset moves) behave differently on unsaved assets. + +### 14. Label ≠ Name ≠ path — use the full path as the stable identifier + +Outliner shows actor LABELS (settable, duplicable, human-friendly). Internal +NAMES are unique per level but auto-generated (`StaticMeshActor_3`). The only +identifier that survives renames and disambiguates duplicates is the full +object path: `/Game/Maps/Level.Level:PersistentLevel.BP_Character_C_0`. +Tools may accept label, name, or path — read the schema. When you create an +actor, immediately set a meaningful label, and record whatever handle the +tool returns for later operations. + +### 14b. Asset path forms mean different things + +| Form | Example | Loads | +|---|---|---| +| Package | `/Game/Foo/Bar` | The package (asset-registry queries) | +| Package.Asset | `/Game/Foo/Bar.Bar` | The primary asset (most load/assign args) | +| Package.Asset_C | `/Game/Foo/Bar.Bar_C` | A Blueprint's **generated class** | + +"Class not found: /Game/Path/BP_Foo" almost always means the missing `_C` +suffix — spawning by Blueprint class needs the generated-class form. + +### 14c. Property writes can silently no-op — round-trip verify + +UPROPERTY names are PascalCase at the reflection layer; snake_case lookups +through some write paths silently change nothing and return no error. After +any property write that matters, READ THE VALUE BACK and compare (allowing +formatting normalization like `1` vs `1.000000`). If it didn't take, retry +with the exact PascalCase name shown by the property dump/schema. + +### 15. Play In Editor changes the world (literally) + +If the user hits Play, queries/edits may target the transient PIE world, and +edits to it evaporate when play stops. If results look inexplicably +transient or actor lists suddenly differ, ask whether PIE is running; do +edit work outside PIE. + +### 16. Undo exists, but don't lean on it + +Editor transactions power Ctrl+Z; tool-driven changes may or may not create +clean transaction boundaries depending on the tool's implementation. Treat +undo as the user's manual escape hatch, not your rollback mechanism — your +rollback is: query state, compute the inverse edit, apply it. + +## Content & Assets + +### 17. Long package names, not file paths + +Assets are addressed as `/Game/Folder/Asset.Asset` (project content), +`/Engine/...` (engine content), `/Script/Module.Class` (native classes). +Windows-style or absolute filesystem paths are wrong everywhere except +import/export file arguments and screenshot output paths. + +### 18. Filesystem results land on the EDITOR host + +Screenshots, renders, and exports write to the machine running Unreal (e.g. +`/Saved/Screenshots/...`). If Hermes runs elsewhere (SSH backend, +container), `read_file` on that path reads the wrong filesystem. Same-machine +setups (the default here) can read captures directly. + +### 19. Referenced ≠ loaded + +Engine basics (`/Engine/BasicShapes/...`) are always available, but project +assets may need loading before use, and a typo'd asset path often fails +soft (empty mesh, default material) rather than loud. After assigning +meshes/materials, re-query the actor to confirm the reference stuck. + +### 20. Material edits: instances, not parents + +Editing a parent Material recompiles shaders (slow, global blast radius). +Create a Material Instance (Dynamic or Constant per the tool surface), set +scalar/vector/texture parameters on it, assign to the mesh. Parameter names +must match the parent's exposed parameters exactly — query/describe before +setting; a misnamed parameter usually no-ops silently. + +### 20b. Shader/asset compilation is async — don't judge or proceed early + +Material creation/edits kick off shader compilation that can run seconds to +minutes; Niagara compiles, DDC builds, and package saves are async too. A +screenshot taken mid-compile shows the old (or default-checkerboard) state. +After material work, wait for compilation before judging visuals (poll a +compile/errors predicate if the tool surface has one; otherwise screenshot +after a delay and re-check if it looks wrong). Same discipline after saves: +don't chain a disk-read straight after a write. + +### 20c. Emissive needs intensity > 1 to bloom + +Emissive at ≤1.0 looks self-lit but never blooms. 3–10 gives visible glow — +and the Post Process Volume must have Bloom enabled (default on). + +### 20d. Crash patterns to avoid outright + +Engine-level, any server: (a) deleting or transforming an ASSET while level +actors still reference it → `RegisteredElementType` assertion, editor down, +unsaved work gone — walk references first, swap actors to a replacement, +then delete; (b) spawn→delete→spawn the same actor in rapid succession can +corrupt the actor registry — don't tight-loop create/destroy cycles; +(c) Niagara/MetaSound assertion during PIE reverts to last on-disk save — +save BEFORE entering PIE when those subsystems are involved. + +## Delivery + +### 21. Screenshot judgment is part of the job + +Don't declare a lighting/composition milestone done from numbers alone — +capture the viewport, `vision_analyze` it, and art-direct (silhouette, +exposure, horizon placement, scale against human height). The user is +non-technical; you are the one with eyes on both the brief and the frame. + +### 21b. Editor screenshots show sprite icons that look like content + +Viewport captures in the editor include per-component sprite icons (light +bulbs, speaker/Niagara icons). They are editor overlay, NOT your scene — +particles especially: an editor screenshot is not proof a Niagara effect is +emitting. Verify effects via the actor's active state or a PIE capture. + +### 22. Report package paths + file paths + +The user needs: what actors/assets now exist (labels + `/Game/...` paths), +where the level was saved, and absolute filesystem paths of any +captures/renders (delivered as `MEDIA:` where appropriate). + +## Sources + +Grounded in Epic's UE 5.8 Unreal MCP documentation, Epic's own agent-facing +skill pack for this server (unreal-engine-skills-for-claude-code), and +engine-level field reports from the UE-via-MCP community (ue5-mcp field +manual). Engine behaviors (crash patterns, Lumen mobility, async compiles, +reflection casing) are server-agnostic; tool names and schemas remain +whatever the live `describe_toolset` says. diff --git a/optional-skills/creative/unreal-mcp/references/recipes.md b/optional-skills/creative/unreal-mcp/references/recipes.md new file mode 100644 index 0000000000000..076d0e8f8e17e --- /dev/null +++ b/optional-skills/creative/unreal-mcp/references/recipes.md @@ -0,0 +1,222 @@ +# Unreal MCP — Worked Recipes + +Complete build sequences from a plain-English brief to a delivered capture. +Written against the discovery contract, because the live tool surface is +project-dependent: each step names the **capability to locate** (via +`list_toolsets` / `describe_toolset`) and the **exact values** to feed it — +not hardcoded tool names, which drift while the plugin is experimental. + +## Recipe grammar + +Every step = four parts: + + INTENT what this step achieves + DISCOVER which toolset/tool capability to use (locate via describe_toolset) + VALUES the exact arguments/numbers (from scene-craft.md) + VERIFY the query or screenshot that proves it worked + +Dispatch shape: `call_tool` with `toolset_name`, `tool_name`, and an +`arguments` object matching the described schema — result returns on the +same turn. + +LIGHTING RULE for every recipe: set **Mobility = Movable** on every light +you spawn (Lumen GI ignores Static/Stationary lights — the #1 "why is GI +dead" cause). + +Session preamble for every recipe (do once): + +1. `list_toolsets` → note actor/scene/material/level capability groups. +2. `describe_toolset` on each group you'll touch → cache schemas. +3. Query current level state (all actors + classes) → never assume empty. +4. Locate the screenshot path for this project: + `/Saved/Screenshots//`. + +Save the level + dirty packages after every phase marked 💾. One tool call +at a time throughout — no batching, ever. + +--- + +## Recipe A — Daylight exterior clearing (blocking-first exterior) + +Brief: "a sunny clearing with some rocks and a path" + +**Phase 1 — environment shell** + +- INTENT sky + sun + atmosphere exist and track each other. + DISCOVER actor-spawn capability (spawn by class). + VALUES spawn `SkyAtmosphere`, `SkyLight` (real-time capture), + `DirectionalLight` at rotation (0, −55, 40) [roll, pitch, yaw], + intensity 90,000 lux, temperature 5,800 K, "atmosphere sun light" on; + `ExponentialHeightFog` density 0.008. + VERIFY actor list shows all four; screenshot reads as daytime sky, not + black (if black: exposure — see Phase 3). + +**Phase 2 — ground & blocking** + +- INTENT walkable ground plane. + DISCOVER spawn-from-asset capability. + VALUES `/Engine/BasicShapes/Plane.Plane` at (0,0,0), scale (100,100,1) + → 100×100 m ground. Label `Ground`. +- INTENT rock cluster + path silhouette from primitives (placeholder for + real assets if the project has none). + VALUES 5–9 `/Engine/BasicShapes/Cube.Cube` at scattered locations within + ±2,000 cm of origin, non-uniform scales between (1.5,1.5,1) and (4,3,2), + yaws randomized 0–360°, sunk 10–30 cm into the ground so nothing floats. + A path: 6–10 flattened cubes scale ≈(1.2,0.8,0.05) snaking through. + VERIFY screenshot at eye height (camera z≈165) along the path axis: rocks + read as varied, nothing floats, scale sane against the 180 cm yardstick + (place one 180 cm-tall cylinder temporarily as a human stand-in, delete + after checking). 💾 + +**Phase 3 — exposure & mood** + +- INTENT deterministic exposure. + DISCOVER PostProcessVolume spawn + property-set capability. + VALUES PPV unbound=true, metering Manual, EV100 = 14.5. + VERIFY screenshot: bright but not blown; shadows readable. + +**Phase 4 — deliver** + +- INTENT hero still. + VALUES `HighResShot 3840x2160` from a framed viewpoint (see Recipe C + Phase 2 for framing rules). + VERIFY file exists in Saved/Screenshots; `vision_analyze` against brief; + iterate lighting yaw/fog once if flat. 💾 Report actor labels + paths. + +--- + +## Recipe B — Moody practical-lit interior + +Brief: "a dim cozy room at night, warm lamp, blue moonlight through window" + +**Phase 1 — room shell from primitives** + +- VALUES floor: Cube at (0,0,−10) scale (6,6,0.2) → 6×6 m room. Four walls: + cubes scale (6,0.2,3) / (0.2,6,3) positioned at ±300 on the respective + axis, z=140 (walls 280 cm tall, sitting on the floor plane; keep tops at + z≈290). Ceiling: cube scale (6,6,0.2) at z≈290 — spawn it LAST so you can + screenshot the interior while open-topped. One window: leave a gap in a + wall by using two shorter wall segments with a 120×120 cm opening at + sill height 90 cm. + VERIFY top-down + interior screenshots; door/window heights sane. + +**Phase 2 — lighting (the point of this recipe)** + +- INTENT kill the sun; interior reads as night. + VALUES if the template level has a DirectionalLight: intensity → 0.05 lux + temperature 4,300 K, pitch −20°, yawed to rake through the window (this + is the "moon"). SkyLight intensity scale down to ≈0.05–0.1. +- INTENT warm practical. + VALUES PointLight at lamp position (e.g. corner table, z≈120): + 800 lumens (or ≈64 candela), temperature 2,700 K, attenuation radius + 600 cm, source radius 10 cm (softer shadows). +- INTENT cool window rim. + VALUES SpotLight outside the window aimed through it: 2,000 lumens, + 6,500–8,000 K if faking without moon; skip if the directional moon + already rakes through visibly. Inner/outer cone 25°/50°. +- INTENT exposure for dim interior. + VALUES PPV unbound, Manual, EV100 = 4.5; fog: ExponentialHeightFog + density 0.015 + volumetric fog on; practical's volumetric scattering + intensity 2–4 so the lamp glows. + VERIFY screenshot from a corner at z≈160: warm pool around lamp, cool + slash from window, deep-but-readable shadows. The warm/cool split IS the + deliverable — iterate intensities (never move both at once) until it + reads. 💾 + +**Phase 3 — dress & deliver** + +- VALUES if Starter Content exists, swap primitives: `/Game/StarterContent/ + Props/SM_TableRound`, `SM_Chair`, `SM_Lamp_Ceiling`, materials + `M_Wood_Pine` on floor, `M_Basic_Wall` on walls. Otherwise assign + MaterialInstances with warm-neutral base colors to primitives. + VERIFY final `HighResShot 3840x2160`, vision-check, 💾, report. + +--- + +## Recipe C — Golden-hour cinematic still (camera craft) + +Brief: "make it golden hour and give me a cinematic shot of " + +**Phase 1 — relight for golden hour** + +- VALUES DirectionalLight: intensity 12,000 lux, temperature 3,200 K, + pitch −8°, yaw set so the sun is 30–60° OFF the camera axis behind the + subject (rim + long shadows — never light flat from the camera). + Fog density 0.02 + volumetric fog, sun volumetric scattering 2–6. + PPV EV100 = 11. + VERIFY screenshot: long shadows, warm rim on subject edges. + +**Phase 2 — the camera** + +- INTENT a framed CineCamera, not a viewport eyeball. + DISCOVER CineCameraActor spawn + property-set; viewport-pilot or + camera-view capability if advertised. + VALUES position: subject-distance by lens — 85 mm at 400–600 cm for a + prop/character subject; height 120–160 cm; aim so subject sits on a + thirds intersection, horizon in upper or lower third (not center). + Focal 85 mm, aperture f/2.0, manual focus distance = measured + camera→subject distance (compute from the two locations). + VERIFY screenshot THROUGH this camera (pilot it / set viewport to its + view first — confirm by a cheap `HighResShot 1` before the big one). + +**Phase 3 — deliver** + +- VALUES `HighResShot 3840x2160` (or user's target res) through the + piloted camera. For a sequence/turntable instead of a still: this needs a + Level Sequence with a Camera Cut track + Movie Render Queue — treat as + its own task; warn about first-render shader-compile stall. + VERIFY read file, `vision_analyze`: rim light present? focus falloff on + the right plane? horizon off-center? Iterate at most twice, then deliver + MEDIA: path + what was changed. 💾 + +--- + +## Recipe D — Import an asset and populate the scene + +Brief: "here's model.fbx / a Fab asset — put a ring of them around the fountain" + +**Phase 1 — import** + +- INTENT asset lands in `/Game/Imported` with no dialog stall. + DISCOVER import capability (if none advertised: custom toolset wrapping + `unreal.AssetImportTask` with `automated=True` — see tool-surface.md; + the `automated` flag is what prevents a modal import dialog from + freezing the whole MCP loop). + VALUES destination `/Game/Imported`, save=true. + VERIFY asset-exists query on the resulting long package name; spawn one + instance at origin, screenshot, check scale against 180 cm yardstick — + DCC exports are routinely 100× off (meters vs centimeters). Fix by + actor scale or reimport with unit conversion. + +**Phase 2 — populate** + +- INTENT ring of N instances around a center C. + VALUES for i in 0..N−1: angle θ=360·i/N, position = C + (r·cosθ, + r·sinθ, 0) with r = fountain radius + clearance (e.g. 350 cm), yaw = + θ+90° so each faces the center (or +270° to face outward — check one + instance first and LOOK). Spawn one, verify facing, then loop the rest + one call at a time. + VERIFY count query matches N; screenshot from above (camera z≈1,500 + looking down) for spacing; eye-level screenshot for scale. 💾 + +**Phase 3 — deliver** + +- Report: asset path, N instances with label prefix, level saved, + overview + eye-level captures as MEDIA paths. + +--- + +## When a recipe's capability is missing + +If discovery shows no shipped tool for a step (no import tool, no +console-exec for HighResShot, etc.): + +1. Say so plainly; don't fake the step. +2. Offer the custom-toolset path (tool-surface.md) — a 20-line Python + toolset usually covers the gap; it needs `RefreshTools` + session + restart to appear. +3. Or hand the user the one-liner to run in the editor's Python/console + themselves, with exact text. + +Never claim a phase done without its VERIFY evidence. The user can't check +the editor for you — the screenshots are the ground truth they see. diff --git a/optional-skills/creative/unreal-mcp/references/scene-craft.md b/optional-skills/creative/unreal-mcp/references/scene-craft.md new file mode 100644 index 0000000000000..26af405ddff2d --- /dev/null +++ b/optional-skills/creative/unreal-mcp/references/scene-craft.md @@ -0,0 +1,271 @@ +# Unreal MCP — Scene-Craft Cheat Sheet + +The numbers and conventions that make a scene read as *good* instead of +merely present. Sources: physical/photographic standards (stable), UE +conventions (stable), and practical ranges from production use (marked ≈). +UE-version-specific defaults drift; when a live schema or editor value +disagrees with this sheet, trust the editor and patch the sheet. + +## Units & Conventions (bedrock — memorize) + +| Thing | Convention | +|---|---| +| Distance | 1 Unreal Unit = **1 cm** | +| Axes | **Z-up**, X-forward, Y-right (left-handed) | +| Rotation | Rotator in **degrees**: Roll (around X), Pitch (around Y), Yaw (around Z) | +| Color | Linear RGBA, each channel 0–1 (`FLinearColor`) | +| Light color | Prefer `use_temperature` + Kelvin over tinting RGB | +| Scale | Multiplier per axis (1,1,1 = authored size) | + +Directional-light aiming: the ROTATION points the light. Pitch −90° = sun +straight overhead (noon); pitch −5° to −15° = sun grazing the horizon +(golden hour); yaw picks the compass direction the light travels toward. + +### Human-scale reference (sanity-check every layout against these) + +| Reference | Size (cm) | +|---|---| +| Eye height (standing) | 160–175 | +| Door | 200–210 tall × 80–90 wide | +| Ceiling, residential | 240–300 | +| One building storey | 300–400 | +| Counter/desk height | 75–110 | +| Chair seat | 45 | +| Stair riser / tread | ≈18 / ≈28 | +| UE default mannequin | ≈180 tall | +| Car | ≈450 long × 180 wide × 145 tall | + +If a "house" door comes out 400 cm tall, the scene reads as toy/giant. +Always place one human-scale object early as a yardstick. + +## Content Paths + +| Root | Meaning | +|---|---| +| `/Game/...` | Project content (Content/ folder) | +| `/Engine/...` | Engine-shipped content, present in every project | +| `/Script/Module.Class` | Native classes (e.g. `/Script/Engine.PointLight`) | + +Long package name form: `/Game/Props/SM_Chair.SM_Chair` (package.object). + +Always-available engine primitives (great for blocking before real assets): + + /Engine/BasicShapes/Cube.Cube (100×100×100 cm at scale 1) + /Engine/BasicShapes/Sphere.Sphere (100 cm diameter) + /Engine/BasicShapes/Cylinder.Cylinder (100 cm ⌀ × 100 cm) + /Engine/BasicShapes/Cone.Cone + /Engine/BasicShapes/Plane.Plane (100×100 cm, single-sided) + +Their default material is plain grey; assign a MaterialInstance for anything +presentational. If the project has Starter Content, useful packs live under +`/Game/StarterContent/` (Props, Materials like `M_Basic_Wall`, `M_Wood_Pine`, +`M_Metal_Steel`, Particles). Query before assuming Starter Content exists. + +Common actor classes for spawning: `StaticMeshActor`, `PointLight`, +`SpotLight`, `RectLight`, `DirectionalLight`, `SkyLight`, +`ExponentialHeightFog`, `SkyAtmosphere`, `VolumetricCloud`, +`PostProcessVolume`, `CameraActor`, `CineCameraActor`, `PlayerStart`. + +## Lighting — physically based values + +UE5 lights default to physical units (directional in lux, point/spot in +candela or lumens, exposure in EV100). Use real-world values; they +compose correctly with exposure instead of fighting it. + +### Sun (DirectionalLight, lux) + +| Condition | Intensity (lux) | Pitch | Temperature | +|---|---|---|---| +| Noon, clear | 75,000–120,000 | −60° to −90° | 5,500–6,000 K | +| Afternoon | 40,000–75,000 | −30° to −50° | 5,000–5,500 K | +| Golden hour | 5,000–20,000 | −5° to −15° | 2,800–3,500 K | +| Overcast | 5,000–20,000 (soft) | −45° ± | 6,500–7,500 K | +| Blue hour / dusk | 10–100 | −2° to +5° | 8,000–12,000 K | +| Full-moon night | 0.05–0.3 | −30° to −60° | 4,000–4,500 K (cool-blue read comes from exposure + grade) | + +Overcast: also drop directional shadow contrast (soften via larger source +angle) and let the sky light dominate. + +### Sky light + +One SkyLight per level, Real-Time Capture (SLS Captured Scene) when using +SkyAtmosphere — it then tracks the sun automatically. Don't stack multiple +sky lights; don't leave a stale static capture after big lighting changes +(recapture if not real-time). + +### Local lights (point/spot/rect) + +Rules of thumb in lumens (candela ≈ lumens/(4π) for a point light): + +| Source | Lumens | +|---|---| +| Candle flame | 10–15 (≈1,850 K) | +| 40 W incandescent equiv. | 450 (2,700 K) | +| 60 W equiv. | 800 (2,700–3,000 K) | +| 100 W equiv. | 1,600 (3,000 K) | +| Bright ceiling fixture | 2,000–4,000 (3,000–4,000 K) | +| Fluorescent tube / office | 2,500–5,000 (4,000–5,000 K) | +| Streetlight (sodium) | 5,000–15,000 (≈2,000 K, orange) | +| Car headlight | 1,000–1,500 each (4,300–6,000 K) | +| Campfire | 100–300, flicker (1,700–2,000 K) | + +Spot cone: inner 20–35°, outer 40–60° for a natural falloff. Attenuation +radius: keep tight (a few hundred cm for practicals) — giant radii cost +performance and flatten the scene. Cast-shadow off for pure fill lights. + +### Color temperature vocabulary (Kelvin) + +1,700–1,900 match/candle · 2,700 warm bulb · 3,200 tungsten studio · +3,500 golden hour · 4,300 moonlight-read · 5,600 daylight/flash · +6,500 overcast · 7,500–10,000 shade/blue hour. +Warm subject + cool ambient (or inverse) is the cheapest way to make a +shot read "lit" instead of "flat". + +### Exposure (PostProcessVolume — the #1 "why is it black/white" knob) + +Auto-exposure fights deterministic lighting reads. For agent-driven work, +prefer **manual exposure** in a PPV: + +1. Spawn/locate a PostProcessVolume, set **Infinite Extent (Unbound) = true**. +2. Metering Mode = Manual, then set Exposure Compensation ≈ 0 and EV100 to + the scene value: + +| Scene | EV100 | +|---|---| +| Bright sun exterior | 14–16 | +| Overcast exterior | 11–13 | +| Golden hour | 10–12 | +| Bright interior (day, windows) | 7–9 | +| Dim practical-lit interior | 4–6 | +| Street at night | 2–4 | +| Moonlit exterior | −2 to 0 | + +If you keep auto-exposure instead (Metering: Auto Histogram), clamp it: +Min/Max EV100 within ±2 of the target so it can't swim. Symptom table: +scene renders black with lights present → EV100 too high for the light +levels; blown white → EV100 too low. + +### Global illumination & reflections + +UE5 defaults: **Lumen** GI + Lumen reflections, no lightmass bake needed — +lighting is live; just keep "Allow Static Lighting" defaults alone. +**Critical: Lumen GI only considers lights with Movable mobility.** Spawned +lights can default to Stationary/Static and then contribute nothing to GI — +set Mobility = Movable explicitly on every light you place, and check +mobility first when "GI isn't working". Metal/mirror surfaces read +correctly only with something to reflect: give the scene a sky and +surroundings before judging materials. + +### Fog & atmosphere + +- **SkyAtmosphere** for a physically-plausible sky (sun disk, horizon + gradient); pairs with directional light "Atmosphere Sun Light = true". +- **ExponentialHeightFog**: density default 0.02. ≈ Practical ranges: + 0.005–0.015 subtle depth cue · 0.02–0.05 moody/morning · 0.05–0.2 heavy. + Enable **Volumetric Fog** on it for light shafts through it; then dial + per-light "Volumetric Scattering Intensity" (1–10) on the key lights. +- **VolumetricCloud** for real sky clouds (exterior only; costs GPU). +- Night sky: drop fog density, add faint cool fill (skylight at low + intensity) so shadows aren't pure black. + +## Mood recipes (compact) + +| Mood | Sun/Key | Sky | Fog | EV100 | Grade notes | +|---|---|---|---|---|---| +| Crisp noon | 100k lux, pitch −70°, 5,800 K | Real-time capture | 0.005 | 15 | Neutral | +| Golden hour | 10k lux, pitch −8°, 3,200 K | Real-time capture | 0.02 + volumetric | 11 | Warm key, long shadows: rotate yaw for rim/side light | +| Overcast | 10k lux soft, 7,000 K | Dominant | 0.01 | 12 | Low contrast, saturation carries color | +| Night, moonlit | 0.25 lux, 4,300 K + practicals ≈800 lm 2,700 K | Very low | 0.015 | −1 to 1 | Cool ambient vs warm practicals | +| Horror interior | No sun; 1–2 practicals, hard shadows | Minimal | 0.03–0.06 volumetric | 4–5 | Single motivated key, deep blacks | +| Sci-fi corridor | Rect lights 2,000 lm 6,500–8,000 K + colored accents | None | 0.02 volumetric | 6 | Complementary accent pair (cyan/orange) | + +## Camera & framing (CineCameraActor) + +Use CineCameraActor (not plain Camera) for anything presentational — it has +real filmback/lens/DoF controls. + +| Intent | Focal length | Aperture | +|---|---|---| +| Establishing / interior wide | 18–28 mm | f/5.6–8 | +| Neutral "human eye" | 35–50 mm | f/4 | +| Portrait / subject isolation | 85–135 mm | f/1.4–2.8 | +| Compression (stacked background) | 100–200 mm | f/2.8–5.6 | + +- Filmback: default 16:9 digital film (≈23.76 × 13.365 mm) is fine; leave it. +- Focus: Manual focus distance = distance camera→subject in cm; shallow DoF + needs long lens + wide aperture + subject far from background. +- Placement: eye-level ≈ 155–170 cm for neutral shots; below ≈ 100 cm = + heroic/imposing; high angle = diminishing. Keep the horizon off + dead-center; put subjects on thirds. Slight camera pitch (−2° to −8°) + usually beats perfectly level for interiors. +- Aspect/eye candy: enable camera's "Constrain Aspect Ratio" for clean + letterboxed stills. + +## Capture & render + +- **Viewport screenshot**: console `HighResShot 1` (viewport res), + `HighResShot 2` (2×), or `HighResShot 3840x2160`. Output: + `/Saved/Screenshots//`. Filenames auto-increment + (`HighresScreenshot00000.png`). +- To frame from a camera: pilot/possess the CineCamera (or set viewport to + its view) before HighResShot; verify by screenshotting first at 1×. +- **Movie Render Queue** (MRQ) is the quality path for finals/sequences: + needs a Level Sequence with the camera bound (Camera Cut track); renders + PNG/EXR sequences or stills at arbitrary resolution with anti-aliasing + temporal sample counts. First render after opening a project stalls on + shader compilation — warn the user, don't declare it hung. +- Judge results by looking: read the file back and `vision_analyze` every + capture against the brief. + +## Editor Python quick reference + +Custom toolsets and any shipped Python-execution tool speak the `unreal` +module. Canonical entry points (verify names against the live editor — +Epic migrates libraries to subsystems over time): + +```python +import unreal + +# Actors (EditorActorSubsystem supersedes EditorLevelLibrary for these) +eas = unreal.get_editor_subsystem(unreal.EditorActorSubsystem) +actors = eas.get_all_level_actors() +actor = eas.spawn_actor_from_class(unreal.PointLight, unreal.Vector(0, 0, 200)) +mesh_a = eas.spawn_actor_from_object( + unreal.EditorAssetLibrary.load_asset("/Engine/BasicShapes/Cube.Cube"), + unreal.Vector(0, 0, 50)) +actor.set_actor_label("Key Light") +actor.set_actor_location(unreal.Vector(100, 0, 250), False, True) +actor.set_actor_rotation(unreal.Rotator(0, -30, 45), True) # roll, pitch, yaw +eas.destroy_actor(actor) + +# Assets +unreal.EditorAssetLibrary.does_asset_exist("/Game/Props/SM_Chair") +unreal.EditorAssetLibrary.list_assets("/Game/Props", recursive=True) +unreal.EditorAssetLibrary.save_directory("/Game", only_if_is_dirty=True) + +# Level save +les = unreal.get_editor_subsystem(unreal.LevelEditorSubsystem) +les.save_current_level() + +# Undo-friendly mutation +with unreal.ScopedEditorTransaction("Agent: dress set") as trans: + ... # property edits inside are one undo step + +# Import (FBX/textures) +task = unreal.AssetImportTask() +task.filename = "/abs/path/model.fbx" +task.destination_path = "/Game/Imported" +task.automated = True # suppresses the import dialog — critical for MCP +task.save = True +unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks([task]) + +# Editor property access works on anything reflected +light_comp = actor.get_component_by_class(unreal.PointLightComponent) +light_comp.set_editor_property("intensity", 800.0) +light_comp.set_editor_property("use_temperature", True) +light_comp.set_editor_property("temperature", 2700.0) +``` + +`set_editor_property`/`get_editor_property` with snake_case names is the +universal fallback when a dedicated setter doesn't exist — property names +match what the Details panel shows (spaces removed). diff --git a/optional-skills/creative/unreal-mcp/references/tool-surface.md b/optional-skills/creative/unreal-mcp/references/tool-surface.md new file mode 100644 index 0000000000000..cee5ee1d7a34d --- /dev/null +++ b/optional-skills/creative/unreal-mcp/references/tool-surface.md @@ -0,0 +1,274 @@ +# Unreal MCP — Tool Surface Reference + +How Epic's editor-embedded MCP server organizes, advertises, and executes +tools, and how to extend the surface when the shipped tools run out. +Everything here is against UE 5.8's experimental plugin (id +`ModelContextProtocol`); expect drift between engine versions — the live +`describe_toolset` schema always outranks this file. + +## Architecture in one paragraph + +The **Unreal MCP** plugin hosts the HTTP server inside the editor process +(default `http://127.0.0.1:8000/mcp`, loopback-only, no auth, HTTP + SSE +only — no stdio/WebSocket). It implements the protocol but ships no tools of +its own. Tools come from **Toolsets** — classes deriving from +`UToolsetDefinition` (C++) or `unreal.ToolsetDefinition` (Python) — collected +at startup by the **Toolset Registry** subsystem (sibling plugin, +auto-enabled). Epic's shipped toolsets are delivered by a separate toolset +provider plugin (**AllToolsets**); project plugins and Game Feature Plugins +can contribute more. Unreal MCP wraps every registered tool call as an MCP +Tool. Execution is **serialized onto the game thread** — one tool call at a +time, editor UI blocked while each runs. + +## Tool-search mode (the default contract) + +With `Enable Tool Search` on (default), `tools/list` advertises exactly three +meta-tools: + +| Meta-tool | Args | Returns | +|---|---|---| +| `list_toolsets` | — | Registered toolset names + descriptions | +| `describe_toolset` | toolset name | JSON Schemas for every tool in that toolset | +| `call_tool` | toolset/tool name + arguments object | The tool's result, same turn | + +Discipline: + +- `list_toolsets` once per session; re-run only after `RefreshTools`, plugin + changes, or reconnect. +- `describe_toolset` before first use of any toolset. Parameter names, types, + and required fields come from the schema — never from memory or this file. +- Results: primitive results arrive wrapped as `{"result": ...}` (CVar + `ModelContextProtocol.WrapPODToolResultsInObject`, default true). + Structured results serialize with field-level schema. +- Errors come back as tool-call errors with the engine-side message — read + them; they usually name the offending parameter or missing asset. + +Eager mode (`Enable Tool Search` off) advertises every tool individually. +Under Hermes that means each tool becomes `mcp_unreal_engine_` at +session start, and `hermes mcp configure unreal-engine` can prune the list. +Schema payload grows with every registered toolset, and tool authors are told +NOT to rely on eager advertising — stay in tool-search mode unless a very +small fixed surface is wanted. + +## call_tool dispatch semantics + +Confirmed against Epic's own agent-facing skill for this server: + +- `call_tool` takes `toolset_name`, `tool_name`, and an `arguments` object + matching the schema from `describe_toolset`. The result returns on the + same turn. +- Tool identities are effectively dotted: `BlueprintTools.create`, + `SequencerTools.create_level_sequence`, + `LiveCodingToolset.CompileLiveCoding` — they are dispatched server-side + and never appear as native MCP tools while tool search is on. +- Top-level dispatch (omitting `toolset_name`) is reserved for tools + registered directly on the MCP server — and is rejected for `call_tool` + itself. + +## Shipped toolsets + +The registry is project-dependent; treat this as orientation, not contract — +`describe_toolset` on the live server is the only source of truth for tool +names and schemas. Epic's plugin pack describes the shipped surface as +"hundreds of tools across 30+ toolsets": actors, blueprints, materials, +Niagara, Control Rigs, Sequencer, State Trees, widgets, Gameplay Ability +System, automation testing, Live Coding. + +Toolset names confirmed in Epic's docs and Epic's own Claude Code skill pack: + +| Toolset | Scope | +|---|---| +| `SceneTools` | Scene-level queries and edits | +| `ActorTools` | Inspect/modify actors: transforms, labels, parent-child relationships, components | +| `MaterialInstanceTools` | Create/configure material instances | +| `MaterialTools` | Material assets | +| `ObjectTools` | Generic UObject property inspection/editing | +| `BlueprintTools` | Blueprint creation/editing (e.g. `BlueprintTools.create`) | +| `StaticMeshTools` | Static mesh asset operations | +| `LevelTools` | Level operations | +| `SequencerTools` | Level Sequences (e.g. `create_level_sequence`) | +| `LiveCodingToolset` | C++ Live Coding recompile (`CompileLiveCoding` blocks until the compile finishes and surfaces compiler diagnostics) | +| `AgentSkillToolset` | Project-registered Agent Skills (see below) | +| `GASToolsets` (plugin, C++) | Gameplay Ability System attributes — ships disabled; enabling prompts an experimental-feature warning | + +Known gap: the shipped toolsets contain **no mesh-modelling tools** — you +can spawn/place/instance existing meshes but not author new geometry. The +supported route to parametric geometry is a custom Python toolset wrapping +**Geometry Script** (`UDynamicMesh`: append box/cylinder/sphere, booleans, +then `Create New Static Mesh Asset from Mesh` to bake an `SM_` asset). For +organic/sculpted meshes, model in Blender (`blender-mcp` skill) and import. + +First-session move: `list_toolsets`, then `describe_toolset` each group you +plan to use, and keep those schemas in working memory for the session. + +## Project Agent Skills (AgentSkillToolset) + +Projects and plugins can register **Agent Skills** — named instruction +bundles for project-specific conventions and workflows (naming schemes, +folder layout, canonical multi-step sequences). They are NOT listed by +`list_toolsets`; reach them through `call_tool`: + +1. `AgentSkillToolset.ListSkills` → names + descriptions of registered + skills. +2. If one matches the task, `AgentSkillToolset.GetSkills` on it → full + instructions, then FOLLOW THEM — a project skill exists precisely + because the project's way differs from the obvious way, and it takes + precedence over this skill's generic defaults. + +Check at the start of unfamiliar work in any project, not just once ever. + +## Seeing your work: screenshots and captures + +An agent that can't see the viewport is flying blind. In order of preference: + +1. **A shipped screenshot/viewport tool, if the registry advertises one** — + check `list_toolsets`/`describe_toolset` output for viewport, screenshot, + or thumbnail capture tools and use those (they return the image through + MCP directly). +2. **Console command via any shipped console/exec tool**: `HighResShot 1` + writes the current viewport to + `/Saved/Screenshots//` on the EDITOR host's + filesystem. `HighResShot 3840x2160` for fixed resolution, + `HighResShot 2` for 2× viewport. Read the file back with `read_file`/ + `vision_analyze` (same machine) — remember paths resolve on the editor + host. +3. **Custom toolset escape hatch** (below) exposing + `unreal.AutomationLibrary.take_high_res_screenshot()` or viewport + capture, when nothing shipped covers it. + +Always `vision_analyze` the capture and art-direct against the brief before +declaring a milestone done. + +## Plugin configuration reference + +Editor Preferences > General > Model Context Protocol: + +| Property | Default | Notes | +|---|---|---| +| Auto Start Server | `false` | Turn on for frictionless sessions | +| Server Port Number | `8000` | Change on conflict; mirror in Hermes config url | +| Server URL Path | `/mcp` | Same | +| Enable Tool Search | `true` | Keep on (see above) | + +Console commands (editor console, backtick): + +| Command | Effect | +|---|---| +| `ModelContextProtocol.StartServer [port]` | Start server (optional port override) | +| `ModelContextProtocol.StopServer` | Stop server, close all sessions | +| `ModelContextProtocol.RefreshTools` | Re-poll toolset providers — run after authoring/hot-reload/Game-Feature activation | +| `ModelContextProtocol.GenerateClientConfig ` | Write client config files (ClaudeCode/Cursor/VSCode/Gemini/Codex) — NOT used for Hermes | + +Command-line flags for launching the editor pre-configured: +`-ModelContextProtocolStartServer` (force start regardless of preference), +`-ModelContextProtocolPort=N`. + +Console variables: + +| CVar | Default | Notes | +|---|---|---| +| `ModelContextProtocol.WrapPODToolResultsInObject` | `true` | Primitive results wrapped as `{"result": ...}` | +| `ModelContextProtocol.AudioResultOggFormat` | `false` | OGG instead of WAV for audio results | +| `ModelContextProtocol.ProgressIntervalSeconds` | `1.0` | Min interval between progress notifications | +| `ModelContextProtocol.PaginationPageSize` | `0` | 0 = no pagination of list results | +| `ModelContextProtocol.EnableAnalytics` | `true` | Epic telemetry gate | + +## Debugging the connection + +- **Output Log** at editor startup logs bind address/port/path — first stop + when the server seems absent. Port-in-use and missing-dependency failures + surface here. +- **Log verbosity:** `Log LogModelContextProtocol Verbose` in the editor + console. +- **MCP Inspector** (`npx @modelcontextprotocol/inspector`, point at + `http://127.0.0.1:8000/mcp`, transport "Streamable HTTP") lists every + advertised tool with schemas and offers form-based invocation — isolates + "server broken" from "agent calling it wrong". +- **After Live Coding / authoring:** connected clients can hold stale + schemas. `ModelContextProtocol.RefreshTools`, then reconnect (new Hermes + session) if schemas still look stale. + +## Extending the surface: custom toolsets + +When shipped tools don't cover an operation, the supported path is authoring +a project toolset — NOT trying to smuggle arbitrary code through unrelated +tools. Python toolsets are first-class and hot-loadable, so prefer them. + +### Python toolset (recommended) + +Any enabled plugin's `Content/Python/` directory (or the project's) can hold +toolset modules; the registry discovers them at startup. Shape (mirrors +Epic's shipped `ActorTools`): + +```python +import unreal +import toolset_registry + +@unreal.uclass() +class MySceneTools(unreal.ToolsetDefinition): + """One-line toolset description — surfaces to the agent in list_toolsets.""" + + @toolset_registry.tool_call + @staticmethod + def take_viewport_screenshot(filename: str, width: int, height: int) -> str: + """Capture the active viewport to Saved/Screenshots. + + Args: + filename: Base filename without extension. + width: Output width in pixels. + height: Output height in pixels. + + Returns: + Absolute path the screenshot will be written to. + """ + ... +``` + +Conventions that matter (they generate the schema the agent sees): + +- `@unreal.uclass()` on the class; inherit `unreal.ToolsetDefinition`. +- Class docstring = toolset description; write it for an agent audience. +- Each advertised function: `@toolset_registry.tool_call` + `@staticmethod`. + Functions without the decorator stay private. +- Type hints (`str`, `bool`, `list[str]`, `unreal.Actor`, dataclasses) drive + the JSON Schema; Google-style docstrings (`Args:`/`Returns:`) become the + parameter descriptions. Write them with API-surface care. +- Small, single-responsibility tools with structured return types beat + mega-tools returning prose. Data leaves the tool via its RETURN VALUE — + `print()`/stdout go to the UE log, not back over MCP. + +After authoring: `ModelContextProtocol.RefreshTools` in the editor console, +then re-`list_toolsets` from Hermes. Users on Claude Code can scaffold with +the `create-toolset` skill from Epic's `unreal-mcp` plugin pack; the +conventions above still apply. + +### C++ toolset + +Derive from `UToolsetDefinition`, mark the class `UCLASS(BlueprintType, +Hidden)`, expose static `UFUNCTION(meta = (AICallable))` methods; doc +comments reflect into schemas. Use only when Python can't reach the API, +when reflected `USTRUCT` signatures are needed, or when the Python boundary +cost matters. Exclude a function with `meta = (AIIgnore)`. Live Coding +propagates edited function bodies, but NEW `UFUNCTION`s require a full +editor restart. There is also a direct-registration path +(`IModelContextProtocolTool` + `IModelContextProtocolModule::AddTool()`) for +runtime-shaped tools; caller owns deregistration. + +## Runtime and cooked builds + +The server is editor-hosted by default but not editor-only: runtime modules +can host it in cooked builds via `IModelContextProtocolModule::StartServer()`. +The Toolset Registry adapter (and the three tool-search meta-tools) are +editor-only, though — cooked-build tools must be registered explicitly +through `AddTool()` and are advertised eagerly. MCP Resources and Prompts are +not advertised by any shipping toolset. + +## Known limitations (5.8, experimental) + +- HTTP + SSE transports only; loopback-only listener; non-loopback `Origin` + headers rejected; no auth layer. Not safe beyond the local machine. +- Serial game-thread execution: overlapping calls unsupported; editor UI + blocks during each call. +- Feature-incomplete by Epic's own labeling; APIs and data formats subject + to change without notice. +- Live Coding does not propagate new `UFUNCTION` declarations.