Blender 2D MCP
AI-driven 2D drawing and animation for Blender 5.x via the Model Context Protocol. 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), modeled after ahujasid/blender-mcp.
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[addon.py<br/>socket server in Blender]
C -->|main thread| D[Grease Pencil API<br/>GPv3]
addon.py— Blender addon embedding a TCP JSON command server. Commands are queued and executed on Blender's main thread viabpy.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
- In Blender: Edit > Preferences > Add-ons > Install…
- Select
addon.pyfrom this repository. - Enable Interface: Blender 2D MCP.
- Open the sidebar (View > Sidebar, or press N) → Blender2DMCP tab.
- Click Connect to MCP Client (default port
9876). Keep Blender running.
Tip:
blender-2d-mcp --install-addoncopiesaddon.pyto 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):
uvx --from "g:\My Projects\blender-2d-mcp" blender-2d-mcp
Option B — editable install:
cd "g:\My Projects\blender-2d-mcp"
pip install -e .
blender-2d-mcp # runs the stdio MCP server
Claude Desktop (claude_desktop_config.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:
{
"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):
{
"servers": {
"blender-2d-mcp": {
"type": "stdio",
"command": "uvx",
"args": ["--from", "g:\\My Projects\\blender-2d-mcp", "blender-2d-mcp"],
"env": {}
}
}
}
Alternatively, if
blender_2d_mcpis pip-installed in some environment, usethat 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 theBlender tools become available (#blender-2d-mcp). Start/stop/restart theserver from the Command Palette with MCP: List Servers. Reload the VS Codewindow 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
- 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.
get_gp_scene_info+get_viewport_screenshot— see what exists.setup_platform_scene("reels")(or any customsetup_2d_scene(resolution=[w,h])) →create_gp_objectif starting fresh.- Style first:
gp_apply_style_presetper layer andgp_create_palettefor named colors (gp_apply_palette_colorapplies them;layer_name='*'restyles everything). - Draw with
gp_draw_shape/gp_draw_stroke; passframe_numberto place strokes on key poses (frames are created automatically). Usegp_draw_strokesto batch detailed drawings. - Filled shapes: create a material with
fill_alpha > 0, thenfill=True+fill_material. - Characters: one GP object per body part →
create_armature(parented bone chains on the XZ plane) →parent_gp_to_boneeach part → animate withpose_boneor one-callapply_motion_preset(bounce/float/blink/wave/swing). - Animate 'on twos' (key poses every 2 frames at 24 fps): copy poses with
gp_copy_frame, tweak withgp_transform_stroke, thengp_interpolatefor in-betweens. Add a NOISE modifier for hand-drawn wobble. Check timing withget_animation_summary. - 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). - Render with
render_animation(use_video=True)and pollget_render_status— output is H264 mp4 with AAC audio, ready to upload. - Always screenshot after changes to verify visually. Mistakes are undoable via
undo.
Development
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