Install
$ agentstack add skill-impertio-studio-blender-bonsai-ifcopenshell-sverchok-claude-skill-package-blender-syntax-addons ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
blender-syntax-addons
Quick Reference
Version Decision Tree
Creating a Blender addon?
│
├── Target Blender ≥ 4.2?
│ ├── YES → Use blender_manifest.toml (extension system)
│ │ NO bl_info dict. NO __init__.py bl_info.
│ │ See: §Extension System
│ │
│ └── Also support Sidebar > My Tab",
"description": "Short description of addon purpose",
"warning": "", # Optional: shown as warning in prefs
"doc_url": "https://example.com/docs",
"tracker_url": "https://example.com/issues",
"category": "Object", # Must be a valid Blender category
}
Valid category values: 3D View, Add Mesh, Add Curve, Animation, Compositing, Development, Game Engine, Import-Export, Lighting, Material, Mesh, Node, Object, Paint, Physics, Render, Rigging, Scene, Sequencer, System, Text Editor, UV.
Pattern 2: Extension Manifest (Blender 4.2+)
Extensions use blender_manifest.toml instead of bl_info. The __init__.py contains ONLY register()/unregister() — no bl_info dict.
# blender_manifest.toml: REQUIRED for extensions (Blender 4.2+)
schema_version = "1.0.0"
id = "my_addon"
version = "1.0.0"
name = "My Addon"
tagline = "Short description without ending punctuation"
maintainer = "Developer Name "
type = "add-on"
# Minimum Blender version (must be >= 4.2.0 for extensions)
blender_version_min = "4.2.0"
# blender_version_max = "5.1.0" # Optional: first UNSUPPORTED version
license = ["SPDX:GPL-3.0-or-later"]
# copyright = ["2024 Developer Name"] # Optional
# Optional: category tags (from Blender's fixed tag list)
tags = ["Object", "Scene"]
# Optional: bundled Python wheels
# wheels = ["./wheels/some_lib-1.0.0-py3-none-any.whl"]
# Optional: platform restrictions
# platforms = ["windows-x64", "macos-arm64", "linux-x64"]
# Optional: permission declarations (required for network/file access)
# [permissions]
# network = "Downloads data from remote server"
# files = "Reads reference files from disk"
# clipboard = "Copies results to clipboard"
Pattern 3: Register / Unregister Lifecycle
# Blender 3.x/4.x/5.x: REQUIRED functions
import bpy
classes = (
MyPropertyGroup, # PropertyGroups FIRST
MY_OT_operator, # Then operators
MY_PT_panel, # Then UI elements
MyAddonPreferences, # Preferences can go anywhere
)
def register():
for cls in classes:
bpy.utils.register_class(cls)
# Attach properties AFTER registering their types
bpy.types.Scene.my_props = bpy.props.PointerProperty(type=MyPropertyGroup)
def unregister():
# Remove properties BEFORE unregistering their types
del bpy.types.Scene.my_props
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
Registration order rules:
PropertyGroupclasses BEFORE any class that references them viaPointerProperty- Parent panels BEFORE sub-panels (if using
bl_parent_id) unregister()MUST reverse the order- Property attachments (
bpy.types.Scene.my_prop = ...) AFTERregister_class() - Property deletions (
del bpy.types.Scene.my_prop) BEFOREunregister_class()
Pattern 4: Class Naming Convention (Enforced)
Blender enforces: {ADDON}_{TYPE}_{name} where {ADDON} is uppercase addon identifier, {TYPE} is a two-letter type code, and {name} is lowercase with underscores.
| Type Code | Base Class | Example | |-----------|-----------|---------| | OT | bpy.types.Operator | MYADDON_OT_do_something | | PT | bpy.types.Panel | MYADDON_PT_main_panel | | MT | bpy.types.Menu | MYADDON_MT_context_menu | | UL | bpy.types.UIList | MYADDON_UL_item_list | | HT | bpy.types.Header | MYADDON_HT_header | | KI | bpy.types.KeyingSetInfo | MYADDON_KI_keying |
For bl_idname on operators: use "addonname.action_name" (all lowercase, dot-separated). Example: bl_idname = "myaddon.export_data".
Pattern 5: Multi-File Addon Structure
my_addon/
├── __init__.py # bl_info (or none for 4.2+) + register/unregister
├── blender_manifest.toml # 4.2+ only
├── operators.py # Operator classes
├── panels.py # Panel classes
├── properties.py # PropertyGroup classes
├── preferences.py # AddonPreferences class
├── utils.py # Helper functions (no bpy.types subclasses)
└── wheels/ # Bundled pip packages (4.2+ only)
└── some_lib.whl
__init__.py with reload support (legacy addon):
bl_info = {
"name": "My Addon",
"blender": (3, 6, 0),
"category": "Object",
"version": (1, 0, 0),
}
# Reload support: REQUIRED for F8 / addon toggle without restart
if "bpy" in locals():
import importlib
importlib.reload(operators)
importlib.reload(panels)
importlib.reload(properties)
else:
from . import operators
from . import panels
from . import properties
import bpy
classes = (
properties.MyPropertyGroup,
operators.MYADDON_OT_example,
panels.MYADDON_PT_main,
)
def register():
for cls in classes:
bpy.utils.register_class(cls)
bpy.types.Scene.my_props = bpy.props.PointerProperty(
type=properties.MyPropertyGroup
)
def unregister():
del bpy.types.Scene.my_props
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
__init__.py for extension (4.2+) — NO bl_info, NO reload boilerplate:
from . import operators, panels, properties
import bpy
classes = (
properties.MyPropertyGroup,
operators.MYADDON_OT_example,
panels.MYADDON_PT_main,
)
def register():
for cls in classes:
bpy.utils.register_class(cls)
bpy.types.Scene.my_props = bpy.props.PointerProperty(
type=properties.MyPropertyGroup
)
def unregister():
del bpy.types.Scene.my_props
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
Pattern 6: AddonPreferences
# Blender 3.x/4.x/5.x
class MyAddonPreferences(bpy.types.AddonPreferences):
bl_idname = __package__ # MUST match addon module name — NEVER hardcode
api_key: bpy.props.StringProperty(
name="API Key",
subtype='PASSWORD',
)
debug_mode: bpy.props.BoolProperty(
name="Debug Mode",
default=False,
)
output_path: bpy.props.StringProperty(
name="Output Path",
subtype='DIR_PATH',
)
def draw(self, context):
layout = self.layout
layout.prop(self, "api_key")
layout.prop(self, "debug_mode")
layout.prop(self, "output_path")
Access preferences at runtime:
# Blender 3.x/4.x/5.x
prefs = bpy.context.preferences.addons[__package__].preferences
value = prefs.debug_mode
Pattern 7: Extension Packaging (4.2+)
Build and validate an extension for distribution:
# Validate manifest
blender --command extension validate blender_manifest.toml
# Build .zip for upload to extensions.blender.org
blender --command extension build --source-dir ./my_addon --output-filepath my_addon.zip
# Install locally for testing
blender --command extension install-file my_addon.zip
Extension guidelines for extensions.blender.org:
- MUST check
bpy.app.online_accessbefore any network request - MUST NOT install pip packages at runtime — bundle in
wheels/ - MUST NOT modify other addons or extensions
- MUST NOT write to the addon installation directory
- MUST use relative imports only — no
sys.pathmanipulation - MUST load all modules as sub-modules within package namespace
- SHOULD follow PEP 8
Pattern 8: User-Writable Storage (4.2+)
# Blender 4.2+: get a writable directory for addon data
import os
user_dir = bpy.utils.extension_path_user(__package__, create=True)
config_path = os.path.join(user_dir, "config.json")
Common Operations
Detect Addon vs Extension Context
# Blender 3.x/4.x/5.x: check if running as extension
import os
def is_extension():
"""Check if this addon is installed as a Blender 4.2+ extension."""
manifest = os.path.join(os.path.dirname(__file__), "blender_manifest.toml")
return os.path.exists(manifest)
Version-Conditional Registration
# Support both legacy and extension systems
def register():
for cls in classes:
bpy.utils.register_class(cls)
# Handler registration
bpy.app.handlers.load_post.append(on_load)
def unregister():
bpy.app.handlers.load_post.remove(on_load)
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
# For both: if __name__ == "__main__" block for script testing
if __name__ == "__main__":
register()
Keymap Registration in Addon
addon_keymaps = []
def register():
for cls in classes:
bpy.utils.register_class(cls)
# Add keymap entry
wm = bpy.context.window_manager
kc = wm.keyconfigs.addon
if kc:
km = kc.keymaps.new(name='3D View', space_type='VIEW_3D')
kmi = km.keymap_items.new("myaddon.do_something", 'D', 'PRESS', ctrl=True)
addon_keymaps.append((km, kmi))
def unregister():
# Remove keymaps BEFORE unregistering classes
for km, kmi in addon_keymaps:
km.keymap_items.remove(kmi)
addon_keymaps.clear()
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
Migration: blinfo to blendermanifest.toml
| bl_info field | Manifest equivalent | Notes | |---------------|-------------------|-------| | "name" | name | Same | | "author" | maintainer | Add email: "Name " | | "version" | version | Tuple → string: (1,0,0) → "1.0.0" | | "blender" | blender_version_min | Tuple → string, min "4.2.0" | | "description" | tagline | No ending punctuation | | "category" | tags | String → array: "Object" → ["Object"] | | "doc_url" | website | Same | | "warning" | (removed) | No equivalent in manifest | | "tracker_url" | (removed) | No equivalent in manifest | | (new) | id | Lowercase identifier, no spaces | | (new) | schema_version | Always "1.0.0" | | (new) | license | SPDX format: ["SPDX:GPL-3.0-or-later"] | | (new) | type | "add-on" or "theme" | | (new) | maintainer | "Name " format |
Reference Links
- [references/methods.md](references/methods.md) — Complete API signatures for addon registration, preferences, and extension utilities
- [references/examples.md](references/examples.md) — Working code examples for single-file, multi-file, and extension addons
- [references/anti-patterns.md](references/anti-patterns.md) — What NOT to do when building Blender addons
Official Sources
- https://docs.blender.org/api/current/info_overview.html
- https://docs.blender.org/manual/en/latest/advanced/scripting/addon_tutorial.html
- https://developer.blender.org/docs/handbook/extensions/
- https://developer.blender.org/docs/handbook/extensions/addon_guidelines/
- https://developer.blender.org/docs/features/extensions/schema/1.0.0/
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: Impertio-Studio
- Source: Impertio-Studio/Blender-Bonsai-ifcOpenshell-Sverchok-Claude-Skill-Package
- License: MIT
- Homepage: https://github.com/OpenAEC-Foundation
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.