Contents

MCX User Guide


MCX is a stateful CLI for inspecting one Minecraft .litematic structure and maintaining agent-readable selection state. It is read-only with respect to schematic blocks.

Why MCX: the thing about 3D in LLM is a problem of compressing existing information, not generating it. The harness is built for inspection; the LLM can still generate code to generate 3D.

.litematicinput only
1region per file
y,z,xcoord order
JSONall output

Quick start

uv run mcx load tmp/my_build.litematic uv run mcx info uv run mcx print --jsonpath '$.result.rle[0].body' uv run mcx select --bbox '0,0,0;8,8,8' --name corner uv run mcx query blocks --id minecraft:snow_block --name snow uv run mcx select union corner snow --name corner_or_snow

1. Hard Invariants

These rules hold unconditionally. Any violation returns a structured JSON error.

Category Invariant Detail
InputFormat.litematic only
InputRegion countExactly one component/region per file
PaletteIndex 0Must be minecraft:air; files that cannot satisfy this fail
StateScopeOne state file ↔ one loaded litematic
StateCommand prereqAll commands except load and clear require loaded state
OutputFormatSuccess and error outputs are always JSON
OutputHelpmcx, mcx -h, mcx --help print human-readable help
OutputNarrowingSuccess JSON narrowable with --jsonpath PATH
OutputFoldingLong arrays folded by default; disable with --fulljson
CoordinatesOrderAlways y,z,x; y is vertical
CoordinatesFrameLoaded-schematic-local coordinates
CoordinatesBboxMin-inclusive, max-exclusive: [y0,z0,x0, y1,z1,x1]
SelectionDefault scopeCommands operate on the current selection
EncodingSupportedsemantic_chunk_rle (default) and raw
PaletteSort orderDescending block count, then by blockstate string

2. State

MCX keeps a JSON state file tracking the loaded schematic, selections, geometry context, and active anchor. All writes are atomic (temp file → fsync → rename).

Default path.mcx/state.json
Override order--state path > MCX_STATE env var > .mcx/state.json

State contents

Loaded litematic metadata · palette · geometry context · current selection · named selections · active anchor.

Geometry context (state.geometry)

{ "encoding": "semantic_chunk_rle", "chunk_size_yzx": [16, 16, 16], "compact_method": "majority" }
FieldValid valuesDefaultHow to change
encodingsemantic_chunk_rle, rawsemantic_chunk_rleSet at load --encoding
chunk_size_yzxAny positive cubic size[16,16,16]Edit directly in state file
compact_methodmajority, weighted, surface_blocks, surface_facesmajorityEdit in state file (no CLI flag)

Selection note: a selected minecraft:air cell is still selected. An unselected cell is encoded as the pseudo blockstate mcx:unselected.

3. JSON Envelope

Every MCX command outputs a top-level JSON envelope. The ok field discriminates success from failure.

{ "ok": true, "schema": "mcx.<command>.v1", "tool_version": "0.1.0", "state_path": ".mcx/state.json", "result": { ... } }

The result object is command-specific. Its structure is documented per command below.

{ "ok": false, "schema": "mcx.error.v1", "tool_version": "0.1.0", "state_path": ".mcx/state.json", "error": { "type": "ErrorType", "message": "Short actionable message.", "details": {}, "suggested_next_commands": [] } }

See the Error Types section for all possible type values.

4. Encodings

MCX supports two geometry encodings set at load time. Switching requires a reload.

Splits geometry into fixed-size chunks. Each chunk has its own local palette and a CODE:COUNT RLE body. Semantic codes (e.g. S_L for spruce log) are designed to be readable by LLMs.

{ "geometry_encoding": "semantic_chunk_rle", "origin_abs_yzx": [0, 64, 0], "shape_yzx": [1, 1, 32], "chunk_size_yzx": [16, 16, 16], "rle": [ { "origin_offset_yzx": [0, 0, 0], "shape_yzx": [1, 1, 16], "palette": [{"code": "ST", "blockstate": "minecraft:stone"}], "body": "ST:16" }, { "origin_offset_yzx": [0, 0, 16], "shape_yzx": [1, 1, 16], "palette": [{"code": "SA", "blockstate": "minecraft:sand"}], "body": "SA:16" } ] }

Body syntax

