Codex CLI Claude Skill

minecraft-datapack

Create, edit, and debug vanilla Minecraft 26.x and 1.21.x datapacks, including functions, advancements, recipes, loot tables, predicates, tags, and pack metadata. Use when the deliverable is a datapack file tree without Java or loader APIs.

LLM Mart · 0 points · 13 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download Jahrome907-minecraft-agent-skills-.codex_skills_minecraft-datapack-dd57c5a.zip · 11 KB
Part of jahrome907/minecraft-agent-skills — 52 skills

Install

skills CLI npx skills add https://github.com/Jahrome907/minecraft-agent-skills/tree/main/.codex/skills/minecraft-datapack
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install jahrome907-minecraft-agent-skills@llmmart
Git git clone https://github.com/Jahrome907/minecraft-agent-skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole jahrome907/minecraft-agent-skills collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

Minecraft Datapack Skill

Inspect pack.mcmeta and the target Minecraft version before editing. Preserve an existing target unless migration is requested; metadata numbers alone do not make older command or registry schemas compatible. Check the edited files with the bundled validator and test in-game when available.

Skill Scope

Routing Boundaries

  • Use when: the deliverable is datapack files (pack.mcmeta, data/...) and .mcfunction/JSON content.
  • Do not use when: the request is command-only snippets not tied to a datapack file tree (minecraft-commands-scripting).
  • Do not use when: the request requires loader APIs, Java code, or runtime mod behavior (minecraft-modding).

Pack Metadata

Minecraft Version Preferred pack metadata
1.21 / 1.21.1 pack_format: 48
1.21.2 / 1.21.3 pack_format: 57
1.21.4 pack_format: 61
1.21.5 pack_format: 71
1.21.6 pack_format: 80
1.21.7 / 1.21.8 pack_format: 81
1.21.9 / 1.21.10 min_format: [88, 0], max_format: [88, 0]
1.21.11 min_format: [94, 1], max_format: [94, 1]
26.1 min_format: [101, 1], max_format: [101, 1]
26.2 min_format: [107, 1], max_format: [107, 1]

Use pack_format for a legacy-only target through data pack format 81. Starting with data pack format 82 in 1.21.9, define both explicit min_format and max_format values. A range that includes a legacy format below 82 must also retain pack_format and supported_formats; do not include supported_formats for a modern-only range. For a legacy-compatible range, supported_formats may be one integer, a two-integer inclusive range, or an object with integer min_inclusive and max_inclusive fields. For exact patch targeting, use [major, minor] arrays for both min_format and max_format, including .0 versions such as [88, 0]. A single integer is equivalent to [major, 0] for min_format, while a single integer in max_format allows any minor version on that major line. Do not write decimal JSON numbers such as 94.1.

Keep pack.mcmeta exact for the patch you target instead of trying to span the multiple Minecraft releases with one metadata block.


Directory Layout

my-datapack/
├── pack.mcmeta
└── data/
    ├── <namespace>/           ← use your pack's name (e.g., mypack)
    │   ├── function/
    │   │   ├── main.mcfunction
    │   │   └── tick.mcfunction
    │   ├── advancement/
    │   │   └── custom_advancement.json
    │   ├── recipe/
    │   │   └── custom_recipe.json
    │   ├── loot_table/
    │   │   └── custom_loot.json
    │   ├── predicate/
    │   │   └── is_night.json
    │   ├── item_modifier/
    │   │   └── add_name.json
    │   └── tags/
    │       ├── block/
    │       │   └── climbable.json
    │       ├── entity_type/
    │       │   └── bosses.json
    │       └── function/
    │           └── custom_flow.json  ← manually invoked tag
    └── minecraft/
        └── tags/function/
            ├── load.json             ← engine tag; runs on /reload
            └── tick.json             ← engine tag; runs every game tick

pack.mcmeta

1.21.8 and earlier

{
  "pack": {
    "pack_format": 81,
    "description": "My Custom Datapack v1.0"
  }
}

Deliberate legacy-to-modern compatibility range

Use this form only when the pack actually supports both sides of the format-82 boundary. Mojang requires the retained legacy fields for this range.

{
  "pack": {
    "pack_format": 81,
    "supported_formats": [81, 88],
    "min_format": [81],
    "max_format": [88],
    "description": "My compatible datapack"
  }
}

1.21.9 / 1.21.10

{
  "pack": {
    "min_format": [88, 0],
    "max_format": [88, 0],
    "description": "My Custom Datapack v1.0"
  }
}

1.21.11

{
  "pack": {
    "min_format": [94, 1],
    "max_format": [94, 1],
    "description": "My Custom Datapack v1.0"
  }
}

26.2

{
  "pack": {
    "min_format": [107, 1],
    "max_format": [107, 1],
    "description": "My Custom Datapack v1.0"
  }
}

Function Tags (load / tick)

The engine recognizes the minecraft:load and minecraft:tick tags, so these files must use the minecraft namespace. A load.json or tick.json in a custom namespace is a valid custom tag name, but it has no automatic behavior.

data/minecraft/tags/function/load.json

