Skip to main content
Glama
README.md
# AgentBridge

AgentBridge is an MCP server for the Unity Editor.
Install the package, run `dffrnt-agent serve`, and your LLM can inspect scenes, write scripts, run tests, and control the Editor from the chat.

## Why AgentBridge

Most Unity MCP tools embed a WebSocket or TCP server inside the Unity process.
That approach breaks on every domain reload.
It needs background threads to work around Unity's single-threaded API.
It also requires a free port on every machine.

AgentBridge uses a file queue instead:

- Commands land in `Temp/agent/requests/` and run on Unity's main thread. No sockets, no marshalling, no port conflicts.
- A heartbeat file (`Temp/agent/session.json`) tells the agent whether Unity is idle, compiling, or in play mode. The agent reads this file before each command.
- Domain reloads are transparent. The queue stays on disk and Unity replays any pending request after reload.
- New commands need only one `IAgentCommand` class in any Editor assembly. No changes to the bridge or the Go CLI are needed.

## Requirements

- Unity 6000.0 or later
- Go 1.23 or later (only to build from source)

## Quickstart

### 1. Install the package

Go to **Window > Package Manager > + > Add package from git URL** and enter:

```
https://github.com/simonwittber/AgentBridge.git?path=/AgentBridge
```

For a specific version:

```
https://github.com/simonwittber/AgentBridge.git?path=/AgentBridge#v0.3.0
```

### 2. Get the CLI

