Content
# Blender MCP Server
MCP server for **Cursor** that controls **Blender** through a local HTTP bridge.
`bpy` runs **inside Blender** — this repo does not import bpy in the MCP process.
**Separate from [Summit](../summit)** — export `.glb` files to Summit's `Assets/` when ready.
## Architecture
```
Cursor Agent → MCP (this repo) → HTTP bridge → bpy → Blender
```
| Path | Role |
|------|------|
| `server.py` | MCP entry (stdio) |
| `mcp_server/` | Tools exposed to the agent |
| `blender_addon/blender_mcp_bridge/` | Blender addon (HTTP + bpy commands) |
| `config.json` | Bridge port, Summit export paths, default `.blend` |
Blender 5.1: `/home/juan/blender-5.1.2/blender`
## Quick start
### 1. Install MCP server (Python)
```bash
cd /home/juan/source/blender_mcp_server
./scripts/setup_mcp.sh
cp config.example.json config.json # edit paths if needed
```
### 2. Install Blender addon
```bash
BLENDER_VERSION=5.1 ./scripts/install_addon.sh
# Detect version: ls ~/.config/blender/
```
In Blender:
1. **Edit → Preferences → Add-ons** → enable **Blender MCP Bridge**
2. **N panel → MCP tab → Start Server** (default `http://127.0.0.1:9876`)
Or start headless with the default export blend:
```bash
./scripts/start_bridge.sh
```
Logs: `bridge.log` in this repo. Override blend: `BLENDER_MCP_BLEND=/path/to/file.blend ./scripts/start_bridge.sh`
### 3. Connect Cursor
Merge `cursor.mcp.example.json` into `~/.cursor/mcp.json`:
```json
{
"mcpServers": {
"blender": {
"command": "/home/juan/source/blender_mcp_server/.venv/bin/python",
"args": ["/home/juan/source/blender_mcp_server/server.py"],
"env": {
"BLENDER_MCP_CONFIG": "/home/juan/source/blender_mcp_server/config.json"
}
}
}
}
```
Restart Cursor. Open this folder as workspace when animating.
### 4. Test
With Blender bridge running:
```bash
curl -s http://127.0.0.1:9876/health
```
In Cursor chat (with MCP enabled):
> Use ping_blender, then scene_information
## MCP tools
**Connection:** `ping_blender`, `get_server_config`
**Inspection:** `scene_information`, `list_objects`, `list_armatures`, `list_bones`, `list_actions`, `get_object_transform`
**Animation:** `create_action`, `rename_action`, `set_active_action`, `set_frame`, `set_bone_rotation_keyframe`, `set_action_loop_mode`
**Export / validate:** `export_gltf`, `export_to_summit`, `validate_scene_scale`, `validate_action_root_motion`, `validate_rts_asset`, `render_preview`
**Modeling (recipes):** `create_summit_bobcat` — low-poly harvester blockout + `idle` / `harvest` animations
### Bridge-only commands (not MCP tools yet)
Callable via `scripts/run_command.py` or HTTP bridge dispatch:
| Command | Purpose |
|---------|---------|
| `create_natural_idle_action` | Breathing idle from a pose frame (quaternion offsets) |
| `create_rifle_attack_action` | Forward rifle aim + recoil clip |
| `create_idle_action` | Static hold from source action |
| `prepare_rifleman_locomotion` | Import walk GLB, remap bones, strip hip motion, create idle |
| `duplicate_action` | Copy an action |
| `strip_action_root_motion` | Remove root bone location keys |
## Human Rifleman pipeline (current)
Source blends live under `~/Documents/BlenderSaves/summit/`:
| File | Content |
|------|---------|
| `riflemenStand.blend` | `idle` — natural breathing (~48 frames) |
| `riflemenwalk.blend` | `walk` — in-place **rifle run** sprint (22 frames) |
| `riflemenfire.blend` | `attack` — rifle aim + recoil (~24 frames) |
| `riflemenExport.blend` | Merged master (idle + walk + attack) |
| `riflemen-backup-dont-edit.blend` | Safety backup |
**Regenerate walk + attack from stand** (run FBX + procedural fire), then merge + export:
```bash
/home/juan/blender-5.1.2/blender --background --python scripts/prepare_rifleman_animations.py
```
**Body textures (Ch36):** merge exports from `riflemenStand.blend` (painted mesh). The exporter rebuilds `Ch36_Body` for glTF PBR and syncs packed images into the GLB. If sidecar PNGs look stale in Godot, reimport `Rifleman.glb` or run:
```bash
/home/juan/blender-5.1.2/blender --background --python scripts/fix_and_export_rifleman_body.py
/home/juan/blender-5.1.2/blender --background --python scripts/extract_textures_from_glb.py
```
**Re-export only** (if walk/fire blends already exist):
```bash
/home/juan/blender-5.1.2/blender --background --python scripts/prepare_rifleman_export.py
```
Output: `/home/juan/source/summit/Assets/Units/Human/Rifleman.glb`
Godot view: `Scenes/Units/HumanRiflemanView.tscn` (animations `idle`, `walk`, `attack`).
Rifle mesh is parented to `mixamorig1:RightHand` in the fire blend. Attack uses explicit aim quaternions (+Y barrel forward), not a copied walk pose.
Or export from the open blend via MCP:
```
export_to_summit(target_key="summit_human_units", filename="Rifleman.glb", action_names=["idle","walk","attack"])
```
## Bobcat harvester (vehicle recipe)
### V2 (RTS parts catalog — recommended)
Procedural **Generals-style** assembly from reusable mechanical parts:
```bash
BLENDER_VERSION=5.1 ./scripts/install_addon.sh
/home/juan/blender-5.1.2/blender --background --python scripts/create_summit_bobcat_v2.py
```
Output blend: `~/Documents/BlenderSaves/summit/bobcatExport.blend`
Silhouette preview: `~/Documents/BlenderSaves/summit/bobcat_v2_silhouette.png`
Output GLB: `/home/juan/source/summit/Assets/Units/Human/Bobcat.glb` (`idle`, `harvest`)
MCP: `create_summit_bobcat_v2`, `validate_rts_asset`, `validate_rts_silhouette`, `render_rts_silhouette_preview`
Parts catalog (`modeling/parts/`): cabin, wheel, bucket, hydraulic, chassis, fender, exhaust, headlight, arm, grille.
Animated pivots (stable Godot contract): `Bobcat_Arm_L`, `Bobcat_Arm_R`, `Bobcat_Bucket`.
### V1 (blockout)
```bash
/home/juan/blender-5.1.2/blender --background --python scripts/create_summit_bobcat.py
```
## Summit export
Config keys in `config.json`:
```json
"export_targets": {
"summit_human_units": "/home/juan/source/summit/Assets/Units/Human",
"summit_props": "/home/juan/source/summit/Assets/Props"
}
```
Default blend: `riflemenExport.blend` (see `default_blend` in config).
Then switch workspace to `/home/juan/source/summit` to wire Godot views and sim.
## Summit conventions
| Rule | Value |
|------|--------|
| Scale | 1 Blender unit = 1 meter |
| Infantry height | ~**1.6 m** (`Ch36` reference mesh) |
| Forward | **+Y** in Blender |
| Animation names | `idle`, `walk`, `attack`, `death` (infantry); vehicles: `idle`, `harvest` |
| Vehicle tri budget | **2500** tris (blockout recipe) |
| Locomotion | Walk **in place** — no hip translation (`validate_action_root_motion`) |
See also Summit [player identity](../summit/docs/02-Architecture/player-identity.md) for **team color masks** when authoring units:
- Paint tint regions in Blender (vertex color or `*_TeamMask.png`)
- One GLB per unit type; player color applied at runtime in Godot
- Human = military paint zones; Robot = LED emissive mask; etc.
## Headless
```bash
blender mychar.blend --background --python scripts/run_command.py -- '{"command":"scene_information","args":{}}'
```
## Roadmap
- [x] Rifleman idle / walk / attack pipeline + `prepare_rifleman_export.py`
- [x] Bobcat blockout recipe + `create_summit_bobcat.py`
- [x] `validate_rts_asset` + MCP modeling tools
- [x] `create_natural_idle_action`, `create_rifle_attack_action` (bridge)
- [x] `start_bridge.sh` headless bridge daemon
- [ ] Expose locomotion commands as MCP tools
- [ ] Team color mask validation / export hints for Summit shader
- [ ] `death` animation for rifleman
- [ ] Rigging helpers
- [ ] Auto tool registration review gate