#!/usr/bin/env bash set -euo pipefail # Source common library for shared functions SCRIPT_DIR="$(dirname "$(readlink -f "$0")")" # shellcheck source=../../lib/common.sh source "$SCRIPT_DIR/../../lib/common.sh" SCRIPT_NAME="$(basename "$0")" RED="\033[31m" YELLOW="\033[33m" BLUE="\033[34m" RESET="\033[0m" info() { printf "%b[%s]%b %s\n" "$BLUE" "$SCRIPT_NAME" "$RESET" "$*" } warn() { printf "%b[%s]%b %s\n" "$YELLOW" "$SCRIPT_NAME" "$RESET" "$*" >&2 } error() { printf "%b[%s]%b %s\n" "$RED" "$SCRIPT_NAME" "$RESET" "$*" >&2 } ensure_pacman_packages() { install_missing_pacman_packages python git curl jq code } install_uv() { if command -v uv > /dev/null 2>&1; then info "uv is already installed." return fi info "Installing uv toolchain manager via official installer." curl -LsSf https://astral.sh/uv/install.sh | sh local local_bin="$HOME/.local/bin" if [[ :$PATH: != *":$local_bin:"* ]]; then warn "Adding $local_bin to PATH in ~/.profile and ~/.zshrc. Open a new shell to apply." printf "\nexport PATH=\"\$HOME/.local/bin:\$PATH\"\n" >> "$HOME/.profile" printf "\nexport PATH=\"\$HOME/.local/bin:\$PATH\"\n" >> "$HOME/.zshrc" fi } ensure_unity_hub() { if command -v unityhub > /dev/null 2>&1; then info "Unity Hub already installed." return fi if command -v yay > /dev/null 2>&1; then info "Installing Unity Hub from AUR using yay." yay -S --needed --noconfirm unityhub elif command -v flatpak > /dev/null 2>&1; then warn "Unity Hub not found. Attempting Flatpak installation." flatpak install -y com.unity.UnityHub || warn "Flatpak installation failed. Install Unity Hub manually via https://unity.com/download" else warn "Unity Hub not found and neither yay nor flatpak is available. Install Unity Hub manually from https://unity.com/download." fi } sync_unity_mcp_repo() { local data_home="${XDG_DATA_HOME:-$HOME/.local/share}" local unity_mcp_root="$data_home/UnityMCP" local repo_dir="$unity_mcp_root/unity-mcp-repo" local server_link="$unity_mcp_root/UnityMcpServer" local candidates=( "UnityMcpServer" "UnityMcpBridge/UnityMcpServer" "UnityMcpBridge/UnityMcpServer~" ) local server_subdir="" mkdir -p "$unity_mcp_root" if [[ -d "$repo_dir/.git" ]]; then info "Updating existing unity-mcp repository." git -C "$repo_dir" pull --ff-only else info "Cloning unity-mcp repository." rm -rf "$repo_dir" git clone --depth=1 https://github.com/CoplayDev/unity-mcp.git "$repo_dir" fi for candidate in "${candidates[@]}"; do if [[ -d "$repo_dir/$candidate/src" ]]; then server_subdir="$candidate" break fi done if [[ -z $server_subdir ]]; then error "UnityMcpServer src directory not found. Checked candidates: ${candidates[*]}" error "Repository layout may have changed. Inspect $repo_dir for the new server location." exit 1 fi ln -sfn "$repo_dir/$server_subdir" "$server_link" info "UnityMcpServer synchronized at $server_link (source: $server_subdir)" } configure_vscode_mcp() { local data_home="${XDG_DATA_HOME:-$HOME/.local/share}" local server_src="$data_home/UnityMCP/UnityMcpServer/src" local mcp_config_dir="$HOME/.config/Code/User" local mcp_config="$mcp_config_dir/mcp.json" local tmp if [[ ! -d $server_src ]]; then error "Server source directory $server_src is missing." exit 1 fi mkdir -p "$mcp_config_dir" if [[ ! -f $mcp_config ]]; then info "Creating new VS Code MCP configuration at $mcp_config" echo '{}' > "$mcp_config" else info "Updating existing VS Code MCP configuration at $mcp_config" fi tmp="$(mktemp)" if ! jq '.' "$mcp_config" > /dev/null 2>&1; then error "Existing $mcp_config is not valid JSON. Please fix it before running this script again." exit 1 fi jq \ --arg path "$server_src" \ '(.servers //= {}) | .servers.unityMCP = { command: "uv", args: ["--directory", $path, "run", "server.py"], type: "stdio" }' \ "$mcp_config" > "$tmp" mv "$tmp" "$mcp_config" info "VS Code MCP server configuration updated for UnityMCP." } verify_python_version() { require_command python "python" local version version="$( python - << 'PY' import sys print("%d.%d.%d" % sys.version_info[:3]) PY )" local major minor IFS='.' read -r major minor _ <<< "$version" if ((major < 3 || (major == 3 && minor < 12))); then error "Python 3.12+ is required. Detected version $version. Upgrade python before continuing." exit 1 fi info "Python version $version satisfies requirement (>= 3.12)." } print_next_steps() { cat << 'EOT' Next steps: 1. Launch Unity Hub and install a Unity Editor version 2021.3 LTS or newer. 2. Open your Unity project and add the MCP for Unity Bridge package via: Window > Package Manager > + > Add package from git URL... https://github.com/CoplayDev/unity-mcp.git?path=/UnityMcpBridge 3. In Unity, open Window > MCP for Unity and run Auto-Setup. Confirm the status shows Connected ✓. 4. Open Visual Studio Code. The MCP server entry "unityMCP" is now configured. Reload if prompted. 5. In VS Code, open the MCP client (e.g., Copilot / Claude Code) and issue a request such as "Create a tic-tac-toe game in 3D". The Unity MCP server should respond by operating inside your Unity project. Optional (Roslyn strict validation): - Install NuGetForUnity and add Microsoft.CodeAnalysis + SQLitePCLRaw packages, then define USE_ROSLYN, OR - Manually place Roslyn DLLs into Assets/Plugins and add USE_ROSLYN to Scripting Define Symbols. Troubleshooting tips: - If VS Code cannot launch the server, ensure `uv` is on PATH and that ~/.local/bin is exported in your shell. - To run the server manually: `uv --directory ~/.local/share/UnityMCP/UnityMcpServer/src run server.py` - Verify the directory path in ~/.config/Code/User/mcp.json matches your installation. EOT } main() { if [[ ! -f /etc/arch-release ]]; then error "This script is intended for Arch Linux." exit 1 fi info "Ensuring base dependencies are installed." require_command sudo "sudo" require_command pacman "pacman" ensure_pacman_packages verify_python_version install_uv ensure_unity_hub sync_unity_mcp_repo configure_vscode_mcp print_next_steps info "Setup complete. Follow the next steps above to finish configuration inside Unity." } main "$@"