Unreal-MCP
About
Open-source MCP server connecting AI agents to Unreal Engine 5.7, editor and runtime (C++ plugin + .NET sidecar).
Explore
Setup
Install Unreal-MCP in your MCP client (Claude Desktop, Cursor, Windsurf, and others).
Repository: https://github.com/ivanmurzak/Unreal-MCP
Follow the installation instructions in the repository README, then restart your MCP client.
This is the headline extensibility feature.Anyone can register theirownAI Tools,prompts, andresources— from any third-party UE plugin — and have them appear in the MCP manifest alongside the built-ins.No fork, no link-time coupling, no load-order assumptions.Your contributions are discovered automatically on editor boot (and on late-load / hot-unload), merged in deterministic order, and exposed to every connected AI agent.
All three kinds use thesamesmall, public,modular-feature-based contract— a provider interface plus a fluent registry builder, both living in theUnrealMcpRuntimemodule (re-exported byUnrealMcpEditor, so the same contract serves editor and](https://github.com/IvanMurzak/Unreal-MCP/blob/HEAD/cli/README.md)runtimeextensions). The full author guide isdocs/EXTENSIONS.md.
ImplementIUnrealMcpToolProviderand declare your tools with the fluentFUnrealMcpToolRegistrybuilder:
#include "IUnrealMcpToolProvider.h" #include "UnrealMcpToolRegistry.h" class FMyExtensionProvider : public IUnrealMcpToolProvider { public: virtual FString GetExtensionId() const override { return TEXT("com.foo.my-extension"); } virtual FText GetDisplayName() const override { return NSLOCTEXT("Foo", "Name", "My Extension"); } virtual FString GetExtensionVersion() const override { return TEXT("1.0.0"); } virtual void RegisterTools(FUnrealMcpToolRegistry& Registry) override { Registry.Tool(TEXT("hello-extension")) .Title(TEXT("Hello Extension")) .Description(TEXT("Returns a friendly greeting.")) .ParamString(TEXT("name"), TEXT("Who to greet. Defaults to 'world'.")) .ReadOnlyHint(true) .IdempotentHint(true) .Handle([](const FUnrealMcpToolCall& Call) -> FUnrealMcpToolResult { const FString Name = Call.Has(TEXT("name")) ? Call.GetString(TEXT("name")) : TEXT("world"); return FUnrealMcpToolResult::Success(FString::Printf(TEXT("Hello, %s!"), Name)); }); } };
Then register the provider as amodular featurein your module'sStartupModule(and unregister inShutdownModule):
// In StartupModule: IModularFeatures::Get().RegisterModularFeature( IUnrealMcpToolProvider::GetModularFeatureName(), Provider.Get());
Prompts are reusable, parameterized prompt templates the agent fetches viaprompts/get. ImplementIUnrealMcpPromptProviderand declare prompts withFUnrealMcpPromptRegistry. Prompt arguments reuse thesameParamhelpers as the tool builder; a handler returns role-tagged messages:
#include "IUnrealMcpPromptProvider.h" #include "UnrealMcpPromptRegistry.h" class FMyPromptProvider : public IUnrealMcpPromptProvider { public: virtual FString GetExtensionId() const override { return TEXT("com.foo.my-extension"); } virtual FText GetDisplayName() const override { return NSLOCTEXT("Foo", "Name", "My Extension"); } virtual FString GetExtensionVersion() const override { return TEXT("1.0.0"); } virtual void RegisterPrompts(FUnrealMcpPromptRegistry& Registry) override { Registry.Prompt(TEXT("level-design-brief")) .Title(TEXT("Level Design Brief")) .Description(TEXT("Generate a level design brief from a single 'theme' argument.")) .Role(EUnrealMcpPromptRole::User) .ParamString(TEXT("theme"), TEXT("The level theme (e.g. 'haunted forest')."), EUnrealMcpParamRequirement::Required) .Handle([](const FUnrealMcpToolCall& Call) -> FUnrealMcpPromptResult { const FString Theme = Call.GetString(TEXT("theme")); if (Theme.IsEmpty()) return FUnrealMcpPromptResult::Error(TEXT("theme is required.")); const FString Text = FString::Printf( TEXT("Draft a level design brief for a \"%s\"-themed level."), Theme); return FUnrealMcpPromptResult::Success(Text, EUnrealMcpPromptRole::User); }); } }; // Register under the prompt modular-feature name (and unregister in ShutdownModule): IModularFeatures::Get().RegisterModularFeature( IUnrealMcpPromptProvider::GetModularFeatureName(), PromptProvider.Get());
level-design-briefis the shippedcoreprompt — seeUnrealMCP/Source/UnrealMcpRuntime/Private/Prompts/UnrealMcpCorePrompts.cpp.
Resources are addressable, readable content the agent fetches viaresources/read— the resource'sURI is its identity. ImplementIUnrealMcpResourceProviderand declare resources withFUnrealMcpResourceRegistry. A read returns content blocks —text XOR a base64 blob+ a mime type:
#include "IUnrealMcpResourceProvider.h" #include "UnrealMcpResourceRegistry.h" virtual void RegisterResources(FUnrealMcpResourceRegistry& Registry) override { Registry.Resource(TEXT("unreal://project/levels")) // JSON (text) resource .Name(TEXT("Project Levels")) .Description(TEXT("A JSON snapshot of the active world and its levels.")) .MimeType(TEXT("application/json")) .Read([](const FString& Uri) -> FUnrealMcpResourceResult { return FUnrealMcpResourceResult::Text(Uri, BuildLevelsJson(), TEXT("application/json")); }); Registry.Resource(TEXT("unreal://project/icon")) // binary (blob) resource .Name(TEXT("Project Icon")) .Description(TEXT("A small PNG, returned as a base64 blob.")) .MimeType(TEXT("image/png")) .Read([](const FString& Uri) -> FUnrealMcpResourceResult { const FString Base64 = FBase64::Encode(IconBytes, sizeof(IconBytes)); return FUnrealMcpResourceResult::Blob(Uri, Base64, TEXT("image/png")); }); } // Register under the resource modular-feature name (and unregister in ShutdownModule): IModularFeatures::Get().RegisterModularFeature( IUnrealMcpResourceProvider::GetModularFeatureName(), ResourceProvider.Get());
unreal://project/levelsandunreal://project/iconare the shippedcoreresources — seeUnrealMCP/Source/UnrealMcpRuntime/Private/Resources/UnrealMcpCoreResources.cpp. Onlystatic, fixed-URIresources are supported today (templated / parameterized URIs are deferred). A blob is base64 on the Unreal side and the IPC wire; a knownupstreamquirk in the shared GameDev-MCP-Server / MCP-Plugin-dotnet can mis-emit blob bytes on the final MCP wire to the client (text resources round-trip cleanly end-to-end) — that is an upstream issue, not the Unreal plugin or bridge.
How it behaves(identical for tools, prompts, and resources):
- Full author guide:docs/EXTENSIONS.md— the contract for tools, prompts, and resources, the builders, lifecycle, ordering, isolation semantics, and versioning.
- Design:docs/ARCHITECTURE.md§5 (tools) +§A(the prompt/resource registration path).
- Working samples:samples/UnrealAITemplate/— a complete, buildableeditorextension plugin with ahello-extensiontool and a compile-time switch (UNREAL_AI_TEMPLATE_INVALID_SCHEMA=1) that demonstrates the isolation behaviour first-hand;samples/UnrealAIRuntimeSample/— theruntime (in-game)counterpart, aType=Runtimeplugin whosegame-time-dilationtool reads/sets the live world's time dilation, callable in a running game over a runtime MCP connection (docs/ARCHITECTURE.md§12.9; see EXTENSIONS.md "Runtime usage"). For prompts and resources, the shippedcorefamilies (UnrealMcpCorePrompts.cpplevel-design-brief,UnrealMcpCoreResources.cppunreal://project/levels+unreal://project/icon) are the runnable reference.
Everything above drives theeditor. Unreal-MCP can also runinside a running game— PIE, Standalone, or a packagedDevelopmentbuild — so an AI assistant can drive your game live. This is the Unreal counterpart ofUnity-MCP's runtime (in-game) support: the UE analog of Unity'sUnityMcpPluginRuntime.Initialize().Build().Connect()and its[AiTool]Chess-bot sample.
The runtime entry point is aUGameInstanceSubsystem,UUnrealMcpRuntimeSubsystem(in the plugin'sUnrealMcpRuntimeruntime module). It is auto-instantiated once perUGameInstancebutnever auto-connects— a connection is always an explicit, opt-in call (seethe security contractbelow).
All three reach the sameUUnrealMcpRuntimeSubsystem::Connect(Host, Token, Mode, bAllowRemoteHost); the connection mode defaults toCustom(a developer-supplied loopback server).
1. From C++(e.g. yourGameMode::BeginPlay):
#include "UnrealMcpRuntimeSubsystem.h" void AMyGameMode::BeginPlay() { Super::BeginPlay(); if (UUnrealMcpRuntimeSubsystem Mcp = UUnrealMcpRuntimeSubsystem::Get(this)) Mcp->Connect(TEXT("http://localhost:8080"), TEXT("my-token")); // Custom mode, loopback // ... and, when you are done: // Mcp->Disconnect(); }
Get(WorldContext)is a staticBlueprintPurehelper that returns the subsystem for the context's game instance (or null).Connectreturnsfalse(and connects nothing) if any security gate rejects — see below.
2. From Blueprint—Get Unreal MCP Runtime Subsystem(theGetnode,WorldContext-aware) →Connect(aBlueprintCallablenode under theUnreal MCPcategory;Token/Mode/bAllowRemoteHostare advanced pins). Pair it with theDisconnectnode and theIs Connectedpure node for status.
3. From the console(QA convenience, no recompile) — registered while the subsystem is alive:
UnrealMcp.Connect <host> [token] UnrealMcp.Disconnect
The console path always uses loopback + Custom mode.
A game ships itsowngameplay tools (and, if useful, prompts and resources) and the AI drives them live — the UE analog of Unity'sWithToolsFromAssembly/[AiTool]Chess-bot. You author all three exactly as for an editor extension (theCustomize Tools, Prompts & Resourcessection above), with two changes for a game module: make itType=Runtimeand depend onUnrealMcpRuntime(notUnrealMcpEditor). Register your providers at module startup, or use the subsystem's discoverable wrappers — one register/unregister pair per kind:
if (UUnrealMcpRuntimeSubsystem Mcp = UUnrealMcpRuntimeSubsystem::Get(this)) { Mcp->RegisterToolProvider(MyToolProvider); // tools merge in; manifest re-pushed Mcp->RegisterPromptProvider(MyPromptProvider); // prompts merge in; manifest re-pushed Mcp->RegisterResourceProvider(MyResourceProvider); // resources merge in; manifest re-pushed } // ... before destroying the providers, call the matching UnregisterProvider(...) for each.
The complete, buildable example issamples/UnrealAIRuntimeSample/— aType=Runtimeplugin whosegame-time-dilationtool reads/sets the live world'sAWorldSettings::TimeDilation(slow-motion / fast-forward), callable in a running game over a runtime MCP connection. (It demonstrates the tool path; prompts and resources register through the same three-wrapper API shown above and the shipped core families are the runnable reference.) The author-side details (the contract, lifecycle, ordering, isolation) are indocs/EXTENSIONS.md→ Runtime usage.
A runtime connection ships exactlyonebuilt-in tool:ping(a liveness probe).pingis asystem tool, so it answers atPOST /api/system-tools/pingand is not advertised to an AI agent — meaning a packaged game that registers nothing of its own presents anemptytool list, which is an honest description of it. Everything a runtime AI agent can do isbring-your-own: register your own tools via the extension bus above (RegisterToolProvider) and they appear normally.
All of the engine-development families — the actor / component family,object-get-data/object-modify,level-get-data, the console / reflection tools, every screenshot tool, plus Blueprint authoring, asset / Content-Browser operations, C++ source edit & compile, level create/open/save, and editor-application state — areeditor-only(the[61 editor tools). They drive the editor and several are RCE-class (e.g.reflection-method-call,console-run-command), so they are not compiled into a shipped game by default. There is no editor in a packaged game, so the runtime built-in surface is intentionally justping+ whatever tools your game registers.
Option B —unreal-mcp-cli(current / advanced)
The CLI is the recommended pathtoday, until the Fab listing is live. Install it from npm —no repo clone, no build step. By defaultinstall-plugin/updateuse a localUnrealMCP/checkout when one is present; otherwise they download the dedicatedunreal-mcp-plugin-source-<version>.zipsource asset from the public GitHub Release that matches the CLI version. That source asset keeps the distributed descriptor semantics (noEngineVersionpin) and carries the signed bridge payload underSource/ThirdParty/UnrealMcpBridge/<rid>/; the installer materializes it intoBinaries/ThirdParty/...for first-open convenience.--plugin-source <dir>remains the offline / CI / dev override. The CLI copies (or, for dev, junctions) the plugin into your project and, onupdate, automatically clears the stale UE build cache so you always get a clean recompile of the new code (seeUpdating the plugin). Ondesktop platforms(Win64/Mac/Linux),unreal-mcp-cli openalso runs a pre-launch build when the project/plugin state still needs native editor binaries, then auto-dismisses the known Unreal blocker dialogs (Missing ... Modules,UnrealMCP is Incompatible) if they still appear during startup. Linux dialog automation isX11-only; Wayland is detected and warned as unsupported.
# 1. Install unreal-mcp-cli (or use npx unreal-mcp-cli@latest <command> for a one-off, no install) npm install -g unreal-mcp-cli # 2. Install the UnrealMCP plugin into your project unreal-mcp-cli install-plugin ./YourProject # 3. Authorize against the cloud server (ai-game.dev) unreal-mcp-cli login ./YourProject # 4. Open the Unreal Editor for the project (wires the MCP connection env vars) unreal-mcp-cli open ./YourProject
Seecli/README.mdfor the full 16-command reference.
- CopyUnrealMCP/into<YourProject>/Plugins/UnrealMCP/(or create a directory junction / symlink to it for live development).
- Open the project; UE compiles theUnrealMcpEditormodule on first launch.
- On editor boot the Output Log prints[Unreal-MCP] plugin loaded— that confirms the plugin and its game-thread dispatcher started.
The sidecar binary (unreal-mcp-bridge) isbundled inside the pluginin a packaged release: a prebuilt, self-contained binary for your platform ships underUnrealMCP/Binaries/ThirdParty/UnrealMcpBridge/<rid>/and the editorauto-spawns it on startup with zero user action— no .NET install, no env var, no manual launch (ARCHITECTURE §6). The first Cloud OAuth device-code browser approval is the only remaining human step; after that, reconnect on later launches is zero-click (the cloud token is cached inSaved/Config/UnrealMcp/).
When you copy the reposource checkout directly(Option C/manual or a live dev junction), the bundled binary is not present — the plugin then resolves the sidecar from theUNREAL_MCP_BRIDGE_PATHenvironment variable instead: point that at a locally built sidecar, or rununreal-mcp-cli bootstrap-localto build the bridge from source into<YourProject>/Intermediate/UnrealMCP/and set the var to the result. With neither a bundled binary nor the env var resolved, the plugin's TCP listener still starts but logs[Unreal-MCP] no sidecar binary resolved for rid <rid> …and spawns nothing.
Updating in place must always leave you running thenewcode. The risk is UE's incremental compiler: if the plugin source changes (new.cppfiles, a new module) but the oldUnrealMCP/Intermediate/build cache survives, UE can do a partial recompile against a stale module file-list and silently leave you on old/partial code. Each channel handles this differently:
-
Fab / Epic Marketplace → automatic.The Epic Games Launcher replaces the precompiled binaries in place; nothing to compile, no cache to clear. This is why Fab is the recommended channel.
unreal-mcp-cli update→ automatic clean rebuild.updatere-copies the plugin source and, bydefault, deletes the installed plugin's staleIntermediate/and the C++Binaries/so UE performs a clean compile on the next editor launch — no manual steps. The bundled sidecar bridge underBinaries/ThirdParty/UnrealMcpBridge/<rid>/is kept intact: release-source installs refresh it fromSource/ThirdParty/..., while repo/dev installs preserve the previously bundled copy when needed. Devjunctioninstalls are never cleaned (that would wipe your live source tree's outputs). Pass--no-cleanto opt out of the cache wipe.
node bin/unreal-mcp-cli.js update <YourProject> # default: clean rebuild on version change node bin/unreal-mcp-cli.js update <YourProject> --force # re-copy even when versions match node bin/unreal-mcp-cli.js update <YourProject> --no-clean # keep the existing build cache
Manual copy → clear the cache yourself.If you overwrite<YourProject>/Plugins/UnrealMCP/by hand,close the editor first, delete<YourProject>/Plugins/UnrealMCP/Intermediate/and the C++Binaries/(keepBinaries/ThirdParty/if a bundled bridge is present), then relaunch so UE recompiles cleanly.
- Open theAI Game Developermain window from the editor'sToolsmenu (the tab is registered under the Tools menu category).
- Choose a connection mode:
- Cloud(default) — connects toai-game.dev. ClickAuthorizeto start the OAuthdevice-code flow: the window shows a verification URL and a short user code; open the URL, enter the code, approve, and the editor finishes authorizing. UseRevoketo clear the stored cloud token.
- Custom— connects to a localgamedev-mcp-serveryou run (or any compatible server). Enter the server URL and point your AI client at it. (The plugin does not start the local server for you — rununreal-mcp-clior your own process; seeTroubleshooting.)
Connection settings persist to<Project>/Saved/Config/UnrealMcp/ai-game-developer-config.json(Saved/is gitignored by every UE template, so tokens never land in VCS by default).
Pinned MCP client URL.unreal-mcp-cli setup-mcp <agent>writes an MCP client config that points at theproject-pinnedcloud URL<base>/mcp/p/<pin>, so the agent routes tothisproject's editor even when your account drives several. Pass--no-pinto write the bare<base>/mcpURL instead. The pin is a routing path segment only — the OAuth resource stays<base>/mcp, and OAuth-capable clients (Claude Code, Cursor, …) still run their own device-code login against it.
That's it. Ask your AI"Spawn three cubes in a row and a point light above them"and watch it happen. ✨
Unreal-MCP ships61 built-in ("core") toolsacross7 familiesthat your AI can call, plus3 system toolsit cannot (see below). Tool ids are kebab-case (actor-create,blueprint-compile), matching the Unity/Godot naming convention. Extensions can add more (seeCustomize Tools, Prompts & Resources).
This list is generated from the STANDARD-surface registration sources (UnrealMCP/Source/UnrealMcpEditor/Private/Tools/UnrealMcp*Tools.cpp, excludingUnrealMcpSkillTools.cpp— its tool is a system tool, listed separately below). Counts: actor 13, blueprint 11, asset 11, editor/reflection 9, level 7, source 6, screenshot 4 =61.
All file operations are jailed to<Project>/Source/.
Captures return a base64PNG as MCP image contentso the LLM can inspect the render directly. Dimensions are clamped (default 1024, hard cap 2048 per side). Pixel capture needs a GPU-backed editor; under headless-nullrhithese tools return a structured error.
These aresystem tools: host plumbing theunreal-mcp-cliand the desktop app drive directly overPOST /api/system-tools/<name>. They are deliberately absent from MCPtools/list, so they never appear to (or spend tokens in) an AI session — the same split Unity and Godot use for the same three tools.
Every tool can be individually enabled or disabled from theMCP Toolswindow — the standaloneMCP Toolstab (registered under the editor'sToolsmenu). The window shows each tool's title, family, and description, plus an "N / M tools enabled" summary line. Disabling a tool:
- removes it from the served manifest entirely— it never appears in the MCPtools/list; and
- isenforced at the execution boundary too— even if a staletools/listis dispatched, a disabled tool is rejected atExecute()rather than run.
Two filters combine to decide whether a tool is served (see ARCHITECTURE §7/§8):
- awhitelist(enabledTools, overridable viaUNREAL_MCP_TOOLS) — when non-empty, only listed tools are served; empty means "no filter";and
- ablocklist(disabledTools) — the per-tool toggles you flip in the UI.
A tool is servediffit passes the whitelistandis not in the blocklist. Both sets are persisted across editor sessions and survive an extension hot-reload (a re-registered tool inherits the retained toggle, so a rebuild can never silently re-enable a tool you disabled).
All connection settings live in the singleAI Game Developermain window's Connection section (there is no separate Settings tab or Project-Settings page — Unity-MCP parity). TheMCP PromptsandMCP Resourceswindows are wired but ship empty in this release — each renders a subdued empty-state message (the "N / M enabled" summary is unique to the Tools window).
A cross-platform Node CLI (unreal-mcp-cli) that scaffolds projects, installs the plugin, configures connection settings, drives the local server, and invokes tools over HTTP. It is a port ofunity-mcp-cli/godot-cli. Full reference:[cli/README.md.
Published on npm — install withnpm install -g unreal-mcp-cli, or run a one-off withnpx unreal-mcp-cli@latest <command>.
Sign in to leave a review
Use Google, GitHub, or an email account so ratings stay tied to real people.
No reviews posted yet.



