<< All versions
Skill v1.0.1
currentAutomated scan100/100cl0nazepamm/3dsmax-mcp/3dsmax-mcp-dev
+15 new
──Details
PublishedOctober 31, 2026 at 10:18 PM
Content Hashsha256:74c8d8457a90bc1d...
Git SHA497b406d22f3
Bump Typepatch
──Files
Files (1 file, 20.4 KB)
SKILL.md20.4 KBactive
SKILL.md · 165 lines · 20.4 KB
name: 3dsmax-mcp description: Tool choices, workflows, and MAXScript pitfalls for controlling 3ds Max via MCP. metadata: version: "1.0.1"
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_sceneandcreate_objectare advertised directly; call them by name. - Progressive: if only
list_toolsets,describe_toolsetandcall_toolare advertised, never call an operational name as a top-level tool. Pick a capability withlist_toolsets, load only that group withdescribe_toolset, then invoke it throughcall_tool(name=..., arguments=...). If the exact tool and arguments are already known,call_toolcan dispatch directly.
Principles
- Match the user's request. Do not run setup, discovery or scene analysis by habit, and do not call
get_bridge_statusorget_session_contextas 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 while it runs. Use
list_max_instances,select_max_instance(pid),get_selected_max_instanceandrelease_max_instance.MCP_MAX_PIPEorMCP_MAX_PIDpins the startup target (the pipe takes precedence); release also clears it. - Shared selection: the selection belongs to the MCP server process, so sessions that share one server share it, and one session's select moves the others. Pass
expected_pid(every tool takes it) to any call that must reach one particular Max. A mismatch returnsTARGET_MISMATCHwith nothing sent.selection_generationin each reply's transport changes on every select, release or re-resolve. - Default target after a restart: a target the server picked by itself (
target_source"default" or "claim") follows its Max's restart (the Max that started after it stopped answering), and the reply warns. A Max that was already open belongs to another session and is never taken over: until the restart is up, calls fail with retryableTARGET_UNAVAILABLEand nothing is sent;select_max_instanceit to work there. Several new instances:AMBIGUOUS, select one. A claimed instance wins. Explicit and environment targets never move; they fail until you select or release.
Tool Choice
- Scene reads:
query_scene(overview/filter/class/property/selection/delta),get_hierarchy,get_instances,get_dependencies,walk_references.resolve_node_refsgives canonical handle/name/path identity.scene_qachecks naming, transforms, hierarchy and timeline only; it never judges meshes or visuals, and itsfixrepairs naming only (preview withdry_run=trueunless 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 asai_standard_surfacemay be missing from class discovery; useinspect_plugin_classorintrospect_osl. - Edits: object, modifier, material, controller, organization (
manage_layers,manage_groups,manage_selection_sets) and viewport tools. Usescene_patchfor a preflighted batch of renames, relative transforms, flags or parenting committed as one undo step. - Fallbacks:
execute_maxscript, orexecute_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_fileandbatch_file_infowork without loading a scene. - Interactive sessions:
watch_scenefollows the user's actions.
Workflows
Identity and tokens
- Node handles are stable only within the loaded scene. Cross-check cached handles with
nameorpath, and refresh after a reset or load. - Pass
expected_scene_seqfromresolve_node_refstoscene_patchwhen 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_meshor an AGENT VIEWPORT capture →pick_componentwith the capture'sview_tokenasexpected_view→mesh_edit(expected_mesh=...)→ verify withinspect_mesh,geometry_qaor a capture. - Construction:
create_meshfor computed vertex/face arrays;loft_meshfor parameterized section lofts;curve_model,inspect_curveandedit_curvefor curve-driven forms (read curve-construction.md first);draw_splineplus Lathe/Extrude/Bevel_Profile/Sweep throughadd_modifier, refined with spline knot edits oredit_verticesconform. - 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 inlinecutters(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_checkand 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; passmaterial_classfor another renderer. Share an existing material withassign_material(names=[...], source_name=...). - Edit:
set_material_property,set_material_properties,set_sub_material,create_texture_map,set_texture_map_properties. Switch pipelines withcreate_shell_material,replace_materialandbatch_replace_materials. replace_materialappliessource_materialwherevertarget_materialis used.source_frompicks where the source is found, e.g."material_editor"for a re-imported Cosmos copy. Onno_match, check the direction before retrying; onblocked, do not swap the arguments.- Audit:
get_material_slots(preferslot_scope="map"),get_materials, andmaterial_roles(follownext_offset; checkcompleteandtruncated). - OSL:
write_osl_shader, thenintrospect_oslbefore wiring. The shader name must matchshader_name; OSLMap lowercases parameter names. material_classis the material's own class name:PhysicalMaterial, notPhysical(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_classorinspect_plugin_instancewithschema_version=2, thenplugin_patchwith 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_viewportaims by eye and target without creating a camera. Usecapture_viewport,capture_multi_vieworcapture_screen(enabled=True)for stills and frame-buffer crops.capture_multi_viewviews take names,three_quarter,currentor[x, y, z]directions.shadingandedgesapply to that capture only: clay with edges shows topology. For animation, rigs and simulations,framesorframe_countgive one view over time in a single image instead of repeated scrub-and-capture calls.- 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_controllerinspect → validate → apply withexpected_controller. A controller that returns zero at every frame has failed; inspect it withsample_frames. - Other controllers:
assign_controller,inspect_controller,inspect_track_view,set_controller_props,add_controller_target. Passlist_wireable_paramspaths, including their[#Parameters]levels, towire_paramsunchanged;get_wired_paramsandunwire_paramsread and remove wiring. keyframe_tracks:timelinereads or sets frame rate, current frame and range.listinspects keys; parentnumKeysis often 0 because keys live on Bezier Float sub-controllers. Retimes needtime/timesor bothfrom_timeandto_time.bakeandresampleskip list, constraint, expression, script and motion-capture controllers. Useloop, ormatchwithorder=hierarchy, for parented rigs.trackstakes exact tokens only:all,position/pos,rotation/rot,scale/scl,transform/tm.- On animated objects, edit keys with
keyframe_tracks;transform_objectrewrites keys at the current frame.
Plugins and assets
- tyFlow graph work: read tyflow-graphs.md completely before acting.
- RailClone (7.3.5+):
get_railclone_style→ edit the XML →set_railclone_style(expected_style=...)→ verify withget_railclone_output. Read railclone.md first. - Data Channel and Max Creation Graph: read 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, andresource://3dsmax-mcp/plugins/{name}/manifest|guide|recipes|gotchas.
Dialogs
execute_maxscriptand tool scripts run in Max's quiet mode: prompts such asqueryBox, overwrite or missing-file warnings silently take their default answer. Passexecute_maxscript(quiet=False)when the user wants to be asked.- Quiet mode would also skip the save-changes prompt (
checkForSave()returns true,resetMaxFile()discards unsaved work,loadMaxFilenever asks), so a script that mentionsresetMaxFile,loadMaxFile,fetchMaxFile,quitMax,checkForSave,max reset file/max file new/max file open/max fetchor the Interface'sFileReset/FileFetch/LoadFromFileruns with prompts shown: a "save changes?" prompt returnsBLOCKED_BY_DIALOGinstead of discarding unsaved work. Never add#noPromptorquiet:trueto those commands, or passquiet=Truewithallow_discard=True, without the user's explicit OK: each discards unsaved changes. The check reads only the script's text: passquiet=Falsewhen a script runs other scripts (fileIn,python.ExecuteFile,macros.run) that may reset, open or quit. While the scene has unsaved changesexecute_maxscriptandexecute_pythonrefuse such a script withUNSAVED_CHANGESand run nothing: ask the user, then save first or passallow_discard=True. ABLOCKED_BY_DIALOGwithretryable: truemeans only the unsaved-changes check waited on a dialog: answer it, then repeat the call. Edits made with undo off (plain execute_maxscript edits by default) don't mark the scene modified. - Reset with
manage_scene, which keeps the prompt; for a load, fetch or quit script, ask the user first. - A call that opens or waits behind a modal dialog returns
BLOCKED_BY_DIALOGwith 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.respondpresses a button by label or index and returns the interrupted call's result. A call whose connection broke comes backstatus: "lost"(outcome unknown): inspect before running it again. - Only dialogs on Max's main thread can be read or pressed. Another thread's dialog is listed but not read, and
respondfails withDIALOG_NOT_ON_MAIN_THREAD;BRIDGE_OUTDATEDmeans the bridge has no dialog monitor. In both cases ask the user to answer it in Max. - 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, Don't Save, overwrite, discard, Fetch, Reset or licensing choices, even when working unattended.
- 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
ToolEnvelopedict (ok/result/error/hint), not a JSON string. Tool-authored hints win over automatic ones;hint.suggested_toolsmay list alternatives. - Classify raw structured errors by
error,codeorstatus=error|failed, never bymessagealone. status: "partial"means only part of the request applied. Readwarningsand the skipped items (notFound,ambiguous,ignored) before continuing.AMBIGUOUS: several objects share the name. Retry with a candidate'shandlewhere the tool takes one; otherwise rename them first (set_object_property(handle=..., property="name")). Never guess which object was meant.TRANSFORM_IGNORED: a constraint, script or expression controller drives that part of the transform. Edit the controller's targets or values, or ask before replacing it; repeating the move changes nothing.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.REQUEST_OUTCOME_UNKNOWN: the request was sent and its reply was lost, so it may have run. Inspect the scene before running it again; never replay it blindly.status: "running"with ajob: a longrender_sceneorcapture_multi_viewoutlived itswait_secondsand keeps running in Max. Call the same tool withjob=to wait again; never repeat the original call. Jobs belong to the MCP server process that started them.MAX_BUSY: Max did not answer within the call's deadline (about 130 s, or a long tool's own timeout). The call keeps running inside Max: never repeat it;max_dialogs(action='inspect')reports its result inblocked_callsonce it finishes.MAX_NOT_RESPONDING, and aMAX_BUSYbefore the request was sent: Max is hung or busy. Retry only whenretryableis true;request_sent: falsemeans nothing reached Max. Wait up to ~10 min, re-checking withget_bridge_status; still blocked after that means a deadlock, and the user must end Max (unsaved work is lost).get_bridge_statusanswers while Max is hung and says what holds its main thread;capture_hang_diagnosticsshows where it is stuck without contacting Max. A modal dialog is not a hang while Max's main thread keeps pumping: calls returnBLOCKED_BY_DIALOGandget_bridge_statusreportsblocked_by_dialog.
Native Core Development
Only when changing the bridge's native C++ handlers; ordinary sessions never need it. reload_native_core (full and progressive profiles) swaps a rebuilt core into the running Max.
reload_native_core(action="status")tells you whether reload applies:layout="hot"reloads.static, the shipped layout, cannot; a native change there applies only after the rebuilt plugin is installed and Max restarts.- After rebuilding the core, call
reload_native_core(action="reload"), check thatbuildchanged (a warning flags an unchanged one), then verify the changed behavior through its real tool, not just the status. - On any error the previous core keeps serving, unless the message says no core is loaded:
CORE_STATIC: static layout. Do not retry.CORE_BUSY(retryable; withwait_msup to 20000 the tool already retried): native calls were still running (including one waiting on a dialog), Max's main thread was busy (a render), or another reload was running. Let long calls finish or answer the dialog withmax_dialogs, then reload, or raisewait_ms(up to 600000).CORE_LOAD_FAILED: the DLL is missing, is not a core build, or would not load or initialize. Fix the cause and rebuild; never retry unchanged.CORE_VERSION_MISMATCH: the core does not match the running shell's interface, SDK, compiler or build configuration. Rebuild Release from the same tree; a change to the shell itself cannot be reloaded.- A lost reload response: never resend it; compare
build,reloadsandlast_errorfrom status. - A reload stops
watch_sceneand drops its events; start it again if needed.query_scene(action="delta")positions, dialogs, the agent viewport, armed render jobs and undo history survive. Calls that arrive mid-swap get a retryableCORE_BUSY. - A reload cannot apply shell changes: the named pipe, main-thread executor, dialog monitoring (Win32 and Qt), scene journal, agent viewport, render job notifications, undo records, the core loader, and the shell/core interface. Those need the rebuilt plugin installed and a Max restart.
- Python tool changes need a fresh MCP server process; a reload does not apply them.
execute_maxscript
- Use it only when no dedicated tool exists: unsupported controller operations, render or environment settings, one-off scripted operations.
codeis JSON-unescaped once before MAXScript parses it. ABAD_PARAMparse 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\Ubreak literals. - Do not put
\nor\tinside 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. - Quit Max only when the user asks:
try (quitMax #noPrompt quiet:true) catch ()discards unsaved changes and drops the connection.MAXSCRIPT_INTERRUPTEDmeans a script was stopped (quitMax, exit, an Esc abort), not that its syntax is wrong.
MAXScript Pitfalls
- Always pass
#noPrompttoimportFile, e.g.importFile @"C:/assets/model.fbx" #noPrompt using:FBXIMP; configure importer options first. - No parentheses with keyword arguments:
Box width:10, notBox() width:10. Boxwidthis X,lengthis Y,heightis Z. - Wrap risky code:
try (...) catch (ex) (ex). Noiseis a texture map;Noisemodifieris the modifier.getDir #tempis Max's temp folder, not the OS temp folder.- Convert .NET strings to MAXScript strings before using string methods.
snapshotAsMeshreturns a world-space TriMesh: read vertices incoordsys worldwithout reapplyingnode.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 Materialis invalid because Material is not a MAXClass; usesceneMaterials.getHandleByAnimreturns values like12345P; quote them when building JSON.- Open Unwrap UVW with
$Box001.modifiers[#Unwrap_UVW].edit(), not only theOpenUnwrapUImacro. - 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 |