MCP client and MCP server with mod on Luanti
  • Lua 52.4%
  • Python 46%
  • Nix 1.5%
  • Shell 0.1%
Find a file
damfle 5aebd0bbee
All checks were successful
CD / Setup (push) Successful in 5s
CD / Build and Push Gateway (push) Successful in 35s
CD / Build and Push MCP (push) Successful in 35s
ci: re add
2026-08-20 14:05:41 +02:00
.forgejo/workflows ci: re add 2026-08-20 14:05:41 +02:00
mcp_luanti_bot_api fix: various fixes 2026-08-20 14:01:40 +02:00
mod_bot_api fix: various fixes 2026-08-20 14:01:40 +02:00
.gitignore init: initial commit 2026-07-04 09:34:12 +02:00
.gitmodules init: initial commit 2026-07-04 09:34:12 +02:00
bot_api_gateway.py fix: various fixes 2026-08-20 14:01:40 +02:00
Dockerfile.gateway init: initial commit 2026-07-04 09:34:12 +02:00
Dockerfile.mcp init: initial commit 2026-07-04 09:34:12 +02:00
entrypoint.gateway.sh init: initial commit 2026-07-04 09:34:12 +02:00
entrypoint.mcp.sh init: initial commit 2026-07-04 09:34:12 +02:00
flake.nix init: initial commit 2026-07-04 09:34:12 +02:00
LICENSE doc: update doc 2026-07-04 18:55:53 +02:00
README.md doc: update doc 2026-07-04 18:55:53 +02:00
requirements.txt fix: various fixes 2026-08-20 14:01:40 +02:00
SKILL.md mod: add skill 2026-07-05 14:15:50 +02:00

luantimcp

ISC License

Control a running Luanti server from an MCP client, without implementing the Luanti network protocol.

Architecture

bot_api (Lua mod)  --JSON-RPC 2.0 poll-->  bot_api_gateway  <--JSON-RPC 2.0-->  mcp_luanti_bot_api  <--stdio-->  MCP client
                (HTTP)         (HTTP)

Three decoupled parts:

  • bot_api_gateway.py a standalone, long-running Python HTTP server. It is the only stable network endpoint. The mod polls it for commands using JSON-RPC 2.0; the MCP server pushes commands to it. Run it once and leave it running.
  • mod_bot_api/ a Luanti mod. It uses JSON-RPC 2.0 to communicate with the gateway, executes commands with the core.* API, and posts results / game events back. Includes bundled mesh model (models/bot.b3d) and texture (textures/bot.png) for bot visualization.
  • mcp_luanti_bot_api/ the MCP server package. It is spawned by the MCP client over stdio and forwards tool calls to the gateway using JSON-RPC 2.0.

Why a separate gateway? mcp_luanti_bot_api is started/stopped by the MCP client and talks over stdio, so it is not a reliable server the mod can connect to. The gateway decouples the two lifecycles.

luanti/ is the Luanti engine (git submodule), used as the API reference.

Project Structure

.
├── bot_api_gateway.py      # JSON-RPC 2.0 HTTP gateway server
├── mcp_luanti_bot_api/     # MCP server package
│   ├── __init__.py
│   ├── __main__.py         # Entry point: python -m mcp_luanti_bot_api
│   ├── client.py           # HTTP client for gateway communication
│   ├── config.py           # Configuration management
│   ├── handlers.py         # MCP tool call handlers
│   ├── server.py           # MCP server implementation
│   └── tools.py            # MCP tool definitions
├── mod_bot_api/            # Luanti mod
│   ├── init.lua            # Main mod implementation
│   ├── mod.conf            # Mod metadata
│   ├── settingtypes.txt    # Setting types
│   ├── models/
│   │   └── bot.b3d         # Bot mesh model
│   └── textures/
│       └── bot.png         # Bot texture
├── requirements.txt        # Python dependencies
└── README.md

Setup

