UEAgent
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| UEAGENT_TOKEN | No | The plugin's Auth token (Editor Preferences > Plugins > UEAgent; default empty). When set, every request to the MCP server must send `Authorization: Bearer <token>`, and the project's `.mcp.json` entry reads the value from `${UEAGENT_TOKEN}`. |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": false
} |
| prompts | {
"listChanged": false
} |
| resources | {
"listChanged": false
} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| find_assets_by_nameA | Find assets by name across every mounted content root (/Game, /Engine, plugin mounts...): the broad "does anything called exist, and where?" search, with each hit's name, object path, class and package. Use it before reading or writing an asset whose path you do not know: a hit's Examples: {"search_pattern": "Grass"} finds every name that contains Grass, in any case; {"search_pattern": "BP_*", "case_sensitive": true} only names that start with BP_, in that case. Returns: { assets: [{ name, path, class, package, is_loaded }], count: int, total_scanned: int, scan_was_pending: bool, search_pattern: string, match_mode: "substring"|"wildcard", case_sensitive: bool, max_results: int, results_truncated: bool } |
| add_graph_nodeA | Create one node in a graph and return it with its node_id and pins, so connect_pins and set_pin_default_value can wire it without another read. Works in a Blueprint graph (Animation and Widget Blueprints included) and in the graph of a material, material function, sound cue, MetaSound Source, PCG graph or Control Rig. To create and wire several Blueprint nodes in one undo step use build_blueprint_graph. Every call adds a new node, except event and custom_event, which give back the one already there; remove_graph_node takes a spare node out. Each value comes from a read: event_name from list_overridable_functions, function_name from get_class_functions (an engine library's list is long enough to come back as a file: get_mcp_audit_report reads its report_path), variable_name from get_blueprint_variables, struct_type from list_structs, enum_path from list_enums, and graph_name from get_blueprint_functions; get_function_pins previews a node's pins before it exists. When a call_function names both, function_library wins over target_class, and node_position wins over position_x and position_y. node_type keywords ignore case. Example: {"asset_path": "/Game/Blueprints/BP_Door", "node_type": "call_function", "function_name": "PrintString", "function_library": "KismetSystemLibrary"}. In another asset, node_type is its editor's own node: a material expression class (/Script/Engine.MaterialExpressionConstant), a sound node type (WavePlayer with sound_wave_path, Mixer, Random...), a MetaSound class path from list_metasound_node_types (UE.Sine.Audio, with major_version), a PCG settings class from list_pcg_node_types (PCGSubgraphSettings with subgraph_path embeds a graph), or a Control Rig unit (SetTransform, TwoBoneIKSimple..., with node_name). Those assets are saved by their editor, and a Control Rig is compiled. A Blueprint is compiled only with compile=true and never saved, and the call is refused during Play-in-Editor (see force). event creates a real override, finding the interface or parent class itself; a name that is not an overridable event is an error, and an interface function with a return value needs add_blueprint_function with override=true. custom_event takes no parameters, so an RPC or anything with arguments must be a function. get_component_by_class makes a node already typed to target_class, so its ReturnValue connects without a cast. Check Returns: { node_id, class, title, pos_x, pos_y, comment, pins: [{ name, direction, type, linked_to, default }], blueprint_name, graph_name, compiled, plus the fields of its node_type } -- event: event_name, is_override (true for a live override), already_exists; custom_event: event_name, node_title, already_existed; cast: cast_to; call_function: function_library, function; make_struct and break_struct: struct_type; select: index_pin, return_value_pin, option_pins, option_count, index_enum; macro_instance: input_pins, output_pins; get_component_by_class: component_class, return_pin_type (when it does not name the class, the pin stayed generic). |
| compile_blueprintA | Compile a Blueprint to regenerate its class and return the real outcome: succeeded is true only with zero errors, with the error and warning counts and, on request, every compiler message tied to its node. Compile after adding nodes, functions, variables or RPCs, and before anything calls them or Play-in-Editor starts. To look for errors without compiling, use check_blueprint_errors for one Blueprint or find_erroring_blueprints for a folder. It does not save: save_asset writes the result. blueprint_name takes a short name or, safer, the full path that find_assets_by_name returns, since a short name shared by two assets compiles one of them. Example: {"blueprint_name": "/Game/Blueprints/BP_Door", "return_messages": true}. With return_messages each message carries the node_id and graph that get_graph_nodes and remove_graph_node take. force only matters during Play-in-Editor, where the call is otherwise refused: force=true compiles anyway, with the risk below. The compile blocks the editor until it ends. Blueprints that depend on this one get their bytecode recompiled, instances of its class are reinstanced, and a garbage collection runs afterwards, so a Blueprint with many dependents costs more and other packages can end up dirty. Undo does not take a compile back: it reverts the graph edit before it, and the class keeps the compiled code until the next compile, so compile again after an undo. During Play-in-Editor the reinstancing leaves a world behind that kills the editor when PIE stops, which is why the call is refused then. num_errors 0 does not prove the graph works: an unresolved wildcard pin on an array or ForEach node can compile clean and still block PIE, and a "was pruned" warning means consumers read defaults, so read the warnings too. Returns: { name, compiled, succeeded, num_errors, num_warnings, messages?: [{ severity: 'error'|'warning'|'note', text, node_id, node_title, graph_name }] } -- messages only with return_messages; the node fields are empty for a message tied to no node. |
| connect_pinsA | Link one node's output pin to another node's input pin, in a Blueprint graph (Animation and Widget Blueprints included) or in the graph of a material, material function, sound cue, MetaSound Source, PCG graph or Control Rig. Use it after add_graph_node, or on nodes that get_graph_nodes lists: both give the node ids and pin names. Not for: testing a pair without linking it (validate_pin_connection), unlinking (disconnect_pin), or creating and linking several Blueprint nodes in one undo step (build_blueprint_graph). Refused during Play-in-Editor (see force). Example: {"asset_path": "/Game/Blueprints/BP_Door", "source_node_id": "<a Branch's node_id>", "source_pin": "then", "target_node_id": "<a PrintString's node_id>", "target_pin": "execute"}. @input and @output stand for the graph's own input and output: target_node_id "@output" with target_pin "BaseColor" feeds a material's main input. On a sound cue, @output makes the source the cue's first node, and target_pin is the parent's input slot index (empty: its first free slot); on a MetaSound they are its graph inputs and outputs, named by the pin; on a PCG graph its input and output nodes. In a Blueprint the graph's schema type-checks the pair, so AnimGraph pose links work too and an incompatible pair is an error, never a silent no-op. An exec output and a data input hold one link each, so linking one that is already linked replaces its old link; each call is one undo step, and undo puts the old link back. A node with a wildcard pin is reconstructed afterwards, so array, ForEach and Select pins take the linked type. A Blueprint is not compiled or saved: compile_blueprint, then save_asset. The other assets are saved by their editor, and a Control Rig is compiled. Returns: { connected, source_node_id, target_node_id, graph_kind? } -- a failed link is an error, so connected is true in every Blueprint reply; another asset answers with its editor's own reply, saved included. |
| get_graph_nodesA | Read the nodes of one graph without changing it: a Blueprint graph (Animation and Widget Blueprints included), or the graph of a material, material function, sound cue, MetaSound Source, PCG graph or Control Rig. It is the read behind add_graph_node, connect_pins and set_pin_default_value, and the way to see what a graph does. Every node has its node_id, which the graph tools take, and its object_path (not in a MetaSound), which get_object_info and set_properties take for the node's own settings. A Blueprint node also lists its class, title, position, comment and pins with their links and defaults; another asset answers with its editor's own view, named by graph_kind. Read a graph, then one node: {"asset_path": "/Game/Blueprints/BP_Door", "compact": true} for the topology, then the same asset_path with "node_id" for every pin of one node. compact leaves out unlinked pins without a default, so a pin missing from a compact listing may still exist: check it by node_id. graph_name picks another graph of a Blueprint, a collapsed graph by its title included, and expand_composites inlines the collapsed graphs instead. It reads the graph as the editor holds it, unsaved edits included, loading the asset from disk if needed, and compiles nothing, so a Blueprint that fails to compile still lists its nodes. A large graph costs many tokens: max_nodes caps the reply, and truncated says whether nodes were left out. Reading a graph under a read-only vendor root leaves its package dirty in memory; reload_asset_package with on_dirty="discard" cleans it. Returns: { blueprint_name, graph_name, nodes: [{ node_id, object_path?, class, title, pos_x, pos_y, comment, pins: [...], subgraph? }], node_count, compact, total_node_count, excluded_count, truncated, graph_kind? } -- node_count counts the nodes returned and total_node_count those that exist; a node holding a nested graph has |
| set_pin_default_valueA | Write the literal default of an UNCONNECTED input pin and return the value read BACK off the pin, in a Blueprint graph, a MetaSound Source or a Control Rig. Blueprint: struct pins are canonicalized -- a bare GameplayTag name becomes (TagName="X"), and an UNREGISTERED tag is rejected up front instead of silently landing as an empty tag; value_json nativizes any struct and is the only way to put SEVERAL tags in a GameplayTagContainer; a plain string is fine for an FText pin. Refused: array, set and map pins (use a MakeArray/MakeSet/MakeMap node), pins split into sub-pins (recombine_blueprint_pin first), and any call while Play-in-Editor runs (see force). TRAP: on a FormatText "Format" pin the literal IS written but the {argument} pins are NOT created, leaving a valid, useless node -- build the string with Concat_StrStr / Conv_StringToText instead. MetaSound: value is read as the input's type ("440", "true", "Hello"), node_id=@input sets a graph input's default, and the asset is saved. Control Rig: value is export text such as (X=1.0,Y=0.0,Z=0.0); the rig is compiled and saved. Material expressions, sound cue nodes and PCG nodes keep such values as properties: write them with set_properties on the node's object_path from get_graph_nodes. Returns: { node_id, pin_name, default_value, graph_kind? } -- default_value is re-read from the pin AFTER the write, so it is the oracle for what actually landed (a rejected value shows up as the old one); for a MetaSound or Control Rig the reply is its editor's own, with the value read back. |
| execute_pythonA | Run Python in the running Unreal Editor, with Unreal's
"Object has no attribute" is the commonest error, and the field usually exists elsewhere: HitResult's impact_point is only in to_dict(), while Transform.translation is an attribute. Ask the object (dir(x), x.to_dict(), x.get_editor_property(...)) instead of guessing. During Play-in-Editor, a call in the script that compiles a Blueprint or saves (compile_blueprint, save_asset, save_loaded_asset, save_packages, save_current_level...) does not run: the script gets a RuntimeError there, and the call fails with pie_refused even if the script catches it. The guard does not see a Blueprint edited without a compile, so stop PIE before editing one. Reading the live world during PIE is safe. One call often answers what a loop would: get_actors_by_class for the actors of a class, get_actor_properties_by_class for their properties with the world that answered, get_properties for one object, get_actor_components, raycast_scene, and watch_pie_property for a value over a PIE session. A script that only enumerates actors gets a Returns: { command, python_success, execution_mode, command_result, output, log_output, log_line_count, engine_log, engine_log_count, engine_log_truncated, log_cursor_before, pie_refused?, hint? } |
| save_assetA | 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 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 path takes the 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 } |
| undoA | Step the editor's undo stack back by one transaction, or by Use it to revert an edit made in the editor, by a tool or by a person: the stack is shared, so read get_undo_state first to see the label you are about to revert. One tool call that changed something is usually one transaction; begin_transaction and end_transaction make several edits one. Undo reverts objects in memory, not files: an edit already saved stays on disk until the next save. Example: {"count": 3} undoes the last three transactions. It stops early at the bottom of the stack or at an undo barrier (set_undo_barrier), and actually_undone then falls short of requested. Returns: { applied: bool, requested: int, actually_undone: int, undo_count: int, can_undo: bool, next_undo: string } |
| get_object_infoA | Portrait of one object whose property names you do not know yet: its identity and class, class flags and interfaces, every reflected property as { name: typed_value }, and for an actor its components. Use it first on anything unfamiliar, then write with set_properties using the names it lists. Once you know the names, get_properties reads only those and is far cheaper: an actor's portrait can run to hundreds of properties. get_class_properties describes a class with no instance, and get_actor_components lists the components alone. object_path takes a A reply over 40,000 characters (the console variable mcp.OversizeResponseChars), common for a Blueprint with include_property_metadata, is written to a file instead: the reply keeps its scalar fields and gives report_path, which get_mcp_audit_report reads. An object or a world it cannot find is an error naming what it searched. Returns: { object_path: string, object_name: string, class_name: string, class_path: string, super_class: string, is_valid: bool, is_abstract, is_deprecated, is_placeable, is_native, config_name, implemented_interfaces: [{interface, implemented_by_k2}], class_group?, is_blueprintable?, is_blueprint_type?, display_name?, tooltip?, properties?: {prop_name: typed_value} | [<typed descriptor + value>], property_count?: int, components?: [{name, class}], world: string, world_name: string|null, world_package: string|null, world_type: string|null, net_mode: string|null, pie_instance: number } -- properties/property_count/components only with include_properties; properties is the array form with include_property_metadata; super_class is "None" for UObject. |
| set_propertiesA | Write one or several editable properties of one object in the editor world, as the Details panel does: an asset, a CDO, a placed actor, one of its components ("Actor.Component") or an asset's sub-object. Each write is undoable, dirties the package and re-registers the component, and the reply lists what was set and what failed, with why. Use it for any setting of a light, fog, sky, decal, camera, post-process volume, sound, Niagara system, material expression or other asset. Not for: a component in a live PIE world (set_component_property), a Blueprint's component template (set_blueprint_component_properties), a widget inside a Widget Blueprint (set_widget_property), or an object's own setter function (call_function). Take the property names from get_object_info, and object_path from the tool that found the object: find_assets_by_name for an asset, get_graph_nodes for a graph node, or "Label.ComponentClass" for a placed actor, such as "Sun.DirectionalLightComponent". Example: {"object_path": "Sun.DirectionalLightComponent", "properties": {"Intensity": 10, "LightColor": {"R": 255, "G": 200, "B": 150}}} writes two properties; property_name "Intensity" with value 10 writes one, and both forms together write properties first. A struct takes only the fields to change: {"RelativeLocation": {"Z": 200}} keeps X and Y. A name can also be a PATH, to write one value deep inside without rewriting what holds it: "RelativeLocation.Z", "Items[2]" or "Items[2].Name" (an existing element; to resize an array, write the whole array), and through an object reference to the object it points to, such as an instanced sub-object: "Layer.Settings.Size". The object holding the last segment is the one written, made undoable and notified with the chain of its segments, as the Details panel does for a nested edit; every segment it holds must be writable, and a reference the path goes through only needs to be visible. A sub-object's asset is finished as its editor does it: a graph node is reconstructed and its Blueprint marked modified (compile it), a sound cue recaches, a Niagara system recompiles and a blend space is resampled; no Blueprint is compiled. clamp=true keeps numbers inside ClampMin/ClampMax and lists what it clamped. save=true saves an asset's package and is refused for an object in a level, which save_level writes. Refused before anything is written: a struct key that names no field, an unregistered gameplay tag, and any call during Play-in-Editor (see force). Returns: { object_path: string, resolved_path: string, set: [string], failed: [{property, error}], owning_asset?: string (a sub-object's asset), finished?: [string] (what ran on that asset), clamped?: [string] ("name=value", with clamp), saved?: bool, save_error?: string (with save), success: bool } |
| find_toolsA | Searches this server's 1549 Unreal Editor tools and returns the best matches, each with its name, domain, parameters (* marks a required one) and the first sentence of its description. Use it before guessing a tool name or writing Python; when you already know the name, skip it: describe_tool and call_tool take the name directly. Examples: {"query": "material parameter"} searches every tool, {"query": "landscape", "domain": "Level"} searches one domain, and {"domain": "Water"} lists a domain's tools. Each query word scores on a whole word of a tool's name first, then the start of one, then the domain, then the description, and a tool that matches more of the words ranks higher, so a query that spells a tool's name, such as "spawn actor", puts it first. With no arguments it lists the 55 domains, each with what it covers and its size, and the packs with their tools. It only reads the catalog, so it is cheap to repeat. Then describe_tool gives a match's full schema and call_tool runs it, or enable_toolset adds its pack or domain to your tool list. Returns: { success, matches: int, tools: [{ name, domain, params?, summary }], more?: string, next?: string } -- with no arguments { success, total_tools, domains: [{ domain, description, tools }], packs: [{ pack, description, tools }], next }. An unknown |
| describe_toolA | Returns one tool's full description, input JSON schema and annotations, as a full tool list carries them. Use it before call_tool when find_tools' parameter line does not say how to fill an argument, such as its format or its allowed values; a tool already in your list has its schema there.
Returns: { success, name, domain, description, inputSchema, annotations?, retired_name? } |
| call_toolA | Runs any of this server's tools by name: The call takes the path of a direct one: the same validation, guards and readback, and the same effects, so it can delete or overwrite whatever the named tool can; describe_tool shows that tool's annotations. Refused before anything runs: no Returns: the named tool's own reply, unchanged. |
| enable_toolsetA | Adds packs or whole domains to this connection's tool list, so their tools are called directly instead of through call_tool; the 11 core tools are always listed. Give A pack is the few tools of one job the core leaves out: actors (delete_actor, find_actors_by_name, spawn_actor, get_actors_by_class), levels (get_current_level, list_dirty_packages, load_level, save_level), play (capture_viewport, is_play_in_editor_active, start_play_in_editor, stop_play_in_editor), assets (get_asset_referencers, delete_asset, duplicate_asset), blueprint_authoring (add_blueprint_variable, build_blueprint_graph, create_blueprint, create_blueprint_from_actors, disconnect_pin, get_blueprint_info, remove_graph_node), data_tables (get_data_table_row, set_data_table_row_fields). A domain is every tool of one area of the editor, such as Material or Animation, and can add a hundred tools to your context: find_tools with no arguments lists the domains and their sizes. Prefer a pack when one covers the job. It changes only which tools this connection lists, never the editor or the project, and lasts until the connection's session ends; enabling the same one again adds nothing. The server announces the new list and your client re-reads it; if your client does not, call_tool reaches the same tools. An unknown name is an error that lists the packs and the domains. Returns: { success, packs?: [{ pack, already_listed, tools }], domains?: [{ domain, already_listed, tools }], tools_added: int, list_changed: bool, note?: string } |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
| ueagent-blueprint-actor | Create a Blueprint actor in Unreal from a description (its components, its event-graph logic, a clean compile and an instance in the level) with UEAgent's MCP tools. Use when the user asks for a new actor Blueprint, such as a door, a pickup, a trigger zone or a turret. |
| ueagent-contribute-fix | Send a change to UEAgent's code as a pull request to its GitHub repository (CarlxsMG/UEAgent) - a fix made in the copy of the plugin inside an Unreal project (Plugins/UEAgent) or in a clone of the repository - following the repository's rules: the regenerated catalog, the checks, how it was measured, and the PR template. Use when the user wants to contribute a fix or a new tool to UEAgent, send a pull request, or share a change they made to the plugin. |
| ueagent-level-lighting | Light the current Unreal level as an outdoor scene (a sun, a sky atmosphere, a sky light and height fog) with UEAgent's MCP tools, then show the result in a screenshot. Use when the user asks to light a level, set up daylight or a sunset, or add a sky. |
| ueagent-project-audit | Audit an Unreal project, or one content folder, with UEAgent's MCP tools - Blueprints that do not compile, graph bugs that compile clean, unused assets, broken references, leftover redirectors and Map Check messages - and report what to fix, most serious first. Use when the user asks to check the health of the project, find what is broken, or clean it up. |
| ueagent-report-issue | Report a problem with UEAgent, or ask for a feature, as an issue on its GitHub repository (CarlxsMG/UEAgent) - a tool that failed, answered success without doing the work, refused a valid call, or closed or froze the editor; or something no tool does yet. It gathers the versions, the call and the editor log, shows the user the full issue, and opens it only once they agree. Use when the user wants to report a UEAgent bug, file an issue, or request a tool. |
| ueagent-skill-builder | Turn an Unreal Editor workflow into a reusable skill built on UEAgent's MCP tools. It finds the tools the task needs, rehearses the task once in the editor, checks the result, and writes a SKILL.md with the tool sequence and the pitfalls it met. Use when the user asks to make, capture or automate an Unreal workflow as a skill, or to repeat a task they just did with the tools. |
| ueagent-tool-map | Map of UEAgent's Unreal Editor tools - which core tool, pack or domain covers each kind of task, and the order to call them in (find, read, change, check, compile, save). Use at the start of an Unreal Editor task when the tool it needs is not in your list, before writing Python for something a tool may already do, or when unsure which UEAgent tool fits. |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
| guide | Map of UEAgent's Unreal Editor tools - which core tool, pack or domain covers each kind of task, and the order to call them in (find, read, change, check, compile, save). Use at the start of an Unreal Editor task when the tool it needs is not in your list, before writing Python for something a tool may already do, or when unsure which UEAgent tool fits. |
| ueagent-blueprint-actor | Create a Blueprint actor in Unreal from a description (its components, its event-graph logic, a clean compile and an instance in the level) with UEAgent's MCP tools. Use when the user asks for a new actor Blueprint, such as a door, a pickup, a trigger zone or a turret. |
| ueagent-contribute-fix | Send a change to UEAgent's code as a pull request to its GitHub repository (CarlxsMG/UEAgent) - a fix made in the copy of the plugin inside an Unreal project (Plugins/UEAgent) or in a clone of the repository - following the repository's rules: the regenerated catalog, the checks, how it was measured, and the PR template. Use when the user wants to contribute a fix or a new tool to UEAgent, send a pull request, or share a change they made to the plugin. |
| ueagent-level-lighting | Light the current Unreal level as an outdoor scene (a sun, a sky atmosphere, a sky light and height fog) with UEAgent's MCP tools, then show the result in a screenshot. Use when the user asks to light a level, set up daylight or a sunset, or add a sky. |
| ueagent-project-audit | Audit an Unreal project, or one content folder, with UEAgent's MCP tools - Blueprints that do not compile, graph bugs that compile clean, unused assets, broken references, leftover redirectors and Map Check messages - and report what to fix, most serious first. Use when the user asks to check the health of the project, find what is broken, or clean it up. |
| ueagent-report-issue | Report a problem with UEAgent, or ask for a feature, as an issue on its GitHub repository (CarlxsMG/UEAgent) - a tool that failed, answered success without doing the work, refused a valid call, or closed or froze the editor; or something no tool does yet. It gathers the versions, the call and the editor log, shows the user the full issue, and opens it only once they agree. Use when the user wants to report a UEAgent bug, file an issue, or request a tool. |
| ueagent-skill-builder | Turn an Unreal Editor workflow into a reusable skill built on UEAgent's MCP tools. It finds the tools the task needs, rehearses the task once in the editor, checks the result, and writes a SKILL.md with the tool sequence and the pitfalls it met. Use when the user asks to make, capture or automate an Unreal workflow as a skill, or to repeat a task they just did with the tools. |
| ueagent-skill-builder/template.md | The file template.md of the ueagent-skill-builder skill, which its instructions refer to. |
TDQS
Scored across 15 tools
The graph quartet (add_graph_node, connect_pins, set_pin_default_value, get_graph_nodes) has crisp read/write/link boundaries, and the discovery quartet (find_tools, describe_tool, call_tool, enable_toolset) is explicitly staged in each description. Minor overlap remains: set_properties and set_pin_default_value both write values (distinguished only by target: object property vs unconnected pin), and execute_python/call_tool can reach anything any other tool does, though both are framed as explicit escape hatches.
All 15 names are snake_case and 14 follow a verb_noun shape (add_graph_node, save_asset, get_object_info, enable_toolset). The single deviation is `undo`, a bare verb with no object, which is readable but breaks the otherwise uniform pattern.
15 core tools sits at the top of the ideal band and each one is load-bearing: graph authoring, property read/write, compile/save, discovery, dispatch, toolset expansion, and undo. The 1549-tool catalog is deliberately kept behind find_tools/call_tool rather than dumped into the list, which is the right scoping decision.
The core surface covers the full edit lifecycle for graphs and properties: read, create, wire, set values, compile, save, and undo, plus a Python escape hatch for anything unmodeled. The visible gaps (no actor spawn, level save, asset delete/duplicate, create_blueprint) are real but bridged by documented packs via enable_toolset and by call_tool, so they are workable rather than dead ends.