Snail.MCP.Blender 0.2.0

{
  "inputs": [
    {
      "type": "promptString",
      "id": "SNAIL_MCP_BLENDER_DATA_DIRECTORY",
      "description": "This server's own files: the add-on zip, downloads without a project, the token of a Blender on this machine. Default: ~/.s3l-mcp-blender."
    },
    {
      "type": "promptString",
      "id": "SNAIL_MCP_BLENDER_PROJECT_DIRECTORY",
      "description": "Over stdio, the project folder: downloads land in it and relative uploads are read from it. Default: the folder the client starts the server in; a server over HTTP has none."
    },
    {
      "type": "promptString",
      "id": "SNAIL_MCP_BLENDER_BRIDGE__HOST",
      "description": "Address of the add-on: the loopback, or the Blender container's name when the server runs beside it in Docker. A Blender on another machine is reached through the tunnel instead.",
      "default": "127.0.0.1"
    },
    {
      "type": "promptString",
      "id": "SNAIL_MCP_BLENDER_BRIDGE__PORT",
      "description": "Port the server connects to: the add-on's port, or the local end of the SSH tunnel.",
      "default": "9876"
    },
    {
      "type": "promptString",
      "id": "SNAIL_MCP_BLENDER_BRIDGE__CONNECT_TIMEOUT_SECONDS",
      "description": "Seconds to wait for the socket to open.",
      "default": "5"
    },
    {
      "type": "promptString",
      "id": "SNAIL_MCP_BLENDER_BRIDGE__REQUEST_TIMEOUT_SECONDS",
      "description": "Seconds to wait for one add-on reply; renders and imports use their own longer timeout.",
      "default": "30"
    },
    {
      "type": "promptString",
      "id": "SNAIL_MCP_BLENDER_BRIDGE__TOKEN",
      "description": "Shared secret for the add-on; empty reads the token file.",
      "password": true
    },
    {
      "type": "promptString",
      "id": "SNAIL_MCP_BLENDER_BRIDGE__TOKEN_FILE",
      "description": "Where to read the add-on's generated token, e.g. a copy of a remote add-on's. Default: <data>/state/token."
    },
    {
      "type": "promptString",
      "id": "SNAIL_MCP_BLENDER_BRIDGE__TUNNEL__HOST",
      "description": "user@host of a Blender on another machine: the server keeps an SSH tunnel to it while it runs. Empty keeps the link on this machine."
    },
    {
      "type": "promptString",
      "id": "SNAIL_MCP_BLENDER_BRIDGE__TUNNEL__KEY",
      "description": "Private key for the tunnel's SSH login; the SSH agent and ~/.ssh/config decide when empty."
    },
    {
      "type": "promptString",
      "id": "SNAIL_MCP_BLENDER_BRIDGE__TUNNEL__REMOTE_PORT",
      "description": "The add-on's port on the other machine.",
      "default": "9876"
    },
    {
      "type": "promptString",
      "id": "SNAIL_MCP_BLENDER_IDLE_TIMEOUT_MINUTES",
      "description": "Over stdio, minutes without a tool call after which the process exits on its own; 0, the default, disables the watchdog, since a client that ends closes the pipe and the server exits with it. A server over HTTP never exits on its own.",
      "default": "0"
    },
    {
      "type": "promptString",
      "id": "SNAIL_MCP_BLENDER_SKILL_IDLE_MINUTES",
      "description": "Minutes a loaded skill may go unused before its tools leave the catalog; 0 keeps skills loaded.",
      "default": "0"
    },
    {
      "type": "promptString",
      "id": "SNAIL_MCP_BLENDER_AGENT",
      "description": "Name this server signs its requests with, so the add-on's journal and leases tell agents apart."
    },
    {
      "type": "promptString",
      "id": "SNAIL_MCP_BLENDER_BRIDGE__AGENT",
      "description": "The name the link signs its requests with, when it should differ from SNAIL_MCP_BLENDER_AGENT."
    },
    {
      "type": "promptString",
      "id": "SNAIL_MCP_BLENDER_PYTHON",
      "description": "How far blender_python may go: allow, fallback (a script only where no dedicated tool covers its work, in sequences and batches too) or off, which also refuses script operators and python steps.",
      "default": "allow"
    },
    {
      "type": "promptString",
      "id": "SNAIL_MCP_BLENDER_OCIO_CONFIG",
      "description": "OpenColorIO config background render jobs run under; empty keeps Blender's own."
    },
    {
      "type": "promptString",
      "id": "SNAIL_MCP_BLENDER_TRANSPORT",
      "description": "Stdio when the client starts the server, which is how a client runs this package; Http when it runs on its own beside Blender, as the Docker image does, and clients connect to it with a bearer token.",
      "default": "Stdio"
    },
    {
      "type": "promptString",
      "id": "SNAIL_MCP_BLENDER_HTTP__URL",
      "description": "Over HTTP: the address to listen on.",
      "default": "http://127.0.0.1:8080"
    },
    {
      "type": "promptString",
      "id": "SNAIL_MCP_BLENDER_HTTP__TOKEN",
      "description": "Over HTTP: the token clients present as a bearer on /mcp and at the pages' sign-in; wins over HTTP__TOKEN_FILE. Over HTTP the server refuses to start without one of the two.",
      "password": true
    },
    {
      "type": "promptString",
      "id": "SNAIL_MCP_BLENDER_HTTP__TOKEN_FILE",
      "description": "Over HTTP: a file holding that token, the way a container receives a secret."
    },
    {
      "type": "promptString",
      "id": "SNAIL_MCP_BLENDER_HTTP__PUBLIC_URL",
      "description": "Over HTTP: the address clients reach the server at behind a reverse proxy, which links to files are built on. Default: HTTP__URL."
    },
    {
      "type": "promptString",
      "id": "SNAIL_MCP_BLENDER_HTTP__LINK_MINUTES",
      "description": "Over HTTP: minutes a signed file link stays valid, 1 to 1440.",
      "default": "15"
    },
    {
      "type": "promptString",
      "id": "SNAIL_MCP_BLENDER_HTTP__UPLOAD_PART_MB",
      "description": "Over HTTP: megabytes an upload sends per request; lower it when a proxy stops reading a body before a part arrives.",
      "default": "16"
    },
    {
      "type": "promptString",
      "id": "SNAIL_MCP_BLENDER_CONFIG",
      "description": "Path of the settings file, with the same keys under a Snail section; when set, it is the only file read, and none is read if it does not exist."
    }
  ],
  "servers": {
    "Snail.MCP.Blender": {
      "type": "stdio",
      "command": "dnx",
      "args": ["Snail.MCP.Blender@0.2.0", "--yes"],
      "env": {
        "SNAIL_MCP_BLENDER_DATA_DIRECTORY": "${input:SNAIL_MCP_BLENDER_DATA_DIRECTORY}",
        "SNAIL_MCP_BLENDER_PROJECT_DIRECTORY": "${input:SNAIL_MCP_BLENDER_PROJECT_DIRECTORY}",
        "SNAIL_MCP_BLENDER_BRIDGE__HOST": "${input:SNAIL_MCP_BLENDER_BRIDGE__HOST}",
        "SNAIL_MCP_BLENDER_BRIDGE__PORT": "${input:SNAIL_MCP_BLENDER_BRIDGE__PORT}",
        "SNAIL_MCP_BLENDER_BRIDGE__CONNECT_TIMEOUT_SECONDS": "${input:SNAIL_MCP_BLENDER_BRIDGE__CONNECT_TIMEOUT_SECONDS}",
        "SNAIL_MCP_BLENDER_BRIDGE__REQUEST_TIMEOUT_SECONDS": "${input:SNAIL_MCP_BLENDER_BRIDGE__REQUEST_TIMEOUT_SECONDS}",
        "SNAIL_MCP_BLENDER_BRIDGE__TOKEN": "${input:SNAIL_MCP_BLENDER_BRIDGE__TOKEN}",
        "SNAIL_MCP_BLENDER_BRIDGE__TOKEN_FILE": "${input:SNAIL_MCP_BLENDER_BRIDGE__TOKEN_FILE}",
        "SNAIL_MCP_BLENDER_BRIDGE__TUNNEL__HOST": "${input:SNAIL_MCP_BLENDER_BRIDGE__TUNNEL__HOST}",
        "SNAIL_MCP_BLENDER_BRIDGE__TUNNEL__KEY": "${input:SNAIL_MCP_BLENDER_BRIDGE__TUNNEL__KEY}",
        "SNAIL_MCP_BLENDER_BRIDGE__TUNNEL__REMOTE_PORT": "${input:SNAIL_MCP_BLENDER_BRIDGE__TUNNEL__REMOTE_PORT}",
        "SNAIL_MCP_BLENDER_IDLE_TIMEOUT_MINUTES": "${input:SNAIL_MCP_BLENDER_IDLE_TIMEOUT_MINUTES}",
        "SNAIL_MCP_BLENDER_SKILL_IDLE_MINUTES": "${input:SNAIL_MCP_BLENDER_SKILL_IDLE_MINUTES}",
        "SNAIL_MCP_BLENDER_AGENT": "${input:SNAIL_MCP_BLENDER_AGENT}",
        "SNAIL_MCP_BLENDER_BRIDGE__AGENT": "${input:SNAIL_MCP_BLENDER_BRIDGE__AGENT}",
        "SNAIL_MCP_BLENDER_PYTHON": "${input:SNAIL_MCP_BLENDER_PYTHON}",
        "SNAIL_MCP_BLENDER_OCIO_CONFIG": "${input:SNAIL_MCP_BLENDER_OCIO_CONFIG}",
        "SNAIL_MCP_BLENDER_TRANSPORT": "${input:SNAIL_MCP_BLENDER_TRANSPORT}",
        "SNAIL_MCP_BLENDER_HTTP__URL": "${input:SNAIL_MCP_BLENDER_HTTP__URL}",
        "SNAIL_MCP_BLENDER_HTTP__TOKEN": "${input:SNAIL_MCP_BLENDER_HTTP__TOKEN}",
        "SNAIL_MCP_BLENDER_HTTP__TOKEN_FILE": "${input:SNAIL_MCP_BLENDER_HTTP__TOKEN_FILE}",
        "SNAIL_MCP_BLENDER_HTTP__PUBLIC_URL": "${input:SNAIL_MCP_BLENDER_HTTP__PUBLIC_URL}",
        "SNAIL_MCP_BLENDER_HTTP__LINK_MINUTES": "${input:SNAIL_MCP_BLENDER_HTTP__LINK_MINUTES}",
        "SNAIL_MCP_BLENDER_HTTP__UPLOAD_PART_MB": "${input:SNAIL_MCP_BLENDER_HTTP__UPLOAD_PART_MB}",
        "SNAIL_MCP_BLENDER_CONFIG": "${input:SNAIL_MCP_BLENDER_CONFIG}"
      }
    }
  }
}
                    
