Files
FreewayGamesTest/Assets/Synaptic AI Pro/MCPServer/tools/advanced-tools.js
T

542 lines
19 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { z } from 'zod';
import { contextManager, analyzeUserIntent } from './context-manager.js';
// ===== 高度な対話・計画ツール =====
export function registerAdvancedTools(mcpServer, sendUnityCommand) {
// プロジェクト計画ツール
mcpServer.registerTool('unity_plan_project', {
title: 'Plan Unity Project',
description: 'Create a detailed plan and todo list for a Unity project based on natural language description',
inputSchema: {
description: z.string().describe('Natural language description of what to create'),
projectType: z.enum(['game', 'tool', 'simulation', 'visualization', 'prototype']).optional(),
complexity: z.enum(['simple', 'medium', 'complex']).optional().default('medium')
}
}, async (params) => {
const plan = await analyzeAndPlanProject(params.description);
await sendUnityCommand('create_project_plan', plan);
return {
content: [{
type: 'text',
text: `プロジェクト計画を作成しました:\n${formatProjectPlan(plan)}`
}]
};
});
// タスク分解ツール
mcpServer.registerTool('unity_decompose_task', {
title: 'Decompose Unity Task',
description: 'Break down a complex task into smaller, manageable subtasks',
inputSchema: {
task: z.string().describe('Task to decompose'),
context: z.string().optional().describe('Additional context or constraints'),
maxDepth: z.number().optional().default(3)
}
}, async (params) => {
const subtasks = await decomposeTask(params.task, params.context);
await sendUnityCommand('create_task_list', { tasks: subtasks });
return {
content: [{
type: 'text',
text: `タスクを${subtasks.length}個のサブタスクに分解しました:\n${formatTaskList(subtasks)}`
}]
};
});
// バッチ実行ツール
mcpServer.registerTool('unity_execute_batch', {
title: 'Execute Multiple Unity Operations',
description: 'Execute a series of Unity operations in sequence with progress feedback',
inputSchema: {
tasks: z.array(z.object({
tool: z.string().describe('Tool name to execute'),
parameters: z.record(z.any()).describe('Parameters for the tool'),
description: z.string().describe('Human readable description')
})),
progressFeedback: z.boolean().default(true).describe('Send progress updates')
}
}, async (params) => {
const results = [];
const totalTasks = params.tasks.length;
for (let i = 0; i < params.tasks.length; i++) {
const task = params.tasks[i];
try {
// 進捗フィードバック
if (params.progressFeedback) {
console.log(`[Batch ${i+1}/${totalTasks}] ${task.description}`);
}
// ツール実行
const result = await sendUnityCommand(task.tool, task.parameters);
results.push({
task: task.description,
success: result.success,
result: result.result || result.error,
index: i + 1
});
// エラーの場合は継続するかどうか判定
if (!result.success) {
console.error(`Task ${i+1} failed: ${result.error}`);
// 重要でないエラーは継続、重大なエラーは停止
if (result.error?.includes('not found') || result.error?.includes('Unknown operation')) {
break;
}
}
// 短い間隔をあける(Unity側の処理待ち)
await new Promise(resolve => setTimeout(resolve, 200));
} catch (error) {
results.push({
task: task.description,
success: false,
result: error.message,
index: i + 1
});
console.error(`Batch execution error: ${error.message}`);
}
}
const successCount = results.filter(r => r.success).length;
const summary = `バッチ実行完了: ${successCount}/${totalTasks}個のタスクが成功\n\n` +
results.map(r => `${r.index}. ${r.task}: ${r.success ? '✅' : '❌'} ${r.result}`).join('\n');
return {
content: [{
type: 'text',
text: summary
}]
};
});
// インテリジェント実装ツール
mcpServer.registerTool('unity_implement_feature', {
title: 'Implement Unity Feature',
description: 'Intelligently implement a feature based on description and context',
inputSchema: {
feature: z.string().describe('Feature description'),
requirements: z.array(z.string()).optional().describe('Specific requirements'),
constraints: z.array(z.string()).optional().describe('Constraints or limitations'),
style: z.enum(['minimal', 'standard', 'detailed']).optional().default('standard')
}
}, async (params) => {
const implementation = await planFeatureImplementation(params);
const steps = implementation.steps;
// 各ステップを順番に実行
for (const step of steps) {
await executeImplementationStep(step, sendUnityCommand);
}
return {
content: [{
type: 'text',
text: `機能「${params.feature}」を実装しました。\n実行したステップ:\n${steps.map((s, i) => `${i+1}. ${s.description}`).join('\n')}`
}]
};
});
// コンテキスト保持ツール
mcpServer.registerTool('unity_set_context', {
title: 'Set Project Context',
description: 'Set or update the current project context for more intelligent responses',
inputSchema: {
projectName: z.string().optional(),
projectType: z.string().optional(),
currentPhase: z.enum(['planning', 'prototyping', 'development', 'testing', 'polish']).optional(),
technologies: z.array(z.string()).optional(),
goals: z.array(z.string()).optional()
}
}, async (params) => {
await updateProjectContext(params);
return {
content: [{
type: 'text',
text: `プロジェクトコンテキストを更新しました:\n${JSON.stringify(params, null, 2)}`
}]
};
});
// 進捗確認ツール
mcpServer.registerTool('unity_check_progress', {
title: 'Check Project Progress',
description: 'Check the current progress of tasks and implementations',
inputSchema: {
scope: z.enum(['all', 'current', 'completed', 'pending']).optional().default('current'),
detailed: z.boolean().optional().default(false)
}
}, async (params) => {
const progress = await getProjectProgress(params.scope);
return {
content: [{
type: 'text',
text: formatProgressReport(progress, params.detailed)
}]
};
});
}
// ===== ヘルパー関数 =====
async function analyzeAndPlanProject(description) {
// 自然言語の説明を分析してプロジェクト計画を生成
const plan = {
title: extractProjectTitle(description),
overview: description,
phases: [],
tasks: [],
components: [],
assets: []
};
// キーワード分析
const keywords = extractKeywords(description);
// ゲームプロジェクトの例
if (keywords.includes('ゲーム') || keywords.includes('game')) {
plan.phases = [
{ name: 'セットアップ', tasks: ['シーン作成', 'フォルダ構造作成'] },
{ name: 'プロトタイプ', tasks: ['基本操作実装', 'コアメカニクス'] },
{ name: '本実装', tasks: ['UI作成', 'ゲームロジック', 'エフェクト'] },
{ name: '仕上げ', tasks: ['バランス調整', 'パフォーマンス最適化'] }
];
}
// 具体的なタスクを生成
plan.tasks = generateTasksFromDescription(description, keywords);
return plan;
}
async function decomposeTask(task, context) {
const subtasks = [];
// タスクの種類を判定
const taskType = identifyTaskType(task);
switch (taskType) {
case 'ui_creation':
subtasks.push(
{ name: 'UIレイアウト設計', priority: 'high' },
{ name: 'Canvas作成', priority: 'high' },
{ name: 'UI要素配置', priority: 'medium' },
{ name: 'スタイル適用', priority: 'low' },
{ name: 'インタラクション実装', priority: 'medium' }
);
break;
case 'character_creation':
subtasks.push(
{ name: 'キャラクターGameObject作成', priority: 'high' },
{ name: 'モデル/スプライト設定', priority: 'high' },
{ name: 'Collider追加', priority: 'medium' },
{ name: '移動スクリプト作成', priority: 'high' },
{ name: 'アニメーション設定', priority: 'medium' }
);
break;
case 'system_creation':
subtasks.push(
{ name: 'システム設計', priority: 'high' },
{ name: 'コアクラス作成', priority: 'high' },
{ name: 'インターフェース定義', priority: 'medium' },
{ name: 'テスト実装', priority: 'medium' },
{ name: 'ドキュメント作成', priority: 'low' }
);
break;
default:
// 一般的なタスク分解
subtasks.push(
{ name: '要件分析', priority: 'high' },
{ name: '設計', priority: 'high' },
{ name: '実装', priority: 'high' },
{ name: 'テスト', priority: 'medium' },
{ name: '最適化', priority: 'low' }
);
}
return subtasks;
}
async function planFeatureImplementation(params) {
const { feature, requirements, constraints } = params;
const steps = [];
// 機能の種類を分析
const featureAnalysis = analyzeFeature(feature);
// 実装ステップを生成
if (featureAnalysis.needsUI) {
steps.push({
type: 'create_ui',
description: 'UI要素の作成',
params: {
elementType: featureAnalysis.uiType || 'Panel',
name: featureAnalysis.name + '_UI'
}
});
}
if (featureAnalysis.needsScript) {
steps.push({
type: 'create_script',
description: 'スクリプトの作成',
params: {
name: featureAnalysis.name + 'Controller',
template: featureAnalysis.scriptTemplate || 'MonoBehaviour'
}
});
}
if (featureAnalysis.needsGameObject) {
steps.push({
type: 'create_gameobject',
description: 'GameObjectの作成',
params: {
objectType: featureAnalysis.objectType || 'Empty',
name: featureAnalysis.name
}
});
}
// 要件に基づいて追加ステップ
if (requirements) {
requirements.forEach(req => {
const additionalSteps = generateStepsFromRequirement(req);
steps.push(...additionalSteps);
});
}
return { steps };
}
async function executeImplementationStep(step, sendUnityCommand) {
console.error(`Executing step: ${step.description}`);
await sendUnityCommand(step.type, step.params);
// ステップ間の待機
await new Promise(resolve => setTimeout(resolve, 500));
}
// プロジェクトコンテキスト管理
const projectContext = {
projectName: '',
projectType: '',
currentPhase: 'planning',
technologies: [],
goals: [],
completedTasks: [],
pendingTasks: []
};
async function updateProjectContext(params) {
Object.assign(projectContext, params);
}
async function getProjectProgress(scope) {
return {
total: projectContext.pendingTasks.length + projectContext.completedTasks.length,
completed: projectContext.completedTasks.length,
pending: projectContext.pendingTasks.length,
tasks: scope === 'all' ?
[...projectContext.completedTasks, ...projectContext.pendingTasks] :
scope === 'completed' ? projectContext.completedTasks :
scope === 'pending' ? projectContext.pendingTasks :
projectContext.pendingTasks.slice(0, 5)
};
}
// ===== ユーティリティ関数 =====
function extractProjectTitle(description) {
// 「〜を作りたい」「〜のような」などのパターンから抽出
const patterns = [
/「(.+?)」/,
/(.+?)を作りたい/,
/(.+?)のような/,
/(.+?)みたいな/
];
for (const pattern of patterns) {
const match = description.match(pattern);
if (match) return match[1];
}
return 'Unityプロジェクト';
}
function extractKeywords(text) {
const keywords = [];
const patterns = {
game: ['ゲーム', 'game', 'プレイ', 'play'],
ui: ['UI', 'ボタン', 'メニュー', 'button', 'menu'],
character: ['キャラ', 'プレイヤー', 'character', 'player'],
system: ['システム', 'system', '機能', 'feature']
};
for (const [category, words] of Object.entries(patterns)) {
if (words.some(word => text.toLowerCase().includes(word))) {
keywords.push(category);
}
}
return keywords;
}
function identifyTaskType(task) {
const taskLower = task.toLowerCase();
if (taskLower.includes('ui') || taskLower.includes('ボタン') || taskLower.includes('メニュー')) {
return 'ui_creation';
} else if (taskLower.includes('キャラ') || taskLower.includes('プレイヤー')) {
return 'character_creation';
} else if (taskLower.includes('システム') || taskLower.includes('機能')) {
return 'system_creation';
}
return 'general';
}
function analyzeFeature(feature) {
const analysis = {
name: feature.split(/[\s ]+/)[0],
needsUI: false,
needsScript: false,
needsGameObject: false,
uiType: null,
scriptTemplate: null,
objectType: null
};
const featureLower = feature.toLowerCase();
// UI関連
if (featureLower.includes('ボタン') || featureLower.includes('button')) {
analysis.needsUI = true;
analysis.uiType = 'Button';
} else if (featureLower.includes('メニュー') || featureLower.includes('menu')) {
analysis.needsUI = true;
analysis.uiType = 'Panel';
}
// スクリプト関連
if (featureLower.includes('動') || featureLower.includes('制御') || featureLower.includes('システム')) {
analysis.needsScript = true;
}
// GameObject関連
if (featureLower.includes('オブジェクト') || featureLower.includes('キャラ')) {
analysis.needsGameObject = true;
}
return analysis;
}
function generateTasksFromDescription(description, keywords) {
const tasks = [];
let taskId = 1;
// 基本タスク
tasks.push({
id: taskId++,
name: 'プロジェクトセットアップ',
status: 'pending',
priority: 'high'
});
// キーワードに基づくタスク生成
if (keywords.includes('game')) {
tasks.push(
{ id: taskId++, name: 'ゲームマネージャー作成', status: 'pending', priority: 'high' },
{ id: taskId++, name: 'プレイヤーコントローラー実装', status: 'pending', priority: 'high' },
{ id: taskId++, name: 'ゲームループ実装', status: 'pending', priority: 'medium' }
);
}
if (keywords.includes('ui')) {
tasks.push(
{ id: taskId++, name: 'UIシステム構築', status: 'pending', priority: 'high' },
{ id: taskId++, name: 'メインメニュー作成', status: 'pending', priority: 'medium' }
);
}
return tasks;
}
function generateStepsFromRequirement(requirement) {
const steps = [];
const reqLower = requirement.toLowerCase();
if (reqLower.includes('アニメーション') || reqLower.includes('animation')) {
steps.push({
type: 'create_animation',
description: 'アニメーション設定',
params: { animationName: 'DefaultAnimation' }
});
}
if (reqLower.includes('物理') || reqLower.includes('physics')) {
steps.push({
type: 'setup_physics',
description: '物理演算設定',
params: { addRigidbody: true }
});
}
return steps;
}
function formatProjectPlan(plan) {
let output = `📋 ${plan.title}\n\n`;
output += `概要: ${plan.overview}\n\n`;
if (plan.phases.length > 0) {
output += '📅 フェーズ:\n';
plan.phases.forEach((phase, i) => {
output += `${i+1}. ${phase.name}\n`;
phase.tasks.forEach(task => {
output += ` - ${task}\n`;
});
});
}
if (plan.tasks.length > 0) {
output += '\n✅ タスク一覧:\n';
plan.tasks.forEach(task => {
output += `- [${task.status === 'completed' ? 'x' : ' '}] ${task.name} (${task.priority})\n`;
});
}
return output;
}
function formatTaskList(tasks) {
return tasks.map((task, i) =>
`${i+1}. ${task.name} [${task.priority}]`
).join('\n');
}
function formatProgressReport(progress, detailed) {
let report = `📊 プロジェクト進捗\n`;
report += `完了: ${progress.completed}/${progress.total} (${Math.round(progress.completed/progress.total*100)}%)\n\n`;
if (detailed && progress.tasks.length > 0) {
report += '📋 タスク詳細:\n';
progress.tasks.forEach(task => {
const status = task.status === 'completed' ? '✅' : '⏳';
report += `${status} ${task.name}\n`;
});
}
return report;
}