Unity代码自动开发协议

Unity Code MCP是一个专注于编码的模型上下文协议(MCP)服务器,使AI代理能够自主编写Unity代码,并通过在游戏视图中实际操作来验证它们构建的运行时UI Toolkit接口。

  • by hackerzhuli
  • Rust
  • 本地部署
  • 11 星标
  • 更新于
在 GitHub 查看 hackerzhuli/unity_code_mcp

使用说明

Unity Code MCP

Description

Unity Code MCP is a coding focused Model Context Protocol (MCP) server that enables AI agents to write Unity code autonomously, and to verify the runtime UI Toolkit interfaces they build by actually operating them in the Game View.

🎯 Why Unity Code MCP?

Unity Code MCP is a high-performance, coding-focused MCP server built in Rust. It handles Unity's compilation cycles gracefully while providing only the essential tools needed for autonomous code development: compilation feedback, test execution, console logs, and hands-on automation of the running game's UI Toolkit interface.

This MCP server enables AI agents to develop Unity code autonomously with exceptional speed and reliability - write code, compile, fix compile errors, test, fix bugs, repeat, just like how a human would.

Autonomous UI Toolkit work

UI code is where AI agents usually stop being autonomous. The code compiles and the tests pass, but whether the interface actually works is invisible to the agent, so a human has to enter Play Mode, click around, and report back what went wrong.

Unity Code MCP closes that loop. An agent can enter Play Mode itself, read the runtime UI Toolkit tree of the Game View, click a button, type into a field, read what the game logged in response, inspect an element's layout and resolved styles when it looks wrong, and take a screenshot of the result - all without a human touching the Editor.

That turns "I wrote the UI, please check it" into an agent that verifies its own UI work the way a developer would:

  • Does the button do anything? Click it, then read the console.
  • Why is it not visible? Inspect its world bounds, display, visibility and resolved styles, like the UI Toolkit Debugger would.
  • Why does the click not land? The click is rejected with a reason, e.g. the element is disabled or covered by another element.
  • Does it look right? Capture the Game View and look at the image.

✨ Key Features

  • 🤖 Coding-Optimized: Streamlined tools designed for efficient AI agent coding workflows
  • 🎮 Runtime UI Automation: Agents can drive and inspect the runtime UI Toolkit interface in the Game View, so UI work can be verified without human intervention
  • ⚡ Lightweight and High Performance: Built in Rust for exceptional speed, minimal memory usage, and lightning-fast response times. Uses only 1MB memory when the workspace is not a Unity Project, and only 10MB when it is and Unity Editor is open for the project. Also, the communication with Unity Editor is based on UDP, so it's minimal load for your system.
  • 🎯 Efficient Communication: Minimal token usage with essential operations, only returns relevant information. Console logs merge repeated messages and are capped per call, and UI snapshots are compact YAML rather than a raw visual tree dump
  • 🧪 Test-Driven: Comprehensive test execution and reporting capabilities
  • 📦 Self-Contained: Single binary with no runtime dependencies (no Node.js, Python, or .NET required)

🛠️ MCP Server Tools

Code workflow

refresh_asset_database

  • Triggers Unity compilation and asset processing
  • Returns compile errors and other non compile warning and errors only include the logs during the refresh
  • Handles domain reload gracefully

Asset database refresh tool in action:

run_tests

  • Runs Unity tests with comprehensive reporting
  • Provides detailed stack traces and logs for failures
  • Supports both EditMode and PlayMode tests

Test execution tool in action:

get_console_logs

  • Reads the Unity console output collected while the server is connected
  • Repeated messages with identical content are merged into one entry with a repeat count, the way Unity's console collapses them
  • Filter by log level (Error, Warning, Info) and by a substring of the message
  • At most 20 entries per call, newest last, and the response says how many entries matched in total so the agent can narrow its filter
  • Stack traces are left out unless asked for, because they are long and only needed once the interesting entry is found

Play Mode control

