MVP foundation for a local MCP relay server and Unity Editor package that let Codex issue JSON-RPC commands to the Unity Editor.
.NET / C#WebSocket relay server- MCP HTTP endpoint for Codex (
/mcp) - Unity Editor package (UPM local package workflow)
- JSON-RPC 2.0 request/response forwarding
- Main-thread Unity command dispatch (via
EditorApplication.update) - 203 implemented tools across 9 batches
- Implemented methods:
- MCP:
initialize,notifications/initialized,ping - MCP:
tools/list,tools/call - MCP:
prompts/list(compatibility, empty) - MCP:
resources/list,resources/templates/list,resources/read ping- Editor:
editor.getPlayModeState,editor.getConsoleLogs,editor.consoleTail,editor.enterPlayMode,editor.exitPlayMode,editor.pausePlayMode,editor.recompileScripts,editor.clearConsole,editor.getUndoHistory,editor.undo,editor.redo,editor.getLayers,editor.addLayer,editor.removeLayer,editor.getTags,editor.addTag,editor.removeTag - Scene (hierarchy):
scene.getActiveScene,scene.listOpenScenes,scene.newScene,scene.openScene,scene.closeScene,scene.save,scene.setActiveScene,scene.getHierarchy - Scene (objects):
scene.getSelection,scene.getSelectionDetails,scene.selectObject,scene.selectByPath,scene.selectByName,scene.setSelection,scene.findByPath,scene.findByTag,scene.createGameObject,scene.instantiatePrefab,scene.destroyObject,scene.duplicateObject,scene.renameObject,scene.setActive,scene.setParent,scene.setLayer,scene.setTag,scene.pingObject,scene.frameSelection,scene.frameObject - Scene (components):
scene.getComponents,scene.addComponent,scene.getComponentProperties,scene.setComponentProperties,scene.setTransform - Camera:
camera.getSettings,camera.setSettings,camera.getProjection,camera.setProjection - Light:
light.getSettings,light.setSettings - Prefab:
prefab.instantiate,prefab.getSource,prefab.applyOverrides,prefab.revertOverrides - Assets:
assets.find,assets.import,assets.ping,assets.reveal,assets.move,assets.delete,assets.createFolder,assets.createScript,assets.createMaterial,assets.createPrefab,assets.createScriptableObject - Renderer:
renderer.getMaterials,renderer.setMaterial,meshRenderer.getSettings,meshRenderer.setSettings,skinnedMeshRenderer.getSettings,skinnedMeshRenderer.setSettings,spriteRenderer.getSettings,spriteRenderer.setSettings,lineRenderer.getSettings,lineRenderer.setSettings,lodGroup.getSettings,lodGroup.setSettings - Physics 3D — Rigidbody:
rigidbody.getSettings,rigidbody.setSettings - Physics 3D — Colliders:
collider.getSettings,collider.setSettings,boxCollider.getSettings,boxCollider.setSettings,sphereCollider.getSettings,sphereCollider.setSettings,capsuleCollider.getSettings,capsuleCollider.setSettings,meshCollider.getSettings,meshCollider.setSettings - Physics 3D — Joints:
joint.getSettings,joint.setSettings,hingeJoint.getSettings,hingeJoint.setSettings,springJoint.getSettings,springJoint.setSettings,fixedJoint.getSettings,fixedJoint.setSettings,characterJoint.getSettings,characterJoint.setSettings,configurableJoint.getSettings,configurableJoint.setSettings - Physics 3D — Queries:
physics.raycast,physics.overlapSphere - Physics 2D — Rigidbody:
rigidbody2D.getSettings,rigidbody2D.setSettings - Physics 2D — Colliders:
collider2D.getSettings,collider2D.setSettings,boxCollider2D.getSettings,boxCollider2D.setSettings,circleCollider2D.getSettings,circleCollider2D.setSettings,capsuleCollider2D.getSettings,capsuleCollider2D.setSettings,polygonCollider2D.getSettings,polygonCollider2D.setSettings,edgeCollider2D.getSettings,edgeCollider2D.setSettings,compositeCollider2D.getSettings,compositeCollider2D.setSettings - Physics 2D — Joints:
hingeJoint2D.getSettings,hingeJoint2D.setSettings,springJoint2D.getSettings,springJoint2D.setSettings,distanceJoint2D.getSettings,distanceJoint2D.setSettings,fixedJoint2D.getSettings,fixedJoint2D.setSettings,sliderJoint2D.getSettings,sliderJoint2D.setSettings,wheelJoint2D.getSettings,wheelJoint2D.setSettings,targetJoint2D.getSettings,targetJoint2D.setSettings - Character:
characterController.getSettings,characterController.setSettings - Animator:
animator.getSettings,animator.setSettings,animator.getParameters,animator.setParameter - Audio (Batch 6):
audio.getSourceSettings,audio.setSourceSettings,audio.play,audio.stop,audio.pause,audio.unpause,audio.getIsPlaying,audio.getMixerSettings,audio.setMixerParameter,audio.getListenerSettings,audio.setListenerSettings - Audio (component wrappers):
audioSource.getSettings,audioSource.setSettings - Particle System:
particleSystem.getSettings,particleSystem.setSettings,particleSystem.play,particleSystem.stop - NavMesh:
navMesh.bake,navMeshAgent.getSettings,navMeshAgent.setSettings,navMeshObstacle.getSettings,navMeshObstacle.setSettings - UI:
canvas.getSettings,canvas.setSettings,canvasGroup.getSettings,canvasGroup.setSettings,rectTransform.getSettings,rectTransform.setSettings - Terrain:
terrain.getSettings,terrain.setSettings - Time:
time.getSettings,time.setSettings - Build:
build.getSettings,build.setSettings,build.build - Test Runner (Batch 7):
testRunner.listTests,testRunner.run,testRunner.getResults,testRunner.cancel - Material (Batch 8):
material.getProperties,material.getProperty,material.setProperty,material.getKeywords,material.setKeyword,material.getShader,material.setShader,material.getRenderQueue,material.setRenderQueue - Scene Capture (Batch 9):
editor.captureSceneView,editor.captureGameView - Project Settings (Batch 9):
projectSettings.getPlayerSettings,projectSettings.setPlayerSettings,projectSettings.getQualitySettings,projectSettings.setQualitySettings,projectSettings.getPhysicsSettings,projectSettings.setPhysicsSettings - Physics2D Settings (Batch 9):
physics2D.getSettings,physics2D.setSettings - Scene Hierarchy (Batch 9):
scene.getHierarchy
- MCP:
src/UnityMcp.Server- MCP relay servertests/UnityMcp.Server.Tests- xUnit unit testsunity/Packages/com.laimis.unitymcp- Unity UPM package (Editor bridge)docs/protocol.md- MVP protocol/method notes
- Primary target: Unity
6.3 LTS (6000.3.9f1)
If you only want the Unity Editor bridge package in your Unity project, you do not need to move this whole repository into the Unity project.
Use one of these UPM methods:
Edit your Unity project's Packages/manifest.json and add:
{
"dependencies": {
"com.laimis.unitymcp": "file:./UnityMCP/unity/Packages/com.laimis.unitymcp"
}
}- Open Unity
Package Manager - Click
+ - Select
Add package from disk... - Choose
./UnityMCP/unity/Packages/com.laimis.unitymcp/package.json
Notes:
- Unity will also install the package dependency
com.unity.nuget.newtonsoft-jsonautomatically. - This adds only the Unity package (
unity/Packages/com.laimis.unitymcp), not the MCP server project. - You still run the MCP server separately from this repo (
src/UnityMcp.Server).
Keep this repository and your Unity game project in separate root folders.
Edit your Unity project's Packages/manifest.json and add:
{
"dependencies": {
"com.laimis.unitymcp": "file:./UnityMCP/unity/Packages/com.laimis.unitymcp"
}
}Alternative in Unity Editor:
- Package Manager ->
+->Add package from disk... - Select
./UnityMCP/unity/Packages/com.laimis.unitymcp/package.json
dotnet run --project ./UnityMCP/src/UnityMcp.ServerThe server listens on:
http://127.0.0.1:5001ws://127.0.0.1:5001/ws/cliws://127.0.0.1:5001/ws/unity
This server now exposes a Codex-compatible MCP HTTP endpoint at http://127.0.0.1:5001/mcp.
Codex registers MCP servers in ~/.codex/config.toml using entries like:
[mcp_servers.someName]
url = "http://127.0.0.1:PORT/mcp"Use this entry (example):
[mcp_servers.unitymcp]
url = "http://127.0.0.1:5001/mcp"Notes:
/mcpis the Codex MCP endpoint./ws/unityis used by the Unity Editor package bridge./ws/cliremains available for direct relay debugging but is not required for Codex MCP usage.
After editing ~/.codex/config.toml, restart Codex so it reloads MCP server registrations.
The package auto-starts the Unity bridge on editor load and attempts to connect to /ws/unity.
Menu items:
Tools/Unity MCP/ConnectTools/Unity MCP/DisconnectTools/Unity MCP/Settings
- Open
Tools/Unity MCP/Settings - Set the WebSocket endpoint (must be
ws://orwss://) - Click
Save - If already connected, use
DisconnectthenConnectto apply immediately
Default endpoint:
ws://127.0.0.1:5001/ws/unity
- Start the server.
- Open Unity and confirm the console logs
[UnityMCP] Connected. - Verify MCP endpoint with HTTP JSON-RPC:
initializeprompts/listresources/listresources/templates/listresources/read(for exampleunitymcp://server/info)tools/listtools/call(for exampleeditor.getPlayModeState)
- Optionally verify direct relay WebSocket JSON-RPC on
/ws/clifor debugging:pingeditor.getPlayModeStateeditor.getConsoleLogseditor.consoleTaileditor.enterPlayModeeditor.exitPlayModescene.getActiveScenescene.listOpenScenesscene.getSelectionscene.selectObjectscene.selectByPathscene.findByPathcamera.getSettingscamera.setSettingslight.getSettingslight.setSettingsrigidbody.getSettingsrigidbody.setSettingscollider.getSettingscollider.setSettingsboxCollider.getSettingsboxCollider.setSettingssphereCollider.getSettingssphereCollider.setSettingscapsuleCollider.getSettingscapsuleCollider.setSettingsmeshCollider.getSettingsmeshCollider.setSettingsscene.getComponentsscene.destroyObjectscene.getComponentPropertiesscene.setComponentPropertiesscene.setTransformscene.addComponentscene.setSelectionscene.pingObjectscene.frameSelectionscene.frameObjectscene.createGameObjectscene.setParentscene.duplicateObjectscene.renameObjectscene.setActiveprefab.instantiateprefab.getSourceprefab.applyOverridesprefab.revertOverridesscene.findByTagassets.findassets.importassets.pingassets.reveal
- Verify a JSON-RPC response is returned.
Selection tool note:
scene.selectObject/scene.selectByPath/scene.setSelectionsupport optionalpingandfocusbooleans to highlight and frame the selection in the Unity Editor.scene.selectByPathalso supports optionalscenePathfor disambiguating duplicate hierarchy paths across open scenes.scene.pingObjectpings/highlights an object byinstanceIdwithout changing selection.scene.frameSelectionframes the current selection in the Scene view (best effort).scene.frameObjectframes a specific scene object byinstanceId(best effort) while preserving the previous selection when possible.scene.getComponents/scene.getComponentProperties/scene.setComponentProperties/scene.setTransform/scene.addComponentenable basic component inspection and safe scene-object edits from MCP.scene.setParent/scene.duplicateObject/scene.renameObject/scene.setActiveadd Undo-aware hierarchy editing and active-state workflows for scene objects.scene.destroyObjectdeletes a sceneGameObjectorComponentwith Undo support (destroyingTransformdirectly is rejected).prefab.instantiatecreates prefab instances by asset path, andprefab.getSourcereturns prefab source metadata for scene instances.prefab.applyOverrides/prefab.revertOverridessupport deterministicinstanceRoot,object, andcomponentscopes; unsupported target/scope combinations fail explicitly instead of widening scope.- Component property read/write uses a constrained serialized-property MVP (common simple types only; unsupported/non-editable properties fail clearly).
camera.*/light.*/rigidbody.*/collider.*provide direct API convenience wrappers (getSettings/setSettings) so you do not need serializedm_*field names for common workflows.- 2D physics convenience wrappers are also available:
rigidbody2D.*,collider2D.*boxCollider2D.*,circleCollider2D.*,capsuleCollider2D.*polygonCollider2D.*,edgeCollider2D.*,compositeCollider2D.*
- 2D subtype wrappers use conservative MVP write support for complex-shape colliders:
polygonCollider2D.setSettingscurrently supports baseCollider2Dfields onlyedgeCollider2D.setSettingsaddsedgeRadiuscompositeCollider2D.setSettingsaddsgeometryType/generationType(safe subset)
- Common 2D joint convenience wrappers are available:
hingeJoint2D.*,springJoint2D.*,distanceJoint2D.*,fixedJoint2D.*sliderJoint2D.*,wheelJoint2D.*,targetJoint2D.*
connectedBodyInstanceIdis supported on anchored Joint2D setters (hingeJoint2D.*,springJoint2D.*,distanceJoint2D.*,fixedJoint2D.*,sliderJoint2D.*,wheelJoint2D.*).targetJoint2D.*does not exposeconnectedBodyInstanceIdbecauseTargetJoint2Dis target-driven rather than body-linked in the same way as the anchored joint families.- Common 3D joint convenience wrappers are available:
hingeJoint.*,springJoint.*,fixedJoint.*,characterJoint.*,configurableJoint.*
- Anchored
Joint2Dsetters and the supported 3D joint setters acceptconnectedBodyInstanceIdas either a matching rigidbody/component target ornullto clear the connection. - Those same anchored-joint setters also accept
connectedAnchorModewithpreserve,auto,zero, andmatchAnchorhelper behavior. characterJoint.*exposes sharedJointfields plus swing axis, projection/preprocessing flags, and practical twist/swing spring-limit editing.configurableJoint.*now exposes a practical editing subset:- shared joint fields plus
secondaryAxis configuredInWorldSpaceandswapBodies- linear/angular motion modes
- linear/angular limits
- target position and target velocities
rotationDriveModeplus x/y/z/angular/slerp drives- projection mode, distance, and angle
targetRotationhelpers remain intentionally out of scope
- shared joint fields plus
collider.setSettingscurrently supports BoxCollider-specificcenter/sizefields in addition to base collider fields.- Subtype collider wrappers are available for
BoxCollider,SphereCollider,CapsuleCollider, andMeshCollider:boxCollider.*,sphereCollider.*,capsuleCollider.*,meshCollider.*
meshCollider.setSettingsintentionally uses a safe subset in the MVP (nosharedMeshassignment yet).assets.ping/assets.revealnavigate the Project window to an asset path (assets.revealalso focuses Project window and selects the asset).- Test Runner tools require the
com.unity.test-frameworkpackage installed in the Unity project:testRunner.listTests— lists all discovered tests for the given mode. Params:mode(required:"editMode"or"playMode"). Returns{mode, count, tests: [{fullName, name, className, assembly}]}.testRunner.run— async: starts a test run and returns immediately. Params:mode(required),testFilter(optional string). Returns{started: true, mode, message}. PolltestRunner.getResultsuntil the run finishes.testRunner.getResults— retrieves results of the last completed test run. No params. Returns{status, summary: {total, passed, failed, skipped}, tests: [{name, fullName, result, duration, message?, stackTrace?}]}.testRunner.cancel— cancels an in-progress run. No params. Returns{cancelled: bool, message}.
Example MCP initialize request:
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {},
"clientInfo": {
"name": "manual-test",
"version": "1.0"
}
}
}Example MCP tools/call request:
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "editor.getPlayModeState",
"arguments": {}
}
}Example MCP resources/read request:
{
"jsonrpc": "2.0",
"id": 3,
"method": "resources/read",
"params": {
"uri": "unitymcp://server/info"
}
}Current MCP resource URIs:
unitymcp://server/infounitymcp://unity/connectionunitymcp://editor/playmode-stateunitymcp://editor/console-logsunitymcp://scene/activeunitymcp://scene/open-scenesunitymcp://scene/selectionunitymcp://scene/selection/active
Current MCP resource templates:
unitymcp://scene/find-by-tag/{tag}unitymcp://assets/find/{query}unitymcp://editor/console-tail/{afterSequence}unitymcp://scene/selection/index/{index}
Resource query parameter support (MVP):
unitymcp://editor/console-logs?maxResults=20&includeStackTrace=trueunitymcp://editor/console-logs?level=warning&level=errorunitymcp://editor/console-logs?contains=MissingReferenceunitymcp://editor/console-tail/125?maxResults=10&includeStackTrace=falseunitymcp://editor/console-tail/125?level=error&includeStackTrace=trueunitymcp://editor/console-tail/125?contains=NullReferenceunitymcp://assets/find/Player?maxResults=25&folder=Assets/Prefabs&type=Prefab&label=Gameplayunitymcp://scene/selection/index/0
Example direct relay /ws/cli request:
{
"jsonrpc": "2.0",
"id": 2,
"method": "scene.createGameObject",
"params": {
"name": "EnemySpawnPoint",
"position": [0, 1.5, 0]
}
}- Local-only MVP (no auth/TLS yet)
- Single Unity connection
- Domain reloads can interrupt the connection; the package reconnect loop handles recovery attempts