testsAndMisc/linux_configuration/features/install_unreal_mcp_kvick.sh
Krzysztof kuhy Rudnicki 4f2b0ccdf7 Dissolve linux_configuration/scripts/ and single_use/
Two levels of pure taxonomy carrying no information, and a 2-directory
cap leaves no room for them. Categories now sit directly under
linux_configuration/: features, fixes, utils, misc, fresh-install,
installers, diagnostics, gaming, boot_recovery, periodic_background, lib,
meta, tests. A script's own lib/ and tests/ are exempt from the cap, so
those stay put.

Depth violations 248 -> 139; roughly 40 of the remainder are inside
digital_wellbeing and hosts, deferred until the guard-lib repoint.

The moves are the easy half. What breaks is every path that counted
levels, and three classes of it had to be found by resolving paths rather
than reading diffs:

  - 52 relative `source` lines for lib/common.sh and lib/android.sh. The
    correct prefix differs per file: 40 were ../../ and 12 were ../../../,
    so a blanket replace would have fixed one set and broken the other.
  - REPO_ROOT in 7 fixes/lib/tests harnesses and setup_syncyomi_guard.sh,
    each walking up four levels from a directory now two levels shallower.
    This caused a real regression -- the fixes suite went red with
    "log_warn: command not found" -- caught by running it and comparing
    against the pre-move baseline worktree.
  - conftest.py's sys.path entries, which took the pytest gate down with
    14 collection errors, and the `# shellcheck source=` directives, which
    took shellcheck down with SC1091.

16 symlinks in $HOME broke, including the entire i3blocks bar. All
repointed, and cpu_monitor/memory/disk/time/volume were run to confirm
they emit real output rather than merely resolving.

Verified: 1798 tests pass at 100% coverage, 8 of 9 lib/tests suites pass
(the ninth needs the coverage jail, identically at baseline), pre-commit
is clean, and the two untracked secret configs are still ignored after
their gitignore paths changed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PJrqKERAo5QUeEHWS5Asgp
2026-08-23 18:53:30 +02:00

244 lines
7.2 KiB
Bash
Executable File