This package contains an MCP Server. The server can be used in VS Code by copying the generated JSON to your VS Code workspace's .vscode/mcp.json settings file.
dotnet tool install --global Snail.MCP.Blender --version 0.2.0
                    
This package contains a .NET tool you can call from the shell/command line.
dotnet new tool-manifest
                    
if you are setting up this repo
dotnet tool install --local Snail.MCP.Blender --version 0.2.0
                    
This package contains a .NET tool you can call from the shell/command line.
#tool dotnet:?package=Snail.MCP.Blender&version=0.2.0
                    
nuke :add-package Snail.MCP.Blender --version 0.2.0
                    

Snail.MCP.Blender

Documentation NuGet License: MIT

An MCP server that lets your AI assistant drive a running Blender: 149 tools for the scene, modeling, materials, animation, physics, rendering, video editing and file exchange. It runs on your machine, talks to your client over stdio, and reaches Blender through a small bundled add-on listening on the loopback interface — of this machine, or of another one through an SSH tunnel the server opens. Or it runs in Docker beside Blender on a machine with a GPU, and any client connects to it over HTTPS.

Ask in plain words: build a low-poly house and give it a brick material, animate the camera orbiting the scene over 120 frames, render a 1080p preview to the desktop, export everything as glTF.

  • Always on: scene and object info, selection, primitives, text, empties, transforms, camera, lights, collections, files and undo, a project folder; finding a tool by what you want done, many commands in one call, files to and from a Blender on another machine; the journal, leases, batches and snapshots for several agents — plus any Blender operator with validated properties, its documentation, and Python.
  • Skills, loaded on demand: modeling (mesh edits on a selection, vertices, modifiers, Geometry Nodes, curves), materials (Principled BSDF, shader node graphs, textures, UVs), animation (keyframes, F-Curves, armatures, constraints, shape keys, drivers), rendering (world and HDRI, stills and animations, product shots, viewport captures, texture bakes, colour management, EXR depth and codecs, stamps, resolution presets, stereo, Cycles and EEVEE in depth with the GPU backend, motion blur, Freestyle, view layers with passes, cryptomatte, AOVs and light groups, light linking, the physical camera, camera moves and light rigs in kelvin, per-object render flags), post (compositor, File Output nodes, cryptomatte mattes, a lens-effects stack with denoise, glare, halation, chromatic aberration and film grain), farm (background render jobs with a queue, chunks, retries, progress and cancel, a pre-flight check, a memory budget with a probe render), io (FBX, OBJ, STL, PLY, glTF, Collada, USD, Alembic, X3D, DXF and 3DS both ways, SVG in; assets from other .blend files), physics (rigid bodies, cloth, fluids, particles, cache baking), video (the sequence editor: strips and image sequences, transitions, fades, cuts, proxies, fractional frame rates, grading, ProRes and H.264/H.265 encode settings, audio mixdown).