enter_play_mode / exit_play_mode

  • Enters or exits Play Mode and waits until Unity confirms the new state, instead of assuming it worked
  • Entering Play Mode reloads Unity's script domain, so the wait matters, and it is what makes the Game View tools below usable without a human pressing play
  • Unity cannot compile scripts while playing, so run_tests only works outside of Play Mode, and refresh_asset_database only compiles there. An agent that changed code exits Play Mode, compiles, and enters it again - which reloads the script domain and therefore invalidates the UI element refs it held
  • A refresh in Play Mode is left to Unity to accept or refuse: it starts one only when the Script Changes While Playing preference is Recompile After Finished Playing, because the other options would recompile immediately and either reload the domain under the running game or stop Play Mode. Such a refresh imports assets and never compiles, so it reports compilation_occurred: false and cannot report compile errors

Game View automation (runtime UI Toolkit)

These tools operate on the runtime UI Toolkit content rendered in Unity's Game View, and require Play Mode. Paused Play Mode works too, which is useful for inspecting a specific frame.

ui_toolkit_snapshot

  • Compact YAML snapshot of every active UIDocument, with element types, their relevant properties, and an automation ref per element
  • The starting point: the agent reads this to see what the game is showing, then uses the refs with the tools below
  • Implementation noise of built-in controls is folded away, invisible subtrees are omitted, and long runs of identical siblings are truncated, so the snapshot stays small enough to read

ui_toolkit_get_element_hierarchy

  • Type, name and USS class hierarchy below one element, with an optional depth
  • Unlike the snapshot this expands built-in implementation nodes and invisible descendants, which is how an agent finds an element the compact snapshot folded away

ui_toolkit_get_element_debug_info

  • Layout, geometry, visibility state, USS classes, stylesheets, inline and resolved styles, and remaining properties of one element
  • The UI Toolkit Debugger equivalent, and the tool to reach for when a click fails or the UI does not look right

ui_toolkit_click_element / ui_toolkit_hover_over_element

  • Interact with an element through the normal UI Toolkit event pipeline, so the game's own callbacks, :hover state and value change notifications behave as they do for a real user
  • A click that cannot land is rejected with a reason, such as the element being disabled, hidden, or covered by another element
  • button selects left (the default), right or middle. Only a left click activates a Button, and a right click never opens a contextual menu, because a runtime panel has no menu manager - it delivers the pointer events, which is what UI handling the right button itself needs

ui_toolkit_set_field_value

  • Assigns a value to a runtime field such as a Toggle, Slider, TextField or enum field, raising the usual change notifications

take_screenshot

  • Captures the rendered Game View (the whole game image, not just the UI) to a PNG in the project's Temp directory and returns its path, so the agent can read the image

Note: These tools automate the runtime UI of your game. They do not inspect Unity's Editor UI. Element refs come from the most recent snapshot or hierarchy, and are invalidated when Unity reloads its script domain, so an agent takes a fresh snapshot after compiling. Failed requests come back with an error code and a hint on how to recover.

Editor state

Unlike the Game View tools, each of these has its own Play Mode requirement.

list_open_scenes

  • YAML list of the scenes currently open in the Editor, with each scene's name, asset path, whether it is the active one, whether it is loaded, whether it has unsaved changes, its build index and its root object count
  • Works in both Edit Mode and Play Mode

open_scene

  • Opens a scene asset, so an agent can set up the right scene before entering Play Mode, and returns the resulting open scenes as the same YAML list_open_scenes gives
  • Works outside of Play Mode only
  • mode selects Single (the default), Additive or AdditiveWithoutLoading
  • unsaved_changes decides what happens to unsaved work when a Single open would close modified scenes: refuse reports them and opens nothing (the default), save saves them first, discard loses them. Unity never shows a save dialog, because a modal dialog would block automation

list_locales / select_locale

  • Reads the project's localization locales and the selected one, and switches the selection, so localized UI can be checked in another language without touching the game's own settings screen
  • Both require Play Mode, because the runtime locale list only exists while playing
  • The Unity package reads Unity's localization settings through reflection, so a project without com.unity.localization keeps working and simply reports that localization is unavailable

Scene and GameObject inspection

These tools read what is inside the open scenes, which is what the Hierarchy and Inspector windows show. Reading never changes a scene, so all of them work in both Edit Mode and Play Mode, except the visual snapshot, which needs a Game View to measure against.

Every GameObject is reported with an instance id, which is the unambiguous way to address it in the next call. A path such as Canvas/Panel/Button is matched in full and case sensitively, and reports back the candidate ids when several objects share it. An id stays valid until Unity reloads its script domain or Play Mode is entered or exited, exactly like a UI element ref.

