pickupItem
Pick up specific items from the ground in Minecraft using a designated item name. This tool integrates with the MCP server to enable AI agents to automate item collection tasks efficiently within the game.
Instructions
Pick up items from the ground
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| itemName | Yes | Name of the item to pick up |
Implementation Reference
- The core handler function for the 'pickupItem' MCP tool. It validates parameters, finds the nearest dropped item by name using bot.nearestEntity, navigates to it with pathfinder GoalFollow, waits nearby until picked up, compares inventory snapshots to confirm pickup, emits observations, and falls back to mining the block if no dropped item is found.export const pickupItem = async ( bot: Bot, params: ISkillParams, serviceParams: ISkillServiceParams, ): Promise<boolean> => { const skillName = 'pickupItem'; const requiredParams = ['itemName']; 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; } const unpackedParams = { signal: serviceParams?.signal, itemName: params.itemName, getStatsData: serviceParams.getStatsData, setStatsData: serviceParams.setStatsData, resetTimeout: serviceParams.resetTimeout, }; const { signal, itemName, getStatsData, setStatsData, resetTimeout } = unpackedParams; const mcData = minecraftData(bot.version); const SEARCH_DISTANCE = bot.nearbyEntityRadius; if (typeof itemName !== 'string') { return bot.emit('alteraBotEndObservation', 'itemName must be a string'); } const closestItemName = findClosestItemName(bot, { name: itemName }); if (!closestItemName) { return bot.emit( 'alteraBotEndObservation', `There is no item named ${itemName} in Minecraft.`, ); } const version = parseInt(bot.version.split('.')[1]); let target = null; if (version < 10) { // 1.8 target = bot.nearestEntity( (entity) => entity?.name?.toLowerCase() === 'item' && (entity.metadata[10] as any)?.blockId == mcData.itemsByName[closestItemName].id, ); } else { target = bot.nearestEntity( (entity) => entity?.name?.toLowerCase() === 'item' && (entity.metadata[8] as any)?.itemId == mcData.itemsByName[closestItemName].id, ); } if (target == null || !target.isValid) { // If the bot can't find the item on the ground, check to see if its a block const closestBlockName = closest( (itemName || '').toLowerCase(), Object.keys(mcData.blocksByName), ); const blockName = mcData.itemsByName[closestBlockName]?.name; if (blockName && distance(closestBlockName, itemName.toLowerCase()) < 3) { console.log( `No ${itemName} found on the ground nearby. Trying to mine it...`, ); return await mineBlock(bot, { name: itemName, count: 1, signal: signal, getStatsData, setStatsData, resetTimeout, }); } else { return bot.emit( 'alteraBotEndObservation', `You were trying to pick up ${itemName}, but there aren't any ${itemName} on the ground nearby for you to pick up!`, ); } } if (target.position.distanceTo(bot.entity.position) > SEARCH_DISTANCE) { return bot.emit( 'alteraBotEndObservation', `You were trying to pick up ${itemName}, but there aren't any ${itemName} on the ground nearby for you to pick up!`, ); } try { // var pickedUpTarget = false; // Collect the item, assuming the bot is close enough to collect it const inventory = getInventorySnapshot(bot); bot.pathfinder.goto(new GoalFollow(target, 0)); while ( target.isValid && target.position.distanceTo(bot.entity.position) < 1.5 ) { // Make sure the item gets registered in the inventory const waitFn = async function () { return new Promise((r) => setTimeout(r, 50)); }; await asyncwrap({ func: waitFn, setStatsData, getStatsData }); // check signal for cancellation if (isSignalAborted(signal)) { return bot.emit( 'alteraBotEndObservation', `You decided to do something else and stopped picking up ${itemName}.`, ); } } // Wait for 10 ticks to make sure the item is registered in the inventory await bot.waitForTicks(10); const differences = compareInventories( inventory, getInventorySnapshot(bot), ); return bot.emit( 'alteraBotEndObservation', `You picked up ${getCollapsedItemCounts(differences)}`, ); } catch (error) { console.log( `An error occurred while trying to go and collect ${itemName}: ${error}`, ); return bot.emit( 'alteraBotEndObservation', `You weren't able to collect any ${itemName} because ${error}`, ); } };
- Defines the input schema and metadata for the 'pickupItem' tool, used during registration. Specifies the required 'itemName' string parameter.pickupItem: { description: "Pick up items from the ground", params: { itemName: { type: "string", description: "Name of the item to pick up" } }, required: ["itemName"]
- mcp-server/src/skillRegistry.ts:350-367 (registration)The loadSkills function registers all skills including 'pickupItem' by iterating over SKILL_METADATA, creating SkillDefinition objects with schema and dynamic executor that imports the handler module.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; }
- Helper function to capture a snapshot of the bot's inventory before attempting pickup, used for comparison after to confirm item collection.const getInventorySnapshot = (bot: Bot): IInventoryItem[] => { return bot.inventory.items().map((item) => ({ name: item.name, count: item.count, })); };
- Helper function to compare pre- and post-pickup inventory snapshots and identify newly acquired items.const compareInventories = ( oldInv: IInventoryItem[], newInv: IInventoryItem[], ): IInventoryItem[] => { const oldMap = new Map(oldInv.map((item) => [item.name, item.count])); const newMap = new Map(newInv.map((item) => [item.name, item.count])); const differences: IInventoryItem[] = []; newMap.forEach((newCount, name) => { const oldCount = oldMap.get(name) || 0; if (newCount > oldCount) { differences.push({ name, count: newCount - oldCount }); } }); return differences; };