{
  "values": [
    "<namespace>:setup"
  ]
}

data/minecraft/tags/function/tick.json

{
  "values": [
    "<namespace>:tick"
  ]
}

data/<namespace>/function/setup.mcfunction

# Runs once on /reload
scoreboard objectives add deaths deathCount
scoreboard objectives add kills playerKillCount
tellraw @a {"text":"[MyPack] Loaded!","color":"green"}

data/<namespace>/function/tick.mcfunction

# Runs every tick — KEEP THIS SHORT
# Only put fast, targeted operations here
execute as @a[scores={deaths=1..}] run function mypack:on_death_check

Commands and Function Syntax

Execute subcommands (datapack-specific patterns)

# Chained execute — common datapack pattern for conditional per-player logic
execute as @a[gamemode=!spectator] at @s if block ~ ~-1 ~ #minecraft:logs run give @s minecraft:apple

# store result into score (bridge between NBT world and scoreboard state)
execute store result score @s mypack.health run data get entity @s Health

# in: run logic in another dimension
execute in minecraft:the_nether run say This runs in the Nether

Storage NBT (datapack-specific global state)

# Storage is the datapack-native key-value store — persists across /reload
data modify storage mypack:data config.difficulty set value "hard"
data get storage mypack:data config.difficulty

# Copy live entity data into storage for macro use or cross-function state
data modify storage mypack:log last_player_pos set from entity @s Pos

For full command syntax, selectors, and scoreboard operations see the Minecraft Wiki — Commands reference. The minecraft-commands-scripting skill covers command-only work in depth.


Macros (1.20.2+)

Macro functions let you pass dynamic arguments to a function.

Define a macro function (data/mypack/function/greet.mcfunction)

# Macro argument: $(name)
$tellraw @a {"text":"Welcome $(name)!","color":"gold"}
$scoreboard players set $(name) points 0

Call with run function + with

# Pass values from storage
data modify storage mypack:tmp input set value {name:"Steve"}
function mypack:greet with storage mypack:tmp input

# Pass values from entity NBT
function mypack:greet with entity @p {}

# Pass value from block NBT
function mypack:greet with block 0 64 0 {}

Registry data examples

Read references/data-examples.md when authoring advancements, recipes, loot tables, predicates, or tags. Load only the relevant section and keep existing namespaces and version targets.

Worldgen Overrides

Override biome noise (data/minecraft/worldgen/noise_settings/overworld.json)

Edit inside an existing copy — do NOT create from scratch without the full JSON. Get the vanilla version from the Minecraft jar: jar xf minecraft.jar data/.

Override a biome's spawn costs

{
  "spawn_costs": {
    "minecraft:zombie": {
      "energy_budget": 0.12,
      "charge": 0.7
    }
  }
}

Installation & Testing

Place the pack folder or ZIP under the world's datapacks/ directory, with pack.mcmeta at its root. Then use these in-game commands:

/datapack list
/datapack enable "file/my-datapack"
/datapack disable "file/my-datapack"
/reload

Development workflow

  1. Edit .mcfunction or .json files
  2. Run the bundled validator to catch JSON and path errors before loading:
    ./scripts/validate-datapack.sh --root /path/to/datapack
    
  3. If errors, fix and re-validate until clean
  4. Run /reload in-game (or /minecraft:reload if a mod intercepts it)
  5. Test with target command (e.g., /function mypack:setup, trigger an advancement)
  6. Check latest.log for runtime errors (missing references, bad selectors)

Common Errors

Error Cause Fix
Unknown or invalid command Syntax error in function Check whitespace, selector, trailing space
Datapack did not load Invalid JSON in any file Validate with jq . < file.json
pack metadata mismatch Wrong pack_format or min_format / max_format values Update pack.mcmeta for the exact 1.21.x patch
Function not running on tick Missing engine tick tag or wrong namespace Check data/minecraft/tags/function/tick.json
Macro error $ line but no with Provide with storage/entity/block

Validator Script

Use the bundled validator script before shipping a datapack update:

# Run from the installed skill directory (for example `.codex/skills/minecraft-datapack`):
./scripts/validate-datapack.sh --root /path/to/datapack

# Strict mode treats warnings as failures:
./scripts/validate-datapack.sh --root /path/to/datapack --strict

What it checks:

  • JSON validity for pack.mcmeta and data/**/*.json
  • Legacy pluralized path mistakes for loot tables, functions, and block/item/function tags
  • data/minecraft/tags/function/load.json and tick.json references resolve to local .mcfunction files
  • custom-namespace load.json and tick.json names, which are valid but do not run automatically

References