Hierarchies are capped at 20 children per object, 50 roots per scene and 200 objects in total. The total budget lowers the depth before anything is written, so the answer is a shallower complete tree rather than a truncated deep one, and everything left out is counted.

get_scene_hierarchy

  • The GameObject tree of one open scene as YAML, each object with its name, instance id and whether it is inactive
  • scene selects the scene by asset path or name, defaulting to the active one, and depth limits the levels, defaulting to as deep as the object budget allows

get_gameobject_hierarchy

  • The same YAML for the descendants of one object, which is how an agent expands a branch the scene tree cut off without paying for the rest of the scene

find_gameobjects

  • Finds objects by name (exactly, or as a substring with match_mode="contains") or by full path, across every open scene or just the one named by scene
  • Each match reports its name, id, scene, path, own and inherited active state, and component count, so the next call can address it by id
  • Finding nothing is a successful answer with count: 0, not an error

inspect_gameobject

  • What the Inspector window shows for one object: tag, layer, prefab status, parent and child count, and every component with its member values
  • Well known built-in components (transforms, renderers, colliders, rigidbodies, Camera, Light, Animator, AudioSource, Canvas, uGUI, ...) report a curated set of members, because reflecting over Camera alone yields around sixty. Name a component in full_detail_components to see all of its members, public and non-public
  • A value that references another Unity object is written with its instance id, so it can be fed straight back into the next call, and a getter that throws is reported instead of failing the request

get_camera_visible_gameobjects

  • What the game is actually drawing outside of UI Toolkit: the 2D and 3D objects a camera can see, as a tree that keeps each object's ancestors, with the pixel rectangle it covers on screen, its depth range, its renderer and its sorting layer and order
  • Screen pixels use the same top left origin as take_screenshot and the world bounds of ui_toolkit_get_element_debug_info, so the three can be compared directly
  • Requires Play Mode, because outside it there is no Game View to be a screen
  • renderers defaults to core (mesh, skinned mesh and sprite renderers), and all adds particles, trails, tilemaps and every other renderer, which is how to find out why something did not show up

Calling into the game's own code

invoke_static_method

  • Calls a public static C# method of the project inside the Editor and returns what it returned, as a string, which is how an agent reaches behaviour no UI exposes: a cheat that grants gold, a save game reset, a debug spawner, or a helper written just to be called here
  • Works in both Edit Mode and Play Mode, because a static method does not depend on anything the player loop provides
  • type_name takes the full namespace, with + before a nested type (MyGame.Systems+Debug). When several loaded assemblies declare the same name, the error lists their assembly qualified names to pick from
  • Arguments are always passed as strings and converted to the parameter types, using the same conversion ui_toolkit_set_field_value uses, so "100", "1.5", "true", "(1,2)" for a Vector2 and enum member names all work, and a JSON null passes null. Collection parameters such as int[] and List<string> have no string conversion and are rejected
  • A method returning nothing answers with returns: void, which is not the same answer as a method that returned null
  • Overloads with the same parameter count are resolved deterministically rather than by reflection order, so the same call always reaches the same method. To pin one exactly, avoid overloading or pass an argument the other overload cannot accept
  • Generic methods, ref/out/in and params parameters, and pointers cannot be invoked from strings and are reported as unsupported_method

Note: This runs arbitrary project code inside your Editor. Its only trust boundary is the loopback check every message of this protocol shares, and there is no method allow-list, because one would be trivial to work around and would only suggest a guarantee that is not there.

🚀 Autonomous AI agent workflow example

Once configured, AI agents can develop Unity code completely autonomously:

User: "Create a player controller that moves with WASD keys and has jump functionality. Make sure it's fully tested."

AI: I'll create a complete player controller with movement and jumping, then write comprehensive tests.

1. [AI writes PlayerController.cs with movement logic]
2. [AI calls: refresh_asset_database]
   → Result: "Compilation failed: CS0246: The type or namespace name 'Rigidbody' could not be found"
3. [AI fixes the missing using UnityEngine; statement]
4. [AI calls: refresh_asset_database]
   → Result: "Compilation successful, no errors"
