How it works
Two processes, and why it is built that way.
HullForge is two programs, not one.
The two halves
The editor plugin is C++ running inside Unreal. It holds every tool, and it is the only part that touches your project. It opens a TCP listener on loopback and does nothing until something connects.
The connector is a compiled binary that Claude Desktop launches. It speaks MCP over stdin and stdout to Claude, and a small length-prefixed JSON protocol over the socket to the editor. It holds no tools of its own.
Claude Desktop <--MCP/stdio--> connector <--127.0.0.1--> Unreal editor
Why not one process
Embedding an MCP server directly in the C++ plugin would mean one program instead of two, which sounds simpler. It is not, for three reasons.
The MCP surface changes far more often than the editor bindings. Splitting them means the protocol layer can be rebuilt in seconds without recompiling a C++ module and restarting Unreal.
The editor’s game thread is precious. Everything that touches a UObject
has to run on it, and anything slow there freezes the editor. Keeping the
protocol, framing, retries and transport outside means only the actual work is
marshalled onto that thread.
Crash isolation. A fault in protocol handling takes down a small external process, not your editor with unsaved work in it.
How the connector finds the editor
You configure nothing. Each running editor advertises itself in two places:
| File | Purpose |
|---|---|
<project>/Saved/HullForge/session.json |
Project-local, useful when debugging |
%LOCALAPPDATA%/HullForge/sessions/<project>-<pid>.json |
User-global, how clients discover editors |
Each holds a port, a random session token, the project name and path, and a process id. Both are written when the editor starts listening and removed when it closes. Entries left behind by a crash are swept on the next startup, and clients check the process id anyway, so a dead editor never masquerades as a live one.
This is why several projects can be open at once, and why nothing needs reconfiguring when the port changes.
Why the tool list changes on its own
The connector declares the MCP listChanged capability and watches that
registry. When an editor appears, it tells Claude to re-fetch the tool list;
when one goes away, it does the same.
That is what removes the restart. MCP servers are launched by the client, so a connector that starts before the editor would otherwise report an empty list forever, because the client has no reason to ask again.
Two tools are implemented in the connector itself rather than the editor, so
they are always available, including when no editor is running.
hf_bridge_status reports which editors it can see and what to do if the answer
is none, and hf_list_tool_areas reports what areas exist. That means “nothing
is here” is never a silent state: an empty list would be baffling, whereas two
tools that explain themselves is a diagnosis.
Two more, hf_enable_tool_areas and hf_disable_tool_areas, appear only when
catalog mode is on, because a tool that would do
nothing is worse than a tool that is absent. That is the same listChanged
mechanism again: switching an area on re-fetches the list without a restart.
What crosses the wire
Only what a tool returns. The connector holds no state between calls beyond a short deduplication cache, and nothing is written to disk except the session files described above.
Every mutating call carries an operation_id. If the transport drops and the
call is retried, the editor recognises the id and returns the original result
rather than applying the change twice.