Install
$ agentstack add skill-mrpippi-mjp-claude-skills-inventory ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ● Filesystem access Used
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Inventory Skill — Paper
Purpose
Reference this skill when building custom clickable inventory GUIs (chest menus, shop UIs, settings screens) in Paper 1.21. Covers inventory creation, InventoryHolder pattern, and event-driven click handling.
When to Use This Skill
- Creating a chest-style menu that players open with a command
- Building a shop or upgrade UI where clicks perform actions
- Handling
InventoryClickEventto route button clicks to plugin logic - Preventing players from taking items out of a GUI inventory
API Quick Reference
| Class / Method | Purpose | Notes | |---------------|---------|-------| | Bukkit.createInventory(holder, size, title) | Create a custom inventory | size must be multiple of 9 (9–54) | | Inventory#setItem(slot, ItemStack) | Place an item in a slot | Slots 0–53 for double chest | | Player#openInventory(Inventory) | Open for a player | Main thread only | | InventoryHolder | Tag interface to identify plugin inventories | Implement on your GUI class | | InventoryClickEvent | Fires when any slot is clicked | Check event.getInventory() | | InventoryOpenEvent | Fires when inventory is opened | | | InventoryCloseEvent | Fires when inventory is closed | | | event.setCancelled(true) | Prevent item movement | Use in GUI click handler | | event.getSlot() | Clicked slot index | Top inventory slots only | | event.getClick() | ClickType (LEFT, RIGHT, SHIFT_LEFT…) | | | event.getWhoClicked() | HumanEntity (cast to Player) | |
Code Pattern
package com.yourorg.myplugin.gui;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextDecoration;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import java.util.List;
// --- 1. Inventory Holder: identifies this as a plugin GUI ---
public class MainMenuGui implements InventoryHolder {
private final Inventory inventory;
public MainMenuGui() {
// 27-slot (3-row) chest with Adventure title
this.inventory = Bukkit.createInventory(this, 27,
Component.text("Main Menu").color(NamedTextColor.DARK_AQUA));
buildContents();
}
private void buildContents() {
// Fill background with grey glass panes
ItemStack filler = makeButton(Material.GRAY_STAINED_GLASS_PANE, " ", List.of());
for (int i = 0; i lore) {
ItemStack item = new ItemStack(mat);
ItemMeta meta = item.getItemMeta();
meta.displayName(Component.text(name)
.color(NamedTextColor.WHITE)
.decoration(TextDecoration.ITALIC, false));
meta.lore(lore.stream()
.map(c -> c.decoration(TextDecoration.ITALIC, false))
.toList());
item.setItemMeta(meta);
return item;
}
public void open(Player player) {
player.openInventory(inventory);
}
@Override
public @NotNull Inventory getInventory() {
return inventory;
}
}
// --- 2. Event Listener: handles all GUI clicks ---
class GuiListener implements Listener {
@EventHandler
public void onInventoryClick(InventoryClickEvent event) {
// Only handle clicks inside MainMenuGui inventories
if (!(event.getInventory().getHolder() instanceof MainMenuGui gui)) return;
// Always cancel to prevent item theft
event.setCancelled(true);
// Ignore clicks on the player's own bottom inventory
if (event.getClickedInventory() != event.getInventory()) return;
if (!(event.getWhoClicked() instanceof Player player)) return;
switch (event.getSlot()) {
case 11 -> player.performCommand("shop");
case 13 -> player.performCommand("stats");
case 15 -> player.closeInventory();
}
}
@EventHandler
public void onInventoryClose(InventoryCloseEvent event) {
if (event.getInventory().getHolder() instanceof MainMenuGui) {
// Clean up resources if needed when GUI is closed
}
}
}
Common Pitfalls
- Not cancelling
InventoryClickEvent: Withoutevent.setCancelled(true), players can pick up the glass-pane filler items or swap them with their own inventory.
- Not checking
getClickedInventory():InventoryClickEventfires for BOTH the top (GUI) inventory and the player's bottom inventory. Filter byevent.getClickedInventory() == event.getInventory()to only handle GUI slots.
- Creating a new
Inventoryobject per click: CallingBukkit.createInventory()on every click creates thousands of objects. Create the inventory once (in the constructor oropen()) and reuse it.
- Identifying GUIs by title string: Titles are user-visible and can collide with other plugins. Always use
InventoryHolder(instanceofcheck) to identify your GUIs reliably.
- Updating items while the GUI is open: Use
inventory.setItem(slot, newItem)to update live — the client sees the change without reopening.
Version Notes
- 1.21 / 1.21.1:
Bukkit.createInventory(holder, size, Component title)is the standard Adventure-title overload. TheStringoverload is deprecated.
Related Skills
- [inventory-events.md](inventory-events.md) — Full InventoryClickEvent, DragEvent reference
- [../items/SKILL.md](../items/SKILL.md) — Building ItemStack buttons
- [../events/SKILL.md](../events/SKILL.md) — Event registration
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: MrPippi
- Source: MrPippi/MJP-Claude-Skills
- License: MIT
- Homepage: https://mrpippi.github.io/MJP-Claude-Skills/
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.