Renders come back with a picture the model can look at and exposure statistics; long renders report progress and can run as queued background jobs. Three MCP prompts (photoreal_product_shot, farm_exr_delivery, vse_grade_and_deliver) lay out the studio order of tools.

Full tool reference with every parameter and JSON schema: orldev.github.io/Snail.MCP.Blender

Requirements

.NET 10 SDK dotnet.microsoft.com/download
An MCP client Claude Code, Claude Desktop, Cursor, VS Code, Codex — anything that speaks MCP over stdio, or over HTTP to the Docker set-up
Blender 5.2 LTS or newer The add-on ships as a Blender extension

Works on macOS, Windows and Linux.

Install

1. The server

dotnet tool install -g Snail.MCP.Blender

Alternatives: dnx Snail.MCP.Blender --yes runs it without installing; or clone the repository and run dotnet build -c Release, then give the client the command dotnet with src/bin/Release/net10.0/Snail.MCP.Blender.dll as its argument.

2. Your client

{
  "mcpServers": {
    "blender": {
      "command": "s3l-mcp-blender"
    }
  }
}

For Claude Code: claude mcp add blender -- s3l-mcp-blender.

For a server that runs in Docker beside Blender, register its address instead; nothing else is installed on the client, the add-on included, and step 3 is done on that machine:

