Skip to content

MCCTP

A Fabric mod that exposes a WebSocket server on your Minecraft client. It streams game state out every tick and accepts action commands in — letting any program, in any language, control the player.

Fabric
Loaders
Fabric
Downloads on Modrinth
407
Followers on Modrinth
2
versions
3
Updated
March 3, 2026Mar 3
Published
in 2026

At a glance

Runs on Fabric
For Minecraft 1.21.11
Client only — no need to install it on the server
Latest version — 1.2.0 from March 3, 2026
License MIT — free
Where it's needed
In gamerequiredOn servernot needed

Install in the game only; not needed on the server

Game versions1.21.11

MCCTP - Minecraft Control Transfer Protocol

A Fabric mod that exposes a WebSocket server on your Minecraft client. It streams game state out every tick and accepts action commands in — letting any program, in any language, control the player.

Features

  • WebSocket API — JSON over WebSocket, language-agnostic
  • 13 action types — movement, combat, inventory, camera, and more
  • Game state every tick — health, position, held item, combat context
  • Item categorization — automatic classification (SWORD, BOW, BLOCK, FOOD, etc.)
  • Hotbar wheel overlay — circular HUD showing all 9 slots, controlled via commands or V key
  • Thread-safe — WebSocket runs on its own Netty thread group, actions dispatch to the game thread

Requirements

  • Minecraft 1.21.11
  • Fabric Loader >= 0.18.0
  • Fabric API
  • Java 21+

Install

./gradlew build

Copy build/libs/mcctp-1.0.0.jar to your .minecraft/mods/ folder (alongside Fabric Loader + Fabric API).

How It Works

When you join a world, the mod starts a WebSocket server on port 8765. Any client can connect to `ws://:8765/mcctp` and:

  1. Receive game state JSON every tick (50ms by default)
  2. Send action commands as JSON to control the player

The server runs on its own Netty NioEventLoopGroup, separate from Minecraft's networking. Incoming actions are dispatched to the game thread via MinecraftClient.execute(), so they're safe and behave identically to real input. The server stops when you leave the world.

Protocol

Connecting

ws://<host>:8765/mcctp

Standard WebSocket connection. No authentication. Multiple clients can connect simultaneously.

Game State (Server → Client)

Broadcast to all connected clients every tick. All fields are always present.

{
  "type": "game_state",
  "timestamp": 1700000000000,
  "selectedSlot": 0,
  "heldItem": {
    "name": "minecraft:diamond_sword",
    "category": "SWORD",
    "stackCount": 1,
    "maxDurability": 1561,
    "currentDurability": 1500
  },
  "offhandItem": {
    "name": "minecraft:shield",
    "category": "SHIELD",
    "stackCount": 1,
    "maxDurability": 336,
    "currentDurability": 336
  },
  "playerState": {
    "health": 20.0,
    "maxHealth": 20.0,
    "hunger": 20,
    "saturation": 5.0,
    "x": 100.5,
    "y": 64.0,
    "z": -200.3,
    "yaw": 45.0,
    "pitch": -10.0,
    "onGround": true,
    "sprinting": false,
    "sneaking": false,
    "swimming": false,
    "flying": false,
    "inWater": false,
    "onFire": false
  },
  "combatContext": {
    "isUsingItem": false,
    "isBlocking": false,
    "activeHand": "MAIN_HAND",
    "crosshairTarget": "ENTITY",
    "crosshairEntityType": "minecraft:zombie",
    "crosshairBlockPos": null
  }
}
Game State Fields
FieldTypeDescription
typestringAlways "game_state"
timestamplongUnix milliseconds
selectedSlotintActive hotbar slot (0-8)

heldItem / offhandItem:

FieldTypeDescription
namestringRegistry ID, e.g. "minecraft:diamond_sword"
categorystringSee Item Categories
stackCountintCurrent stack size
maxDurabilityintMax durability (0 if not damageable)
currentDurabilityintRemaining durability

playerState:

FieldTypeDescription
healthfloatCurrent health (0-20)
maxHealthfloatMax health
hungerintFood level (0-20)
saturationfloatSaturation level
x, y, zfloatWorld position
yaw, pitchfloatCamera rotation (degrees)
onGroundboolStanding on solid ground
sprintingboolCurrently sprinting
sneakingboolCurrently sneaking
swimmingboolCurrently swimming
flyingboolCreative/elytra flight
inWaterboolSubmerged in water
onFireboolOn fire

combatContext:

FieldTypeDescription
isUsingItemboolHolding right click (eating, drawing bow, etc.)
isBlockingboolBlocking with shield
activeHandstring"MAIN_HAND" or "OFF_HAND"
crosshairTargetstring"ENTITY", "BLOCK", or "MISS"
crosshairEntityTypestring?Entity registry ID, or null
crosshairBlockPosint[3]?[x, y, z] block position, or null

Actions (Client → Server)

Send a JSON message with action and params:

{"action": "<action_name>", "params": {<params>}}
Action Reference
ActionParamsDescription
movedirection: "forward" "backward" "left" "right", state: "start" "stop"WASD movement. Holds the key until you send "stop".
lookyaw: float, pitch: float, relative: boolCamera control. relative: true adds to current angles. relative: false sets absolute.
jump(none)Single jump.
attack(none)Left click — swing weapon, hit entity, start breaking block.
use_itemstate: "start" "stop"Right click hold. Use "start" to begin (draw bow, eat, raise shield) and "stop" to release.
throw_item(none)Single right click pulse — for snowballs, ender pearls, firing a loaded crossbow.
select_slotslot: int (0-8)Switch active hotbar slot.
sneakstate: "start" "stop"Crouch. Holds until stopped.
sprintstate: "start" "stop"Sprint. Holds until stopped.
drop_itemfull_stack: boolDrop held item. true drops the entire stack (Ctrl+Q).
swap_hands(none)Swap main hand and offhand (F key).
open_inventory(none)Toggle inventory screen (E key).
toggle_wheel(none)Toggle the hotbar wheel HUD overlay.
Action Examples
// Walk forward
{"action": "move", "params": {"direction": "forward", "state": "start"}}

