Skip to main content
Glama

Minecraft Bedrock MCP Server

by Mming-Lab
camera.ts17.4 kB
import { BaseTool } from '../base/tool'; import { InputSchema, ToolCallResult } from '../../types'; /** * Minecraft Camera Control Tool - Optimized for AI Usage * Provides simplified camera manipulation for Minecraft Bedrock Edition * Actions are grouped logically to reduce decision complexity for AI systems */ 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; } } }

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/Mming-Lab/minecraft-bedrock-mcp-server'

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