Skip to main content
Glama

listTrades

Display available trades from nearby villagers in Minecraft to help players identify exchange options and plan resource management.

Instructions

List available trades from a nearby villager

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
villagerNameNoName or identifier of the villager (optional)
rangeNoRange to search for villagers

Implementation Reference

  • Primary registration of the 'listTrades' tool using server.tool(). Includes tool name, description, input schema (villagerName and range), and the full handler function that locates villagers, opens the trading interface, extracts trade items from slots, and returns a formatted list.
    server.tool( 'listTrades', 'List available trades from a nearby villager', { villagerName: z .string() .optional() .describe('Name or identifier of the villager (optional)'), range: z .number() .optional() .default(4) .describe('Range to search for villagers'), }, async ({ villagerName, range }) => { if (!botState.isConnected || !botState.bot) { return createNotConnectedResponse() } try { // Find villagers in range const villagers = Object.values(botState.bot.entities).filter( (entity) => { if (!entity || !entity.position || !botState.bot) return false const distance = entity.position.distanceTo( botState.bot.entity.position ) return ( distance <= range && String(entity.type) === 'villager' && (!villagerName || (entity.name && entity.name .toLowerCase() .includes(villagerName.toLowerCase()))) ) } ) if (villagers.length === 0) { return createSuccessResponse( villagerName ? `No villager named "${villagerName}" found nearby.` : 'No villagers found nearby.' ) } // Choose the closest villager if multiple are found const villager = villagers.reduce((closest, current) => { if (!closest.position || !current.position || !botState.bot) return current const distClosest = closest.position.distanceTo( botState.bot.entity.position ) const distCurrent = current.position.distanceTo( botState.bot.entity.position ) return distCurrent < distClosest ? current : closest }) // We need to use the real Villager class // First step is to use openVillager method const villagerEntity = await botState.bot.openVillager(villager as any) await botState.bot.trade(villagerEntity, 1, 1) // Just to open the window, numbers don't matter // Check if window is open before accessing properties if (!botState.bot.currentWindow) { return createSuccessResponse( `Could not open trading window with villager.` ) } const trades = botState.bot.currentWindow.slots .filter((item) => item !== null && item !== undefined) .map((item) => { if (!item) return null // Try to extract trade details return { name: item.name, displayName: item.displayName || item.name, count: item.count || 1, // Add more properties as needed } }) .filter((trade) => trade !== null) // Close the trading window if open if (botState.bot.currentWindow) { await botState.bot.closeWindow(botState.bot.currentWindow) } if (trades.length === 0) { return createSuccessResponse( `Villager found, but no trades are available.` ) } // Format the response let response = `Available trades from villager ${ villager.name || 'Unknown' } (${villager.position .distanceTo(botState.bot.entity.position) .toFixed(1)} blocks away):\n\n` trades.forEach((trade, index) => { response += `${index + 1}. ${trade.displayName} (x${trade.count})\n` }) return createSuccessResponse(response) } catch (error) { return createErrorResponse(error) } } )
  • Input schema for listTrades tool defined using Zod: optional villagerName (string), optional range (number, default 4).
    { villagerName: z .string() .optional() .describe('Name or identifier of the villager (optional)'), range: z .number() .optional() .default(4) .describe('Range to search for villagers'), },
  • Core handler function for listTrades: filters nearby villagers by distance and name, selects closest, opens villager trading window using bot.openVillager and bot.trade, reads slots for trade items, formats and returns numbered list of available trades.
    async ({ villagerName, range }) => { if (!botState.isConnected || !botState.bot) { return createNotConnectedResponse() } try { // Find villagers in range const villagers = Object.values(botState.bot.entities).filter( (entity) => { if (!entity || !entity.position || !botState.bot) return false const distance = entity.position.distanceTo( botState.bot.entity.position ) return ( distance <= range && String(entity.type) === 'villager' && (!villagerName || (entity.name && entity.name .toLowerCase() .includes(villagerName.toLowerCase()))) ) } ) if (villagers.length === 0) { return createSuccessResponse( villagerName ? `No villager named "${villagerName}" found nearby.` : 'No villagers found nearby.' ) } // Choose the closest villager if multiple are found const villager = villagers.reduce((closest, current) => { if (!closest.position || !current.position || !botState.bot) return current const distClosest = closest.position.distanceTo( botState.bot.entity.position ) const distCurrent = current.position.distanceTo( botState.bot.entity.position ) return distCurrent < distClosest ? current : closest }) // We need to use the real Villager class // First step is to use openVillager method const villagerEntity = await botState.bot.openVillager(villager as any) await botState.bot.trade(villagerEntity, 1, 1) // Just to open the window, numbers don't matter // Check if window is open before accessing properties if (!botState.bot.currentWindow) { return createSuccessResponse( `Could not open trading window with villager.` ) } const trades = botState.bot.currentWindow.slots .filter((item) => item !== null && item !== undefined) .map((item) => { if (!item) return null // Try to extract trade details return { name: item.name, displayName: item.displayName || item.name, count: item.count || 1, // Add more properties as needed } }) .filter((trade) => trade !== null) // Close the trading window if open if (botState.bot.currentWindow) { await botState.bot.closeWindow(botState.bot.currentWindow) } if (trades.length === 0) { return createSuccessResponse( `Villager found, but no trades are available.` ) } // Format the response let response = `Available trades from villager ${ villager.name || 'Unknown' } (${villager.position .distanceTo(botState.bot.entity.position) .toFixed(1)} blocks away):\n\n` trades.forEach((trade, index) => { response += `${index + 1}. ${trade.displayName} (x${trade.count})\n` }) return createSuccessResponse(response) } catch (error) { return createErrorResponse(error) } }
  • Within registerAllTools(), calls registerTradingTools() to register the trading tools including listTrades.
    registerContainerInteractionTools() registerCraftingTools() registerTradingTools()
  • src/index.ts:4-7 (registration)
    Top-level registration: imports and calls registerAllTools() in main entry point, which chains to listTrades registration.
    import { registerAllTools } from './tools/index.js' // Register all tools registerAllTools()

Latest Blog Posts

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/nacal/mcp-minecraft-remote'

If you have feedback or need assistance with the MCP directory API, please join our Discord server