#!/bin/bash
set -e
# Colors for output
GREEN='\033[0;32m'
BLUE='\033[0;34m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
echo -e "${BLUE}=== Unreal MCP Installer for Arch Linux ===${NC}"
# Check dependencies
echo -e "${BLUE}Checking dependencies...${NC}"
for cmd in git python pip; do
if ! command -v $cmd &>/dev/null; then
echo -e "${RED}Error: $cmd is not installed. Please install it (e.g., sudo pacman -S $cmd)${NC}"
exit 1
fi
done
# Get Unreal Project Path
PROJECT_PATH="$1"
if [ -z "$PROJECT_PATH" ]; then
echo -e "${YELLOW}Please enter the path to your Unreal Engine Project (the folder containing .uproject file):${NC}"
read -r -e -p "> " PROJECT_PATH
fi
# Validate path
# Expand tilde if present
PROJECT_PATH="${PROJECT_PATH/#\~/$HOME}"
PROJECT_PATH=$(realpath "$PROJECT_PATH" 2>/dev/null || echo "")
if [ -z "$PROJECT_PATH" ] || [ ! -d "$PROJECT_PATH" ]; then
echo -e "${RED}Error: Invalid directory: $PROJECT_PATH${NC}"
exit 1
fi
UPROJECT_FILES=("$PROJECT_PATH"/*.uproject)
if [ ! -e "${UPROJECT_FILES[0]}" ]; then
echo -e "${RED}Error: No .uproject file found in $PROJECT_PATH${NC}"
exit 1
fi
echo -e "${GREEN}Target Project: $PROJECT_PATH${NC}"
# Create Plugins directory if it doesn't exist
PLUGINS_DIR="$PROJECT_PATH/Plugins"
mkdir -p "$PLUGINS_DIR"
# Clone UnrealMCP
MCP_PLUGIN_DIR="$PLUGINS_DIR/UnrealMCP"
if [ -d "$MCP_PLUGIN_DIR" ]; then
echo -e "${BLUE}UnrealMCP already exists. Updating...${NC}"
cd "$MCP_PLUGIN_DIR"
git pull
else
echo -e "${BLUE}Cloning UnrealMCP...${NC}"
git clone https://github.com/kvick-games/UnrealMCP.git "$MCP_PLUGIN_DIR"
fi
# Setup Python Environment
echo -e "${BLUE}Setting up Python environment...${NC}"
MCP_DIR="$MCP_PLUGIN_DIR/MCP"
if [ ! -f "$MCP_DIR/unreal_mcp_bridge.py" ]; then
echo -e "${RED}Error: unreal_mcp_bridge.py not found in $MCP_DIR. Repository structure might have changed.${NC}"
exit 1
fi
VENV_DIR="$MCP_DIR/python_env"
if [ ! -d "$VENV_DIR" ]; then
echo "Creating virtual environment..."
python -m venv "$VENV_DIR"
fi
# Install requirements
echo "Installing dependencies in virtual environment..."
# shellcheck source=/dev/null
source "$VENV_DIR/bin/activate"
pip install --upgrade pip >/dev/null
pip install "mcp>=0.1.0" >/dev/null
# Patch unreal_mcp_bridge.py for newer mcp package compatibility
# The newer mcp package (1.x) renamed 'description' parameter to 'instructions'
BRIDGE_SCRIPT="$MCP_DIR/unreal_mcp_bridge.py"
if grep -q 'description="Unreal Engine integration' "$BRIDGE_SCRIPT" 2>/dev/null; then
echo "Patching unreal_mcp_bridge.py for mcp package compatibility..."
sed -i 's/description="Unreal Engine integration through the Model Context Protocol"/instructions="Unreal Engine integration through the Model Context Protocol"/' "$BRIDGE_SCRIPT"
fi
# Fix case-sensitive includes for Linux (Windows is case-insensitive, Linux is not)
echo "Fixing case-sensitive includes for Linux..."
find "$MCP_PLUGIN_DIR/Source/" \( -name "*.cpp" -o -name "*.h" \) -exec sed -i 's/HAL\/PlatformFilemanager\.h/HAL\/PlatformFileManager.h/g' {} + 2>/dev/null || true
# Create Linux Run Script
RUN_SCRIPT="$MCP_DIR/run_unreal_mcp.sh"
echo -e "${BLUE}Creating run script at $RUN_SCRIPT...${NC}"
cat <<EOF >"$RUN_SCRIPT"
#!/bin/bash
set -e
SCRIPT_DIR="\$(cd "\$(dirname "\${BASH_SOURCE[0]}")" && pwd)"
source "\$SCRIPT_DIR/python_env/bin/activate"
# Run the bridge script, passing any arguments
exec python "\$SCRIPT_DIR/unreal_mcp_bridge.py" "\$@"
EOF
chmod +x "$RUN_SCRIPT"
echo -e "${GREEN}Run script created successfully.${NC}"
# VS Code / MCP Configuration Helper
echo -e "${BLUE}=== Configuration Setup ===${NC}"
# Python script to update JSON configs
CONFIG_UPDATER_SCRIPT=$(mktemp)
cat <<EOF >"$CONFIG_UPDATER_SCRIPT"
import json
import os
import sys
config_path = sys.argv[1]
run_script = sys.argv[2]
config_type = sys.argv[3] # 'claude' or 'vscode_settings' or 'roo_code'
print(f"Updating {config_path}...")
data = {}
if os.path.exists(config_path):
try:
with open(config_path, 'r') as f:
data = json.load(f)
except json.JSONDecodeError:
print(f"Warning: Could not parse {config_path}. Starting with empty config.")
if config_type == 'claude' or config_type == 'roo_code':
# Standard MCP config format
if 'mcpServers' not in data:
data['mcpServers'] = {}
data['mcpServers']['unreal'] = {
'command': run_script,
'args': []
}
elif config_type == 'vscode_settings':
# VS Code settings.json format (example for some extensions)
# This varies by extension, but we'll add a generic mcp.servers key if it exists
# or just print instructions.
pass
# Ensure directory exists
os.makedirs(os.path.dirname(config_path), exist_ok=True)
with open(config_path, 'w') as f:
json.dump(data, f, indent=4)
print("Config updated successfully.")
EOF
# Detect and offer to update configurations
ROO_CODE_CONFIG="$HOME/.config/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/mcpSettings.json"
CLAUDE_CONFIG="$HOME/.config/Claude/claude_desktop_config.json"
# Function to ask and update
update_config() {
local path="$1"
local type="$2"
local name="$3"
if [ -f "$path" ] || [ -d "$(dirname "$path")" ]; then
echo -e "Found $name configuration at: $path"
read -p "Do you want to add UnrealMCP to this config? (y/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
python "$CONFIG_UPDATER_SCRIPT" "$path" "$RUN_SCRIPT" "$type"
fi
fi
}
update_config "$ROO_CODE_CONFIG" "roo_code" "Roo Code (VS Code Extension)"
update_config "$CLAUDE_CONFIG" "claude" "Claude Desktop"
rm "$CONFIG_UPDATER_SCRIPT"
# Create .vscode/mcp.json in the project (Workspace-specific config)
VSCODE_DIR="$PROJECT_PATH/.vscode"
mkdir -p "$VSCODE_DIR"
MCP_JSON="$VSCODE_DIR/mcp.json"
if [ ! -f "$MCP_JSON" ]; then
echo -e "${BLUE}Creating workspace MCP config at $MCP_JSON...${NC}"
cat <<EOF >"$MCP_JSON"
{
"mcpServers": {
"unreal": {
"command": "$RUN_SCRIPT",
"args": []
}
}
}
EOF
else
echo -e "${YELLOW}Workspace MCP config already exists at $MCP_JSON. Skipping overwrite.${NC}"
echo "Ensure it contains the following configuration:"
echo "\"unreal\": { \"command\": \"$RUN_SCRIPT\", \"args\": [] }"
fi
echo -e "${BLUE}=== Build Instructions ===${NC}"
echo "1. You need to regenerate project files."
if [ -f "$PROJECT_PATH/GenerateProjectFiles.sh" ]; then
echo " Found GenerateProjectFiles.sh in project root."
read -p " Do you want to run it now? (y/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
cd "$PROJECT_PATH"
./GenerateProjectFiles.sh
fi
else
echo " Run your engine's GenerateProjectFiles.sh or right-click .uproject -> Generate Project Files."
fi
echo "2. Build the project (e.g., run 'make' in the project root)."
echo "3. Open your project in Unreal Engine."
echo "4. Go to Edit > Plugins and enable 'UnrealMCP'."
echo "5. Also ensure 'Python Editor Script Plugin' is enabled."
echo "6. Restart the editor if prompted."
echo -e "${GREEN}Installation Complete!${NC}"
echo "If you need to manually configure an MCP client, use this command:"
echo -e "${YELLOW}$RUN_SCRIPT${NC}"
echo
echo "For VS Code (User Settings), add this to your settings.json:"
echo -e "${GREEN}"
cat <<EOF
"mcpServers": {
"unreal": {
"command": "$RUN_SCRIPT",
"args": []
}
}
EOF
echo -e "${NC}"