Skip to main content
Glama

Minecraft Bedrock MCP Server

by Mming-Lab
build-transform.ts16.6 kB
import { BaseTool } from '../../base/tool'; import { ToolCallResult, InputSchema } from '../../../types'; /** * 構造物変形・複製ツール * * @description * 指定された領域の構造物をコピー、回転、鏡像、スケール変換します。 * 建物の複製、鏡像コピー、サイズ変更などの構造物変換操作に最適で、 * 各種の幾何学的変換(コピー、回転、鏡像、スケール)をサポートします。 * * @extends BaseTool * * @example * ```typescript * const tool = new BuildTransformTool(); * * // 家をコピー * await tool.execute({ * sourceCorner1X: 0, sourceCorner1Y: 64, sourceCorner1Z: 0, * sourceCorner2X: 10, sourceCorner2Y: 70, sourceCorner2Z: 10, * targetX: 20, targetY: 64, targetZ: 0, * transformation: "copy" * }); * * // 塔をX軸で鏡像反転してコピー * await tool.execute({ * sourceCorner1X: 0, sourceCorner1Y: 64, sourceCorner1Z: 0, * sourceCorner2X: 5, sourceCorner2Y: 80, sourceCorner2Z: 5, * targetX: 10, targetY: 64, targetZ: 0, * transformation: "mirror_x", * material: "cobblestone" * }); * ``` * * @since 1.0.0 * @author mcbk-mcp contributors */ export class BuildTransformTool extends BaseTool { readonly name = 'build_transform'; readonly description = 'Transform structures by copying, rotating, mirroring, or scaling. Perfect for duplicating buildings, creating mirror images, or making scaled versions. Popular options: copy=duplicate, mirror_x=flip horizontally, scale_up=make 2x bigger'; readonly inputSchema: InputSchema = { type: 'object', properties: { action: { type: 'string', description: 'Build action to perform', enum: ['build'], default: 'build' }, sourceCorner1X: { type: 'number', description: 'Source region corner 1 X coordinate' }, sourceCorner1Y: { type: 'number', description: 'Source region corner 1 Y coordinate' }, sourceCorner1Z: { type: 'number', description: 'Source region corner 1 Z coordinate' }, sourceCorner2X: { type: 'number', description: 'Source region corner 2 X coordinate' }, sourceCorner2Y: { type: 'number', description: 'Source region corner 2 Y coordinate' }, sourceCorner2Z: { type: 'number', description: 'Source region corner 2 Z coordinate' }, targetX: { type: 'number', description: 'Target location X coordinate' }, targetY: { type: 'number', description: 'Target location Y coordinate' }, targetZ: { type: 'number', description: 'Target location Z coordinate' }, transformation: { type: 'string', description: 'Transformation type: copy=exact duplicate, rotate_90/180/270=turn horizontally, mirror_x=flip east-west, mirror_y=flip up-down, mirror_z=flip north-south, scale_up=double size, scale_down=half size', enum: ['copy', 'rotate_90', 'rotate_180', 'rotate_270', 'mirror_x', 'mirror_y', 'mirror_z', 'scale_up', 'scale_down'] }, material: { type: 'string', description: 'Block type for the transformed structure (leave empty to copy original blocks, or specify material like stone, wood, etc.)', default: '' } }, required: ['sourceCorner1X', 'sourceCorner1Y', 'sourceCorner1Z', 'sourceCorner2X', 'sourceCorner2Y', 'sourceCorner2Z', 'targetX', 'targetY', 'targetZ', 'transformation'] }; /** * 座標を指定された変形に従って変換する */ private transformCoordinate( x: number, y: number, z: number, transformation: string, sourceWidth: number, sourceHeight: number, sourceDepth: number ): { x: number, y: number, z: number } { switch (transformation) { case 'copy': return { x, y, z }; case 'rotate_90': // Y軸周りに90度回転 (X,Z平面) return { x: -z, y, z: x }; case 'rotate_180': // Y軸周りに180度回転 return { x: -x, y, z: -z }; case 'rotate_270': // Y軸周りに270度回転 return { x: z, y, z: -x }; case 'mirror_x': // X軸に対して鏡像 return { x: sourceWidth - 1 - x, y, z }; case 'mirror_y': // Y軸に対して鏡像 return { x, y: sourceHeight - 1 - y, z }; case 'mirror_z': // Z軸に対して鏡像 return { x, y, z: sourceDepth - 1 - z }; case 'scale_up': // 2倍スケール return { x: x * 2, y: y * 2, z: z * 2 }; case 'scale_down': // 1/2スケール return { x: Math.floor(x / 2), y: Math.floor(y / 2), z: Math.floor(z / 2) }; default: return { x, y, z }; } } /** * 構造物を変形・複製します * * @param args - 変形パラメータ * @param args.sourceCorner1X - ソース領域の角点1のX座標 * @param args.sourceCorner1Y - ソース領域の角点1のY座標 * @param args.sourceCorner1Z - ソース領域の角点1のZ座標 * @param args.sourceCorner2X - ソース領域の角点2のX座標 * @param args.sourceCorner2Y - ソース領域の角点2のY座標 * @param args.sourceCorner2Z - ソース領域の角点2のZ座標 * @param args.targetX - ターゲット位置のX座標 * @param args.targetY - ターゲット位置のY座標 * @param args.targetZ - ターゲット位置のZ座標 * @param args.transformation - 変形タイプ(copy, rotate_90/180/270, mirror_x/y/z, scale_up/down) * @param args.material - 変形後の構造物に使用するブロック素材(空文字の場合は元のブロックをコピー) * @returns 変形実行結果 * * @throws 変形タイプが無効な値の場合 * @throws 座標が有効範囲を超える場合 * @throws 変形後の構造物が大きすぎる場合(10000ブロック超過) * @throws 変形後に配置するブロックがない場合 * @throws 簡単コピー以外で素材が指定されていない場合 * * @example * ```typescript * // 建物をスケールアップしてコピー * const result = await tool.execute({ * sourceCorner1X: 0, sourceCorner1Y: 64, sourceCorner1Z: 0, * sourceCorner2X: 5, sourceCorner2Y: 70, sourceCorner2Z: 5, * targetX: 20, targetY: 64, targetZ: 20, * transformation: "scale_up", * material: "stone_bricks" * }); * * if (result.success) { * console.log(`変形完了: ${result.data.blocksPlaced}ブロック配置`); * } * ``` */ async execute(args: { action?: string; sourceCorner1X: number; sourceCorner1Y: number; sourceCorner1Z: number; sourceCorner2X: number; sourceCorner2Y: number; sourceCorner2Z: number; targetX: number; targetY: number; targetZ: number; transformation: string; material?: string; }): Promise<ToolCallResult> { try { // Socket-BE API接続確認 if (!this.world) { return { success: false, message: "World not available. Ensure Minecraft is connected." }; } const { action = 'build', sourceCorner1X, sourceCorner1Y, sourceCorner1Z, sourceCorner2X, sourceCorner2Y, sourceCorner2Z, targetX, targetY, targetZ, transformation, material = '' } = args; // actionパラメータをサポート(現在は build のみ) if (action !== 'build') { return this.createErrorResponse(`Unknown action: ${action}. Only 'build' is supported.`); } // 座標の整数化 const source1 = { x: this.normalizeCoordinate(sourceCorner1X), y: this.normalizeCoordinate(sourceCorner1Y), z: this.normalizeCoordinate(sourceCorner1Z) }; const source2 = { x: this.normalizeCoordinate(sourceCorner2X), y: this.normalizeCoordinate(sourceCorner2Y), z: this.normalizeCoordinate(sourceCorner2Z) }; const target = { x: this.normalizeCoordinate(targetX), y: this.normalizeCoordinate(targetY), z: this.normalizeCoordinate(targetZ) }; // パラメータ検証 const validTransformations = ['copy', 'rotate_90', 'rotate_180', 'rotate_270', 'mirror_x', 'mirror_y', 'mirror_z', 'scale_up', 'scale_down']; if (!validTransformations.includes(transformation)) { return this.createErrorResponse('Invalid transformation type'); } // ソース領域の範囲を計算 const minX = Math.min(source1.x, source2.x); const maxX = Math.max(source1.x, source2.x); const minY = Math.min(source1.y, source2.y); const maxY = Math.max(source1.y, source2.y); const minZ = Math.min(source1.z, source2.z); const maxZ = Math.max(source1.z, source2.z); const sourceWidth = maxX - minX + 1; const sourceHeight = maxY - minY + 1; const sourceDepth = maxZ - minZ + 1; // 座標検証 if (!this.validateCoordinates(minX, minY, minZ) || !this.validateCoordinates(maxX, maxY, maxZ) || !this.validateCoordinates(target.x, target.y, target.z)) { return this.createErrorResponse('Coordinates extend beyond valid range'); } // ブロック数の事前チェック let totalBlocks = sourceWidth * sourceHeight * sourceDepth; if (transformation === 'scale_up') { totalBlocks *= 8; // 2倍スケールなので8倍のブロック数 } if (totalBlocks > 10000) { return this.createErrorResponse('Structure too large after transformation (maximum 10000 blocks)'); } const commands: string[] = []; let blocksPlaced = 0; const placedPositions = new Set<string>(); // 材料が指定されている場合 if (material && material.trim() !== '') { const blockId = this.normalizeBlockId(material); // ソース領域の各座標を変形して配置 for (let x = 0; x < sourceWidth; x++) { for (let y = 0; y < sourceHeight; y++) { for (let z = 0; z < sourceDepth; z++) { // 座標を変形 const transformed = this.transformCoordinate( x, y, z, transformation, sourceWidth, sourceHeight, sourceDepth ); // ターゲット位置に配置 const finalX = target.x + transformed.x; const finalY = target.y + transformed.y; const finalZ = target.z + transformed.z; // 座標が有効範囲内かチェック if (this.validateCoordinates(finalX, finalY, finalZ)) { const positionKey = `${finalX},${finalY},${finalZ}`; if (!placedPositions.has(positionKey)) { placedPositions.add(positionKey); commands.push(`setblock ${finalX} ${finalY} ${finalZ} ${blockId}`); blocksPlaced++; } } } } } } else { // 材料が指定されていない場合はcloneコマンドを使用(Minecraft Bedrock Editionの場合) if (transformation === 'copy') { // 単純なコピーの場合 commands.push(`clone ${minX} ${minY} ${minZ} ${maxX} ${maxY} ${maxZ} ${target.x} ${target.y} ${target.z}`); blocksPlaced = totalBlocks; } else { return this.createErrorResponse('Material must be specified for transformations other than simple copy'); } } if (commands.length === 0) { return this.createErrorResponse('No valid blocks to place after transformation'); } try { // Socket-BE APIを使用した実装 let actualBlocksPlaced = 0; // コマンド配列をSocket-BE API呼び出しに変換 for (const command of commands) { if (command.startsWith('setblock ')) { const parts = command.split(' '); if (parts.length >= 5) { const x = parseInt(parts[1]); const y = parseInt(parts[2]); const z = parseInt(parts[3]); const block = parts[4]; await this.world.setBlock({x, y, z}, block); actualBlocksPlaced++; if (actualBlocksPlaced > 5000) { return this.createErrorResponse('Too many blocks to place (maximum 5000)'); } } } else if (command.startsWith('clone ')) { // cloneコマンドはSocket-BE APIではサポートされていないため、コマンドで実行 await this.executeCommand(command); actualBlocksPlaced = blocksPlaced; // 予想値を使用 } } const materialInfo = material && material.trim() !== '' ? ` with ${this.normalizeBlockId(material)}` : ''; return this.createSuccessResponse( `Structure transformed using ${transformation}${materialInfo}. Processed ${totalBlocks} original blocks, placed ${actualBlocksPlaced} blocks at target (${target.x},${target.y},${target.z}).`, { type: 'transformation', sourceRegion: { corner1: source1, corner2: source2, dimensions: { width: sourceWidth, height: sourceHeight, depth: sourceDepth } }, target: target, transformation: transformation, material: material ? this.normalizeBlockId(material) : null, originalBlocks: totalBlocks, blocksPlaced: actualBlocksPlaced, apiUsed: 'Socket-BE + Command' } ); } catch (buildError) { return this.createErrorResponse(`Building error: ${buildError instanceof Error ? buildError.message : String(buildError)}`); } } catch (error) { return this.createErrorResponse( `Error transforming structure: ${error instanceof Error ? error.message : String(error)}` ); } } }

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