UNPKG

maplestory-mcp-server

Version:

Official-style NEXON MapleStory MCP Server for Claude Desktop - Complete character info, union details, guild data, rankings, and game mechanics

472 lines 21.3 kB
"use strict"; /** * Character Information Tools for MCP Maple * Provides MCP tools for retrieving MapleStory character data */ Object.defineProperty(exports, "__esModule", { value: true }); exports.GetCharacterFullInfoTool = exports.GetCharacterEquipmentTool = exports.GetCharacterStatsTool = exports.GetCharacterBasicInfoTool = void 0; const base_tool_1 = require("./base-tool"); /** * Tool for getting basic character information */ class GetCharacterBasicInfoTool extends base_tool_1.EnhancedBaseTool { name = 'get_character_basic_info'; description = 'Retrieve basic information about a MapleStory character including level, job, world, and guild'; inputSchema = { type: 'object', properties: { characterName: { type: 'string', description: 'The name of the character to look up', minLength: 1, maxLength: 12, pattern: '^[a-zA-Z0-9가-힣]+$', }, date: { type: 'string', description: 'Date for character info in YYYY-MM-DD format (optional, defaults to yesterday)', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, }, required: ['characterName'], additionalProperties: false, }; metadata = { category: base_tool_1.ToolCategory.CHARACTER, tags: ['character', 'basic', 'info', 'level', 'job'], examples: [ { description: 'Get basic info for character', arguments: { characterName: '스카니아용사' }, }, { description: 'Get basic info for specific date', arguments: { characterName: '스카니아용사', date: '2024-01-15' }, }, ], }; async executeImpl(args, context) { const characterName = this.getRequiredString(args, 'characterName'); const date = this.getOptionalString(args, 'date'); try { const startTime = Date.now(); // Get character OCID first context.logger.info('Looking up character OCID', { characterName }); const ocidResult = await context.nexonClient.getCharacterOcid(characterName); const ocid = ocidResult.ocid; // Get basic character info context.logger.info('Fetching character basic info', { characterName, ocid }); const basicInfo = await context.nexonClient.getCharacterBasic(ocid, date); const executionTime = Date.now() - startTime; context.logger.info('Character basic info retrieved successfully', { characterName, level: basicInfo.character_level, job: basicInfo.character_class, executionTime, }); return this.formatResult({ characterName: basicInfo.character_name, level: basicInfo.character_level, job: basicInfo.character_class, jobDetail: basicInfo.character_class_level, exp: basicInfo.character_exp, expRate: basicInfo.character_exp_rate, guildName: basicInfo.character_guild_name || null, world: basicInfo.world_name, gender: basicInfo.character_gender, // Basic stats are not available in CharacterBasic endpoint // They need to be fetched separately from getCharacterStat date: basicInfo.date || date || 'latest', }, { executionTime, cacheHit: false, apiCalls: 2, // OCID lookup + basic info }); } catch (error) { context.logger.error('Failed to get character basic info', { characterName, error: error instanceof Error ? error.message : String(error), }); return this.formatError(`Failed to get basic info for character "${characterName}": ${error instanceof Error ? error.message : String(error)}`); } } } exports.GetCharacterBasicInfoTool = GetCharacterBasicInfoTool; /** * Tool for getting detailed character statistics */ class GetCharacterStatsTool extends base_tool_1.EnhancedBaseTool { name = 'get_character_stats'; description = 'Retrieve detailed statistics for a MapleStory character including damage, critical rate, and all combat stats'; inputSchema = { type: 'object', properties: { characterName: { type: 'string', description: 'The name of the character to look up', minLength: 1, maxLength: 12, pattern: '^[a-zA-Z0-9가-힣]+$', }, date: { type: 'string', description: 'Date for character stats in YYYY-MM-DD format (optional, defaults to yesterday)', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, }, required: ['characterName'], additionalProperties: false, }; metadata = { category: base_tool_1.ToolCategory.CHARACTER, tags: ['character', 'stats', 'damage', 'critical', 'combat'], examples: [ { description: 'Get detailed stats for character', arguments: { characterName: '스카니아용사' }, }, { description: 'Get stats for specific date', arguments: { characterName: '스카니아용사', date: '2024-01-15' }, }, ], }; async executeImpl(args, context) { const characterName = this.getRequiredString(args, 'characterName'); const date = this.getOptionalString(args, 'date'); try { const startTime = Date.now(); // Get character OCID first context.logger.info('Looking up character OCID for stats', { characterName }); const ocidResult = await context.nexonClient.getCharacterOcid(characterName); const ocid = ocidResult.ocid; // Get character stats context.logger.info('Fetching character detailed stats', { characterName, ocid }); const stats = await context.nexonClient.getCharacterStat(ocid, date); const executionTime = Date.now() - startTime; // Group stats by category for better presentation const basicStats = stats.final_stat?.filter((s) => ['STR', 'DEX', 'INT', 'LUK', 'HP', 'MP'].includes(s.stat_name)) || []; const combatStats = stats.final_stat?.filter((s) => [ '공격력', '마력', '크리티컬 확률', '크리티컬 데미지', '보스 몬스터 데미지', '방어율 무시', '데미지', ].includes(s.stat_name)) || []; const defenseStats = stats.final_stat?.filter((s) => ['물리방어력', '마법방어력', '스탠스', '방어율'].includes(s.stat_name)) || []; context.logger.info('Character stats retrieved successfully', { characterName, statCount: stats.final_stat?.length || 0, executionTime, }); return this.formatResult({ characterName, date: stats.date || date || 'latest', basicStats: basicStats.reduce((acc, stat) => { acc[stat.stat_name] = stat.stat_value; return acc; }, {}), combatStats: combatStats.reduce((acc, stat) => { acc[stat.stat_name] = stat.stat_value; return acc; }, {}), defenseStats: defenseStats.reduce((acc, stat) => { acc[stat.stat_name] = stat.stat_value; return acc; }, {}), allStats: stats.final_stat?.reduce((acc, stat) => { acc[stat.stat_name] = stat.stat_value; return acc; }, {}) || {}, remainAp: stats.remain_ap, }, { executionTime, cacheHit: false, apiCalls: 2, // OCID lookup + stats }); } catch (error) { context.logger.error('Failed to get character stats', { characterName, error: error instanceof Error ? error.message : String(error), }); return this.formatError(`Failed to get stats for character "${characterName}": ${error instanceof Error ? error.message : String(error)}`); } } } exports.GetCharacterStatsTool = GetCharacterStatsTool; /** * Tool for getting character equipment information */ class GetCharacterEquipmentTool extends base_tool_1.EnhancedBaseTool { name = 'get_character_equipment'; description = 'Retrieve equipment information for a MapleStory character including all equipped items and their stats'; inputSchema = { type: 'object', properties: { characterName: { type: 'string', description: 'The name of the character to look up', minLength: 1, maxLength: 12, pattern: '^[a-zA-Z0-9가-힣]+$', }, date: { type: 'string', description: 'Date for character equipment in YYYY-MM-DD format (optional, defaults to yesterday)', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, }, required: ['characterName'], additionalProperties: false, }; metadata = { category: base_tool_1.ToolCategory.CHARACTER, tags: ['character', 'equipment', 'items', 'gear', 'weapon'], examples: [ { description: 'Get equipment for character', arguments: { characterName: '스카니아용사' }, }, { description: 'Get equipment for specific date', arguments: { characterName: '스카니아용사', date: '2024-01-15' }, }, ], }; async executeImpl(args, context) { const characterName = this.getRequiredString(args, 'characterName'); const date = this.getOptionalString(args, 'date'); try { const startTime = Date.now(); // Get character OCID first context.logger.info('Looking up character OCID for equipment', { characterName }); const ocidResult = await context.nexonClient.getCharacterOcid(characterName); const ocid = ocidResult.ocid; // Get character equipment context.logger.info('Fetching character equipment', { characterName, ocid }); const equipment = await context.nexonClient.getCharacterItemEquipment(ocid, date); const executionTime = Date.now() - startTime; // Organize equipment by category const equipmentBySlot = equipment.item_equipment?.reduce((acc, item) => { acc[item.item_equipment_slot] = { name: item.item_name, icon: item.item_icon, description: item.item_description, shapeIcon: item.item_shape_icon, shapeName: item.item_shape_name, gender: item.item_gender, totalOption: item.item_total_option, baseOption: item.item_base_option, exceptionalOption: item.item_exceptional_option, addOption: item.item_add_option, starforceOption: item.item_starforce_option, etcOption: item.item_etc_option, starforce: item.starforce, starforceScrollFlag: item.starforce_scroll_flag, cuttableCount: item.cuttable_count, goldenHammerFlag: item.golden_hammer_flag, scrollUpgrade: item.scroll_upgrade, scrollUpgradableCount: item.scroll_upgradeable_count, scrollResilienceCount: item.scroll_resilience_count, scrollUpgradeCount: item.scroll_upgradeable_count, potential: item.potential_option_grade, potentialOptions: [ item.potential_option_1, item.potential_option_2, item.potential_option_3, ].filter(Boolean), additionalPotential: item.additional_potential_option_grade, additionalPotentialOptions: [ item.additional_potential_option_1, item.additional_potential_option_2, item.additional_potential_option_3, ].filter(Boolean), }; return acc; }, {}) || {}; context.logger.info('Character equipment retrieved successfully', { characterName, equipmentCount: equipment.item_equipment?.length || 0, executionTime, }); return this.formatResult({ characterName, date: equipment.date || date || 'latest', equipment: equipmentBySlot, equipmentList: equipment.item_equipment || [], presetNo: equipment.preset_no, title: equipment.title, }, { executionTime, cacheHit: false, apiCalls: 2, // OCID lookup + equipment }); } catch (error) { context.logger.error('Failed to get character equipment', { characterName, error: error instanceof Error ? error.message : String(error), }); return this.formatError(`Failed to get equipment for character "${characterName}": ${error instanceof Error ? error.message : String(error)}`); } } } exports.GetCharacterEquipmentTool = GetCharacterEquipmentTool; /** * Tool for getting comprehensive character information (combines basic, stats, and equipment) */ class GetCharacterFullInfoTool extends base_tool_1.EnhancedBaseTool { name = 'get_character_full_info'; description = 'Retrieve comprehensive character information including basic info, stats, and equipment in a single request'; inputSchema = { type: 'object', properties: { characterName: { type: 'string', description: 'The name of the character to look up', minLength: 1, maxLength: 12, pattern: '^[a-zA-Z0-9가-힣]+$', }, date: { type: 'string', description: 'Date for character info in YYYY-MM-DD format (optional, defaults to yesterday)', pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, includeEquipment: { type: 'boolean', description: 'Whether to include equipment information (defaults to true)', default: true, }, }, required: ['characterName'], additionalProperties: false, }; metadata = { category: base_tool_1.ToolCategory.CHARACTER, tags: ['character', 'comprehensive', 'full', 'complete', 'all'], examples: [ { description: 'Get full character information', arguments: { characterName: '스카니아용사' }, }, { description: 'Get full info without equipment', arguments: { characterName: '스카니아용사', includeEquipment: false }, }, { description: 'Get full info for specific date', arguments: { characterName: '스카니아용사', date: '2024-01-15' }, }, ], }; async executeImpl(args, context) { const characterName = this.getRequiredString(args, 'characterName'); const date = this.getOptionalString(args, 'date'); const includeEquipment = this.getOptionalBoolean(args, 'includeEquipment', true); try { const startTime = Date.now(); // Get character OCID first context.logger.info('Looking up character OCID for full info', { characterName }); const ocidResult = await context.nexonClient.getCharacterOcid(characterName); const ocid = ocidResult.ocid; // Get all character data in parallel context.logger.info('Fetching comprehensive character data', { characterName, ocid, includeEquipment, }); const [basicInfo, stats, equipment] = await Promise.all([ context.nexonClient.getCharacterBasic(ocid, date), context.nexonClient.getCharacterStat(ocid, date), includeEquipment ? context.nexonClient.getCharacterItemEquipment(ocid, date) : null, ]); const executionTime = Date.now() - startTime; const result = { characterName: basicInfo.character_name, basicInfo: { level: basicInfo.character_level, job: basicInfo.character_class, jobDetail: basicInfo.character_class_level, exp: basicInfo.character_exp, expRate: basicInfo.character_exp_rate, guildName: basicInfo.character_guild_name || null, world: basicInfo.world_name, gender: basicInfo.character_gender, date: basicInfo.date || date || 'latest', }, stats: { basicStats: stats.final_stat ?.filter((s) => ['STR', 'DEX', 'INT', 'LUK', 'HP', 'MP'].includes(s.stat_name)) .reduce((acc, stat) => { acc[stat.stat_name] = stat.stat_value; return acc; }, {}) || {}, combatStats: stats.final_stat ?.filter((s) => [ '공격력', '마력', '크리티컬 확률', '크리티컬 데미지', '보스 몬스터 데미지', '방어율 무시', '데미지', ].includes(s.stat_name)) .reduce((acc, stat) => { acc[stat.stat_name] = stat.stat_value; return acc; }, {}) || {}, remainAp: stats.remain_ap, }, ...(includeEquipment && equipment && { equipment: { presetNo: equipment.preset_no, title: equipment.title, items: equipment.item_equipment?.reduce((acc, item) => { acc[item.item_equipment_slot] = { name: item.item_name, icon: item.item_icon, starforce: item.starforce, potential: item.potential_option_grade, potentialOptions: [ item.potential_option_1, item.potential_option_2, item.potential_option_3, ].filter(Boolean), }; return acc; }, {}) || {}, }, }), date: date || 'latest', }; context.logger.info('Character full info retrieved successfully', { characterName, level: basicInfo.character_level, job: basicInfo.character_class, statsCount: stats.final_stat?.length || 0, equipmentCount: equipment?.item_equipment?.length || 0, executionTime, }); return this.formatResult(result, { executionTime, cacheHit: false, apiCalls: includeEquipment ? 4 : 3, // OCID + basic + stats + equipment (optional) }); } catch (error) { context.logger.error('Failed to get character full info', { characterName, error: error instanceof Error ? error.message : String(error), }); return this.formatError(`Failed to get full information for character "${characterName}": ${error instanceof Error ? error.message : String(error)}`); } } } exports.GetCharacterFullInfoTool = GetCharacterFullInfoTool; //# sourceMappingURL=character-tools.js.map