from __future__ import annotations
import json
from textwrap import dedent
from typing import Any
from fastmcp import FastMCP
def _json_block(value: Any) -> str:
return json.dumps(value, indent=2, sort_keys=True)
def register_usage_resources(mcp: FastMCP) -> None:
"""
Register documentation-style MCP Resources for BlenderMCP.
These are intentionally read-only, side-effect-free resources that help clients
discover how to use the server and copy/paste working examples.
"""
@mcp.resource(
"blender://help",
title="BlenderMCP Help",
description="Start here: how to set up and use BlenderMCP.",
mime_type="text/markdown",
tags={"help", "docs"},
)
def help_index() -> str:
return dedent(
"""
# BlenderMCP Help
This server exposes Blender controls primarily as **Tools** (e.g. `get_scene_info`, `execute_ops`, etc).
These **Resources** are read-only documentation to help you get started.
## Quick links
- `blender://help/quickstart`
- `blender://help/tools`
- `blender://help/troubleshooting`
- `blender://help/security`
- `blender://examples/ops/basic`
- `blender://examples/ops/modifiers`
- `blender://examples/workflows/asset_strategy`
## Recommended usage pattern
- Call `get_scene_info()` first to ground the model in the current state.
- Call `get_capabilities()` if you’re unsure what’s supported (integrations, DSL version, formats).
- Prefer `execute_ops()` for structured/allowlisted changes; use `execute_blender_code()` only when needed.
"""
).strip() + "\n"
@mcp.resource(
"blender://help/quickstart",
title="BlenderMCP Quickstart",
description="Minimal steps to connect Blender + MCP client and run a first command.",
mime_type="text/markdown",
tags={"help", "docs"},
)
def help_quickstart() -> str:
return dedent(
"""
# Quickstart
## 1) Install & enable the Blender Addon
- In Blender: **Edit → Preferences → Add-ons → Install…**
- Choose the repo’s `addon.py`
- Enable **Interface: Blender MCP**
## 2) Start the addon’s socket server
- In Blender’s 3D View sidebar (press **N**): open the **BlenderMCP** tab
- Click **Connect to Claude** (name may vary by version)
## 3) Configure your MCP client
- If you use `uvx blender-mcp` as your server command, the MCP client will start the server for you.
- Make sure you only run **one** instance of the MCP server at a time.
## 4) Run a safe first check
- Call `get_scene_info()`
- Then create a primitive via `execute_ops()` (see `blender://examples/ops/basic`)
"""
).strip() + "\n"
@mcp.resource(
"blender://help/tools",
title="Tool Usage Tips",
description="High-signal tips for using BlenderMCP tools effectively.",
mime_type="text/markdown",
tags={"help", "docs"},
)
def help_tools() -> str:
return dedent(
"""
# Tool usage tips
## Discovery
- Use `get_capabilities()` to learn which integrations are enabled and what formats/ops are supported.
## Prefer the DSL for edits
- Use `execute_ops({dsl_version, transaction, dry_run, ops:[...]})` for structured scene edits.
- Use `transaction="atomic"` (default) when you want Blender undo-based rollback behavior.
- Use `dry_run=true` to validate and normalize before running.
## When to use Python execution
- Use `execute_blender_code()` only when an operation is not available as a tool/DSL op.
- Keep code small and step-by-step; prefer multiple small calls over one huge script.
## Visual grounding
- Use `get_viewport_screenshot()` when the model needs visual context.
"""
).strip() + "\n"
@mcp.resource(
"blender://help/troubleshooting",
title="Troubleshooting",
description="Common setup and runtime issues and what to try next.",
mime_type="text/markdown",
tags={"help", "docs"},
)
def help_troubleshooting() -> str:
return dedent(
"""
# Troubleshooting
## Connection errors
- Ensure the Blender addon is enabled and its server is running.
- Ensure the MCP server’s `BLENDER_HOST` / `BLENDER_PORT` match the addon’s host/port.
- If you set an auth token in Blender’s addon preferences, set `BLENDER_AUTH_TOKEN` for the MCP server too.
## Timeouts
- Break large requests into smaller steps.
- Prefer `execute_ops()` over large `execute_blender_code()` scripts.
## Integrations (PolyHaven / Sketchfab / Hyper3D / Hunyuan3D)
- Use the `get_*_status()` tools to confirm what’s enabled.
- If an integration is disabled in the Blender sidebar UI, enable it there and retry.
"""
).strip() + "\n"
@mcp.resource(
"blender://help/security",
title="Security Notes",
description="Key safety considerations when using BlenderMCP.",
mime_type="text/markdown",
tags={"help", "docs", "security"},
)
def help_security() -> str:
return dedent(
"""
# Security notes
## Arbitrary code execution
- `execute_blender_code()` runs arbitrary Python in Blender. Treat it as powerful and potentially dangerous.
- Save your work before running complex code.
## Network + assets
- Downloading assets (PolyHaven/Sketchfab) may fetch content from the internet depending on your setup.
## Auth token
- If you configure a shared secret in the Blender addon, also set `BLENDER_AUTH_TOKEN` in the MCP server environment.
- Do not log or paste secrets into chat transcripts.
"""
).strip() + "\n"
@mcp.resource(
"blender://examples/ops/basic",
title="Example: execute_ops basic",
description="Create a cube, make a material, assign it (DSL v1).",
mime_type="application/json",
tags={"examples", "dsl"},
)
def example_ops_basic() -> str:
return _json_block(
{
"dsl_version": "1",
"transaction": "atomic",
"dry_run": False,
"ops": [
{"type": "create_primitive", "primitive": "cube", "name": "Cube_A", "size": 2.0, "location": [0, 0, 1]},
{"type": "ensure_material", "name": "RedPaint", "model": "pbr"},
{"type": "set_material_params", "material": "RedPaint", "params": {"baseColor": [1, 0, 0, 1], "roughness": 0.4}},
{"type": "assign_material", "object": "Cube_A", "material": "RedPaint", "slot": 0},
],
}
) + "\n"
@mcp.resource(
"blender://examples/ops/modifiers",
title="Example: execute_ops modifiers",
description="Add and apply a bevel modifier (DSL v1).",
mime_type="application/json",
tags={"examples", "dsl"},
)
def example_ops_modifiers() -> str:
return _json_block(
{
"dsl_version": "1",
"transaction": "atomic",
"dry_run": False,
"ops": [
{"type": "add_modifier", "name": "Cube_A", "modifier_type": "bevel", "params": {"width": 0.02, "segments": 3}},
{"type": "apply_modifier", "name": "Cube_A", "modifier_name": "BEVEL"},
],
}
) + "\n"
@mcp.resource(
"blender://examples/workflows/asset_strategy",
title="Workflow: Asset creation strategy",
description="Recommended step-by-step workflow when creating assets/scenes.",
mime_type="text/markdown",
tags={"examples", "workflow"},
)
def example_workflow_asset_strategy() -> str:
return dedent(
"""
# Workflow: Asset creation strategy
This server already includes a prompt named `asset_creation_strategy()` that describes a recommended flow.
Practical usage:
- Call `get_scene_info()` first.
- Check integrations:
- `get_polyhaven_status()`
- `get_sketchfab_status()`
- `get_hyper3d_status()`
- `get_hunyuan3d_status()`
- Prefer:
- PolyHaven for textures/HDRIs and some models
- Sketchfab for realistic downloadable models
- Hyper3D/Hunyuan3D for “single item” generation when libraries fail
- Use `execute_ops()` for deterministic scene edits.
"""
).strip() + "\n"