NarshaADK: Unreal Engine MCP server
NarshaMCP is an Unreal Engine MCP server. It is distributed on Fab as NarshaADK.
It parses C++ source, PDB symbols and binary .uasset files and serves the result over MCP. It is tested with
Claude Code and Codex. Most reads work from disk with the Unreal Editor closed; edits need the Editor.
Get it on Fab · Latest release · Documentation
UE 5.7 UE 5.8 Early Access
Early Access. Tool names, parameters and output shapes may change between releases.
The Fab product is named NarshaADK. The installed plugin folder, code module and runtime executable are named NarshaMCP.
Quick start
The runtime is a prebuilt Windows 64-bit executable inside the plugin, and it runs on your machine. There is no hosted MCP endpoint. Python and Node.js are not required.
- In an Unreal Engine 5.7 or 5.8 C++ project, install NarshaADK from Fab and enable the NarshaMCP plugin, or extract the ZIP for your engine version from the latest release into
YourProject/Plugins/. - Build the C++ project once. The first build writes the project's MCP configuration.
- If you configure a client by hand, point it at the runtime that ships inside the plugin:
{
"mcpServers": {
"narshamcp": {
"command": "<Project>/Plugins/NarshaMCP/Source/ThirdParty/bin/narshamcp.exe",
"args": ["connect", "--project-path", "<Project>"],
"env": { "RUST_LOG": "error" }
}
}
}
Fab installs the plugin under the engine's Plugins folder (<UE>/Engine/Plugins/Marketplace/NarshaMCP or <UE>/Engine/Plugins/Fab/NarshaMCP, where <UE> is your engine install folder), not in the project. For a Fab install, replace <Project>/Plugins/NarshaMCP in the command with that plugin folder. Clients that launch the server as a direct process, such as Cursor, use --stdio in place of connect.
The full steps are in Requirements, Install and Connect your MCP client.
What you can do
Most tools take an operation argument. The table lists common read operations. ue_tool_docs returns every operation and its parameters, and your client lists the tools under tools/list.
ue_check_health reports the runtime version and whether the indexes are ready.
Before and after
Each example shows the request, the MCP tool call and the JSON response. Captured from NarshaMCP v0.13.7 on Epic's Lyra Starter Game (UE 5.8.1) with the Editor closed. Values are as returned; removed keys and array elements are marked in the block.
C++ and Blueprint cross-references
Request. Which Blueprints call StartRangedWeaponTargeting, which C++ does GA_Weapon_Fire call, and what derives from ALyraCharacter?
Before. grep -rn StartRangedWeaponTargeting finds the C++ declaration and definition. The Blueprint call sites are inside binary .uasset packages.
Call. Editor closed.
{"name": "ue_analyze_symbols", "arguments": {"operation": "find_cpp_to_bp", "function_name": "ULyraGameplayAbility_RangedWeapon::StartRangedWeaponTargeting", "limit": 5}}
Output. As returned; cuts are marked.
{
"function_name": "ULyraGameplayAbility_RangedWeapon::StartRangedWeaponTargeting",
"total_callers": 2,
"truncated": false,
"blueprint_callers": [
{
"blueprint_name": "GA_Weapon_Fire",
"blueprint_path": "/Game/Weapons/GA_Weapon_Fire",
"owning_function": "K2_ActivateAbility",
"node_name": "K2Node_CallFunction_31",
"match_tier": "qualified",
"confidence": 0.75
},
{
"blueprint_name": "GA_WeaponNetShooter",
"blueprint_path": "/ShooterCore/Weapons/NetShooter_PROTO/GA_WeaponNetShooter",
"owning_function": "K2_ActivateAbility",
"node_name": "K2Node_CallFunction_31",
"match_tier": "qualified",
"confidence": 0.75
}
]
// [truncated] blueprint_callers[] fields omitted: call_type, is_pure; keys omitted: operation, skip_dedup, success
}
Call. Editor closed.
{"name": "ue_analyze_symbols", "arguments": {"operation": "find_bp_to_cpp", "blueprint_name": "GA_Weapon_Fire", "limit": 2, "offset": 18}}
Output. As returned; cuts are marked.
{
"blueprint_name": "GA_Weapon_Fire",
"total_calls": 33,
"offset": 18,
"truncated": true,
"cpp_calls": [
{
"full_name": "LyraGameplayAbility_RangedWeapon::StartRangedWeaponTargeting",
"owning_function": "K2_ActivateAbility",
"node_name": "K2Node_CallFunction_31",
"is_pure": false
},
{
"full_name": "KismetSystemLibrary::SetTimerDelegate",
"owning_function": "K2_ActivateAbility",
"node_name": "K2Node_CallFunction_4",
"is_pure": false
}
]
// [truncated] cpp_calls[] fields omitted: function_name, owner_class; keys omitted: operation, returned, success, total_count
}
Call. Editor closed.
{"name": "ue_analyze_symbols", "arguments": {"operation": "trace_hierarchy", "class_name": "ALyraCharacter", "direction": "down"}}
Output. As returned; cuts are marked.
{
"class_name": "ALyraCharacter",
"cpp_descendants_count": 1,
"bp_descendants_count": 10,
"descendants": [
{"depth": 1, "name": "ALyraCharacterWithAbilities", "relation": "child", "source": "pdb"},
{"depth": 1, "name": "Character_Default", "relation": "child_blueprint", "source": "blueprint"},
{"depth": 2, "name": "B_Hero_Default", "relation": "child_blueprint", "source": "blueprint"},
{"depth": 2, "name": "B_ShootingTarget", "relation": "child_blueprint", "source": "blueprint"}
],
"interface_parents": ["IAbilitySystemInterface", "IGameplayCueInterface", "IGameplayTagAssetInterface", "ILyraTeamAgentInterface"]
// [truncated] descendants: 4 of 11 elements; descendants[] fields omitted: parent; keys omitted: 13 of 18
}
Result. owning_function is the Blueprint event that contains the call, and match_tier with confidence records how the symbol was matched to the node. descendants[].source is pdb for a C++ subclass and blueprint for a Blueprint one.
Blueprint events, variables and nodes
Request. What events does GA_Weapon_Fire handle, what is its fire delay, and how is a node wired?
Before. Content/Weapons/GA_Weapon_Fire.uasset is binary, and grep -rn FireDelayTimeSecs over the source finds nothing: the variable exists only in the asset.
Call. Editor closed.
{"name": "ue_manage_blueprint", "arguments": {"operation": "get_event_graph", "blueprint_name": "GA_Weapon_Fire"}}
Output. As returned; cuts are marked.
{
"blueprint_name": "GA_Weapon_Fire",
"count": 5,
"events": [
{"node_title": "FireComplete", "node_class": "K2Node_CustomEvent"},
{"node_title": "FailureMontage Delay Complete", "node_class": "K2Node_CustomEvent"}
]
// [truncated] events: 2 of 5 elements; events[] fields omitted: node_id, owning_function; keys omitted: operation, success
}
Call. Editor closed.
{"name": "ue_manage_blueprint", "arguments": {"operation": "search_variables", "blueprint_name": "GA_Weapon_Fire", "pattern": "*Fire*"}}
Output. As returned; cuts are marked.
{
"parent_class": "LyraGameplayAbility_RangedWeapon",
"total_count": 2,
"results": [
{
"name": "CharacterFireMontage",
"default_value": "AM_MM_Rifle_Fire",
"default_value_type": "Object",
"edit_condition": "EditDefaultsOnly"
},
{
"name": "FireDelayTimeSecs",
"default_value": "0.1",
"default_value_type": "Double",
"edit_condition": "EditDefaultsOnly"
}
]
// [truncated] results[] fields omitted: 9 of 13; keys omitted: 14 of 17
}
Call. Editor closed.
{"name": "ue_manage_blueprint", "arguments": {"operation": "get_raw_nodes", "blueprint_name": "GA_Weapon_Fire", "page": 1, "page_size": 1}}
Output. As returned; cuts are marked.
{
"total_nodes": 98,
"has_more": true,
"nodes": [
{
"id": "K2Node_AsyncAction_ListenForGameplayMessages_0",
"node_class": "K2Node_AsyncAction_ListenForGameplayMessages",
"owning_function": "K2_OnAbilityAdded",
"pins": [
{
"name": "execute",
"category": "exec",
"direction": "input",
"linked_to": [
{"node_id": "K2Node_Event_1", "pin_name": "then", "export_index": 70}
]
},
{"name": "then", "category": "exec", "direction": "output", "linked_to": []}
]
}
]
// [truncated] nodes[].pins: 2 of 12 elements; nodes[].pins[].linked_to[] fields omitted: owning_function; nodes[].pins[] fields omitted: container_type, default_value, pin_id, sub_category; nodes[] fields omitted: 5 of 9; keys omitted: 10 of 13
}
Result. parent_class, events[].node_title and results[].default_value are read from the .uasset. pins[].linked_to names the node and pin each pin connects to; where a node id repeats inside an asset, export_index identifies the endpoint.
Material graph summary
Request. How is M_TeamColorBasic built?
Before. M_TeamColorBasic.uasset is binary; the expression graph is not in any text file.
Call. Editor closed.
{"name": "ue_manage_material", "arguments": {"operation": "get_graph_summary", "material_path": "/Game/Characters/Cosmetics/M_TeamColorBasic"}}
Output. As returned; cuts are marked.
{
"material_name": "M_TeamColorBasic",
"source": "uasset_metadata_cache",
"node_count": 4,
"connection_count": 3,
"stage_count": 3,
"output_chains": [
{"output_pin": "Material", "root_node": "MaterialExpressionVectorParameter_3", "upstream_node_count": 1},
{"output_pin": "Material", "root_node": "MaterialExpressionAdd_0", "upstream_node_count": 4}
]
// [truncated] output_chains: 2 of 3 elements; output_chains[] fields omitted: exclusive_node_count, role, top_node_types; keys omitted: 6 of 12
}
Result. With the Editor closed the response has the node count, the connection count and the root node of each output chain. ue_manage_material is Experimental; with the Editor running, its get_nodes operation adds node names and name-based edges.
StateTree states and transitions
Request. What does the L_STT_FollowPlayer StateTree do?
Before. L_STT_FollowPlayer.uasset is binary, and grep -rn L_STT_FollowPlayer over the source finds nothing: no code describes this tree.
Call. Editor closed.
{"name": "ue_manage_ai", "arguments": {"operation": "get_summary", "ai_type": "statetree", "asset_name": "L_STT_FollowPlayer"}}
Output. As returned; cuts are marked.
{
"asset_name": "L_STT_FollowPlayer",
"root_state": "Root",
"state_count": 9,
"property_binding_count": 19,
"states": [
{
"name": "Wait 3s Follow",
"tasks": ["StateTreeDebugTextTask", "StateTreeDelayTask"],
"transitions": [
{"trigger": "OnStateCompleted", "to_state": "Wait 3s Follow TooClose"},
{"trigger": "OnStateCompleted", "to_state": "Follow"}
]
},
{
"name": "Follow",
"tasks": ["STT_Follow"],
"transitions": [
{"trigger": "OnStateSucceeded", "to_state": "Root"},
{"trigger": "OnStateFailed", "to_state": "Wait 3s FailFollow"}
]
}
]
// [truncated] states: 2 of 9 elements; states[] fields omitted: conditions, enabled, state_type; keys omitted: 11 of 16
}
Result. states[].tasks lists each state's tasks and states[].transitions[].to_state lists its outgoing transitions.
Niagara emitter
Request. What does the NE_ImpactCore emitter of NS_ImpactGlass render?
Before. NS_ImpactGlass.uasset is binary.
Call. Editor closed.
{"name": "ue_manage_niagara", "arguments": {"operation": "get_emitter_details", "asset_name": "NS_ImpactGlass", "emitter_name": "NE_ImpactCore"}}
Output. As returned; cuts are marked.
{
"system_name": "NS_ImpactGlass",
"emitter_name": "NE_ImpactCore",
"emitter_data": {
"loop_behavior": "Once",
"renderer_count": 2,
"renderers": [
{"renderer_type": "NiagaraSpriteRendererProperties", "facing_mode": "FaceCamera"},
{"renderer_type": "NiagaraSpriteRendererProperties", "facing_mode": "FaceCamera"}
],
"data_interface_count": 2,
"data_interfaces": [
{"di_type": "NiagaraDataInterfaceArrayFloat3"},
{"di_type": "NiagaraDataInterfaceArrayPosition"}
]
}
// [truncated] emitter_data.renderers[] fields omitted: 5 of 7; emitter_data.data_interfaces[] fields omitted: name; emitter_data fields omitted: 21 of 26; keys omitted: mode, operation, requested_path, success
}
Result. renderers[].renderer_type is the renderer class and data_interfaces[].di_type is the data interface type. ue_manage_niagara is Experimental.
Animation Blueprint graph
Request. How large is ABP_Mannequin_Base and which skeleton does it target?
Before. ABP_Mannequin_Base.uasset is binary.
Call. Editor closed.
{"name": "ue_manage_blueprint", "arguments": {"operation": "get_animgraph", "blueprint_name": "ABP_Mannequin_Base", "detail_level": "summary"}}
Output. As returned; cuts are marked.
{
"blueprint_name": "ABP_Mannequin_Base",
"target_skeleton": "SK_Mannequin",
"source": "metadata_category:anim_blueprints",
"anim_graph_node_count": 138,
"state_machine_count": 1,
"pose_link_count": 25,
"anim_node_types": {
"anim_state": 10,
"anim_state_transition": 27,
"anim_blend_space": 3,
"anim_linked_layer": 14
}
// [truncated] anim_node_types fields omitted: 15 of 19; keys omitted: 6 of 13
}
Result. target_skeleton names the skeleton and anim_node_types counts the graph's nodes by kind. Raise detail_level to read a single state machine.
PCG
Lyra ships no PCG graph, so PCG is not shown here. ue_manage_pcg get_structure reads a PCG graph with the Editor closed and is Experimental; it is listed in the comparison table below.
Mock-up, not a captured session. For C2065: 'UAbilitySystemComponent': undeclared identifier, ue_fix_errors adds the #include, adds GameplayAbilities to Build.cs and switches to IAbilitySystemInterface.
A session in Claude Code
Mock-up of a Claude Code session that calls ue_analyze_symbols, then ue_generate_code, then ue_fix_errors.
Requirements
- Windows 64-bit
- An Unreal Engine 5.7 or 5.8 C++ project. Blueprint-only projects are not supported.
- Visual Studio 2022 with the Unreal Engine C++ build tools
- An MCP client. Tested with Claude Code and Codex. AI clients are separate products and may need their own subscription.
- Python and Node.js are not required; the runtime is a prebuilt executable inside the plugin ZIP.
Install
From Fab. Install NarshaADK from the Fab listing and enable the NarshaMCP plugin in a C++ project.
From GitHub Releases
- Download the ZIP for your engine version from the latest release.
- Extract the ZIP into
YourProject/Plugins/. The ZIP already contains theNarshaMCPfolder, so the plugin lands inYourProject/Plugins/NarshaMCP/. When upgrading, remove the previousPlugins/NarshaMCP/folder first, and keep a copy if you may want to go back. - Build the C++ project once. The first build runs local setup and writes the project's MCP configuration, unless the plugin was installed in plugin-only mode.
- Open the local dashboard's AI Clients section and confirm the Claude Code or Codex connection.
- Verify from the client: call
ue_check_health. It returnsstatusandversion. The first start indexes the PDBs your build produced. While the index is building, symbol operations return"status": "symbol_index_loading", with aprogressobject once the total is known; call again when it finishes.symbol_index_degraded_budgetmeans a memory budget capped the index, and waiting does not clear it.
Connect your MCP client
The first build writes the project's MCP configuration for you. If you configure a client by hand, point it at the runtime that ships inside the plugin:
{
"mcpServers": {
"narshamcp": {
"command": "<Project>/Plugins/NarshaMCP/Source/ThirdParty/bin/narshamcp.exe",
"args": ["connect", "--project-path", "<Project>"],
"env": { "RUST_LOG": "error" }
}
}
}
- Claude Code:
claude mcp add narshamcp -- "<Project>/Plugins/NarshaMCP/Source/ThirdParty/bin/narshamcp.exe" connect --project-path "<Project>" - Codex, in
~/.codex/config.toml, with forward slashes in the path:
[mcp_servers.narshamcp]
command = "<Project>/Plugins/NarshaMCP/Source/ThirdParty/bin/narshamcp.exe"
args = ["connect", "--project-path", "<Project>"]
connect attaches the client to the project's background daemon and starts it when none is running. Clients that launch the server as a direct process, such as Cursor, use --stdio in place of connect.
Fab installs the plugin under the engine's Plugins folder (<UE>/Engine/Plugins/Marketplace/NarshaMCP or <UE>/Engine/Plugins/Fab/NarshaMCP, where <UE> is your engine install folder), not in the project. For a Fab install, replace <Project>/Plugins/NarshaMCP in the commands above with that plugin folder.
How it works
The symbol index is built from the PDBs of your own build, so it covers your project's classes, and the engine's when the engine's debug symbols are installed. It needs one successful build.
The Unreal MCP built into UE 5.8 runs inside the Editor. NarshaMCP reads the project from disk, so the Editor can be closed, and it indexes PDBs for a C++ call graph. The table below compares the two read by read.
What it cannot do (yet)
- Cooked assets cannot be read. Cooking strips the editor node graph from packaged game data.
- Blueprint data pins are returned as connections only; values are not evaluated. Only execution order is traced.
- Latent, async and delegate edges are approximated.
- Blueprint-only projects are not supported; a C++ project is required.
- A running Editor is needed by every operation that creates or edits an asset, by actor spawning, by automation tests, by
ue_fix_errorsget_compiler_results, and by the node names and name-based edges ofue_manage_materialget_nodes. The read operations named in the table above run with the Editor closed. - A response above the runtime's inline size limit comes back as a
detail_handleto a local file, whichue_cache_controlresolve_detail_handleloads. Ask for a narrower read, such as one emitter or alimit, to get the data inline.
How far it reads, next to other Unreal MCP servers
What each server can read and hand to an agent, and whether that read works with the Editor closed. ✅ reads the project's files; the Editor never has to run · ● in the running Editor (a design choice) · — not provided · ? not confirmed at the pin shown · superscript p = partial.
Where other servers go further
- The engine's built-in server ships with Unreal Engine as an engine plugin, so there is nothing separate to download. NarshaADK installs as its own plugin package, with a prebuilt runtime inside it. (Unreal MCP (built into UE 5.8))
- sam-david's server drives the Editor through the engine's built-in Python remote execution, so most of its tools work without compiling a plugin into the project. NarshaADK needs a C++ project (Blueprint-only projects are not supported) and, for its symbol reads, the PDBs from one successful build. (sam-david/unreal-mcp@8b88ec3 · src/transports/connection-manager.ts, sam-david/unreal-mcp@8b88ec3 · src/transports/python-exec.ts, sam-david/unreal-mcp@8b88ec3 · src/tools/blueprint.ts)
- VibeUE and ZiggyMar's server are open source under the MIT license, so they can be read, forked and changed. NarshaADK is a commercial product: its Unreal C++ plugin source ships in the package, and its runtime's source does not. (kevinpbuckley/VibeUE@0ea59b3 · LICENSE, ZiggyMar/unreal-mcp@d1a8539 · LICENSE)
- Reads with the Editor closed — NarshaADK: Binary asset parsing, the compiler symbol index, parsed config and build-log parsing all run with the Editor closed. Where a row is marked partial, that row's own note names what the mark does not cover. Writing assets, spawning actors and running automation need the Editor. Requires a C++ project and the PDBs from one successful build; symbol reads reflect that build until the next one. Cooked assets are not supported.
- Reads with the Editor closed — Unreal MCP (built into UE 5.8): In-process toolsets served by the Editor over HTTP. This column is the only one not read from a repository: it was measured by enumerating the toolsets live in one Editor at the engine version shown, and toolsets ship behind plugins, so a toolset missing from that enumeration is absent from that install rather than from the engine.
- Reads with the Editor closed — VibeUE: Extends the Editor's own MCP endpoint; no separate server.
- Reads with the Editor closed — unreal-mcp (sam-david): A standalone server that sends Python to the Editor through its built-in remote execution, with the Remote Control API as a fallback and an optional bridge plugin for graph commands; only its build tools, which run the engine build tool as a child process and return the parsed compiler output, read with the Editor closed, while asset, Blueprint, material, level and sequence reads need the Editor.
- Reads with the Editor closed — unreal-mcp (ZiggyMar): A Node server forwards tool calls over local TCP to a C++ plugin running inside the Editor. The log, C++ source and C++ compile tools read files or run the build from the server process, but they locate the project by asking that Editor first.
- Blueprint graphs: nodes, pins, exec flow — unreal-mcp (sam-david): The graph node listing tool forwards to an optional bridge plugin whose source is not in the repository at this commit, and without that plugin it returns an error; what the plugin would return could not be checked.
- Blueprint variables, functions, components, defaults — Unreal MCP (built into UE 5.8): Blueprint variables, functions and defaults are named reads; the enumeration names no read of a Blueprint's component list, because the Blueprint toolset's component units bind or list events and the units that read components belong to the actor toolset and take a placed actor.
- Blueprint variables, functions, components, defaults — unreal-mcp (sam-david): Its Blueprint info tool returns the parent class and the component list, the latter gathered by spawning a temporary actor; variables, functions and defaults are not among the fields its script reads, although the tool description names them.
- Material graphs and expressions — NarshaADK: Offline node topology and graph summary; live name-based edges when the Editor runs. The material tool is Experimental.
Capability presence measured from each compared open-source project's public source at the commit and date shown, from a live toolset enumeration for the engine's built-in server at the engine version shown, and for NarshaADK from this product's own tool registry at the version shown. Compared: servers that publish their own comparison with another Unreal MCP server or with the engine's built-in one, plus that built-in server as the baseline; this does not cover every Unreal MCP server. Not a ranking. Where the evidence supports a narrower statement than the mark, a note in the README says so. Not affiliated with or endorsed by Epic Games or the compared projects. Corrections welcome in this repository's Discussions.
Supported versions
Each release includes these files:
NarshaMCP-v<version>-UE5.7-Release-RustMCP.zipandNarshaMCP-v<version>-UE5.8-Release-RustMCP.zipare the same plugin ZIPs submitted to Fab. They contain the plugin's Unreal C++ source, which your project builds, and the runtime as a prebuilt executable. The runtime's own source is not included.narshamcp-windows-x64.exeis the standalone Windows runtime, byte-identical to the runtime inside the ZIPs.
Each release's notes list the SHA-256 digest of every file. Third-party software notices ship inside the runtime: run narshamcp --licenses (or narshamcp --licenses-json).
License
NarshaADK is licensed under the Fab End User License Agreement (the "Fab Standard License"). Copies downloaded from this repository are granted the same terms directly by Next Stage Inc.: the Personal tier applies at no charge if you, together with any controlling entity and any entity under common control with you, did not generate more than the Fab revenue threshold from commercial activity in the digital content industry in the last 12 months (USD 100,000 when this was written; the Fab EULA states the current figure). Above it a Professional license acquired through the Fab listing is required. LICENSE.md states the threshold and the full terms.
Support
Documentation and examples are at narshaadk.ai. To ask questions or report issues, use the support channel on the Fab listing.
NarshaADK is built and maintained by Next Stage Inc.