Skip to main content
Glama

Minecraft Bedrock MCP Server

by Mming-Lab
minecraft-wiki.ts21.4 kB
import { BaseTool, SequenceStep } from '../base/tool'; import { ToolCallResult, InputSchema } from '../../types'; /** * Minecraft Wiki検索ツール * MediaWiki APIを使用してMinecraft Wikiから情報を検索し、 * Bedrock Edition特有の情報を提供します */ export class MinecraftWikiTool extends BaseTool { readonly name = 'minecraft_wiki'; readonly description = 'Search Minecraft Wiki for Bedrock Edition and Education Edition commands, blocks, items, entities, and game mechanics. Excludes Java Edition-only features. Returns accurate, up-to-date information from the official Minecraft Wiki. Use sequence action for step-by-step information gathering: 1) search for topic, 2) get specific page, 3) get detailed section - reduces overwhelming responses.'; readonly inputSchema: InputSchema = { type: 'object', properties: { action: { type: 'string', description: 'Wiki search action to perform', enum: ['search', 'get_page', 'get_page_summary', 'get_section', 'sequence'] }, query: { type: 'string', description: 'Search query (e.g., "give command", "diamond sword", "teleport", "setblock")' }, page_title: { type: 'string', description: 'Specific wiki page title to retrieve' }, title: { type: 'string', description: 'Wiki page title (alternative parameter name for compatibility)' }, section: { type: 'string', description: 'Specific section name within a page' }, focus: { type: 'string', description: 'Focus area for search results', enum: ['commands', 'blocks', 'items', 'entities', 'mechanics', 'bedrock_edition', 'education_edition'] }, steps: { type: 'array', description: 'Array of wiki search actions for sequence. Example: [{"type":"search","query":"give command","focus":"commands"},{"type":"get_page","title":"Commands/give"},{"type":"get_section","title":"Commands/give","section":"Syntax"}]. Use this to break down information gathering into manageable steps instead of getting overwhelming responses.', items: { type: 'object', description: 'Wiki sequence step', properties: { type: { type: 'string', enum: ['search', 'get_page', 'get_page_summary', 'get_section', 'wait'], description: 'Wiki action type' }, query: { type: 'string', description: 'Search query for search action' }, title: { type: 'string', description: 'Page title for get_page/get_section actions' }, page_title: { type: 'string', description: 'Alternative page title parameter' }, section: { type: 'string', description: 'Section name for get_section action' }, focus: { type: 'string', enum: ['commands', 'blocks', 'items', 'entities', 'mechanics', 'bedrock_edition', 'education_edition'], description: 'Search focus area' }, wait_time: { type: 'number', description: 'Wait time in seconds after this step' } }, required: ['type'] } } }, required: ['action'] }; /** * Minecraft Wiki検索を実行します * * @param args - Wiki検索パラメータ * @param args.action - 実行するアクション(search, get_page, get_page_summary, get_section) * @param args.query - 検索クエリ * @param args.page_title - 取得するページタイトル * @param args.title - 取得するページタイトル(互換性用) * @param args.section - 取得するセクション名 * @param args.focus - 検索フォーカス(commands, blocks, items等) * @returns Wiki検索結果 */ async execute(args: { action: string; query?: string; page_title?: string; title?: string; section?: string; focus?: string; steps?: SequenceStep[]; }): Promise<ToolCallResult> { try { const { action } = args; let result: any; let message: string; switch (action) { case 'search': if (!args.query) { return { success: false, message: 'Query required for search action' }; } result = await this.searchWiki(args.query, args.focus); message = `Wiki search completed for: "${args.query}"`; break; case 'get_page': const pageTitle = args.page_title || args.title; if (!pageTitle) { return { success: false, message: 'Page title required for get_page action' }; } result = await this.getPageContent(pageTitle); message = `Retrieved wiki page: "${pageTitle}"`; break; case 'get_page_summary': const summaryTitle = args.page_title || args.title; if (!summaryTitle) { return { success: false, message: 'Page title required for get_page_summary action' }; } result = await this.getPageSummary(summaryTitle); message = `Retrieved wiki page summary: "${summaryTitle}"`; break; case 'get_section': const sectionPageTitle = args.page_title || args.title; if (!sectionPageTitle || !args.section) { return { success: false, message: 'Page title and section required for get_section action' }; } result = await this.getPageSection(sectionPageTitle, args.section); message = `Retrieved section "${args.section}" from page "${sectionPageTitle}"`; break; case 'sequence': if (!args.steps) { return this.createErrorResponse('steps array is required for sequence action'); } return await this.executeSequence(args.steps as SequenceStep[]); default: return { success: false, message: `Unknown action: ${action}. Supported actions: search, get_page, get_page_summary, get_section, sequence` }; } return { success: true, message: message, data: { action, result, timestamp: Date.now() } }; } catch (error) { return { success: false, message: `Minecraft Wiki search error: ${error instanceof Error ? error.message : String(error)}` }; } } /** * MediaWiki APIを呼び出します * * @param params - APIパラメータ * @returns API応答 * @private */ private async callMediaWikiAPI(params: Record<string, string>): Promise<any> { const baseParams = { format: 'json', origin: '*', ...params }; const url = `https://minecraft.wiki/api.php?${new URLSearchParams(baseParams)}`; try { // タイムアウト付きでfetch APIを使用 const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 8000); // 8秒タイムアウト const response = await fetch(url, { method: 'GET', headers: { 'User-Agent': 'Minecraft-Bedrock-MCP-Server/1.0', 'Accept': 'application/json' }, signal: controller.signal }); clearTimeout(timeoutId); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const data = await response.json() as any; if (data.error) { throw new Error(`MediaWiki API Error: ${data.error.info || data.error.code}`); } return data; } catch (error) { if (error instanceof Error && error.name === 'AbortError') { throw new Error('MediaWiki API request timed out (8 seconds)'); } throw new Error(`MediaWiki API call failed: ${error instanceof Error ? error.message : String(error)}`); } } /** * Wiki検索を実行します * * @param query - 検索クエリ * @param focus - 検索フォーカス * @returns 検索結果 * @private */ private async searchWiki(query: string, focus?: string): Promise<any> { // Bedrock Edition/Education Edition特有の情報を優先するようにクエリを調整 let searchQuery = query; if (focus === 'bedrock_edition' || focus === 'commands') { searchQuery += ' bedrock edition OR education edition'; } else if (focus === 'education_edition') { searchQuery += ' education edition OR classroom mode'; } const searchParams = { action: 'query', list: 'search', srsearch: searchQuery, srlimit: '5', // 10→5に削減で高速化 srinfo: 'totalhits|suggestion', srprop: 'size|wordcount|timestamp|snippet' // 必要な情報のみ取得 }; const searchResult = await this.callMediaWikiAPI(searchParams); if (!searchResult.query || !searchResult.query.search) { return { pages: [], totalHits: 0, suggestion: null }; } // 検索結果を処理してBedrock Edition関連を優先 const pages = searchResult.query.search .filter((page: any) => this.isBedrockRelevant(page.title, page.snippet)) .map((page: any) => ({ title: page.title, snippet: this.cleanSnippet(page.snippet), size: page.size, wordcount: page.wordcount, timestamp: page.timestamp })); return { pages, totalHits: searchResult.query.searchinfo?.totalhits || 0, suggestion: searchResult.query.searchinfo?.suggestion || null }; } /** * ページサマリーを取得します * * @param pageTitle - ページタイトル * @returns ページサマリー * @private */ private async getPageSummary(pageTitle: string): Promise<any> { const summaryParams = { action: 'query', prop: 'extracts|info', titles: pageTitle, exintro: 'true', explaintext: 'true', exchars: '300', // 500→300に削減 inprop: 'url' }; const summaryResult = await this.callMediaWikiAPI(summaryParams); if (!summaryResult.query || !summaryResult.query.pages) { return null; } const pages = Object.values(summaryResult.query.pages) as any[]; const page = pages[0]; if (page.missing) { return { error: `Page "${pageTitle}" not found` }; } // セクション取得はオプションとして簡素化 let sections: any[] = []; return { title: page.title, summary: this.extractBedrockInfo(page.extract || ''), url: page.fullurl, lastModified: page.touched, sections: [] // セクションは別途get_sectionで取得 }; } /** * ページコンテンツを取得します * * @param pageTitle - ページタイトル * @returns ページコンテンツ * @private */ private async getPageContent(pageTitle: string): Promise<any> { const contentParams = { action: 'query', prop: 'extracts|info', titles: pageTitle, exintro: 'true', explaintext: 'true', exsectionformat: 'wiki', inprop: 'url' }; const contentResult = await this.callMediaWikiAPI(contentParams); if (!contentResult.query || !contentResult.query.pages) { return null; } const pages = Object.values(contentResult.query.pages) as any[]; const page = pages[0]; if (page.missing) { return null; } return { title: page.title, extract: this.extractBedrockInfo(page.extract), url: page.fullurl, lastModified: page.touched }; } /** * ページの特定セクションを取得します * * @param pageTitle - ページタイトル * @param sectionName - セクション名 * @returns セクションコンテンツ * @private */ private async getPageSection(pageTitle: string, sectionName: string): Promise<any> { // まずページの全セクションを取得 const sectionsParams = { action: 'parse', page: pageTitle, prop: 'sections' }; const sectionsResult = await this.callMediaWikiAPI(sectionsParams); if (!sectionsResult.parse || !sectionsResult.parse.sections) { return null; } // セクション名に一致するセクションを検索 const section = sectionsResult.parse.sections.find((s: any) => s.line.toLowerCase().includes(sectionName.toLowerCase()) || s.anchor.toLowerCase().includes(sectionName.toLowerCase()) ); if (!section) { return { error: `Section "${sectionName}" not found in page "${pageTitle}"` }; } // セクションの内容を取得 const sectionParams = { action: 'parse', page: pageTitle, section: section.index, prop: 'text', disablelimitreport: 'true' }; const sectionResult = await this.callMediaWikiAPI(sectionParams); return { title: section.line, level: section.level, content: this.extractBedrockInfo(sectionResult.parse?.text?.['*'] || ''), anchor: section.anchor }; } /** * Bedrock Edition/Education Editionに関連する内容かチェックします * * @param title - ページタイトル * @param snippet - ページスニペット * @returns Bedrock/Education Edition関連かどうか * @private */ private isBedrockRelevant(title: string, snippet: string): boolean { const supportedKeywords = [ 'bedrock', 'pocket edition', 'mcpe', 'behavior pack', 'resource pack', 'addon', 'marketplace', 'education edition', 'minecraft education', 'edu', 'mcee', 'classroom mode' ]; const javaOnlyKeywords = [ 'java edition only', 'java exclusive', 'not available in bedrock', 'java edition exclusive', 'pc edition only', 'desktop only', 'mod support', 'forge', 'fabric', 'optifine', 'not supported in bedrock', 'bedrock does not support' ]; const text = (title + ' ' + snippet).toLowerCase(); // Java専用機能を除外 if (javaOnlyKeywords.some(keyword => text.includes(keyword))) { return false; } // Bedrock/Education Edition関連キーワードがあるか、または一般的な内容 return supportedKeywords.some(keyword => text.includes(keyword)) || !text.includes('java edition'); } /** * スニペットからHTMLタグを除去します * * @param snippet - 元のスニペット * @returns クリーンなスニペット * @private */ private cleanSnippet(snippet: string): string { return snippet .replace(/<[^>]+>/g, '') // HTMLタグを除去 .replace(/&[^;]+;/g, ' ') // HTMLエンティティを除去 .replace(/\s+/g, ' ') // 連続する空白を単一に .trim(); } /** * コンテンツからBedrock Edition情報を抽出します * * @param content - 元のコンテンツ * @returns Bedrock Edition関連情報 * @private */ private extractBedrockInfo(content: string): string { if (!content) return ''; // HTMLタグを除去 let cleanContent = content.replace(/<[^>]+>/g, ''); // Bedrock Edition特有の情報を強調 const bedrockSections = [ 'bedrock edition', 'pocket edition', 'behavior pack', 'commands', 'syntax' ]; // セクションごとに分割して関連部分を抽出 const lines = cleanContent.split('\n'); const relevantLines: string[] = []; for (let i = 0; i < lines.length; i++) { const line = lines[i].toLowerCase(); if (bedrockSections.some(section => line.includes(section))) { // 関連セクションの前後数行を含める const start = Math.max(0, i - 2); const end = Math.min(lines.length, i + 5); relevantLines.push(...lines.slice(start, end)); i = end - 1; // 次のループでスキップ } } return relevantLines.length > 0 ? relevantLines.join('\n') : cleanContent.slice(0, 1000); } /** * Wiki専用のシーケンスステップ実行 * * @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); } // Wiki特有のステップを実行 // titleパラメータをpage_titleにもコピーして互換性を確保 const wikiArgs = { action: step.type, ...step, page_title: step.page_title || step.title // titleからpage_titleにもコピー }; const result = await this.execute(wikiArgs); // シーケンス内では結果を簡潔にする if (result.success && result.data) { const data = result.data as any; // 検索結果の場合、件数のみ表示 if (step.type === 'search' && data.result?.pages) { return { success: true, message: `Step ${index + 1}: Found ${data.result.pages.length} results for "${step.query || 'unknown'}"`, data: { action: step.type, count: data.result.pages.length, titles: data.result.pages.slice(0, 3).map((p: any) => p.title) } }; } // ページ取得の場合、概要のみ if (step.type === 'get_page' || step.type === 'get_page_summary') { return { success: true, message: `Step ${index + 1}: Retrieved page "${data.result?.title || 'unknown'}"`, data: { action: step.type, title: data.result?.title, summary: data.result?.summary?.substring(0, 150) + '...' || data.result?.extract?.substring(0, 150) + '...' } }; } // セクション取得の場合、セクション内容の要約のみ if (step.type === 'get_section') { return { success: true, message: `Step ${index + 1}: Retrieved section "${data.result?.title || step.section}" from page`, data: { action: step.type, section: data.result?.title || step.section, content: data.result?.content?.substring(0, 200) + '...' || 'Section content retrieved' } }; } } return result; } }

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