Skip to main content
Glama
chdnl0420-svg

ProjectM QA MCP

NX3 Unity MCP

NX3 Unity MCP is a Unity Package Manager package that installs a small Unity Editor command bridge and ships a bundled Node MCP server.

The package is intentionally ProjectM-focused. It avoids the long-lived WebSocket bridge used by general Unity MCP packages and uses request/response JSON files under .codex/unity-commands.

Install in Unity

Use Unity Package Manager with Add package from git URL.... The URL must include the .git suffix:

https://github.com/chdnl0420-svg/UnityMCP.git

You can also edit Packages/manifest.json directly:

{
  "dependencies": {
    "com.nx3games.unity-mcp": "https://github.com/chdnl0420-svg/UnityMCP.git"
  }
}

For local development, add this folder as a local package:

{
  "dependencies": {
    "com.nx3games.unity-mcp": "file:D:/Project/UnityMCP"
  }
}

Related MCP server: Unity-MCP

Codex MCP Registration

Add a server entry to C:\Users\NX3GAMES\.codex\config.toml. Adjust the args path to the installed package location.

[mcp_servers.nx3-unity-mcp]
command = "node"
args = ['D:\Project\UnityMCP\Server~\build\index.js']
startup_timeout_sec = 120

[mcp_servers.nx3-unity-mcp.env]
PROJECTM_UNITY_PATH = 'C:\Program Files\Unity\Hub\Editor\2022.3.76f1\Editor\Unity.exe'
PROJECTM_DEFAULT_PROJECT_PATH = 'C:\Project\CLIENT_KSH_ASIA_L\client\ProjectM'
PROJECTM_COMMAND_ROOT = 'C:\Project\CLIENT_KSH_ASIA_L\client\ProjectM\.codex\unity-commands'

Build and Test

cd D:\Project\UnityMCP\Server~
npm install
npm test
npm run build

The generated MCP server entrypoint is:

Server~/build/index.js

Editor-tool automation and in-editor tests

Editor tools (EditorWindow) cannot be driven by the runtime NGUI commands: those go through NguiRaycast and UICamera.Notify, a path an editor window never takes, and IMGUI keeps no retained widget tree to walk instead.

unity_editor_* tools cover that: open a window by type or menu path, dump its instance fields, its callable methods and its IMGUI layout rects, set fields by dotted/indexed path with a before/after readback, inject real clicks and keystrokes, run menu items, read the Console, and read or write EditorPrefs/PlayerPrefs.

unity_run_tests_in_editor, unity_get_test_results and unity_list_tests run Unity Test Framework tests inside the already-open editor via TestRunnerApi. The older CLI test tools spawn a second Unity in batch mode, which cannot work while an editor holds the project lock.

See Documentation~/projectm-qa-mcp.md for the mechanisms, the coordinate-space gotchas, and the current limitation on window pixel capture.

Testing hover: unity_editor_move

unity_editor_drag cannot test hover. Its moves are MouseDrag events, and the MouseDown that opens the gesture registers a pressed button, so UI Toolkit delivers them as a MouseMoveEvent with pressedButtons != 0. Everything that reacts to a bare cursor — GraphView highlighting the edge under the mouse, a rollover tint, a hover tooltip — sits on the other branch and never runs.

unity_editor_move sends one MouseMove with no button held, and nothing else: no MouseDown, MouseDrag or MouseUp, so it cannot move a node, change the selection, start a marquee, pan the view or open a context menu. Coordinates follow unity_editor_drag — content-local by default with the dock tab strip added automatically, coordinateSpace: "host" for the raw host-view space unity_editor_click uses. Consecutive calls on the same window carry the delta between the two points, and every window keeps its own last position, so one window's hover path never depends on another's.

// hover the middle of a GraphView, then photograph the result
{ "tool": "unity_editor_move",
  "arguments": { "windowType": "MoveProbeWindow", "x": 300, "y": 160 } }
{ "tool": "unity_editor_window_capture",
  "arguments": { "windowType": "MoveProbeWindow", "outputPath": "C:/tmp/hover.png" } }

The response reports what was sent — target window, resolved x/y, deltaX/deltaY, pressedButtons: 0, eventType: MouseMove and the raw sendEventReturned. None of that is proof the UI reacted, exactly as with click and drag: verify with unity_editor_window_capture right after the move, or by reading the tool's own state with unity_editor_get_field.

