agentsclimarketplace

Commands

Skill MrPippi/MJP-Claude-Skills/docs/paper/commands

Minecraft Java Plugin Claude Skills

Install
npx -y skills add MrPippi/MJP-Claude-Skills --skill commands

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

7.3 KB, as published. Nobody here has run it

Commands Skill — Paper

Purpose

Reference this skill when registering commands on a Paper 1.21 server. Paper uses native Brigadier via LifecycleEvents.COMMANDS; this skill explains when and how to use it versus the legacy CommandExecutor approach.

When to Use This Skill

  • Creating any /command that players or operators can run
  • Adding tab completion to commands
  • Restricting commands to players with specific permissions
  • Supporting subcommands with typed arguments (integers, players, locations, etc.)

API Quick Reference

Class / MethodPurposeNotes
LifecycleEvents.COMMANDSThe lifecycle event type for command registrationUse with getLifecycleManager().registerEventHandler()
Commands (registrar)The Brigadier command registrar provided by PaperObtained from the lifecycle event
Commands#register(LiteralCommandNode, String)Register a root command node with descriptionDescription appears in /help
Commands.literal(String)Start building a command literal nodeFrom com.mojang.brigadier.builder.LiteralArgumentBuilder via Paper wrapper
Commands.argument(String, ArgumentType<T>)Add a typed argumentFrom Brigadier's argument builder
ArgumentTypesPaper's built-in argument typesArgumentTypes.player(), ArgumentTypes.world(), etc.
StringArgumentType.word()Single-word string argumentFrom com.mojang.brigadier.arguments
IntegerArgumentType.integer(min, max)Bounded integer argument
CommandSourceStackThe command source (wraps CommandSender)Access via ctx.getSource()
ctx.getSource().getSender()Get the underlying CommandSenderCast to Player if needed
Command.SINGLE_SUCCESSReturn value indicating successReturn 1 from executes lambda
.requires(source -> ...)Permission predicate for the commandCheck source.getSender().hasPermission(...)

Code Pattern

package com.yourorg.myplugin.commands;

import com.mojang.brigadier.Command;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.context.CommandContext;
import io.papermc.paper.command.brigadier.CommandSourceStack;
import io.papermc.paper.command.brigadier.Commands;
import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;

public class CommandRegistrar {

    public static void register(JavaPlugin plugin) {
        plugin.getLifecycleManager().registerEventHandler(
            LifecycleEvents.COMMANDS,
            event -> {
                Commands commands = event.registrar();

                // Simple command: /myplugin
                commands.register(
                    Commands.literal("myplugin")
                        .requires(source -> source.getSender().hasPermission("myplugin.use"))
                        .executes(ctx -> executeRoot(ctx))
                        // Subcommand: /myplugin info <playerName>
                        .then(Commands.literal("info")
                            .then(Commands.argument("player", StringArgumentType.word())
                                .executes(ctx -> executeInfo(ctx))))
                        // Subcommand: /myplugin level <number>
                        .then(Commands.literal("level")
                            .requires(source -> source.getSender().hasPermission("myplugin.admin"))
                            .then(Commands.argument("amount", IntegerArgumentType.integer(1, 100))
                                .executes(ctx -> executeLevel(ctx))))
                        .build(),
                    "MyPlugin main command"
                );
            }
        );
    }

    private static int executeRoot(CommandContext<CommandSourceStack> ctx) {
        CommandSender sender = ctx.getSource().getSender();
        sender.sendMessage(Component.text("MyPlugin v1.0.0").color(NamedTextColor.GOLD));
        return Command.SINGLE_SUCCESS;
    }

    private static int executeInfo(CommandContext<CommandSourceStack> ctx) {
        CommandSender sender = ctx.getSource().getSender();
        String targetName = StringArgumentType.getString(ctx, "player");

        Player target = sender.getServer().getPlayer(targetName);
        if (target == null) {
            sender.sendMessage(Component.text("Player not found: " + targetName)
                .color(NamedTextColor.RED));
            return 0;   // return 0 to signal failure
        }

        sender.sendMessage(Component.text("Player: " + target.getName())
            .color(NamedTextColor.AQUA));
        return Command.SINGLE_SUCCESS;
    }

    private static int executeLevel(CommandContext<CommandSourceStack> ctx) {
        CommandSender sender = ctx.getSource().getSender();

        // Only allow players (not console) for level command
        if (!(sender instanceof Player player)) {
            sender.sendMessage(Component.text("Only players can use this command.")
                .color(NamedTextColor.RED));
            return 0;
        }

        int amount = IntegerArgumentType.getInteger(ctx, "amount");
        player.setLevel(player.getLevel() + amount);
        player.sendMessage(Component.text("Level set to " + player.getLevel())
            .color(NamedTextColor.GREEN));
        return Command.SINGLE_SUCCESS;
    }
}

Call from main class onEnable:

CommandRegistrar.register(this);

Common Pitfalls

  • Mixing Brigadier with plugin.yml command declarations: Brigadier commands registered via LifecycleEvents.COMMANDS do NOT need to be declared in plugin.yml. Declaring them there AND in Brigadier causes duplicate registration in some builds.

  • Returning 0 without a message: When a command fails, always send an error message before returning 0. Silent failures confuse players.

  • Using CommandSender as Player without checking: Console can run commands too. Always instanceof check before casting: if (sender instanceof Player player) { ... }.

  • Registering commands outside LifecycleEvents.COMMANDS: Calling Commands.register() directly in onEnable() (not inside the lifecycle handler) is not supported and will throw an error.

  • Forgetting .requires() for admin commands: Without a permission check, any player can run admin subcommands.

Version Notes

  • 1.21: LifecycleEvents.COMMANDS is the supported way to register Brigadier commands. Legacy CommandExecutor still works but does not support typed arguments or native client-side suggestions.
  • 1.21.1: No breaking changes to command API.
  • Both: Tab completion for Brigadier arguments is automatic — clients see valid options natively without any SuggestionProvider for built-in types. See command-completion.md for custom suggestions.

Related Skills

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.