MulverineX

mcdoc-language

Comprehensive guide to the mcdoc schema language for describing Minecraft data structures (CODECs, JSONs, NBTs). Use when working with .mcdoc files, writing schemas, understanding type definitions, dispatch systems, or validating Minecraft data formats.

MulverineX 0 Updated 4mo ago
GitHub

Install

npx skillscat add mulverinex/mcdoc-language-skill

Install via the SkillsCat registry.

SKILL.md

mcdoc Language Guide

mcdoc is a schema format for describing data structures used by Minecraft, including its CODECs, JSONs, and NBTs. It is interpreted by the Spyglass language server.

Core Concepts

Primitive Types

  • boolean - true/false values
  • byte, short, int, long - Integer types (use suffix: 0b, 0s, 0, 0L)
  • float, double - Floating-point types
  • string - Text values
  • any - Accepts any value (use sparingly)

Struct Definitions

Structs define composite types with named fields:

struct EntityData {
    id: string,              // Required field
    pos?: [double] @ 3,      // Optional field (note the ?)
    /// Doc comment shown in hover
    custom_name?: string,
}

Enum Definitions

Enums define fixed sets of values with a backing type:

enum(string) Direction {
    Down = "down",
    Up = "up",
    North = "north",
}

enum(byte) DyeColorByte {
    White = 0,
    Orange = 1,
}

Type Aliases

Type aliases create named references to types, optionally with generics:

type Text = ::java::util::text::Text

type Tag<E> = struct {
    replace?: boolean,
    values: [TagEntry<E>],
}

Union Types

Union types allow a value to be one of several types (using |):

type Color = (
    #[color="hex_rgb"] string |
    #[color="composite_rgb"] int |
)

Arrays and Collections

[Type]           // Array of any length
[Type] @ 3       // Array of exactly 3 elements
[Type] @ 1..5    // Array with 1-5 elements
[Type] @ 1..     // Array with 1+ elements

Numeric Constraints

int @ 0..100       // Inclusive range (0 to 100)
float @ 0<..1      // Exclusive lower bound (>0, <=1)
float @ 0..<1      // Exclusive upper bound (>=0, <1)
int @ 0..          // Minimum only (0 or greater)

Dispatch System

Dispatches map registry entries to specific type schemas:

// Direct dispatch
dispatch minecraft:entity[zombie] to struct Zombie {
    ...super::Monster,
    CanBreakDoors?: boolean,
}

// Multiple keys to same type
dispatch minecraft:item[potion, splash_potion, lingering_potion] to struct PotionItem {
    Potion?: #[id="potion"] string,
}

// Polymorphic dispatch using a type field
struct TreeDecorator {
    type: #[id="worldgen/tree_decorator_type"] string,
    ...minecraft:tree_decorator[[type]],  // Dispatch based on type field value
}

Special Dispatch Keys

  • %unknown - Fallback for unrecognized keys
  • %none - When no key is specified
  • %fallback - Used in NBT path references

Path Variables

Path variables reference values from the data structure in dispatch keys:

  • %parent - The containing struct (chain for ancestors: %parent.%parent)
  • %key - The current map key (in mapped types)
struct CopyState {
    block: #[id="block"] string,
    properties: [mcdoc:block_state_keys[[%parent.block]]],  // References sibling field
}

// Arrays don't count as levels - %parent skips over them
struct Container {
    modifier: ModifierType,
    items: [struct {
        value: minecraft:modifier[[%parent.%parent.modifier]],  // %parent.%parent = Container
    }],
}

Generic Dispatchers

Dispatchers can have type parameters:

// Define generic dispatcher
dispatch minecraft:int_provider[constant]<T> to struct {
    value: T,
}

dispatch minecraft:int_provider[uniform]<T> to struct {
    min_inclusive: T,
    max_inclusive: T,
}

// Use with type parameter passed through
type IntProvider<T> = (
    T |
    struct {
        type: #[id="int_provider_type"] string,
        ...minecraft:int_provider[[type]]<T>,
    } |
)

// Usage with constrained type
log_length: IntProvider<int @ 0..16>,

Structural Composition

Spread Operator

The spread operator (...) includes fields from another struct:

struct Monster {
    Health?: float,
}

dispatch minecraft:entity[zombie] to struct Zombie {
    ...super::Monster,       // Include Monster fields
    CanBreakDoors?: boolean, // Add zombie-specific fields
}

Inline Structs

Structs can be defined inline within fields:

struct Container {
    items: [struct SlottedItem {
        Slot: byte,
        ...ItemStack,
    }],
}

Attributes

Attributes provide metadata using #[name] or #[name="value"] syntax:

Version Control

#[since="1.20"]           // Available from version 1.20
#[until="1.21"]           // Removed in version 1.21
#[deprecated="1.19"]      // Deprecated since 1.19

Registry References

#[id="item"] string                           // Reference to item registry
#[id(registry="block",tags="allowed")] string // Allow tags
#[id(registry="texture",path="entity/")] string // Path prefix

Validation

#[color="composite_rgb"] int    // RGB color as integer
#[color="hex_rgb"] string       // Hex color string
#[regex_pattern] string         // Regex pattern
#[uuid] string                  // UUID format
#[nbt=minecraft:item[[item]]] string // NBT validation

Other Attributes

#[canonical]              // Preferred form in a union
#[id]                     // Mark as identifier

Module System

Use Statements

use ::java::util::text::Text           // Absolute path
use super::block_state::BlockState     // Parent directory
use super::super::HeightmapType        // Grandparent

Module Resolution

A path like use modifier::Type resolves to either:

  • modifier.mcdoc (single-file module) — check first
  • modifier/mod.mcdoc (directory module)

Path Conventions

  • :: prefix for absolute paths from root
  • super:: for parent module
  • No prefix for sibling imports

Mapped Types (String Maps)

Define structs with dynamic string keys:

struct Lang {
    [#[translation_key(definition=true)] string]: #[translation_value] string,
}

struct Criteria {
    [#[criterion(definition=true)] string]: AdvancementCriterion,
}

Comments

// Single-line comment (not included in output)

/// Doc comment (appears in hover/autocomplete)
/// Multiple lines supported
field_name: Type,

Best Practices

  1. Naming: Use PascalCase for NBT/CODEC structs, snake_case for JSON fields
  2. Avoid unnecessary doc comments - Only document complex or non-obvious fields
  3. Use string enums when a string union changes between MC versions
  4. Always name dispatched structs - Named types for all dispatch targets
  5. Limit nesting to 3 levels - Extract deeply nested structs
  6. Use spread operator to avoid code repetition

Additional Resources