This document explains the internal structure, design, code style, and main principles of Unity-MCP. Use it if you are a contributor or want to understand the project in depth.
💬 Join our Discord Server - Ask questions, showcase your work, and connect with other developers!
- Vision & Goals
- Prerequisites
- Local Development Setup
- Contribute
- Projects structure
- Code Style
- Running Tests
- CI/CD
We believe that AI will be (if not already) an important part of game development. There are amazing AI interfaces such as Claude, Copilot, Cursor and many others that keep improving. We connect game development with these tools, not against them — Unity MCP is a foundation for AI systems in the Unity Engine ecosystem, not an isolated chat window.
Project goals
- Deliver a high quality AI game development solution for free to everyone
- Provide a highly customizable platform for game developers to extend AI features for their needs
- Allow utilization of the best AI instruments for game development, all in one place
- Maintain and support cutting-edge AI technologies especially in Unity Engine and beyond
Before contributing, ensure the following tools are installed:
| Tool | Version | Purpose |
|---|---|---|
| Unity Editor | 2022.3+ / 2023.2+ / 6000.3+ | Run and test the plugin |
| .NET SDK | 9.0+ | Build and run the MCP Server |
| Node.js | 18+ | Run MCP Inspector for debugging |
| PowerShell | 7+ | Execute build and utility scripts |
| Docker (optional) | Latest | Build and test Docker images |
A free Unity Personal license works for contribution.
-
Clone the repository
git clone https://github.com/IvanMurzak/Unity-MCP.git cd Unity-MCP -
Open the Plugin in Unity
- Open Unity Hub → Add project → select the
Unity-MCP-Plugin/folder - Unity will compile all assemblies automatically on first open
- Open Unity Hub → Add project → select the
-
Open the Server in your IDE
- Open
Unity-MCP-Server/Server.slnin Visual Studio, Rider, or VS Code - Restore NuGet packages:
dotnet restore
- Open
-
Run the Server locally
cd Unity-MCP-Server dotnet run --project com.IvanMurzak.Unity.MCP.Server.csproj -- --port 8080 --client-transport stdio -
Point the Plugin at your local server (optional — skips the auto-downloaded binary)
- In Unity: open
Window/AI Game Developer — MCP - Set the port to match your local server (
8080by default) - The plugin will connect automatically
- In Unity: open
-
Debug with MCP Inspector (optional)
Unity-MCP-Plugin/Commands/start_mcp_inspector.bat # Windows (.bat) Unity-MCP-Server/commands/start-mcp-inspector.ps1 # PowerShell (cross-platform)
Requires Node.js. Opens a browser UI at
http://localhost:5173for live inspection of MCP protocol messages.
Lets build the bright game development future together, contribute to the project. Use this document to understand the project structure and how exactly it works.
- Fork the project
- Make your improvements, follow code style
- Create Pull Request
graph LR
A(◽AI agent)
B(🔹Unity-MCP-Server)
C(🔸Unity-MCP-Plugin)
D(🎮Unity)
%% Relationships
A <--> B
B <--> C
C <--> D
◽AI agent - Any AI interface such as: Claude, Copilot, Cursor or any other, it is not part of these project, but it is an important element of the architecture.
🔹Unity-MCP-Server - MCP Server that connects to AI agent and operates with it. In the same Unity-MCP-Server communicates with Unity-MCP-Plugin over SignalR. May run locally or in a cloud with HTTP transport. Tech stack: C#, ASP.NET Core, SignalR
🔸Unity-MCP-Plugin - Unity Plugin which is integrated into a Unity project, has access to Unity's API. Communicates with Unity-MCP-Server and executes commands from the server. Tech stack: C#, Unity, SignalR
🎮Unity - Unity Engine, game engine.
A C# ASP.NET Core application that acts as a bridge between AI agents (AI interfaces like Claude, Cursor) and Unity Editor instances. The server implements the Model Context Protocol using the csharp-sdk.
Project location:
Unity-MCP-Server
Main Responsibilities:
-
MCP Protocol Implementation (ExtensionsMcpServer.cs)
- Implements MCP server with support for Tools, Prompts, and Resources
- Supports both STDIO and HTTP transport methods
- Handles AI agent requests:
CallTool,GetPrompt,ReadResource, and their list operations - Sends notifications to AI agents when capabilities change (tool/prompt list updates)
-
SignalR Hub Communication (RemoteApp.cs, BaseHub.cs)
- Manages real-time bidirectional communication with Unity-MCP-Plugin via SignalR
- Handles version handshake to ensure API compatibility between server and plugin
- Tracks client connections and manages disconnections
- Routes tool/prompt/resource update notifications from Unity to AI agents
-
Request Routing & Execution (ToolRouter.Call.cs, PromptRouter.Get.cs, ResourceRouter.ReadResource.cs)
- Routes AI agent requests to the appropriate Unity-MCP-Plugin instance
- Handles Tool calls, Prompt requests, and Resource reads
- Performs error handling and validation
- Converts between MCP protocol formats and internal data models
-
Remote Execution Service (RemoteToolRunner.cs, RemotePromptRunner.cs, RemoteResourceRunner.cs)
- Invokes remote procedures on Unity-MCP-Plugin through SignalR
- Tracks asynchronous requests and manages timeouts
- Implements request/response patterns with cancellation support
- Handles request completion callbacks from Unity instances
-
Server Lifecycle Management (Program.cs, McpServerService.cs)
- Configures and starts ASP.NET Core web server with Kestrel
- Initializes MCP server, SignalR hub, and dependency injection
- Manages logging with NLog (redirects logs to stderr in STDIO mode)
- Handles graceful shutdown and resource cleanup
- Subscribes to Unity tool/prompt list change events
Unity-MCP-Server is deployable into a docker image. It contains Dockerfile and .dockerignore files in the folder of the project.
Integrates into Unity environment. Uses Unity-MCP-Common for searching for MCP Tool, Resource and Prompt in the local codebase using reflection. Communicates with Unity-MCP-Server for sending updates about MCP Tool, Resource and Prompt. Takes commands from Unity-MCP-Server and executes it.
Project location:
Unity-MCP-Plugin
Unity-MCP-Plugin is a UPM package, the root folder of the package is located at . It contains package.json. Which is used for uploading the package directly from GitHub release to OpenUPM.
Location
Unity-MCP-Plugin/Assets/root
The Editor component provides Unity Editor integration, implementing MCP capabilities (Tools, Prompts, Resources) and managing the Unity-MCP-Server lifecycle.
Location
Unity-MCP-Plugin/Assets/root/Editor
Main Responsibilities:
-
Plugin Lifecycle Management (Startup.cs)
- Auto-initializes on Unity Editor load via
[InitializeOnLoad] - Manages connection persistence across Editor lifecycle events (assembly reload, play mode transitions)
- Automatic reconnection after domain reload or Play mode exit
- Auto-initializes on Unity Editor load via
-
MCP Server Binary Management (McpServerManager.cs)
- Downloads and manages
Unity-MCP-Serverexecutable from GitHub releases - Cross-platform binary selection (Windows/macOS/Linux, x86/x64/ARM/ARM64)
- Version compatibility enforcement between server and plugin
- Configuration generation for AI agents (JSON with executable paths and connection settings)
- Downloads and manages
-
MCP API Implementation (Scripts/API/)
- Tools (50+): GameObject, Scene, Assets, Prefabs, Scripts, Components, Editor Control, Test Runner, Console, Reflection
- Prompts: Pre-built templates for common Unity development tasks
- Resources: URI-based access to Unity Editor data with JSON serialization
- All operations execute on Unity's main thread for thread safety
- Attribute-based discovery using
[McpPluginTool],[McpPluginPrompt],[McpPluginResource]
-
Editor UI (Scripts/UI/)
- Configuration window for connection management (
Window > AI Game Developer) - Server binary management and log access via Unity menu items
- Configuration window for connection management (
The Runtime component provides core infrastructure shared between Editor and Runtime modes, handling SignalR communication, serialization, and thread-safe Unity API access.
Location
Unity-MCP-Plugin/Assets/root/Runtime
Main Responsibilities:
-
Plugin Core & SignalR Connection (UnityMcpPlugin.cs)
- Thread-safe singleton managing plugin lifecycle via
BuildAndStart() - Discovers MCP Tools/Prompts/Resources from assemblies using reflection
- Establishes SignalR connection to Unity-MCP-Server with reactive state monitoring (R3 library)
- Configuration management: host, port, timeout, version compatibility
- Thread-safe singleton managing plugin lifecycle via
-
Main Thread Dispatcher (MainThreadDispatcher.cs)
- Marshals Unity API calls from SignalR background threads to Unity's main thread
- Queue-based execution in Unity's Update loop
- Critical for thread-safe MCP operation execution
-
Unity Type Serialization (ReflectionConverters/, JsonConverters/)
- Custom JSON serialization for Unity types (GameObject, Component, Transform, Vector3, Quaternion, etc.)
- Converts Unity objects to reference format (
GameObjectRef,ComponentRef) with instanceID tracking - Integrates with ReflectorNet for object introspection and component serialization
- Provides JSON schemas for MCP protocol type definitions
-
Logging & Diagnostics (Logger/, Unity/Logs/)
- Bridges Microsoft.Extensions.Logging to Unity Console with color-coded levels
- Collects Unity Console logs for AI context retrieval via MCP Tools
[McpPluginToolType]
public class Tool_GameObject
{
[McpPluginTool
(
"MyCustomTask",
Title = "Create a new GameObject"
)]
[Description("Explain here to LLM what is this, when it should be called.")]
public string CustomTask
(
[Description("Explain to LLM what is this.")]
string inputData
)
{
// do anything in background thread
return MainThread.Instance.Run(() =>
{
// do something in main thread if needed
return $"[Success] Operation completed.";
});
}
}MCP Prompt allows you to inject custom prompts into the conversation with the LLM. It supports two sender roles: User and Assistant. This is a quick way to instruct the LLM to perform specific tasks. You can generate prompts using custom data, providing lists or any other relevant information.
[McpPluginPromptType]
public static class Prompt_ScriptingCode
{
[McpPluginPrompt(Name = "add-event-system", Role = Role.User)]
[Description("Implement UnityEvent-based communication system between GameObjects.")]
public string AddEventSystem()
{
return "Create event system using UnityEvents, UnityActions, or custom event delegates for decoupled communication between game systems and components.";
}
}graph LR
A(◾Installer)
subgraph Installation
B(🎮Unity)
C(🔸Unity-MCP-Plugin)
end
%% Relationships
A --> B
B -.- C
Installer installs Unity-MCP-Plugin and dependencies as an NPM packages into a Unity project.
Project location:
Installer
This project follows consistent C# coding patterns. All new code must adhere to these conventions.
- File Headers: Include copyright notice in box comment format at the top of every file
- Nullable Context: Use
#nullable enablefor null safety — no implicit nulls - Attributes: Leverage
[McpPluginTool],[McpPluginPrompt],[McpPluginResource]for MCP discovery - Partial Classes: Split functionality across files (e.g.,
Tool_GameObject.Create.cs,Tool_GameObject.Destroy.cs) - Main Thread Execution: Wrap all Unity API calls with
MainThread.Instance.Run() - Error Handling: Throw exceptions for errors — use
ArgumentExceptionorException, never return error strings - Return Types: Return typed data models annotated with
[Description]for structured AI feedback - Descriptions: Annotate all public APIs and parameters with
[Description]for AI guidance - Naming: PascalCase for public members and types,
_camelCasefor private readonly fields - Null Safety: Use nullable types (
?) and null-coalescing operators (??,??=)
The annotated example below demonstrates how these conventions work together:
/*
┌──────────────────────────────────────────────────────────────────┐
│ Author: Ivan Murzak (https://github.com/IvanMurzak) │
│ Repository: GitHub (https://github.com/IvanMurzak/Unity-MCP) │
│ Copyright (c) 2025 Ivan Murzak │
│ Licensed under the Apache License, Version 2.0. │
│ See the LICENSE file in the project root for more information. │
└──────────────────────────────────────────────────────────────────┘
*/
// Enable nullable reference types for better null safety
#nullable enable
// Conditional compilation for platform-specific code
#if UNITY_EDITOR
using UnityEditor;
#endif
using System;
using System.ComponentModel;
using com.IvanMurzak.McpPlugin;
using com.IvanMurzak.Unity.MCP.Runtime.Data;
using com.IvanMurzak.Unity.MCP.Runtime.Utils;
using UnityEngine;
namespace com.IvanMurzak.Unity.MCP.Editor.API
{
// Use [McpPluginToolType] for tool classes - enables MCP discovery via reflection
[McpPluginToolType]
// Partial classes allow splitting implementation across multiple files
// Pattern: One file per operation (e.g., GameObject.Create.cs, GameObject.Destroy.cs)
public partial class Tool_GameObject
{
// MCP Tool declaration with attribute-based metadata
[McpPluginTool(
"gameobject-create", // Unique tool identifier (kebab-case)
Title = "GameObject / Create" // Human-readable title
)]
// Description attribute guides AI on when/how to use this tool
[Description(@"Create a new GameObject in the scene.
Provide position, rotation, and scale to minimize subsequent operations.")]
public CreateResult Create // Return a typed data model, not a string
(
// Parameter descriptions help AI understand expected inputs
[Description("Name of the new GameObject.")]
string name,
[Description("Parent GameObject reference. If not provided, created at scene root.")]
GameObjectRef? parentGameObjectRef = null, // Nullable with default value
[Description("Transform position of the GameObject.")]
Vector3? position = null, // Unity struct, nullable
[Description("Transform rotation in Euler angles (degrees).")]
Vector3? rotation = null,
[Description("Transform scale of the GameObject.")]
Vector3? scale = null
)
{
// Validate before entering the main thread — throw exceptions for errors
if (string.IsNullOrEmpty(name))
throw new ArgumentException("Name cannot be null or empty.", nameof(name));
return MainThread.Instance.Run(() => // All Unity API calls MUST run on main thread
{
// Null-coalescing assignment for default values
position ??= Vector3.zero;
rotation ??= Vector3.zero;
scale ??= Vector3.one;
// Resolve optional parent — throw on bad reference, don't return error strings
var parentGo = default(GameObject);
if (parentGameObjectRef?.IsValid(out _) == true)
{
parentGo = parentGameObjectRef.FindGameObject(out var error);
if (error != null)
throw new ArgumentException(error, nameof(parentGameObjectRef));
}
// Create GameObject using Unity API
var go = new GameObject(name);
// Set parent if provided
if (parentGo != null)
go.transform.SetParent(parentGo.transform, worldPositionStays: false);
// Apply transform values
go.transform.localPosition = position.Value;
go.transform.localRotation = Quaternion.Euler(rotation.Value);
go.transform.localScale = scale.Value;
// Mark as modified for Unity Editor
EditorUtility.SetDirty(go);
// Return a typed result — properties annotated with [Description] for AI
return new CreateResult
{
InstanceId = go.GetInstanceID(),
Path = go.GetPath(),
Name = go.name,
};
});
}
// Typed result class — structured data returned to the AI client
public class CreateResult
{
[Description("Instance ID of the created GameObject.")]
public int InstanceId { get; set; }
[Description("Hierarchy path of the created GameObject.")]
public string? Path { get; set; }
[Description("Name of the created GameObject.")]
public string? Name { get; set; }
}
}
// Separate partial class file for prompts
[McpPluginPromptType]
public static partial class Prompt_SceneManagement
{
// MCP Prompt with role definition (User or Assistant)
[McpPluginPrompt(Name = "setup-basic-scene", Role = Role.User)]
[Description("Setup a basic scene with camera, lighting, and environment.")]
public static string SetupBasicScene()
{
// Return prompt text for AI to process
return "Create a basic Unity scene with Main Camera, Directional Light, and basic environment setup.";
}
}
}Tests cover three modes across three Unity versions (2022, 2023, 6000) and two OSes (Windows, Ubuntu) — 18 combinations total.
Unity Test Runner (GUI)
- Open the
Unity-MCP-Plugin/project in Unity - Go to
Window > General > Test Runner - Select EditMode or PlayMode tab
- Click Run All or select specific tests and Run Selected
PowerShell script (command line)
# Run tests for a specific Unity version and mode
.\commands\run-unity-tests.ps1 -unityVersion "6000.3.1f1" -testMode "editmode"| Mode | What it tests | Location |
|---|---|---|
| EditMode | Tool logic, serialization, editor utilities — no Play mode needed | Assets/root/Tests/Editor |
| PlayMode | Runtime plugin, SignalR connection, main thread dispatch | Assets/root/Tests/Runtime |
| Standalone | Full player build with embedded plugin | Requires a player build step |
In projects that use multiple UPM packages, you can control which packages’ tests appear in the Test Runner via the project manifest testables field. Only packages listed in testables have their tests compiled and shown. Add this package (or any other) to testables in your project manifest to include its tests.
Example — in Packages/manifest.json:
{
"dependencies": {
"com.ivanmurzak.unity.mcp": "X.X.X"
},
"testables": [
"com.ivanmurzak.unity.mcp"
]
}See Unity: Add tests to your package and Unity: Project manifest (testables) for full documentation.
Each CI job is named test-unity-{version}-{mode} (e.g., test-unity-6000-3-1f1-editmode). When a job fails:
- Open the failing job in GitHub Actions
- Expand the Unity Test Runner step for inline output
- Download the test-results artifact for the full XML report
- Fix the test and push — CI reruns automatically
The project implements a comprehensive CI/CD pipeline using GitHub Actions with multiple workflows orchestrating the build, test, and deployment processes.
Here is what you need to know when working with CI as a contributor:
- PRs from forks require a maintainer to apply the
ci-oklabel before CI starts. This is a security measure to prevent untrusted code from accessing secrets. - Do not modify workflow files in
.github/workflows/in your PR — the CI check will abort if it detects changes to these files from an untrusted contributor. - All 18 test matrix combinations must pass before a PR can be merged. If your change breaks only one combination (e.g.,
2022-editmode), that job will show a red ✗ while others are green. - Re-running failed jobs: Go to the PR → Checks tab → click a failed job → Re-run failed jobs. This is useful for transient Unity Editor crashes.
- Workflow run order:
test_pull_request.ymlruns on your PR.release.ymlruns only after merging tomain. You don't need to trigger releases manually.
Location:
.github/workflows
Trigger: Push to main branch
Purpose: Main release workflow that orchestrates the entire release process
Process:
- Version Check - Extracts version from package.json and checks if release tag already exists
- Build Unity Installer - Tests and exports Unity package installer (
AI-Game-Dev-Installer.unitypackage) - Build MCP Server - Compiles cross-platform executables (Windows, macOS, Linux) using build-all.sh
- Unity Plugin Testing - Runs comprehensive tests across:
- 3 Unity versions:
2022.3.62f3,2023.2.22f1,6000.3.1f1 - 3 test modes:
editmode,playmode,standalone - 2 operating systems:
windows-latest,ubuntu-latest - Total: 18 test matrix combinations
- 3 Unity versions:
- Release Creation - Generates release notes from commits and creates GitHub release with tag
- Publishing - Uploads Unity installer package and MCP Server executables to the release
- Discord Notification - Sends formatted release notes to Discord channel
- Deploy - Triggers deployment workflow for NuGet and Docker
- Cleanup - Removes build artifacts after successful publishing
Trigger: Pull requests to main or dev branches
Purpose: Validates PR changes before merging
Process:
- Builds MCP Server executables for all platforms
- Runs the same 18 Unity test matrix combinations as the release workflow
- All tests must pass before PR can be merged
Type: Reusable workflow Purpose: Parameterized Unity testing workflow used by both release and PR workflows
Features:
- Accepts parameters:
projectPath,unityVersion,testMode - Runs on matrix of operating systems (Windows, Ubuntu)
- Uses Game CI Unity Test Runner with custom Docker images
- Implements security checks for PR contributors (requires
ci-oklabel for untrusted PRs) - Aborts if workflow files are modified in PRs
- Caches Unity Library for faster subsequent runs
- Uploads test artifacts for debugging
Trigger: Called by release workflow OR manual dispatch OR on release published Purpose: Deploys MCP Server to NuGet and Docker Hub
Jobs:
1. Deploy to NuGet:
- Builds and tests the MCP Server
- Packs NuGet package
- Publishes to nuget.org
2. Deploy Docker Image:
- Builds multi-platform Docker image (linux/amd64, linux/arm64)
- Pushes to Docker Hub
- Tags with version number and
latest - Uses GitHub Actions cache for build optimization
Trigger: GitHub release published Purpose: Builds and uploads cross-platform server executables to release
Process:
- Runs on macOS for cross-compilation support
- Builds executables for Windows, macOS, Linux using build-all.sh
- Creates ZIP archives for each platform
- Uploads to the GitHub release
- CI Platform: GitHub Actions
- Unity Testing: Game CI with Unity Test Runner
- Containerization: Docker with multi-platform builds
- Package Management: NuGet, OpenUPM, Docker Hub
- Build Tools: .NET 9.0, bash scripts
- Artifact Storage: GitHub Actions artifacts (temporary), GitHub Releases (permanent)
- Unity license, email, and password stored as GitHub secrets
- NuGet API key and Docker credentials secured
- PR workflow includes safety checks for workflow file modifications
- Untrusted PR contributions require maintainer approval via
ci-oklabel
- GitHub Releases - Unity installer package and MCP Server executables
- NuGet - MCP Server package for .NET developers
- Docker Hub - Containerized MCP Server for cloud deployments
- OpenUPM - Unity plugin package (automatically synced from GitHub releases)