Skip to content

Contributing a Custom Skill ​

dcc-mcp-maya is designed to be extended. You can ship your own skill packages alongside the built-ins — without touching the core package.

Cross-repo maintenance checklist (frontmatter, I/O copy, reference wiring): skill-maintenance.md in dcc-mcp-core. Bundled reference skills live under dcc-mcp-core/python/dcc_mcp_core/skills/. Before opening a PR, run python tools/lint_skills.py from the dcc-mcp-maya repo root on your skill tree.

Skill Package Layout ​

A skill is a directory with the following structure:

my-skill/
├── SKILL.md          ← required skill manifest
└── scripts/
    ├── do_something.py
    └── get_info.py

Each .py file in scripts/ becomes one MCP tool automatically.

Writing an Action Script ​

Every script must expose a top-level function whose name matches the file stem:

python
# scripts/create_locator_grid.py
"""Create a grid of locators at evenly-spaced positions."""

from __future__ import annotations
from typing import Optional


def create_locator_grid(
    rows: int = 3,
    cols: int = 3,
    spacing: float = 2.0,
    name_prefix: Optional[str] = "loc",
) -> dict:
    """Create a grid of locators.

    Args:
        rows: Number of rows.
        cols: Number of columns.
        spacing: Distance between locators in scene units.
        name_prefix: Prefix for locator names.

    Returns:
        ToolResult dict with ``context.locators`` list.
    """
    # Always lazy-import maya.cmds to allow skill discovery outside Maya
    import maya.cmds as cmds
    from dcc_mcp_core import success_result, error_result

    try:
        locators = []
        for r in range(rows):
            for c in range(cols):
                loc = cmds.spaceLocator(
                    name=f"{name_prefix}_{r}_{c}",
                    position=(c * spacing, 0, r * spacing),
                )[0]
                locators.append(loc)

        return success_result(
            f"Created {len(locators)} locators",
            locators=locators,
            count=len(locators),
        ).to_dict()
    except Exception as exc:
        return error_result("Failed to create locator grid", str(exc)).to_dict()


def main(**kwargs):
    return create_locator_grid(**kwargs)

Key rules ​

RuleWhy
Lazy-import maya.cmds inside the functionSkills are discovered without a running Maya
Return a dict (use dcc_mcp_core.success_result / error_result)Consistent MCP response format
Function name = file stemAuto-registration relies on this convention
Module docstring = MCP tool descriptionShown to the AI in the tool list

Tool Manifest Thread Affinity ​

Every tools.yaml action must make its thread contract explicit:

yaml
- name: create_locator_grid
  execution: sync
  affinity: main

Use affinity: main for anything that imports maya.cmds or touches scene state. Use affinity: any only for pure Python or filesystem helpers that never touch Maya. Upstream core treats affinity as the thread-affinity contract and enables structured thread-affinity enforcement automatically when the field is present. Do not also add thread_affinity or enforce_thread_affinity in normal skill manifests; explicit enforcement overrides are reserved for adapter runtime compatibility shims.

Registering Your Skill ​

Option 1 — Environment variable ​

Point DCC_MCP_MAYA_SKILL_PATHS (or DCC_MCP_SKILL_PATHS) at the parent directory of your skill folders:

my-skills/
├── my-skill/
│   └── scripts/
└── another-skill/
    └── scripts/
bash
# Windows PowerShell
$env:DCC_MCP_MAYA_SKILL_PATHS = "C:\Users\me\my-skills"
python
# Or set it before starting the server
import os
os.environ["DCC_MCP_MAYA_SKILL_PATHS"] = r"C:\Users\me\my-skills"

import dcc_mcp_maya
dcc_mcp_maya.start_server()

Option 2 — Pass paths directly ​

python
import dcc_mcp_maya

dcc_mcp_maya.start_server(
    extra_skill_paths=[r"C:\Users\me\my-skills"]
)

Option 3 — Place inside the built-in skills directory ​

For permanent additions, copy your skill folder into the installed package:

<site-packages>/dcc_mcp_maya/skills/my-skill/

Note: This approach will be lost on package upgrades. Prefer env vars for development and option 2 for production deployments.

Naming Convention ​

The MCP tool name is derived automatically:

{skill_dir_name.replace("-", "_")}__{script_file_stem}

Examples:

Skill dirScript fileMCP tool name
my-skilldo_something.pymy_skill__do_something
studio-pipelinepublish_asset.pystudio_pipeline__publish_asset

Testing Your Action Locally ​

You can run an action script directly from a Python shell without Maya to verify its structure:

python
import importlib.util, pathlib

spec = importlib.util.spec_from_file_location(
    "create_locator_grid",
    pathlib.Path("my-skill/scripts/create_locator_grid.py"),
)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)

# Check the function exists and signature is correct
import inspect
print(inspect.signature(mod.create_locator_grid))

For a full end-to-end test, use the dcc_execute_action MCP tool from your AI host after registering the skill.

Checklist Before Sharing ​

  • [ ] Module-level docstring is concise and descriptive (shown to the AI)
  • [ ] All parameters have type annotations and docstring entries
  • [ ] maya.cmds is imported lazily inside the function
  • [ ] Returns success_result(…).to_dict() on success
  • [ ] Returns error_result(…).to_dict() on known error conditions
  • [ ] main(**kwargs) wrapper is present
  • [ ] Tested with vitepress build if adding docs

Released under the MIT License.