Prerequisites

  • Python 3.8+
  • Luanti server
  • MCP client (e.g., Cursor, Claude Code)

1. Install the mod

Copy or symlink mod_bot_api/ into your world's worldmods/ (or the global mods/) directory and enable it for the world. The mod includes bundled assets:

  • models/bot.b3d - Bot mesh model
  • textures/bot.png - Bot texture

2. Configure Luanti (minetest.conf)

HTTP access is only granted to trusted mods. Add to your configuration:

secure.http_mods = bot_api
# URL of the gateway (default shown)
bot_api_url = http://127.0.0.1:8765
# Polling settings (optional - adjust based on network latency)
bot_api_poll_interval = 0.2
bot_api_poll_timeout = 35
# Bot visualization settings (optional)
bot_api_bot_forceload = true
bot_api_use_inventory = true

3. Install Python dependencies and start the gateway

pip install -r requirements.txt
python bot_api_gateway.py --host 127.0.0.1 --port 8765

Gateway options:

  • --host - Host to bind to (default: 127.0.0.1)
  • --port - Port to listen on (default: 8765)
  • --debug - Enable debug logging

4. Configure the MCP client

Create or update your .mcp.json to include the MCP server pointed at the gateway:

{
  "mcpServers": {
    "luanti-bot-api": {
      "command": "python",
      "args": ["-m", "mcp_luanti_bot_api", "--gateway-url", "http://127.0.0.1:8765"]
    }
  }
}

