MCP Server
The MCP server is a small Python process that exposes both backends as tools for an MCP client: the Unreal plugin over HTTP, and the Blender add-on over its socket. Your client talks to one server, and the server talks to whichever editor the tool belongs to.
How it fits together
┌─ HTTP POST 127.0.0.1:8791 ──► MifBridge (Unreal editor)
client ── MCP (stdio) ──► server.py
└─ framed JSON 127.0.0.1:8792 ─► MifBlender (Blender add-on)
Two transports and no shared dispatch. Unreal tools go to the editor's HTTP API, and the Blender tools, the ones named bl_*, go to the add-on's socket.
Install and run
cd <YourProject>/Plugins/MifBridge/Content/Python/MifBridge/mcp-server
pip install -r requirements.txt
python server.py # stdio transport
python server.py --debug # plus request/response tracing on stderr
It needs Python 3.10 or newer. The dependencies are the MCP SDK and requests. A copy from GitHub keeps the server in tools/mcp-server/ instead.
Connect a client
With Claude Code, the editor can do this for you: SETUP ▸ Set up Claude Code in the MifBridge panel installs the dependencies into a private environment and registers the server with the editor's own port and token. See Installation.
By hand, add a project-scoped .mcp.json (or put the same block in your user config):
{
"mcpServers": {
"mif-bridge": {
"command": "python",
"args": ["<YourProject>/Plugins/MifBridge/Content/Python/MifBridge/mcp-server/server.py"],
"env": {
"MIF_BRIDGE_URL": "http://127.0.0.1:8791/api",
"MIF_BRIDGE_TOKEN": "change-me-to-match-the-editor"
}
}
}
}
The server key (mif-bridge) is only a local name that sets the prefix your client shows on tool names. The token must match the editor's MIF_BRIDGE_TOKEN. Do not commit a real secret.
Cursor, Claude Desktop and Codex by name. From 1.2, the setup script writes the same checked entry into another client's own config, keeping the file's other servers and a backup of it: python Content/Python/MifBridge/setup_claude_code.py --project <folder with the .uproject> --client cursor (or claude-desktop, or codex; repeat --client for several). Nothing is written for a client you do not name.
Configuration
| Variable | Default | Notes |
|---|---|---|
MIF_BRIDGE_URL |
http://127.0.0.1:8791/api |
The editor's API base URL. Unset, it follows MIF_BRIDGE_PORT when that is set. |
MIF_BRIDGE_TOKEN |
dev |
Must match the editor. Sent as the X-Mif-Token header. |
MIF_BRIDGE_TIMEOUT |
30 |
Per-request timeout in seconds. Raise it if compiles are slow. |
MIF_BRIDGE_DEBUG |
unset | 1 logs every request and response to stderr, like --debug. |
MIF_AGENT |
the client's own name | A name for the caller, sent as the X-Mif-Agent header and shown in the panel's Activity tab. Unset, the name the MCP client gives for itself is used. |
MIF_TOOL_PACKS |
lean |
Which tool packs to load. See below. |
MIF_GENERATIVE |
unset | off switches the generative bl_gen_* tools off for every project. See Using MifBridge Without Generative AI. |
MIF_BLENDER_HOST / MIF_BLENDER_PORT |
127.0.0.1 / 8792 |
The Blender add-on's socket, where a session starts. A MifBlender on another port is found anyway; see below. |
MIF_BLENDER_TOKEN |
falls back to MIF_BRIDGE_TOKEN |
Must match the add-on's Token preference. |
MIF_BLENDER_CONNECT_TIMEOUT |
3 |
A closed Blender reports "refused" in about 3 s instead of hanging. |
MIF_BLENDER_PROBE_TIMEOUT |
5 |
bl_status only. |
MIF_BLENDER_TIMEOUT |
180 |
Per Blender operation. Geometry work can be slow. |
See Configuration for the editor's own variables and the port map.
Tool packs: load only what you need
Every tool definition a client loads is sent to the model on every turn, used or not, and MifBridge has several hundred tools. So by default the server registers only three: find_tools, mif_call and run_python. find_tools searches every tool, loaded or not, and names the pack that has it; mif_call calls any tool by name. Loading a pack registers its tools directly instead.
| Pack | Covers |
|---|---|
core |
Orientation and the commonest reads and writes: self_audit, describe_endpoint, mif_help, find_tools, the pack tools, property reads and writes, and batch. Loaded with any named pack. |
assets |
Unreal assets: find, create, rename, move and delete; import and export; materials and material instances; meshes; DataTables; collections; cooked content; MetaSounds; source control |
animation |
Skeletons, animation sequences, montages and Animation Blueprints, Sequencer, Control Rig and IK, MetaHuman, Live Link, StateTrees |
world |
Building a level: actors, the viewport camera and captures, levels and World Partition streaming, landscapes, foliage, water, PCG, navigation, collision and physics |
rendering |
Lights and cameras, the viewport and captures, view layers, render settings and performance views |
vfx_niagara |
Niagara: an emitter's module stack, renderers, emitter and system properties, event handlers, User parameters, compiling, and a placed effect's live particles |
vfx_look |
How an effect looks: particle materials, flipbook atlases rendered in Blender, flicker lights, palettes, and effect recipes |
vfx_volumes |
Volumetric effects: OpenVDB frames as a Sparse Volume Texture, the volume material, and real-time gas from the Niagara Fluids templates |
blueprint_core |
Create, open, compile and save any Blueprint. Loaded with ui and systems |
ui |
UMG widget trees, bindings and previews, live widgets, details panels, and pipeline recipes |
systems |
Blueprint authoring: variables, functions and event graphs, nodes and pins, components, interfaces, graph patches, calling functions, and reading cooked Blueprints |
dev |
Play-In-Editor, console commands, undo transactions, Live Coding, tracing and frame stats, project settings, automation tests, the game framework, gameplay abilities and tags |
blender_core |
Start here for Blender: status, scene and object info, the call log, batch, undo, transforms, visibility, collections, scenes and .blend files |
blender_modeling |
Primitives, mesh from data, curves, text, trees, rocks, ground cover, kit pieces and LODs, booleans, converting, the modifier stack, origins and the placement audit |
blender_mesh_edit |
Editing a mesh's parts: selecting, deleting, bevel, bisect, clean-up and decimate, UVs, vertex colors, geometric queries and mesh quality, and a garment checked against its body |
blender_materials |
Materials, Principled settings, procedural patterns, image textures, volumes, texture baking |
blender_nodes |
Node groups, nodes and links, geometry-node groups, and the compositor |
blender_lighting |
Lights, IES, light linking, light probes, cameras, aiming, the world, and the viewport's shading |
blender_animation |
Keyframes, actions, NLA, drivers, armatures and bones, shape keys, weights, constraints, and sound on the timeline |
blender_rigging |
Control rigs: Rigify metarigs, rig types, bone collections, generating and regenerating, IK/FK snapping, FK and IK controls over a deform skeleton, and a pose library |
blender_motion |
Editing motion already keyed: motion paths, a pose change spread over frames, keys smoothed, thinned or blended, transforms matched between objects, action slots, NLA strips, and Grease Pencil drawing |
blender_physics |
Rigid bodies, cloth, soft body, fluid, dynamic paint, force fields, baking, and particles |
blender_render |
Render settings, stills and animations, color management, view layers, and comparing a render to a reference |
blender_io |
Scene and mesh import and export, the game-ready recipe, the Blender-to-Unreal round trip, a whole kit sent to Unreal in one call, and generated assets |
misc |
Anything no category rule places, so nothing is left out |
- Choose at startup with
MIF_TOOL_PACKSin the server's environment, for example"MIF_TOOL_PACKS": "core,world"in.mcp.json.corecomes with any named pack. Unset, orlean, is the three-tool default above;allregisters every tool, as 1.0.0 did by default. - Add one mid-session.
mif_list_packsshows every pack and what is loaded, andmif_enable_packloads one. Clients that support tool-list updates show the new tools at once; otherwise, restart with the pack added toMIF_TOOL_PACKS. - Nothing becomes unreachable. Every tool belongs to a pack, and
find_toolsalso searches the tools that are not loaded, naming the pack that has them. - Pack prompts. Each pack also appears as a prompt,
mif-pack-<name>, which your client lists alongside its commands. It tells the agent what the pack is for, enables it, and carries the working rules. A prompt costs nothing until you pick it.
How it behaves
stdout is the protocol. It carries the MCP JSON-RPC stream, so all logging goes to stderr. Anything that prints to stdout would corrupt the stream.
Every tool returns the backend's JSON unchanged.
ok,errorand the read-back fields come straight from the editor.Blender is connected lazily, on the first Blender call and never at startup. If Blender is closed, that call answers
{"ok": false, "error": ...}naming the fix. It is never a server fault you have to debug.Any Blender, on any port. From 1.2, each MifBlender records where it listens.
bl_statuslists the other live ones, andbl_statuswithportorpidswitches the session to one, with no restart. When the usual port refuses and one other MifBlender is running, the refusal names it.The Blender connection is one serialized socket, so a long operation blocks the ones behind it.
bl_statuslimits its own wait, and when the line is busy it reports which operation holds it and for how long.The timeouts are a ladder. The add-on abandons a job after 150 seconds, which must stay below
MIF_BLENDER_TIMEOUT. That way Blender gives up first and the socket carries a real error, instead of the server abandoning a call that Blender goes on executing.A write is never run twice. Every Unreal call carries a request id. When a write's reply times out, the answer says the outcome is unknown and is not retryable, with the id; the identical call sent again replays the first result, and
request_statusreads what became of it. A read that times out stays retryable.It checks the versions it talks to. The plugin and the add-on report their protocol version and what they accept. When that disagrees with what the server's tools send, the first reply says which side is older, what it costs by name and how to update. A newer MifBridge seen by the plugin's update check is mentioned once, on the first reply from that editor.
Shaping a response
From 1.2, every tool takes a few reserved keys, in a direct call's arguments or inside mif_call's args. They are added to no schema, and the editor never sees them.
| Key | What it does |
|---|---|
_select / _omit |
Keep or drop dotted paths of the answer, through lists row by row. ok and error are always kept, and a selected path found nowhere is named. |
_page with _cursor |
_page 50 returns 50 items of the answer's longest list with a nextCursor. Later pages come from the server's copy of that one read, so the editor is not called again and no row is skipped or repeated. |
_wait |
On a job-status tool, _wait 120 polls every 2 seconds until the job ends, sending a progress notification each time. |
Editors and games the server starts
From 1.2, the server can run Unreal for you, as processes it owns:
mif_editorstarts an editor for a.uprojecton a free port with dialog-free flags, waits until the bridge answers as that process, and stops, restarts or lists the editors it started. It refuses to stop one while packages are unsaved, unless told to discard them.buildbuilds the editor target, refused while an editor has the project open, with compiler errors returned by file and line.mif_runstarts the game standalone, or a packaged build, with named profiling flags and a trace from the first frame, and converts a map to World Partition or builds its HLODs through the engine's own commandlets.mif_analyze_tracereads a recorded trace back with no window: the slowest timers on a thread and the frames over a budget.mif_inireads a config key layer by layer, as the engine resolves it.
Updating from 1.0.0
The server moved from tools/mcp-server/ to Content/Python/MifBridge/mcp-server/, where Fab's rules put a plugin's Python. A config written for 1.0.0 names .../tools/mcp-server/server.py, which the update removes. Press SETUP ▸ Set up Claude Code again, which replaces its own earlier entry, or change that path in the config's args. A copy from GitHub is unchanged: it keeps tools/.
1.0.0 also registered every tool by default. 1.1 registers three and reaches the rest through find_tools and mif_call (see Tool packs); set MIF_TOOL_PACKS=all for the old behavior.
Upgrading from 0.2.0
The server moved from tools/ue5-mcp-bridge/ to tools/mcp-server/. An old .mcp.json keeps working, because a small forwarding shim is left at the old path and prints a deprecation notice to stderr. Update the path when convenient; the shim will be removed in a later release. This applies to the GitHub download: the store package, from Fab, mifshowcase.store or Gumroad, does not carry the shim, because no store release ever used the old path.