y=0 layer: z=0 row: ST:4 A:4 z=1 row: A:8 z=2 row: A:8 | y=1 layer: z=0 row: A:8 ...
TokenMeaning
CODE:NN consecutive blocks of type CODE
spaceSeparate runs within a row
;Separate rows (z axis)
|Separate layers (y axis)

Scan order

Always y → z → x with x fastest (innermost). Each row must decode to exactly shape_yzx[2] cells. Each layer must have exactly shape_yzx[1] rows. Each body must have exactly shape_yzx[0] layers.

Chunk rules

  • Chunks tile the full geometry without gaps or overlaps
  • Emitted in y,z,x chunk order (x changes fastest)
  • Boundary chunks may be smaller than chunk_size_yzx
  • Palette codes and blockstates must be unique within each chunk
  • mcx:unselected marks unselected cells

Absolute coordinate formula

abs_y = origin_abs_yzx[0] + origin_offset_yzx[0] + local_y abs_z = origin_abs_yzx[1] + origin_offset_yzx[1] + local_z abs_x = origin_abs_yzx[2] + origin_offset_yzx[2] + local_x

A fixed-width palette-symbol grid. Easy to visually inspect, but may spend tokens on selected air and unselected cells.

{ "geometry_encoding": "raw", "origin_abs_yzx": [0, 64, 0], "shape_yzx": [2, 2, 3], "symbol_width": 1, "unselected_symbol": "~", "blocks": [ ["A.C", ".BA"], ["CC.", "BA."] ] }

blocks[y][z] is one row string. Row string length = shape_yzx[2] × symbol_width.

SymbolBlockstate
. (× symbol_width)minecraft:air
~ (× symbol_width)mcx:unselected
OtherAssigned in stable loaded-palette-index order

If one-character symbols are insufficient, MCX increases symbol_width automatically.

With local palette (for input / stored selections)

{ "geometry_encoding": "raw", "origin_abs_yzx": [0, 0, 0], "shape_yzx": [1, 1, 1], "symbol_width": 1, "unselected_symbol": "~", "palette": [ {"symbol": "~", "blockstate": "mcx:unselected"}, {"symbol": "A", "blockstate": "minecraft:stone"} ], "blocks": [["A"]] }

5. Semantic Code Generation Rules

Palette codes are short human-readable aliases. The algorithm generates the shortest unique prefix without inventing arbitrary codes.

Algorithm

  1. Remove namespace prefix (minecraft:)
  2. Split block name on underscores into semantic tokens
  3. If multiple palette entries share the same base block, append needed state tokens (facing, axis, etc.)
  4. Pick the shortest uppercase prefix that is unique within the local palette
  5. If uniqueness cannot be achieved → InvalidPalette error (no arbitrary fallback)

Examples

Blockstate Code Reasoning
minecraft:stoneSTstone → ST
minecraft:sandSAsand → SA (distinguishes from stone)
minecraft:spruce_logS_Lspruce + log → S_L
minecraft:spruce_planksS_Pspruce + planks → S_P
minecraft:spruce_stairsS_Sspruce + stairs → S_S (only one stairs variant)
minecraft:spruce_stairs[facing=north]S_S_Nstate discriminator appended when multiple variants present
minecraft:spruce_stairs[facing=east]S_S_Estate discriminator appended when multiple variants present
minecraft:smooth_stoneSM_STsmooth + stone → SM_ST (if needed to distinguish)
minecraft:stone_slabST_SLstone + slab → ST_SL (if needed to distinguish)

Concrete body example — shape [4, 4, 8]

ST:8 ; ST:2 SA:4 ST:2 ; ST:2 SA:4 ST:2 ; ST:8 | A:8 ; A:1 S_L:1 S_P:4 S_L:1 A:1 ; A:1 S_L:1 S_P:4 S_L:1 A:1 ; A:8 | A:8 ; A:1 S_S:6 A:1 ; A:1 S_S:6 A:1 ; A:8 | A:8 ; A:3 S_L:2 A:3 ; A:3 S_L:2 A:3 ; A:8

Each line = one y-layer. Rows separated by ;. Runs are CODE:COUNT separated by spaces. The 4 lines are joined by | in the actual JSON string.

Palette validity rules

  1. Local code values must be unique within a chunk
  2. Local blockstate values must be unique within a chunk
  3. Duplicate exact blockstates → InvalidPalette error
  4. Cannot generate unique codes → InvalidPalette error (not arbitrary fallback)