Download a pre-built binary from the [latest release](https://github.com/simonwittber/AgentBridge/releases/latest) and place it on your `PATH`.

To build from source:

```bash
cd AgentBridge/Harness~/dffrnt-agent
go build -o dffrnt-agent .        # macOS / Linux
go build -o dffrnt-agent.exe .    # Windows
```

### 3. Configure Claude Code

Add to `.claude/settings.json` inside your Unity project:

```json
{
  "mcpServers": {
    "unity": {
      "command": "dffrnt-agent",
      "args": ["serve"]
    }
  }
}
```

Run `dffrnt-agent` from your Unity project root, or pass `--project <path>` to set the project directory.

### 4. Open Unity and verify

Open or focus your Unity project.
The `unity` MCP server appears in `/mcp` in Claude Code with all bridge commands available as tools.

### 5. Check status

```bash
dffrnt-agent status
```

Expected output:

```json
{
  "cmd": "status",
  "status": "ok",
  "uptime_s": 42.3,
  "busy": false
}
```

---

## Built-in commands

### Core

| Command | Description |
|---|---|
| `status` | Bridge liveness, uptime, queue depth |
| `compile` | Request script compilation; returns errors and warnings |
| `refresh` | Trigger `AssetDatabase.Refresh()` and wait for completion |
| `list_commands` | List all available commands and their arguments |
| `help` | Full description and argument details for a named command |
| `focus` | Bring the Unity Editor window to the foreground |

### Scene

| Command | Description |
|---|---|
| `scene_info` | Name, path, dirty flag, root count |
| `scene_open` | Open a scene by asset path |
| `scene_save` | Save the active scene |
| `scene_new` | Create a new empty or default scene |

### Hierarchy and objects

| Command | Description |
|---|---|
| `hierarchy` | Scene tree as JSON (configurable depth) |
| `object_find` | Find a GameObject by path; returns components |
| `objects_find` | Find all objects with a given component type |
| `object_create` | Create a GameObject or primitive |
| `object_delete` | Delete a GameObject |
| `object_active` | Activate or deactivate a GameObject |
| `object_rename` | Rename a GameObject |
| `object_select` | Select one or more objects in the Editor |
| `duplicate_object` | Duplicate a GameObject |
| `reparent_object` | Move a GameObject to a new parent |
| `set_transform` | Set position, rotation, and scale in one call |

### Components and assets

| Command | Description |
|---|---|
| `component_get` | Get all serialized fields of a component |
| `component_set` | Set a serialized field on a component |
| `component_add` | Add a component by type name |
| `prefab_open` | Open a prefab in prefab stage |
| `prefab_save` | Save and exit the current prefab stage |
| `asset_info` | GUID and importer settings for an asset |
| `asset_set` | Set an importer field and reimport |
| `asset_find` | Find assets by type or label filter |
| `asset_create` | Create a new folder or material asset |
| `asset_delete` | Delete an asset |
| `asset_move` | Move an asset to a new path |
| `asset_copy` | Copy an asset to a new path |
| `asset_write_text` | Write a text file under `Assets/` and reimport |
| `material_get` | Get all shader properties of a material |
| `material_set` | Set a shader property on a material |
| `scriptable_get` | Get a named serialized field from a ScriptableObject asset |
| `scriptable_set` | Set a named serialized field on a ScriptableObject asset and save |

### Editor and console

| Command | Description |
|---|---|
| `console_logs` | All Unity console messages (ring buffer, newest first) |
| `play_enter` | Enter play mode |
| `play_exit` | Exit play mode |
| `menu_item` | Invoke a Unity menu item by path |
| `run_editor_tests` | Run edit-mode tests; returns pass/fail/skip |
| `run_playmode_tests` | Run play-mode tests; returns pass/fail/skip |
| `screenshot` | Capture the current view to PNG; returns immediately and fires a `screenshot_ready` notification with the file path |
| `execute_script` | Compile and run a C# snippet in the Editor |
| `selection_get` | Return the currently selected GameObjects and assets |
| `undo` | Perform an undo operation |
| `redo` | Perform a redo operation |
| `uuid` | Generate a UUID v4 |

### Profiler

| Command | Description |
|---|---|
| `profiler_start` | Begin recording named `ProfilerMarker` samples. Works in edit mode and play mode. Use `execute_script` to fire custom markers, then call `profiler_get_samples`. |
| `profiler_stop` | Stop the current recording session |
| `profiler_clear` | Stop and dispose all recorders |
| `profiler_get_samples` | Return summary stats (and optionally raw values) for recorded markers |

### Player settings and editor prefs

| Command | Description |
|---|---|
| `player_settings_get` | Return current PlayerSettings values |
| `player_settings_set` | Set a PlayerSettings value by key |
| `editor_pref_get` | Get a value from EditorPrefs |
| `editor_pref_set` | Set a value in EditorPrefs |

### Tags and layers

| Command | Description |
|---|---|
| `tags_layers` | Return all tags and layers defined in the project |
| `tag_add` | Add a new tag |
| `layer_add` | Add a new layer |

### Packages

| Command | Description |
|---|---|
| `package_list` | List installed Unity packages |
| `package_add` | Add or update a package by identifier |
| `package_remove` | Remove an installed package |
| `package_search` | Search the Unity Package Registry |

### Reflection

| Command | Description |
|---|---|
| `reflect_assemblies` | List loaded assemblies |
| `reflect_types` | Search for public types by name or namespace |
| `reflect_members` | List public members of a named type |

### Build

| Command | Description |
|---|---|
| `build` | Build the Unity player for the specified target |

---

## Adding custom commands

Implement `IAgentCommand` in any Editor assembly:

```csharp
using System.Text.Json.Nodes;
using LLMDevTools;

public class MyCommand : IAgentCommand
{
    public string    Cmd         => "my_cmd";
    public string    Description => "Does something useful.";
    public ArgSpec[] Args        => new[]
    {
        new ArgSpec("message", "string", "", "Text to log"),
    };

    public JsonObject Execute(string uid, string requestJson)
    {
        var resp = AgentBridge.MakeResponse(uid, Cmd, "ok");
        resp["echoed"] = requestJson;
        return resp;
    }
}
```

AgentBridge discovers the class automatically on the next domain reload.
No `[InitializeOnLoad]` attribute or manual registration call is needed.

---

## Protocol

Commands are JSON objects written to `Temp/agent/requests/<timestamp>-<uid>.json`:

```json
{"uid":"a1b2c3d4","cmd":"compile","agent_id":"agent-1"}
```

Responses appear in `Temp/agent/responses/<uid>.json`:

```json
{"uid":"a1b2c3d4","cmd":"compile","status":"ok","errors":[],"warnings":[]}
```

Unity also writes `Temp/agent/session.json` every 5 seconds:

```json
{
  "pid": 12345,
  "state": "idle",
  "active_scene": "Main",
  "play_mode": false,
  "compile_errors": 0,
  "agent_id": "",
  "written_at": 1749123456789
}
```

`dffrnt-agent` reads this file to check that Unity is alive before each command.
`agent_id` is empty when no command is active and contains the current agent identifier when a command runs.

### Notifications

Unity writes compiler and asset-import lifecycle events to `Temp/agent/notifications/<ts>-<uid>.json`.
In `serve` mode, `dffrnt-agent` polls this directory every 500 ms and forwards each event to MCP clients as a `notifications/message`.

Example notification file:

```json
{"type":"compile_finished","data":{"error_count":0},"written_at":1749123456789}
```

Event types: `compile_started`, `compile_finished`, `compile_failure`, `refresh_started`, `refresh_finished`, `play_mode_entered`, `play_mode_exited`, `scene_opened`, `screenshot_ready`.

Commands that trigger async state changes note their notifications in the tool description.

---

## Testing

Build and install the `dffrnt-agent` binary, open `AgentBridge/Example~` in Unity, then:

```bash
cd AgentBridge/Harness~/dffrnt-agent
go test -timeout 300s
```

The tests run against a live Unity session over the MCP protocol.
They skip automatically if Unity is not running or the session file is missing.

---

## LLM Agent Log window

Open via **Window > General > LLM Agent Log**.
This window shows a live view of all commands and responses: green for success, red for error.