IMGUI is a special case worth knowing: OnGUI only receives EventType.MouseMove in a window that set EditorWindow.wantsMouseMove, which is Unity's rule for a real mouse too. unity_editor_move turns that flag on for the send and puts it straight back, and reports both the window's original setting and whether it did so. Pass ensureWantsMouseMove: false for strict production fidelity.

Window/NX3 MCP/Move Probe (plus (Floating) and (Docked)) opens a window built for checking all of this, and Tests/Editor/EditorMoveTests.cs asserts it. Package tests only compile in a project that lists the package under testables in Packages/manifest.json:

{ "testables": ["com.nx3games.unity-mcp"] }

Opening a context menu: unity_editor_context_click

What unity_editor_click with button: 1 misses is one event, not the button. Measured on 2022.3.62, Windows, against MoveProbeWindow:

Path

Right unity_editor_click

unity_editor_context_click

UI Toolkit ContextualMenuManipulator

already worked — it listens on MouseUpEvent

works

GraphView BuildContextualMenu

already worked — same manipulator path

works

IMGUI GenericMenu on EventType.ContextClick

never fired

works

So the gap is IMGUI: OnGUI code builds its menu by testing Event.current.type against EventType.ContextClick, and unity_editor_click delivers no such event, leaving that branch dead. Unity's native input layer synthesises ContextClick for a real right-click; EditorWindow.SendEvent does not, so the bridge sends it itself — which reaches IMGUI menu code and makes the gesture what a real right-click is, rather than only what UI Toolkit happens to accept.

unity_editor_context_click sends three events in order — MouseDown (button 1), MouseUp (button 1), then ContextClick at the same point — and reports mouseDownReturned, mouseUpReturned and contextClickReturned separately. The press pair still goes first because a menu is a gesture, not a lone event: handlers that track which button is down, dismiss an open popup, or take the menu's anchor from the press would otherwise see a ContextClick arrive out of nowhere.

// right-click a GraphView, then check what opened
{ "tool": "unity_editor_context_click",
  "arguments": { "windowType": "MoveProbeWindow", "targetMode": "element", "elementName": "graph-area" } }
{ "tool": "unity_editor_window_capture",
  "arguments": { "windowType": "MoveProbeWindow", "outputPath": "C:/tmp/menu.png" } }

Targeting is the same resolver unity_editor_click uses — a point, an entryIndex, or targetMode: "element" with the element filters — so a right-click can aim at exactly the pixel a left click just hit. Coordinates therefore follow click, not drag: x/y are host-view by default, the space unity_editor_element_query reports. Pass coordinateSpace: "content" for the content-corner convention unity_editor_drag and unity_editor_move default to. There is no button or clickCount parameter, because a context click is one right-button gesture by definition.

The three *Returned values are raw SendEvent returns, not proof a menu opened — the same caveat as click, drag and move. One case looks identical from the outside and is worth knowing: a menu whose BuildContextualMenu appends no items is built and then displays nothing at all.

A menu that does display blocks the editor until it is dismissed. Unity shows it from inside SendEvent, so the main thread and this bridge are held for the duration — 4s, 24s and 123s measured for the same click, differing only in how long the menu stayed up. contextClickBlockedMs reports it. Because nothing else can run meanwhile, the popup cannot be inspected while it is up; confirm the menu afterwards by reading the tool's own state with unity_editor_get_field.

Window/NX3 MCP/Move Probe carries a GraphView with a BuildContextualMenu override that always appends an item, named graph-area, alongside hover-area and imgui-strip; _graphContextMenuCount and _contextMenuCount are readable with unity_editor_get_field. Tests/Editor/EditorContextClickTests.cs asserts the event sequence, and deliberately aims away from the GraphView so no popup is left on screen mid-run.