claude mcp add --transport http --scope user blender https://blender.example.com/mcp \
  --header "Authorization: Bearer $(cat ~/.s3l-mcp-blender/remote/docker.token)"

docs/content/docker.md sets up the machine.

3. The add-on in Blender

Ask the agent to run blender_install_addon. It builds snail_bridge-<version>.zip in ~/.s3l-mcp-blender/addon and lists the steps: Edit → Preferences → Get Extensions → Install from Disk, pick the zip. The Snail tab in the 3D viewport sidebar (N) then says listening on port 9876.

4. Check

Ask for blender_diagnose: with Blender open it reports the Blender version and reachable: true.

Configuration

Environment variables, all optional:

Variable Default Meaning
SNAIL_MCP_BLENDER_DATA_DIRECTORY ~/.s3l-mcp-blender This server's files: the add-on zip, downloads without a project, the token of a local Blender
SNAIL_MCP_BLENDER_PROJECT_DIRECTORY the folder the client starts the server in Over stdio, the project: downloads land in it, relative uploads are read from it. A server over HTTP has none
SNAIL_MCP_BLENDER_BRIDGE__PORT 9876 Port the add-on listens on
SNAIL_MCP_BLENDER_BRIDGE__REQUEST_TIMEOUT_SECONDS 30 Timeout for one add-on reply
SNAIL_MCP_BLENDER_IDLE_TIMEOUT_MINUTES 0 Over stdio, exit after this long without a tool call; 0, the default, keeps it running. A server over HTTP never exits on its own
SNAIL_MCP_BLENDER_SKILL_IDLE_MINUTES 0 Unload a skill unused for this many minutes; 0 keeps skills loaded, which suits clients that cache the tool list
SNAIL_MCP_BLENDER_BRIDGE__TOKEN — Shared secret for the add-on; empty reads the one the add-on generated into <data>/state/token
SNAIL_MCP_BLENDER_BRIDGE__TOKEN_FILE <data>/state/token Where to read that token, e.g. a copy of a remote add-on's
SNAIL_MCP_BLENDER_BRIDGE__TUNNEL__HOST — user@host of a Blender on another machine; the server keeps an SSH tunnel to it while it runs
SNAIL_MCP_BLENDER_BRIDGE__TUNNEL__KEY — Private key for that login
SNAIL_MCP_BLENDER_BRIDGE__TUNNEL__REMOTE_PORT 9876 The add-on's port over there; BRIDGE__PORT is the local end
SNAIL_MCP_BLENDER_AGENT — Name this server signs its requests with; the add-on's journal and leases tell agents apart
SNAIL_MCP_BLENDER_TRANSPORT Stdio Http runs the server on its own beside Blender; the HTTP__* settings in docs/content/configuration.md go with it
SNAIL_MCP_BLENDER_PYTHON allow fallback refuses a script whose work the tools already do, in a sequence or a batch too; off switches blender_python off together with the operators that run scripts, sequences and batches holding a script, and uploads outside the add-on's files folder
SNAIL_MCP_BLENDER_OCIO_CONFIG — OpenColorIO config background render jobs run under

