Skip to main content
Glama

MCP Server for Unity

by zabaglione
unity-mcp-tools.ts16.6 kB
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}` }] }; } } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/zabaglione/mcp-server-unity'

If you have feedback or need assistance with the MCP directory API, please join our Discord server