Skip to main content

minecraft-protocolo-py

A multi-version Minecraft Java Edition client/server protocol library for Python, built on top of two existing pieces instead of reinventing them:

  • protolib — the generic binary protocol engine (reads/writes packets from JSON/YAML schema definitions, in the node-protodef / minecraft-data format).
  • minecraft-data — the actual per-version packet definitions (protocol.json), the same dataset used by node-minecraft-protocol.

minecraft_protocolo is the missing glue layer: pick a Minecraft version, get a working Client or Server that speaks that version's wire protocol — handshake, login, compression, keep-alive, and error handling included.

protolib  (generic protocol engine)
    │
minecraft-data  (per-version protocol.json definitions)
    │
minecraft_protocolo  (this library: Client / Server / version resolver)
    │
your own app  (game logic only)

Install

pip install protolib
pip install minecraft-data
pip install minecraft-protocolo-py

protolib and minecraft-data are separate, real dependencies — neither is vendored or copied into this package. minecraft-data in particular carries the full per-version block/item/protocol dataset for both Java and Bedrock Edition (~100 MB) and is genuinely useless to duplicate inside every project that needs it; VersionResolver locates it automatically wherever pip installed it.

Supported versions

Any Minecraft Java Edition version your installed minecraft-data package tracks — 52+ versions as of minecraft-data 3.20.0, from early snapshots through 1.19.2, likely more in newer releases since that dataset keeps growing. Well-known build aliases that people actually type — 1.8.9, 1.7.10 — resolve automatically to the entry minecraft-data tracks internally (1.8, 1.7), since the network protocol never changed between those builds.

from minecraft_protocolo import VersionResolver

resolver = VersionResolver()
resolver.list_versions()              # ['1.7', '1.8', ..., '1.16.5']
resolver.protocol_version_number("1.8.9")   # 47

Quick start — client

from minecraft_protocolo import Client

client = Client(host="localhost", port=25565, username="Xd", version="1.8.9")

@client.on("connect")
def on_connect():
    print("connected!")

@client.on("packet")
def on_packet(name, params):
    if name == "chat":
        print("chat:", params["message"])

client.on("error", lambda e: print("error:", e))
client.on("kicked", lambda reason: print("kicked:", reason))

client.connect()  # blocks; pass background=True to run in a thread

