#
tokens: 44526/50000 28/29 files (page 1/2)
lines: off (toggle) GitHub
raw markdown copy
This is page 1 of 2. Use http://codebase.md/zabaglione/mcp-server-unity?page={x} to view the full context.

# Directory Structure

```
├── .claude
│   └── settings.local.json
├── .gitignore
├── build-bundle.js
├── build-final-dxt.sh
├── BUILD.md
├── CHANGELOG.md
├── CLAUDE.md
├── CONTRIBUTING.md
├── create-bundled-dxt.sh
├── docs
│   ├── API.md
│   └── ARCHITECTURE.md
├── generate-embedded-scripts.cjs
├── LICENSE
├── manifest.json
├── package-lock.json
├── package.json
├── README-ja.md
├── README.md
├── src
│   ├── adapters
│   │   └── unity-http-adapter.ts
│   ├── embedded-scripts.ts
│   ├── services
│   │   └── unity-bridge-deploy-service.ts
│   ├── simple-index.ts
│   ├── tools
│   │   └── unity-mcp-tools.ts
│   └── unity-scripts
│       ├── UnityHttpServer.cs
│       └── UnityMCPServerWindow.cs
├── TECHNICAL.md
├── tests
│   ├── integration
│   │   └── simple-integration.test.ts
│   ├── unit
│   │   ├── adapters
│   │   │   └── unity-http-adapter.test.ts
│   │   ├── templates
│   │   │   └── shaders
│   │   │       └── shader-templates.test.ts
│   │   └── tools
│   │       └── unity-mcp-tools.test.ts
│   └── unity
│       └── UnityHttpServerTests.cs
├── tsconfig.json
├── tsconfig.test.json
├── unity-mcp-server.bundle.js
└── vitest.config.ts
```

# Files

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

```
# Node.js
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Build output
build/
dist/

# IDE
.idea/
.vscode/
*.swp
*.swo
.DS_Store

# Environment
.env
.env.local
.env.*.local

# Logs
logs/
*.log

# OS files
Thumbs.db
Desktop.ini

# fot AI
.github/

# Extension build artifacts
extension-package/
*.dxt

# Test output directories
test-*-output/

# Legacy scripts
install-*.js
reinstall-*.js

```

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

```markdown
# Unity MCP Server

Unity MCP Server lets Claude work with your Unity projects! Create scripts, manage shaders, organize folders - all through natural conversation with Claude.

[日本語版 README はこちら](README-ja.md) | [English](README.md)

## 🎮 What Can You Do?

Talk to Claude to:
- **Create Unity Scripts**: "Create a PlayerController script with jump functionality"
- **Manage Shaders**: "Create a toon shader for my character"
- **Organize Projects**: "Create a folder structure for my RPG game"
- **Get Project Info**: "What render pipeline is my project using?"

## 🚀 Quick Start (Recommended: Claude Desktop Extension)

### Option 1: Install via Claude Desktop Extension (Easiest)

1. **Download the Extension**
   - Go to [Latest Release](https://github.com/zabaglione/mcp-server-unity/releases/latest)
   - Download `unity-mcp-server.dxt` (42KB)

2. **Install in Claude Desktop**
   - Open Claude Desktop
   - Go to Extensions
   - Click "Install from file"
   - Select the downloaded `unity-mcp-server.dxt`

3. **Start Using!**
   - Open any Unity project (2019.4 or newer)
   - Install Newtonsoft JSON package in Unity:
     - Open Window → Package Manager
     - Click the "+" button and select "Add package by name..."
     - Enter: `com.unity.nuget.newtonsoft-json`
     - Click "Add"
   - Ask Claude: "Setup Unity MCP in my project at /path/to/project"
   - Claude will install everything automatically!

### Option 2: Manual Installation (For developers)

<details>
<summary>Click to see manual installation steps</summary>

1. Clone and build:
   ```bash
   git clone https://github.com/zabaglione/mcp-server-unity.git
   cd mcp-server-unity
   npm install
   npm run build
   ```

2. Configure Claude Desktop:
   ```json
   {
     "mcpServers": {
       "unity": {
         "command": "node",
         "args": ["/path/to/mcp-server-unity/build/simple-index.js"]
       }
     }
   }
   ```

</details>

## 📝 How to Use

Once installed, just talk to Claude naturally:

### Creating Scripts
```
You: "Create a PlayerHealth script that handles damage and healing"
Claude: I'll create a PlayerHealth script for you...
```

### Creating Shaders
```
You: "I need a water shader with wave animation"
Claude: I'll create a water shader with wave animation...
```

### Organizing Your Project
```
You: "Set up a folder structure for a platformer game"
Claude: I'll create an organized folder structure for your platformer...
```

### Checking Project Info
```
You: "What Unity version and render pipeline am I using?"
Claude: Let me check your project information...
```

## 🎯 Features

- ✅ **Smart Script Creation** - Claude understands Unity patterns and creates proper MonoBehaviours
- ✅ **Shader Support** - Works with Built-in, URP, and HDRP render pipelines
- ✅ **Project Organization** - Create, move, and rename folders to keep projects tidy
- ✅ **Auto Setup** - Claude automatically sets up the Unity integration when needed
- ✅ **Safe Operations** - All changes are made safely with proper Unity asset handling

## 🛠️ Troubleshooting

### "Unity server not responding"
1. Make sure Unity Editor is open
2. Check Window → Unity MCP Server in Unity
3. Click "Start Server" if it's not running

### "Can't find my project"
- Tell Claude the exact path: "My Unity project is at C:/Projects/MyGame"
- Make sure it's a valid Unity project with an Assets folder

### Need Help?
- Ask Claude: "Help me troubleshoot Unity MCP"
- Check [Issues](https://github.com/zabaglione/mcp-server-unity/issues)
- See [Technical Documentation](TECHNICAL.md) for advanced details

## 🎮 Unity Version Support

- **Unity 2019.4+** - Full support
- **Unity 6 (6000.0+)** - Recommended for best experience
- Works on Windows, macOS, and Linux

## 📈 Latest Updates (v3.1.1)

- ✅ Fixed render pipeline detection (now correctly identifies Built-in, URP, HDRP)
- ✅ Resolved AssetDatabase synchronization errors
- ✅ Improved file management and Unity integration stability

## 🤝 Contributing

Want to help improve Unity MCP Server? Check out our [Contributing Guide](CONTRIBUTING.md)!

## 📝 License

MIT License - see [LICENSE](LICENSE)

## 🙏 Acknowledgments

- [Anthropic](https://anthropic.com) for Claude and MCP
- [Unity Technologies](https://unity.com) for the amazing game engine
- All our contributors and users!

---

**Ready to supercharge your Unity development with Claude?** [Download the extension now!](https://github.com/zabaglione/mcp-server-unity/releases/latest)
```

--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------

```markdown
# Contributing to Unity MCP Server

Thank you for your interest in contributing to Unity MCP Server! This document provides guidelines and instructions for contributing.

## Development Setup

### Prerequisites

1. Node.js 18.x or higher
2. npm or yarn
3. Git
4. A Unity installation (for testing)
5. Claude Desktop (for integration testing)

### Setting Up the Development Environment

1. Fork and clone the repository:
```bash
git clone https://github.com/zabaglione/mcp-server-unity.git
cd mcp-server-unity
```

2. Install dependencies:
```bash
npm install
```

3. Build the project:
```bash
npm run build
```

4. For development with auto-rebuild:
```bash
npm run dev
```

## Code Style Guidelines

### TypeScript Conventions

- Use strict TypeScript settings (already configured in tsconfig.json)
- Always specify types explicitly for function parameters and return values
- Use interfaces for complex object types
- Prefer `const` over `let` when variables won't be reassigned

### Code Organization

- Keep related functionality together
- Each tool should have clear error handling
- Use descriptive variable and function names
- Add JSDoc comments for complex functions

### Error Handling

- Always validate inputs before processing
- Provide clear, actionable error messages
- Use MCP's error types appropriately
- Never expose sensitive file paths in error messages

## Testing

### Manual Testing

1. Configure Claude Desktop to use your development build
2. Test each tool with various inputs:
   - Valid inputs
   - Invalid inputs
   - Edge cases
   - Missing optional parameters

### Testing Checklist

- [ ] Set Unity project with valid path
- [ ] Set Unity project with invalid path
- [ ] Create scripts with various content
- [ ] Read existing and non-existing scripts
- [ ] List scripts in projects with 0, 1, and many scripts
- [ ] Create scenes and materials
- [ ] Test all asset type filters
- [ ] Test build command (if Unity is available)

## Pull Request Process

### Before Submitting

1. Ensure your code builds without errors:
```bash
npm run build
```

2. Test your changes thoroughly
3. Update documentation if needed
4. Add yourself to the contributors list (if first contribution)

### PR Guidelines

1. **Title**: Use a clear, descriptive title
   - Good: "Add support for prefab creation"
   - Bad: "Update code"

2. **Description**: Include:
   - What changes were made
   - Why the changes were necessary
   - Any breaking changes
   - Testing performed

3. **Scope**: Keep PRs focused
   - One feature or fix per PR
   - Separate refactoring from feature additions

### Review Process

- PRs require at least one review
- Address all feedback constructively
- Update based on feedback and re-request review
- Squash commits if requested

## Adding New Tools

When adding a new Unity-related tool:

1. Add tool definition in `setupHandlers()`
2. Implement the tool handler method
3. Add appropriate error handling
4. Update README.md with usage examples
5. Test thoroughly with Claude Desktop

### Tool Implementation Template

```typescript
private async toolName(param1: string, param2?: string): Promise<any> {
  // Validate Unity project is set
  if (!this.unityProject) {
    throw new Error('Unity project not set. Use set_unity_project first.');
  }

  // Validate inputs
  if (!param1) {
    throw new Error('param1 is required');
  }

  try {
    // Tool implementation
    
    return {
      content: [
        {
          type: 'text',
          text: `Success message with details`,
        },
      ],
    };
  } catch (error) {
    throw new Error(`Tool operation failed: ${error}`);
  }
}
```

## Reporting Issues

### Bug Reports

Include:
- Unity version
- Node.js version
- OS and version
- Steps to reproduce
- Expected vs actual behavior
- Error messages/logs

### Feature Requests

Include:
- Use case description
- Proposed implementation (if any)
- Unity version compatibility requirements
- Potential impact on existing features

## Community

- Be respectful and constructive
- Help others when possible
- Share your Unity MCP use cases
- Suggest improvements

## Release Process

1. Update version in package.json
2. Update CHANGELOG.md
3. Create release PR
4. After merge, tag release
5. Publish release notes

Thank you for contributing to Unity MCP Server!
```

--------------------------------------------------------------------------------
/CLAUDE.md:
--------------------------------------------------------------------------------

```markdown
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

# Unity MCP Server - Project Knowledge Base

## Project Overview
Unity MCP Server is a Model Context Protocol (MCP) server that bridges AI assistants (like Claude) with Unity game development. It supports both legacy file-based operations (v2.x) and direct Unity Editor integration (v3.0.0 for Unity 6000+).

## Commands

### Build and Development
- `npm run build` - Compile TypeScript to JavaScript
- `npm run dev` - Watch mode for development
- `npm start` - Start MCP server (stdio mode for Claude Desktop)
- `npm run start:http` - Start HTTP API server (default port 3000)
- `npm run clean` - Clean build artifacts

### Optimized Mode
- `npm run start:optimized` - MCP server with streaming for large files
- `npm run start:http:optimized` - HTTP server with streaming

### Testing
- `npm test` - Run all tests
- `npm run test:unit` - Unit tests only
- `npm run test:integration` - Integration tests only  
- `npm run test:e2e` - End-to-end tests
- `npm run test:coverage` - Generate coverage report
- `npm run test:manual` - Interactive manual test runner
- `npm run test:performance` - Run performance benchmarks
- `npm run test:watch` - Watch mode for tests
- `npm run test:legacy` - Old integration test
- `npm run test:direct` - Direct TypeScript test runner

### No Linting/Formatting
- No ESLint or Prettier configured - maintain existing code style

## Architecture Evolution

### v3.0.0 - Unity Bridge Architecture (Unity 6000+ only)
- **Direct Unity API Integration**: No file system manipulation
- **Unity Bridge Client**: Named Pipes/Domain Sockets communication
- **Simplified API**: Focus on script and folder operations only
- **Real-time Events**: Connection, compilation, project changes
- **Entry Points**: `src/index.ts`, `src/unity6-mcp-server.ts`
- **Unity Script**: `src/unity-scripts/MCPBridge.cs` (place in Unity project)

### v2.x - Service-Based Architecture (Legacy, Unity 2019.4+)
All services extend from `BaseService` and follow a consistent pattern:
- **ProjectService**: Unity project validation and setup
- **ScriptService**: C# script creation and management  
- **AssetService**: Unity asset reading and listing
- **BuildService**: Multi-platform build automation
- **ShaderService**: Shader creation for Built-in/URP/HDRP
- **MaterialService**: Material creation and property management
- **EditorScriptService**: Editor extensions (windows, inspectors)
- **CodeAnalysisService**: Code diff, namespace management, duplicate detection
- **CompilationService**: Real-time compilation error monitoring
- **UnityRefreshService**: Asset database refresh with batch operations
- **UnityDiagnosticsService**: Editor log analysis and error tracking
- **UIToolkitService**: UXML/USS file creation and management

### Key Design Patterns

1. **Service Container Pattern** (v2.x)
   - All services registered in `ServicesContainer`
   - Dependency injection for service dependencies
   - Factory pattern for service instantiation

2. **Template-based Code Generation**
   - Templates in `src/templates/` for all generated code
   - Supports shader variants (Built-in, URP, HDRP)
   - Namespace auto-detection based on file location

3. **Meta File Management** (v2.x critical)
   - Automatic .meta file generation with consistent GUIDs
   - GUID preservation for shader/material updates
   - Prevents Unity reference breakage

4. **Render Pipeline Detection**
   - Auto-detects Built-in, URP, or HDRP from project packages
   - Adjusts shader/material creation accordingly

## Development Guidelines

### Error Handling
- All services use custom error types from `src/errors/`
- Detailed error messages with actionable suggestions
- Validation before operations (project path, Unity version)

### File Operations
- Always use absolute paths
- Create parent directories automatically
- Generate .meta files for all Unity assets (v2.x)
- Respect Unity's folder structure conventions

### Code Style
- TypeScript with strict type checking
- ES modules with .js extensions in imports
- Async/await for all I/O operations
- Comprehensive logging with context

### Testing
- Jest framework with TypeScript support
- Virtual Unity project utility for test environments (`tests/utils/virtualUnityProject.ts`)
- Snapshot testing for generated content validation
- Performance benchmarks exported to JSON
- Coverage thresholds: 80% lines, 70% branches/functions
- Test structure mirrors source structure (e.g., `src/services/foo.ts` → `tests/unit/services/foo.test.ts`)

## Unity Integration Points

### Project Structure Expected
```
UnityProject/
├── Assets/
│   ├── Scripts/
│   ├── Materials/
│   ├── Shaders/
│   └── Editor/
│       └── MCP/
│           └── MCPBridge.cs  # v3.0 Unity Bridge script
├── Packages/
│   └── manifest.json (render pipeline detection)
├── Library/
│   ├── Bee/fullprofile.json (compilation errors)
│   └── Logs/ (Unity console logs)
└── ProjectSettings/
```

### Compilation Monitoring
- Watches `Library/Bee/fullprofile.json` for errors
- Parses Unity console logs from `Library/Logs/`
- Real-time feedback on script compilation

### Asset Database Refresh (v2.x)
- Triggers Unity refresh via EditorApplication
- Supports batch operations to minimize refreshes
- Handles both immediate and deferred refresh modes

## Common Workflows

### v3.0 Unity Bridge Workflow
1. Ensure Unity 6000+ with MCPBridge.cs installed
2. Unity automatically starts bridge on project open
3. Use script/folder operations via MCP tools
4. Bridge handles all Unity API calls directly

### Material Shader Updates (v2.x)
1. Read current material properties
2. Find target shader GUID
3. Update material preserving properties
4. Maintain material GUID for references

### Script Creation
1. Detect namespace from file path
2. Apply project conventions
3. Generate with proper using statements
4. Create accompanying .meta file (v2.x only)

### Build Automation (v2.x)
1. Validate project and target platform
2. Configure build settings
3. Execute build with error handling
4. Report build results and logs

## Performance Considerations
- Batch operations for multiple files
- Minimize Unity refreshes
- Cache render pipeline detection
- Efficient file system operations

## Security Notes
- Path validation to prevent directory traversal
- No execution of arbitrary Unity code
- Safe template rendering
- Input sanitization for all operations

## Critical Implementation Details

### Unity Asset Refresh (v2.x)
- **CRITICAL**: Always trigger Unity refresh after file operations using `UnityRefreshService`
- Unity won't recognize new/modified assets without refresh
- Batch operations supported to minimize refresh calls
- Both immediate and deferred refresh modes available

### Meta File Generation (v2.x)
- Every Unity asset MUST have a corresponding .meta file
- GUIDs must be consistent to prevent reference breakage
- When updating shaders/materials, preserve existing GUIDs
- Meta files generated automatically by all asset creation services

### Service Dependencies (v2.x)
- Services can depend on each other (inject via constructor)
- Example: `MaterialService` depends on `ShaderService`
- All services registered in `ServicesContainer`
- Use `ServiceFactory` to create properly wired instances

### Template System
- All code generation uses templates from `src/templates/`
- Templates support placeholders: `{{NAMESPACE}}`, `{{CLASS_NAME}}`, etc.
- Shader templates vary by render pipeline (builtin/urp/hdrp)
- UI Toolkit templates for windows, documents, and components

### Large File Support
- Automatic streaming for files larger than 10MB
- Maximum file size limit: 1GB
- Services automatically use streaming for read/write operations
- HTTP API supports up to 1GB request bodies
- Implemented in: ScriptService, ShaderService, MaterialService, UIToolkitService

### Unity Bridge Protocol (v3.0)
- JSON-RPC style messages over Named Pipes/Domain Sockets
- Event types: connection, compilation, projectChanged
- Request timeout: 6 minutes (configurable)
- Automatic reconnection on Unity restart
- Unity-side implementation in MCPBridge.cs

## Environment Variables
- `UNITY_MCP_LOG_LEVEL`: Logging level (debug, info, warn, error)
- `UNITY_MCP_TIMEOUT`: Request timeout in milliseconds
- `USE_OPTIMIZED_SERVICES`: Enable streaming for large files
- `PORT`: HTTP server port (default 3000)

## Version Compatibility
- **v2.x**: Unity 2019.4+, full service coverage, file-based operations
- **v3.0**: Unity 6000+ only, simplified API, direct Unity integration
```

--------------------------------------------------------------------------------
/tsconfig.test.json:
--------------------------------------------------------------------------------

```json
{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "module": "ESNext",
    "moduleResolution": "node",
    "allowImportingTsExtensions": false,
    "noEmit": true
  },
  "include": ["tests/**/*", "src/**/*"],
  "ts-node": {
    "esm": true,
    "experimentalSpecifierResolution": "node"
  }
}
```

--------------------------------------------------------------------------------
/vitest.config.ts:
--------------------------------------------------------------------------------

```typescript
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    globals: true,
    environment: 'node',
    coverage: {
      provider: 'v8',
      reporter: ['text', 'json', 'html'],
      exclude: [
        'node_modules/',
        'tests/',
        'build/',
        '**/*.test.ts',
        '**/*.config.ts'
      ]
    }
  },
  resolve: {
    extensions: ['.ts', '.js', '.json']
  }
});
```

--------------------------------------------------------------------------------
/create-bundled-dxt.sh:
--------------------------------------------------------------------------------

```bash
#!/bin/bash

# Clean up
rm -rf dxt-bundled
mkdir -p dxt-bundled

# Copy manifest.json and modify entry point
cp manifest.json dxt-bundled/
sed -i '' 's|"entry_point": "server/simple-index.js"|"entry_point": "unity-mcp-server.bundle.js"|g' dxt-bundled/manifest.json
sed -i '' 's|"args": \["${__dirname}/server/simple-index.js"\]|"args": ["${__dirname}/unity-mcp-server.bundle.js"]|g' dxt-bundled/manifest.json

# Copy bundle
cp unity-mcp-server.bundle.js dxt-bundled/

# Create ZIP file
cd dxt-bundled
zip -r ../unity-mcp-server.dxt * -x "*.DS_Store" -x "__MACOSX/*"
cd ..

# Clean up
rm -rf dxt-bundled

echo "Created unity-mcp-server.dxt"
```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "node",
    "lib": ["ES2020"],
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": false,
    "outDir": "./build",
    "rootDir": "./src",
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "build", "dist"]
}
```

--------------------------------------------------------------------------------
/build-bundle.js:
--------------------------------------------------------------------------------

```javascript
#!/usr/bin/env node

import * as esbuild from 'esbuild';
import { readFileSync, writeFileSync } from 'fs';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

// Build the bundle
await esbuild.build({
  entryPoints: ['build/simple-index.js'],
  bundle: true,
  platform: 'node',
  target: 'node18',
  outfile: 'unity-mcp-server.bundle.js',
  format: 'esm',
  // No banner needed - causes conflicts
  external: [
    'crypto',
    'fs',
    'path',
    'url',
    'util',
    'stream',
    'os',
    'events',
    'http',
    'https',
    'net',
    'child_process',
    'readline',
    'zlib',
    'buffer',
    'string_decoder',
    'querystring',
    'assert',
    'tty',
    'dgram',
    'dns',
    'v8',
    'vm',
    'worker_threads',
    'perf_hooks'
  ],
  minify: false,
  sourcemap: false,
  metafile: true
});

console.log('Bundle created: unity-mcp-server.bundle.js');
```

--------------------------------------------------------------------------------
/BUILD.md:
--------------------------------------------------------------------------------

```markdown
# Building Unity MCP Server DXT Package

## Quick Build

To build the complete DXT package for Claude Desktop:

```bash
npm run build:dxt
```

This will create `unity-mcp-server.dxt` which can be installed in Claude Desktop.

## Manual Build Steps

If you need to build manually:

1. **Build TypeScript:**
   ```bash
   npm run build
   ```

2. **Create JavaScript bundle:**
   ```bash
   node build-bundle.js
   ```

3. **Create DXT package:**
   ```bash
   ./create-bundled-dxt.sh
   ```

## Installation

1. Build the DXT package using `npm run build:dxt`
2. Install `unity-mcp-server.dxt` in Claude Desktop
3. The server will automatically start when Claude Desktop loads

## What's Included

The DXT package contains:
- **manifest.json** - Extension metadata
- **unity-mcp-server.bundle.js** - Complete bundled server with embedded Unity scripts

All Unity C# scripts are embedded directly in the JavaScript bundle, eliminating file dependencies.

## Clean Build

To start fresh:

```bash
npm run clean
npm run build:dxt
```
```

--------------------------------------------------------------------------------
/build-final-dxt.sh:
--------------------------------------------------------------------------------

```bash
#!/bin/bash

# Unity MCP Server - Final DXT Builder
# This script builds the complete Unity MCP Server DXT package

set -e

echo "Building Unity MCP Server DXT package..."

# Clean previous builds
echo "Cleaning previous builds..."
rm -f unity-mcp-server.bundle.js
rm -f unity-mcp-server.dxt

# Generate embedded scripts from Unity source files
echo "Generating embedded scripts..."
node generate-embedded-scripts.cjs

# Build TypeScript
echo "Building TypeScript..."
npm run build

# Create bundle
echo "Creating JavaScript bundle..."
node build-bundle.js

# Create DXT package
echo "Creating DXT package..."
./create-bundled-dxt.sh

# Verify final package
if [ -f "unity-mcp-server.dxt" ]; then
    echo "✓ Successfully created unity-mcp-server.dxt"
    echo "  Size: $(ls -lh unity-mcp-server.dxt | awk '{print $5}')"
    echo "  Contents:"
    unzip -l unity-mcp-server.dxt | grep -E '\.(json|js)$' | awk '{print "    " $4}'
else
    echo "✗ Failed to create unity-mcp-server.dxt"
    exit 1
fi

