lego-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@lego-mcp create a new build called 'house' on a 32x32 baseplate"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
lego-mcp
An MCP server where a model can only build physically valid Lego. Instead of letting the model free-form geometry (and hallucinate impossible structures), every action goes through a constraint engine that behaves like real plastic:
Catalog-only parts —
brick_2x4,plate_1x6,tile_2x2, slopes (slope_2x2,slope_inv_1x2, …), baseplates (baseplate_32x32, …). Unknown part names are rejected with suggestions. No hallucinated 3×5 bricks.Solid bricks — placements that overlap existing pieces are rejected, with the exact colliding cells and the lowest free z reported back.
Stud-to-tube connection required — a piece must sit on studs of an existing piece, on the baseplate, or press its own studs up under an overhang. Floating bricks are rejected with a valid z suggested.
Tiles are smooth — nothing can stack on a tile.
Slopes are directional — studs only on their non-sloped rows (nothing attaches to the sloped face); rotation is 0/90/180/270 and slopes descend toward +y at rotation 0. Inverted slopes grip below only on their tall rows.
Baseplates are ground-only — studs on top, smooth underneath, z=0 only.
No orphaning — removing a piece that would leave others disconnected from the baseplate is refused ("remove those first, top down").
Stability hints — extreme cantilevers succeed but return a warning.
The error messages are written for the model: each rejection explains the violated constraint and suggests a fix, so an agent converges on buildable structures instead of drifting.
Built by a model, through the constraints
Every piece below was placed by an LLM whose only tools were this server's — each brick validated stud-by-stud, then exported to Blender and rendered. No piece floats, overlaps, or stacks on a tile.