Keep-alive is handled automatically (the client answers the server's keep_alive packets on its own — no need to do it in your handler). Compression is detected and applied automatically as soon as the server sends set_compression during login.

Quick start — server

from minecraft_protocolo import Server

server = Server(host="0.0.0.0", port=25565, version="1.8.9",
                 motd="my server", max_players=20)

@server.on("login")
def on_login(conn):
    print(conn.username, "joined —", conn.uuid)

@server.on("packet")
def on_packet(conn, name, params):
    if name == "chat":
        print(conn.username, ":", params["message"])

server.on("error", lambda e, conn=None: print("error:", e))

server.listen()  # blocks

The server answers server-list pings (MOTD, player count, protocol version) and sends its own periodic keep_alive, kicking players who stop responding — same defaults as node-minecraft-protocol (keepAlive=True, 4s ping interval, 10s kick timeout).

online_mode=True is implemented -- RSA key exchange, AES-128-CFB8 encryption, and Mojang session verification, matching node-minecraft-protocol's server-side flow exactly (see "Online-mode (real Minecraft accounts)" below). Requires the cryptography package.

Online-mode (real Minecraft accounts)

Both Server and Client support online_mode -- RSA key exchange, AES-128-CFB8 stream encryption, and verification against Mojang's session server, the same flow real Minecraft uses. Requires pip install cryptography.

Running on Termux/Android? pip install cryptography installs without error, but importing it can fail with: dlopen failed: cannot locate symbol "PyBaseObject_Type" referenced by ".../cryptography/hazmat/bindings/_rust.abi3.so". This is a known ABI mismatch between the prebuilt PyPI wheel (compiled for glibc Linux) and Termux's Python (built for Android/bionic libc) -- not a bug in this library, and not something pip install cryptography itself will warn you about. Fix: use Termux's own build instead of the PyPI wheel:

pip uninstall cryptography -y
pkg install python-cryptography

This installs a version built specifically for Termux's Python and resolves the import error. Confirmed working end-to-end (RSA + AES-128-CFB8 online-mode login against a real vanilla server) on Termux with python-cryptography 48.0.1.

Server, hosting for real accounts (rejects cracked/offline clients):

server = mp.Server(host="0.0.0.0", port=25565, version="1.8.9", online_mode=True)
# per-username exceptions if you need them:
# server = mp.Server(..., online_mode=True, online_mode_exceptions=["mytestbot"])
server.listen()

Client, connecting to a real online-mode server: this library does NOT perform the Microsoft/Mojang login flow itself (that needs a browser round-trip and belongs in something like prismarine-auth or your launcher's own auth, not a protocol library). Get a valid access_token and the account's session_uuid from wherever you already authenticate, then:

client = mp.Client(
    host="mc.example.com", port=25565, username="YourRealUsername", version="1.8.9",
    auth=True, access_token="<a valid, unexpired Mojang/MSA access token>",
    session_uuid="<the account's real profile UUID>",
)
client.connect()

Without auth=True, both sides behave like vanilla does with no credentials: the RSA/AES handshake still completes, but the server's own Mojang check will reject the login right after -- exactly like trying to join a real server with a cracked client.

Note on this implementation: the RSA key exchange and AES-128-CFB8 encryption are fully implemented and tested end-to-end against real protolib over real TCP sockets in this repo's test suite. The actual HTTP calls to sessionserver.mojang.com are implemented against the publicly documented API (https://wiki.vg/Protocol_Encryption) but untested against the live service (no network access in the environment this was built in) -- give the full flow one real-world smoke test against an actual Mojang-authenticated client/server before relying on it in production.

Examples

examples/client.py and examples/server.py are runnable, tested reference implementations built on the library — game logic only, none of the networking (that's all handled by Client/Server):

# a chat bot that responds to !hello and !ping
python examples/client.py <host> <port> <username> [version]

# a genuinely playable flat-world server: real client can log in,
# walk around, and use chat commands (!ping, !online, !spawn,
# !creative, !survival)
python examples/server.py --port 25565 --version 1.8.9 --motd "my server"

# same flat-world server, but online_mode=True -- only real,
# logged-in Minecraft accounts can join (see "Online-mode" above)
python examples/server_online_mode.py --port 25565 --version 1.8.9

# query a server's status (MOTD/players/version/latency) without logging in
python examples/ping_server.py <host> [port] [version]

These are meant as a starting point to copy from, not a CLI you're expected to use as-is — real projects put their own game logic on top of Client/Server directly.

Error handling

Deserialization failures are wrapped to match the format used by node-minecraft-protocol (PartialReadError), so anyone familiar with that library will recognize the shape immediately:

PartialReadError: Deserialization error for play.toClient : Read error : buffer exhausted at offset=5: needed 1 bytes, 0 remaining

Client/Server never raise these directly into your code — they're emitted through the error event, same as Node's EventEmitter convention (an unhandled 'error' event still gets printed to stderr instead of silently disappearing).

Discovering the packet API

The API is the protocol.json for whatever version you loaded — not a fixed list this README could show (74 packets just for play.toClient in 1.8, times every version your minecraft-data install tracks). Field names are also case-sensitive and don't always match what the wiki or another bot would lead you to expect (gameMode not gamemode, flyingSpeed not flySpeed). Two ways to find the real name for the version you're using:

# From a live connection (Client or Server's ClientConnection) --
# always exact for whatever version was actually negotiated:
print(client.list_packet_names())          # every valid play.toServer packet name
print(conn.list_packet_names("play", "toClient"))  # play.toClient for a server connection

# Or straight from minecraft-data itself, no connection needed (strip
# the "packet_" prefix and the internal "packet" dispatcher entry):
import json
from minecraft_protocolo.version_resolver import _default_data_root
with open(f"{_default_data_root()}/pc/1.8/protocol.json") as f:
    proto = json.load(f)
names = [k[len("packet_"):] for k in proto["play"]["toClient"]["types"] if k.startswith("packet_")]
print(names)

Sending a name that doesn't exist raises UnknownPacketError instead of a confusing low-level error, and lists the valid names for that state. A wrong or missing field inside otherwise-correct params raises SerializationError with a hint pointing at protocol.json.

What's implemented

  • Handshake, login (offline-mode), status/server-list ping
  • ping_status(host, port, version) — standalone status query (MOTD, player count/sample, version, favicon, latency) without a full login, same as node-minecraft-protocol's mc.ping()
  • Correct offline-mode UUID (uuid_utils.offline_uuid) — byte-for-byte identical to vanilla/node-minecraft-protocol's nameToMcOfflineUUID (plain MD5 with patched version/variant bits, not uuid.uuid3() with a namespace — those produce different UUIDs and will desync with anything that computes its own)
  • zlib packet compression, detected and applied automatically
  • Automatic keep-alive, both directions, with timeout/kick handling
  • Multi-version support in a single Server (version=None) — resolves each connecting client's version from its handshake protocolVersion
  • Plugin channels (custom_payload) — register_channel() / unregister_channel() / send_channel() on both Client and server-side ClientConnection
  • login_plugin_request/response handled automatically (1.13+, protocol ≥ 385) so modern servers don't hang the login waiting for a reply this client doesn't have a plugin for
  • state event (fires on every handshaking → login → play transition), raw event (every packet, name + params together), and every packet is also emitted under its own name (client.on("chat", ...)) in addition to the generic "packet" event — all three always fire together, pick whichever fits
  • write(name, params) alias for send_packet() on both Client and ClientConnection, matching node-minecraft-protocol's spelling
  • Server(favicon=..., before_ping=...) — server-list icon and a hook to customize/reject the status response per-connection
  • online_mode=True — RSA key exchange, AES-128-CFB8 encryption, Mojang session verification (server_hash() matches wiki.vg's published test vectors exactly), online_mode_exceptions allowlist, on both Server (hosting) and Client (auth=True + a token you obtained elsewhere — see "Online-mode" above)
  • node-minecraft-protocol-style error formatting

What's not implemented yet

  • The Microsoft/Mojang OAuth login flow itself (getting an access_token in the first place) — intentionally out of scope, see "Online-mode" above for why.
  • Bedrock Edition (only Java/"pc" protocols are wired up) — a library limitation.
  • Automatic parsing of arbitrary plugin-channel payloads (e.g. a Forge/Fabric mod's custom binary format) — register_channel() hands you the raw bytes; decoding them requires a schema only the mod author has, same tradeoff node-minecraft-protocol makes.

None of the above is the library's job, and intentionally so — world state, chunks, entity spawning, and physics are game logic, which lives in your own code on top of Client/Server, not in minecraft_protocolo itself. examples/server.py shows a working approach (flat world, chunks, tab list) that a real client can actually join and walk around in — extend that pattern for anything beyond a flat world.

Project layout

minecraft_protocolo/
├── __init__.py
├── version_resolver.py   # "1.12.2" -> a loaded protolib.Protocol
├── client.py              # Client + ping_status(): connect, handshake, login, events
├── server.py               # Server: accept connections, per-player events
├── errors.py                # PartialReadError and friends
├── events.py                  # small Node-style EventEmitter
├── uuid_utils.py                # correct offline-mode UUID (not uuid.uuid3!)
├── encryption.py                  # AES-128-CFB8 for online-mode
└── mojang_auth.py                   # server RSA keypair + Mojang session verification
# (no data/ here -- protocol.json/version.json etc. come from the
#  minecraft-data PyPI package, located automatically at runtime)

examples/
├── client.py                     # chat bot example
├── server.py                      # playable flat-world server example
├── server_online_mode.py           # same, but online_mode=True (real accounts only)
└── ping_server.py                   # query a server's status without logging in

pyproject.toml

Metadata

Release files for minecraft-protocolo-py 0.1.4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for minecraft-protocolo-py 0.1.4
File Size Uploaded
minecraft_protocolo_py-0.1.4.tar.gz 42.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for minecraft-protocolo-py 0.1.4
File Interpreter ABI Platform
minecraft_protocolo_py-0.1.4-py3-none-any.whl Python 3 none any Details

Total release size: 82.8 kB

Release files / minecraft_protocolo_py-0.1.4.tar.gz

Download URL minecraft_protocolo_py-0.1.4.tar.gz
Size 42.0 kB
Tags Source
SHA-256 checksum
How to use checksums
e03f685f8f609c98ff027c1cbd00fd165671b6e364554f2f51630db50aed59b8
BLAKE2b-256 checksum
How to use checksums
36e113ad0fa32c96b273959f7d7deea12c3f57d9cd343933630366081b6850c9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.13

Release files / minecraft_protocolo_py-0.1.4-py3-none-any.whl

Download URL minecraft_protocolo_py-0.1.4-py3-none-any.whl
Size 40.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ba31bad8b4a1f3d563d9bcc3d12d2af182cfeaebebd2382ffb641ee970d31d09
BLAKE2b-256 checksum
How to use checksums
24e95358b97adfcffac9c0caec1e823e6fed992f8c5cb40d44d0893a19b04e70
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.13

Release history Release notifications | RSS feed

This release

0.1.4 This release

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page