echo "DXT package build complete!"
echo "Install the package: unity-mcp-server.dxt"
```

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

```json
{
  "name": "unity-mcp-server",
  "version": "3.1.1",
  "description": "Unity MCP Server - Simple HTTP-based Unity Editor integration for AI assistants",
  "type": "module",
  "main": "build/simple-index.js",
  "bin": {
    "unity-mcp-server": "./build/simple-index.js"
  },
  "scripts": {
    "build": "tsc",
    "dev": "tsc --watch",
    "start": "node build/simple-index.js",
    "clean": "rm -rf build",
    "prepare": "npm run build",
    "build:dxt": "./build-final-dxt.sh",
    "test": "vitest",
    "test:watch": "vitest --watch",
    "test:coverage": "vitest run --coverage",
    "test:unit": "vitest run tests/unit",
    "test:integration": "vitest run tests/integration"
  },
  "keywords": [
    "mcp",
    "mcp-server",
    "model-context-protocol",
    "unity",
    "unity3d",
    "unity-editor",
    "gamedev",
    "game-development",
    "claude",
    "ai",
    "ai-tools"
  ],
  "author": "zabaglione",
  "license": "MIT",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/zabaglione/mcp-server-unity.git"
  },
  "bugs": {
    "url": "https://github.com/zabaglione/mcp-server-unity/issues"
  },
  "homepage": "https://github.com/zabaglione/mcp-server-unity#readme",
  "dependencies": {
    "@modelcontextprotocol/sdk": "^0.5.0"
  },
  "devDependencies": {
    "@types/node": "^20.0.0",
    "@vitest/coverage-v8": "^3.2.4",
    "esbuild": "^0.25.6",
    "typescript": "^5.0.0",
    "vitest": "^3.2.4"
  },
  "engines": {
    "node": ">=16.0.0"
  }
}

```

--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------

```json
{
  "dxt_version": "1.0",
  "name": "unity-mcp-server",
  "version": "3.1.0",
  "description": "Unity MCP Server - Enable AI assistants to interact with Unity projects",
  "author": {
    "name": "zabaglione",
    "url": "https://github.com/zabaglione"
  },
  "server": {
    "type": "node",
    "entry_point": "server/simple-index.js",
    "mcp_config": {
      "command": "node",
      "args": ["${__dirname}/server/simple-index.js"]
    }
  },
  "tools": [
    {
      "name": "project_info",
      "description": "Get Unity project information"
    },
    {
      "name": "project_status",
      "description": "Check Unity connection status"
    },
    {
      "name": "setup_unity_bridge",
      "description": "Install/update Unity MCP scripts"
    },
    {
      "name": "script_create",
      "description": "Create a new C# script"
    },
    {
      "name": "script_read",
      "description": "Read script contents"
    },
    {
      "name": "script_apply_diff",
      "description": "Apply unified diff to update scripts"
    },
    {
      "name": "script_delete",
      "description": "Delete a script"
    },
    {
      "name": "shader_create",
      "description": "Create a new shader"
    },
    {
      "name": "shader_read",
      "description": "Read shader contents"
    },
    {
      "name": "shader_delete",
      "description": "Delete a shader"
    },
    {
      "name": "folder_create",
      "description": "Create a new folder"
    },
    {
      "name": "folder_rename",
      "description": "Rename a folder"
    },
    {
      "name": "folder_move",
      "description": "Move a folder to new location"
    },
    {
      "name": "folder_delete",
      "description": "Delete a folder recursively"
    },
    {
      "name": "folder_list",
      "description": "List folder contents"
    }
  ]
}
```

--------------------------------------------------------------------------------
/src/simple-index.ts:
--------------------------------------------------------------------------------

```typescript
#!/usr/bin/env node

import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
import { UnityMcpTools } from './tools/unity-mcp-tools.js';

/**
 * Simple Unity MCP Server
 * Provides Unity Editor integration through MCP protocol
 */
class UnityMcpServer {
  private server: Server;
  private tools: UnityMcpTools;

  constructor() {
    this.tools = new UnityMcpTools();
    this.server = new Server(
      {
        name: 'unity-mcp-server',
        version: '1.0.0',
      },
      {
        capabilities: {
          tools: {},
        },
      }
    );

    this.setupHandlers();
  }

  private setupHandlers() {
    // Handle tool listing
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: this.tools.getTools(),
    }));

    // Handle tool execution
    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      return this.tools.executeTool(request.params.name, request.params.arguments || {});
    });
  }

  async run() {
    try {
      console.error('[Unity MCP] Starting server...');
      const transport = new StdioServerTransport();
      
      console.error('[Unity MCP] Connecting to transport...');
      await this.server.connect(transport);
      console.error('[Unity MCP] Server connected successfully');
      
      // Keep the process alive - this is critical!
      process.stdin.resume();
      
      process.on('SIGINT', () => {
        console.error('[Unity MCP] Received SIGINT, shutting down...');
        process.exit(0);
      });
      
      process.on('SIGTERM', () => {
        console.error('[Unity MCP] Received SIGTERM, shutting down...');
        process.exit(0);
      });
      
      // Log that we're ready
      console.error('[Unity MCP] Server is ready and listening');
    } catch (error) {
      console.error('[Unity MCP] Failed to start server:', error);
      throw error;
    }
  }
}

// Main entry point
const server = new UnityMcpServer();
server.run().catch((error) => {
  console.error('[Unity MCP] Fatal error:', error);
  process.exit(1);
});

// Handle uncaught errors
process.on('uncaughtException', (error) => {
  console.error('[Unity MCP] Uncaught exception:', error);
  process.exit(1);
});

process.on('unhandledRejection', (reason, promise) => {
  console.error('[Unity MCP] Unhandled rejection at:', promise, 'reason:', reason);
  process.exit(1);
});
```

--------------------------------------------------------------------------------
/generate-embedded-scripts.cjs:
--------------------------------------------------------------------------------

```
const fs = require('fs').promises;
const path = require('path');

