camera
Control camera movements for filming, showcasing builds, or creating cinematic sequences in Minecraft Bedrock. Perform actions like move_to, smooth_move, track_entity, fade, reset, and set_mode with coordinates, angles, and animation styles.
Instructions
CAMERA control: move_to (position camera), smooth_move (animated movement), track_entity (follow targets), fade (screen effects), reset (clear camera), set_mode (view presets). Supports both individual actions and cinematic sequences. Perfect for filming, showcasing builds, or creating camera movements. Requires cheats enabled.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| action | Yes | Camera action: move_to=instant positioning, smooth_move=animated movement, track_entity=follow entities, fade=screen effects, reset=clear camera, set_mode=view presets/controls, sequence=cinematic camera sequences | |
| auto_clear | No | Automatically clear camera after action completes | |
| color | No | Fade color | black |
| duration | No | Animation time in seconds (default: 3) | |
| easing | No | Animation style: linear=constant, smooth=ease in/out, bounce=bouncy, elastic=springy | smooth |
| entity | No | Entity selector to look at or track (@p=nearest player, @e=entities) | |
| fade_hold | No | Hold time in seconds | |
| fade_in | No | Fade in time in seconds | |
| fade_out | No | Fade out time in seconds | |
| look_at_x | No | X coordinate to look at (optional) | |
| look_at_y | No | Y coordinate to look at (optional) | |
| look_at_z | No | Z coordinate to look at (optional) | |
| mode | No | View mode or control setting | |
| pitch | No | Vertical angle: -90=down, 0=horizon, 90=up | |
| shots | No | Array of camera shots for sequence action. Each shot executes after the previous one completes. Each shot object should have a "type" field (move_to, smooth_move, track_entity, fade, wait) plus relevant parameters like x,y,z coordinates, duration, etc. | |
| x | No | Camera X coordinate | |
| y | No | Camera Y coordinate | |
| yaw | No | Horizontal angle: 0=north, 90=west, -90=east, 180=south | |
| z | No | Camera Z coordinate |
Implementation Reference
- src/tools/core/camera.ts:9-440 (handler)Complete implementation of CameraTool class. Defines name='camera', inputSchema, and execute() method that switches on 'action' parameter to perform various camera operations using Minecraft Bedrock camera commands. Includes smooth animations, entity tracking, fades, modes, and sequences.export class CameraTool extends BaseTool { readonly name = 'camera'; readonly description = `CAMERA control: move_to (position camera), smooth_move (animated movement), track_entity (follow targets), fade (screen effects), reset (clear camera), set_mode (view presets). Supports both individual actions and cinematic sequences. Perfect for filming, showcasing builds, or creating camera movements. Requires cheats enabled.`; readonly inputSchema: InputSchema = { type: 'object', properties: { action: { type: 'string', enum: [ 'move_to', 'smooth_move', 'track_entity', 'fade', 'reset', 'set_mode', 'sequence' ], description: 'Camera action: move_to=instant positioning, smooth_move=animated movement, track_entity=follow entities, fade=screen effects, reset=clear camera, set_mode=view presets/controls, sequence=cinematic camera sequences' }, x: { type: 'number', description: 'Camera X coordinate' }, y: { type: 'number', description: 'Camera Y coordinate' }, z: { type: 'number', description: 'Camera Z coordinate' }, look_at_x: { type: 'number', description: 'X coordinate to look at (optional)' }, look_at_y: { type: 'number', description: 'Y coordinate to look at (optional)' }, look_at_z: { type: 'number', description: 'Z coordinate to look at (optional)' }, entity: { type: 'string', description: 'Entity selector to look at or track (@p=nearest player, @e=entities)' }, pitch: { type: 'number', minimum: -90, maximum: 90, description: 'Vertical angle: -90=down, 0=horizon, 90=up' }, yaw: { type: 'number', minimum: -180, maximum: 180, description: 'Horizontal angle: 0=north, 90=west, -90=east, 180=south' }, duration: { type: 'number', minimum: 0.1, default: 3.0, description: 'Animation time in seconds (default: 3)' }, easing: { type: 'string', enum: ['linear', 'smooth', 'bounce', 'elastic'], default: 'smooth', description: 'Animation style: linear=constant, smooth=ease in/out, bounce=bouncy, elastic=springy' }, fade_in: { type: 'number', minimum: 0, default: 1, description: 'Fade in time in seconds' }, fade_hold: { type: 'number', minimum: 0, default: 1, description: 'Hold time in seconds' }, fade_out: { type: 'number', minimum: 0, default: 1, description: 'Fade out time in seconds' }, color: { type: 'string', enum: ['black', 'white', 'red', 'green', 'blue'], default: 'black', description: 'Fade color' }, mode: { type: 'string', enum: ['first_person', 'third_person', 'front_view', 'enable_movement', 'disable_movement', 'enable_camera_control', 'disable_camera_control'], description: 'View mode or control setting' }, auto_clear: { type: 'boolean', default: true, description: 'Automatically clear camera after action completes' }, shots: { type: 'array', description: 'Array of camera shots for sequence action. Each shot executes after the previous one completes. Each shot object should have a "type" field (move_to, smooth_move, track_entity, fade, wait) plus relevant parameters like x,y,z coordinates, duration, etc.' } }, required: ['action'] }; async execute(args: any): Promise<ToolCallResult> { const { action } = args; try { switch (action) { case 'move_to': return await this.moveTo(args); case 'smooth_move': return await this.smoothMove(args); case 'track_entity': return await this.trackEntity(args); case 'fade': return await this.fadeEffect(args); case 'reset': return await this.resetCamera(); case 'set_mode': return await this.setMode(args); case 'sequence': return await this.executeCameraSequence(args); default: return this.createErrorResponse(`Unknown camera action: ${action}`); } } catch (error) { return this.createErrorResponse(`Camera error: ${error instanceof Error ? error.message : String(error)}`); } } /** * Instantly moves camera to position with optional look direction * Combines position setting with look_at coordinates, entity, or pitch/yaw angles */ private async moveTo(args: any): Promise<ToolCallResult> { const { x, y, z, look_at_x, look_at_y, look_at_z, entity, pitch, yaw } = args; if (!this.validateCoordinates(x, y, z)) { return this.createErrorResponse('Invalid camera coordinates'); } const commands: string[] = []; // Set camera position commands.push(`camera @s set minecraft:free pos ${x} ${y} ${z}`); // Set look direction based on available parameters if (look_at_x !== undefined && look_at_y !== undefined && look_at_z !== undefined) { if (!this.validateCoordinates(look_at_x, look_at_y, look_at_z)) { return this.createErrorResponse('Invalid look_at coordinates'); } commands.push(`camera @s set minecraft:free facing ${look_at_x} ${look_at_y} ${look_at_z}`); } else if (entity) { commands.push(`camera @s set minecraft:free facing ${entity}`); } else if (pitch !== undefined && yaw !== undefined) { if (pitch < -90 || pitch > 90 || yaw < -180 || yaw > 180) { return this.createErrorResponse('Invalid rotation: pitch (-90 to 90), yaw (-180 to 180)'); } commands.push(`camera @s set minecraft:free rot ${pitch} ${yaw}`); } return this.executeBatch(commands); } /** * Smoothly animates camera movement with easing * Supports movement to coordinates with look direction and automatic clearing */ private async smoothMove(args: any): Promise<ToolCallResult> { const { x, y, z, look_at_x, look_at_y, look_at_z, entity, pitch, yaw, duration = 3, easing = 'smooth', auto_clear = true } = args; if (!this.validateCoordinates(x, y, z)) { return this.createErrorResponse('Invalid camera coordinates'); } if (duration < 0.1) { return this.createErrorResponse('Duration must be at least 0.1 seconds'); } const easingMap: { [key: string]: string } = { 'linear': 'linear', 'smooth': 'in_out_cubic', 'bounce': 'out_bounce', 'elastic': 'out_elastic' }; const easingType = easingMap[easing] || 'in_out_cubic'; let command: string; // Build command based on look direction if (look_at_x !== undefined && look_at_y !== undefined && look_at_z !== undefined) { if (!this.validateCoordinates(look_at_x, look_at_y, look_at_z)) { return this.createErrorResponse('Invalid look_at coordinates'); } command = `camera @s set minecraft:free ease ${duration} ${easingType} pos ${x} ${y} ${z} facing ${look_at_x} ${look_at_y} ${look_at_z}`; } else if (entity) { command = `camera @s set minecraft:free ease ${duration} ${easingType} pos ${x} ${y} ${z} facing ${entity}`; } else if (pitch !== undefined && yaw !== undefined) { if (pitch < -90 || pitch > 90 || yaw < -180 || yaw > 180) { return this.createErrorResponse('Invalid rotation: pitch (-90 to 90), yaw (-180 to 180)'); } command = `camera @s set minecraft:free ease ${duration} ${easingType} pos ${x} ${y} ${z} rot ${pitch} ${yaw}`; } else { command = `camera @s set minecraft:free ease ${duration} ${easingType} pos ${x} ${y} ${z}`; } const result = await this.executeCommand(command); if (auto_clear && result.success) { setTimeout(async () => { await this.executeCommand('camera @s clear'); }, (duration + 0.5) * 1000); } return result; } /** * Makes camera track and follow an entity with smooth movement */ private async trackEntity(args: any): Promise<ToolCallResult> { const { entity, x, y, z, duration = 3, easing = 'smooth', auto_clear = true } = args; if (!entity) { return this.createErrorResponse('Entity selector is required for tracking'); } if (x !== undefined && y !== undefined && z !== undefined) { if (!this.validateCoordinates(x, y, z)) { return this.createErrorResponse('Invalid camera coordinates'); } } const easingMap: { [key: string]: string } = { 'linear': 'linear', 'smooth': 'in_out_cubic', 'bounce': 'out_bounce', 'elastic': 'out_elastic' }; const easingType = easingMap[easing] || 'in_out_cubic'; let command: string; if (x !== undefined && y !== undefined && z !== undefined) { command = `camera @s set minecraft:free ease ${duration} ${easingType} pos ${x} ${y} ${z} facing ${entity}`; } else { command = `camera @s set minecraft:free facing ${entity}`; } const result = await this.executeCommand(command); if (auto_clear && result.success && duration > 0) { setTimeout(async () => { await this.executeCommand('camera @s clear'); }, (duration + 0.5) * 1000); } return result; } /** * Creates screen fade effect with color and timing */ private async fadeEffect(args: any): Promise<ToolCallResult> { const { fade_in = 1, fade_hold = 1, fade_out = 1, color = 'black' } = args; if (fade_in < 0 || fade_hold < 0 || fade_out < 0) { return this.createErrorResponse('Fade times must be non-negative'); } const colorMap: { [key: string]: [number, number, number] } = { 'black': [0, 0, 0], 'white': [255, 255, 255], 'red': [255, 0, 0], 'green': [0, 255, 0], 'blue': [0, 0, 255] }; const [r, g, b] = colorMap[color] || [0, 0, 0]; const command = `camera @s fade time ${fade_in} ${fade_hold} ${fade_out} color ${r} ${g} ${b}`; return this.executeCommand(command); } /** * Resets camera to normal view */ private async resetCamera(): Promise<ToolCallResult> { return this.executeCommand('camera @s clear'); } /** * Sets view mode or controls player input permissions */ private async setMode(args: any): Promise<ToolCallResult> { const { mode } = args; if (!mode) { return this.createErrorResponse('Mode parameter is required'); } switch (mode) { case 'first_person': return this.executeCommand('camera @s set minecraft:first_person'); case 'third_person': return this.executeCommand('camera @s set minecraft:third_person'); case 'front_view': return this.executeCommand('camera @s set minecraft:third_person_front'); case 'enable_movement': return this.executeCommand('inputpermission set @s movement enabled'); case 'disable_movement': return this.executeCommand('inputpermission set @s movement disabled'); case 'enable_camera_control': return this.executeCommand('inputpermission set @s camera enabled'); case 'disable_camera_control': return this.executeCommand('inputpermission set @s camera disabled'); default: return this.createErrorResponse(`Unknown mode: ${mode}`); } } /** * Executes a sequence of camera shots with proper timing * Enables cinematic camera work by chaining multiple shots automatically */ private async executeCameraSequence(args: any): Promise<ToolCallResult> { const { shots } = args; if (!shots || !Array.isArray(shots) || shots.length === 0) { return this.createErrorResponse('Shots array is required for sequence action'); } try { let totalDuration = 0; const results: string[] = []; // Execute shots sequentially with proper timing for (let i = 0; i < shots.length; i++) { const shot = shots[i]; const shotResult = await this.executeSingleShot(shot, i); if (!shotResult.success) { return this.createErrorResponse(`Shot ${i + 1} failed: ${shotResult.message || 'Unknown error'}`); } results.push(`Shot ${i + 1}: ${shotResult.message || 'Executed'}`); // Calculate and wait for shot duration const shotDuration = this.calculateShotDuration(shot); totalDuration += shotDuration; // Wait for this shot to complete before starting the next if (i < shots.length - 1 && shotDuration > 0) { await this.delay(shotDuration); } } return this.createSuccessResponse( `Camera sequence completed: ${shots.length} shots executed over ${totalDuration.toFixed(1)}s\n` + results.join('\n') ); } catch (error) { return this.createErrorResponse(`Sequence execution failed: ${error instanceof Error ? error.message : String(error)}`); } } /** * Executes a single shot within a sequence */ private async executeSingleShot(shot: any, index: number): Promise<ToolCallResult> { const { type } = shot; switch (type) { case 'move_to': return await this.moveTo(shot); case 'smooth_move': // Disable auto_clear for sequence shots return await this.smoothMove({ ...shot, auto_clear: false }); case 'track_entity': return await this.trackEntity({ ...shot, auto_clear: false }); case 'fade': return await this.fadeEffect(shot); case 'wait': const waitTime = shot.wait_time || 1; await this.delay(waitTime); return this.createSuccessResponse(`Waited ${waitTime}s`); default: return this.createErrorResponse(`Unknown shot type: ${type}`); } } /** * Calculates the duration of a single shot for timing purposes */ private calculateShotDuration(shot: any): number { const { type } = shot; switch (type) { case 'move_to': return 0.1; // Instant movement, small buffer case 'smooth_move': case 'track_entity': return shot.duration || 3; case 'fade': return (shot.fade_in || 1) + (shot.fade_hold || 1) + (shot.fade_out || 1); case 'wait': return shot.wait_time || 1; default: return 0.1; } } }
- src/tools/core/camera.ts:13-103 (schema)Input schema defining all parameters for camera tool: action enum, coordinates, durations, easing types, fade timings, entity selectors, sequence shots array, etc.readonly inputSchema: InputSchema = { type: 'object', properties: { action: { type: 'string', enum: [ 'move_to', 'smooth_move', 'track_entity', 'fade', 'reset', 'set_mode', 'sequence' ], description: 'Camera action: move_to=instant positioning, smooth_move=animated movement, track_entity=follow entities, fade=screen effects, reset=clear camera, set_mode=view presets/controls, sequence=cinematic camera sequences' }, x: { type: 'number', description: 'Camera X coordinate' }, y: { type: 'number', description: 'Camera Y coordinate' }, z: { type: 'number', description: 'Camera Z coordinate' }, look_at_x: { type: 'number', description: 'X coordinate to look at (optional)' }, look_at_y: { type: 'number', description: 'Y coordinate to look at (optional)' }, look_at_z: { type: 'number', description: 'Z coordinate to look at (optional)' }, entity: { type: 'string', description: 'Entity selector to look at or track (@p=nearest player, @e=entities)' }, pitch: { type: 'number', minimum: -90, maximum: 90, description: 'Vertical angle: -90=down, 0=horizon, 90=up' }, yaw: { type: 'number', minimum: -180, maximum: 180, description: 'Horizontal angle: 0=north, 90=west, -90=east, 180=south' }, duration: { type: 'number', minimum: 0.1, default: 3.0, description: 'Animation time in seconds (default: 3)' }, easing: { type: 'string', enum: ['linear', 'smooth', 'bounce', 'elastic'], default: 'smooth', description: 'Animation style: linear=constant, smooth=ease in/out, bounce=bouncy, elastic=springy' }, fade_in: { type: 'number', minimum: 0, default: 1, description: 'Fade in time in seconds' }, fade_hold: { type: 'number', minimum: 0, default: 1, description: 'Hold time in seconds' }, fade_out: { type: 'number', minimum: 0, default: 1, description: 'Fade out time in seconds' }, color: { type: 'string', enum: ['black', 'white', 'red', 'green', 'blue'], default: 'black', description: 'Fade color' }, mode: { type: 'string', enum: ['first_person', 'third_person', 'front_view', 'enable_movement', 'disable_movement', 'enable_camera_control', 'disable_camera_control'], description: 'View mode or control setting' }, auto_clear: { type: 'boolean', default: true, description: 'Automatically clear camera after action completes' }, shots: { type: 'array', description: 'Array of camera shots for sequence action. Each shot executes after the previous one completes. Each shot object should have a "type" field (move_to, smooth_move, track_entity, fade, wait) plus relevant parameters like x,y,z coordinates, duration, etc.' } }, required: ['action'] };
- src/server.ts:348-372 (registration)Instantiation of CameraTool instance added to this.tools array in initializeTools() method.this.tools = [ // Socket-BE Core API ツール(推奨 - シンプルでAI使いやすい) new AgentTool(), new WorldTool(), new PlayerTool(), new BlocksTool(), new SystemTool(), new CameraTool(), new SequenceTool(), new MinecraftWikiTool(), // Advanced Building ツール(高レベル建築機能) new BuildCubeTool(), // ✅ 完全動作 new BuildLineTool(), // ✅ 完全動作 new BuildSphereTool(), // ✅ 完全動作 new BuildCylinderTool(), // ✅ 修正済み new BuildParaboloidTool(), // ✅ 基本動作 new BuildHyperboloidTool(), // ✅ 基本動作 new BuildRotateTool(), // ✅ 基本動作 new BuildTransformTool(), // ✅ 基本動作 new BuildTorusTool(), // ✅ 修正完了 new BuildHelixTool(), // ✅ 修正完了 new BuildEllipsoidTool(), // ✅ 修正完了 new BuildBezierTool(), // ✅ 新規追加(可変制御点ベジェ曲線) ];
- src/server.ts:494-573 (registration)Registration loop in registerModularTools() that registers all tools including 'camera' to MCP server using tool.name, converted schema, and wrapper around tool.execute()this.tools.forEach((tool) => { // inputSchemaをZod形式に変換(SchemaToZodConverterを使用) const zodSchema = schemaConverter.convert(tool.inputSchema); // ツールを登録 this.mcpServer.registerTool( tool.name, { title: tool.name, description: tool.description, inputSchema: zodSchema, }, async (args: any) => { try { const result = await tool.execute(args); let responseText: string; if (result.success) { // 建築ツールの場合は最適化 if (tool.name.startsWith('build_')) { const optimized = optimizeBuildResult(result); responseText = `✅ ${optimized.message}`; if (optimized.summary) { responseText += `\n\n📊 Summary:\n${JSON.stringify(optimized.summary, null, 2)}`; } } else { // 通常ツールの場合 responseText = result.message || `Tool ${tool.name} executed successfully`; if (result.data) { // データサイズチェック const dataStr = JSON.stringify(result.data, null, 2); const sizeWarning = checkResponseSize(dataStr); if (sizeWarning) { // 大きすぎる場合はデータタイプのみ表示 responseText += `\n\n${sizeWarning}`; responseText += `\nData type: ${Array.isArray(result.data) ? `Array[${result.data.length}]` : typeof result.data}`; } else { responseText += `\n\nData: ${dataStr}`; } } } } else { // エラーメッセージにヒントを追加 const errorMsg = result.message || "Tool execution failed"; const enrichedError = enrichErrorWithHints(errorMsg); responseText = `❌ ${enrichedError}`; if (result.data) { responseText += `\n\nDetails:\n${JSON.stringify(result.data, null, 2)}`; } } return { content: [ { type: "text", text: responseText, }, ], }; } catch (error) { const errorMsg = error instanceof Error ? error.message : String(error); const errorStack = error instanceof Error ? error.stack : undefined; const exceptionMessage = `Tool execution failed with exception: ${errorMsg}${errorStack ? `\n\nStack trace:\n${errorStack}` : ""}`; return { content: [ { type: "text", text: `❌ ${exceptionMessage}`, }, ], }; } } ); });