attackSomeone
Initiate combat, defend, or eliminate targets in Minecraft using this tool. Specify entity type (player, mob, animal), optional name, duration, and kill count for precise actions. Works with MCP Server for game control.
Instructions
Attack, kill, defend against, or initiate combat with someone
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| count | No | Optional: Number of kills to achieve (default: 1) | |
| duration | No | Optional: Duration in seconds to attack (default: 20, max: 120) | |
| targetName | No | Optional: The specific name of the entity to attack | |
| targetType | Yes | The type of target: 'player', 'mob', or 'animal' |
Implementation Reference
- The main exported handler function for the 'attackSomeone' MCP tool. It validates the input parameters and delegates execution to the core 'attack' helper function.export const attackSomeone = async ( bot: Bot, params: ISkillParams, serviceParams: ISkillServiceParams, ): Promise<boolean> => { const skillName = 'attackSomeone'; const requiredParams = ['targetType']; const isParamsValid = validateSkillParams( params, requiredParams, skillName, ); if (!isParamsValid) { serviceParams.cancelExecution?.(); bot.emit( 'alteraBotEndObservation', `Mistake: You didn't provide all of the required parameters ${requiredParams.join(', ')} for the ${skillName} skill.`, ); return false; } return attack(bot, { targetType: params.targetType ?? null, targetName: params.targetName ?? '', duration: params.duration ?? 20, killNumber: params.count ?? 1, signal: serviceParams.signal, getStatsData: serviceParams.getStatsData, setStatsData: serviceParams.setStatsData, resetTimeout: serviceParams.resetTimeout, }); };
- Core helper function implementing the attack logic: finds targets, equips sword, attacks, handles multiple kills, collects loot by navigating to death position, manages timeouts and cancellations.export const attack = async ( bot: Bot, options: IAttackOptions, ): Promise<boolean> => { const defaultOptions = { targetType: '', targetName: '', duration: 20, killNumber: 1, }; let { targetType, targetName, duration, killNumber, signal, setStatsData, getStatsData, resetTimeout, } = {...defaultOptions, ...options}; const ENTITY_RADIUS = bot.nearbyEntityRadius; const WAIT_TIME = 10; // 10 milliseconds const MAX_DURATION = 60; // 60 seconds const MAX_KILL_NUMBER = 5; // Maximum number of entities to kill // Stop attacking anyone before attacking someone else bot.pvp.forceStop(); let kills = 0; targetName = targetName ?? ''; const targetStr = targetName ? targetName : targetType; targetName = targetName.replace(/\s/g, '_'); // Replace spaces in name with _ duration = Math.min(duration, MAX_DURATION); // Set max duration to 60 seconds. killNumber = Math.min(killNumber, MAX_KILL_NUMBER); // Set max duration to 60 seconds. // filter for the target using the entity type let target = findTarget(bot, {targetType, targetName}); if (!target) { return bot.emit( 'alteraBotEndObservation', `Mistake: You chose to attack ${targetName} but you couldn't find anyone with that name.`, ); } if ( target.position.distanceTo(bot.entity.position) > ENTITY_RADIUS && (targetType === 'mob' || targetType === 'animal') ) { return bot.emit( 'alteraBotEndObservation', `There's no ${targetStr} close enough for you to attack.`, ); } autoEquipSword(bot); const startTime = Date.now(); let endMessage = null; try { // Event handler to collect loot after killing the target const handleDeath = async (entity: Entity) => { // console.log(entity === target) if (entity === target) { bot.pvp.forceStop(); kills++; resetTimeout(); // reset the skill timeout on the bot to make sure the bot doesn't stop the skill while it's still attacking if (kills < killNumber) bot.emit( 'alteraBotTextObservation', `You have killed ${kills} of ${killNumber} ${targetStr}.`, ); // move to the target's last known position to (hopefully) collect loot const navigateFunc = async function () { return navigateToLocation(bot, { x: target.position.x, y: target.position.y, z: target.position.z, range: 0.5, }); }; await asyncwrap({func: navigateFunc, setStatsData, getStatsData}); // Check if we're done killing await bot.waitForTicks(2); if (isSignalAborted(signal)) { stopAttack(); endMessage = `You decided to do something else after you killed ${kills} ${targetStr}.`; return; } // check signal for cancellation if (kills >= killNumber) { stopAttack(); endMessage = `You finished killing ${kills} ${targetStr}.`; return; } // Select a new target, since there are more to kill let newTarget = findTarget(bot, {targetType, targetName}); // make sure the target is the entity we just killed and that we have a new target // find target may still find the same target for a brief window let findTargetLimit = 40; // emergency limit to prevent infinite loop // this is in ticks while (newTarget == target && findTargetLimit >= 0) { newTarget = findTarget(bot, {targetType, targetName}); await bot.waitForTicks(1); findTargetLimit--; } if (findTargetLimit <= 0) { stopAttack(); endMessage = `ERROR: couldn't find a new ${targetStr} to kill.`; return; } target = newTarget; if ( !target || (target.position.distanceTo(bot.entity.position) > ENTITY_RADIUS && (targetType === 'mob' || targetType === 'animal')) ) { stopAttack(); endMessage = `You killed ${kills} ${targetStr} and there are no more ${targetStr} nearby.`; return; } console.log(` attacking the next ${targetStr}.`); // re-equip a sword (in case old one broke) autoEquipSword(bot); bot.pathfinder.stop(); // Stop pathfinding // Reset the loot trigger and start attacking the new target bot.waitForTicks(2); bot.pvp.attack(target); } return; }; bot.on('entityDead', handleDeath); // await asyncwrap(async function(){return bot.pvp.attack(target)}); bot.pvp.attack(target); let cleanUpTimeout; let stoppedAttacking = false; const stopAttack = () => { if (!stoppedAttacking) { bot.removeListener('entityDead', handleDeath); bot.pvp.forceStop(); stoppedAttacking = true; attacking = false; } }; let attackTime = 0; let attacking = true; const totalDuration = duration * 1000; while (attacking && attackTime < totalDuration) { // check signal for cancellation // this check may be redundant, but it's here just in case if (isSignalAborted(signal)) { stopAttack(); if (!endMessage) endMessage = `You decided to do something else instead of attacking ${targetStr}.`; return bot.emit('alteraBotEndObservation', endMessage); } // wait for 10 milliseconds await new Promise((resolve) => setTimeout(resolve, WAIT_TIME)); attackTime += WAIT_TIME; } console.log(` stopped attacking after ${duration} seconds.`); stopAttack(); if (!endMessage) endMessage = `You stopped attacking ${targetStr} after ${duration} seconds.`; return bot.emit('alteraBotEndObservation', endMessage); } catch (err) { const error = err as Error; return bot.emit( 'alteraBotEndObservation', `You couldn't attack ${targetStr} because: ${error.message}`, ); } };
- Input schema and metadata definition for the 'attackSomeone' tool, including parameters, descriptions, and required fields.attackSomeone: { description: "Attack, kill, defend against, or initiate combat with someone", params: { targetType: { type: "string", description: "The type of target: 'player', 'mob', or 'animal'" }, targetName: { type: "string", description: "Optional: The specific name of the entity to attack" }, duration: { type: "number", description: "Optional: Duration in seconds to attack (default: 20, max: 120)" }, count: { type: "number", description: "Optional: Number of kills to achieve (default: 1)" } }, required: ["targetType"] },
- mcp-server/src/skillRegistry.ts:350-367 (registration)Dynamic registration of all skills, including 'attackSomeone', by iterating over SKILL_METADATA and creating SkillDefinition objects with schema and executor.export async function loadSkills(): Promise<SkillDefinition[]> { const skills: SkillDefinition[] = []; for (const [skillName, metadata] of Object.entries(SKILL_METADATA)) { skills.push({ name: skillName, description: metadata.description, inputSchema: { type: "object", properties: metadata.params, required: metadata.required }, execute: createSkillExecutor(skillName) }); } return skills; }