Aiming, scrolling, selecting, compiling

  • unity_editor_element_query finds UI Toolkit elements by name, USS class, type or text and returns where each one is, with enabled, visible and pickable. unity_editor_click, unity_editor_move and unity_editor_scroll take the same filters with targetMode: "element", so input can aim at "the button named Build" instead of a pixel that a resize invalidates.

  • unity_editor_context_click opens a context menu, which unity_editor_click with button: 1 never did — see below.

  • unity_editor_scroll turns the wheel at a point: ScrollViews, long inspectors and GraphView zoom.

  • unity_editor_selection_get / unity_editor_selection_set read and set the editor selection, which is how an inspector-driven tool is put in front of the asset it should act on.

  • unity_editor_refresh reimports, recompiles and waits for the verdict; unity_editor_compile_status reads the last one. errorCount is Unity's own compiler output, so this is usable as a gate, and the result is stored on disk so it survives the domain reload a recompile causes.

  • unity_editor_wait_for_field polls a field until it reaches an expected value instead of sleeping a fixed amount after triggering slow work.

Tool Success Criteria

unity_status must return real JSON data, not just a connection signal. Editor commands must write response JSON with success, command, elapsedMs, logs, outputs, and error. unity_enter_play_mode and unity_exit_play_mode must wait for editor_status to report the requested isPlaying value before returning success. unity_click_ui_text must resolve a visible NGUI label to its clickable target instead of requiring manual dump_ui coordinate transfer. When a label has no direct clickable target, text-click and dump_ui fallback to a nearby clickable UI object and mark it with clickResolution=nearest. dump_ui omits off-screen UI by default to keep QA responses small; pass includeOffscreen=true when scroll-buffered or hidden coordinates are needed. unity_wait_ui_text must poll dump_ui until expected text appears or return a timed-out response with the last observed UI dump. unity_click_ui_text_and_wait must click a visible label and return the post-click matched UI text in one response. unity_run_ui_text_qa_flow must enter PlayMode, wait for initial text, capture before/after screenshots, click text, wait for expected text, and return every step in one response. Test tools parse Unity Test Framework XML and expose failure counts. Screenshot tools verify that a PNG exists and has non-zero size, and that its pixels are not a single flat colour — a blank capture is reported as a failure rather than handed back as an image. Screenshot responses also report requested dimensions, actual dimensions, and matchesRequestedSize; set requireRequestedSize=true when dimension mismatch should fail the tool call.

Frame-sequence recording (fast motion)

For fast motion that a single unity_capture_screenshot round-trip misses, record the game view as a PNG frame sequence:

  • unity_start_frame_capture — starts capturing the game view camera on every editor update into a frames folder and returns immediately. Run the fast action next. Bounded by maxFrames and maxDurationSeconds (auto-stops).

  • unity_stop_frame_capture — stops recording and returns framesDir, frameCount, and fps. Read the frame_NNNNN.png files in order to inspect the motion.

Use recording only when a single screenshot cannot catch the change; a normal screenshot is cheaper for static checks.

Code, QA, and UI Tools

Beyond PlayMode/screenshot/click tools, the server exposes code-iteration, QA inspection, and UI mutation tools. See Documentation~/nx3-unity-mcp.md for the full list. Highlights:

  • unity_editor_refresh — reimport assets and recompile scripts, wait for the domain reload to settle, and return the compiler's own verdict. Use this after editing C# so the next QA step runs against fresh code. Scripts only compile in Edit mode, so exit PlayMode first.

  • unity_editor_compile_status / unity_editor_console_read / unity_editor_console_clear — inspect compilation state and the Editor console for errors and warnings.

  • unity_inspect_object / unity_find_objects / unity_scene_info / unity_get_hierarchy — discover and inspect scene objects for QA.

  • unity_set_active / unity_set_label_text / unity_set_input_text / unity_set_sprite — mutate existing NGUI widgets for UI checks.

New typed tools appear after the MCP server is reconnected. The underlying bridge commands are usable immediately through unity_execute_editor_command.

Recovery Notes

If Unity does not answer a command, inspect:

<ProjectM>/.codex/unity-commands/requests
<ProjectM>/.codex/unity-commands/responses
<ProjectM>/.codex/unity-commands/processed
%LOCALAPPDATA%/Unity/Editor/Editor.log

Use unity_kill_stale without kill=true first. It reports candidates and reasons before termination is requested.

F
license - not found
-
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

View all related MCP servers

Related MCP Connectors

  • Control Unreal Engine to browse assets, import content, and manage levels and sequences. Automate…

  • A paid remote MCP for Unity-MCP, built to return verdicts, receipts, usage logs, and audit-ready JSO

  • Drive a live Cinevva game session: edit game files, import CC0 assets, preview changes.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/chdnl0420-svg/UnityMCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server