#
tokens: 49884/50000 107/263 files (page 1/18)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 1 of 18. Use http://codebase.md/justinpbarnett/unity-mcp?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .claude
│   ├── prompts
│   │   ├── nl-unity-suite-nl.md
│   │   └── nl-unity-suite-t.md
│   └── settings.json
├── .github
│   ├── scripts
│   │   └── mark_skipped.py
│   └── workflows
│       ├── bump-version.yml
│       ├── claude-nl-suite.yml
│       ├── github-repo-stats.yml
│       └── unity-tests.yml
├── .gitignore
├── deploy-dev.bat
├── docs
│   ├── CURSOR_HELP.md
│   ├── CUSTOM_TOOLS.md
│   ├── README-DEV-zh.md
│   ├── README-DEV.md
│   ├── screenshots
│   │   ├── v5_01_uninstall.png
│   │   ├── v5_02_install.png
│   │   ├── v5_03_open_mcp_window.png
│   │   ├── v5_04_rebuild_mcp_server.png
│   │   ├── v5_05_rebuild_success.png
│   │   ├── v6_2_create_python_tools_asset.png
│   │   ├── v6_2_python_tools_asset.png
│   │   ├── v6_new_ui_asset_store_version.png
│   │   ├── v6_new_ui_dark.png
│   │   └── v6_new_ui_light.png
│   ├── TELEMETRY.md
│   ├── v5_MIGRATION.md
│   └── v6_NEW_UI_CHANGES.md
├── LICENSE
├── logo.png
├── mcp_source.py
├── MCPForUnity
│   ├── Editor
│   │   ├── AssemblyInfo.cs
│   │   ├── AssemblyInfo.cs.meta
│   │   ├── Data
│   │   │   ├── DefaultServerConfig.cs
│   │   │   ├── DefaultServerConfig.cs.meta
│   │   │   ├── McpClients.cs
│   │   │   ├── McpClients.cs.meta
│   │   │   ├── PythonToolsAsset.cs
│   │   │   └── PythonToolsAsset.cs.meta
│   │   ├── Data.meta
│   │   ├── Dependencies
│   │   │   ├── DependencyManager.cs
│   │   │   ├── DependencyManager.cs.meta
│   │   │   ├── Models
│   │   │   │   ├── DependencyCheckResult.cs
│   │   │   │   ├── DependencyCheckResult.cs.meta
│   │   │   │   ├── DependencyStatus.cs
│   │   │   │   └── DependencyStatus.cs.meta
│   │   │   ├── Models.meta
│   │   │   ├── PlatformDetectors
│   │   │   │   ├── IPlatformDetector.cs
│   │   │   │   ├── IPlatformDetector.cs.meta
│   │   │   │   ├── LinuxPlatformDetector.cs
│   │   │   │   ├── LinuxPlatformDetector.cs.meta
│   │   │   │   ├── MacOSPlatformDetector.cs
│   │   │   │   ├── MacOSPlatformDetector.cs.meta
│   │   │   │   ├── PlatformDetectorBase.cs
│   │   │   │   ├── PlatformDetectorBase.cs.meta
│   │   │   │   ├── WindowsPlatformDetector.cs
│   │   │   │   └── WindowsPlatformDetector.cs.meta
│   │   │   └── PlatformDetectors.meta
│   │   ├── Dependencies.meta
│   │   ├── External
│   │   │   ├── Tommy.cs
│   │   │   └── Tommy.cs.meta
│   │   ├── External.meta
│   │   ├── Helpers
│   │   │   ├── AssetPathUtility.cs
│   │   │   ├── AssetPathUtility.cs.meta
│   │   │   ├── CodexConfigHelper.cs
│   │   │   ├── CodexConfigHelper.cs.meta
│   │   │   ├── ConfigJsonBuilder.cs
│   │   │   ├── ConfigJsonBuilder.cs.meta
│   │   │   ├── ExecPath.cs
│   │   │   ├── ExecPath.cs.meta
│   │   │   ├── GameObjectSerializer.cs
│   │   │   ├── GameObjectSerializer.cs.meta
│   │   │   ├── McpConfigFileHelper.cs
│   │   │   ├── McpConfigFileHelper.cs.meta
│   │   │   ├── McpConfigurationHelper.cs
│   │   │   ├── McpConfigurationHelper.cs.meta
│   │   │   ├── McpLog.cs
│   │   │   ├── McpLog.cs.meta
│   │   │   ├── McpPathResolver.cs
│   │   │   ├── McpPathResolver.cs.meta
│   │   │   ├── PackageDetector.cs
│   │   │   ├── PackageDetector.cs.meta
│   │   │   ├── PackageInstaller.cs
│   │   │   ├── PackageInstaller.cs.meta
│   │   │   ├── PortManager.cs
│   │   │   ├── PortManager.cs.meta
│   │   │   ├── PythonToolSyncProcessor.cs
│   │   │   ├── PythonToolSyncProcessor.cs.meta
│   │   │   ├── Response.cs
│   │   │   ├── Response.cs.meta
│   │   │   ├── ServerInstaller.cs
│   │   │   ├── ServerInstaller.cs.meta
│   │   │   ├── ServerPathResolver.cs
│   │   │   ├── ServerPathResolver.cs.meta
│   │   │   ├── TelemetryHelper.cs
│   │   │   ├── TelemetryHelper.cs.meta
│   │   │   ├── Vector3Helper.cs
│   │   │   └── Vector3Helper.cs.meta
│   │   ├── Helpers.meta
│   │   ├── Importers
│   │   │   ├── PythonFileImporter.cs
│   │   │   └── PythonFileImporter.cs.meta
│   │   ├── Importers.meta
│   │   ├── MCPForUnity.Editor.asmdef
│   │   ├── MCPForUnity.Editor.asmdef.meta
│   │   ├── MCPForUnityBridge.cs
│   │   ├── MCPForUnityBridge.cs.meta
│   │   ├── Models
│   │   │   ├── Command.cs
│   │   │   ├── Command.cs.meta
│   │   │   ├── McpClient.cs
│   │   │   ├── McpClient.cs.meta
│   │   │   ├── McpConfig.cs
│   │   │   ├── McpConfig.cs.meta
│   │   │   ├── MCPConfigServer.cs
│   │   │   ├── MCPConfigServer.cs.meta
│   │   │   ├── MCPConfigServers.cs
│   │   │   ├── MCPConfigServers.cs.meta
│   │   │   ├── McpStatus.cs
│   │   │   ├── McpStatus.cs.meta
│   │   │   ├── McpTypes.cs
│   │   │   ├── McpTypes.cs.meta
│   │   │   ├── ServerConfig.cs
│   │   │   └── ServerConfig.cs.meta
│   │   ├── Models.meta
│   │   ├── Resources
│   │   │   ├── McpForUnityResourceAttribute.cs
│   │   │   ├── McpForUnityResourceAttribute.cs.meta
│   │   │   ├── MenuItems
│   │   │   │   ├── GetMenuItems.cs
│   │   │   │   └── GetMenuItems.cs.meta
│   │   │   ├── MenuItems.meta
│   │   │   ├── Tests
│   │   │   │   ├── GetTests.cs
│   │   │   │   └── GetTests.cs.meta
│   │   │   └── Tests.meta
│   │   ├── Resources.meta
│   │   ├── Services
│   │   │   ├── BridgeControlService.cs
│   │   │   ├── BridgeControlService.cs.meta
│   │   │   ├── ClientConfigurationService.cs
│   │   │   ├── ClientConfigurationService.cs.meta
│   │   │   ├── IBridgeControlService.cs
│   │   │   ├── IBridgeControlService.cs.meta
│   │   │   ├── IClientConfigurationService.cs
│   │   │   ├── IClientConfigurationService.cs.meta
│   │   │   ├── IPackageUpdateService.cs
│   │   │   ├── IPackageUpdateService.cs.meta
│   │   │   ├── IPathResolverService.cs
│   │   │   ├── IPathResolverService.cs.meta
│   │   │   ├── IPythonToolRegistryService.cs
│   │   │   ├── IPythonToolRegistryService.cs.meta
│   │   │   ├── ITestRunnerService.cs
│   │   │   ├── ITestRunnerService.cs.meta
│   │   │   ├── IToolSyncService.cs
│   │   │   ├── IToolSyncService.cs.meta
│   │   │   ├── MCPServiceLocator.cs
│   │   │   ├── MCPServiceLocator.cs.meta
│   │   │   ├── PackageUpdateService.cs
│   │   │   ├── PackageUpdateService.cs.meta
│   │   │   ├── PathResolverService.cs
│   │   │   ├── PathResolverService.cs.meta
│   │   │   ├── PythonToolRegistryService.cs
│   │   │   ├── PythonToolRegistryService.cs.meta
│   │   │   ├── TestRunnerService.cs
│   │   │   ├── TestRunnerService.cs.meta
│   │   │   ├── ToolSyncService.cs
│   │   │   └── ToolSyncService.cs.meta
│   │   ├── Services.meta
│   │   ├── Setup
│   │   │   ├── SetupWizard.cs
│   │   │   ├── SetupWizard.cs.meta
│   │   │   ├── SetupWizardWindow.cs
│   │   │   └── SetupWizardWindow.cs.meta
│   │   ├── Setup.meta
│   │   ├── Tools
│   │   │   ├── CommandRegistry.cs
│   │   │   ├── CommandRegistry.cs.meta
│   │   │   ├── ExecuteMenuItem.cs
│   │   │   ├── ExecuteMenuItem.cs.meta
│   │   │   ├── ManageAsset.cs
│   │   │   ├── ManageAsset.cs.meta
│   │   │   ├── ManageEditor.cs
│   │   │   ├── ManageEditor.cs.meta
│   │   │   ├── ManageGameObject.cs
│   │   │   ├── ManageGameObject.cs.meta
│   │   │   ├── ManageScene.cs
│   │   │   ├── ManageScene.cs.meta
│   │   │   ├── ManageScript.cs
│   │   │   ├── ManageScript.cs.meta
│   │   │   ├── ManageShader.cs
│   │   │   ├── ManageShader.cs.meta
│   │   │   ├── McpForUnityToolAttribute.cs
│   │   │   ├── McpForUnityToolAttribute.cs.meta
│   │   │   ├── Prefabs
│   │   │   │   ├── ManagePrefabs.cs
│   │   │   │   └── ManagePrefabs.cs.meta
│   │   │   ├── Prefabs.meta
│   │   │   ├── ReadConsole.cs
│   │   │   ├── ReadConsole.cs.meta
│   │   │   ├── RunTests.cs
│   │   │   └── RunTests.cs.meta
│   │   ├── Tools.meta
│   │   ├── Windows
│   │   │   ├── ManualConfigEditorWindow.cs
│   │   │   ├── ManualConfigEditorWindow.cs.meta
│   │   │   ├── MCPForUnityEditorWindow.cs
│   │   │   ├── MCPForUnityEditorWindow.cs.meta
│   │   │   ├── MCPForUnityEditorWindowNew.cs
│   │   │   ├── MCPForUnityEditorWindowNew.cs.meta
│   │   │   ├── MCPForUnityEditorWindowNew.uss
│   │   │   ├── MCPForUnityEditorWindowNew.uss.meta
│   │   │   ├── MCPForUnityEditorWindowNew.uxml
│   │   │   ├── MCPForUnityEditorWindowNew.uxml.meta
│   │   │   ├── VSCodeManualSetupWindow.cs
│   │   │   └── VSCodeManualSetupWindow.cs.meta
│   │   └── Windows.meta
│   ├── Editor.meta
│   ├── package.json
│   ├── package.json.meta
│   ├── README.md
│   ├── README.md.meta
│   ├── Runtime
│   │   ├── MCPForUnity.Runtime.asmdef
│   │   ├── MCPForUnity.Runtime.asmdef.meta
│   │   ├── Serialization
│   │   │   ├── UnityTypeConverters.cs
│   │   │   └── UnityTypeConverters.cs.meta
│   │   └── Serialization.meta
│   ├── Runtime.meta
│   └── UnityMcpServer~
│       └── src
│           ├── __init__.py
│           ├── config.py
│           ├── Dockerfile
│           ├── models.py
│           ├── module_discovery.py
│           ├── port_discovery.py
│           ├── pyproject.toml
│           ├── pyrightconfig.json
│           ├── registry
│           │   ├── __init__.py
│           │   ├── resource_registry.py
│           │   └── tool_registry.py
│           ├── reload_sentinel.py
│           ├── resources
│           │   ├── __init__.py
│           │   ├── menu_items.py
│           │   └── tests.py
│           ├── server_version.txt
│           ├── server.py
│           ├── telemetry_decorator.py
│           ├── telemetry.py
│           ├── test_telemetry.py
│           ├── tools
│           │   ├── __init__.py
│           │   ├── execute_menu_item.py
│           │   ├── manage_asset.py
│           │   ├── manage_editor.py
│           │   ├── manage_gameobject.py
│           │   ├── manage_prefabs.py
│           │   ├── manage_scene.py
│           │   ├── manage_script.py
│           │   ├── manage_shader.py
│           │   ├── read_console.py
│           │   ├── resource_tools.py
│           │   ├── run_tests.py
│           │   └── script_apply_edits.py
│           ├── unity_connection.py
│           └── uv.lock
├── prune_tool_results.py
├── README-zh.md
├── README.md
├── restore-dev.bat
├── scripts
│   └── validate-nlt-coverage.sh
├── test_unity_socket_framing.py
├── TestProjects
│   └── UnityMCPTests
│       ├── .gitignore
│       ├── Assets
│       │   ├── Editor.meta
│       │   ├── Scenes
│       │   │   ├── SampleScene.unity
│       │   │   └── SampleScene.unity.meta
│       │   ├── Scenes.meta
│       │   ├── Scripts
│       │   │   ├── Hello.cs
│       │   │   ├── Hello.cs.meta
│       │   │   ├── LongUnityScriptClaudeTest.cs
│       │   │   ├── LongUnityScriptClaudeTest.cs.meta
│       │   │   ├── TestAsmdef
│       │   │   │   ├── CustomComponent.cs
│       │   │   │   ├── CustomComponent.cs.meta
│       │   │   │   ├── TestAsmdef.asmdef
│       │   │   │   └── TestAsmdef.asmdef.meta
│       │   │   └── TestAsmdef.meta
│       │   ├── Scripts.meta
│       │   ├── Tests
│       │   │   ├── EditMode
│       │   │   │   ├── Data
│       │   │   │   │   ├── PythonToolsAssetTests.cs
│       │   │   │   │   └── PythonToolsAssetTests.cs.meta
│       │   │   │   ├── Data.meta
│       │   │   │   ├── Helpers
│       │   │   │   │   ├── CodexConfigHelperTests.cs
│       │   │   │   │   ├── CodexConfigHelperTests.cs.meta
│       │   │   │   │   ├── WriteToConfigTests.cs
│       │   │   │   │   └── WriteToConfigTests.cs.meta
│       │   │   │   ├── Helpers.meta
│       │   │   │   ├── MCPForUnityTests.Editor.asmdef
│       │   │   │   ├── MCPForUnityTests.Editor.asmdef.meta
│       │   │   │   ├── Resources
│       │   │   │   │   ├── GetMenuItemsTests.cs
│       │   │   │   │   └── GetMenuItemsTests.cs.meta
│       │   │   │   ├── Resources.meta
│       │   │   │   ├── Services
│       │   │   │   │   ├── PackageUpdateServiceTests.cs
│       │   │   │   │   ├── PackageUpdateServiceTests.cs.meta
│       │   │   │   │   ├── PythonToolRegistryServiceTests.cs
│       │   │   │   │   ├── PythonToolRegistryServiceTests.cs.meta
│       │   │   │   │   ├── ToolSyncServiceTests.cs
│       │   │   │   │   └── ToolSyncServiceTests.cs.meta
│       │   │   │   ├── Services.meta
│       │   │   │   ├── Tools
│       │   │   │   │   ├── AIPropertyMatchingTests.cs
│       │   │   │   │   ├── AIPropertyMatchingTests.cs.meta
│       │   │   │   │   ├── CommandRegistryTests.cs
│       │   │   │   │   ├── CommandRegistryTests.cs.meta
│       │   │   │   │   ├── ComponentResolverTests.cs
│       │   │   │   │   ├── ComponentResolverTests.cs.meta
│       │   │   │   │   ├── ExecuteMenuItemTests.cs
│       │   │   │   │   ├── ExecuteMenuItemTests.cs.meta
│       │   │   │   │   ├── ManageGameObjectTests.cs
│       │   │   │   │   ├── ManageGameObjectTests.cs.meta
│       │   │   │   │   ├── ManagePrefabsTests.cs
│       │   │   │   │   ├── ManagePrefabsTests.cs.meta
│       │   │   │   │   ├── ManageScriptValidationTests.cs
│       │   │   │   │   └── ManageScriptValidationTests.cs.meta
│       │   │   │   ├── Tools.meta
│       │   │   │   ├── Windows
│       │   │   │   │   ├── ManualConfigJsonBuilderTests.cs
│       │   │   │   │   └── ManualConfigJsonBuilderTests.cs.meta
│       │   │   │   └── Windows.meta
│       │   │   └── EditMode.meta
│       │   └── Tests.meta
│       ├── Packages
│       │   └── manifest.json
│       └── ProjectSettings
│           ├── Packages
│           │   └── com.unity.testtools.codecoverage
│           │       └── Settings.json
│           └── ProjectVersion.txt
├── tests
│   ├── conftest.py
│   ├── test_edit_normalization_and_noop.py
│   ├── test_edit_strict_and_warnings.py
│   ├── test_find_in_file_minimal.py
│   ├── test_get_sha.py
│   ├── test_improved_anchor_matching.py
│   ├── test_logging_stdout.py
│   ├── test_manage_script_uri.py
│   ├── test_read_console_truncate.py
│   ├── test_read_resource_minimal.py
│   ├── test_resources_api.py
│   ├── test_script_editing.py
│   ├── test_script_tools.py
│   ├── test_telemetry_endpoint_validation.py
│   ├── test_telemetry_queue_worker.py
│   ├── test_telemetry_subaction.py
│   ├── test_transport_framing.py
│   └── test_validate_script_summary.py
├── tools
│   └── stress_mcp.py
└── UnityMcpBridge
    ├── Editor
    │   ├── AssemblyInfo.cs
    │   ├── AssemblyInfo.cs.meta
    │   ├── Data
    │   │   ├── DefaultServerConfig.cs
    │   │   ├── DefaultServerConfig.cs.meta
    │   │   ├── McpClients.cs
    │   │   └── McpClients.cs.meta
    │   ├── Data.meta
    │   ├── Dependencies
    │   │   ├── DependencyManager.cs
    │   │   ├── DependencyManager.cs.meta
    │   │   ├── Models
    │   │   │   ├── DependencyCheckResult.cs
    │   │   │   ├── DependencyCheckResult.cs.meta
    │   │   │   ├── DependencyStatus.cs
    │   │   │   └── DependencyStatus.cs.meta
    │   │   ├── Models.meta
    │   │   ├── PlatformDetectors
    │   │   │   ├── IPlatformDetector.cs
    │   │   │   ├── IPlatformDetector.cs.meta
    │   │   │   ├── LinuxPlatformDetector.cs
    │   │   │   ├── LinuxPlatformDetector.cs.meta
    │   │   │   ├── MacOSPlatformDetector.cs
    │   │   │   ├── MacOSPlatformDetector.cs.meta
    │   │   │   ├── PlatformDetectorBase.cs
    │   │   │   ├── PlatformDetectorBase.cs.meta
    │   │   │   ├── WindowsPlatformDetector.cs
    │   │   │   └── WindowsPlatformDetector.cs.meta
    │   │   └── PlatformDetectors.meta
    │   ├── Dependencies.meta
    │   ├── External
    │   │   ├── Tommy.cs
    │   │   └── Tommy.cs.meta
    │   ├── External.meta
    │   ├── Helpers
    │   │   ├── AssetPathUtility.cs
    │   │   ├── AssetPathUtility.cs.meta
    │   │   ├── CodexConfigHelper.cs
    │   │   ├── CodexConfigHelper.cs.meta
    │   │   ├── ConfigJsonBuilder.cs
    │   │   ├── ConfigJsonBuilder.cs.meta
    │   │   ├── ExecPath.cs
    │   │   ├── ExecPath.cs.meta
    │   │   ├── GameObjectSerializer.cs
    │   │   ├── GameObjectSerializer.cs.meta
    │   │   ├── McpConfigFileHelper.cs
    │   │   ├── McpConfigFileHelper.cs.meta
    │   │   ├── McpConfigurationHelper.cs
    │   │   ├── McpConfigurationHelper.cs.meta
    │   │   ├── McpLog.cs
    │   │   ├── McpLog.cs.meta
    │   │   ├── McpPathResolver.cs
    │   │   ├── McpPathResolver.cs.meta
    │   │   ├── PackageDetector.cs
    │   │   ├── PackageDetector.cs.meta
    │   │   ├── PackageInstaller.cs
    │   │   ├── PackageInstaller.cs.meta
    │   │   ├── PortManager.cs
    │   │   ├── PortManager.cs.meta
    │   │   ├── Response.cs
    │   │   ├── Response.cs.meta
    │   │   ├── ServerInstaller.cs
    │   │   ├── ServerInstaller.cs.meta
    │   │   ├── ServerPathResolver.cs
    │   │   ├── ServerPathResolver.cs.meta
    │   │   ├── TelemetryHelper.cs
    │   │   ├── TelemetryHelper.cs.meta
    │   │   ├── Vector3Helper.cs
    │   │   └── Vector3Helper.cs.meta
    │   ├── Helpers.meta
    │   ├── MCPForUnity.Editor.asmdef
    │   ├── MCPForUnity.Editor.asmdef.meta
    │   ├── MCPForUnityBridge.cs
    │   ├── MCPForUnityBridge.cs.meta
    │   ├── Models
    │   │   ├── Command.cs
    │   │   ├── Command.cs.meta
    │   │   ├── McpClient.cs
    │   │   ├── McpClient.cs.meta
    │   │   ├── McpConfig.cs
    │   │   ├── McpConfig.cs.meta
    │   │   ├── MCPConfigServer.cs
    │   │   ├── MCPConfigServer.cs.meta
    │   │   ├── MCPConfigServers.cs
    │   │   ├── MCPConfigServers.cs.meta
    │   │   ├── McpStatus.cs
    │   │   ├── McpStatus.cs.meta
    │   │   ├── McpTypes.cs
    │   │   ├── McpTypes.cs.meta
    │   │   ├── ServerConfig.cs
    │   │   └── ServerConfig.cs.meta
    │   ├── Models.meta
    │   ├── Setup
    │   │   ├── SetupWizard.cs
    │   │   ├── SetupWizard.cs.meta
    │   │   ├── SetupWizardWindow.cs
    │   │   └── SetupWizardWindow.cs.meta
    │   ├── Setup.meta
    │   ├── Tools
    │   │   ├── CommandRegistry.cs
    │   │   ├── CommandRegistry.cs.meta
    │   │   ├── ManageAsset.cs
    │   │   ├── ManageAsset.cs.meta
    │   │   ├── ManageEditor.cs
    │   │   ├── ManageEditor.cs.meta
    │   │   ├── ManageGameObject.cs
    │   │   ├── ManageGameObject.cs.meta
    │   │   ├── ManageScene.cs
    │   │   ├── ManageScene.cs.meta
    │   │   ├── ManageScript.cs
    │   │   ├── ManageScript.cs.meta
    │   │   ├── ManageShader.cs
    │   │   ├── ManageShader.cs.meta
    │   │   ├── McpForUnityToolAttribute.cs
    │   │   ├── McpForUnityToolAttribute.cs.meta
    │   │   ├── MenuItems
    │   │   │   ├── ManageMenuItem.cs
    │   │   │   ├── ManageMenuItem.cs.meta
    │   │   │   ├── MenuItemExecutor.cs
    │   │   │   ├── MenuItemExecutor.cs.meta
    │   │   │   ├── MenuItemsReader.cs
    │   │   │   └── MenuItemsReader.cs.meta
    │   │   ├── MenuItems.meta
    │   │   ├── Prefabs
    │   │   │   ├── ManagePrefabs.cs
    │   │   │   └── ManagePrefabs.cs.meta
    │   │   ├── Prefabs.meta
    │   │   ├── ReadConsole.cs
    │   │   └── ReadConsole.cs.meta
    │   ├── Tools.meta
    │   ├── Windows
    │   │   ├── ManualConfigEditorWindow.cs
    │   │   ├── ManualConfigEditorWindow.cs.meta
    │   │   ├── MCPForUnityEditorWindow.cs
    │   │   ├── MCPForUnityEditorWindow.cs.meta
    │   │   ├── VSCodeManualSetupWindow.cs
    │   │   └── VSCodeManualSetupWindow.cs.meta
    │   └── Windows.meta
    ├── Editor.meta
    ├── package.json
    ├── package.json.meta
    ├── README.md
    ├── README.md.meta
    ├── Runtime
    │   ├── MCPForUnity.Runtime.asmdef
    │   ├── MCPForUnity.Runtime.asmdef.meta
    │   ├── Serialization
    │   │   ├── UnityTypeConverters.cs
    │   │   └── UnityTypeConverters.cs.meta
    │   └── Serialization.meta
    ├── Runtime.meta
    └── UnityMcpServer~
        └── src
            ├── __init__.py
            ├── config.py
            ├── Dockerfile
            ├── port_discovery.py
            ├── pyproject.toml
            ├── pyrightconfig.json
            ├── registry
            │   ├── __init__.py
            │   └── tool_registry.py
            ├── reload_sentinel.py
            ├── server_version.txt
            ├── server.py
            ├── telemetry_decorator.py
            ├── telemetry.py
            ├── test_telemetry.py
            ├── tools
            │   ├── __init__.py
            │   ├── manage_asset.py
            │   ├── manage_editor.py
            │   ├── manage_gameobject.py
            │   ├── manage_menu_item.py
            │   ├── manage_prefabs.py
            │   ├── manage_scene.py
            │   ├── manage_script.py
            │   ├── manage_shader.py
            │   ├── read_console.py
            │   ├── resource_tools.py
            │   └── script_apply_edits.py
            ├── unity_connection.py
            └── uv.lock
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
 1 | # AI-related files
 2 | .cursorrules
 3 | .cursorignore
 4 | .windsurf
 5 | .codeiumignore
 6 | .kiro
 7 | CLAUDE.md
 8 | 
 9 | # Code-copy related files
