--- name: 3dsmax-mcp description: Tool choices, workflows, and MAXScript pitfalls for controlling 3ds Max via MCP. --- # 3ds Max MCP — Agent Guide Each tool's description documents its parameters, modes and limits. This guide covers what no single tool can: routing, tool choice, cross-tool workflows, safety rules and MAXScript pitfalls. ## Tool Profile Routing - **Full/core:** operational tools such as `query_scene` and `create_object` are advertised directly; call them by name. - **Progressive:** if only `list_toolsets`, `describe_toolset` and `call_tool` are advertised, never call an operational name as a top-level tool. Pick a capability with `list_toolsets`, load only that group with `describe_toolset`, then invoke it through `call_tool(name=..., arguments=...)`. If the exact tool and arguments are already known, `call_tool` can dispatch directly. ## Principles - Match the user's request. Do not run setup, discovery or scene analysis by habit, and do not call `get_bridge_status` or `get_session_context` as a preamble. - Prefer a dedicated tool over raw MAXScript whenever one clearly matches. - Do not render unless the user asks. Viewport capture is fine when visual proof helps. - Verify meaningful edits with `query_scene(action="delta")`, re-inspection or a capture. - Keep mutating calls sequential. Concurrent writes interleave undo transactions: a crash, then persistent corruption such as phantom successes and wrong handles. - Multiple Max instances: the first native connection stays bound to that Max and never silently switches. Use `list_max_instances`, `select_max_instance(pid)`, `get_selected_max_instance` and `release_max_instance`. `MCP_MAX_PIPE` or `MCP_MAX_PID` pins the startup target (the pipe takes precedence); release also clears it. ## Tool Choice - **Scene reads:** `query_scene` (overview/filter/class/property/selection/delta), `get_hierarchy`, `get_instances`, `get_dependencies`, `walk_references`. `resolve_node_refs` gives canonical handle/name/path identity. `scene_qa` checks naming, transforms, hierarchy and timeline only; it never judges meshes or visuals, and its `fix` repairs naming only (preview with `dry_run=true` unless approved). - **Inspection:** `inspect_object`, `inspect_properties`, `analyze_node_orientation` (before rig, vehicle or camera transforms). For unfamiliar plugin APIs: `introspect_class`, `introspect_instance`, `discover_plugin_classes`, `map_class_relationships`, `inspect_plugin_class`. Arnold materials such as `ai_standard_surface` may be missing from class discovery; use `inspect_plugin_class` or `introspect_osl`. - **Edits:** object, modifier, material, controller, organization (`manage_layers`, `manage_groups`, `manage_selection_sets`) and viewport tools. Use `scene_patch` for a preflighted batch of renames, relative transforms, flags or parenting committed as one undo step. - **Fallbacks:** `execute_maxscript`, or `execute_python` (pymxs, needs safe mode off), only when no dedicated tool exists. - **Scene files:** `manage_scene` (hold/fetch/reset/save/info). `inspect_max_file`, `search_max_files`, `merge_from_file` and `batch_file_info` work without loading a scene. - **Interactive sessions:** `watch_scene` follows the user's actions. ## Workflows ### Identity and tokens - Node handles are stable only within the loaded scene. Cross-check cached handles with `name` or `path`, and refresh after a reset or load. - Pass `expected_scene_seq` from `resolve_node_refs` to `scene_patch` when stale targeting matters; selection changes do not invalidate it. - Guarded edits take the token from a fresh inspection: `expected_mesh`, `expected_view`, `curve_token`, `expected_style`, `expected_controller`, plugin schema/state tokens, `expected_dialog`. A refused token means inspect again, never bypass the guard. ### Modeling - Polygon edits: `inspect_mesh` or an AGENT VIEWPORT capture → `pick_component` with the capture's `view_token` as `expected_view` → `mesh_edit(expected_mesh=...)` → verify with `inspect_mesh`, `geometry_qa` or a capture. - Construction: `create_mesh` for computed vertex/face arrays; `loft_mesh` for parameterized section lofts; `curve_model`, `inspect_curve` and `edit_curve` for curve-driven forms (read [curve-construction.md](curve-construction.md) first); `draw_spline` plus Lathe/Extrude/Bevel_Profile/Sweep through `add_modifier`, refined with spline knot edits or `edit_vertices` conform. - Edits act on the editable base cage and preserve modifiers above it. Collapsing (`convert=true`, `collapse_modifier_stack`) happens only when requested. - `boolean_operation`: prefer inline `cutters` (named, atomic, no scene litter). Non-live operands are consumed, so never consume a node other tools still reference by name. - After placing parts that should sit on or join each other, run `contact_check` and judge reported depth against the design intent. ### Materials - Create and assign: `assign_material`, `create_material_from_textures`, `smart_import`, `palette_laydown`. They default to OpenPBR; pass `material_class` for another renderer. Share an existing material with `assign_material(names=[...], source_name=...)`. - Edit: `set_material_property`, `set_material_properties`, `set_sub_material`, `create_texture_map`, `set_texture_map_properties`. Switch pipelines with `create_shell_material`, `replace_material` and `batch_replace_materials`. - Audit: `get_material_slots` (prefer `slot_scope="map"`), `get_materials`, and `material_roles` (follow `next_offset`; check `complete` and `truncated`). - OSL: `write_osl_shader`, then `introspect_osl` before wiring. The shader name must match `shader_name`; OSLMap lowercases parameter names. - `material_class` is the material's own class name: `PhysicalMaterial`, not `Physical` (the camera). ### Lighting and plugin settings - `lighting_capabilities` → `create_lights` → `inspect_lights` → `edit_lights`. Never infer integer enum meanings. RGB is linear in the rendering color space, Kelvin is explicit, and EXR/HDR inputs get no extra gamma. - Other plugin settings: `inspect_plugin_class` or `inspect_plugin_instance` with `schema_version=2`, then `plugin_patch` with the returned schema and state tokens. Shared or animated resources need deliberate handling. ### Viewport and capture - Open `agent_viewport(action="open")` before inspection captures. Navigation and captures then default to it and never redirect into the user's view; `source="active"` targets the user's view explicitly. Release and reopen after a scene load. - `set_viewport` aims by eye and target without creating a camera. Use `capture_viewport`, `capture_multi_view` or `capture_screen(enabled=True)` for stills and frame-buffer crops. - Start interactive previews (`agent_viewport(action="render")`) and production renders (`render_automations`) only on request. A capture never certifies convergence or finished denoising. ### Animation and controllers - Script controllers: `script_controller` inspect → validate → apply with `expected_controller`. A controller that returns zero at every frame has failed; inspect it with `sample_frames`. - Other controllers: `assign_controller`, `inspect_controller`, `inspect_track_view`, `set_controller_props`, `add_controller_target`. Pass `list_wireable_params` paths, including their `[#Parameters]` levels, to `wire_params` unchanged; `get_wired_params` and `unwire_params` read and remove wiring. - `keyframe_tracks`: `timeline` reads or sets frame rate, current frame and range. `list` inspects keys; parent `numKeys` is often 0 because keys live on Bezier Float sub-controllers. Retimes need `time`/`times` or both `from_time` and `to_time`. `bake` and `resample` skip list, constraint, expression, script and motion-capture controllers. Use `loop`, or `match` with `order=hierarchy`, for parented rigs. `tracks` takes exact tokens only: `all`, `position`/`pos`, `rotation`/`rot`, `scale`/`scl`, `transform`/`tm`. - On animated objects, edit keys with `keyframe_tracks`; `transform_object` rewrites keys at the current frame. ### Plugins and assets - tyFlow graph work: read [tyflow-graphs.md](tyflow-graphs.md) completely before acting. - RailClone (7.3.5+): `get_railclone_style` → edit the XML → `set_railclone_style(expected_style=...)` → verify with `get_railclone_output`. Read [railclone.md](railclone.md) first. - Data Channel and Max Creation Graph: read [procedural-graphs.md](procedural-graphs.md) completely before acting. - Forest Pack: `scatter_forest_pack`, then hide the source meshes. - Cosmos: `cosmos_search` → `cosmos_download` → `cosmos_import`. Repeating a completed import adds another instance. - Plugin surfaces: `discover_plugin_surface`, `get_plugin_manifest`, and `resource://3dsmax-mcp/plugins/{name}/manifest|guide|recipes|gotchas`. ## Dialogs - `execute_maxscript` and tool scripts run in Max's quiet mode: prompts such as `queryBox`, overwrite or missing-file warnings silently take their default answer. Pass `execute_maxscript(quiet=False)` when the user wants to be asked. - A call that opens or waits behind a modal dialog returns `BLOCKED_BY_DIALOG` with its title, text and buttons. The call keeps running inside Max: never repeat it. Other replies warn while any dialog is open. - Read and answer dialogs with `max_dialogs`. `respond` presses a button by label or index and returns the interrupted call's result. - Choose the button yourself only when the user asked you to proceed without confirmation and the task determines the answer. Otherwise show the user the dialog and ask. Always ask before save, overwrite, discard, Fetch, Reset or licensing choices. - A MAXScript error box during a call fails that call with `MAX_DIALOG_ERROR`. Callback errors appear just after the triggering call returns; the next reply warns, so read the error and acknowledge it. ## Results and Errors - Replies are a `ToolEnvelope` dict (`ok`/`result`/`error`/`hint`), not a JSON string. Tool-authored hints win over automatic ones; `hint.suggested_tools` may list alternatives. - Classify raw structured errors by `error`, `code` or `status=error|failed`, never by `message` alone. - `USER_BUSY`: Max has an open undo operation and the write was rejected before any change. Continue read-only and retry after it finishes; never bypass it with MAXScript. ## execute_maxscript - Use it only when no dedicated tool exists: unsupported controller operations, render or environment settings, one-off scripted operations. - `code` is JSON-unescaped once before MAXScript parses it. A `BAD_PARAM` parse error without a line number almost always means escaping corruption, not bad logic: - Use forward slashes in path literals (`"C:/assets/tex/"`) or derive paths at runtime; `\"` and `\U` break literals. - Do not put `\n` or `\t` inside string literals. - Keep the script on one line, with statements separated by `;`. - Debug by shrinking to a known-good core (`try (...) catch (getCurrentException() as string)`) and adding pieces back. ## MAXScript Pitfalls - Always pass `#noPrompt` to `importFile`, e.g. `importFile @"C:/assets/model.fbx" #noPrompt using:FBXIMP`; configure importer options first. - No parentheses with keyword arguments: `Box width:10`, not `Box() width:10`. Box `width` is X, `length` is Y, `height` is Z. - Wrap risky code: `try (...) catch (ex) (ex)`. - `Noise` is a texture map; `Noisemodifier` is the modifier. - `getDir #temp` is Max's temp folder, not the OS temp folder. - Convert .NET strings to MAXScript strings before using string methods. - `snapshotAsMesh` returns a world-space TriMesh: read vertices in `coordsys world` without reapplying `node.objectTransform`, then delete it. Base-cage poly vertices still need the object transform. - Normalize controller and wire display tokens: `[#Z Position]` becomes `[#z_position]`. - `getClassInstances Material` is invalid because Material is not a MAXClass; use `sceneMaterials`. - `getHandleByAnim` returns values like `12345P`; quote them when building JSON. - Open Unwrap UVW with `$Box001.modifiers[#Unwrap_UVW].edit()`, not only the `OpenUnwrapUI` macro. - TCP fallback is opt-in. If viewport interaction stutters while it runs, stop it and use the native bridge. ## MAXScript Reference (bundled) Read the relevant file before writing unfamiliar MAXScript: | File | Covers | |------|--------| | `maxscript-core-syntax.md` | Variables, scope, types, operators, control flow | | `maxscript-common-patterns.md` | Undo/animate blocks, callbacks, file I/O | | `maxscript-3dsmax-objects.md` | Nodes, transforms, hierarchy, properties | | `maxscript-mesh-poly-ops.md` | Sub-object mesh/poly ops | | `maxscript-materials-textures.md` | Materials, texmaps, PBR | | `maxscript-animation-controllers.md` | Controllers, constraints, wire params | | `maxscript-rendering-cameras.md` | Render settings, cameras, environment | | `maxscript-splines-shapes.md` | Splines and shapes | | `maxscript-scripted-plugins.md` | Scripted geometry, modifiers, utilities | | `maxscript-ui-rollouts.md` | Rollout UIs and dialogs |