Skip to main content
Glama
README.md
# Blender 2D MCP

AI-driven 2D drawing and animation for **Blender 5.x** via the [Model Context Protocol](https://modelcontextprotocol.io). Lets MCP clients (Claude Desktop, Cursor, VS Code Copilot agent mode, …) create Grease Pencil drawings, keyframes, materials, and renders directly inside a running Blender instance.

Built on Blender's **GPv3** API (`bpy.ops.grease_pencil.*`, `GreasePencilDrawing`).

```mermaid
flowchart LR
    A[MCP Client<br/>Claude / Cursor / VS Code] <-->|stdio / JSON-RPC| B[blender-2d-mcp<br/>FastMCP server]
    B <-->|TCP JSON<br/>localhost:9876| C[blender_2d_addon.py<br/>socket server in Blender]
    C -->|main thread| D[Grease Pencil API<br/>GPv3]
```

- **`blender_2d_addon.py`** — Blender addon embedding a TCP JSON command server. Commands are queued and executed on Blender's main thread via `bpy.app.timers`.
- **`src/blender_2d_mcp/server.py`** — FastMCP stdio server exposing the addon's commands as MCP tools.

## Requirements

- **Blender 5.2 LTS only** (the addon refuses to enable on older builds; the GPv3 API surface it uses is 5.2-specific)
- Python ≥ 3.10 with `mcp[cli]` (installed automatically)

## Installation

### 1. Install the addon into Blender

1. In Blender: **Edit > Preferences > Add-ons > Install…**
2. Select `blender_2d_addon.py` from this repository.
3. Enable **Interface: Blender 2D MCP**.
4. Open the sidebar (**View > Sidebar**, or press <kbd>N</kbd>) → **Blender2DMCP** tab.
5. Click **Connect to MCP Client** (default port `9876`). Keep Blender running.

> Tip: `blender-2d-mcp --install-addon` copies `blender_2d_addon.py` to your Downloads folder and prints these steps.

### 2. Connect an MCP client

The server binds to `localhost` only — no auth token is needed for local use.

**Option A — uvx (no install):**

```bash
uvx --from "g:\My Projects\blender-2d-mcp" blender-2d-mcp
```

**Option B — editable install:**

```bash
cd "g:\My Projects\blender-2d-mcp"
pip install -e .
blender-2d-mcp            # runs the stdio MCP server
```

**Claude Desktop** (`claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "blender-2d-mcp": {
      "command": "uvx",
      "args": ["--from", "g:\\My Projects\\blender-2d-mcp", "blender-2d-mcp"]
    }
  }
}
```

**Cursor** (`.cursor/mcp.json`) or generic stdio clients:

```json
{
  "mcpServers": {
    "blender-2d-mcp": {
      "command": "C:\\Path\\To\\Python\\Scripts\\blender-2d-mcp.exe",
      "args": []
    }
  }
}
```

**VS Code / GitHub Copilot Chat** (`.vscode/mcp.json` — already included in this repo):

```json
{
  "servers": {
    "blender-2d-mcp": {
      "type": "stdio",
      "command": "uvx",
      "args": ["--from", "g:\\My Projects\\blender-2d-mcp", "blender-2d-mcp"],
      "env": {}
    }
  }
}
```

> Alternatively, if `blender_2d_mcp` is pip-installed in some environment, use
> that environment's Python: `"command": "C:\\Path\\To\\Python\\python.exe",
"args": ["-m", "blender_2d_mcp.server"]`.

To use it: open Copilot Chat, switch the mode dropdown to **Agent**, and the
Blender tools become available (`#blender-2d-mcp`). Start/stop/restart the
server from the Command Palette with **MCP: List Servers**. Reload the VS Code
window after editing `mcp.json`.

## The 2D canvas

With `setup_2d_scene` the viewport is set to a **front orthographic view**, so the drawing plane is world **XZ**:

| 2D point you send | World position | Meaning                         |
| ----------------- | -------------- | ------------------------------- |
| `[x, y]`          | `(x, 0, y)`    | x = screen right, y = screen up |
| `[x, y, z]`       | `(x, y, z)`    | used as-is                      |

Units are meters (Blender default); a 1920×1080 HD frame at default camera zoom spans roughly ±5 units horizontally.

## Tool catalog

| Tool                                                                                | Purpose                                                                                                |
| ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| `get_gp_scene_info`                                                                 | List GP objects, layers, keyframes, stroke counts                                                      |
| `get_scene_info` / `get_object_info`                                                | General scene/object introspection                                                                     |
| `get_addon_info`                                                                    | Addon/protocol/Blender version + compatibility status                                                  |
| `setup_2d_scene`                                                                    | White background, front ortho view, HD resolution                                                      |
| `create_gp_object`                                                                  | New GP object with a layer + black stroke material                                                     |
| `gp_add_layer` / `gp_remove_layer` / `gp_set_active_layer` / `gp_list_layers`       | Layer management                                                                                       |
| `gp_rename_layer` / `gp_reorder_layer` / `gp_duplicate_layer`                       | Layer stack editing                                                                                    |
| `gp_set_layer_props`                                                                | Opacity, blend mode, hide, lock                                                                        |
| `gp_set_layer_mask`                                                                 | GPv3 layer masking (cut-out effects)                                                                   |
| `gp_insert_blank_frame`                                                             | Idempotent blank keyframe creation                                                                     |
| `gp_copy_frame` / `gp_move_frame` / `gp_delete_frame`                               | Keyframe manipulation (`instance_drawing=True` links drawings)                                         |
| `gp_draw_stroke`                                                                    | Freeform stroke; per-point radius/opacity profiles for tapering                                        |
| `gp_draw_strokes`                                                                   | Batch-draw many strokes in one call                                                                    |
| `gp_draw_shape`                                                                     | `circle` / `box` / `rounded_box` / `arc` / `line` / `polygon` / `star` / `spiral`, with optional fills |
| `gp_list_strokes` / `gp_delete_stroke` / `gp_transform_stroke` / `gp_edit_points`   | Stroke inspection & editing (move/scale/rotate/smooth)                                                 |
| `gp_create_material` / `gp_list_materials`                                          | Stroke + fill colors                                                                                   |
| `gp_create_palette` / `gp_list_palettes` / `gp_delete_palette`                      | Named color palettes (persist with the .blend file)                                                    |
| `gp_apply_palette_color`                                                            | Recolor a layer (or all layers with `layer_name='*'`) from a palette                                   |
| `gp_apply_style_preset` / `gp_list_style_presets`                                   | Named drawing styles: pencil, ink, brush, marker, thin_detail                                          |
| `create_armature` / `add_bone` / `remove_bone` / `list_bones`                       | Skeletons for cut-out character animation (bones on the XZ canvas plane)                               |
| `parent_gp_to_bone`                                                                 | Bind a GP object to one bone via an ARMATURE modifier                                                  |
| `pose_bone` / `get_pose`                                                            | Rotate/translate/scale bones with keyframes; read back rig state                                       |
| `apply_motion_preset`                                                               | One-call motions: bounce, float, blink (objects); wave, swing (bones)                                  |
| `setup_platform_scene`                                                              | Platform presets: youtube 16:9, shorts/reels/tiktok 9:16, square 1:1 (any custom size still allowed)   |
| `add_audio_strip` / `set_audio_volume` / `list_audio_strips` / `remove_audio_strip` | Background music/voiceover via VSE; embedded as AAC in mp4 renders                                     |
| `add_text_object` / `animate_text_in` / `remove_object`                             | Titles, hooks, captions on the canvas plane with pop/slide entrances                                   |
| `add_marker` / `remove_marker` / `list_markers`                                     | Timeline markers for shot sequencing                                                                   |
| `get_animation_summary`                                                             | Dump all keyframes/f-curves/markers to reason about timing                                             |
| `gp_interpolate`                                                                    | Generate in-betweens between two keyframes (easing control)                                            |
| `gp_keyframe_transform`                                                             | Keyframe object location/rotation/scale                                                                |
| `gp_set_onion_skinning`                                                             | Ghost previous/next frames                                                                             |
| `gp_add_modifier` / `gp_remove_modifier` / `gp_set_modifier` / `gp_list_modifiers`  | GP modifiers: Noise wobble, Smooth, Thickness, Time offset…                                            |
| `set_frame` / `set_scene_settings`                                                  | Playhead, fps, frame range, render engine, film transparency                                           |
| `setup_camera`                                                                      | Ortho front camera fitted to the canvas                                                                |
| `add_reference_image`                                                               | Background image empty for tracing                                                                     |
| `render_frame` / `render_animation` / `get_render_status` / `cancel_render`         | Non-blocking renders incl. mp4 video output                                                            |
| `undo` / `redo`                                                                     | Walk back AI-driven edits                                                                              |
| `get_viewport_screenshot`                                                           | Viewport capture returned as an image                                                                  |
| `run_gp_operator`                                                                   | Escape hatch: any `bpy.ops.grease_pencil.*` operator                                                   |
| `execute_blender_code`                                                              | Arbitrary `bpy` Python as a last resort                                                                |

### Typical workflow

0. **Ask first**: when a user requests an animation, the AI confirms target platform/aspect (YouTube 16:9, Shorts/Reels 9:16, square), duration, fps, and audio/text needs before drawing anything. The MCP itself stays orientation-neutral — every tool accepts explicit parameters.
1. `get_gp_scene_info` + `get_viewport_screenshot` — see what exists.
2. `setup_platform_scene("reels")` (or any custom `setup_2d_scene(resolution=[w,h])`) → `create_gp_object` if starting fresh.
3. Style first: `gp_apply_style_preset` per layer and `gp_create_palette` for named colors (`gp_apply_palette_color` applies them; `layer_name='*'` restyles everything).
4. Draw with `gp_draw_shape` / `gp_draw_stroke`; pass `frame_number` to place strokes on key poses (frames are created automatically). Use `gp_draw_strokes` to batch detailed drawings.
5. Filled shapes: create a material with `fill_alpha > 0`, then `fill=True` + `fill_material`.
6. Characters: one GP object per body part → `create_armature` (parented bone chains on the XZ plane) → `parent_gp_to_bone` each part → animate with `pose_bone` or one-call `apply_motion_preset` (bounce/float/blink/wave/swing).
7. Animate 'on twos' (key poses every 2 frames at 24 fps): copy poses with `gp_copy_frame`, tweak with `gp_transform_stroke`, then `gp_interpolate` for in-betweens. Add a NOISE modifier for hand-drawn wobble. Check timing with `get_animation_summary`.
8. Social polish: hook text in the first second (`add_text_object` + `animate_text_in`), keep action inside the central ~80% (platform UI covers edges), add music (`add_audio_strip`).
9. Render with `render_animation(use_video=True)` and poll `get_render_status` — output is H264 mp4 with AAC audio, ready to upload.
10. **Always screenshot after changes** to verify visually. Mistakes are undoable via `undo`.

## Skill for AI agents

This repository ships an [agent skill](https://code.visualstudio.com/docs/copilot/customization/agent-skills) at
[`.github/skills/blender-2d-animation/SKILL.md`](.github/skills/blender-2d-animation/SKILL.md) that teaches AI agents
**how to combine the tools**: workflow ordering, step-by-step recipes (loop animation, cut-out character rig,
explainer video with audio), batching rules, common pitfalls, and the screenshot-verify loop.

- **VS Code / GitHub Copilot agent mode**: picked up automatically from `.github/skills/` in the workspace.
- **Claude Code / other clients**: copy or symlink the folder to their skills location, e.g.

```bash
# Claude Code (project scope)
mkdir -p .claude/skills
cp -r .github/skills/blender-2d-animation .claude/skills/
```

The skill is documentation only — it doesn't change server behavior. Clients without skill support still get the
essential guidance via the MCP server's built-in `instructions` field.

## Development

```bash
pip install -e ".[dev]"
python -m pytest tests/ -v
```

The test suite runs the addon's TCP transport outside Blender by AST-lifting the server class against a fake `bpy` — no Blender install needed. Handler logic that touches real Grease Pencil data must be verified inside Blender.

## Troubleshooting

| Symptom                                         | Fix                                                                                                                                                                    |
| ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| MCP client can't connect                        | Start the server in Blender's sidebar panel first; check the port matches (default 9876)                                                                               |
| Port already in use                             | Change the port in the sidebar panel and add `--port N` to the MCP command                                                                                             |
| Firewall prompt on startup                      | Allow Blender on private networks (server binds localhost only)                                                                                                        |
| Screenshot is black                             | Click into the Blender window once (compositor issue), or ensure a 3D viewport is visible; the tool falls back from offscreen GPU capture to window grab automatically |
| `gp_interpolate` fails                          | It needs a visible 3D viewport in Blender and keyframes at both boundary frames; open one                                                                              |
| Modal operators (pen, brush strokes) don't work | Not supported over MCP — use `gp_draw_stroke` instead                                                                                                                  |
| `Unauthorized: missing or invalid auth token`   | No longer used — restart the MCP server and make sure both the addon and `blender-2d-mcp` are up to date (auth was removed in v0.3)                                    |
| Renders seem to hang                            | They're non-blocking now — poll `get_render_status` until `state=done`                                                                                                 |
| Addon won't enable                              | This project requires **Blender 5.2 LTS** exactly (GPv3 API only); older builds are refused by design                                                                  |

## License

MIT