The mcp_luanti_bot_api supports these options:

  • --gateway-url - URL of the gateway (default: http://127.0.0.1:8765)
  • --name - Default bot name for this instance (default: "bot")
  • --debug - Enable debug logging
  • --huge_context - Include memory-intensive tools (get_blocks, get_all_items, get_entities)

Then start the Luanti server. Call the status tool to confirm the mod is connected to the gateway.

Configuration Reference

Gateway Configuration (bot_api_gateway.py)

Parameter Default Description
--host 127.0.0.1 Host to bind to
--port 8765 Port to listen on
--debug false Enable debug logging

Internal settings: Command timeout 20s, Poll hold 25s, Max events 500.

MCP Server Configuration (mcp_luanti_bot_api)

Parameter Default Description
--gateway-url http://127.0.0.1:8765 URL of the gateway
--name bot Default bot name for this instance
--debug false Enable debug logging
--huge_context false Enable memory-intensive tools

Environment variables: GATEWAY_URL, BOT_NAME, HUGE_CONTEXT.

Luanti Mod Configuration (minetest.conf)

Setting Default Description
secure.http_mods - Must include bot_api for HTTP access
bot_api_url http://127.0.0.1:8765 Gateway URL
bot_api_poll_interval 0.2 Seconds between polls
bot_api_poll_timeout 35 Poll request timeout in seconds
bot_api_bot_forceload true Keep bot's mapblock loaded
bot_api_use_inventory true Enable bot inventory system

Bots

A Luanti mod cannot create a real network player (players are network clients), so the agent does not appear in the player list. Instead, spawn_bot creates a server-side entity that behaves like a player:

  • renders as a bundled mesh by default (works on any game) with a floating nametag (color: #3a86ff). The mod uses bundled models from mod_bot_api/models/bot.b3d with texture mod_bot_api/textures/bot.png.
  • is affected by gravity and collision with a standard player collision box,
  • stays active even with no player nearby (its mapblock is transiently force-loaded; disable with bot_api_bot_forceload = false in minetest.conf),
  • has a virtual inventory (32 slots, disable with bot_api_use_inventory = false) that must contain items to place blocks,
  • move_bot makes it walk to a target (with walk animation) at 4.0 nodes/sec and auto-jumps over 1-node obstacles in its path; teleport_bot moves it instantly, and jump makes it hop on demand (jump speed: 6.0),
  • world actions can be attributed to it: pass bot to dig_block, place_block, or say and the bot faces the spot, plays the mining animation, and the dig/place fires the normal on_dignode/on_placenode callbacks with the bot as the actor. Digged nodes' drops are added to the bot's inventory.

Spawn it near a loaded area (e.g. close to a player) so the mapblock is active. Example: spawn_bot {name:"alice", x:10, y:8, z:10} then dig_block {x:11, y:8, z:10, bot:"alice"}.

Troubleshooting

Mod not connecting to gateway

  • Verify secure.http_mods = bot_api is set in minetest.conf
  • Check that bot_api_url points to the correct gateway address
  • Ensure the gateway is running (python bot_api_gateway.py --host 127.0.0.1 --port 8765)
  • Check gateway logs for connection attempts

MCP server not responding

  • Verify the gateway URL in your .mcp.json matches the gateway's host/port
  • Ensure the gateway is running and accessible
  • Check that python -m mcp_luanti_bot_api starts without errors
  • Use --debug flag on both gateway and MCP server for detailed logging

Bot spawning fails

  • Spawn near a loaded area (close to a player)
  • Verify the mod is loaded (check Luanti server logs for BotAPI initialization)
  • Ensure the bundled mesh and texture files exist in mod_bot_api/models/ and mod_bot_api/textures/

Commands timing out

  • Increase bot_api_poll_timeout in minetest.conf (try 60)
  • Check network connectivity between mod, gateway, and MCP server
  • Reduce --huge_context memory usage if dealing with large datasets

Multiple bots

The gateway and mod are bot-agnostic (bots are keyed by name), so you can run one mcp_luanti_bot_api per bot, each with its own --name, all pointing at the same gateway:

python -m mcp_luanti_bot_api --gateway-url http://127.0.0.1:8765 --name alice
python -m mcp_luanti_bot_api --gateway-url http://127.0.0.1:8765 --name bob

Each instance defaults its bot-management tools (spawn_bot, move_bot, ...) and action attribution (dig_block, place_block, say) to its own --name, so a client just calls spawn_bot {x,y,z} / dig_block {x,y,z} without repeating the name. Any tool can still target another bot explicitly via the name / bot argument. The .mcp.json in this repo registers alice and bob as an example.

Tools

Gateway Tools

Tool Description
status Gateway/mod connection status, bot name, command queue info
get_server_info Server uptime, gametime, time of day, max players, player count
get_players List of connected player names and spawned bots

Bot Management Tools

Tool Description
spawn_bot Spawn a bot entity at position (x, y, z). Uses instance's --name by default.
move_bot Walk a bot to position (with animation and auto-jump). Uses instance's --name.
teleport_bot Instantly move a bot to position. Uses instance's --name.
jump Make a bot jump if grounded. Uses instance's --name.
despawn_bot Remove a spawned bot. Uses instance's --name.
list_bots List all spawned bots with their positions
get_inventory Get a bot's inventory contents (requires bot_api_use_inventory = true)
give_item Give items to a bot's inventory (auto-drops if full)

World Interaction Tools

Tool Description
get_position Get position of a connected player
teleport Teleport a connected player to position
place_block Place a node at position (optionally as a bot with bot param)
dig_block Dig/remove node at position (drops go to bot inventory if bot is actor)
get_node Read the node name and param2 at a position
say Broadcast chat message to all players (optionally as a bot)
whisper Send private chat message to a specific player

Information Tools

Tool Description
get_chat Recent game events (chat messages, player join/leave)
get_config Get bot_api configuration (visual settings, inventory status)

Huge Context Tools (requires --huge_context flag)

Tool Description
get_blocks All registered blocks/nodes (paginated, default 100 per page)
get_entities All connected players and spawned bots with positions
get_all_items All registered items (nodes, tools, craftitems) - paginated

Note: Tools that default to the instance's bot name (--name flag) can target other bots by explicitly passing the name or bot parameter. Example: spawn_bot {name:"alice", x:10, y:8, z:10} creates a bot named "alice", and subsequent calls can target it with bot:"alice".