Welcome to MindPy — an asyncio-native Python framework for building intelligent Minecraft bots.
- Python 3.12+
- A Minecraft Java Edition server (offline or online mode)
- For online mode: a Microsoft account with a Minecraft licence
pip install mindpygit clone https://github.com/CybersharpX/MindPy.git
cd MindPy
pip install -e ".[dev,llm]"
pre-commit install# examples/hello_bot.py
import asyncio
from mindpy import Bot, EventTypes, Event
async def main():
bot = Bot(
host="localhost",
port=25565,
username="HelloBot",
)
@bot.on(EventTypes.BOT_SPAWNED)
async def on_spawn(event: Event) -> None:
print(f"Spawned at {bot.state.position}")
await bot.chat("Hello, world! 👋")
@bot.on(EventTypes.CHAT_MESSAGE)
async def on_chat(event: Event) -> None:
raw = event.data.get("raw", "")
print(f"[CHAT] {raw}")
async with bot: # connects, runs, disconnects automatically
await bot.run()
asyncio.run(main())Run it:
python examples/hello_bot.py# examples/online_bot.py
import asyncio
from mindpy import Bot
from mindpy.protocol.auth import MicrosoftAuth
async def main():
# Step 1: authenticate (one-time or cache the profile)
async with MicrosoftAuth() as auth:
profile = await auth.device_flow_auth()
# Prints: "Visit https://microsoft.com/link and enter code XXXX-XXXX"
# Sign in with the account that owns your Minecraft licence.
# Step 2: run the bot
async with Bot(
"play.example.com",
auth_profile=profile,
online_mode=True,
protocol_version=769, # 1.21+
) as bot:
await bot.run()
asyncio.run(main())Bot() ← construct
│
▼
connect() ← TCP open + handshake + login
│
▼
PLAY state ← packet read loop running in background
│
▼
run() ← blocks; calls reconnect() on drop
│
▼
disconnect() ← publishes event, stops bus, closes TCP
Using async with bot: automatically calls connect() on enter and disconnect() on exit.
All communication between components flows through the event bus. You can subscribe with:
# Fluent decorator on the bot instance
@bot.on("chat.message")
async def on_chat(event: Event) -> None:
print(event.data["raw"])
# Wildcard — matches all bot.* events
@bot.on("bot.*")
async def on_any(event: Event) -> None:
print(event.event_type)
# Direct subscription with cancellation token
token = bot.event_bus.subscribe("player.joined", handler_fn)
# ...
token.cancel() # or use as context manager: `with token:`
# Wait for a single event (with timeout)
spawn = await bot.event_bus.wait_for("bot.spawned", timeout=30.0)Event priority controls dispatch order within the same event type:
from mindpy.events.event import EventPriority
@bot.on("chat.message", priority=EventPriority.HIGH)
async def high_priority_handler(event: Event) -> None:
...Priority order (lowest value = first): CRITICAL(0) → HIGH(1) → NORMAL(2) → LOW(3).
The current in-game state is always available on bot.state:
bot.state.connected # bool
bot.state.health # float 0.0–20.0
bot.state.hunger # int 0–20
bot.state.saturation # float
bot.state.x, .y, .z # float world coordinates
bot.state.yaw, .pitch # float look direction (degrees)
bot.state.entity_id # int server-assigned entity ID
bot.state.game_mode # int 0=survival 1=creative 2=adventure 3=spectator
bot.state.is_hardcore # bool
bot.state.dimension # str e.g. "minecraft:overworld"
bot.state.position # (x, y, z) convenience tupleThe bot automatically keeps state in sync from incoming Play-state packets.
Create config.yaml:
bot:
username: MindPyBot
host: localhost
port: 25565
max_reconnect_attempts: 5
reconnect_delay: 5.0
logging:
level: INFO
file: logs/mindpy.logLoad it:
from mindpy.config import Config
from mindpy import Bot
config = Config("config.yaml")
bot = Bot(
host=config.get("bot.host", "localhost"),
port=config.get("bot.port", 25565),
username=config.get("bot.username", "MindPyBot"),
config=config,
)Or use environment variables (take precedence over YAML):
export MINDPY_BOT_USERNAME=Agent1
export MINDPY_BOT_HOST=play.example.comfrom mindpy.memory import MemoryManager
memory = MemoryManager()
# Working memory — current context
memory.working_memory.add("current_task", "mining_diamonds")
# Long-term memory — persistent facts
memory.long_term_memory.store_fact("home", (100, 64, 200))
home = memory.long_term_memory.get_fact("home")
# Conversation memory — chat history
memory.conversation_memory.add_message("player", "Go get wood.")
# Save / load all memory layers
await memory.save_all()
await memory.load_all()from mindpy.tasks import TaskManager, BaseTask, TaskData, TaskStatus
class MineOresTask(BaseTask):
@property
def name(self) -> str:
return "mine_ores"
@property
def description(self) -> str:
return "Mine iron ore near current position"
async def execute(self, context) -> str:
# ... mining logic ...
return TaskStatus.COMPLETED.value
task_manager = TaskManager(bot.event_bus)
await task_manager.start(num_workers=2)
task_id = await task_manager.submit_task(MineOresTask())Tasks are interruptible, suspendable, and serializable:
await task_manager.suspend_task(task_id)
await task_manager.resume_task(task_id)
await task_manager.cancel_task(task_id)from mindpy.goals import GoalManager, GoalPriority
goal_manager = GoalManager(bot.event_bus)
goal = goal_manager.create_goal(
name="Collect Diamonds",
description="Mine and collect 10 diamonds",
priority=GoalPriority.HIGH,
)
# Goals decompose into tasks automatically
sub_goals = await goal_manager.decompose_goal(goal.goal_id)from mindpy.llm import LLMManager
from mindpy.ai import AIAgent, AgentContext
# Setup
llm = LLMManager()
llm.setup_openai(api_key="sk-...", model="gpt-4o")
# or: llm.setup_ollama(base_url="http://localhost:11434", model="llama3")
agent = AIAgent(
llm,
system_prompt=(
"You are a helpful Minecraft bot. "
"Always respond in one short sentence."
),
)
# Decide an action based on context
ctx = AgentContext(
position=bot.state.position,
health=bot.state.health,
inventory=[],
)
response = await agent.decide(ctx, user_message="What should I do now?")MindPy supports Minecraft Java Edition 1.8 through 1.21+:
| Protocol | Minecraft | Notes |
|---|---|---|
| 47 | 1.8 | VarInt keepalive, string UUID |
| 340 | 1.12.2 | int64 keepalive, ClientSettings |
| 754 | 1.16.5 | UUID as bytes, TeleportConfirm |
| 762 | 1.19.4 | Signed chat, SystemChatMessage |
| 765 | 1.20.4 | Configuration state |
| 769 | 1.21+ | Latest IDs, forward-compat fallback |
Unknown versions ≥ 47 automatically fall back to the closest older supported version.
pytest # all tests
pytest tests/test_protocol.py -v # protocol layer only
pytest tests/test_bot.py -v # bot tests only
pytest --cov=mindpy --cov-report=html # with coverage report- Architecture Overview — system design and data-flow
- API Reference — complete API documentation
- Protocol Guide — writing custom packet handlers
- Plugin Development — extending MindPy
- Examples — runnable example bots