@akiojin/unity-editor-mcp
Version:
MCP server for Unity Editor integration - enables AI assistants to control Unity Editor
133 lines (118 loc) • 4.58 kB
JavaScript
import { BaseToolHandler } from '../base/BaseToolHandler.js';
/**
* Handler for the get_hierarchy tool
* Gets the Unity scene hierarchy
*/
export class GetHierarchyToolHandler extends BaseToolHandler {
constructor(unityConnection) {
super(
'get_hierarchy',
'Get scene hierarchy. For large scenes, prefer nameOnly=true. With nameOnly, use maxObjects ~100–500; when requesting details, keep maxObjects ~10–50 to conserve tokens. Only enable includeComponents/includeTransform when necessary.',
{
type: 'object',
properties: {
rootPath: {
type: 'string',
description: 'Path to GameObject whose children will be the root of the returned hierarchy (e.g., "/Team_0" returns Team_0\'s children). If not specified, returns scene root objects.'
},
includeInactive: {
type: 'boolean',
description: 'Include inactive GameObjects (default: true)'
},
maxDepth: {
type: 'number',
description: 'Maximum depth to traverse from the root. 0=immediate children only (default), 1=children+grandchildren, 2=children+grandchildren+great-grandchildren, etc. (-1 for unlimited)',
minimum: -1
},
includeComponents: {
type: 'boolean',
description: 'Include component information (default: false). WARNING: Significantly increases response size - use with small maxObjects values.'
},
includeTransform: {
type: 'boolean',
description: 'Include transform information (default: false). WARNING: Increases response size - use with small maxObjects values.'
},
includeTags: {
type: 'boolean',
description: 'Include tag information (default: false)'
},
includeLayers: {
type: 'boolean',
description: 'Include layer information (default: false)'
},
nameOnly: {
type: 'boolean',
description: 'Return only names and paths for minimal data size (default: false). RECOMMENDED for large hierarchies to reduce token usage.'
},
maxObjects: {
type: 'number',
description: 'Maximum number of objects to include in response. Unity default: 100 (-1 for unlimited). RECOMMENDED: 10-50 for detailed info, 100-500 for nameOnly mode to avoid MCP token limits',
minimum: -1
}
},
required: []
}
);
this.unityConnection = unityConnection;
}
/**
* Validates the input parameters
* @param {object} params - Input parameters
* @throws {Error} If validation fails
*/
validate(params) {
super.validate(params);
// Validate maxDepth
if (params.maxDepth !== undefined) {
const depth = Number(params.maxDepth);
if (isNaN(depth) || depth < -1) {
throw new Error('maxDepth must be -1 or a positive number');
}
}
// Validate maxObjects
if (params.maxObjects !== undefined) {
const maxObjects = Number(params.maxObjects);
if (isNaN(maxObjects) || maxObjects < -1) {
throw new Error('maxObjects must be -1 or a positive number');
}
}
}
/**
* Executes the get_hierarchy command
* @param {object} params - Input parameters
* @returns {Promise<object>} Scene hierarchy
*/
async execute(params) {
// Ensure connected
if (!this.unityConnection.isConnected()) {
await this.unityConnection.connect();
}
// Send get_hierarchy command
const result = await this.unityConnection.sendCommand('get_hierarchy', params);
// Check for errors from Unity
if (result.error) {
throw new Error(result.error);
}
// Add helpful summary
if (result.hierarchy) {
result.totalObjects = this.countObjects(result.hierarchy);
result.summary = `Scene "${result.sceneName}" contains ${result.totalObjects} GameObject${result.totalObjects !== 1 ? 's' : ''} (${result.objectCount} at root level)`;
}
return result;
}
/**
* Counts total objects in hierarchy recursively
* @param {Array} hierarchy - Hierarchy array
* @returns {number} Total object count
*/
countObjects(hierarchy) {
let count = 0;
for (const node of hierarchy) {
count++; // Count this node
if (node.children && Array.isArray(node.children)) {
count += this.countObjects(node.children);
}
}
return count;
}
}