Corbelled arch | Colonnade |
|
|
The corbelled arch is the constraint engine at its most demanding: every course steps inward off the studs of the one below, and the two flanks have to meet at the crown without a single floating brick — the model gets there because each illegal step is bounced back with the reason and a legal alternative.
Related MCP server: mcp-toolbox
Coordinates
x,yin studs,zin plates (1 brick = 3 plates tall).z=0is the baseplate; a brick on top of a brick placed atz=0goes atz=3.rotationis 0 or 90 (swaps the footprint).
Tools
Tool | Purpose |
| fresh baseplate |
| full catalog + colors |
| validated placement |
| validated removal |
| full state as JSON |
| top-down ASCII map per plate layer |
| reload a saved build (re-validated on load) |
|
|
State auto-saves to <state>/<name>.json after every mutation and the latest
build is resumed on server restart. The state directory is, in order:
$LEGO_MCP_HOME/builds if set, else the repo's builds/ when run from a
writable checkout, else ~/.lego-mcp/builds when installed. Set
LEGO_MCP_HOME to keep builds wherever you like.
Install
lego-mcp is a standard stdio MCP server,
so any MCP-capable host can run it. It's packaged as a normal Python
distribution — the only thing a host needs is a command that launches it.
Run it with no install (recommended)
uvx fetches and runs it in a throwaway
environment, so there's nothing to maintain:
uvx --from "git+https://github.com/Axel-Jalonen/lego-mcp" lego-mcp # straight from this repo
uvx --from ./dist/lego_mcp-0.1.0-py3-none-any.whl lego-mcp # from a built wheelOr install it as a tool
pipx install "git+https://github.com/Axel-Jalonen/lego-mcp" # or: pip install ...
lego-mcp # the `lego-mcp` command is now on PATHOr grab the prebuilt wheel from the latest
release and
pipx install lego_mcp-0.1.0-py3-none-any.whl.
Build the distributable yourself
uv build # -> dist/lego_mcp-0.1.0-py3-none-any.whl (+ .tar.gz)The .whl is the portable artifact: copy it anywhere, uvx --from <the.whl> lego-mcp, done. No source checkout, venv, or absolute paths needed.
Wiring it into a host
Every MCP host takes the same shape — a command + args. Use uvx so the host
doesn't need Python set up:
{
"mcpServers": {
"lego": {
"command": "uvx",
"args": ["--from", "git+https://github.com/Axel-Jalonen/lego-mcp", "lego-mcp"],
"env": { "LEGO_MCP_HOME": "~/lego-builds" }
}
}
}Claude Desktop →
claude_desktop_config.json(Settings → Developer).Claude Code →
claude mcp add lego -- uvx --from git+https://github.com/Axel-Jalonen/lego-mcp lego-mcp(this repo also ships a dev .mcp.json).Cursor / Windsurf / VS Code MCP → the same
mcpServersblock.Ollama → Ollama's own runtime doesn't load MCP servers directly; run it through an MCP host that talks to Ollama, e.g.
mcphostor Open WebUI.mcphostreads the identical config above, so pointing it at the sameuvxcommand gives your local model the eight Lego tools.
Development
python3 -m venv .venv
.venv/bin/pip install -e . pytest # editable install
.venv/bin/python -m pytest tests/ # 15 constraint testsPointing a small model at it
agent/build_agent.py runs an agent loop where a model's only tools are this server's — no filesystem, no bash, no repo access. The constraint engine rejects every invalid move with a corrective hint, so even a small model can only ever produce physically valid builds:
export ANTHROPIC_API_KEY=sk-ant-...
.venv/bin/python agent/build_agent.py "build a small watchtower"
.venv/bin/python agent/build_agent.py --model claude-sonnet-5 "build a bridge"Defaults to claude-haiku-4-5. The final state is reported by the engine
(get_build), not by the model's own claims, and builds land in builds/.
Layout
lego_mcp/catalog.py — part definitions (footprint, height in plates, stud availability, LDraw ids) and colors
lego_mcp/engine.py — occupancy grid, connection rules, orphan detection, ASCII views, save/load
lego_mcp/export.py — LDraw + Blender exporters
lego_mcp/server.py — FastMCP tool layer
Available Tools
8 toolsexport_buildA
Export the build. fmt='ldraw' writes builds/.ldr (openable in BrickLink Studio / LeoCAD); fmt='blender' writes builds/_blender.py, a bpy script that constructs the model in Blender.
Inputs: fmt. Returns: result
get_buildA
Current build state: piece list with positions, inventory, dimensions.
Inputs: no named parameters. Returns: result
list_partsA
List every part in the catalog (the ONLY placeable pieces) and all colors. Sizes are studs; height is in plates (brick=3, plate/tile=1). Tiles have smooth tops. Slopes have studs only on their non-sloped rows (at rotation 0 the slope descends toward +y; rotate 90/180/270 to face it). Baseplates have studs on top, nothing underneath, and only go at z=0.
Inputs: no named parameters. Returns: result
load_buildA
Load a previously saved build by name (see saved .json files in builds/). The save is re-validated piece by piece on load.
Inputs: name (required). Returns: result
new_buildA
Start a fresh build on an empty baseplate of width x depth studs. Replaces the current build (previous one stays saved on disk).
Inputs: name, depth, width. Returns: result
place_brickA
Place a part with its minimum-x,y corner at stud (x,y), bottom at plate height z (z=0 on the ground; on top of a brick placed at z=0 means z=3). rotation is 0, 90, 180 or 270 (90/270 swap the footprint; slopes descend toward +y at 0 and rotate with the piece). Fails with an explanation if the placement collides, floats, or leaves the build area.
Inputs: x (required), y (required), z (required), part (required), +2 more. Returns: result
remove_brickA
Remove a piece by id. Refused if other pieces would be left floating.
Inputs: piece_id (required). Returns: result
view_layersA
Top-down ASCII map of each plate layer (pieces shown by id, '.' empty). Optionally limit to plate range [z_from, z_to).
Inputs: z_to, z_from. Returns: result
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 8 tools
Each tool has a clearly distinct purpose and target action: export, get, list, load, new, place, remove, view. No overlap or ambiguity among them.
All tool names follow a consistent verb_noun pattern in snake_case, such as export_build, list_parts, place_brick, making them predictable and easy to understand.
With 8 tools, the server is well-scoped for a LEGO building assistant, covering essential operations without being overly complex or sparse.
The tool set covers core building workflows (create, read, delete, export) but lacks an update tool for bricks (e.g., reposition) and a way to list saved builds, requiring minor workarounds.
Maintenance
Related MCP Connectors
MCP server for AI dialogue using various LLM models via AceDataCloud
Generate game-ready 3D models, textures, and audio from natural language, over MCP.
Capability registry for the agentic economy. Semantic search over verified MCP server listings.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceA Three.js MCP server for designing 3D brick constructions, enabling AI or interactive placement of bricks through natural language and providing tools to manage and export scenes.30MIT
- AlicenseNot gradedqualityDmaintenanceA comprehensive MCP server enabling LLMs to execute commands, manage files, interact with Figma, search the web, generate images, and more, extending their capabilities beyond text generation.Apache 2.0
- AlicenseBqualityDmaintenanceAn MCP server for parametric part modeling in Onshape, producing fully-defined, variable-driven sketches and features. It enables LLMs to create editable CAD models using semantic selection and geometrically grounded constraints.331MIT
- AlicenseAqualityDmaintenanceAn MCP server that provides structural load and stability math (tipping, support reactions, beam checks) that language models often get wrong, enabling AI assistants to compute accurate engineering estimates.321 PyPIMIT