async function generateEmbeddedScripts() {
  const scriptsDir = path.join(__dirname, 'src', 'unity-scripts');
  const outputPath = path.join(__dirname, 'src', 'embedded-scripts.ts');
  
  try {
    console.log('Generating embedded-scripts.ts from Unity source files...');
    
    // Define scripts to embed
    const scriptsToEmbed = [
      {
        fileName: 'UnityHttpServer.cs',
        version: '1.1.0',
        sourcePath: path.join(scriptsDir, 'UnityHttpServer.cs')
      },
      {
        fileName: 'UnityMCPServerWindow.cs', 
        version: '1.0.0',
        sourcePath: path.join(scriptsDir, 'UnityMCPServerWindow.cs')
      }
    ];
    
    // Read all script contents
    const embeddedScripts = [];
    
    for (const script of scriptsToEmbed) {
      try {
        const content = await fs.readFile(script.sourcePath, 'utf-8');
        
        // Escape content for JavaScript template literal
        const escapedContent = content
          .replace(/\\/g, '\\\\')
          .replace(/`/g, '\\`')
          .replace(/\$\{/g, '\\${');
        
        embeddedScripts.push({
          fileName: script.fileName,
          version: script.version,
          content: escapedContent
        });
        
        console.log(`✓ Embedded ${script.fileName} (${content.length} chars)`);
      } catch (error) {
        console.error(`✗ Failed to read ${script.fileName}: ${error.message}`);
        throw error;
      }
    }
    
    // Generate TypeScript content
    const tsContent = `import * as fs from 'fs/promises';
import * as path from 'path';

export interface EmbeddedScript {
  fileName: string;
  content: string;
  version: string;
}

/**
 * Static embedded scripts provider
 * Generated at build time from Unity source files
 */
export class EmbeddedScriptsProvider {
  private scripts: Map<string, EmbeddedScript> = new Map();

  constructor() {
    this.initializeScripts();
  }

  private initializeScripts() {
${embeddedScripts.map(script => `    // ${script.fileName} content
    this.scripts.set('${script.fileName}', {
      fileName: '${script.fileName}',
      version: '${script.version}',
      content: \`${script.content}\`
    });`).join('\n\n')}
  }

  /**
   * Get script by filename
   */
  async getScript(fileName: string): Promise<EmbeddedScript | null> {
    return this.scripts.get(fileName) || null;
  }

  /**
   * Get script synchronously
   */
  getScriptSync(fileName: string): EmbeddedScript | null {
    return this.scripts.get(fileName) || null;
  }

  /**
   * Write script to file with proper UTF-8 BOM for Unity compatibility
   */
  async writeScriptToFile(fileName: string, targetPath: string): Promise<void> {
    const script = await this.getScript(fileName);
    if (!script) {
      throw new Error(\`Script not found: \${fileName}\`);
    }

    // Ensure target directory exists
    await fs.mkdir(path.dirname(targetPath), { recursive: true });
    
    // Write with UTF-8 BOM for Unity compatibility
    const utf8BOM = Buffer.from([0xEF, 0xBB, 0xBF]);
    const contentBuffer = Buffer.from(script.content, 'utf8');
    const finalBuffer = Buffer.concat([utf8BOM, contentBuffer]);
    
    await fs.writeFile(targetPath, finalBuffer);
  }

  /**
   * Get all available script names
   */
  getAvailableScripts(): string[] {
    return Array.from(this.scripts.keys());
  }

  /**
   * Get script version
   */
  getScriptVersion(fileName: string): string | null {
    const script = this.scripts.get(fileName);
    return script?.version || null;
  }
}`;

    // Write the generated file
    await fs.writeFile(outputPath, tsContent, 'utf-8');
    
    console.log(`✓ Generated embedded-scripts.ts with ${embeddedScripts.length} scripts`);
    console.log(`  Output: ${outputPath}`);
    
  } catch (error) {
    console.error('Failed to generate embedded scripts:', error.message);
    process.exit(1);
  }
}

generateEmbeddedScripts();
```

--------------------------------------------------------------------------------
/tests/integration/simple-integration.test.ts:
--------------------------------------------------------------------------------

```typescript
import { describe, it, expect, beforeAll, afterAll, test } from 'vitest';
import { spawn } from 'child_process';
import { UnityHttpAdapter } from '../../src/adapters/unity-http-adapter.js';

describe('Unity MCP Integration Tests', () => {
  let adapter: UnityHttpAdapter;

  beforeAll(() => {
    adapter = new UnityHttpAdapter();
  });

  describe('Connection', () => {
    it('should check connection status', async () => {
      // This test will pass if Unity is running with the HTTP server
      // or fail gracefully if not
      const connected = await adapter.isConnected();
      console.log('Unity connection status:', connected);
      expect(typeof connected).toBe('boolean');
    });
  });

  describe('Script Operations (if connected)', () => {
    it.skipIf(async () => !(await adapter.isConnected()))('should create, read, and delete a script', async () => {
      // Test will only run if connected

      // Create
      const createResult = await adapter.createScript(
        'IntegrationTestScript',
        'public class IntegrationTestScript : MonoBehaviour { }',
        'Assets/Scripts/Tests'
      );
      expect(createResult.path).toContain('IntegrationTestScript.cs');
      expect(createResult.guid).toBeTruthy();

      // Read
      const readResult = await adapter.readScript(createResult.path);
      expect(readResult.content).toContain('IntegrationTestScript');
      expect(readResult.path).toBe(createResult.path);

      // Delete
      const deleteResult = await adapter.deleteScript(createResult.path);
      expect(deleteResult.message).toContain('successfully');
    });
  });

  describe('Shader Operations (if connected)', () => {
    it.skipIf(async () => !(await adapter.isConnected()))('should create, read, and delete a shader', async () => {

      // Create
      const createResult = await adapter.createShader(
        'IntegrationTestShader',
        'Shader "Custom/IntegrationTest" { SubShader { Pass { } } }',
        'Assets/Shaders/Tests'
      );
      expect(createResult.path).toContain('IntegrationTestShader.shader');

      // Read
      const readResult = await adapter.readShader(createResult.path);
      expect(readResult.content).toContain('IntegrationTest');

      // Delete
      const deleteResult = await adapter.deleteShader(createResult.path);
      expect(deleteResult.message).toContain('successfully');
    });
  });

  describe('Project Operations (if connected)', () => {
    it.skipIf(async () => !(await adapter.isConnected()))('should get project info', async () => {

      const info = await adapter.getProjectInfo();
      expect(info.projectPath).toBeTruthy();
      expect(info.unityVersion).toMatch(/\d{4}\.\d+\.\d+/);
      expect(info.platform).toBeTruthy();
    });
  });
});

describe('MCP Server Integration', () => {
  let mcpProcess: any;

  beforeAll(async () => {
    // Start MCP server
    mcpProcess = spawn('node', ['build/simple-index.js'], {
      stdio: ['pipe', 'pipe', 'pipe']
    });

    // Wait for server to start
    await new Promise(resolve => setTimeout(resolve, 1000));
  });

  afterAll(() => {
    mcpProcess?.kill();
  });

  it('should start without errors', () => {
    expect(mcpProcess.pid).toBeTruthy();
  });

  it('should respond to MCP protocol', async () => {
    // Send initialize request
    const request = {
      jsonrpc: '2.0',
      id: 1,
      method: 'initialize',
      params: {
        protocolVersion: '2024-11-05',
        capabilities: {}
      }
    };

    mcpProcess.stdin.write(JSON.stringify(request) + '\n');

    // Wait for response with timeout
    const response = await new Promise((resolve, reject) => {
      const timeout = setTimeout(() => {
        reject(new Error('Timeout waiting for response'));
      }, 5000);
      
      mcpProcess.stdout.once('data', (data: Buffer) => {
        clearTimeout(timeout);
        const lines = data.toString().split('\n');
        for (const line of lines) {
          if (line.trim()) {
            try {
              const parsed = JSON.parse(line);
              resolve(parsed);
              break;
            } catch {
              // Continue if not JSON
            }
          }
        }
      });
      
      mcpProcess.stderr.once('data', (data: Buffer) => {
        console.error('MCP server error:', data.toString());
      });
    });

    console.log('MCP response:', response);
    expect(response).toBeDefined();
    expect((response as any).jsonrpc).toBe('2.0');
  });
});
```

--------------------------------------------------------------------------------
/docs/API.md:
--------------------------------------------------------------------------------

```markdown
# Unity MCP Server API Reference

## Overview

Unity MCP Server provides a simple HTTP-based API for Unity Editor integration. All operations are performed through MCP tools that communicate with Unity via HTTP.

## MCP Tools

### Script Operations

#### `script_create`
Create a new C# script in the Unity project.

**Parameters:**
- `fileName` (string, required): Name of the script file (without .cs extension)
- `content` (string, optional): Script content. If not provided, creates an empty MonoBehaviour
- `folder` (string, optional): Target folder path. Defaults to "Assets/Scripts"

**Example:**
```json
{
  "fileName": "PlayerController",
  "content": "public class PlayerController : MonoBehaviour { }",
  "folder": "Assets/Scripts/Player"
}
```

#### `script_read`
Read the contents of an existing C# script.

**Parameters:**
- `path` (string, required): Path to the script file

**Example:**
```json
{
  "path": "Assets/Scripts/PlayerController.cs"
}
```

#### `script_delete`
Delete a C# script from the Unity project.

**Parameters:**
- `path` (string, required): Path to the script file

**Example:**
```json
{
  "path": "Assets/Scripts/PlayerController.cs"
}
```

### Shader Operations

#### `shader_create`
Create a new shader file in the Unity project.

**Parameters:**
- `name` (string, required): Name of the shader (without .shader extension)
- `content` (string, optional): Shader content. If not provided, creates a default shader
- `folder` (string, optional): Target folder path. Defaults to "Assets/Shaders"

**Example:**
```json
{
  "name": "CustomShader",
  "content": "Shader \"Custom/MyShader\" { SubShader { Pass { } } }",
  "folder": "Assets/Shaders/Custom"
}
```

#### `shader_read`
Read the contents of an existing shader file.

**Parameters:**
- `path` (string, required): Path to the shader file

**Example:**
```json
{
  "path": "Assets/Shaders/CustomShader.shader"
}
```

#### `shader_delete`
Delete a shader file from the Unity project.

**Parameters:**
- `path` (string, required): Path to the shader file

**Example:**
```json
{
  "path": "Assets/Shaders/CustomShader.shader"
}
```

### Project Operations

#### `project_info`
Get information about the current Unity project.

**Parameters:** None

**Returns:**
- `projectPath`: Path to the Unity project
- `unityVersion`: Version of Unity being used
- `platform`: Current build platform
- `isPlaying`: Whether Unity is in play mode

**Example Response:**
```json
{
  "projectPath": "/Users/user/MyUnityProject",
  "unityVersion": "2022.3.0f1",
  "platform": "StandaloneOSX",
  "isPlaying": false
}
```

#### `project_status`
Check the connection status between MCP server and Unity.

**Parameters:** None

**Returns:**
- Connection status message
- Project path (if connected)

## HTTP API (Unity Side)

The Unity HTTP server listens on port 3001 and provides the following endpoints:

### Base URL
```
http://localhost:3001
```

### Endpoints

#### `GET /ping`
Health check endpoint.

**Response:**
```json
{
  "status": "ok",
  "timestamp": "2024-01-10T12:00:00Z"
}
```

#### `POST /script/create`
Create a new C# script.

**Request Body:**
```json
{
  "fileName": "string",
  "content": "string",
  "folder": "string"
}
```

#### `POST /script/read`
Read script contents.

**Request Body:**
```json
{
  "path": "string"
}
```

#### `POST /script/delete`
Delete a script.

**Request Body:**
```json
{
  "path": "string"
}
```

#### `POST /shader/create`
Create a new shader.

**Request Body:**
```json
{
  "name": "string",
  "content": "string",
  "folder": "string"
}
```

#### `POST /shader/read`
Read shader contents.

**Request Body:**
```json
{
  "path": "string"
}
```

#### `POST /shader/delete`
Delete a shader.

**Request Body:**
```json
{
  "path": "string"
}
```

#### `GET /project/info`
Get project information.

**Response:**
```json
{
  "projectPath": "string",
  "unityVersion": "string",
  "platform": "string",
  "isPlaying": boolean
}
```

## Error Handling

All errors are returned with appropriate HTTP status codes and error messages:

- `200 OK`: Success
- `400 Bad Request`: Invalid parameters
- `404 Not Found`: Resource not found
- `500 Internal Server Error`: Unity operation failed

Error response format:
```json
{
  "error": "Error message describing what went wrong"
}
```

## Usage with Claude

When using with Claude Desktop, the tools are automatically available after configuration. You can use natural language to interact with Unity:

```
"Create a new PlayerController script in the Scripts folder"
"Read the contents of the GameManager script"
"Delete the old TestScript"
"Create a new water shader"
"Show me the project information"
```

Claude will translate these requests into the appropriate tool calls.
```

--------------------------------------------------------------------------------
/src/adapters/unity-http-adapter.ts:
--------------------------------------------------------------------------------

```typescript
// Using Node.js 18+ built-in fetch

export interface UnityHttpAdapterOptions {
  url?: string;
  timeout?: number;
}

export interface UnityResponse {
  success: boolean;
  result?: any;
  error?: string;
}

export interface FolderEntry {
  path: string;
  name: string;
  type: 'file' | 'folder';
  extension?: string;
  guid: string;
}

/**
 * HTTP adapter for Unity MCP Server
 * Provides a clean interface to communicate with Unity HTTP server
 */
export class UnityHttpAdapter {
  private url: string;
  private timeout: number;

  constructor(options: UnityHttpAdapterOptions = {}) {
    this.url = options.url || 'http://localhost:23457/';
    this.timeout = options.timeout || 15000;
  }

  /**
   * Call a method on the Unity server
   */
  async call(method: string, params: Record<string, any> = {}): Promise<any> {
    const startTime = Date.now();
    console.error(`[Unity HTTP] Calling method: ${method}`);
    
    const maxRetries = 3;
    let lastError: any;
    
    for (let retry = 0; retry < maxRetries; retry++) {
      if (retry > 0) {
        console.error(`[Unity HTTP] Retry ${retry}/${maxRetries - 1} for method: ${method}`);
        await new Promise(resolve => setTimeout(resolve, 1000 * retry)); // Exponential backoff
      }
      
      try {
        const controller = new AbortController();
        const timeoutId = setTimeout(() => {
          console.error(`[Unity HTTP] Request timeout after ${this.timeout}ms for method: ${method}`);
          controller.abort();
        }, this.timeout);

        const response = await fetch(this.url, {
          method: 'POST',
          headers: { 
            'Content-Type': 'application/json; charset=utf-8',
            'Accept': 'application/json; charset=utf-8'
          },
          body: JSON.stringify({ method, ...params }),
          signal: controller.signal
        });

        clearTimeout(timeoutId);
        
        const elapsed = Date.now() - startTime;
        console.error(`[Unity HTTP] Response received in ${elapsed}ms for method: ${method}`);

        const result = await response.json() as UnityResponse;
        
        if (!result.success) {
          throw new Error(result.error || 'Unknown error');
        }

        return result.result;
        
      } catch (error: any) {
        lastError = error;
        
        if (error.name === 'AbortError') {
          lastError = new Error('Request timeout');
        } else if (error.message?.includes('ECONNREFUSED')) {
          lastError = new Error('Unity HTTP server is not running');
        } else if (error.message?.includes('Failed to fetch')) {
          lastError = new Error('Failed to connect to Unity HTTP server');
        }
        
        console.error(`[Unity HTTP] Error on attempt ${retry + 1}: ${lastError.message}`);
        
        // Don't retry on certain errors
        if (error.message?.includes('Method not found')) {
          throw error;
        }
      }
    }
    
    // All retries failed
    throw lastError || new Error('Unknown error after retries');
  }

  /**
   * Check if Unity server is connected
   */
  async isConnected(): Promise<boolean> {
    try {
      await this.call('ping');
      return true;
    } catch {
      return false;
    }
  }

  // Script operations
  async createScript(fileName: string, content?: string, folder?: string): Promise<any> {
    return this.call('script/create', { fileName, content, folder });
  }

  async readScript(path: string): Promise<any> {
    return this.call('script/read', { path });
  }

  async deleteScript(path: string): Promise<any> {
    return this.call('script/delete', { path });
  }
  
  async applyDiff(path: string, diff: string, options?: any): Promise<any> {
    return this.call('script/applyDiff', { path, diff, options });
  }

  // Shader operations
  async createShader(name: string, content?: string, folder?: string): Promise<any> {
    return this.call('shader/create', { name, content, folder });
  }

  async readShader(path: string): Promise<any> {
    return this.call('shader/read', { path });
  }

  async deleteShader(path: string): Promise<any> {
    return this.call('shader/delete', { path });
  }

  // Project operations
  async getProjectInfo(): Promise<any> {
    return this.call('project/info');
  }
  
  // Folder operations
  async createFolder(path: string): Promise<{ path: string; guid: string }> {
    return this.call('folder/create', { path });
  }
  
  async renameFolder(oldPath: string, newName: string): Promise<{ oldPath: string; newPath: string; guid: string }> {
    return this.call('folder/rename', { oldPath, newName });
  }
  
  async moveFolder(sourcePath: string, targetPath: string): Promise<{ sourcePath: string; targetPath: string; guid: string }> {
    return this.call('folder/move', { sourcePath, targetPath });
  }
  
  async deleteFolder(path: string, recursive: boolean = true): Promise<{ path: string }> {
    return this.call('folder/delete', { path, recursive });
  }
  
  async listFolder(path?: string, recursive: boolean = false): Promise<{ path: string; entries: FolderEntry[] }> {
    return this.call('folder/list', { path, recursive });
  }
}
```

--------------------------------------------------------------------------------
/tests/unit/templates/shaders/shader-templates.test.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Shader template tests
 */

import { getBuiltInShaderTemplate } from '../../../../src/templates/shaders/builtin-shader.js';
import { getURPShaderTemplate } from '../../../../src/templates/shaders/urp-shader.js';
import { getHDRPShaderTemplate } from '../../../../src/templates/shaders/hdrp-shader.js';

describe('Shader Templates', () => {
  describe('Built-in Shader Template', () => {
    it('should generate valid built-in shader', () => {
      const shader = getBuiltInShaderTemplate('TestShader');
      
      expect(shader).toContain('Shader "Custom/TestShader"');
      expect(shader).toContain('Properties');
      expect(shader).toContain('_MainTex');
      expect(shader).toContain('_Color');
      expect(shader).toContain('SubShader');
      expect(shader).toContain('CGPROGRAM');
      expect(shader).toContain('#pragma surface surf Standard');
      expect(shader).toContain('struct Input');
      expect(shader).toContain('void surf');
      expect(shader).toContain('ENDCG');
      expect(shader).toContain('FallBack "Diffuse"');
    });

    it('should use shader name in declaration', () => {
      const shader = getBuiltInShaderTemplate('MyCustomShader');
      expect(shader).toContain('Shader "Custom/MyCustomShader"');
    });

    it('should include metallic and smoothness properties', () => {
      const shader = getBuiltInShaderTemplate('TestShader');
      expect(shader).toContain('_Metallic');
      expect(shader).toContain('_Glossiness'); // Built-in uses _Glossiness
    });
  });

  describe('URP Shader Template', () => {
    it('should generate valid URP shader', () => {
      const shader = getURPShaderTemplate('TestShader');
      
      expect(shader).toContain('Shader "Universal Render Pipeline/Custom/TestShader"');
      expect(shader).toContain('Properties');
      expect(shader).toContain('_BaseMap');
      expect(shader).toContain('_BaseColor');
      expect(shader).toContain('SubShader');
      expect(shader).toContain('Tags');
      expect(shader).toContain('"RenderType"="Opaque"');
      expect(shader).toContain('"RenderPipeline" = "UniversalPipeline"'); // Fixed spacing
      expect(shader).toContain('HLSLPROGRAM');
      expect(shader).toContain('#pragma vertex vert');
      expect(shader).toContain('#pragma fragment frag');
      expect(shader).toContain('ENDHLSL');
    });

    it('should use shader name in declaration', () => {
      const shader = getURPShaderTemplate('MyURPShader');
      expect(shader).toContain('Shader "Universal Render Pipeline/Custom/MyURPShader"');
    });

    it('should include URP-specific includes', () => {
      const shader = getURPShaderTemplate('TestShader');
      expect(shader).toContain('Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl');
      expect(shader).toContain('Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl');
    });

    it('should have Forward pass', () => {
      const shader = getURPShaderTemplate('TestShader');
      expect(shader).toContain('Name "ForwardLit"');
      expect(shader).toContain('"LightMode" = "UniversalForward"'); // Fixed spacing
    });
  });

  describe('HDRP Shader Template', () => {
    it('should generate valid HDRP shader', () => {
      const shader = getHDRPShaderTemplate('TestShader');
      
      expect(shader).toContain('Shader "HDRP/Custom/TestShader"');
      expect(shader).toContain('Properties');
      expect(shader).toContain('_BaseColorMap');
      expect(shader).toContain('_BaseColor');
      expect(shader).toContain('_Metallic');
      expect(shader).toContain('_Smoothness');
      expect(shader).toContain('SubShader');
      expect(shader).toContain('HLSLPROGRAM');
      expect(shader).toContain('#pragma vertex Vert');
      expect(shader).toContain('#pragma fragment Frag');
      expect(shader).toContain('ENDHLSL');
    });

    it('should use shader name in declaration', () => {
      const shader = getHDRPShaderTemplate('MyHDRPShader');
      expect(shader).toContain('Shader "HDRP/Custom/MyHDRPShader"');
    });

    it('should include HDRP-specific includes', () => {
      const shader = getHDRPShaderTemplate('TestShader');
      expect(shader).toContain('Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl');
      expect(shader).toContain('Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl');
      expect(shader).toContain('Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Material.hlsl');
    });

    it('should have proper HDRP tags', () => {
      const shader = getHDRPShaderTemplate('TestShader');
      expect(shader).toContain('"RenderPipeline"="HDRenderPipeline"');
      expect(shader).toContain('"Queue"="Geometry"');
    });

    it('should have ForwardOnly and ShadowCaster passes', () => {
      const shader = getHDRPShaderTemplate('TestShader');
      expect(shader).toContain('Name "ForwardOnly"');
      expect(shader).toContain('"LightMode" = "ForwardOnly"');
      expect(shader).toContain('Name "ShadowCaster"');
      expect(shader).toContain('"LightMode" = "ShadowCaster"');
    });

    it('should have fallback to HDRP/Lit', () => {
      const shader = getHDRPShaderTemplate('TestShader');
      expect(shader).toContain('FallBack "HDRP/Lit"');
    });
  });
});
```

--------------------------------------------------------------------------------
/src/unity-scripts/UnityMCPServerWindow.cs:
--------------------------------------------------------------------------------

```csharp
using System;
using UnityEngine;
using UnityEditor;

namespace UnityMCP
{
    /// <summary>
    /// Unity MCP Server control window
    /// </summary>
    public class UnityMCPServerWindow : EditorWindow
    {
        // Version information (should match UnityHttpServer)
        private const string SCRIPT_VERSION = "1.1.0";
        
        private int serverPort = 23457;
        private bool isServerRunning = false;
        private string serverStatus = "Stopped";
        private string lastError = "";
        
        [MenuItem("Window/Unity MCP Server")]
        public static void ShowWindow()
        {
            GetWindow<UnityMCPServerWindow>("Unity MCP Server");
        }
        
        void OnEnable()
        {
            // Load saved settings
            serverPort = EditorPrefs.GetInt("UnityMCP.ServerPort", 23457);
            UpdateStatus();
        }
        
        void OnDisable()
        {
            // Save settings
            EditorPrefs.SetInt("UnityMCP.ServerPort", serverPort);
        }
        
        void OnGUI()
        {
            GUILayout.Label("Unity MCP Server Control", EditorStyles.boldLabel);
            GUILayout.Label($"Version: {SCRIPT_VERSION}", EditorStyles.miniLabel);
            
            EditorGUILayout.Space();
            
            // Server Status
            EditorGUILayout.BeginHorizontal();
            GUILayout.Label("Status:", GUILayout.Width(60));
            var statusColor = isServerRunning ? Color.green : Color.red;
            var originalColor = GUI.color;
            GUI.color = statusColor;
            GUILayout.Label(serverStatus, EditorStyles.boldLabel);
            GUI.color = originalColor;
            EditorGUILayout.EndHorizontal();
            
            EditorGUILayout.Space();
            
            // Port Configuration
            EditorGUILayout.BeginHorizontal();
            GUILayout.Label("Port:", GUILayout.Width(60));
            var newPort = EditorGUILayout.IntField(serverPort);
            if (newPort != serverPort && newPort > 0 && newPort <= 65535)
            {
                serverPort = newPort;
                EditorPrefs.SetInt("UnityMCP.ServerPort", serverPort);
            }
            EditorGUILayout.EndHorizontal();
            
            // Port validation
            if (serverPort < 1024)
            {
                EditorGUILayout.HelpBox("Warning: Ports below 1024 may require administrator privileges.", MessageType.Warning);
            }
            
            EditorGUILayout.Space();
            
            // Control Buttons
            EditorGUILayout.BeginHorizontal();
            
            GUI.enabled = !isServerRunning;
            if (GUILayout.Button("Start Server", GUILayout.Height(30)))
            {
                StartServer();
            }
            
            GUI.enabled = isServerRunning;
            if (GUILayout.Button("Stop Server", GUILayout.Height(30)))
            {
                StopServer();
            }
            
            GUI.enabled = true;
            EditorGUILayout.EndHorizontal();
            
            EditorGUILayout.Space();
            
            // Connection Info
            if (isServerRunning)
            {
                EditorGUILayout.BeginVertical(EditorStyles.helpBox);
                GUILayout.Label("Connection Information", EditorStyles.boldLabel);
                EditorGUILayout.SelectableLabel($"http://localhost:{serverPort}/");
                EditorGUILayout.EndVertical();
            }
            
            // Error Display
            if (!string.IsNullOrEmpty(lastError))
            {
                EditorGUILayout.Space();
                EditorGUILayout.HelpBox(lastError, MessageType.Error);
                if (GUILayout.Button("Clear Error"))
                {
                    lastError = "";
                }
            }
            
            EditorGUILayout.Space();
            
            // Instructions
            EditorGUILayout.BeginVertical(EditorStyles.helpBox);
            GUILayout.Label("Instructions", EditorStyles.boldLabel);
            GUILayout.Label("1. Configure the port (default: 23457)");
            GUILayout.Label("2. Click 'Start Server' to begin");
            GUILayout.Label("3. Use the MCP client to connect");
            GUILayout.Label("4. Click 'Stop Server' when done");
            EditorGUILayout.EndVertical();
        }
        
        void StartServer()
        {
            try
            {
                UnityHttpServer.Start(serverPort);
                UpdateStatus();
                lastError = "";
                Debug.Log($"[UnityMCP] Server started on port {serverPort}");
            }
            catch (Exception e)
            {
                lastError = $"Failed to start server: {e.Message}";
                Debug.LogError($"[UnityMCP] {lastError}");
            }
        }
        
        void StopServer()
        {
            try
            {
                UnityHttpServer.Shutdown();
                UpdateStatus();
                lastError = "";
                Debug.Log("[UnityMCP] Server stopped");
            }
            catch (Exception e)
            {
                lastError = $"Failed to stop server: {e.Message}";
                Debug.LogError($"[UnityMCP] {lastError}");
            }
        }
        
        void UpdateStatus()
        {
            isServerRunning = UnityHttpServer.IsRunning;
            serverStatus = isServerRunning ? $"Running on port {UnityHttpServer.CurrentPort}" : "Stopped";
            Repaint();
        }
        
        void Update()
        {
            // Update status periodically
            UpdateStatus();
        }
    }
}
```

--------------------------------------------------------------------------------
/tests/unit/tools/unity-mcp-tools.test.ts:
--------------------------------------------------------------------------------

```typescript
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { UnityMcpTools } from '../../../src/tools/unity-mcp-tools.js';
import { UnityHttpAdapter } from '../../../src/adapters/unity-http-adapter.js';

// Mock the adapter
vi.mock('../../../src/adapters/unity-http-adapter.js');

describe('UnityMcpTools', () => {
  let tools: UnityMcpTools;
  let mockAdapter: any;

  beforeEach(() => {
    mockAdapter = {
      isConnected: vi.fn().mockResolvedValue(true),
      createScript: vi.fn(),
      readScript: vi.fn(),
      deleteScript: vi.fn(),
      createShader: vi.fn(),
      readShader: vi.fn(),
      deleteShader: vi.fn(),
      getProjectInfo: vi.fn()
    };
    
    vi.mocked(UnityHttpAdapter).mockImplementation(() => mockAdapter);
    tools = new UnityMcpTools();
  });

  describe('getTools', () => {
    it('should return all available tools', () => {
      const toolList = tools.getTools();
      
      expect(toolList).toHaveLength(8); // 3 script + 3 shader + 2 project tools
      expect(toolList.map(t => t.name)).toContain('script_create');
      expect(toolList.map(t => t.name)).toContain('shader_create');
      expect(toolList.map(t => t.name)).toContain('project_info');
    });

    it('should have proper input schemas', () => {
      const toolList = tools.getTools();
      const scriptCreate = toolList.find(t => t.name === 'script_create');
      
      expect(scriptCreate?.inputSchema).toMatchObject({
        type: 'object',
        properties: {
          fileName: { type: 'string' },
          content: { type: 'string' },
          folder: { type: 'string' }
        },
        required: ['fileName']
      });
    });
  });

  describe('executeTool', () => {
    describe('script_create', () => {
      it('should create script with all parameters', async () => {
        // Arrange
        mockAdapter.createScript.mockResolvedValue({
          path: 'Assets/Scripts/Test.cs',
          guid: 'test-guid'
        });

        // Act
        const result = await tools.executeTool('script_create', {
          fileName: 'Test',
          content: 'public class Test {}',
          folder: 'Assets/Scripts'
        });

        // Assert
        expect(mockAdapter.createScript).toHaveBeenCalledWith(
          'Test',
          'public class Test {}',
          'Assets/Scripts'
        );
        expect(result).toMatchObject({
          content: [{
            type: 'text',
            text: expect.stringContaining('Script created successfully')
          }]
        });
      });

      it('should handle missing fileName', async () => {
        // Act
        const result = await tools.executeTool('script_create', {});
        
        // Assert
        expect(result.content[0].text).toContain('Error: fileName is required');
      });
    });

    describe('script_read', () => {
      it('should read script and return content', async () => {
        // Arrange
        mockAdapter.readScript.mockResolvedValue({
          path: 'Assets/Scripts/Test.cs',
          content: 'public class Test {}',
          guid: 'test-guid'
        });

        // Act
        const result = await tools.executeTool('script_read', {
          path: 'Assets/Scripts/Test.cs'
        });

        // Assert
        expect(result.content[0].text).toContain('public class Test {}');
      });
    });

    describe('script_delete', () => {
      it('should delete script', async () => {
        // Arrange
        mockAdapter.deleteScript.mockResolvedValue({
          message: 'Script deleted successfully'
        });

        // Act
        const result = await tools.executeTool('script_delete', {
          path: 'Assets/Scripts/Test.cs'
        });

        // Assert
        expect(result.content[0].text).toContain('Script deleted successfully');
      });
    });

    describe('shader_create', () => {
      it('should create shader', async () => {
        // Arrange
        mockAdapter.createShader.mockResolvedValue({
          path: 'Assets/Shaders/Test.shader',
          guid: 'shader-guid'
        });

        // Act
        const result = await tools.executeTool('shader_create', {
          name: 'Test',
          content: 'Shader "Custom/Test" {}',
          folder: 'Assets/Shaders'
        });

        // Assert
        expect(mockAdapter.createShader).toHaveBeenCalledWith(
          'Test',
          'Shader "Custom/Test" {}',
          'Assets/Shaders'
        );
        expect(result.content[0].text).toContain('Shader created successfully');
      });
    });

    describe('project_info', () => {
      it('should return project information', async () => {
        // Arrange
        mockAdapter.getProjectInfo.mockResolvedValue({
          projectPath: '/Users/test/UnityProject',
          unityVersion: '2022.3.0f1',
          platform: 'StandaloneOSX',
          isPlaying: false
        });

        // Act
        const result = await tools.executeTool('project_info', {});

        // Assert
        expect(result.content[0].text).toContain('Unity Project Information');
        expect(result.content[0].text).toContain('2022.3.0f1');
      });
    });

    describe('project_status', () => {
      it('should return connected status', async () => {
        // Arrange
        mockAdapter.isConnected.mockResolvedValue(true);
        mockAdapter.getProjectInfo.mockResolvedValue({
          projectPath: '/Users/test/UnityProject'
        });

        // Act
        const result = await tools.executeTool('project_status', {});

        // Assert
        expect(result.content[0].text).toContain('Unity server is connected');
      });

      it('should return disconnected status', async () => {
        // Arrange
        mockAdapter.isConnected.mockResolvedValue(false);

        // Act
        const result = await tools.executeTool('project_status', {});

        // Assert
        expect(result.content[0].text).toContain('Unity server is not connected');
      });
    });

    it('should throw error for unknown tool', async () => {
      // Act
      const result = await tools.executeTool('unknown_tool', {});
      
      // Assert
      expect(result.content[0].text).toContain('Error: Unknown tool: unknown_tool');
    });
  });
});
```

--------------------------------------------------------------------------------
/tests/unity/UnityHttpServerTests.cs:
--------------------------------------------------------------------------------

```csharp
using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using NUnit.Framework;
using Newtonsoft.Json;
using UnityEngine;
using UnityEngine.TestTools;

namespace UnityMCP.Tests
{
    [TestFixture]
    public class UnityHttpServerTests
    {
        private const string BASE_URL = "http://localhost:23457";
        private HttpClient httpClient;

        [SetUp]
        public void Setup()
        {
            httpClient = new HttpClient();
            httpClient.Timeout = TimeSpan.FromSeconds(5);
        }

        [TearDown]
        public void TearDown()
        {
            httpClient?.Dispose();
        }

        [Test]
        public async Task Server_ShouldRespondToPing()
        {
            // Arrange
            var request = new { method = "ping" };
            var content = new StringContent(JsonConvert.SerializeObject(request), Encoding.UTF8, "application/json");

            // Act
            var response = await httpClient.PostAsync($"{BASE_URL}/", content);
            var responseBody = await response.Content.ReadAsStringAsync();
            var result = JsonConvert.DeserializeObject<dynamic>(responseBody);

            // Assert
            Assert.IsTrue(response.IsSuccessStatusCode);
            Assert.AreEqual("ok", result.status.ToString());
        }

        [Test]
        public async Task CreateScript_ShouldReturnSuccessWithValidParams()
        {
            // Arrange
            var request = new
            {
                method = "script/create",
                fileName = "TestScript",
                content = "public class TestScript : MonoBehaviour { }",
                folder = "Assets/Scripts/Test"
            };
            var content = new StringContent(JsonConvert.SerializeObject(request), Encoding.UTF8, "application/json");

            // Act
            var response = await httpClient.PostAsync($"{BASE_URL}/", content);
            var responseBody = await response.Content.ReadAsStringAsync();
            var result = JsonConvert.DeserializeObject<dynamic>(responseBody);

            // Assert
            Assert.IsTrue(response.IsSuccessStatusCode);
            Assert.IsTrue(result.success);
            Assert.IsNotNull(result.path);
            Assert.IsNotNull(result.guid);
        }

        [Test]
        public async Task CreateScript_ShouldReturnErrorWithMissingFileName()
        {
            // Arrange
            var request = new
            {
                method = "script/create",
                content = "public class TestScript : MonoBehaviour { }"
            };
            var content = new StringContent(JsonConvert.SerializeObject(request), Encoding.UTF8, "application/json");

            // Act
            var response = await httpClient.PostAsync($"{BASE_URL}/", content);
            var responseBody = await response.Content.ReadAsStringAsync();
            var result = JsonConvert.DeserializeObject<dynamic>(responseBody);

            // Assert
            Assert.AreEqual(400, (int)response.StatusCode);
            Assert.IsFalse(result.success);
            Assert.IsNotNull(result.error);
        }

        [Test]
        public async Task ReadScript_ShouldReturnContentForExistingFile()
        {
            // Arrange - First create a script
            var createRequest = new
            {
                method = "script/create",
                fileName = "ReadTestScript",
                content = "// Test content",
                folder = "Assets/Scripts/Test"
            };
            await httpClient.PostAsync($"{BASE_URL}/", 
                new StringContent(JsonConvert.SerializeObject(createRequest), Encoding.UTF8, "application/json"));

            // Act - Read the script
            var readRequest = new
            {
                method = "script/read",
                path = "Assets/Scripts/Test/ReadTestScript.cs"
            };
            var response = await httpClient.PostAsync($"{BASE_URL}/",
                new StringContent(JsonConvert.SerializeObject(readRequest), Encoding.UTF8, "application/json"));
            var responseBody = await response.Content.ReadAsStringAsync();
            var result = JsonConvert.DeserializeObject<dynamic>(responseBody);

            // Assert
            Assert.IsTrue(response.IsSuccessStatusCode);
            Assert.IsTrue(result.success);
            Assert.AreEqual("// Test content", result.content.ToString());
        }

        [Test]
        public async Task CreateShader_ShouldReturnSuccessWithValidParams()
        {
            // Arrange
            var request = new
            {
                method = "shader/create",
                name = "TestShader",
                content = "Shader \"Custom/TestShader\" { }",
                folder = "Assets/Shaders"
            };
            var content = new StringContent(JsonConvert.SerializeObject(request), Encoding.UTF8, "application/json");

            // Act
            var response = await httpClient.PostAsync($"{BASE_URL}/", content);
            var responseBody = await response.Content.ReadAsStringAsync();
            var result = JsonConvert.DeserializeObject<dynamic>(responseBody);

            // Assert
            Assert.IsTrue(response.IsSuccessStatusCode);
            Assert.IsTrue(result.success);
            Assert.IsNotNull(result.path);
            Assert.IsNotNull(result.guid);
        }

        [Test]
        public async Task GetProjectInfo_ShouldReturnProjectDetails()
        {
            // Arrange
            var request = new { method = "project/info" };
            var content = new StringContent(JsonConvert.SerializeObject(request), Encoding.UTF8, "application/json");

            // Act
            var response = await httpClient.PostAsync($"{BASE_URL}/", content);
            var responseBody = await response.Content.ReadAsStringAsync();
            var result = JsonConvert.DeserializeObject<dynamic>(responseBody);

            // Assert
            Assert.IsTrue(response.IsSuccessStatusCode);
            Assert.IsNotNull(result.projectPath);
            Assert.IsNotNull(result.unityVersion);
        }

        [Test]
        public async Task InvalidMethod_ShouldReturn404()
        {
            // Arrange
            var request = new { method = "invalid/method" };
            var content = new StringContent(JsonConvert.SerializeObject(request), Encoding.UTF8, "application/json");

            // Act
            var response = await httpClient.PostAsync($"{BASE_URL}/", content);

            // Assert
            Assert.AreEqual(404, (int)response.StatusCode);
        }
    }
}
```

--------------------------------------------------------------------------------
/src/services/unity-bridge-deploy-service.ts:
--------------------------------------------------------------------------------

```typescript
import * as fs from 'fs/promises';
import * as path from 'path';
import { EmbeddedScriptsProvider } from '../embedded-scripts.js';

interface DeploymentOptions {
  projectPath: string;
  forceUpdate?: boolean;
}

interface ScriptInfo {
  fileName: string;
  targetPath: string;
  version: string;
}

interface Logger {
  info(message: string): void;
  debug(message: string): void;
  error(message: string): void;
}

export class UnityBridgeDeployService {
  private logger: Logger = {
    info: (msg: string) => console.error(`[Unity MCP Deploy] ${msg}`),
    debug: (msg: string) => console.error(`[Unity MCP Deploy] DEBUG: ${msg}`),
    error: (msg: string) => console.error(`[Unity MCP Deploy] ERROR: ${msg}`)
  };
  
  private scriptsProvider: EmbeddedScriptsProvider = new EmbeddedScriptsProvider();
  
  private readonly SCRIPTS: ScriptInfo[] = [
    {
      fileName: 'UnityHttpServer.cs',
      targetPath: 'Assets/Editor/MCP/UnityHttpServer.cs',
      version: '1.1.0'
    },
    {
      fileName: 'UnityMCPServerWindow.cs',
      targetPath: 'Assets/Editor/MCP/UnityMCPServerWindow.cs',
      version: '1.1.0'
    }
  ];

  async deployScripts(options: DeploymentOptions): Promise<void> {
    const { projectPath, forceUpdate = false } = options;
    
    // Validate Unity project
    const projectValidation = await this.validateUnityProject(projectPath);
    if (!projectValidation.isValid) {
      throw new Error(`Invalid Unity project: ${projectValidation.error}`);
    }

    // Create Editor/MCP directory if it doesn't exist
    const editorMCPPath = path.join(projectPath, 'Assets', 'Editor', 'MCP');
    await fs.mkdir(editorMCPPath, { recursive: true });

    // Deploy each script
    for (const script of this.SCRIPTS) {
      await this.deployScript(projectPath, script, forceUpdate);
    }

    this.logger.info('Unity MCP scripts deployed successfully');
  }

  private async deployScript(projectPath: string, script: ScriptInfo, forceUpdate: boolean): Promise<void> {
    const targetPath = path.join(projectPath, script.targetPath);

    // Check if script exists and needs update
    const needsUpdate = await this.checkNeedsUpdate(targetPath, script.version, forceUpdate);
    
    if (needsUpdate) {
      // Get script from embedded provider (now async)
      const embeddedScript = await this.scriptsProvider.getScript(script.fileName);
      if (!embeddedScript) {
        throw new Error(`Embedded script not found: ${script.fileName}`);
      }
      
      this.logger.debug(`Using embedded script: ${script.fileName} (loaded from source)`);
      
      // Remove existing files if they exist (including .meta files)
      await this.removeExistingFiles(targetPath);
      
      // Write script using the embedded provider's method (handles UTF-8 BOM)
      await this.scriptsProvider.writeScriptToFile(script.fileName, targetPath);
      
      // Generate .meta file
      await this.generateMetaFile(targetPath);
      
      this.logger.info(`Deployed ${script.fileName} to ${script.targetPath}`);
    } else {
      this.logger.debug(`${script.fileName} is up to date`);
    }
  }

  private async checkNeedsUpdate(targetPath: string, currentVersion: string, forceUpdate: boolean): Promise<boolean> {
    if (forceUpdate) {
      return true;
    }

    try {
      const content = await fs.readFile(targetPath, 'utf8');
      
      // Extract version from file
      const versionMatch = content.match(/SCRIPT_VERSION\s*=\s*"([^"]+)"/);
      if (versionMatch) {
        const installedVersion = versionMatch[1];
        return this.compareVersions(currentVersion, installedVersion) > 0;
      }
      
      // If no version found, update needed
      return true;
    } catch (error) {
      // File doesn't exist, needs deployment
      return true;
    }
  }

  private compareVersions(version1: string, version2: string): number {
    const v1Parts = version1.split('.').map(Number);
    const v2Parts = version2.split('.').map(Number);
    
    for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) {
      const v1Part = v1Parts[i] || 0;
      const v2Part = v2Parts[i] || 0;
      
      if (v1Part > v2Part) return 1;
      if (v1Part < v2Part) return -1;
    }
    
    return 0;
  }

  private async generateMetaFile(filePath: string): Promise<void> {
    const metaPath = filePath + '.meta';
    
    // Check if meta file already exists
    try {
      await fs.access(metaPath);
      return; // Meta file exists, don't overwrite
    } catch {
      // Meta file doesn't exist, create it
    }

    const guid = this.generateGUID();
    const metaContent = `fileFormatVersion: 2
guid: ${guid}
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData: 
  assetBundleName: 
  assetBundleVariant: 
`;
    
    await fs.writeFile(metaPath, metaContent, 'utf8');
  }

  private async removeExistingFiles(targetPath: string): Promise<void> {
    try {
      // Remove the script file if it exists
      await fs.unlink(targetPath);
      this.logger.debug(`Removed existing file: ${targetPath}`);
    } catch (error: any) {
      if (error.code !== 'ENOENT') {
        this.logger.debug(`Failed to remove file ${targetPath}: ${error.message}`);
      }
    }

    try {
      // Remove the .meta file if it exists
      const metaPath = targetPath + '.meta';
      await fs.unlink(metaPath);
      this.logger.debug(`Removed existing meta file: ${metaPath}`);
    } catch (error: any) {
      if (error.code !== 'ENOENT') {
        this.logger.debug(`Failed to remove meta file ${targetPath}.meta: ${error.message}`);
      }
    }
  }

  private generateGUID(): string {
    // Generate a Unity-compatible GUID
    const hex = '0123456789abcdef';
    let guid = '';
    for (let i = 0; i < 32; i++) {
      guid += hex[Math.floor(Math.random() * 16)];
    }
    return guid;
  }
  
  private async validateUnityProject(projectPath: string): Promise<{ isValid: boolean; error?: string }> {
    try {
      // Check if directory exists
      const stats = await fs.stat(projectPath);
      if (!stats.isDirectory()) {
        return { isValid: false, error: 'Path is not a directory' };
      }
      
      // Check for Unity project structure
      const requiredDirs = ['Assets', 'ProjectSettings'];
      for (const dir of requiredDirs) {
        try {
          const dirPath = path.join(projectPath, dir);
          const dirStats = await fs.stat(dirPath);
          if (!dirStats.isDirectory()) {
            return { isValid: false, error: `Missing ${dir} directory` };
          }
        } catch {
          return { isValid: false, error: `Missing ${dir} directory` };
        }
      }
      
      return { isValid: true };
    } catch (error: any) {
      return { isValid: false, error: error.message };
    }
  }
}
```

--------------------------------------------------------------------------------
/tests/unit/adapters/unity-http-adapter.test.ts:
--------------------------------------------------------------------------------

```typescript
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { UnityHttpAdapter } from '../../../src/adapters/unity-http-adapter.js';

// Mock global fetch
const mockFetch = vi.fn();
global.fetch = mockFetch;

describe('UnityHttpAdapter', () => {
  let adapter: UnityHttpAdapter;

  beforeEach(() => {
    adapter = new UnityHttpAdapter();
    vi.clearAllMocks();
  });

  afterEach(() => {
    vi.restoreAllMocks();
  });

  describe('call', () => {
    it('should send POST request with correct format', async () => {
      // Arrange
      const mockResponse = {
        json: vi.fn().mockResolvedValue({
          success: true,
          result: { status: 'ok' }
        }),
        ok: true,
        status: 200
      };
      mockFetch.mockResolvedValue(mockResponse as any);

      // Act
      const result = await adapter.call('ping');

      // Assert
      expect(mockFetch).toHaveBeenCalledWith(
        'http://localhost:23457/',
        expect.objectContaining({
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ method: 'ping' })
        })
      );
      expect(result).toEqual({ status: 'ok' });
    });

    it('should pass parameters correctly', async () => {
      // Arrange
      const mockResponse = {
        json: vi.fn().mockResolvedValue({
          success: true,
          result: { path: 'Assets/Scripts/Test.cs' }
        }),
        ok: true,
        status: 200
      };
      mockFetch.mockResolvedValue(mockResponse as any);

      const params = {
        fileName: 'Test',
        content: 'public class Test {}',
        folder: 'Assets/Scripts'
      };

      // Act
      await adapter.call('script/create', params);

      // Assert
      expect(mockFetch).toHaveBeenCalledWith(
        'http://localhost:23457/',
        expect.objectContaining({
          body: JSON.stringify({ method: 'script/create', ...params })
        })
      );
    });

    it('should throw error when success is false', async () => {
      // Arrange
      const mockResponse = {
        json: vi.fn().mockResolvedValue({
          success: false,
          error: 'File not found'
        }),
        ok: true,
        status: 404
      };
      mockFetch.mockResolvedValue(mockResponse as any);

      // Act & Assert
      await expect(adapter.call('script/read', { path: 'missing.cs' }))
        .rejects.toThrow('File not found');
    });

    it('should handle connection refused error', async () => {
      // Arrange
      mockFetch.mockRejectedValue(new Error('ECONNREFUSED'));

      // Act & Assert
      await expect(adapter.call('ping'))
        .rejects.toThrow('Unity HTTP server is not running');
    });

    it('should handle timeout', async () => {
      // Arrange
      const abortError = new Error('The operation was aborted');
      (abortError as any).name = 'AbortError';
      mockFetch.mockRejectedValue(abortError);

      // Act & Assert
      await expect(adapter.call('ping'))
        .rejects.toThrow('Request timeout');
    });
  });

  describe('isConnected', () => {
    it('should return true when ping succeeds', async () => {
      // Arrange
      const mockResponse = {
        json: vi.fn().mockResolvedValue({
          success: true,
          result: { status: 'ok' }
        }),
        ok: true,
        status: 200
      };
      mockFetch.mockResolvedValue(mockResponse as any);

      // Act
      const connected = await adapter.isConnected();

      // Assert
      expect(connected).toBe(true);
    });

    it('should return false when ping fails', async () => {
      // Arrange
      mockFetch.mockRejectedValue(new Error('Connection failed'));

      // Act
      const connected = await adapter.isConnected();

      // Assert
      expect(connected).toBe(false);
    });
  });

  describe('script operations', () => {
    it('should create script with correct parameters', async () => {
      // Arrange
      const mockResponse = {
        json: vi.fn().mockResolvedValue({
          success: true,
          result: { path: 'Assets/Scripts/Test.cs', guid: 'test-guid' }
        }),
        ok: true,
        status: 200
      };
      mockFetch.mockResolvedValue(mockResponse as any);

      // Act
      const result = await adapter.createScript('Test', 'public class Test {}', 'Assets/Scripts');

      // Assert
      expect(mockFetch).toHaveBeenCalledWith(
        'http://localhost:23457/',
        expect.objectContaining({
          body: JSON.stringify({
            method: 'script/create',
            fileName: 'Test',
            content: 'public class Test {}',
            folder: 'Assets/Scripts'
          })
        })
      );
      expect(result).toEqual({ path: 'Assets/Scripts/Test.cs', guid: 'test-guid' });
    });

    it('should read script content', async () => {
      // Arrange
      const mockResponse = {
        json: vi.fn().mockResolvedValue({
          success: true,
          result: {
            path: 'Assets/Scripts/Test.cs',
            content: 'public class Test {}',
            guid: 'test-guid'
          }
        }),
        ok: true,
        status: 200
      };
      mockFetch.mockResolvedValue(mockResponse as any);

      // Act
      const result = await adapter.readScript('Assets/Scripts/Test.cs');

      // Assert
      expect(result.content).toBe('public class Test {}');
    });

    it('should delete script', async () => {
      // Arrange
      const mockResponse = {
        json: vi.fn().mockResolvedValue({
          success: true,
          result: { message: 'Script deleted successfully' }
        }),
        ok: true,
        status: 200
      };
      mockFetch.mockResolvedValue(mockResponse as any);

      // Act
      const result = await adapter.deleteScript('Assets/Scripts/Test.cs');

      // Assert
      expect(result.message).toBe('Script deleted successfully');
    });
  });

  describe('shader operations', () => {
    it('should create shader', async () => {
      // Arrange
      const mockResponse = {
        json: vi.fn().mockResolvedValue({
          success: true,
          result: { path: 'Assets/Shaders/Test.shader', guid: 'shader-guid' }
        }),
        ok: true,
        status: 200
      };
      mockFetch.mockResolvedValue(mockResponse as any);

      // Act
      const result = await adapter.createShader('Test', 'Shader "Custom/Test" {}', 'Assets/Shaders');

      // Assert
      expect(result.path).toBe('Assets/Shaders/Test.shader');
    });
  });

  describe('project operations', () => {
    it('should get project info', async () => {
      // Arrange
      const mockResponse = {
        json: vi.fn().mockResolvedValue({
          success: true,
          result: {
            projectPath: '/Users/test/UnityProject',
            unityVersion: '2022.3.0f1',
            platform: 'StandaloneOSX'
          }
        }),
        ok: true,
        status: 200
      };
      mockFetch.mockResolvedValue(mockResponse as any);

      // Act
      const result = await adapter.getProjectInfo();

      // Assert
      expect(result.unityVersion).toBe('2022.3.0f1');
    });
  });
});
```

--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------

```markdown
# Changelog

All notable changes to Unity MCP Server will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [3.1.1] - 2025-07-10

### Fixed
- **Render Pipeline Detection**: Fixed "Unknown" render pipeline issue
  - `project/info` now correctly runs on main thread for Unity API access
  - Enhanced render pipeline detection with GraphicsSettings.renderPipelineAsset
  - Added fallback to package detection when no render pipeline asset is configured
  - Improved debug logging for render pipeline detection troubleshooting
- **AssetDatabase Synchronization**: Fixed "[Worker0] Import Error Code:(4)" errors
  - Added proper file cleanup before script deployment
  - Implemented removeExistingFiles method to delete both .cs and .meta files
  - Enhanced UnityBridgeDeployService with proper file lifecycle management
  - Eliminated modification time mismatches between SourceAssetDB and disk

### Changed
- **Script Generation**: Unity scripts now generated dynamically from source files
  - `generate-embedded-scripts.cjs` creates embedded-scripts.ts from actual Unity C# files
  - Eliminates static embedded script content in favor of build-time generation
  - Ensures Unity scripts are always up-to-date with source modifications

## [3.1.0] - 2025-07-10

### Added
- Folder operations support:
  - `folder_create` - Create new folders in Unity project
  - `folder_rename` - Rename existing folders
  - `folder_move` - Move folders to new locations
  - `folder_delete` - Delete folders recursively
  - `folder_list` - List folder contents with optional recursion
- Automatic Unity MCP bridge script deployment:
  - `setup_unity_bridge` - Install/update Unity scripts automatically
  - Auto-deployment on first project info request
  - Version checking and auto-update support
- Unity control window (Window > Unity MCP Server) for server management
- Shader operations (simplified):
  - `shader_create` - Create new shaders with default template
  - `shader_read` - Read shader contents
  - `shader_delete` - Delete shaders
- **DXT Package Build System**:
  - `npm run build:dxt` - One-command DXT package creation
  - Embedded Unity C# scripts for elimination of file dependencies
  - Unified build system with TypeScript compilation and bundling

### Changed
- **DXT Package Optimization**: Reduced package size from 24MB to 41KB through embedded scripts
- **Package Name**: Changed final DXT filename from `unity-mcp-server-bundled.dxt` to `unity-mcp-server.dxt`
- **Build System**: Complete overhaul with automated bundling and packaging scripts
- **Script Deployment**: Unity C# scripts now embedded directly in TypeScript bundle
- Updated port from 3001 to 23457 for better conflict avoidance
- Improved UTF-8 BOM handling for Unity compatibility
- Simplified HTTP-based architecture for better reliability
- Enhanced error messages and logging with [UnityMCP] prefix
- Scripts now install to `Assets/Editor/MCP/` folder structure

### Fixed
- **UTF-8 BOM Encoding**: Fixed BOM generation using proper byte array `[0xEF, 0xBB, 0xBF]`
- **Module Resolution**: Eliminated all external file dependencies in DXT package
- **Process Lifecycle**: Added `process.stdin.resume()` to prevent early server shutdown
- Character encoding issues with UTF-8 BOM for Unity files
- Script deployment path handling for various Unity project structures
- Connection stability with retry logic

### Removed
- Obsolete shader template files (builtin-shader.ts, hdrp-shader.ts, urp-shader.ts, index.ts)
- External file dependencies in DXT package

### Documentation
- Updated README files with correct repository name (mcp-server-unity)
- Added comprehensive usage examples for all operations
- Improved setup instructions with automatic and manual options
- Added BUILD.md with complete build system documentation

## [3.0.0] - 2025-06-08

### Changed
- Complete rewrite with simplified HTTP-based architecture
- Removed complex service layer in favor of direct API implementation
- Industry-standard diff processing for script updates
- Streamlined to essential features only

### Added
- Desktop Extension support with bundled configuration
- Large file support (up to 1GB) with streaming
- Diff-based script update system (`script_apply_diff`)
- Comprehensive test coverage (100%)

### Removed
- Legacy service-based architecture
- Complex material and shader management
- Compilation monitoring features
- ProBuilder and package management

## [2.2.0] - 2025-06-06

### Added
- Shader and material update features:
  - `asset_update_shader` - Update existing shader content with automatic temporary backup
  - `asset_read_shader` - Read shader file content (supports both code and ShaderGraph)
  - `asset_update_material` - Update entire material content with YAML validation
  - `asset_clone_material` - Clone material with a new name
  - `asset_list_materials` - List all materials in the project
- Enhanced shader service:
  - Shader GUID caching for faster lookups
  - Shader name detection from file content
  - Support for finding shaders by file name or internal shader name
- Temporary backup system:
  - Creates backup files only during update operations
  - Automatically cleans up backup files after success or failure
  - Restores original content on update failure

### Changed
- Backup system now uses temporary files with automatic cleanup
- Improved shader lookup to check both file names and shader declarations
- Enhanced error handling with try-finally blocks for resource cleanup

### Fixed
- Fixed shader-material GUID reference issues for custom shaders
- Added UnityMetaGenerator for proper meta file creation
- Improved material service to work with custom shader GUIDs

## [2.1.0] - 2025-06-06

### Added
- Material management features:
  - `asset_update_material_shader` - Change material shaders dynamically
  - `asset_update_material_properties` - Update material colors, floats, textures, vectors
  - `asset_read_material` - Read and inspect material properties
  - `asset_batch_convert_materials` - Batch convert materials to different shaders
- Script update functionality:
  - `asset_update_script` - Update existing script content
- Code analysis tools:
  - `code_analyze_diff` - Get detailed diff between current and new content
  - `code_detect_duplicates` - Detect duplicate class names across project
  - `code_suggest_namespace` - Auto-suggest namespace based on file location
  - `code_apply_namespace` - Apply namespace to scripts automatically
- Compilation monitoring:
  - `compile_get_errors` - Get detailed compilation errors with file context
  - `compile_get_status` - Check current compilation status
  - `compile_install_helper` - Install real-time compilation monitoring
- Render pipeline detection:
  - Automatic detection of Built-in, URP, or HDRP
  - Material creation uses correct default shader for detected pipeline

### Changed
- Material creation now detects render pipeline and uses appropriate shader
- Improved YAML parsing to handle Unity's custom tags (!u!21 &2100000)
- Enhanced project info to include render pipeline information

### Fixed
- Fixed URP detection incorrectly showing as Built-in
- Fixed YAML parsing errors for Unity material files
- Fixed material shader GUID handling

### Removed
- Removed ProBuilder service (non-functional)
- Removed package management service (non-functional)
- Removed backup functionality from all services

## [1.0.0] - 2025-06-02

### Added
- Initial release of Unity MCP Server
- Core MCP server implementation with stdio transport
- Unity project management tools:
  - `set_unity_project` - Set and validate Unity project path
  - `create_script` - Create C# scripts with folder support
  - `read_script` - Read C# scripts with recursive search
  - `list_scripts` - List all scripts in the project
  - `create_scene` - Create Unity scene files with YAML template
  - `create_material` - Create Unity material files
  - `list_assets` - List and filter project assets
  - `project_info` - Get Unity version and project statistics
  - `build_project` - Build Unity projects via command line
- Cross-platform support (Windows, macOS, Linux)
- TypeScript implementation with strict typing
- Comprehensive error handling and validation
- Setup scripts for easy installation
- GitHub Actions CI/CD pipeline
- Full documentation and contribution guidelines

### Security
- Path traversal protection
- Input validation for all tools
- Safe error messages without sensitive information

[1.0.0]: https://github.com/zabaglione/mcp-server-unity/releases/tag/v1.0.0
```

--------------------------------------------------------------------------------
/src/tools/unity-mcp-tools.ts:
--------------------------------------------------------------------------------

```typescript
import { Tool } from '@modelcontextprotocol/sdk/types.js';
import { UnityHttpAdapter } from '../adapters/unity-http-adapter.js';
import { UnityBridgeDeployService } from '../services/unity-bridge-deploy-service.js';

/**
 * Unity MCP Tools
 * Provides MCP tool definitions for Unity operations
 */
export class UnityMcpTools {
  private adapter: UnityHttpAdapter;
  private deployService: UnityBridgeDeployService;

  constructor() {
    const port = process.env.UNITY_MCP_PORT ? parseInt(process.env.UNITY_MCP_PORT) : 23457;
    const url = `http://localhost:${port}/`;
    console.error(`[Unity MCP] Connecting to Unity at ${url}`);
    
    this.adapter = new UnityHttpAdapter({ 
      url,
      timeout: parseInt(process.env.UNITY_MCP_TIMEOUT || '120000')
    });
    
    this.deployService = new UnityBridgeDeployService();
    
    // Check connection on startup
    this.checkConnection();
  }
  
  private async checkConnection() {
    try {
      const connected = await this.adapter.isConnected();
      if (connected) {
        console.error('[Unity MCP] Successfully connected to Unity HTTP server');
      } else {
        console.error('[Unity MCP] Unity HTTP server is not responding');
      }
    } catch (error: any) {
      console.error(`[Unity MCP] Connection check failed: ${error.message}`);
    }
  }
  
  /**
   * Auto-deploy Unity MCP scripts if connected
   */
  private async autoDeployScripts(): Promise<void> {
    try {
      const result = await this.adapter.getProjectInfo();
      await this.deployService.deployScripts({ 
        projectPath: result.projectPath,
        forceUpdate: false 
      });
    } catch (error: any) {
      console.error(`[Unity MCP] Failed to auto-deploy scripts: ${error.message}`);
    }
  }

  /**
   * Get all available tools
   */
  getTools(): Tool[] {
    return [
      // Script tools
      {
        name: 'script_create',
        description: 'Create a new C# script in Unity project',
        inputSchema: {
          type: 'object',
          properties: {
            fileName: {
              type: 'string',
              description: 'Name of the script file (without .cs extension)'
            },
            content: {
              type: 'string',
              description: 'Script content (optional, will use template if not provided)'
            },
            folder: {
              type: 'string',
              description: 'Target folder path (default: Assets/Scripts)'
            }
          },
          required: ['fileName']
        }
      },
      {
        name: 'script_read',
        description: 'Read a C# script from Unity project',
        inputSchema: {
          type: 'object',
          properties: {
            path: {
              type: 'string',
              description: 'Path to the script file'
            }
          },
          required: ['path']
        }
      },
      {
        name: 'script_delete',
        description: 'Delete a C# script from Unity project',
        inputSchema: {
          type: 'object',
          properties: {
            path: {
              type: 'string',
              description: 'Path to the script file'
            }
          },
          required: ['path']
        }
      },
      {
        name: 'script_apply_diff',
        description: 'Apply a unified diff to a C# script',
        inputSchema: {
          type: 'object',
          properties: {
            path: {
              type: 'string',
              description: 'Path to the script file'
            },
            diff: {
              type: 'string',
              description: 'Unified diff content to apply'
            },
            options: {
              type: 'object',
              description: 'Optional diff application settings',
              properties: {
                dryRun: {
                  type: 'boolean',
                  description: 'Preview changes without applying (default: false)'
                }
              }
            }
          },
          required: ['path', 'diff']
        }
      },
      
      // Shader tools
      {
        name: 'shader_create',
        description: 'Create a new shader in Unity project',
        inputSchema: {
          type: 'object',
          properties: {
            name: {
              type: 'string',
              description: 'Name of the shader (without .shader extension)'
            },
            content: {
              type: 'string',
              description: 'Shader content (optional, will use template if not provided)'
            },
            folder: {
              type: 'string',
              description: 'Target folder path (default: Assets/Shaders)'
            }
          },
          required: ['name']
        }
      },
      {
        name: 'shader_read',
        description: 'Read a shader from Unity project',
        inputSchema: {
          type: 'object',
          properties: {
            path: {
              type: 'string',
              description: 'Path to the shader file'
            }
          },
          required: ['path']
        }
      },
      {
        name: 'shader_delete',
        description: 'Delete a shader from Unity project',
        inputSchema: {
          type: 'object',
          properties: {
            path: {
              type: 'string',
              description: 'Path to the shader file'
            }
          },
          required: ['path']
        }
      },
      
      // Project tools
      {
        name: 'project_info',
        description: 'Get comprehensive Unity project information including render pipeline details, project path, Unity version, and platform info',
        inputSchema: {
          type: 'object',
          properties: {}
        }
      },
      {
        name: 'project_status',
        description: 'Check Unity MCP server connection status (simple connectivity test only)',
        inputSchema: {
          type: 'object',
          properties: {}
        }
      },
      {
        name: 'setup_unity_bridge',
        description: 'Install/update Unity MCP bridge scripts to a Unity project (works even if Unity server is not running)',
        inputSchema: {
          type: 'object',
          properties: {
            projectPath: {
              type: 'string',
              description: 'Path to the Unity project'
            },
            forceUpdate: {
              type: 'boolean',
              description: 'Force update even if scripts are up to date',
              default: false
            }
          },
          required: ['projectPath']
        }
      },
      
      // Folder tools
      {
        name: 'folder_create',
        description: 'Create a new folder in Unity project',
        inputSchema: {
          type: 'object',
          properties: {
            path: {
              type: 'string',
              description: 'Path for the new folder (e.g., Assets/MyFolder)'
            }
          },
          required: ['path']
        }
      },
      {
        name: 'folder_rename',
        description: 'Rename a folder in Unity project',
        inputSchema: {
          type: 'object',
          properties: {
            oldPath: {
              type: 'string',
              description: 'Current path of the folder'
            },
            newName: {
              type: 'string',
              description: 'New name for the folder'
            }
          },
          required: ['oldPath', 'newName']
        }
      },
      {
        name: 'folder_move',
        description: 'Move a folder to a new location in Unity project',
        inputSchema: {
          type: 'object',
          properties: {
            sourcePath: {
              type: 'string',
              description: 'Current path of the folder'
            },
            targetPath: {
              type: 'string',
              description: 'Target path for the folder'
            }
          },
          required: ['sourcePath', 'targetPath']
        }
      },
      {
        name: 'folder_delete',
        description: 'Delete a folder from Unity project',
        inputSchema: {
          type: 'object',
          properties: {
            path: {
              type: 'string',
              description: 'Path of the folder to delete'
            },
            recursive: {
              type: 'boolean',
              description: 'Delete all contents recursively (default: true)'
            }
          },
          required: ['path']
        }
      },
      {
        name: 'folder_list',
        description: 'List contents of a folder in Unity project',
        inputSchema: {
          type: 'object',
          properties: {
            path: {
              type: 'string',
              description: 'Path of the folder to list (default: Assets)'
            },
            recursive: {
              type: 'boolean',
              description: 'List all subdirectories recursively (default: false)'
            }
          }
        }
      }
    ];
  }

  /**
   * Execute a tool
   */
  async executeTool(toolName: string, args: any): Promise<{ content: Array<{ type: string; text: string }> }> {
    try {
      switch (toolName) {
        // Script operations
        case 'script_create': {
          if (!args.fileName) {
            throw new Error('fileName is required');
          }
          const result = await this.adapter.createScript(args.fileName, args.content, args.folder);
          return {
            content: [{
              type: 'text',
              text: `Script created successfully:\nPath: ${result.path}\nGUID: ${result.guid}`
            }]
          };
        }
        
        case 'script_read': {
          if (!args.path) {
            throw new Error('path is required');
          }
          const result = await this.adapter.readScript(args.path);
          return {
            content: [{
              type: 'text',
              text: `Script content from ${result.path}:\n\n${result.content}`
            }]
          };
        }
        
        case 'script_delete': {
          if (!args.path) {
            throw new Error('path is required');
          }
          await this.adapter.deleteScript(args.path);
          return {
            content: [{
              type: 'text',
              text: `Script deleted successfully: ${args.path}`
            }]
          };
        }
        
        case 'script_apply_diff': {
          if (!args.path || !args.diff) {
            throw new Error('path and diff are required');
          }
          const result = await this.adapter.applyDiff(args.path, args.diff, args.options);
          return {
            content: [{
              type: 'text',
              text: `Diff applied successfully:\nPath: ${result.path}\nLines added: ${result.linesAdded}\nLines removed: ${result.linesRemoved}`
            }]
          };
        }
        
        // Shader operations
        case 'shader_create': {
          if (!args.name) {
            throw new Error('name is required');
          }
          const result = await this.adapter.createShader(args.name, args.content, args.folder);
          return {
            content: [{
              type: 'text',
              text: `Shader created successfully:\nPath: ${result.path}\nGUID: ${result.guid}`
            }]
          };
        }
        
        case 'shader_read': {
          if (!args.path) {
            throw new Error('path is required');
          }
          const result = await this.adapter.readShader(args.path);
          return {
            content: [{
              type: 'text',
              text: `Shader content from ${result.path}:\n\n${result.content}`
            }]
          };
        }
        
        case 'shader_delete': {
          if (!args.path) {
            throw new Error('path is required');
          }
          await this.adapter.deleteShader(args.path);
          return {
            content: [{
              type: 'text',
              text: `Shader deleted successfully: ${args.path}`
            }]
          };
        }
        
        // Project operations
        case 'project_info': {
          const result = await this.adapter.getProjectInfo();
          
          // Auto-deploy scripts if needed
          await this.autoDeployScripts();
          
          return {
            content: [{
              type: 'text',
              text: `Unity Project Information:
Project Path: ${result.projectPath}
Project Name: ${result.projectName || 'N/A'}
Unity Version: ${result.unityVersion}
Platform: ${result.platform}
Is Playing: ${result.isPlaying}
Render Pipeline: ${result.renderPipeline || 'Unknown'}
Render Pipeline Version: ${result.renderPipelineVersion || 'N/A'}`
            }]
          };
        }
        
        case 'project_status': {
          const connected = await this.adapter.isConnected();
          const status = connected ? 'Unity server is connected and ready' : 'Unity server is not connected or not responding';
          
          return {
            content: [{
              type: 'text',
              text: status
            }]
          };
        }
        
        case 'setup_unity_bridge': {
          const { projectPath, forceUpdate } = args;
          if (!projectPath) {
            throw new Error('projectPath is required');
          }
          
          try {
            await this.deployService.deployScripts({ projectPath, forceUpdate });
            return {
              content: [{
                type: 'text',
                text: `Unity MCP bridge scripts installed successfully to:\n${projectPath}/Assets/Editor/MCP/\n\nPlease restart Unity Editor or open Window > Unity MCP Server to start the server.`
              }]
            };
          } catch (error: any) {
            throw new Error(`Failed to install scripts: ${error.message}`);
          }
        }
        
        // Folder operations
        case 'folder_create': {
          if (!args.path) {
            throw new Error('path is required');
          }
          const result = await this.adapter.createFolder(args.path);
          return {
            content: [{
              type: 'text',
              text: `Folder created successfully:\nPath: ${result.path}\nGUID: ${result.guid}`
            }]
          };
        }
        
        case 'folder_rename': {
          if (!args.oldPath || !args.newName) {
            throw new Error('oldPath and newName are required');
          }
          const result = await this.adapter.renameFolder(args.oldPath, args.newName);
          return {
            content: [{
              type: 'text',
              text: `Folder renamed successfully:\nOld Path: ${result.oldPath}\nNew Path: ${result.newPath}\nGUID: ${result.guid}`
            }]
          };
        }
        
        case 'folder_move': {
          if (!args.sourcePath || !args.targetPath) {
            throw new Error('sourcePath and targetPath are required');
          }
          const result = await this.adapter.moveFolder(args.sourcePath, args.targetPath);
          return {
            content: [{
              type: 'text',
              text: `Folder moved successfully:\nFrom: ${result.sourcePath}\nTo: ${result.targetPath}\nGUID: ${result.guid}`
            }]
          };
        }
        
        case 'folder_delete': {
          if (!args.path) {
            throw new Error('path is required');
          }
          await this.adapter.deleteFolder(args.path, args.recursive);
          return {
            content: [{
              type: 'text',
              text: `Folder deleted successfully: ${args.path}`
            }]
          };
        }
        
        case 'folder_list': {
          const result = await this.adapter.listFolder(args.path, args.recursive);
          const entries = result.entries.map(e => {
            const prefix = e.type === 'folder' ? '📁' : '📄';
            const info = e.type === 'file' ? ` (${e.extension})` : '';
            return `${prefix} ${e.name}${info} - ${e.path}`;
          }).join('\n');
          
          return {
            content: [{
              type: 'text',
              text: `Contents of ${result.path}:\n\n${entries || '(empty)'}`
            }]
          };
        }
        
        default:
          throw new Error(`Unknown tool: ${toolName}`);
      }
    } catch (error: any) {
      return {
        content: [{
          type: 'text',
          text: `Error: ${error.message}`
        }]
      };
    }
  }
}
```

--------------------------------------------------------------------------------
/src/unity-scripts/UnityHttpServer.cs:
--------------------------------------------------------------------------------

```csharp
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using UnityEngine;
using UnityEditor;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace UnityMCP
{
    [InitializeOnLoad]
    public static class UnityMCPInstaller
    {
        static UnityMCPInstaller()
        {
            CheckAndUpdateScripts();
        }
        
        static void CheckAndUpdateScripts()
        {
            var installedVersion = EditorPrefs.GetString(UnityHttpServer.VERSION_META_KEY, "0.0.0");
            if (installedVersion != UnityHttpServer.SCRIPT_VERSION)
            {
                Debug.Log($"[UnityMCP] Updating Unity MCP scripts from version {installedVersion} to {UnityHttpServer.SCRIPT_VERSION}");
                // Version update logic will be handled by the MCP server
                EditorPrefs.SetString(UnityHttpServer.VERSION_META_KEY, UnityHttpServer.SCRIPT_VERSION);
            }
        }
    }
    /// <summary>
    /// Simple HTTP server for Unity MCP integration
    /// </summary>
    public static class UnityHttpServer
    {
        // Version information for auto-update
        public const string SCRIPT_VERSION = "1.1.0";
        public const string VERSION_META_KEY = "UnityMCP.InstalledVersion";
        
        // Configuration constants
        private const int DEFAULT_PORT = 23457;
        private const int REQUEST_TIMEOUT_MS = 120000; // 2 minutes
        private const int THREAD_JOIN_TIMEOUT_MS = 1000; // 1 second
        private const int ASSET_REFRESH_DELAY_MS = 500; // Wait after asset operations
        public const string SERVER_LOG_PREFIX = "[UnityMCP]";
        private const string PREFS_PORT_KEY = "UnityMCP.ServerPort";
        private const string PREFS_PORT_BEFORE_PLAY_KEY = "UnityMCP.ServerPortBeforePlay";
        
        // File path constants
        private const string ASSETS_PREFIX = "Assets/";
        private const int ASSETS_PREFIX_LENGTH = 7;
        private const string DEFAULT_SCRIPTS_FOLDER = "Assets/Scripts";
        private const string DEFAULT_SHADERS_FOLDER = "Assets/Shaders";
        private const string CS_EXTENSION = ".cs";
        private const string SHADER_EXTENSION = ".shader";
        
        private static HttpListener httpListener;
        private static Thread listenerThread;
        private static bool isRunning = false;
        
        // Request queue for serialization
        private static readonly Queue<Action> requestQueue = new Queue<Action>();
        private static bool isProcessingRequest = false;
        private static int currentPort = DEFAULT_PORT;
        
        /// <summary>
        /// Gets whether the server is currently running
        /// </summary>
        public static bool IsRunning => isRunning;
        
        /// <summary>
        /// Gets the current port the server is running on
        /// </summary>
        public static int CurrentPort => currentPort;
        
        [InitializeOnLoad]
        static class AutoShutdown
        {
            static AutoShutdown()
            {
                EditorApplication.playModeStateChanged += OnPlayModeChanged;
                EditorApplication.quitting += Shutdown;
                
                // Handle script recompilation
                UnityEditor.Compilation.CompilationPipeline.compilationStarted += OnCompilationStarted;
                UnityEditor.Compilation.CompilationPipeline.compilationFinished += OnCompilationFinished;
                
                // Auto-start server on Unity startup
                EditorApplication.delayCall += () => {
                    if (!isRunning)
                    {
                        var savedPort = EditorPrefs.GetInt(PREFS_PORT_KEY, DEFAULT_PORT);
                        Debug.Log($"{SERVER_LOG_PREFIX} Auto-starting server on port {savedPort}");
                        Start(savedPort);
                    }
                };
            }
            
            static void OnCompilationStarted(object obj)
            {
                Debug.Log($"{SERVER_LOG_PREFIX} Compilation started - stopping server");
                if (isRunning)
                {
                    Shutdown();
                }
            }
            
            static void OnCompilationFinished(object obj)
            {
                Debug.Log($"{SERVER_LOG_PREFIX} Compilation finished - auto-restarting server");
                // Always auto-restart after compilation
                var savedPort = EditorPrefs.GetInt(PREFS_PORT_KEY, DEFAULT_PORT);
                EditorApplication.delayCall += () => Start(savedPort);
            }
        }
        
        /// <summary>
        /// Start the HTTP server on the specified port
        /// </summary>
        /// <param name="port">Port to listen on</param>
        public static void Start(int port = DEFAULT_PORT)
        {
            if (isRunning) 
            {
                Debug.LogWarning($"{SERVER_LOG_PREFIX} Server is already running. Stop it first.");
                return;
            }
            
            currentPort = port;
            
            try
            {
                httpListener = new HttpListener();
                httpListener.Prefixes.Add($"http://localhost:{currentPort}/");
                httpListener.Start();
                isRunning = true;
                
                listenerThread = new Thread(ListenLoop) 
                { 
                    IsBackground = true,
                    Name = "UnityMCPHttpListener"
                };
                listenerThread.Start();
                
                Debug.Log($"{SERVER_LOG_PREFIX} HTTP Server started on port {currentPort}");
            }
            catch (Exception e)
            {
                isRunning = false;
                Debug.LogError($"{SERVER_LOG_PREFIX} Failed to start HTTP server: {e.Message}");
                throw;
            }
        }
        
        /// <summary>
        /// Stop the HTTP server
        /// </summary>
        public static void Shutdown()
        {
            if (!isRunning)
            {
                Debug.LogWarning($"{SERVER_LOG_PREFIX} Server is not running.");
                return;
            }
            
            isRunning = false;
            
            try
            {
                httpListener?.Stop();
                httpListener?.Close();
                listenerThread?.Join(THREAD_JOIN_TIMEOUT_MS);
                Debug.Log($"{SERVER_LOG_PREFIX} HTTP Server stopped");
            }
            catch (Exception e)
            {
                Debug.LogError($"{SERVER_LOG_PREFIX} Error during shutdown: {e.Message}");
            }
            finally
            {
                httpListener = null;
                listenerThread = null;
            }
        }
        
        static void OnPlayModeChanged(PlayModeStateChange state)
        {
            // Stop server when entering play mode to avoid conflicts
            if (state == PlayModeStateChange.ExitingEditMode)
            {
                if (isRunning)
                {
                    Debug.Log($"{SERVER_LOG_PREFIX} Stopping server due to play mode change");
                    EditorPrefs.SetInt(PREFS_PORT_BEFORE_PLAY_KEY, currentPort);
                    Shutdown();
                }
            }
            // Restart server when returning to edit mode
            else if (state == PlayModeStateChange.EnteredEditMode)
            {
                var savedPort = EditorPrefs.GetInt(PREFS_PORT_BEFORE_PLAY_KEY, DEFAULT_PORT);
                Debug.Log($"{SERVER_LOG_PREFIX} Restarting server after play mode on port {savedPort}");
                EditorApplication.delayCall += () => Start(savedPort);
            }
        }
        
        static void ListenLoop()
        {
            while (isRunning)
            {
                try
                {
                    var context = httpListener.GetContext();
                    ThreadPool.QueueUserWorkItem(_ => HandleRequest(context));
                }
                catch (Exception e)
                {
                    if (isRunning)
                        Debug.LogError($"{SERVER_LOG_PREFIX} Listen error: {e.Message}");
                }
            }
        }
        
        static void HandleRequest(HttpListenerContext context)
        {
            var request = context.Request;
            var response = context.Response;
            response.Headers.Add("Access-Control-Allow-Origin", "*");
            
            try
            {
                if (request.HttpMethod != "POST")
                {
                    SendResponse(response, 405, false, null, "Method not allowed");
                    return;
                }
                
                string requestBody;
                // Force UTF-8 encoding for request body
                using (var reader = new StreamReader(request.InputStream, Encoding.UTF8))
                {
                    requestBody = reader.ReadToEnd();
                }
                
                var requestData = JObject.Parse(requestBody);
                var method = requestData["method"]?.ToString();
                
                if (string.IsNullOrEmpty(method))
                {
                    SendResponse(response, 400, false, null, "Method is required");
                    return;
                }
                
                Debug.Log($"{SERVER_LOG_PREFIX} Processing request: {method}");
                
                // Check if this request requires main thread
                bool requiresMainThread = RequiresMainThread(method);
                
                if (!requiresMainThread)
                {
                    // Process directly on worker thread
                    try
                    {
                        var result = ProcessRequestOnWorkerThread(method, requestData);
                        SendResponse(response, 200, true, result, null);
                    }
                    catch (Exception e)
                    {
                        var statusCode = e is ArgumentException ? 400 : 500;
                        SendResponse(response, statusCode, false, null, e.Message);
                    }
                }
                else
                {
                    // Execute on main thread for Unity API calls
                    object result = null;
                    Exception error = null;
                    var resetEvent = new ManualResetEvent(false);
                    
                    EditorApplication.delayCall += () =>
                    {
                        try
                        {
                            Debug.Log($"{SERVER_LOG_PREFIX} Processing on main thread: {method}");
                            result = ProcessRequest(method, requestData);
                            Debug.Log($"{SERVER_LOG_PREFIX} Completed processing: {method}");
                        }
                        catch (Exception e)
                        {
                            error = e;
                            Debug.LogError($"{SERVER_LOG_PREFIX} Error processing {method}: {e.Message}");
                        }
                        finally
                        {
                            resetEvent.Set();
                        }
                    };
                    
                    if (!resetEvent.WaitOne(REQUEST_TIMEOUT_MS))
                    {
                        SendResponse(response, 504, false, null, "Request timeout - Unity may be busy or unfocused");
                        return;
                    }
                    
                    if (error != null)
                    {
                        var statusCode = error is ArgumentException ? 400 : 500;
                        SendResponse(response, statusCode, false, null, error.Message);
                        return;
                    }
                    
                    SendResponse(response, 200, true, result, null);
                }
            }
            catch (Exception e)
            {
                SendResponse(response, 400, false, null, $"Bad request: {e.Message}");
            }
        }
        
        static bool RequiresMainThread(string method)
        {
            // These methods can run on worker thread
            switch (method)
            {
                case "ping":
                case "script/read":
                case "shader/read":
                    return false;
                    
                // project/info now requires Unity API for render pipeline detection
                // Creating, deleting files require Unity API (AssetDatabase)
                default:
                    return true;
            }
        }
        
        static object ProcessRequestOnWorkerThread(string method, JObject request)
        {
            switch (method)
            {
                case "ping":
                    return new { status = "ok", time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") };
                    
                case "project/info":
                    // project/info requires Unity API for render pipeline detection
                    throw new NotImplementedException("project/info requires main thread for render pipeline detection");
                    
                case "script/read":
                    return ReadScriptOnWorkerThread(request);
                    
                case "shader/read":
                    return ReadShaderOnWorkerThread(request);
                    
                // Folder operations (can run on worker thread)
                case "folder/create":
                    return CreateFolderOnWorkerThread(request);
                case "folder/rename":
                    return RenameFolderOnWorkerThread(request);
                case "folder/move":
                    return MoveFolderOnWorkerThread(request);
                case "folder/delete":
                    return DeleteFolderOnWorkerThread(request);
                case "folder/list":
                    return ListFolderOnWorkerThread(request);
                    
                default:
                    throw new NotImplementedException($"Method not implemented for worker thread: {method}");
            }
        }
        
        static object ReadScriptOnWorkerThread(JObject request)
        {
            var path = request["path"]?.ToString();
            if (string.IsNullOrEmpty(path))
                throw new ArgumentException("path is required");
            
            var fullPath = Path.Combine(Application.dataPath, path.Substring(ASSETS_PREFIX_LENGTH));
            if (!File.Exists(fullPath))
                throw new FileNotFoundException($"File not found: {path}");
            
            return new
            {
                path = path,
                content = File.ReadAllText(fullPath, new UTF8Encoding(true)),
                guid = "" // GUID requires AssetDatabase, skip in worker thread
            };
        }
        
        static object ReadShaderOnWorkerThread(JObject request)
        {
            var path = request["path"]?.ToString();
            if (string.IsNullOrEmpty(path))
                throw new ArgumentException("path is required");
            
            var fullPath = Path.Combine(Application.dataPath, path.Substring(ASSETS_PREFIX_LENGTH));
            if (!File.Exists(fullPath))
                throw new FileNotFoundException($"File not found: {path}");
            
            return new
            {
                path = path,
                content = File.ReadAllText(fullPath, new UTF8Encoding(true)),
                guid = "" // GUID requires AssetDatabase, skip in worker thread
            };
        }
        
        static object ProcessRequest(string method, JObject request)
        {
            switch (method)
            {
                case "ping":
                    return new { status = "ok", time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") };
                
                // Script operations
                case "script/create":
                    return CreateScript(request);
                case "script/read":
                    return ReadScript(request);
                case "script/delete":
                    return DeleteScript(request);
                case "script/applyDiff":
                    return ApplyDiff(request);
                
                // Shader operations
                case "shader/create":
                    return CreateShader(request);
                case "shader/read":
                    return ReadShader(request);
                case "shader/delete":
                    return DeleteShader(request);
                
                // Project operations
                case "project/info":
                    return GetProjectInfo();
                
                // Folder operations
                case "folder/create":
                    return CreateFolder(request);
                case "folder/rename":
                    return RenameFolder(request);
                case "folder/move":
                    return MoveFolder(request);
                case "folder/delete":
                    return DeleteFolder(request);
                case "folder/list":
                    return ListFolder(request);
                
                default:
                    throw new NotImplementedException($"Method not found: {method}");
            }
        }
        
        static object CreateScript(JObject request)
        {
            var fileName = request["fileName"]?.ToString();
            if (string.IsNullOrEmpty(fileName))
                throw new ArgumentException("fileName is required");
            
            if (!fileName.EndsWith(CS_EXTENSION))
                fileName += CS_EXTENSION;
            
            var content = request["content"]?.ToString();
            var folder = request["folder"]?.ToString() ?? DEFAULT_SCRIPTS_FOLDER;
            
            var path = Path.Combine(folder, fileName);
            var directory = Path.GetDirectoryName(path);
            
            // Create directory if needed
            if (!AssetDatabase.IsValidFolder(directory))
            {
                CreateFolderRecursive(directory);
            }
            
            // Use Unity-safe file creation approach
            var scriptContent = content ?? GetDefaultScriptContent(fileName);
            
            // First, ensure the asset doesn't already exist
            if (AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(path) != null)
            {
                throw new InvalidOperationException($"Asset already exists: {path}");
            }
            
            // Write file using UTF-8 with BOM (Unity standard)
            var fullPath = Path.Combine(Application.dataPath, path.Substring(ASSETS_PREFIX_LENGTH));
            var utf8WithBom = new UTF8Encoding(true);
            File.WriteAllText(fullPath, scriptContent, utf8WithBom);
            
            // Import the asset immediately and wait for completion
            AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate);
            
            // Verify the asset was imported successfully
            var attempts = 0;
            const int maxAttempts = 10;
            while (AssetDatabase.AssetPathToGUID(path) == "" && attempts < maxAttempts)
            {
                System.Threading.Thread.Sleep(100);
                AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport);
                attempts++;
            }
            
            if (AssetDatabase.AssetPathToGUID(path) == "")
            {
                throw new InvalidOperationException($"Failed to import asset: {path}");
            }
            
            return new
            {
                path = path,
                guid = AssetDatabase.AssetPathToGUID(path)
            };
        }
        
        static object ReadScript(JObject request)
        {
            var path = request["path"]?.ToString();
            if (string.IsNullOrEmpty(path))
                throw new ArgumentException("path is required");
            
            var fullPath = Path.Combine(Application.dataPath, path.Substring(ASSETS_PREFIX_LENGTH));
            if (!File.Exists(fullPath))
                throw new FileNotFoundException($"File not found: {path}");
            
            return new
            {
                path = path,
                content = File.ReadAllText(fullPath, new UTF8Encoding(true)),
                guid = AssetDatabase.AssetPathToGUID(path)
            };
        }
        
        static object DeleteScript(JObject request)
        {
            var path = request["path"]?.ToString();
            if (string.IsNullOrEmpty(path))
                throw new ArgumentException("path is required");
            
            // Verify file exists before deletion
            var fullPath = Path.Combine(Application.dataPath, path.Substring(ASSETS_PREFIX_LENGTH));
            if (!File.Exists(fullPath))
                throw new FileNotFoundException($"File not found: {path}");
            
            // Delete using AssetDatabase
            if (!AssetDatabase.DeleteAsset(path))
                throw new InvalidOperationException($"Failed to delete: {path}");
            
            // Force immediate refresh
            AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport);
            
            // Wait for asset database to process deletion
            System.Threading.Thread.Sleep(ASSET_REFRESH_DELAY_MS);
            
            return new { message = "Script deleted successfully" };
        }
        
        static object ApplyDiff(JObject request)
        {
            var path = request["path"]?.ToString();
            var diff = request["diff"]?.ToString();
            var options = request["options"] as JObject;
            
            if (string.IsNullOrEmpty(path))
                throw new ArgumentException("path is required");
            if (string.IsNullOrEmpty(diff))
                throw new ArgumentException("diff is required");
            
            var fullPath = Path.Combine(Application.dataPath, path.Substring(ASSETS_PREFIX_LENGTH));
            if (!File.Exists(fullPath))
                throw new FileNotFoundException($"File not found: {path}");
            
            var dryRun = options?["dryRun"]?.Value<bool>() ?? false;
            
            // Read current content using UTF-8 with BOM (Unity standard)
            var utf8WithBom = new UTF8Encoding(true);
            var originalContent = File.ReadAllText(fullPath, utf8WithBom);
            var lines = originalContent.Split('\n').ToList();
            
            // Parse and apply unified diff
            var diffLines = diff.Split('\n');
            var linesAdded = 0;
            var linesRemoved = 0;
            var currentLine = 0;
            
            for (int i = 0; i < diffLines.Length; i++)
            {
                var line = diffLines[i];
                if (line.StartsWith("@@"))
                {
                    // Parse hunk header: @@ -l,s +l,s @@
                    var match = System.Text.RegularExpressions.Regex.Match(line, @"@@ -(\d+),?\d* \+(\d+),?\d* @@");
                    if (match.Success)
                    {
                        currentLine = int.Parse(match.Groups[1].Value) - 1;
                    }
                }
                else if (line.StartsWith("-") && !line.StartsWith("---"))
                {
                    // Remove line
                    if (currentLine < lines.Count)
                    {
                        lines.RemoveAt(currentLine);
                        linesRemoved++;
                    }
                }
                else if (line.StartsWith("+") && !line.StartsWith("+++"))
                {
                    // Add line
                    lines.Insert(currentLine, line.Substring(1));
                    currentLine++;
                    linesAdded++;
                }
                else if (line.StartsWith(" "))
                {
                    // Context line
                    currentLine++;
                }
            }
            
            // Write result if not dry run
            if (!dryRun)
            {
                var updatedContent = string.Join("\n", lines);
                // Write with UTF-8 with BOM (Unity standard)
                File.WriteAllText(fullPath, updatedContent, utf8WithBom);
                AssetDatabase.Refresh();
                
                // Wait for asset database to process
                System.Threading.Thread.Sleep(ASSET_REFRESH_DELAY_MS);
            }
            
            return new
            {
                path = path,
                linesAdded = linesAdded,
                linesRemoved = linesRemoved,
                dryRun = dryRun,
                guid = AssetDatabase.AssetPathToGUID(path)
            };
        }
        
        static object CreateShader(JObject request)
        {
            var name = request["name"]?.ToString();
            if (string.IsNullOrEmpty(name))
                throw new ArgumentException("name is required");
            
            if (!name.EndsWith(SHADER_EXTENSION))
                name += SHADER_EXTENSION;
            
            var content = request["content"]?.ToString();
            var folder = request["folder"]?.ToString() ?? DEFAULT_SHADERS_FOLDER;
            
            var path = Path.Combine(folder, name);
            var directory = Path.GetDirectoryName(path);
            
            if (!AssetDatabase.IsValidFolder(directory))
            {
                CreateFolderRecursive(directory);
            }
            
            // Use Unity-safe file creation approach
            var shaderContent = content ?? GetDefaultShaderContent(name);
            
            // First, ensure the asset doesn't already exist
            if (AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(path) != null)
            {
                throw new InvalidOperationException($"Asset already exists: {path}");
            }
            
            // Write file using UTF-8 with BOM (Unity standard)
            var fullPath = Path.Combine(Application.dataPath, path.Substring(ASSETS_PREFIX_LENGTH));
            var utf8WithBom = new UTF8Encoding(true);
            File.WriteAllText(fullPath, shaderContent, utf8WithBom);
            
            // Import the asset immediately and wait for completion
            AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate);
            
            // Verify the asset was imported successfully
            var attempts = 0;
            const int maxAttempts = 10;
            while (AssetDatabase.AssetPathToGUID(path) == "" && attempts < maxAttempts)
            {
                System.Threading.Thread.Sleep(100);
                AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport);
                attempts++;
            }
            
            if (AssetDatabase.AssetPathToGUID(path) == "")
            {
                throw new InvalidOperationException($"Failed to import asset: {path}");
            }
            
            return new
            {
                path = path,
                guid = AssetDatabase.AssetPathToGUID(path)
            };
        }
        
        static object ReadShader(JObject request)
        {
            var path = request["path"]?.ToString();
            if (string.IsNullOrEmpty(path))
                throw new ArgumentException("path is required");
            
            var fullPath = Path.Combine(Application.dataPath, path.Substring(ASSETS_PREFIX_LENGTH));
            if (!File.Exists(fullPath))
                throw new FileNotFoundException($"File not found: {path}");
            
            return new
            {
                path = path,
                content = File.ReadAllText(fullPath, new UTF8Encoding(true)),
                guid = AssetDatabase.AssetPathToGUID(path)
            };
        }
        
        static object DeleteShader(JObject request)
        {
            var path = request["path"]?.ToString();
            if (string.IsNullOrEmpty(path))
                throw new ArgumentException("path is required");
            
            if (!AssetDatabase.DeleteAsset(path))
                throw new InvalidOperationException($"Failed to delete: {path}");
            
            // Wait for asset database to process deletion
            System.Threading.Thread.Sleep(ASSET_REFRESH_DELAY_MS);
            
            return new { message = "Shader deleted successfully" };
        }
        
        static object GetProjectInfo()
        {
            // Detect render pipeline with multiple methods
            string renderPipeline = "Built-in";
            string renderPipelineVersion = "N/A";
            string detectionMethod = "Default";
            
            try
            {
                // Method 1: Check GraphicsSettings.renderPipelineAsset
                var renderPipelineAsset = UnityEngine.Rendering.GraphicsSettings.renderPipelineAsset;
                Debug.Log($"{SERVER_LOG_PREFIX} RenderPipelineAsset: {(renderPipelineAsset != null ? renderPipelineAsset.GetType().FullName : "null")}");
                
                if (renderPipelineAsset != null)
                {
                    var assetType = renderPipelineAsset.GetType();
                    var typeName = assetType.Name;
                    var fullTypeName = assetType.FullName;
                    
                    Debug.Log($"{SERVER_LOG_PREFIX} Asset type: {typeName}, Full type: {fullTypeName}");
                    
                    if (fullTypeName.Contains("Universal") || typeName.Contains("Universal") || 
                        fullTypeName.Contains("URP") || typeName.Contains("URP"))
                    {
                        renderPipeline = "URP";
                        detectionMethod = "GraphicsSettings.renderPipelineAsset";
                    }
                    else if (fullTypeName.Contains("HighDefinition") || typeName.Contains("HighDefinition") || 
                             fullTypeName.Contains("HDRP") || typeName.Contains("HDRP"))
                    {
                        renderPipeline = "HDRP";
                        detectionMethod = "GraphicsSettings.renderPipelineAsset";
                    }
                    else
                    {
                        renderPipeline = $"Custom ({typeName})";
                        detectionMethod = "GraphicsSettings.renderPipelineAsset";
                    }
                }
                else
                {
                    // Method 2: Check for installed packages if no render pipeline asset
                    Debug.Log($"{SERVER_LOG_PREFIX} No render pipeline asset found, checking packages...");
                    
                    try
                    {
                        var urpPackage = UnityEditor.PackageManager.PackageInfo.FindForPackageName("com.unity.render-pipelines.universal");
                        var hdrpPackage = UnityEditor.PackageManager.PackageInfo.FindForPackageName("com.unity.render-pipelines.high-definition");
                        
                        if (urpPackage != null)
                        {
                            renderPipeline = "URP (Package Available)";
                            renderPipelineVersion = urpPackage.version;
                            detectionMethod = "Package Detection";
                        }
                        else if (hdrpPackage != null)
                        {
                            renderPipeline = "HDRP (Package Available)";
                            renderPipelineVersion = hdrpPackage.version;
                            detectionMethod = "Package Detection";
                        }
                        else
                        {
                            renderPipeline = "Built-in";
                            detectionMethod = "No SRP packages found";
                        }
                    }
                    catch (System.Exception ex)
                    {
                        Debug.LogWarning($"{SERVER_LOG_PREFIX} Package detection failed: {ex.Message}");
                        renderPipeline = "Built-in (Package detection failed)";
                        detectionMethod = "Package detection error";
                    }
                }
                
                // Try to get version info if not already obtained
                if (renderPipelineVersion == "N/A" && renderPipeline.StartsWith("URP"))
                {
                    try
                    {
                        var packageInfo = UnityEditor.PackageManager.PackageInfo.FindForPackageName("com.unity.render-pipelines.universal");
                        if (packageInfo != null)
                        {
                            renderPipelineVersion = packageInfo.version;
                        }
                    }
                    catch (System.Exception ex)
                    {
                        Debug.LogWarning($"{SERVER_LOG_PREFIX} URP version detection failed: {ex.Message}");
                        renderPipelineVersion = "Version unknown";
                    }
                }
                else if (renderPipelineVersion == "N/A" && renderPipeline.StartsWith("HDRP"))
                {
                    try
                    {
                        var packageInfo = UnityEditor.PackageManager.PackageInfo.FindForPackageName("com.unity.render-pipelines.high-definition");
                        if (packageInfo != null)
                        {
                            renderPipelineVersion = packageInfo.version;
                        }
                    }
                    catch (System.Exception ex)
                    {
                        Debug.LogWarning($"{SERVER_LOG_PREFIX} HDRP version detection failed: {ex.Message}");
                        renderPipelineVersion = "Version unknown";
                    }
                }
                
                Debug.Log($"{SERVER_LOG_PREFIX} Detected render pipeline: {renderPipeline} (v{renderPipelineVersion}) via {detectionMethod}");
            }
            catch (System.Exception ex)
            {
                Debug.LogError($"{SERVER_LOG_PREFIX} Render pipeline detection failed: {ex.Message}");
                renderPipeline = "Detection Failed";
                detectionMethod = "Exception occurred";
            }
            
            return new
            {
                projectPath = Application.dataPath.Replace("/Assets", ""),
                projectName = Application.productName,
                unityVersion = Application.unityVersion,
                platform = Application.platform.ToString(),
                isPlaying = Application.isPlaying,
                renderPipeline = renderPipeline,
                renderPipelineVersion = renderPipelineVersion,
                detectionMethod = detectionMethod
            };
        }
        
        static void CreateFolderRecursive(string path)
        {
            var folders = path.Split('/');
            var currentPath = folders[0];
            
            for (int i = 1; i < folders.Length; i++)
            {
                var newPath = currentPath + "/" + folders[i];
                if (!AssetDatabase.IsValidFolder(newPath))
                {
                    AssetDatabase.CreateFolder(currentPath, folders[i]);
                }
                currentPath = newPath;
            }
        }
        
        static string GetDefaultScriptContent(string fileName)
        {
            var className = Path.GetFileNameWithoutExtension(fileName);
            return "using UnityEngine;\n\n" +
                   $"public class {className} : MonoBehaviour\n" +
                   "{\n" +
                   "    void Start()\n" +
                   "    {\n" +
                   "        \n" +
                   "    }\n" +
                   "    \n" +
                   "    void Update()\n" +
                   "    {\n" +
                   "        \n" +
                   "    }\n" +
                   "}";
        }
        
        static string GetDefaultShaderContent(string fileName)
        {
            var shaderName = Path.GetFileNameWithoutExtension(fileName);
            return $"Shader \"Custom/{shaderName}\"\n" +
                   "{\n" +
                   "    Properties\n" +
                   "    {\n" +
                   "        _MainTex (\"Texture\", 2D) = \"white\" {}\n" +
                   "    }\n" +
                   "    SubShader\n" +
                   "    {\n" +
                   "        Tags { \"RenderType\"=\"Opaque\" }\n" +
                   "        LOD 200\n" +
                   "\n" +
                   "        CGPROGRAM\n" +
                   "        #pragma surface surf Standard fullforwardshadows\n" +
                   "\n" +
                   "        sampler2D _MainTex;\n" +
                   "\n" +
                   "        struct Input\n" +
                   "        {\n" +
                   "            float2 uv_MainTex;\n" +
                   "        };\n" +
                   "\n" +
                   "        void surf (Input IN, inout SurfaceOutputStandard o)\n" +
                   "        {\n" +
                   "            fixed4 c = tex2D (_MainTex, IN.uv_MainTex);\n" +
                   "            o.Albedo = c.rgb;\n" +
                   "            o.Alpha = c.a;\n" +
                   "        }\n" +
                   "        ENDCG\n" +
                   "    }\n" +
                   "    FallBack \"Diffuse\"\n" +
                   "}";
        }
        
        // Folder operations
        static object CreateFolder(JObject request)
        {
            var path = request["path"]?.ToString();
            if (string.IsNullOrEmpty(path))
                throw new ArgumentException("path is required");
            
            if (!path.StartsWith(ASSETS_PREFIX))
                path = Path.Combine(DEFAULT_SCRIPTS_FOLDER, path);
            
            // Use Unity-safe folder creation
            if (AssetDatabase.IsValidFolder(path))
            {
                throw new InvalidOperationException($"Folder already exists: {path}");
            }
            
            // Create directory structure properly
            var fullPath = Path.Combine(Application.dataPath, path.Substring(ASSETS_PREFIX_LENGTH));
            Directory.CreateDirectory(fullPath);
            
            // Import the folder immediately
            AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceSynchronousImport);
            
            // Verify the folder was imported successfully
            var attempts = 0;
            const int maxAttempts = 10;
            while (!AssetDatabase.IsValidFolder(path) && attempts < maxAttempts)
            {
                System.Threading.Thread.Sleep(100);
                AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport);
                attempts++;
            }
            
            if (!AssetDatabase.IsValidFolder(path))
            {
                throw new InvalidOperationException($"Failed to import folder: {path}");
            }
            
            return new
            {
                path = path,
                guid = AssetDatabase.AssetPathToGUID(path)
            };
        }
        
        static object CreateFolderOnWorkerThread(JObject request)
        {
            var path = request["path"]?.ToString();
            if (string.IsNullOrEmpty(path))
                throw new ArgumentException("path is required");
            
            if (!path.StartsWith(ASSETS_PREFIX))
                path = Path.Combine(DEFAULT_SCRIPTS_FOLDER, path);
            
            var fullPath = Path.Combine(Application.dataPath, path.Substring(ASSETS_PREFIX_LENGTH));
            Directory.CreateDirectory(fullPath);
            
            return new
            {
                path = path,
                guid = "" // GUID requires AssetDatabase
            };
        }
        
        static object RenameFolder(JObject request)
        {
            var oldPath = request["oldPath"]?.ToString();
            var newName = request["newName"]?.ToString();
            
            if (string.IsNullOrEmpty(oldPath))
                throw new ArgumentException("oldPath is required");
            if (string.IsNullOrEmpty(newName))
                throw new ArgumentException("newName is required");
            
            var error = AssetDatabase.RenameAsset(oldPath, newName);
            if (!string.IsNullOrEmpty(error))
                throw new InvalidOperationException(error);
            
            // Wait for asset database to process
            System.Threading.Thread.Sleep(ASSET_REFRESH_DELAY_MS);
            
            var newPath = Path.Combine(Path.GetDirectoryName(oldPath), newName);
            return new
            {
                oldPath = oldPath,
                newPath = newPath,
                guid = AssetDatabase.AssetPathToGUID(newPath)
            };
        }
        
        static object RenameFolderOnWorkerThread(JObject request)
        {
            var oldPath = request["oldPath"]?.ToString();
            var newName = request["newName"]?.ToString();
            
            if (string.IsNullOrEmpty(oldPath))
                throw new ArgumentException("oldPath is required");
            if (string.IsNullOrEmpty(newName))
                throw new ArgumentException("newName is required");
            
            var oldFullPath = Path.Combine(Application.dataPath, oldPath.Substring(ASSETS_PREFIX_LENGTH));
            var parentDir = Path.GetDirectoryName(oldFullPath);
            var newFullPath = Path.Combine(parentDir, newName);
            
            if (!Directory.Exists(oldFullPath))
                throw new DirectoryNotFoundException($"Directory not found: {oldPath}");
            
            Directory.Move(oldFullPath, newFullPath);
            
            var newPath = Path.Combine(Path.GetDirectoryName(oldPath), newName);
            return new
            {
                oldPath = oldPath,
                newPath = newPath,
                guid = "" // GUID requires AssetDatabase
            };
        }
        
        static object MoveFolder(JObject request)
        {
            var sourcePath = request["sourcePath"]?.ToString();
            var targetPath = request["targetPath"]?.ToString();
            
            if (string.IsNullOrEmpty(sourcePath))
                throw new ArgumentException("sourcePath is required");
            if (string.IsNullOrEmpty(targetPath))
                throw new ArgumentException("targetPath is required");
            
            var error = AssetDatabase.MoveAsset(sourcePath, targetPath);
            if (!string.IsNullOrEmpty(error))
                throw new InvalidOperationException(error);
            
            // Wait for asset database to process
            System.Threading.Thread.Sleep(ASSET_REFRESH_DELAY_MS);
            
            return new
            {
                sourcePath = sourcePath,
                targetPath = targetPath,
                guid = AssetDatabase.AssetPathToGUID(targetPath)
            };
        }
        
        static object MoveFolderOnWorkerThread(JObject request)
        {
            var sourcePath = request["sourcePath"]?.ToString();
            var targetPath = request["targetPath"]?.ToString();
            
            if (string.IsNullOrEmpty(sourcePath))
                throw new ArgumentException("sourcePath is required");
            if (string.IsNullOrEmpty(targetPath))
                throw new ArgumentException("targetPath is required");
            
            var sourceFullPath = Path.Combine(Application.dataPath, sourcePath.Substring(ASSETS_PREFIX_LENGTH));
            var targetFullPath = Path.Combine(Application.dataPath, targetPath.Substring(ASSETS_PREFIX_LENGTH));
            
            if (!Directory.Exists(sourceFullPath))
                throw new DirectoryNotFoundException($"Directory not found: {sourcePath}");
            
            // Ensure target parent directory exists
            var targetParent = Path.GetDirectoryName(targetFullPath);
            if (!Directory.Exists(targetParent))
                Directory.CreateDirectory(targetParent);
            
            Directory.Move(sourceFullPath, targetFullPath);
            
            return new
            {
                sourcePath = sourcePath,
                targetPath = targetPath,
                guid = "" // GUID requires AssetDatabase
            };
        }
        
        static object DeleteFolder(JObject request)
        {
            var path = request["path"]?.ToString();
            var recursive = request["recursive"]?.Value<bool>() ?? true;
            
            if (string.IsNullOrEmpty(path))
                throw new ArgumentException("path is required");
            
            var fullPath = Path.Combine(Application.dataPath, path.Substring(ASSETS_PREFIX_LENGTH));
            if (!Directory.Exists(fullPath))
                throw new DirectoryNotFoundException($"Directory not found: {path}");
            
            if (!AssetDatabase.DeleteAsset(path))
                throw new InvalidOperationException($"Failed to delete folder: {path}");
            
            // Wait for asset database to process deletion
            System.Threading.Thread.Sleep(ASSET_REFRESH_DELAY_MS);
            
            return new { path = path };
        }
        
        static object DeleteFolderOnWorkerThread(JObject request)
        {
            var path = request["path"]?.ToString();
            var recursive = request["recursive"]?.Value<bool>() ?? true;
            
            if (string.IsNullOrEmpty(path))
                throw new ArgumentException("path is required");
            
            var fullPath = Path.Combine(Application.dataPath, path.Substring(ASSETS_PREFIX_LENGTH));
            if (!Directory.Exists(fullPath))
                throw new DirectoryNotFoundException($"Directory not found: {path}");
            
            Directory.Delete(fullPath, recursive);
            
            // Also delete .meta file
            var metaPath = fullPath + ".meta";
            if (File.Exists(metaPath))
                File.Delete(metaPath);
            
            return new { path = path };
        }
        
        static object ListFolder(JObject request)
        {
            var path = request["path"]?.ToString() ?? ASSETS_PREFIX;
            var recursive = request["recursive"]?.Value<bool>() ?? false;
            
            var fullPath = Path.Combine(Application.dataPath, path.StartsWith(ASSETS_PREFIX) ? path.Substring(ASSETS_PREFIX_LENGTH) : path);
            if (!Directory.Exists(fullPath))
                throw new DirectoryNotFoundException($"Directory not found: {path}");
            
            var entries = new List<object>();
            
            // Get directories
            var dirs = Directory.GetDirectories(fullPath, "*", recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);
            foreach (var dir in dirs)
            {
                var relativePath = ASSETS_PREFIX + GetRelativePath(Application.dataPath, dir);
                entries.Add(new
                {
                    path = relativePath,
                    name = Path.GetFileName(dir),
                    type = "folder",
                    guid = AssetDatabase.AssetPathToGUID(relativePath)
                });
            }
            
            // Get files
            var files = Directory.GetFiles(fullPath, "*", recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly)
                                 .Where(f => !f.EndsWith(".meta"));
            foreach (var file in files)
            {
                var relativePath = ASSETS_PREFIX + GetRelativePath(Application.dataPath, file);
                entries.Add(new
                {
                    path = relativePath,
                    name = Path.GetFileName(file),
                    type = "file",
                    extension = Path.GetExtension(file),
                    guid = AssetDatabase.AssetPathToGUID(relativePath)
                });
            }
            
            return new
            {
                path = path,
                entries = entries
            };
        }
        
        static object ListFolderOnWorkerThread(JObject request)
        {
            var path = request["path"]?.ToString() ?? ASSETS_PREFIX;
            var recursive = request["recursive"]?.Value<bool>() ?? false;
            
            var fullPath = Path.Combine(Application.dataPath, path.StartsWith(ASSETS_PREFIX) ? path.Substring(ASSETS_PREFIX_LENGTH) : path);
            if (!Directory.Exists(fullPath))
                throw new DirectoryNotFoundException($"Directory not found: {path}");
            
            var entries = new List<object>();
            
            // Get directories
            var dirs = Directory.GetDirectories(fullPath, "*", recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);
            foreach (var dir in dirs)
            {
                var relativePath = ASSETS_PREFIX + GetRelativePath(Application.dataPath, dir);
                entries.Add(new
                {
                    path = relativePath,
                    name = Path.GetFileName(dir),
                    type = "folder",
                    guid = "" // GUID requires AssetDatabase
                });
            }
            
            // Get files
            var files = Directory.GetFiles(fullPath, "*", recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly)
                                 .Where(f => !f.EndsWith(".meta"));
            foreach (var file in files)
            {
                var relativePath = ASSETS_PREFIX + GetRelativePath(Application.dataPath, file);
                entries.Add(new
                {
                    path = relativePath,
                    name = Path.GetFileName(file),
                    type = "file",
                    extension = Path.GetExtension(file),
                    guid = "" // GUID requires AssetDatabase
                });
            }
            
            return new
            {
                path = path,
                entries = entries
            };
        }
        
        static string GetRelativePath(string basePath, string fullPath)
        {
            if (!fullPath.StartsWith(basePath))
                return fullPath;
            
            var relativePath = fullPath.Substring(basePath.Length);
            if (relativePath.StartsWith(Path.DirectorySeparatorChar.ToString()))
                relativePath = relativePath.Substring(1);
            
            return relativePath.Replace(Path.DirectorySeparatorChar, '/');
        }
        
        static void SendResponse(HttpListenerResponse response, int statusCode, bool success, object result, string error)
        {
            response.StatusCode = statusCode;
            response.ContentType = "application/json; charset=utf-8";
            response.ContentEncoding = Encoding.UTF8;
            
            var responseData = new Dictionary<string, object>
            {
                ["success"] = success
            };
            
            if (result != null)
                responseData["result"] = result;
            
            if (!string.IsNullOrEmpty(error))
                responseData["error"] = error;
            
            var json = JsonConvert.SerializeObject(responseData);
            var buffer = Encoding.UTF8.GetBytes(json);
            
            response.ContentLength64 = buffer.Length;
            response.OutputStream.Write(buffer, 0, buffer.Length);
            response.Close();
        }
    }
}
```

--------------------------------------------------------------------------------
/src/embedded-scripts.ts:
--------------------------------------------------------------------------------

```typescript
import * as fs from 'fs/promises';
import * as path from 'path';

