@akiojin/unity-editor-mcp
Version:
MCP server for Unity Editor integration - enables AI assistants to control Unity Editor
134 lines (121 loc) • 3.96 kB
JavaScript
/**
* Base class for all tool handlers
* Provides common functionality for validation, execution, and error handling
*/
import { logger } from '../../core/config.js';
export class BaseToolHandler {
constructor(name, description, inputSchema = {}) {
this.name = name;
this.description = description;
this.inputSchema = inputSchema;
}
/**
* Validates the input parameters against the schema
* Override this method for custom validation
* @param {object} params - Input parameters
* @throws {Error} If validation fails
*/
validate(params) {
// Basic validation - check required fields from schema
if (this.inputSchema.required) {
for (const field of this.inputSchema.required) {
if (params[field] === undefined || params[field] === null) {
throw new Error(`Missing required parameter: ${field}`);
}
}
}
}
/**
* Executes the tool logic
* Must be implemented by subclasses
* @param {object} params - Validated input parameters
* @returns {Promise<object>} Tool result
*/
async execute(params) {
throw new Error('execute() must be implemented by subclass');
}
/**
* Main handler method that orchestrates validation and execution
* @param {object} params - Input parameters
* @returns {Promise<object>} Standardized response
*/
async handle(params = {}) {
logger.debug(`[Handler ${this.name}] Starting handle() with params:`, params);
try {
// Validate parameters
logger.debug(`[Handler ${this.name}] Validating parameters...`);
this.validate(params);
logger.debug(`[Handler ${this.name}] Validation passed`);
// Execute tool logic
logger.debug(`[Handler ${this.name}] Executing tool logic...`);
const startTime = Date.now();
const result = await this.execute(params);
const duration = Date.now() - startTime;
logger.info(`[Handler ${this.name}] Execution completed in ${duration}ms`);
// Return success response in new format
return {
status: 'success',
result
};
} catch (error) {
logger.error(`[Handler ${this.name}] Error occurred: ${error.message}`);
if (error.stack) {
logger.debug(`[Handler ${this.name}] Error stack: ${error.stack}`);
}
// Return error response in new format
return {
status: 'error',
error: error.message,
code: error.code || 'TOOL_ERROR',
details: {
tool: this.name,
params: this.summarizeParams(params),
stack: process.env.NODE_ENV === 'development' ? error.stack : undefined
}
};
}
}
/**
* Summarizes parameters for error reporting
* @param {object} params - Parameters to summarize
* @returns {string} Summary string
*/
summarizeParams(params) {
if (!params || typeof params !== 'object') {
return 'No parameters';
}
const entries = Object.entries(params);
if (entries.length === 0) {
return 'Empty parameters';
}
return entries
.map(([key, value]) => {
let valueStr = '';
if (value === null) {
valueStr = 'null';
} else if (value === undefined) {
valueStr = 'undefined';
} else if (typeof value === 'string') {
// Truncate long strings
valueStr = value.length > 50 ? `"${value.substring(0, 47)}..."` : `"${value}"`;
} else if (typeof value === 'object') {
valueStr = Array.isArray(value) ? `[Array(${value.length})]` : '[Object]';
} else {
valueStr = String(value);
}
return `${key}: ${valueStr}`;
})
.join(', ');
}
/**
* Returns the tool definition for MCP
* @returns {object} Tool definition
*/
getDefinition() {
return {
name: this.name,
description: this.description,
inputSchema: this.inputSchema
};
}
}