The same keys go into a settings file, under a Snail section, and the variables win over it. The file is the one SNAIL_MCP_BLENDER_CONFIG names, the only one read when it is set, or else the first that exists of the chain every Snail server shares: .s3l-mcp-blender.json and s3l-mcp-blender.json in the working directory, ~/.config/s3l-mcp-blender/config.json, ~/.s3l-mcp-blender.json. A token belongs in ~/.config/s3l-mcp-blender/config.json, made readable by you alone with chmod 600 — never in a launchd job or a client's registration, which travel wherever they are copied. The whole list is in docs/content/configuration.md.

How it works

The add-on listens on 127.0.0.1:9876 — in the Docker image on the private network, started with --host — and accepts one JSON line per request. Its socket threads leave the scene alone: they queue the request, and Blender's main thread runs it — through a bpy.app.timers callback with the interface open, or the loop of blender -b -c snail_bridge without one — and writes one JSON line back. ping, progress, the file transfers and the storage commands are answered from the socket thread, so a busy Blender still reports what it is doing and still moves files. Every request carries a token — the add-on generates one into ~/.s3l-mcp-blender/state/token and the server reads it from there, so a fresh install needs nothing typed; a remote add-on's token is copied into a file BRIDGE__TOKEN_FILE names. A failure comes back as data with a type, a message and details, and the server adds a hint that fits the type. A call that outlives its timeout resets the link so a late reply can never be mistaken for the next answer.

Both sides carry a digest of the add-on's Python, so blender_diagnose says whether the add-on installed in Blender is the build the server ships — and, when it is not, which commands it lacks.

Towards the client, every tool answers with structuredContent next to the text, flags a failed call with isError, and declares through its annotations whether it is read-only, destructive or open-world. Two resources, snail://renders/last and snail://journal, hand the last picture and the journal to clients that read resources.