5. [AI writes PlayerControllerTests.cs with movement and jump tests]
6. [AI calls: run_tests]
   → Result: "5 tests passed, 2 tests failed: NullReferenceException in Jump_ShouldApplyUpwardForce, ArgumentException in Move_WithInvalidInput"
7. [AI analyzes failures and fixes null reference and input validation bugs]
8. [AI calls: refresh_asset_database]
   → Result: "Compilation successful, no errors"
9. [AI calls: run_tests]
   → Result: "All 7 tests passed successfully"

AI: ✅ Complete! Created a fully functional PlayerController with WASD movement, jump mechanics, and comprehensive test coverage. All tests are passing.

This demonstrates true autonomous development - from user request to fully tested, working Unity code without any human intervention.

AI agent in action, tell Kimi K2 to fix compile errors in Unity project with no context:

🎮 Autonomous UI Toolkit workflow example

Compiling and passing tests says nothing about whether a UI actually works. With Play Mode control and Game View automation, the agent checks that itself:

User: "Add a settings panel, reachable from the main menu, with a volume slider and a mute
       toggle. Muting should disable the slider."

AI: I'll build the panel, then play the game and check it behaves correctly.

 1. [AI writes SettingsPanel.uxml, SettingsPanel.uss and SettingsPanel.cs, and adds a
     Settings button to the main menu]
 2. [AI calls: refresh_asset_database]
    → Result: "Compilation successful, no errors"
 3. [AI calls: enter_play_mode]
    → Result: "is_playing: true, changed: true, duration_seconds: 4.2"
 4. [AI calls: ui_toolkit_snapshot]                       # the game starts at the main menu
    → Result: "- UIDocument [name=\"MainMenu\"]:
                 - Button [name=\"play\"] [text=\"Play\"] [ref=e2]
                 - Button [name=\"settings\"] [text=\"Settings\"] [ref=e3]
                 - Button [name=\"quit\"] [text=\"Quit\"] [ref=e4]"
 5. [AI calls: ui_toolkit_click_element  ref=e3]          # navigate into the settings panel
    → Result: "ok: true"
 6. [AI calls: ui_toolkit_snapshot]                       # what the panel actually looks like now
    → Result: "- UIDocument [name=\"Settings\"]:
                 - Slider [name=\"volume\"] [label=\"Volume\"] [value=0.8] [ref=e7]
                 - Toggle [name=\"mute\"] [label=\"Mute\"] [value=false] [ref=e8]
                 - Button [name=\"back\"] [text=\"Back\"] [ref=e9]"
 7. [AI calls: ui_toolkit_set_field_value  ref=e8  value=true]   # mute it
    → Result: "ok: true"
 8. [AI calls: ui_toolkit_snapshot]                       # did the slider get disabled?
    → Result: "- Slider [name=\"volume\"] [label=\"Volume\"] [value=0.8] [ref=e7]"
                                                        # no enabledSelf=false, so the bug is real
 9. [AI calls: get_console_logs  levels=["Error"]]
    → Result: "NullReferenceException in SettingsPanel.OnMuteChanged (x1)"
10. [AI fixes the null reference: the slider was queried before the tree was cloned]
11. [AI calls: exit_play_mode]                            # scripts cannot compile while playing
12. [AI calls: refresh_asset_database]
    → Result: "Compilation successful, no errors"
13. [AI calls: enter_play_mode]                           # the game restarts at the main menu
14. [AI calls: ui_toolkit_snapshot]                       # refs are new, the domain was reloaded
15. [AI calls: ui_toolkit_click_element  ref=e3]          # navigate to the panel again
16. [AI calls: ui_toolkit_snapshot]
17. [AI calls: ui_toolkit_set_field_value  ref=e8  value=true]
18. [AI calls: ui_toolkit_get_element_debug_info  ref=e7]
    → Result: "element: { enabledSelf: false, enabledInHierarchy: false }"   # fixed
19. [AI calls: ui_toolkit_click_element  ref=e7]          # a disabled slider must reject input
    → Result: "ok: false, error_code: disabled"
20. [AI calls: take_screenshot]
    → Result: ".../Temp/GameViewScreenshots/game-view-....png"
21. [AI reads the PNG and confirms the panel looks right]
22. [AI calls: exit_play_mode]

AI: ✅ Done. The settings panel opens from the main menu, and muting disables the volume
    slider, which now rejects input. I found and fixed a NullReferenceException that made
    the first attempt do nothing.

The agent played the game to get to its own feature, and found a bug that compiled cleanly and would have passed a naive unit test, because it actually operated the UI and read back what the game said. Note steps 13 to 16: re-entering Play Mode restarts the game, so the agent navigates from the main menu again and works from fresh refs, because the domain reload invalidated the old ones.

📦 Installation

Prerequisites

  • Unity 6.0 or higher
  • Rust toolchain (for building from source)
  • CMake and a C compiler (required for building dependencies)

Step 1: Install Unity Package

Install the Visual Studio Code Editor package in your Unity project.

Important: The Visual Studio Code Editor package must be enabled (that is, you most select an external script editor provided by Visual Studio Code Editor for it to be enabled) for unity_code_mcp to work. Verify this in Unity at Preferences → External Tools → External Script Editor - it should show something like "Visual Studio Code Editor v1.0.12 enabled".

Note: The Game View automation tools need the package to implement the Game View automation messages of the messaging protocol, which version 1.0.14 does. Every other tool works with older versions too.

Step 2: Get the Binary

Option A: Download Release (Recommended) (Windows Only)

  • Download the latest binary from the Releases page

Option B: Build from Source

cargo build --release

Step 3: Configure Your AI Assistant

Add the MCP server to your AI assistant configuration:

For Cursor/Trae:

{
  "mcpServers": {
    "unity-code": {
      "command": "/path/to/unity-code-mcp",
      "env": {
        "UNITY_PROJECT_PATH": "${workspaceFolder}"
      }
    }
  }
}

For VS Code with MCP Extension:

{
  "mcp.servers": {
    "unity-code": {
      "command": "/path/to/unity_code_mcp",
      "env": {
        "UNITY_PROJECT_PATH": "${workspaceFolder}"
      }
    }
  }
}

For Claude Desktop:

{
  "mcpServers": {
    "unity-code": {
      "command": "/path/to/unity_code_mcp"
    }
  }
}

Note: The UNITY_PROJECT_PATH environment variable is a fallback option. If your IDE supports the MCP roots capability, the server will automatically detect Unity projects from the provided workspace roots and prioritize that over the environment variable.

Troubleshooting: If ${workspaceFolder} doesn't work in your IDE, try using an absolute path instead, e.g., "UNITY_PROJECT_PATH": "/absolute/path/to/your/unity/project". Alternatively, ask your IDE developer to add support for the MCP roots capability.

Important: Use absolute paths for the binary command.

Platform support

The code is cross platform, but I can't build or test for other platforms, because I only use Windows. If there are platform specific bugs, you have to fix them yourself.

🧪 Development & Testing

Running Tests

To run the test suite:

  1. Start Unity Editor with the embedded test project:

    # Open Unity Editor and load the project at:
    # ./UnityProject
    
  2. Run tests (single-threaded to avoid Unity conflicts):

    cargo test -- --test-threads=1
    

Note: Tests require a running Unity Editor instance with the embedded project loaded. Tests may take 30-60 seconds to complete due to Unity Editor interactions.

  1. For the Game View automation tests, open Assets/Scenes/GameViewAutomationTest.unity and enter Play Mode. That scene contains the runtime UI the tests operate on, and it reports every interaction in its status label so a test can confirm a click or a value assignment really reached the game. Without it those tests report what is missing and skip, so the suite still passes.

Note: Most Unity dependent tests need the Editor to be outside of Play Mode, because refresh_asset_database and run_tests do not work while playing. Run the Game View automation tests separately from the rest.

Building from Source

Prerequisites

  • C Compiler: Required for building aws-lc-rs dependency
    • Windows: MSVC (Visual Studio Build Tools)
    • macOS: Xcode Command Line Tools (xcode-select --install)
    • Linux: GCC (sudo apt-get install build-essential on Ubuntu/Debian)
  • CMake: Required for building the aws-lc-rs dependency
    • Windows: Follow the official guide
    • macOS: brew install cmake
    • Linux: sudo apt-get install cmake (Ubuntu/Debian)

Build Commands

# Debug build
cargo build

# Release build (recommended for production)
cargo build --release

🤝 Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Run tests with cargo test -- --test-threads=1
  4. Submit a pull request

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

🔗 Related Projects