---
name: maya-scripting
description: |-
  Bootstrap stage — escape hatch for Maya work that has no packaged skill yet.
  Agents should prefer search_skills / dcc_capability_manifest → load_skill →
  typed tools (inputSchema + annotations) from domain skills; use execute_python
  or execute_mel only when no skill matches, for bulk in-process loops, or for
  API introspection. Includes typed plug-in lifecycle tools and introspection
  tools so an agent can discover flags and method signatures without leaving the
  loop.
license: MIT
allowed-tools: Bash Read
metadata:
  dcc-mcp:
    dcc: maya
    layer: thin-harness
    stage: bootstrap
    version: 2.2.0
    tags:
    - maya
    - scripting
    - mel
    - python
    - plugins
    - introspect
    search-hint: |-
      last resort after load_skill, no-matching-tool, bulk loop in maya,
      MEL Python escape hatch, load plugin, unload plugin, pluginInfo,
      loadPlugin, unloadPlugin, inspect api, cmds help, signature,
      flag list, introspect only
    tools: tools.yaml
    groups: groups.yaml
    recipes: references/RECIPES.md
    introspection: references/INTROSPECTION.md
---
# maya-scripting (Bootstrap stage)

**Skills-first default:** `search_skills` / `dcc_capability_manifest` → `load_skill("<domain>")` → call the concrete tool from `tools.yaml` (validated `inputSchema`, safety hints). Reserve `execute_python` / `execute_mel` for **escape hatches**: no matching skill, intentional bulk work inside one Maya payload, OpenMaya-only gaps, or introspection-only passes.

Studios can hard-block arbitrary execution with `DCC_MCP_MAYA_DISABLE_EXECUTE_PYTHON=1`, `DCC_MCP_MAYA_DISABLE_EXECUTE_MEL=1`, or `DCC_MCP_MAYA_DISABLE_ARBITRARY_SCRIPT=1` (blocks both).

## Decision tree

```
Intent matches a Pipeline-stage skill (shot-export, render-farm, pipeline)?
  → load that skill instead.
Intent matches an Interchange skill (FBX/OBJ/preset import or export)?
  → load maya-geometry / maya-export-preset.
Intent matches an Authoring skill (mesh, uv, material, rig, anim, light)?
  → load that domain skill — its tools.yaml has full inputSchema and safety hints.
Only need cmds / OpenMaya discovery (no mutation)?
  → activate introspect group; prefer introspect_* over execute_python.
Need to load, unload, or inspect a Maya plug-in?
  → call list_plugins / load_plugin / unload_plugin instead of raw MEL/Python.
Genuine gap, bulk loop, or one-off not worth a new skill yet?
  → load maya-scripting, read RECIPES.md if helpful, call execute_python / execute_mel.
Unsure of flag name or method signature while authoring a script?
  → activate the introspect group, call introspect_signature / introspect_search.
```

## Concurrency model (agent guidance)

`execute_python` is **thread-safe under bursty load**. Every off-main-thread call is funneled through a single-writer FIFO queue and marshaled to Maya's UI thread with `maya.utils.executeInMainThreadWithResult` — so an agent issuing many concurrent `/v1/call` POSTs gets:

- **Strict serialisation** at the Maya boundary: exactly one user script runs at a time on the UI thread (the only thread that can safely call `cmds.*`, load native plug-ins, mutate the scene graph). No interleaving, no torn state.
- **No drops**: jobs sit in a bounded queue (default depth **64**, configurable via `DCC_MCP_MAYA_EXEC_QUEUE_DEPTH`) until the pump can hand them to Maya.
- **Clean backpressure**: when the queue is full, the call returns a `QueueFullError` envelope with `"back off and retry"` instead of stalling the connection. Wait and retry — do **not** open more concurrent connections to work around it.
- **Single in-flight marshalling call** at a time: avoids thrashing Maya's deferred queue when N agents target the same instance.

Queue depth is visible via `io action=status` (`context.main_thread_queue.{depth,submitted,completed,failed,rejected,pump_alive}`). Use this for diagnostics, not for tight scheduling — by the time you read it the value is already stale.

Opt-out for callers that have already promoted themselves onto the main thread (rare; only for advanced workflow scripts): pass `inplace=true` to skip the queue entirely.

## Bulk operations (agent guidance)

When the user wants **many similar steps** inside Maya (e.g. 10 spheres → 10 FBX files with a naming convention, batch import folders, mass rename):