// Stop walking
{"action": "move", "params": {"direction": "forward", "state": "stop"}}

// Turn 90 degrees right
{"action": "look", "params": {"yaw": 90.0, "pitch": 0.0, "relative": true}}

// Face north, looking straight ahead
{"action": "look", "params": {"yaw": 180.0, "pitch": 0.0, "relative": false}}

// Draw and fire a bow
{"action": "use_item", "params": {"state": "start"}}
// ... wait ~1 second ...
{"action": "use_item", "params": {"state": "stop"}}

// Throw a snowball
{"action": "throw_item", "params": {}}

// Switch to slot 3 and attack
{"action": "select_slot", "params": {"slot": 3}}
{"action": "attack", "params": {}}

Item Categories

Every held item is classified into one of these categories:

CategoryExamples
SWORDDiamond sword, netherite sword
BOWBow
CROSSBOWCrossbow
AXEIron axe, wooden axe
PICKAXEDiamond pickaxe
SHOVELStone shovel
HOENetherite hoe
TRIDENTTrident
SHIELDShield
FOODCooked beef, golden apple
BLOCKCobblestone, dirt, planks
THROWABLESnowball, egg, ender pearl
FISHING_RODFishing rod
EMPTYEmpty hand
OTHERAnything not matched above

Configuration

Config file: .minecraft/config/mcctp.json (created on first launch)

{
  "port": 8765,
  "tickInterval": 1
}
FieldDefaultDescription
port8765WebSocket server port
tickInterval1Broadcast game state every N ticks (20 ticks = 1 second)

Architecture

Any Client ←── WebSocket (JSON) ──→ Fabric Mod
                                       │
                                       ├── WebSocketServer (Netty, own thread group)
                                       │     ├── WebSocketServerInitializer (HTTP → WS upgrade)
                                       │     ├── WebSocketFrameHandler (parse inbound JSON)
                                       │     └── ConnectionManager (track channels, broadcast)
                                       │
                                       ├── GameStateCollector (END_CLIENT_TICK → JSON)
                                       │     ├── GameStatePayload
                                       │     ├── HeldItemInfo + ItemCategorizer
                                       │     ├── PlayerStateInfo
                                       │     └── CombatContextInfo
                                       │
                                       ├── ActionDispatcher (JSON → ActionHandler)
                                       │     ├── MoveHandler, LookHandler, JumpHandler
                                       │     ├── AttackHandler, UseItemHandler, ThrowItemHandler
                                       │     ├── SelectSlotHandler, SneakHandler, SprintHandler
                                       │     ├── DropItemHandler, SwapHandsHandler
                                       │     ├── OpenInventoryHandler, ToggleWheelHandler
                                       │     └── KeyReleaseScheduler (timed key pulses)
                                       │
                                       └── HotbarWheelRenderer (HUD overlay)

Key implementation details:

  • The WebSocket server uses Netty's NioEventLoopGroup, completely separate from Minecraft's network thread
  • All action handlers run on the game thread via MinecraftClient.execute() — same as real player input
  • Pulse actions (jump, attack, throw, swap, inventory) use KeyReleaseScheduler which holds the key for 3 ticks and increments timesPressed, matching how Minecraft processes single key presses
  • Held actions (move, sneak, sprint, use_item) set KeyBinding.pressed directly and stay held until a "stop" command
  • State collection hooks into END_CLIENT_TICK and serializes with Gson
  • The mod uses a Mixin accessor (KeyBindingAccessor) to access KeyBinding.pressed and KeyBinding.timesPressed

Building from Source

# Build
./gradlew build

# Output jar
build/libs/mcctp-1.0.0.jar

Build targets Java 21. Tested with Gradle 9.2.0 and Fabric Loom 1.15.3.

Python Client

A reference Python client is included in python/. Install with:

cd python
pip install -e .

Provides async and sync WebSocket clients with typed dataclasses for game state:

import time
from mcctp import SyncMCCTPClient, Actions

with SyncMCCTPClient("localhost", 8765) as client:
    client.on_state(lambda s: print(s.player_state.health))
    client.send(Actions.move("forward", "start"))
    time.sleep(2)
    client.send(Actions.move("forward", "stop"))

Also includes remote_play.py — a keyboard/mouse passthrough that lets someone play on your world from another computer.

License

MIT

FAQ

How to install MCCTP?

Install Fabric for your Minecraft version, download MCCTP for the same version and loader, put the file into the mods folder and launch the game with the loader profile.

Which Minecraft versions does MCCTP support?

MCCTP has builds for Minecraft 1.21.11.

Is MCCTP needed on the server?

Client only — no need to install it on the server

License and source
MIT Licensefree to use and share with credit to the author
Original — Modrinth↗Files unchanged, verified with SHA-512Is this your mod? For rights holders

More mods by this author

MCSTP↓ 197

Often needed together

MCSTP↓ 197

The site uses cookies: the necessary ones for sign-in and the theme, plus the Yandex Metrica and LiveInternet visit counters — they show us how the site is used. We have no advertising cookies. More