diff --git a/CHANGELOG.md b/CHANGELOG.md index e353d2c..e3a1320 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,75 @@ All notable changes to this project are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.2.0] - 2026-07-02 — the Epic MCP era + +> **Version renumbering:** the public line continues from v0.1.1. An internal +> 2.x numbering existed in-repo but was never released; it was retired in this +> release. `pyproject` now reads the single version source +> (`ue_mcp/__version__.py`) via hatchling, and tags must match it. + +UE 5.8 ships Epic's official **Unreal MCP** (830 tools across 52 toolsets with +`AllToolsets`). This release repositions the bridge around what Epic does NOT +ship, with every decision grounded in a live probe of the real surface. + +### Changed — the retirement flip +- **Tiered tool registry** at the `register_all_tools()` seam. Default profile + is `core`: **20 tools mounted** (the differentiated layer + `ue_status`/ + `ue_health_check`). `UE_MCP_PROFILE=full` remounts the 36 Epic-covered + commodity tools; `all` adds the two honest not-implemented slots. +- `ue_health_check` now reports the active profile, mount counts, and Epic + MCP (`:8000`) reachability. +- `ue_undo`/`ue_redo` return an explicit not-implemented error (no editor + round-trip): no scriptable editor-transaction route exists in the UE Python + API, and they previously probed nonexistent APIs on every call. + +### Added +- **`docs/EPIC_MCP_MATRIX.md`** — the retirement contract-of-record: verdicts + for all 58 tools against the probed Epic surface, plus raw captures + (`docs/epic_mcp/`) and a repeatable prober (`scripts/probe_epic_mcp.py`). +- **Exec-simulating test harness** (`tests/exec_sim/`): a strict fake `unreal` + module, per-tool sentinel registry, compile/exec/sentinel gates, and + scripted-failure honesty contracts. Proven red on the pre-fix tree — every + failure mapped 1:1 to a known bug (see `tests/exec_sim/README.md`). + **580 tests total** (was 415). +- `tests/test_registry_tiers.py` pins the matrix arithmetic and profile + semantics; tier/tool drift fails CI in both directions. +- Two-server `.mcp.json` (this bridge over stdio + Epic's server on `:8000`); + `ModelContextProtocol` + `AllToolsets` staged `Optional: true` in the + `.uproject` (no-ops on 5.7, self-enables on 5.8). + +### Fixed +- **All 11 confirmed bugs** from the 2026-06-11 hand-verified review: the + level-actor resolver (asset-API misuse in delete/transform), `true`→`True` + NameError, spawn-blueprint label indent, `load_level` false success, + `focus_actor` unconditional success, phantom `is_hidden()`, unescaped + `find_assets` patterns, the cloner arg-discard, the viewport-fallback race, + material-parameter API-family mixing, and the wheel-killing shim import. +- Adversarial-verification findings fixed pre-merge: a stale-frame regression + in the perception fallback, wrong ClonerEffector class names for 5.7 (all + cloner writes are now read-back-verified), a working `CAMERA ALIGN` focus + route, and `find_assets` escaping moved into the codegen chokepoint. +- `metrics` uptime rounding (coarse-clock test flake). + +### Removed +- **The legacy Translators questionnaire runtime**: the game-flow state + machine entry points, trivia UI, Blueprint relay component, and the + repo-root runner. The `usd_bridge/` package is parked in-repo, out of the + ship path. + +### Engine & packaging (UE 5.8) +- **Retargeted to UE 5.8** (`EngineAssociation` 5.8; both plugins + `EngineVersion` 5.8.0). The `ViewportPerception` `FrameProducer` was ported + for 5.8's changed `OnBackBufferReadyToPresent` signature (`FTextureRHIRef` + → `ISlateViewportProvider&`) behind a dual-version `#if`, so it still + compiles against 5.7. Verified: the editor target builds clean on 5.8. +- **Marketplace-ready plugins**: branded 128×128 icons, storefront-grade + `.uplugin` metadata, explicit `Category` specifiers on every + Blueprint-exposed property (required for engine-module distribution), and a + clean `RunUAT BuildPlugin -installed` package. Draft storefront copy in + `docs/FAB_LISTING.md`. The plugins can now be installed engine-wide and + picked from Edit → Plugins in any 5.8 project. + ## [0.1.0] - 2026-05-30 First tagged release. Demo-ready on a maintainer's machine; see **Status** below for what stands between this and a distributable plugin. diff --git a/INVENTORY.md b/INVENTORY.md index edd434f..cc7cd5f 100644 --- a/INVENTORY.md +++ b/INVENTORY.md @@ -1,4 +1,4 @@ -# UnrealEngine_Bridge v2.1.0 — Inventory +# UnrealEngine_Bridge v0.2.0 — Inventory MCP server bridging Claude Code to Unreal Engine 5 via Remote Control API. @@ -6,11 +6,10 @@ MCP server bridging Claude Code to Unreal Engine 5 via Remote Control API. | Metric | Count | |--------|-------| -| MCP Tools | 56 | -| Tool Modules | 11 | -| Tests | 222 | -| Python source files | 27 | -| Total lines (Python) | ~8,000 | +| MCP Tools | 58 (56 registry + 2 server) | +| Mounted by default | 20 (`core` profile; `UE_MCP_PROFILE=full` remounts the commodity tier) | +| Tool Modules | 14 | +| Tests | 580 | ## Dependencies diff --git a/Plugins/UEBridge/Config/FilterPlugin.ini b/Plugins/UEBridge/Config/FilterPlugin.ini new file mode 100644 index 0000000..ccebca2 --- /dev/null +++ b/Plugins/UEBridge/Config/FilterPlugin.ini @@ -0,0 +1,8 @@ +[FilterPlugin] +; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and +; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively. +; +; Examples: +; /README.txt +; /Extras/... +; /Binaries/ThirdParty/*.dll diff --git a/Plugins/UEBridge/Resources/Icon128.png b/Plugins/UEBridge/Resources/Icon128.png index 62545c2..541cf84 100644 Binary files a/Plugins/UEBridge/Resources/Icon128.png and b/Plugins/UEBridge/Resources/Icon128.png differ diff --git a/Plugins/UEBridge/Source/UEBridgeRuntime/Private/UEBridgeSubsystem.cpp b/Plugins/UEBridge/Source/UEBridgeRuntime/Private/UEBridgeSubsystem.cpp index f94f3c0..1e3858f 100644 --- a/Plugins/UEBridge/Source/UEBridgeRuntime/Private/UEBridgeSubsystem.cpp +++ b/Plugins/UEBridge/Source/UEBridgeRuntime/Private/UEBridgeSubsystem.cpp @@ -14,6 +14,7 @@ #include "Serialization/JsonSerializer.h" #include "Internationalization/Regex.h" #include "Engine/Engine.h" +#include "Misc/EngineVersion.h" // === LIFECYCLE === @@ -147,7 +148,7 @@ void UUEBridgeSubsystem::SendAcknowledge() Content = UpdateUsdaVariant(Content, TEXT("message_type"), TEXT("ack")); Content = UpdateUsdaAttribute(Content, TEXT("Ack"), TEXT("ready"), TEXT("true"), false); - Content = UpdateUsdaAttribute(Content, TEXT("Ack"), TEXT("ue_version"), FString(ENGINE_VERSION_STRING), true); + Content = UpdateUsdaAttribute(Content, TEXT("Ack"), TEXT("ue_version"), FEngineVersion::Current().ToString(), true); Content = UpdateUsdaAttribute(Content, TEXT("Ack"), TEXT("project"), TEXT("UnrealEngineBridge"), true); Content = UpdateUsdaAttribute(Content, TEXT("Ack"), TEXT("timestamp"), Timestamp, true); @@ -167,7 +168,7 @@ void UUEBridgeSubsystem::SendAcknowledge() TSharedPtr AckObj = MakeShared(); AckObj->SetBoolField(TEXT("ready"), true); - AckObj->SetStringField(TEXT("ue_version"), FString(ENGINE_VERSION_STRING)); + AckObj->SetStringField(TEXT("ue_version"), FEngineVersion::Current().ToString()); AckObj->SetStringField(TEXT("project"), TEXT("UnrealEngineBridge")); JsonObj->SetObjectField(TEXT("ack"), AckObj); diff --git a/Plugins/UEBridge/Source/UEBridgeRuntime/Public/UEBridgeSubsystem.h b/Plugins/UEBridge/Source/UEBridgeRuntime/Public/UEBridgeSubsystem.h index cb1e45a..12a8fc8 100644 --- a/Plugins/UEBridge/Source/UEBridgeRuntime/Public/UEBridgeSubsystem.h +++ b/Plugins/UEBridge/Source/UEBridgeRuntime/Public/UEBridgeSubsystem.h @@ -13,6 +13,11 @@ #include "BridgeTypes.h" #include "UEBridgeSubsystem.generated.h" +// Used by reference only in method signatures below; forward-declared so the +// header is self-contained under non-unity / installed-plugin builds (a unity +// build would otherwise pull FJsonObject in transitively). +class FJsonObject; + UCLASS() class UEBRIDGERUNTIME_API UUEBridgeSubsystem : public UGameInstanceSubsystem diff --git a/Plugins/UEBridge/Source/UEBridgeRuntime/UEBridgeRuntime.Build.cs b/Plugins/UEBridge/Source/UEBridgeRuntime/UEBridgeRuntime.Build.cs index b1cdf93..f90e709 100644 --- a/Plugins/UEBridge/Source/UEBridgeRuntime/UEBridgeRuntime.Build.cs +++ b/Plugins/UEBridge/Source/UEBridgeRuntime/UEBridgeRuntime.Build.cs @@ -11,17 +11,8 @@ public UEBridgeRuntime(ReadOnlyTargetRules Target) : base(Target) { PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; - PublicIncludePaths.AddRange(new string[] - { - Path.Combine(ModuleDirectory, "Public"), - Path.Combine(ModuleDirectory, "Public", "UI") - }); - - PrivateIncludePaths.AddRange(new string[] - { - Path.Combine(ModuleDirectory, "Private"), - Path.Combine(ModuleDirectory, "Private", "UI") - }); + PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "Public")); + PrivateIncludePaths.Add(Path.Combine(ModuleDirectory, "Private")); // Core runtime dependencies — ship in packaged builds PublicDependencyModuleNames.AddRange(new string[] @@ -50,7 +41,7 @@ public UEBridgeRuntime(ReadOnlyTargetRules Target) : base(Target) PrivateDependencyModuleNames.Add("Projects"); // Version definition — single source of truth - PublicDefinitions.Add("BRIDGE_VERSION=TEXT(\"2.1.0\")"); + PublicDefinitions.Add("BRIDGE_VERSION=TEXT(\"0.2.0\")"); // USD support flag: editor-only via pxr, runtime uses text-based USDA parser if (Target.bBuildEditor) diff --git a/Plugins/UEBridge/UEBridge.uplugin b/Plugins/UEBridge/UEBridge.uplugin index 181e18f..19f74fd 100644 --- a/Plugins/UEBridge/UEBridge.uplugin +++ b/Plugins/UEBridge/UEBridge.uplugin @@ -1,11 +1,11 @@ { "FileVersion": 3, "Version": 1, - "VersionName": "2.1.0", - "EngineVersion": "5.7.0", - "FriendlyName": "UE Bridge", - "Description": "Agentic AI bridge for Unreal Engine. Provides MCP tool integration, file-based bridge protocol with USD transport, behavioral signal collection, and Remote Control API access for Claude Code.", - "Category": "Gameplay", + "VersionName": "0.2.0", + "EngineVersion": "5.8.0", + "FriendlyName": "UE Bridge — AI Editor Control", + "Description": "Give Claude Code and other MCP clients editor-side control of Unreal Engine: an in-editor status panel, a directory-watch bridge, and Remote Control API access. Pairs with the UnrealEngine Bridge MCP server for AI-driven scene building, lighting, spatial reasoning, and viewport perception.", + "Category": "Code Plugins", "CreatedBy": "Joseph Ibrahim", "CreatedByURL": "https://github.com/JosephOIbrahim", "DocsURL": "https://github.com/JosephOIbrahim/UnrealEngine_Bridge#readme", @@ -14,18 +14,21 @@ "CanContainContent": true, "IsBetaVersion": false, "IsExperimentalVersion": false, + "Installed": false, "EnabledByDefault": false, "SupportedTargetPlatforms": ["Win64"], "Modules": [ { "Name": "UEBridgeRuntime", "Type": "Runtime", - "LoadingPhase": "Default" + "LoadingPhase": "Default", + "WhitelistPlatforms": ["Win64"] }, { "Name": "UEBridgeEditor", "Type": "Editor", - "LoadingPhase": "Default" + "LoadingPhase": "Default", + "WhitelistPlatforms": ["Win64"] } ], "Plugins": [ diff --git a/Plugins/ViewportPerception/Config/FilterPlugin.ini b/Plugins/ViewportPerception/Config/FilterPlugin.ini new file mode 100644 index 0000000..ccebca2 --- /dev/null +++ b/Plugins/ViewportPerception/Config/FilterPlugin.ini @@ -0,0 +1,8 @@ +[FilterPlugin] +; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and +; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively. +; +; Examples: +; /README.txt +; /Extras/... +; /Binaries/ThirdParty/*.dll diff --git a/Plugins/ViewportPerception/Resources/Icon128.png b/Plugins/ViewportPerception/Resources/Icon128.png new file mode 100644 index 0000000..5d72a44 Binary files /dev/null and b/Plugins/ViewportPerception/Resources/Icon128.png differ diff --git a/Plugins/ViewportPerception/Source/ViewportPerception/Private/FrameProducer.cpp b/Plugins/ViewportPerception/Source/ViewportPerception/Private/FrameProducer.cpp index e41b848..1a7710f 100644 --- a/Plugins/ViewportPerception/Source/ViewportPerception/Private/FrameProducer.cpp +++ b/Plugins/ViewportPerception/Source/ViewportPerception/Private/FrameProducer.cpp @@ -7,6 +7,9 @@ #include "RenderingThread.h" #include "Framework/Application/SlateApplication.h" #include "RHISurfaceDataConversion.h" +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 8 +#include "Slate/SlateViewportProvider.h" +#endif FFrameProducer::FFrameProducer() : FrameCounter(0) @@ -72,13 +75,20 @@ void FFrameProducer::SetThrottleInterval(double Seconds) MinCaptureInterval = FMath::Max(Seconds, 0.01); // Cap at 100fps } -void FFrameProducer::OnFrameBufferReady(SWindow& SlateWindow, const FTextureRHIRef& FrameBuffer) +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 8 +void FFrameProducer::OnFrameBufferReady(SWindow& SlateWindow, ISlateViewportProvider& ViewportProvider) { + FRHITexture* FrameBuffer = ViewportProvider.GetBackBufferResource(); +#else +void FFrameProducer::OnFrameBufferReady(SWindow& SlateWindow, const FTextureRHIRef& FrameBufferRef) +{ + FRHITexture* FrameBuffer = FrameBufferRef.GetReference(); +#endif // Runs on the render thread. All readback state below is render-thread-only, // so no synchronization is needed for it. We NEVER block the render thread: // a copy is enqueued on one present and drained on a later one once the GPU // has finished, instead of a synchronous ReadSurfaceData stall. - if (!PixelBus || !FrameBuffer.IsValid()) + if (!PixelBus || !FrameBuffer) { return; } diff --git a/Plugins/ViewportPerception/Source/ViewportPerception/Private/FrameProducer.h b/Plugins/ViewportPerception/Source/ViewportPerception/Private/FrameProducer.h index 275e71e..69c1b88 100644 --- a/Plugins/ViewportPerception/Source/ViewportPerception/Private/FrameProducer.h +++ b/Plugins/ViewportPerception/Source/ViewportPerception/Private/FrameProducer.h @@ -7,8 +7,12 @@ #include "CoreMinimal.h" #include "RHI.h" #include "RHIGPUReadback.h" +#include "Runtime/Launch/Resources/Version.h" class FPixelBus; +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 8 +class ISlateViewportProvider; +#endif class FFrameProducer { @@ -29,8 +33,15 @@ class FFrameProducer bool IsActive() const { return bActive; } private: - /** Called on the render thread when the backbuffer is ready. */ - void OnFrameBufferReady(SWindow& SlateWindow, const FTextureRHIRef& FrameBuffer); + /** Called on the render thread when the backbuffer is ready. + * 5.8 changed OnBackBufferReadyToPresent's second param from the raw + * texture to an ISlateViewportProvider — both signatures are kept so the + * plugin compiles against 5.7 and 5.8 from the same source. */ +#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 8 + void OnFrameBufferReady(SWindow& SlateWindow, ISlateViewportProvider& ViewportProvider); +#else + void OnFrameBufferReady(SWindow& SlateWindow, const FTextureRHIRef& FrameBufferRef); +#endif FDelegateHandle DelegateHandle; FPixelBus* PixelBus = nullptr; diff --git a/Plugins/ViewportPerception/Source/ViewportPerception/Public/PerceptionTypes.h b/Plugins/ViewportPerception/Source/ViewportPerception/Public/PerceptionTypes.h index 4c1eef2..ae9c5a3 100644 --- a/Plugins/ViewportPerception/Source/ViewportPerception/Public/PerceptionTypes.h +++ b/Plugins/ViewportPerception/Source/ViewportPerception/Public/PerceptionTypes.h @@ -20,13 +20,13 @@ struct FPerceptionCamera { GENERATED_BODY() - UPROPERTY(BlueprintReadOnly) + UPROPERTY(BlueprintReadOnly, Category = "Perception") FVector Location = FVector::ZeroVector; - UPROPERTY(BlueprintReadOnly) + UPROPERTY(BlueprintReadOnly, Category = "Perception") FRotator Rotation = FRotator::ZeroRotator; - UPROPERTY(BlueprintReadOnly) + UPROPERTY(BlueprintReadOnly, Category = "Perception") float FOV = 90.0f; }; @@ -37,31 +37,31 @@ struct FPerceptionMetadata GENERATED_BODY() // Camera - UPROPERTY(BlueprintReadOnly) + UPROPERTY(BlueprintReadOnly, Category = "Perception") FPerceptionCamera Camera; // Viewport - UPROPERTY(BlueprintReadOnly) + UPROPERTY(BlueprintReadOnly, Category = "Perception") FIntPoint ViewportSize = FIntPoint::ZeroValue; - UPROPERTY(BlueprintReadOnly) + UPROPERTY(BlueprintReadOnly, Category = "Perception") FString ViewportType; // Scene context - UPROPERTY(BlueprintReadOnly) + UPROPERTY(BlueprintReadOnly, Category = "Perception") TArray SelectedActors; - UPROPERTY(BlueprintReadOnly) + UPROPERTY(BlueprintReadOnly, Category = "Perception") FString MapName; - UPROPERTY(BlueprintReadOnly) + UPROPERTY(BlueprintReadOnly, Category = "Perception") int32 ActorCount = 0; // Timing - UPROPERTY(BlueprintReadOnly) + UPROPERTY(BlueprintReadOnly, Category = "Perception") float DeltaTime = 0.0f; - UPROPERTY(BlueprintReadOnly) + UPROPERTY(BlueprintReadOnly, Category = "Perception") float FPS = 0.0f; }; @@ -76,29 +76,29 @@ struct FPerceptionPacket TArray ImageData; /** Image dimensions after resize/encode. */ - UPROPERTY(BlueprintReadOnly) + UPROPERTY(BlueprintReadOnly, Category = "Perception") int32 Width = 0; - UPROPERTY(BlueprintReadOnly) + UPROPERTY(BlueprintReadOnly, Category = "Perception") int32 Height = 0; /** Format used for encoding. */ - UPROPERTY(BlueprintReadOnly) + UPROPERTY(BlueprintReadOnly, Category = "Perception") EPerceptionImageFormat Format = EPerceptionImageFormat::JPEG; /** Monotonically increasing frame counter. */ - UPROPERTY(BlueprintReadOnly) + UPROPERTY(BlueprintReadOnly, Category = "Perception") int64 FrameNumber = 0; /** Platform time at capture. */ - UPROPERTY(BlueprintReadOnly) + UPROPERTY(BlueprintReadOnly, Category = "Perception") double Timestamp = 0.0; /** Scene metadata collected at capture time. */ - UPROPERTY(BlueprintReadOnly) + UPROPERTY(BlueprintReadOnly, Category = "Perception") FPerceptionMetadata Metadata; /** True if this packet contains valid data. */ - UPROPERTY(BlueprintReadOnly) + UPROPERTY(BlueprintReadOnly, Category = "Perception") bool bValid = false; }; diff --git a/Plugins/ViewportPerception/ViewportPerception.uplugin b/Plugins/ViewportPerception/ViewportPerception.uplugin index 3b94d0f..069f4c5 100644 --- a/Plugins/ViewportPerception/ViewportPerception.uplugin +++ b/Plugins/ViewportPerception/ViewportPerception.uplugin @@ -1,21 +1,28 @@ { "FileVersion": 3, "Version": 1, - "VersionName": "2.1.0", - "EngineVersion": "5.7.0", - "FriendlyName": "Viewport Perception", - "Description": "AI visual awareness for UE5 — captures the editor viewport and serves perception packets (frame + metadata) to the MCP bridge.", - "Category": "Editor", + "VersionName": "0.2.0", + "EngineVersion": "5.8.0", + "FriendlyName": "Viewport Perception — AI Visual Awareness", + "Description": "Gives AI assistants eyes inside the Unreal editor. Captures the level viewport on the render thread via async GPU readback and serves perception packets (encoded frame + camera, selection, and scene metadata) over a localhost HTTP endpoint for the UnrealEngine Bridge MCP server.", + "Category": "Code Plugins", "CreatedBy": "Joseph Ibrahim", + "CreatedByURL": "https://github.com/JosephOIbrahim", + "DocsURL": "https://github.com/JosephOIbrahim/UnrealEngine_Bridge#readme", + "MarketplaceURL": "", + "SupportURL": "https://github.com/JosephOIbrahim/UnrealEngine_Bridge/issues", "CanContainContent": false, + "IsBetaVersion": false, + "IsExperimentalVersion": false, + "Installed": false, "EnabledByDefault": false, - "EditorCustomVirtualPath": "", - "SupportedTargetPlatforms": [ "Win64" ], + "SupportedTargetPlatforms": ["Win64"], "Modules": [ { "Name": "ViewportPerception", "Type": "EditorNoCommandlet", - "LoadingPhase": "PostEngineInit" + "LoadingPhase": "PostEngineInit", + "WhitelistPlatforms": ["Win64"] } ], "Plugins": [ diff --git a/README.md b/README.md index ada864d..c54e333 100644 --- a/README.md +++ b/README.md @@ -3,18 +3,24 @@ [![Release](https://img.shields.io/github/v/release/JosephOIbrahim/UnrealEngine_Bridge?sort=semver)](https://github.com/JosephOIbrahim/UnrealEngine_Bridge/releases/latest) [![CI](https://github.com/JosephOIbrahim/UnrealEngine_Bridge/actions/workflows/ci.yml/badge.svg)](https://github.com/JosephOIbrahim/UnrealEngine_Bridge/actions/workflows/ci.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) -[![Unreal Engine 5.7](https://img.shields.io/badge/Unreal%20Engine-5.7-0a0a14)](https://www.unrealengine.com/) +[![Unreal Engine 5.8](https://img.shields.io/badge/Unreal%20Engine-5.8-0a0a14)](https://www.unrealengine.com/) [![Python 3.11+](https://img.shields.io/badge/Python-3.11%2B-3776AB?logo=python&logoColor=white)](https://www.python.org/) -**Give Claude Code full control of Unreal Engine 5.7.** Spawn actors, tweak materials, light scenes, reason about space, capture the viewport, keyframe animations — all through natural language via the [Model Context Protocol](https://modelcontextprotocol.io/). +**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. -58 MCP tools (20 mounted by default · commodity tier via `UE_MCP_PROFILE=full`) | 14 tool modules | 570+ tests | Python 3.11+ · [Changelog](CHANGELOG.md) · [Security](SECURITY.md) +**58 MCP tools · 20 mounted by default · 580 tests** · [Changelog](CHANGELOG.md) · [Security](SECURITY.md) -> **Epic MCP era:** UE 5.8 ships an official [Unreal MCP](docs/EPIC_MCP_MATRIX.md) covering the commodity control plane (830 tools with AllToolsets). This bridge now mounts its **differentiated core** by default — arbitrary editor Python, console exec, lighting moods, ClonerEffector mograph, continuous perception + scene diffing, normal-aware spatial reasoning, resilience — and keeps the 36 Epic-covered tools available behind `UE_MCP_PROFILE=full`. Dispositions: [EPIC_MCP_MATRIX.md](docs/EPIC_MCP_MATRIX.md). +--- + +## The one-paragraph story + +UE 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. --- -## How It Works +## How it works + +Two MCP servers, one editor. Claude uses both. ```mermaid graph LR @@ -22,45 +28,51 @@ graph LR A[MCP Client] end - subgraph Python + subgraph B1["This bridge — Python"] B[MCP Server
FastMCP · stdio] - B --> C[Tool Modules
14 modules · 56 tools] + B --> C[Tiered Registry
core: 20 · full: 56] C --> D[Remote Control Bridge
httpx · circuit breaker] end - subgraph Unreal Engine 5.7 + subgraph Unreal Editor E[Remote Control API
localhost:30010] E --> F[Editor Runtime] G[ViewportPerception
localhost:30011] G --> F + H[Epic Unreal MCP
localhost:8000 · UE 5.8] + H --> F end A -- stdio --> B + A -. HTTP /mcp .-> H D -- HTTP --> E C -. viewport capture .-> G style A fill:#5CFFDB,color:#05050D style B fill:#5CFFDB,color:#05050D - style C fill:#12121F,color:#fff + style C fill:#5CFFDB,color:#05050D style D fill:#12121F,color:#fff style E fill:#FFCC33,color:#05050D style F fill:#12121F,color:#fff style G fill:#FFCC33,color:#05050D + style H fill:#FFCC33,color:#05050D ``` ---- +**Who does what:** -## Quick Start +| Job | Server | +|---|---| +| Spawn / transform / materials / Blueprints / Sequencer | **Epic's MCP** (UE 5.8, `AllToolsets`) | +| Editor Python, console, lighting moods, cloner, perception, spatial reasoning | **This bridge** | +| Works on UE 5.7 (no Epic MCP there) | **This bridge**, with `UE_MCP_PROFILE=full` | -### Prerequisites +--- -| Requirement | Version | -|---|---| -| Unreal Engine | 5.7 | -| Python | 3.11+ | -| Remote Control API plugin | Enabled in UE5 (ships with engine) | +## Quick start -### 1. Clone & Install +**You need:** UE 5.7 or 5.8 · Python 3.11+ · the Remote Control API plugin enabled (ships with the engine). + +**1 — Install** ```bash git clone https://github.com/JosephOIbrahim/UnrealEngine_Bridge.git @@ -68,197 +80,146 @@ cd UnrealEngine_Bridge pip install -e . ``` -### 2. Open the UE5 Project +**2 — Open the project** -1. Open `UnrealEngine_Bridge.uproject` in Unreal Engine 5.7 -2. Verify the **Remote Control API** plugin is enabled (Edit > Plugins) -3. Confirm `localhost:30010` is reachable (check Output Log for "Remote Control Web Server started") +Open `UnrealEngine_Bridge.uproject`. Check the Output Log for *"Remote Control Web Server started"* — that's `localhost:30010` up. -### 3. Connect Claude Code +On **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`. -Add this to your Claude Code MCP configuration (`~/.claude/settings.json` or project `.mcp.json`): +**3 — Connect Claude Code** -```json -{ - "mcpServers": { - "unreal": { - "command": "python", - "args": ["-m", "ue_mcp.mcp_server"], - "cwd": "/path/to/UnrealEngine_Bridge" - } - } -} -``` +The 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. + +**4 — Try it** -### 4. Start Building +> *"Set golden hour and snap the crates to the terrain."* -Open Claude Code and try: +Claude uses `ue_apply_mood_preset` and `ue_snap_to_ground` — two things no other UE MCP can do. -> *"Spawn a cube at the origin, make it red, and rotate it 45 degrees"* +**Want the commodity tools from this bridge instead of Epic's?** Set the env var and restart the server: -Claude will use `ue_spawn_actor`, `ue_create_material`, `ue_assign_material`, and `ue_set_transform` automatically. +```json +"unreal": { "command": "python", "args": ["-m", "ue_mcp.mcp_server"], "env": { "UE_MCP_PROFILE": "full" } } +``` --- -## MCP Tools Reference +## What's mounted by default (the core 20) -### Actors (6 tools) -| Tool | Description | -|---|---| -| `ue_spawn_actor` | Create an actor by class name with position/rotation | -| `ue_delete_actor` | Remove an actor from the level | -| `ue_list_actors` | List all level actors, optionally filtered by class | -| `ue_set_transform` | Set location, rotation, and/or scale | -| `ue_duplicate_actor` | Clone an actor with an offset | -| `ue_get_actor_bounds` | Get axis-aligned bounding box | - -### Scene Understanding (4 tools) -| Tool | Description | -|---|---| -| `ue_get_actor_details` | Full inspection: class, transform, components, tags, parent | -| `ue_query_scene` | Multi-filter search (class, tag, name pattern, spatial proximity) | -| `ue_get_component_details` | Deep component info (mesh assets, materials, light properties) | -| `ue_get_actor_hierarchy` | Recursive parent-child attachment tree | +These are the tools Epic's MCP does **not** have — verified against all 830 of theirs ([the matrix](docs/EPIC_MCP_MATRIX.md)). -### Spatial Reasoning (4 tools) -| Tool | Description | +### 🐍 Editor Python & console (2) +| Tool | What it does | |---|---| -| `ue_ground_trace` | Line-trace down at (x, y) to find the ground — hit point, normal, distance, actor | -| `ue_snap_to_ground` | Drop an actor onto the surface beneath it, optionally tilting to the slope | -| `ue_spatial_query` | Nearest-N, AABB overlap, combined-bounds footprint, or box-contents queries | -| `ue_measure` | Straight-line distance between actors, or extent + footprint area of one | +| `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`. | +| `ue_console_command` | Run console commands with structured output parsing. No console exec exists anywhere in Epic's surface. | -### Materials (4 tools) -| Tool | Description | +### 👁 Perception (4) +| Tool | What it does | |---|---| -| `ue_create_material_instance` | Create a MaterialInstanceConstant from a parent material | -| `ue_set_material_parameter` | Set scalar, vector, or texture parameters | -| `ue_get_material_parameters` | List all exposed parameters with current values | -| `ue_assign_material` | Apply a material to a specific mesh slot | +| `ue_viewport_percept` | One frame + camera, selection, and scene metadata in a single call | +| `ue_viewport_watch` | Continuous capture at a set rate — Claude keeps watching | +| `ue_viewport_diff` | Two-snapshot structural diff: what moved, appeared, disappeared | +| `ue_viewport_config` | Resolution / format / rate for the capture pipeline | -### Blueprints (7 tools) -| Tool | Description | +### 🌅 Lighting & atmosphere (5) +| Tool | What it does | |---|---| -| `ue_create_blueprint` | Create a new Blueprint asset | -| `ue_add_component` | Add a component to a live actor | -| `ue_set_component_property` | Set a property on a component | -| `ue_set_blueprint_defaults` | Override CDO default values | -| `ue_compile_blueprint` | Compile and save a Blueprint | -| `ue_get_actor_components` | List all components on an actor | -| `ue_spawn_blueprint` | Spawn a Blueprint instance into the level | - -### Sequencer & Animation (4 tools) -| Tool | Description | +| `ue_apply_mood_preset` | One command = coordinated sun + fog + clouds + color grade | +| `ue_blend_mood_presets` | Interpolate two moods (t 0→1) for in-between looks | +| `ue_set_time_of_day` | Hour 0–24 → sun elevation, azimuth, color, intensity | +| `ue_setup_sky_atmosphere` | Build or update the whole sky rig, idempotently | +| `ue_list_mood_presets` | See the built-in cinematic presets | + +### 📐 Spatial reasoning (4) +| Tool | What it does | |---|---| -| `ue_create_level_sequence` | Create a new LevelSequence asset | -| `ue_play_sequence` | Play or scrub a sequence at a given time/rate | -| `ue_add_actor_to_sequence` | Bind a level actor to a sequence | -| `ue_add_keyframe` | Add a keyframe for a property at a given time | +| `ue_ground_trace` | Trace down at (x, y): hit point, **surface normal**, distance, actor. Epic's `trace_world` returns a bare distance. | +| `ue_snap_to_ground` | Drop an actor onto the surface, optionally tilting to the slope | +| `ue_spatial_query` | Nearest-N, AABB overlap, combined bounds, box contents | +| `ue_get_actor_hierarchy` | The full recursive attachment tree in one call | -### Perception (4 tools) -| Tool | Description | +### 🧩 Scene gaps Epic left open (3) +| Tool | What it does | |---|---| -| `ue_viewport_percept` | Capture the viewport with camera, selection, and scene metadata | -| `ue_viewport_watch` | Start/stop continuous viewport capture | -| `ue_viewport_config` | Configure capture resolution, format, and rate | -| `ue_viewport_diff` | Two-snapshot structural diff (actors, camera, selection changes) | - -### Level (4 tools) -| Tool | Description | -|---|---| -| `ue_save_level` | Save the current level | -| `ue_get_level_info` | Get level name and actor count | -| `ue_load_level` | Load a level by content path | +| `ue_duplicate_actor` | Clone a level actor with an offset (no Epic equivalent) | | `ue_get_world_info` | Streaming levels, world settings, game mode | +| `ue_create_cloner` | ClonerEffector procedural instancing, with read-back-verified configuration | -### Assets (3 tools) -| Tool | Description | +### ❤️ Bridge health (2) +| Tool | What it does | |---|---| -| `ue_find_assets` | Search the Content Browser by pattern and class | -| `ue_create_material` | Create a material with BaseColor/Roughness/Metallic nodes | -| `ue_delete_asset` | Delete an asset from the Content Browser | +| `ue_status` | Is the editor up? Is Remote Control reachable? | +| `ue_health_check` | Version, uptime, circuit breaker, per-call metrics, active tool profile, **Epic MCP reachability** | -### Motion Graphics (3 tools) -| Tool | Description | -|---|---| -| `ue_create_cloner` | ClonerEffector procedural instancing | -| `ue_create_niagara_system` | Spawn a Niagara particle system | -| `ue_create_pcg_graph` | Create a PCG procedural generation volume | +--- -### Lighting & Atmosphere (5 tools) -| Tool | Description | -|---|---| -| `ue_setup_sky_atmosphere` | Build/update the sky rig (sun, sky, fog, clouds, post) idempotently | -| `ue_set_time_of_day` | Map hour 0–24 → sun elevation/azimuth/colour/intensity | -| `ue_list_mood_presets` | List the built-in cinematic mood presets | -| `ue_apply_mood_preset` | Apply a coordinated sun + fog + clouds + colour-grade package | -| `ue_blend_mood_presets` | Interpolate between two presets (t in 0..1) and apply | - -### Editor Utilities (5 tools) -| Tool | Description | -|---|---| -| `ue_console_command` | Execute console commands with structured output parsing | -| `ue_undo` / `ue_redo` | Undo or redo the last editor transaction | -| `ue_focus_actor` | Focus the viewport camera on an actor | -| `ue_select_actors` | Set editor selection by label | +## The commodity tier (36 tools, `UE_MCP_PROFILE=full`) -### Properties (2 tools) -| Tool | Description | -|---|---| -| `ue_get_property` | Read any UObject property by path | -| `ue_set_property` | Write any UObject property by path | +Epic'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: -### Python Execution (1 tool) -| Tool | Description | +| Module | Tools | |---|---| -| `ue_execute_python` | Run arbitrary Python in the editor (AST-sandboxed) | +| Actors | spawn · delete · list · set_transform · get_bounds | +| Scene | actor details · scene query · component details | +| Blueprints | create · add component · set properties · CDO defaults · compile · list components · spawn | +| Materials | create instance · set / get parameters · assign to slot | +| Sequencer | create sequence · play/scrub · bind actor · keyframe | +| Level | save · info · load | +| Assets | find · create material · delete | +| Mograph | Niagara system · PCG graph | +| Properties | get / set any UObject property | +| Editor | focus actor · select actors | +| Spatial | measure (distance / extent) | + +**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. --- -## Architecture +## Why trust the results? Honesty as architecture + +This bridge's tools **cannot silently lie** — that's enforced, not promised: + +- **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. +- **Read-back verification.** Writes that UE can silently ignore (cloner layout names) are read back before being reported "applied". +- **Honest statuses.** The viewport fallback reports `capture_status: timeout` instead of an empty image with `success: true`. Not-implemented tools say "not implemented". +- **580 tests**, including scripted-failure contracts for every historical lying-tool bug. ```mermaid graph TB - subgraph "Python Layer" + subgraph Python layer MCP["MCP Server
ue_mcp/mcp_server.py"] + Reg["Tiered Registry
core / full / all"] Tools["Tool Modules (14)
ue_mcp/tools/"] + Val["Validation & AST Sandbox
_validation.py"] RC["Remote Control Bridge
remote_control/"] - Val["Validation & Sandbox
_validation.py"] - Met["Metrics & Circuit Breaker
metrics.py"] - Log["JSON Logger
logging.py"] + Met["Metrics & Circuit Breaker"] end - subgraph "File Bridge Layer" - Orch["Bridge Orchestrator
bridge_orchestrator.py"] - USD["USD Bridge
usd_bridge/"] - FS[("~/.translators/
bridge_state.usda
heartbeat.json")] + subgraph Test layer + Sim["Exec-Sim Harness
strict fake unreal · honesty contracts"] end - subgraph "C++ Plugins (UE5)" - Sub["UEBridgeSubsystem
State machine · polling"] - Ed["BridgeEditorSubsystem
File watching · process mgmt"] - VP["ViewportPerception
GPU readback · ring buffer"] - RCAPI["Remote Control API
REST · port 30010"] + subgraph Unreal Editor + RCAPI["Remote Control API
REST · :30010"] + VP["ViewportPerception plugin
GPU readback · :30011"] + EPIC["Epic Unreal MCP
HTTP+SSE · :8000 · UE 5.8"] end - MCP --> Tools + MCP --> Reg --> Tools Tools --> Val - Tools --> RC - RC --> Met - MCP --> Log - RC -- "HTTP" --> RCAPI - RCAPI --> Sub - Orch --> USD - USD --> FS - FS --> Sub - Sub --> Ed - VP -- "HTTP :30011" --> Tools + Tools --> RC --> Met + RC -- HTTP --> RCAPI + Tools -. capture .-> VP + Sim -. gates every generated script .-> Tools style MCP fill:#5CFFDB,color:#05050D + style Reg fill:#5CFFDB,color:#05050D style Tools fill:#5CFFDB,color:#05050D + style Sim fill:#12121F,color:#fff style RCAPI fill:#FFCC33,color:#05050D - style Sub fill:#12121F,color:#fff style VP fill:#FFCC33,color:#05050D + style EPIC fill:#FFCC33,color:#05050D ``` ### Resilience @@ -269,116 +230,63 @@ graph TB | **Connection pooling** | 10 max connections, 5 keepalive | | **Timeout** | 10s adaptive default | | **Atomic file I/O** | `tempfile` + `os.replace` (NTFS-safe) | -| **Python sandbox** | AST-based validation blocking `os`, `subprocess`, `open`, `getattr`, dunders | - -### Security - -All Python code executed in UE5 is validated through a multi-layer sandbox: - -- **Blocked modules**: `os`, `sys`, `subprocess`, `shutil`, `socket`, `pickle`, `importlib`, and 20+ more -- **Blocked builtins**: `exec`, `eval`, `open`, `getattr`, `globals`, `__import__` -- **Blocked attributes**: `system`, `popen`, `rmtree`, `kill`, plus dangerous dunders -- **Path validation**: Content path traversal (`..`) prevention on all inputs -- **Console safety**: Blocked commands (`exit`, `quit`, `crash`) and newline injection prevention +| **Python sandbox** | AST validation blocking `os`, `subprocess`, `open`, `getattr`, dunders | ### Network surface & trust model -The bridge talks to the editor over two **localhost** services: the Unreal Remote Control API (`:30010`) and ViewportPerception (`:30011`). They are designed for a **single trusted operator on a single machine** — the developer running Claude Code and the editor on the same workstation — and should not be exposed to untrusted networks. Viewport perception is **opt-in (disabled by default)**. +Everything 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. -See **[SECURITY.md](SECURITY.md)** for the full trust model, operator hardening guidance, and how to report a vulnerability. +Full trust model and hardening guidance: **[SECURITY.md](SECURITY.md)**. --- -## Project Structure +## Project structure ``` UnrealEngine_Bridge/ ├── ue_mcp/ # MCP server package -│ ├── mcp_server.py # FastMCP entry point (stdio) +│ ├── mcp_server.py # FastMCP entry point (stdio) + health tools │ ├── metrics.py # Telemetry + observability -│ ├── logging.py # Structured JSON logging -│ └── tools/ # 14 tool modules (56 tools) -│ ├── actors.py # Spawn, delete, list, transform -│ ├── scene.py # Query, details, hierarchy -│ ├── spatial.py # Ground trace, snap-to-ground, measure -│ ├── materials.py # Create, set params, assign -│ ├── lighting.py # Sky/atmosphere rig, time of day, mood presets -│ ├── blueprints.py # Create, compile, components -│ ├── sequencer.py # Animation / Level Sequence -│ ├── perception.py # Viewport capture + diff -│ ├── editor.py # Console, undo/redo, focus -│ ├── level.py # Save, load, world info -│ ├── assets.py # Find, create, delete -│ ├── mograph.py # Cloner, Niagara, PCG -│ ├── properties.py # Get/set UObject properties -│ ├── python_exec.py # Sandboxed Python execution -│ ├── _validation.py # Input sanitization + AST sandbox -│ ├── _codegen.py # Shared code generation snippets -│ ├── _console_parsers.py # Structured stat output parsers -│ └── _types.py # Protocol types -│ -├── remote_control/ # UE5 HTTP bridge package -│ ├── circuit_breaker.py # CLOSED/OPEN/HALF_OPEN state machine -│ ├── async_client.py # AsyncUnrealRemoteControl (MCP) -│ ├── sync_client.py # UnrealRemoteControl (standalone) -│ ├── codegen.py # UE5 Python script generation -│ ├── execution.py # File-based result polling -│ └── constants.py # URLs, timeouts, pool config -│ -├── usd_bridge/ # USD file I/O package -│ ├── io.py # Atomic writes, locking, paths -│ ├── question.py # Question read/write -│ ├── transition.py # State transitions, finales -│ ├── signals.py # Behavioral signal extraction -│ ├── profile.py # Cognitive profiling + checksums -│ └── validation.py # Bridge state validation -│ +│ ├── ue_logging.py # Structured JSON logging +│ └── tools/ # 14 modules · 56 tools · tiered registry in __init__.py +├── remote_control/ # UE5 HTTP bridge (circuit breaker, codegen, polling) +├── usd_bridge/ # USD file I/O package (parked, out of the ship path) ├── Plugins/ -│ ├── UEBridge/ # Core bridge C++ plugin -│ │ ├── UEBridgeRuntime/ # Subsystem, types, UI, style -│ │ └── UEBridgeEditor/ # File watching, process management -│ └── ViewportPerception/ # GPU readback, ring buffer, HTTP endpoint -│ -├── bridge_orchestrator.py # Game flow orchestration -├── remote_control_bridge.py # Backward-compat shim → remote_control/ -├── usd_bridge.py # Backward-compat shim → usd_bridge/ -├── tests/ # 415 tests (pytest) -├── pyproject.toml # Build config, dependencies, tooling -└── .github/workflows/ci.yml # CI: Python 3.11/3.12, coverage, lint +│ ├── UEBridge/ # Editor panel, file watcher (C++) +│ └── ViewportPerception/ # GPU readback + HTTP endpoint (C++) +├── docs/ +│ ├── EPIC_MCP_MATRIX.md # Retirement contract-of-record (probe-grounded) +│ └── epic_mcp/ # Raw probe captures of Epic's 830-tool surface +├── scripts/probe_epic_mcp.py # Re-probe Epic's surface per engine version +├── tests/ # 580 tests, incl. tests/exec_sim/ + tier gates +├── smoke_live.py # Live-editor smoke harness (on-demand) +└── .mcp.json # Two-server config: this bridge + Epic's MCP ``` --- ## Development -### Running Tests +**Run the tests** ```bash pip install -e ".[dev]" -python -m pytest tests/ -v -``` - -### Running with Coverage - -```bash -python -m pytest tests/ --cov=ue_mcp --cov=remote_control --cov-report=term-missing +python -m pytest -q # 580 tests ``` -### Linting +**Lint** ```bash -pip install ruff ruff check ue_mcp/ remote_control/ tests/ ``` -### Adding a New Tool +**Add a tool** (each step is checked by CI) -1. Create `ue_mcp/tools/your_module.py` -2. Define a `register(server: MCPServer, ue: UEBridge) -> None` function -3. Use `@server.tool()` decorators inside `register()` -4. Validate inputs with sanitizers from `_validation.py` -5. Register in `ue_mcp/tools/__init__.py` -6. Add tests in `tests/` +1. Create `ue_mcp/tools/your_module.py` with a `register(server, ue)` function +2. Decorate tools with `@server.tool(...)`; validate inputs via `_validation.py` +3. Wire the module into `ue_mcp/tools/__init__.py` — **and classify the tool in `TIERS`** (an unclassified tool fails CI) +4. Add a sentinel entry in `tests/exec_sim/registry.py` (a missing entry fails CI) +5. Add tests --- diff --git a/UnrealEngine_Bridge.uproject b/UnrealEngine_Bridge.uproject index 1c7d264..2e223d1 100644 --- a/UnrealEngine_Bridge.uproject +++ b/UnrealEngine_Bridge.uproject @@ -1,6 +1,6 @@ { "FileVersion": 3, - "EngineAssociation": "5.7", + "EngineAssociation": "5.8", "Category": "Games", "Description": "UnrealEngine Bridge - Agentic AI bridge connecting Claude Code to Unreal Engine via MCP tools, Remote Control API, and USD transport", "Modules": [ diff --git a/docs/FAB_LISTING.md b/docs/FAB_LISTING.md new file mode 100644 index 0000000..36b449e --- /dev/null +++ b/docs/FAB_LISTING.md @@ -0,0 +1,122 @@ +# FAB Marketplace Listing — UnrealEngine Bridge + +Draft storefront copy + the technical fields FAB requires for a code plugin. +The plugin package (icon, `.uplugin` metadata, compiled binaries) is separate; +this is the listing that wraps it. + +--- + +## Product name + +**UnrealEngine Bridge — AI Editor Control (Claude / MCP)** + +## Tagline (≤ 80 chars) + +Let Claude Code drive your editor — the AI abilities Epic's MCP doesn't ship. + +## Category + +Code Plugins → Editor / Utility + +## Price + +Suggested: free or low-cost dev tool (the value is the workflow, not the C++). +The Python MCP server is MIT on GitHub; the marketplace product is the +signed, precompiled, one-click editor plugins. + +--- + +## Short description (≤ 160 chars) + +Two editor plugins that give Claude Code eyes and hands in Unreal 5.8: +viewport perception + a Remote-Control bridge. Pairs with the open-source MCP server. + +## Full description + +Unreal Engine 5.8 ships Epic's official **Unreal MCP** for the commodity control +plane — spawn, transform, materials, Blueprints. **UnrealEngine Bridge is the +other half**: the differentiated AI abilities Epic's server deliberately does +not expose. + +Install these two editor plugins, run the open-source MCP server, and Claude +Code (or any MCP client) can: + +- **See the viewport** — continuous, render-thread GPU-readback capture served + as perception packets (frame + camera + selection + scene metadata). Single + frames, continuous watch, and structural before/after diffs. Epic's MCP has + no viewport capture at all. +- **Run real editor Python** — the full `unreal` API, not a sandboxed + tool-script runner. +- **Light a scene by mood** — one command sets a coordinated sun + fog + clouds + + colour-grade package, or blends two looks. +- **Reason about space with surface normals** — ground traces that return the + hit point, normal, distance, and actor; snap-to-slope placement. +- **Stay honest** — every tool's result is enforced by an exec-simulated test + suite; tools report real status, never a hard-coded success. + +The bridge coexists cleanly with Epic's official MCP: Epic handles the basics, +this handles the rest, and both run against the same editor. + +### What you get (the plugins) + +| Plugin | What it does | +|---|---| +| **UE Bridge** | In-editor status panel, directory-watch bridge, Remote Control access — the editor-side control surface. | +| **Viewport Perception** | Render-thread viewport capture over a localhost HTTP endpoint — the AI's "eyes." | + +### What you also need (disclosed up front — required by FAB) + +This is an **editor-integration + AI-tooling** product, not a self-contained +runtime feature. To use it you also need, all free: + +- The **UnrealEngine Bridge MCP server** (open-source, MIT) — a Python process + the AI client launches. `pip install` from the linked GitHub repo. +- An **MCP client** — Claude Code, Claude Desktop, Cursor, etc. +- The engine's built-in **Remote Control** plugin (enable it; ships with UE). + +The plugins do nothing at runtime in a packaged game — they are **editor-only +developer tooling**. + +--- + +## Technical details (FAB fields) + +- **Code Modules:** + - `UEBridgeRuntime` (Runtime) + - `UEBridgeEditor` (Editor) + - `ViewportPerception` (EditorNoCommandlet) +- **Number of Blueprints:** 0 +- **Number of C++ Classes:** ~12 (subsystems, HTTP endpoint, frame producer, pixel bus, types) +- **Network Replicated:** No +- **Supported Development Platforms:** Windows +- **Supported Target Build Platforms:** Windows (Win64) — editor-only +- **Engine Version:** 5.8 +- **Documentation:** https://github.com/JosephOIbrahim/UnrealEngine_Bridge#readme +- **Example / setup guide:** README + `docs/EPIC_MCP_MATRIX.md` (what this adds over Epic's MCP) +- **Support:** https://github.com/JosephOIbrahim/UnrealEngine_Bridge/issues + +### Important notes for reviewers + +- Both plugins are **editor-only** (`Editor` / `EditorNoCommandlet` module + types); nothing loads in a cooked build. +- Two **localhost** HTTP surfaces (Remote Control `:30010`, Viewport + Perception `:30011`) — single-operator dev trust model, not for untrusted + networks. Viewport Perception is opt-in. See `SECURITY.md`. +- No third-party binaries; no external runtime dependencies beyond the + engine's own Remote Control + (optional) Python Script plugins. + +--- + +## Gallery assets to produce (human step) + +FAB needs storefront imagery the package can't carry: + +1. **Featured image** 1920×1080 — the two-server architecture diagram + a + "Claude driving the editor" screenshot. +2. **Screenshots** (≥ 5, 1920×1080): the in-editor UE Bridge panel; a + perception frame Claude captured; a mood-preset before/after; a + ground-trace/snap demo; the health-check output showing both servers. +3. **Thumbnail** 512×512 — the plugin icon on the brand background. + +The plugin `Icon128.png` (shipped in each plugin's `Resources/`) is the +in-editor browser icon, not the storefront thumbnail. diff --git a/pyproject.toml b/pyproject.toml index 109b374..7b4e5d6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "ue-bridge" -version = "2.1.0" +dynamic = ["version"] description = "MCP server bridging Claude Code to Unreal Engine 5 via Remote Control API" readme = "README.md" requires-python = ">=3.11" @@ -27,6 +27,10 @@ usd = [ [project.scripts] ue-mcp = "ue_mcp.mcp_server:main" +[tool.hatch.version] +# Single version source; the tag gate is `git tag vX.Y.Z` == this value. +path = "ue_mcp/__version__.py" + [tool.hatch.build.targets.wheel] # Flat layout: the distribution name (ue-bridge) has no matching directory, so the # first-party packages must be named explicitly — newer hatchling no longer diff --git a/ue_mcp/__version__.py b/ue_mcp/__version__.py index bc0f8ee..180684e 100644 --- a/ue_mcp/__version__.py +++ b/ue_mcp/__version__.py @@ -1,4 +1,9 @@ -"""Version metadata for ue-bridge MCP server.""" +"""Version metadata for ue-bridge MCP server. -__version__ = "2.1.0" -__version_info__ = (2, 1, 0) +Single source of truth: pyproject reads it via hatchling; git tags must match +(v0.2.0 <-> 0.2.0). The public line continues from v0.1.1 — the internal 2.x +numbering was never released and was retired at the Epic-MCP-era reset. +""" + +__version__ = "0.2.0" +__version_info__ = (0, 2, 0)