1. **Prefer** loading the owning skill once (`load_skill("maya-geometry")`, `maya-primitives`, …) and either calling typed tools **or** mirroring the same flags after reading that skill's `SKILL.md` contract.
2. When MCP round-trip cost dominates and everything is homogeneous, **one** `execute_python` payload that loops locally and returns structured `context` (`written_files`, `failed`, `roots`) is acceptable — it is still weaker than schema tools for validation and crash isolation.
3. **Do not** emit dozens of sequential tool calls for the same mechanical pattern unless each step must be individually schema-validated or audited.
4. **Gateway clients** (dcc-mcp-gateway): prefer the gateway **REST** API
   (`POST /v1/search`, `/v1/describe`, `/v1/call`, `/v1/call_batch` on the
   gateway port). Use MCP `call_tool` / per-Maya `/mcp` only when your host
   requires Streamable HTTP. Use `call_tools` / `POST /v1/call_batch` for up
   to **25** *distinct* tool steps; for homogeneous bulk I/O, still collapse
   to **one** backend script when possible (see repo `examples/workflows/maya_bulk_rbd_fbx.md`).
5. **Long loops**: import `check_maya_cancelled` from `dcc_mcp_maya` and poll inside the loop so cancellation does not wedge the session.
6. For creation loops, prefer the injected helpers
   `maya_created_object_context(cmds.polyCube(), "name")` and
   `maya_node_summary("name")` over keeping raw `maya.cmds` return lists.
   They preserve `object_name` while adding long DAG paths, shape names,
   transform, and bounding-box data when Maya exposes it.

## Why this skill is special

- It is the **only** stage = `bootstrap` skill. The minimal-mode default
  loads it eagerly so an agent always has a reachable escape hatch.
- The plug-in **persistently disables Maya AutoSave** at load time so an
  unsaved-scene AutoSave timer cannot pop a modal dialog and block the
  dispatcher between jobs. Restored on plug-in unload. Opt out via
  `DCC_MCP_MAYA_DISABLE_AUTOSAVE=0` if you need AutoSave to keep firing.
- Dialog `cmds.*` entries (`confirmDialog`, `promptDialog`, `fileDialog`,
  `fileDialog2`, `layoutDialog`) are **not** monkey-patched. The previous
  wrapper corrupted Maya's internal state on `cmds.file(new=True)`,
  Arnold renderer switch, and other paths where the engine consumes the
  same `cmds.*` entries internally (removed 2026-05-16).
- `cmds.file` itself is guarded during MCP execution. Dirty-scene
  `new/open/save` calls that would open a modal prompt return a structured
  error instead; import/reference calls get `prompt=False` when the caller did
  not choose a prompt policy. Pass `force=True` only when discarding unsaved
  changes is intentional.
- The previous `mcp_safe_session()` context-manager wrapper has been
  removed entirely. Dispatched jobs run with a bare `exec` against the
  same `maya.cmds` surface a user types into the Script Editor, with only the
  narrow `cmds.file` prompt guard described above.

## Dynamics / solver safety (host crashes)

Legacy **`cmds.rigidBody` + `cmds.gravity`** snippets from older tutorials are a
frequent source of **fatal Maya exits** when driven from MCP / batch contexts
(the solver stack is not fully re-entrant with arbitrary evaluation order).
Prefer **Bullet / Bifrost** workflows documented for your Maya generation, bake
to keys for FBX, or wrap experiments in a **local scene file** you can reopen
after a crash. If you only need motion for export, **keyframed or cached**
results are more stable than live rigid iteration inside one `execute_python`
payload.

## Groups

- **core** (`default_active: true`) — `execute_mel`, `execute_python`,
  `list_plugins`, `load_plugin`, `unload_plugin`, `list_mel_procedures`,
  `get_script_node`. Always loaded.
- **introspect** (`default_active: false`) — API introspection tools.
  Load with `activate_group("introspect")`. See `references/INTROSPECTION.md`.

## Scripts

- `execute_python` — Inline Python (isolated namespace) or `file_path` / `script_path` to a `.py` (runs in `__main__` with `__file__` / `this_root`, Maya-native)
- `execute_mel` — Inline MEL or `file_path` to a `.mel` (MEL `source` via `mel.eval`)
- `list_plugins` — List loaded Maya plug-ins and optional `pluginInfo` metadata
- `load_plugin` — Load a Maya plug-in with `cmds.loadPlugin` and optional autoload update
- `unload_plugin` — Unload a Maya plug-in with `cmds.unloadPlugin` and optional autoload clearing
- `list_mel_procedures` — List available MEL global procedures
- `get_script_node` — Inspect a Maya scriptNode's content
- `introspect_list_module` — List public names in `maya.cmds` / OpenMaya (paginated)
- `introspect_signature` — Return flag list / method signature for a Maya API name
- `introspect_search` — Case-insensitive search over module names and flag names
- `introspect_eval` — Evaluate a read-only Python expression inside Maya (main-thread)