InvalidPalette error example
{ "ok": false, "schema": "mcx.error.v1", "tool_version": "0.1.0", "state_path": ".mcx/state.json", "error": { "type": "InvalidPalette", "message": "semantic_rle palette contains duplicate blockstates.", "details": { "blockstate": "minecraft:stone" }, "suggested_next_commands": [] } }

6. Commands

Command dependency on state
flowchart LR
  A[/"mcx load"/] -->|creates| S[("state.json")]
  B[/"mcx clear"/] -->|deletes| S
  S --> C[/"mcx info"/]
  S --> D[/"mcx print"/]
  S --> E[/"mcx query blocks"/]
  S --> F[/"mcx select"/]
  S --> G[/"mcx diff"/]
  S --> H[/"mcx anchors"/]
        

All commands except load and clear require loaded state. Commands below are expandable.

load — Load a .litematic file & initialize state
mcx load path/to/file.litematic mcx load --encoding raw path/to/file.litematic
FlagDefaultDescription
--encodingsemantic_chunk_rleGeometry encoding for this state

Validates invariants, stores geometry context, resets anchor to [0,0,0], sets current selection to the entire litematic, and replaces prior state.

Falls back to MCIO NBT/block-container primitives for older single-region files; success output includes a warnings entry when this path is used.

info — Show schematic metadata and selection summary
mcx info

Returns: loaded schematic metadata · geometry context · current selection occupancy · active anchor · symbol width · full palette.

Never prints the stored selection representation — only summary data.

diff — Compare a candidate litematic against current selection
mcx diff path/to/candidate.litematic

The candidate must have the same size_yzx as the loaded schematic.

Error type countedDefinition
Occupancy errorOne side is air, other is non-air
Blockstate errorMatching occupancy but different exact blockstate strings
{ "candidate_schematic": { "path": "path/to/candidate.litematic", "sha256": "sha256:...", "size_yzx": [2, 2, 3] }, "compared_selection": { "name": "__entire__", "bbox_abs_yzx": [0, 0, 0, 2, 2, 3], "voxel_count_selected": 12, "envelope_volume": 12 }, "total_compared_blocks": 12, "correct_blocks": 9, "wrong_occupancy_blocks": 2, "wrong_blockstate_blocks": 1, "wrong_total_blocks": 3, "wrong_occupancy_rate": 0.16666666666666666, "wrong_blockstate_rate": 0.08333333333333333, "wrong_total_rate": 0.25 }

Read-only; does not mutate state. Empty selections return zero counts and 0.0 rates.

print — Output geometry of the current selection
mcx print mcx print --compact 16 mcx print --projection=+y mcx print --projection=-y

Modes

ModeFlagDescription
Exact(default)Full selection bbox; unselected cells → mcx:unselected
Compact--compact NDownsampled; caps each representative axis at N; strategy from state.geometry.compact_method
Projection--projection DIRCollapses one axis to a single visible non-air surface layer

Compact methods (state.geometry.compact_method)

MethodStrategy
majorityMost frequent selected blockstate in integer source region (default)
weightedBlockstate with greatest float-box overlap volume
surface_blocksRanks non-air by exposed surface block count; air only if no non-air exists
surface_facesSame as surface_blocks but scores exposed face count (0–6 per block)

Projection directions

Flag valueCameraNotes
+yAbove, looking downRows in ascending z; first row = −z side
-yBelow, looking upRows in ascending z; first row = +z side
+zSouth, looking northOne z-row per y-layer; display higher y first
-zNorth, looking southOne z-row per y-layer; display higher y first
+xEast, looking westOne x-column per y,z cell; display higher y first
-xWest, looking eastOne x-column per y,z cell; display higher y first

Use --projection=-y form (equals sign) so argparse does not treat the negative value as an option flag.

query blocks — Search the current selection for blocks
mcx query blocks --id minecraft:stone mcx query blocks --state 'minecraft:oak_stairs[facing=east,half=bottom,shape=straight,waterlogged=false]' --name stairs
FlagMatches byNotes
--idBlock ID onlyIgnores block state properties
--stateExact blockstate stringMust include all state properties
--nameSaves matching coordinates as a named selection; must be unique

Exactly one of --id or --state is required. A duplicate name returns SelectionNameAlreadyExists.

select — Manage the current and named selections

Selection forms

