Skip to content

Addon preferences

BlenderMCPPreferences is an AddonPreferences subclass that holds all per-user addon config. Lives in addon/preferences.py. Accessed everywhere via get_prefs().

AddonPreferences are stored in Blender’s userpref.blend (in the per-user config dir), not in scene files. This is the headline distinction from the pre-Phase-8 layout, where secrets sat on bpy.types.Scene and shipped with every .blend file someone shared.

FieldTypeDefaultNotes
server_urlStringPropertyhttp://localhost:8000/mcpThe Streamable HTTP endpoint. /auth/login is derived by stripping /mcp.
usernameStringProperty""Persists across sessions; used by the Login operator.
jwt_tokenStringProperty (PASSWORD)""Populated by the Login operator. Bearer token for MCP requests.
jwt_expires_atStringProperty""ISO 8601 timestamp of access-token expiry.
FieldTypeDefaultNotes
use_polyhavenBoolPropertyFalseEnables Poly Haven commands.
use_hyper3dBoolPropertyFalseEnables Hyper3D Rodin commands.
hyper3d_modeEnumPropertyMAIN_SITEOne of MAIN_SITE (hyperhuman.deemos.com) or FAL_AI (fal.ai).
hyper3d_api_keyStringProperty (PASSWORD)""Free trial key is shipped as a constant.
use_sketchfabBoolPropertyFalseEnables Sketchfab commands.
sketchfab_api_keyStringProperty (PASSWORD)""Sketchfab v3 API token.
from addon.preferences import get_prefs
prefs = get_prefs()
if prefs.use_polyhaven:
api_key = prefs.hyper3d_api_key
...

get_prefs(context=None) defaults to bpy.context. Pass an explicit context when running inside an operator or panel where the active context might differ.

Three properties remain on bpy.types.Scene/WindowManager because they’re per-session state, not per-user config:

PropertyPurpose
blendermcp_server_runningIs the bus client connected right now?
blendermcp_client_idThe sticky UUID for display in the panel.
blendermcp_password_tmpIn-flight password before Login runs; cleared on success.

These get cleaned out on unregister() so they never leak into saved .blend files.

Pre-Phase-8 installs stored everything on Scene. A one-shot migration runs in register() on first load after upgrade:

addon/preferences.py
_LEGACY_SCENE_PROP_MAP = {
"server_url": "blendermcp_server_url",
"username": "blendermcp_username",
"jwt_token": "blendermcp_jwt_token",
"use_polyhaven": "blendermcp_use_polyhaven",
"use_hyper3d": "blendermcp_use_hyper3d",
"hyper3d_mode": "blendermcp_hyper3d_mode",
"hyper3d_api_key": "blendermcp_hyper3d_api_key",
"use_sketchfab": "blendermcp_use_sketchfab",
"sketchfab_api_key":"blendermcp_sketchfab_api_key",
}

migrate_from_scene(scene) copies legacy values into prefs only when:

  1. The legacy value differs from the pref’s default (so we don’t migrate placeholders), and
  2. The pref is currently at its default (so we don’t overwrite an already-migrated user value).

After migration, register() deletes the legacy Scene properties so they stop riding along in newly-saved .blend files.

BlenderMCPPreferences.draw() renders a compact panel in Edit > Preferences > Add-ons > BlenderMCP:

  • Connection: server URL, username, JWT (revealed on click). Next to the server URL field is a Test Connection button (URL icon) that probes the configured server before you bother with login.
  • Asset Integrations: a checkbox per integration; Hyper3D and Sketchfab reveal mode/key fields when enabled.

BLENDERMCP_OT_TestConnection (blendermcp.test_connection operator) does a quick smoke test against the configured server_url before the user clicks Login. Useful for catching wrong URLs, missing trailing slashes, expired certs, and unreachable hosts without producing a “wrong credentials?” red herring from the login flow.

What it does:

GET {server_url stripped of /mcp}/health # 3s timeout

What it reports (via self.report(...) in Blender’s operator log):

OutcomeMessage
200 + healthy JSONOK, server healthy, {buses} bus(es) active (green INFO)
HTTP 4xx/5xxFAILED: HTTP {code} from {url}
Timeout (>3s)Timeout (>3s) hitting {url}
TLS failureTLS error: {detail}
Connection refused / DNS failureCannot reach {url}: {detail}
Empty URLServer URL is empty

The 3-second timeout is deliberate — Blender’s preferences panel freezes during operator execution, so a stuck network call would freeze the UI. Fast failure beats hung UI.