---
title: Commands
description: Declare the command tree the client uses for tab completion and syntax highlighting, without hand-numbering nodes.
---

The [Commands](/packets/play/commands) packet tells the client which commands exist and how each argument parses. It is a flat array of nodes that reference each other by index, with flags packed into a byte and a parser ID plus parser-specific properties per argument. `helpers.createCommandTree` takes a nested description and produces that array.

```ts
import { helpers } from "mc-protocol";
const { literal, argument, createCommandTree } = helpers;

await client.sendPacket("Commands", createCommandTree([
  literal("ping", { executable: true }),
  literal("tp", {
    children: [argument("target", "entity", { single: true, playersOnly: true, executable: true })],
  }),
  literal("say", {
    children: [argument("message", "string", { type: "greedy", executable: true, suggestions: "ask_server" })],
  }),
  literal("run", { redirect: "root" }),
]));
```

This declares the syntax. Executing commands is still yours: listen for the serverbound [ChatCommand](/packets/play/chatcommand) packet and parse `data.command`.

## `createCommandTree(commands)`

Takes the root's children and returns `{ nodes, rootIndex: 0 }`, ready for `sendPacket("Commands", …)`. Nodes are numbered breadth-first from the root, so the order of your array is the order of the root's children. A node object that appears under several parents is written once and shared.

Errors are thrown before anything is encoded when:

- a `redirect` points at a node that is not reachable from the root (`RangeError`)
- two children of the same node share a name (`RangeError`)
- children form a cycle; loops such as `/execute run …` must use `redirect` (`RangeError`)
- an element is not a node from `literal()` or `argument()` (`TypeError`)

## `literal(name, options?)`

A fixed keyword. Options:

| Option | Type | Wire effect |
| --- | --- | --- |
| `children` | `CommandNode[]` | child indices |
| `executable` | `boolean` | flag `0x04`; the path up to this node is a complete command |
| `restricted` | `boolean` | flag `0x20`; requires permission level above 0 |
| `redirect` | `CommandNode \| "root"` | flag `0x08` and the target's index |

## `argument(name, parser, options?)`

A typed argument. `parser` is any 26.2 parser name, with or without its prefix: `"integer"` and `"brigadier:integer"` are the same, as are `"entity"` and `"minecraft:entity"`. Unknown names throw; the parser's numeric ID is resolved by the wire codec. Argument nodes take the literal options plus `suggestions`, a suggestion provider: `"ask_server"`, `"all_recipes"`, `"available_sounds"`, `"summonable_entities"`, or a namespaced identifier.

Parsers with properties take them in the same options object. TypeScript narrows the accepted keys by parser name.

| Parser | Properties | Notes |
| --- | --- | --- |
| `float`, `double`, `integer` | `min?`, `max?` | Only the bounds you pass are written. `integer` bounds must be 32-bit integers. |
| `long` | `min?`, `max?` | `number` or `bigint`, 64-bit signed. |
| `string` | `type?` | `"word"` (default), `"phrase"` (quotable), or `"greedy"` (rest of the line). |
| `entity` | `single?`, `playersOnly?` | Both default to `false`. |
| `score_holder` | `multiple?` | Defaults to `false`. |
| `time` | `min?` | Minimum ticks, defaults to `0`. |
| `resource`, `resource_key`, `resource_or_tag`, `resource_or_tag_key`, `resource_selector` | `registry` | Required. `"entity_type"` becomes `"minecraft:entity_type"`. |

Every other parser (`block_pos`, `vec3`, `game_profile`, `uuid`, `component`, …) takes no properties. Any key a parser does not take throws, so a typo like `mni` is caught. The full list is `helpers.COMMAND_PARSERS`. The wiki's `minecraft:nbt_compound_tag` is spelled `minecraft:nbt` in the bundled codec; `nbt_compound_tag`, `nbt` and their prefixed forms are all accepted and encode as parser 21.

```ts
argument("amount", "integer", { min: 1, max: 64, executable: true });
argument("delay", "time", { min: 20 });
argument("biome", "resource_or_tag", { registry: "worldgen/biome", executable: true });
argument("mode", "gamemode", { executable: true });
```

## Sharing and redirects

Nodes are plain objects. Keep a reference to share a subtree or to point a redirect at it:

```ts
const amount = argument("amount", "integer", { min: 1, executable: true });
const give = literal("give", { children: [amount] });
const take = literal("take", { children: [amount] });
const again = literal("again", { redirect: give }); // continue parsing at /give

createCommandTree([give, take, again]); // 5 nodes: root, give, take, again, amount
```

`redirect: "root"` is how vanilla builds `/execute run <command>`: after `run`, parsing restarts at the root.

## What you get back

```ts
createCommandTree([literal("ping", { executable: true })]);
// {
//   nodes: [
//     { flags: { unused: 0, allows_restricted: 0, has_custom_suggestions: 0, has_redirect_node: 0, has_command: 0, command_node_type: 0 }, children: [1] },
//     { flags: { …, has_command: 1, command_node_type: 1 }, children: [], extraNodeData: { name: "ping" } },
//   ],
//   rootIndex: 0,
// }
```

The `flags` object uses the codec's bit-field names. Argument nodes carry `extraNodeData: { name, parser, properties, suggestionType }`. Decoded packets return enums as `{ value, translation }`, so a node read from the wire has `parser: { value: 6, translation: "minecraft:entity" }` rather than the bare string.

## Types

All exported through `helpers`: `CommandNode`, `CommandNodeWire`, `CommandsPacket`, `CommandNodeOptions`, `ArgumentNodeOptions`, `CommandParser`, `CommandParserName`, `ParserProperties<P>`, `CommandSuggestions`.