mcx select --bbox '0,0,0;10,5,10' # absolute bbox mcx select --bbox 0:4,0:4,0:4 # anchor-relative bbox mcx select --input selection.json --name custom mcx select --input - --name custom # stdin mcx select --prompt "chimney" --name chimney mcx select --prompt "chimney" --propagate mcx select infect mcx select infect --max-voxels 32768 mcx select union roof walls --name shell mcx select subtract shell windows --name solid_shell mcx select intersect shell stone --name stone_shell mcx select clear mcx select named_selection
FormDescription
--bbox y0,z0,x0;y1,z1,x1Absolute bbox selection
--bbox y0:y1,z0:z1,x0:x1Anchor-relative bbox selection
--input file.jsonLoad from raw/chunk RLE geometry or full MCX success envelope
--input -Read geometry from stdin (folded JSON is rejected; use --fulljson)
--prompt "text"Visual selection via mc_vision; cannot combine with bbox, input, or set ops
--propagateValid only with --prompt
infect6-neighbor expand through matching block IDs; stops at air / mismatch
union A BUnion of named selections A and B
subtract A BSubtract B from A
intersect A BIntersection of A and B
clearReset to empty selection
named_selectionRestore a previously saved named selection

Selection output is summaries only — no stored representations, chunks, masks, or full coordinate lists. Unnamed selections replace the previous unnamed selection. Named selections must be unique; --max-voxels defaults to 32768 for infect.

anchors — Manage coordinate anchors for relative bbox syntax
mcx anchors mcx anchors set --origin 10,64,10 --name doorway mcx anchors reset

Anchor-relative bbox syntax (y0:y1,z0:z1,x0:x1) resolves through the active anchor. Anchor changes do not alter the loaded schematic or existing selection coordinates.

clear — Delete the current state file
mcx clear

Deletes the state file when it exists. Result includes cleared: true and state_existed (whether a file was present before the command ran).

7. JSONPath & Folding

--jsonpath

Use --jsonpath PATH to select part of success JSON before stdout folding is applied:

mcx print --jsonpath '$.result.rle[0].body' mcx info --jsonpath '$.result.palette[0:5]'
SyntaxExample
Root$
Dot child$.result.palette
Bracket child$["result"]
Array index$.result.rle[0]
Array slice$.result.palette[0:5]

Folding

Arrays with ≥ 6 non-numeric items are folded by default — first 5 kept, rest replaced by a marker object. Use --fulljson to disable folding entirely.

{ "$mcx_folded_list_v1": { "path": "$.result.palette", "shown_range": "[0:5]", "total_count": 12, "omitted_count": 7, "note": "Use --jsonpath '$.result.palette[5:10]' to inspect another range." } }

Tip: pipe to mcx print --fulljson then use --input to load the full geometry into a selection.

Error Types

Error type When it occurs
InvalidArgumentsInvalid CLI arguments, coordinate syntax, JSONPath, or incompatible options
StateNotLoadedState file is missing or not a valid MCX state
InvalidStateState file exists but violates the MCX schema
InvalidLitematicLitematic cannot be loaded or normalized
InvalidSelectionSupplied bbox or geometry input is malformed or out of bounds
SelectionNotFoundNamed selection does not exist
SelectionNameAlreadyExistsAttempted to save a selection with a duplicate name
InfectSelectionTooLargeselect infect exceeded --max-voxels limit
VisionSelectionFailedselect --prompt could not render, edit, project, or encode the visual selection
InvalidPaletteDuplicate blockstates or unresolvable code collisions in RLE palette

8. Manual Test Commands

These examples use the bundled fixture. Run from the project root with uv run mcx ....

uv run mcx --help uv run mcx clear uv run mcx load tmp/雪屋.litematic uv run mcx info uv run mcx print --jsonpath '$.result.rle[0].body' uv run mcx print --fulljson > /tmp/mcx-semantic-print.json uv run mcx select --input /tmp/mcx-semantic-print.json --name from_semantic_print uv run mcx select --bbox '0,0,0;8,8,8' --name corner uv run mcx print --compact 8 uv run mcx print --projection=+y uv run mcx print --projection=-y uv run mcx query blocks --id minecraft:snow_block --name snow uv run mcx select union corner snow --name corner_or_snow uv run mcx anchors set --origin 4,0,4 --name local uv run mcx select --bbox 0:4,0:4,0:4
uv run mcx clear uv run mcx load --encoding raw tmp/雪屋.litematic uv run mcx print uv run mcx print --fulljson > /tmp/mcx-raw-print.json uv run mcx select --input /tmp/mcx-raw-print.json --name from_raw_print