dcc-mcp-core · diff
"0.20.22" # x-release-please-version to "0.20.23" # x-release-please-version
1 added, 1 removed. Audit A to A.
---
name: dcc-mcp-core
description: "Foundation library for the DCC Model Context Protocol (MCP) ecosystem. Provides Rust-powered action management, skills system, IPC transport, MCP Streamable HTTP server (2025-03-26 spec, with 2025-06-18 and 2025-11-25 awareness), sandbox security, shared memory, screen capture, USD scene support, and telemetry for AI-assisted DCC workflows. Use when working with Maya, Blender, Houdini, 3ds Max, or any DCC MCP integration."
allowed-tools: Bash Read Write Edit
compatibility: "Python 3.7-3.14; Rust 1.95+ required to build from source; zero runtime Python dependencies"
- version: "0.20.22" # x-release-please-version
+ version: "0.20.23" # x-release-please-version
---
# dcc-mcp-core — DCC MCP Ecosystem Foundation
The foundational library enabling AI assistants to interact with Digital Content Creation (DCC) software through the Model Context Protocol (MCP).
## Quick Decision Guide — Use the Right API
| Task | Use this | Not this |
|------|----------|----------|
| Operate a live DCC from an agent | [`dcc-mcp`](https://clawhub.ai/loonghao/skills/dcc-mcp) + `dcc-mcp-cli` | embedding the Python API in the agent |
| Create or modernize a DCC-MCP adapter | [`dcc-mcp-creator`](https://clawhub.ai/loonghao/skills/dcc-mcp-creator) | adapter-local copies of core wiring |
| Create a DCC-specific Skill package | [`dcc-mcp-skills-creator`](https://clawhub.ai/loonghao/skills/dcc-mcp-skills-creator) | a new adapter repository |
| Analyze or report a failed DCC call | `dcc-mcp` recovery flow: `doctor`, failure-filtered `stats`, `dcc_feedback__report`, public-safe issue report | raw unreviewed logs |
| Return action result | `success_result()` / `error_result()` | raw dicts |
| Load skills | `scan_and_load()` → `(skills, skipped)` | manual file scanning |
| One-call MCP server | `create_skill_server("maya", McpHttpConfig(port=8765))` | manual wiring |
| Validate params | `ToolValidator.from_schema_json()` | isinstance checks |
| Connect to DCC | `IpcChannelAdapter.connect(name)` or `SocketServerAdapter(path)` | raw sockets |
| Define MCP tool | `ToolDefinition` + `ToolAnnotations` | raw JSON |
| Serve MCP over HTTP | `McpHttpServer(registry, McpHttpConfig(port=8765))` | raw HTTP server |
| Build DCC adapter | `DccServerOptions.from_env(...)` + `DccServerBase(options=opts)` | legacy 17-parameter constructor |
| Main-thread DCC calls | `HostExecutionBridge` / dispatcher passed via `DccServerOptions` | private `_core` imports |
| Enable skill hot-reload | `DccSkillHotReloader(dcc_name, server)` | custom file watchers |
| Gateway failover | `DccGatewayElection(dcc_name, server)` | manual election logic |
| Write skill scripts | `skill_entry` + `skill_success` / `skill_error` | manual JSON output |
## What This Library Does
| Capability | Description |
|------------|-------------|
| **Action Management** | Register, validate, dispatch, and execute actions with typed inputs/outputs |
| **Skills System** | Zero-code script registration (Python/MEL/Batch/Shell/JS) as MCP tools via `SKILL.md` |
| **Transport Layer** | High-performance IPC via ipckit with DccLink framing (IpcChannelAdapter, SocketServerAdapter) |
| **MCP HTTP Server** | MCP Streamable HTTP (**2025-03-26 spec**) powered by axum/Tokio, runs in background thread |
| **Process Management** | Launch, monitor, auto-recover DCC processes (Maya, Blender, Houdini, etc.) |
| **Sandbox Security** | Policy-based access control, input validation, audit logging |
| **Shared Memory** | LZ4-compressed inter-process data exchange for large scenes |
| **Screen Capture** | Cross-platform DCC viewport capture for visual feedback |
| **USD Support** | Read/write Universal Scene Description for pipeline integration |
| **Telemetry** | Structured tracing and recording for observability |
| **MCP Protocol Types** | Complete Tool/Resource/Prompt schema implementations |
| **DCC Server Base** | Reusable base class for DCC adapters (hot-reload, gateway election, lifecycle) |
| **Gateway Failover** | Automatic gateway election when primary gateway becomes unreachable |
| **Skill Hot-Reload** | File-watching auto-reload for live skill development |
## Installation
For agent-side DCC control, install the published `dcc-mcp` Skill and use its
CLI workflow; the Python package below is for adapters and embedded runtimes:
```bash
openclaw skills install @loonghao/dcc-mcp
# Direct ClawHub CLI:
npx --yes clawhub@0.23.1 install @loonghao/dcc-mcp
```
Use [`dcc-mcp-creator`](https://clawhub.ai/loonghao/skills/dcc-mcp-creator)
only for a complete adapter/runtime, and
[`dcc-mcp-skills-creator`](https://clawhub.ai/loonghao/skills/dcc-mcp-skills-creator)
only for a DCC-specific Skill package.
```bash
pip install dcc-mcp-core
# Python 3.7-3.14, zero runtime dependencies
```
## Local Dependency Maintenance
Use the repository-pinned `vx` toolchain for Rust dependency refreshes and CI
parity:
```bash
vx --version # CI pins loonghao/vx@v0.9.7
vx cargo update
vx cargo tree -d
vx cargo build --workspace --all-targets --timings
```
Review duplicate dependency output before editing manifests, and keep generated
lockfile changes only when they are part of the intended dependency refresh.
## Core Patterns
### Pattern 1: Skills-First — one-call MCP server (recommended)
```python
import os
from dcc_mcp_core import create_skill_server, McpHttpConfig
os.environ["DCC_MCP_MAYA_SKILL_PATHS"] = "/opt/my-skills"
# One call: creates registry + dispatcher + catalog + discovers skills + server
server = create_skill_server("maya", McpHttpConfig(port=8765))
handle = server.start()
print(f"Maya MCP server: {handle.mcp_url()}")
# Agents connect and use on-demand skill discovery:
# → search_tools(query="bevel") or search_skills(query="modeling")
# → get_skill_info(skill_name="maya-bevel") to inspect schemas
# → load_skill("maya-bevel") only when selected
# → tools/call maya_bevel__bevel to execute
# Do not treat the first tools/list page as complete; follow nextCursor if listing.
handle.shutdown()
```
### Pattern 2: Return structured results (always use factories)
```python
from dcc_mcp_core import success_result, error_result, from_exception
# All actions should return ActionResultModel
def my_action(params):
try:
result = do_work(params)
return success_result(
f"Created {result['name']}",
prompt="Object created. You can now modify its properties.",
object_name=result["name"],
)
except Exception as e:
return from_exception(str(e), message="Action failed")
```
### Pattern 3: Validate action inputs
```python
import json
from dcc_mcp_core import ToolValidator, error_result
schema = json.dumps({
"type": "object",
"required": ["name", "radius"],
"properties": {
"name": {"type": "string", "maxLength": 64},
"radius": {"type": "number", "minimum": 0.001},
},
})
validator = ToolValidator.from_schema_json(schema)
ok, errors = validator.validate(json.dumps(params))
if not ok:
return error_result("Invalid parameters", "; ".join(errors))
```
### Pattern 4: Connect to a running DCC via IPC
```python
from dcc_mcp_core import DccLinkFrame, IpcChannelAdapter, success_result, error_result
# Connect to a DCC process via named pipe / Unix domain socket
channel = IpcChannelAdapter.connect("dcc-mcp-maya-12345")
try:
# Send a Call frame and receive the reply
channel.send_frame(DccLinkFrame(msg_type=1, seq=1, body=b'{"method":"execute_python","params":"cmds.sphere()"}'))
reply = channel.recv_frame() # DccLinkFrame
if reply.msg_type == 2: # Reply
return success_result(reply.body.decode())
else:
return error_result("DCC call failed", reply.body.decode())
finally:
channel.shutdown() if hasattr(channel, 'shutdown') else None
```
### Pattern 5: Build a DCC adapter with DccServerBase
```python
from pathlib import Path
from dcc_mcp_core import DccServerBase, DccServerOptions
class BlenderMcpServer(DccServerBase):
def __init__(self, port: int = 8765, **kwargs):
opts = DccServerOptions.from_env(
"blender",
Path(__file__).parent / "skills",
port=port,
**kwargs,
)
super().__init__(options=opts)
def _version_string(self) -> str:
import bpy
return bpy.app.version_string
# All skill methods, hot-reload, gateway are ready:
server = BlenderMcpServer(port=8765)
server.register_builtin_actions()
handle = server.start()
print(f"MCP: {handle.mcp_url()}")
```
### Pattern 6: Watch skills for live reload
```python
from dcc_mcp_core import SkillWatcher
watcher = SkillWatcher(debounce_ms=300)
watcher.watch("/my/dev/skills") # immediate load + start watching
# Get always-up-to-date snapshot
current_skills = watcher.skills() # -> List[SkillMetadata]
```
### Pattern 7: ActionDispatcher with handlers
```python
import json
from dcc_mcp_core import ToolRegistry, ToolDispatcher
reg = ToolRegistry()
reg.register("create_sphere",
input_schema=json.dumps({"type": "object", "required": ["radius"],
"properties": {"radius": {"type": "number", "minimum": 0.0}}}))
dispatcher = ToolDispatcher(reg)
dispatcher.register_handler("create_sphere", lambda params: {"created": True, "r": params["radius"]})
# Introspect handlers
dispatcher.has_handler("create_sphere") # True
dispatcher.handler_count() # 1
dispatcher.handler_names() # ["create_sphere"]
dispatcher.remove_handler("create_sphere") # True
result = dispatcher.dispatch("create_sphere", json.dumps({"radius": 2.0}))
# result == {"action": "create_sphere", "output": {"created": True, "r": 2.0}, "validation_skipped": False}
```
### Pattern 8: DCC main-thread safety
Most DCC applications (Maya, Blender, Houdini) require scene API calls on their **main thread**.
For Python adapters, prefer the public host bridge/dispatcher stack and pass it through `DccServerOptions` before skills are loaded. Low-level `DeferredExecutor` details are covered in `docs/guide/dcc-thread-safety.md`.
```python
from pathlib import Path
from dcc_mcp_core import DccServerBase, DccServerOptions, HostExecutionBridge, InProcessCallableDispatcher
dispatcher = InProcessCallableDispatcher() # replace with the DCC UI-thread dispatcher
bridge = HostExecutionBridge(dispatcher=dispatcher)
opts = DccServerOptions.from_env("maya", Path("skills"), execution_bridge=bridge)
server = DccServerBase(options=opts)
handle = server.start()
```
### Pattern 9: Write skill scripts with skill_entry
```python
from dcc_mcp_core.skill import skill_entry, skill_success, skill_error, skill_exception
@skill_entry
def create_sphere(radius: float = 1.0, name: str = "sphere") -> dict:
import maya.cmds as cmds
obj = cmds.polySphere(r=radius, n=name)[0]
return skill_success(
f"Created sphere '{obj}' with radius {radius}",
prompt="You can now adjust properties or add materials.",
object_name=obj,
radius=radius,
)
```
### Pattern 10: Launch an isolated DCC child
Use child-only environment overrides instead of mutating `os.environ` when
multiple artist and automation sessions share a machine:
```python
from dcc_mcp_core import PyDccLauncher
launcher = PyDccLauncher()
info = launcher.launch(
name="nuke-mcp",
executable="Nuke15.2",
args=["--disable-nuke-frameserver", "project.nk"],
environment={
"NUKE_DISABLE_FRAMESERVER": "1",
"DCC_MCP_NUKE_PORT": "0",
},
working_directory="/projects/solar-system",
)
```
## Creating a Custom Skill (Zero Python Code)
```bash
# 1. Create directory structure
mkdir -p my-tool/scripts/
# 2. Write SKILL.md (name is required, follows agentskills.io spec)
cat > my-tool/SKILL.md << 'EOF'
---
name: my-tool
description: "My custom DCC automation tools. Use when automating scene setup or batch operations."
compatibility: "python>=3.7"
allowed-tools: "python"
metadata:
dcc-mcp:
dcc: maya
version: "1.0.0"
layer: example
tags: ["automation", "custom"]
tools: tools.yaml
---
# My Tool
Automation scripts for Maya workflow optimization.
EOF
# 3. Add the sibling tool declaration referenced by metadata.dcc-mcp.tools
cat > my-tool/tools.yaml << 'YEOF'
tools:
- name: list_selected
description: List selected objects in the Maya scene.
input_schema:
type: object
properties: {}
read_only: true
idempotent: true
source_file: scripts/list_selected.py
YEOF
# 4. Add a script
cat > my-tool/scripts/list_selected.py << 'PYEOF'
#!/usr/bin/env python3
"""List selected objects in the Maya scene."""
import json
result = {"selected": ["pSphere1", "pCube1"], "count": 2}
print(json.dumps(result))
PYEOF
# 5. Use it
export DCC_MCP_SKILL_PATHS="$(pwd)/my-tool"
python -c "
from dcc_mcp_core import scan_and_load
skills, _ = scan_and_load(dcc_name='maya')
print(f'Loaded: {[s.name for s in skills]}')
# Action: my_tool__list_selected
"
```
## Architecture Overview
```
┌─────────────────────────────────────────────────────┐
│ Python Layer │
│ dcc_mcp_core/__init__.py → _core (PyO3 cdyll) │
│ 380+ public symbols re-exported from Rust core │
│ + Pure-Python: DccServerBase, DccServerOptions, │
│ gateway election, hot-reload, factory, helpers │
└──────────────────────┬──────────────────────────────┘
│ PyO3 bindings
┌──────────────────────▼──────────────────────────────┐
│ Rust Workspace (47 members total) │
│ 46 functional crates + workspace-hack │
│ naming → models → actions → skills → protocols │
│ gateway/http-types/http-server/http-py/http │
│ host → transport → process → sandbox → telemetry │
└─────────────────────────────────────────────────────┘
```
## Environment Variables
| Variable | Purpose |
|----------|---------|
| `DCC_MCP_SKILL_PATHS` | Colon/semicolon-separated paths to scan for `SKILL.md` dirs |
| `DCC_MCP_{APP}_SKILL_PATHS` | Per-app skill paths (e.g. `DCC_MCP_MAYA_SKILL_PATHS`) |
| `DCC_MCP_GATEWAY_PORT` | Gateway port for multi-DCC setup |
| `DCC_MCP_REGISTRY_DIR` | Directory for FileRegistry JSON |
| `MCP_LOG_LEVEL` | Log level override (`DEBUG`, `INFO`, `WARN`) |
| `DCC_MCP_IPC_ADDRESS` | IPC endpoint address (auto-set by register_diagnostic_handlers) |
| `DCC_MCP_GATEWAY_PROBE_INTERVAL` | Seconds between gateway health probes (default 1) |
| `DCC_MCP_GATEWAY_PROBE_TIMEOUT` | Timeout per probe in seconds (default 2) |
| `DCC_MCP_GATEWAY_PROBE_FAILURES` | Consecutive failures before election (default 2) |
## Key Files in This Repository
| File | Purpose |
|------|---------|
| `AGENTS.md` | AI agent navigation map — entry point, decision tables, top traps |
| `docs/guide/agents-reference.md` | Detailed agent rules — traps, do/don't, code style, project-specific architecture |
| `llms.txt` | Concise API reference for LLMs |
| `llms-full.txt` | Comprehensive API reference with all examples |
| `python/dcc_mcp_core/__init__.py` | Complete public API (380+ symbols, ground truth for imports) |
| `python/dcc_mcp_core/_core.pyi` | Generated type stubs — authoritative parameter names after a dev/stub build |
| `examples/skills/` | 15 complete skill package examples |
| `tests/` | Python integration tests (executable usage examples) |
## Supported DCC Software
- **Autodesk Maya** — MEL/Python scripting (`dcc: maya`)
- **Blender** — Python API (`dcc: blender`)
- **SideFX Houdini** — HScript/Python (`dcc: houdini`)
- **Autodesk 3ds Max** — MaxScript/Python (`dcc: 3dsmax`)
- **Any DCC** — Generic Python wrapper (`dcc: python`)
## Related Projects
- [dcc-mcp-rpyc](https://github.com/loonghao/dcc-mcp-rpyc) — RPyC bridge for remote DCC operations
- [dcc-mcp-maya](https://github.com/loonghao/dcc-mcp-maya) — Maya MCP server implementation
## MCP Specification Roadmap
The library currently implements **MCP 2025-03-26** (Streamable HTTP). The ecosystem has since released:
| Version | Key Features | Status in dcc-mcp-core |
|---------|-------------|----------------------|
| 2025-03-26 | Streamable HTTP, Tool Annotations, OAuth 2.1 | **Implemented** |
| 2025-06-18 | Structured Tool Output, Elicitation, Resource Links, JSON-RPC batching removed, `MCP-Protocol-Version` header mandatory | Planned |
| 2025-11-25 | Icon metadata, Tasks (experimental), Sampling with tool calls, JSON Schema 2020-12, enhanced OAuth | Planned |
**AI Agents**: Do NOT implement draft features manually. Wait for `dcc-mcp-core` to expose them via `McpHttpServer`. Track progress at the GitHub repository.
## Common Pitfalls
1. `scan_and_load` returns `(List[SkillMetadata], List[str])` — always unpack: `skills, skipped = scan_and_load(...)`
2. Prefer public `HostExecutionBridge` / dispatcher wiring; use `DeferredExecutor` only when following `docs/guide/dcc-thread-safety.md` low-level guidance
3. Register ALL actions before `server.start()` — server reads from registry at startup only
4. Use `IpcChannelAdapter` + `DccLinkFrame` for IPC (v0.14+) — `FramedChannel`/`connect_ipc` were removed in #251
5. `ToolDispatcher(registry)` takes ONE arg — no `validator=` parameter
6. Action naming: `{skill_name.replace('-','_')}__{script_stem}` (double underscore)
7. SKILL.md `name` must match parent directory name (agentskills.io spec)
8. `allowed-tools` in SKILL.md is space-separated string, not a list (agentskills.io spec)
9. `DccServerBase` provides all skill/lifecycle/gateway/hot-reload methods — don't reimplement
10. MCP 2025-06-18 removes JSON-RPC batching — do not implement batch calls manually
11. `MCP-Protocol-Version` header is mandatory in 2025-06-18 — handled by `McpHttpServer` internally