Updated:

The complete command (Java 1.20.5+)

/summon minecraft:villager ~ ~ ~ {VillagerData:{profession:"minecraft:librarian",level:5,type:"minecraft:plains"},NoAI:1b,Invulnerable:1b,PersistenceRequired:1b,Silent:1b,CustomName:"Tool Shop",Offers:{Recipes:[{buy:{id:"minecraft:emerald",count:5},sell:{id:"minecraft:diamond_pickaxe",count:1},maxUses:9999,rewardExp:0b},{buy:{id:"minecraft:gold_ingot",count:10},sell:{id:"minecraft:golden_apple",count:1},maxUses:9999,rewardExp:0b}]}}

This villager sells a diamond pickaxe for 5 emeralds and a golden apple for 10 gold ingots. The CustomName format is for 1.21.5 and later; on older versions write CustomName:'"Tool Shop"'.

What each part does

FieldMeaning
VillagerDataJob, level and biome look. Level 5 stops the job from changing.
NoAI:1bDoes not move or turn.
Invulnerable:1bCannot be killed by attacks.
PersistenceRequired:1bDoes not despawn.
buyWhat the player pays.
buyBOptional second payment.
sellWhat the player receives.
maxUsesHow many times the trade works.
rewardExp:0bNo XP from trading.

Never sell out

A villager without AI does not restock at a workstation, so set maxUses high from the start (9999 is effectively unlimited).

Keep prices fixed

Prices move with demand, reputation and Hero of the Village. Add priceMultiplier:0f and demand:0 to each trade to keep them nearly constant.

Change the look

profession sets the outfit and type the biome style: plains, desert, jungle, savanna, snow, swamp or taiga. Match the shop, for example weaponsmith for weapons or librarian for books.

Bedrock Edition

Bedrock's /summon cannot set trades. Build a scoreboard shop with command blocks (pay, then receive), or change trade tables with a behavior pack. The clear command guide shows a command-block payment.

Common questions

Check your command

The Command Error Doctor checks common syntax mistakes in your browser.