Tools live in four layers, from most to least specific: the dedicated tools, found by intent with find_tool, which opens the skill they live in; run_tools, which sends a list of them to the open Blender in one call, so a build of hundreds of steps stays inside the guards instead of becoming a script; blender_run_operator for any of Blender's thousands of operators with blender_describe_operator for their properties; and blender_python for what none of them reaches. 42 tools are always on; the other 107 arrive with a skill. A script skips validation and leases, and the journal records only that it ran, not what it touched, so its reply names the tools that cover what it did, and blender_diagnose reports how much of the traffic went that way.

Blender can also run on another machine — a workstation, or a GPU server with no display through blender -b -c snail_bridge — with renders, jobs and files living there. The server opens the SSH tunnel itself for as long as it runs, and the folder you work in is the project: blender_upload and blender_download mirror it with its twin on the other machine. docs/content/remote.md walks through the set-up, and docs/content/workflow.md is the working order: which Blender is in use, what belongs to which machine, and where projects and renders live.

The third way puts both on the GPU machine in Docker: ghcr.io/orldev/s3l-blender is Blender with the add-on, ghcr.io/orldev/s3l-blender-mcp the server in HTTP mode, and deploy/compose.yml joins them on an internal network. Clients connect to one HTTPS address with a bearer token; projects live in a volume, blender_open_project opens one, and blender_upload and blender_download hand the agent curl commands with links that expire. The server's own pages, behind a sign-in with the client token, show the status and let you look through the projects, render jobs, batches and snapshots, cancel a job and delete what is done with. docs/content/docker.md walks through it.

Developing

One folder per concern:

Folder Holds
src/ the server: Configuration, Domain with the command catalog in Commands, Application, Adapters, Hosting, Web, Extensions, and Tools — Base for the layer that is always on, one folder per skill, and Prompts for the descriptions, messages and prompts the model reads
addon/ the Blender add-on, one Python module per area
tests/ a folder per layer of src, Contracts for the guards, Tools and Live split by skill like the tools, Support for the fakes and the headless Blender
docs/ the documentation site: mkdocs.yml and the pages in content/ — the theme, which the server's pages wear too, comes with Snail.Sdk — and tools.json, the families of the tool reference the build writes
deploy/ the two images, each with its Dockerfile and its ignore file, the compose file with its .env.example, and the Traefik route
.mcp/ the MCP registry entry the NuGet package carries

global.json pins the .NET SDK to the 10.0.3xx band that CI uses; an older SDK refuses the build with a message naming the version.

dotnet build
dotnet test
ruff check addon                   # the add-on's Python, configured by ruff.toml
dotnet build src -p:SnailReference=true # regenerates docs/content/tools from the server itself
docker build -f deploy/blender/Dockerfile -t ghcr.io/orldev/s3l-blender:latest .
docker build -f deploy/mcp/Dockerfile --build-arg VERSION=0.1.0 -t ghcr.io/orldev/s3l-blender-mcp:latest .
dotnet msbuild src/Snail.MCP.Blender.csproj -t:SnailSite -p:SnailSiteServe=true   # the documentation site, in the Snail.Sdk theme

GitHub Actions: ci.yml builds and tests every push, lints the add-on, builds the site, runs the live tests against Blender 5.2.3 and builds both images when their sources change. A release is a tag, git tag v0.2.0 && git push --tags, and the checkout holds only a placeholder version, 0.1.0 in the project and .mcp/server.json: publish.yml writes the version of the tag into the assembly, the package and its registry manifest and the image tags. It repeats the whole CI, pushes both images to GHCR under their version, starts the pair to see that the server reaches Blender, then sends the package to NuGet, and only then creates the GitHub Release, publishes the documentation site and moves latest — the last two for a tag without a suffix. Dependabot proposes updates every two months.

The repository's .mcp.json holds an entry for the Debug build, commented out; uncomment it to register that build with Claude Code. Most tests do not need Blender: the add-on side is a scripted fake over a real TCP socket, and contract tests keep the C# command catalog equal to the registry in the add-on's Python. The live tests do need one — they start a headless Blender 5.2 and send it every command of the catalog but the four that need Blender's interface — and are skipped when none is installed.

License

MIT

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

Version Downloads Last Updated
0.2.0 0 10/28/2026
0.1.0 112 10/2/2026