export interface EmbeddedScript {
  fileName: string;
  content: string;
  version: string;
}

/**
 * Static embedded scripts provider
 * Generated at build time from Unity source files
 */
export class EmbeddedScriptsProvider {
  private scripts: Map<string, EmbeddedScript> = new Map();

  constructor() {
    this.initializeScripts();
  }

  private initializeScripts() {
    // UnityHttpServer.cs content
    this.scripts.set('UnityHttpServer.cs', {
      fileName: 'UnityHttpServer.cs',
      version: '1.1.0',
      content: `using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using UnityEngine;
using UnityEditor;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace UnityMCP
{
    [InitializeOnLoad]
    public static class UnityMCPInstaller
    {
        static UnityMCPInstaller()
        {
            CheckAndUpdateScripts();
        }
        
        static void CheckAndUpdateScripts()
        {
            var installedVersion = EditorPrefs.GetString(UnityHttpServer.VERSION_META_KEY, "0.0.0");
            if (installedVersion != UnityHttpServer.SCRIPT_VERSION)
            {
                Debug.Log($"[UnityMCP] Updating Unity MCP scripts from version {installedVersion} to {UnityHttpServer.SCRIPT_VERSION}");
                // Version update logic will be handled by the MCP server
                EditorPrefs.SetString(UnityHttpServer.VERSION_META_KEY, UnityHttpServer.SCRIPT_VERSION);
            }
        }
    }
    /// <summary>
    /// Simple HTTP server for Unity MCP integration
    /// </summary>
    public static class UnityHttpServer
    {
        // Version information for auto-update
        public const string SCRIPT_VERSION = "1.1.0";
        public const string VERSION_META_KEY = "UnityMCP.InstalledVersion";
        
        // Configuration constants
        private const int DEFAULT_PORT = 23457;
        private const int REQUEST_TIMEOUT_MS = 120000; // 2 minutes
        private const int THREAD_JOIN_TIMEOUT_MS = 1000; // 1 second
        private const int ASSET_REFRESH_DELAY_MS = 500; // Wait after asset operations
        public const string SERVER_LOG_PREFIX = "[UnityMCP]";
        private const string PREFS_PORT_KEY = "UnityMCP.ServerPort";
        private const string PREFS_PORT_BEFORE_PLAY_KEY = "UnityMCP.ServerPortBeforePlay";
        
        // File path constants
        private const string ASSETS_PREFIX = "Assets/";
        private const int ASSETS_PREFIX_LENGTH = 7;
        private const string DEFAULT_SCRIPTS_FOLDER = "Assets/Scripts";
        private const string DEFAULT_SHADERS_FOLDER = "Assets/Shaders";
        private const string CS_EXTENSION = ".cs";
        private const string SHADER_EXTENSION = ".shader";
        
        private static HttpListener httpListener;
        private static Thread listenerThread;
        private static bool isRunning = false;
        
        // Request queue for serialization
        private static readonly Queue<Action> requestQueue = new Queue<Action>();
        private static bool isProcessingRequest = false;
        private static int currentPort = DEFAULT_PORT;
        
        /// <summary>
        /// Gets whether the server is currently running
        /// </summary>
        public static bool IsRunning => isRunning;
        
        /// <summary>
        /// Gets the current port the server is running on
        /// </summary>
        public static int CurrentPort => currentPort;
        
        [InitializeOnLoad]
        static class AutoShutdown
        {
            static AutoShutdown()
            {
                EditorApplication.playModeStateChanged += OnPlayModeChanged;
                EditorApplication.quitting += Shutdown;
                
                // Handle script recompilation
                UnityEditor.Compilation.CompilationPipeline.compilationStarted += OnCompilationStarted;
                UnityEditor.Compilation.CompilationPipeline.compilationFinished += OnCompilationFinished;
                
                // Auto-start server on Unity startup
                EditorApplication.delayCall += () => {
                    if (!isRunning)
                    {
                        var savedPort = EditorPrefs.GetInt(PREFS_PORT_KEY, DEFAULT_PORT);
                        Debug.Log($"{SERVER_LOG_PREFIX} Auto-starting server on port {savedPort}");
                        Start(savedPort);
                    }
                };
            }
            
            static void OnCompilationStarted(object obj)
            {
                Debug.Log($"{SERVER_LOG_PREFIX} Compilation started - stopping server");
                if (isRunning)
                {
                    Shutdown();
                }
            }
            
            static void OnCompilationFinished(object obj)
            {
                Debug.Log($"{SERVER_LOG_PREFIX} Compilation finished - auto-restarting server");
                // Always auto-restart after compilation
                var savedPort = EditorPrefs.GetInt(PREFS_PORT_KEY, DEFAULT_PORT);
                EditorApplication.delayCall += () => Start(savedPort);
            }
        }
        
        /// <summary>
        /// Start the HTTP server on the specified port
        /// </summary>
        /// <param name="port">Port to listen on</param>
        public static void Start(int port = DEFAULT_PORT)
        {
            if (isRunning) 
            {
                Debug.LogWarning($"{SERVER_LOG_PREFIX} Server is already running. Stop it first.");
                return;
            }
            
            currentPort = port;
            
            try
            {
                httpListener = new HttpListener();
                httpListener.Prefixes.Add($"http://localhost:{currentPort}/");
                httpListener.Start();
                isRunning = true;
                
                listenerThread = new Thread(ListenLoop) 
                { 
                    IsBackground = true,
                    Name = "UnityMCPHttpListener"
                };
                listenerThread.Start();
                
                Debug.Log($"{SERVER_LOG_PREFIX} HTTP Server started on port {currentPort}");
            }
            catch (Exception e)
            {
                isRunning = false;
                Debug.LogError($"{SERVER_LOG_PREFIX} Failed to start HTTP server: {e.Message}");
                throw;
            }
        }
        
        /// <summary>
        /// Stop the HTTP server
        /// </summary>
        public static void Shutdown()
        {
            if (!isRunning)
            {
                Debug.LogWarning($"{SERVER_LOG_PREFIX} Server is not running.");
                return;
            }
            
            isRunning = false;
            
            try
            {
                httpListener?.Stop();
                httpListener?.Close();
                listenerThread?.Join(THREAD_JOIN_TIMEOUT_MS);
                Debug.Log($"{SERVER_LOG_PREFIX} HTTP Server stopped");
            }
            catch (Exception e)
            {
                Debug.LogError($"{SERVER_LOG_PREFIX} Error during shutdown: {e.Message}");
            }
            finally
            {
                httpListener = null;
                listenerThread = null;
            }
        }
        
        static void OnPlayModeChanged(PlayModeStateChange state)
        {
            // Stop server when entering play mode to avoid conflicts
            if (state == PlayModeStateChange.ExitingEditMode)
            {
                if (isRunning)
                {
                    Debug.Log($"{SERVER_LOG_PREFIX} Stopping server due to play mode change");
                    EditorPrefs.SetInt(PREFS_PORT_BEFORE_PLAY_KEY, currentPort);
                    Shutdown();
                }
            }
            // Restart server when returning to edit mode
            else if (state == PlayModeStateChange.EnteredEditMode)
            {
                var savedPort = EditorPrefs.GetInt(PREFS_PORT_BEFORE_PLAY_KEY, DEFAULT_PORT);
                Debug.Log($"{SERVER_LOG_PREFIX} Restarting server after play mode on port {savedPort}");
                EditorApplication.delayCall += () => Start(savedPort);
            }
        }
        
        static void ListenLoop()
        {
            while (isRunning)
            {
                try
                {
                    var context = httpListener.GetContext();
                    ThreadPool.QueueUserWorkItem(_ => HandleRequest(context));
                }
                catch (Exception e)
                {
                    if (isRunning)
                        Debug.LogError($"{SERVER_LOG_PREFIX} Listen error: {e.Message}");
                }
            }
        }
        
        static void HandleRequest(HttpListenerContext context)
        {
            var request = context.Request;
            var response = context.Response;
            response.Headers.Add("Access-Control-Allow-Origin", "*");
            
            try
            {
                if (request.HttpMethod != "POST")
                {
                    SendResponse(response, 405, false, null, "Method not allowed");
                    return;
                }
                
                string requestBody;
                // Force UTF-8 encoding for request body
                using (var reader = new StreamReader(request.InputStream, Encoding.UTF8))
                {
                    requestBody = reader.ReadToEnd();
                }
                
                var requestData = JObject.Parse(requestBody);
                var method = requestData["method"]?.ToString();
                
                if (string.IsNullOrEmpty(method))
                {
                    SendResponse(response, 400, false, null, "Method is required");
                    return;
                }
                
                Debug.Log($"{SERVER_LOG_PREFIX} Processing request: {method}");
                
                // Check if this request requires main thread
                bool requiresMainThread = RequiresMainThread(method);
                
                if (!requiresMainThread)
                {
                    // Process directly on worker thread
                    try
                    {
                        var result = ProcessRequestOnWorkerThread(method, requestData);
                        SendResponse(response, 200, true, result, null);
                    }
                    catch (Exception e)
                    {
                        var statusCode = e is ArgumentException ? 400 : 500;
                        SendResponse(response, statusCode, false, null, e.Message);
                    }
                }
                else
                {
                    // Execute on main thread for Unity API calls
                    object result = null;
                    Exception error = null;
                    var resetEvent = new ManualResetEvent(false);
                    
                    EditorApplication.delayCall += () =>
                    {
                        try
                        {
                            Debug.Log($"{SERVER_LOG_PREFIX} Processing on main thread: {method}");
                            result = ProcessRequest(method, requestData);
                            Debug.Log($"{SERVER_LOG_PREFIX} Completed processing: {method}");
                        }
                        catch (Exception e)
                        {
                            error = e;
                            Debug.LogError($"{SERVER_LOG_PREFIX} Error processing {method}: {e.Message}");
                        }
                        finally
                        {
                            resetEvent.Set();
                        }
                    };
                    
                    if (!resetEvent.WaitOne(REQUEST_TIMEOUT_MS))
                    {
                        SendResponse(response, 504, false, null, "Request timeout - Unity may be busy or unfocused");
                        return;
                    }
                    
                    if (error != null)
                    {
                        var statusCode = error is ArgumentException ? 400 : 500;
                        SendResponse(response, statusCode, false, null, error.Message);
                        return;
                    }
                    
                    SendResponse(response, 200, true, result, null);
                }
            }
            catch (Exception e)
            {
                SendResponse(response, 400, false, null, $"Bad request: {e.Message}");
            }
        }
        
        static bool RequiresMainThread(string method)
        {
            // These methods can run on worker thread
            switch (method)
            {
                case "ping":
                case "script/read":
                case "shader/read":
                    return false;
                    
                // project/info now requires Unity API for render pipeline detection
                // Creating, deleting files require Unity API (AssetDatabase)
                default:
                    return true;
            }
        }
        
        static object ProcessRequestOnWorkerThread(string method, JObject request)
        {
            switch (method)
            {
                case "ping":
                    return new { status = "ok", time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") };
                    
                case "project/info":
                    // project/info requires Unity API for render pipeline detection
                    throw new NotImplementedException("project/info requires main thread for render pipeline detection");
                    
                case "script/read":
                    return ReadScriptOnWorkerThread(request);
                    
                case "shader/read":
                    return ReadShaderOnWorkerThread(request);
                    
                // Folder operations (can run on worker thread)
                case "folder/create":
                    return CreateFolderOnWorkerThread(request);
                case "folder/rename":
                    return RenameFolderOnWorkerThread(request);
                case "folder/move":
                    return MoveFolderOnWorkerThread(request);
                case "folder/delete":
                    return DeleteFolderOnWorkerThread(request);
                case "folder/list":
                    return ListFolderOnWorkerThread(request);
                    
                default:
                    throw new NotImplementedException($"Method not implemented for worker thread: {method}");
            }
        }
        
        static object ReadScriptOnWorkerThread(JObject request)
        {
            var path = request["path"]?.ToString();
            if (string.IsNullOrEmpty(path))
                throw new ArgumentException("path is required");
            
            var fullPath = Path.Combine(Application.dataPath, path.Substring(ASSETS_PREFIX_LENGTH));
            if (!File.Exists(fullPath))
                throw new FileNotFoundException($"File not found: {path}");
            
            return new
            {
                path = path,
                content = File.ReadAllText(fullPath, new UTF8Encoding(true)),
                guid = "" // GUID requires AssetDatabase, skip in worker thread
            };
        }
        
        static object ReadShaderOnWorkerThread(JObject request)
        {
            var path = request["path"]?.ToString();
            if (string.IsNullOrEmpty(path))
                throw new ArgumentException("path is required");
            
            var fullPath = Path.Combine(Application.dataPath, path.Substring(ASSETS_PREFIX_LENGTH));
            if (!File.Exists(fullPath))
                throw new FileNotFoundException($"File not found: {path}");
            
            return new
            {
                path = path,
                content = File.ReadAllText(fullPath, new UTF8Encoding(true)),
                guid = "" // GUID requires AssetDatabase, skip in worker thread
            };
        }
        
        static object ProcessRequest(string method, JObject request)
        {
            switch (method)
            {
                case "ping":
                    return new { status = "ok", time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") };
                
                // Script operations
                case "script/create":
                    return CreateScript(request);
                case "script/read":
                    return ReadScript(request);
                case "script/delete":
                    return DeleteScript(request);
                case "script/applyDiff":
                    return ApplyDiff(request);
                
                // Shader operations
                case "shader/create":
                    return CreateShader(request);
                case "shader/read":
                    return ReadShader(request);
                case "shader/delete":
                    return DeleteShader(request);
                
                // Project operations
                case "project/info":
                    return GetProjectInfo();
                
                // Folder operations
                case "folder/create":
                    return CreateFolder(request);
                case "folder/rename":
                    return RenameFolder(request);
                case "folder/move":
                    return MoveFolder(request);
                case "folder/delete":
                    return DeleteFolder(request);
                case "folder/list":
                    return ListFolder(request);
                
                default:
                    throw new NotImplementedException($"Method not found: {method}");
            }
        }
        
        static object CreateScript(JObject request)
        {
            var fileName = request["fileName"]?.ToString();
            if (string.IsNullOrEmpty(fileName))
                throw new ArgumentException("fileName is required");
            
            if (!fileName.EndsWith(CS_EXTENSION))
                fileName += CS_EXTENSION;
            
            var content = request["content"]?.ToString();
            var folder = request["folder"]?.ToString() ?? DEFAULT_SCRIPTS_FOLDER;
            
            var path = Path.Combine(folder, fileName);
            var directory = Path.GetDirectoryName(path);
            
            // Create directory if needed
            if (!AssetDatabase.IsValidFolder(directory))
            {
                CreateFolderRecursive(directory);
            }
            
            // Use Unity-safe file creation approach
            var scriptContent = content ?? GetDefaultScriptContent(fileName);
            
            // First, ensure the asset doesn't already exist
            if (AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(path) != null)
            {
                throw new InvalidOperationException($"Asset already exists: {path}");
            }
            
            // Write file using UTF-8 with BOM (Unity standard)
            var fullPath = Path.Combine(Application.dataPath, path.Substring(ASSETS_PREFIX_LENGTH));
            var utf8WithBom = new UTF8Encoding(true);
            File.WriteAllText(fullPath, scriptContent, utf8WithBom);
            
            // Import the asset immediately and wait for completion
            AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate);
            
            // Verify the asset was imported successfully
            var attempts = 0;
            const int maxAttempts = 10;
            while (AssetDatabase.AssetPathToGUID(path) == "" && attempts < maxAttempts)
            {
                System.Threading.Thread.Sleep(100);
                AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport);
                attempts++;
            }
            
            if (AssetDatabase.AssetPathToGUID(path) == "")
            {
                throw new InvalidOperationException($"Failed to import asset: {path}");
            }
            
            return new
            {
                path = path,
                guid = AssetDatabase.AssetPathToGUID(path)
            };
        }
        
        static object ReadScript(JObject request)
        {
            var path = request["path"]?.ToString();
            if (string.IsNullOrEmpty(path))
                throw new ArgumentException("path is required");
            
            var fullPath = Path.Combine(Application.dataPath, path.Substring(ASSETS_PREFIX_LENGTH));
            if (!File.Exists(fullPath))
                throw new FileNotFoundException($"File not found: {path}");
            
            return new
            {
                path = path,
                content = File.ReadAllText(fullPath, new UTF8Encoding(true)),
                guid = AssetDatabase.AssetPathToGUID(path)
            };
        }
        
        static object DeleteScript(JObject request)
        {
            var path = request["path"]?.ToString();
            if (string.IsNullOrEmpty(path))
                throw new ArgumentException("path is required");
            
            // Verify file exists before deletion
            var fullPath = Path.Combine(Application.dataPath, path.Substring(ASSETS_PREFIX_LENGTH));
            if (!File.Exists(fullPath))
                throw new FileNotFoundException($"File not found: {path}");
            
            // Delete using AssetDatabase
            if (!AssetDatabase.DeleteAsset(path))
                throw new InvalidOperationException($"Failed to delete: {path}");
            
            // Force immediate refresh
            AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport);
            
            // Wait for asset database to process deletion
            System.Threading.Thread.Sleep(ASSET_REFRESH_DELAY_MS);
            
            return new { message = "Script deleted successfully" };
        }
        
        static object ApplyDiff(JObject request)
        {
            var path = request["path"]?.ToString();
            var diff = request["diff"]?.ToString();
            var options = request["options"] as JObject;
            
            if (string.IsNullOrEmpty(path))
                throw new ArgumentException("path is required");
            if (string.IsNullOrEmpty(diff))
                throw new ArgumentException("diff is required");
            
            var fullPath = Path.Combine(Application.dataPath, path.Substring(ASSETS_PREFIX_LENGTH));
            if (!File.Exists(fullPath))
                throw new FileNotFoundException($"File not found: {path}");
            
            var dryRun = options?["dryRun"]?.Value<bool>() ?? false;
            
            // Read current content using UTF-8 with BOM (Unity standard)
            var utf8WithBom = new UTF8Encoding(true);
            var originalContent = File.ReadAllText(fullPath, utf8WithBom);
            var lines = originalContent.Split('\\n').ToList();
            
            // Parse and apply unified diff
            var diffLines = diff.Split('\\n');
            var linesAdded = 0;
            var linesRemoved = 0;
            var currentLine = 0;
            
            for (int i = 0; i < diffLines.Length; i++)
            {
                var line = diffLines[i];
                if (line.StartsWith("@@"))
                {
                    // Parse hunk header: @@ -l,s +l,s @@
                    var match = System.Text.RegularExpressions.Regex.Match(line, @"@@ -(\\d+),?\\d* \\+(\\d+),?\\d* @@");
                    if (match.Success)
                    {
                        currentLine = int.Parse(match.Groups[1].Value) - 1;
                    }
                }
                else if (line.StartsWith("-") && !line.StartsWith("---"))
                {
                    // Remove line
                    if (currentLine < lines.Count)
                    {
                        lines.RemoveAt(currentLine);
                        linesRemoved++;
                    }
                }
                else if (line.StartsWith("+") && !line.StartsWith("+++"))
                {
                    // Add line
                    lines.Insert(currentLine, line.Substring(1));
                    currentLine++;
                    linesAdded++;
                }
                else if (line.StartsWith(" "))
                {
                    // Context line
                    currentLine++;
                }
            }
            
            // Write result if not dry run
            if (!dryRun)
            {
                var updatedContent = string.Join("\\n", lines);
                // Write with UTF-8 with BOM (Unity standard)
                File.WriteAllText(fullPath, updatedContent, utf8WithBom);
                AssetDatabase.Refresh();
                
                // Wait for asset database to process
                System.Threading.Thread.Sleep(ASSET_REFRESH_DELAY_MS);
            }
            
            return new
            {
                path = path,
                linesAdded = linesAdded,
                linesRemoved = linesRemoved,
                dryRun = dryRun,
                guid = AssetDatabase.AssetPathToGUID(path)
            };
        }
        
        static object CreateShader(JObject request)
        {
            var name = request["name"]?.ToString();
            if (string.IsNullOrEmpty(name))
                throw new ArgumentException("name is required");
            
            if (!name.EndsWith(SHADER_EXTENSION))
                name += SHADER_EXTENSION;
            
            var content = request["content"]?.ToString();
            var folder = request["folder"]?.ToString() ?? DEFAULT_SHADERS_FOLDER;
            
            var path = Path.Combine(folder, name);
            var directory = Path.GetDirectoryName(path);
            
            if (!AssetDatabase.IsValidFolder(directory))
            {
                CreateFolderRecursive(directory);
            }
            
            // Use Unity-safe file creation approach
            var shaderContent = content ?? GetDefaultShaderContent(name);
            
            // First, ensure the asset doesn't already exist
            if (AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(path) != null)
            {
                throw new InvalidOperationException($"Asset already exists: {path}");
            }
            
            // Write file using UTF-8 with BOM (Unity standard)
            var fullPath = Path.Combine(Application.dataPath, path.Substring(ASSETS_PREFIX_LENGTH));
            var utf8WithBom = new UTF8Encoding(true);
            File.WriteAllText(fullPath, shaderContent, utf8WithBom);
            
            // Import the asset immediately and wait for completion
            AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate);
            
            // Verify the asset was imported successfully
            var attempts = 0;
            const int maxAttempts = 10;
            while (AssetDatabase.AssetPathToGUID(path) == "" && attempts < maxAttempts)
            {
                System.Threading.Thread.Sleep(100);
                AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport);
                attempts++;
            }
            
            if (AssetDatabase.AssetPathToGUID(path) == "")
            {
                throw new InvalidOperationException($"Failed to import asset: {path}");
            }
            
            return new
            {
                path = path,
                guid = AssetDatabase.AssetPathToGUID(path)
            };
        }
        
        static object ReadShader(JObject request)
        {
            var path = request["path"]?.ToString();
            if (string.IsNullOrEmpty(path))
                throw new ArgumentException("path is required");
            
            var fullPath = Path.Combine(Application.dataPath, path.Substring(ASSETS_PREFIX_LENGTH));
            if (!File.Exists(fullPath))
                throw new FileNotFoundException($"File not found: {path}");
            
            return new
            {
                path = path,
                content = File.ReadAllText(fullPath, new UTF8Encoding(true)),
                guid = AssetDatabase.AssetPathToGUID(path)
            };
        }
        
        static object DeleteShader(JObject request)
        {
            var path = request["path"]?.ToString();
            if (string.IsNullOrEmpty(path))
                throw new ArgumentException("path is required");
            
            if (!AssetDatabase.DeleteAsset(path))
                throw new InvalidOperationException($"Failed to delete: {path}");
            
            // Wait for asset database to process deletion
            System.Threading.Thread.Sleep(ASSET_REFRESH_DELAY_MS);
            
            return new { message = "Shader deleted successfully" };
        }
        
        static object GetProjectInfo()
        {
            // Detect render pipeline with multiple methods
            string renderPipeline = "Built-in";
            string renderPipelineVersion = "N/A";
            string detectionMethod = "Default";
            
            try
            {
                // Method 1: Check GraphicsSettings.renderPipelineAsset
                var renderPipelineAsset = UnityEngine.Rendering.GraphicsSettings.renderPipelineAsset;
                Debug.Log($"{SERVER_LOG_PREFIX} RenderPipelineAsset: {(renderPipelineAsset != null ? renderPipelineAsset.GetType().FullName : "null")}");
                
                if (renderPipelineAsset != null)
                {
                    var assetType = renderPipelineAsset.GetType();
                    var typeName = assetType.Name;
                    var fullTypeName = assetType.FullName;
                    
                    Debug.Log($"{SERVER_LOG_PREFIX} Asset type: {typeName}, Full type: {fullTypeName}");
                    
                    if (fullTypeName.Contains("Universal") || typeName.Contains("Universal") || 
                        fullTypeName.Contains("URP") || typeName.Contains("URP"))
                    {
                        renderPipeline = "URP";
                        detectionMethod = "GraphicsSettings.renderPipelineAsset";
                    }
                    else if (fullTypeName.Contains("HighDefinition") || typeName.Contains("HighDefinition") || 
                             fullTypeName.Contains("HDRP") || typeName.Contains("HDRP"))
                    {
                        renderPipeline = "HDRP";
                        detectionMethod = "GraphicsSettings.renderPipelineAsset";
                    }
                    else
                    {
                        renderPipeline = $"Custom ({typeName})";
                        detectionMethod = "GraphicsSettings.renderPipelineAsset";
                    }
                }
                else
                {
                    // Method 2: Check for installed packages if no render pipeline asset
                    Debug.Log($"{SERVER_LOG_PREFIX} No render pipeline asset found, checking packages...");
                    
                    try
                    {
                        var urpPackage = UnityEditor.PackageManager.PackageInfo.FindForPackageName("com.unity.render-pipelines.universal");
                        var hdrpPackage = UnityEditor.PackageManager.PackageInfo.FindForPackageName("com.unity.render-pipelines.high-definition");
                        
                        if (urpPackage != null)
                        {
                            renderPipeline = "URP (Package Available)";
                            renderPipelineVersion = urpPackage.version;
                            detectionMethod = "Package Detection";
                        }
                        else if (hdrpPackage != null)
                        {
                            renderPipeline = "HDRP (Package Available)";
                            renderPipelineVersion = hdrpPackage.version;
                            detectionMethod = "Package Detection";
                        }
                        else
                        {
                            renderPipeline = "Built-in";
                            detectionMethod = "No SRP packages found";
                        }
                    }
                    catch (System.Exception ex)
                    {
                        Debug.LogWarning($"{SERVER_LOG_PREFIX} Package detection failed: {ex.Message}");
                        renderPipeline = "Built-in (Package detection failed)";
                        detectionMethod = "Package detection error";
                    }
                }
                
                // Try to get version info if not already obtained
                if (renderPipelineVersion == "N/A" && renderPipeline.StartsWith("URP"))
                {
                    try
                    {
                        var packageInfo = UnityEditor.PackageManager.PackageInfo.FindForPackageName("com.unity.render-pipelines.universal");
                        if (packageInfo != null)
                        {
                            renderPipelineVersion = packageInfo.version;
                        }
                    }
                    catch (System.Exception ex)
                    {
                        Debug.LogWarning($"{SERVER_LOG_PREFIX} URP version detection failed: {ex.Message}");
                        renderPipelineVersion = "Version unknown";
                    }
                }
                else if (renderPipelineVersion == "N/A" && renderPipeline.StartsWith("HDRP"))
                {
                    try
                    {
                        var packageInfo = UnityEditor.PackageManager.PackageInfo.FindForPackageName("com.unity.render-pipelines.high-definition");
                        if (packageInfo != null)
                        {
                            renderPipelineVersion = packageInfo.version;
                        }
                    }
                    catch (System.Exception ex)
                    {
                        Debug.LogWarning($"{SERVER_LOG_PREFIX} HDRP version detection failed: {ex.Message}");
                        renderPipelineVersion = "Version unknown";
                    }
                }
                
                Debug.Log($"{SERVER_LOG_PREFIX} Detected render pipeline: {renderPipeline} (v{renderPipelineVersion}) via {detectionMethod}");
            }
            catch (System.Exception ex)
            {
                Debug.LogError($"{SERVER_LOG_PREFIX} Render pipeline detection failed: {ex.Message}");
                renderPipeline = "Detection Failed";
                detectionMethod = "Exception occurred";
            }
            
            return new
            {
                projectPath = Application.dataPath.Replace("/Assets", ""),
                projectName = Application.productName,
                unityVersion = Application.unityVersion,
                platform = Application.platform.ToString(),
                isPlaying = Application.isPlaying,
                renderPipeline = renderPipeline,
                renderPipelineVersion = renderPipelineVersion,
                detectionMethod = detectionMethod
            };
        }
        
        static void CreateFolderRecursive(string path)
        {
            var folders = path.Split('/');
            var currentPath = folders[0];
            
            for (int i = 1; i < folders.Length; i++)
            {
                var newPath = currentPath + "/" + folders[i];
                if (!AssetDatabase.IsValidFolder(newPath))
                {
                    AssetDatabase.CreateFolder(currentPath, folders[i]);
                }
                currentPath = newPath;
            }
        }
        
        static string GetDefaultScriptContent(string fileName)
        {
            var className = Path.GetFileNameWithoutExtension(fileName);
            return "using UnityEngine;\\n\\n" +
                   $"public class {className} : MonoBehaviour\\n" +
                   "{\\n" +
                   "    void Start()\\n" +
                   "    {\\n" +
                   "        \\n" +
                   "    }\\n" +
                   "    \\n" +
                   "    void Update()\\n" +
                   "    {\\n" +
                   "        \\n" +
                   "    }\\n" +
                   "}";
        }
        
        static string GetDefaultShaderContent(string fileName)
        {
            var shaderName = Path.GetFileNameWithoutExtension(fileName);
            return $"Shader \\"Custom/{shaderName}\\"\\n" +
                   "{\\n" +
                   "    Properties\\n" +
                   "    {\\n" +
                   "        _MainTex (\\"Texture\\", 2D) = \\"white\\" {}\\n" +
                   "    }\\n" +
                   "    SubShader\\n" +
                   "    {\\n" +
                   "        Tags { \\"RenderType\\"=\\"Opaque\\" }\\n" +
                   "        LOD 200\\n" +
                   "\\n" +
                   "        CGPROGRAM\\n" +
                   "        #pragma surface surf Standard fullforwardshadows\\n" +
                   "\\n" +
                   "        sampler2D _MainTex;\\n" +
                   "\\n" +
                   "        struct Input\\n" +
                   "        {\\n" +
                   "            float2 uv_MainTex;\\n" +
                   "        };\\n" +
                   "\\n" +
                   "        void surf (Input IN, inout SurfaceOutputStandard o)\\n" +
                   "        {\\n" +
                   "            fixed4 c = tex2D (_MainTex, IN.uv_MainTex);\\n" +
                   "            o.Albedo = c.rgb;\\n" +
                   "            o.Alpha = c.a;\\n" +
                   "        }\\n" +
                   "        ENDCG\\n" +
                   "    }\\n" +
                   "    FallBack \\"Diffuse\\"\\n" +
                   "}";
        }
        
        // Folder operations
        static object CreateFolder(JObject request)
        {
            var path = request["path"]?.ToString();
            if (string.IsNullOrEmpty(path))
                throw new ArgumentException("path is required");
            
            if (!path.StartsWith(ASSETS_PREFIX))
                path = Path.Combine(DEFAULT_SCRIPTS_FOLDER, path);
            
            // Use Unity-safe folder creation
            if (AssetDatabase.IsValidFolder(path))
            {
                throw new InvalidOperationException($"Folder already exists: {path}");
            }
            
            // Create directory structure properly
            var fullPath = Path.Combine(Application.dataPath, path.Substring(ASSETS_PREFIX_LENGTH));
            Directory.CreateDirectory(fullPath);
            
            // Import the folder immediately
            AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceSynchronousImport);
            
            // Verify the folder was imported successfully
            var attempts = 0;
            const int maxAttempts = 10;
            while (!AssetDatabase.IsValidFolder(path) && attempts < maxAttempts)
            {
                System.Threading.Thread.Sleep(100);
                AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport);
                attempts++;
            }
            
            if (!AssetDatabase.IsValidFolder(path))
            {
                throw new InvalidOperationException($"Failed to import folder: {path}");
            }
            
            return new
            {
                path = path,
                guid = AssetDatabase.AssetPathToGUID(path)
            };
        }
        
        static object CreateFolderOnWorkerThread(JObject request)
        {
            var path = request["path"]?.ToString();
            if (string.IsNullOrEmpty(path))
                throw new ArgumentException("path is required");
            
            if (!path.StartsWith(ASSETS_PREFIX))
                path = Path.Combine(DEFAULT_SCRIPTS_FOLDER, path);
            
            var fullPath = Path.Combine(Application.dataPath, path.Substring(ASSETS_PREFIX_LENGTH));
            Directory.CreateDirectory(fullPath);
            
            return new
            {
                path = path,
                guid = "" // GUID requires AssetDatabase
            };
        }
        
        static object RenameFolder(JObject request)
        {
            var oldPath = request["oldPath"]?.ToString();
            var newName = request["newName"]?.ToString();
            
            if (string.IsNullOrEmpty(oldPath))
                throw new ArgumentException("oldPath is required");
            if (string.IsNullOrEmpty(newName))
                throw new ArgumentException("newName is required");
            
            var error = AssetDatabase.RenameAsset(oldPath, newName);
            if (!string.IsNullOrEmpty(error))
                throw new InvalidOperationException(error);
            
            // Wait for asset database to process
            System.Threading.Thread.Sleep(ASSET_REFRESH_DELAY_MS);
            
            var newPath = Path.Combine(Path.GetDirectoryName(oldPath), newName);
            return new
            {
                oldPath = oldPath,
                newPath = newPath,
                guid = AssetDatabase.AssetPathToGUID(newPath)
            };
        }
        
        static object RenameFolderOnWorkerThread(JObject request)
        {
            var oldPath = request["oldPath"]?.ToString();
            var newName = request["newName"]?.ToString();
            
            if (string.IsNullOrEmpty(oldPath))
                throw new ArgumentException("oldPath is required");
            if (string.IsNullOrEmpty(newName))
                throw new ArgumentException("newName is required");
            
            var oldFullPath = Path.Combine(Application.dataPath, oldPath.Substring(ASSETS_PREFIX_LENGTH));
            var parentDir = Path.GetDirectoryName(oldFullPath);
            var newFullPath = Path.Combine(parentDir, newName);
            
            if (!Directory.Exists(oldFullPath))
                throw new DirectoryNotFoundException($"Directory not found: {oldPath}");
            
            Directory.Move(oldFullPath, newFullPath);
            
            var newPath = Path.Combine(Path.GetDirectoryName(oldPath), newName);
            return new
            {
                oldPath = oldPath,
                newPath = newPath,
                guid = "" // GUID requires AssetDatabase
            };
        }
        
        static object MoveFolder(JObject request)
        {
            var sourcePath = request["sourcePath"]?.ToString();
            var targetPath = request["targetPath"]?.ToString();
            
            if (string.IsNullOrEmpty(sourcePath))
                throw new ArgumentException("sourcePath is required");
            if (string.IsNullOrEmpty(targetPath))
                throw new ArgumentException("targetPath is required");
            
            var error = AssetDatabase.MoveAsset(sourcePath, targetPath);
            if (!string.IsNullOrEmpty(error))
                throw new InvalidOperationException(error);
            
            // Wait for asset database to process
            System.Threading.Thread.Sleep(ASSET_REFRESH_DELAY_MS);
            
            return new
            {
                sourcePath = sourcePath,
                targetPath = targetPath,
                guid = AssetDatabase.AssetPathToGUID(targetPath)
            };
        }
        
        static object MoveFolderOnWorkerThread(JObject request)
        {
            var sourcePath = request["sourcePath"]?.ToString();
            var targetPath = request["targetPath"]?.ToString();
            
            if (string.IsNullOrEmpty(sourcePath))
                throw new ArgumentException("sourcePath is required");
            if (string.IsNullOrEmpty(targetPath))
                throw new ArgumentException("targetPath is required");
            
            var sourceFullPath = Path.Combine(Application.dataPath, sourcePath.Substring(ASSETS_PREFIX_LENGTH));
            var targetFullPath = Path.Combine(Application.dataPath, targetPath.Substring(ASSETS_PREFIX_LENGTH));
            
            if (!Directory.Exists(sourceFullPath))
                throw new DirectoryNotFoundException($"Directory not found: {sourcePath}");
            
            // Ensure target parent directory exists
            var targetParent = Path.GetDirectoryName(targetFullPath);
            if (!Directory.Exists(targetParent))
                Directory.CreateDirectory(targetParent);
            
            Directory.Move(sourceFullPath, targetFullPath);
            
            return new
            {
                sourcePath = sourcePath,
                targetPath = targetPath,
                guid = "" // GUID requires AssetDatabase
            };
        }
        
        static object DeleteFolder(JObject request)
        {
            var path = request["path"]?.ToString();
            var recursive = request["recursive"]?.Value<bool>() ?? true;
            
            if (string.IsNullOrEmpty(path))
                throw new ArgumentException("path is required");
            
            var fullPath = Path.Combine(Application.dataPath, path.Substring(ASSETS_PREFIX_LENGTH));
            if (!Directory.Exists(fullPath))
                throw new DirectoryNotFoundException($"Directory not found: {path}");
            
            if (!AssetDatabase.DeleteAsset(path))
                throw new InvalidOperationException($"Failed to delete folder: {path}");
            
            // Wait for asset database to process deletion
            System.Threading.Thread.Sleep(ASSET_REFRESH_DELAY_MS);
            
            return new { path = path };
        }
        
        static object DeleteFolderOnWorkerThread(JObject request)
        {
            var path = request["path"]?.ToString();
            var recursive = request["recursive"]?.Value<bool>() ?? true;
            
            if (string.IsNullOrEmpty(path))
                throw new ArgumentException("path is required");
            
            var fullPath = Path.Combine(Application.dataPath, path.Substring(ASSETS_PREFIX_LENGTH));
            if (!Directory.Exists(fullPath))
                throw new DirectoryNotFoundException($"Directory not found: {path}");
            
            Directory.Delete(fullPath, recursive);
            
            // Also delete .meta file
            var metaPath = fullPath + ".meta";
            if (File.Exists(metaPath))
                File.Delete(metaPath);
            
            return new { path = path };
        }
        
        static object ListFolder(JObject request)
        {
            var path = request["path"]?.ToString() ?? ASSETS_PREFIX;
            var recursive = request["recursive"]?.Value<bool>() ?? false;
            
            var fullPath = Path.Combine(Application.dataPath, path.StartsWith(ASSETS_PREFIX) ? path.Substring(ASSETS_PREFIX_LENGTH) : path);
            if (!Directory.Exists(fullPath))
                throw new DirectoryNotFoundException($"Directory not found: {path}");
            
            var entries = new List<object>();
            
            // Get directories
            var dirs = Directory.GetDirectories(fullPath, "*", recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);
            foreach (var dir in dirs)
            {
                var relativePath = ASSETS_PREFIX + GetRelativePath(Application.dataPath, dir);
                entries.Add(new
                {
                    path = relativePath,
                    name = Path.GetFileName(dir),
                    type = "folder",
                    guid = AssetDatabase.AssetPathToGUID(relativePath)
                });
            }
            
            // Get files
            var files = Directory.GetFiles(fullPath, "*", recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly)
                                 .Where(f => !f.EndsWith(".meta"));
            foreach (var file in files)
            {
                var relativePath = ASSETS_PREFIX + GetRelativePath(Application.dataPath, file);
                entries.Add(new
                {
                    path = relativePath,
                    name = Path.GetFileName(file),
                    type = "file",
                    extension = Path.GetExtension(file),
                    guid = AssetDatabase.AssetPathToGUID(relativePath)
                });
            }
            
            return new
            {
                path = path,
                entries = entries
            };
        }
        
        static object ListFolderOnWorkerThread(JObject request)
        {
            var path = request["path"]?.ToString() ?? ASSETS_PREFIX;
            var recursive = request["recursive"]?.Value<bool>() ?? false;
            
            var fullPath = Path.Combine(Application.dataPath, path.StartsWith(ASSETS_PREFIX) ? path.Substring(ASSETS_PREFIX_LENGTH) : path);
            if (!Directory.Exists(fullPath))
                throw new DirectoryNotFoundException($"Directory not found: {path}");
            
            var entries = new List<object>();
            
            // Get directories
            var dirs = Directory.GetDirectories(fullPath, "*", recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);
            foreach (var dir in dirs)
            {
                var relativePath = ASSETS_PREFIX + GetRelativePath(Application.dataPath, dir);
                entries.Add(new
                {
                    path = relativePath,
                    name = Path.GetFileName(dir),
                    type = "folder",
                    guid = "" // GUID requires AssetDatabase
                });
            }
            
            // Get files
            var files = Directory.GetFiles(fullPath, "*", recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly)
                                 .Where(f => !f.EndsWith(".meta"));
            foreach (var file in files)
            {
                var relativePath = ASSETS_PREFIX + GetRelativePath(Application.dataPath, file);
                entries.Add(new
                {
                    path = relativePath,
                    name = Path.GetFileName(file),
                    type = "file",
                    extension = Path.GetExtension(file),
                    guid = "" // GUID requires AssetDatabase
                });
            }
            
            return new
            {
                path = path,
                entries = entries
            };
        }
        
        static string GetRelativePath(string basePath, string fullPath)
        {
            if (!fullPath.StartsWith(basePath))
                return fullPath;
            
            var relativePath = fullPath.Substring(basePath.Length);
            if (relativePath.StartsWith(Path.DirectorySeparatorChar.ToString()))
                relativePath = relativePath.Substring(1);
            
            return relativePath.Replace(Path.DirectorySeparatorChar, '/');
        }
        
        static void SendResponse(HttpListenerResponse response, int statusCode, bool success, object result, string error)
        {
            response.StatusCode = statusCode;
            response.ContentType = "application/json; charset=utf-8";
            response.ContentEncoding = Encoding.UTF8;
            
            var responseData = new Dictionary<string, object>
            {
                ["success"] = success
            };
            
            if (result != null)
                responseData["result"] = result;
            
            if (!string.IsNullOrEmpty(error))
                responseData["error"] = error;
            
            var json = JsonConvert.SerializeObject(responseData);
            var buffer = Encoding.UTF8.GetBytes(json);
            
            response.ContentLength64 = buffer.Length;
            response.OutputStream.Write(buffer, 0, buffer.Length);
            response.Close();
        }
    }
}`
    });

    // UnityMCPServerWindow.cs content
    this.scripts.set('UnityMCPServerWindow.cs', {
      fileName: 'UnityMCPServerWindow.cs',
      version: '1.0.0',
      content: `using System;
using UnityEngine;
using UnityEditor;

namespace UnityMCP
{
    /// <summary>
    /// Unity MCP Server control window
    /// </summary>
    public class UnityMCPServerWindow : EditorWindow
    {
        // Version information (should match UnityHttpServer)
        private const string SCRIPT_VERSION = "1.1.0";
        
        private int serverPort = 23457;
        private bool isServerRunning = false;
        private string serverStatus = "Stopped";
        private string lastError = "";
        
        [MenuItem("Window/Unity MCP Server")]
        public static void ShowWindow()
        {
            GetWindow<UnityMCPServerWindow>("Unity MCP Server");
        }
        
        void OnEnable()
        {
            // Load saved settings
            serverPort = EditorPrefs.GetInt("UnityMCP.ServerPort", 23457);
            UpdateStatus();
        }
        
        void OnDisable()
        {
            // Save settings
            EditorPrefs.SetInt("UnityMCP.ServerPort", serverPort);
        }
        
        void OnGUI()
        {
            GUILayout.Label("Unity MCP Server Control", EditorStyles.boldLabel);
            GUILayout.Label($"Version: {SCRIPT_VERSION}", EditorStyles.miniLabel);
            
            EditorGUILayout.Space();
            
            // Server Status
            EditorGUILayout.BeginHorizontal();
            GUILayout.Label("Status:", GUILayout.Width(60));
            var statusColor = isServerRunning ? Color.green : Color.red;
            var originalColor = GUI.color;
            GUI.color = statusColor;
            GUILayout.Label(serverStatus, EditorStyles.boldLabel);
            GUI.color = originalColor;
            EditorGUILayout.EndHorizontal();
            
            EditorGUILayout.Space();
            
            // Port Configuration
            EditorGUILayout.BeginHorizontal();
            GUILayout.Label("Port:", GUILayout.Width(60));
            var newPort = EditorGUILayout.IntField(serverPort);
            if (newPort != serverPort && newPort > 0 && newPort <= 65535)
            {
                serverPort = newPort;
                EditorPrefs.SetInt("UnityMCP.ServerPort", serverPort);
            }
            EditorGUILayout.EndHorizontal();
            
            // Port validation
            if (serverPort < 1024)
            {
                EditorGUILayout.HelpBox("Warning: Ports below 1024 may require administrator privileges.", MessageType.Warning);
            }
            
            EditorGUILayout.Space();
            
            // Control Buttons
            EditorGUILayout.BeginHorizontal();
            
            GUI.enabled = !isServerRunning;
            if (GUILayout.Button("Start Server", GUILayout.Height(30)))
            {
                StartServer();
            }
            
            GUI.enabled = isServerRunning;
            if (GUILayout.Button("Stop Server", GUILayout.Height(30)))
            {
                StopServer();
            }
            
            GUI.enabled = true;
            EditorGUILayout.EndHorizontal();
            
            EditorGUILayout.Space();
            
            // Connection Info
            if (isServerRunning)
            {
                EditorGUILayout.BeginVertical(EditorStyles.helpBox);
                GUILayout.Label("Connection Information", EditorStyles.boldLabel);
                EditorGUILayout.SelectableLabel($"http://localhost:{serverPort}/");
                EditorGUILayout.EndVertical();
            }
            
            // Error Display
            if (!string.IsNullOrEmpty(lastError))
            {
                EditorGUILayout.Space();
                EditorGUILayout.HelpBox(lastError, MessageType.Error);
                if (GUILayout.Button("Clear Error"))
                {
                    lastError = "";
                }
            }
            
            EditorGUILayout.Space();
            
            // Instructions
            EditorGUILayout.BeginVertical(EditorStyles.helpBox);
            GUILayout.Label("Instructions", EditorStyles.boldLabel);
            GUILayout.Label("1. Configure the port (default: 23457)");
            GUILayout.Label("2. Click 'Start Server' to begin");
            GUILayout.Label("3. Use the MCP client to connect");
            GUILayout.Label("4. Click 'Stop Server' when done");
            EditorGUILayout.EndVertical();
        }
        
        void StartServer()
        {
            try
            {
                UnityHttpServer.Start(serverPort);
                UpdateStatus();
                lastError = "";
                Debug.Log($"[UnityMCP] Server started on port {serverPort}");
            }
            catch (Exception e)
            {
                lastError = $"Failed to start server: {e.Message}";
                Debug.LogError($"[UnityMCP] {lastError}");
            }
        }
        
        void StopServer()
        {
            try
            {
                UnityHttpServer.Shutdown();
                UpdateStatus();
                lastError = "";
                Debug.Log("[UnityMCP] Server stopped");
            }
            catch (Exception e)
            {
                lastError = $"Failed to stop server: {e.Message}";
                Debug.LogError($"[UnityMCP] {lastError}");
            }
        }
        
        void UpdateStatus()
        {
            isServerRunning = UnityHttpServer.IsRunning;
            serverStatus = isServerRunning ? $"Running on port {UnityHttpServer.CurrentPort}" : "Stopped";
            Repaint();
        }
        
        void Update()
        {
            // Update status periodically
            UpdateStatus();
        }
    }
}`
    });
  }

