Skip to main content
Glama

Minecraft Bedrock MCP Server

by Mming-Lab
system.ts14.4 kB
import { BaseTool, SequenceStep } from '../base/tool'; import { ToolCallResult, InputSchema } from '../../types'; /** * Minecraftシステムツール * スコアボード、画面表示、システム機能に特化 */ export class SystemTool extends BaseTool { readonly name = 'system'; readonly description = 'System features: scoreboards (create/edit/delete objectives), screen displays (titles/subtitles/action_bars), player UI notifications. Actions: scoreboard_create/edit/delete, title_show(big_text), subtitle_show(small_text), actionbar_show(bottom_message). Perfect for displaying game stats, notifications, or creating UI elements'; readonly inputSchema: InputSchema = { type: 'object', properties: { category: { type: 'string', description: 'System category to use', enum: ['scoreboard', 'screen', 'sequence'] }, action: { type: 'string', description: 'Action to perform within the category' }, // Scoreboard parameters objective_id: { type: 'string', description: 'Scoreboard objective ID' }, display_name: { type: 'string', description: 'Display name for objective' }, player_name: { type: 'string', description: 'Target player name (optional, defaults to local player)' }, score: { type: 'number', description: 'Score value' }, display_slot: { type: 'string', description: 'Display slot for scoreboard', enum: ['sidebar', 'list', 'belowname'] }, sort_order: { type: 'string', description: 'Sort order for scoreboard', enum: ['ascending', 'descending'] }, // Screen display parameters title: { type: 'string', description: 'Title text' }, subtitle: { type: 'string', description: 'Subtitle text' }, message: { type: 'string', description: 'Action bar message' }, fade_in: { type: 'number', description: 'Fade in duration in ticks (default: 10)', default: 10 }, stay: { type: 'number', description: 'Stay duration in ticks (default: 70)', default: 70 }, fade_out: { type: 'number', description: 'Fade out duration in ticks (default: 20)', default: 20 }, steps: { type: 'array', description: 'Array of system actions for sequence. Each step should have "category" and "action" fields and relevant parameters.' } }, required: ['category', 'action'] }; /** * システム機能を実行します * * @param args - システム操作パラメータ * @param args.category - システムカテゴリ(scoreboard, screen) * @param args.action - 実行するアクション * @param args.objective_id - スコアボード目標ID * @param args.display_name - 表示名 * @param args.player_name - 対象プレイヤー名 * @param args.score - スコア値 * @param args.display_slot - 表示スロット(sidebar, belowname等) * @param args.sort_order - ソート順(ascending, descending) * @param args.title - タイトルテキスト * @param args.subtitle - サブタイトルテキスト * @param args.message - メッセージテキスト * @returns ツール実行結果 */ async execute(args: { category: string; action: string; objective_id?: string; display_name?: string; player_name?: string; score?: number; display_slot?: string; sort_order?: string; title?: string; subtitle?: string; message?: string; fade_in?: number; stay?: number; fade_out?: number; steps?: SequenceStep[]; }): Promise<ToolCallResult> { if (!this.world) { return { success: false, message: 'World not available for system operations.' }; } try { const { category, action } = args; switch (category) { case 'scoreboard': return await this.executeScoreboardOperation(action, args); case 'screen': return await this.executeScreenOperation(action, args); case 'sequence': if (!args.steps) { return this.createErrorResponse('steps array is required for sequence category'); } return await this.executeSequence(args.steps as SequenceStep[]); default: return { success: false, message: `Unknown category: ${category}. Use: scoreboard, screen, sequence` }; } } catch (error) { return { success: false, message: `System operation error: ${error instanceof Error ? error.message : String(error)}` }; } } private async executeScoreboardOperation(action: string, args: any): Promise<ToolCallResult> { const scoreboard = this.world!.scoreboard; let result: any; let message: string; switch (action) { case 'list_objectives': result = await scoreboard.getObjectives(); message = 'Scoreboard objectives listed'; break; case 'create_objective': if (!args.objective_id) return { success: false, message: 'objective_id required for create_objective' }; result = await scoreboard.addObjective(args.objective_id, args.display_name); message = `Objective ${args.objective_id} created`; break; case 'remove_objective': if (!args.objective_id) return { success: false, message: 'objective_id required for remove_objective' }; result = await scoreboard.removeObjective(args.objective_id); message = `Objective ${args.objective_id} removed`; break; case 'get_score': if (!args.objective_id) return { success: false, message: 'objective_id required for get_score' }; const targetForGet = args.player_name || this.world!.localPlayer.name; result = await scoreboard.getScore(targetForGet, args.objective_id); message = `Score retrieved for ${targetForGet} in ${args.objective_id}: ${result}`; break; case 'set_score': if (!args.objective_id || args.score === undefined) { return { success: false, message: 'objective_id and score required for set_score' }; } const targetForSet = args.player_name || this.world!.localPlayer.name; result = await scoreboard.setScore(targetForSet, args.objective_id, args.score); message = `Score set to ${args.score} for ${targetForSet} in ${args.objective_id}`; break; case 'add_score': if (!args.objective_id || args.score === undefined) { return { success: false, message: 'objective_id and score required for add_score' }; } const targetForAdd = args.player_name || this.world!.localPlayer.name; result = await scoreboard.addScore(targetForAdd, args.objective_id, args.score); message = `Added ${args.score} to ${targetForAdd} in ${args.objective_id}`; break; case 'remove_score': if (!args.objective_id || args.score === undefined) { return { success: false, message: 'objective_id and score required for remove_score' }; } const targetForRemove = args.player_name || this.world!.localPlayer.name; result = await scoreboard.removeScore(targetForRemove, args.objective_id, args.score); message = `Removed ${args.score} from ${targetForRemove} in ${args.objective_id}`; break; case 'reset_score': const targetForReset = args.player_name || this.world!.localPlayer.name; await scoreboard.resetScore(targetForReset, args.objective_id); message = `Score reset for ${targetForReset}`; break; case 'set_display': if (!args.display_slot) return { success: false, message: 'display_slot required for set_display' }; await scoreboard.setDisplay(args.display_slot as any, args.objective_id, args.sort_order as any); message = `Display set for ${args.display_slot}`; break; case 'get_all_scores': const targetForAll = args.player_name || this.world!.localPlayer.name; result = await scoreboard.getScores(targetForAll); message = `All scores retrieved for ${targetForAll}`; break; default: return { success: false, message: `Unknown scoreboard action: ${action}` }; } return { success: true, message: message, data: { category: 'scoreboard', action, result, timestamp: Date.now() } }; } private async executeScreenOperation(action: string, args: any): Promise<ToolCallResult> { // Get target player let player = this.world!.localPlayer; if (args.player_name) { const players = await this.world!.getPlayers(); const targetPlayer = players.find(p => p.name === args.player_name); if (!targetPlayer) { return { success: false, message: `Player ${args.player_name} not found` }; } player = targetPlayer; } const screen = player.onScreenDisplay; let message: string; let result: any; switch (action) { case 'show_title': if (!args.title) return { success: false, message: 'title required for show_title' }; const titleOptions = { subtitle: args.subtitle, times: { fadeIn: args.fade_in || 10, stay: args.stay || 70, fadeOut: args.fade_out || 20 } }; await screen.setTitle(args.title, titleOptions); message = `Title displayed to ${player.name}: "${args.title}"`; if (args.subtitle) message += ` with subtitle: "${args.subtitle}"`; break; case 'update_subtitle': if (!args.subtitle) return { success: false, message: 'subtitle required for update_subtitle' }; await screen.updateSubtitle(args.subtitle); message = `Subtitle updated for ${player.name}: "${args.subtitle}"`; break; case 'show_action_bar': if (!args.message) return { success: false, message: 'message required for show_action_bar' }; await screen.setActionBar(args.message); message = `Action bar shown to ${player.name}: "${args.message}"`; break; case 'set_title_duration': if (args.fade_in === undefined || args.stay === undefined || args.fade_out === undefined) { return { success: false, message: 'fade_in, stay, and fade_out required for set_title_duration' }; } await screen.setTitleDuration({ fadeIn: args.fade_in, stay: args.stay, fadeOut: args.fade_out }); message = `Title duration set for ${player.name}: fadeIn=${args.fade_in}, stay=${args.stay}, fadeOut=${args.fade_out}`; break; case 'clear_title': await screen.clearTitle(); message = `Title cleared for ${player.name}`; break; case 'reset_title': await screen.resetTitle(); message = `Title reset for ${player.name}`; break; case 'check_screen_valid': result = screen.isValid; message = `Screen display validity checked for ${player.name}: ${result}`; break; default: return { success: false, message: `Unknown screen action: ${action}` }; } return { success: true, message: message, data: { category: 'screen', action, result, targetPlayer: player.name, timestamp: Date.now() } }; } /** * システム専用のシーケンスステップ実行 * * @param step - 実行するステップ * @param index - ステップのインデックス * @returns ステップ実行結果 * * @protected * @override */ protected async executeSequenceStep(step: SequenceStep, index: number): Promise<ToolCallResult> { // wait ステップは基底クラスで処理される if (step.type === 'wait') { return await super.executeSequenceStep(step, index); } // システム特有のステップを実行 // SystemToolは category が必要なので、step.type を category に、step.action を action に変換 const systemArgs = { category: step.category || step.type, // categoryが指定されていればそれを使用、なければtypeをcategory扱い action: step.action || 'default_action', ...step }; return await this.execute(systemArgs); } }

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