10 | .clipignore
11 | 
12 | # Python-generated files
13 | __pycache__/
14 | __pycache__.meta
15 | build/
16 | dist/
17 | wheels/
18 | *.egg-info
19 | UnityMcpServer/**/*.meta
20 | UnityMcpServer.meta
21 | 
22 | # Virtual environments
23 | .venv
24 | 
25 | # Unity Editor
26 | *.unitypackage
27 | *.asset
28 | LICENSE.meta
29 | CONTRIBUTING.md.meta
30 | 
31 | # IDE
32 | .idea/
33 | .vscode/
34 | .aider*
35 | .DS_Store*
36 | # Unity test project lock files
37 | TestProjects/UnityMCPTests/Packages/packages-lock.json
38 | 
39 | # Backup artifacts
40 | *.backup
41 | *.backup.meta
42 | 
```

--------------------------------------------------------------------------------
/TestProjects/UnityMCPTests/.gitignore:
--------------------------------------------------------------------------------

```
  1 | # This .gitignore file should be placed at the root of your Unity project directory
  2 | #
  3 | # Get latest from https://github.com/github/gitignore/blob/main/Unity.gitignore
  4 | #
  5 | .utmp/
  6 | /[Ll]ibrary/
  7 | /[Tt]emp/
  8 | /[Oo]bj/
  9 | /[Bb]uild/
 10 | /[Bb]uilds/
 11 | /[Ll]ogs/
 12 | /[Uu]ser[Ss]ettings/
 13 | *.log
 14 | 
 15 | # By default unity supports Blender asset imports, *.blend1 blender files do not need to be commited to version control.
 16 | *.blend1
 17 | *.blend1.meta
 18 | 
 19 | # MemoryCaptures can get excessive in size.
 20 | # They also could contain extremely sensitive data
 21 | /[Mm]emoryCaptures/
 22 | 
 23 | # Recordings can get excessive in size
 24 | /[Rr]ecordings/
 25 | 
 26 | # Uncomment this line if you wish to ignore the asset store tools plugin
 27 | # /[Aa]ssets/AssetStoreTools*
 28 | 
 29 | # Autogenerated Jetbrains Rider plugin
 30 | /[Aa]ssets/Plugins/Editor/JetBrains*
 31 | # Jetbrains Rider personal-layer settings
 32 | *.DotSettings.user
 33 | 
 34 | # Visual Studio cache directory
 35 | .vs/
 36 | 
 37 | # Gradle cache directory
 38 | .gradle/
 39 | 
 40 | # Autogenerated VS/MD/Consulo solution and project files
 41 | ExportedObj/
 42 | .consulo/
 43 | *.csproj
 44 | *.unityproj
 45 | *.sln
 46 | *.suo
 47 | *.tmp
 48 | *.user
 49 | *.userprefs
 50 | *.pidb
 51 | *.booproj
 52 | *.svd
 53 | *.pdb
 54 | *.mdb
 55 | *.opendb
 56 | *.VC.db
 57 | 
 58 | # Unity3D generated meta files
 59 | *.pidb.meta
 60 | *.pdb.meta
 61 | *.mdb.meta
 62 | 
 63 | # Unity3D generated file on crash reports
 64 | sysinfo.txt
 65 | 
 66 | # Mono auto generated files
 67 | mono_crash.*
 68 | 
 69 | # Builds
 70 | *.apk
 71 | *.aab
 72 | *.unitypackage
 73 | *.unitypackage.meta
 74 | *.app
 75 | 
 76 | # Crashlytics generated file
 77 | crashlytics-build.properties
 78 | 
 79 | # TestRunner generated files
 80 | InitTestScene*.unity*
 81 | 
 82 | # Addressables default ignores, before user customizations
 83 | /ServerData
 84 | /[Aa]ssets/StreamingAssets/aa*
 85 | /[Aa]ssets/AddressableAssetsData/link.xml*
 86 | /[Aa]ssets/Addressables_Temp*
 87 | # By default, Addressables content builds will generate addressables_content_state.bin
 88 | # files in platform-specific subfolders, for example:
 89 | # /Assets/AddressableAssetsData/OSX/addressables_content_state.bin
 90 | /[Aa]ssets/AddressableAssetsData/*/*.bin*
 91 | 
 92 | # Visual Scripting auto-generated files
 93 | /[Aa]ssets/Unity.VisualScripting.Generated/VisualScripting.Flow/UnitOptions.db
 94 | /[Aa]ssets/Unity.VisualScripting.Generated/VisualScripting.Flow/UnitOptions.db.meta
 95 | /[Aa]ssets/Unity.VisualScripting.Generated/VisualScripting.Core/Property Providers
 96 | /[Aa]ssets/Unity.VisualScripting.Generated/VisualScripting.Core/Property Providers.meta
 97 | 
 98 | # Auto-generated scenes by play mode tests
 99 | /[Aa]ssets/[Ii]nit[Tt]est[Ss]cene*.unity*
100 | 
101 | .vscode
102 | .cursor
103 | .windsurf
104 | .claude
105 | .DS_Store
106 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/README.md:
--------------------------------------------------------------------------------

```markdown
 1 | # MCP for Unity — Editor Plugin Guide
 2 | 
 3 | Use this guide to configure and run MCP for Unity inside the Unity Editor. Installation is covered elsewhere; this document focuses on the Editor window, client configuration, and troubleshooting.
 4 | 
 5 | ## Open the window
 6 | - Unity menu: Window > MCP for Unity
 7 | 
 8 | The window has four areas: Server Status, Unity Bridge, MCP Client Configuration, and Script Validation.
 9 | 
10 | ---
11 | 
12 | ## Quick start
13 | 1. Open Window > MCP for Unity.
14 | 2. Click “Auto-Setup”.
15 | 3. If prompted:
16 |    - Select the server folder that contains `server.py` (UnityMcpServer~/src).
17 |    - Install Python and/or uv if missing.
18 |    - For Claude Code, ensure the `claude` CLI is installed.
19 | 4. Click “Start Bridge” if the Unity Bridge shows “Stopped”.
20 | 5. Use your MCP client (Cursor, VS Code, Windsurf, Claude Code) to connect.
21 | 
22 | ---
23 | 
24 | ## Server Status
25 | - Status dot and label:
26 |   - Installed / Installed (Embedded) / Not Installed.
27 | - Mode and ports:
28 |   - Mode: Auto or Standard.
29 |   - Ports: Unity (varies; shown in UI), MCP 6500.
30 | - Actions:
31 |   - Auto-Setup: Registers/updates your selected MCP client(s), ensures bridge connectivity. Shows “Connected ✓” after success.
32 |   - Rebuild MCP Server: Rebuilds the Python based MCP server
33 |   - Select server folder…: Choose the folder containing `server.py`.
34 |   - Verify again: Re-checks server presence.
35 |   - If Python isn’t detected, use “Open Install Instructions”.
36 | 
37 | ---
38 | 
39 | ## Unity Bridge
40 | - Shows Running or Stopped with a status dot.
41 | - Start/Stop Bridge button toggles the Unity bridge process used by MCP clients to talk to Unity.
42 | - Tip: After Auto-Setup, the bridge may auto-start in Auto mode.
43 | 
44 | ---
45 | 
46 | ## MCP Client Configuration
47 | - Select Client: Choose your target MCP client (e.g., Cursor, VS Code, Windsurf, Claude Code).
48 | - Per-client actions:
49 |   - Cursor / VS Code / Windsurf:
50 |     - Auto Configure: Writes/updates your config to launch the server via uv:
51 |       - Command: uv
52 |       - Args: run --directory <pythonDir> server.py
53 |     - Manual Setup: Opens a window with a pre-filled JSON snippet to copy/paste into your client config.
54 |     - Choose `uv` Install Location: If uv isn’t on PATH, select the uv binary.
55 |     - A compact “Config:” line shows the resolved config file name once uv/server are detected.
56 |   - Claude Code:
57 |     - Register with Claude Code / Unregister MCP for Unity with Claude Code.
58 |     - If the CLI isn’t found, click “Choose Claude Install Location”.
59 |     - The window displays the resolved Claude CLI path when detected.
60 | 
61 | Notes:
62 | - The UI shows a status dot and a short status text (e.g., “Configured”, “uv Not Found”, “Claude Not Found”).
63 | - Use “Auto Configure” for one-click setup; use “Manual Setup” when you prefer to review/copy config.
64 | 
65 | ---
66 | 
67 | ## Script Validation
68 | - Validation Level options:
69 |   - Basic — Only syntax checks
70 |   - Standard — Syntax + Unity practices
71 |   - Comprehensive — All checks + semantic analysis
72 |   - Strict — Full semantic validation (requires Roslyn)
73 | - Pick a level based on your project’s needs. A description is shown under the dropdown.
74 | 
75 | ---
76 | 
77 | ## Troubleshooting
78 | - Python or `uv` not found:
79 |   - Help: [Fix MCP for Unity with Cursor, VS Code & Windsurf](https://github.com/CoplayDev/unity-mcp/wiki/1.-Fix-Unity-MCP-and-Cursor,-VSCode-&-Windsurf)
80 | - Claude CLI not found:
81 |   - Help: [Fix MCP for Unity with Claude Code](https://github.com/CoplayDev/unity-mcp/wiki/2.-Fix-Unity-MCP-and-Claude-Code)
82 | 
83 | ---
84 | 
85 | ## Tips
86 | - Enable “Show Debug Logs” in the header for more details in the Console when diagnosing issues.
87 | 
88 | ---
```

--------------------------------------------------------------------------------
/UnityMcpBridge/README.md:
--------------------------------------------------------------------------------

```markdown
 1 | # MCP for Unity — Editor Plugin Guide
 2 | 
 3 | Use this guide to configure and run MCP for Unity inside the Unity Editor. Installation is covered elsewhere; this document focuses on the Editor window, client configuration, and troubleshooting.
 4 | 
 5 | ## Open the window
 6 | - Unity menu: Window > MCP for Unity
 7 | 
 8 | The window has four areas: Server Status, Unity Bridge, MCP Client Configuration, and Script Validation.
 9 | 
10 | ---
11 | 
12 | ## Quick start
13 | 1. Open Window > MCP for Unity.
14 | 2. Click “Auto-Setup”.
15 | 3. If prompted:
16 |    - Select the server folder that contains `server.py` (UnityMcpServer~/src).
17 |    - Install Python and/or uv if missing.
18 |    - For Claude Code, ensure the `claude` CLI is installed.
19 | 4. Click “Start Bridge” if the Unity Bridge shows “Stopped”.
20 | 5. Use your MCP client (Cursor, VS Code, Windsurf, Claude Code) to connect.
21 | 
22 | ---
23 | 
24 | ## Server Status
25 | - Status dot and label:
26 |   - Installed / Installed (Embedded) / Not Installed.
27 | - Mode and ports:
28 |   - Mode: Auto or Standard.
29 |   - Ports: Unity (varies; shown in UI), MCP 6500.
30 | - Actions:
31 |   - Auto-Setup: Registers/updates your selected MCP client(s), ensures bridge connectivity. Shows “Connected ✓” after success.
32 |   - Rebuild MCP Server: Rebuilds the Python based MCP server
33 |   - Select server folder…: Choose the folder containing `server.py`.
34 |   - Verify again: Re-checks server presence.
35 |   - If Python isn’t detected, use “Open Install Instructions”.
36 | 
37 | ---
38 | 
39 | ## Unity Bridge
40 | - Shows Running or Stopped with a status dot.
41 | - Start/Stop Bridge button toggles the Unity bridge process used by MCP clients to talk to Unity.
42 | - Tip: After Auto-Setup, the bridge may auto-start in Auto mode.
43 | 
44 | ---
45 | 
46 | ## MCP Client Configuration
47 | - Select Client: Choose your target MCP client (e.g., Cursor, VS Code, Windsurf, Claude Code).
48 | - Per-client actions:
49 |   - Cursor / VS Code / Windsurf:
50 |     - Auto Configure: Writes/updates your config to launch the server via uv:
51 |       - Command: uv
52 |       - Args: run --directory <pythonDir> server.py
53 |     - Manual Setup: Opens a window with a pre-filled JSON snippet to copy/paste into your client config.
54 |     - Choose `uv` Install Location: If uv isn’t on PATH, select the uv binary.
55 |     - A compact “Config:” line shows the resolved config file name once uv/server are detected.
56 |   - Claude Code:
57 |     - Register with Claude Code / Unregister MCP for Unity with Claude Code.
58 |     - If the CLI isn’t found, click “Choose Claude Install Location”.
59 |     - The window displays the resolved Claude CLI path when detected.
60 | 
61 | Notes:
62 | - The UI shows a status dot and a short status text (e.g., “Configured”, “uv Not Found”, “Claude Not Found”).
63 | - Use “Auto Configure” for one-click setup; use “Manual Setup” when you prefer to review/copy config.
64 | 
65 | ---
66 | 
67 | ## Script Validation
68 | - Validation Level options:
69 |   - Basic — Only syntax checks
70 |   - Standard — Syntax + Unity practices
71 |   - Comprehensive — All checks + semantic analysis
72 |   - Strict — Full semantic validation (requires Roslyn)
73 | - Pick a level based on your project’s needs. A description is shown under the dropdown.
74 | 
75 | ---
76 | 
77 | ## Troubleshooting
78 | - Python or `uv` not found:
79 |   - Help: [Fix MCP for Unity with Cursor, VS Code & Windsurf](https://github.com/CoplayDev/unity-mcp/wiki/1.-Fix-Unity-MCP-and-Cursor,-VSCode-&-Windsurf)
80 | - Claude CLI not found:
81 |   - Help: [Fix MCP for Unity with Claude Code](https://github.com/CoplayDev/unity-mcp/wiki/2.-Fix-Unity-MCP-and-Claude-Code)
82 | 
83 | ---
84 | 
85 | ## Tips
86 | - Enable “Show Debug Logs” in the header for more details in the Console when diagnosing issues.
87 | 
88 | ---
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | <img width="676" height="380" alt="MCP for Unity" src="https://github.com/user-attachments/assets/b712e41d-273c-48b2-9041-82bd17ace267" />
  2 | 
  3 | | [English](README.md) | [简体中文](README-zh.md) |
  4 | |----------------------|---------------------------------|
  5 | 
  6 | #### Proudly sponsored and maintained by [Coplay](https://www.coplay.dev/?ref=unity-mcp) -- the best AI assistant for Unity.
  7 | 
  8 | [![Discord](https://img.shields.io/badge/discord-join-red.svg?logo=discord&logoColor=white)](https://discord.gg/y4p8KfzrN4)
  9 | [![](https://img.shields.io/badge/Website-Visit-purple)](https://www.coplay.dev/?ref=unity-mcp)
 10 | [![](https://img.shields.io/badge/Unity-000000?style=flat&logo=unity&logoColor=blue 'Unity')](https://unity.com/releases/editor/archive)
 11 | [![python](https://img.shields.io/badge/Python-3.12-3776AB.svg?style=flat&logo=python&logoColor=white)](https://www.python.org)
 12 | [![](https://badge.mcpx.dev?status=on 'MCP Enabled')](https://modelcontextprotocol.io/introduction)
 13 | ![GitHub commit activity](https://img.shields.io/github/commit-activity/w/CoplayDev/unity-mcp)
 14 | ![GitHub Issues or Pull Requests](https://img.shields.io/github/issues/CoplayDev/unity-mcp)
 15 | [![](https://img.shields.io/badge/License-MIT-red.svg 'MIT License')](https://opensource.org/licenses/MIT)
 16 | 
 17 | **Create your Unity apps with LLMs!**
 18 | 
 19 | MCP for Unity acts as a bridge, allowing AI assistants (like Claude, Cursor) to interact directly with your Unity Editor via a local **MCP (Model Context Protocol) Client**. Give your LLM tools to manage assets, control scenes, edit scripts, and automate tasks within Unity.
 20 | 
 21 | ---
 22 | 
 23 | ### 💬 Join Our [Discord](https://discord.gg/y4p8KfzrN4)
 24 | 
 25 | **Get help, share ideas, and collaborate with other MCP for Unity developers!**  
 26 | 
 27 | ---
 28 | 
 29 | ## Key Features 🚀
 30 | 
 31 | *   **🗣️ Natural Language Control:** Instruct your LLM to perform Unity tasks.
 32 | *   **🛠️ Powerful Tools:** Manage assets, scenes, materials, scripts, and editor functions.
 33 | *   **🤖 Automation:** Automate repetitive Unity workflows.
 34 | *   **🧩 Extensible:** Designed to work with various MCP Clients.
 35 | 
 36 | <details open>
 37 |   <summary><strong> Available Tools </strong></summary>
 38 | 
 39 |   Your LLM can use functions like:
 40 | 
 41 |   *   `read_console`: Gets messages from or clears the console.
 42 |   *   `manage_script`: Manages C# scripts (create, read, update, delete).
 43 |   *   `manage_editor`: Controls and queries the editor's state and settings.
 44 |   *   `manage_scene`: Manages scenes (load, save, create, get hierarchy, etc.).
 45 |   *   `manage_asset`: Performs asset operations (import, create, modify, delete, etc.).
 46 |   *   `manage_shader`: Performs shader CRUD operations (create, read, modify, delete).
 47 |   *   `manage_gameobject`: Manages GameObjects: create, modify, delete, find, and component operations.
 48 |   *   `execute_menu_item`: Executes Unity Editor menu items (e.g., "File/Save Project").
 49 |   *   `apply_text_edits`: Precise text edits with precondition hashes and atomic multi-edit batches.
 50 |   *   `script_apply_edits`: Structured C# method/class edits (insert/replace/delete) with safer boundaries.
 51 |   *   `validate_script`: Fast validation (basic/standard) to catch syntax/structure issues before/after writes.
 52 | </details>
 53 | 
 54 | ---
 55 | 
 56 | ## How It Works 
 57 | 
 58 | MCP for Unity connects your tools using two components:
 59 | 
 60 | 1.  **MCP for Unity Bridge:** A Unity package running inside the Editor. (Installed via Package Manager).
 61 | 2.  **MCP for Unity Server:** A Python server that runs locally, communicating between the Unity Bridge and your MCP Client. (Installed automatically by the package on first run or via Auto-Setup; manual setup is available as a fallback).
 62 | 
 63 | <img width="562" height="121" alt="image" src="https://github.com/user-attachments/assets/9abf9c66-70d1-4b82-9587-658e0d45dc3e" />
 64 | 
 65 | ---
 66 | 
 67 | ## Installation ⚙️
 68 | 
 69 | ### Prerequisites
 70 | 
 71 |   *   **Python:** Version 3.12 or newer. [Download Python](https://www.python.org/downloads/)
 72 |   *   **Unity Hub & Editor:** Version 2021.3 LTS or newer. [Download Unity](https://unity.com/download)
 73 |   *   **uv (Python toolchain manager):**
 74 |       ```bash
 75 |       # macOS / Linux
 76 |       curl -LsSf https://astral.sh/uv/install.sh | sh
 77 | 
 78 |       # Windows (PowerShell)
 79 |       winget install --id=astral-sh.uv  -e
 80 | 
 81 |       # Docs: https://docs.astral.sh/uv/getting-started/installation/
 82 |       ```
 83 |       
 84 |   *   **An MCP Client:** : [Claude Desktop](https://claude.ai/download) | [Claude Code](https://github.com/anthropics/claude-code) | [Cursor](https://www.cursor.com/en/downloads) | [Visual Studio Code Copilot](https://code.visualstudio.com/docs/copilot/overview) | [Windsurf](https://windsurf.com) | Others work with manual config
 85 | 
 86 |  *    <details> <summary><strong>[Optional] Roslyn for Advanced Script Validation</strong></summary>
 87 | 
 88 |         For **Strict** validation level that catches undefined namespaces, types, and methods: 
 89 | 
 90 |         **Method 1: NuGet for Unity (Recommended)**
 91 |         1. Install [NuGetForUnity](https://github.com/GlitchEnzo/NuGetForUnity)
 92 |         2. Go to `Window > NuGet Package Manager`
 93 |         3. Search for `Microsoft.CodeAnalysis`, select version 4.14.0, and install the package
 94 |         4. Also install package `SQLitePCLRaw.core` and `SQLitePCLRaw.bundle_e_sqlite3`.
 95 |         5. Go to `Player Settings > Scripting Define Symbols`
 96 |         6. Add `USE_ROSLYN`
 97 |         7. Restart Unity
 98 | 
 99 |         **Method 2: Manual DLL Installation**
100 |         1. Download Microsoft.CodeAnalysis.CSharp.dll and dependencies from [NuGet](https://www.nuget.org/packages/Microsoft.CodeAnalysis.CSharp/)
101 |         2. Place DLLs in `Assets/Plugins/` folder
102 |         3. Ensure .NET compatibility settings are correct
103 |         4. Add `USE_ROSLYN` to Scripting Define Symbols
104 |         5. Restart Unity
105 | 
106 |         **Note:** Without Roslyn, script validation falls back to basic structural checks. Roslyn enables full C# compiler diagnostics with precise error reporting.</details>
107 | 
108 | ---
109 | ### 🌟 Step 1: Install the Unity Package
110 | 
111 | #### To install via Git URL
112 | 
113 | 1.  Open your Unity project.
114 | 2.  Go to `Window > Package Manager`.
115 | 3.  Click `+` -> `Add package from git URL...`.
116 | 4.  Enter:
117 |     ```
118 |     https://github.com/CoplayDev/unity-mcp.git?path=/MCPForUnity
119 |     ```
120 | 5.  Click `Add`.
121 | 6. The MCP server is installed automatically by the package on first run or via Auto-Setup. If that fails, use Manual Configuration (below).
122 | 
123 | #### To install via OpenUPM
124 | 
125 | 1.  Install the [OpenUPM CLI](https://openupm.com/docs/getting-started-cli.html)
126 | 2.  Open a terminal (PowerShell, Terminal, etc.) and navigate to your Unity project directory
127 | 3.  Run `openupm add com.coplaydev.unity-mcp`
128 | 
129 | **Note:** If you installed the MCP Server before Coplay's maintenance, you will need to uninstall the old package before re-installing the new one.
130 | 
131 | ### 🛠️ Step 2: Configure Your MCP Client
132 | Connect your MCP Client (Claude, Cursor, etc.) to the Python server set up in Step 1 (auto) or via Manual Configuration (below).
133 | 
134 | <img width="648" height="599" alt="MCPForUnity-Readme-Image" src="https://github.com/user-attachments/assets/b4a725da-5c43-4bd6-80d6-ee2e3cca9596" />
135 | 
136 | **Option A: Auto-Setup (Recommended for Claude/Cursor/VSC Copilot)**
137 | 
138 | 1.  In Unity, go to `Window > MCP for Unity`.
139 | 2.  Click `Auto-Setup`.
140 | 3.  Look for a green status indicator 🟢 and "Connected ✓". *(This attempts to modify the MCP Client's config file automatically).* 
141 | 
142 | <details><summary><strong>Client-specific troubleshooting</strong></summary>
143 | 
144 |   - **VSCode**: uses `Code/User/mcp.json` with top-level `servers.unityMCP` and `"type": "stdio"`. On Windows, MCP for Unity writes an absolute `uv.exe` (prefers WinGet Links shim) to avoid PATH issues.
145 |   - **Cursor / Windsurf** [(**help link**)](https://github.com/CoplayDev/unity-mcp/wiki/1.-Fix-Unity-MCP-and-Cursor,-VSCode-&-Windsurf): if `uv` is missing, the MCP for Unity window shows "uv Not Found" with a quick [HELP] link and a "Choose `uv` Install Location" button.
146 |   - **Claude Code** [(**help link**)](https://github.com/CoplayDev/unity-mcp/wiki/2.-Fix-Unity-MCP-and-Claude-Code): if `claude` isn't found, the window shows "Claude Not Found" with [HELP] and a "Choose Claude Location" button. Unregister now updates the UI immediately.</details>
147 | 
148 | 
149 | **Option B: Manual Configuration**
150 | 
151 | If Auto-Setup fails or you use a different client:
152 | 
153 | 1.  **Find your MCP Client's configuration file.** (Check client documentation).
154 |     *   *Claude Example (macOS):* `~/Library/Application Support/Claude/claude_desktop_config.json`
155 |     *   *Claude Example (Windows):* `%APPDATA%\Claude\claude_desktop_config.json`
156 | 2.  **Edit the file** to add/update the `mcpServers` section, using the *exact* paths from Step 1.
157 | 
158 | <details>
159 | <summary><strong>Click for Client-Specific JSON Configuration Snippets...</strong></summary>
160 | 
161 |   ---
162 | **Claude Code**
163 | 
164 | If you're using Claude Code, you can register the MCP server using the below commands:
165 | 🚨**make sure to run these from your Unity project's home directory**🚨
166 | 
167 | **macOS:**
168 | 
169 | ```bash
170 | claude mcp add UnityMCP -- uv --directory /Users/USERNAME/Library/AppSupport/UnityMCP/UnityMcpServer/src run server.py
171 | ```
172 | 
173 | **Windows:**
174 | 
175 | ```bash
176 | claude mcp add UnityMCP -- "C:/Users/USERNAME/AppData/Local/Microsoft/WinGet/Links/uv.exe" --directory "C:/Users/USERNAME/AppData/Local/UnityMCP/UnityMcpServer/src" run server.py
177 | ```
178 | **VSCode (all OS)**
179 | 
180 | ```json
181 | {
182 |   "servers": {
183 |     "unityMCP": {
184 |       "command": "uv",
185 |       "args": ["--directory","<ABSOLUTE_PATH_TO>/UnityMcpServer/src","run","server.py"],
186 |       "type": "stdio"
187 |     }
188 |   }
189 | }
190 | ```
191 | 
192 | On Windows, set `command` to the absolute shim, e.g. `C:\\Users\\YOU\\AppData\\Local\\Microsoft\\WinGet\\Links\\uv.exe`.
193 | 
194 | **Windows:**
195 | 
196 |   ```json
197 |   {
198 |     "mcpServers": {
199 |       "UnityMCP": {
200 |         "command": "uv",
201 |         "args": [
202 |           "run",
203 |           "--directory",
204 |           "C:\\Users\\YOUR_USERNAME\\AppData\\Local\\UnityMCP\\UnityMcpServer\\src",
205 |           "server.py"
206 |         ]
207 |       }
208 |       // ... other servers might be here ...
209 |     }
210 |   }
211 | ``` 
212 | 
213 | (Remember to replace YOUR_USERNAME and use double backslashes \\)
214 | 
215 | **macOS:**
216 | 
217 | ```json
218 | {
219 |   "mcpServers": {
220 |     "UnityMCP": {
221 |       "command": "uv",
222 |       "args": [
223 |         "run",
224 |         "--directory",
225 |         "/Users/YOUR_USERNAME/Library/AppSupport/UnityMCP/UnityMcpServer/src",
226 |         "server.py"
227 |       ]
228 |     }
229 |     // ... other servers might be here ...
230 |   }
231 | }
232 | ```
233 | 
234 | (Replace YOUR_USERNAME. Note: AppSupport is a symlink to "Application Support" to avoid quoting issues)
235 | 
236 | **Linux:**
237 | 
238 | ```json
239 | {
240 |   "mcpServers": {
241 |     "UnityMCP": {
242 |       "command": "uv",
243 |       "args": [
244 |         "run",
245 |         "--directory",
246 |         "/home/YOUR_USERNAME/.local/share/UnityMCP/UnityMcpServer/src",
247 |         "server.py"
248 |       ]
249 |     }
250 |     // ... other servers might be here ...
251 |   }
252 | }
253 | ```
254 | 
255 | (Replace YOUR_USERNAME)
256 | 
257 | 
258 | </details>
259 | 
260 | ---
261 | 
262 | ## Usage ▶️
263 | 
264 | 1. **Open your Unity Project.** The MCP for Unity package should connect automatically. Check status via Window > MCP for Unity.
265 |     
266 | 2. **Start your MCP Client** (Claude, Cursor, etc.). It should automatically launch the MCP for Unity Server (Python) using the configuration from Installation Step 2.
267 |     
268 | 3. **Interact!** Unity tools should now be available in your MCP Client.
269 |     
270 |     Example Prompt: `Create a 3D player controller`, `Create a tic-tac-toe game in 3D`, `Create a cool shader and apply to a cube`.
271 | 
272 | ---
273 | 
274 | ## Development & Contributing 🛠️
275 | 
276 | ### Adding Custom Tools
277 | 
278 | MCP for Unity uses a Python MCP Server tied with Unity's C# scripts for tools. If you'd like to extend the functionality with your own tools, learn how to do so in **[CUSTOM_TOOLS.md](docs/CUSTOM_TOOLS.md)**.
279 | 
280 | ### Contributing to the Project
281 | 
282 | If you're contributing to MCP for Unity or want to test core changes, we have development tools to streamline your workflow:
283 | 
284 | - **Development Deployment Scripts**: Quickly deploy and test your changes to MCP for Unity Bridge and Python Server
285 | - **Automatic Backup System**: Safe testing with easy rollback capabilities  
286 | - **Hot Reload Workflow**: Fast iteration cycle for core development
287 | 
288 | 📖 **See [README-DEV.md](docs/README-DEV.md)** for complete development setup and workflow documentation.
289 | 
290 | ### Contributing 🤝
291 | 
292 | Help make MCP for Unity better!
293 | 
294 | 1. **Fork** the main repository.
295 | 2. **Create a branch** (`feature/your-idea` or `bugfix/your-fix`).
296 | 3. **Make changes.**
297 | 4. **Commit** (feat: Add cool new feature).
298 | 5. **Push** your branch.
299 | 6. **Open a Pull Request** against the main branch.
300 | 
301 | ---
302 | 
303 | ## 📊 Telemetry & Privacy
304 | 
305 | MCP for Unity includes **privacy-focused, anonymous telemetry** to help us improve the product. We collect usage analytics and performance data, but **never** your code, project names, or personal information.
306 | 
307 | - **🔒 Anonymous**: Random UUIDs only, no personal data
308 | - **🚫 Easy opt-out**: Set `DISABLE_TELEMETRY=true` environment variable
309 | - **📖 Transparent**: See [TELEMETRY.md](docs/TELEMETRY.md) for full details
310 | 
311 | Your privacy matters to us. All telemetry is optional and designed to respect your workflow.
312 | 
313 | ---
314 | 
315 | ## Troubleshooting ❓
316 | 
317 | <details>  
318 | <summary><strong>Click to view common issues and fixes...</strong></summary>  
319 | 
320 | - **Unity Bridge Not Running/Connecting:**
321 |     - Ensure Unity Editor is open.
322 |     - Check the status window: Window > MCP for Unity.
323 |     - Restart Unity.
324 | - **MCP Client Not Connecting / Server Not Starting:**
325 |     - **Verify Server Path:** Double-check the --directory path in your MCP Client's JSON config. It must exactly match the installation location:
326 |       - **Windows:** `%USERPROFILE%\AppData\Local\UnityMCP\UnityMcpServer\src`
327 |       - **macOS:** `~/Library/AppSupport/UnityMCP/UnityMcpServer\src` 
328 |       - **Linux:** `~/.local/share/UnityMCP/UnityMcpServer\src`
329 |     - **Verify uv:** Make sure `uv` is installed and working (`uv --version`).
330 |     - **Run Manually:** Try running the server directly from the terminal to see errors: 
331 |       ```bash
332 |       cd /path/to/your/UnityMCP/UnityMcpServer/src
333 |       uv run server.py
334 |       ```
335 | - **Auto-Configure Failed:**
336 |     - Use the Manual Configuration steps. Auto-configure might lack permissions to write to the MCP client's config file.
337 | 
338 | </details>  
339 | 
340 | Still stuck? [Open an Issue](https://github.com/CoplayDev/unity-mcp/issues) or [Join the Discord](https://discord.gg/y4p8KfzrN4)!
341 | 
342 | ---
343 | 
344 | ## License 📜
345 | 
346 | MIT License. See [LICENSE](LICENSE) file.
347 | 
348 | ---
349 | 
350 | ## Star History
351 | 
352 | [![Star History Chart](https://api.star-history.com/svg?repos=CoplayDev/unity-mcp&type=Date)](https://www.star-history.com/#CoplayDev/unity-mcp&Date)
353 | 
354 | ## Unity AI Tools by Coplay
355 | 
356 | Coplay offers 2 AI tools for Unity
357 | - **MCP for Unity** is available freely under the MIT license.
358 | - **Coplay** is a premium Unity AI assistant that sits within Unity and is more than the MCP for Unity.
359 | 
360 | (These tools have different tech stacks. See this blog post [comparing Coplay to MCP for Unity](https://www.coplay.dev/blog/comparing-coplay-and-unity-mcp).)
361 | 
362 | ## Disclaimer
363 | 
364 | This project is a free and open-source tool for the Unity Editor, and is not affiliated with Unity Technologies.
365 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/UnityMcpServer~/src/__init__.py:
--------------------------------------------------------------------------------

```python
1 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/UnityMcpServer~/src/server_version.txt:
--------------------------------------------------------------------------------

```
1 | 6.2.0
2 | 
```

--------------------------------------------------------------------------------
/UnityMcpBridge/UnityMcpServer~/src/server_version.txt:
--------------------------------------------------------------------------------

```
1 | 4.1.1
2 | 
```

--------------------------------------------------------------------------------
/UnityMcpBridge/UnityMcpServer~/src/__init__.py:
--------------------------------------------------------------------------------

```python
1 | """
2 | MCP for Unity Server package.
3 | """
4 | 
```

--------------------------------------------------------------------------------
/TestProjects/UnityMCPTests/ProjectSettings/ProjectVersion.txt:
--------------------------------------------------------------------------------

```
1 | m_EditorVersion: 2021.3.45f2
2 | m_EditorVersionWithRevision: 2021.3.45f2 (88f88f591b2e)
3 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/Editor/AssemblyInfo.cs:
--------------------------------------------------------------------------------

```csharp
1 | using System.Runtime.CompilerServices;
2 | 
3 | [assembly: InternalsVisibleTo("MCPForUnityTests.EditMode")]
4 | 
```

--------------------------------------------------------------------------------
/UnityMcpBridge/Editor/AssemblyInfo.cs:
--------------------------------------------------------------------------------

```csharp
1 | using System.Runtime.CompilerServices;
2 | 
3 | [assembly: InternalsVisibleTo("MCPForUnityTests.EditMode")]
4 | 
```

--------------------------------------------------------------------------------
/TestProjects/UnityMCPTests/Assets/Scripts/Hello.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using UnityEngine;
 2 | using System.Collections;
 3 | 
 4 | public class Hello : MonoBehaviour
 5 | {
 6 |     void Start()
 7 |     {
 8 |         Debug.Log("Hello World");
 9 |     }
10 | }
11 | 
```

--------------------------------------------------------------------------------
/TestProjects/UnityMCPTests/ProjectSettings/Packages/com.unity.testtools.codecoverage/Settings.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |     "m_Name": "Settings",
3 |     "m_Path": "ProjectSettings/Packages/com.unity.testtools.codecoverage/Settings.json",
4 |     "m_Dictionary": {
5 |         "m_DictionaryValues": []
6 |     }
7 | }
```

--------------------------------------------------------------------------------
/MCPForUnity/UnityMcpServer~/src/pyrightconfig.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "typeCheckingMode": "basic",
 3 |   "reportMissingImports": "none",
 4 |   "pythonVersion": "3.11",
 5 |   "executionEnvironments": [
 6 |     {
 7 |       "root": ".",
 8 |       "pythonVersion": "3.11"
 9 |     }
10 |   ]
11 | }
12 | 
```

--------------------------------------------------------------------------------
/UnityMcpBridge/UnityMcpServer~/src/pyrightconfig.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "typeCheckingMode": "basic",
 3 |   "reportMissingImports": "none",
 4 |   "pythonVersion": "3.11",
 5 |   "executionEnvironments": [
 6 |     {
 7 |       "root": ".",
 8 |       "pythonVersion": "3.11"
 9 |     }
10 |   ]
11 | }
12 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/UnityMcpServer~/src/models.py:
--------------------------------------------------------------------------------

```python
 1 | from typing import Any
 2 | from pydantic import BaseModel
 3 | 
 4 | 
 5 | class MCPResponse(BaseModel):
 6 |     success: bool
 7 |     message: str | None = None
 8 |     error: str | None = None
 9 |     data: Any | None = None
10 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/Editor/Models/McpTypes.cs:
--------------------------------------------------------------------------------

```csharp
 1 | namespace MCPForUnity.Editor.Models
 2 | {
 3 |     public enum McpTypes
 4 |     {
 5 |         ClaudeCode,
 6 |         ClaudeDesktop,
 7 |         Codex,
 8 |         Cursor,
 9 |         Kiro,
10 |         VSCode,
11 |         Windsurf,
12 |     }
13 | }
14 | 
```

--------------------------------------------------------------------------------
/UnityMcpBridge/Editor/Models/McpTypes.cs:
--------------------------------------------------------------------------------

```csharp
 1 | namespace MCPForUnity.Editor.Models
 2 | {
 3 |     public enum McpTypes
 4 |     {
 5 |         ClaudeCode,
 6 |         ClaudeDesktop,
 7 |         Codex,
 8 |         Cursor,
 9 |         Kiro,
10 |         VSCode,
11 |         Windsurf,
12 |     }
13 | }
14 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/Editor/Models/McpConfig.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using System;
 2 | using Newtonsoft.Json;
 3 | 
 4 | namespace MCPForUnity.Editor.Models
 5 | {
 6 |     [Serializable]
 7 |     public class McpConfig
 8 |     {
 9 |         [JsonProperty("mcpServers")]
10 |         public McpConfigServers mcpServers;
11 |     }
12 | }
13 | 
```

--------------------------------------------------------------------------------
/UnityMcpBridge/Editor/Models/McpConfig.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using System;
 2 | using Newtonsoft.Json;
 3 | 
 4 | namespace MCPForUnity.Editor.Models
 5 | {
 6 |     [Serializable]
 7 |     public class McpConfig
 8 |     {
 9 |         [JsonProperty("mcpServers")]
10 |         public McpConfigServers mcpServers;
11 |     }
12 | }
13 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/Editor/Models/MCPConfigServers.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using System;
 2 | using Newtonsoft.Json;
 3 | 
 4 | namespace MCPForUnity.Editor.Models
 5 | {
 6 |     [Serializable]
 7 |     public class McpConfigServers
 8 |     {
 9 |         [JsonProperty("unityMCP")]
10 |         public McpConfigServer unityMCP;
11 |     }
12 | }
13 | 
```

--------------------------------------------------------------------------------
/UnityMcpBridge/Editor/Models/MCPConfigServers.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using System;
 2 | using Newtonsoft.Json;
 3 | 
 4 | namespace MCPForUnity.Editor.Models
 5 | {
 6 |     [Serializable]
 7 |     public class McpConfigServers
 8 |     {
 9 |         [JsonProperty("unityMCP")]
10 |         public McpConfigServer unityMCP;
11 |     }
12 | }
13 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/UnityMcpServer~/src/Dockerfile:
--------------------------------------------------------------------------------

```dockerfile
 1 | FROM python:3.13-slim
 2 | 
 3 | RUN apt-get update && apt-get install -y --no-install-recommends \
 4 |     git \
 5 |     && rm -rf /var/lib/apt/lists/*
 6 | 
 7 | WORKDIR /app
 8 | 
 9 | RUN pip install uv
10 | 
11 | COPY . /app
12 | 
13 | RUN uv sync
14 | 
15 | CMD ["uv", "run", "server.py"]
16 | 
```

--------------------------------------------------------------------------------
/UnityMcpBridge/UnityMcpServer~/src/registry/__init__.py:
--------------------------------------------------------------------------------

```python
 1 | """
 2 | Registry package for MCP tool auto-discovery.
 3 | """
 4 | from .tool_registry import (
 5 |     mcp_for_unity_tool,
 6 |     get_registered_tools,
 7 |     clear_registry
 8 | )
 9 | 
10 | __all__ = [
11 |     'mcp_for_unity_tool',
12 |     'get_registered_tools',
13 |     'clear_registry'
14 | ]
15 | 
```

--------------------------------------------------------------------------------
/.claude/settings.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "permissions": {
 3 |     "allow": [
 4 |       "mcp__unity",
 5 |       "Edit(reports/**)",
 6 |       "MultiEdit(reports/**)"
 7 |     ],
 8 |     "deny": [
 9 |       "Bash",
10 |       "WebFetch",
11 |       "WebSearch",
12 |       "Task",
13 |       "TodoWrite",
14 |       "NotebookEdit",
15 |       "NotebookRead"
16 |     ]
17 |   }
18 | }
19 | 
```

--------------------------------------------------------------------------------
/tests/conftest.py:
--------------------------------------------------------------------------------

```python
1 | import os
2 | 
3 | # Ensure telemetry is disabled during test collection and execution to avoid
4 | # any background network or thread startup that could slow or block pytest.
5 | os.environ.setdefault("DISABLE_TELEMETRY", "true")
6 | os.environ.setdefault("UNITY_MCP_DISABLE_TELEMETRY", "true")
7 | os.environ.setdefault("MCP_DISABLE_TELEMETRY", "true")
8 | 
```

--------------------------------------------------------------------------------
/scripts/validate-nlt-coverage.sh:
--------------------------------------------------------------------------------

```bash
 1 | #!/usr/bin/env bash
 2 | set -euo pipefail
 3 | cd "$(git rev-parse --show-toplevel)"
 4 | missing=()
 5 | for id in NL-0 NL-1 NL-2 NL-3 NL-4 T-A T-B T-C T-D T-E T-F T-G T-H T-I T-J; do
 6 |   [[ -s "reports/${id}_results.xml" ]] || missing+=("$id")
 7 | done
 8 | if (( ${#missing[@]} )); then
 9 |   echo "Missing fragments: ${missing[*]}"
10 |   exit 2
11 | fi
12 | echo "All NL/T fragments present."
13 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/UnityMcpServer~/src/reload_sentinel.py:
--------------------------------------------------------------------------------

```python
 1 | """
 2 | Deprecated: Sentinel flipping is handled inside Unity via the MCP menu
 3 | 'MCP/Flip Reload Sentinel'. This module remains only as a compatibility shim.
 4 | All functions are no-ops to prevent accidental external writes.
 5 | """
 6 | 
 7 | 
 8 | def flip_reload_sentinel(*args, **kwargs) -> str:
 9 |     return "reload_sentinel.py is deprecated; use execute_menu_item → 'MCP/Flip Reload Sentinel'"
10 | 
```

--------------------------------------------------------------------------------
/UnityMcpBridge/UnityMcpServer~/src/reload_sentinel.py:
--------------------------------------------------------------------------------

```python
 1 | """
 2 | Deprecated: Sentinel flipping is handled inside Unity via the MCP menu
 3 | 'MCP/Flip Reload Sentinel'. This module remains only as a compatibility shim.
 4 | All functions are no-ops to prevent accidental external writes.
 5 | """
 6 | 
 7 | 
 8 | def flip_reload_sentinel(*args, **kwargs) -> str:
 9 |     return "reload_sentinel.py is deprecated; use execute_menu_item → 'MCP/Flip Reload Sentinel'"
10 | 
```

--------------------------------------------------------------------------------
/TestProjects/UnityMCPTests/Assets/Scripts/TestAsmdef/CustomComponent.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using UnityEngine;
 2 | 
 3 | namespace TestNamespace
 4 | {
 5 |     public class CustomComponent : MonoBehaviour
 6 |     {
 7 |         [SerializeField]
 8 |         private string customText = "Hello from custom asmdef!";
 9 | 
10 |         [SerializeField]
11 |         private float customFloat = 42.0f;
12 | 
13 |         void Start()
14 |         {
15 |             Debug.Log($"CustomComponent started: {customText}, value: {customFloat}");
16 |         }
17 |     }
18 | }
19 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/Editor/Services/IPythonToolRegistryService.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using System.Collections.Generic;
 2 | using UnityEngine;
 3 | using MCPForUnity.Editor.Data;
 4 | 
 5 | namespace MCPForUnity.Editor.Services
 6 | {
 7 |     public interface IPythonToolRegistryService
 8 |     {
 9 |         IEnumerable<PythonToolsAsset> GetAllRegistries();
10 |         bool NeedsSync(PythonToolsAsset registry, TextAsset file);
11 |         void RecordSync(PythonToolsAsset registry, TextAsset file);
12 |         string ComputeHash(TextAsset file);
13 |     }
14 | }
15 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/Editor/Models/MCPConfigServer.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using System;
 2 | using Newtonsoft.Json;
 3 | 
 4 | namespace MCPForUnity.Editor.Models
 5 | {
 6 |     [Serializable]
 7 |     public class McpConfigServer
 8 |     {
 9 |         [JsonProperty("command")]
10 |         public string command;
11 | 
12 |         [JsonProperty("args")]
13 |         public string[] args;
14 | 
15 |         // VSCode expects a transport type; include only when explicitly set
16 |         [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
17 |         public string type;
18 |     }
19 | }
20 | 
```

--------------------------------------------------------------------------------
/UnityMcpBridge/Editor/Models/MCPConfigServer.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using System;
 2 | using Newtonsoft.Json;
 3 | 
 4 | namespace MCPForUnity.Editor.Models
 5 | {
 6 |     [Serializable]
 7 |     public class McpConfigServer
 8 |     {
 9 |         [JsonProperty("command")]
10 |         public string command;
11 | 
12 |         [JsonProperty("args")]
13 |         public string[] args;
14 | 
15 |         // VSCode expects a transport type; include only when explicitly set
16 |         [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
17 |         public string type;
18 |     }
19 | }
20 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/Editor/Models/Command.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using Newtonsoft.Json.Linq;
 2 | 
 3 | namespace MCPForUnity.Editor.Models
 4 | {
 5 |     /// <summary>
 6 |     /// Represents a command received from the MCP client
 7 |     /// </summary>
 8 |     public class Command
 9 |     {
10 |         /// <summary>
11 |         /// The type of command to execute
12 |         /// </summary>
13 |         public string type { get; set; }
14 | 
15 |         /// <summary>
16 |         /// The parameters for the command
17 |         /// </summary>
18 |         public JObject @params { get; set; }
19 |     }
20 | }
21 | 
22 | 
```

--------------------------------------------------------------------------------
/UnityMcpBridge/Editor/Models/Command.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using Newtonsoft.Json.Linq;
 2 | 
 3 | namespace MCPForUnity.Editor.Models
 4 | {
 5 |     /// <summary>
 6 |     /// Represents a command received from the MCP client
 7 |     /// </summary>
 8 |     public class Command
 9 |     {
10 |         /// <summary>
11 |         /// The type of command to execute
12 |         /// </summary>
13 |         public string type { get; set; }
14 | 
15 |         /// <summary>
16 |         /// The parameters for the command
17 |         /// </summary>
18 |         public JObject @params { get; set; }
19 |     }
20 | }
21 | 
22 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/UnityMcpServer~/src/registry/__init__.py:
--------------------------------------------------------------------------------

```python
 1 | """
 2 | Registry package for MCP tool auto-discovery.
 3 | """
 4 | from .tool_registry import (
 5 |     mcp_for_unity_tool,
 6 |     get_registered_tools,
 7 |     clear_tool_registry,
 8 | )
 9 | from .resource_registry import (
10 |     mcp_for_unity_resource,
11 |     get_registered_resources,
12 |     clear_resource_registry,
13 | )
14 | 
15 | __all__ = [
16 |     'mcp_for_unity_tool',
17 |     'get_registered_tools',
18 |     'clear_tool_registry',
19 |     'mcp_for_unity_resource',
20 |     'get_registered_resources',
21 |     'clear_resource_registry'
22 | ]
23 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/Editor/Services/IToolSyncService.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using System.Collections.Generic;
 2 | 
 3 | namespace MCPForUnity.Editor.Services
 4 | {
 5 |     public class ToolSyncResult
 6 |     {
 7 |         public int CopiedCount { get; set; }
 8 |         public int SkippedCount { get; set; }
 9 |         public int ErrorCount { get; set; }
10 |         public List<string> Messages { get; set; } = new List<string>();
11 |         public bool Success => ErrorCount == 0;
12 |     }
13 | 
14 |     public interface IToolSyncService
15 |     {
16 |         ToolSyncResult SyncProjectTools(string destToolsDir);
17 |     }
18 | }
19 | 
```

--------------------------------------------------------------------------------
/UnityMcpBridge/UnityMcpServer~/src/pyproject.toml:
--------------------------------------------------------------------------------

```toml
 1 | [project]
 2 | name = "MCPForUnityServer"
 3 | version = "4.1.1"
 4 | description = "MCP for Unity Server: A Unity package for Unity Editor integration via the Model Context Protocol (MCP)."
 5 | readme = "README.md"
 6 | requires-python = ">=3.10"
 7 | dependencies = [
 8 |  "httpx>=0.27.2",
 9 |  "mcp[cli]>=1.15.0",
10 |  "tomli>=2.3.0",
11 | ]
12 | 
13 | [build-system]
14 | requires = ["setuptools>=64.0.0", "wheel"]
15 | build-backend = "setuptools.build_meta"
16 | 
17 | [tool.setuptools]
18 | py-modules = ["config", "server", "unity_connection"]
19 | packages = ["tools"]
20 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/UnityMcpServer~/src/pyproject.toml:
--------------------------------------------------------------------------------

```toml
 1 | [project]
 2 | name = "MCPForUnityServer"
 3 | version = "6.2.0"
 4 | description = "MCP for Unity Server: A Unity package for Unity Editor integration via the Model Context Protocol (MCP)."
 5 | readme = "README.md"
 6 | requires-python = ">=3.10"
 7 | dependencies = [
 8 |  "httpx>=0.27.2",
 9 |  "mcp[cli]>=1.17.0",
10 |  "pydantic>=2.12.0",
11 |  "tomli>=2.3.0",
12 | ]
13 | 
14 | [build-system]
15 | requires = ["setuptools>=64.0.0", "wheel"]
16 | build-backend = "setuptools.build_meta"
17 | 
18 | [tool.setuptools]
19 | py-modules = ["config", "server", "unity_connection"]
20 | packages = ["tools"]
21 | 
```

--------------------------------------------------------------------------------
/.github/workflows/github-repo-stats.yml:
--------------------------------------------------------------------------------

```yaml
 1 | name: github-repo-stats
 2 | 
 3 | on:
 4 |   schedule:
 5 |     # Run this once per day, towards the end of the day for keeping the most
 6 |     # recent data point most meaningful (hours are interpreted in UTC).
 7 |     - cron: "0 23 * * *"
 8 |   workflow_dispatch: # Allow for running this manually.
 9 | 
10 | jobs:
11 |   j1:
12 |     name: github-repo-stats
13 |     runs-on: ubuntu-latest
14 |     steps:
15 |       - name: run-ghrs
16 |         # Use latest release.
17 |         uses: jgehrcke/github-repo-stats@RELEASE
18 |         with:
19 |           ghtoken: ${{ secrets.ghrs_github_api_token }}
20 | 
```

--------------------------------------------------------------------------------
/UnityMcpBridge/UnityMcpServer~/src/Dockerfile:
--------------------------------------------------------------------------------

```dockerfile
 1 | FROM python:3.12-slim
 2 | 
 3 | # Install required system dependencies
 4 | RUN apt-get update && apt-get install -y --no-install-recommends \
 5 |     git \
 6 |     && rm -rf /var/lib/apt/lists/*
 7 | 
 8 | # Set working directory
 9 | WORKDIR /app
10 | 
11 | # Install uv package manager
12 | RUN pip install uv
13 | 
14 | # Copy required files
15 | COPY config.py /app/
16 | COPY server.py /app/
17 | COPY unity_connection.py /app/
18 | COPY pyproject.toml /app/
19 | COPY __init__.py /app/
20 | COPY tools/ /app/tools/
21 | 
22 | # Install dependencies using uv
23 | RUN uv pip install --system -e .
24 | 
25 | 
26 | # Command to run the server
27 | CMD ["uv", "run", "server.py"]
28 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/Editor/Data/DefaultServerConfig.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using MCPForUnity.Editor.Models;
 2 | 
 3 | namespace MCPForUnity.Editor.Data
 4 | {
 5 |     public class DefaultServerConfig : ServerConfig
 6 |     {
 7 |         public new string unityHost = "localhost";
 8 |         public new int unityPort = 6400;
 9 |         public new int mcpPort = 6500;
10 |         public new float connectionTimeout = 15.0f;
11 |         public new int bufferSize = 32768;
12 |         public new string logLevel = "INFO";
13 |         public new string logFormat = "%(asctime)s - %(name)s - %(levelname)s - %(message)s";
14 |         public new int maxRetries = 3;
15 |         public new float retryDelay = 1.0f;
16 |     }
17 | }
18 | 
```

--------------------------------------------------------------------------------
/UnityMcpBridge/Editor/Data/DefaultServerConfig.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using MCPForUnity.Editor.Models;
 2 | 
 3 | namespace MCPForUnity.Editor.Data
 4 | {
 5 |     public class DefaultServerConfig : ServerConfig
 6 |     {
 7 |         public new string unityHost = "localhost";
 8 |         public new int unityPort = 6400;
 9 |         public new int mcpPort = 6500;
10 |         public new float connectionTimeout = 15.0f;
11 |         public new int bufferSize = 32768;
12 |         public new string logLevel = "INFO";
13 |         public new string logFormat = "%(asctime)s - %(name)s - %(levelname)s - %(message)s";
14 |         public new int maxRetries = 3;
15 |         public new float retryDelay = 1.0f;
16 |     }
17 | }
18 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/Editor/Models/McpStatus.cs:
--------------------------------------------------------------------------------

```csharp
 1 | namespace MCPForUnity.Editor.Models
 2 | {
 3 |     // Enum representing the various status states for MCP clients
 4 |     public enum McpStatus
 5 |     {
 6 |         NotConfigured, // Not set up yet
 7 |         Configured, // Successfully configured
 8 |         Running, // Service is running
 9 |         Connected, // Successfully connected
10 |         IncorrectPath, // Configuration has incorrect paths
11 |         CommunicationError, // Connected but communication issues
12 |         NoResponse, // Connected but not responding
13 |         MissingConfig, // Config file exists but missing required elements
14 |         UnsupportedOS, // OS is not supported
15 |         Error, // General error state
16 |     }
17 | }
18 | 
19 | 
```

--------------------------------------------------------------------------------
/UnityMcpBridge/Editor/Models/McpStatus.cs:
--------------------------------------------------------------------------------

```csharp
 1 | namespace MCPForUnity.Editor.Models
 2 | {
 3 |     // Enum representing the various status states for MCP clients
 4 |     public enum McpStatus
 5 |     {
 6 |         NotConfigured, // Not set up yet
 7 |         Configured, // Successfully configured
 8 |         Running, // Service is running
 9 |         Connected, // Successfully connected
10 |         IncorrectPath, // Configuration has incorrect paths
11 |         CommunicationError, // Connected but communication issues
12 |         NoResponse, // Connected but not responding
13 |         MissingConfig, // Config file exists but missing required elements
14 |         UnsupportedOS, // OS is not supported
15 |         Error, // General error state
16 |     }
17 | }
18 | 
19 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/Editor/Importers/PythonFileImporter.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using UnityEngine;
 2 | using UnityEditor.AssetImporters;
 3 | using System.IO;
 4 | 
 5 | namespace MCPForUnity.Editor.Importers
 6 | {
 7 |     /// <summary>
 8 |     /// Custom importer that allows Unity to recognize .py files as TextAssets.
 9 |     /// This enables Python files to be selected in the Inspector and used like any other text asset.
10 |     /// </summary>
11 |     [ScriptedImporter(1, "py")]
12 |     public class PythonFileImporter : ScriptedImporter
13 |     {
14 |         public override void OnImportAsset(AssetImportContext ctx)
15 |         {
16 |             var textAsset = new TextAsset(File.ReadAllText(ctx.assetPath));
17 |             ctx.AddObjectToAsset("main obj", textAsset);
18 |             ctx.SetMainObject(textAsset);
19 |         }
20 |     }
21 | }
22 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/Editor/Services/ITestRunnerService.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using System.Collections.Generic;
 2 | using System.Threading.Tasks;
 3 | using UnityEditor.TestTools.TestRunner.Api;
 4 | 
 5 | namespace MCPForUnity.Editor.Services
 6 | {
 7 |     /// <summary>
 8 |     /// Provides access to Unity Test Runner data and execution.
 9 |     /// </summary>
10 |     public interface ITestRunnerService
11 |     {
12 |         /// <summary>
13 |         /// Retrieve the list of tests for the requested mode(s).
14 |         /// When <paramref name="mode"/> is null, tests for both EditMode and PlayMode are returned.
15 |         /// </summary>
16 |         Task<IReadOnlyList<Dictionary<string, string>>> GetTestsAsync(TestMode? mode);
17 | 
18 |         /// <summary>
19 |         /// Execute tests for the supplied mode.
20 |         /// </summary>
21 |         Task<TestRunResult> RunTestsAsync(TestMode mode);
22 |     }
23 | }
24 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/Editor/Models/ServerConfig.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using System;
 2 | using Newtonsoft.Json;
 3 | 
 4 | namespace MCPForUnity.Editor.Models
 5 | {
 6 |     [Serializable]
 7 |     public class ServerConfig
 8 |     {
 9 |         [JsonProperty("unity_host")]
10 |         public string unityHost = "localhost";
11 | 
12 |         [JsonProperty("unity_port")]
13 |         public int unityPort;
14 | 
15 |         [JsonProperty("mcp_port")]
16 |         public int mcpPort;
17 | 
18 |         [JsonProperty("connection_timeout")]
19 |         public float connectionTimeout;
20 | 
21 |         [JsonProperty("buffer_size")]
22 |         public int bufferSize;
23 | 
24 |         [JsonProperty("log_level")]
25 |         public string logLevel;
26 | 
27 |         [JsonProperty("log_format")]
28 |         public string logFormat;
29 | 
30 |         [JsonProperty("max_retries")]
31 |         public int maxRetries;
32 | 
33 |         [JsonProperty("retry_delay")]
34 |         public float retryDelay;
35 |     }
36 | }
37 | 
```

--------------------------------------------------------------------------------
/UnityMcpBridge/Editor/Models/ServerConfig.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using System;
 2 | using Newtonsoft.Json;
 3 | 
 4 | namespace MCPForUnity.Editor.Models
 5 | {
 6 |     [Serializable]
 7 |     public class ServerConfig
 8 |     {
 9 |         [JsonProperty("unity_host")]
10 |         public string unityHost = "localhost";
11 | 
12 |         [JsonProperty("unity_port")]
13 |         public int unityPort;
14 | 
15 |         [JsonProperty("mcp_port")]
16 |         public int mcpPort;
17 | 
18 |         [JsonProperty("connection_timeout")]
19 |         public float connectionTimeout;
20 | 
21 |         [JsonProperty("buffer_size")]
22 |         public int bufferSize;
23 | 
24 |         [JsonProperty("log_level")]
25 |         public string logLevel;
26 | 
27 |         [JsonProperty("log_format")]
28 |         public string logFormat;
29 | 
30 |         [JsonProperty("max_retries")]
31 |         public int maxRetries;
32 | 
33 |         [JsonProperty("retry_delay")]
34 |         public float retryDelay;
35 |     }
36 | }
37 | 
```

--------------------------------------------------------------------------------
/UnityMcpBridge/Editor/Helpers/AssetPathUtility.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using System;
 2 | 
 3 | namespace MCPForUnity.Editor.Helpers
 4 | {
 5 |     /// <summary>
 6 |     /// Provides common utility methods for working with Unity asset paths.
 7 |     /// </summary>
 8 |     public static class AssetPathUtility
 9 |     {
10 |         /// <summary>
11 |         /// Normalizes a Unity asset path by ensuring forward slashes are used and that it is rooted under "Assets/".
12 |         /// </summary>
13 |         public static string SanitizeAssetPath(string path)
14 |         {
15 |             if (string.IsNullOrEmpty(path))
16 |             {
17 |                 return path;
18 |             }
19 | 
20 |             path = path.Replace('\\', '/');
21 |             if (!path.StartsWith("Assets/", StringComparison.OrdinalIgnoreCase))
22 |             {
23 |                 return "Assets/" + path.TrimStart('/');
24 |             }
25 | 
26 |             return path;
27 |         }
28 |     }
29 | }
30 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/UnityMcpServer~/src/resources/menu_items.py:
--------------------------------------------------------------------------------

```python
 1 | from models import MCPResponse
 2 | from registry import mcp_for_unity_resource
 3 | from unity_connection import async_send_command_with_retry
 4 | 
 5 | 
 6 | class GetMenuItemsResponse(MCPResponse):
 7 |     data: list[str] = []
 8 | 
 9 | 
10 | @mcp_for_unity_resource(
11 |     uri="mcpforunity://menu-items",
12 |     name="get_menu_items",
13 |     description="Provides a list of all menu items."
14 | )
15 | async def get_menu_items() -> GetMenuItemsResponse:
16 |     """Provides a list of all menu items."""
17 |     # Later versions of FastMCP support these as query parameters
18 |     # See: https://gofastmcp.com/servers/resources#query-parameters
19 |     params = {
20 |         "refresh": True,
21 |         "search": "",
22 |     }
23 | 
24 |     response = await async_send_command_with_retry("get_menu_items", params)
25 |     return GetMenuItemsResponse(**response) if isinstance(response, dict) else response
26 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/Editor/Helpers/Vector3Helper.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using Newtonsoft.Json.Linq;
 2 | using UnityEngine;
 3 | 
 4 | namespace MCPForUnity.Editor.Helpers
 5 | {
 6 |     /// <summary>
 7 |     /// Helper class for Vector3 operations
 8 |     /// </summary>
 9 |     public static class Vector3Helper
10 |     {
11 |         /// <summary>
12 |         /// Parses a JArray into a Vector3
13 |         /// </summary>
14 |         /// <param name="array">The array containing x, y, z coordinates</param>
15 |         /// <returns>A Vector3 with the parsed coordinates</returns>
16 |         /// <exception cref="System.Exception">Thrown when array is invalid</exception>
17 |         public static Vector3 ParseVector3(JArray array)
18 |         {
19 |             if (array == null || array.Count != 3)
20 |                 throw new System.Exception("Vector3 must be an array of 3 floats [x, y, z].");
21 |             return new Vector3((float)array[0], (float)array[1], (float)array[2]);
22 |         }
23 |     }
24 | }
25 | 
```

--------------------------------------------------------------------------------
/UnityMcpBridge/Editor/Helpers/Vector3Helper.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using Newtonsoft.Json.Linq;
 2 | using UnityEngine;
 3 | 
 4 | namespace MCPForUnity.Editor.Helpers
 5 | {
 6 |     /// <summary>
 7 |     /// Helper class for Vector3 operations
 8 |     /// </summary>
 9 |     public static class Vector3Helper
10 |     {
11 |         /// <summary>
12 |         /// Parses a JArray into a Vector3
13 |         /// </summary>
14 |         /// <param name="array">The array containing x, y, z coordinates</param>
15 |         /// <returns>A Vector3 with the parsed coordinates</returns>
16 |         /// <exception cref="System.Exception">Thrown when array is invalid</exception>
17 |         public static Vector3 ParseVector3(JArray array)
18 |         {
19 |             if (array == null || array.Count != 3)
20 |                 throw new System.Exception("Vector3 must be an array of 3 floats [x, y, z].");
21 |             return new Vector3((float)array[0], (float)array[1], (float)array[2]);
22 |         }
23 |     }
24 | }
25 | 
```

--------------------------------------------------------------------------------
/UnityMcpBridge/Editor/Helpers/McpLog.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using UnityEditor;
 2 | using UnityEngine;
 3 | 
 4 | namespace MCPForUnity.Editor.Helpers
 5 | {
 6 |     internal static class McpLog
 7 |     {
 8 |         private const string Prefix = "<b><color=#2EA3FF>MCP-FOR-UNITY</color></b>:";
 9 | 
10 |         private static bool IsDebugEnabled()
11 |         {
12 |             try { return EditorPrefs.GetBool("MCPForUnity.DebugLogs", false); } catch { return false; }
13 |         }
14 | 
15 |         public static void Info(string message, bool always = true)
16 |         {
17 |             if (!always && !IsDebugEnabled()) return;
18 |             Debug.Log($"{Prefix} {message}");
19 |         }
20 | 
21 |         public static void Warn(string message)
22 |         {
23 |             Debug.LogWarning($"<color=#cc7a00>{Prefix} {message}</color>");
24 |         }
25 | 
26 |         public static void Error(string message)
27 |         {
28 |             Debug.LogError($"<color=#cc3333>{Prefix} {message}</color>");
29 |         }
30 |     }
31 | }
32 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/UnityMcpServer~/src/tools/execute_menu_item.py:
--------------------------------------------------------------------------------

```python
 1 | """
 2 | Defines the execute_menu_item tool for executing and reading Unity Editor menu items.
 3 | """
 4 | from typing import Annotated, Any
 5 | 
 6 | from mcp.server.fastmcp import Context
 7 | 
 8 | from models import MCPResponse
 9 | from registry import mcp_for_unity_tool
10 | from unity_connection import async_send_command_with_retry
11 | 
12 | 
13 | @mcp_for_unity_tool(
14 |     description="Execute a Unity menu item by path."
15 | )
16 | async def execute_menu_item(
17 |     ctx: Context,
18 |     menu_path: Annotated[str,
19 |                          "Menu path for 'execute' or 'exists' (e.g., 'File/Save Project')"] | None = None,
20 | ) -> MCPResponse:
21 |     await ctx.info(f"Processing execute_menu_item: {menu_path}")
22 |     params_dict: dict[str, Any] = {"menuPath": menu_path}
23 |     params_dict = {k: v for k, v in params_dict.items() if v is not None}
24 |     result = await async_send_command_with_retry("execute_menu_item", params_dict)
25 |     return MCPResponse(**result) if isinstance(result, dict) else result
26 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "com.coplaydev.unity-mcp",
 3 |   "version": "6.2.0",
 4 |   "displayName": "MCP for Unity",
 5 |   "description": "A bridge that connects AI assistants to Unity via the MCP (Model Context Protocol). Allows AI clients like Claude Code, Cursor, and VSCode to directly control your Unity Editor for enhanced development workflows.\n\nFeatures automated setup wizard, cross-platform support, and seamless integration with popular AI development tools.\n\nJoin Our Discord: https://discord.gg/y4p8KfzrN4",
 6 |   "unity": "2021.3",
 7 |   "documentationUrl": "https://github.com/CoplayDev/unity-mcp",
 8 |   "licensesUrl": "https://github.com/CoplayDev/unity-mcp/blob/main/LICENSE",
 9 |   "dependencies": {
10 |     "com.unity.nuget.newtonsoft-json": "3.0.2"
11 |   },
12 |   "keywords": [
13 |     "unity",
14 |     "ai",
15 |     "llm",
16 |     "mcp",
17 |     "model-context-protocol",
18 |     "mcp-server",
19 |     "mcp-client"
20 |   ],
21 |   "author": {
22 |     "name": "Coplay",
23 |     "email": "[email protected]",
24 |     "url": "https://coplay.dev"
25 |   }
26 | }
27 | 
```

--------------------------------------------------------------------------------
/UnityMcpBridge/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "com.coplaydev.unity-mcp",
 3 |   "version": "4.1.1",
 4 |   "displayName": "MCP for Unity",
 5 |   "description": "A bridge that connects AI assistants to Unity via the MCP (Model Context Protocol). Allows AI clients like Claude Code, Cursor, and VSCode to directly control your Unity Editor for enhanced development workflows.\n\nFeatures automated setup wizard, cross-platform support, and seamless integration with popular AI development tools.\n\nJoin Our Discord: https://discord.gg/y4p8KfzrN4",
 6 |   "unity": "2021.3",
 7 |   "documentationUrl": "https://github.com/CoplayDev/unity-mcp",
 8 |   "licensesUrl": "https://github.com/CoplayDev/unity-mcp/blob/main/LICENSE",
 9 |   "dependencies": {
10 |     "com.unity.nuget.newtonsoft-json": "3.0.2"
11 |   },
12 |   "keywords": [
13 |     "unity",
14 |     "ai",
15 |     "llm",
16 |     "mcp",
17 |     "model-context-protocol",
18 |     "mcp-server",
19 |     "mcp-client"
20 |   ],
21 |   "author": {
22 |     "name": "Coplay",
23 |     "email": "[email protected]",
24 |     "url": "https://coplay.dev"
25 |   }
26 | }
27 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/Editor/Helpers/McpLog.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using UnityEditor;
 2 | using UnityEngine;
 3 | 
 4 | namespace MCPForUnity.Editor.Helpers
 5 | {
 6 |     internal static class McpLog
 7 |     {
 8 |         private const string LogPrefix = "<b><color=#2EA3FF>MCP-FOR-UNITY</color></b>:";
 9 |         private const string WarnPrefix = "<b><color=#cc7a00>MCP-FOR-UNITY</color></b>:";
10 |         private const string ErrorPrefix = "<b><color=#cc3333>MCP-FOR-UNITY</color></b>:";
11 | 
12 |         private static bool IsDebugEnabled()
13 |         {
14 |             try { return EditorPrefs.GetBool("MCPForUnity.DebugLogs", false); } catch { return false; }
15 |         }
16 | 
17 |         public static void Info(string message, bool always = true)
18 |         {
19 |             if (!always && !IsDebugEnabled()) return;
20 |             Debug.Log($"{LogPrefix} {message}");
21 |         }
22 | 
23 |         public static void Warn(string message)
24 |         {
25 |             Debug.LogWarning($"{WarnPrefix} {message}");
26 |         }
27 | 
28 |         public static void Error(string message)
29 |         {
30 |             Debug.LogError($"{ErrorPrefix} {message}");
31 |         }
32 |     }
33 | }
34 | 
```

--------------------------------------------------------------------------------
/tests/test_script_editing.py:
--------------------------------------------------------------------------------

```python
 1 | import pytest
 2 | 
 3 | 
 4 | @pytest.mark.xfail(strict=False, reason="pending: create new script, validate, apply edits, build and compile scene")
 5 | def test_script_edit_happy_path():
 6 |     pass
 7 | 
 8 | 
 9 | @pytest.mark.xfail(strict=False, reason="pending: multiple micro-edits debounce to single compilation")
10 | def test_micro_edits_debounce():
11 |     pass
12 | 
13 | 
14 | @pytest.mark.xfail(strict=False, reason="pending: line ending variations handled correctly")
15 | def test_line_endings_and_columns():
16 |     pass
17 | 
18 | 
19 | @pytest.mark.xfail(strict=False, reason="pending: regex_replace no-op with allow_noop honored")
20 | def test_regex_replace_noop_allowed():
21 |     pass
22 | 
23 | 
24 | @pytest.mark.xfail(strict=False, reason="pending: large edit size boundaries and overflow protection")
25 | def test_large_edit_size_and_overflow():
26 |     pass
27 | 
28 | 
29 | @pytest.mark.xfail(strict=False, reason="pending: symlink and junction protections on edits")
30 | def test_symlink_and_junction_protection():
31 |     pass
32 | 
33 | 
34 | @pytest.mark.xfail(strict=False, reason="pending: atomic write guarantees")
35 | def test_atomic_write_guarantees():
36 |     pass
37 | 
```

--------------------------------------------------------------------------------
/tests/test_find_in_file_minimal.py:
--------------------------------------------------------------------------------

```python
 1 | from tools.resource_tools import register_resource_tools  # type: ignore
 2 | import sys
 3 | import pathlib
 4 | import importlib.util
 5 | import types
 6 | import asyncio
 7 | import pytest
 8 | 
 9 | ROOT = pathlib.Path(__file__).resolve().parents[1]
10 | SRC = ROOT / "MCPForUnity" / "UnityMcpServer~" / "src"
11 | sys.path.insert(0, str(SRC))
12 | 
13 | 
14 | class DummyMCP:
15 |     def __init__(self):
16 |         self.tools = {}
17 | 
18 |     def tool(self, *args, **kwargs):
19 |         def deco(fn):
20 |             self.tools[fn.__name__] = fn
21 |             return fn
22 |         return deco
23 | 
24 | 
25 | @pytest.fixture()
26 | def resource_tools():
27 |     mcp = DummyMCP()
28 |     register_resource_tools(mcp)
29 |     return mcp.tools
30 | 
31 | 
32 | def test_find_in_file_returns_positions(resource_tools, tmp_path):
33 |     proj = tmp_path
34 |     assets = proj / "Assets"
35 |     assets.mkdir()
36 |     f = assets / "A.txt"
37 |     f.write_text("hello world", encoding="utf-8")
38 |     find_in_file = resource_tools["find_in_file"]
39 |     loop = asyncio.new_event_loop()
40 |     try:
41 |         resp = loop.run_until_complete(
42 |             find_in_file(uri="unity://path/Assets/A.txt",
43 |                          pattern="world", ctx=None, project_root=str(proj))
44 |         )
45 |     finally:
46 |         loop.close()
47 |     assert resp["success"] is True
48 |     assert resp["data"]["matches"] == [
49 |         {"startLine": 1, "startCol": 7, "endLine": 1, "endCol": 12}]
50 | 
```

--------------------------------------------------------------------------------
/UnityMcpBridge/UnityMcpServer~/src/registry/tool_registry.py:
--------------------------------------------------------------------------------

```python
 1 | """
 2 | Tool registry for auto-discovery of MCP tools.
 3 | """
 4 | from typing import Callable, Any
 5 | 
 6 | # Global registry to collect decorated tools
 7 | _tool_registry: list[dict[str, Any]] = []
 8 | 
 9 | 
10 | def mcp_for_unity_tool(
11 |     name: str | None = None,
12 |     description: str | None = None,
13 |     **kwargs
14 | ) -> Callable:
15 |     """
16 |     Decorator for registering MCP tools in the server's tools directory.
17 | 
18 |     Tools are registered in the global tool registry.
19 | 
20 |     Args:
21 |         name: Tool name (defaults to function name)
22 |         description: Tool description
23 |         **kwargs: Additional arguments passed to @mcp.tool()
24 | 
25 |     Example:
26 |         @mcp_for_unity_tool(description="Does something cool")
27 |         async def my_custom_tool(ctx: Context, ...):
28 |             pass
29 |     """
30 |     def decorator(func: Callable) -> Callable:
31 |         tool_name = name if name is not None else func.__name__
32 |         _tool_registry.append({
33 |             'func': func,
34 |             'name': tool_name,
35 |             'description': description,
36 |             'kwargs': kwargs
37 |         })
38 | 
39 |         return func
40 | 
41 |     return decorator
42 | 
43 | 
44 | def get_registered_tools() -> list[dict[str, Any]]:
45 |     """Get all registered tools"""
46 |     return _tool_registry.copy()
47 | 
48 | 
49 | def clear_registry():
50 |     """Clear the tool registry (useful for testing)"""
51 |     _tool_registry.clear()
52 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/UnityMcpServer~/src/registry/tool_registry.py:
--------------------------------------------------------------------------------

```python
 1 | """
 2 | Tool registry for auto-discovery of MCP tools.
 3 | """
 4 | from typing import Callable, Any
 5 | 
 6 | # Global registry to collect decorated tools
 7 | _tool_registry: list[dict[str, Any]] = []
 8 | 
 9 | 
10 | def mcp_for_unity_tool(
11 |     name: str | None = None,
12 |     description: str | None = None,
13 |     **kwargs
14 | ) -> Callable:
15 |     """
16 |     Decorator for registering MCP tools in the server's tools directory.
17 | 
18 |     Tools are registered in the global tool registry.
19 | 
20 |     Args:
21 |         name: Tool name (defaults to function name)
22 |         description: Tool description
23 |         **kwargs: Additional arguments passed to @mcp.tool()
24 | 
25 |     Example:
26 |         @mcp_for_unity_tool(description="Does something cool")
27 |         async def my_custom_tool(ctx: Context, ...):
28 |             pass
29 |     """
30 |     def decorator(func: Callable) -> Callable:
31 |         tool_name = name if name is not None else func.__name__
32 |         _tool_registry.append({
33 |             'func': func,
34 |             'name': tool_name,
35 |             'description': description,
36 |             'kwargs': kwargs
37 |         })
38 | 
39 |         return func
40 | 
41 |     return decorator
42 | 
43 | 
44 | def get_registered_tools() -> list[dict[str, Any]]:
45 |     """Get all registered tools"""
46 |     return _tool_registry.copy()
47 | 
48 | 
49 | def clear_tool_registry():
50 |     """Clear the tool registry (useful for testing)"""
51 |     _tool_registry.clear()
52 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/Editor/Tools/McpForUnityToolAttribute.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using System;
 2 | 
 3 | namespace MCPForUnity.Editor.Tools
 4 | {
 5 |     /// <summary>
 6 |     /// Marks a class as an MCP tool handler for auto-discovery.
 7 |     /// The class must have a public static HandleCommand(JObject) method.
 8 |     /// </summary>
 9 |     [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
10 |     public class McpForUnityToolAttribute : Attribute
11 |     {
12 |         /// <summary>
13 |         /// The command name used to route requests to this tool.
14 |         /// If not specified, defaults to the PascalCase class name converted to snake_case.
15 |         /// </summary>
16 |         public string CommandName { get; }
17 | 
18 |         /// <summary>
19 |         /// Create an MCP tool attribute with auto-generated command name.
20 |         /// The command name will be derived from the class name (PascalCase → snake_case).
21 |         /// Example: ManageAsset → manage_asset
22 |         /// </summary>
23 |         public McpForUnityToolAttribute()
24 |         {
25 |             CommandName = null; // Will be auto-generated
26 |         }
27 | 
28 |         /// <summary>
29 |         /// Create an MCP tool attribute with explicit command name.
30 |         /// </summary>
31 |         /// <param name="commandName">The command name (e.g., "manage_asset")</param>
32 |         public McpForUnityToolAttribute(string commandName)
33 |         {
34 |             CommandName = commandName;
35 |         }
36 |     }
37 | }
38 | 
```

--------------------------------------------------------------------------------
/UnityMcpBridge/Editor/Tools/McpForUnityToolAttribute.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using System;
 2 | 
 3 | namespace MCPForUnity.Editor.Tools
 4 | {
 5 |     /// <summary>
 6 |     /// Marks a class as an MCP tool handler for auto-discovery.
 7 |     /// The class must have a public static HandleCommand(JObject) method.
 8 |     /// </summary>
 9 |     [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
10 |     public class McpForUnityToolAttribute : Attribute
11 |     {
12 |         /// <summary>
13 |         /// The command name used to route requests to this tool.
14 |         /// If not specified, defaults to the PascalCase class name converted to snake_case.
15 |         /// </summary>
16 |         public string CommandName { get; }
17 | 
18 |         /// <summary>
19 |         /// Create an MCP tool attribute with auto-generated command name.
20 |         /// The command name will be derived from the class name (PascalCase → snake_case).
21 |         /// Example: ManageAsset → manage_asset
22 |         /// </summary>
23 |         public McpForUnityToolAttribute()
24 |         {
25 |             CommandName = null; // Will be auto-generated
26 |         }
27 | 
28 |         /// <summary>
29 |         /// Create an MCP tool attribute with explicit command name.
30 |         /// </summary>
31 |         /// <param name="commandName">The command name (e.g., "manage_asset")</param>
32 |         public McpForUnityToolAttribute(string commandName)
33 |         {
34 |             CommandName = commandName;
35 |         }
36 |     }
37 | }
38 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/Editor/Resources/McpForUnityResourceAttribute.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using System;
 2 | 
 3 | namespace MCPForUnity.Editor.Resources
 4 | {
 5 |     /// <summary>
 6 |     /// Marks a class as an MCP resource handler for auto-discovery.
 7 |     /// The class must have a public static HandleCommand(JObject) method.
 8 |     /// </summary>
 9 |     [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
10 |     public class McpForUnityResourceAttribute : Attribute
11 |     {
12 |         /// <summary>
13 |         /// The resource name used to route requests to this resource.
14 |         /// If not specified, defaults to the PascalCase class name converted to snake_case.
15 |         /// </summary>
16 |         public string ResourceName { get; }
17 | 
18 |         /// <summary>
19 |         /// Create an MCP resource attribute with auto-generated resource name.
20 |         /// The resource name will be derived from the class name (PascalCase → snake_case).
21 |         /// Example: ManageAsset → manage_asset
22 |         /// </summary>
23 |         public McpForUnityResourceAttribute()
24 |         {
25 |             ResourceName = null; // Will be auto-generated
26 |         }
27 | 
28 |         /// <summary>
29 |         /// Create an MCP resource attribute with explicit resource name.
30 |         /// </summary>
31 |         /// <param name="resourceName">The resource name (e.g., "manage_asset")</param>
32 |         public McpForUnityResourceAttribute(string resourceName)
33 |         {
34 |             ResourceName = resourceName;
35 |         }
36 |     }
37 | }
38 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/UnityMcpServer~/src/config.py:
--------------------------------------------------------------------------------

```python
 1 | """
 2 | Configuration settings for the MCP for Unity Server.
 3 | This file contains all configurable parameters for the server.
 4 | """
 5 | 
 6 | from dataclasses import dataclass
 7 | 
 8 | 
 9 | @dataclass
10 | class ServerConfig:
11 |     """Main configuration class for the MCP server."""
12 | 
13 |     # Network settings
14 |     unity_host: str = "localhost"
15 |     unity_port: int = 6400
16 |     mcp_port: int = 6500
17 | 
18 |     # Connection settings
19 |     connection_timeout: float = 30.0
20 |     buffer_size: int = 16 * 1024 * 1024  # 16MB buffer
21 |     # Framed receive behavior
22 |     # max seconds to wait while consuming heartbeats only
23 |     framed_receive_timeout: float = 2.0
24 |     # cap heartbeat frames consumed before giving up
25 |     max_heartbeat_frames: int = 16
26 | 
27 |     # Logging settings
28 |     log_level: str = "INFO"
29 |     log_format: str = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
30 | 
31 |     # Server settings
32 |     max_retries: int = 5
33 |     retry_delay: float = 0.25
34 |     # Backoff hint returned to clients when Unity is reloading (milliseconds)
35 |     reload_retry_ms: int = 250
36 |     # Number of polite retries when Unity reports reloading
37 |     # 40 × 250ms ≈ 10s default window
38 |     reload_max_retries: int = 40
39 | 
40 |     # Telemetry settings
41 |     telemetry_enabled: bool = True
42 |     # Align with telemetry.py default Cloud Run endpoint
43 |     telemetry_endpoint: str = "https://api-prod.coplay.dev/telemetry/events"
44 | 
45 | 
46 | # Create a global config instance
47 | config = ServerConfig()
48 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/Editor/Dependencies/PlatformDetectors/IPlatformDetector.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using MCPForUnity.Editor.Dependencies.Models;
 2 | 
 3 | namespace MCPForUnity.Editor.Dependencies.PlatformDetectors
 4 | {
 5 |     /// <summary>
 6 |     /// Interface for platform-specific dependency detection
 7 |     /// </summary>
 8 |     public interface IPlatformDetector
 9 |     {
10 |         /// <summary>
11 |         /// Platform name this detector handles
12 |         /// </summary>
13 |         string PlatformName { get; }
14 | 
15 |         /// <summary>
16 |         /// Whether this detector can run on the current platform
17 |         /// </summary>
18 |         bool CanDetect { get; }
19 | 
20 |         /// <summary>
21 |         /// Detect Python installation on this platform
22 |         /// </summary>
23 |         DependencyStatus DetectPython();
24 | 
25 |         /// <summary>
26 |         /// Detect UV package manager on this platform
27 |         /// </summary>
28 |         DependencyStatus DetectUV();
29 | 
30 |         /// <summary>
31 |         /// Detect MCP server installation on this platform
32 |         /// </summary>
33 |         DependencyStatus DetectMCPServer();
34 | 
35 |         /// <summary>
36 |         /// Get platform-specific installation recommendations
37 |         /// </summary>
38 |         string GetInstallationRecommendations();
39 | 
40 |         /// <summary>
41 |         /// Get platform-specific Python installation URL
42 |         /// </summary>
43 |         string GetPythonInstallUrl();
44 | 
45 |         /// <summary>
46 |         /// Get platform-specific UV installation URL
47 |         /// </summary>
48 |         string GetUVInstallUrl();
49 |     }
50 | }
51 | 
```

--------------------------------------------------------------------------------
/UnityMcpBridge/Editor/Dependencies/PlatformDetectors/IPlatformDetector.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using MCPForUnity.Editor.Dependencies.Models;
 2 | 
 3 | namespace MCPForUnity.Editor.Dependencies.PlatformDetectors
 4 | {
 5 |     /// <summary>
 6 |     /// Interface for platform-specific dependency detection
 7 |     /// </summary>
 8 |     public interface IPlatformDetector
 9 |     {
10 |         /// <summary>
11 |         /// Platform name this detector handles
12 |         /// </summary>
13 |         string PlatformName { get; }
14 | 
15 |         /// <summary>
16 |         /// Whether this detector can run on the current platform
17 |         /// </summary>
18 |         bool CanDetect { get; }
19 | 
20 |         /// <summary>
21 |         /// Detect Python installation on this platform
22 |         /// </summary>
23 |         DependencyStatus DetectPython();
24 | 
25 |         /// <summary>
26 |         /// Detect UV package manager on this platform
27 |         /// </summary>
28 |         DependencyStatus DetectUV();
29 | 
30 |         /// <summary>
31 |         /// Detect MCP server installation on this platform
32 |         /// </summary>
33 |         DependencyStatus DetectMCPServer();
34 | 
35 |         /// <summary>
36 |         /// Get platform-specific installation recommendations
37 |         /// </summary>
38 |         string GetInstallationRecommendations();
39 | 
40 |         /// <summary>
41 |         /// Get platform-specific Python installation URL
42 |         /// </summary>
43 |         string GetPythonInstallUrl();
44 | 
45 |         /// <summary>
46 |         /// Get platform-specific UV installation URL
47 |         /// </summary>
48 |         string GetUVInstallUrl();
49 |     }
50 | }
51 | 
```

--------------------------------------------------------------------------------
/UnityMcpBridge/UnityMcpServer~/src/config.py:
--------------------------------------------------------------------------------

```python
 1 | """
 2 | Configuration settings for the MCP for Unity Server.
 3 | This file contains all configurable parameters for the server.
 4 | """
 5 | 
 6 | from dataclasses import dataclass
 7 | 
 8 | 
 9 | @dataclass
10 | class ServerConfig:
11 |     """Main configuration class for the MCP server."""
12 | 
13 |     # Network settings
14 |     unity_host: str = "localhost"
15 |     unity_port: int = 6400
16 |     mcp_port: int = 6500
17 | 
18 |     # Connection settings
19 |     # short initial timeout; retries use shorter timeouts
20 |     connection_timeout: float = 1.0
21 |     buffer_size: int = 16 * 1024 * 1024  # 16MB buffer
22 |     # Framed receive behavior
23 |     # max seconds to wait while consuming heartbeats only
24 |     framed_receive_timeout: float = 2.0
25 |     # cap heartbeat frames consumed before giving up
26 |     max_heartbeat_frames: int = 16
27 | 
28 |     # Logging settings
29 |     log_level: str = "INFO"
30 |     log_format: str = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
31 | 
32 |     # Server settings
33 |     max_retries: int = 10
34 |     retry_delay: float = 0.25
35 |     # Backoff hint returned to clients when Unity is reloading (milliseconds)
36 |     reload_retry_ms: int = 250
37 |     # Number of polite retries when Unity reports reloading
38 |     # 40 × 250ms ≈ 10s default window
39 |     reload_max_retries: int = 40
40 | 
41 |     # Telemetry settings
42 |     telemetry_enabled: bool = True
43 |     # Align with telemetry.py default Cloud Run endpoint
44 |     telemetry_endpoint: str = "https://api-prod.coplay.dev/telemetry/events"
45 | 
46 | 
47 | # Create a global config instance
48 | config = ServerConfig()
49 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/UnityMcpServer~/src/resources/tests.py:
--------------------------------------------------------------------------------

```python
 1 | from typing import Annotated, Literal
 2 | from pydantic import BaseModel, Field
 3 | 
 4 | from models import MCPResponse
 5 | from registry import mcp_for_unity_resource
 6 | from unity_connection import async_send_command_with_retry
 7 | 
 8 | 
 9 | class TestItem(BaseModel):
10 |     name: Annotated[str, Field(description="The name of the test.")]
11 |     full_name: Annotated[str, Field(description="The full name of the test.")]
12 |     mode: Annotated[Literal["EditMode", "PlayMode"],
13 |                     Field(description="The mode the test is for.")]
14 | 
15 | 
16 | class GetTestsResponse(MCPResponse):
17 |     data: list[TestItem] = []
18 | 
19 | 
20 | @mcp_for_unity_resource(uri="mcpforunity://tests", name="get_tests", description="Provides a list of all tests.")
21 | async def get_tests() -> GetTestsResponse:
22 |     """Provides a list of all tests."""
23 |     response = await async_send_command_with_retry("get_tests", {})
24 |     return GetTestsResponse(**response) if isinstance(response, dict) else response
25 | 
26 | 
27 | @mcp_for_unity_resource(uri="mcpforunity://tests/{mode}", name="get_tests_for_mode", description="Provides a list of tests for a specific mode.")
28 | async def get_tests_for_mode(mode: Annotated[Literal["EditMode", "PlayMode"], Field(description="The mode to filter tests by.")]) -> GetTestsResponse:
29 |     """Provides a list of tests for a specific mode."""
30 |     response = await async_send_command_with_retry("get_tests_for_mode", {"mode": mode})
31 |     return GetTestsResponse(**response) if isinstance(response, dict) else response
32 | 
```

--------------------------------------------------------------------------------
/UnityMcpBridge/Editor/Tools/MenuItems/ManageMenuItem.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using System;
 2 | using Newtonsoft.Json.Linq;
 3 | using MCPForUnity.Editor.Helpers;
 4 | 
 5 | namespace MCPForUnity.Editor.Tools.MenuItems
 6 | {
 7 |     [McpForUnityTool("manage_menu_item")]
 8 |     public static class ManageMenuItem
 9 |     {
10 |         /// <summary>
11 |         /// Routes actions: execute, list, exists, refresh
12 |         /// </summary>
13 |         public static object HandleCommand(JObject @params)
14 |         {
15 |             string action = @params["action"]?.ToString()?.ToLowerInvariant();
16 |             if (string.IsNullOrEmpty(action))
17 |             {
18 |                 return Response.Error("Action parameter is required. Valid actions are: execute, list, exists, refresh.");
19 |             }
20 | 
21 |             try
22 |             {
23 |                 switch (action)
24 |                 {
25 |                     case "execute":
26 |                         return MenuItemExecutor.Execute(@params);
27 |                     case "list":
28 |                         return MenuItemsReader.List(@params);
29 |                     case "exists":
30 |                         return MenuItemsReader.Exists(@params);
31 |                     default:
32 |                         return Response.Error($"Unknown action: '{action}'. Valid actions are: execute, list, exists, refresh.");
33 |                 }
34 |             }
35 |             catch (Exception e)
36 |             {
37 |                 McpLog.Error($"[ManageMenuItem] Action '{action}' failed: {e}");
38 |                 return Response.Error($"Internal error: {e.Message}");
39 |             }
40 |         }
41 |     }
42 | }
43 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/UnityMcpServer~/src/registry/resource_registry.py:
--------------------------------------------------------------------------------

```python
 1 | """
 2 | Resource registry for auto-discovery of MCP resources.
 3 | """
 4 | from typing import Callable, Any
 5 | 
 6 | # Global registry to collect decorated resources
 7 | _resource_registry: list[dict[str, Any]] = []
 8 | 
 9 | 
10 | def mcp_for_unity_resource(
11 |     uri: str,
12 |     name: str | None = None,
13 |     description: str | None = None,
14 |     **kwargs
15 | ) -> Callable:
16 |     """
17 |     Decorator for registering MCP resources in the server's resources directory.
18 | 
19 |     Resources are registered in the global resource registry.
20 | 
21 |     Args:
22 |         name: Resource name (defaults to function name)
23 |         description: Resource description
24 |         **kwargs: Additional arguments passed to @mcp.resource()
25 | 
26 |     Example:
27 |         @mcp_for_unity_resource("mcpforunity://resource", description="Gets something interesting")
28 |         async def my_custom_resource(ctx: Context, ...):
29 |             pass
30 |     """
31 |     def decorator(func: Callable) -> Callable:
32 |         resource_name = name if name is not None else func.__name__
33 |         _resource_registry.append({
34 |             'func': func,
35 |             'uri': uri,
36 |             'name': resource_name,
37 |             'description': description,
38 |             'kwargs': kwargs
39 |         })
40 | 
41 |         return func
42 | 
43 |     return decorator
44 | 
45 | 
46 | def get_registered_resources() -> list[dict[str, Any]]:
47 |     """Get all registered resources"""
48 |     return _resource_registry.copy()
49 | 
50 | 
51 | def clear_resource_registry():
52 |     """Clear the resource registry (useful for testing)"""
53 |     _resource_registry.clear()
54 | 
```

--------------------------------------------------------------------------------
/.github/workflows/unity-tests.yml:
--------------------------------------------------------------------------------

```yaml
 1 | name: Unity Tests
 2 | 
 3 | on:
 4 |   push:
 5 |     branches: [main]
 6 |     paths:
 7 |       - TestProjects/UnityMCPTests/**
 8 |       - MCPForUnity/Editor/**
 9 |       - .github/workflows/unity-tests.yml
10 | 
11 | jobs:
12 |   testAllModes:
13 |     name: Test in ${{ matrix.testMode }}
14 |     runs-on: ubuntu-latest
15 |     strategy:
16 |       fail-fast: false
17 |       matrix:
18 |         projectPath:
19 |           - TestProjects/UnityMCPTests
20 |         testMode:
21 |           - editmode
22 |         unityVersion:
23 |           - 2021.3.45f2
24 |     steps:
25 |       # Checkout
26 |       - name: Checkout repository
27 |         uses: actions/checkout@v4
28 |         with:
29 |           lfs: true
30 | 
31 |       # Cache
32 |       - uses: actions/cache@v4
33 |         with:
34 |           path: ${{ matrix.projectPath }}/Library
35 |           key: Library-${{ matrix.projectPath }}-${{ matrix.unityVersion }}
36 |           restore-keys: |
37 |             Library-${{ matrix.projectPath }}-
38 |             Library-
39 | 
40 |       # Test
41 |       - name: Run tests
42 |         uses: game-ci/unity-test-runner@v4
43 |         id: tests
44 |         env:
45 |           UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
46 |           UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
47 |           UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
48 |         with:
49 |           projectPath: ${{ matrix.projectPath }}
50 |           unityVersion: ${{ matrix.unityVersion }}
51 |           testMode: ${{ matrix.testMode }}
52 | 
53 |       # Upload test results
54 |       - uses: actions/upload-artifact@v4
55 |         if: always()
56 |         with:
57 |           name: Test results for ${{ matrix.testMode }}
58 |           path: ${{ steps.tests.outputs.artifactsPath }}
59 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/Editor/Helpers/PackageInstaller.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using UnityEditor;
 2 | using UnityEngine;
 3 | 
 4 | namespace MCPForUnity.Editor.Helpers
 5 | {
 6 |     /// <summary>
 7 |     /// Handles automatic installation of the MCP server when the package is first installed.
 8 |     /// </summary>
 9 |     [InitializeOnLoad]
10 |     public static class PackageInstaller
11 |     {
12 |         private const string InstallationFlagKey = "MCPForUnity.ServerInstalled";
13 | 
14 |         static PackageInstaller()
15 |         {
16 |             // Check if this is the first time the package is loaded
17 |             if (!EditorPrefs.GetBool(InstallationFlagKey, false))
18 |             {
19 |                 // Schedule the installation for after Unity is fully loaded
20 |                 EditorApplication.delayCall += InstallServerOnFirstLoad;
21 |             }
22 |         }
23 | 
24 |         private static void InstallServerOnFirstLoad()
25 |         {
26 |             try
27 |             {
28 |                 ServerInstaller.EnsureServerInstalled();
29 | 
30 |                 // Mark as installed/checked
31 |                 EditorPrefs.SetBool(InstallationFlagKey, true);
32 | 
33 |                 // Only log success if server was actually embedded and copied
34 |                 if (ServerInstaller.HasEmbeddedServer())
35 |                 {
36 |                     McpLog.Info("MCP server installation completed successfully.");
37 |                 }
38 |             }
39 |             catch (System.Exception)
40 |             {
41 |                 EditorPrefs.SetBool(InstallationFlagKey, true); // Mark as handled
42 |                 McpLog.Info("Server installation pending. Open Window > MCP For Unity to download the server.");
43 |             }
44 |         }
45 |     }
46 | }
47 | 
```

--------------------------------------------------------------------------------
/TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ExecuteMenuItemTests.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using NUnit.Framework;
 2 | using Newtonsoft.Json.Linq;
 3 | using MCPForUnity.Editor.Tools;
 4 | 
 5 | namespace MCPForUnityTests.Editor.Tools
 6 | {
 7 |     public class ExecuteMenuItemTests
 8 |     {
 9 |         private static JObject ToJO(object o) => JObject.FromObject(o);
10 | 
11 |         [Test]
12 |         public void Execute_MissingParam_ReturnsError()
13 |         {
14 |             var res = ExecuteMenuItem.HandleCommand(new JObject());
15 |             var jo = ToJO(res);
16 |             Assert.IsFalse((bool)jo["success"], "Expected success false");
17 |             StringAssert.Contains("Required parameter", (string)jo["error"]);
18 |         }
19 | 
20 |         [Test]
21 |         public void Execute_Blacklisted_ReturnsError()
22 |         {
23 |             var res = ExecuteMenuItem.HandleCommand(new JObject { ["menuPath"] = "File/Quit" });
24 |             var jo = ToJO(res);
25 |             Assert.IsFalse((bool)jo["success"], "Expected success false for blacklisted menu");
26 |             StringAssert.Contains("blocked for safety", (string)jo["error"], "Expected blacklist message");
27 |         }
28 | 
29 |         [Test]
30 |         public void Execute_NonBlacklisted_ReturnsImmediateSuccess()
31 |         {
32 |             // We don't rely on the menu actually existing; execution is delayed and we only check the immediate response shape
33 |             var res = ExecuteMenuItem.HandleCommand(new JObject { ["menuPath"] = "File/Save Project" });
34 |             var jo = ToJO(res);
35 |             Assert.IsTrue((bool)jo["success"], "Expected immediate success response");
36 |             StringAssert.Contains("Attempted to execute menu item", (string)jo["message"], "Expected attempt message");
37 |         }
38 |     }
39 | }
40 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/UnityMcpServer~/src/tools/__init__.py:
--------------------------------------------------------------------------------

```python
 1 | """
 2 | MCP Tools package - Auto-discovers and registers all tools in this directory.
 3 | """
 4 | import logging
 5 | from pathlib import Path
 6 | 
 7 | from mcp.server.fastmcp import FastMCP
 8 | from telemetry_decorator import telemetry_tool
 9 | 
10 | from registry import get_registered_tools
11 | from module_discovery import discover_modules
12 | 
13 | logger = logging.getLogger("mcp-for-unity-server")
14 | 
15 | # Export decorator for easy imports within tools
16 | __all__ = ['register_all_tools']
17 | 
18 | 
19 | def register_all_tools(mcp: FastMCP):
20 |     """
21 |     Auto-discover and register all tools in the tools/ directory.
22 | 
23 |     Any .py file in this directory or subdirectories with @mcp_for_unity_tool decorated
24 |     functions will be automatically registered.
25 |     """
26 |     logger.info("Auto-discovering MCP for Unity Server tools...")
27 |     # Dynamic import of all modules in this directory
28 |     tools_dir = Path(__file__).parent
29 | 
30 |     # Discover and import all modules
31 |     list(discover_modules(tools_dir, __package__))
32 | 
33 |     tools = get_registered_tools()
34 | 
35 |     if not tools:
36 |         logger.warning("No MCP tools registered!")
37 |         return
38 | 
39 |     for tool_info in tools:
40 |         func = tool_info['func']
41 |         tool_name = tool_info['name']
42 |         description = tool_info['description']
43 |         kwargs = tool_info['kwargs']
44 | 
45 |         # Apply the @mcp.tool decorator and telemetry
46 |         wrapped = telemetry_tool(tool_name)(func)
47 |         wrapped = mcp.tool(
48 |             name=tool_name, description=description, **kwargs)(wrapped)
49 |         tool_info['func'] = wrapped
50 |         logger.debug(f"Registered tool: {tool_name} - {description}")
51 | 
52 |     logger.info(f"Registered {len(tools)} MCP tools")
53 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/UnityMcpServer~/src/tools/run_tests.py:
--------------------------------------------------------------------------------

```python
 1 | """Tool for executing Unity Test Runner suites."""
 2 | from typing import Annotated, Literal, Any
 3 | 
 4 | from mcp.server.fastmcp import Context
 5 | from pydantic import BaseModel, Field
 6 | 
 7 | from models import MCPResponse
 8 | from registry import mcp_for_unity_tool
 9 | from unity_connection import async_send_command_with_retry
10 | 
11 | 
12 | class RunTestsSummary(BaseModel):
13 |     total: int
14 |     passed: int
15 |     failed: int
16 |     skipped: int
17 |     durationSeconds: float
18 |     resultState: str
19 | 
20 | 
21 | class RunTestsTestResult(BaseModel):
22 |     name: str
23 |     fullName: str
24 |     state: str
25 |     durationSeconds: float
26 |     message: str | None = None
27 |     stackTrace: str | None = None
28 |     output: str | None = None
29 | 
30 | 
31 | class RunTestsResult(BaseModel):
32 |     mode: str
33 |     summary: RunTestsSummary
34 |     results: list[RunTestsTestResult]
35 | 
36 | 
37 | class RunTestsResponse(MCPResponse):
38 |     data: RunTestsResult | None = None
39 | 
40 | 
41 | @mcp_for_unity_tool(description="Runs Unity tests for the specified mode")
42 | async def run_tests(
43 |     ctx: Context,
44 |     mode: Annotated[Literal["edit", "play"], Field(
45 |         description="Unity test mode to run")] = "edit",
46 |     timeout_seconds: Annotated[int, Field(
47 |         description="Optional timeout in seconds for the Unity test run")] | None = None,
48 | ) -> RunTestsResponse:
49 |     await ctx.info(f"Processing run_tests: mode={mode}")
50 | 
51 |     params: dict[str, Any] = {"mode": mode}
52 |     if timeout_seconds is not None:
53 |         params["timeoutSeconds"] = timeout_seconds
54 | 
55 |     response = await async_send_command_with_retry("run_tests", params)
56 |     await ctx.info(f'Response {response}')
57 |     return RunTestsResponse(**response) if isinstance(response, dict) else response
58 | 
```

--------------------------------------------------------------------------------
/UnityMcpBridge/Editor/Helpers/PackageInstaller.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using UnityEditor;
 2 | using UnityEngine;
 3 | 
 4 | namespace MCPForUnity.Editor.Helpers
 5 | {
 6 |     /// <summary>
 7 |     /// Handles automatic installation of the Python server when the package is first installed.
 8 |     /// </summary>
 9 |     [InitializeOnLoad]
10 |     public static class PackageInstaller
11 |     {
12 |         private const string InstallationFlagKey = "MCPForUnity.ServerInstalled";
13 | 
14 |         static PackageInstaller()
15 |         {
16 |             // Check if this is the first time the package is loaded
17 |             if (!EditorPrefs.GetBool(InstallationFlagKey, false))
18 |             {
19 |                 // Schedule the installation for after Unity is fully loaded
20 |                 EditorApplication.delayCall += InstallServerOnFirstLoad;
21 |             }
22 |         }
23 | 
24 |         private static void InstallServerOnFirstLoad()
25 |         {
26 |             try
27 |             {
28 |                 Debug.Log("<b><color=#2EA3FF>MCP-FOR-UNITY</color></b>: Installing Python server...");
29 |                 ServerInstaller.EnsureServerInstalled();
30 | 
31 |                 // Mark as installed
32 |                 EditorPrefs.SetBool(InstallationFlagKey, true);
33 | 
34 |                 Debug.Log("<b><color=#2EA3FF>MCP-FOR-UNITY</color></b>: Python server installation completed successfully.");
35 |             }
36 |             catch (System.Exception ex)
37 |             {
38 |                 Debug.LogError($"<b><color=#2EA3FF>MCP-FOR-UNITY</color></b>: Failed to install Python server: {ex.Message}");
39 |                 Debug.LogWarning("<b><color=#2EA3FF>MCP-FOR-UNITY</color></b>: You may need to manually install the Python server. Check the MCP For Unity Window for instructions.");
40 |             }
41 |         }
42 |     }
43 | }
44 | 
```

--------------------------------------------------------------------------------
/UnityMcpBridge/UnityMcpServer~/src/tools/manage_menu_item.py:
--------------------------------------------------------------------------------

```python
 1 | """
 2 | Defines the manage_menu_item tool for executing and reading Unity Editor menu items.
 3 | """
 4 | import asyncio
 5 | from typing import Annotated, Any, Literal
 6 | 
 7 | from mcp.server.fastmcp import Context
 8 | from registry import mcp_for_unity_tool
 9 | from unity_connection import async_send_command_with_retry
10 | 
11 | 
12 | @mcp_for_unity_tool(
13 |     description="Manage Unity menu items (execute/list/exists). If you're not sure what menu item to use, use the 'list' action to find it before using 'execute'."
14 | )
15 | async def manage_menu_item(
16 |     ctx: Context,
17 |     action: Annotated[Literal["execute", "list", "exists"], "Read and execute Unity menu items."],
18 |     menu_path: Annotated[str,
19 |                          "Menu path for 'execute' or 'exists' (e.g., 'File/Save Project')"] | None = None,
20 |     search: Annotated[str,
21 |                       "Optional filter string for 'list' (e.g., 'Save')"] | None = None,
22 |     refresh: Annotated[bool,
23 |                        "Optional flag to force refresh of the menu cache when listing"] | None = None,
24 | ) -> dict[str, Any]:
25 |     ctx.info(f"Processing manage_menu_item: {action}")
26 |     # Prepare parameters for the C# handler
27 |     params_dict: dict[str, Any] = {
28 |         "action": action,
29 |         "menuPath": menu_path,
30 |         "search": search,
31 |         "refresh": refresh,
32 |     }
33 |     # Remove None values
34 |     params_dict = {k: v for k, v in params_dict.items() if v is not None}
35 | 
36 |     # Get the current asyncio event loop
37 |     loop = asyncio.get_running_loop()
38 | 
39 |     # Use centralized async retry helper
40 |     result = await async_send_command_with_retry("manage_menu_item", params_dict, loop=loop)
41 |     return result if isinstance(result, dict) else {"success": False, "message": str(result)}
42 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/Editor/Models/McpClient.cs:
--------------------------------------------------------------------------------

```csharp
 1 | namespace MCPForUnity.Editor.Models
 2 | {
 3 |     public class McpClient
 4 |     {
 5 |         public string name;
 6 |         public string windowsConfigPath;
 7 |         public string macConfigPath;
 8 |         public string linuxConfigPath;
 9 |         public McpTypes mcpType;
10 |         public string configStatus;
11 |         public McpStatus status = McpStatus.NotConfigured;
12 | 
13 |         // Helper method to convert the enum to a display string
14 |         public string GetStatusDisplayString()
15 |         {
16 |             return status switch
17 |             {
18 |                 McpStatus.NotConfigured => "Not Configured",
19 |                 McpStatus.Configured => "Configured",
20 |                 McpStatus.Running => "Running",
21 |                 McpStatus.Connected => "Connected",
22 |                 McpStatus.IncorrectPath => "Incorrect Path",
23 |                 McpStatus.CommunicationError => "Communication Error",
24 |                 McpStatus.NoResponse => "No Response",
25 |                 McpStatus.UnsupportedOS => "Unsupported OS",
26 |                 McpStatus.MissingConfig => "Missing MCPForUnity Config",
27 |                 McpStatus.Error => configStatus.StartsWith("Error:") ? configStatus : "Error",
28 |                 _ => "Unknown",
29 |             };
30 |         }
31 | 
32 |         // Helper method to set both status enum and string for backward compatibility
33 |         public void SetStatus(McpStatus newStatus, string errorDetails = null)
34 |         {
35 |             status = newStatus;
36 | 
37 |             if (newStatus == McpStatus.Error && !string.IsNullOrEmpty(errorDetails))
38 |             {
39 |                 configStatus = $"Error: {errorDetails}";
40 |             }
41 |             else
42 |             {
43 |                 configStatus = GetStatusDisplayString();
44 |             }
45 |         }
46 |     }
47 | }
48 | 
```

--------------------------------------------------------------------------------
/UnityMcpBridge/Editor/Models/McpClient.cs:
--------------------------------------------------------------------------------

```csharp
 1 | namespace MCPForUnity.Editor.Models
 2 | {
 3 |     public class McpClient
 4 |     {
 5 |         public string name;
 6 |         public string windowsConfigPath;
 7 |         public string macConfigPath;
 8 |         public string linuxConfigPath;
 9 |         public McpTypes mcpType;
10 |         public string configStatus;
11 |         public McpStatus status = McpStatus.NotConfigured;
12 | 
13 |         // Helper method to convert the enum to a display string
14 |         public string GetStatusDisplayString()
15 |         {
16 |             return status switch
17 |             {
18 |                 McpStatus.NotConfigured => "Not Configured",
19 |                 McpStatus.Configured => "Configured",
20 |                 McpStatus.Running => "Running",
21 |                 McpStatus.Connected => "Connected",
22 |                 McpStatus.IncorrectPath => "Incorrect Path",
23 |                 McpStatus.CommunicationError => "Communication Error",
24 |                 McpStatus.NoResponse => "No Response",
25 |                 McpStatus.UnsupportedOS => "Unsupported OS",
26 |                 McpStatus.MissingConfig => "Missing MCPForUnity Config",
27 |                 McpStatus.Error => configStatus.StartsWith("Error:") ? configStatus : "Error",
28 |                 _ => "Unknown",
29 |             };
30 |         }
31 | 
32 |         // Helper method to set both status enum and string for backward compatibility
33 |         public void SetStatus(McpStatus newStatus, string errorDetails = null)
34 |         {
35 |             status = newStatus;
36 | 
37 |             if (newStatus == McpStatus.Error && !string.IsNullOrEmpty(errorDetails))
38 |             {
39 |                 configStatus = $"Error: {errorDetails}";
40 |             }
41 |             else
42 |             {
43 |                 configStatus = GetStatusDisplayString();
44 |             }
45 |         }
46 |     }
47 | }
48 | 
```

--------------------------------------------------------------------------------
/TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/CommandRegistryTests.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using System;
 2 | using System.Collections.Generic;
 3 | using System.Linq;
 4 | using Newtonsoft.Json;
 5 | using Newtonsoft.Json.Linq;
 6 | using NUnit.Framework;
 7 | using MCPForUnity.Editor.Tools;
 8 | 
 9 | namespace MCPForUnityTests.Editor.Tools
10 | {
11 |     public class CommandRegistryTests
12 |     {
13 |         [OneTimeSetUp]
14 |         public void OneTimeSetUp()
15 |         {
16 |             // Ensure CommandRegistry is initialized before tests run
17 |             CommandRegistry.Initialize();
18 |         }
19 | 
20 |         [Test]
21 |         public void GetHandler_ThrowsException_ForUnknownCommand()
22 |         {
23 |             var unknown = "nonexistent_command_that_should_not_exist";
24 | 
25 |             Assert.Throws<InvalidOperationException>(() =>
26 |             {
27 |                 CommandRegistry.GetHandler(unknown);
28 |             }, "Should throw InvalidOperationException for unknown handler");
29 |         }
30 | 
31 |         [Test]
32 |         public void AutoDiscovery_RegistersAllBuiltInTools()
33 |         {
34 |             // Verify that all expected built-in tools are registered by trying to get their handlers
35 |             var expectedTools = new[]
36 |             {
37 |                 "manage_asset",
38 |                 "manage_editor",
39 |                 "manage_gameobject",
40 |                 "manage_scene",
41 |                 "manage_script",
42 |                 "manage_shader",
43 |                 "read_console",
44 |                 "execute_menu_item",
45 |                 "manage_prefabs"
46 |             };
47 | 
48 |             foreach (var toolName in expectedTools)
49 |             {
50 |                 Assert.DoesNotThrow(() =>
51 |                 {
52 |                     var handler = CommandRegistry.GetHandler(toolName);
53 |                     Assert.IsNotNull(handler, $"Handler for '{toolName}' should not be null");
54 |                 }, $"Expected tool '{toolName}' to be auto-registered");
55 |             }
56 |         }
57 |     }
58 | }
59 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/Editor/Services/PythonToolRegistryService.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using System;
 2 | using System.Collections.Generic;
 3 | using System.IO;
 4 | using System.Linq;
 5 | using System.Security.Cryptography;
 6 | using UnityEditor;
 7 | using UnityEngine;
 8 | using MCPForUnity.Editor.Data;
 9 | 
10 | namespace MCPForUnity.Editor.Services
11 | {
12 |     public class PythonToolRegistryService : IPythonToolRegistryService
13 |     {
14 |         public IEnumerable<PythonToolsAsset> GetAllRegistries()
15 |         {
16 |             // Find all PythonToolsAsset instances in the project
17 |             string[] guids = AssetDatabase.FindAssets("t:PythonToolsAsset");
18 |             foreach (string guid in guids)
19 |             {
20 |                 string path = AssetDatabase.GUIDToAssetPath(guid);
21 |                 var asset = AssetDatabase.LoadAssetAtPath<PythonToolsAsset>(path);
22 |                 if (asset != null)
23 |                     yield return asset;
24 |             }
25 |         }
26 | 
27 |         public bool NeedsSync(PythonToolsAsset registry, TextAsset file)
28 |         {
29 |             if (!registry.useContentHashing) return true;
30 | 
31 |             string currentHash = ComputeHash(file);
32 |             return registry.NeedsSync(file, currentHash);
33 |         }
34 | 
35 |         public void RecordSync(PythonToolsAsset registry, TextAsset file)
36 |         {
37 |             string hash = ComputeHash(file);
38 |             registry.RecordSync(file, hash);
39 |             EditorUtility.SetDirty(registry);
40 |         }
41 | 
42 |         public string ComputeHash(TextAsset file)
43 |         {
44 |             if (file == null || string.IsNullOrEmpty(file.text))
45 |                 return string.Empty;
46 | 
47 |             using (var sha256 = SHA256.Create())
48 |             {
49 |                 byte[] bytes = System.Text.Encoding.UTF8.GetBytes(file.text);
50 |                 byte[] hash = sha256.ComputeHash(bytes);
51 |                 return BitConverter.ToString(hash).Replace("-", "").ToLower();
52 |             }
53 |         }
54 |     }
55 | }
56 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/UnityMcpServer~/src/resources/__init__.py:
--------------------------------------------------------------------------------

```python
 1 | """
 2 | MCP Resources package - Auto-discovers and registers all resources in this directory.
 3 | """
 4 | import logging
 5 | from pathlib import Path
 6 | 
 7 | from mcp.server.fastmcp import FastMCP
 8 | from telemetry_decorator import telemetry_resource
 9 | 
10 | from registry import get_registered_resources
11 | from module_discovery import discover_modules
12 | 
13 | logger = logging.getLogger("mcp-for-unity-server")
14 | 
15 | # Export decorator for easy imports within tools
16 | __all__ = ['register_all_resources']
17 | 
18 | 
19 | def register_all_resources(mcp: FastMCP):
20 |     """
21 |     Auto-discover and register all resources in the resources/ directory.
22 | 
23 |     Any .py file in this directory or subdirectories with @mcp_for_unity_resource decorated
24 |     functions will be automatically registered.
25 |     """
26 |     logger.info("Auto-discovering MCP for Unity Server resources...")
27 |     # Dynamic import of all modules in this directory
28 |     resources_dir = Path(__file__).parent
29 | 
30 |     # Discover and import all modules
31 |     list(discover_modules(resources_dir, __package__))
32 | 
33 |     resources = get_registered_resources()
34 | 
35 |     if not resources:
36 |         logger.warning("No MCP resources registered!")
37 |         return
38 | 
39 |     for resource_info in resources:
40 |         func = resource_info['func']
41 |         uri = resource_info['uri']
42 |         resource_name = resource_info['name']
43 |         description = resource_info['description']
44 |         kwargs = resource_info['kwargs']
45 | 
46 |         # Apply the @mcp.resource decorator and telemetry
47 |         wrapped = telemetry_resource(resource_name)(func)
48 |         wrapped = mcp.resource(uri=uri, name=resource_name,
49 |                                description=description, **kwargs)(wrapped)
50 |         resource_info['func'] = wrapped
51 |         logger.debug(f"Registered resource: {resource_name} - {description}")
52 | 
53 |     logger.info(f"Registered {len(resources)} MCP resources")
54 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/Editor/Dependencies/Models/DependencyStatus.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using System;
 2 | 
 3 | namespace MCPForUnity.Editor.Dependencies.Models
 4 | {
 5 |     /// <summary>
 6 |     /// Represents the status of a dependency check
 7 |     /// </summary>
 8 |     [Serializable]
 9 |     public class DependencyStatus
10 |     {
11 |         /// <summary>
12 |         /// Name of the dependency being checked
13 |         /// </summary>
14 |         public string Name { get; set; }
15 | 
16 |         /// <summary>
17 |         /// Whether the dependency is available and functional
18 |         /// </summary>
19 |         public bool IsAvailable { get; set; }
20 | 
21 |         /// <summary>
22 |         /// Version information if available
23 |         /// </summary>
24 |         public string Version { get; set; }
25 | 
26 |         /// <summary>
27 |         /// Path to the dependency executable/installation
28 |         /// </summary>
29 |         public string Path { get; set; }
30 | 
31 |         /// <summary>
32 |         /// Additional details about the dependency status
33 |         /// </summary>
34 |         public string Details { get; set; }
35 | 
36 |         /// <summary>
37 |         /// Error message if dependency check failed
38 |         /// </summary>
39 |         public string ErrorMessage { get; set; }
40 | 
41 |         /// <summary>
42 |         /// Whether this dependency is required for basic functionality
43 |         /// </summary>
44 |         public bool IsRequired { get; set; }
45 | 
46 |         /// <summary>
47 |         /// Suggested installation method or URL
48 |         /// </summary>
49 |         public string InstallationHint { get; set; }
50 | 
51 |         public DependencyStatus(string name, bool isRequired = true)
52 |         {
53 |             Name = name;
54 |             IsRequired = isRequired;
55 |             IsAvailable = false;
56 |         }
57 | 
58 |         public override string ToString()
59 |         {
60 |             var status = IsAvailable ? "✓" : "✗";
61 |             var version = !string.IsNullOrEmpty(Version) ? $" ({Version})" : "";
62 |             return $"{status} {Name}{version}";
63 |         }
64 |     }
65 | }
66 | 
```

--------------------------------------------------------------------------------
/UnityMcpBridge/Editor/Dependencies/Models/DependencyStatus.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using System;
 2 | 
 3 | namespace MCPForUnity.Editor.Dependencies.Models
 4 | {
 5 |     /// <summary>
 6 |     /// Represents the status of a dependency check
 7 |     /// </summary>
 8 |     [Serializable]
 9 |     public class DependencyStatus
10 |     {
11 |         /// <summary>
12 |         /// Name of the dependency being checked
13 |         /// </summary>
14 |         public string Name { get; set; }
15 | 
16 |         /// <summary>
17 |         /// Whether the dependency is available and functional
18 |         /// </summary>
19 |         public bool IsAvailable { get; set; }
20 | 
21 |         /// <summary>
22 |         /// Version information if available
23 |         /// </summary>
24 |         public string Version { get; set; }
25 | 
26 |         /// <summary>
27 |         /// Path to the dependency executable/installation
28 |         /// </summary>
29 |         public string Path { get; set; }
30 | 
31 |         /// <summary>
32 |         /// Additional details about the dependency status
33 |         /// </summary>
34 |         public string Details { get; set; }
35 | 
36 |         /// <summary>
37 |         /// Error message if dependency check failed
38 |         /// </summary>
39 |         public string ErrorMessage { get; set; }
40 | 
41 |         /// <summary>
42 |         /// Whether this dependency is required for basic functionality
43 |         /// </summary>
44 |         public bool IsRequired { get; set; }
45 | 
46 |         /// <summary>
47 |         /// Suggested installation method or URL
48 |         /// </summary>
49 |         public string InstallationHint { get; set; }
50 | 
51 |         public DependencyStatus(string name, bool isRequired = true)
52 |         {
53 |             Name = name;
54 |             IsRequired = isRequired;
55 |             IsAvailable = false;
56 |         }
57 | 
58 |         public override string ToString()
59 |         {
60 |             var status = IsAvailable ? "✓" : "✗";
61 |             var version = !string.IsNullOrEmpty(Version) ? $" ({Version})" : "";
62 |             return $"{status} {Name}{version}";
63 |         }
64 |     }
65 | }
66 | 
```

--------------------------------------------------------------------------------
/tests/test_read_resource_minimal.py:
--------------------------------------------------------------------------------

```python
 1 | from tools.resource_tools import register_resource_tools  # type: ignore
 2 | import sys
 3 | import pathlib
 4 | import asyncio
 5 | import types
 6 | import pytest
 7 | 
 8 | ROOT = pathlib.Path(__file__).resolve().parents[1]
 9 | SRC = ROOT / "MCPForUnity" / "UnityMcpServer~" / "src"
10 | sys.path.insert(0, str(SRC))
11 | 
12 | # Stub mcp.server.fastmcp to satisfy imports without full package
13 | mcp_pkg = types.ModuleType("mcp")
14 | server_pkg = types.ModuleType("mcp.server")
15 | fastmcp_pkg = types.ModuleType("mcp.server.fastmcp")
16 | 
17 | 
18 | class _Dummy:
19 |     pass
20 | 
21 | 
22 | fastmcp_pkg.FastMCP = _Dummy
23 | fastmcp_pkg.Context = _Dummy
24 | server_pkg.fastmcp = fastmcp_pkg
25 | mcp_pkg.server = server_pkg
26 | sys.modules.setdefault("mcp", mcp_pkg)
27 | sys.modules.setdefault("mcp.server", server_pkg)
28 | sys.modules.setdefault("mcp.server.fastmcp", fastmcp_pkg)
29 | 
30 | 
31 | class DummyMCP:
32 |     def __init__(self):
33 |         self.tools = {}
34 | 
35 |     def tool(self, *args, **kwargs):
36 |         def deco(fn):
37 |             self.tools[fn.__name__] = fn
38 |             return fn
39 |         return deco
40 | 
41 | 
42 | @pytest.fixture()
43 | def resource_tools():
44 |     mcp = DummyMCP()
45 |     register_resource_tools(mcp)
46 |     return mcp.tools
47 | 
48 | 
49 | def test_read_resource_minimal_metadata_only(resource_tools, tmp_path):
50 |     proj = tmp_path
51 |     assets = proj / "Assets"
52 |     assets.mkdir()
53 |     f = assets / "A.txt"
54 |     content = "hello world"
55 |     f.write_text(content, encoding="utf-8")
56 | 
57 |     read_resource = resource_tools["read_resource"]
58 |     loop = asyncio.new_event_loop()
59 |     try:
60 |         resp = loop.run_until_complete(
61 |             read_resource(uri="unity://path/Assets/A.txt",
62 |                           ctx=None, project_root=str(proj))
63 |         )
64 |     finally:
65 |         loop.close()
66 | 
67 |     assert resp["success"] is True
68 |     data = resp["data"]
69 |     assert "text" not in data
70 |     meta = data["metadata"]
71 |     assert "sha256" in meta and len(meta["sha256"]) == 64
72 |     assert meta["lengthBytes"] == len(content.encode("utf-8"))
73 | 
```

--------------------------------------------------------------------------------
/UnityMcpBridge/UnityMcpServer~/src/tools/__init__.py:
--------------------------------------------------------------------------------

```python
 1 | """
 2 | MCP Tools package - Auto-discovers and registers all tools in this directory.
 3 | """
 4 | import importlib
 5 | import logging
 6 | from pathlib import Path
 7 | import pkgutil
 8 | 
 9 | from mcp.server.fastmcp import FastMCP
10 | from telemetry_decorator import telemetry_tool
11 | 
12 | from registry import get_registered_tools, mcp_for_unity_tool
13 | 
14 | logger = logging.getLogger("mcp-for-unity-server")
15 | 
16 | # Export decorator for easy imports within tools
17 | __all__ = ['register_all_tools', 'mcp_for_unity_tool']
18 | 
19 | 
20 | def register_all_tools(mcp: FastMCP):
21 |     """
22 |     Auto-discover and register all tools in the tools/ directory.
23 | 
24 |     Any .py file in this directory with @mcp_for_unity_tool decorated
25 |     functions will be automatically registered.
26 |     """
27 |     logger.info("Auto-discovering MCP for Unity Server tools...")
28 |     # Dynamic import of all modules in this directory
29 |     tools_dir = Path(__file__).parent
30 | 
31 |     for _, module_name, _ in pkgutil.iter_modules([str(tools_dir)]):
32 |         # Skip private modules and __init__
33 |         if module_name.startswith('_'):
34 |             continue
35 | 
36 |         try:
37 |             importlib.import_module(f'.{module_name}', __package__)
38 |         except Exception as e:
39 |             logger.warning(f"Failed to import tool module {module_name}: {e}")
40 | 
41 |     tools = get_registered_tools()
42 | 
43 |     if not tools:
44 |         logger.warning("No MCP tools registered!")
45 |         return
46 | 
47 |     for tool_info in tools:
48 |         func = tool_info['func']
49 |         tool_name = tool_info['name']
50 |         description = tool_info['description']
51 |         kwargs = tool_info['kwargs']
52 | 
53 |         # Apply the @mcp.tool decorator and telemetry
54 |         wrapped = telemetry_tool(tool_name)(func)
55 |         wrapped = mcp.tool(
56 |             name=tool_name, description=description, **kwargs)(wrapped)
57 |         tool_info['func'] = wrapped
58 |         logger.info(f"Registered tool: {tool_name} - {description}")
59 | 
60 |     logger.info(f"Registered {len(tools)} MCP tools")
61 | 
```

--------------------------------------------------------------------------------
/TestProjects/UnityMCPTests/Packages/manifest.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "dependencies": {
 3 |     "com.coplaydev.unity-mcp": "file:../../../MCPForUnity",
 4 |     "com.unity.collab-proxy": "2.5.2",
 5 |     "com.unity.feature.development": "1.0.1",
 6 |     "com.unity.ide.rider": "3.0.31",
 7 |     "com.unity.ide.visualstudio": "2.0.22",
 8 |     "com.unity.ide.vscode": "1.2.5",
 9 |     "com.unity.ide.windsurf": "https://github.com/Asuta/com.unity.ide.windsurf.git",
10 |     "com.unity.test-framework": "1.1.33",
11 |     "com.unity.textmeshpro": "3.0.6",
12 |     "com.unity.timeline": "1.6.5",
13 |     "com.unity.ugui": "1.0.0",
14 |     "com.unity.visualscripting": "1.9.4",
15 |     "com.unity.modules.ai": "1.0.0",
16 |     "com.unity.modules.androidjni": "1.0.0",
17 |     "com.unity.modules.animation": "1.0.0",
18 |     "com.unity.modules.assetbundle": "1.0.0",
19 |     "com.unity.modules.audio": "1.0.0",
20 |     "com.unity.modules.cloth": "1.0.0",
21 |     "com.unity.modules.director": "1.0.0",
22 |     "com.unity.modules.imageconversion": "1.0.0",
23 |     "com.unity.modules.imgui": "1.0.0",
24 |     "com.unity.modules.jsonserialize": "1.0.0",
25 |     "com.unity.modules.particlesystem": "1.0.0",
26 |     "com.unity.modules.physics": "1.0.0",
27 |     "com.unity.modules.physics2d": "1.0.0",
28 |     "com.unity.modules.screencapture": "1.0.0",
29 |     "com.unity.modules.terrain": "1.0.0",
30 |     "com.unity.modules.terrainphysics": "1.0.0",
31 |     "com.unity.modules.tilemap": "1.0.0",
32 |     "com.unity.modules.ui": "1.0.0",
33 |     "com.unity.modules.uielements": "1.0.0",
34 |     "com.unity.modules.umbra": "1.0.0",
35 |     "com.unity.modules.unityanalytics": "1.0.0",
36 |     "com.unity.modules.unitywebrequest": "1.0.0",
37 |     "com.unity.modules.unitywebrequestassetbundle": "1.0.0",
38 |     "com.unity.modules.unitywebrequestaudio": "1.0.0",
39 |     "com.unity.modules.unitywebrequesttexture": "1.0.0",
40 |     "com.unity.modules.unitywebrequestwww": "1.0.0",
41 |     "com.unity.modules.vehicles": "1.0.0",
42 |     "com.unity.modules.video": "1.0.0",
43 |     "com.unity.modules.vr": "1.0.0",
44 |     "com.unity.modules.wind": "1.0.0",
45 |     "com.unity.modules.xr": "1.0.0"
46 |   }
47 | }
48 | 
```

--------------------------------------------------------------------------------
/tests/test_validate_script_summary.py:
--------------------------------------------------------------------------------

```python
 1 | import sys
 2 | import pathlib
 3 | import importlib.util
 4 | import types
 5 | 
 6 | ROOT = pathlib.Path(__file__).resolve().parents[1]
 7 | SRC = ROOT / "MCPForUnity" / "UnityMcpServer~" / "src"
 8 | sys.path.insert(0, str(SRC))
 9 | 
10 | # stub mcp.server.fastmcp similar to test_get_sha
11 | mcp_pkg = types.ModuleType("mcp")
12 | server_pkg = types.ModuleType("mcp.server")
13 | fastmcp_pkg = types.ModuleType("mcp.server.fastmcp")
14 | 
15 | 
16 | class _Dummy:
17 |     pass
18 | 
19 | 
20 | fastmcp_pkg.FastMCP = _Dummy
21 | fastmcp_pkg.Context = _Dummy
22 | server_pkg.fastmcp = fastmcp_pkg
23 | mcp_pkg.server = server_pkg
24 | sys.modules.setdefault("mcp", mcp_pkg)
25 | sys.modules.setdefault("mcp.server", server_pkg)
26 | sys.modules.setdefault("mcp.server.fastmcp", fastmcp_pkg)
27 | 
28 | 
29 | def _load_module(path: pathlib.Path, name: str):
30 |     spec = importlib.util.spec_from_file_location(name, path)
31 |     mod = importlib.util.module_from_spec(spec)
32 |     spec.loader.exec_module(mod)
33 |     return mod
34 | 
35 | 
36 | manage_script = _load_module(
37 |     SRC / "tools" / "manage_script.py", "manage_script_mod")
38 | 
39 | 
40 | class DummyMCP:
41 |     def __init__(self):
42 |         self.tools = {}
43 | 
44 |     def tool(self, *args, **kwargs):
45 |         def deco(fn):
46 |             self.tools[fn.__name__] = fn
47 |             return fn
48 |         return deco
49 | 
50 | 
51 | def setup_tools():
52 |     mcp = DummyMCP()
53 |     manage_script.register_manage_script_tools(mcp)
54 |     return mcp.tools
55 | 
56 | 
57 | def test_validate_script_returns_counts(monkeypatch):
58 |     tools = setup_tools()
59 |     validate_script = tools["validate_script"]
60 | 
61 |     def fake_send(cmd, params):
62 |         return {
63 |             "success": True,
64 |             "data": {
65 |                 "diagnostics": [
66 |                     {"severity": "warning"},
67 |                     {"severity": "error"},
68 |                     {"severity": "fatal"},
69 |                 ]
70 |             },
71 |         }
72 | 
73 |     monkeypatch.setattr(manage_script, "send_command_with_retry", fake_send)
74 | 
75 |     resp = validate_script(None, uri="unity://path/Assets/Scripts/A.cs")
76 |     assert resp == {"success": True, "data": {"warnings": 1, "errors": 2}}
77 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/Editor/Services/IBridgeControlService.cs:
--------------------------------------------------------------------------------

```csharp
 1 | namespace MCPForUnity.Editor.Services
 2 | {
 3 |     /// <summary>
 4 |     /// Service for controlling the MCP for Unity Bridge connection
 5 |     /// </summary>
 6 |     public interface IBridgeControlService
 7 |     {
 8 |         /// <summary>
 9 |         /// Gets whether the bridge is currently running
10 |         /// </summary>
11 |         bool IsRunning { get; }
12 |         
13 |         /// <summary>
14 |         /// Gets the current port the bridge is listening on
15 |         /// </summary>
16 |         int CurrentPort { get; }
17 |         
18 |         /// <summary>
19 |         /// Gets whether the bridge is in auto-connect mode
20 |         /// </summary>
21 |         bool IsAutoConnectMode { get; }
22 |         
23 |         /// <summary>
24 |         /// Starts the MCP for Unity Bridge
25 |         /// </summary>
26 |         void Start();
27 |         
28 |         /// <summary>
29 |         /// Stops the MCP for Unity Bridge
30 |         /// </summary>
31 |         void Stop();
32 |         
33 |         /// <summary>
34 |         /// Verifies the bridge connection by sending a ping and waiting for a pong response
35 |         /// </summary>
36 |         /// <param name="port">The port to verify</param>
37 |         /// <returns>Verification result with detailed status</returns>
38 |         BridgeVerificationResult Verify(int port);
39 |     }
40 |     
41 |     /// <summary>
42 |     /// Result of a bridge verification attempt
43 |     /// </summary>
44 |     public class BridgeVerificationResult
45 |     {
46 |         /// <summary>
47 |         /// Whether the verification was successful
48 |         /// </summary>
49 |         public bool Success { get; set; }
50 |         
51 |         /// <summary>
52 |         /// Human-readable message about the verification result
53 |         /// </summary>
54 |         public string Message { get; set; }
55 |         
56 |         /// <summary>
57 |         /// Whether the handshake was valid (FRAMING=1 protocol)
58 |         /// </summary>
59 |         public bool HandshakeValid { get; set; }
60 |         
61 |         /// <summary>
62 |         /// Whether the ping/pong exchange succeeded
63 |         /// </summary>
64 |         public bool PingSucceeded { get; set; }
65 |     }
66 | }
67 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/UnityMcpServer~/src/module_discovery.py:
--------------------------------------------------------------------------------

```python
 1 | """
 2 | Shared module discovery utilities for auto-registering tools and resources.
 3 | """
 4 | import importlib
 5 | import logging
 6 | from pathlib import Path
 7 | import pkgutil
 8 | from typing import Generator
 9 | 
10 | logger = logging.getLogger("mcp-for-unity-server")
11 | 
12 | 
13 | def discover_modules(base_dir: Path, package_name: str) -> Generator[str, None, None]:
14 |     """
15 |     Discover and import all Python modules in a directory and its subdirectories.
16 | 
17 |     Args:
18 |         base_dir: The base directory to search for modules
19 |         package_name: The package name to use for relative imports (e.g., 'tools' or 'resources')
20 | 
21 |     Yields:
22 |         Full module names that were successfully imported
23 |     """
24 |     # Discover modules in the top level
25 |     for _, module_name, _ in pkgutil.iter_modules([str(base_dir)]):
26 |         # Skip private modules and __init__
27 |         if module_name.startswith('_'):
28 |             continue
29 | 
30 |         try:
31 |             full_module_name = f'.{module_name}'
32 |             importlib.import_module(full_module_name, package_name)
33 |             yield full_module_name
34 |         except Exception as e:
35 |             logger.warning(f"Failed to import module {module_name}: {e}")
36 | 
37 |     # Discover modules in subdirectories (one level deep)
38 |     for subdir in base_dir.iterdir():
39 |         if not subdir.is_dir() or subdir.name.startswith('_') or subdir.name.startswith('.'):
40 |             continue
41 | 
42 |         # Check if subdirectory contains Python modules
43 |         for _, module_name, _ in pkgutil.iter_modules([str(subdir)]):
44 |             # Skip private modules and __init__
45 |             if module_name.startswith('_'):
46 |                 continue
47 | 
48 |             try:
49 |                 # Import as package.subdirname.modulename
50 |                 full_module_name = f'.{subdir.name}.{module_name}'
51 |                 importlib.import_module(full_module_name, package_name)
52 |                 yield full_module_name
53 |             except Exception as e:
54 |                 logger.warning(
55 |                     f"Failed to import module {subdir.name}.{module_name}: {e}")
56 | 
```

--------------------------------------------------------------------------------
/docs/v5_MIGRATION.md:
--------------------------------------------------------------------------------

```markdown
 1 | # MCP for Unity v5 Migration Guide
 2 | 
 3 | This guide will help you migrate from the legacy UnityMcpBridge installation to the new MCPForUnity package structure in version 5.
 4 | 
 5 | ## Overview
 6 | 
 7 | Version 5 introduces a new package structure. The package is now installed from the `MCPForUnity` folder instead of the legacy `UnityMcpBridge` folder.
 8 | 
 9 | ## Migration Steps
10 | 
11 | ### Step 1: Uninstall the Current Package
12 | 
13 | 1. Open the Unity Package Manager (**Window > Package Manager**)
14 | 2. Select **Packages: In Project** from the dropdown
15 | 3. Find **MCP for Unity** in the list
16 | 4. Click the **Remove** button to uninstall the legacy package
17 | 
18 | ![Uninstalling the legacy package](screenshots/v5_01_uninstall.png)
19 | 
20 | ### Step 2: Install from the New Path
21 | 
22 | 1. In the Package Manager, click the **+** button in the top-left corner
23 | 2. Select **Add package from git URL...**
24 | 3. Enter the following URL: `https://github.com/CoplayDev/unity-mcp.git?path=/MCPForUnity`
25 | 4. Click **Add** to install the package
26 | 
27 | ![Installing from the new MCPForUnity path](screenshots/v5_02_install.png)
28 | 
29 | ### Step 3: Rebuild MCP Server
30 | 
31 | After installing the new package, you need to rebuild the MCP server:
32 | 
33 | 1. In Unity, go to **Window > MCP for Unity > Open MCP Window**
34 | ![Opening the MCP window](screenshots/v5_03_open_mcp_window.png)
35 | 2. Click the **Rebuild MCP Server** button
36 | ![Rebuilding the MCP server](screenshots/v5_04_rebuild_mcp_server.png)
37 | 3. You should see a success message confirming the rebuild
38 | ![Rebuild success](screenshots/v5_05_rebuild_success.png)
39 | 
40 | ## Verification
41 | 
42 | After completing these steps, verify the migration was successful:
43 | 
44 | - Check that the package appears in the Package Manager as **MCP for Unity**
45 | - Confirm the package location shows the new `MCPForUnity` path
46 | - Test basic MCP functionality to ensure everything works correctly
47 | 
48 | ## Troubleshooting
49 | 
50 | - Check the Unity Console for specific error messages
51 | - Ensure Python dependencies are properly installed
52 | - Try pressing the rebuild button again
53 | - Try restarting Unity and repeating the installation steps
54 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/Editor/Tools/ExecuteMenuItem.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using System;
 2 | using System.Collections.Generic;
 3 | using MCPForUnity.Editor.Helpers;
 4 | using Newtonsoft.Json.Linq;
 5 | using UnityEditor;
 6 | 
 7 | namespace MCPForUnity.Editor.Tools
 8 | {
 9 |     [McpForUnityTool("execute_menu_item")]
10 |     public static class ExecuteMenuItem
11 |     {
12 |         // Basic blacklist to prevent execution of disruptive menu items.
13 |         private static readonly HashSet<string> _menuPathBlacklist = new HashSet<string>(
14 |             StringComparer.OrdinalIgnoreCase)
15 |         {
16 |             "File/Quit",
17 |         };
18 | 
19 |         public static object HandleCommand(JObject @params)
20 |         {
21 |             McpLog.Info("[ExecuteMenuItem] Handling menu item command");
22 |             string menuPath = @params["menu_path"]?.ToString() ?? @params["menuPath"]?.ToString();
23 |             if (string.IsNullOrWhiteSpace(menuPath))
24 |             {
25 |                 return Response.Error("Required parameter 'menu_path' or 'menuPath' is missing or empty.");
26 |             }
27 | 
28 |             if (_menuPathBlacklist.Contains(menuPath))
29 |             {
30 |                 return Response.Error($"Execution of menu item '{menuPath}' is blocked for safety reasons.");
31 |             }
32 | 
33 |             try
34 |             {
35 |                 bool executed = EditorApplication.ExecuteMenuItem(menuPath);
36 |                 if (!executed)
37 |                 {
38 |                     McpLog.Error($"[MenuItemExecutor] Failed to execute menu item '{menuPath}'. It might be invalid, disabled, or context-dependent.");
39 |                     return Response.Error($"Failed to execute menu item '{menuPath}'. It might be invalid, disabled, or context-dependent.");
40 |                 }
41 |                 return Response.Success($"Attempted to execute menu item: '{menuPath}'. Check Unity logs for confirmation or errors.");
42 |             }
43 |             catch (Exception e)
44 |             {
45 |                 McpLog.Error($"[MenuItemExecutor] Failed to setup execution for '{menuPath}': {e}");
46 |                 return Response.Error($"Error setting up execution for menu item '{menuPath}': {e.Message}");
47 |             }
48 |         }
49 |     }
50 | }
51 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/Editor/Services/IPackageUpdateService.cs:
--------------------------------------------------------------------------------

```csharp
 1 | namespace MCPForUnity.Editor.Services
 2 | {
 3 |     /// <summary>
 4 |     /// Service for checking package updates and version information
 5 |     /// </summary>
 6 |     public interface IPackageUpdateService
 7 |     {
 8 |         /// <summary>
 9 |         /// Checks if a newer version of the package is available
10 |         /// </summary>
11 |         /// <param name="currentVersion">The current package version</param>
12 |         /// <returns>Update check result containing availability and latest version info</returns>
13 |         UpdateCheckResult CheckForUpdate(string currentVersion);
14 |         
15 |         /// <summary>
16 |         /// Compares two version strings to determine if the first is newer than the second
17 |         /// </summary>
18 |         /// <param name="version1">First version string</param>
19 |         /// <param name="version2">Second version string</param>
20 |         /// <returns>True if version1 is newer than version2</returns>
21 |         bool IsNewerVersion(string version1, string version2);
22 |         
23 |         /// <summary>
24 |         /// Determines if the package was installed via Git or Asset Store
25 |         /// </summary>
26 |         /// <returns>True if installed via Git, false if Asset Store or unknown</returns>
27 |         bool IsGitInstallation();
28 |         
29 |         /// <summary>
30 |         /// Clears the cached update check data, forcing a fresh check on next request
31 |         /// </summary>
32 |         void ClearCache();
33 |     }
34 |     
35 |     /// <summary>
36 |     /// Result of an update check operation
37 |     /// </summary>
38 |     public class UpdateCheckResult
39 |     {
40 |         /// <summary>
41 |         /// Whether an update is available
42 |         /// </summary>
43 |         public bool UpdateAvailable { get; set; }
44 |         
45 |         /// <summary>
46 |         /// The latest version available (null if check failed or no update)
47 |         /// </summary>
48 |         public string LatestVersion { get; set; }
49 |         
50 |         /// <summary>
51 |         /// Whether the check was successful (false if network error, etc.)
52 |         /// </summary>
53 |         public bool CheckSucceeded { get; set; }
54 |         
55 |         /// <summary>
56 |         /// Optional message about the check result
57 |         /// </summary>
58 |         public string Message { get; set; }
59 |     }
60 | }
61 | 
```

--------------------------------------------------------------------------------
/UnityMcpBridge/Editor/Tools/MenuItems/MenuItemExecutor.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using System;
 2 | using System.Collections.Generic;
 3 | using Newtonsoft.Json.Linq;
 4 | using UnityEditor;
 5 | using MCPForUnity.Editor.Helpers;
 6 | 
 7 | namespace MCPForUnity.Editor.Tools.MenuItems
 8 | {
 9 |     /// <summary>
10 |     /// Executes Unity Editor menu items by path with safety checks.
11 |     /// </summary>
12 |     public static class MenuItemExecutor
13 |     {
14 |         // Basic blacklist to prevent execution of disruptive menu items.
15 |         private static readonly HashSet<string> _menuPathBlacklist = new HashSet<string>(
16 |             StringComparer.OrdinalIgnoreCase)
17 |         {
18 |             "File/Quit",
19 |         };
20 | 
21 |         /// <summary>
22 |         /// Execute a specific menu item. Expects 'menu_path' or 'menuPath' in params.
23 |         /// </summary>
24 |         public static object Execute(JObject @params)
25 |         {
26 |             string menuPath = @params["menu_path"]?.ToString() ?? @params["menuPath"]?.ToString();
27 |             if (string.IsNullOrWhiteSpace(menuPath))
28 |             {
29 |                 return Response.Error("Required parameter 'menu_path' or 'menuPath' is missing or empty.");
30 |             }
31 | 
32 |             if (_menuPathBlacklist.Contains(menuPath))
33 |             {
34 |                 return Response.Error($"Execution of menu item '{menuPath}' is blocked for safety reasons.");
35 |             }
36 | 
37 |             try
38 |             {
39 |                 bool executed = EditorApplication.ExecuteMenuItem(menuPath);
40 |                 if (!executed)
41 |                 {
42 |                     McpLog.Error($"[MenuItemExecutor] Failed to execute menu item '{menuPath}'. It might be invalid, disabled, or context-dependent.");
43 |                     return Response.Error($"Failed to execute menu item '{menuPath}'. It might be invalid, disabled, or context-dependent.");
44 |                 }
45 |                 return Response.Success($"Attempted to execute menu item: '{menuPath}'. Check Unity logs for confirmation or errors.");
46 |             }
47 |             catch (Exception e)
48 |             {
49 |                 McpLog.Error($"[MenuItemExecutor] Failed to setup execution for '{menuPath}': {e}");
50 |                 return Response.Error($"Error setting up execution for menu item '{menuPath}': {e.Message}");
51 |             }
52 |         }
53 |     }
54 | }
55 | 
```

--------------------------------------------------------------------------------
/TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/ToolSyncServiceTests.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using System.IO;
 2 | using NUnit.Framework;
 3 | using UnityEngine;
 4 | using MCPForUnity.Editor.Data;
 5 | using MCPForUnity.Editor.Services;
 6 | 
 7 | namespace MCPForUnityTests.Editor.Services
 8 | {
 9 |     public class ToolSyncServiceTests
10 |     {
11 |         private ToolSyncService _service;
12 |         private string _testToolsDir;
13 | 
14 |         [SetUp]
15 |         public void SetUp()
16 |         {
17 |             _service = new ToolSyncService();
18 |             _testToolsDir = Path.Combine(Path.GetTempPath(), "UnityMCPTests", "tools");
19 | 
20 |             // Clean up any existing test directory
21 |             if (Directory.Exists(_testToolsDir))
22 |             {
23 |                 Directory.Delete(_testToolsDir, true);
24 |             }
25 |         }
26 | 
27 |         [TearDown]
28 |         public void TearDown()
29 |         {
30 |             // Clean up test directory
31 |             if (Directory.Exists(_testToolsDir))
32 |             {
33 |                 try
34 |                 {
35 |                     Directory.Delete(_testToolsDir, true);
36 |                 }
37 |                 catch
38 |                 {
39 |                     // Ignore cleanup errors
40 |                 }
41 |             }
42 |         }
43 | 
44 |         [Test]
45 |         public void SyncProjectTools_CreatesDestinationDirectory()
46 |         {
47 |             _service.SyncProjectTools(_testToolsDir);
48 | 
49 |             Assert.IsTrue(Directory.Exists(_testToolsDir), "Should create destination directory");
50 |         }
51 | 
52 |         [Test]
53 |         public void SyncProjectTools_ReturnsSuccess_WhenNoPythonToolsAssets()
54 |         {
55 |             var result = _service.SyncProjectTools(_testToolsDir);
56 | 
57 |             Assert.IsNotNull(result, "Should return a result");
58 |             Assert.AreEqual(0, result.CopiedCount, "Should not copy any files");
59 |             Assert.AreEqual(0, result.ErrorCount, "Should not have errors");
60 |         }
61 | 
62 |         [Test]
63 |         public void SyncProjectTools_ReportsCorrectCounts()
64 |         {
65 |             var result = _service.SyncProjectTools(_testToolsDir);
66 | 
67 |             Assert.IsTrue(result.CopiedCount >= 0, "Copied count should be non-negative");
68 |             Assert.IsTrue(result.SkippedCount >= 0, "Skipped count should be non-negative");
69 |             Assert.IsTrue(result.ErrorCount >= 0, "Error count should be non-negative");
70 |         }
71 |     }
72 | }
73 | 
```

--------------------------------------------------------------------------------
/tests/test_get_sha.py:
--------------------------------------------------------------------------------

```python
 1 | import sys
 2 | import pathlib
 3 | import importlib.util
 4 | import types
 5 | 
 6 | 
 7 | ROOT = pathlib.Path(__file__).resolve().parents[1]
 8 | SRC = ROOT / "MCPForUnity" / "UnityMcpServer~" / "src"
 9 | sys.path.insert(0, str(SRC))
10 | 
11 | # stub mcp.server.fastmcp to satisfy imports without full dependency
12 | mcp_pkg = types.ModuleType("mcp")
13 | server_pkg = types.ModuleType("mcp.server")
14 | fastmcp_pkg = types.ModuleType("mcp.server.fastmcp")
15 | 
16 | 
17 | class _Dummy:
18 |     pass
19 | 
20 | 
21 | fastmcp_pkg.FastMCP = _Dummy
22 | fastmcp_pkg.Context = _Dummy
23 | server_pkg.fastmcp = fastmcp_pkg
24 | mcp_pkg.server = server_pkg
25 | sys.modules.setdefault("mcp", mcp_pkg)
26 | sys.modules.setdefault("mcp.server", server_pkg)
27 | sys.modules.setdefault("mcp.server.fastmcp", fastmcp_pkg)
28 | 
29 | 
30 | def _load_module(path: pathlib.Path, name: str):
31 |     spec = importlib.util.spec_from_file_location(name, path)
32 |     mod = importlib.util.module_from_spec(spec)
33 |     spec.loader.exec_module(mod)
34 |     return mod
35 | 
36 | 
37 | manage_script = _load_module(
38 |     SRC / "tools" / "manage_script.py", "manage_script_mod")
39 | 
40 | 
41 | class DummyMCP:
42 |     def __init__(self):
43 |         self.tools = {}
44 | 
45 |     def tool(self, *args, **kwargs):
46 |         def deco(fn):
47 |             self.tools[fn.__name__] = fn
48 |             return fn
49 |         return deco
50 | 
51 | 
52 | def setup_tools():
53 |     mcp = DummyMCP()
54 |     manage_script.register_manage_script_tools(mcp)
55 |     return mcp.tools
56 | 
57 | 
58 | def test_get_sha_param_shape_and_routing(monkeypatch):
59 |     tools = setup_tools()
60 |     get_sha = tools["get_sha"]
61 | 
62 |     captured = {}
63 | 
64 |     def fake_send(cmd, params):
65 |         captured["cmd"] = cmd
66 |         captured["params"] = params
67 |         return {"success": True, "data": {"sha256": "abc", "lengthBytes": 1, "lastModifiedUtc": "2020-01-01T00:00:00Z", "uri": "unity://path/Assets/Scripts/A.cs", "path": "Assets/Scripts/A.cs"}}
68 | 
69 |     monkeypatch.setattr(manage_script, "send_command_with_retry", fake_send)
70 | 
71 |     resp = get_sha(None, uri="unity://path/Assets/Scripts/A.cs")
72 |     assert captured["cmd"] == "manage_script"
73 |     assert captured["params"]["action"] == "get_sha"
74 |     assert captured["params"]["name"] == "A"
75 |     assert captured["params"]["path"].endswith("Assets/Scripts")
76 |     assert resp["success"] is True
77 |     assert resp["data"] == {"sha256": "abc", "lengthBytes": 1}
78 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/Editor/Resources/MenuItems/GetMenuItems.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using System;
 2 | using System.Collections.Generic;
 3 | using System.Linq;
 4 | using MCPForUnity.Editor.Helpers;
 5 | using Newtonsoft.Json.Linq;
 6 | using UnityEditor;
 7 | 
 8 | namespace MCPForUnity.Editor.Resources.MenuItems
 9 | {
10 |     /// <summary>
11 |     /// Provides a simple read-only resource that returns Unity menu items.
12 |     /// </summary>
13 |     [McpForUnityResource("get_menu_items")]
14 |     public static class GetMenuItems
15 |     {
16 |         private static List<string> _cached;
17 | 
18 |         [InitializeOnLoadMethod]
19 |         private static void BuildCache() => Refresh();
20 | 
21 |         public static object HandleCommand(JObject @params)
22 |         {
23 |             bool forceRefresh = @params?["refresh"]?.ToObject<bool>() ?? false;
24 |             string search = @params?["search"]?.ToString();
25 | 
26 |             var items = GetMenuItemsInternal(forceRefresh);
27 | 
28 |             if (!string.IsNullOrEmpty(search))
29 |             {
30 |                 items = items
31 |                     .Where(item => item.IndexOf(search, StringComparison.OrdinalIgnoreCase) >= 0)
32 |                     .ToList();
33 |             }
34 | 
35 |             string message = $"Retrieved {items.Count} menu items";
36 |             return Response.Success(message, items);
37 |         }
38 | 
39 |         internal static List<string> GetMenuItemsInternal(bool forceRefresh)
40 |         {
41 |             if (forceRefresh || _cached == null)
42 |             {
43 |                 Refresh();
44 |             }
45 | 
46 |             return (_cached ?? new List<string>()).ToList();
47 |         }
48 | 
49 |         private static void Refresh()
50 |         {
51 |             try
52 |             {
53 |                 var methods = TypeCache.GetMethodsWithAttribute<MenuItem>();
54 |                 _cached = methods
55 |                     .SelectMany(m => m
56 |                         .GetCustomAttributes(typeof(MenuItem), false)
57 |                         .OfType<MenuItem>()
58 |                         .Select(attr => attr.menuItem))
59 |                     .Where(s => !string.IsNullOrEmpty(s))
60 |                     .Distinct(StringComparer.Ordinal)
61 |                     .OrderBy(s => s, StringComparer.Ordinal)
62 |                     .ToList();
63 |             }
64 |             catch (Exception ex)
65 |             {
66 |                 McpLog.Error($"[GetMenuItems] Failed to scan menu items: {ex}");
67 |                 _cached ??= new List<string>();
68 |             }
69 |         }
70 |     }
71 | }
72 | 
```

--------------------------------------------------------------------------------
/prune_tool_results.py:
--------------------------------------------------------------------------------

```python
 1 | #!/usr/bin/env python3
 2 | import sys, json
 3 | 
 4 | def summarize(txt):
 5 |     try:
 6 |         obj = json.loads(txt)
 7 |     except Exception:
 8 |         return f"tool_result: {len(txt)} bytes"
 9 |     data = obj.get("data", {}) or {}
10 |     msg  = obj.get("message") or obj.get("status") or ""
11 |     # Common tool shapes
12 |     if "sha256" in str(data):
13 |         ln  = data.get("lengthBytes") or data.get("length") or ""
14 |         return f"len={ln}".strip()
15 |     if "diagnostics" in data:
16 |         diags = data["diagnostics"] or []
17 |         w = sum(d.get("severity","" ).lower()=="warning" for d in diags)
18 |         e = sum(d.get("severity","" ).lower() in ("error","fatal") for d in diags)
19 |         ok = "OK" if not e else "FAIL"
20 |         return f"validate: {ok} (warnings={w}, errors={e})"
21 |     if "matches" in data:
22 |         m = data["matches"] or []
23 |         if m:
24 |             first = m[0]
25 |             return f"find_in_file: {len(m)} match(es) first@{first.get('line',0)}:{first.get('col',0)}"
26 |         return "find_in_file: 0 matches"
27 |     if "lines" in data:  # console
28 |         lines = data["lines"] or []
29 |         lvls = {"info":0,"warning":0,"error":0}
30 |         for L in lines:
31 |             lvls[L.get("level","" ).lower()] = lvls.get(L.get("level","" ).lower(),0)+1
32 |         return f"console: {len(lines)} lines (info={lvls.get('info',0)},warn={lvls.get('warning',0)},err={lvls.get('error',0)})"
33 |     # Fallback: short status
34 |     return (msg or "tool_result")[:80]
35 | 
36 | def prune_message(msg):
37 |     if "content" not in msg: return msg
38 |     newc=[]
39 |     for c in msg["content"]:
40 |         if c.get("type")=="tool_result" and c.get("content"):
41 |             out=[]
42 |             for chunk in c["content"]:
43 |                 if chunk.get("type")=="text":
44 |                     out.append({"type":"text","text":summarize(chunk.get("text","" ))})
45 |             newc.append({"type":"tool_result","tool_use_id":c.get("tool_use_id"),"content":out})
46 |         else:
47 |             newc.append(c)
48 |     msg["content"]=newc
49 |     return msg
50 | 
51 | def main():
52 |     convo=json.load(sys.stdin)
53 |     if isinstance(convo, dict) and "messages" in convo:
54 |         convo["messages"]=[prune_message(m) for m in convo["messages"]]
55 |     elif isinstance(convo, list):
56 |         convo=[prune_message(m) for m in convo]
57 |     json.dump(convo, sys.stdout, ensure_ascii=False)
58 | main()
59 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/Editor/Helpers/Response.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using System;
 2 | using System.Collections.Generic;
 3 | 
 4 | namespace MCPForUnity.Editor.Helpers
 5 | {
 6 |     /// <summary>
 7 |     /// Provides static methods for creating standardized success and error response objects.
 8 |     /// Ensures consistent JSON structure for communication back to the Python server.
 9 |     /// </summary>
10 |     public static class Response
11 |     {
12 |         /// <summary>
13 |         /// Creates a standardized success response object.
14 |         /// </summary>
15 |         /// <param name="message">A message describing the successful operation.</param>
16 |         /// <param name="data">Optional additional data to include in the response.</param>
17 |         /// <returns>An object representing the success response.</returns>
18 |         public static object Success(string message, object data = null)
19 |         {
20 |             if (data != null)
21 |             {
22 |                 return new
23 |                 {
24 |                     success = true,
25 |                     message = message,
26 |                     data = data,
27 |                 };
28 |             }
29 |             else
30 |             {
31 |                 return new { success = true, message = message };
32 |             }
33 |         }
34 | 
35 |         /// <summary>
36 |         /// Creates a standardized error response object.
37 |         /// </summary>
38 |         /// <param name="errorCodeOrMessage">A message describing the error.</param>
39 |         /// <param name="data">Optional additional data (e.g., error details) to include.</param>
40 |         /// <returns>An object representing the error response.</returns>
41 |         public static object Error(string errorCodeOrMessage, object data = null)
42 |         {
43 |             if (data != null)
44 |             {
45 |                 // Note: The key is "error" for error messages, not "message"
46 |                 return new
47 |                 {
48 |                     success = false,
49 |                     // Preserve original behavior while adding a machine-parsable code field.
50 |                     // If callers pass a code string, it will be echoed in both code and error.
51 |                     code = errorCodeOrMessage,
52 |                     error = errorCodeOrMessage,
53 |                     data = data,
54 |                 };
55 |             }
56 |             else
57 |             {
58 |                 return new { success = false, code = errorCodeOrMessage, error = errorCodeOrMessage };
59 |             }
60 |         }
61 |     }
62 | }
63 | 
```

--------------------------------------------------------------------------------
/UnityMcpBridge/Editor/Helpers/Response.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using System;
 2 | using System.Collections.Generic;
 3 | 
 4 | namespace MCPForUnity.Editor.Helpers
 5 | {
 6 |     /// <summary>
 7 |     /// Provides static methods for creating standardized success and error response objects.
 8 |     /// Ensures consistent JSON structure for communication back to the Python server.
 9 |     /// </summary>
10 |     public static class Response
11 |     {
12 |         /// <summary>
13 |         /// Creates a standardized success response object.
14 |         /// </summary>
15 |         /// <param name="message">A message describing the successful operation.</param>
16 |         /// <param name="data">Optional additional data to include in the response.</param>
17 |         /// <returns>An object representing the success response.</returns>
18 |         public static object Success(string message, object data = null)
19 |         {
20 |             if (data != null)
21 |             {
22 |                 return new
23 |                 {
24 |                     success = true,
25 |                     message = message,
26 |                     data = data,
27 |                 };
28 |             }
29 |             else
30 |             {
31 |                 return new { success = true, message = message };
32 |             }
33 |         }
34 | 
35 |         /// <summary>
36 |         /// Creates a standardized error response object.
37 |         /// </summary>
38 |         /// <param name="errorCodeOrMessage">A message describing the error.</param>
39 |         /// <param name="data">Optional additional data (e.g., error details) to include.</param>
40 |         /// <returns>An object representing the error response.</returns>
41 |         public static object Error(string errorCodeOrMessage, object data = null)
42 |         {
43 |             if (data != null)
44 |             {
45 |                 // Note: The key is "error" for error messages, not "message"
46 |                 return new
47 |                 {
48 |                     success = false,
49 |                     // Preserve original behavior while adding a machine-parsable code field.
50 |                     // If callers pass a code string, it will be echoed in both code and error.
51 |                     code = errorCodeOrMessage,
52 |                     error = errorCodeOrMessage,
53 |                     data = data,
54 |                 };
55 |             }
56 |             else
57 |             {
58 |                 return new { success = false, code = errorCodeOrMessage, error = errorCodeOrMessage };
59 |             }
60 |         }
61 |     }
62 | }
63 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/UnityMcpServer~/src/tools/manage_scene.py:
--------------------------------------------------------------------------------

```python
 1 | from typing import Annotated, Literal, Any
 2 | 
 3 | from mcp.server.fastmcp import Context
 4 | from registry import mcp_for_unity_tool
 5 | from unity_connection import send_command_with_retry
 6 | 
 7 | 
 8 | @mcp_for_unity_tool(description="Manage Unity scenes")
 9 | def manage_scene(
10 |     ctx: Context,
11 |     action: Annotated[Literal["create", "load", "save", "get_hierarchy", "get_active", "get_build_settings"], "Perform CRUD operations on Unity scenes."],
12 |     name: Annotated[str,
13 |                     "Scene name. Not required get_active/get_build_settings"] | None = None,
14 |     path: Annotated[str,
15 |                     "Asset path for scene operations (default: 'Assets/')"] | None = None,
16 |     build_index: Annotated[int,
17 |                            "Build index for load/build settings actions"] | None = None,
18 | ) -> dict[str, Any]:
19 |     ctx.info(f"Processing manage_scene: {action}")
20 |     try:
21 |         # Coerce numeric inputs defensively
22 |         def _coerce_int(value, default=None):
23 |             if value is None:
24 |                 return default
25 |             try:
26 |                 if isinstance(value, bool):
27 |                     return default
28 |                 if isinstance(value, int):
29 |                     return int(value)
30 |                 s = str(value).strip()
31 |                 if s.lower() in ("", "none", "null"):
32 |                     return default
33 |                 return int(float(s))
34 |             except Exception:
35 |                 return default
36 | 
37 |         coerced_build_index = _coerce_int(build_index, default=None)
38 | 
39 |         params = {"action": action}
40 |         if name:
41 |             params["name"] = name
42 |         if path:
43 |             params["path"] = path
44 |         if coerced_build_index is not None:
45 |             params["buildIndex"] = coerced_build_index
46 | 
47 |         # Use centralized retry helper
48 |         response = send_command_with_retry("manage_scene", params)
49 | 
50 |         # Preserve structured failure data; unwrap success into a friendlier shape
51 |         if isinstance(response, dict) and response.get("success"):
52 |             return {"success": True, "message": response.get("message", "Scene operation successful."), "data": response.get("data")}
53 |         return response if isinstance(response, dict) else {"success": False, "message": str(response)}
54 | 
55 |     except Exception as e:
56 |         return {"success": False, "message": f"Python error managing scene: {str(e)}"}
57 | 
```

--------------------------------------------------------------------------------
/UnityMcpBridge/UnityMcpServer~/src/tools/manage_scene.py:
--------------------------------------------------------------------------------

```python
 1 | from typing import Annotated, Literal, Any
 2 | 
 3 | from mcp.server.fastmcp import Context
 4 | from registry import mcp_for_unity_tool
 5 | from unity_connection import send_command_with_retry
 6 | 
 7 | 
 8 | @mcp_for_unity_tool(description="Manage Unity scenes")
 9 | def manage_scene(
10 |     ctx: Context,
11 |     action: Annotated[Literal["create", "load", "save", "get_hierarchy", "get_active", "get_build_settings"], "Perform CRUD operations on Unity scenes."],
12 |     name: Annotated[str,
13 |                     "Scene name. Not required get_active/get_build_settings"] | None = None,
14 |     path: Annotated[str,
15 |                     "Asset path for scene operations (default: 'Assets/')"] | None = None,
16 |     build_index: Annotated[int,
17 |                            "Build index for load/build settings actions"] | None = None,
18 | ) -> dict[str, Any]:
19 |     ctx.info(f"Processing manage_scene: {action}")
20 |     try:
21 |         # Coerce numeric inputs defensively
22 |         def _coerce_int(value, default=None):
23 |             if value is None:
24 |                 return default
25 |             try:
26 |                 if isinstance(value, bool):
27 |                     return default
28 |                 if isinstance(value, int):
29 |                     return int(value)
30 |                 s = str(value).strip()
31 |                 if s.lower() in ("", "none", "null"):
32 |                     return default
33 |                 return int(float(s))
34 |             except Exception:
35 |                 return default
36 | 
37 |         coerced_build_index = _coerce_int(build_index, default=None)
38 | 
39 |         params = {"action": action}
40 |         if name:
41 |             params["name"] = name
42 |         if path:
43 |             params["path"] = path
44 |         if coerced_build_index is not None:
45 |             params["buildIndex"] = coerced_build_index
46 | 
47 |         # Use centralized retry helper
48 |         response = send_command_with_retry("manage_scene", params)
49 | 
50 |         # Preserve structured failure data; unwrap success into a friendlier shape
51 |         if isinstance(response, dict) and response.get("success"):
52 |             return {"success": True, "message": response.get("message", "Scene operation successful."), "data": response.get("data")}
53 |         return response if isinstance(response, dict) else {"success": False, "message": str(response)}
54 | 
55 |     except Exception as e:
56 |         return {"success": False, "message": f"Python error managing scene: {str(e)}"}
57 | 
```

--------------------------------------------------------------------------------
/tests/test_telemetry_endpoint_validation.py:
--------------------------------------------------------------------------------

```python
 1 | import os
 2 | import importlib
 3 | 
 4 | 
 5 | def test_endpoint_rejects_non_http(tmp_path, monkeypatch):
 6 |     # Point data dir to temp to avoid touching real files
 7 |     monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path))
 8 |     monkeypatch.setenv("UNITY_MCP_TELEMETRY_ENDPOINT", "file:///etc/passwd")
 9 | 
10 |     telemetry = importlib.import_module(
11 |         "MCPForUnity.UnityMcpServer~.src.telemetry")
12 |     importlib.reload(telemetry)
13 | 
14 |     tc = telemetry.TelemetryCollector()
15 |     # Should have fallen back to default endpoint
16 |     assert tc.config.endpoint == tc.config.default_endpoint
17 | 
18 | 
19 | def test_config_preferred_then_env_override(tmp_path, monkeypatch):
20 |     # Simulate config telemetry endpoint
21 |     monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path))
22 |     monkeypatch.delenv("UNITY_MCP_TELEMETRY_ENDPOINT", raising=False)
23 | 
24 |     # Patch config.telemetry_endpoint via import mocking
25 |     import importlib
26 |     cfg_mod = importlib.import_module(
27 |         "MCPForUnity.UnityMcpServer~.src.config")
28 |     old_endpoint = cfg_mod.config.telemetry_endpoint
29 |     cfg_mod.config.telemetry_endpoint = "https://example.com/telemetry"
30 |     try:
31 |         telemetry = importlib.import_module(
32 |             "MCPForUnity.UnityMcpServer~.src.telemetry")
33 |         importlib.reload(telemetry)
34 |         tc = telemetry.TelemetryCollector()
35 |         assert tc.config.endpoint == "https://example.com/telemetry"
36 | 
37 |         # Env should override config
38 |         monkeypatch.setenv("UNITY_MCP_TELEMETRY_ENDPOINT",
39 |                            "https://override.example/ep")
40 |         importlib.reload(telemetry)
41 |         tc2 = telemetry.TelemetryCollector()
42 |         assert tc2.config.endpoint == "https://override.example/ep"
43 |     finally:
44 |         cfg_mod.config.telemetry_endpoint = old_endpoint
45 | 
46 | 
47 | def test_uuid_preserved_on_malformed_milestones(tmp_path, monkeypatch):
48 |     monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path))
49 | 
50 |     telemetry = importlib.import_module(
51 |         "MCPForUnity.UnityMcpServer~.src.telemetry")
52 |     importlib.reload(telemetry)
53 | 
54 |     tc1 = telemetry.TelemetryCollector()
55 |     first_uuid = tc1._customer_uuid
56 | 
57 |     # Write malformed milestones
58 |     tc1.config.milestones_file.write_text("{not-json}", encoding="utf-8")
59 | 
60 |     # Reload collector; UUID should remain same despite bad milestones
61 |     importlib.reload(telemetry)
62 |     tc2 = telemetry.TelemetryCollector()
63 |     assert tc2._customer_uuid == first_uuid
64 | 
```

--------------------------------------------------------------------------------
/MCPForUnity/Editor/Tools/RunTests.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using System;
 2 | using System.Threading.Tasks;
 3 | using MCPForUnity.Editor.Helpers;
 4 | using MCPForUnity.Editor.Resources.Tests;
 5 | using MCPForUnity.Editor.Services;
 6 | using Newtonsoft.Json.Linq;
 7 | 
 8 | namespace MCPForUnity.Editor.Tools
 9 | {
10 |     /// <summary>
11 |     /// Executes Unity tests for a specified mode and returns detailed results.
12 |     /// </summary>
13 |     [McpForUnityTool("run_tests")]
14 |     public static class RunTests
15 |     {
16 |         private const int DefaultTimeoutSeconds = 600; // 10 minutes
17 | 
18 |         public static async Task<object> HandleCommand(JObject @params)
19 |         {
20 |             string modeStr = @params?["mode"]?.ToString();
21 |             if (string.IsNullOrWhiteSpace(modeStr))
22 |             {
23 |                 modeStr = "edit";
24 |             }
25 | 
26 |             if (!ModeParser.TryParse(modeStr, out var parsedMode, out var parseError))
27 |             {
28 |                 return Response.Error(parseError);
29 |             }
30 | 
31 |             int timeoutSeconds = DefaultTimeoutSeconds;
32 |             try
33 |             {
34 |                 var timeoutToken = @params?["timeoutSeconds"];
35 |                 if (timeoutToken != null && int.TryParse(timeoutToken.ToString(), out var parsedTimeout) && parsedTimeout > 0)
36 |                 {
37 |                     timeoutSeconds = parsedTimeout;
38 |                 }
39 |             }
40 |             catch
41 |             {
42 |                 // Preserve default timeout if parsing fails
43 |             }
44 | 
45 |             var testService = MCPServiceLocator.Tests;
46 |             Task<TestRunResult> runTask;
47 |             try
48 |             {
49 |                 runTask = testService.RunTestsAsync(parsedMode.Value);
50 |             }
51 |             catch (Exception ex)
52 |             {
53 |                 return Response.Error($"Failed to start test run: {ex.Message}");
54 |             }
55 | 
56 |             var timeoutTask = Task.Delay(TimeSpan.FromSeconds(timeoutSeconds));
57 |             var completed = await Task.WhenAny(runTask, timeoutTask).ConfigureAwait(true);
58 | 
59 |             if (completed != runTask)
60 |             {
61 |                 return Response.Error($"Test run timed out after {timeoutSeconds} seconds");
62 |             }
63 | 
64 |             var result = await runTask.ConfigureAwait(true);
65 | 
66 |             string message =
67 |                 $"{parsedMode.Value} tests completed: {result.Passed}/{result.Total} passed, {result.Failed} failed, {result.Skipped} skipped";
68 | 
69 |             var data = result.ToSerializable(parsedMode.Value.ToString());
70 |             return Response.Success(message, data);
71 |         }
72 |     }
73 | }
74 | 
```
Page 1/18FirstPrevNextLast