Files (minecraft-agent-skills)
  • references
    • data-examples.md 5.3 KB
      # Datapack data examples
      
      Read the section for the requested registry: advancements, recipes, loot
      tables, predicates, or tags. Match its JSON schema to the exact Minecraft
      version; copying pack metadata alone does not port older data.
      
      ## Advancements
      
      ### `data/<namespace>/advancement/my_advancement.json`
      ```json
      {
        "display": {
          "icon": {
            "id": "minecraft:diamond"
          },
          "title": {"text": "Diamond Hunter"},
          "description": {"text": "Obtain your first diamond"},
          "frame": "task",
          "show_toast": true,
          "announce_to_chat": true,
          "hidden": false
        },
        "criteria": {
          "obtained_diamond": {
            "trigger": "minecraft:inventory_changed",
            "conditions": {
              "items": [
                {"items": "minecraft:diamond"}
              ]
            }
          }
        },
        "rewards": {
          "function": "mypack:on_diamond_obtained",
          "experience": 10
        }
      }
      ```
      
      ### Common advancement triggers
      | Trigger | When it fires |
      |---------|--------------|
      | `minecraft:impossible` | Never (use for manual grants) |
      | `minecraft:tick` | Every tick while player is online |
      | `minecraft:player_killed_entity` | Player kills an entity |
      | `minecraft:entity_killed_player` | Entity kills a player |
      | `minecraft:thrown_item_picked_up_by_player` | Player picks up a thrown item |
      | `minecraft:placed_block` | Player places a block |
      | `minecraft:inventory_changed` | Player inventory changes |
      | `minecraft:changed_dimension` | Player changes dimension |
      | `minecraft:consume_item` | Player consumes an item |
      | `minecraft:location` | Player at a specific location |
      | `minecraft:recipe_unlocked` | Player unlocks a recipe |
      
      ---
      
      ## Custom Recipes
      
      These ingredients use the string/tag/list format introduced in 1.21.2 and
      retained in 26.x. Preserve the older ingredient-object schema for 1.21/1.21.1.
      
      ### Shaped crafting (`data/<namespace>/recipe/shaped.json`)
      ```json
      {
        "type": "minecraft:crafting_shaped",
        "pattern": [
          "DDD",
          "D D",
          "DDD"
        ],
        "key": {
          "D": "minecraft:diamond"
        },
        "result": {
          "id": "minecraft:diamond_block",
          "count": 1
        }
      }
      ```
      
      ### Shapeless crafting
      ```json
      {
        "type": "minecraft:crafting_shapeless",
        "ingredients": [
          "minecraft:wheat",
          "minecraft:wheat",
          "minecraft:wheat"
        ],
        "result": {
          "id": "minecraft:bread",
          "count": 2
        }
      }
      ```
      
      ### Smelting / blasting / smoking / campfire
      ```json
      {
        "type": "minecraft:smelting",
        "ingredient": "minecraft:beef",
        "result": { "id": "minecraft:cooked_beef" },
        "experience": 0.35,
        "cookingtime": 200
      }
      ```
      
      ### Disable a vanilla recipe
      
      An empty `{}` is not a valid recipe and produces a decode error. To hide a
      specific lower-priority recipe, merge a top-level `filter` into the existing
      `pack.mcmeta`, alongside its `pack` section. For example, this filter blocks
      the vanilla piston recipe:
      
      ```json
      {
        "filter": {
          "block": [
            {
              "namespace": "minecraft",
              "path": "recipe/piston\\.json"
            }
          ]
        }
      }
      ```
      
      Check the exact path in the target version's vanilla data. Filters only affect
      packs below the filtering pack; they do not remove a replacement recipe in the
      same pack. See [Mojang's pack-filter format](https://www.minecraft.net/en-us/article/minecraft-snapshot-22w11a).
      
      ### Smithing transform
      ```json
      {
        "type": "minecraft:smithing_transform",
        "template": "minecraft:netherite_upgrade_smithing_template",
        "base": "minecraft:diamond_sword",
        "addition": "minecraft:netherite_ingot",
        "result": { "id": "minecraft:netherite_sword" }
      }
      ```
      
      ---
      
      ## Loot Tables
      
      ### `data/<namespace>/loot_table/custom_chest.json`
      ```json
      {
        "type": "minecraft:chest",
        "pools": [
          {
            "rolls": { "type": "minecraft:uniform", "min": 3, "max": 8 },
            "entries": [
              {
                "type": "minecraft:item",
                "name": "minecraft:diamond",
                "weight": 5,
                "functions": [
                  {
                    "function": "minecraft:set_count",
                    "count": { "type": "minecraft:uniform", "min": 1, "max": 3 }
                  }
                ]
              },
              {
                "type": "minecraft:item",
                "name": "minecraft:gold_ingot",
                "weight": 20
              },
              {
                "type": "minecraft:empty",
                "weight": 30
              }
            ]
          }
        ]
      }
      ```
      
      ---
      
      ## Predicates
      
      ### `data/<namespace>/predicate/is_daytime.json`
      ```json
      {
        "condition": "minecraft:time_check",
        "value": { "min": 0, "max": 12000 }
      }
      ```
      
      ### `data/<namespace>/predicate/player_has_diamond.json`
      ```json
      {
        "condition": "minecraft:entity_properties",
        "entity": "this",
        "predicate": {
          "inventory": {
            "items": [
              { "items": ["minecraft:diamond"] }
            ]
          }
        }
      }
      ```
      
      ### Using predicates in functions
      ```mcfunction
      execute if predicate mypack:is_daytime run say It is daytime!
      execute unless predicate mypack:player_has_diamond run tell @s You need a diamond!
      ```
      
      ---
      
      ## Tags
      
      ### Block tag (`data/minecraft/tags/block/climbable.json` — override vanilla)
      ```json
      {
        "replace": false,
        "values": [
          "minecraft:ladder",
          "minecraft:vine",
          "#minecraft:wool"
        ]
      }
      ```
      
      ### Item tag (`data/<namespace>/tags/item/my_fuel.json`)
      ```json
      {
        "replace": false,
        "values": [
          "minecraft:coal",
          "minecraft:charcoal",
          "minecraft:blaze_rod"
        ]
      }
      ```
      
      Use `"replace": false` to append to existing tags. Use `"replace": true` to completely
      override (use with care for vanilla tags).
      
      ---
      
  • scripts
    • jq-shim.mjs 10 KB · in bundle
    • validate-datapack.sh 9.8 KB
      #!/usr/bin/env bash
      set -euo pipefail
      
      PASS='[PASS]'
      WARN='[WARN]'
      FAIL='[FAIL]'
      
      ROOT='.'
      STRICT=0
      
      while [[ $# -gt 0 ]]; do
        case "$1" in
          --root)
            ROOT="${2:-}"
            shift 2
            ;;
          --strict)
            STRICT=1
            shift
            ;;
          --help|-h)
            cat <<'USAGE'
      Usage: validate-datapack.sh [--root <path>] [--strict]
      
      Checks datapack structure and JSON validity:
      - pack.mcmeta and data/** JSON parse with jq
      - current path conventions (loot_table, function, tags/block, tags/item, tags/function)
      - engine load/tick tags under data/minecraft/tags/function resolve local references
      USAGE
            exit 0
            ;;
          *)
            echo "$FAIL unknown arg: $1" >&2
            exit 1
            ;;
        esac
      done
      
      if ! command -v jq >/dev/null 2>&1; then
        SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
        JQ_SHIM="$SCRIPT_DIR/jq-shim.mjs"
        if command -v node >/dev/null 2>&1 && [[ -f "$JQ_SHIM" ]]; then
          jq() {
            node "$JQ_SHIM" "$@"
          }
        else
          echo "$FAIL jq is required"
          exit 1
        fi
      fi
      
      if [[ ! -d "$ROOT" ]]; then
        echo "$FAIL root path does not exist: $ROOT"
        exit 1
      fi
      
      FAILURES=0
      WARNINGS=0
      
      pass() { echo "$PASS $*"; }
      warn() { echo "$WARN $*"; WARNINGS=$((WARNINGS + 1)); }
      fail() { echo "$FAIL $*"; FAILURES=$((FAILURES + 1)); }
      strip_cr() { printf '%s' "${1%$'\r'}"; }
      
      check_json() {
        local file="$1"
        if jq empty "$file" >/dev/null 2>&1; then
          pass "valid JSON: ${file#$ROOT/}"
        else
          fail "invalid JSON: ${file#$ROOT/}"
        fi
      }
      
      check_pack_metadata() {
        local file="$1"
        local min_parts max_parts min_major min_minor max_major max_minor pack_format
        local has_pack_format=0
        local has_min_format=0
        local has_max_format=0
        local has_supported_formats=0
        local valid_pack_format=0
      
        if jq -e '.pack | has("pack_format")' "$file" >/dev/null 2>&1; then
          has_pack_format=1
        fi
        if jq -e '.pack | has("min_format")' "$file" >/dev/null 2>&1; then
          has_min_format=1
        fi
        if jq -e '.pack | has("max_format")' "$file" >/dev/null 2>&1; then
          has_max_format=1
        fi
        if jq -e '.pack | has("supported_formats")' "$file" >/dev/null 2>&1; then
          has_supported_formats=1
        fi
      
        if jq -e '.pack.pack_format | type == "number" and . == floor' "$file" >/dev/null 2>&1; then
          pass "pack.mcmeta uses integer pack.pack_format"
          valid_pack_format=1
        elif [[ "$has_pack_format" -eq 1 ]]; then
          fail "pack.mcmeta pack.pack_format must be an integer when present"
        fi
      
        if jq -e '.pack.min_format | ((type == "number" and . == floor) or (type == "array" and (length == 1 or length == 2) and all(.[]; type == "number" and . == floor)))' "$file" >/dev/null 2>&1; then
          pass "pack.mcmeta uses valid pack.min_format"
        elif [[ "$has_min_format" -eq 1 ]]; then
          fail "pack.mcmeta pack.min_format must be an integer or a one/two-integer array"
        fi
      
        if jq -e '.pack.max_format | ((type == "number" and . == floor) or (type == "array" and (length == 1 or length == 2) and all(.[]; type == "number" and . == floor)))' "$file" >/dev/null 2>&1; then
          pass "pack.mcmeta uses valid pack.max_format"
        elif [[ "$has_max_format" -eq 1 ]]; then
          fail "pack.mcmeta pack.max_format must be an integer or a one/two-integer array"
        fi
      
        if [[ "$has_min_format" -ne "$has_max_format" ]]; then
          fail "pack.mcmeta must define both .pack.min_format and .pack.max_format together"
          return
        fi
      
        if [[ "$has_min_format" -eq 1 ]]; then
          if ! jq -e '.pack.min_format | ((type == "number" and . == floor) or (type == "array" and (length == 1 or length == 2) and all(.[]; type == "number" and . == floor)))' "$file" >/dev/null 2>&1 || ! jq -e '.pack.max_format | ((type == "number" and . == floor) or (type == "array" and (length == 1 or length == 2) and all(.[]; type == "number" and . == floor)))' "$file" >/dev/null 2>&1; then
            return
          fi
      
          min_parts="$(jq -r '.pack.min_format | if type == "number" then "\(.)\t0" elif type == "array" and length == 1 then "\(.[0])\t0" else "\(.[0])\t\(.[1])" end' "$file")"
          max_parts="$(jq -r '.pack.max_format | if type == "number" then "\(.)\t2147483647" elif type == "array" and length == 1 then "\(.[0])\t2147483647" else "\(.[0])\t\(.[1])" end' "$file")"
          IFS=$'\t' read -r min_major min_minor <<<"$min_parts"
          IFS=$'\t' read -r max_major max_minor <<<"$max_parts"
      
          if (( min_major > max_major || (min_major == max_major && min_minor > max_minor) )); then
            fail "pack.mcmeta pack.min_format must not be greater than pack.max_format"
            return
          fi
      
          if (( min_major < 82 )) && [[ "$valid_pack_format" -ne 1 ]]; then
            fail "pack.mcmeta ranges that include legacy data pack formats below 82 require integer pack.pack_format"
            return
          fi
      
          if (( min_major < 82 )) && [[ "$has_supported_formats" -ne 1 ]]; then
            fail "pack.mcmeta ranges that include legacy data pack formats below 82 require pack.supported_formats"
            return
          fi
      
          if (( min_major < 82 )) && ! jq -e '.pack.supported_formats | ((type == "number" and . == floor) or (type == "array" and length == 2 and all(.[]; type == "number" and . == floor)) or (type == "object" and (.min_inclusive | type == "number" and . == floor) and (.max_inclusive | type == "number" and . == floor)))' "$file" >/dev/null 2>&1; then
            fail "pack.mcmeta pack.supported_formats must be an integer, two-integer array, or object with integer min_inclusive and max_inclusive"
            return
          fi
      
          if (( min_major >= 82 )) && [[ "$has_supported_formats" -eq 1 ]]; then
            fail "pack.mcmeta must not define pack.supported_formats for modern-only data pack formats"
            return
          fi
      
          return
        fi
      
        if jq -e '.pack.pack_format | type == "number" and . == floor' "$file" >/dev/null 2>&1; then
          pack_format="$(jq -r '.pack.pack_format | numbers' "$file")"
          if (( pack_format < 82 )); then
            return
          fi
          fail "modern data pack formats 82 and newer require both .pack.min_format and .pack.max_format"
          return
        fi
      
        fail "pack.mcmeta must define legacy integer .pack.pack_format or both .pack.min_format and .pack.max_format"
      }
      
      echo "=== Datapack Validator ==="
      
      echo "Checking required root files..."
      if [[ -f "$ROOT/pack.mcmeta" ]]; then
        check_json "$ROOT/pack.mcmeta"
        check_pack_metadata "$ROOT/pack.mcmeta"
      else
        fail "missing pack.mcmeta"
      fi
      
      if [[ ! -d "$ROOT/data" ]]; then
        fail "missing data/ directory"
      else
        pass "found data/ directory"
      fi
      
      echo "Checking JSON files under data/..."
      while IFS= read -r -d '' json_file; do
        check_json "$json_file"
      done < <(find "$ROOT/data" -type f -name '*.json' -print0 2>/dev/null)
      
      echo "Checking banned legacy paths..."
      while IFS= read -r -d '' bad_path; do
        fail "legacy path detected: ${bad_path#$ROOT/}"
      done < <(find "$ROOT/data" -type f \( -path '*/loot_tables/*' -o -path '*/functions/*' -o -path '*/tags/blocks/*' -o -path '*/tags/items/*' -o -path '*/tags/functions/*' \) -print0 2>/dev/null)
      
      resolve_function_ref() {
        local tag_file="$1"
        local ref="$2"
        local required="$3"
        local ancestry="$4"
        local target_ns target_path resolved kind
      
        if [[ "$ref" == *:* ]]; then
          target_ns="${ref%%:*}"
          target_path="${ref#*:}"
        else
          fail "invalid function id (missing namespace): ${tag_file#$ROOT/} -> $ref"
          return
        fi
      
        if [[ "$ref" == \#* ]]; then
          target_ns="${target_ns#\#}"
          kind="function tag"
          resolved="$ROOT/data/$target_ns/tags/function/$target_path.json"
        else
          kind="function"
          resolved="$ROOT/data/$target_ns/function/$target_path.mcfunction"
        fi
      
        if [[ ! -d "$ROOT/data/$target_ns" ]]; then
          warn "external $kind reference not verified: ${tag_file#$ROOT/} -> $ref"
          return
        fi
      
        if [[ -f "$resolved" ]]; then
          pass "$kind target exists: $ref"
          if [[ "$ref" == \#* ]]; then
            if [[ "$ancestry" == *"|$resolved|"* ]]; then
              fail "cyclic function tag reference: ${tag_file#$ROOT/} -> $ref"
              return
            fi
            check_function_tag "$resolved" "$ancestry|$resolved|"
          fi
        elif [[ "$required" == "false" ]]; then
          pass "optional $kind reference is absent: $ref"
        else
          fail "missing $kind for tag reference: $ref (expected ${resolved#$ROOT/})"
        fi
      }
      
      check_function_tag() {
        local tag_file="$1"
        local ancestry="$2"
        local required ref
      
        if ! jq -e '.values | type == "array"' "$tag_file" >/dev/null 2>&1; then
          fail "tag file missing array .values: ${tag_file#$ROOT/}"
          return
        fi
      
        while IFS=$'\t' read -r required ref; do
          required="$(strip_cr "$required")"
          ref="$(strip_cr "$ref")"
          if [[ "$required" == "invalid" || -z "$ref" ]]; then
            fail "invalid function tag entry: ${tag_file#$ROOT/}"
            continue
          fi
          resolve_function_ref "$tag_file" "$ref" "$required" "$ancestry"
        done < <(jq -r '.values[]? | if type == "string" then "true\t" + . elif type == "object" and (.id | type == "string") and ((.required? // true) | type == "boolean") then ((if .required == false then "false" else "true" end) + "\t" + .id) else "invalid\t" end' "$tag_file")
      }
      
      echo "Checking custom namespace load/tick tag names..."
      while IFS= read -r -d '' tag_file; do
        case "${tag_file#"$ROOT/data/"}" in
          minecraft/*) ;;
          *) warn "custom namespace load/tick tag has no automatic engine behavior: ${tag_file#$ROOT/} (use data/minecraft/tags/function/)" ;;
        esac
      done < <(find "$ROOT/data" -type f \( -path '*/tags/function/load.json' -o -path '*/tags/function/tick.json' \) -print0 2>/dev/null)
      
      echo "Checking engine load/tick function tag references..."
      for tag_file in "$ROOT/data/minecraft/tags/function/load.json" "$ROOT/data/minecraft/tags/function/tick.json"; do
        [[ -f "$tag_file" ]] || continue
        check_function_tag "$tag_file" "|$tag_file|"
      done
      
      echo ""
      if [[ "$FAILURES" -gt 0 ]]; then
        echo "$FAIL datapack validation failed with $FAILURES error(s) and $WARNINGS warning(s)"
        exit 1
      fi
      
      if [[ "$STRICT" -eq 1 && "$WARNINGS" -gt 0 ]]; then
        echo "$FAIL datapack validation strict mode failed on $WARNINGS warning(s)"
        exit 1
      fi
      
      echo "$PASS datapack validation passed with $WARNINGS warning(s)"
      
  • SKILL.md 10.6 KB
    ---
    name: minecraft-datapack
    description: "Create, edit, and debug vanilla Minecraft 26.x and 1.21.x datapacks, including functions, advancements, recipes, loot tables, predicates, tags, and pack metadata. Use when the deliverable is a datapack file tree without Java or loader APIs."
    ---
    
    # Minecraft Datapack Skill
    
    Inspect `pack.mcmeta` and the target Minecraft version before editing. Preserve
    an existing target unless migration is requested; metadata numbers alone do not
    make older command or registry schemas compatible. Check the edited files with
    the bundled validator and test in-game when available.
    
    ## Skill Scope
    
    ### Routing Boundaries
    - `Use when`: the deliverable is datapack files (`pack.mcmeta`, `data/...`) and `.mcfunction`/JSON content.
    - `Do not use when`: the request is command-only snippets not tied to a datapack file tree (`minecraft-commands-scripting`).
    - `Do not use when`: the request requires loader APIs, Java code, or runtime mod behavior (`minecraft-modding`).
    
    ---
    
    ## Pack Metadata
    
    | Minecraft Version | Preferred `pack` metadata |
    |-------------------|---------------------------|
    | 1.21 / 1.21.1     | `pack_format: 48` |
    | 1.21.2 / 1.21.3   | `pack_format: 57` |
    | 1.21.4            | `pack_format: 61` |
    | 1.21.5            | `pack_format: 71` |
    | 1.21.6            | `pack_format: 80` |
    | 1.21.7 / 1.21.8   | `pack_format: 81` |
    | 1.21.9 / 1.21.10  | `min_format: [88, 0]`, `max_format: [88, 0]` |
    | 1.21.11           | `min_format: [94, 1]`, `max_format: [94, 1]` |
    | 26.1              | `min_format: [101, 1]`, `max_format: [101, 1]` |
    | 26.2              | `min_format: [107, 1]`, `max_format: [107, 1]` |
    
    Use `pack_format` for a legacy-only target through data pack format 81. Starting
    with data pack format 82 in 1.21.9, define both explicit `min_format` and
    `max_format` values. A range that includes a legacy format below 82 must also
    retain `pack_format` and `supported_formats`; do not include
    `supported_formats` for a modern-only range.
    For a legacy-compatible range, `supported_formats` may be one integer, a
    two-integer inclusive range, or an object with integer `min_inclusive` and
    `max_inclusive` fields.
    For exact patch targeting, use `[major, minor]` arrays for both `min_format` and
    `max_format`, including `.0` versions such as `[88, 0]`. A single integer is
    equivalent to `[major, 0]` for `min_format`, while a single integer in
    `max_format` allows any minor version on that major line. Do not write decimal
    JSON numbers such as `94.1`.
    
    Keep `pack.mcmeta` exact for the patch you target instead of trying to span the
    multiple Minecraft releases with one metadata block.
    
    ---
    
    ## Directory Layout
    
    ```
    my-datapack/
    ├── pack.mcmeta
    └── data/
        ├── <namespace>/           ← use your pack's name (e.g., mypack)
        │   ├── function/
        │   │   ├── main.mcfunction
        │   │   └── tick.mcfunction
        │   ├── advancement/
        │   │   └── custom_advancement.json
        │   ├── recipe/
        │   │   └── custom_recipe.json
        │   ├── loot_table/
        │   │   └── custom_loot.json
        │   ├── predicate/
        │   │   └── is_night.json
        │   ├── item_modifier/
        │   │   └── add_name.json
        │   └── tags/
        │       ├── block/
        │       │   └── climbable.json
        │       ├── entity_type/
        │       │   └── bosses.json
        │       └── function/
        │           └── custom_flow.json  ← manually invoked tag
        └── minecraft/
            └── tags/function/
                ├── load.json             ← engine tag; runs on /reload
                └── tick.json             ← engine tag; runs every game tick
    ```
    
    ---
    
    ## `pack.mcmeta`
    
    ### 1.21.8 and earlier
    
    ```json
    {
      "pack": {
        "pack_format": 81,
        "description": "My Custom Datapack v1.0"
      }
    }
    ```
    
    ### Deliberate legacy-to-modern compatibility range
    
    Use this form only when the pack actually supports both sides of the format-82
    boundary. Mojang requires the retained legacy fields for this range.
    
    ```json
    {
      "pack": {
        "pack_format": 81,
        "supported_formats": [81, 88],
        "min_format": [81],
        "max_format": [88],
        "description": "My compatible datapack"
      }
    }
    ```
    
    ### 1.21.9 / 1.21.10
    
    ```json
    {
      "pack": {
        "min_format": [88, 0],
        "max_format": [88, 0],
        "description": "My Custom Datapack v1.0"
      }
    }
    ```
    
    ### 1.21.11
    
    ```json
    {
      "pack": {
        "min_format": [94, 1],
        "max_format": [94, 1],
        "description": "My Custom Datapack v1.0"
      }
    }
    ```
    
    ### 26.2
    
    ```json
    {
      "pack": {
        "min_format": [107, 1],
        "max_format": [107, 1],
        "description": "My Custom Datapack v1.0"
      }
    }
    ```
    
    ---
    
    ## Function Tags (load / tick)
    
    The engine recognizes the `minecraft:load` and `minecraft:tick` tags, so these
    files must use the `minecraft` namespace. A `load.json` or `tick.json` in a
    custom namespace is a valid custom tag name, but it has no automatic behavior.
    
    ### `data/minecraft/tags/function/load.json`
    ```json
    {
      "values": [
        "<namespace>:setup"
      ]
    }
    ```
    
    ### `data/minecraft/tags/function/tick.json`
    ```json
    {
      "values": [
        "<namespace>:tick"
      ]
    }
    ```
    
    ### `data/<namespace>/function/setup.mcfunction`
    ```mcfunction
    # Runs once on /reload
    scoreboard objectives add deaths deathCount
    scoreboard objectives add kills playerKillCount
    tellraw @a {"text":"[MyPack] Loaded!","color":"green"}
    ```
    
    ### `data/<namespace>/function/tick.mcfunction`
    ```mcfunction
    # Runs every tick — KEEP THIS SHORT
    # Only put fast, targeted operations here
    execute as @a[scores={deaths=1..}] run function mypack:on_death_check
    ```
    
    ---
    
    ## Commands and Function Syntax
    
    ### Execute subcommands (datapack-specific patterns)
    ```mcfunction
    # Chained execute — common datapack pattern for conditional per-player logic
    execute as @a[gamemode=!spectator] at @s if block ~ ~-1 ~ #minecraft:logs run give @s minecraft:apple
    
    # store result into score (bridge between NBT world and scoreboard state)
    execute store result score @s mypack.health run data get entity @s Health
    
    # in: run logic in another dimension
    execute in minecraft:the_nether run say This runs in the Nether
    ```
    
    ### Storage NBT (datapack-specific global state)
    ```mcfunction
    # Storage is the datapack-native key-value store — persists across /reload
    data modify storage mypack:data config.difficulty set value "hard"
    data get storage mypack:data config.difficulty
    
    # Copy live entity data into storage for macro use or cross-function state
    data modify storage mypack:log last_player_pos set from entity @s Pos
    ```
    
    For full command syntax, selectors, and scoreboard operations see the
    [Minecraft Wiki — Commands](https://minecraft.wiki/w/Commands) reference.
    The `minecraft-commands-scripting` skill covers command-only work in depth.
    
    ---
    
    ## Macros (1.20.2+)
    
    Macro functions let you pass dynamic arguments to a function.
    
    ### Define a macro function (`data/mypack/function/greet.mcfunction`)
    ```mcfunction
    # Macro argument: $(name)
    $tellraw @a {"text":"Welcome $(name)!","color":"gold"}
    $scoreboard players set $(name) points 0
    ```
    
    ### Call with `run function` + `with`
    ```mcfunction
    # Pass values from storage
    data modify storage mypack:tmp input set value {name:"Steve"}
    function mypack:greet with storage mypack:tmp input
    
    # Pass values from entity NBT
    function mypack:greet with entity @p {}
    
    # Pass value from block NBT
    function mypack:greet with block 0 64 0 {}
    ```
    
    ---
    
    ## Registry data examples
    
    Read [references/data-examples.md](references/data-examples.md) when authoring
    advancements, recipes, loot tables, predicates, or tags. Load only the relevant
    section and keep existing namespaces and version targets.
    
    ## Worldgen Overrides
    
    ### Override biome noise (`data/minecraft/worldgen/noise_settings/overworld.json`)
    Edit inside an existing copy — do NOT create from scratch without the full JSON.
    Get the vanilla version from the Minecraft jar: `jar xf minecraft.jar data/`.
    
    ### Override a biome's spawn costs
    ```json
    {
      "spawn_costs": {
        "minecraft:zombie": {
          "energy_budget": 0.12,
          "charge": 0.7
        }
      }
    }
    ```
    
    ---
    
    ## Installation & Testing
    
    Place the pack folder or ZIP under the world's `datapacks/` directory, with
    `pack.mcmeta` at its root. Then use these in-game commands:
    
    ```text
    /datapack list
    /datapack enable "file/my-datapack"
    /datapack disable "file/my-datapack"
    /reload
    ```
    
    ### Development workflow
    1. Edit `.mcfunction` or `.json` files
    2. Run the bundled validator to catch JSON and path errors before loading:
       ```bash
       ./scripts/validate-datapack.sh --root /path/to/datapack
       ```
    3. If errors, fix and re-validate until clean
    4. Run `/reload` in-game (or `/minecraft:reload` if a mod intercepts it)
    5. Test with target command (e.g., `/function mypack:setup`, trigger an advancement)
    6. Check `latest.log` for runtime errors (missing references, bad selectors)
    
    ---
    
    ## Common Errors
    
    | Error | Cause | Fix |
    |-------|-------|-----|
    | `Unknown or invalid command` | Syntax error in function | Check whitespace, selector, trailing space |
    | `Datapack did not load` | Invalid JSON in any file | Validate with `jq . < file.json` |
    | `pack metadata mismatch` | Wrong `pack_format` or `min_format` / `max_format` values | Update `pack.mcmeta` for the exact 1.21.x patch |
    | Function not running on tick | Missing engine tick tag or wrong namespace | Check `data/minecraft/tags/function/tick.json` |
    | Macro error | `$` line but no `with` | Provide `with storage/entity/block` |
    
    ## Validator Script
    
    Use the bundled validator script before shipping a datapack update:
    
    ```bash
    # Run from the installed skill directory (for example `.codex/skills/minecraft-datapack`):
    ./scripts/validate-datapack.sh --root /path/to/datapack
    
    # Strict mode treats warnings as failures:
    ./scripts/validate-datapack.sh --root /path/to/datapack --strict
    ```
    
    What it checks:
    - JSON validity for `pack.mcmeta` and `data/**/*.json`
    - Legacy pluralized path mistakes for loot tables, functions, and block/item/function tags
    - `data/minecraft/tags/function/load.json` and `tick.json` references resolve to local `.mcfunction` files
    - custom-namespace `load.json` and `tick.json` names, which are valid but do not run automatically
    
    ---
    
    ## References
    
    - Minecraft Wiki — Data Pack: https://minecraft.wiki/w/Data_pack
    - Minecraft Java Edition 1.21.9 release notes: https://www.minecraft.net/en-us/article/minecraft-java-edition-1-21-9
    - Minecraft Wiki — Function: https://minecraft.wiki/w/Function_(Java_Edition)
    - Minecraft Wiki — Commands: https://minecraft.wiki/w/Commands
    - Pack format history: https://minecraft.wiki/w/Pack_format
    - NBT format: https://minecraft.wiki/w/NBT_format
    - Predicate conditions: https://minecraft.wiki/w/Predicate
    - Loot table format: https://minecraft.wiki/w/Loot_table
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related