save_asset
Writes one Unreal asset package to disk by path and verifies the file persisted, so in-memory edits are kept and failed saves are caught.
Instructions
Write one asset's package to disk by its path and verify the result: still_dirty_after_save false and file_on_disk true are the proof, not saved.
Use it after an edit you want to keep, because most edit tools change the asset in memory only. save_level writes a level's .umap, and reload_asset_package throws an unsaved edit away instead. It writes exactly one package: a compile can dirty others, so run list_dirty_packages before a bulk save and read git status after.
path takes the path or the package that find_assets_by_name returns, as in {"path": "/Game/Core/BP_Door"}. readonly_roots chooses which folders count as vendored, and refuse_readonly whether a save under them is refused or only reported; leave both out to use the project's Read Only Content Roots, which refuse.
Refused during Play-in-Editor, where the engine would drop the write and still report success (see force). When a save fails otherwise, file_read_only says whether the file is read-only on disk (usually a Git LFS lockable file nobody has locked), and the error says whether that is the cause.
Returns: { path: string, package_name: string, saved: bool, success: bool, under_readonly_root: bool, readonly_root_matched: string, refused_readonly: bool, readonly_roots: [string], readonly_roots_defaulted: bool, still_dirty_after_save: bool, file_on_disk: bool, file_read_only: bool, verification: object, verification_checks: [object], error?: string }
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| path | No | Full asset path to save, e.g. /Game/MyGame/Core/BP_MyActor (an .Object suffix is tolerated). Must already exist or the call errors. | |
| force | No | Optional. This command is REFUSED while a Play-in-Editor session is running: it either compiles a Blueprint (the reinstancer can retain a world, and CheckForWorldGCLeaks is Fatal -- it KILLS the editor) or writes to disk (Editor.AllowSavingAssetsDuringPIE=0 makes the engine refuse the write, so it would report success without writing a byte). Stop PIE and retry instead; force=true runs it anyway, at your own risk. | |
| asset_path | No | Alias of `path` (same value, same meaning). Accepted because sibling tools spell this parameter differently; prefer `path`. | |
| readonly_roots | No | Optional array of long-path prefixes treated as vendored/upstream (default: the project's Read Only Content Roots). A save under one of them is REFUSED by default; see refuse_readonly. Pass [] to disable the classification entirely. | |
| refuse_readonly | No | Optional bool. When true, a save whose package falls under one of readonly_roots is REFUSED (success:false, saved:false, refused_readonly:true) instead of performed. Default = the process-wide switch (mcp_common::IsReadOnlyRootSaveEnforcementEnabled(), true unless `mcp.ReadOnlyRootEnforcement 0`); when false the write happens and is only reported + ledgered. |