UE-MCP

Architecture

Architecture documentation.

UE-MCP has two main components: a TypeScript MCP server that handles the AI protocol, and a C++ plugin that runs inside the Unreal Editor and exposes engine APIs over WebSocket.

MCP Server (TypeScript)

Entry point: src/index.ts

The server creates an McpServer instance (from @modelcontextprotocol/sdk), registers 19 category tools plus a flow tool, and communicates with the AI client over stdio.

Key Modules

ModulePurpose
index.tsTool registration, MCP server lifecycle
tools.tsThe ALL_TOOLS registry consumed by index.ts and tests
bridge.tsEditorBridge (implements IBridge) - WebSocket client, JSON-RPC messaging, auto-reconnect
project.tsProjectContext - path resolution, INI parsing, C++ header parsing
types.tsToolDef, ActionSpec, categoryTool() factory
schemas.tsShared Zod schemas - Vec3, Rotator, Color, Quat
errors.tsMcpError class with ErrorCode enum for structured error handling
deployer.tsFirst-run deployment: copy plugin, mutate .uproject
editor-control.tsStart/stop/restart the Unreal Editor process
instructions.tsAI-facing server instructions (embedded documentation)
github-app.tsGitHub App auth for agent feedback issue submission
flow/Flow engine (registry, loader, task factory, HTTP server) — see Flows
init.ts / update.ts / resolve.ts / hook-handler.tsCLI subcommands (npx ue-mcp init, update, resolve, hook)

Tool Registration Pattern

All tools use the categoryTool() factory:

export const levelTool: ToolDef = categoryTool(
  "level",                              // tool name
  "Actors, selection, components...",    // description
  {
    get_outliner: bp("get_outliner"),           // bridge action
    get_current:  { handler: localHandler },    // local action
  },
  "- get_outliner: List actors...",     // AI-facing docs
);

Two action types:

  • Bridge actions (bp()) — forwarded to the C++ plugin over WebSocket
  • Local actions — handled in Node.js (filesystem operations like INI parsing, C++ header reading)

Bridge Communication

The EditorBridge maintains a WebSocket connection to ws://localhost:9877.

Protocol: JSON-RPC 2.0

// Request
{
  "jsonrpc": "2.0",
  "id": "req-42",
  "method": "get_outliner",
  "params": { "classFilter": "StaticMeshActor" }
}

// Response
{
  "jsonrpc": "2.0",
  "id": "req-42",
  "result": { "actors": [...] }
}
  • Timeout: 30 seconds per request
  • Reconnect: Automatic every 15 seconds if disconnected
  • Thread safety: All responses are correlated by request ID

C++ Bridge Plugin

Location: plugin/ue_mcp_bridge/ Module type: Editor-only

The plugin runs a raw WebSocket server on a dedicated thread, dispatches incoming JSON-RPC requests to registered handler functions, and executes them on the game thread.

Core Classes

ClassPurpose
FBridgeServerWebSocket server (raw platform sockets, Windows + Linux/Mac)
FHandlerRegistryMaps method names to C++ handler functions
FGameThreadExecutorQueues tasks to the game thread (required for UE API access)
HandlerUtils.hShared utilities — MCPError(), MCPSuccess(), RequireString(), FindClassByShortName(), LoadAssetByPath[T](), etc.

Handler Categories

22 C++ handler groups are registered in BridgeServer.cpp. Together they expose 438+ method names (some of which are aliases mapped onto a smaller number of canonical handlers):

Handler groupCoverage
EditorHandlersConsole, Python, PIE, viewport, build, logs, perf, screenshots, scalability
AssetHandlersCRUD, import, search, datatables, textures, sockets, FTS search
BlueprintHandlersRead/write, graphs, compilation, node types, T3D import/export, reparent, validate
LevelHandlersActors, components, volumes, lights, world settings, splines
ReflectionHandlersClass/struct/enum reflection, gameplay tags
MaterialHandlersMaterials, instances, expression graph authoring, declarative builder, render preview
AnimationHandlersAnim BPs, montages, blendspaces, skeletons, IK Rig, ControlRig, virtual bones
AudioHandlersPlayback, ambient sounds, SoundCues, MetaSounds
WidgetHandlersUMG widget trees, editor utility widgets and blueprints
FoliageHandlersFoliage painting, types, instance queries
LandscapeHandlersTerrain sculpting, layer painting, heightmap import
NetworkingHandlersReplication, dormancy, relevancy, net priority
NiagaraHandlersVFX systems, emitters, renderers, data interfaces, GPU HLSL inspection
PCGHandlersProcedural generation graphs, mesh spawner authoring
GasHandlersGameplay Ability System (attributes, abilities, effects, cues)
GameplayHandlersPhysics, collision, navigation, AI (BTs, EQS, perception), input, game framework
PhysicsHandlersCollision profiles, simulation toggles, body properties
SequencerHandlersLevel sequences and tracks
SplineHandlersSpline actor authoring
DialogHandlersModal dialog auto-response policies
ProjectHandlersProject info, world subsystem queries
DemoHandlersNeon Shrine demo builder

Plugin Dependencies

The C++ plugin links against a wide range of UE modules:

  • Core: Core, CoreUObject, Engine, Json, JsonUtilities, GameplayTags
  • Editor: UnrealEd, AssetRegistry, BlueprintGraph, Kismet, KismetCompiler, PropertyEditor
  • Systems: Landscape, Niagara, PCG, Sequencer, UMG, GameplayAbilities, NavigationSystem, AIModule
  • Tools: LiveCoding (Windows only), MaterialEditor, EditorScriptingUtilities, DataValidation

Hybrid Architecture

A key design principle: read operations work without the editor.

Operation TypeRequires Editor?How
INI config parsingNoDirect filesystem
C++ header reflectionNoRegex-based parsing
Asset directory listingNoFilesystem scan
Blueprint readingYesC++ bridge
Actor placementYesC++ bridge
Material authoringYesC++ bridge
PIE controlYesC++ bridge
Build pipelineYesC++ bridge

This means the AI can explore project structure, read configs, and understand C++ code even when the editor isn't running.

Path Resolution

The ProjectContext handles path formats:

InputResolved To
/Game/MyAsset[ProjectDir]/Content/MyAsset
/MyPlugin/Assets/Foo[ProjectDir]/Plugins/MyPlugin/Content/Assets/Foo
Absolute pathUsed as-is
Relative pathRelative to project root

Data Flow Example

Here's what happens when the AI calls blueprint(action="read", assetPath="/Game/BP_Player"):