Skip to content

Custom Skills

Learn how to build custom Skills for DCC applications — from the recommended Skills-First approach to the low-level registry API.

The Skills-First approach discovers SKILL.md packages via environment variables and is the recommended way to build DCC tools:

  • Zero boilerplate — no manual handler registration, tools are auto-discovered
  • Auto-exposed as MCP tools — the skill manager exposes each tool to AI via the MCP protocol
  • Hot-reload — changes to SKILL.md take effect without restart

Step 1: Create a SKILL.md Package

markdown
---
name: maya-geometry
description: Maya geometry creation tools
version: 1.0.0
dcc: maya
tags: [geometry, create]
tools:
  - name: create_sphere
    description: Create a polygon sphere
    input_schema: |
      {
        "type": "object",
        "required": ["radius"],
        "properties": {
          "radius": {"type": "number", "minimum": 0.1},
          "name": {"type": "string"}
        }
      }
scripts:
  - create_sphere.py
---

# Maya Geometry Tools

A toolset for creating and editing geometry in Maya.

Step 2: Implement the Script

create_sphere.py:

python
import maya.cmds as cmds
from dcc_mcp_core import success_result, error_result


def create_sphere(radius: float = 1.0, name: str | None = None):
    try:
        sphere = cmds.polySphere(r=radius, n=name)[0]
        return success_result(
            message=f"Created sphere: {sphere}",
            context={"object_name": sphere, "radius": radius}
        )
    except Exception as e:
        return error_result(str(e))

Step 3: Register via Environment Variable and Start

python
import os
from dcc_mcp_core import create_skill_server

os.environ["DCC_MCP_MAYA_SKILL_PATHS"] = "/path/to/my/skills"

# One line: auto-discovers skills, starts MCP HTTP server
server = create_skill_server("maya", McpHttpConfig(port=8765))

Skills-First is the recommended pattern

All new DCC tools should use SKILL.md packages first. Only fall back to the registry API when you need runtime dynamic control over handler logic.


Low-Level Registry API

When you need programmatic control over handler registration at runtime, use the ToolRegistry + ToolDispatcher API.

Full Example

python
import json
from dcc_mcp_core import ToolRegistry, ToolDispatcher

# 1. Register action metadata with JSON Schema
reg = ToolRegistry()
reg.register(
    name="create_sphere",
    description="Create a polygon sphere in Maya",
    category="geometry",
    tags=["geo", "create", "mesh"],
    dcc="maya",
    version="1.0.0",
    input_schema=json.dumps({
        "type": "object",
        "required": ["radius"],
        "properties": {
            "radius": {
                "type": "number",
                "minimum": 0.1,
                "description": "Sphere radius"
            },
            "segments": {
                "type": "integer",
                "minimum": 4,
                "default": 16,
                "description": "Subdivision segments"
            },
            "name": {
                "type": "string",
                "description": "Optional sphere name"
            }
        }
    }),
)

# 2. Create dispatcher and register handler
dispatcher = ToolDispatcher(reg)

def handle_create_sphere(params):
    radius = params.get("radius", 1.0)
    segments = params.get("segments", 16)
    name = params.get("name")

    # Call Maya API (example using maya.cmds)
    import maya.cmds as cmds
    sphere_name = cmds.polySphere(r=radius, sx=segments, sy=segments, n=name)[0]

    return {
        "created": True,
        "object_name": sphere_name,
        "radius": radius,
        "segments": segments,
    }

dispatcher.register_handler("create_sphere", handle_create_sphere)

# 3. Dispatch using JSON (wire format)
import json
result = dispatcher.dispatch("create_sphere", json.dumps({"radius": 2.0, "segments": 32}))
print(result["output"]["object_name"])  # "pSphere1"

Key Takeaways

  1. Use ToolRegistry.register() — pass name, description, tags, DCC, version, and JSON Schema
  2. Implement a handler function — receives params: dict, returns a result dictionary
  3. Use ToolDispatcher to register handlers — connects an action name to a Python callable
  4. Use JSON Schema for validationToolDispatcher validates JSON input before calling the handler
  5. Dispatch with JSON strings — the wire format uses JSON, not Python dicts

Handler Function Signature

python
def my_handler(params: dict) -> Any:
    """
    Args:
        params: Validated parameters (parsed from JSON input)
    Returns:
        A dictionary to be used as the action result (serializable to JSON)
    """
    pass

Validation with ToolValidator

Validate inputs before dispatching:

python
from dcc_mcp_core import ToolValidator

validator = ToolValidator.from_action_registry(reg, "create_sphere", dcc_name="maya")
ok, errors = validator.validate('{"radius": 1.5}')
if not ok:
    print(f"Validation failed: {errors}")
    # Handle error

Versioned Actions

Use VersionedRegistry for backward compatibility:

python
from dcc_mcp_core import VersionedRegistry

vr = VersionedRegistry()

# v1: basic sphere
vr.register_versioned(
    "create_sphere", dcc="maya", version="1.0.0",
    description="Basic sphere creation",
)

# v2: add subdivision parameter
vr.register_versioned(
    "create_sphere", dcc="maya", version="2.0.0",
    description="Sphere with subdivision control",
)

# Automatically resolve the best version
result = vr.resolve("create_sphere", "maya", "^1.0.0")
print(result["version"])  # "2.0.0"

JSON Schema Tips

  • Use $ref for reusable schemas (ToolValidator does not support this — inline all definitions)
  • "default" fields set defaults when keys are missing from input
  • Use "minimum"/"maximum" for numeric constraints
  • Use "minLength"/"maxLength" for string length constraints
  • Use "enum" to restrict string choices
python
input_schema = json.dumps({
    "type": "object",
    "required": ["radius"],
    "properties": {
        "radius": {
            "type": "number",
            "minimum": 0.1,
            "maximum": 1000.0,
        },
        "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
        },
        "align_to_world": {
            "type": "boolean",
            "default": False,
        }
    }
})

Released under the MIT License.