  /**
   * Get script by filename
   */
  async getScript(fileName: string): Promise<EmbeddedScript | null> {
    return this.scripts.get(fileName) || null;
  }

  /**
   * Get script synchronously
   */
  getScriptSync(fileName: string): EmbeddedScript | null {
    return this.scripts.get(fileName) || null;
  }

  /**
   * Write script to file with proper UTF-8 BOM for Unity compatibility
   */
  async writeScriptToFile(fileName: string, targetPath: string): Promise<void> {
    const script = await this.getScript(fileName);
    if (!script) {
      throw new Error(`Script not found: ${fileName}`);
    }

    // Ensure target directory exists
    await fs.mkdir(path.dirname(targetPath), { recursive: true });
    
    // Write with UTF-8 BOM for Unity compatibility
    const utf8BOM = Buffer.from([0xEF, 0xBB, 0xBF]);
    const contentBuffer = Buffer.from(script.content, 'utf8');
    const finalBuffer = Buffer.concat([utf8BOM, contentBuffer]);
    
    await fs.writeFile(targetPath, finalBuffer);
  }

  /**
   * Get all available script names
   */
  getAvailableScripts(): string[] {
    return Array.from(this.scripts.keys());
  }

  /**
   * Get script version
   */
  getScriptVersion(fileName: string): string | null {
    const script = this.scripts.get(fileName);
    return script?.version || null;
  }
}
```
Page 1/2FirstPrevNextLast