{"id":"6c110fb0-3714-431a-8c31-b1101e85d4df","slug":"unrealengine-bridge","kind":"mcp","name":"UnrealEngine-Bridge","summary":"Claude Code inside Unreal - the MCP layer Epic doesn't ship: real editor Python, viewport perception + scene diffing, one-command lighting moods, normal-aware spatial reasoning, and a lossless UE<->X3","owner":"JosephOIbrahim","repoUrl":"https://github.com/JosephOIbrahim/UnrealEngine_Bridge","license":"MIT","licenseTier":"permissive","trust":"community","stars":3,"match":null,"fewStars":true,"format":"mcp","createdAt":"2026-02-21T00:33:33.000Z","addedAt":"2026-10-21T04:15:58.520Z","logoUrl":"https://avatars.githubusercontent.com/u/6024731?u=be2dc91c993af08580da3f8452137e5e386b6f14&v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["developer-tools","cloud-infra","memory-knowledge"],"tags":["agentic-ai","ai-agent","anthropic","claude","claude-code","gamedev","llm-tools","python"],"runtime":"python","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-21T04:19:19.715Z","sample":false,"url":"https://conduits.dev/tools/unrealengine-bridge","language":"Python","transports":["stdio"],"install":{"pypi":"ue-bridge"},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"# UnrealEngine Bridge\n\n[![Release](https://img.shields.io/github/v/release/JosephOIbrahim/UnrealEngine_Bridge?sort=semver)](https://github.com/JosephOIbrahim/UnrealEngine_Bridge/releases/latest)\n[![CI](https://github.com/JosephOIbrahim/UnrealEngine_Bridge/actions/workflows/ci.yml/badge.svg)](https://github.com/JosephOIbrahim/UnrealEngine_Bridge/actions/workflows/ci.yml)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n[![Unreal Engine 5.8](https://img.shields.io/badge/Unreal%20Engine-5.8-0a0a14)](https://www.unrealengine.com/)\n[![Python 3.11+](https://img.shields.io/badge/Python-3.11%2B-3776AB?logo=python&logoColor=white)](https://www.python.org/)\n\n**Claude Code, working inside your Unreal editor.** This bridge gives Claude the abilities Epic's own MCP doesn't ship: run real editor Python, see the viewport continuously, light scenes with one command, reason about space with surface normals, and stay honest about every result.\n\n**58 MCP tools · 20 mounted by default · 635 tests** · [Changelog](CHANGELOG.md) · [Security](SECURITY.md)\n\n---\n\n## The one-paragraph story\n\nUE 5.8 ships Epic's official **Unreal MCP** — 830 tools once you enable `AllToolsets`. That's the commodity control plane: spawn, transform, materials, Blueprints, Sequencer. **So this bridge stopped competing and specialized.** We live-probed Epic's entire surface, wrote the verdict for every one of our tools into [EPIC_MCP_MATRIX.md](docs/EPIC_MCP_MATRIX.md), and flipped the default: you get our **differentiated core** out of the box, Epic handles the basics, and the 36 overlapping tools stay one env var away.\n\n---\n\n## How it works\n\nTwo MCP servers, one editor. Claude uses both.\n\n```mermaid\ngraph LR\n    subgraph Claude Code\n        A[MCP Client]\n    end\n\n    subgraph B1[\"This bridge — Python\"]\n        B[MCP Server<br/><i>FastMCP · stdio</i>]\n        B --> C[Tiered Registry<br/><i>core: 20 · full: 56</i>]\n        C --> D[Remote Control Bridge<br/><i>httpx · circuit breaker</i>]\n    end\n\n    subgraph Unreal Editor\n        E[Remote Control API<br/><i>localhost:30010</i>]\n        E --> F[Editor Runtime]\n        G[ViewportPerception<br/><i>localhost:30011</i>]\n        G --> F\n        H[Epic Unreal MCP<br/><i>localhost:8000 · UE 5.8</i>]\n        H --> F\n    end\n\n    A -- stdio --> B\n    A -. HTTP /mcp .-> H\n    D -- HTTP --> E\n    C -. viewport capture .-> G\n\n    style A fill:#5CFFDB,color:#05050D\n    style B fill:#5CFFDB,color:#05050D\n    style C fill:#5CFFDB,color:#05050D\n    style D fill:#12121F,color:#fff\n    style E fill:#FFCC33,color:#05050D\n    style F fill:#12121F,color:#fff\n    style G fill:#FFCC33,color:#05050D\n    style H fill:#FFCC33,color:#05050D\n```\n\n**Who does what:**\n\n| Job | Server |\n|---|---|\n| Spawn / transform / materials / Blueprints / Sequencer | **Epic's MCP** (UE 5.8, `AllToolsets`) |\n| Editor Python, console, lighting moods, cloner, perception, spatial reasoning | **This bridge** |\n| Works on UE 5.7 (no Epic MCP there) | **This bridge**, with `UE_MCP_PROFILE=full` |\n\n---\n\n## Quick start\n\n**You need:** UE 5.7 or 5.8 · Python 3.11+ · the Remote Control API plugin enabled (ships with the engine).\n\n**1 — Install**\n\n```bash\ngit clone https://github.com/JosephOIbrahim/UnrealEngine_Bridge.git\ncd UnrealEngine_Bridge\npip install -e .\n```\n\n**2 — Open the project**\n\nOpen `UnrealEngine_Bridge.uproject`. Check the Output Log for *\"Remote Control Web Server started\"* — that's `localhost:30010` up.\n\nOn **UE 5.8**, the `ModelContextProtocol` and `AllToolsets` plugins self-enable (they're staged `Optional` in the project). Start Epic's server from the console: `ModelContextProtocol.StartServer`.\n\n**3 — Connect Claude Code**\n\nThe repo ships a ready [`.mcp.json`](.mcp.json) — open the repo folder in Claude Code and both servers are configured. For other projects, copy it and fix the path.\n\n**4 — Try it**\n\n> *\"Set golden hour and snap the crates to the terrain.\"*\n\nClaude uses `ue_apply_mood_preset` and `ue_snap_to_ground` — two things no other UE MCP can do.\n\n**Want the commodity tools from this bridge instead of Epic's?** Set the env var and restart the server:\n\n```json\n\"unreal\": { \"command\": \"python\", \"args\": [\"-m\", \"ue_mcp.mcp_server\"], \"env\": { \"UE_MCP_PROFILE\": \"full\" } }\n```\n\n---\n\n## What's mounted by default (the core 20)\n\nThese are the tools Epic's MCP does **not** have — verified against all 830 of theirs ([the matrix](docs/EPIC_MCP_MATRIX.md)).\n\n### 🐍 Editor Python & console (2)\n| Tool | What it does |\n|---|---|\n| `ue_execute_python` | **The escape hatch.** Run real Python in the editor with the full `unreal` API (AST-sandboxed). Epic's `execute_tool_script` can't even `import unreal`. |\n| `ue_console_command` | Run console commands with structured output parsing. No console exec exists anywhere in Epic's surface. |\n\n### 👁 Perception (4)\n| Tool | What it does |\n|---|---|\n| `ue_viewport_percept` | One frame + camera, selection, and scene metadata in a single call |\n| `ue_viewport_watch` | Continuous capture at a set rate — Claude keeps watching |\n| `ue_viewport_diff` | Two-snapshot structural diff: what moved, appeared, disappeared |\n| `ue_viewport_config` | Resolution / format / rate for the capture pipeline |\n\n### 🌅 Lighting & atmosphere (5)\n| Tool | What it does |\n|---|---|\n| `ue_apply_mood_preset` | One command = coordinated sun + fog + clouds + color grade |\n| `ue_blend_mood_presets` | Interpolate two moods (t 0→1) for in-between looks |\n| `ue_set_time_of_day` | Hour 0–24 → sun elevation, azimuth, color, intensity |\n| `ue_setup_sky_atmosphere` | Build or update the whole sky rig, idempotently |\n| `ue_list_mood_presets` | See the built-in cinematic presets |\n\n### 📐 Spatial reasoning (4)\n| Tool | What it does |\n|---|---|\n| `ue_ground_trace` | Trace down at (x, y): hit point, **surface normal**, distance, actor. Epic's `trace_world` returns a bare distance. |\n| `ue_snap_to_ground` | Drop an actor onto the surface, optionally tilting to the slope |\n| `ue_spatial_query` | Nearest-N, AABB overlap, combined bounds, box contents |\n| `ue_get_actor_hierarchy` | The full recursive attachment tree in one call |\n\n### 🧩 Scene gaps Epic left open (3)\n| Tool | What it does |\n|---|---|\n| `ue_duplicate_actor` | Clone a level actor with an offset (no Epic equivalent) |\n| `ue_get_world_info` | Streaming levels, world settings, game mode |\n| `ue_create_cloner` | ClonerEffector procedural instancing, with read-back-verified configuration |\n\n### ❤️ Bridge health (2)\n| Tool | What it does |\n|---|---|\n| `ue_status` | Is the editor up? Is Remote Control reachable? |\n| `ue_health_check` | Version, uptime, circuit breaker, per-call metrics, active tool profile, **Epic MCP reachability** |\n\n---\n\n## The commodity tier (36 tools, `UE_MCP_PROFILE=full`)\n\nEpic's MCP covers these — the matrix cites the exact equivalent for every row. They stay in the codebase, tested and honest, for UE 5.7 or as a fallback:\n\n| Module | Tools |\n|---|---|\n| Actors | spawn · delete · list · set_transform · get_bounds |\n| Scene | actor details · scene query · component details |\n| Blueprints | create · add component · set properties · CDO defaults · compile · list components · spawn |\n| Materials | create instance · set / get parameters · assign to slot |\n| Sequencer | create sequence · play/scrub · bind actor · keyframe |\n| Level | save · info · load |\n| Assets | find · create material · delete |\n| Mograph | Niagara system · PCG graph |\n| Properties | get / set any UObject property |\n| Editor | focus actor · select actors |\n| Spatial | measure (distance / extent) |\n\n**Experimental tier** (`UE_MCP_PROFILE=all`): `ue_undo` / `ue_redo` — honest not-implemented slots. The UE Python API has no scriptable editor-transaction route; these say so instead of pretending.\n\n---\n\n## 🧊 UE ↔ X3D round-trip harness  *(new in v0.3.0)*\n\n**Hand Claude a level as open X3D it can read, edit, and validate — then write it back losslessly.** A thin, fully-tested slice: no live editor needed to prove an edit is safe.\n\n**The one invariant it defends:**\n\n```\ndeserialize( serialize( level ) )  ==  level\n```\n\nLossless round trip, plus malformed edits rejected **on paper** before any mutation. If both hold, the bridge is trustworthy — if either fails, nothing else matters.\n\n| Piece | Job |\n|---|---|\n| **Coordinate crux** | UE (Z-up, LH, cm) ↔ X3D (Y-up, RH, m) as one orthonormal basis `B` (det −1). `B⁻¹ = Bᵀ`, so the round trip is exact *by construction* — picking the right `B` is calibration, not correctness. |\n| **Closed grammar** | An X3D node set small enough to hand a model *whole* and validate against. UE specifics (mesh · mobility · folder · parent) ride in `Metadata*`. |\n| **Validate boundary** | Out-of-grammar nodes, dangling `USE`, NaN/∞, wrong arity, non-X3D root — all die here, before the editor is touched. |\n| **Apply seam** | Diffs two scenes into typed ops (spawn · transform · material · reparent) that emit `ue_execute_python`. |\n| **Preview** | The same X3D drops into a browser via X_ITE — free. |\n\n```python\nfrom x3d_bridge import Actor, serialize, deserialize, validate\n\nx3d = serialize([Actor(guid=\"Rock_01\", mesh=\"/Game/Meshes/SM_Rock\",\n                       t=(420.0, 0.0, 155.0))])\nok, errors = validate(x3d)     # the paper boundary\nlevel = deserialize(x3d)       # lossless\n```\n\n**Status — honest, per the house rule:**\n\n- ✅ **55 tests** — round-trip · validation battery · apply-op sequence — all green\n- ✅ Basis `B` independently re-derived and confirmed three ways\n- ⏳ `B` is *analytic* — **not yet calibrated against a live glTF export** (round-trip is basis-agnostic, so this is fidelity-only, never correctness)\n- ⏳ A library today — **not yet exposed as MCP tools**\n\n---\n\n## Why trust the results? Honesty as architecture\n\nThis bridge's tools **cannot silently lie** — that's enforced, not promised:\n\n- **Exec-simulated codegen tests.** Every generated editor script is compiled and executed against a strict fake `unreal` module in CI. Phantom APIs raise. Dropped arguments fail a sentinel gate. Hard-coded success prints fail honesty contracts.\n- **Read-back verification.** Writes that UE can silently ignore (cloner layout names) are read back before being reported \"applied\".\n- **Honest statuses.** The viewport fallback reports `capture_status: timeout` instead of an empty image with `success: true`. Not-implemented tools say \"not implemented\".\n- **635 tests**, including scripted-failure contracts for every historical lying-tool bug.\n\n```mermaid\ngraph TB\n    subgraph Python layer\n        MCP[\"MCP Server<br/><code>ue_mcp/mcp_server.py</code>\"]\n        Reg[\"Tiered Registry<br/><i>core / full / all</i>\"]\n        Tools[\"Tool Modules (14)<br/><code>ue_mcp/tools/</code>\"]\n        Val[\"Validation & AST Sandbox<br/><code>_validation.py</code>\"]\n        RC[\"Remote Control Bridge<br/><code>remote_control/</code>\"]\n        Met[\"Metrics & Circuit Breaker\"]\n    end\n\n    subgraph Test layer\n        Sim[\"Exec-Sim Harness<br/><i>strict fake unreal · honesty contracts</i>\"]\n    end\n\n    subgraph Unreal Editor\n        RCAPI[\"Remote Control API<br/><i>REST · :30010</i>\"]\n        VP[\"ViewportPerception plugin<br/><i>GPU readback · :30011</i>\"]\n        EPIC[\"Epic Unreal MCP<br/><i>HTTP+SSE · :8000 · UE 5.8</i>\"]\n    end\n\n    MCP --> Reg --> Tools\n    Tools --> Val\n    Tools --> RC --> Met\n    RC -- HTTP --> RCAPI\n    Tools -. capture .-> VP\n    Sim -. gates every generated script .-> Tools\n\n    style MCP fill:#5CFFDB,color:#05050D\n    style Reg fill:#5CFFDB,color:#05050D\n    style Tools fill:#5CFFDB,color:#05050D\n    style Sim fill:#12121F,color:#fff\n    style RCAPI fill:#FFCC33,color:#05050D\n    style VP fill:#FFCC33,color:#05050D\n    style EPIC fill:#FFCC33,color:#05050D\n```\n\n### Resilience\n\n| Pattern | Detail |\n|---|---|\n| **Circuit breaker** | CLOSED → OPEN (5 failures) → HALF_OPEN (30s, 1 probe) → CLOSED |\n| **Connection pooling** | 10 max connections, 5 keepalive |\n| **Timeout** | 10s adaptive default |\n| **Atomic file I/O** | `tempfile` + `os.replace` (NTFS-safe) |\n| **Python sandbox** | AST validation blocking `os`, `subprocess`, `open`, `getattr`, dunders |\n\n### Network surface & trust model\n\nEverything is **localhost, single trusted operator**: Remote Control (`:30010`), ViewportPerception (`:30011`, **off by default**), and — on 5.8 — Epic's MCP (`:8000`, no auth, serial on the game thread; Epic's EULA §6(e) governs what your LLM provider may do with transmitted data). Don't expose any of them to untrusted networks.\n\nFull trust model and hardening guidance: **[SECURITY.md](SECURITY.md)**.\n\n---\n\n## Project structure\n\n```\nUnrealEngine_Bridge/\n├── ue_mcp/                        # MCP server package\n│   ├── mcp_server.py              # FastMCP entry point (stdio) + health tools\n│   ├── metrics.py                 # Telemetry + observability\n│   ├── ue_logging.py              # Structured JSON logging\n│   └── tools/                     # 14 modules · 56 tools · tiered registry in __init__.py\n├── remote_control/                # UE5 HTTP bridge (circuit breaker, codegen, polling)\n├── x3d_bridge/                    # UE↔X3D round-trip harness (coords · grammar · validate · apply)\n├── usd_bridge/                    # USD file I/O package (parked, out of the ship path)\n├── Plugins/\n│   ├── UEBridge/                  # Editor panel, file watcher (C++)\n│   └── ViewportPerception/        # GPU readback + HTTP endpoint (C++)\n├── docs/\n│   ├── EPIC_MCP_MATRIX.md         # Retirement contract-of-record (probe-grounded)\n│   └── epic_mcp/                  # Raw probe captures of Epic's 830-tool surface\n├── scripts/probe_epic_mcp.py      # Re-probe Epic's surface per engine version\n├── tests/                         # 635 tests, incl. tests/exec_sim/ + tier gates\n├── smoke_live.py                  # Live-editor smoke harness (on-demand)\n└── .mcp.json                      # Two-server config: this bridge + Epic's MCP\n```\n\n---\n\n## Development\n\n**Run the tests**\n\n```bash\npip install -e \".[dev]\"\npython -m pytest -q                # 635 tests\n```\n\n**Lint**\n\n```bash\nruff check ue_mcp/ remote_control/ tests/\n```\n\n**Add a tool** (each step is checked by CI)\n\n1. Create `ue_mcp/tools/your_module.py` with a `register(server, ue)` function\n2. Decorate tools with `@server.tool(...)`; validate inputs via `_validation.py`\n3. Wire the module into `ue_mcp/tools/__init__.py` — **and classify the tool in `TIERS`** (an unclassified tool fails CI)\n4. Add a sentinel entry in `tests/exec_sim/registry.py` (a missing entry fails CI)\n5. Add tests\n\n---\n\n## License\n\nReleased under the [MIT License](LICENSE) — Copyright (c) 2026 Joseph Ibrahim.\n"}